<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.wesnoth.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Pentarctagon</id>
	<title>The Battle for Wesnoth Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.wesnoth.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Pentarctagon"/>
	<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/Special:Contributions/Pentarctagon"/>
	<updated>2026-09-08T13:01:11Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.31.16</generator>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Wesnoth:Copyrights&amp;diff=75641</id>
		<title>Wesnoth:Copyrights</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Wesnoth:Copyrights&amp;diff=75641"/>
		<updated>2026-09-04T07:24:51Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* User Made Content - Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page contains all relevant information regarding copyright and how it applies to different categories of Wesnoth content and code.  Should there ever be any contradicting information from other sources concerning Wesnoth and copyright, the information on this page takes precedence.&lt;br /&gt;
&lt;br /&gt;
== The Wiki ==&lt;br /&gt;
The content of the Wesnoth wiki is under the [https://www.gnu.org/licenses/fdl.html GNU Free Documentation License (GFDL)]. By contributing content to the wiki, you agree for your contributions to be distributed under the GFDL.&lt;br /&gt;
&lt;br /&gt;
As the project owners, with the right to issue copies of its content under dual or multiple licenses, the Wesnoth maintainers specifically grant the right to copy content from game to wiki or wiki to game, said copy acquiring only the license obligations associated with the destination.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Content&amp;quot; here refers to any and all text explanations, code snippets and examples, images, and audio files that are part of the wiki.&lt;br /&gt;
&lt;br /&gt;
== The Battle for Wesnoth - Code Contributions ==&lt;br /&gt;
Code contributed to the Battle for Wesnoth project is licensed under the [https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html GNU GPL v2] or later. This includes '''all''' code, whether it be C++, lua, WML, or any other language that is currently part of the project or is added in the future. This also includes data files, such as maps, as well as story content.&lt;br /&gt;
&lt;br /&gt;
Some portions of the source code can be used under alternate license terms, whenever stated as such in the source.&lt;br /&gt;
&lt;br /&gt;
== The Battle for Wesnoth - Visual and Audio Contributions ==&lt;br /&gt;
All visual and audio assets are licensed under either the GNU GPL v2 or later, or the [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons BY-SA 4.0]. While existing contributions may be under the GNU GPL v2 or later, all future contributions will be under the CC BY-SA 4.0 license unless they are specifically noted to instead be under the GNU GPL v2 or later. In any case, regardless of whether a visual or audio contribution is licensed under the GNU GPL v2 or later or the CC BY-SA 4.0, the person contributing the asset always retains the full rights to their own work and can continue to use it in whatever way they wish.&lt;br /&gt;
&lt;br /&gt;
Additionally, unless explicitly stated otherwise, all visual and audio assets that are posted to either the [https://forums.wesnoth.org/viewforum.php?f=14 Music &amp;amp; Sound Development forum] or to the [https://forums.wesnoth.org/viewforum.php?f=9 Art Contributions forum] are considered as potential contributions and are automatically placed under either the GNU GPL v2 or later (prior to July 30th, 2017), or the CC BY-SA 4.0 (as of July 30th, 2017).&lt;br /&gt;
&lt;br /&gt;
For visual and audio assets that are licensed under the GNU GPL v2 or later, we interpret &amp;quot;preferred form of the work for making modifications&amp;quot; as the modifiable form that the author chooses to provide us for the source tree.&lt;br /&gt;
&lt;br /&gt;
Visual and audio assets for which you do not own the rights '''cannot''' be accepted, nor can any derivative works, nor anything that falls under [https://en.wikipedia.org/wiki/Fair_use fair use].&lt;br /&gt;
&lt;br /&gt;
== User Made Content - Code ==&lt;br /&gt;
Code uploaded to the official Wesnoth add-ons server must be licensed under a version of the GNU GPL or a license that's GPL-compatible. This includes '''all code''', whether it be lua, WML, or any other language that is supported by Wesnoth. This also includes data files, such as maps, as well as story content. If no version of the GNU GPL is specified, all code will by default be placed under the GNU GPL v2 or later.&lt;br /&gt;
&lt;br /&gt;
Code that for any reason cannot be licensed under the GNU GPL is '''not''' allowed on the add-ons server.&lt;br /&gt;
&lt;br /&gt;
== User Made Content - Visual and Audio Content ==&lt;br /&gt;
All visual and audio assets must be licensed under either a version of the GNU GPL or [https://en.wikipedia.org/wiki/Creative_Commons_license#Types_of_licenses any Creative Commons license]. This includes &amp;quot;non-Free&amp;quot; variations such as Non-Commercial (NC) and Non-Derivative (ND). If no version of the GNU GPL nor any Creative Commons license is specified, all visual and audio assets will by default be placed under the GNU GPL v2 or later.&lt;br /&gt;
&lt;br /&gt;
For visual and audio assets that are licensed under the GNU GPL, we interpret &amp;quot;preferred form of the work for making modifications&amp;quot; as the modifiable form that the author chooses to upload to the add-ons server.&lt;br /&gt;
&lt;br /&gt;
For visual and audio assets that are not licensed under the GNU GPL, they must be explicitly denoted as released under a Creative Commons license in either:&lt;br /&gt;
* a combined toplevel file, e.g. &amp;quot;add-ons/My_Addon/ART_LICENSE&amp;quot;&lt;br /&gt;
* a file in the same folder as the asset with &amp;quot;.license&amp;quot; appended, e.g. &amp;quot;add-ons/My_Addon/images/units/axeman.png.license&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Visual and audio assets that for any reason cannot be placed under the GNU GPL or a Creative Commons license are '''not''' allowed on the add-ons server.&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=MultiplayerServerWML&amp;diff=75635</id>
		<title>MultiplayerServerWML</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=MultiplayerServerWML&amp;diff=75635"/>
		<updated>2026-08-29T15:46:23Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* Queues */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the [[WML]] used to communicate with the multiplayer server for Wesnoth, [[wesnothd]].&lt;br /&gt;
&lt;br /&gt;
== The handshake ==&lt;br /&gt;
&lt;br /&gt;
The client sends four bytes, then the server replies with four bytes. To get a new connection number, the client will send these four bytes: 0x00 0x00 0x00 0x00. The server then sends back the connection number (wesnothd calls this number the &amp;quot;socket number&amp;quot;). Since 1.13+ the server no longer is using socket numbers to keep track of clients and always sends the same number to them all. Since 1.15+ client can also send 0x00 0x00 0x00 0x01 instead to request entire connection to be [https://github.com/wesnoth/wesnoth/blob/2f8136951cd77526188cf8d0fb2cf21eaa2ebe63/src/server/common/server_base.hpp#L60-L76 encapsulated in TLS] immediately '''after'''. If the handshake is successful, the server will be the first to send a data package. All packages are in [http://en.wikipedia.org/wiki/Gzip gzip] format and are preceded by four bytes that specify the size of the package to come in '''big-endian''' (network byte order). Below you'll find information about what data the (unzipped) packages contain. Unpacked WML uses utf-8 charset.&lt;br /&gt;
&lt;br /&gt;
== The login procedure ==&lt;br /&gt;
&lt;br /&gt;
* server request (optional)&lt;br /&gt;
** '''[version]'''&lt;br /&gt;
&lt;br /&gt;
* client response&lt;br /&gt;
** '''[version]'''&lt;br /&gt;
*** '''version''': The client's version string.&lt;br /&gt;
*** '''client_source''': The client's distribution info. (Steam, SourceForge, App Store, etc.)&lt;br /&gt;
&lt;br /&gt;
* server response (if the server does not accept this version)&lt;br /&gt;
** '''[redirect]'''&lt;br /&gt;
*** '''host''': The host you should connect to.&lt;br /&gt;
*** '''port''': The port you should connect to.&lt;br /&gt;
*** '''version''': A comma-separated list of globs that this server should accept (e.g. &amp;quot;1.0*,1.2*,1.4*,1.7*,1.8*&amp;quot;)&lt;br /&gt;
** or '''[reject]''' (if the version is unknown)&lt;br /&gt;
*** '''accepted_versions''': A comma-separated list of globs that this server does accept&lt;br /&gt;
&lt;br /&gt;
* server request&lt;br /&gt;
** '''[mustlogin]'''&lt;br /&gt;
&lt;br /&gt;
* client response&lt;br /&gt;
** '''[login]'''&lt;br /&gt;
*** '''username''': The username the client would like to have.&lt;br /&gt;
*** '''password''': The hashed password, created from the password and salt received from the server. More information about how this password is being generated, including a real world example, can be found in the file [http://forum.wesnoth.org/download/file.php?id=41145 HashedPasswords.pdf] (885 KiB). Since version 1.15+ if TLS was successfully established before then password will be passed as is, without hashing, relying on TLS for secrecy. Passing password hashes is no longer supported to free the client from responsibility to support all hash schemes the forum can potentially use. Client will emit error instead of trying to send password if TLS wasn't established.&lt;br /&gt;
&lt;br /&gt;
* server response&lt;br /&gt;
** '''[join_lobby]'''&lt;br /&gt;
*** '''is_moderator''': &amp;quot;yes&amp;quot; if the user is a moderator, &amp;quot;no&amp;quot; otherwise.&lt;br /&gt;
*** '''profile_url_prefix''': The external URL prefix for player profiles (empty if the server doesn't have an attached database)&lt;br /&gt;
** or '''[error]''' (server is waiting for another '''[login]''' message now)&lt;br /&gt;
*** '''message''': The error message.&lt;br /&gt;
*** '''password_request''': If not empty the server asks the client to provide a password for its desired username.&lt;br /&gt;
*** '''phpbb_encryption''': If &amp;quot;yes&amp;quot; the client will encrypt the password using phpbb's algorithm.&lt;br /&gt;
*** '''random_salt''': Random salt sent to the client for mixing with the password hash.&lt;br /&gt;
*** '''hash_seed''': Salt generated from the original hash that is required to recreate it.&lt;br /&gt;
*** '''salt''': Salt generated from the original hash that is required to recreate it.&lt;br /&gt;
*** '''force_confirmation''': Display an ok/cancel dialog with the content of the 'message' key.&lt;br /&gt;
&lt;br /&gt;
* server response&lt;br /&gt;
** '''[gamelist]'''&lt;br /&gt;
*** '''[game]''' (repeated)&lt;br /&gt;
**** '''id''': A unique id of the game.&lt;br /&gt;
**** '''name''': The title of the game.&lt;br /&gt;
**** '''mp_scenario''': The id of the scenario.&lt;br /&gt;
**** '''mp_era''': The id of the used era.&lt;br /&gt;
**** '''mp_use_map_settings''': Does the game use the map settings specified in the scenario.&lt;br /&gt;
**** '''mp_fog''': Does the game use fog.&lt;br /&gt;
**** '''mp_shroud''': Does the game use shroud.&lt;br /&gt;
**** '''mp_village_gold''': The number of gold per village.&lt;br /&gt;
**** '''experience_modifier''': The experience setting.&lt;br /&gt;
**** '''mp_countdown''': Does the game use a timer.&lt;br /&gt;
**** '''mp_countdown_reservoir_time''': Upper limit of the possibly available time.&lt;br /&gt;
**** '''mp_countdown_init_time''': Initial time.&lt;br /&gt;
**** '''mp_countdown_action_bonus''': Time bonus per action.&lt;br /&gt;
**** '''mp_countdown_turn_bonus''': Time bonus per turn.&lt;br /&gt;
**** '''map_data''': The map data. ''Notice: not sent to lobby if the game uses shroud''&lt;br /&gt;
**** '''hash''': The hash value of the map_data.&lt;br /&gt;
**** '''observer''': Are observers allowed or not.&lt;br /&gt;
**** '''human_sides''': The number of sides played by humans.&lt;br /&gt;
**** '''slots''': The number of vacant/max slots.&lt;br /&gt;
**** '''[slot_data]''' replaces '''slots''' since {{DevFeature1.13|12}}&lt;br /&gt;
***** '''max''': The number of total slots.&lt;br /&gt;
***** '''vacant''': The number of vacant slots.&lt;br /&gt;
**** '''turn''': The current turn/max turn.&lt;br /&gt;
**** '''[turn_data]''' replaces '''turn''' since {{DevFeature1.13|12}}&lt;br /&gt;
***** '''current''': The current turn number.&lt;br /&gt;
***** '''max''': The total number of turns.&lt;br /&gt;
**** '''[modification]''' Modifications used in this game. See [[ModificationWML]].&lt;br /&gt;
***** '''id''': ID of the modification.&lt;br /&gt;
***** '''name''': Name of the modification.&lt;br /&gt;
***** '''addon_id''': ID of the addon the modification is from.&lt;br /&gt;
***** '''require_modification''': A boolean value; if set to yes, all players have to have this modification installed to join the game.&lt;br /&gt;
**** '''[options]''' Options selected for this game. See [[OptionWML]].&lt;br /&gt;
***** '''[campaign|era|modification|multiplayer]'''&lt;br /&gt;
****** '''id''': ID of the addon the campaign|era|modification|multiplayer (scenario) is from.&lt;br /&gt;
****** '''[option]'''&lt;br /&gt;
******* '''id''': ID of the option.&lt;br /&gt;
******* '''value''': Value of the option.&lt;br /&gt;
** '''[user]''' (repeated)&lt;br /&gt;
*** '''name''': The username of the player.&lt;br /&gt;
*** '''registered''': Whether the player is registered (on the forum).&lt;br /&gt;
*** '''moderator''': Whether the player is a moderator.&lt;br /&gt;
*** '''forum_id''': The forum ID of the player. With '''profile_url_prefix''' this can be used to construct the profile page URL of a player.&lt;br /&gt;
*** '''game_id''': The ID of the game the player is in.&lt;br /&gt;
*** '''location''': The name of the game the player is in.&lt;br /&gt;
*** '''available''': &amp;quot;yes&amp;quot; if the player is in the lobby; &amp;quot;no&amp;quot; if in a game.&lt;br /&gt;
Many of the keys under [game] are described more indepth on the [[ScenarioWML]] page.&lt;br /&gt;
&lt;br /&gt;
== Error messages ==&lt;br /&gt;
&lt;br /&gt;
* '''[error]'''&lt;br /&gt;
** '''message''': The error message.&lt;br /&gt;
** '''password_request''': This is a response to a login attempt. The client needs to send a password on another login attempt.&lt;br /&gt;
** '''force_confirmation''': Confirmation to login even if there is an existing client with the same name. If login is continued then that existing client is getting kicked.&lt;br /&gt;
&lt;br /&gt;
== Chat (lobby and in-game) ==&lt;br /&gt;
&lt;br /&gt;
* '''[message]'''&lt;br /&gt;
** '''sender''': (optional - filled by the server) The sender of the message.&lt;br /&gt;
** '''message''': The message itself.&lt;br /&gt;
** '''room''': The room the message is from/to&lt;br /&gt;
* '''[whisper]'''&lt;br /&gt;
** '''receiver''': The receiver of the whisper&lt;br /&gt;
** '''sender''': (optional - filled by the server) The sender of the whisper.&lt;br /&gt;
** '''message''': The message itself.&lt;br /&gt;
&lt;br /&gt;
== Nickname registration related commands (lobby and in-game) ==&lt;br /&gt;
&lt;br /&gt;
* '''[nickserv]'''&lt;br /&gt;
** '''[info]''': Request info about another username.&lt;br /&gt;
*** '''name''': The username.&lt;br /&gt;
&lt;br /&gt;
== Updating the lobby state ==&lt;br /&gt;
&lt;br /&gt;
* '''[gamelist_diff]''': server message - basically a [[DiffWML|diff]] from two gamelists, which also includes the user list.&lt;br /&gt;
&lt;br /&gt;
* '''[observer]''' or '''[observer_quit]''': server message - players joining([observer_quit] - quitting the lobby &amp;quot;game&amp;quot;)/quitting([observer] - joining the lobby &amp;quot;game&amp;quot;) a game&lt;br /&gt;
** '''name''': Username of the player/observer.&lt;br /&gt;
* '''[refresh_lobby]''': Request the full gamelist.&lt;br /&gt;
&lt;br /&gt;
== Game setup (the phase from creation to start) ==&lt;br /&gt;
To create a game the client sends:&lt;br /&gt;
* '''[create_game]'''&lt;br /&gt;
** '''name''': The title of the game.&lt;br /&gt;
** '''password''': The password to use to join the game.&lt;br /&gt;
** '''ignored''': The list of ignored players from the host.&lt;br /&gt;
** '''auto_hosted''': True if this request is from a bot or a server-side queue, false otherwise.&lt;br /&gt;
** '''queue_type''': Either &amp;quot;normal&amp;quot; or &amp;quot;server_preset&amp;quot;.&lt;br /&gt;
** '''queue_id''': The ID of the queue this game is being created from.&lt;br /&gt;
&lt;br /&gt;
followed by a message with the scenario options as under [game] (see above) plus the scenario data ([time], [era], [side], etc. see [[ScenarioWML]])&lt;br /&gt;
&lt;br /&gt;
* '''[join]'''&lt;br /&gt;
** '''id''': The id of the game.&lt;br /&gt;
** '''observe''': Join the game as an observer.&lt;br /&gt;
&lt;br /&gt;
* '''[scenario_diff]''': [[DiffWML|diff]] of the [[ScenarioWML]] (side changes, etc.)&lt;br /&gt;
&lt;br /&gt;
* '''[start_game]''': sent by the host to start a game&lt;br /&gt;
* '''[leave_game]''': sent by the client when it leaves a game; sent by the server to make a client leave a game&lt;br /&gt;
** '''reason''': optional reason if sent by the server and was initiated by moderator action&lt;br /&gt;
&lt;br /&gt;
== In-game communication ==&lt;br /&gt;
&lt;br /&gt;
Normal scenario communication ([[ReplayWML]]):&lt;br /&gt;
* '''[turn]'''&lt;br /&gt;
** '''[command]''': (repeated) can contain all the tags you can find in a [[ReplayWML|replay]]: [recruit], [move], [end_turn], etc.&lt;br /&gt;
*** '''[speak]'''&lt;br /&gt;
**** '''message''': text of the message&lt;br /&gt;
**** '''id''': the sender&lt;br /&gt;
**** '''team_name''': the name of the team the message is for - empty if it's a public message&lt;br /&gt;
&lt;br /&gt;
Multiplayer specific communication:&lt;br /&gt;
* '''[request_choice]'''&lt;br /&gt;
** '''request_id''': unique ID of the choice request&lt;br /&gt;
** '''[random_seed]''': client requests a random number (used for attacks for example)&lt;br /&gt;
** '''[change_controller_wml]''': change controller request from scenario WML&lt;br /&gt;
*** '''side''': side number&lt;br /&gt;
*** '''old_controller''': old [[SideWML#controller|controller]] value&lt;br /&gt;
*** '''new_controller''': new [[SideWML#controller|controller]] value&lt;br /&gt;
* '''[store_next_scenario]''': sent by the host - the scenario data (see [[ScenarioWML]]) to advance to the next scenario&lt;br /&gt;
* '''[notify_next_scenario]''': sent by the server to tell players that the data for the next scenario is available&lt;br /&gt;
* '''[load_next_scenario]''': sent by the client to request the data for the next scenario&lt;br /&gt;
* '''[next_scenario]''': data for the next scenario (see [[ScenarioWML]]), sent by the server on request&lt;br /&gt;
&lt;br /&gt;
* '''[info]''': sent by the host on game end - info about the game state&lt;br /&gt;
** '''type''': &amp;quot;termination&amp;quot; &lt;br /&gt;
** '''condition''': the termination reason&lt;br /&gt;
&lt;br /&gt;
If a player leaves this is sent to the host for all sides he owned.&lt;br /&gt;
* '''side_drop''': The number of a side that dropped because a player left.&lt;br /&gt;
* '''controller''': The controller of that side. (&amp;quot;ai&amp;quot;, &amp;quot;network&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
Client commands:&lt;br /&gt;
* '''[change_controller]''': a player (un)droids one of his sides or assigns control to someone else (The host can assign control for any side.)&lt;br /&gt;
** '''side''': the side to change controller&lt;br /&gt;
** '''player''': the nickname of the player to take control&lt;br /&gt;
** '''controller''': the new controller: &amp;quot;human&amp;quot; or &amp;quot;human_ai&amp;quot;&lt;br /&gt;
** '''own_side''': &amp;quot;yes&amp;quot;&lt;br /&gt;
* '''[muteall]''': the host mutes/unmutes all observers - toggles&lt;br /&gt;
* '''[mute]''': the host mutes an observer - toggles&lt;br /&gt;
** '''username''': the username of the observer - if not specified the servers returns a list of muted usernames&lt;br /&gt;
* '''[kick]''' or '''[ban]''': the host kicks/bans a player/observer&lt;br /&gt;
** '''username''': the username of the player/observer&lt;br /&gt;
&lt;br /&gt;
== Game history ==&lt;br /&gt;
This is a request to query a set of 11 rows of game history data based on the provided search criteria. The official client calls this from the Match History button in the  multiplayer lobby to display 10 rows of data. The 11th row is used as a flag to indicate whether there is more data to be queried or not via the right/left arrows on the dialog.&lt;br /&gt;
&lt;br /&gt;
* '''[game_history_request]'''&lt;br /&gt;
** '''offset''': where in the result set to start returning data from. If there are 50 results and offset 10 is given, then rows 10-21 will be returned.&lt;br /&gt;
** '''search_player''': the forum username of the player to search for.&lt;br /&gt;
** '''search_game_name''': the name of the game to filter results by. Can use the * (matches any character before or after it's used) and _ (matches any single character) wildcards.&lt;br /&gt;
** '''search_content_type''': the type of content to filter by. Must be one of:&lt;br /&gt;
*** '''0''': scenario&lt;br /&gt;
*** '''1''': era&lt;br /&gt;
*** '''2''':modification&lt;br /&gt;
** '''search_content''': The content to filter by. This is the ID of the content, not the name displayed on the UI, due to the translated name getting stored in the database.&lt;br /&gt;
&lt;br /&gt;
== Queues ==&lt;br /&gt;
Queue info sent to the client on join or when the server's config is reloaded and the queue information has changed:&lt;br /&gt;
* '''[queue_update]'''&lt;br /&gt;
** '''queue_id''': The server's unique ID for the queue.&lt;br /&gt;
** '''action''': One of add/update/remove.&lt;br /&gt;
** '''display_name''': The text to show in the list of queues in the lobby. Only used by add/update.&lt;br /&gt;
** '''players_required''': How many players are required before a game is started. Only used by add/update.&lt;br /&gt;
&lt;br /&gt;
When there are enough players to start a game, the last player to join the queue is chosen as the host and their client is told to create the game with the provided settings. This skips the game creation screen and goes straight to the staging screen. The other players in the queue are then told to join that game using the normal [join] command:&lt;br /&gt;
* '''[create_game]'''&lt;br /&gt;
** '''queue_id''': The ID of the queue to create the game for.&lt;br /&gt;
** '''[game]'''&lt;br /&gt;
*** '''scenario''': The ID of the scenario to create the game for.&lt;br /&gt;
*** '''era''': The ID of the era to use.&lt;br /&gt;
*** '''fog''': Whether to have fog enabled.&lt;br /&gt;
*** '''shroud''': Whether to have shroud enabled.&lt;br /&gt;
*** '''village_gold''': How much gold each village provides.&lt;br /&gt;
*** '''village_support''': How much unit support each village provides&lt;br /&gt;
*** '''experience_modifier''': The experience modifier to use.&lt;br /&gt;
*** '''countdown''': Whether turn timers are enabled.&lt;br /&gt;
*** '''countdown_init_time''': The initial timer value in seconds.&lt;br /&gt;
*** '''countdown_turn_bonus''': The amount of additional time a player gets each turn&lt;br /&gt;
*** '''countdown_reservoir_time''': The granted number of seconds each turn to complete the turn bonus.&lt;br /&gt;
*** '''countdown_action_bonus''': Thegranted number of seconds for each unit who have done an action this turn.&lt;br /&gt;
*** '''random_start_time''': Whether to start at a random time of day.&lt;br /&gt;
*** '''shuffle_sides''': Whether to shuffle the sides' starting positions.&lt;br /&gt;
*** '''[options]'''&lt;br /&gt;
**** '''[multiplayer]|[era]|[modification]|[campaign]'''&lt;br /&gt;
***** '''id''': The id of the content where the option comes from.&lt;br /&gt;
***** '''[option]'''&lt;br /&gt;
****** '''id''': The name of the variable to store the option's value.&lt;br /&gt;
****** '''value''': The option's value.&lt;br /&gt;
&lt;br /&gt;
== Administrative commands ==&lt;br /&gt;
* '''[query]'''&lt;br /&gt;
** '''type''': The type of query. See [[ServerAdministration]] for details.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
[https://github.com/renom/fastbot fastbot] -  the bot for tournaments which implements the protocol, can log in into the lobby and host games. Written in Go. &lt;br /&gt;
[[Category:WML Reference]]&lt;br /&gt;
[[Category:Server Documentation]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=MultiplayerServerWML&amp;diff=75634</id>
		<title>MultiplayerServerWML</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=MultiplayerServerWML&amp;diff=75634"/>
		<updated>2026-08-29T15:45:16Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* Queues */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the [[WML]] used to communicate with the multiplayer server for Wesnoth, [[wesnothd]].&lt;br /&gt;
&lt;br /&gt;
== The handshake ==&lt;br /&gt;
&lt;br /&gt;
The client sends four bytes, then the server replies with four bytes. To get a new connection number, the client will send these four bytes: 0x00 0x00 0x00 0x00. The server then sends back the connection number (wesnothd calls this number the &amp;quot;socket number&amp;quot;). Since 1.13+ the server no longer is using socket numbers to keep track of clients and always sends the same number to them all. Since 1.15+ client can also send 0x00 0x00 0x00 0x01 instead to request entire connection to be [https://github.com/wesnoth/wesnoth/blob/2f8136951cd77526188cf8d0fb2cf21eaa2ebe63/src/server/common/server_base.hpp#L60-L76 encapsulated in TLS] immediately '''after'''. If the handshake is successful, the server will be the first to send a data package. All packages are in [http://en.wikipedia.org/wiki/Gzip gzip] format and are preceded by four bytes that specify the size of the package to come in '''big-endian''' (network byte order). Below you'll find information about what data the (unzipped) packages contain. Unpacked WML uses utf-8 charset.&lt;br /&gt;
&lt;br /&gt;
== The login procedure ==&lt;br /&gt;
&lt;br /&gt;
* server request (optional)&lt;br /&gt;
** '''[version]'''&lt;br /&gt;
&lt;br /&gt;
* client response&lt;br /&gt;
** '''[version]'''&lt;br /&gt;
*** '''version''': The client's version string.&lt;br /&gt;
*** '''client_source''': The client's distribution info. (Steam, SourceForge, App Store, etc.)&lt;br /&gt;
&lt;br /&gt;
* server response (if the server does not accept this version)&lt;br /&gt;
** '''[redirect]'''&lt;br /&gt;
*** '''host''': The host you should connect to.&lt;br /&gt;
*** '''port''': The port you should connect to.&lt;br /&gt;
*** '''version''': A comma-separated list of globs that this server should accept (e.g. &amp;quot;1.0*,1.2*,1.4*,1.7*,1.8*&amp;quot;)&lt;br /&gt;
** or '''[reject]''' (if the version is unknown)&lt;br /&gt;
*** '''accepted_versions''': A comma-separated list of globs that this server does accept&lt;br /&gt;
&lt;br /&gt;
* server request&lt;br /&gt;
** '''[mustlogin]'''&lt;br /&gt;
&lt;br /&gt;
* client response&lt;br /&gt;
** '''[login]'''&lt;br /&gt;
*** '''username''': The username the client would like to have.&lt;br /&gt;
*** '''password''': The hashed password, created from the password and salt received from the server. More information about how this password is being generated, including a real world example, can be found in the file [http://forum.wesnoth.org/download/file.php?id=41145 HashedPasswords.pdf] (885 KiB). Since version 1.15+ if TLS was successfully established before then password will be passed as is, without hashing, relying on TLS for secrecy. Passing password hashes is no longer supported to free the client from responsibility to support all hash schemes the forum can potentially use. Client will emit error instead of trying to send password if TLS wasn't established.&lt;br /&gt;
&lt;br /&gt;
* server response&lt;br /&gt;
** '''[join_lobby]'''&lt;br /&gt;
*** '''is_moderator''': &amp;quot;yes&amp;quot; if the user is a moderator, &amp;quot;no&amp;quot; otherwise.&lt;br /&gt;
*** '''profile_url_prefix''': The external URL prefix for player profiles (empty if the server doesn't have an attached database)&lt;br /&gt;
** or '''[error]''' (server is waiting for another '''[login]''' message now)&lt;br /&gt;
*** '''message''': The error message.&lt;br /&gt;
*** '''password_request''': If not empty the server asks the client to provide a password for its desired username.&lt;br /&gt;
*** '''phpbb_encryption''': If &amp;quot;yes&amp;quot; the client will encrypt the password using phpbb's algorithm.&lt;br /&gt;
*** '''random_salt''': Random salt sent to the client for mixing with the password hash.&lt;br /&gt;
*** '''hash_seed''': Salt generated from the original hash that is required to recreate it.&lt;br /&gt;
*** '''salt''': Salt generated from the original hash that is required to recreate it.&lt;br /&gt;
*** '''force_confirmation''': Display an ok/cancel dialog with the content of the 'message' key.&lt;br /&gt;
&lt;br /&gt;
* server response&lt;br /&gt;
** '''[gamelist]'''&lt;br /&gt;
*** '''[game]''' (repeated)&lt;br /&gt;
**** '''id''': A unique id of the game.&lt;br /&gt;
**** '''name''': The title of the game.&lt;br /&gt;
**** '''mp_scenario''': The id of the scenario.&lt;br /&gt;
**** '''mp_era''': The id of the used era.&lt;br /&gt;
**** '''mp_use_map_settings''': Does the game use the map settings specified in the scenario.&lt;br /&gt;
**** '''mp_fog''': Does the game use fog.&lt;br /&gt;
**** '''mp_shroud''': Does the game use shroud.&lt;br /&gt;
**** '''mp_village_gold''': The number of gold per village.&lt;br /&gt;
**** '''experience_modifier''': The experience setting.&lt;br /&gt;
**** '''mp_countdown''': Does the game use a timer.&lt;br /&gt;
**** '''mp_countdown_reservoir_time''': Upper limit of the possibly available time.&lt;br /&gt;
**** '''mp_countdown_init_time''': Initial time.&lt;br /&gt;
**** '''mp_countdown_action_bonus''': Time bonus per action.&lt;br /&gt;
**** '''mp_countdown_turn_bonus''': Time bonus per turn.&lt;br /&gt;
**** '''map_data''': The map data. ''Notice: not sent to lobby if the game uses shroud''&lt;br /&gt;
**** '''hash''': The hash value of the map_data.&lt;br /&gt;
**** '''observer''': Are observers allowed or not.&lt;br /&gt;
**** '''human_sides''': The number of sides played by humans.&lt;br /&gt;
**** '''slots''': The number of vacant/max slots.&lt;br /&gt;
**** '''[slot_data]''' replaces '''slots''' since {{DevFeature1.13|12}}&lt;br /&gt;
***** '''max''': The number of total slots.&lt;br /&gt;
***** '''vacant''': The number of vacant slots.&lt;br /&gt;
**** '''turn''': The current turn/max turn.&lt;br /&gt;
**** '''[turn_data]''' replaces '''turn''' since {{DevFeature1.13|12}}&lt;br /&gt;
***** '''current''': The current turn number.&lt;br /&gt;
***** '''max''': The total number of turns.&lt;br /&gt;
**** '''[modification]''' Modifications used in this game. See [[ModificationWML]].&lt;br /&gt;
***** '''id''': ID of the modification.&lt;br /&gt;
***** '''name''': Name of the modification.&lt;br /&gt;
***** '''addon_id''': ID of the addon the modification is from.&lt;br /&gt;
***** '''require_modification''': A boolean value; if set to yes, all players have to have this modification installed to join the game.&lt;br /&gt;
**** '''[options]''' Options selected for this game. See [[OptionWML]].&lt;br /&gt;
***** '''[campaign|era|modification|multiplayer]'''&lt;br /&gt;
****** '''id''': ID of the addon the campaign|era|modification|multiplayer (scenario) is from.&lt;br /&gt;
****** '''[option]'''&lt;br /&gt;
******* '''id''': ID of the option.&lt;br /&gt;
******* '''value''': Value of the option.&lt;br /&gt;
** '''[user]''' (repeated)&lt;br /&gt;
*** '''name''': The username of the player.&lt;br /&gt;
*** '''registered''': Whether the player is registered (on the forum).&lt;br /&gt;
*** '''moderator''': Whether the player is a moderator.&lt;br /&gt;
*** '''forum_id''': The forum ID of the player. With '''profile_url_prefix''' this can be used to construct the profile page URL of a player.&lt;br /&gt;
*** '''game_id''': The ID of the game the player is in.&lt;br /&gt;
*** '''location''': The name of the game the player is in.&lt;br /&gt;
*** '''available''': &amp;quot;yes&amp;quot; if the player is in the lobby; &amp;quot;no&amp;quot; if in a game.&lt;br /&gt;
Many of the keys under [game] are described more indepth on the [[ScenarioWML]] page.&lt;br /&gt;
&lt;br /&gt;
== Error messages ==&lt;br /&gt;
&lt;br /&gt;
* '''[error]'''&lt;br /&gt;
** '''message''': The error message.&lt;br /&gt;
** '''password_request''': This is a response to a login attempt. The client needs to send a password on another login attempt.&lt;br /&gt;
** '''force_confirmation''': Confirmation to login even if there is an existing client with the same name. If login is continued then that existing client is getting kicked.&lt;br /&gt;
&lt;br /&gt;
== Chat (lobby and in-game) ==&lt;br /&gt;
&lt;br /&gt;
* '''[message]'''&lt;br /&gt;
** '''sender''': (optional - filled by the server) The sender of the message.&lt;br /&gt;
** '''message''': The message itself.&lt;br /&gt;
** '''room''': The room the message is from/to&lt;br /&gt;
* '''[whisper]'''&lt;br /&gt;
** '''receiver''': The receiver of the whisper&lt;br /&gt;
** '''sender''': (optional - filled by the server) The sender of the whisper.&lt;br /&gt;
** '''message''': The message itself.&lt;br /&gt;
&lt;br /&gt;
== Nickname registration related commands (lobby and in-game) ==&lt;br /&gt;
&lt;br /&gt;
* '''[nickserv]'''&lt;br /&gt;
** '''[info]''': Request info about another username.&lt;br /&gt;
*** '''name''': The username.&lt;br /&gt;
&lt;br /&gt;
== Updating the lobby state ==&lt;br /&gt;
&lt;br /&gt;
* '''[gamelist_diff]''': server message - basically a [[DiffWML|diff]] from two gamelists, which also includes the user list.&lt;br /&gt;
&lt;br /&gt;
* '''[observer]''' or '''[observer_quit]''': server message - players joining([observer_quit] - quitting the lobby &amp;quot;game&amp;quot;)/quitting([observer] - joining the lobby &amp;quot;game&amp;quot;) a game&lt;br /&gt;
** '''name''': Username of the player/observer.&lt;br /&gt;
* '''[refresh_lobby]''': Request the full gamelist.&lt;br /&gt;
&lt;br /&gt;
== Game setup (the phase from creation to start) ==&lt;br /&gt;
To create a game the client sends:&lt;br /&gt;
* '''[create_game]'''&lt;br /&gt;
** '''name''': The title of the game.&lt;br /&gt;
** '''password''': The password to use to join the game.&lt;br /&gt;
** '''ignored''': The list of ignored players from the host.&lt;br /&gt;
** '''auto_hosted''': True if this request is from a bot or a server-side queue, false otherwise.&lt;br /&gt;
** '''queue_type''': Either &amp;quot;normal&amp;quot; or &amp;quot;server_preset&amp;quot;.&lt;br /&gt;
** '''queue_id''': The ID of the queue this game is being created from.&lt;br /&gt;
&lt;br /&gt;
followed by a message with the scenario options as under [game] (see above) plus the scenario data ([time], [era], [side], etc. see [[ScenarioWML]])&lt;br /&gt;
&lt;br /&gt;
* '''[join]'''&lt;br /&gt;
** '''id''': The id of the game.&lt;br /&gt;
** '''observe''': Join the game as an observer.&lt;br /&gt;
&lt;br /&gt;
* '''[scenario_diff]''': [[DiffWML|diff]] of the [[ScenarioWML]] (side changes, etc.)&lt;br /&gt;
&lt;br /&gt;
* '''[start_game]''': sent by the host to start a game&lt;br /&gt;
* '''[leave_game]''': sent by the client when it leaves a game; sent by the server to make a client leave a game&lt;br /&gt;
** '''reason''': optional reason if sent by the server and was initiated by moderator action&lt;br /&gt;
&lt;br /&gt;
== In-game communication ==&lt;br /&gt;
&lt;br /&gt;
Normal scenario communication ([[ReplayWML]]):&lt;br /&gt;
* '''[turn]'''&lt;br /&gt;
** '''[command]''': (repeated) can contain all the tags you can find in a [[ReplayWML|replay]]: [recruit], [move], [end_turn], etc.&lt;br /&gt;
*** '''[speak]'''&lt;br /&gt;
**** '''message''': text of the message&lt;br /&gt;
**** '''id''': the sender&lt;br /&gt;
**** '''team_name''': the name of the team the message is for - empty if it's a public message&lt;br /&gt;
&lt;br /&gt;
Multiplayer specific communication:&lt;br /&gt;
* '''[request_choice]'''&lt;br /&gt;
** '''request_id''': unique ID of the choice request&lt;br /&gt;
** '''[random_seed]''': client requests a random number (used for attacks for example)&lt;br /&gt;
** '''[change_controller_wml]''': change controller request from scenario WML&lt;br /&gt;
*** '''side''': side number&lt;br /&gt;
*** '''old_controller''': old [[SideWML#controller|controller]] value&lt;br /&gt;
*** '''new_controller''': new [[SideWML#controller|controller]] value&lt;br /&gt;
* '''[store_next_scenario]''': sent by the host - the scenario data (see [[ScenarioWML]]) to advance to the next scenario&lt;br /&gt;
* '''[notify_next_scenario]''': sent by the server to tell players that the data for the next scenario is available&lt;br /&gt;
* '''[load_next_scenario]''': sent by the client to request the data for the next scenario&lt;br /&gt;
* '''[next_scenario]''': data for the next scenario (see [[ScenarioWML]]), sent by the server on request&lt;br /&gt;
&lt;br /&gt;
* '''[info]''': sent by the host on game end - info about the game state&lt;br /&gt;
** '''type''': &amp;quot;termination&amp;quot; &lt;br /&gt;
** '''condition''': the termination reason&lt;br /&gt;
&lt;br /&gt;
If a player leaves this is sent to the host for all sides he owned.&lt;br /&gt;
* '''side_drop''': The number of a side that dropped because a player left.&lt;br /&gt;
* '''controller''': The controller of that side. (&amp;quot;ai&amp;quot;, &amp;quot;network&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
Client commands:&lt;br /&gt;
* '''[change_controller]''': a player (un)droids one of his sides or assigns control to someone else (The host can assign control for any side.)&lt;br /&gt;
** '''side''': the side to change controller&lt;br /&gt;
** '''player''': the nickname of the player to take control&lt;br /&gt;
** '''controller''': the new controller: &amp;quot;human&amp;quot; or &amp;quot;human_ai&amp;quot;&lt;br /&gt;
** '''own_side''': &amp;quot;yes&amp;quot;&lt;br /&gt;
* '''[muteall]''': the host mutes/unmutes all observers - toggles&lt;br /&gt;
* '''[mute]''': the host mutes an observer - toggles&lt;br /&gt;
** '''username''': the username of the observer - if not specified the servers returns a list of muted usernames&lt;br /&gt;
* '''[kick]''' or '''[ban]''': the host kicks/bans a player/observer&lt;br /&gt;
** '''username''': the username of the player/observer&lt;br /&gt;
&lt;br /&gt;
== Game history ==&lt;br /&gt;
This is a request to query a set of 11 rows of game history data based on the provided search criteria. The official client calls this from the Match History button in the  multiplayer lobby to display 10 rows of data. The 11th row is used as a flag to indicate whether there is more data to be queried or not via the right/left arrows on the dialog.&lt;br /&gt;
&lt;br /&gt;
* '''[game_history_request]'''&lt;br /&gt;
** '''offset''': where in the result set to start returning data from. If there are 50 results and offset 10 is given, then rows 10-21 will be returned.&lt;br /&gt;
** '''search_player''': the forum username of the player to search for.&lt;br /&gt;
** '''search_game_name''': the name of the game to filter results by. Can use the * (matches any character before or after it's used) and _ (matches any single character) wildcards.&lt;br /&gt;
** '''search_content_type''': the type of content to filter by. Must be one of:&lt;br /&gt;
*** '''0''': scenario&lt;br /&gt;
*** '''1''': era&lt;br /&gt;
*** '''2''':modification&lt;br /&gt;
** '''search_content''': The content to filter by. This is the ID of the content, not the name displayed on the UI, due to the translated name getting stored in the database.&lt;br /&gt;
&lt;br /&gt;
== Queues ==&lt;br /&gt;
Queue info sent to the client on join or when the server's config is reloaded and the queue information has changed:&lt;br /&gt;
* '''[queue_update]'''&lt;br /&gt;
** '''queue_id''': The server's unique ID for the queue.&lt;br /&gt;
** '''action''': One of add/update/remove.&lt;br /&gt;
** '''display_name''': The text to show in the list of queues in the lobby. Only used by add/update.&lt;br /&gt;
** '''players_required''': How many players are required before a game is started. Only used by add/update.&lt;br /&gt;
&lt;br /&gt;
When there are enough players to start a game, the last player to join the queue is chosen as the host and their client is told to create the game with the provided settings. This skips the game creation screen and goes straight to the staging screen. The other players in the queue are then told to join that game using the normal [join] command:&lt;br /&gt;
* '''[create_game]'''&lt;br /&gt;
** '''queue_id''': The ID of the queue to create the game for.&lt;br /&gt;
** '''[game]'''&lt;br /&gt;
*** '''scenario''': The ID of the scenario to create the game for.&lt;br /&gt;
*** '''era''': The ID of the era to use.&lt;br /&gt;
*** '''fog''': Whether to have fog enabled.&lt;br /&gt;
*** '''shroud''': Whether to have shroud enabled.&lt;br /&gt;
*** '''village_gold''': How much gold each village provides.&lt;br /&gt;
*** '''village_support''': How much unit support each village provides&lt;br /&gt;
*** '''experience_modifier''': The experience modifier to use.&lt;br /&gt;
*** '''countdown''': Whether turn timers are enabled.&lt;br /&gt;
*** '''countdown_init_time''': The initial timer value in seconds.&lt;br /&gt;
*** '''countdown_turn_bonus''': The amount of additional time a player gets each turn&lt;br /&gt;
*** '''countdown_reservoir_time''': The granted number of seconds each turn to complete the turn bonus.&lt;br /&gt;
*** '''countdown_action_bonus''': Thegranted number of seconds for each unit who have done an action this turn.&lt;br /&gt;
*** '''random_start_time''': Whether to start at a random time of day.&lt;br /&gt;
*** '''shuffle_sides''': Whether to shuffle the sides' starting positions.&lt;br /&gt;
*** '''[options]'''&lt;br /&gt;
**** '''[multiplayer]'''&lt;br /&gt;
***** '''id''': The id of the content where the option comes from.&lt;br /&gt;
***** '''[option]'''&lt;br /&gt;
****** '''id''': The name of the variable to store the option's value.&lt;br /&gt;
****** '''value''': The option's value.&lt;br /&gt;
&lt;br /&gt;
== Administrative commands ==&lt;br /&gt;
* '''[query]'''&lt;br /&gt;
** '''type''': The type of query. See [[ServerAdministration]] for details.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
[https://github.com/renom/fastbot fastbot] -  the bot for tournaments which implements the protocol, can log in into the lobby and host games. Written in Go. &lt;br /&gt;
[[Category:WML Reference]]&lt;br /&gt;
[[Category:Server Documentation]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=MultiplayerServerWML&amp;diff=75633</id>
		<title>MultiplayerServerWML</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=MultiplayerServerWML&amp;diff=75633"/>
		<updated>2026-08-29T15:45:04Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* Queues */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the [[WML]] used to communicate with the multiplayer server for Wesnoth, [[wesnothd]].&lt;br /&gt;
&lt;br /&gt;
== The handshake ==&lt;br /&gt;
&lt;br /&gt;
The client sends four bytes, then the server replies with four bytes. To get a new connection number, the client will send these four bytes: 0x00 0x00 0x00 0x00. The server then sends back the connection number (wesnothd calls this number the &amp;quot;socket number&amp;quot;). Since 1.13+ the server no longer is using socket numbers to keep track of clients and always sends the same number to them all. Since 1.15+ client can also send 0x00 0x00 0x00 0x01 instead to request entire connection to be [https://github.com/wesnoth/wesnoth/blob/2f8136951cd77526188cf8d0fb2cf21eaa2ebe63/src/server/common/server_base.hpp#L60-L76 encapsulated in TLS] immediately '''after'''. If the handshake is successful, the server will be the first to send a data package. All packages are in [http://en.wikipedia.org/wiki/Gzip gzip] format and are preceded by four bytes that specify the size of the package to come in '''big-endian''' (network byte order). Below you'll find information about what data the (unzipped) packages contain. Unpacked WML uses utf-8 charset.&lt;br /&gt;
&lt;br /&gt;
== The login procedure ==&lt;br /&gt;
&lt;br /&gt;
* server request (optional)&lt;br /&gt;
** '''[version]'''&lt;br /&gt;
&lt;br /&gt;
* client response&lt;br /&gt;
** '''[version]'''&lt;br /&gt;
*** '''version''': The client's version string.&lt;br /&gt;
*** '''client_source''': The client's distribution info. (Steam, SourceForge, App Store, etc.)&lt;br /&gt;
&lt;br /&gt;
* server response (if the server does not accept this version)&lt;br /&gt;
** '''[redirect]'''&lt;br /&gt;
*** '''host''': The host you should connect to.&lt;br /&gt;
*** '''port''': The port you should connect to.&lt;br /&gt;
*** '''version''': A comma-separated list of globs that this server should accept (e.g. &amp;quot;1.0*,1.2*,1.4*,1.7*,1.8*&amp;quot;)&lt;br /&gt;
** or '''[reject]''' (if the version is unknown)&lt;br /&gt;
*** '''accepted_versions''': A comma-separated list of globs that this server does accept&lt;br /&gt;
&lt;br /&gt;
* server request&lt;br /&gt;
** '''[mustlogin]'''&lt;br /&gt;
&lt;br /&gt;
* client response&lt;br /&gt;
** '''[login]'''&lt;br /&gt;
*** '''username''': The username the client would like to have.&lt;br /&gt;
*** '''password''': The hashed password, created from the password and salt received from the server. More information about how this password is being generated, including a real world example, can be found in the file [http://forum.wesnoth.org/download/file.php?id=41145 HashedPasswords.pdf] (885 KiB). Since version 1.15+ if TLS was successfully established before then password will be passed as is, without hashing, relying on TLS for secrecy. Passing password hashes is no longer supported to free the client from responsibility to support all hash schemes the forum can potentially use. Client will emit error instead of trying to send password if TLS wasn't established.&lt;br /&gt;
&lt;br /&gt;
* server response&lt;br /&gt;
** '''[join_lobby]'''&lt;br /&gt;
*** '''is_moderator''': &amp;quot;yes&amp;quot; if the user is a moderator, &amp;quot;no&amp;quot; otherwise.&lt;br /&gt;
*** '''profile_url_prefix''': The external URL prefix for player profiles (empty if the server doesn't have an attached database)&lt;br /&gt;
** or '''[error]''' (server is waiting for another '''[login]''' message now)&lt;br /&gt;
*** '''message''': The error message.&lt;br /&gt;
*** '''password_request''': If not empty the server asks the client to provide a password for its desired username.&lt;br /&gt;
*** '''phpbb_encryption''': If &amp;quot;yes&amp;quot; the client will encrypt the password using phpbb's algorithm.&lt;br /&gt;
*** '''random_salt''': Random salt sent to the client for mixing with the password hash.&lt;br /&gt;
*** '''hash_seed''': Salt generated from the original hash that is required to recreate it.&lt;br /&gt;
*** '''salt''': Salt generated from the original hash that is required to recreate it.&lt;br /&gt;
*** '''force_confirmation''': Display an ok/cancel dialog with the content of the 'message' key.&lt;br /&gt;
&lt;br /&gt;
* server response&lt;br /&gt;
** '''[gamelist]'''&lt;br /&gt;
*** '''[game]''' (repeated)&lt;br /&gt;
**** '''id''': A unique id of the game.&lt;br /&gt;
**** '''name''': The title of the game.&lt;br /&gt;
**** '''mp_scenario''': The id of the scenario.&lt;br /&gt;
**** '''mp_era''': The id of the used era.&lt;br /&gt;
**** '''mp_use_map_settings''': Does the game use the map settings specified in the scenario.&lt;br /&gt;
**** '''mp_fog''': Does the game use fog.&lt;br /&gt;
**** '''mp_shroud''': Does the game use shroud.&lt;br /&gt;
**** '''mp_village_gold''': The number of gold per village.&lt;br /&gt;
**** '''experience_modifier''': The experience setting.&lt;br /&gt;
**** '''mp_countdown''': Does the game use a timer.&lt;br /&gt;
**** '''mp_countdown_reservoir_time''': Upper limit of the possibly available time.&lt;br /&gt;
**** '''mp_countdown_init_time''': Initial time.&lt;br /&gt;
**** '''mp_countdown_action_bonus''': Time bonus per action.&lt;br /&gt;
**** '''mp_countdown_turn_bonus''': Time bonus per turn.&lt;br /&gt;
**** '''map_data''': The map data. ''Notice: not sent to lobby if the game uses shroud''&lt;br /&gt;
**** '''hash''': The hash value of the map_data.&lt;br /&gt;
**** '''observer''': Are observers allowed or not.&lt;br /&gt;
**** '''human_sides''': The number of sides played by humans.&lt;br /&gt;
**** '''slots''': The number of vacant/max slots.&lt;br /&gt;
**** '''[slot_data]''' replaces '''slots''' since {{DevFeature1.13|12}}&lt;br /&gt;
***** '''max''': The number of total slots.&lt;br /&gt;
***** '''vacant''': The number of vacant slots.&lt;br /&gt;
**** '''turn''': The current turn/max turn.&lt;br /&gt;
**** '''[turn_data]''' replaces '''turn''' since {{DevFeature1.13|12}}&lt;br /&gt;
***** '''current''': The current turn number.&lt;br /&gt;
***** '''max''': The total number of turns.&lt;br /&gt;
**** '''[modification]''' Modifications used in this game. See [[ModificationWML]].&lt;br /&gt;
***** '''id''': ID of the modification.&lt;br /&gt;
***** '''name''': Name of the modification.&lt;br /&gt;
***** '''addon_id''': ID of the addon the modification is from.&lt;br /&gt;
***** '''require_modification''': A boolean value; if set to yes, all players have to have this modification installed to join the game.&lt;br /&gt;
**** '''[options]''' Options selected for this game. See [[OptionWML]].&lt;br /&gt;
***** '''[campaign|era|modification|multiplayer]'''&lt;br /&gt;
****** '''id''': ID of the addon the campaign|era|modification|multiplayer (scenario) is from.&lt;br /&gt;
****** '''[option]'''&lt;br /&gt;
******* '''id''': ID of the option.&lt;br /&gt;
******* '''value''': Value of the option.&lt;br /&gt;
** '''[user]''' (repeated)&lt;br /&gt;
*** '''name''': The username of the player.&lt;br /&gt;
*** '''registered''': Whether the player is registered (on the forum).&lt;br /&gt;
*** '''moderator''': Whether the player is a moderator.&lt;br /&gt;
*** '''forum_id''': The forum ID of the player. With '''profile_url_prefix''' this can be used to construct the profile page URL of a player.&lt;br /&gt;
*** '''game_id''': The ID of the game the player is in.&lt;br /&gt;
*** '''location''': The name of the game the player is in.&lt;br /&gt;
*** '''available''': &amp;quot;yes&amp;quot; if the player is in the lobby; &amp;quot;no&amp;quot; if in a game.&lt;br /&gt;
Many of the keys under [game] are described more indepth on the [[ScenarioWML]] page.&lt;br /&gt;
&lt;br /&gt;
== Error messages ==&lt;br /&gt;
&lt;br /&gt;
* '''[error]'''&lt;br /&gt;
** '''message''': The error message.&lt;br /&gt;
** '''password_request''': This is a response to a login attempt. The client needs to send a password on another login attempt.&lt;br /&gt;
** '''force_confirmation''': Confirmation to login even if there is an existing client with the same name. If login is continued then that existing client is getting kicked.&lt;br /&gt;
&lt;br /&gt;
== Chat (lobby and in-game) ==&lt;br /&gt;
&lt;br /&gt;
* '''[message]'''&lt;br /&gt;
** '''sender''': (optional - filled by the server) The sender of the message.&lt;br /&gt;
** '''message''': The message itself.&lt;br /&gt;
** '''room''': The room the message is from/to&lt;br /&gt;
* '''[whisper]'''&lt;br /&gt;
** '''receiver''': The receiver of the whisper&lt;br /&gt;
** '''sender''': (optional - filled by the server) The sender of the whisper.&lt;br /&gt;
** '''message''': The message itself.&lt;br /&gt;
&lt;br /&gt;
== Nickname registration related commands (lobby and in-game) ==&lt;br /&gt;
&lt;br /&gt;
* '''[nickserv]'''&lt;br /&gt;
** '''[info]''': Request info about another username.&lt;br /&gt;
*** '''name''': The username.&lt;br /&gt;
&lt;br /&gt;
== Updating the lobby state ==&lt;br /&gt;
&lt;br /&gt;
* '''[gamelist_diff]''': server message - basically a [[DiffWML|diff]] from two gamelists, which also includes the user list.&lt;br /&gt;
&lt;br /&gt;
* '''[observer]''' or '''[observer_quit]''': server message - players joining([observer_quit] - quitting the lobby &amp;quot;game&amp;quot;)/quitting([observer] - joining the lobby &amp;quot;game&amp;quot;) a game&lt;br /&gt;
** '''name''': Username of the player/observer.&lt;br /&gt;
* '''[refresh_lobby]''': Request the full gamelist.&lt;br /&gt;
&lt;br /&gt;
== Game setup (the phase from creation to start) ==&lt;br /&gt;
To create a game the client sends:&lt;br /&gt;
* '''[create_game]'''&lt;br /&gt;
** '''name''': The title of the game.&lt;br /&gt;
** '''password''': The password to use to join the game.&lt;br /&gt;
** '''ignored''': The list of ignored players from the host.&lt;br /&gt;
** '''auto_hosted''': True if this request is from a bot or a server-side queue, false otherwise.&lt;br /&gt;
** '''queue_type''': Either &amp;quot;normal&amp;quot; or &amp;quot;server_preset&amp;quot;.&lt;br /&gt;
** '''queue_id''': The ID of the queue this game is being created from.&lt;br /&gt;
&lt;br /&gt;
followed by a message with the scenario options as under [game] (see above) plus the scenario data ([time], [era], [side], etc. see [[ScenarioWML]])&lt;br /&gt;
&lt;br /&gt;
* '''[join]'''&lt;br /&gt;
** '''id''': The id of the game.&lt;br /&gt;
** '''observe''': Join the game as an observer.&lt;br /&gt;
&lt;br /&gt;
* '''[scenario_diff]''': [[DiffWML|diff]] of the [[ScenarioWML]] (side changes, etc.)&lt;br /&gt;
&lt;br /&gt;
* '''[start_game]''': sent by the host to start a game&lt;br /&gt;
* '''[leave_game]''': sent by the client when it leaves a game; sent by the server to make a client leave a game&lt;br /&gt;
** '''reason''': optional reason if sent by the server and was initiated by moderator action&lt;br /&gt;
&lt;br /&gt;
== In-game communication ==&lt;br /&gt;
&lt;br /&gt;
Normal scenario communication ([[ReplayWML]]):&lt;br /&gt;
* '''[turn]'''&lt;br /&gt;
** '''[command]''': (repeated) can contain all the tags you can find in a [[ReplayWML|replay]]: [recruit], [move], [end_turn], etc.&lt;br /&gt;
*** '''[speak]'''&lt;br /&gt;
**** '''message''': text of the message&lt;br /&gt;
**** '''id''': the sender&lt;br /&gt;
**** '''team_name''': the name of the team the message is for - empty if it's a public message&lt;br /&gt;
&lt;br /&gt;
Multiplayer specific communication:&lt;br /&gt;
* '''[request_choice]'''&lt;br /&gt;
** '''request_id''': unique ID of the choice request&lt;br /&gt;
** '''[random_seed]''': client requests a random number (used for attacks for example)&lt;br /&gt;
** '''[change_controller_wml]''': change controller request from scenario WML&lt;br /&gt;
*** '''side''': side number&lt;br /&gt;
*** '''old_controller''': old [[SideWML#controller|controller]] value&lt;br /&gt;
*** '''new_controller''': new [[SideWML#controller|controller]] value&lt;br /&gt;
* '''[store_next_scenario]''': sent by the host - the scenario data (see [[ScenarioWML]]) to advance to the next scenario&lt;br /&gt;
* '''[notify_next_scenario]''': sent by the server to tell players that the data for the next scenario is available&lt;br /&gt;
* '''[load_next_scenario]''': sent by the client to request the data for the next scenario&lt;br /&gt;
* '''[next_scenario]''': data for the next scenario (see [[ScenarioWML]]), sent by the server on request&lt;br /&gt;
&lt;br /&gt;
* '''[info]''': sent by the host on game end - info about the game state&lt;br /&gt;
** '''type''': &amp;quot;termination&amp;quot; &lt;br /&gt;
** '''condition''': the termination reason&lt;br /&gt;
&lt;br /&gt;
If a player leaves this is sent to the host for all sides he owned.&lt;br /&gt;
* '''side_drop''': The number of a side that dropped because a player left.&lt;br /&gt;
* '''controller''': The controller of that side. (&amp;quot;ai&amp;quot;, &amp;quot;network&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
Client commands:&lt;br /&gt;
* '''[change_controller]''': a player (un)droids one of his sides or assigns control to someone else (The host can assign control for any side.)&lt;br /&gt;
** '''side''': the side to change controller&lt;br /&gt;
** '''player''': the nickname of the player to take control&lt;br /&gt;
** '''controller''': the new controller: &amp;quot;human&amp;quot; or &amp;quot;human_ai&amp;quot;&lt;br /&gt;
** '''own_side''': &amp;quot;yes&amp;quot;&lt;br /&gt;
* '''[muteall]''': the host mutes/unmutes all observers - toggles&lt;br /&gt;
* '''[mute]''': the host mutes an observer - toggles&lt;br /&gt;
** '''username''': the username of the observer - if not specified the servers returns a list of muted usernames&lt;br /&gt;
* '''[kick]''' or '''[ban]''': the host kicks/bans a player/observer&lt;br /&gt;
** '''username''': the username of the player/observer&lt;br /&gt;
&lt;br /&gt;
== Game history ==&lt;br /&gt;
This is a request to query a set of 11 rows of game history data based on the provided search criteria. The official client calls this from the Match History button in the  multiplayer lobby to display 10 rows of data. The 11th row is used as a flag to indicate whether there is more data to be queried or not via the right/left arrows on the dialog.&lt;br /&gt;
&lt;br /&gt;
* '''[game_history_request]'''&lt;br /&gt;
** '''offset''': where in the result set to start returning data from. If there are 50 results and offset 10 is given, then rows 10-21 will be returned.&lt;br /&gt;
** '''search_player''': the forum username of the player to search for.&lt;br /&gt;
** '''search_game_name''': the name of the game to filter results by. Can use the * (matches any character before or after it's used) and _ (matches any single character) wildcards.&lt;br /&gt;
** '''search_content_type''': the type of content to filter by. Must be one of:&lt;br /&gt;
*** '''0''': scenario&lt;br /&gt;
*** '''1''': era&lt;br /&gt;
*** '''2''':modification&lt;br /&gt;
** '''search_content''': The content to filter by. This is the ID of the content, not the name displayed on the UI, due to the translated name getting stored in the database.&lt;br /&gt;
&lt;br /&gt;
== Queues ==&lt;br /&gt;
Queue info sent to the client on join or when the server's config is reloaded and the queue information has changed:&lt;br /&gt;
* '''[queue_update]'''&lt;br /&gt;
** '''queue_id''': The server's unique ID for the queue.&lt;br /&gt;
** '''action''': One of add/update/remove.&lt;br /&gt;
** '''display_name''': The text to show in the list of queues in the lobby. Only used by add/update.&lt;br /&gt;
** '''players_required''': How many players are required before a game is started. Only used by add/update.&lt;br /&gt;
&lt;br /&gt;
When there are enough players to start a game, the last player to join the queue is chosen as the host and their client is told to create the game with the provided settings. This skips the game creation screen and goes straight to the staging screen. The other players in the queue are then told to join that game using the normal [join] command:&lt;br /&gt;
* '''[create_game]'''&lt;br /&gt;
** '''queue_id''': The ID of the queue to create the game for.&lt;br /&gt;
** '''[game]'''&lt;br /&gt;
*** '''scenario''': The ID of the scenario to create the game for.&lt;br /&gt;
*** '''era''': The ID of the era to use.&lt;br /&gt;
*** '''fog''': Whether to have fog enabled.&lt;br /&gt;
*** '''shroud''': Whether to have shroud enabled.&lt;br /&gt;
*** '''village_gold''': How much gold each village provides.&lt;br /&gt;
*** '''village_support''': How much unit support each village provides&lt;br /&gt;
*** '''experience_modifier''': The experience modifier to use.&lt;br /&gt;
*** '''countdown''': Whether turn timers are enabled.&lt;br /&gt;
*** '''countdown_init_time''': The initial timer value in seconds.&lt;br /&gt;
*** '''countdown_turn_bonus''': The amount of additional time a player gets each turn&lt;br /&gt;
*** '''countdown_reservoir_time''': The granted number of seconds each turn to complete the turn bonus.&lt;br /&gt;
*** '''countdown_action_bonus''': Thegranted number of seconds for each unit who have done an action this turn.&lt;br /&gt;
*** '''random_start_time''': Whether to start at a random time of day.&lt;br /&gt;
*** '''shuffle_sides''': Whether to shuffle the sides' starting positions.&lt;br /&gt;
**** '''[options]'''&lt;br /&gt;
***** '''[multiplayer]'''&lt;br /&gt;
****** '''id''': The id of the content where the option comes from.&lt;br /&gt;
****** '''[option]'''&lt;br /&gt;
******* '''id''': The name of the variable to store the option's value.&lt;br /&gt;
******* '''value''': The option's value.&lt;br /&gt;
&lt;br /&gt;
== Administrative commands ==&lt;br /&gt;
* '''[query]'''&lt;br /&gt;
** '''type''': The type of query. See [[ServerAdministration]] for details.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
[https://github.com/renom/fastbot fastbot] -  the bot for tournaments which implements the protocol, can log in into the lobby and host games. Written in Go. &lt;br /&gt;
[[Category:WML Reference]]&lt;br /&gt;
[[Category:Server Documentation]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Release_Steps&amp;diff=75627</id>
		<title>Release Steps</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Release_Steps&amp;diff=75627"/>
		<updated>2026-08-20T22:30:49Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* Release (stable and master branches) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Pre-release (stable only) ==&lt;br /&gt;
* Start the string freeze two weeks before the release.&lt;br /&gt;
* Do the pot update:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
scons pot-update update-po4a manual&lt;br /&gt;
git add -A&lt;br /&gt;
git commit -am &amp;quot;pot-update and regenerate doc files&amp;quot;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* Email the translator's mailing list stating that the string freeze is starting.&lt;br /&gt;
&lt;br /&gt;
== Release (stable and master branches) ==&lt;br /&gt;
* Update [http://www.wesnoth.org/macro-reference.html macro-reference.html]:&lt;br /&gt;
&lt;br /&gt;
 cd data/tools/&lt;br /&gt;
 make macro-reference.html&lt;br /&gt;
* If stable&lt;br /&gt;
 scp macro-reference.html wesnoth@wesnoth.org:WWW/html/macro-reference.html&lt;br /&gt;
* If master&lt;br /&gt;
 scp macro-reference.html wesnoth@wesnoth.org:WWW/html/macro-reference-1.&amp;lt;version&amp;gt;.html&lt;br /&gt;
&lt;br /&gt;
* Regenerate the game credits and paste the contents to [[Credits]] on the wiki:&lt;br /&gt;
&lt;br /&gt;
 data/tools/about_cfg_to_wiki -w ./wesnoth &amp;gt; ./about.wiki&lt;br /&gt;
&lt;br /&gt;
* Check the '''changelog_entries''' folder for entries and update the changelog as necessary.&lt;br /&gt;
* Run the following command:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;/utils/update_appdata &amp;lt;version&amp;gt; packaging/org.wesnoth.Wesnoth.appdata.xml&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* Run the pot update (second time if this is a stable release).&lt;br /&gt;
* Do the pre-tag commit (check a previous commit as an example - this removes the &amp;quot;+dev&amp;quot; suffix everywhere).&lt;br /&gt;
* The the post-tag commit (check a previous commit as an example - this re-adds the &amp;quot;+dev&amp;quot; suffix everywhere).&lt;br /&gt;
* Tag the pre-tag commit with:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;git tag -a &amp;lt;version&amp;gt; -m &amp;quot;Wesnoth &amp;lt;version&amp;gt; (Alpha)&amp;quot; &amp;lt;commit hash&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
::- (Alpha)/(Beta) is only added for dev releases depending on where we are in the release cycle.&lt;br /&gt;
* Push the commits and the tag:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;git push&lt;br /&gt;
git push origin &amp;lt;version&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* Ping the packagers on Discord that the release has been tagged:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;@packagers &amp;lt;version&amp;gt; has been tagged.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* Check that the new version is allowed on its respective multiplayer server.&lt;br /&gt;
* Update the header in Discord's #development channel.&lt;br /&gt;
* Upload master.zip and the patch to SourceForge to the respective version's folder for use by Android&lt;br /&gt;
** master.zip contains the folders&lt;br /&gt;
*** data (data/core/music/ is removed)&lt;br /&gt;
*** fonts&lt;br /&gt;
*** images&lt;br /&gt;
*** sounds&lt;br /&gt;
*** translations&lt;br /&gt;
** For the patch, go to packaging/android/ and run &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;./create_patch.sh oldtag newtag&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then upload the resulting patch.zip and manifest.txt next to master.zip.&lt;br /&gt;
* Upload source code tarball to SourceForge and files.wesnoth.org (done by loonycyborg).&lt;br /&gt;
* Send an email to the packagers mailing list (done by loonycyborg).&lt;br /&gt;
* Upload the release to the various places Wesnoth is distributed (Steam, itch.io, SourceForge, macOS App Store, Flathub, F-Droid).&lt;br /&gt;
** Windows/Linux - handled by loonycyborg.&lt;br /&gt;
** macOS - handled by hrubymar.&lt;br /&gt;
** Android (F-Droid) - handled by LumiousE&lt;br /&gt;
* Post the forum announcements.&lt;br /&gt;
* Update the Downloads wiki page.&lt;br /&gt;
** url: https://wiki.wesnoth.org/Download&lt;br /&gt;
** dev template: https://wiki.wesnoth.org/Template:DevDownload#Development_.281.15_branch.29&lt;br /&gt;
** stable template: https://wiki.wesnoth.org/Template:StableDownload&lt;br /&gt;
* Add the News forum post.&lt;br /&gt;
* Post the Discord announcement.&lt;br /&gt;
** Make sure to publish it.&lt;br /&gt;
** Short link is: '''&amp;lt;nowiki&amp;gt;https://r.wesnoth.org/t#####&amp;lt;/nowiki&amp;gt;'''&lt;br /&gt;
* Post the itch.io devlog (copy of the Discord announcement).&lt;br /&gt;
* Update the wesnoth.org front page.&lt;br /&gt;
** See a previous version update commit to the '''wesmere''' repository.&lt;br /&gt;
** ssh into the website VM and execute the following shell commands (yes, it is specifically ''''make'''', not ''''make install''''!):&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;sudo -iu wesnoth&lt;br /&gt;
cd ~/git/wesmere/static&lt;br /&gt;
git pull&lt;br /&gt;
make&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* Generate the Steam announcement by running the following command in your local wesnoth repository root:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;python3 data/tools/steam-changelog changelog.md &amp;lt;version&amp;gt; &amp;gt; &amp;lt;version&amp;gt;.txt&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* Post the Steam announcement.&lt;br /&gt;
* Log into Wesnoth's Steam page.&lt;br /&gt;
** Click &amp;quot;A Game Update&amp;quot;.&lt;br /&gt;
** Pick &amp;quot;Small Update&amp;quot;.&lt;br /&gt;
** Paste the output of of the above python command into the event description.&lt;br /&gt;
** Click &amp;quot;Link To Build&amp;quot; and select the appropriate branch.&lt;br /&gt;
** Edit the appropriate .xcf file from the [https://github.com/wesnoth/resources/tree/master/social-media-assets Resources repository] ('''wesnoth_event_header.xcf''' for stable releases, '''wesnoth_beta_header.xcf''' for development releases) and export it to PNG.&lt;br /&gt;
*** This requires the '''oldania''' font to be installed, which on Linux/Ubuntu-derivatives can be installed via the command:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;apt install fonts-adf-oldania&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* Post the announcement to Fosstodon.&lt;br /&gt;
* Post the announcement to Reddit. (Elvish_Hunter)&lt;br /&gt;
** Add the &amp;quot;Development release&amp;quot;/&amp;quot;Stable release&amp;quot; flair.&lt;br /&gt;
** Don't paste the link in the post, directly post it as a link.&lt;br /&gt;
&lt;br /&gt;
== New Stable Series ==&lt;br /&gt;
=== Beta 1 ===&lt;br /&gt;
Write up the new Start page (ie: https://www.wesnoth.org/start/1.16/).&lt;br /&gt;
&lt;br /&gt;
=== Beta 2 ===&lt;br /&gt;
Make the new Start page available to Translators.&lt;br /&gt;
&lt;br /&gt;
=== RC1 ===&lt;br /&gt;
Before release, the new multiplayer server and add-ons server instances need to be setup. The server running the website status is owned by Iris - she needs to push updates to it.&lt;br /&gt;
* Multiplayer server&lt;br /&gt;
** Needs no changes since it goes to port 15000 and then that redirects based on version. The existing dev multiplayer server will be reused as the new stable instance.&lt;br /&gt;
** The Discord website status bot and the site status page (https://status.wesnoth.org/ - bin/valen.pl) need to be updated to rename the dev version to the stable version.&lt;br /&gt;
** A new stable multiplayer instance needs to be added to the alternate server.&lt;br /&gt;
&lt;br /&gt;
* Add-ons server - The port is the version number, for example 1.16 is 15016, 1.18 is 15018, etc.&lt;br /&gt;
** C++ update (currently default_campaignd_port in addon/validation.cpp)&lt;br /&gt;
** Python update (data/tools/wesnoth/campaignserver_client.py)&lt;br /&gt;
** Website update (bin/valen.pl) in the valen repository to add the new instance.&lt;br /&gt;
** The Discord website status bot and the site status page (https://status.wesnoth.org/ - bin/valen.pl) need to be updated to add the new version.&lt;br /&gt;
** Add a 1.18 cron job for ~wesnoth/bin/update_addons in the websites VM and remove the 1.17 cron job.&lt;br /&gt;
&lt;br /&gt;
* Flatpak&lt;br /&gt;
** &lt;br /&gt;
&lt;br /&gt;
* Add screenshots to https://wiki.wesnoth.org/Screenshots&lt;br /&gt;
&lt;br /&gt;
* Start page - soft link the folder from the repository to the folder that shows up on the actual website:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
cd /srv/www/html/start&lt;br /&gt;
ln -s /home/git/wesnoth/start/&amp;lt;version&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Update ci_main.yml to use the new branch instead of the master branch.&lt;br /&gt;
&lt;br /&gt;
[[Category:Release_Notes]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=75619</id>
		<title>Template:DevDownload</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=75619"/>
		<updated>2026-08-18T21:06:52Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Development (1.19 branch) ==&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
==== Windows (10 1903 and later) {{{4|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.27 | filename=wesnoth-1.19.27-win64.exe |&lt;br /&gt;
hash=392eeb6fe448e72cfd43bc20318956287ae9bf59c66bce60d47115de0bb63545}}&lt;br /&gt;
&lt;br /&gt;
==== macOS (10.13 and later) {{{5|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.27 | filename=Wesnoth_1.19.27.dmg |&lt;br /&gt;
hash=471feb944087f9985791f812de99d3792d3cc047eaacde8c92da764171b586de}}&lt;br /&gt;
&lt;br /&gt;
==== Source code ====&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/blob/master/INSTALL.md Compiling Wesnoth] - How to compile the source code&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.27 | filename=wesnoth-1.19.27.tar.bz2 |&lt;br /&gt;
hash=05c82d2ee379bf80ee028b0f6f8554315c53675a131a30305b4301c01ea8af98}}&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Template:StableDownload&amp;diff=75618</id>
		<title>Template:StableDownload</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Template:StableDownload&amp;diff=75618"/>
		<updated>2026-08-18T20:59:08Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Stable (1.18 branch) ==&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
==== Windows (10 1903 and later, 64-bit only) {{{4|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth-1.18 |&lt;br /&gt;
version=1.18.8 | filename=wesnoth-1.18.8-win64.exe |&lt;br /&gt;
hash=464a2edfce8a0fe08e9956a363785938970254ee385672388bb8ba3556d8a09c}}&lt;br /&gt;
&lt;br /&gt;
==== macOS (10.12 and later) {{{5|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth-1.18 |&lt;br /&gt;
version=1.18.8 | filename=Wesnoth_1.18.8.dmg |&lt;br /&gt;
hash=aba4db430e18ed9bad7e98e645c620766be0757fbf03eb48d8205eb73471ee15}}&lt;br /&gt;
&lt;br /&gt;
==== Source code ====&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/blob/master/INSTALL.md Compiling Wesnoth] - How to compile the source code&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth-1.18 |&lt;br /&gt;
version=1.18.8 | filename=wesnoth-1.18.8.tar.bz2 |&lt;br /&gt;
hash=fce3bd71d4ed32c9d11c0f707d19743cec6adf5c34761fab94ab72754aaedd89}}&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=75598</id>
		<title>Template:DevDownload</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=75598"/>
		<updated>2026-08-11T23:16:22Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Development (1.19 branch) ==&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
==== Windows (10 1903 and later) {{{4|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.26 | filename=wesnoth-1.19.26-win64.exe |&lt;br /&gt;
hash=b77289f7a8233ff2af8a4d3d651d6bfd047bb923557e27471f5f18f870e4fd94}}&lt;br /&gt;
&lt;br /&gt;
==== macOS (10.13 and later) {{{5|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.26 | filename=Wesnoth_1.19.26.dmg |&lt;br /&gt;
hash=8ced5829d75333acd16dcc0ae9a942f4c355609503e2692d2769797561d685b9}}&lt;br /&gt;
&lt;br /&gt;
==== Source code ====&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/blob/master/INSTALL.md Compiling Wesnoth] - How to compile the source code&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.26 | filename=wesnoth-1.19.26.tar.bz2 |&lt;br /&gt;
hash=5bde64ae099cea7e469359f01ae350e9ba6c57f559cc66975a0f7bf7a2e6b6c6}}&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Template:WML_Tags&amp;diff=75592</id>
		<title>Template:WML Tags</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Template:WML_Tags&amp;diff=75592"/>
		<updated>2026-08-09T17:17:41Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{| class=&amp;quot;reference-sidebar&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;span class=&amp;quot;editlink&amp;quot;&amp;gt;&amp;amp;#91;[{{SERVER}}{{localurl:Template:WML Tags|action=edit}} edit]&amp;amp;#93;&amp;lt;/span&amp;gt;'''[[ReferenceWML|WML Tags]]'''&lt;br /&gt;
|-&lt;br /&gt;
|''A:'' &lt;br /&gt;
[[AbilitiesWML#The .5Babilities.5D tag|abilities]],&lt;br /&gt;
[[CreditsWML#.5Babout.5D|about]],&lt;br /&gt;
[[AchievementsWML#.5Bachievement.5D|achievement]],&lt;br /&gt;
[[AchievementsWML#.5Bachievement_group.5D|achievement_group]],&lt;br /&gt;
[[Lua_AI_Legacy_Methods_Howto#Behavior_.28Sticky.29_Candidate_Actions|add_ai_behavior]],&lt;br /&gt;
[[AdvancedPreferenceWML|advanced_preference]],&lt;br /&gt;
[[UnitTypeWML#advancefrom|advancefrom]],&lt;br /&gt;
[[UnitTypeWML#After_max_level_advancement_.28AMLA.29|advancement]],&lt;br /&gt;
[[StatisticalScenarioWML#The_.5Bteam.5D_tag|advances]],&lt;br /&gt;
[[AbilitiesWML#affect_adjacent|affect_adjacent]],&lt;br /&gt;
[[Wesnoth_AI_Framework#The_.5Bai.5D_Tag_.E2.80.94_Top-level_Elements|ai]],&lt;br /&gt;
[[StandardSideFilter#allied_with|allied_with]], &lt;br /&gt;
[[DirectActionsWML#.5Ballow_end_turn.5D|allow_end_turn]],&lt;br /&gt;
[[DirectActionsWML#.5Ballow_extra_recruit.5D|allow_extra_recruit]],&lt;br /&gt;
[[DirectActionsWML#.5Ballow_recruit.5D|allow_recruit]],&lt;br /&gt;
[[DirectActionsWML#.5Ballow_undo.5D|allow_undo]],&lt;br /&gt;
[[ConditionalActionsWML#Meta-Condition_Tags|and]],&lt;br /&gt;
[[InterfaceActionsWML#.5Banimate_unit.5D|animate_unit]],&lt;br /&gt;
[[AnimationWML#The .5Banimation.5D tag|animation]],&lt;br /&gt;
[[Wesnoth_AI_Framework#The_.5Bai.5D_Tag_.E2.80.94_Aspects|aspect]],&lt;br /&gt;
attack ([[ReplayWML#attack|replay]], [[UnitTypeWML#Attacks|weapon]]),&lt;br /&gt;
[[AnimationWML#short-attack|attack_anim]],&lt;br /&gt;
attacks ([[AbilitiesWML#The_.5Bspecials.5D_tag|special]], [[StatisticalScenarioWML#The_.5Bteam.5D_tag|stats]]),&lt;br /&gt;
[[AiWML#avoid|avoid]];&lt;br /&gt;
|-&lt;br /&gt;
|''B:'' &lt;br /&gt;
[[UnitTypeWML#base_unit|base_unit]], &lt;br /&gt;
[[IntroWML#.5Bbackground_layer.5D|background_layer]],&lt;br /&gt;
[[AbilitiesWML#The_.5Bspecials.5D_tag|berserk]], &lt;br /&gt;
[[BinaryPathWML|binary_path]],&lt;br /&gt;
[[InternalActionsWML#Flow_control_actions|break]],&lt;br /&gt;
[[EditorWML#The_.5Bbrush.5D_tag|brush]];&lt;br /&gt;
|-&lt;br /&gt;
|''C:'' &lt;br /&gt;
[[CampaignWML#The_.5Bcampaign.5D_tag|campaign]],&lt;br /&gt;
[[DirectActionsWML#.5Bcancel_action.5D|cancel_action]],&lt;br /&gt;
[[Wesnoth_AI_Framework#The_.5Bcandidate_action.5D_Tag|candidate_action]], &lt;br /&gt;
[[DirectActionsWML#.5Bcapture_village.5D|capture_village]],&lt;br /&gt;
[[ConditionalActionsWML#.5Bswitch.5D|case]],&lt;br /&gt;
[[AbilitiesWML#The_.5Bspecials.5D_tag|chance_to_hit]], &lt;br /&gt;
[[InterfaceActionsWML#.5Bchange_theme.5D|change_theme]], &lt;br /&gt;
[[InterfaceActionsWML#.5Bchat.5D|chat]],&lt;br /&gt;
[[OptionWML#checkbox|checkbox]],&lt;br /&gt;
[[OptionWML#choice|choice]],&lt;br /&gt;
[[ReplayWML#choose|choose]],&lt;br /&gt;
[[PersistenceWML#WML Syntax|clear_global_variable]],&lt;br /&gt;
[[InterfaceActionsWML#.5Bclear_menu_item.5D|clear_menu_item]],&lt;br /&gt;
[[InternalActionsWML#.5Bclear_variable.5D|clear_variable]],&lt;br /&gt;
[[InterfaceActionsWML#.5Bcolor_adjust.5D|color_adjust]],&lt;br /&gt;
[[GameConfigWML#Color_Palettes|color_palette]],&lt;br /&gt;
[[GameConfigWML#Color_Palettes|color_range]],&lt;br /&gt;
command&amp;amp;nbsp;([[ConditionalActionsWML#.5Bcommand.5D|action]], [[ReplayWML|replay]]),&lt;br /&gt;
[[InternalActionsWML#Flow_control_actions|continue]],&lt;br /&gt;
[[CoreWML|core]],&lt;br /&gt;
[[CreditsWML#.5Bcredits_group.5D|credits_group]],&lt;br /&gt;
[[AiWML#The_.5Bgoal.5D_Tag|criteria]];&lt;br /&gt;
|-&lt;br /&gt;
|''D:'' &lt;br /&gt;
[[AbilitiesWML#The_.5Bspecials.5D_tag|damage]],&lt;br /&gt;
[[AbilitiesWML#The_.5Bspecials.5D_tag|damage_type]], &lt;br /&gt;
[[AnimationWML#short-death|death]], &lt;br /&gt;
[[StatisticalScenarioWML#The_.5Bteam.5D_tag|deaths]],&lt;br /&gt;
[[Wesnoth_AI_Framework#The_.5Bai.5D_Tag_.E2.80.94_Aspects|default]], &lt;br /&gt;
[[AnimationWML#short-defend|defend]],&lt;br /&gt;
[[StatisticalScenarioWML#The_.5Bteam.5D_tag|defends]],&lt;br /&gt;
[[UnitsWML#defense|defense]],&lt;br /&gt;
[[InterfaceActionsWML#.5Bdelay.5D|delay]],&lt;br /&gt;
[[InterfaceActionsWML#.5Bdeprecated_message.5D|deprecated_message]],&lt;br /&gt;
[[ReplayWML#attack|destination]],&lt;br /&gt;
[[CampaignWML#difficulty|difficulty]],&lt;br /&gt;
[[AbilitiesWML#The_.5Bspecials.5D_tag|disable]],&lt;br /&gt;
[[DirectActionsWML#.5Bdisallow_end_turn.5D|disallow_end_turn]],&lt;br /&gt;
[[DirectActionsWML#.5Bdisallow_extra_recruit.5D|disallow_extra_recruit]],&lt;br /&gt;
[[DirectActionsWML#.5Bdisallow_recruit.5D|disallow_recruit]],&lt;br /&gt;
[[ConditionalActionsWML#.5Bwhile.5D|do]], &lt;br /&gt;
[[DirectActionsWML#.5Bdo_command.5D|do_command]],&lt;br /&gt;
[[AbilitiesWML#The_.5Bspecials.5D_tag|drains]], &lt;br /&gt;
[[AnimationWML#short-draw_weapon|draw_weapon_anim]];&lt;br /&gt;
|-&lt;br /&gt;
|''E:'' &lt;br /&gt;
[[EditorWML#The_.5Beditor_group.5D_tag|editor_group]],&lt;br /&gt;
[[EditorWML#The_.5Beditor_music.5D_tag|editor_music]],&lt;br /&gt;
[[EditorWML#The_.5Beditor_times.5D_tag|editor_times]],&lt;br /&gt;
[[EffectWML|effect]],&lt;br /&gt;
else&amp;amp;nbsp;([[ConditionalActionsWML#.5Bif.5D|action]], [[AnimationWML#Conditional Branches|animation]]), [[ConditionalActionsWML#.5Bif.5D|elseif]],&lt;br /&gt;
[[DirectActionsWML#.5Bendlevel.5D|endlevel]],&lt;br /&gt;
end_turn&amp;amp;nbsp;([[DirectActionsWML#.5Bend_turn.5D|action]], [[ReplayWML#end_turn|replay]]),&lt;br /&gt;
[[StandardSideFilter#enemy_of|enemy_of]], &lt;br /&gt;
[[Wesnoth_AI_Framework#The_.5Bai.5D_Tag_.E2.80.94_Engines|engine]], &lt;br /&gt;
entry&amp;amp;nbsp;([[CreditsWML#.5Bentry.5D|credits]], [[OptionWML|options]]),&lt;br /&gt;
[[EraWML|era]],&lt;br /&gt;
[[EventWML|event]],&lt;br /&gt;
[[StandardUnitFilter#filter_ability|experimental_filter_ability]],&lt;br /&gt;
[[StandardUnitFilter#filter_ability_active|experimental_filter_ability_active]],&lt;br /&gt;
[[AbilitiesWML#filter_specials|experimental_filter_specials]],&lt;br /&gt;
[[AnimationWML#short-extra|extra_anim]];&lt;br /&gt;
|-&lt;br /&gt;
|''F:''&lt;br /&gt;
[[Wesnoth_AI_Framework#The_.5Bai.5D_Tag_.E2.80.94_Aspects|facet]],&lt;br /&gt;
[[InterfaceActionsWML#.5Banimate_unit.5D|facing]], &lt;br /&gt;
[[InterfaceActionsWML#.5Bmove_units_fake.5D|fake_unit]], &lt;br /&gt;
[[ConditionalActionsWML#.5Bfalse.5D|false]],&lt;br /&gt;
[[PblWML#.5Bfeedback.5D|feedback]],&lt;br /&gt;
[[UnitTypeWML#variation|female]], &lt;br /&gt;
filter ([[FilterWML|concept]], [[EventWML#.5Bfilter.5D|event]]),&lt;br /&gt;
[[StandardUnitFilter#filter_adjacent|filter_adjacent]], &lt;br /&gt;
[[StandardLocationFilter#filter_adjacent_location|filter_adjacent_location]], &lt;br /&gt;
[[FilterWML#Filtering_Weapons|filter_attack]],&lt;br /&gt;
[[AbilitiesWML#filter_attacker|filter_attacker]], &lt;br /&gt;
[[AbilitiesWML#filter_base_value|filter_base_value]], &lt;br /&gt;
[[EventWML#.5Bfilter_condition.5D|filter_condition]],&lt;br /&gt;
[[AbilitiesWML#filter_defender|filter_defender]], &lt;br /&gt;
[[AiWML#Filtering_Combat_with_the_attacks_Aspect|filter_enemy]],&lt;br /&gt;
[[StandardLocationFilter|filter_location]],&lt;br /&gt;
[[AbilitiesWML#filter_opponent|filter_opponent]], &lt;br /&gt;
[[AiWML#Filtering_Combat_with_the_attacks_Aspect|filter_own]],&lt;br /&gt;
[[StandardLocationFilter#filter_owner|filter_owner]], &lt;br /&gt;
[[StandardLocationFilter#filter_radius|filter_radius]], &lt;br /&gt;
[[SingleUnitWML#filter_recall|filter_recall]], &lt;br /&gt;
[[StandardUnitFilter|filter_second]],&lt;br /&gt;
[[FilterWML#Filtering_Weapons|filter_second_attack]],&lt;br /&gt;
[[AbilitiesWML#filter_self|filter_self]], &lt;br /&gt;
[[StandardSideFilter|filter_side]],&lt;br /&gt;
[[AbilitiesWML#filter_student|filter_student]], &lt;br /&gt;
[[FilterWML#Filtering_Vision|filter_vision]],&lt;br /&gt;
[[FilterWML#Filtering_Weapons|filter_weapon]], &lt;br /&gt;
[[FilterWML#Filtering_on_WML_data|filter_wml]],&lt;br /&gt;
[[InternalActionsWML#.5Bfind_path.5D|find_path]],&lt;br /&gt;
[[InternalActionsWML#.5Bfire_event.5D|fire_event]],&lt;br /&gt;
[[AbilitiesWML#The_.5Bspecials.5D_tag|firststrike]], &lt;br /&gt;
[[InterfaceActionsWML#.5Bfloating_text.5D|floating_text]],&lt;br /&gt;
[[FontsWML|fonts]],&lt;br /&gt;
[[ConditionalActionsWML#.5Bfor.5D|for]],&lt;br /&gt;
[[ConditionalActionsWML#.5Bforeach.5D|foreach]],&lt;br /&gt;
[[ConditionalActionsWML#.5Bfound_item.5D|found_item]],&lt;br /&gt;
[[AnimationWML#The .5Bframe.5D tag|frame]];&lt;br /&gt;
|-&lt;br /&gt;
|''G:'' &lt;br /&gt;
[[GameConfigWML|game_config]],&lt;br /&gt;
[[PersistenceWML#WML Syntax|get_global_variable]],&lt;br /&gt;
[[AiWML#The_.5Bgoal.5D_Tag|goal]],&lt;br /&gt;
[[DirectActionsWML#.5Bgold.5D|gold]],&lt;br /&gt;
[[InterfaceActionsWML#objectives-gold_carryover|gold_carryover]];&lt;br /&gt;
|-&lt;br /&gt;
|''H:'' &lt;br /&gt;
[[DirectActionsWML#.5Bharm_unit.5D|harm_unit]],&lt;br /&gt;
[[StandardSideFilter#has_ally|has_ally]], &lt;br /&gt;
[[StandardUnitFilter#has_attack|has_attack]],&lt;br /&gt;
[[StandardSideFilter#has_unit|has_unit]], &lt;br /&gt;
[[ConditionalActionsWML#.5Bhas_achievement.5D|has_achievement]],&lt;br /&gt;
[[ConditionalActionsWML#.5Bhave_location.5D|have_location]],&lt;br /&gt;
[[ConditionalActionsWML#.5Bhave_side.5D|have_side]],&lt;br /&gt;
[[ConditionalActionsWML#.5Bhave_unit.5D|have_unit]],&lt;br /&gt;
[[AbilitiesWML#The_.5Bspecials.5D_tag|heal_on_hit]], &lt;br /&gt;
[[DirectActionsWML#.5Bheal_unit.5D|heal_unit]],&lt;br /&gt;
[[AnimationWML#short-healed|healed_anim]], &lt;br /&gt;
[[AnimationWML#short-healing|healing_anim]], &lt;br /&gt;
[[AbilitiesWML#The_.5Babilities.5D_tag|heals]], &lt;br /&gt;
[[UnitsWML#.5Bhide_help.5D|hide_help]],&lt;br /&gt;
[[InterfaceActionsWML#.5Bhide_unit.5D|hide_unit]],&lt;br /&gt;
[[AbilitiesWML#The_.5Babilities.5D_tag|hides]];&lt;br /&gt;
|-&lt;br /&gt;
|''I:'' &lt;br /&gt;
[[AnimationWML#short-idle|idle_anim]], &lt;br /&gt;
if&amp;amp;nbsp;([[ConditionalActionsWML#.5Bif.5D|action]], [[AnimationWML#Conditional Branches|animation]], [[IntroWML|intro]]),&lt;br /&gt;
[[AbilitiesWML#The_.5Babilities.5D_tag|illuminates]], &lt;br /&gt;
image&amp;amp;nbsp;([[IntroWML#.5Bimage.5D|intro]], [[TerrainGraphicsWML#The_.5Bimage.5D_subtag|terrain]]),&lt;br /&gt;
[[ReplayWML#init_side|init_side]],&lt;br /&gt;
[[VariablesWML#.5Binsert_tag.5D|insert_tag]],&lt;br /&gt;
[[InterfaceActionsWML#.5Binspect.5D|inspect]],&lt;br /&gt;
[[InterfaceActionsWML#.5Bitem.5D|item]],&lt;br /&gt;
[[EditorWML#The_.5Bitem_group.5D_tag|item_group]];&lt;br /&gt;
|-&lt;br /&gt;
|''J:''&lt;br /&gt;
[[UnitsWML#jamming_costs|jamming_costs]],&lt;br /&gt;
[[InternalActionsWML#join|join]];&lt;br /&gt;
|-&lt;br /&gt;
|''K:'' &lt;br /&gt;
[[DirectActionsWML#.5Bkill.5D|kill]],&lt;br /&gt;
[[StatisticalScenarioWML#The_.5Bteam.5D_tag|killed]];&lt;br /&gt;
|-&lt;br /&gt;
|''L:'' &lt;br /&gt;
[[InterfaceActionsWML#.5Blabel.5D|label]],&lt;br /&gt;
[[LanguageWML|language]],&lt;br /&gt;
[[SideWML#leader|leader]],&lt;br /&gt;
[[AiWML#leader_goal|leader_goal]],&lt;br /&gt;
[[AbilitiesWML#The_.5Babilities.5D_tag|leadership]], &lt;br /&gt;
[[AnimationWML#short-leading|leading_anim]], &lt;br /&gt;
[[AnimationWML#short-levelin|levelin_anim]],&lt;br /&gt;
[[AnimationWML#short-levelout|levelout_anim]], &lt;br /&gt;
[[DirectActionsWML#.5Blift_fog.5D|lift_fog]],&lt;br /&gt;
[[AI_Recruitment#instructions-limit|limit]],&lt;br /&gt;
[[InternalActionsWML#set_variables-literal|literal]],&lt;br /&gt;
[[AddonsWML#load_resource|load_resource]],&lt;br /&gt;
[[LocaleWML|locale]],&lt;br /&gt;
[[InterfaceActionsWML#.5Block_view.5D|lock_view]],&lt;br /&gt;
[[LuaWML|lua]];&lt;br /&gt;
|-&lt;br /&gt;
|''M:'' &lt;br /&gt;
[[UnitTypeWML#variation|male]],&lt;br /&gt;
[[ReplayWML#map_data|map_data]], &lt;br /&gt;
[[InterfaceActionsWML#.5Bmessage.5D|message]],&lt;br /&gt;
[[Micro AIs|micro_ai]],&lt;br /&gt;
[[AnimationWML#The .5Bframe.5D tag|missile_frame]],&lt;br /&gt;
[[ModificationWML|modification]],&lt;br /&gt;
[[SingleUnitWML#modifications|modifications]],&lt;br /&gt;
[[DirectActionsWML#.5Bmodify_ai.5D|modify_ai]],&lt;br /&gt;
[[DirectActionsWML#.5Bmodify_side.5D|modify_side]],&lt;br /&gt;
[[DirectActionsWML#.5Bmodify_turns.5D|modify_turns]],&lt;br /&gt;
[[DirectActionsWML#.5Bmodify_unit.5D|modify_unit]],&lt;br /&gt;
[[AddonsWML#modify_unit_type|modify_unit_type]],&lt;br /&gt;
[[ReplayWML#move|move]],&lt;br /&gt;
[[DirectActionsWML#.5Bmove_unit.5D|move_unit]],&lt;br /&gt;
[[InterfaceActionsWML#.5Bmove_unit_fake.5D|move_unit_fake]],&lt;br /&gt;
[[InterfaceActionsWML#.5Bmove_units_fake.5D|move_units_fake]],&lt;br /&gt;
[[AnimationWML#short-movement|movement_anim]], &lt;br /&gt;
[[UnitsWML#movement_costs|movement costs]],&lt;br /&gt;
[[UnitsWML#.5Bmovetype.5D|movetype]],&lt;br /&gt;
[[ScenarioWML#The_.5Bmultiplayer.5D_tag|multiplayer]],&lt;br /&gt;
[[EraWML#Defining_Factions|multiplayer_side]],&lt;br /&gt;
[[MusicListWML#.5Bmusic.5D|music]];&lt;br /&gt;
|-&lt;br /&gt;
|''N:'' &lt;br /&gt;
[[ConditionalActionsWML#Meta-Condition_Tags|not]], &lt;br /&gt;
[[InterfaceActionsWML#objectives-note|note]];&lt;br /&gt;
|-&lt;br /&gt;
|''O:'' &lt;br /&gt;
[[DirectActionsWML#.5Bobject.5D|object]],&lt;br /&gt;
[[InterfaceActionsWML#objectives-objective|objective]],&lt;br /&gt;
[[InterfaceActionsWML#.5Bobjectives.5D|objectives]],&lt;br /&gt;
[[DirectActionsWML#.5Bon_undo.5D|on_undo]],&lt;br /&gt;
[[InterfaceActionsWML#.5Bopen_help.5D|open_help]],&lt;br /&gt;
[[InterfaceActionsWML#.5Bmessage.5D|option]],&lt;br /&gt;
[[OptionWML|options]],&lt;br /&gt;
[[ConditionalActionsWML#Meta-Condition_Tags|or]];&lt;br /&gt;
|-&lt;br /&gt;
|''P:'' &lt;br /&gt;
[[IntroWML#.5Bpart.5D|part]], &lt;br /&gt;
[[AbilitiesWML#The_.5Bspecials.5D_tag|petrifies]], &lt;br /&gt;
[[DirectActionsWML#.5Bpetrify.5D|petrify]], &lt;br /&gt;
[[DirectActionsWML#.5Bplace_shroud.5D|place_shroud]], &lt;br /&gt;
[[AbilitiesWML#The_.5Bspecials.5D_tag|plague]], &lt;br /&gt;
[[AbilitiesWML#The_.5Bspecials.5D_tag|poison]], &lt;br /&gt;
[[AnimationWML#short-post_movement|post_movement_anim]], &lt;br /&gt;
[[AnimationWML#short-pre_movement|pre_movement_anim]], &lt;br /&gt;
[[InternalActionsWML#.5Bfire_event.5D|primary_attack]], &lt;br /&gt;
[[InternalActionsWML#.5Bfire_event.5D|primary_unit]], &lt;br /&gt;
[[InterfaceActionsWML#.5Bprint.5D|print]], &lt;br /&gt;
[[DirectActionsWML#.5Bprogress_achievement.5D|progress_achievement]], &lt;br /&gt;
[[DirectActionsWML#.5Bput_to_recall_list.5D|put_to_recall_list]];&lt;br /&gt;
|-&lt;br /&gt;
|''R:'' &lt;br /&gt;
[[UnitsWML#.5Brace.5D|race]], &lt;br /&gt;
[[InternalActionsWML#.5Brandom_placement.5D|random_placement]], &lt;br /&gt;
recall&amp;amp;nbsp;([[DirectActionsWML#.5Brecall.5D|action]], [[ReplayWML#recall|replay]]), &lt;br /&gt;
[[StatisticalScenarioWML#The_.5Bteam.5D_tag|recalls]],&lt;br /&gt;
[[ReplayWML#recruit|recruit]], &lt;br /&gt;
[[AnimationWML#short-recruit|recruit_anim]], &lt;br /&gt;
[[AnimationWML#short-recruiting|recruiting_anim]], &lt;br /&gt;
[[StatisticalScenarioWML#The_.5Bteam.5D_tag|recruits]], &lt;br /&gt;
[[InterfaceActionsWML#.5Bredraw.5D|redraw]],&lt;br /&gt;
[[AbilitiesWML#The_.5Babilities.5D_tag|regenerate]],&lt;br /&gt;
[[InternalActionsWML#.5Bremove_event.5D|remove_event]],&lt;br /&gt;
[[InterfaceActionsWML#.5Bremove_item.5D|remove_item]], &lt;br /&gt;
[[DirectActionsWML#.5Bremove_object.5D|remove_object]], &lt;br /&gt;
[[DirectActionsWML#.5Bremove_shroud.5D|remove_shroud]], &lt;br /&gt;
[[InterfaceActionsWML#.5Bremove_sound_source.5D|remove_sound_source]], &lt;br /&gt;
[[DirectActionsWML#.5Bremove_time_area.5D|remove_time_area]], &lt;br /&gt;
[[DirectActionsWML#.5Bremove_trait.5D|remove_trait]],&lt;br /&gt;
[[InterfaceActionsWML#.5Bremove_unit_overlay.5D|remove_unit_overlay]],&lt;br /&gt;
[[ConditionalActionsWML#.5Brepeat.5D|repeat]],&lt;br /&gt;
[[DirectActionsWML#.5Breplace_map.5D|replace_map]], &lt;br /&gt;
[[DirectActionsWML#.5Breplace_schedule.5D|replace_schedule]], &lt;br /&gt;
[[ReplayWML|replay]], &lt;br /&gt;
[[DirectActionsWML#.5Breset_fog.5D|reset_fog]], &lt;br /&gt;
resistance&amp;amp;nbsp;([[AbilitiesWML#The_.5Babilities.5D_tag|ability]], [[UnitsWML#resistance|unit]]),&lt;br /&gt;
[[UnitsWML#.5Bresistance_defaults.5D|resistance_defaults]],&lt;br /&gt;
[[ThemeWML#The_toplevel_.5Btheme.5D_tag|resolution]],&lt;br /&gt;
[[ModificationWML#The_.5Bresource.5D_toplevel_tag|resource]],&lt;br /&gt;
[[InternalActionsWML#Flow_control_actions|return]],&lt;br /&gt;
[[InternalActionsWML#.5Brole.5D|role]], &lt;br /&gt;
[[TerrainMaskWML#rule|rule]];&lt;br /&gt;
|-&lt;br /&gt;
|''S:'' &lt;br /&gt;
[[ScenarioWML#The_.5Bscenario.5D_tag|scenario]],&lt;br /&gt;
[[InterfaceActionsWML#.5Bscreen_fade.5D|screen_fade]], &lt;br /&gt;
[[InterfaceActionsWML#.5Bscroll.5D|scroll]], &lt;br /&gt;
[[InterfaceActionsWML#.5Bscroll_to.5D|scroll_to]],&lt;br /&gt;
[[InterfaceActionsWML#.5Bscroll_to_unit.5D|scroll_to_unit]], &lt;br /&gt;
[[InternalActionsWML#.5Bfire_event.5D|secondary_attack]], &lt;br /&gt;
[[InternalActionsWML#.5Bfire_event.5D|secondary_unit]], &lt;br /&gt;
[[HelpWML#section|section]], &lt;br /&gt;
[[InterfaceActionsWML#.5Bselect_unit.5D|select_unit]], &lt;br /&gt;
[[ReplayWML#sequence|sequence]], &lt;br /&gt;
[[DirectActionsWML#.5Bset_achievement.5D|set_achievement]],&lt;br /&gt;
[[DirectActionsWML#.5Bset_extra_recruit.5D|set_extra_recruit]],&lt;br /&gt;
[[PersistenceWML#WML_Syntax|set_global_variable]],&lt;br /&gt;
[[InterfaceActionsWML#.5Bset_menu_item.5D|set_menu_item]], &lt;br /&gt;
[[DirectActionsWML#.5Bset_recruit.5D|set_recruit]],&lt;br /&gt;
[[EffectWML#set_specials|set_specials]], &lt;br /&gt;
[[InternalActionsWML#.5Bset_variable.5D|set_variable]], &lt;br /&gt;
[[InternalActionsWML#.5Bset_variables.5D|set_variables]], &lt;br /&gt;
[[AnimationWML#short-sheath_weapon|sheath_weapon_anim]], &lt;br /&gt;
show_if&amp;amp;nbsp;([[InterfaceActionsWML#.5Bmessage.5D|message]], &lt;br /&gt;
[[InterfaceActionsWML#.5Bobjectives.5D|objective]], &lt;br /&gt;
[[InterfaceActionsWML#.5Bset_menu_item.5D|set_menu_item]]),&lt;br /&gt;
[[InterfaceActionsWML#.5Bshow_objectives.5D|show_objectives]],&lt;br /&gt;
[[SideWML|side]], &lt;br /&gt;
[[AbilitiesWML#The_.5Babilities.5D_tag|skirmisher]], &lt;br /&gt;
[[OptionWML#slider|slider]],&lt;br /&gt;
[[AbilitiesWML#The_.5Bspecials.5D_tag|slow]], &lt;br /&gt;
[[InterfaceActionsWML#.5Bsound.5D|sound]], &lt;br /&gt;
[[InterfaceActionsWML#.5Bsound_source.5D|sound_source]], &lt;br /&gt;
source&amp;amp;nbsp;([[ReplayWML#attack|replay]], [[DirectActionsWML#.5Btunnel.5D|teleport]]),&lt;br /&gt;
[[UnitTypeWML#Special Notes|special_note]],&lt;br /&gt;
[[AbilitiesWML#The_.5Bspecials.5D_tag|specials]], &lt;br /&gt;
[[InternalActionsWML#set_variables-split|split]],&lt;br /&gt;
[[Wesnoth_AI_Framework#The_.5Bai.5D_Tag_.E2.80.94_Stages|stage]], &lt;br /&gt;
[[AnimationWML#short-standing|standing_anim]], &lt;br /&gt;
[[StatisticalScenarioWML#The_.5Bstatistics.5D_tag|statistics]],&lt;br /&gt;
[[SingleUnitWML#status|status]], &lt;br /&gt;
[[InternalActionsWML#.5Bstore_gold.5D|store_gold]], &lt;br /&gt;
[[InternalActionsWML#.5Bstore_items.5D|store_items]], &lt;br /&gt;
[[InternalActionsWML#.5Bstore_locations.5D|store_locations]],&lt;br /&gt;
[[InternalActionsWML#.5Bstore_map_dimensions.5D|store_map_dimensions]],&lt;br /&gt;
[[InternalActionsWML#.5Bstore_reachable_locations.5D|store_reachable_locations]],&lt;br /&gt;
[[InternalActionsWML#.5Bstore_relative_direction.5D|store_relative_direction]],&lt;br /&gt;
[[InternalActionsWML#.5Bstore_side.5D|store_side]], &lt;br /&gt;
[[InternalActionsWML#.5Bstore_starting_location.5D|store_starting_location]], &lt;br /&gt;
[[InternalActionsWML#.5Bstore_time_of_day.5D|store_time_of_day]], &lt;br /&gt;
[[InternalActionsWML#.5Bstore_turns.5D|store_turns]], &lt;br /&gt;
[[InternalActionsWML#.5Bstore_unit.5D|store_unit]], &lt;br /&gt;
[[InternalActionsWML#.5Bstore_unit_defense.5D|store_unit_defense]], &lt;br /&gt;
[[InternalActionsWML#.5Bstore_unit_defense_on.5D|store_unit_defense_on]], &lt;br /&gt;
[[InternalActionsWML#.5Bstore_unit_type.5D|store_unit_type]], &lt;br /&gt;
[[InternalActionsWML#.5Bstore_unit_type_ids.5D|store_unit_type_ids]], &lt;br /&gt;
[[InternalActionsWML#.5Bstore_villages.5D|store_villages]], &lt;br /&gt;
[[IntroWML|story]], &lt;br /&gt;
[[AbilitiesWML#The_.5Bspecials.5D_tag|swarm]], &lt;br /&gt;
[[AchievementsWML#.5Bsub_achievement.5D|sub_achievement]],&lt;br /&gt;
[[ConditionalActionsWML#.5Bswitch.5D|switch]],&lt;br /&gt;
[[InternalActionsWML#.5Bsync_variable.5D|sync_variable]];&lt;br /&gt;
|-&lt;br /&gt;
|''T:'' &lt;br /&gt;
[[DirectActionsWML#.5Btunnel.5D|target]], &lt;br /&gt;
[[StatisticalScenarioWML#The_.5Bteam.5D_tag|team]],&lt;br /&gt;
teleport&amp;amp;nbsp;([[AbilitiesWML#Extra_tags_used_by_the_.5Bteleport.5D_ability|ability]], [[DirectActionsWML#.5Bteleport.5D|action]]),&lt;br /&gt;
[[AnimationWML#short-teleport|teleport_anim]],&lt;br /&gt;
[[DirectActionsWML#.5Bterrain.5D|terrain]], &lt;br /&gt;
[[UnitsWML#.5Bterrain_defaults.5D|terrain_defaults]],&lt;br /&gt;
[[TerrainGraphicsWML|terrain_graphics]], &lt;br /&gt;
[[TerrainMaskWML|terrain_mask]], &lt;br /&gt;
[[TerrainWML|terrain_type]], &lt;br /&gt;
[[ScenarioWML#The_.5Btest.5D_tag|test]],&lt;br /&gt;
[[InterfaceActionsWML#.5Btest_condition.5D|test_condition]],&lt;br /&gt;
[[TestWML#The_.5Btest_do_attack_by_id.5D_tag|test_do_attack_by_id]],&lt;br /&gt;
[[InterfaceActionsWML#message-text_input|text_input]], &lt;br /&gt;
[[GettextForWesnothDevelopers#The_textdomain_tag|textdomain]],&lt;br /&gt;
[[ThemeWML|theme]],&lt;br /&gt;
[[ConditionalActionsWML#.5Bif.5D|then]],&lt;br /&gt;
[[TerrainGraphicsWML#The_.5Btile.5D_subtag|tile]], &lt;br /&gt;
[[TimeWML|time]], &lt;br /&gt;
[[DirectActionsWML#.5Btime_area.5D|time_area]], &lt;br /&gt;
[[HelpWML#topic|topic]], &lt;br /&gt;
[[HelpWML#toplevel|toplevel]], &lt;br /&gt;
[[UnitsWML#.5Btrait.5D|trait]], &lt;br /&gt;
[[DirectActionsWML#.5Btransform_unit.5D|transform_unit]], &lt;br /&gt;
[[InternalActionsWML#.5Bfind_path.5D|traveler]], &lt;br /&gt;
[[ConditionalActionsWML#.5Btrue.5D|true]],&lt;br /&gt;
[[DirectActionsWML#.5Btunnel.5D|tunnel]];&lt;br /&gt;
|-&lt;br /&gt;
|''U:'' &lt;br /&gt;
[[InterfaceActionsWML#.5Bunhide_unit.5D|unhide_unit]], &lt;br /&gt;
unit&amp;amp;nbsp;([[DirectActionsWML#.5Bunit.5D|action]], [[SingleUnitWML|scenario]]), &lt;br /&gt;
[[InterfaceActionsWML#.5Bunit_overlay.5D|unit_overlay]], &lt;br /&gt;
[[UnitTypeWML|unit_type]], &lt;br /&gt;
[[InternalActionsWML#.5Bunit_worth.5D|unit_worth]], &lt;br /&gt;
[[UnitsWML|units]],&lt;br /&gt;
[[InterfaceActionsWML#.5Bunlock_view.5D|unlock_view]],&lt;br /&gt;
[[DirectActionsWML#.5Bunpetrify.5D|unpetrify]], &lt;br /&gt;
[[DirectActionsWML#.5Bunstore_unit.5D|unstore_unit]],&lt;br /&gt;
[[InternalActionsWML#.5Bunsynced.5D|unsynced]];&lt;br /&gt;
|-&lt;br /&gt;
| ''V:'' &lt;br /&gt;
[[InternalActionsWML#set_variables-value|value]], &lt;br /&gt;
[[ConditionalActionsWML#.5Bvariable.5D|variable]],&lt;br /&gt;
[[VariablesWML#The_.5Bvariables.5D_tag|variables]],&lt;br /&gt;
[[TerrainGraphicsWML#variant|variant]],&lt;br /&gt;
[[UnitTypeWML#variation|variation]], &lt;br /&gt;
[[AnimationWML#short-victory|victory_anim]], &lt;br /&gt;
[[SideWML#village|village]],&lt;br /&gt;
[[UnitsWML#vision_costs|vision_costs]],&lt;br /&gt;
[[InterfaceActionsWML#.5Bvolume.5D|volume]];&lt;br /&gt;
|-&lt;br /&gt;
| ''W:'' &lt;br /&gt;
[[ConditionalActionsWML#.5Bwhile.5D|while]],&lt;br /&gt;
[[InterfaceActionsWML#.5Bwml_message.5D|wml_message]],&lt;br /&gt;
[[SchemaWML|wml_schema]];&lt;br /&gt;
|-&lt;br /&gt;
| ''Z:''&lt;br /&gt;
[[InterfaceActionsWML#.5Bzoom.5D|zoom]];&lt;br /&gt;
|}&amp;lt;includeonly&amp;gt;[[Category:WML Reference]]&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt;A box with all the WML tags, each one linking to the page and section they are described in. This box should be included in each of the [[ReferenceWML|WML reference]] pages.&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=CampaignWML&amp;diff=75513</id>
		<title>CampaignWML</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=CampaignWML&amp;diff=75513"/>
		<updated>2026-07-10T18:49:50Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WML Tags}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
&lt;br /&gt;
Dacyn and/or Invisible Philosopher -- please be careful&lt;br /&gt;
you don't reduce the signal-to-noise ratio on the WML pages&lt;br /&gt;
when editing!  Eg. knowing that a tag is translatable is _important_&lt;br /&gt;
for the 29 translations we have in progress. -- ott&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This page describes how the campaign is displayed in the &amp;quot;Campaign&amp;quot; menu, and how it starts.&lt;br /&gt;
&lt;br /&gt;
==The [campaign] Tag==&lt;br /&gt;
&lt;br /&gt;
The following keys and tags are recognized in '''[campaign]''' tags, in addition to all the common [[AddonsWML|addon module keys and tags]]:&lt;br /&gt;
* '''icon''': the image displayed in the campaign selection menu&lt;br /&gt;
* '''abbrev''': (translatable) abbreviation used as a prefix for savefile names made from this campaign&lt;br /&gt;
* '''image''': the image shown in the information pane when this campaign is selected in the campaign selection menu (typically a transparent, 350×350 pixels portrait)&lt;br /&gt;
* '''background''': {{DevFeature1.15|9}} the image used as a backdrop for the Campaigns menu (typically a high resolution image intended for displaying fullscreen in story screens). If blank or unspecified, a stock story background from core is used.&lt;br /&gt;
* '''description_alignment''': {{DevFeature1.13|3}} The text alignment of the description. Choose between &amp;quot;left&amp;quot; (default), &amp;quot;center&amp;quot;, or &amp;quot;right&amp;quot;.&lt;br /&gt;
* '''type''': campaign's type to specify if it should be visible in singleplayer, multiplayer or both. Possible values are &amp;quot;sp&amp;quot;, &amp;quot;mp&amp;quot; and &amp;quot;hybrid&amp;quot;. Defaults to &amp;quot;sp&amp;quot;.&lt;br /&gt;
* '''define''': The preprocessor symbol that will be defined for this campaign. All other campaign-specific code should be contained in an ''#ifdef'' checking for this symbol.&lt;br /&gt;
* '''extra_defines''': a comma(''',''') separated list of preprocessor symbols. Those symbols will be defined ''before'' any .cfg is preprocessed. (In the past, this tag was used to define common optional advancements, but that use is deprecated. There are now macros to add those advancements defined in [https://www.wesnoth.org/macro-reference.html#file:optional_unit_advancements.cfg data/core/macros/optional_unit_advancements.cfg].)&lt;br /&gt;
* '''difficulties''': a comma(''',''') separated list of preprocessor symbols, exactly one of which will be stored depending on the difficulty setting chosen when the campaign is started. The symbols '''EASY''', '''NORMAL''', and '''HARD''' are usually used, and there are several macros in utils.cfg (see [https://www.wesnoth.org/macro-reference.html#file:utils.cfg| Macro Reference]) which check for these values to set WML keys to different values depending on difficulty.  If you use different difficulty symbols, you may need to define your own versions of these macros. {{DevFeature1.13|2}} This key has been deprecated in favor of [difficulty] define=.&lt;br /&gt;
* '''difficulty_descriptions''': the menu of difficulties; this is a list of descriptions (see [[DescriptionWML]]) that correspond to different difficulty levels. Since each description is a menu option for a difficulty level, this must provide the same number of descriptions as there are levels in the ''difficulties'' list. {{DevFeature1.13|2}} This key has been deprecated in favor of [difficulty] description=&lt;br /&gt;
* {{anchor|difficulty|'''[difficulty]'''}}:  {{DevFeature1.13|2}} specifies a single campaign difficulty. The difficulties are expected to be ordered from easiest to hardest. The following keys are accepted:&lt;br /&gt;
** '''define''': the preprocessor symbol defined when this difficulty is selected. Uses the same format as an entry in the old ''difficulties'' list.&lt;br /&gt;
** '''image''': the image to display for this difficulty in the selection menu&lt;br /&gt;
** '''label''': a flavor label describing this difficulty. Displayed second after the image&lt;br /&gt;
** '''description''': a description of the difficulty, usually along the lines of &amp;quot;Beginner&amp;quot; or &amp;quot;Challenging&amp;quot;. Displayed third after the image.&lt;br /&gt;
** '''default''': whether this is the difficulty which will be selected by default when the difficulty selection menu is displayed.&lt;br /&gt;
** '''auto_markup''': {{DevFeature1.15|0}} By default, the description is shown in small, gray text within parentheses. Setting '''auto_markup=no''' disables these, so no markup will be applied implicitly. Any markup in '''description''' will be honored regardless of this setting.&lt;br /&gt;
* '''allow_difficulty_change''': Allows difficulty switching during an ongoing campaign. Default:yes&lt;br /&gt;
* '''first_scenario''': the ID of the first scenario in the campaign; see ''id'' in [[ScenarioWML]]&lt;br /&gt;
* '''rank''': a number that determines the order of campaigns in the campaign selection menu.  Lower ''rank'' campaigns appear earlier, with unranked campaigns at the end. Currently the mainline campaigns use multiples of 10 from 0 to 399, with 0-99 for Novice campaigns, 100-199 for Intermediate campaigns, and 200-399 for Expert campaigns; if you specify this, it should not be less than 400.  (Note: This replaces an older convention that topped out at 50.) {{DevFeature1.14|6}} a number that determines the order of campaigns in the campaign selection menu. Lower rank campaigns appear earlier, with unranked campaigns at the end. Currently the mainline campaigns use multiples of 5 from 0 to 249, with 0-49 for Rookie campaigns, 50-99 for Novice campaigns, 100-149 for Intermediate campaigns, 150-199 for Hard campaigns, and 200-249 for Expert campaigns; if you specify this, it should not be less than 300.&lt;br /&gt;
* '''start_year''': a string that determines the order of campaigns when the campaign selection menu is sorted by date. The date needs a year number and an epoch, for example '''20 BW''', '''20 YW''', '''20 BF''' or '''20 AF'''. In Wesnoth 1.14, this is the only place in which this date-parsing is used.&lt;br /&gt;
* '''end_year''': a string that helps determine the order of campaigns when two campaigns have the same '''start_year'''. Ignored if '''start_year''' is not set.&lt;br /&gt;
* '''year''': shortcut for specifying both '''start_year''' and '''end_year''', for campaigns that happen inside a single calendar year. Ignored if '''start_year''' is given. &lt;br /&gt;
* '''[about]''': inserts your own credits into the game's list of credits. See [[CreditsWML]] for syntax.&lt;br /&gt;
* '''end_credits''': Whether to display the credits screen at the end of the campaign. Defaults to ''yes''.&lt;br /&gt;
* '''end_text''': (translatable) Text that is shown centered in a black screen at the end of a campaign. Defaults to &amp;quot;The End&amp;quot;.&lt;br /&gt;
* '''end_text_duration''': Delay, in milliseconds, before displaying the game credits at the end of a campaign. In other words, for how much time '''end_text''' is displayed on screen. Defaults to 3500. {{DevFeature1.15|6}} This value is capped at 5000 (5 seconds).&lt;br /&gt;
The following keys are additionally recognized in multiplayer:&lt;br /&gt;
* '''min_players''': Minimum number of players which the campaign supports. This only serves to inform users when choosing a campaign. Defaults to 2.&lt;br /&gt;
* '''max_players''': Maximum number of players which the campaign supports. This only serves to inform users when choosing a campaign. Defaults to either '''min_players''' or 2, whichever is higher.&lt;br /&gt;
* '''allow_era_choice''': Whether to allow era selection (when set to ''yes'') or hide it and use a default one when creating a game (when set to ''no''). Defaults to ''yes''.&lt;br /&gt;
* '''require_campaign''': Whether clients are required to have this campaign installed beforehand to be allowed join a game using this campaign. Possible values 'yes' (the default) and 'no'.&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&lt;br /&gt;
* [[PreprocessorRef]]&lt;br /&gt;
* [[ScenarioWML]]&lt;br /&gt;
* [[ReferenceWML]]&lt;br /&gt;
* [[PblWML]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category: WML Reference]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=WesnothTranslations&amp;diff=75498</id>
		<title>WesnothTranslations</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=WesnothTranslations&amp;diff=75498"/>
		<updated>2026-07-05T06:35:42Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==  Translations  ==&lt;br /&gt;
&lt;br /&gt;
Wesnoth is currently being translated into the following languages. These languages are active and have a maintainer.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Translation !! Maintainer !! Contact&lt;br /&gt;
|-&lt;br /&gt;
| [[AncientGreekTranslation|Ancient Greek]] || Mejri Ziad (Hermestrismi)|| [mailto:beja.comuneATgmailDOTcom]&lt;br /&gt;
|-&lt;br /&gt;
| [[BengaliTranslation|Bengali]] || Subhraman Sarkar (LumiousE) || lumious_e on Discord&lt;br /&gt;
|-&lt;br /&gt;
| [[CatalanTranslation|Catalan]] || Arnau Vàzquez Palma || [mailto:arnauvpATmurenaDOTio]&lt;br /&gt;
|-&lt;br /&gt;
| [[ChineseTranslation|Chinese]] || CloudiDust || [mailto:cloudidustATgmailDOTcom]&lt;br /&gt;
|-&lt;br /&gt;
| [[CzechTranslation|Czech]] || Michal Žejdl || [mailto:lachimATemerDOTcz]&lt;br /&gt;
|-&lt;br /&gt;
| [[EnglishGBTranslation|English (GB)]] || Wedge009 || [mailto:wedge009ATwedge009DOTnet]&lt;br /&gt;
|-&lt;br /&gt;
| [[FinnishTranslation|Finnish]] || Jaakko Saarikko (styxnix) || [mailto:jaakkoDOTsaarikkoATprotonmailDOTcom]&lt;br /&gt;
|-&lt;br /&gt;
| [[FrenchTranslation|French]] || demario || [mailto:wesnothfr-request@lists.tuxfamily.org?subject=subscribe mailing list]&lt;br /&gt;
|-&lt;br /&gt;
| [[GalicianTranslation|Galician]] || Adrian Chaves || [mailto:adrianATchavesDOTio]&lt;br /&gt;
|-&lt;br /&gt;
| [[GermanTranslation|German]] || Aaron Winter (Bitron) ||&lt;br /&gt;
|-&lt;br /&gt;
| [[GreekTranslation|Greek]] || Spiros Ioannou || [mailto:sivannATgmailDOTcom]&lt;br /&gt;
|-&lt;br /&gt;
| [[HungarianTranslation|Hungarian]] || Berda Jenő (bigbilly) || [mailto:big4billyATgmailDOTcom]&lt;br /&gt;
|-&lt;br /&gt;
| [[ItalianTranslation|Italian]] || Antonio Rosella || [mailto:arosellaATyahooDOTcom]&lt;br /&gt;
|-&lt;br /&gt;
| [[PolishTranslation|Polish]] || ForPeace || [https://forums.wesnoth.org/viewtopic.php?f=7&amp;amp;t=3796 forum thread]&lt;br /&gt;
|-&lt;br /&gt;
| [[PortugueseTranslation|Portuguese Brazilian]] || Andrei Machado || [mailto:andreisp.machadoATyahooDOTcom]&lt;br /&gt;
|-&lt;br /&gt;
| [[RussianTranslation|Russian]] || [[User:Alexinor|Alexinor]] || [mailto:obolonkinATprotonDOTme]&lt;br /&gt;
|-&lt;br /&gt;
| [[SerbianTranslation|Serbian]] || Vlastimir Krstonosic || [mailto:krstonosicvlastimirATgmailDOTcom]&lt;br /&gt;
|-&lt;br /&gt;
| [[SpanishTranslation|Spanish]] || Sebastián Lecchini (Sebas38760) || [mailto:sebas38760ATgmailDOTcom]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
These languages have a maintainer, but have not made any commits for over a year (as of summer 2026).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Translation !! Maintainer !! Contact&lt;br /&gt;
|-&lt;br /&gt;
| [[ArabicTranslation|Arabic]] || Mejri Ziad (Hermestrismi)|| [mailto:beja.comuneATgmailDOTcom]&lt;br /&gt;
|-&lt;br /&gt;
| [[BulgarianTranslation|Bulgarian]] || Ivan Petrov (TheWhiteKnight) || [mailto:vankata_petrovATabvDOTbg]&lt;br /&gt;
|-&lt;br /&gt;
| [[ChineseTaiwanTranslation|Chinese (Taiwan)]] || 楊綮銘 (Taiwan) || [mailto:steven2880ATgmailDOTcom]&lt;br /&gt;
|-&lt;br /&gt;
| [[DutchTranslation|Dutch]] || Merijn de Vet || [mailto:merijndevetAThotmailDOTcom]&lt;br /&gt;
|-&lt;br /&gt;
| [[EnglishShawTranslation|English (Shaw)]] || Arc Riley || [mailto:ArcRileyATubuntuDOTcom]&lt;br /&gt;
|-&lt;br /&gt;
| [[Esperanto_translation|Esperanto]] || Mariano Street (mctpyt) || [mailto:mctpytATprotonDOTme]&lt;br /&gt;
|-&lt;br /&gt;
| [[IndonesianTranslation|Indonesian]] || Irsyad Musthafa || [mailto:sevennightmareATtutanotaDOTde]&lt;br /&gt;
|-&lt;br /&gt;
| [[JapaneseTranslation|Japanese]] || Hironori Fujimoto (RatArmy) || [mailto:broadbarredfirefishATgmailDOTcom]&lt;br /&gt;
|-&lt;br /&gt;
| [[KoreanTranslation|Korean]] || mistzone || [mailto:drier22ATgmailDOTcom]&lt;br /&gt;
|-&lt;br /&gt;
| [[LatinTranslation|Latin]] || Daniel Faustmann || [mailto:donnerstag.freitag213@gmail.com]&lt;br /&gt;
|-&lt;br /&gt;
| [[NorwegianTranslation|Norwegian]] || Bloodaxe || [mailto:bloodaxenor@protonmail.com]&lt;br /&gt;
|-&lt;br /&gt;
| [[PortugueseContinentalTranslation|Portuguese (European)]] || trewe || [mailto:sjrs456ATyahooDOTfr]&lt;br /&gt;
|-&lt;br /&gt;
| [[Scottish_Gaelic_Translation|Scottish Gaelic]] || GunChleoc || [mailto:fiosAIGforamnagaidhligDOTnet]&lt;br /&gt;
|-&lt;br /&gt;
| [[SlovakTranslation#Preklad|Slovak]] || Stanislav Hoferek || [mailto:shoferek@gmail.com]&lt;br /&gt;
|-&lt;br /&gt;
| [[SwedishTranslation|Swedish]] || Alex Alowersson (fluxbird) || [mailto:alexalowersonATgmailDOTcom]&lt;br /&gt;
|-&lt;br /&gt;
| [[TurkishTranslation|Turkish]] || Nilgün Belma Bugüner || [mailto:nilgunATbelgelerDOTorg]&lt;br /&gt;
|-&lt;br /&gt;
| [[UkrainianTranslation|Ukrainian]] || Oleksii Okhrimenko (lexa04) || Discord: amakri Email: [mailto:ohrimaleATgmailDOTcom]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Currently inactive translations. If you wish to improve some of these languages (or one of the above maintainers is not responsive), contact Ivanovic on the [https://wiki.wesnoth.org/Support Wesnoth Discord channel].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Translation !! Maintainer !! Contact&lt;br /&gt;
|-&lt;br /&gt;
| [[AfrikaansTranslation|Afrikaans]] || None || N/A&lt;br /&gt;
|-&lt;br /&gt;
| [[BasqueTranslation|Basque]] || None || N/A&lt;br /&gt;
|-&lt;br /&gt;
| [[BurmeseTranslation|Burmese]] || None || N/A&lt;br /&gt;
|-&lt;br /&gt;
| [[CroatianTranslation|Croatian]] || None || N/A&lt;br /&gt;
|-&lt;br /&gt;
| [[DanishTranslation|Danish]] || None || N/A&lt;br /&gt;
|-&lt;br /&gt;
| [[HebrewTranslation|Hebrew]] || None || N/A&lt;br /&gt;
|-&lt;br /&gt;
| [[IcelandicTranslation|Icelandic]] || None || N/A&lt;br /&gt;
|-&lt;br /&gt;
| [[IrishTranslation|Irish]] || None || N/A&lt;br /&gt;
|-&lt;br /&gt;
| [[LatvianTranslation|Latvian]] || None || N/A&lt;br /&gt;
|-&lt;br /&gt;
| [[LithuanianTranslation|Lithuanian]] || None || N/A&lt;br /&gt;
|-&lt;br /&gt;
| [[MarathiTranslation|Marathi]] || None || N/A&lt;br /&gt;
|-&lt;br /&gt;
| [[MacedonianTranslation|Macedonian]] || None || N/A&lt;br /&gt;
|-&lt;br /&gt;
| [[OldEnglishTranslation|Old English]] || None || N/A&lt;br /&gt;
|-&lt;br /&gt;
| [[RACVTranslation|RACV]] || None || N/A&lt;br /&gt;
|-&lt;br /&gt;
| [[RomanianTranslation|Romanian]] || None || N/A&lt;br /&gt;
|-&lt;br /&gt;
| [[SlovenianTranslation|Slovenian]] || None || N/A&lt;br /&gt;
|-&lt;br /&gt;
| [[SpanishLatinAmericanTranslation|Spanish (Latin American)]] || None || N/A&lt;br /&gt;
|-&lt;br /&gt;
| [[ValencianTranslation|Valencian]] || None || N/A&lt;br /&gt;
|-&lt;br /&gt;
| [[VietnameseTranslation|Vietnamese]] || None || N/A&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Mailing List ==&lt;br /&gt;
&lt;br /&gt;
There now is a mailing list dedicated to translation matters. It is mainly intended to be used for informing translation maintainers about important changes, to announce string freezes and other special things. Everyone is free to subscribe to this list.&lt;br /&gt;
&lt;br /&gt;
* [https://groups.io/g/wesnoth-translations From February 2025]&lt;br /&gt;
* [https://listengine.tuxfamily.org/wesnoth.org/i18n/ List info, how to subscribe, and archives from April 2022]&lt;br /&gt;
* [https://mailman.wesnoth.org/pipermail/i18n/ Old list archives (up until April 2022)]&lt;br /&gt;
&lt;br /&gt;
Please keep in mind that this list is not meant for discussing changes for one single translation but instead subjects which are relevant to all translations.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[WesnothTranslationsHowTo]]&lt;br /&gt;
* [[ImageLocalization]]&lt;br /&gt;
* [[GetText]]&lt;br /&gt;
* [http://gettext.wesnoth.org Translations statistics (stable)]&lt;br /&gt;
* [http://gettext.wesnoth.org/index.php?version=trunk&amp;amp;package=alloff Translations statistics (development)]&lt;br /&gt;
* [[GettextForTranslators#For_add-ons|Translating User Made Campaigns and add-ons]]&lt;br /&gt;
* [[SpellingMistakes]]&lt;br /&gt;
* [[CharactersStorys| Character descriptions for Translators (Spoiler Warning)]]&lt;br /&gt;
* [[Poetry of Wesnoth Translations]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Translations|*]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Maintenance_tools&amp;diff=75484</id>
		<title>Maintenance tools</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Maintenance_tools&amp;diff=75484"/>
		<updated>2026-07-03T16:36:50Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* wmllint */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;floatright&amp;quot;&amp;gt; __TOC__ &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Wesnoth source code distribution includes a couple of tools intended to help authors maintain campaigns, faction &amp;amp; unit packs, and other WML resources. These&lt;br /&gt;
are:&lt;br /&gt;
 &lt;br /&gt;
; wmlscope: a cross-reference lister, useful for finding unresolved macro and resource-file references.&lt;br /&gt;
&lt;br /&gt;
; wmllint: a utility for sanity-checking WML syntax and porting your old WML to the current version of WML.  &lt;br /&gt;
&lt;br /&gt;
; wmlindent: a utility for reindenting WML to a uniform style.&lt;br /&gt;
&lt;br /&gt;
; GUI.pyw: a graphical interface&lt;br /&gt;
&lt;br /&gt;
== General Information ==&lt;br /&gt;
&lt;br /&gt;
You will need a Python 3 interpreter on your system to use these tools.  Linux, *BSD, and Mac OS/X should already have Python 3 installed; for Windows it's a free download&lt;br /&gt;
from http://www.python.org.  You will also need to know how to run command-line tools on your system.&lt;br /&gt;
&lt;br /&gt;
If you're working with Debian or Ubuntu you might have to install the package wesnoth-1.16-tools (or the convenient version).&lt;br /&gt;
 sudo apt install wesnoth-1.16-tools&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
All three tools will require you to supply a &amp;lt;i&amp;gt;directory list&amp;lt;/i&amp;gt;.  This is a set of directories containing the WML files you want to work on.&lt;br /&gt;
&lt;br /&gt;
This page is intended as documentation for users.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Note to Windows Users:&amp;lt;/u&amp;gt; This means you have to run it from the '''Command Line'''. The command line may be reached by hitting Start, then Run, then &amp;quot;cmd&amp;quot; or &amp;quot;command&amp;quot; depending on your version of Windows.&lt;br /&gt;
&lt;br /&gt;
Example uses:&lt;br /&gt;
 python wmllint path\to\files&lt;br /&gt;
 python wmlindent path\to\files&lt;br /&gt;
&lt;br /&gt;
Another example:&lt;br /&gt;
 &amp;quot;C:\Program Files\Python3.7\python.exe&amp;quot; data\tools\wmllint --dryrun data\core data\{multiplayer,themes} data\campaigns &lt;br /&gt;
(You have to specify the full directory path to the executable if you don't have your environment variables set up correctly).&lt;br /&gt;
The first thing you type is the path to your python executable, followed by a space. The second thing you type is the path to the desired script to run, followed by a space. The third thing you type is the path to the folder (or file) to be processed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''A convenient way of running wmllint''' on Linux (Debian or Ubuntu) and Windows in comparison, '''Linux''':&lt;br /&gt;
&lt;br /&gt;
Assuming we're working with wesnoth 1.16 or more advanced versions.&lt;br /&gt;
 python3 /usr/share/games/wesnoth/1.16/data/tools/wmllint --dryrun /usr/share/games/wesnoth/1.16/data/core ~/.local/share/wesnoth/1.16/data/add-ons/A_Simple_Campaign 1&amp;gt;wmllint-run.log 2&amp;gt;wmllint-err.log&lt;br /&gt;
I have these commands inside of a file named&lt;br /&gt;
 wmllint_dryrun_ASC.sh&lt;br /&gt;
and execute it by opening a shell (=terminal, console, command window, bash,...), navigating into the directory with that file and typing&lt;br /&gt;
 bash wmllint_dryrun_ASC.sh&lt;br /&gt;
The python3 command should be automatically known on Debian. The path to the script tells the python interpreter what to execute. --dryrun: A wmllint option, see below. The path to the core files is needed to let wmllint know about e.g. defined core units, followed by the path to the add-on that shall be checked; the last two commands cause the result of the wmllint usage to be written into those files in the same directory as the script.&lt;br /&gt;
'''Windows''', this is logically exactly the same as the Linux shell script above, so if you are on a Mac you can probably conclude how you need to adapt the paths:&lt;br /&gt;
 E:\Python37\python.exe E:\Programme\Wesnoth_1.16_git\data\tools\wmllint --dryrun E:\Programme\Wesnoth_1.16_git\data\core E:\Programme\Wesnoth_1.16_git\userdata\data\add-ons\A_Simple_Campaign 1&amp;gt;wmllint-run.log 2&amp;gt;wmllint-err.log&lt;br /&gt;
This is the content of a .txt file, whose extension I rename to .bat and double-click onto it. Opening a command window is not needed this way.&lt;br /&gt;
Since Python isn't natively installed on windows and I don't have environment variables set, the full path to python.exe is given. If your directories contain spaces it may help to include the path in quotes:&lt;br /&gt;
 &amp;quot;C:\Programs\Battle for Wesnoth 1.16\data\tools\wmllint&amp;quot;&lt;br /&gt;
Remember that you do not need to enter all of the commands/paths at once. If it doesn't work, start with only &amp;quot;python&amp;quot; or &amp;quot;C:\Python37\python.exe&amp;quot; or the like and interpret the error messages that you get. If you get an &amp;quot;unknown command&amp;quot;, python isn't installed or environment variables aren't set correctly. After that, you can add the later commands one by one.&lt;br /&gt;
&lt;br /&gt;
== wmlscope ==&lt;br /&gt;
&lt;br /&gt;
The main use for &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; is to find WML macro references without definitions and references to resource files (sounds and images) that don't exist.  These are difficult to spot from in-game because they usually result in silence or a missing image rather than actual broken game logic (see [https://github.com/wesnoth/wesnoth/issues/5332 issue 5332] for more info).  They may happen because of typos in your WML, or because the name of a macro or the location of a resource file changed between versions of the game.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; also checks macro invocations for consistency.  It will complain&lt;br /&gt;
if a macro is called with the wrong number of arguments.  In most cases it can deduce information about the type of the literal expected to be passed to a given macro argument by looking at the name of the formal.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table class=&amp;quot;wikitable&amp;quot;&amp;gt;&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;Type&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;Meaning&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;Formals requiring this type&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;Literals of this type&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;side&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a single side number&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;SIDE, *_SIDE, SIDE[0-9]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a numeric or &amp;quot;global&amp;quot;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;numeric&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a numeric integer literal&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;SIDE, X, Y, RED, GREEN, BLUE, TURN, PROB, LAYER, TIME, *_SIDE, *NUMBER, *AMOUNT, *COST, *RADIUS, *_X, *_Y, *_INCREMENT, *_FACTOR, *_TIME, *_SIZE, DURATION&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;\-?[0-9]+&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;percentage&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a percentage&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;*PERCENTAGE&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a numeric or 0\.[0-9]+&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;position&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a single x,y coordinate&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;POSITION, *_POSITION, BASE&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;-?[0-9]+,-?[0-9]+&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;span&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a set of coordinates or coordinate ranges&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;*_SPAN&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a numeric, position or ([0-9]+\-[0-9]+,?|[0-9]+,?)+&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;alliance&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a set of side numbers&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;SIDES, *_SIDES&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a span, or the empty string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;range&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;an attack range&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;RANGE&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;quot;melee&amp;quot; or &amp;quot;ranged&amp;quot;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;alignment&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;an alignment keyword&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;ALIGN&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;quot;lawful&amp;quot; or &amp;quot;neutral&amp;quot; or &amp;quot;chaotic&amp;quot;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;types&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a set of unit types&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;TYPES&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a shortname, name, or anything that contains spaces and matches no other type&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;terrain_pattern&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a set of terrain codes to filter&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;ADJACENT*, TERRAINLIST*, *TERRAIN_PATTERN, RESTRICTING&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a terrain_code or name&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;terrain_code&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a single terrain code, perhaps with overlay&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;TERRAIN*, *TERRAIN&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a shortname or (\*|[A-Z][a-z]+)\^([A-Z][a-z\\|/]+\Z)?&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;shortname&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a terrain code or a short, capitalized variable name&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[A-Z][a-z][a-z]?&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;name&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a name or identifier&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;NAME, VAR, IMAGESTEM, ID, FLAG, *_NAME, *_ID, NAMESPACE, BUILDER, *_VAR&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;anything without spaces that matches no other type&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;optional_string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a string value (may be empty)&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;ID_STRING, NAME_STRING, DESCRIPTION, IPF&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a string, or the empty string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a nonempty string not matching any of the preceding types&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;STRING, TYPE, TEXT, *_STRING, *_TYPE, *_TEXT&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a shortname, a name, a stringliteral, or anything that contains spaces and matches no other type&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;stringliteral&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a string in doublequotes or a translated string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;quot;.*&amp;quot; or _.* but not _[a-z].*&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;image&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;an image path, perhaps with [[ImagePathFunctionWML|image path functions]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;*IMAGE, PROFILE&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[A-Za-z0-9{}.][A-Za-z0-9_/+{}.-]*\.(png|jpg)(?=(~.*)?)&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;sound&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a music or sound filename&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;MUSIC, SOUND&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;string ending with &amp;quot;.wav&amp;quot; or &amp;quot;.ogg&amp;quot;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;filter&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[FilterWML|WML filter]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;FILTER&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;any non-quoted string containing &amp;quot;=&amp;quot;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;WML&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;arbitrary WML fragment&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;WML, *_WML&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;any non-quoted string containing &amp;quot;=&amp;quot;, or the empty string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;affix&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a prefix, suffix, or infix for a variable name&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;AFFIX, *AFFIX, POSTFIX, ROTATION&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a shortname or name, or the empty string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;any&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;anything&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;*VALUE, [ARS][0-9]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;anything&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the actual argument is a macro call {.*}, then it matches any formal.  Otherwise, if the formal has an identifiable type, &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; will complain if the actual literal does not match it.&lt;br /&gt;
&lt;br /&gt;
The argument type check only works in macro calls that fit on a single line.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; has many options for changing the reports it generates; the more advanced ones are intended for Wesnoth developers.  Invocations for the most commonly useful reports it generates are included in &amp;lt;i&amp;gt;data/tools/Makefile&amp;lt;/i&amp;gt; of the source distribution. Here are some of those reports:&lt;br /&gt;
&lt;br /&gt;
; make unresolved: Report on unresolved macro calls and resource references; also report macro argument-type mismatches.  (This is what you are most likely to want to do). &lt;br /&gt;
&lt;br /&gt;
; make all: Report all macro and resource file references, not just unresolved ones.&lt;br /&gt;
&lt;br /&gt;
; make collisions: Report on duplicate resource files.&lt;br /&gt;
&lt;br /&gt;
For more advanced users, or those who want to understand what the canned Makefile invocations are doing, here is a summary of &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt;'s options. Some of the more advanced options will require you to understand &lt;br /&gt;
[http://docs.python.org/lib/re-syntax.html Python regular expressions].&lt;br /&gt;
&lt;br /&gt;
; -h, --help:                 Emit a help message and quit&lt;br /&gt;
; -c, --crossreference:       Report resolved macro references (implies &amp;lt;tt&amp;gt;-w 1&amp;lt;/tt&amp;gt;)&lt;br /&gt;
; -C, --collisions:           Report duplicate resource files   &lt;br /&gt;
; -d, --deflist:              Make definition list.  (This one is for campaign server maintainers.)&lt;br /&gt;
; -e &amp;lt;i&amp;gt;regexp&amp;lt;/i&amp;gt;, --exclude &amp;lt;i&amp;gt;regexp&amp;lt;/i&amp;gt;:   Ignore files matching the specified regular expression. &lt;br /&gt;
; -f &amp;lt;i&amp;gt;dir&amp;lt;/i&amp;gt;, --from &amp;lt;i&amp;gt;dir&amp;lt;/i&amp;gt;:         Report only on macros defined under &amp;lt;i&amp;gt;dir&amp;lt;/i&amp;gt;&lt;br /&gt;
; -l, --listfiles:            List files that will be processed&lt;br /&gt;
; -r &amp;lt;i&amp;gt;ddd&amp;lt;/i&amp;gt;, --refcount=&amp;lt;i&amp;gt;ddd&amp;lt;/i&amp;gt;:     Report only on macros with references in exactly &amp;lt;i&amp;gt;ddd&amp;lt;/i&amp;gt; files.&lt;br /&gt;
; -t &amp;lt;i&amp;gt;TYPELIST&amp;lt;/i&amp;gt;, --typelist &amp;lt;i&amp;gt;TYPELIST&amp;lt;/i&amp;gt;: List actual &amp;amp; formal argtypes for calls in fname&lt;br /&gt;
; -u, --unresolved:           Report unresolved macro references&lt;br /&gt;
; -w, --warnlevel:            Set to 1 to warn of duplicate macro definitions&lt;br /&gt;
; -p, --progress:             Show progress&lt;br /&gt;
; --force-used reg:           Ignore reference count 0 on names matching regexp&lt;br /&gt;
; --extracthelp:              Extract help from macro definition comments.&lt;br /&gt;
; --unchecked:                Report all macros with untyped formals.&lt;br /&gt;
; --version:                  show program's version number and exit&lt;br /&gt;
&lt;br /&gt;
These options are used with a list of directories as arguments; if none is given,&lt;br /&gt;
&amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; behaves as though the current directory had been specified as a&lt;br /&gt;
single argument.  Each directory is treated as a separate domain for&lt;br /&gt;
macro and resource visibility purposes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; recognizes two kinds of namespace, exporting and non-exporting.&lt;br /&gt;
Exporting namespaces make all their resources and macro names&lt;br /&gt;
globally visible.  You can make a namespace exporting by embedding&lt;br /&gt;
a comment like this in it:&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: export=yes&lt;br /&gt;
&lt;br /&gt;
Wesnoth core data is an exporting namespace.  Campaigns are non-exporting;&lt;br /&gt;
they should contain the declaration&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: export=no&lt;br /&gt;
&lt;br /&gt;
somewhere.  &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; will complain when it sees a namespace with no export&lt;br /&gt;
property, then treat it as non-exporting.&lt;br /&gt;
&lt;br /&gt;
You can tell &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; to ignore stretches of config files&lt;br /&gt;
with the following magic comments:&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: start ignoring&lt;br /&gt;
    # wmlscope: stop ignoring&lt;br /&gt;
&lt;br /&gt;
Similarly, you can tell &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; to ignore multiple or duplicate macro&lt;br /&gt;
definitions in a range of lines with the following magic comments:&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: start conditionals&lt;br /&gt;
    # wmlscope: stop conditionals&lt;br /&gt;
&lt;br /&gt;
The following magic comment:&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: prune FOOBAR&lt;br /&gt;
&lt;br /&gt;
will cause &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; to forget about all but one of the definitions of&lt;br /&gt;
&amp;lt;tt&amp;gt;FOOBAR&amp;lt;/tt&amp;gt; it has seen.  This will be useful mainly for symbols that have&lt;br /&gt;
different definitions enabled by an &amp;lt;tt&amp;gt;#ifdef&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Due to a preprocessor limitation, inline macros cannot contain a documentation&lt;br /&gt;
string. If you need to document these macros in the HTML macro reference, you&lt;br /&gt;
can use the following directive:&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: docstring FOOBAR&lt;br /&gt;
&lt;br /&gt;
The docstring for the FOOBAR macro will be collected until a non-comment line,&lt;br /&gt;
a &amp;lt;tt&amp;gt;#define&amp;lt;/tt&amp;gt; or another &amp;lt;tt&amp;gt;# wmlscope: docstring&amp;lt;/tt&amp;gt; are found. External&lt;br /&gt;
docstrings '''''must''''' be defined before the macro to which they refer; defining&lt;br /&gt;
two or more external docstrings keeps only the most recent one, but having both an&lt;br /&gt;
external and an internal docstring is allowed (in this case, the internal one&lt;br /&gt;
will be appended to the external one in the macro reference).&lt;br /&gt;
&lt;br /&gt;
This tool does catch one kind of implicit reference: if an attack name&lt;br /&gt;
is specified but no icon is given, the attack icon will default to&lt;br /&gt;
a name generated from the attack name.  This behavior can be suppressed&lt;br /&gt;
by adding a magic comment containing the string &amp;quot;no-icon&amp;quot; to the &amp;lt;tt&amp;gt;name=&amp;lt;/tt&amp;gt;&lt;br /&gt;
line.&lt;br /&gt;
&lt;br /&gt;
The checking done by this tool has a couple of flaws:&lt;br /&gt;
&lt;br /&gt;
(1) It doesn't actually evaluate file inclusions.  Instead, any&lt;br /&gt;
macro definition satisfies any macro call made under the same&lt;br /&gt;
directory.  Exception: when an &amp;lt;tt&amp;gt;#undef&amp;lt;/tt&amp;gt; is detected, the macro is&lt;br /&gt;
tagged local and not visible outside the span of lines where it was&lt;br /&gt;
defined.&lt;br /&gt;
&lt;br /&gt;
(2) It doesn't read &amp;lt;tt&amp;gt;[binary_path]&amp;lt;/tt&amp;gt; tags, as this would require&lt;br /&gt;
implementing a WML parser.  Instead, it assumes that a resource-file&lt;br /&gt;
reference can be satisfied by any matching image file from anywhere&lt;br /&gt;
in the same directory it came from.  The resources under the '''''first'''''&lt;br /&gt;
directory argument (only) are visible everywhere.&lt;br /&gt;
&lt;br /&gt;
(3) A reference with embedded {}s in a macro will have the macro's&lt;br /&gt;
formal args substituted in at WML evaluation time.  Instead, this&lt;br /&gt;
tool treats each {} as a .* wildcard and considers the reference to&lt;br /&gt;
match '''''every''''' resource filename that matches that pattern.&lt;br /&gt;
Under appropriate circumstances this might report a resource filename&lt;br /&gt;
statically matching the pattern as having been referenced even&lt;br /&gt;
though none of the actual macro calls would actually generate it.&lt;br /&gt;
&lt;br /&gt;
Problems (1) and (2) imply that this tool might conceivably report&lt;br /&gt;
that a reference has been satisfied when under actual&lt;br /&gt;
WML-interpreter rules it has not.&lt;br /&gt;
&lt;br /&gt;
The reporting format is compatible with GNU Emacs compile mode.&lt;br /&gt;
&lt;br /&gt;
For debugging purposes, an in-line comment of the form&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: warnlevel NNN&lt;br /&gt;
&lt;br /&gt;
sets the warning level.&lt;br /&gt;
&lt;br /&gt;
== wmllint ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; is a tool for migrating your WML to the current version.  It handles two problems: &lt;br /&gt;
&lt;br /&gt;
* Resource files and macro names may change between versions of the game. &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; knows about these changes and will tweak your WML to fit where it can.&lt;br /&gt;
&lt;br /&gt;
* Between 1.2.x and 1.3.1 the terrain-coding system used in map files underwent a major change. It changed again in a minor way between 1.3.1 and 1.3.2. If you port such old code, use &amp;lt;tt&amp;gt;wmllint-1.4&amp;lt;/tt&amp;gt;, which is located in the same directory as &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;. It will translate your maps for you, unless you use custom terrains in which case you will have to do it by hand.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; also performs various sanity-checking operations, reporting:&lt;br /&gt;
&lt;br /&gt;
* unbalanced tags&lt;br /&gt;
* strings that need a translation mark and do not have them&lt;br /&gt;
* strings that have a translation mark and should not&lt;br /&gt;
* translatable strings containing macro references &lt;br /&gt;
* filter references by description= (id= in 1.5) not matched by an actual unit&lt;br /&gt;
* abilities or traits without matching special notes, or vice-versa&lt;br /&gt;
* consistency between recruit= and recruitment_pattern= instances&lt;br /&gt;
* double space after punctuation in translatable strings.&lt;br /&gt;
* unknown races or movement types in units&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; takes a directory-path argument specifying the WML directories to work on.  It will modify any cfg and map files under those directories that need to be changed.  Here is a summary of its options:&lt;br /&gt;
&lt;br /&gt;
; -h, --help:                 Emit a help message and quit.&lt;br /&gt;
; -c, --clean:                Clean up -bak files.&lt;br /&gt;
; -D, --diffs:                Display diffs between converted and unconverted files.&lt;br /&gt;
; -d, --dryrun:               List changes (-v) but don't perform them.&lt;br /&gt;
; -r, --revert:               Revert the conversion from the -bak files.&lt;br /&gt;
; -m, --missing:              Warn about tags without side= keys now applying to all sides.&lt;br /&gt;
; -s, --stripcr:              Convert DOS-style CR/LF to Unix-style LF.&lt;br /&gt;
; -v, --verbose:              Set verbosity; more details below.&lt;br /&gt;
; -K, --known:                Suppress check for unknown unit types, recruits, races, scenarios, etc.&lt;br /&gt;
; --version:                  show program's version number and exit&lt;br /&gt;
; --config:                   allows specifying directories to include ('''include_dirs'''), directories to exclude ('''ignore_directories'''), and files to exclude ('''ignore_files''').&lt;br /&gt;
&lt;br /&gt;
The verbosity option works like this:&lt;br /&gt;
&lt;br /&gt;
; -v:          lists changes.&lt;br /&gt;
; -v -v:       warns of maps already converted.&lt;br /&gt;
; -v -v -v:    names each file before it's processed.&lt;br /&gt;
; -v -v -v -v: shows verbose parse details (developers only).&lt;br /&gt;
&lt;br /&gt;
The recommended procedure is this:&lt;br /&gt;
&lt;br /&gt;
# Run it with --dryrun first to see what it will do.&lt;br /&gt;
# If the messages look good, run without --dryrun; the old content will be left in backup files with a -bak extension.&lt;br /&gt;
# Eyeball the changes with the --diff option.&lt;br /&gt;
# Use wmlscope, with a directory path including the Wesnoth mainline WML, to check that you have no unresolved references.&lt;br /&gt;
# Test the conversion.&lt;br /&gt;
# Use either --clean to remove the -bak files or --revert to undo the conversion.&lt;br /&gt;
&lt;br /&gt;
wmllint supports a number of magic comments to customize its behaviour and avoid false positives. Almost all magic wmllint comments begin with the string &amp;lt;tt&amp;gt;wmllint:&amp;lt;/tt&amp;gt;, followed by some additional keyword and potentially some arguments. These are all documented at the top of the wmllint tool itself in the MAGIC COMMENTS section: https://github.com/wesnoth/wesnoth/blob/master/data/tools/wmllint&lt;br /&gt;
&lt;br /&gt;
=== Explanations of &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; diagnostics ===&lt;br /&gt;
Some &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; diagnostics may require further explanation for UMC authors to understand; this section will be for providing such explanations, and descriptions of how to solve and/or silence them.&lt;br /&gt;
&lt;br /&gt;
In these, &amp;lt;code&amp;gt;%s&amp;lt;/code&amp;gt; will be replaced by a string. When the recommended solution also includes a &amp;lt;code&amp;gt;%s&amp;lt;/code&amp;gt;, it's a suggestion to copy the string from the error message into your code.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;nonstandard word-wrap style within message&amp;lt;/code&amp;gt;: This message meant there was an unexpected newline character within a &amp;lt;code&amp;gt;[message]&amp;lt;/code&amp;gt; tag. However, this check was removed in the 1.15.10 release. Pre-1.15.10, add-on developers could silence by putting a &amp;lt;code&amp;gt;# wmllint: display on&amp;lt;/code&amp;gt; comment before the string and a &amp;lt;code&amp;gt;# wmllint: display off&amp;lt;/code&amp;gt; comment after the string, however, post-1.15.10, this is no longer necessary.&lt;br /&gt;
* &amp;lt;code&amp;gt;%s is not a known unit type&amp;lt;/code&amp;gt; (in cases where you'd think the unit type ''would'' be known): This means the unit has a type that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can load the mainline unit types. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;unknown speaker '%s' of [message]&amp;lt;/code&amp;gt;: use a &amp;lt;code&amp;gt;# wmllint: recognize %s&amp;lt;/code&amp;gt; magic comment, or, alternatively, if the speaker is created by a macro, use a magic comment of the form of either &amp;lt;code&amp;gt;# wmllint: who MACRO is SPEAKER&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;# wmllint: whofield MACRO NUMBER&amp;lt;/code&amp;gt;, depending on whether the macro takes an argument for the unit's name or not.&lt;br /&gt;
* &amp;lt;code&amp;gt;unknown '%s' referred to by id&amp;lt;/code&amp;gt;: use a &amp;lt;code&amp;gt;# wmllint: recognize %s&amp;lt;/code&amp;gt; magic comment, or, alternatively, if the unit is created by a macro, use a magic comment of the form of either &amp;lt;code&amp;gt;# wmllint: who MACRO is UNIT&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;# wmllint: whofield MACRO NUMBER&amp;lt;/code&amp;gt;, depending on whether the macro takes an argument for the unit's name or not.&lt;br /&gt;
* &amp;lt;code&amp;gt;%s has unknown advancements&amp;lt;/code&amp;gt; (in cases where you'd think the advancement ''would'' be known): This means the unit has an advancement that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can load mainline units for checking advancements. Note that it's also possible that you just made typo, too, so be sure to check your spelling. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;.description may need hand fixup&amp;lt;/code&amp;gt;: This one comes from mucking around with the &amp;lt;code&amp;gt;.description&amp;lt;/code&amp;gt; field of unit data manually in a hackish fashion. There isn't really much of a way to work around it, besides just the &amp;quot;don't do that&amp;quot; solution.&lt;br /&gt;
* &amp;lt;code&amp;gt;tag stack nonempty (%s) at end of file.&amp;lt;/code&amp;gt;: This means that you have unbalanced tags somewhere in the file, e.g. an opener without a closer, or vice versa. This can often be seen when defining macros for unit abilities. A way to fix this warning is to wrap the section with unbalanced tags with a &amp;lt;code&amp;gt;# wmllint: unbalanced-on&amp;lt;/code&amp;gt; magic comment beforehand and a &amp;lt;code&amp;gt;# wmllint: unbalanced-off&amp;lt;/code&amp;gt; magic comment afterwards. As having unbalanced tags will also cause issues for other WML maintenance tools, such as &amp;lt;tt&amp;gt;wmlindent&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;wmlxgettext&amp;lt;/tt&amp;gt;, you may also want to add separate magic comments for each of them (see their documentation for the form they take).&lt;br /&gt;
* &amp;lt;code&amp;gt;unit declaration without side attribute&amp;lt;/code&amp;gt;: the default side for a unit declaration when left implicit is side 1. Specify your unit sides explicitly to solve this.&lt;br /&gt;
* &amp;lt;code&amp;gt;no %s units recruitable at difficulty %s&amp;lt;/code&amp;gt; (even when there are such units recruitable): This diagnostic has to do with matching the &amp;lt;code&amp;gt;usage&amp;lt;/code&amp;gt; key of units recruitable by an AI side with their &amp;lt;code&amp;gt;recruitment_pattern&amp;lt;/code&amp;gt;. It means the unit has a usage that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can know which mainline units are recruitable. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;%s has unknown movement type&amp;lt;/code&amp;gt; (even when you'd think that that movement type ''would'' actually be known): This means the unit has a movetype that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can load the mainline movement types. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;%s has unknown race&amp;lt;/code&amp;gt; (even when you'd think that that race ''would'' actually be known): This means the unit has a race that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can load the mainline races. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;derivation of %s from %s does not resolve&amp;lt;/code&amp;gt; (even when you'd think it would): This means that a unit using the [[UnitTypeWML#Other_tags|[base_unit]]] tag specifies a unit ID in that tag that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can load the core units for its derivation checks. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;[advancefrom] needs to be manually updated to [modify_unit_type] and moved into the _main.cfg file&amp;lt;/code&amp;gt;: This one is pretty self-explanatory: [[UnitTypeWML#Unit_Type|[advancefrom]]] was deprecated in [https://github.com/wesnoth/wesnoth/commit/3950f40f3f0483032bc70b3e57166bd355acd9fc commit 3950f40] due to [https://github.com/wesnoth/wesnoth/issues/3955 issue #3955], and in fact doesn't even work anymore (in 1.16) as per [https://github.com/wesnoth/wesnoth/issues/6204 issue #6204]. The main reason &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; can't fix this automatically is because it could end up being too complicated for it to figure out which files to edit if there are multiple uses of &amp;lt;code&amp;gt;[advancefrom]&amp;lt;/code&amp;gt;, and it also doesn't want to assume where to put the [[ModificationWML|[modify_unit_type]]] tag in &amp;lt;tt&amp;gt;_main.cfg&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== wmlindent ==&lt;br /&gt;
&lt;br /&gt;
Call with no arguments to filter WML on standard input to reindented WML on&lt;br /&gt;
standard output.  If arguments are specified, they are taken to be files to be&lt;br /&gt;
re-indented in place; a directory name causes reindenting on all WML&lt;br /&gt;
beneath it.&lt;br /&gt;
&lt;br /&gt;
The indent unit is four spaces.  Absence of an option to change this is&lt;br /&gt;
deliberate; the purpose of this tool is to ''prevent'' style wars, not encourage&lt;br /&gt;
them.&lt;br /&gt;
&lt;br /&gt;
On non-empty lines, this code never modifies anything but leading and&lt;br /&gt;
trailing whitespace. Leading whitespace will be regularized to the&lt;br /&gt;
current indent; trailing whitespace will be stripped.  After processing&lt;br /&gt;
all lines will end with a Unix-style &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt; end-of-line marker.&lt;br /&gt;
&lt;br /&gt;
Runs of entirely blank lines will be reduced to one blank line, except&lt;br /&gt;
in two cases where they will be discarded: (a) before WML closing&lt;br /&gt;
tags, and (b) after WML opening tags.&lt;br /&gt;
&lt;br /&gt;
It is possible to wrap a section of lines in special comments so that&lt;br /&gt;
&amp;lt;tt&amp;gt;wmlindent&amp;lt;/tt&amp;gt; will ignore them.  You may need to do this for unbalanced&lt;br /&gt;
macros (it's better, though, to get rid of those where possible).&lt;br /&gt;
Use '&amp;lt;code&amp;gt;wmlindent: {start,stop} ignoring&amp;lt;/code&amp;gt;' anywhere in a comment.&lt;br /&gt;
&lt;br /&gt;
It is also possible to declare custom openers an closers, e.g for macros&lt;br /&gt;
that are actually control constructs.  To do this, use declarations&lt;br /&gt;
&lt;br /&gt;
    # wmlindent: opener &amp;quot;{EXCEPTIONAL_OPENER &amp;quot;&lt;br /&gt;
    # wmlindent: closer &amp;quot;{EXCEPTIONAL_CLOSER &amp;quot;&lt;br /&gt;
&lt;br /&gt;
The lines after an opener will be indented an extra level; a closer&lt;br /&gt;
and lines following will be indented one level less. Note that these&lt;br /&gt;
declare prefixes; any prefix match to the non-whitespace text of a line&lt;br /&gt;
will be recognized.&lt;br /&gt;
&lt;br /&gt;
The public utility macros &amp;quot;&amp;lt;code&amp;gt;{FOREACH&amp;lt;/code&amp;gt;&amp;quot; and &amp;quot;&amp;lt;code&amp;gt;{NEXT&amp;lt;/code&amp;gt;&amp;quot; come as wired-in exceptions,&lt;br /&gt;
because it is not guaranteed that their indent declarations will be processed&lt;br /&gt;
before the macro library is reached.&lt;br /&gt;
&lt;br /&gt;
Interrupting &amp;lt;tt&amp;gt;wmlindent&amp;lt;/tt&amp;gt; ought to be safe, as each reindenting will be done to a copy&lt;br /&gt;
that is atomically renamed when it's done.  If the output file is identical&lt;br /&gt;
to the input, the output file will simply be deleted, so the timestamp&lt;br /&gt;
on the input file won't be touched.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;--dryrun&amp;lt;/tt&amp;gt; option detects and reports files that would be changed&lt;br /&gt;
without changing them. The &amp;lt;tt&amp;gt;--verbose&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;-v&amp;lt;/tt&amp;gt; option enables reporting&lt;br /&gt;
of files that are (or would be, under &amp;lt;tt&amp;gt;--dryrun&amp;lt;/tt&amp;gt;) changed.  With &amp;lt;tt&amp;gt;-v -v&amp;lt;/tt&amp;gt;,&lt;br /&gt;
unchanged files are also reported.  The &amp;lt;tt&amp;gt;--exclude&amp;lt;/tt&amp;gt; option takes a regexp&lt;br /&gt;
and excludes files matching it.&lt;br /&gt;
&lt;br /&gt;
If you don't apply this tool to your own WML that you wish to submit, the&lt;br /&gt;
mainline-campaign maintainers will do it when and if your code is accepted into the tree.&lt;br /&gt;
&lt;br /&gt;
Note: This tool does not include a parser.  It will produce bad results on WML&lt;br /&gt;
that is syntactically unbalanced.  Unbalanced double quotes that aren't part&lt;br /&gt;
of a multiline literal will also confuse it.  You will receive warnings&lt;br /&gt;
if there's an indent open at end of file or if a closer occurs with&lt;br /&gt;
indent already zero; these two conditions strongly suggest unbalanced WML.&lt;br /&gt;
&lt;br /&gt;
== GUI.pyw ==&lt;br /&gt;
&lt;br /&gt;
Starting from version 1.11.15 and 1.13.0, a GUI (written in Tkinter, plus the themed widgets ttk) is available in the same directory as the other tools. To use it, you need to have a version of Python equal to or greater than 3.1.0 (the 3.0.x series doesn't include the ttk widgets, and as such is unsuitable for this script).&lt;br /&gt;
&lt;br /&gt;
If you're on Linux, be sure to have installed the ''python3-tk'' module, '''or the application won't run at all'''. To install it in a Debian-based distro (like Ubuntu), type this line in a Terminal:&lt;br /&gt;
 sudo apt install python3-tk&lt;br /&gt;
&lt;br /&gt;
To start it, just double click on the GUI.pyw file. The interface is pretty much self-explanatory, and allows you to run wmllint, wmlscope, wmlindent and wmlxgettext, modify their options, select an add-on and save the tools' output as a text file.&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
* [[Translation Maintenance Commands]] (for &amp;lt;tt&amp;gt;wmlxgettext&amp;lt;/tt&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
[[Category:Create]]&lt;br /&gt;
[[Category:Tools]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Maintenance_tools&amp;diff=75483</id>
		<title>Maintenance tools</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Maintenance_tools&amp;diff=75483"/>
		<updated>2026-07-03T16:36:21Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* wmllint */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;floatright&amp;quot;&amp;gt; __TOC__ &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Wesnoth source code distribution includes a couple of tools intended to help authors maintain campaigns, faction &amp;amp; unit packs, and other WML resources. These&lt;br /&gt;
are:&lt;br /&gt;
 &lt;br /&gt;
; wmlscope: a cross-reference lister, useful for finding unresolved macro and resource-file references.&lt;br /&gt;
&lt;br /&gt;
; wmllint: a utility for sanity-checking WML syntax and porting your old WML to the current version of WML.  &lt;br /&gt;
&lt;br /&gt;
; wmlindent: a utility for reindenting WML to a uniform style.&lt;br /&gt;
&lt;br /&gt;
; GUI.pyw: a graphical interface&lt;br /&gt;
&lt;br /&gt;
== General Information ==&lt;br /&gt;
&lt;br /&gt;
You will need a Python 3 interpreter on your system to use these tools.  Linux, *BSD, and Mac OS/X should already have Python 3 installed; for Windows it's a free download&lt;br /&gt;
from http://www.python.org.  You will also need to know how to run command-line tools on your system.&lt;br /&gt;
&lt;br /&gt;
If you're working with Debian or Ubuntu you might have to install the package wesnoth-1.16-tools (or the convenient version).&lt;br /&gt;
 sudo apt install wesnoth-1.16-tools&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
All three tools will require you to supply a &amp;lt;i&amp;gt;directory list&amp;lt;/i&amp;gt;.  This is a set of directories containing the WML files you want to work on.&lt;br /&gt;
&lt;br /&gt;
This page is intended as documentation for users.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Note to Windows Users:&amp;lt;/u&amp;gt; This means you have to run it from the '''Command Line'''. The command line may be reached by hitting Start, then Run, then &amp;quot;cmd&amp;quot; or &amp;quot;command&amp;quot; depending on your version of Windows.&lt;br /&gt;
&lt;br /&gt;
Example uses:&lt;br /&gt;
 python wmllint path\to\files&lt;br /&gt;
 python wmlindent path\to\files&lt;br /&gt;
&lt;br /&gt;
Another example:&lt;br /&gt;
 &amp;quot;C:\Program Files\Python3.7\python.exe&amp;quot; data\tools\wmllint --dryrun data\core data\{multiplayer,themes} data\campaigns &lt;br /&gt;
(You have to specify the full directory path to the executable if you don't have your environment variables set up correctly).&lt;br /&gt;
The first thing you type is the path to your python executable, followed by a space. The second thing you type is the path to the desired script to run, followed by a space. The third thing you type is the path to the folder (or file) to be processed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''A convenient way of running wmllint''' on Linux (Debian or Ubuntu) and Windows in comparison, '''Linux''':&lt;br /&gt;
&lt;br /&gt;
Assuming we're working with wesnoth 1.16 or more advanced versions.&lt;br /&gt;
 python3 /usr/share/games/wesnoth/1.16/data/tools/wmllint --dryrun /usr/share/games/wesnoth/1.16/data/core ~/.local/share/wesnoth/1.16/data/add-ons/A_Simple_Campaign 1&amp;gt;wmllint-run.log 2&amp;gt;wmllint-err.log&lt;br /&gt;
I have these commands inside of a file named&lt;br /&gt;
 wmllint_dryrun_ASC.sh&lt;br /&gt;
and execute it by opening a shell (=terminal, console, command window, bash,...), navigating into the directory with that file and typing&lt;br /&gt;
 bash wmllint_dryrun_ASC.sh&lt;br /&gt;
The python3 command should be automatically known on Debian. The path to the script tells the python interpreter what to execute. --dryrun: A wmllint option, see below. The path to the core files is needed to let wmllint know about e.g. defined core units, followed by the path to the add-on that shall be checked; the last two commands cause the result of the wmllint usage to be written into those files in the same directory as the script.&lt;br /&gt;
'''Windows''', this is logically exactly the same as the Linux shell script above, so if you are on a Mac you can probably conclude how you need to adapt the paths:&lt;br /&gt;
 E:\Python37\python.exe E:\Programme\Wesnoth_1.16_git\data\tools\wmllint --dryrun E:\Programme\Wesnoth_1.16_git\data\core E:\Programme\Wesnoth_1.16_git\userdata\data\add-ons\A_Simple_Campaign 1&amp;gt;wmllint-run.log 2&amp;gt;wmllint-err.log&lt;br /&gt;
This is the content of a .txt file, whose extension I rename to .bat and double-click onto it. Opening a command window is not needed this way.&lt;br /&gt;
Since Python isn't natively installed on windows and I don't have environment variables set, the full path to python.exe is given. If your directories contain spaces it may help to include the path in quotes:&lt;br /&gt;
 &amp;quot;C:\Programs\Battle for Wesnoth 1.16\data\tools\wmllint&amp;quot;&lt;br /&gt;
Remember that you do not need to enter all of the commands/paths at once. If it doesn't work, start with only &amp;quot;python&amp;quot; or &amp;quot;C:\Python37\python.exe&amp;quot; or the like and interpret the error messages that you get. If you get an &amp;quot;unknown command&amp;quot;, python isn't installed or environment variables aren't set correctly. After that, you can add the later commands one by one.&lt;br /&gt;
&lt;br /&gt;
== wmlscope ==&lt;br /&gt;
&lt;br /&gt;
The main use for &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; is to find WML macro references without definitions and references to resource files (sounds and images) that don't exist.  These are difficult to spot from in-game because they usually result in silence or a missing image rather than actual broken game logic (see [https://github.com/wesnoth/wesnoth/issues/5332 issue 5332] for more info).  They may happen because of typos in your WML, or because the name of a macro or the location of a resource file changed between versions of the game.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; also checks macro invocations for consistency.  It will complain&lt;br /&gt;
if a macro is called with the wrong number of arguments.  In most cases it can deduce information about the type of the literal expected to be passed to a given macro argument by looking at the name of the formal.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table class=&amp;quot;wikitable&amp;quot;&amp;gt;&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;Type&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;Meaning&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;Formals requiring this type&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;Literals of this type&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;side&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a single side number&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;SIDE, *_SIDE, SIDE[0-9]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a numeric or &amp;quot;global&amp;quot;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;numeric&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a numeric integer literal&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;SIDE, X, Y, RED, GREEN, BLUE, TURN, PROB, LAYER, TIME, *_SIDE, *NUMBER, *AMOUNT, *COST, *RADIUS, *_X, *_Y, *_INCREMENT, *_FACTOR, *_TIME, *_SIZE, DURATION&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;\-?[0-9]+&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;percentage&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a percentage&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;*PERCENTAGE&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a numeric or 0\.[0-9]+&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;position&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a single x,y coordinate&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;POSITION, *_POSITION, BASE&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;-?[0-9]+,-?[0-9]+&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;span&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a set of coordinates or coordinate ranges&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;*_SPAN&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a numeric, position or ([0-9]+\-[0-9]+,?|[0-9]+,?)+&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;alliance&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a set of side numbers&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;SIDES, *_SIDES&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a span, or the empty string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;range&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;an attack range&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;RANGE&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;quot;melee&amp;quot; or &amp;quot;ranged&amp;quot;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;alignment&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;an alignment keyword&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;ALIGN&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;quot;lawful&amp;quot; or &amp;quot;neutral&amp;quot; or &amp;quot;chaotic&amp;quot;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;types&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a set of unit types&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;TYPES&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a shortname, name, or anything that contains spaces and matches no other type&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;terrain_pattern&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a set of terrain codes to filter&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;ADJACENT*, TERRAINLIST*, *TERRAIN_PATTERN, RESTRICTING&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a terrain_code or name&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;terrain_code&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a single terrain code, perhaps with overlay&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;TERRAIN*, *TERRAIN&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a shortname or (\*|[A-Z][a-z]+)\^([A-Z][a-z\\|/]+\Z)?&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;shortname&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a terrain code or a short, capitalized variable name&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[A-Z][a-z][a-z]?&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;name&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a name or identifier&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;NAME, VAR, IMAGESTEM, ID, FLAG, *_NAME, *_ID, NAMESPACE, BUILDER, *_VAR&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;anything without spaces that matches no other type&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;optional_string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a string value (may be empty)&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;ID_STRING, NAME_STRING, DESCRIPTION, IPF&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a string, or the empty string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a nonempty string not matching any of the preceding types&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;STRING, TYPE, TEXT, *_STRING, *_TYPE, *_TEXT&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a shortname, a name, a stringliteral, or anything that contains spaces and matches no other type&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;stringliteral&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a string in doublequotes or a translated string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;quot;.*&amp;quot; or _.* but not _[a-z].*&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;image&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;an image path, perhaps with [[ImagePathFunctionWML|image path functions]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;*IMAGE, PROFILE&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[A-Za-z0-9{}.][A-Za-z0-9_/+{}.-]*\.(png|jpg)(?=(~.*)?)&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;sound&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a music or sound filename&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;MUSIC, SOUND&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;string ending with &amp;quot;.wav&amp;quot; or &amp;quot;.ogg&amp;quot;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;filter&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[FilterWML|WML filter]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;FILTER&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;any non-quoted string containing &amp;quot;=&amp;quot;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;WML&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;arbitrary WML fragment&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;WML, *_WML&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;any non-quoted string containing &amp;quot;=&amp;quot;, or the empty string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;affix&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a prefix, suffix, or infix for a variable name&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;AFFIX, *AFFIX, POSTFIX, ROTATION&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a shortname or name, or the empty string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;any&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;anything&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;*VALUE, [ARS][0-9]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;anything&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the actual argument is a macro call {.*}, then it matches any formal.  Otherwise, if the formal has an identifiable type, &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; will complain if the actual literal does not match it.&lt;br /&gt;
&lt;br /&gt;
The argument type check only works in macro calls that fit on a single line.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; has many options for changing the reports it generates; the more advanced ones are intended for Wesnoth developers.  Invocations for the most commonly useful reports it generates are included in &amp;lt;i&amp;gt;data/tools/Makefile&amp;lt;/i&amp;gt; of the source distribution. Here are some of those reports:&lt;br /&gt;
&lt;br /&gt;
; make unresolved: Report on unresolved macro calls and resource references; also report macro argument-type mismatches.  (This is what you are most likely to want to do). &lt;br /&gt;
&lt;br /&gt;
; make all: Report all macro and resource file references, not just unresolved ones.&lt;br /&gt;
&lt;br /&gt;
; make collisions: Report on duplicate resource files.&lt;br /&gt;
&lt;br /&gt;
For more advanced users, or those who want to understand what the canned Makefile invocations are doing, here is a summary of &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt;'s options. Some of the more advanced options will require you to understand &lt;br /&gt;
[http://docs.python.org/lib/re-syntax.html Python regular expressions].&lt;br /&gt;
&lt;br /&gt;
; -h, --help:                 Emit a help message and quit&lt;br /&gt;
; -c, --crossreference:       Report resolved macro references (implies &amp;lt;tt&amp;gt;-w 1&amp;lt;/tt&amp;gt;)&lt;br /&gt;
; -C, --collisions:           Report duplicate resource files   &lt;br /&gt;
; -d, --deflist:              Make definition list.  (This one is for campaign server maintainers.)&lt;br /&gt;
; -e &amp;lt;i&amp;gt;regexp&amp;lt;/i&amp;gt;, --exclude &amp;lt;i&amp;gt;regexp&amp;lt;/i&amp;gt;:   Ignore files matching the specified regular expression. &lt;br /&gt;
; -f &amp;lt;i&amp;gt;dir&amp;lt;/i&amp;gt;, --from &amp;lt;i&amp;gt;dir&amp;lt;/i&amp;gt;:         Report only on macros defined under &amp;lt;i&amp;gt;dir&amp;lt;/i&amp;gt;&lt;br /&gt;
; -l, --listfiles:            List files that will be processed&lt;br /&gt;
; -r &amp;lt;i&amp;gt;ddd&amp;lt;/i&amp;gt;, --refcount=&amp;lt;i&amp;gt;ddd&amp;lt;/i&amp;gt;:     Report only on macros with references in exactly &amp;lt;i&amp;gt;ddd&amp;lt;/i&amp;gt; files.&lt;br /&gt;
; -t &amp;lt;i&amp;gt;TYPELIST&amp;lt;/i&amp;gt;, --typelist &amp;lt;i&amp;gt;TYPELIST&amp;lt;/i&amp;gt;: List actual &amp;amp; formal argtypes for calls in fname&lt;br /&gt;
; -u, --unresolved:           Report unresolved macro references&lt;br /&gt;
; -w, --warnlevel:            Set to 1 to warn of duplicate macro definitions&lt;br /&gt;
; -p, --progress:             Show progress&lt;br /&gt;
; --force-used reg:           Ignore reference count 0 on names matching regexp&lt;br /&gt;
; --extracthelp:              Extract help from macro definition comments.&lt;br /&gt;
; --unchecked:                Report all macros with untyped formals.&lt;br /&gt;
; --version:                  show program's version number and exit&lt;br /&gt;
&lt;br /&gt;
These options are used with a list of directories as arguments; if none is given,&lt;br /&gt;
&amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; behaves as though the current directory had been specified as a&lt;br /&gt;
single argument.  Each directory is treated as a separate domain for&lt;br /&gt;
macro and resource visibility purposes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; recognizes two kinds of namespace, exporting and non-exporting.&lt;br /&gt;
Exporting namespaces make all their resources and macro names&lt;br /&gt;
globally visible.  You can make a namespace exporting by embedding&lt;br /&gt;
a comment like this in it:&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: export=yes&lt;br /&gt;
&lt;br /&gt;
Wesnoth core data is an exporting namespace.  Campaigns are non-exporting;&lt;br /&gt;
they should contain the declaration&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: export=no&lt;br /&gt;
&lt;br /&gt;
somewhere.  &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; will complain when it sees a namespace with no export&lt;br /&gt;
property, then treat it as non-exporting.&lt;br /&gt;
&lt;br /&gt;
You can tell &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; to ignore stretches of config files&lt;br /&gt;
with the following magic comments:&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: start ignoring&lt;br /&gt;
    # wmlscope: stop ignoring&lt;br /&gt;
&lt;br /&gt;
Similarly, you can tell &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; to ignore multiple or duplicate macro&lt;br /&gt;
definitions in a range of lines with the following magic comments:&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: start conditionals&lt;br /&gt;
    # wmlscope: stop conditionals&lt;br /&gt;
&lt;br /&gt;
The following magic comment:&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: prune FOOBAR&lt;br /&gt;
&lt;br /&gt;
will cause &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; to forget about all but one of the definitions of&lt;br /&gt;
&amp;lt;tt&amp;gt;FOOBAR&amp;lt;/tt&amp;gt; it has seen.  This will be useful mainly for symbols that have&lt;br /&gt;
different definitions enabled by an &amp;lt;tt&amp;gt;#ifdef&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Due to a preprocessor limitation, inline macros cannot contain a documentation&lt;br /&gt;
string. If you need to document these macros in the HTML macro reference, you&lt;br /&gt;
can use the following directive:&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: docstring FOOBAR&lt;br /&gt;
&lt;br /&gt;
The docstring for the FOOBAR macro will be collected until a non-comment line,&lt;br /&gt;
a &amp;lt;tt&amp;gt;#define&amp;lt;/tt&amp;gt; or another &amp;lt;tt&amp;gt;# wmlscope: docstring&amp;lt;/tt&amp;gt; are found. External&lt;br /&gt;
docstrings '''''must''''' be defined before the macro to which they refer; defining&lt;br /&gt;
two or more external docstrings keeps only the most recent one, but having both an&lt;br /&gt;
external and an internal docstring is allowed (in this case, the internal one&lt;br /&gt;
will be appended to the external one in the macro reference).&lt;br /&gt;
&lt;br /&gt;
This tool does catch one kind of implicit reference: if an attack name&lt;br /&gt;
is specified but no icon is given, the attack icon will default to&lt;br /&gt;
a name generated from the attack name.  This behavior can be suppressed&lt;br /&gt;
by adding a magic comment containing the string &amp;quot;no-icon&amp;quot; to the &amp;lt;tt&amp;gt;name=&amp;lt;/tt&amp;gt;&lt;br /&gt;
line.&lt;br /&gt;
&lt;br /&gt;
The checking done by this tool has a couple of flaws:&lt;br /&gt;
&lt;br /&gt;
(1) It doesn't actually evaluate file inclusions.  Instead, any&lt;br /&gt;
macro definition satisfies any macro call made under the same&lt;br /&gt;
directory.  Exception: when an &amp;lt;tt&amp;gt;#undef&amp;lt;/tt&amp;gt; is detected, the macro is&lt;br /&gt;
tagged local and not visible outside the span of lines where it was&lt;br /&gt;
defined.&lt;br /&gt;
&lt;br /&gt;
(2) It doesn't read &amp;lt;tt&amp;gt;[binary_path]&amp;lt;/tt&amp;gt; tags, as this would require&lt;br /&gt;
implementing a WML parser.  Instead, it assumes that a resource-file&lt;br /&gt;
reference can be satisfied by any matching image file from anywhere&lt;br /&gt;
in the same directory it came from.  The resources under the '''''first'''''&lt;br /&gt;
directory argument (only) are visible everywhere.&lt;br /&gt;
&lt;br /&gt;
(3) A reference with embedded {}s in a macro will have the macro's&lt;br /&gt;
formal args substituted in at WML evaluation time.  Instead, this&lt;br /&gt;
tool treats each {} as a .* wildcard and considers the reference to&lt;br /&gt;
match '''''every''''' resource filename that matches that pattern.&lt;br /&gt;
Under appropriate circumstances this might report a resource filename&lt;br /&gt;
statically matching the pattern as having been referenced even&lt;br /&gt;
though none of the actual macro calls would actually generate it.&lt;br /&gt;
&lt;br /&gt;
Problems (1) and (2) imply that this tool might conceivably report&lt;br /&gt;
that a reference has been satisfied when under actual&lt;br /&gt;
WML-interpreter rules it has not.&lt;br /&gt;
&lt;br /&gt;
The reporting format is compatible with GNU Emacs compile mode.&lt;br /&gt;
&lt;br /&gt;
For debugging purposes, an in-line comment of the form&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: warnlevel NNN&lt;br /&gt;
&lt;br /&gt;
sets the warning level.&lt;br /&gt;
&lt;br /&gt;
== wmllint ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; is a tool for migrating your WML to the current version.  It handles two problems: &lt;br /&gt;
&lt;br /&gt;
* Resource files and macro names may change between versions of the game. &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; knows about these changes and will tweak your WML to fit where it can.&lt;br /&gt;
&lt;br /&gt;
* Between 1.2.x and 1.3.1 the terrain-coding system used in map files underwent a major change. It changed again in a minor way between 1.3.1 and 1.3.2. If you port such old code, use &amp;lt;tt&amp;gt;wmllint-1.4&amp;lt;/tt&amp;gt;, which is located in the same directory as &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;. It will translate your maps for you, unless you use custom terrains in which case you will have to do it by hand.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; also performs various sanity-checking operations, reporting:&lt;br /&gt;
&lt;br /&gt;
* unbalanced tags&lt;br /&gt;
* strings that need a translation mark and do not have them&lt;br /&gt;
* strings that have a translation mark and should not&lt;br /&gt;
* translatable strings containing macro references &lt;br /&gt;
* filter references by description= (id= in 1.5) not matched by an actual unit&lt;br /&gt;
* abilities or traits without matching special notes, or vice-versa&lt;br /&gt;
* consistency between recruit= and recruitment_pattern= instances&lt;br /&gt;
* double space after punctuation in translatable strings.&lt;br /&gt;
* unknown races or movement types in units&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; takes a directory-path argument specifying the WML directories to work on.  It will modify any cfg and map files under those directories that need to be changed.  Here is a summary of its options:&lt;br /&gt;
&lt;br /&gt;
; -h, --help:                 Emit a help message and quit.&lt;br /&gt;
; -c, --clean:                Clean up -bak files.&lt;br /&gt;
; -D, --diffs:                Display diffs between converted and unconverted files.&lt;br /&gt;
; -d, --dryrun:               List changes (-v) but don't perform them.&lt;br /&gt;
; -r, --revert:               Revert the conversion from the -bak files.&lt;br /&gt;
; -m, --missing:              Warn about tags without side= keys now applying to all sides.&lt;br /&gt;
; -s, --stripcr:              Convert DOS-style CR/LF to Unix-style LF.&lt;br /&gt;
; -v, --verbose:              Set verbosity; more details below.&lt;br /&gt;
; -K, --known:                Suppress check for unknown unit types, recruits, races, scenarios, etc.&lt;br /&gt;
; --version:                  show program's version number and exit&lt;br /&gt;
; --config:                   allows specifying directories to include ('''include_dirs'''), directories to exclude ('''ignore_directories'''), and files to exclude ('''ignore_files''').&lt;br /&gt;
&lt;br /&gt;
The verbosity option works like this:&lt;br /&gt;
&lt;br /&gt;
; -v:          lists changes.&lt;br /&gt;
; -v -v:       warns of maps already converted.&lt;br /&gt;
; -v -v -v:    names each file before it's processed.&lt;br /&gt;
; -v -v -v -v: shows verbose parse details (developers only).&lt;br /&gt;
&lt;br /&gt;
The recommended procedure is this:&lt;br /&gt;
&lt;br /&gt;
# Run it with --dryrun first to see what it will do.&lt;br /&gt;
# If the messages look good, run without --dryrun; the old content will be left in backup files with a -bak extension.&lt;br /&gt;
# Eyeball the changes with the --diff option.&lt;br /&gt;
# Use wmlscope, with a directory path including the Wesnoth mainline WML, to check that you have no unresolved references.&lt;br /&gt;
# Test the conversion.&lt;br /&gt;
# Use either --clean to remove the -bak files or --revert to undo the conversion.&lt;br /&gt;
&lt;br /&gt;
wmllint supports a number of magic comments to customize its behaviour and avoid false positives. Almost all magic wmllint comments begin with the string &amp;lt;tt&amp;gt;wmllint:&amp;lt;/tt&amp;gt;, followed by some additional keyword and potentially some arguments. These are all documented at the top of the wmllint tool itself: https://github.com/wesnoth/wesnoth/blob/master/data/tools/wmllint&lt;br /&gt;
&lt;br /&gt;
=== Explanations of &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; diagnostics ===&lt;br /&gt;
Some &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; diagnostics may require further explanation for UMC authors to understand; this section will be for providing such explanations, and descriptions of how to solve and/or silence them.&lt;br /&gt;
&lt;br /&gt;
In these, &amp;lt;code&amp;gt;%s&amp;lt;/code&amp;gt; will be replaced by a string. When the recommended solution also includes a &amp;lt;code&amp;gt;%s&amp;lt;/code&amp;gt;, it's a suggestion to copy the string from the error message into your code.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;nonstandard word-wrap style within message&amp;lt;/code&amp;gt;: This message meant there was an unexpected newline character within a &amp;lt;code&amp;gt;[message]&amp;lt;/code&amp;gt; tag. However, this check was removed in the 1.15.10 release. Pre-1.15.10, add-on developers could silence by putting a &amp;lt;code&amp;gt;# wmllint: display on&amp;lt;/code&amp;gt; comment before the string and a &amp;lt;code&amp;gt;# wmllint: display off&amp;lt;/code&amp;gt; comment after the string, however, post-1.15.10, this is no longer necessary.&lt;br /&gt;
* &amp;lt;code&amp;gt;%s is not a known unit type&amp;lt;/code&amp;gt; (in cases where you'd think the unit type ''would'' be known): This means the unit has a type that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can load the mainline unit types. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;unknown speaker '%s' of [message]&amp;lt;/code&amp;gt;: use a &amp;lt;code&amp;gt;# wmllint: recognize %s&amp;lt;/code&amp;gt; magic comment, or, alternatively, if the speaker is created by a macro, use a magic comment of the form of either &amp;lt;code&amp;gt;# wmllint: who MACRO is SPEAKER&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;# wmllint: whofield MACRO NUMBER&amp;lt;/code&amp;gt;, depending on whether the macro takes an argument for the unit's name or not.&lt;br /&gt;
* &amp;lt;code&amp;gt;unknown '%s' referred to by id&amp;lt;/code&amp;gt;: use a &amp;lt;code&amp;gt;# wmllint: recognize %s&amp;lt;/code&amp;gt; magic comment, or, alternatively, if the unit is created by a macro, use a magic comment of the form of either &amp;lt;code&amp;gt;# wmllint: who MACRO is UNIT&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;# wmllint: whofield MACRO NUMBER&amp;lt;/code&amp;gt;, depending on whether the macro takes an argument for the unit's name or not.&lt;br /&gt;
* &amp;lt;code&amp;gt;%s has unknown advancements&amp;lt;/code&amp;gt; (in cases where you'd think the advancement ''would'' be known): This means the unit has an advancement that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can load mainline units for checking advancements. Note that it's also possible that you just made typo, too, so be sure to check your spelling. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;.description may need hand fixup&amp;lt;/code&amp;gt;: This one comes from mucking around with the &amp;lt;code&amp;gt;.description&amp;lt;/code&amp;gt; field of unit data manually in a hackish fashion. There isn't really much of a way to work around it, besides just the &amp;quot;don't do that&amp;quot; solution.&lt;br /&gt;
* &amp;lt;code&amp;gt;tag stack nonempty (%s) at end of file.&amp;lt;/code&amp;gt;: This means that you have unbalanced tags somewhere in the file, e.g. an opener without a closer, or vice versa. This can often be seen when defining macros for unit abilities. A way to fix this warning is to wrap the section with unbalanced tags with a &amp;lt;code&amp;gt;# wmllint: unbalanced-on&amp;lt;/code&amp;gt; magic comment beforehand and a &amp;lt;code&amp;gt;# wmllint: unbalanced-off&amp;lt;/code&amp;gt; magic comment afterwards. As having unbalanced tags will also cause issues for other WML maintenance tools, such as &amp;lt;tt&amp;gt;wmlindent&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;wmlxgettext&amp;lt;/tt&amp;gt;, you may also want to add separate magic comments for each of them (see their documentation for the form they take).&lt;br /&gt;
* &amp;lt;code&amp;gt;unit declaration without side attribute&amp;lt;/code&amp;gt;: the default side for a unit declaration when left implicit is side 1. Specify your unit sides explicitly to solve this.&lt;br /&gt;
* &amp;lt;code&amp;gt;no %s units recruitable at difficulty %s&amp;lt;/code&amp;gt; (even when there are such units recruitable): This diagnostic has to do with matching the &amp;lt;code&amp;gt;usage&amp;lt;/code&amp;gt; key of units recruitable by an AI side with their &amp;lt;code&amp;gt;recruitment_pattern&amp;lt;/code&amp;gt;. It means the unit has a usage that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can know which mainline units are recruitable. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;%s has unknown movement type&amp;lt;/code&amp;gt; (even when you'd think that that movement type ''would'' actually be known): This means the unit has a movetype that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can load the mainline movement types. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;%s has unknown race&amp;lt;/code&amp;gt; (even when you'd think that that race ''would'' actually be known): This means the unit has a race that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can load the mainline races. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;derivation of %s from %s does not resolve&amp;lt;/code&amp;gt; (even when you'd think it would): This means that a unit using the [[UnitTypeWML#Other_tags|[base_unit]]] tag specifies a unit ID in that tag that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can load the core units for its derivation checks. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;[advancefrom] needs to be manually updated to [modify_unit_type] and moved into the _main.cfg file&amp;lt;/code&amp;gt;: This one is pretty self-explanatory: [[UnitTypeWML#Unit_Type|[advancefrom]]] was deprecated in [https://github.com/wesnoth/wesnoth/commit/3950f40f3f0483032bc70b3e57166bd355acd9fc commit 3950f40] due to [https://github.com/wesnoth/wesnoth/issues/3955 issue #3955], and in fact doesn't even work anymore (in 1.16) as per [https://github.com/wesnoth/wesnoth/issues/6204 issue #6204]. The main reason &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; can't fix this automatically is because it could end up being too complicated for it to figure out which files to edit if there are multiple uses of &amp;lt;code&amp;gt;[advancefrom]&amp;lt;/code&amp;gt;, and it also doesn't want to assume where to put the [[ModificationWML|[modify_unit_type]]] tag in &amp;lt;tt&amp;gt;_main.cfg&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== wmlindent ==&lt;br /&gt;
&lt;br /&gt;
Call with no arguments to filter WML on standard input to reindented WML on&lt;br /&gt;
standard output.  If arguments are specified, they are taken to be files to be&lt;br /&gt;
re-indented in place; a directory name causes reindenting on all WML&lt;br /&gt;
beneath it.&lt;br /&gt;
&lt;br /&gt;
The indent unit is four spaces.  Absence of an option to change this is&lt;br /&gt;
deliberate; the purpose of this tool is to ''prevent'' style wars, not encourage&lt;br /&gt;
them.&lt;br /&gt;
&lt;br /&gt;
On non-empty lines, this code never modifies anything but leading and&lt;br /&gt;
trailing whitespace. Leading whitespace will be regularized to the&lt;br /&gt;
current indent; trailing whitespace will be stripped.  After processing&lt;br /&gt;
all lines will end with a Unix-style &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt; end-of-line marker.&lt;br /&gt;
&lt;br /&gt;
Runs of entirely blank lines will be reduced to one blank line, except&lt;br /&gt;
in two cases where they will be discarded: (a) before WML closing&lt;br /&gt;
tags, and (b) after WML opening tags.&lt;br /&gt;
&lt;br /&gt;
It is possible to wrap a section of lines in special comments so that&lt;br /&gt;
&amp;lt;tt&amp;gt;wmlindent&amp;lt;/tt&amp;gt; will ignore them.  You may need to do this for unbalanced&lt;br /&gt;
macros (it's better, though, to get rid of those where possible).&lt;br /&gt;
Use '&amp;lt;code&amp;gt;wmlindent: {start,stop} ignoring&amp;lt;/code&amp;gt;' anywhere in a comment.&lt;br /&gt;
&lt;br /&gt;
It is also possible to declare custom openers an closers, e.g for macros&lt;br /&gt;
that are actually control constructs.  To do this, use declarations&lt;br /&gt;
&lt;br /&gt;
    # wmlindent: opener &amp;quot;{EXCEPTIONAL_OPENER &amp;quot;&lt;br /&gt;
    # wmlindent: closer &amp;quot;{EXCEPTIONAL_CLOSER &amp;quot;&lt;br /&gt;
&lt;br /&gt;
The lines after an opener will be indented an extra level; a closer&lt;br /&gt;
and lines following will be indented one level less. Note that these&lt;br /&gt;
declare prefixes; any prefix match to the non-whitespace text of a line&lt;br /&gt;
will be recognized.&lt;br /&gt;
&lt;br /&gt;
The public utility macros &amp;quot;&amp;lt;code&amp;gt;{FOREACH&amp;lt;/code&amp;gt;&amp;quot; and &amp;quot;&amp;lt;code&amp;gt;{NEXT&amp;lt;/code&amp;gt;&amp;quot; come as wired-in exceptions,&lt;br /&gt;
because it is not guaranteed that their indent declarations will be processed&lt;br /&gt;
before the macro library is reached.&lt;br /&gt;
&lt;br /&gt;
Interrupting &amp;lt;tt&amp;gt;wmlindent&amp;lt;/tt&amp;gt; ought to be safe, as each reindenting will be done to a copy&lt;br /&gt;
that is atomically renamed when it's done.  If the output file is identical&lt;br /&gt;
to the input, the output file will simply be deleted, so the timestamp&lt;br /&gt;
on the input file won't be touched.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;--dryrun&amp;lt;/tt&amp;gt; option detects and reports files that would be changed&lt;br /&gt;
without changing them. The &amp;lt;tt&amp;gt;--verbose&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;-v&amp;lt;/tt&amp;gt; option enables reporting&lt;br /&gt;
of files that are (or would be, under &amp;lt;tt&amp;gt;--dryrun&amp;lt;/tt&amp;gt;) changed.  With &amp;lt;tt&amp;gt;-v -v&amp;lt;/tt&amp;gt;,&lt;br /&gt;
unchanged files are also reported.  The &amp;lt;tt&amp;gt;--exclude&amp;lt;/tt&amp;gt; option takes a regexp&lt;br /&gt;
and excludes files matching it.&lt;br /&gt;
&lt;br /&gt;
If you don't apply this tool to your own WML that you wish to submit, the&lt;br /&gt;
mainline-campaign maintainers will do it when and if your code is accepted into the tree.&lt;br /&gt;
&lt;br /&gt;
Note: This tool does not include a parser.  It will produce bad results on WML&lt;br /&gt;
that is syntactically unbalanced.  Unbalanced double quotes that aren't part&lt;br /&gt;
of a multiline literal will also confuse it.  You will receive warnings&lt;br /&gt;
if there's an indent open at end of file or if a closer occurs with&lt;br /&gt;
indent already zero; these two conditions strongly suggest unbalanced WML.&lt;br /&gt;
&lt;br /&gt;
== GUI.pyw ==&lt;br /&gt;
&lt;br /&gt;
Starting from version 1.11.15 and 1.13.0, a GUI (written in Tkinter, plus the themed widgets ttk) is available in the same directory as the other tools. To use it, you need to have a version of Python equal to or greater than 3.1.0 (the 3.0.x series doesn't include the ttk widgets, and as such is unsuitable for this script).&lt;br /&gt;
&lt;br /&gt;
If you're on Linux, be sure to have installed the ''python3-tk'' module, '''or the application won't run at all'''. To install it in a Debian-based distro (like Ubuntu), type this line in a Terminal:&lt;br /&gt;
 sudo apt install python3-tk&lt;br /&gt;
&lt;br /&gt;
To start it, just double click on the GUI.pyw file. The interface is pretty much self-explanatory, and allows you to run wmllint, wmlscope, wmlindent and wmlxgettext, modify their options, select an add-on and save the tools' output as a text file.&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
* [[Translation Maintenance Commands]] (for &amp;lt;tt&amp;gt;wmlxgettext&amp;lt;/tt&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
[[Category:Create]]&lt;br /&gt;
[[Category:Tools]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Maintenance_tools&amp;diff=75482</id>
		<title>Maintenance tools</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Maintenance_tools&amp;diff=75482"/>
		<updated>2026-07-03T16:34:10Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* wmllint */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;floatright&amp;quot;&amp;gt; __TOC__ &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Wesnoth source code distribution includes a couple of tools intended to help authors maintain campaigns, faction &amp;amp; unit packs, and other WML resources. These&lt;br /&gt;
are:&lt;br /&gt;
 &lt;br /&gt;
; wmlscope: a cross-reference lister, useful for finding unresolved macro and resource-file references.&lt;br /&gt;
&lt;br /&gt;
; wmllint: a utility for sanity-checking WML syntax and porting your old WML to the current version of WML.  &lt;br /&gt;
&lt;br /&gt;
; wmlindent: a utility for reindenting WML to a uniform style.&lt;br /&gt;
&lt;br /&gt;
; GUI.pyw: a graphical interface&lt;br /&gt;
&lt;br /&gt;
== General Information ==&lt;br /&gt;
&lt;br /&gt;
You will need a Python 3 interpreter on your system to use these tools.  Linux, *BSD, and Mac OS/X should already have Python 3 installed; for Windows it's a free download&lt;br /&gt;
from http://www.python.org.  You will also need to know how to run command-line tools on your system.&lt;br /&gt;
&lt;br /&gt;
If you're working with Debian or Ubuntu you might have to install the package wesnoth-1.16-tools (or the convenient version).&lt;br /&gt;
 sudo apt install wesnoth-1.16-tools&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
All three tools will require you to supply a &amp;lt;i&amp;gt;directory list&amp;lt;/i&amp;gt;.  This is a set of directories containing the WML files you want to work on.&lt;br /&gt;
&lt;br /&gt;
This page is intended as documentation for users.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Note to Windows Users:&amp;lt;/u&amp;gt; This means you have to run it from the '''Command Line'''. The command line may be reached by hitting Start, then Run, then &amp;quot;cmd&amp;quot; or &amp;quot;command&amp;quot; depending on your version of Windows.&lt;br /&gt;
&lt;br /&gt;
Example uses:&lt;br /&gt;
 python wmllint path\to\files&lt;br /&gt;
 python wmlindent path\to\files&lt;br /&gt;
&lt;br /&gt;
Another example:&lt;br /&gt;
 &amp;quot;C:\Program Files\Python3.7\python.exe&amp;quot; data\tools\wmllint --dryrun data\core data\{multiplayer,themes} data\campaigns &lt;br /&gt;
(You have to specify the full directory path to the executable if you don't have your environment variables set up correctly).&lt;br /&gt;
The first thing you type is the path to your python executable, followed by a space. The second thing you type is the path to the desired script to run, followed by a space. The third thing you type is the path to the folder (or file) to be processed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''A convenient way of running wmllint''' on Linux (Debian or Ubuntu) and Windows in comparison, '''Linux''':&lt;br /&gt;
&lt;br /&gt;
Assuming we're working with wesnoth 1.16 or more advanced versions.&lt;br /&gt;
 python3 /usr/share/games/wesnoth/1.16/data/tools/wmllint --dryrun /usr/share/games/wesnoth/1.16/data/core ~/.local/share/wesnoth/1.16/data/add-ons/A_Simple_Campaign 1&amp;gt;wmllint-run.log 2&amp;gt;wmllint-err.log&lt;br /&gt;
I have these commands inside of a file named&lt;br /&gt;
 wmllint_dryrun_ASC.sh&lt;br /&gt;
and execute it by opening a shell (=terminal, console, command window, bash,...), navigating into the directory with that file and typing&lt;br /&gt;
 bash wmllint_dryrun_ASC.sh&lt;br /&gt;
The python3 command should be automatically known on Debian. The path to the script tells the python interpreter what to execute. --dryrun: A wmllint option, see below. The path to the core files is needed to let wmllint know about e.g. defined core units, followed by the path to the add-on that shall be checked; the last two commands cause the result of the wmllint usage to be written into those files in the same directory as the script.&lt;br /&gt;
'''Windows''', this is logically exactly the same as the Linux shell script above, so if you are on a Mac you can probably conclude how you need to adapt the paths:&lt;br /&gt;
 E:\Python37\python.exe E:\Programme\Wesnoth_1.16_git\data\tools\wmllint --dryrun E:\Programme\Wesnoth_1.16_git\data\core E:\Programme\Wesnoth_1.16_git\userdata\data\add-ons\A_Simple_Campaign 1&amp;gt;wmllint-run.log 2&amp;gt;wmllint-err.log&lt;br /&gt;
This is the content of a .txt file, whose extension I rename to .bat and double-click onto it. Opening a command window is not needed this way.&lt;br /&gt;
Since Python isn't natively installed on windows and I don't have environment variables set, the full path to python.exe is given. If your directories contain spaces it may help to include the path in quotes:&lt;br /&gt;
 &amp;quot;C:\Programs\Battle for Wesnoth 1.16\data\tools\wmllint&amp;quot;&lt;br /&gt;
Remember that you do not need to enter all of the commands/paths at once. If it doesn't work, start with only &amp;quot;python&amp;quot; or &amp;quot;C:\Python37\python.exe&amp;quot; or the like and interpret the error messages that you get. If you get an &amp;quot;unknown command&amp;quot;, python isn't installed or environment variables aren't set correctly. After that, you can add the later commands one by one.&lt;br /&gt;
&lt;br /&gt;
== wmlscope ==&lt;br /&gt;
&lt;br /&gt;
The main use for &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; is to find WML macro references without definitions and references to resource files (sounds and images) that don't exist.  These are difficult to spot from in-game because they usually result in silence or a missing image rather than actual broken game logic (see [https://github.com/wesnoth/wesnoth/issues/5332 issue 5332] for more info).  They may happen because of typos in your WML, or because the name of a macro or the location of a resource file changed between versions of the game.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; also checks macro invocations for consistency.  It will complain&lt;br /&gt;
if a macro is called with the wrong number of arguments.  In most cases it can deduce information about the type of the literal expected to be passed to a given macro argument by looking at the name of the formal.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table class=&amp;quot;wikitable&amp;quot;&amp;gt;&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;Type&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;Meaning&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;Formals requiring this type&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;Literals of this type&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;side&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a single side number&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;SIDE, *_SIDE, SIDE[0-9]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a numeric or &amp;quot;global&amp;quot;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;numeric&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a numeric integer literal&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;SIDE, X, Y, RED, GREEN, BLUE, TURN, PROB, LAYER, TIME, *_SIDE, *NUMBER, *AMOUNT, *COST, *RADIUS, *_X, *_Y, *_INCREMENT, *_FACTOR, *_TIME, *_SIZE, DURATION&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;\-?[0-9]+&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;percentage&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a percentage&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;*PERCENTAGE&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a numeric or 0\.[0-9]+&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;position&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a single x,y coordinate&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;POSITION, *_POSITION, BASE&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;-?[0-9]+,-?[0-9]+&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;span&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a set of coordinates or coordinate ranges&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;*_SPAN&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a numeric, position or ([0-9]+\-[0-9]+,?|[0-9]+,?)+&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;alliance&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a set of side numbers&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;SIDES, *_SIDES&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a span, or the empty string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;range&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;an attack range&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;RANGE&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;quot;melee&amp;quot; or &amp;quot;ranged&amp;quot;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;alignment&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;an alignment keyword&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;ALIGN&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;quot;lawful&amp;quot; or &amp;quot;neutral&amp;quot; or &amp;quot;chaotic&amp;quot;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;types&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a set of unit types&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;TYPES&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a shortname, name, or anything that contains spaces and matches no other type&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;terrain_pattern&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a set of terrain codes to filter&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;ADJACENT*, TERRAINLIST*, *TERRAIN_PATTERN, RESTRICTING&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a terrain_code or name&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;terrain_code&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a single terrain code, perhaps with overlay&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;TERRAIN*, *TERRAIN&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a shortname or (\*|[A-Z][a-z]+)\^([A-Z][a-z\\|/]+\Z)?&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;shortname&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a terrain code or a short, capitalized variable name&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[A-Z][a-z][a-z]?&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;name&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a name or identifier&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;NAME, VAR, IMAGESTEM, ID, FLAG, *_NAME, *_ID, NAMESPACE, BUILDER, *_VAR&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;anything without spaces that matches no other type&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;optional_string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a string value (may be empty)&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;ID_STRING, NAME_STRING, DESCRIPTION, IPF&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a string, or the empty string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a nonempty string not matching any of the preceding types&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;STRING, TYPE, TEXT, *_STRING, *_TYPE, *_TEXT&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a shortname, a name, a stringliteral, or anything that contains spaces and matches no other type&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;stringliteral&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a string in doublequotes or a translated string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;quot;.*&amp;quot; or _.* but not _[a-z].*&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;image&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;an image path, perhaps with [[ImagePathFunctionWML|image path functions]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;*IMAGE, PROFILE&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[A-Za-z0-9{}.][A-Za-z0-9_/+{}.-]*\.(png|jpg)(?=(~.*)?)&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;sound&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a music or sound filename&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;MUSIC, SOUND&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;string ending with &amp;quot;.wav&amp;quot; or &amp;quot;.ogg&amp;quot;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;filter&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[FilterWML|WML filter]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;FILTER&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;any non-quoted string containing &amp;quot;=&amp;quot;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;WML&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;arbitrary WML fragment&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;WML, *_WML&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;any non-quoted string containing &amp;quot;=&amp;quot;, or the empty string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;affix&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a prefix, suffix, or infix for a variable name&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;AFFIX, *AFFIX, POSTFIX, ROTATION&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a shortname or name, or the empty string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;any&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;anything&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;*VALUE, [ARS][0-9]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;anything&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the actual argument is a macro call {.*}, then it matches any formal.  Otherwise, if the formal has an identifiable type, &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; will complain if the actual literal does not match it.&lt;br /&gt;
&lt;br /&gt;
The argument type check only works in macro calls that fit on a single line.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; has many options for changing the reports it generates; the more advanced ones are intended for Wesnoth developers.  Invocations for the most commonly useful reports it generates are included in &amp;lt;i&amp;gt;data/tools/Makefile&amp;lt;/i&amp;gt; of the source distribution. Here are some of those reports:&lt;br /&gt;
&lt;br /&gt;
; make unresolved: Report on unresolved macro calls and resource references; also report macro argument-type mismatches.  (This is what you are most likely to want to do). &lt;br /&gt;
&lt;br /&gt;
; make all: Report all macro and resource file references, not just unresolved ones.&lt;br /&gt;
&lt;br /&gt;
; make collisions: Report on duplicate resource files.&lt;br /&gt;
&lt;br /&gt;
For more advanced users, or those who want to understand what the canned Makefile invocations are doing, here is a summary of &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt;'s options. Some of the more advanced options will require you to understand &lt;br /&gt;
[http://docs.python.org/lib/re-syntax.html Python regular expressions].&lt;br /&gt;
&lt;br /&gt;
; -h, --help:                 Emit a help message and quit&lt;br /&gt;
; -c, --crossreference:       Report resolved macro references (implies &amp;lt;tt&amp;gt;-w 1&amp;lt;/tt&amp;gt;)&lt;br /&gt;
; -C, --collisions:           Report duplicate resource files   &lt;br /&gt;
; -d, --deflist:              Make definition list.  (This one is for campaign server maintainers.)&lt;br /&gt;
; -e &amp;lt;i&amp;gt;regexp&amp;lt;/i&amp;gt;, --exclude &amp;lt;i&amp;gt;regexp&amp;lt;/i&amp;gt;:   Ignore files matching the specified regular expression. &lt;br /&gt;
; -f &amp;lt;i&amp;gt;dir&amp;lt;/i&amp;gt;, --from &amp;lt;i&amp;gt;dir&amp;lt;/i&amp;gt;:         Report only on macros defined under &amp;lt;i&amp;gt;dir&amp;lt;/i&amp;gt;&lt;br /&gt;
; -l, --listfiles:            List files that will be processed&lt;br /&gt;
; -r &amp;lt;i&amp;gt;ddd&amp;lt;/i&amp;gt;, --refcount=&amp;lt;i&amp;gt;ddd&amp;lt;/i&amp;gt;:     Report only on macros with references in exactly &amp;lt;i&amp;gt;ddd&amp;lt;/i&amp;gt; files.&lt;br /&gt;
; -t &amp;lt;i&amp;gt;TYPELIST&amp;lt;/i&amp;gt;, --typelist &amp;lt;i&amp;gt;TYPELIST&amp;lt;/i&amp;gt;: List actual &amp;amp; formal argtypes for calls in fname&lt;br /&gt;
; -u, --unresolved:           Report unresolved macro references&lt;br /&gt;
; -w, --warnlevel:            Set to 1 to warn of duplicate macro definitions&lt;br /&gt;
; -p, --progress:             Show progress&lt;br /&gt;
; --force-used reg:           Ignore reference count 0 on names matching regexp&lt;br /&gt;
; --extracthelp:              Extract help from macro definition comments.&lt;br /&gt;
; --unchecked:                Report all macros with untyped formals.&lt;br /&gt;
; --version:                  show program's version number and exit&lt;br /&gt;
&lt;br /&gt;
These options are used with a list of directories as arguments; if none is given,&lt;br /&gt;
&amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; behaves as though the current directory had been specified as a&lt;br /&gt;
single argument.  Each directory is treated as a separate domain for&lt;br /&gt;
macro and resource visibility purposes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; recognizes two kinds of namespace, exporting and non-exporting.&lt;br /&gt;
Exporting namespaces make all their resources and macro names&lt;br /&gt;
globally visible.  You can make a namespace exporting by embedding&lt;br /&gt;
a comment like this in it:&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: export=yes&lt;br /&gt;
&lt;br /&gt;
Wesnoth core data is an exporting namespace.  Campaigns are non-exporting;&lt;br /&gt;
they should contain the declaration&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: export=no&lt;br /&gt;
&lt;br /&gt;
somewhere.  &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; will complain when it sees a namespace with no export&lt;br /&gt;
property, then treat it as non-exporting.&lt;br /&gt;
&lt;br /&gt;
You can tell &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; to ignore stretches of config files&lt;br /&gt;
with the following magic comments:&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: start ignoring&lt;br /&gt;
    # wmlscope: stop ignoring&lt;br /&gt;
&lt;br /&gt;
Similarly, you can tell &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; to ignore multiple or duplicate macro&lt;br /&gt;
definitions in a range of lines with the following magic comments:&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: start conditionals&lt;br /&gt;
    # wmlscope: stop conditionals&lt;br /&gt;
&lt;br /&gt;
The following magic comment:&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: prune FOOBAR&lt;br /&gt;
&lt;br /&gt;
will cause &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; to forget about all but one of the definitions of&lt;br /&gt;
&amp;lt;tt&amp;gt;FOOBAR&amp;lt;/tt&amp;gt; it has seen.  This will be useful mainly for symbols that have&lt;br /&gt;
different definitions enabled by an &amp;lt;tt&amp;gt;#ifdef&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Due to a preprocessor limitation, inline macros cannot contain a documentation&lt;br /&gt;
string. If you need to document these macros in the HTML macro reference, you&lt;br /&gt;
can use the following directive:&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: docstring FOOBAR&lt;br /&gt;
&lt;br /&gt;
The docstring for the FOOBAR macro will be collected until a non-comment line,&lt;br /&gt;
a &amp;lt;tt&amp;gt;#define&amp;lt;/tt&amp;gt; or another &amp;lt;tt&amp;gt;# wmlscope: docstring&amp;lt;/tt&amp;gt; are found. External&lt;br /&gt;
docstrings '''''must''''' be defined before the macro to which they refer; defining&lt;br /&gt;
two or more external docstrings keeps only the most recent one, but having both an&lt;br /&gt;
external and an internal docstring is allowed (in this case, the internal one&lt;br /&gt;
will be appended to the external one in the macro reference).&lt;br /&gt;
&lt;br /&gt;
This tool does catch one kind of implicit reference: if an attack name&lt;br /&gt;
is specified but no icon is given, the attack icon will default to&lt;br /&gt;
a name generated from the attack name.  This behavior can be suppressed&lt;br /&gt;
by adding a magic comment containing the string &amp;quot;no-icon&amp;quot; to the &amp;lt;tt&amp;gt;name=&amp;lt;/tt&amp;gt;&lt;br /&gt;
line.&lt;br /&gt;
&lt;br /&gt;
The checking done by this tool has a couple of flaws:&lt;br /&gt;
&lt;br /&gt;
(1) It doesn't actually evaluate file inclusions.  Instead, any&lt;br /&gt;
macro definition satisfies any macro call made under the same&lt;br /&gt;
directory.  Exception: when an &amp;lt;tt&amp;gt;#undef&amp;lt;/tt&amp;gt; is detected, the macro is&lt;br /&gt;
tagged local and not visible outside the span of lines where it was&lt;br /&gt;
defined.&lt;br /&gt;
&lt;br /&gt;
(2) It doesn't read &amp;lt;tt&amp;gt;[binary_path]&amp;lt;/tt&amp;gt; tags, as this would require&lt;br /&gt;
implementing a WML parser.  Instead, it assumes that a resource-file&lt;br /&gt;
reference can be satisfied by any matching image file from anywhere&lt;br /&gt;
in the same directory it came from.  The resources under the '''''first'''''&lt;br /&gt;
directory argument (only) are visible everywhere.&lt;br /&gt;
&lt;br /&gt;
(3) A reference with embedded {}s in a macro will have the macro's&lt;br /&gt;
formal args substituted in at WML evaluation time.  Instead, this&lt;br /&gt;
tool treats each {} as a .* wildcard and considers the reference to&lt;br /&gt;
match '''''every''''' resource filename that matches that pattern.&lt;br /&gt;
Under appropriate circumstances this might report a resource filename&lt;br /&gt;
statically matching the pattern as having been referenced even&lt;br /&gt;
though none of the actual macro calls would actually generate it.&lt;br /&gt;
&lt;br /&gt;
Problems (1) and (2) imply that this tool might conceivably report&lt;br /&gt;
that a reference has been satisfied when under actual&lt;br /&gt;
WML-interpreter rules it has not.&lt;br /&gt;
&lt;br /&gt;
The reporting format is compatible with GNU Emacs compile mode.&lt;br /&gt;
&lt;br /&gt;
For debugging purposes, an in-line comment of the form&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: warnlevel NNN&lt;br /&gt;
&lt;br /&gt;
sets the warning level.&lt;br /&gt;
&lt;br /&gt;
== wmllint ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; is a tool for migrating your WML to the current version.  It handles two problems: &lt;br /&gt;
&lt;br /&gt;
* Resource files and macro names may change between versions of the game. &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; knows about these changes and will tweak your WML to fit where it can.&lt;br /&gt;
&lt;br /&gt;
* Between 1.2.x and 1.3.1 the terrain-coding system used in map files underwent a major change. It changed again in a minor way between 1.3.1 and 1.3.2. If you port such old code, use &amp;lt;tt&amp;gt;wmllint-1.4&amp;lt;/tt&amp;gt;, which is located in the same directory as &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;. It will translate your maps for you, unless you use custom terrains in which case you will have to do it by hand.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; also performs various sanity-checking operations, reporting:&lt;br /&gt;
&lt;br /&gt;
* unbalanced tags&lt;br /&gt;
* strings that need a translation mark and do not have them&lt;br /&gt;
* strings that have a translation mark and should not&lt;br /&gt;
* translatable strings containing macro references &lt;br /&gt;
* filter references by description= (id= in 1.5) not matched by an actual unit&lt;br /&gt;
* abilities or traits without matching special notes, or vice-versa&lt;br /&gt;
* consistency between recruit= and recruitment_pattern= instances&lt;br /&gt;
* double space after punctuation in translatable strings.&lt;br /&gt;
* unknown races or movement types in units&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; takes a directory-path argument specifying the WML directories to work on.  It will modify any cfg and map files under those directories that need to be changed.  Here is a summary of its options:&lt;br /&gt;
&lt;br /&gt;
; -h, --help:                 Emit a help message and quit.&lt;br /&gt;
; -c, --clean:                Clean up -bak files.&lt;br /&gt;
; -D, --diffs:                Display diffs between converted and unconverted files.&lt;br /&gt;
; -d, --dryrun:               List changes (-v) but don't perform them.&lt;br /&gt;
; -r, --revert:               Revert the conversion from the -bak files.&lt;br /&gt;
; -m, --missing:              Warn about tags without side= keys now applying to all sides.&lt;br /&gt;
; -s, --stripcr:              Convert DOS-style CR/LF to Unix-style LF.&lt;br /&gt;
; -v, --verbose:              Set verbosity; more details below.&lt;br /&gt;
; -K, --known:                Suppress check for unknown unit types, recruits, races, scenarios, etc.&lt;br /&gt;
; --version:                  show program's version number and exit&lt;br /&gt;
; --config:                   allows specifying directories to include ('''include_dirs'''), directories to exclude ('''ignore_directories'''), and files to exclude ('''ignore_files''').&lt;br /&gt;
&lt;br /&gt;
The verbosity option works like this:&lt;br /&gt;
&lt;br /&gt;
; -v:          lists changes.&lt;br /&gt;
; -v -v:       warns of maps already converted.&lt;br /&gt;
; -v -v -v:    names each file before it's processed.&lt;br /&gt;
; -v -v -v -v: shows verbose parse details (developers only).&lt;br /&gt;
&lt;br /&gt;
The recommended procedure is this:&lt;br /&gt;
&lt;br /&gt;
# Run it with --dryrun first to see what it will do.&lt;br /&gt;
# If the messages look good, run without --dryrun; the old content will be left in backup files with a -bak extension.&lt;br /&gt;
# Eyeball the changes with the --diff option.&lt;br /&gt;
# Use wmlscope, with a directory path including the Wesnoth mainline WML, to check that you have no unresolved references.&lt;br /&gt;
# Test the conversion.&lt;br /&gt;
# Use either --clean to remove the -bak files or --revert to undo the conversion.&lt;br /&gt;
&lt;br /&gt;
wmllint supports a number of magic comments to customize its behaviour and avoid false positives. All magic wmllint comments begin with the string &amp;lt;tt&amp;gt;wmllint:&amp;lt;/tt&amp;gt;, followed by some additional keyword and potentially some arguments. In the below explanations, a string of the form &amp;lt;code&amp;gt;[a|b]&amp;lt;/code&amp;gt; means you may use either &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;b&amp;lt;/code&amp;gt; at that location, while a string of the form &amp;lt;code&amp;gt;&amp;lt;arg&amp;gt;&amp;lt;/code&amp;gt; indicates free text substitution.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ignore&amp;lt;/code&amp;gt;: Disables checking of terrains and translation marks on the current line only.&lt;br /&gt;
* &amp;lt;code&amp;gt;noconvert&amp;lt;/code&amp;gt;: Disables conversion of terrains and image/sound filenames on the current line only.&lt;br /&gt;
* &amp;lt;code&amp;gt;markcheck [on|off]&amp;lt;/code&amp;gt;: Enables or disables translation mark checking from the current (next?) line onward.&lt;br /&gt;
* &amp;lt;code&amp;gt;no-icon&amp;lt;/code&amp;gt;: If an attack has no description, without this wmllint adds a dummy description to the attack which is just the name of the attack. With this, this description insertion is suppressed.&lt;br /&gt;
* &amp;lt;code&amp;gt;recognize &amp;lt;name&amp;gt;&amp;lt;/code&amp;gt;: Indicates that the character with the given name exists even though a declaration (ie a &amp;lt;code&amp;gt;[unit]&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;[side]&amp;lt;/code&amp;gt; tag) is not visible.&lt;br /&gt;
* &amp;lt;code&amp;gt;whofield &amp;lt;macro&amp;gt; &amp;lt;number&amp;gt;&amp;lt;/code&amp;gt;: Indicates that the specified macro declares a character whose name is given by the specified argument to the macro.&lt;br /&gt;
* &amp;lt;code&amp;gt;whofield clear &amp;lt;macro&amp;gt;&amp;lt;/code&amp;gt;: Removes the &amp;lt;code&amp;gt;whofield&amp;lt;/code&amp;gt; definition for the specified macro.&lt;br /&gt;
* &amp;lt;code&amp;gt;who &amp;lt;macro&amp;gt; is &amp;lt;name&amp;gt; &amp;lt;name&amp;gt; ...&amp;lt;/code&amp;gt;: Indicates that the specified macro declares one or more characters whose names are given as a space-separated list. This can also be used for macros that auto-recall a list of characters, some of which join later or may die at some point. If a definition for the macro already exists, then the specified names are appended to it. If a name is preceded by a double minus (&amp;lt;code&amp;gt;-- &amp;lt;name&amp;gt;&amp;lt;/code&amp;gt;) then it is removed from the definition.&lt;br /&gt;
* &amp;lt;code&amp;gt;unwho all|&amp;lt;name&amp;gt;&amp;lt;/code&amp;gt;: Removes the &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt; definition for the specified macro, or all &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt; definitions.&lt;br /&gt;
* &amp;lt;code&amp;gt;usage of &amp;quot;&amp;lt;unit&amp;gt;&amp;quot; is &amp;lt;class&amp;gt;&amp;lt;/code&amp;gt;: Declares the usage of the specified unit (the &amp;lt;code&amp;gt;usage=&amp;lt;/code&amp;gt; key in the &amp;lt;code&amp;gt;[unit_type]&amp;lt;/code&amp;gt; tag). Useful if you are using macros to generate several similar unit types.&lt;br /&gt;
* &amp;lt;code&amp;gt;usagetype &amp;lt;class&amp;gt;&amp;lt;/code&amp;gt;: Declares a valid usage type for units. &amp;lt;code&amp;gt;usagetypes&amp;lt;/code&amp;gt; is also recognized, and a comma-separated list of usage types can be specified.&lt;br /&gt;
* &amp;lt;code&amp;gt;validate-[on|off]&amp;lt;/code&amp;gt;: Enables or disables stack-based validation checks. Use when you have unbalanced tags in macros.&lt;br /&gt;
* &amp;lt;code&amp;gt;unbalanced-[on|off]&amp;lt;/code&amp;gt;: Similar to above, the precise difference is unclear.&lt;br /&gt;
* &amp;lt;code&amp;gt;no translatables&amp;lt;/code&amp;gt;: Suppresses warnings about a missing textdomain declaration in the current file. Make sure the file really does have no translatable strings!&lt;br /&gt;
* &amp;lt;code&amp;gt;display [on|off]&amp;lt;/code&amp;gt;: Enable or disable warnings about newlines in messages.&lt;br /&gt;
* &amp;lt;code&amp;gt;notecheck [on|off]&amp;lt;/code&amp;gt;: Enable or disable note consistency checks for unit descriptions.&lt;br /&gt;
* &amp;lt;code&amp;gt;deathcheck [on|off]&amp;lt;/code&amp;gt;: Enable or disable the check for units speaking in their death events.&lt;br /&gt;
* &amp;lt;code&amp;gt;[general|directory|local] spellings &amp;lt;word&amp;gt; &amp;lt;word&amp;gt; ...&amp;lt;/code&amp;gt;: Declares the specified space-separated list of words to be valid spellings in the specified context. The context &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt; indicates the current file only, while &amp;lt;code&amp;gt;directory&amp;lt;/code&amp;gt; means the current file and any siblings in the same directory or subdirectories. The &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; context indicates the spellings are valid anywhere.&lt;br /&gt;
* &amp;lt;code&amp;gt;no spellcheck&amp;lt;/code&amp;gt;: Disables spell checking on the current line.&lt;br /&gt;
* &amp;lt;code&amp;gt;skip-side&amp;lt;/code&amp;gt;: Indicates that there is a missing side declaration at this location that will be provided by a macro expansion.&lt;br /&gt;
* &amp;lt;code&amp;gt;match &amp;lt;string&amp;gt; with &amp;lt;notes_macro&amp;gt;&amp;lt;/code&amp;gt;: Indicates that uses of the specified string in a unit definition (usually a macro, including the curly braces) should be matched up with the specified special notes macro (which is also specified as the full macro with curly braces).&lt;br /&gt;
* &amp;lt;code&amp;gt;no ellipsecheck&amp;lt;/code&amp;gt;: Disables checking of unit ellipses on the current line.&lt;br /&gt;
&lt;br /&gt;
=== Explanations of &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; diagnostics ===&lt;br /&gt;
Some &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; diagnostics may require further explanation for UMC authors to understand; this section will be for providing such explanations, and descriptions of how to solve and/or silence them.&lt;br /&gt;
&lt;br /&gt;
In these, &amp;lt;code&amp;gt;%s&amp;lt;/code&amp;gt; will be replaced by a string. When the recommended solution also includes a &amp;lt;code&amp;gt;%s&amp;lt;/code&amp;gt;, it's a suggestion to copy the string from the error message into your code.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;nonstandard word-wrap style within message&amp;lt;/code&amp;gt;: This message meant there was an unexpected newline character within a &amp;lt;code&amp;gt;[message]&amp;lt;/code&amp;gt; tag. However, this check was removed in the 1.15.10 release. Pre-1.15.10, add-on developers could silence by putting a &amp;lt;code&amp;gt;# wmllint: display on&amp;lt;/code&amp;gt; comment before the string and a &amp;lt;code&amp;gt;# wmllint: display off&amp;lt;/code&amp;gt; comment after the string, however, post-1.15.10, this is no longer necessary.&lt;br /&gt;
* &amp;lt;code&amp;gt;%s is not a known unit type&amp;lt;/code&amp;gt; (in cases where you'd think the unit type ''would'' be known): This means the unit has a type that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can load the mainline unit types. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;unknown speaker '%s' of [message]&amp;lt;/code&amp;gt;: use a &amp;lt;code&amp;gt;# wmllint: recognize %s&amp;lt;/code&amp;gt; magic comment, or, alternatively, if the speaker is created by a macro, use a magic comment of the form of either &amp;lt;code&amp;gt;# wmllint: who MACRO is SPEAKER&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;# wmllint: whofield MACRO NUMBER&amp;lt;/code&amp;gt;, depending on whether the macro takes an argument for the unit's name or not.&lt;br /&gt;
* &amp;lt;code&amp;gt;unknown '%s' referred to by id&amp;lt;/code&amp;gt;: use a &amp;lt;code&amp;gt;# wmllint: recognize %s&amp;lt;/code&amp;gt; magic comment, or, alternatively, if the unit is created by a macro, use a magic comment of the form of either &amp;lt;code&amp;gt;# wmllint: who MACRO is UNIT&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;# wmllint: whofield MACRO NUMBER&amp;lt;/code&amp;gt;, depending on whether the macro takes an argument for the unit's name or not.&lt;br /&gt;
* &amp;lt;code&amp;gt;%s has unknown advancements&amp;lt;/code&amp;gt; (in cases where you'd think the advancement ''would'' be known): This means the unit has an advancement that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can load mainline units for checking advancements. Note that it's also possible that you just made typo, too, so be sure to check your spelling. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;.description may need hand fixup&amp;lt;/code&amp;gt;: This one comes from mucking around with the &amp;lt;code&amp;gt;.description&amp;lt;/code&amp;gt; field of unit data manually in a hackish fashion. There isn't really much of a way to work around it, besides just the &amp;quot;don't do that&amp;quot; solution.&lt;br /&gt;
* &amp;lt;code&amp;gt;tag stack nonempty (%s) at end of file.&amp;lt;/code&amp;gt;: This means that you have unbalanced tags somewhere in the file, e.g. an opener without a closer, or vice versa. This can often be seen when defining macros for unit abilities. A way to fix this warning is to wrap the section with unbalanced tags with a &amp;lt;code&amp;gt;# wmllint: unbalanced-on&amp;lt;/code&amp;gt; magic comment beforehand and a &amp;lt;code&amp;gt;# wmllint: unbalanced-off&amp;lt;/code&amp;gt; magic comment afterwards. As having unbalanced tags will also cause issues for other WML maintenance tools, such as &amp;lt;tt&amp;gt;wmlindent&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;wmlxgettext&amp;lt;/tt&amp;gt;, you may also want to add separate magic comments for each of them (see their documentation for the form they take).&lt;br /&gt;
* &amp;lt;code&amp;gt;unit declaration without side attribute&amp;lt;/code&amp;gt;: the default side for a unit declaration when left implicit is side 1. Specify your unit sides explicitly to solve this.&lt;br /&gt;
* &amp;lt;code&amp;gt;no %s units recruitable at difficulty %s&amp;lt;/code&amp;gt; (even when there are such units recruitable): This diagnostic has to do with matching the &amp;lt;code&amp;gt;usage&amp;lt;/code&amp;gt; key of units recruitable by an AI side with their &amp;lt;code&amp;gt;recruitment_pattern&amp;lt;/code&amp;gt;. It means the unit has a usage that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can know which mainline units are recruitable. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;%s has unknown movement type&amp;lt;/code&amp;gt; (even when you'd think that that movement type ''would'' actually be known): This means the unit has a movetype that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can load the mainline movement types. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;%s has unknown race&amp;lt;/code&amp;gt; (even when you'd think that that race ''would'' actually be known): This means the unit has a race that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can load the mainline races. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;derivation of %s from %s does not resolve&amp;lt;/code&amp;gt; (even when you'd think it would): This means that a unit using the [[UnitTypeWML#Other_tags|[base_unit]]] tag specifies a unit ID in that tag that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can load the core units for its derivation checks. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;[advancefrom] needs to be manually updated to [modify_unit_type] and moved into the _main.cfg file&amp;lt;/code&amp;gt;: This one is pretty self-explanatory: [[UnitTypeWML#Unit_Type|[advancefrom]]] was deprecated in [https://github.com/wesnoth/wesnoth/commit/3950f40f3f0483032bc70b3e57166bd355acd9fc commit 3950f40] due to [https://github.com/wesnoth/wesnoth/issues/3955 issue #3955], and in fact doesn't even work anymore (in 1.16) as per [https://github.com/wesnoth/wesnoth/issues/6204 issue #6204]. The main reason &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; can't fix this automatically is because it could end up being too complicated for it to figure out which files to edit if there are multiple uses of &amp;lt;code&amp;gt;[advancefrom]&amp;lt;/code&amp;gt;, and it also doesn't want to assume where to put the [[ModificationWML|[modify_unit_type]]] tag in &amp;lt;tt&amp;gt;_main.cfg&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== wmlindent ==&lt;br /&gt;
&lt;br /&gt;
Call with no arguments to filter WML on standard input to reindented WML on&lt;br /&gt;
standard output.  If arguments are specified, they are taken to be files to be&lt;br /&gt;
re-indented in place; a directory name causes reindenting on all WML&lt;br /&gt;
beneath it.&lt;br /&gt;
&lt;br /&gt;
The indent unit is four spaces.  Absence of an option to change this is&lt;br /&gt;
deliberate; the purpose of this tool is to ''prevent'' style wars, not encourage&lt;br /&gt;
them.&lt;br /&gt;
&lt;br /&gt;
On non-empty lines, this code never modifies anything but leading and&lt;br /&gt;
trailing whitespace. Leading whitespace will be regularized to the&lt;br /&gt;
current indent; trailing whitespace will be stripped.  After processing&lt;br /&gt;
all lines will end with a Unix-style &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt; end-of-line marker.&lt;br /&gt;
&lt;br /&gt;
Runs of entirely blank lines will be reduced to one blank line, except&lt;br /&gt;
in two cases where they will be discarded: (a) before WML closing&lt;br /&gt;
tags, and (b) after WML opening tags.&lt;br /&gt;
&lt;br /&gt;
It is possible to wrap a section of lines in special comments so that&lt;br /&gt;
&amp;lt;tt&amp;gt;wmlindent&amp;lt;/tt&amp;gt; will ignore them.  You may need to do this for unbalanced&lt;br /&gt;
macros (it's better, though, to get rid of those where possible).&lt;br /&gt;
Use '&amp;lt;code&amp;gt;wmlindent: {start,stop} ignoring&amp;lt;/code&amp;gt;' anywhere in a comment.&lt;br /&gt;
&lt;br /&gt;
It is also possible to declare custom openers an closers, e.g for macros&lt;br /&gt;
that are actually control constructs.  To do this, use declarations&lt;br /&gt;
&lt;br /&gt;
    # wmlindent: opener &amp;quot;{EXCEPTIONAL_OPENER &amp;quot;&lt;br /&gt;
    # wmlindent: closer &amp;quot;{EXCEPTIONAL_CLOSER &amp;quot;&lt;br /&gt;
&lt;br /&gt;
The lines after an opener will be indented an extra level; a closer&lt;br /&gt;
and lines following will be indented one level less. Note that these&lt;br /&gt;
declare prefixes; any prefix match to the non-whitespace text of a line&lt;br /&gt;
will be recognized.&lt;br /&gt;
&lt;br /&gt;
The public utility macros &amp;quot;&amp;lt;code&amp;gt;{FOREACH&amp;lt;/code&amp;gt;&amp;quot; and &amp;quot;&amp;lt;code&amp;gt;{NEXT&amp;lt;/code&amp;gt;&amp;quot; come as wired-in exceptions,&lt;br /&gt;
because it is not guaranteed that their indent declarations will be processed&lt;br /&gt;
before the macro library is reached.&lt;br /&gt;
&lt;br /&gt;
Interrupting &amp;lt;tt&amp;gt;wmlindent&amp;lt;/tt&amp;gt; ought to be safe, as each reindenting will be done to a copy&lt;br /&gt;
that is atomically renamed when it's done.  If the output file is identical&lt;br /&gt;
to the input, the output file will simply be deleted, so the timestamp&lt;br /&gt;
on the input file won't be touched.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;--dryrun&amp;lt;/tt&amp;gt; option detects and reports files that would be changed&lt;br /&gt;
without changing them. The &amp;lt;tt&amp;gt;--verbose&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;-v&amp;lt;/tt&amp;gt; option enables reporting&lt;br /&gt;
of files that are (or would be, under &amp;lt;tt&amp;gt;--dryrun&amp;lt;/tt&amp;gt;) changed.  With &amp;lt;tt&amp;gt;-v -v&amp;lt;/tt&amp;gt;,&lt;br /&gt;
unchanged files are also reported.  The &amp;lt;tt&amp;gt;--exclude&amp;lt;/tt&amp;gt; option takes a regexp&lt;br /&gt;
and excludes files matching it.&lt;br /&gt;
&lt;br /&gt;
If you don't apply this tool to your own WML that you wish to submit, the&lt;br /&gt;
mainline-campaign maintainers will do it when and if your code is accepted into the tree.&lt;br /&gt;
&lt;br /&gt;
Note: This tool does not include a parser.  It will produce bad results on WML&lt;br /&gt;
that is syntactically unbalanced.  Unbalanced double quotes that aren't part&lt;br /&gt;
of a multiline literal will also confuse it.  You will receive warnings&lt;br /&gt;
if there's an indent open at end of file or if a closer occurs with&lt;br /&gt;
indent already zero; these two conditions strongly suggest unbalanced WML.&lt;br /&gt;
&lt;br /&gt;
== GUI.pyw ==&lt;br /&gt;
&lt;br /&gt;
Starting from version 1.11.15 and 1.13.0, a GUI (written in Tkinter, plus the themed widgets ttk) is available in the same directory as the other tools. To use it, you need to have a version of Python equal to or greater than 3.1.0 (the 3.0.x series doesn't include the ttk widgets, and as such is unsuitable for this script).&lt;br /&gt;
&lt;br /&gt;
If you're on Linux, be sure to have installed the ''python3-tk'' module, '''or the application won't run at all'''. To install it in a Debian-based distro (like Ubuntu), type this line in a Terminal:&lt;br /&gt;
 sudo apt install python3-tk&lt;br /&gt;
&lt;br /&gt;
To start it, just double click on the GUI.pyw file. The interface is pretty much self-explanatory, and allows you to run wmllint, wmlscope, wmlindent and wmlxgettext, modify their options, select an add-on and save the tools' output as a text file.&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
* [[Translation Maintenance Commands]] (for &amp;lt;tt&amp;gt;wmlxgettext&amp;lt;/tt&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
[[Category:Create]]&lt;br /&gt;
[[Category:Tools]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Maintenance_tools&amp;diff=75481</id>
		<title>Maintenance tools</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Maintenance_tools&amp;diff=75481"/>
		<updated>2026-07-03T16:33:25Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* wmllint */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;floatright&amp;quot;&amp;gt; __TOC__ &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Wesnoth source code distribution includes a couple of tools intended to help authors maintain campaigns, faction &amp;amp; unit packs, and other WML resources. These&lt;br /&gt;
are:&lt;br /&gt;
 &lt;br /&gt;
; wmlscope: a cross-reference lister, useful for finding unresolved macro and resource-file references.&lt;br /&gt;
&lt;br /&gt;
; wmllint: a utility for sanity-checking WML syntax and porting your old WML to the current version of WML.  &lt;br /&gt;
&lt;br /&gt;
; wmlindent: a utility for reindenting WML to a uniform style.&lt;br /&gt;
&lt;br /&gt;
; GUI.pyw: a graphical interface&lt;br /&gt;
&lt;br /&gt;
== General Information ==&lt;br /&gt;
&lt;br /&gt;
You will need a Python 3 interpreter on your system to use these tools.  Linux, *BSD, and Mac OS/X should already have Python 3 installed; for Windows it's a free download&lt;br /&gt;
from http://www.python.org.  You will also need to know how to run command-line tools on your system.&lt;br /&gt;
&lt;br /&gt;
If you're working with Debian or Ubuntu you might have to install the package wesnoth-1.16-tools (or the convenient version).&lt;br /&gt;
 sudo apt install wesnoth-1.16-tools&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
All three tools will require you to supply a &amp;lt;i&amp;gt;directory list&amp;lt;/i&amp;gt;.  This is a set of directories containing the WML files you want to work on.&lt;br /&gt;
&lt;br /&gt;
This page is intended as documentation for users.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Note to Windows Users:&amp;lt;/u&amp;gt; This means you have to run it from the '''Command Line'''. The command line may be reached by hitting Start, then Run, then &amp;quot;cmd&amp;quot; or &amp;quot;command&amp;quot; depending on your version of Windows.&lt;br /&gt;
&lt;br /&gt;
Example uses:&lt;br /&gt;
 python wmllint path\to\files&lt;br /&gt;
 python wmlindent path\to\files&lt;br /&gt;
&lt;br /&gt;
Another example:&lt;br /&gt;
 &amp;quot;C:\Program Files\Python3.7\python.exe&amp;quot; data\tools\wmllint --dryrun data\core data\{multiplayer,themes} data\campaigns &lt;br /&gt;
(You have to specify the full directory path to the executable if you don't have your environment variables set up correctly).&lt;br /&gt;
The first thing you type is the path to your python executable, followed by a space. The second thing you type is the path to the desired script to run, followed by a space. The third thing you type is the path to the folder (or file) to be processed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''A convenient way of running wmllint''' on Linux (Debian or Ubuntu) and Windows in comparison, '''Linux''':&lt;br /&gt;
&lt;br /&gt;
Assuming we're working with wesnoth 1.16 or more advanced versions.&lt;br /&gt;
 python3 /usr/share/games/wesnoth/1.16/data/tools/wmllint --dryrun /usr/share/games/wesnoth/1.16/data/core ~/.local/share/wesnoth/1.16/data/add-ons/A_Simple_Campaign 1&amp;gt;wmllint-run.log 2&amp;gt;wmllint-err.log&lt;br /&gt;
I have these commands inside of a file named&lt;br /&gt;
 wmllint_dryrun_ASC.sh&lt;br /&gt;
and execute it by opening a shell (=terminal, console, command window, bash,...), navigating into the directory with that file and typing&lt;br /&gt;
 bash wmllint_dryrun_ASC.sh&lt;br /&gt;
The python3 command should be automatically known on Debian. The path to the script tells the python interpreter what to execute. --dryrun: A wmllint option, see below. The path to the core files is needed to let wmllint know about e.g. defined core units, followed by the path to the add-on that shall be checked; the last two commands cause the result of the wmllint usage to be written into those files in the same directory as the script.&lt;br /&gt;
'''Windows''', this is logically exactly the same as the Linux shell script above, so if you are on a Mac you can probably conclude how you need to adapt the paths:&lt;br /&gt;
 E:\Python37\python.exe E:\Programme\Wesnoth_1.16_git\data\tools\wmllint --dryrun E:\Programme\Wesnoth_1.16_git\data\core E:\Programme\Wesnoth_1.16_git\userdata\data\add-ons\A_Simple_Campaign 1&amp;gt;wmllint-run.log 2&amp;gt;wmllint-err.log&lt;br /&gt;
This is the content of a .txt file, whose extension I rename to .bat and double-click onto it. Opening a command window is not needed this way.&lt;br /&gt;
Since Python isn't natively installed on windows and I don't have environment variables set, the full path to python.exe is given. If your directories contain spaces it may help to include the path in quotes:&lt;br /&gt;
 &amp;quot;C:\Programs\Battle for Wesnoth 1.16\data\tools\wmllint&amp;quot;&lt;br /&gt;
Remember that you do not need to enter all of the commands/paths at once. If it doesn't work, start with only &amp;quot;python&amp;quot; or &amp;quot;C:\Python37\python.exe&amp;quot; or the like and interpret the error messages that you get. If you get an &amp;quot;unknown command&amp;quot;, python isn't installed or environment variables aren't set correctly. After that, you can add the later commands one by one.&lt;br /&gt;
&lt;br /&gt;
== wmlscope ==&lt;br /&gt;
&lt;br /&gt;
The main use for &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; is to find WML macro references without definitions and references to resource files (sounds and images) that don't exist.  These are difficult to spot from in-game because they usually result in silence or a missing image rather than actual broken game logic (see [https://github.com/wesnoth/wesnoth/issues/5332 issue 5332] for more info).  They may happen because of typos in your WML, or because the name of a macro or the location of a resource file changed between versions of the game.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; also checks macro invocations for consistency.  It will complain&lt;br /&gt;
if a macro is called with the wrong number of arguments.  In most cases it can deduce information about the type of the literal expected to be passed to a given macro argument by looking at the name of the formal.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table class=&amp;quot;wikitable&amp;quot;&amp;gt;&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;Type&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;Meaning&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;Formals requiring this type&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;th&amp;gt;Literals of this type&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;side&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a single side number&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;SIDE, *_SIDE, SIDE[0-9]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a numeric or &amp;quot;global&amp;quot;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;numeric&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a numeric integer literal&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;SIDE, X, Y, RED, GREEN, BLUE, TURN, PROB, LAYER, TIME, *_SIDE, *NUMBER, *AMOUNT, *COST, *RADIUS, *_X, *_Y, *_INCREMENT, *_FACTOR, *_TIME, *_SIZE, DURATION&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;\-?[0-9]+&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;percentage&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a percentage&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;*PERCENTAGE&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a numeric or 0\.[0-9]+&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;position&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a single x,y coordinate&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;POSITION, *_POSITION, BASE&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;-?[0-9]+,-?[0-9]+&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;span&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a set of coordinates or coordinate ranges&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;*_SPAN&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a numeric, position or ([0-9]+\-[0-9]+,?|[0-9]+,?)+&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;alliance&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a set of side numbers&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;SIDES, *_SIDES&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a span, or the empty string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;range&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;an attack range&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;RANGE&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;quot;melee&amp;quot; or &amp;quot;ranged&amp;quot;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;alignment&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;an alignment keyword&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;ALIGN&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;quot;lawful&amp;quot; or &amp;quot;neutral&amp;quot; or &amp;quot;chaotic&amp;quot;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;types&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a set of unit types&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;TYPES&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a shortname, name, or anything that contains spaces and matches no other type&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;terrain_pattern&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a set of terrain codes to filter&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;ADJACENT*, TERRAINLIST*, *TERRAIN_PATTERN, RESTRICTING&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a terrain_code or name&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;terrain_code&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a single terrain code, perhaps with overlay&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;TERRAIN*, *TERRAIN&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a shortname or (\*|[A-Z][a-z]+)\^([A-Z][a-z\\|/]+\Z)?&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;shortname&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a terrain code or a short, capitalized variable name&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[A-Z][a-z][a-z]?&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;name&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a name or identifier&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;NAME, VAR, IMAGESTEM, ID, FLAG, *_NAME, *_ID, NAMESPACE, BUILDER, *_VAR&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;anything without spaces that matches no other type&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;optional_string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a string value (may be empty)&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;ID_STRING, NAME_STRING, DESCRIPTION, IPF&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a string, or the empty string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a nonempty string not matching any of the preceding types&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;STRING, TYPE, TEXT, *_STRING, *_TYPE, *_TEXT&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a shortname, a name, a stringliteral, or anything that contains spaces and matches no other type&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;stringliteral&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a string in doublequotes or a translated string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&amp;quot;.*&amp;quot; or _.* but not _[a-z].*&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;image&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;an image path, perhaps with [[ImagePathFunctionWML|image path functions]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;*IMAGE, PROFILE&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[A-Za-z0-9{}.][A-Za-z0-9_/+{}.-]*\.(png|jpg)(?=(~.*)?)&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;sound&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a music or sound filename&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;MUSIC, SOUND&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;string ending with &amp;quot;.wav&amp;quot; or &amp;quot;.ogg&amp;quot;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;filter&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[FilterWML|WML filter]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;FILTER&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;any non-quoted string containing &amp;quot;=&amp;quot;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;WML&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;arbitrary WML fragment&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;WML, *_WML&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;any non-quoted string containing &amp;quot;=&amp;quot;, or the empty string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;affix&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a prefix, suffix, or infix for a variable name&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;AFFIX, *AFFIX, POSTFIX, ROTATION&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;a shortname or name, or the empty string&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;any&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;anything&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;*VALUE, [ARS][0-9]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;anything&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the actual argument is a macro call {.*}, then it matches any formal.  Otherwise, if the formal has an identifiable type, &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; will complain if the actual literal does not match it.&lt;br /&gt;
&lt;br /&gt;
The argument type check only works in macro calls that fit on a single line.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; has many options for changing the reports it generates; the more advanced ones are intended for Wesnoth developers.  Invocations for the most commonly useful reports it generates are included in &amp;lt;i&amp;gt;data/tools/Makefile&amp;lt;/i&amp;gt; of the source distribution. Here are some of those reports:&lt;br /&gt;
&lt;br /&gt;
; make unresolved: Report on unresolved macro calls and resource references; also report macro argument-type mismatches.  (This is what you are most likely to want to do). &lt;br /&gt;
&lt;br /&gt;
; make all: Report all macro and resource file references, not just unresolved ones.&lt;br /&gt;
&lt;br /&gt;
; make collisions: Report on duplicate resource files.&lt;br /&gt;
&lt;br /&gt;
For more advanced users, or those who want to understand what the canned Makefile invocations are doing, here is a summary of &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt;'s options. Some of the more advanced options will require you to understand &lt;br /&gt;
[http://docs.python.org/lib/re-syntax.html Python regular expressions].&lt;br /&gt;
&lt;br /&gt;
; -h, --help:                 Emit a help message and quit&lt;br /&gt;
; -c, --crossreference:       Report resolved macro references (implies &amp;lt;tt&amp;gt;-w 1&amp;lt;/tt&amp;gt;)&lt;br /&gt;
; -C, --collisions:           Report duplicate resource files   &lt;br /&gt;
; -d, --deflist:              Make definition list.  (This one is for campaign server maintainers.)&lt;br /&gt;
; -e &amp;lt;i&amp;gt;regexp&amp;lt;/i&amp;gt;, --exclude &amp;lt;i&amp;gt;regexp&amp;lt;/i&amp;gt;:   Ignore files matching the specified regular expression. &lt;br /&gt;
; -f &amp;lt;i&amp;gt;dir&amp;lt;/i&amp;gt;, --from &amp;lt;i&amp;gt;dir&amp;lt;/i&amp;gt;:         Report only on macros defined under &amp;lt;i&amp;gt;dir&amp;lt;/i&amp;gt;&lt;br /&gt;
; -l, --listfiles:            List files that will be processed&lt;br /&gt;
; -r &amp;lt;i&amp;gt;ddd&amp;lt;/i&amp;gt;, --refcount=&amp;lt;i&amp;gt;ddd&amp;lt;/i&amp;gt;:     Report only on macros with references in exactly &amp;lt;i&amp;gt;ddd&amp;lt;/i&amp;gt; files.&lt;br /&gt;
; -t &amp;lt;i&amp;gt;TYPELIST&amp;lt;/i&amp;gt;, --typelist &amp;lt;i&amp;gt;TYPELIST&amp;lt;/i&amp;gt;: List actual &amp;amp; formal argtypes for calls in fname&lt;br /&gt;
; -u, --unresolved:           Report unresolved macro references&lt;br /&gt;
; -w, --warnlevel:            Set to 1 to warn of duplicate macro definitions&lt;br /&gt;
; -p, --progress:             Show progress&lt;br /&gt;
; --force-used reg:           Ignore reference count 0 on names matching regexp&lt;br /&gt;
; --extracthelp:              Extract help from macro definition comments.&lt;br /&gt;
; --unchecked:                Report all macros with untyped formals.&lt;br /&gt;
; --version:                  show program's version number and exit&lt;br /&gt;
&lt;br /&gt;
These options are used with a list of directories as arguments; if none is given,&lt;br /&gt;
&amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; behaves as though the current directory had been specified as a&lt;br /&gt;
single argument.  Each directory is treated as a separate domain for&lt;br /&gt;
macro and resource visibility purposes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; recognizes two kinds of namespace, exporting and non-exporting.&lt;br /&gt;
Exporting namespaces make all their resources and macro names&lt;br /&gt;
globally visible.  You can make a namespace exporting by embedding&lt;br /&gt;
a comment like this in it:&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: export=yes&lt;br /&gt;
&lt;br /&gt;
Wesnoth core data is an exporting namespace.  Campaigns are non-exporting;&lt;br /&gt;
they should contain the declaration&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: export=no&lt;br /&gt;
&lt;br /&gt;
somewhere.  &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; will complain when it sees a namespace with no export&lt;br /&gt;
property, then treat it as non-exporting.&lt;br /&gt;
&lt;br /&gt;
You can tell &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; to ignore stretches of config files&lt;br /&gt;
with the following magic comments:&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: start ignoring&lt;br /&gt;
    # wmlscope: stop ignoring&lt;br /&gt;
&lt;br /&gt;
Similarly, you can tell &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; to ignore multiple or duplicate macro&lt;br /&gt;
definitions in a range of lines with the following magic comments:&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: start conditionals&lt;br /&gt;
    # wmlscope: stop conditionals&lt;br /&gt;
&lt;br /&gt;
The following magic comment:&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: prune FOOBAR&lt;br /&gt;
&lt;br /&gt;
will cause &amp;lt;tt&amp;gt;wmlscope&amp;lt;/tt&amp;gt; to forget about all but one of the definitions of&lt;br /&gt;
&amp;lt;tt&amp;gt;FOOBAR&amp;lt;/tt&amp;gt; it has seen.  This will be useful mainly for symbols that have&lt;br /&gt;
different definitions enabled by an &amp;lt;tt&amp;gt;#ifdef&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Due to a preprocessor limitation, inline macros cannot contain a documentation&lt;br /&gt;
string. If you need to document these macros in the HTML macro reference, you&lt;br /&gt;
can use the following directive:&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: docstring FOOBAR&lt;br /&gt;
&lt;br /&gt;
The docstring for the FOOBAR macro will be collected until a non-comment line,&lt;br /&gt;
a &amp;lt;tt&amp;gt;#define&amp;lt;/tt&amp;gt; or another &amp;lt;tt&amp;gt;# wmlscope: docstring&amp;lt;/tt&amp;gt; are found. External&lt;br /&gt;
docstrings '''''must''''' be defined before the macro to which they refer; defining&lt;br /&gt;
two or more external docstrings keeps only the most recent one, but having both an&lt;br /&gt;
external and an internal docstring is allowed (in this case, the internal one&lt;br /&gt;
will be appended to the external one in the macro reference).&lt;br /&gt;
&lt;br /&gt;
This tool does catch one kind of implicit reference: if an attack name&lt;br /&gt;
is specified but no icon is given, the attack icon will default to&lt;br /&gt;
a name generated from the attack name.  This behavior can be suppressed&lt;br /&gt;
by adding a magic comment containing the string &amp;quot;no-icon&amp;quot; to the &amp;lt;tt&amp;gt;name=&amp;lt;/tt&amp;gt;&lt;br /&gt;
line.&lt;br /&gt;
&lt;br /&gt;
The checking done by this tool has a couple of flaws:&lt;br /&gt;
&lt;br /&gt;
(1) It doesn't actually evaluate file inclusions.  Instead, any&lt;br /&gt;
macro definition satisfies any macro call made under the same&lt;br /&gt;
directory.  Exception: when an &amp;lt;tt&amp;gt;#undef&amp;lt;/tt&amp;gt; is detected, the macro is&lt;br /&gt;
tagged local and not visible outside the span of lines where it was&lt;br /&gt;
defined.&lt;br /&gt;
&lt;br /&gt;
(2) It doesn't read &amp;lt;tt&amp;gt;[binary_path]&amp;lt;/tt&amp;gt; tags, as this would require&lt;br /&gt;
implementing a WML parser.  Instead, it assumes that a resource-file&lt;br /&gt;
reference can be satisfied by any matching image file from anywhere&lt;br /&gt;
in the same directory it came from.  The resources under the '''''first'''''&lt;br /&gt;
directory argument (only) are visible everywhere.&lt;br /&gt;
&lt;br /&gt;
(3) A reference with embedded {}s in a macro will have the macro's&lt;br /&gt;
formal args substituted in at WML evaluation time.  Instead, this&lt;br /&gt;
tool treats each {} as a .* wildcard and considers the reference to&lt;br /&gt;
match '''''every''''' resource filename that matches that pattern.&lt;br /&gt;
Under appropriate circumstances this might report a resource filename&lt;br /&gt;
statically matching the pattern as having been referenced even&lt;br /&gt;
though none of the actual macro calls would actually generate it.&lt;br /&gt;
&lt;br /&gt;
Problems (1) and (2) imply that this tool might conceivably report&lt;br /&gt;
that a reference has been satisfied when under actual&lt;br /&gt;
WML-interpreter rules it has not.&lt;br /&gt;
&lt;br /&gt;
The reporting format is compatible with GNU Emacs compile mode.&lt;br /&gt;
&lt;br /&gt;
For debugging purposes, an in-line comment of the form&lt;br /&gt;
&lt;br /&gt;
    # wmlscope: warnlevel NNN&lt;br /&gt;
&lt;br /&gt;
sets the warning level.&lt;br /&gt;
&lt;br /&gt;
== wmllint ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; is a tool for migrating your WML to the current version.  It handles two problems: &lt;br /&gt;
&lt;br /&gt;
* Resource files and macro names may change between versions of the game. &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; knows about these changes and will tweak your WML to fit where it can.&lt;br /&gt;
&lt;br /&gt;
* Between 1.2.x and 1.3.1 the terrain-coding system used in map files underwent a major change. It changed again in a minor way between 1.3.1 and 1.3.2. If you port such old code, use &amp;lt;tt&amp;gt;wmllint-1.4&amp;lt;/tt&amp;gt;, which is located in the same directory as &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;. It will translate your maps for you, unless you use custom terrains in which case you will have to do it by hand.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; also performs various sanity-checking operations, reporting:&lt;br /&gt;
&lt;br /&gt;
* unbalanced tags&lt;br /&gt;
* strings that need a translation mark and do not have them&lt;br /&gt;
* strings that have a translation mark and should not&lt;br /&gt;
* translatable strings containing macro references &lt;br /&gt;
* filter references by description= (id= in 1.5) not matched by an actual unit&lt;br /&gt;
* abilities or traits without matching special notes, or vice-versa&lt;br /&gt;
* consistency between recruit= and recruitment_pattern= instances&lt;br /&gt;
* double space after punctuation in translatable strings.&lt;br /&gt;
* unknown races or movement types in units&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; takes a directory-path argument specifying the WML directories to work on.  It will modify any cfg and map files under those directories that need to be changed.  Here is a summary of its options:&lt;br /&gt;
&lt;br /&gt;
; -h, --help:                 Emit a help message and quit.&lt;br /&gt;
; -c, --clean:                Clean up -bak files.&lt;br /&gt;
; -D, --diffs:                Display diffs between converted and unconverted files.&lt;br /&gt;
; -d, --dryrun:               List changes (-v) but don't perform them.&lt;br /&gt;
; -r, --revert:               Revert the conversion from the -bak files.&lt;br /&gt;
; -m, --missing:              Warn about tags without side= keys now applying to all sides.&lt;br /&gt;
; -s, --stripcr:              Convert DOS-style CR/LF to Unix-style LF.&lt;br /&gt;
; -v, --verbose:              Set verbosity; more details below.&lt;br /&gt;
; -K, --known:                Suppress check for unknown unit types, recruits, races, scenarios, etc.&lt;br /&gt;
; -S, --nospellcheck:         Suppress spellchecking&lt;br /&gt;
; --version:                  show program's version number and exit&lt;br /&gt;
; --config:                   allows specifying directories to include ('''include_dirs'''), directories to exclude ('''ignore_directories'''), and files to exclude ('''ignore_files''').&lt;br /&gt;
&lt;br /&gt;
The verbosity option works like this:&lt;br /&gt;
&lt;br /&gt;
; -v:          lists changes.&lt;br /&gt;
; -v -v:       warns of maps already converted.&lt;br /&gt;
; -v -v -v:    names each file before it's processed.&lt;br /&gt;
; -v -v -v -v: shows verbose parse details (developers only).&lt;br /&gt;
&lt;br /&gt;
The recommended procedure is this:&lt;br /&gt;
&lt;br /&gt;
# Run it with --dryrun first to see what it will do.&lt;br /&gt;
# If the messages look good, run without --dryrun; the old content will be left in backup files with a -bak extension.&lt;br /&gt;
# Eyeball the changes with the --diff option.&lt;br /&gt;
# Use wmlscope, with a directory path including the Wesnoth mainline WML, to check that you have no unresolved references.&lt;br /&gt;
# Test the conversion.&lt;br /&gt;
# Use either --clean to remove the -bak files or --revert to undo the conversion.&lt;br /&gt;
&lt;br /&gt;
Additionally, wmllint tries to locate a spell checker on your system and spell-checks storyline and message strings.  It will work automatically with any of [https://github.com/AbiWord/enchant enchant]'s spellchecking backends (including aspell, myspell, ispell, applespell, hunspell, nuspell, and so on, depending on the version of enchant), provided you have the &amp;lt;tt&amp;gt;enchant.py&amp;lt;/tt&amp;gt; Python library installed. Note that the spellchecking results can vary depending on which backend enchant decides to use.&lt;br /&gt;
&lt;br /&gt;
wmllint supports a number of magic comments to customize its behaviour and avoid false positives. All magic wmllint comments begin with the string &amp;lt;tt&amp;gt;wmllint:&amp;lt;/tt&amp;gt;, followed by some additional keyword and potentially some arguments. In the below explanations, a string of the form &amp;lt;code&amp;gt;[a|b]&amp;lt;/code&amp;gt; means you may use either &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;b&amp;lt;/code&amp;gt; at that location, while a string of the form &amp;lt;code&amp;gt;&amp;lt;arg&amp;gt;&amp;lt;/code&amp;gt; indicates free text substitution.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ignore&amp;lt;/code&amp;gt;: Disables checking of terrains and translation marks on the current line only.&lt;br /&gt;
* &amp;lt;code&amp;gt;noconvert&amp;lt;/code&amp;gt;: Disables conversion of terrains and image/sound filenames on the current line only.&lt;br /&gt;
* &amp;lt;code&amp;gt;markcheck [on|off]&amp;lt;/code&amp;gt;: Enables or disables translation mark checking from the current (next?) line onward.&lt;br /&gt;
* &amp;lt;code&amp;gt;no-icon&amp;lt;/code&amp;gt;: If an attack has no description, without this wmllint adds a dummy description to the attack which is just the name of the attack. With this, this description insertion is suppressed.&lt;br /&gt;
* &amp;lt;code&amp;gt;recognize &amp;lt;name&amp;gt;&amp;lt;/code&amp;gt;: Indicates that the character with the given name exists even though a declaration (ie a &amp;lt;code&amp;gt;[unit]&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;[side]&amp;lt;/code&amp;gt; tag) is not visible.&lt;br /&gt;
* &amp;lt;code&amp;gt;whofield &amp;lt;macro&amp;gt; &amp;lt;number&amp;gt;&amp;lt;/code&amp;gt;: Indicates that the specified macro declares a character whose name is given by the specified argument to the macro.&lt;br /&gt;
* &amp;lt;code&amp;gt;whofield clear &amp;lt;macro&amp;gt;&amp;lt;/code&amp;gt;: Removes the &amp;lt;code&amp;gt;whofield&amp;lt;/code&amp;gt; definition for the specified macro.&lt;br /&gt;
* &amp;lt;code&amp;gt;who &amp;lt;macro&amp;gt; is &amp;lt;name&amp;gt; &amp;lt;name&amp;gt; ...&amp;lt;/code&amp;gt;: Indicates that the specified macro declares one or more characters whose names are given as a space-separated list. This can also be used for macros that auto-recall a list of characters, some of which join later or may die at some point. If a definition for the macro already exists, then the specified names are appended to it. If a name is preceded by a double minus (&amp;lt;code&amp;gt;-- &amp;lt;name&amp;gt;&amp;lt;/code&amp;gt;) then it is removed from the definition.&lt;br /&gt;
* &amp;lt;code&amp;gt;unwho all|&amp;lt;name&amp;gt;&amp;lt;/code&amp;gt;: Removes the &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt; definition for the specified macro, or all &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt; definitions.&lt;br /&gt;
* &amp;lt;code&amp;gt;usage of &amp;quot;&amp;lt;unit&amp;gt;&amp;quot; is &amp;lt;class&amp;gt;&amp;lt;/code&amp;gt;: Declares the usage of the specified unit (the &amp;lt;code&amp;gt;usage=&amp;lt;/code&amp;gt; key in the &amp;lt;code&amp;gt;[unit_type]&amp;lt;/code&amp;gt; tag). Useful if you are using macros to generate several similar unit types.&lt;br /&gt;
* &amp;lt;code&amp;gt;usagetype &amp;lt;class&amp;gt;&amp;lt;/code&amp;gt;: Declares a valid usage type for units. &amp;lt;code&amp;gt;usagetypes&amp;lt;/code&amp;gt; is also recognized, and a comma-separated list of usage types can be specified.&lt;br /&gt;
* &amp;lt;code&amp;gt;validate-[on|off]&amp;lt;/code&amp;gt;: Enables or disables stack-based validation checks. Use when you have unbalanced tags in macros.&lt;br /&gt;
* &amp;lt;code&amp;gt;unbalanced-[on|off]&amp;lt;/code&amp;gt;: Similar to above, the precise difference is unclear.&lt;br /&gt;
* &amp;lt;code&amp;gt;no translatables&amp;lt;/code&amp;gt;: Suppresses warnings about a missing textdomain declaration in the current file. Make sure the file really does have no translatable strings!&lt;br /&gt;
* &amp;lt;code&amp;gt;display [on|off]&amp;lt;/code&amp;gt;: Enable or disable warnings about newlines in messages.&lt;br /&gt;
* &amp;lt;code&amp;gt;notecheck [on|off]&amp;lt;/code&amp;gt;: Enable or disable note consistency checks for unit descriptions.&lt;br /&gt;
* &amp;lt;code&amp;gt;deathcheck [on|off]&amp;lt;/code&amp;gt;: Enable or disable the check for units speaking in their death events.&lt;br /&gt;
* &amp;lt;code&amp;gt;[general|directory|local] spellings &amp;lt;word&amp;gt; &amp;lt;word&amp;gt; ...&amp;lt;/code&amp;gt;: Declares the specified space-separated list of words to be valid spellings in the specified context. The context &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt; indicates the current file only, while &amp;lt;code&amp;gt;directory&amp;lt;/code&amp;gt; means the current file and any siblings in the same directory or subdirectories. The &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; context indicates the spellings are valid anywhere.&lt;br /&gt;
* &amp;lt;code&amp;gt;no spellcheck&amp;lt;/code&amp;gt;: Disables spell checking on the current line.&lt;br /&gt;
* &amp;lt;code&amp;gt;skip-side&amp;lt;/code&amp;gt;: Indicates that there is a missing side declaration at this location that will be provided by a macro expansion.&lt;br /&gt;
* &amp;lt;code&amp;gt;match &amp;lt;string&amp;gt; with &amp;lt;notes_macro&amp;gt;&amp;lt;/code&amp;gt;: Indicates that uses of the specified string in a unit definition (usually a macro, including the curly braces) should be matched up with the specified special notes macro (which is also specified as the full macro with curly braces).&lt;br /&gt;
* &amp;lt;code&amp;gt;no ellipsecheck&amp;lt;/code&amp;gt;: Disables checking of unit ellipses on the current line.&lt;br /&gt;
&lt;br /&gt;
=== Explanations of &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; diagnostics ===&lt;br /&gt;
Some &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; diagnostics may require further explanation for UMC authors to understand; this section will be for providing such explanations, and descriptions of how to solve and/or silence them.&lt;br /&gt;
&lt;br /&gt;
In these, &amp;lt;code&amp;gt;%s&amp;lt;/code&amp;gt; will be replaced by a string. When the recommended solution also includes a &amp;lt;code&amp;gt;%s&amp;lt;/code&amp;gt;, it's a suggestion to copy the string from the error message into your code.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;nonstandard word-wrap style within message&amp;lt;/code&amp;gt;: This message meant there was an unexpected newline character within a &amp;lt;code&amp;gt;[message]&amp;lt;/code&amp;gt; tag. However, this check was removed in the 1.15.10 release. Pre-1.15.10, add-on developers could silence by putting a &amp;lt;code&amp;gt;# wmllint: display on&amp;lt;/code&amp;gt; comment before the string and a &amp;lt;code&amp;gt;# wmllint: display off&amp;lt;/code&amp;gt; comment after the string, however, post-1.15.10, this is no longer necessary.&lt;br /&gt;
* &amp;lt;code&amp;gt;%s is not a known unit type&amp;lt;/code&amp;gt; (in cases where you'd think the unit type ''would'' be known): This means the unit has a type that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can load the mainline unit types. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;unknown speaker '%s' of [message]&amp;lt;/code&amp;gt;: use a &amp;lt;code&amp;gt;# wmllint: recognize %s&amp;lt;/code&amp;gt; magic comment, or, alternatively, if the speaker is created by a macro, use a magic comment of the form of either &amp;lt;code&amp;gt;# wmllint: who MACRO is SPEAKER&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;# wmllint: whofield MACRO NUMBER&amp;lt;/code&amp;gt;, depending on whether the macro takes an argument for the unit's name or not.&lt;br /&gt;
* &amp;lt;code&amp;gt;unknown '%s' referred to by id&amp;lt;/code&amp;gt;: use a &amp;lt;code&amp;gt;# wmllint: recognize %s&amp;lt;/code&amp;gt; magic comment, or, alternatively, if the unit is created by a macro, use a magic comment of the form of either &amp;lt;code&amp;gt;# wmllint: who MACRO is UNIT&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;# wmllint: whofield MACRO NUMBER&amp;lt;/code&amp;gt;, depending on whether the macro takes an argument for the unit's name or not.&lt;br /&gt;
* &amp;lt;code&amp;gt;%s has unknown advancements&amp;lt;/code&amp;gt; (in cases where you'd think the advancement ''would'' be known): This means the unit has an advancement that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can load mainline units for checking advancements. Note that it's also possible that you just made typo, too, so be sure to check your spelling. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;.description may need hand fixup&amp;lt;/code&amp;gt;: This one comes from mucking around with the &amp;lt;code&amp;gt;.description&amp;lt;/code&amp;gt; field of unit data manually in a hackish fashion. There isn't really much of a way to work around it, besides just the &amp;quot;don't do that&amp;quot; solution.&lt;br /&gt;
* &amp;lt;code&amp;gt;tag stack nonempty (%s) at end of file.&amp;lt;/code&amp;gt;: This means that you have unbalanced tags somewhere in the file, e.g. an opener without a closer, or vice versa. This can often be seen when defining macros for unit abilities. A way to fix this warning is to wrap the section with unbalanced tags with a &amp;lt;code&amp;gt;# wmllint: unbalanced-on&amp;lt;/code&amp;gt; magic comment beforehand and a &amp;lt;code&amp;gt;# wmllint: unbalanced-off&amp;lt;/code&amp;gt; magic comment afterwards. As having unbalanced tags will also cause issues for other WML maintenance tools, such as &amp;lt;tt&amp;gt;wmlindent&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;wmlxgettext&amp;lt;/tt&amp;gt;, you may also want to add separate magic comments for each of them (see their documentation for the form they take).&lt;br /&gt;
* &amp;lt;code&amp;gt;unit declaration without side attribute&amp;lt;/code&amp;gt;: the default side for a unit declaration when left implicit is side 1. Specify your unit sides explicitly to solve this.&lt;br /&gt;
* &amp;lt;code&amp;gt;no %s units recruitable at difficulty %s&amp;lt;/code&amp;gt; (even when there are such units recruitable): This diagnostic has to do with matching the &amp;lt;code&amp;gt;usage&amp;lt;/code&amp;gt; key of units recruitable by an AI side with their &amp;lt;code&amp;gt;recruitment_pattern&amp;lt;/code&amp;gt;. It means the unit has a usage that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can know which mainline units are recruitable. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;%s has unknown movement type&amp;lt;/code&amp;gt; (even when you'd think that that movement type ''would'' actually be known): This means the unit has a movetype that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can load the mainline movement types. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;%s has unknown race&amp;lt;/code&amp;gt; (even when you'd think that that race ''would'' actually be known): This means the unit has a race that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can load the mainline races. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;derivation of %s from %s does not resolve&amp;lt;/code&amp;gt; (even when you'd think it would): This means that a unit using the [[UnitTypeWML#Other_tags|[base_unit]]] tag specifies a unit ID in that tag that was never defined in mainline or in the add-on. This is why you're supposed to always add Wesnoth's core directory as the first item to be checked when using &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; (which is something that the GUI version of it will do automatically for you), so that it can load the core units for its derivation checks. (You can also silence this warning by passing the &amp;lt;tt&amp;gt;-K&amp;lt;/tt&amp;gt; flag to &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* &amp;lt;code&amp;gt;[advancefrom] needs to be manually updated to [modify_unit_type] and moved into the _main.cfg file&amp;lt;/code&amp;gt;: This one is pretty self-explanatory: [[UnitTypeWML#Unit_Type|[advancefrom]]] was deprecated in [https://github.com/wesnoth/wesnoth/commit/3950f40f3f0483032bc70b3e57166bd355acd9fc commit 3950f40] due to [https://github.com/wesnoth/wesnoth/issues/3955 issue #3955], and in fact doesn't even work anymore (in 1.16) as per [https://github.com/wesnoth/wesnoth/issues/6204 issue #6204]. The main reason &amp;lt;tt&amp;gt;wmllint&amp;lt;/tt&amp;gt; can't fix this automatically is because it could end up being too complicated for it to figure out which files to edit if there are multiple uses of &amp;lt;code&amp;gt;[advancefrom]&amp;lt;/code&amp;gt;, and it also doesn't want to assume where to put the [[ModificationWML|[modify_unit_type]]] tag in &amp;lt;tt&amp;gt;_main.cfg&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== wmlindent ==&lt;br /&gt;
&lt;br /&gt;
Call with no arguments to filter WML on standard input to reindented WML on&lt;br /&gt;
standard output.  If arguments are specified, they are taken to be files to be&lt;br /&gt;
re-indented in place; a directory name causes reindenting on all WML&lt;br /&gt;
beneath it.&lt;br /&gt;
&lt;br /&gt;
The indent unit is four spaces.  Absence of an option to change this is&lt;br /&gt;
deliberate; the purpose of this tool is to ''prevent'' style wars, not encourage&lt;br /&gt;
them.&lt;br /&gt;
&lt;br /&gt;
On non-empty lines, this code never modifies anything but leading and&lt;br /&gt;
trailing whitespace. Leading whitespace will be regularized to the&lt;br /&gt;
current indent; trailing whitespace will be stripped.  After processing&lt;br /&gt;
all lines will end with a Unix-style &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt; end-of-line marker.&lt;br /&gt;
&lt;br /&gt;
Runs of entirely blank lines will be reduced to one blank line, except&lt;br /&gt;
in two cases where they will be discarded: (a) before WML closing&lt;br /&gt;
tags, and (b) after WML opening tags.&lt;br /&gt;
&lt;br /&gt;
It is possible to wrap a section of lines in special comments so that&lt;br /&gt;
&amp;lt;tt&amp;gt;wmlindent&amp;lt;/tt&amp;gt; will ignore them.  You may need to do this for unbalanced&lt;br /&gt;
macros (it's better, though, to get rid of those where possible).&lt;br /&gt;
Use '&amp;lt;code&amp;gt;wmlindent: {start,stop} ignoring&amp;lt;/code&amp;gt;' anywhere in a comment.&lt;br /&gt;
&lt;br /&gt;
It is also possible to declare custom openers an closers, e.g for macros&lt;br /&gt;
that are actually control constructs.  To do this, use declarations&lt;br /&gt;
&lt;br /&gt;
    # wmlindent: opener &amp;quot;{EXCEPTIONAL_OPENER &amp;quot;&lt;br /&gt;
    # wmlindent: closer &amp;quot;{EXCEPTIONAL_CLOSER &amp;quot;&lt;br /&gt;
&lt;br /&gt;
The lines after an opener will be indented an extra level; a closer&lt;br /&gt;
and lines following will be indented one level less. Note that these&lt;br /&gt;
declare prefixes; any prefix match to the non-whitespace text of a line&lt;br /&gt;
will be recognized.&lt;br /&gt;
&lt;br /&gt;
The public utility macros &amp;quot;&amp;lt;code&amp;gt;{FOREACH&amp;lt;/code&amp;gt;&amp;quot; and &amp;quot;&amp;lt;code&amp;gt;{NEXT&amp;lt;/code&amp;gt;&amp;quot; come as wired-in exceptions,&lt;br /&gt;
because it is not guaranteed that their indent declarations will be processed&lt;br /&gt;
before the macro library is reached.&lt;br /&gt;
&lt;br /&gt;
Interrupting &amp;lt;tt&amp;gt;wmlindent&amp;lt;/tt&amp;gt; ought to be safe, as each reindenting will be done to a copy&lt;br /&gt;
that is atomically renamed when it's done.  If the output file is identical&lt;br /&gt;
to the input, the output file will simply be deleted, so the timestamp&lt;br /&gt;
on the input file won't be touched.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;--dryrun&amp;lt;/tt&amp;gt; option detects and reports files that would be changed&lt;br /&gt;
without changing them. The &amp;lt;tt&amp;gt;--verbose&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;-v&amp;lt;/tt&amp;gt; option enables reporting&lt;br /&gt;
of files that are (or would be, under &amp;lt;tt&amp;gt;--dryrun&amp;lt;/tt&amp;gt;) changed.  With &amp;lt;tt&amp;gt;-v -v&amp;lt;/tt&amp;gt;,&lt;br /&gt;
unchanged files are also reported.  The &amp;lt;tt&amp;gt;--exclude&amp;lt;/tt&amp;gt; option takes a regexp&lt;br /&gt;
and excludes files matching it.&lt;br /&gt;
&lt;br /&gt;
If you don't apply this tool to your own WML that you wish to submit, the&lt;br /&gt;
mainline-campaign maintainers will do it when and if your code is accepted into the tree.&lt;br /&gt;
&lt;br /&gt;
Note: This tool does not include a parser.  It will produce bad results on WML&lt;br /&gt;
that is syntactically unbalanced.  Unbalanced double quotes that aren't part&lt;br /&gt;
of a multiline literal will also confuse it.  You will receive warnings&lt;br /&gt;
if there's an indent open at end of file or if a closer occurs with&lt;br /&gt;
indent already zero; these two conditions strongly suggest unbalanced WML.&lt;br /&gt;
&lt;br /&gt;
== GUI.pyw ==&lt;br /&gt;
&lt;br /&gt;
Starting from version 1.11.15 and 1.13.0, a GUI (written in Tkinter, plus the themed widgets ttk) is available in the same directory as the other tools. To use it, you need to have a version of Python equal to or greater than 3.1.0 (the 3.0.x series doesn't include the ttk widgets, and as such is unsuitable for this script).&lt;br /&gt;
&lt;br /&gt;
If you're on Linux, be sure to have installed the ''python3-tk'' module, '''or the application won't run at all'''. To install it in a Debian-based distro (like Ubuntu), type this line in a Terminal:&lt;br /&gt;
 sudo apt install python3-tk&lt;br /&gt;
&lt;br /&gt;
To start it, just double click on the GUI.pyw file. The interface is pretty much self-explanatory, and allows you to run wmllint, wmlscope, wmlindent and wmlxgettext, modify their options, select an add-on and save the tools' output as a text file.&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
* [[Translation Maintenance Commands]] (for &amp;lt;tt&amp;gt;wmlxgettext&amp;lt;/tt&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
[[Category:Create]]&lt;br /&gt;
[[Category:Tools]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=75466</id>
		<title>Template:DevDownload</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=75466"/>
		<updated>2026-06-28T14:59:08Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Development (1.19 branch) ==&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
==== Windows (10 1903 and later) {{{4|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.25 | filename=wesnoth-1.19.25-win64.exe |&lt;br /&gt;
hash=b29d0e9bed037f4eb7bbeece49dea6a0cce1bc3c4e53e5c3f4a19d53ee7a2271}}&lt;br /&gt;
&lt;br /&gt;
==== macOS (10.13 and later) {{{5|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.25 | filename=Wesnoth_1.19.25.dmg |&lt;br /&gt;
hash=c884e5bb31dfabd8fd56ca5d804174f296d57bffd018da875267e499473cdc6c}}&lt;br /&gt;
&lt;br /&gt;
==== Source code ====&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/blob/master/INSTALL.md Compiling Wesnoth] - How to compile the source code&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.25 | filename=wesnoth-1.19.25.tar.bz2 |&lt;br /&gt;
hash=f0d55de8ee25f3189c6d0ba530c10911939c4cf2d8a6be1d6d7a8f29d31eba4b}}&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=75465</id>
		<title>Template:DevDownload</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=75465"/>
		<updated>2026-06-28T14:04:59Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Development (1.19 branch) ==&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
==== Windows (10 1903 and later) {{{4|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.25 | filename=wesnoth-1.19.25-win64.exe |&lt;br /&gt;
hash=b29d0e9bed037f4eb7bbeece49dea6a0cce1bc3c4e53e5c3f4a19d53ee7a2271}}&lt;br /&gt;
&lt;br /&gt;
==== macOS (10.13 and later) {{{5|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.24 | filename=Wesnoth_1.19.24.dmg |&lt;br /&gt;
hash=11d87318b4fc3b83c043bd5e5d675c4ea8ca877904ec77fffecbb9ef050ab5b8}}&lt;br /&gt;
&lt;br /&gt;
==== Source code ====&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/blob/master/INSTALL.md Compiling Wesnoth] - How to compile the source code&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.25 | filename=wesnoth-1.19.25.tar.bz2 |&lt;br /&gt;
hash=f0d55de8ee25f3189c6d0ba530c10911939c4cf2d8a6be1d6d7a8f29d31eba4b}}&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=75464</id>
		<title>Template:DevDownload</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=75464"/>
		<updated>2026-06-28T14:04:41Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Development (1.19 branch) ==&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
==== Windows (10 1903 and later) {{{4|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.24 | filename=wesnoth-1.19.24-win64.exe |&lt;br /&gt;
hash=b29d0e9bed037f4eb7bbeece49dea6a0cce1bc3c4e53e5c3f4a19d53ee7a2271}}&lt;br /&gt;
&lt;br /&gt;
==== macOS (10.13 and later) {{{5|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.24 | filename=Wesnoth_1.19.24.dmg |&lt;br /&gt;
hash=11d87318b4fc3b83c043bd5e5d675c4ea8ca877904ec77fffecbb9ef050ab5b8}}&lt;br /&gt;
&lt;br /&gt;
==== Source code ====&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/blob/master/INSTALL.md Compiling Wesnoth] - How to compile the source code&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.24 | filename=wesnoth-1.19.24.tar.bz2 |&lt;br /&gt;
hash=f0d55de8ee25f3189c6d0ba530c10911939c4cf2d8a6be1d6d7a8f29d31eba4b}}&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=1.19_Roadmap&amp;diff=75240</id>
		<title>1.19 Roadmap</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=1.19_Roadmap&amp;diff=75240"/>
		<updated>2026-05-29T05:55:43Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page is for consolidating and planning when new features and fixes are intended to land in the 1.19 development branch. The release schedule for Development releases can be found [https://forums.wesnoth.org/viewtopic.php?f=2&amp;amp;t=52785 here (link is to the 1.17 page, might change for 1.19)].&lt;br /&gt;
&lt;br /&gt;
== Instructions ==&lt;br /&gt;
Place the feature or fix you intend to implement within the section of the point release that you intend to have it implemented by, as well as your forum username in parenthesis after the feature description. The point release something is planned to be released with is not set in stone, and can be updated as needed depending on the circumstances.&lt;br /&gt;
&lt;br /&gt;
This is just an outline currently, with some point releases for the early, middle and late parts of the branch.&lt;br /&gt;
&lt;br /&gt;
== Note on Single-Player Campaign Reworks ==&lt;br /&gt;
&lt;br /&gt;
We have some campaign reworks planned and in-progress.&lt;br /&gt;
&lt;br /&gt;
* In-hiatus: The Rise of Wesnoth&lt;br /&gt;
* In-progress but slower development: Legend of Wesmere (still Hybrid), Asheviere's Dogs (AD)&lt;br /&gt;
* post-development: SotBE revisions&lt;br /&gt;
&lt;br /&gt;
The other three might be going into 1.21.x so have been mentioned here just to prepare for an unprecedented development allowances (more assistance in coding, accelerated development speed, etc).&lt;br /&gt;
&lt;br /&gt;
If you have inquiries on the reworks, contact SP rework team leaders (forums/discord): &lt;br /&gt;
* Dalas (TSG/TDG/HttT)&lt;br /&gt;
* Dwarftough/Mechanical (LoW)&lt;br /&gt;
* Gweoddeoran (TRoW)&lt;br /&gt;
* LK (SotBE)&lt;br /&gt;
&lt;br /&gt;
New Campaigns added in&lt;br /&gt;
&lt;br /&gt;
* The South Guard: Tutorial&lt;br /&gt;
* The Deceivers Gambits, Parts I and II&lt;br /&gt;
* Dusk of Dawn&lt;br /&gt;
* Heir to the Throne (Revised)&lt;br /&gt;
&lt;br /&gt;
Revised Campaigns:&lt;br /&gt;
&lt;br /&gt;
* Liberty&lt;br /&gt;
* The Hammer of Thursagan&lt;br /&gt;
* Sceptre of Fire&lt;br /&gt;
* Under the Burning Suns&lt;br /&gt;
&lt;br /&gt;
== 1.19.0 (05/26/2024) ==&lt;br /&gt;
* Move the Dunefolk into Default Era (Pentarctagon) - completed in #8688&lt;br /&gt;
&lt;br /&gt;
== 1.19.1 (06/16/2024) ==&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/pull/6826 #6826] Merge hardware accelerated unit submerge effect, if possible (Pentarctagon)&lt;br /&gt;
** Completed in [https://github.com/wesnoth/wesnoth/pull/8687 #8687].&lt;br /&gt;
&lt;br /&gt;
== 1.19.2 (07/21/2024) ==&lt;br /&gt;
* Finish adding ability/weapon special unit tests (Pentarctagon)&lt;br /&gt;
* Release tentative TSG re-revision add-on, building on Yumi's work (Dalas)&lt;br /&gt;
** Depending on reception, additional development may be needed, or the rework may be dropped altogether.&lt;br /&gt;
** If all goes well, some months from now I'll need to convert this from add-on to mainline so translators can begin work.&lt;br /&gt;
&lt;br /&gt;
== 1.19.3 (08/18/2024) ==&lt;br /&gt;
* Investigate replacing mariadbpp with Boost.MySql (Pentarctagon)&lt;br /&gt;
&lt;br /&gt;
== 1.19.4 (09/15/2024) ==&lt;br /&gt;
* Release final (hopefully) TDG add-on. Collect feedback and fix bugs (Dalas)&lt;br /&gt;
** If all goes well, some months from now I'll need to convert this from add-on to mainline so translators can begin work.&lt;br /&gt;
&lt;br /&gt;
== 1.19.5 (10/20/2024) ==&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/pull/6512 #6512] New type of deprecation warning, when a deprecated attribute is used alongside its replacement (octalot)&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/pull/7082 #7082] Get wmllint running against mainline during CI with no errors (Pentarctagon)&lt;br /&gt;
* GUI2 port of the Help Browser (LumiousE/babaissarkar, CelticMinstrel)&lt;br /&gt;
** Completed in [https://github.com/wesnoth/wesnoth/pull/3653 #3653]&lt;br /&gt;
&lt;br /&gt;
== 1.19.6 (11/17/2024) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.7 (12/15/2024) ==&lt;br /&gt;
* Unified recruit/recall dialog (LumiousE/babaissarkar) (related issues: [https://github.com/wesnoth/wesnoth/issues/1059 1059], [https://github.com/wesnoth/wesnoth/issues/8829 8829], [https://github.com/wesnoth/wesnoth/issues/5237 5237])&lt;br /&gt;
** Completed in [https://github.com/wesnoth/wesnoth/pull/9499 #9499]&lt;br /&gt;
&lt;br /&gt;
== 1.19.8 (01/19/2025) ==&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/issues/8585 #8585], [https://github.com/wesnoth/wesnoth/issues/9035 #9035] Solve all issues with [damage_type] (octalot)&lt;br /&gt;
* Go through all the added translatable strings, and check that they're used. For example, look for campaign-specific abilities which aren't used in the campaign. This is on the roadmap twice, I'm planning to do it annually. (octalot, but open to volunteers)&lt;br /&gt;
&lt;br /&gt;
== 1.19.9 (02/16/2025) ==&lt;br /&gt;
* Release first half of HttT revision and gather feedback, fix bugs, etc (Dalas)&lt;br /&gt;
** This is a VERY rough release date. Probably +/- 2 months.&lt;br /&gt;
&lt;br /&gt;
== 1.19.10 (03/16/2025) ==&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/issues/5041 #5041] Draw text on images in [[IntroWML]], useable for place-name labels on the journey-tracker maps (octalot)&lt;br /&gt;
* Add &amp;quot;pseudo-queues&amp;quot; as a built-in feature for the multiplayer lobby (pentarctagon)&lt;br /&gt;
&lt;br /&gt;
== 1.19.11 (04/20/2025) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.12 (05/18/2025) ==&lt;br /&gt;
* Improve UtBS. (Hejnewar) This includes:&lt;br /&gt;
** Removal and replacement of some abilities, mainly hero ones.&lt;br /&gt;
** Better accomodation of high level units to the new recall system. &lt;br /&gt;
** Difficulty of Nightmare+.&lt;br /&gt;
** Changes to some maps and scenarios, mainly in order to make them more fluid. &lt;br /&gt;
* Mercenaries SP/MP campaign (still didn't give up) (Hejnewar) This includes:&lt;br /&gt;
** New pixelart terrains and backgrounds (particularily tough for someone without pixelart skills).&lt;br /&gt;
* Balance (Hejnewar)&lt;br /&gt;
* New MP maps and scenarios. (Hejnewar) This includes:&lt;br /&gt;
** New short survival.&lt;br /&gt;
** Recovery of long lost mapmaking art.&lt;br /&gt;
** New PvP and PvPvE maps, including experimental scenarios with events.&lt;br /&gt;
&lt;br /&gt;
== 1.19.13 (06/15/2025) ==&lt;br /&gt;
* Establish clear direction on weather graphics (doofus-01)&lt;br /&gt;
&lt;br /&gt;
== 1.19.14 (07/20/2025) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.15 (08/17/2025) ==&lt;br /&gt;
* Play through any remaining mainline campaigns worth playing through (ie: not LoW, TRoW or SotBE) and add at least a minimum baseline of achievements to them (Pentarctagon)&lt;br /&gt;
* Replace TDG's current magic system with an improved and more developer-friendly version (amakriLexa04)&lt;br /&gt;
&lt;br /&gt;
== 1.19.16 (09/21/2025) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.17 (10/19/2025) ==&lt;br /&gt;
* Release second half of HttT revision and gather feedback, fix bugs, etc (Dalas)&lt;br /&gt;
** This is a VERY rough release date. Probably +/- 3 months.&lt;br /&gt;
** If all goes well, some months from now I'll need to convert this from add-on to mainline so translators can begin work.&lt;br /&gt;
&lt;br /&gt;
== 1.19.18 (11/16/2025) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.19 (12/21/2025) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.20 (01/18/2026) ==&lt;br /&gt;
* Go through all the added translatable strings, and check that they're used. For example, look for campaign-specific abilities which aren't used in the campaign. (octalot, but open to volunteers)&lt;br /&gt;
* Take a serious look at supporting a competitive mode/ELO ranking natively (pentarctagon)&lt;br /&gt;
&lt;br /&gt;
== 1.19.21 (02/15/2026) ==&lt;br /&gt;
'''This marks the beginning of the string freeze for 1.19'''&lt;br /&gt;
&lt;br /&gt;
== 1.19.22 (03/15/2026) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.23 (04/19/2026) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.24 (05/17/2026) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.25 (06/21/2026) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.26 (07/19/2026) (Beta 1) ==&lt;br /&gt;
'''This marks the beginning of the feature freeze for 1.19;''' the only API changes made past this point must be to fix bugs.&lt;br /&gt;
&lt;br /&gt;
== 1.19.27 (08/16/2026) (Beta 2) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.28 (09/20/2026) (Beta 3) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.29 (10/18/2026) (RC1) ==&lt;br /&gt;
&lt;br /&gt;
* Add new screenshots for 1.20&lt;br /&gt;
&lt;br /&gt;
== 1.20.0 (11/15/2026) ==&lt;br /&gt;
&lt;br /&gt;
[[Category:Roadmaps]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=1.19_Roadmap&amp;diff=75239</id>
		<title>1.19 Roadmap</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=1.19_Roadmap&amp;diff=75239"/>
		<updated>2026-05-29T05:04:03Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page is for consolidating and planning when new features and fixes are intended to land in the 1.19 development branch. The release schedule for Development releases can be found [https://forums.wesnoth.org/viewtopic.php?f=2&amp;amp;t=52785 here (link is to the 1.17 page, might change for 1.19)].&lt;br /&gt;
&lt;br /&gt;
== Instructions ==&lt;br /&gt;
Place the feature or fix you intend to implement within the section of the point release that you intend to have it implemented by, as well as your forum username in parenthesis after the feature description. The point release something is planned to be released with is not set in stone, and can be updated as needed depending on the circumstances.&lt;br /&gt;
&lt;br /&gt;
This is just an outline currently, with some point releases for the early, middle and late parts of the branch.&lt;br /&gt;
&lt;br /&gt;
== Note on Single-Player Campaign Reworks ==&lt;br /&gt;
&lt;br /&gt;
We have some campaign reworks planned and in-progress.&lt;br /&gt;
&lt;br /&gt;
* In-hiatus: The Rise of Wesnoth&lt;br /&gt;
* In-progress but slower development: Legend of Wesmere (still Hybrid), Asheviere's Dogs (AD)&lt;br /&gt;
* post-development: SotBE revisions&lt;br /&gt;
&lt;br /&gt;
The other three might be going into 1.21.x so have been mentioned here just to prepare for an unprecedented development allowances (more assistance in coding, accelerated development speed, etc).&lt;br /&gt;
&lt;br /&gt;
If you have inquiries on the reworks, contact SP rework team leaders (forums/discord): &lt;br /&gt;
* Dalas (TSG/TDG/HttT)&lt;br /&gt;
* Dwarftough/Mechanical (LoW)&lt;br /&gt;
* Gweoddeoran (TRoW)&lt;br /&gt;
* LK (SotBE)&lt;br /&gt;
&lt;br /&gt;
New Campaigns added in&lt;br /&gt;
&lt;br /&gt;
* The South Guard: Tutorial&lt;br /&gt;
* The Deceivers Gambits, Parts I and II&lt;br /&gt;
* Dusk of Dawn&lt;br /&gt;
* Heir to the Throne (Revised)&lt;br /&gt;
&lt;br /&gt;
Revised Campaigns:&lt;br /&gt;
&lt;br /&gt;
* Liberty&lt;br /&gt;
* The Hammer of Thursagan&lt;br /&gt;
* Sceptre of Fire&lt;br /&gt;
* Under the Burning Suns&lt;br /&gt;
&lt;br /&gt;
== 1.19.0 (05/26/2024) ==&lt;br /&gt;
* Move the Dunefolk into Default Era (Pentarctagon) - completed in #8688&lt;br /&gt;
&lt;br /&gt;
== 1.19.1 (06/16/2024) ==&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/pull/6826 #6826] Merge hardware accelerated unit submerge effect, if possible (Pentarctagon)&lt;br /&gt;
** Completed in [https://github.com/wesnoth/wesnoth/pull/8687 #8687].&lt;br /&gt;
&lt;br /&gt;
== 1.19.2 (07/21/2024) ==&lt;br /&gt;
* Finish adding ability/weapon special unit tests (Pentarctagon)&lt;br /&gt;
* Release tentative TSG re-revision add-on, building on Yumi's work (Dalas)&lt;br /&gt;
** Depending on reception, additional development may be needed, or the rework may be dropped altogether.&lt;br /&gt;
** If all goes well, some months from now I'll need to convert this from add-on to mainline so translators can begin work.&lt;br /&gt;
&lt;br /&gt;
== 1.19.3 (08/18/2024) ==&lt;br /&gt;
* Investigate replacing mariadbpp with Boost.MySql (Pentarctagon)&lt;br /&gt;
&lt;br /&gt;
== 1.19.4 (09/15/2024) ==&lt;br /&gt;
* Release final (hopefully) TDG add-on. Collect feedback and fix bugs (Dalas)&lt;br /&gt;
** If all goes well, some months from now I'll need to convert this from add-on to mainline so translators can begin work.&lt;br /&gt;
&lt;br /&gt;
== 1.19.5 (10/20/2024) ==&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/pull/6512 #6512] New type of deprecation warning, when a deprecated attribute is used alongside its replacement (octalot)&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/pull/7082 #7082] Get wmllint running against mainline during CI with no errors (Pentarctagon)&lt;br /&gt;
* GUI2 port of the Help Browser (LumiousE/babaissarkar, CelticMinstrel)&lt;br /&gt;
** Completed in [https://github.com/wesnoth/wesnoth/pull/3653 #3653]&lt;br /&gt;
&lt;br /&gt;
== 1.19.6 (11/17/2024) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.7 (12/15/2024) ==&lt;br /&gt;
* Unified recruit/recall dialog (LumiousE/babaissarkar) (related issues: [https://github.com/wesnoth/wesnoth/issues/1059 1059], [https://github.com/wesnoth/wesnoth/issues/8829 8829], [https://github.com/wesnoth/wesnoth/issues/5237 5237])&lt;br /&gt;
** Completed in [https://github.com/wesnoth/wesnoth/pull/9499 #9499]&lt;br /&gt;
&lt;br /&gt;
== 1.19.8 (01/19/2025) ==&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/issues/8585 #8585], [https://github.com/wesnoth/wesnoth/issues/9035 #9035] Solve all issues with [damage_type] (octalot)&lt;br /&gt;
* Go through all the added translatable strings, and check that they're used. For example, look for campaign-specific abilities which aren't used in the campaign. This is on the roadmap twice, I'm planning to do it annually. (octalot, but open to volunteers)&lt;br /&gt;
&lt;br /&gt;
== 1.19.9 (02/16/2025) ==&lt;br /&gt;
* Release first half of HttT revision and gather feedback, fix bugs, etc (Dalas)&lt;br /&gt;
** This is a VERY rough release date. Probably +/- 2 months.&lt;br /&gt;
&lt;br /&gt;
== 1.19.10 (03/16/2025) ==&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/issues/5041 #5041] Draw text on images in [[IntroWML]], useable for place-name labels on the journey-tracker maps (octalot)&lt;br /&gt;
* Add &amp;quot;pseudo-queues&amp;quot; as a built-in feature for the multiplayer lobby (pentarctagon)&lt;br /&gt;
&lt;br /&gt;
== 1.19.11 (04/20/2025) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.12 (05/18/2025) ==&lt;br /&gt;
* Improve UtBS. (Hejnewar) This includes:&lt;br /&gt;
** Removal and replacement of some abilities, mainly hero ones.&lt;br /&gt;
** Better accomodation of high level units to the new recall system. &lt;br /&gt;
** Difficulty of Nightmare+.&lt;br /&gt;
** Changes to some maps and scenarios, mainly in order to make them more fluid. &lt;br /&gt;
* Mercenaries SP/MP campaign (still didn't give up) (Hejnewar) This includes:&lt;br /&gt;
** New pixelart terrains and backgrounds (particularily tough for someone without pixelart skills).&lt;br /&gt;
* Balance (Hejnewar)&lt;br /&gt;
* New MP maps and scenarios. (Hejnewar) This includes:&lt;br /&gt;
** New short survival.&lt;br /&gt;
** Recovery of long lost mapmaking art.&lt;br /&gt;
** New PvP and PvPvE maps, including experimental scenarios with events.&lt;br /&gt;
&lt;br /&gt;
== 1.19.13 (06/15/2025) ==&lt;br /&gt;
* Establish clear direction on weather graphics (doofus-01)&lt;br /&gt;
&lt;br /&gt;
== 1.19.14 (07/20/2025) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.15 (08/17/2025) ==&lt;br /&gt;
* Play through any remaining mainline campaigns worth playing through (ie: not LoW, TRoW or SotBE) and add at least a minimum baseline of achievements to them (Pentarctagon)&lt;br /&gt;
* Replace TDG's current magic system with an improved and more developer-friendly version (amakriLexa04)&lt;br /&gt;
&lt;br /&gt;
== 1.19.16 (09/21/2025) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.17 (10/19/2025) ==&lt;br /&gt;
* Release second half of HttT revision and gather feedback, fix bugs, etc (Dalas)&lt;br /&gt;
** This is a VERY rough release date. Probably +/- 3 months.&lt;br /&gt;
** If all goes well, some months from now I'll need to convert this from add-on to mainline so translators can begin work.&lt;br /&gt;
&lt;br /&gt;
== 1.19.18 (11/16/2025) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.19 (12/21/2025) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.20 (01/18/2026) ==&lt;br /&gt;
* Go through all the added translatable strings, and check that they're used. For example, look for campaign-specific abilities which aren't used in the campaign. (octalot, but open to volunteers)&lt;br /&gt;
* Take a serious look at supporting a competitive mode/ELO ranking natively (pentarctagon)&lt;br /&gt;
&lt;br /&gt;
== 1.19.21 (02/15/2026) ==&lt;br /&gt;
'''This marks the beginning of the string freeze for 1.19'''&lt;br /&gt;
&lt;br /&gt;
== 1.19.22 (03/15/2026) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.23 (04/19/2026) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.24 (05/17/2026) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.25 (06/21/2026) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.26 (07/19/2026) (Beta 1) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.27 (08/16/2026) (Beta 2) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.28 (09/20/2026) (Beta 3) ==&lt;br /&gt;
&lt;br /&gt;
== 1.19.29 (10/18/2026) (RC1) ==&lt;br /&gt;
&lt;br /&gt;
* Add new screenshots for 1.20&lt;br /&gt;
&lt;br /&gt;
== 1.20.0 (11/15/2026) ==&lt;br /&gt;
&lt;br /&gt;
[[Category:Roadmaps]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=75217</id>
		<title>Template:DevDownload</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=75217"/>
		<updated>2026-05-24T14:40:32Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Development (1.19 branch) ==&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
==== Windows (10 1903 and later) {{{4|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.24 | filename=wesnoth-1.19.24-win64.exe |&lt;br /&gt;
hash=8ec391e8e8e285567fec2535737bd3e5f6e4222f7769734c67ae821e12a2f63b}}&lt;br /&gt;
&lt;br /&gt;
==== macOS (10.13 and later) {{{5|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.24 | filename=Wesnoth_1.19.24.dmg |&lt;br /&gt;
hash=11d87318b4fc3b83c043bd5e5d675c4ea8ca877904ec77fffecbb9ef050ab5b8}}&lt;br /&gt;
&lt;br /&gt;
==== Source code ====&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/blob/master/INSTALL.md Compiling Wesnoth] - How to compile the source code&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.24 | filename=wesnoth-1.19.24.tar.bz2 |&lt;br /&gt;
hash=53044f21e5060ba0eef255ee988773cd109e178159ae2b84fbffdb0201ff25e8}}&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=CompatibilityStandards&amp;diff=75193</id>
		<title>CompatibilityStandards</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=CompatibilityStandards&amp;diff=75193"/>
		<updated>2026-05-18T14:17:38Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This policy is now outdated. Find the updated policy at https://wiki.wesnoth.org/CompatibilityStandardsV2&lt;br /&gt;
&lt;br /&gt;
Created: 2017-07-11&lt;br /&gt;
Updated: 2017-07-11&lt;br /&gt;
&lt;br /&gt;
As a piece of software matures, there are often new designs, paradigms, and idioms developed which are superior to old ones. This creates an inherent conflict between the need for progress and the need for compatibility. Wesnoth is no exception. This document describes Wesnoth's approach toward resolving that conflict in a way which is most beneficial to both goals, as well as the rationale behind this approach.&lt;br /&gt;
&lt;br /&gt;
== Policy ==&lt;br /&gt;
This policy defines how the creation of new Wesnoth APIs and the deprecation and removal of old ones are to be handled. For the purposes of this document &amp;quot;API&amp;quot; means &amp;quot;any technical channel by which a content creator interacts with the game engine&amp;quot;. This includes, but is not limited to: preprocessor macros, WML tags, WFL functions, IPFs, and the Lua API. Note that this policy applies only to software APIs. Core content such as sprites, portraits, animations, lore, etc. are to be updated freely, without consideration for stylistic or literary conflicts that such content changes may present to add-ons.&lt;br /&gt;
&lt;br /&gt;
=== When to deprecate ===&lt;br /&gt;
Any time a superior API is introduced which has '''complete feature-parity''' with an existing API, the old one should be immediately deprecated. Note that in most cases, in order to be considered to have &amp;quot;complete feature-parity&amp;quot;, the API should be available in the same language or area of the code as the obsolete one was. For example, the introduction of a more powerful API in Lua which can accomplish a superset of the functionality which had previously been available with a certain WML tag would not obsolete that WML tag. Exceptions can be made to this rule in cases where it is clear that the feature does not make a lot of sense in its current language or area, and was merely there for legacy reasons (such as the proper place for it not having been introduced yet at the time of its creation). Such exceptions should be determined by developer consensus.&lt;br /&gt;
&lt;br /&gt;
=== How to deprecate ===&lt;br /&gt;
Each deprecated feature should make use of an appropriate deprecation function call for that language or subsystem to ensure that the appropriate deprecation notice is printed to the log output as well as displayed in-game if running in debug mode. In-game deprecation messages should ONLY appear if debug mode is active. Deprecation notices should, ideally, point the user in the direction of the replacement API that they should be using instead. Documentation on deprecated features should be updated to reflect the deprecated nature of the given API, and should likewise have appropriate signposting toward the preferred replacement.&lt;br /&gt;
&lt;br /&gt;
Every effort should be made to create the simplest possible wrappers which will translate from an obsolete API to the updated one. Such wrappers should ideally be organized into their own compatibility file or module, and set up in such a way that the internals of the updated API will not affect how the old calls get wrapped to the new one. Essentially, the idea is to create a set-it-and-forget-it compatibility wrapper which will continue to work regardless of updates made to the newer API.&lt;br /&gt;
&lt;br /&gt;
=== Deprecation levels - When to remove deprecated features ===&lt;br /&gt;
While creating simple compatibility wrappers should be possible at least 90% of the time, it would be unreasonable to assume that this approach will be viable in absolutely every case. Wesnoth's compatibility policy therefore allows for four different levels of deprecation, depending on the severity of the change being made and the cost of maintaining backwards compatibility:&lt;br /&gt;
&lt;br /&gt;
#Deprecated indefinitely &amp;amp;mdash; This deprecation level is for changes which are mostly stylistic or organizational in nature and, though the current conventions for core content say not to use them, can theoretically be maintained as wrappers forever. (For example, functions or attributes which have had their names changed or macros which have been replaced with tags.) It is the lowest level of deprecation and is used to signify that a certain API has been replaced with something cleaner which is what should ideally be used, but that there is also no forseeable reason why it will ever need to be forcibly removed while still in use. (This excludes the possibility that an even newer paradigm replaces the one obsoleting this one and causes the need for more aggressive deprecation, in which case the deprecation level should obviously be raised accordingly to match.)&lt;br /&gt;
#Deprecated preemptively &amp;amp;mdash; This deprecation level is the one which should be aimed for the vast majority of the time. Obsolete APIs which can be implemented in terms of a simple minimal-maintenance wrapper should be deprecated, but left in the code indefinitely. At such time as an obsolete API presents itself as an active obstacle toward further development, it can be slated for removal in the next release, or even removed at will immediately, so long as it has already survived a minimum deprecation period of at least one stable release version. The functions for generating deprecation messages for this level should take a parameter for the first version in which the given feature was deprecated, and their output should reflect whether that period has passed or not, with gentler warnings for those which have not (&amp;quot;this feature is a candidate for removal and may be removed as early as the next version&amp;quot;) and harsher warnings for those which have (&amp;quot;this feature is obsolete and may be removed without warning at any time&amp;quot;). Logistically, this differs from &amp;quot;Deprecated indefinitely&amp;quot; only in that it provides a hint to content creators about how likely it is that a future removal will occur, allowing them to prioritize their updates accordingly.&lt;br /&gt;
#Deprecated for removal &amp;amp;mdash; This deprecation level should be used for features which cannot be maintained as simple wrappers, but which could be maintained as separate, coexisting code. In order to prevent the problem of double-maintenance and code bloat, this level has a built-in lifespan cap of one stable release version following its initial deprecation, after which it will be removed. There may also be cases where a level 1 deprecated feature presents itself as a hindrance to future development, but can be temporarily maintained in this manner as well. In such cases, it is ideal to move the deprecated feature from level 1 to level 2 rather than removing it outright. Deprecation messages for this level should reflect the severity of the deprecation (&amp;quot;this feature will be removed in the next version and requires immediate maintenance&amp;quot;).&lt;br /&gt;
#Removed without deprecation &amp;amp;mdash; This level should be used EXTREMELY rarely, and only in cases where it is ABSOLUTELY NECESSARY. Occasionally, an update to a feature will change the underlying architecture in such a fundamental way that the old paradigm cannot coexist with the new one no matter how much redundant code one would create. While this kind of scenario is extremely rare, and every effort should be made to find creative solutions to avoid it, there are occasionally cases where it truly is impossible to maintain both methods even in the short term. This level should only be used with broad developer consensus, after the majority of active developers familiar with the feature in question have given at least some thought to trying to deprecate gracefully and failed.&lt;br /&gt;
&lt;br /&gt;
It should also be noted that for the purposes of reducing clutter, a deprecated API may also be removed at whatever point that API is no longer being used by any actively maintained add-ons, even if keeping them around would not actively hinder current progress. For the purposes of this rule an &amp;quot;actively maintained add-on&amp;quot; is any add-on in the current stable release or the one directly previous to that. Add-ons which exist for both are to have their current considered the &amp;quot;active&amp;quot; version (meaning that an add-on which used a deprecated API in the previous release but no longer does in the current release is not considered to be actively using that API, and can be disregarded).&lt;br /&gt;
&lt;br /&gt;
=== Post-removal ===&lt;br /&gt;
Features which have been removed (subsequent to any of the deprecation levels) should ideally maintain a skeleton of their former interface for at least one stable release version following their removal. This skeleton should print a more detailed error message than the usual &amp;quot;invalid tag/function/object/attribute/whatever&amp;quot; which would result from their complete removal, and the messages should include information on when (and possibly why) the feature was deprecated and where to look for its replacement.&lt;br /&gt;
&lt;br /&gt;
== Rationale ==&lt;br /&gt;
The above policy is the result of a large amount of thought, discussion, and debate. Following is a brief outline of the considerations and goals on both sides of the problem, why there is an inherent conflict between them, some failed approaches to resolving the conflict, and how the final policy ultimately maximizes the pursuit of both goals.&lt;br /&gt;
&lt;br /&gt;
=== The Problem - The paradox of progress ===&lt;br /&gt;
Invariably, as development on any project moves forward, developers will realize that there are better, cleaner, or more elegant ways to structure things than they had been previously. These changes can be to improve efficiency, make an API more intuitive, keep code better organized, make common tasks more straightforward, or accomplish any number of other positive things. These changes can also make the development of additional features much more viable. In short, progress is good.&lt;br /&gt;
&lt;br /&gt;
On the other hand, a content-heavy program such as Wesnoth relies on the ability of content creators to efficiently create, maintain, and update their content. Too many changes all at once will force creators of existing content to spend obscene amounts time updating their creations just to keeping them up-to-date and in working order. This can lead to a high amount of frustration, a drop in motivation, and a decline in content being created. In short, progress is bad.&lt;br /&gt;
&lt;br /&gt;
=== Backwards Compatibility - benefits and drawbacks ===&lt;br /&gt;
Most of the time, older paradigms can still be maintained in a manner in which they coexist with the newer ones. This allows for existing content to continue functioning, while at the same time allowing and encouraging new content to be created using the newer methods. However, maintaining such code can be problematic in the following ways:&lt;br /&gt;
#There may eventually be architectural changes a developer would want to make where the old method's square peg no longer fits, even forcibly, into the new method's round hole.&lt;br /&gt;
#Having compatibility code hanging around may, depending on how it is implemented, mean that any updates made to the feature, module, or subsystem in question would have to made in both the new, cleaner design, and the older, poorly structured one, adding more work for developers.&lt;br /&gt;
&lt;br /&gt;
=== The Naive Approach - Deprecate and remove everything old ===&lt;br /&gt;
One approach to balancing old and new is to deprecate the old and slate its eventual removal after either a certain amount of time has passed or a certain number of subsequent versions have been released. This sounds good in theory, but in practice, there will be many changes which are minor or cosmetic in nature and which will add up. Things like replacing macros with WML tags, updating the name of an API call or order of parameters to be more consistent with other similar functions, or switching from a functional to an object-oriented structure are very good for organizational purposes, but will result in a large amount of maintenance required on the part of content creators to keep existing code operational, and for very little real gain. This approach invariably leads to the situation where so much is being changed from one version to the next that creators turn into maintainers, forced to spend nearly all of their time trying to stay ahead of the update curve in an attempt to keep their existing content working, and leaving them very little time and motivation to create new content. And of course, by the time they're finished painstakingly updating their existing code for every little change made for the current release, whoops, there's a new release with a whole slew of new changes that need accounting for. It simply becomes unmanageable.&lt;br /&gt;
&lt;br /&gt;
=== The Naive Approach - Deprecate and remove only when necessary ===&lt;br /&gt;
The opposite aproach would be to keep all existing paradigms until they actively interfere with a new architecture or create a double-maintenance problem. While this approach does cut down on the maintenance burden by ensuring that content using an older design continues to work, it hinders progress by the fact that the moment at which it first becomes clear that an architectural or double-maintenance problem will occur is exactly the same moment at which keeping the old structure around becomes problematic. Beginning a deprecation cycle at that point and then having to &amp;quot;wait out&amp;quot; the old paradigm will cause an unacceptable delay in development.&lt;br /&gt;
&lt;br /&gt;
=== The Middle Ground - Deprecate everything old, remove only when necessary ===&lt;br /&gt;
Most of the time, older APIs can be implemented in terms of their newer, cleaner counterparts through the use of things like simple wrappers, parse-translators which re-write the older paradigm's code in terms of the new one, or other relatively low-maintenance &amp;quot;set-it-and-forget-it&amp;quot; approaches. These simple wrappers are not really detrimental to making progress, don't require updating when the new APIs internals are changed, and can usually be organized into their own files and/or modules so that they don't clutter the cleaner code. Many such wrappers will never truly present either of the backwards compatibility drawbacks mentioned above. As such, there is really no detriment to keeping them around indefinitely. However, occasionally, a new idea or approach will be put forth that updates the newer paradigm in such a way that the older one can no longer cleanly wrap to it. This usually happens, as inspiration is wont to do, suddenly, unexpectedly, and without warning. As such developers need the flexibility to be able to remove outdated code as freely as possible when the situation requires. Therefore, the ideal solution would be to deprecate any API which has newer, feature-complete ways to do it, while leaving it in the codebase until such time as its presence becomes a hindrance. Essentially, deprecation need not necessarily mean &amp;quot;this WILL be removed&amp;quot; so much as &amp;quot;this is now a candidate for removal&amp;quot;. By separating the concepts of deprecation and removal, both goals can be better served.&lt;br /&gt;
&lt;br /&gt;
=== The Net Result - Mutual benefit ===&lt;br /&gt;
By deprecating old methods while keeping them around for as long as feasible, the work for content creators to update for a newer release is drastically reduced. They need only update a bare minimum in order to get their content working again, while still being encouraged to update the rest. While it is true that ideally, a content creator would update all deprecated code to use the updated APIs, the human element can't be ignored here. It is extremely frustrating to find that your existing code has thousands of places which need updating before any of it works at all. It is far more pleasant for to be able to do a small amount of tweaking to get something back into working order and then spend time gradually correcting the &amp;quot;uglier&amp;quot; or &amp;quot;less desirable&amp;quot; code while still having something functional to work off of. Counter-intuitive though it may be, indefinite deprecation actually does a better job of encouraging users to update their code than deprecation with a hard removal time does, by avoiding what would otherwise be a massive drain on motivation. Likewise, when things are deprecated as early as possible, developers have the freedom to remove things at will when the need arises, which will better maintain their motivation as well.&lt;br /&gt;
&lt;br /&gt;
=== More Complex Cases - One size does not fit all ===&lt;br /&gt;
There may, however, be cases where the obsolete API cannot be implemented cleanly in terms of the updated one and must be maintained separately, or, in extremely rare cases, cannot coexist at all. There needs to be some leeway for such cases as well. In the former case, it therefore makes sense to allow for a feature to be deprecated pending removal after the shortest reasonable deprecation period. In the latter case, there is obviously no choice but to make the change and remove the old immediately. Developers should, naturally, be encouraged to find creative solutions to avoid such cases, but it is inevitable that there will eventually be a few cases where no graceful transition procedure can be found. These more aggressive forms of deprecation should only be done with developer consensus, and only after all options of creating a backwards-compatible transition have been exhausted.&lt;br /&gt;
&lt;br /&gt;
=== Graphics - The exception that proves the rule ===&lt;br /&gt;
The one area where backwards compatibility should NOT be a factor is graphical changes. Any change to the terrain graphics, unit sprites, or portrait images has the potential to result in visually-incompatible custom content. Core content creators cannot be expected to maintain a deprecated visual style alongside a more modern one, as any approach toward doing so would add an unreasonable amount of bloat and overhead, and make it very difficult for core graphics to be updated without having to do double the work. In addition, add-ons will continue to function even with such visual incompatibilities present, they just won't look right. As such, it can be said that stylistic incompatibilities fall more within the realm of content than of code, and it is not unreasonable to expect a content creator to... well...  create content. Expecting the graphical style to remain backwards-compatible would be just as unreasonable as expecting stories, help descriptions, or other forms of lore to never change because they may introduce plot holes into add-on stories. Essentially, since the goal of the software portion of Wesnoth is, at its heart, merely to facilitate the creation and advancement of this kind of content, core content needs to be free to develop unhindered by considerations for add-on content.&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=PblWML&amp;diff=75029</id>
		<title>PblWML</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=PblWML&amp;diff=75029"/>
		<updated>2026-05-02T15:44:44Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* primary_authors */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WML Tags}}&lt;br /&gt;
&lt;br /&gt;
To upload an add-on you have made, you need a '''_server.pbl''' file in your add-on's directory, at the same level as the '''_main.cfg''' file. When you upload the add-on, the entire directory and subdirectories containing the _server.pbl file will be published. Your add-on must be based entirely on these paths.&lt;br /&gt;
&lt;br /&gt;
See [[AddonStructure]] for more on setting up the add-on folder if you have not done so, and [[Distributing_content]] for more on uploading an add-on to the server with this file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Note:&amp;lt;/b&amp;gt; Be aware that translations in the .pbl-files are '''not''' used, so don't mark these strings as translatable. {{DevFeature1.15|4}} The translations in the .pbl-files are used, but they are used as a plain text instead of Gettext strings, so don't mark these strings as translatable.&lt;br /&gt;
&lt;br /&gt;
== What goes into a .pbl file? ==&lt;br /&gt;
&lt;br /&gt;
'''Note:''' ''You should '''not''' use special formatting or coloring in any of these keys when uploading to the official server.'''''&lt;br /&gt;
&lt;br /&gt;
The following keys are recognized for .pbl files:&lt;br /&gt;
&lt;br /&gt;
=== icon ===&lt;br /&gt;
: An image, displayed leftmost in the add-ons download dialog. It must be a standard Wesnoth file and '''not a custom one'''. A custom file will only work for users who already have the relevant add-on installed. This is not related to the icon used for entries in the campaigns menu -- see [[CampaignWML]] for more information.&lt;br /&gt;
&lt;br /&gt;
: If the icon is a unit with magenta team-color bits, please use [[ImagePathFunctions]] to recolor it. For example: &lt;br /&gt;
&lt;br /&gt;
::icon=&amp;quot;units/elves-wood/archer+female-sword-1.png~RC(magenta&amp;gt;brightorange)&amp;quot;&lt;br /&gt;
:or&lt;br /&gt;
::icon=&amp;quot;units/human-peasants/peasant-ranged.png~RC(magenta&amp;gt;white)~CS(24,24,24)&amp;quot;&lt;br /&gt;
:or&lt;br /&gt;
::icon=&amp;quot;units/human-peasants/ruffian.png~RC(magenta&amp;gt;green)~BLIT(units/human-peasants/woodsman.png~RC(magenta&amp;gt;lightblue),18,12)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
: Because the add-on manager's UI is dark, recoloring to light colors looks usually better. [https://irydacea.me/projects/wespal Wespal] is a tool that provides a convenient way to preview recolored unit sprites without needing to launch the game with specific WML or Lua edits.&lt;br /&gt;
&lt;br /&gt;
: {{DevFeature1.13|12}} Instead of a standard Wesnoth image, a [[DataURI]] can also be used. This way, an image can be directly included into the _server.pbl file. Take care to leave no trailing newline.&lt;br /&gt;
&lt;br /&gt;
=== title ===&lt;br /&gt;
: Displayed to the right of the icon, it is just text. It should usually be the same as the name of your add-on when it is played.&lt;br /&gt;
: '''This value is required'''.&lt;br /&gt;
&lt;br /&gt;
=== version ===&lt;br /&gt;
: Displayed to the right of the title; it is merely text. However, starting with Wesnoth 1.6, the required format is '''x.y.z''' where '''x''', '''y''' and '''z''' are numbers — and a value for '''x''' greater than ''0'' implies the add-on is complete, feature-wise. Trailing non-numeric elements are allowed, but nothing should appear before or between these numbers. The string of numbers will be modified on the server by inserting or appending zeros as neccesary to meet the required format. All this is necessary for the “Update All” button to work correctly. ([[#Version Key Examples|See Examples]])&lt;br /&gt;
: '''This value is required'''.&lt;br /&gt;
&lt;br /&gt;
=== author ===&lt;br /&gt;
: Displayed to the right of the version; it is merely text. Put your name or nickname here. If several people have contributed significantly to the add-on you may want to list all of their names.&lt;br /&gt;
&lt;br /&gt;
: {{DevFeature1.17|3}} When using forum_auth, this value is a single forum account name which will have the ability to upload new versions of the add-on, delete the add-on from the add-ons server, and update the secondary_authors field.&lt;br /&gt;
&lt;br /&gt;
: {{DevFeature1.19|7}} This value is now used the same as it was pre-forum_auth. It is only used to display in the addons manager.&lt;br /&gt;
&lt;br /&gt;
: '''This value is required'''.&lt;br /&gt;
&lt;br /&gt;
=== passphrase ===&lt;br /&gt;
: Not displayed. It prevents others from modifying the version of your add-on on the server. You do not need to input a passphrase when initially publishing a add-on; if you do not, one will be randomly generated for you and replaced in your local copy of the .pbl file.&lt;br /&gt;
: '''SECURITY NOTE:''' If you do specify a passphrase of your own, note that it is stored in '''clear text''' form in the server; '''do NOT use a password you would normally use for any other services or web sites!'''&lt;br /&gt;
&lt;br /&gt;
: {{DevFeature1.15|12}}&lt;br /&gt;
&lt;br /&gt;
: It is no longer required to keep the passphrase in the .pbl file at all. If it is not present, then Wesnoth will prompt for it to be entered when uploading or deleting an add-on.&lt;br /&gt;
&lt;br /&gt;
=== description ===&lt;br /&gt;
: This can be used to provide a brief description of your add-on, and for pre-1.0 versions, let people know how playable it is. The description can be viewed by users by clicking on the Description button in the built-in client, or by moving their mouse over the add-on's icon in the web interface.&lt;br /&gt;
: '''This value is required'''.&lt;br /&gt;
&lt;br /&gt;
=== dependencies ===&lt;br /&gt;
: An optional list of dependencies (a comma separated list of ''addon-name'' – the directory names of the needed add-ons), which should be provided if your add-on relies on other user-made content to work properly. ([[#Dependency Key Example|See Example]])&lt;br /&gt;
&lt;br /&gt;
=== tags ===&lt;br /&gt;
{{DevFeature1.13|12}}&lt;br /&gt;
: An optional string including a comma-separated list of keywords used for matching add-ons when typing terms into the Filter box on the top left of the Add-ons Manager. There are no specific requirements on the syntax of the keywords listed here, but a general recommendation is to keep them relevant for players. For example, one might include the add-on's acronym in the tags, the names or acronyms of add-ons to which it is related, and so on.&lt;br /&gt;
&lt;br /&gt;
{{DevFeature1.15|13}} The in-game add-ons manager will show all the tags in the UI, and also includes a drop-down list of tags to filter by. Not all tags are listed in the filter box, and the exact list of tags supported may change before 1.16 is released, but the list is currently:&lt;br /&gt;
&lt;br /&gt;
* '''cooperative''': All human players are on the same team, versus the AI&lt;br /&gt;
* '''cosmetic''': These make the game look different, without changing gameplay&lt;br /&gt;
* '''difficulty''': Can make campaigns easier or harder&lt;br /&gt;
* '''rng''': Modify the randomness in the combat mechanics, or remove it entirely&lt;br /&gt;
* '''survival''': Fight against waves of enemies&lt;br /&gt;
* '''terraforming''': Players can change the terrain&lt;br /&gt;
&lt;br /&gt;
For example, if an A New Land style add-on had tags ''building'', ''terraforming'', ''anl'', ''city'', and ''survival'' then it would be shown if either ''Terraforming'' or ''Survival'' was selected in the drop-down; the other tags wouldn't affect the filtering.&lt;br /&gt;
&lt;br /&gt;
=== core ===&lt;br /&gt;
{{DevFeature1.13|0}}&lt;br /&gt;
: An optional string defining the id of the core which the addon is designed for. Defaults to &amp;quot;''default''&amp;quot;. Don't specify for an addon which is of type &amp;quot;''core''&amp;quot; itself. Note: DO NOT SET this unless you know why you need it! Giving it an invalid value can lead to mysterious errors with your campaign failing to load!&lt;br /&gt;
&lt;br /&gt;
=== translate ===&lt;br /&gt;
: If set to ''true'', the add-on would have been sent to and updated with [[WesCamp|WesCamp-i18n]], if that project were still active. However, as WesCamp is no longer active, this no longer does anything.&lt;br /&gt;
&lt;br /&gt;
: You should make sure your add-on complies with some very specific [[WesCamp#Preparing_your_add-on_for_WesCamp|conventions]] required to ease the process for translators as well as technical requirements.&lt;br /&gt;
&lt;br /&gt;
: Note: WesCamp was abandoned in 2014. Instead, please refer to:&lt;br /&gt;
* [[GettextForWesnothDevelopers]]&lt;br /&gt;
* [[GettextForTranslators#For_add-ons]]&lt;br /&gt;
* forum thread: [https://r.wesnoth.org/t46366 Guide: Translating your UMC without WesCamp].&lt;br /&gt;
&lt;br /&gt;
=== type ===&lt;br /&gt;
: Indicates the type of the add-on; used to filter listings in the downloads manager dialog. Acceptable values are:&lt;br /&gt;
&lt;br /&gt;
:* ''core'': replaces the whole wml tree. {{DevFeature1.13|0}}&lt;br /&gt;
:* ''campaign'': single player campaign.&lt;br /&gt;
:* ''scenario'': single player scenario.&lt;br /&gt;
:* ''campaign_sp_mp'': hybrid campaign.&lt;br /&gt;
:* ''era'': multiplayer era.&lt;br /&gt;
:* ''faction'': multiplayer stand-alone faction, or add-on for other available era.&lt;br /&gt;
:* ''map_pack'': multiplayer map-pack.&lt;br /&gt;
:* ''campaign_mp'': multiplayer campaign.&lt;br /&gt;
:* ''scenario_mp'': multiplayer scenario. (See the note below.)&lt;br /&gt;
:* ''mod_mp'': multiplayer modification ({{DevFeature1.13|11}} can also used for single-player modifications, although it's still called ''mod_mp'').&lt;br /&gt;
:* ''media'': miscellaneous resources for UMC authors/users, for example, music packs, packages of general-purpose WML, etc. &amp;lt;small&amp;gt;Note: Shows as Resources, not Media, in the add-ons interface&amp;lt;/small&amp;gt;&lt;br /&gt;
:* ''other'': The type to use when no other type fits.&lt;br /&gt;
: '''Note:''' If your add-on contains two or more separate multiplayer scenarios, use ''map_pack''.&lt;br /&gt;
&lt;br /&gt;
: '''This value is required'''.&lt;br /&gt;
&lt;br /&gt;
=== email ===&lt;br /&gt;
: Hidden e-mail address used by the server administrators to contact content authors in case of major issues. Again, this will only be seen by the server administrators and it is required that you provide one in case you need to be contacted about your add-on.&lt;br /&gt;
&lt;br /&gt;
: '''This value is required if forum_auth is not set to true'''.&lt;br /&gt;
&lt;br /&gt;
=== forum_auth ===&lt;br /&gt;
{{DevFeature1.17|3}}&lt;br /&gt;
: When set to ''true'', you will be prompted for your forum password when uploading your add-on. The username(s) available to select will be populated from the '''author''' field (before 1.19.7) or the '''primary_authors''' field (1.19.7+). If the username dropdown is empty, make sure to confirm that either the '''author''' or '''primary_authors''' field is present and spelled correctly, depending on what version of Wesnoth you are running.&lt;br /&gt;
&lt;br /&gt;
When set to true, the ''passphrase'' and ''email'' fields are also not required.&lt;br /&gt;
&lt;br /&gt;
=== primary_authors ===&lt;br /&gt;
{{DevFeature1.19|7}}&lt;br /&gt;
: A comma-delimited list of forum accounts that are allowed to upload new versions of the add-on or delete the add-on from the add-ons server.&lt;br /&gt;
&lt;br /&gt;
: '''Note:''' Due to limitations with Wesnoth's schema validation, this attribute needs to be on the line after '''forum_auth'''.&lt;br /&gt;
&lt;br /&gt;
=== secondary_authors ===&lt;br /&gt;
: A comma-delimited list of forum accounts that are allowed to upload new versions of the add-on, but aren't allowed to delete the add-on.&lt;br /&gt;
&lt;br /&gt;
=== [feedback] ===&lt;br /&gt;
: The [feedback] tag includes information used by the server to provide the client with a website URL for players to post feedback on an add-on and communicate with the maintainers. At this time, the official add-ons server is configured to take a single parameter described below.&lt;br /&gt;
&lt;br /&gt;
==== topic_id ====&lt;br /&gt;
: Topic id from the [http://forums.wesnoth.org/ Wesnoth.org forums] for the add-on's feedback or development topic maintained by the add-on uploader or author. For existing topics, this topic_id corresponds to the series of digits in the ''t=YYYYY'' portion of a URL like &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;http://forums.wesnoth.org/viewtopic.php?f=XX&amp;amp;t=YYYYY&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;. You must take special care to ensure this information is valid before uploading if you want players to be able to reach you!&lt;br /&gt;
&lt;br /&gt;
=== [translation] ===&lt;br /&gt;
{{DevFeature1.15|4}}&lt;br /&gt;
: Multiple [translation] tags can be used to provide the addon with a localized title and description to be seen in the addons manager. However, it should be noted that the declared translations won't influence the list of supported locales as it depends only on the presence of .mo and .po files for corresponding languages.&lt;br /&gt;
&lt;br /&gt;
==== language ====&lt;br /&gt;
: The target language code for the translation. The codes for each language are given in the big table on [https://www.wesnoth.org/gettext/] . You can use either its contracted version (like ''sv'' for Swedish) or a more precise variety (like ''zh_CN'' or ''ca_ES@valencia'').&lt;br /&gt;
: '''This value is required'''.&lt;br /&gt;
&lt;br /&gt;
==== title ====&lt;br /&gt;
: The translation of addon's title for the target language.&lt;br /&gt;
: '''This value is required'''.&lt;br /&gt;
&lt;br /&gt;
==== description ====&lt;br /&gt;
: The translation of addon's description for the target language.&lt;br /&gt;
&lt;br /&gt;
The add-on server keeps track of some other information about uploaded content, including when they were uploaded, what languages they have been at least partly translated into, how large they are on the server and the number of times they have been downloaded. For more information about this you can read [[CampaignServerWML]].&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Dependency Key Example ===&lt;br /&gt;
&lt;br /&gt;
The following dependency key could be used when the add-on needs the ''Imperial_Era'' and ''Era_of_Myths'' to be installed before it will work properly:&lt;br /&gt;
&lt;br /&gt;
 dependencies=Imperial_Era,Era_of_Myths&lt;br /&gt;
&lt;br /&gt;
=== Version Key Examples ===&lt;br /&gt;
&lt;br /&gt;
{{DevFeature1.17|13}} the schema validation rejects many of the '''good''' examples. https://github.com/wesnoth/wesnoth/issues/7396&lt;br /&gt;
&lt;br /&gt;
The following are examples of '''good''' version values:&lt;br /&gt;
&lt;br /&gt;
 version=&amp;quot;1.5&amp;quot;&lt;br /&gt;
 version=&amp;quot;0.11.4&amp;quot;&lt;br /&gt;
 version=&amp;quot;0.1.4beta&amp;quot;&lt;br /&gt;
 version=&amp;quot;1.5c&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The following are examples of '''bad''' version values:&lt;br /&gt;
&lt;br /&gt;
 version=&amp;quot;Beta1.5&amp;quot;&lt;br /&gt;
 version=&amp;quot;Incomplete (0.3.4)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
In both of the above examples the version number as read by the server will be '''0.0.0Beta1.5''' and '''0.0.0Incomplete (0.3.4)'''. You can clearly see why this will not be a good thing with the ''Update add-ons'' feature.&lt;br /&gt;
&lt;br /&gt;
Finally, here are some example version numbers and how they will be interpreted by the ''Update add-ons'' button. The number on the left will be considered an earlier number than the number on the right in each example.&lt;br /&gt;
&lt;br /&gt;
 0.5 &amp;lt; 1.0&lt;br /&gt;
 1.5 &amp;lt; 1.5c&lt;br /&gt;
 1.0 &amp;lt; 1.0.1&lt;br /&gt;
 1.0c &amp;lt; 1.0.1a&lt;br /&gt;
 1.0.1a &amp;lt; 1.0.1c&lt;br /&gt;
 1.0 Final &amp;lt; 1.0.1 Beta&lt;br /&gt;
&lt;br /&gt;
=== Example .pbl File ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=wml&amp;gt;&lt;br /&gt;
title=&amp;quot;My Campaign&amp;quot;&lt;br /&gt;
type=&amp;quot;campaign&amp;quot;&lt;br /&gt;
icon=&amp;quot;misc/ball.png&amp;quot;&lt;br /&gt;
version=&amp;quot;0.1.2&amp;quot;&lt;br /&gt;
author=&amp;quot;Me, artwork by myself&amp;quot;&lt;br /&gt;
passphrase=&amp;quot;This is like a password; see the security note in the documentation above before choosing a value of your own&amp;quot;&lt;br /&gt;
description=&amp;quot;You get to kill a lot of bad guys. But only the first map is done.&amp;quot;&lt;br /&gt;
email=&amp;quot;name@example.com&amp;quot;&lt;br /&gt;
[feedback]&lt;br /&gt;
    topic_id=12345&lt;br /&gt;
[/feedback]&lt;br /&gt;
# Note: the translation feature works on version 1.14.14, 1.15.4 and later only&lt;br /&gt;
[translation]&lt;br /&gt;
    language=&amp;quot;ru&amp;quot;&lt;br /&gt;
	title=&amp;quot;Моя Кампания&amp;quot;&lt;br /&gt;
    description=&amp;quot;Вам придётся завалить немало плохишей. Но пока что готова лишь первая карта.&amp;quot;&lt;br /&gt;
[/translation]&lt;br /&gt;
[translation]&lt;br /&gt;
    language=&amp;quot;zh_CN&amp;quot;&lt;br /&gt;
	title=&amp;quot;我的竞选&amp;quot;&lt;br /&gt;
    description=&amp;quot;你会杀死很多坏人。 但是只完成了第一张地图。(translated online)&amp;quot;&lt;br /&gt;
[/translation]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&lt;br /&gt;
* [[IGNFileFormat]]&lt;br /&gt;
* [[FancyAddonIcons]]&lt;br /&gt;
* [[ReferenceWML]]&lt;br /&gt;
* [[CampaignServerWML]]&lt;br /&gt;
&lt;br /&gt;
[[Category: WML Reference]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=74999</id>
		<title>Template:DevDownload</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=74999"/>
		<updated>2026-04-23T23:50:42Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Development (1.19 branch) ==&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
==== Windows (10 1903 and later) {{{4|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.23 | filename=wesnoth-1.19.23-win64.exe |&lt;br /&gt;
hash=69d2d16d491d1cb374a9a687cd23b803b44775ecb8339641abe45bef4bd273d9}}&lt;br /&gt;
&lt;br /&gt;
==== macOS (10.13 and later) {{{5|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.23 | filename=Wesnoth_1.19.23.dmg |&lt;br /&gt;
hash=44ecfc8257a0bb48baa958b86e300b2f01d46952deddd0e1770e87160f3958e7}}&lt;br /&gt;
&lt;br /&gt;
==== Source code ====&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/blob/master/INSTALL.md Compiling Wesnoth] - How to compile the source code&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.23 | filename=wesnoth-1.19.23.tar.bz2 |&lt;br /&gt;
hash=f5bd748ead5b0b9c838fa95ac6142cceac74354db4e57e1f9de5b9369bd39d48}}&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=74997</id>
		<title>Template:DevDownload</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=74997"/>
		<updated>2026-04-23T16:05:26Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Development (1.19 branch) ==&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
==== Windows (10 1903 and later) {{{4|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.22 | filename=wesnoth-1.19.22-win64.exe |&lt;br /&gt;
hash=805a9da0b230f4334431704ba0cea6b8dcb4613da2530fcf1f315ff382ec67e2}}&lt;br /&gt;
&lt;br /&gt;
==== macOS (10.13 and later) {{{5|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.22 | filename=Wesnoth_1.19.22.dmg |&lt;br /&gt;
hash=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855}}&lt;br /&gt;
&lt;br /&gt;
==== Source code ====&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/blob/master/INSTALL.md Compiling Wesnoth] - How to compile the source code&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.22 | filename=wesnoth-1.19.22.tar.bz2 |&lt;br /&gt;
hash=dccf874092cf42dfbef61e30217f55d40ab4608532ff6dba3be7c052ca3e0c66}}&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Template:StableDownload&amp;diff=74996</id>
		<title>Template:StableDownload</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Template:StableDownload&amp;diff=74996"/>
		<updated>2026-04-23T16:05:08Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Stable (1.18 branch) ==&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
==== Windows (10 1903 and later, 64-bit only) {{{4|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth-1.18 |&lt;br /&gt;
version=1.18.7 | filename=wesnoth-1.18.7-win64.exe |&lt;br /&gt;
hash=1ebe433b8f7b526944b63d15caf11f42481375130431237b3f1139a517c7c7bb}}&lt;br /&gt;
&lt;br /&gt;
==== macOS (10.12 and later) {{{5|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth-1.18 |&lt;br /&gt;
version=1.18.7 | filename=Wesnoth_1.18.7.dmg |&lt;br /&gt;
hash=258f797ad4ae1f82b4479706875a372857c17130e221689c56647234513eca5f}}&lt;br /&gt;
&lt;br /&gt;
==== Source code ====&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/blob/master/INSTALL.md Compiling Wesnoth] - How to compile the source code&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth-1.18 |&lt;br /&gt;
version=1.18.7 | filename=wesnoth-1.18.7.tar.bz2 |&lt;br /&gt;
hash=d6b50cfdf4388954a1c3da66a61abacdc7643a17aa78a16196e16e720a661fc2}}&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=CompatibilityStandardsV2&amp;diff=74995</id>
		<title>CompatibilityStandardsV2</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=CompatibilityStandardsV2&amp;diff=74995"/>
		<updated>2026-04-23T15:57:16Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* Deprecation levels - When to remove deprecated features */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;As a piece of software matures, there are often new designs, paradigms, and idioms developed which are superior to old ones. This creates an inherent conflict between the need for progress and the need for compatibility. Wesnoth is no exception. This document describes Wesnoth's approach toward resolving that conflict in a way which is most beneficial to both goals, as well as the rationale behind this approach.&lt;br /&gt;
&lt;br /&gt;
== Policy ==&lt;br /&gt;
This policy defines how the creation of new Wesnoth APIs and the deprecation and removal of old ones are to be handled. For the purposes of this document &amp;quot;API&amp;quot; means &amp;quot;any technical channel by which a content creator interacts with the game engine&amp;quot;. This includes, but is not limited to: preprocessor macros, WML tags, WFL functions, IPFs, and the Lua API. Note that this policy applies only to software APIs. Core content such as sprites, portraits, animations, lore, etc. are to be updated freely, without consideration for stylistic or literary conflicts that such content changes may present to add-ons.&lt;br /&gt;
&lt;br /&gt;
=== When to deprecate ===&lt;br /&gt;
==== Adding a better API ====&lt;br /&gt;
Any time a superior API is introduced which has '''complete feature-parity''' with an existing API, the old one should be immediately deprecated. Note that in most cases, in order to be considered to have &amp;quot;complete feature-parity&amp;quot;, the API should be available in the same language or area of the code as the obsolete one was. For example, the introduction of a more powerful API in Lua which can accomplish a superset of the functionality which had previously been available with a certain WML tag would not obsolete that WML tag. Exceptions can be made to this rule in cases where it is clear that the feature does not make a lot of sense in its current language or area, and was merely there for legacy reasons (such as the proper place for it not having been introduced yet at the time of its creation). Such exceptions should be determined by developer consensus.&lt;br /&gt;
&lt;br /&gt;
==== Preventing needed fixes or improvements ====&lt;br /&gt;
In such cases where a feature is preventing an important new feature from being added or makes it impossible to fix a problem impacting players, it can be preferable to deprecate the API to allow for the necessary changes to be made. APIs deprecated for this reason should still follow the deprecation schedule as normal, unless the fix or improvement is urgently needed.&lt;br /&gt;
&lt;br /&gt;
==== Actively harmful ====&lt;br /&gt;
If an API is found to cause significant problems for players or UMC authors, such as being prone to causing crashes while also very difficult to properly fix, corrupting saves and replays when not used correctly while being difficult to use correctly, or other similar situations, then such APIs should be deprecated and removed regardless of whether there is a replacement available.&lt;br /&gt;
&lt;br /&gt;
==== Unused ====&lt;br /&gt;
Any API that is not used in mainline and also is not used by the most recent version of an add-on on the add-ons server of the current or previous stable release can be deprecated. For example, if an add-on for 1.16 uses a deprecated API but the updated version of the add-on for 1.18 does not, then that add-on is not considered as currently using the API. Once deprecated for this reason, a new add-on being uploaded that uses the API is not a reason to undeprecate it.&lt;br /&gt;
&lt;br /&gt;
This does not need to be a passive process where developers simply check the add-ons server for whether an API is used - developers who want to deprecate an API for removal can proactively talk to and work with UMC authors to help update their add-ons to remove usage of said API. This can be anything from talking with them online about how to update to submitting updated code directly (ie: opening a PR against an add-on's public git repository).&lt;br /&gt;
&lt;br /&gt;
Deprecating and then removing APIs that are unused is the most preferred approach since their removal does not have any impact on UMC authors.&lt;br /&gt;
&lt;br /&gt;
=== When NOT to deprecate ===&lt;br /&gt;
==== Style ====&lt;br /&gt;
Deprecation should not be done purely for reasons of style. This is very subjective and prone to change as new contributors join and current contributors leave or become less active. As such, allowing deprecation for stylistic reasons would lead to entirely unnecessary work for UMC authors as developer preferences change over time.&lt;br /&gt;
&lt;br /&gt;
==== Renaming ====&lt;br /&gt;
While there can be exceptions, it is rarely a net positive to deprecate an API simply for the sake of renaming it to something else. It is preferable to either add a second name for the same function, leaving the old name as-is, or simply live with the current name rather than expecting all UMC authors using the API to update to the new name.&lt;br /&gt;
&lt;br /&gt;
==== Any other reason ====&lt;br /&gt;
Accepted reasons for deprecating APIs should be something that's discussed and agreed upon by the development team while also, ideally, including UMC authors. It should not become the norm that additional reasons to deprecate APIs are treated as exceptions and left as an increasingly forgotten discussion on Discord, IRC, or the forums - they should be added to here with the reasoning behind them.&lt;br /&gt;
&lt;br /&gt;
=== Deprecation awareness ===&lt;br /&gt;
==== Conflicting goals ====&lt;br /&gt;
When deprecating APIs there is an inherent conflict in terms of how to make UMC authors aware of the deprecation. After all, if they aren't aware something is deprecated, they can't know they may need to update their add-on. Therefore, deprecations need to be displayed in a place where they will see them and most UMC authors don't look at Wesnoth's logs unless there's some other issue they're investigating. At the same time, deprecation warnings aren't relevant to players and spamming deprecation warnings is not an effective way of communicating what the issues are.&lt;br /&gt;
&lt;br /&gt;
==== A middle ground ====&lt;br /&gt;
Each deprecated feature should make use of an appropriate deprecation function call for that language or subsystem to ensure that the appropriate deprecation notice is printed to the log output. Additionally, deprecation warnings should be displayed in-game in the following cases:&lt;br /&gt;
* If the player is running a development version, level 3 and level 4 deprecations should be shown in-game by default.&lt;br /&gt;
* If the player enables debug mode then all deprecation warnings should be shown, regardless of whether they're using a stable release or a development release.&lt;br /&gt;
&lt;br /&gt;
==== Documentation ====&lt;br /&gt;
It is also not enough to only display a warning at runtime when something deprecated is encountered. It is the responsibility of the development team to proactively make UMC authors aware of the deprecations and removals being done. To accomplish this:&lt;br /&gt;
* When an API is deprecated, and again if it's later removed, its deprecation or removal must be documented in the appropriate section of the changelog for the version it was deprecated or removed in.&lt;br /&gt;
* Likewise, it should be added to https://wiki.wesnoth.org/CompatibilityBreakingChanges&lt;br /&gt;
&lt;br /&gt;
Additionally, it is not enough to simply say that an API is deprecated. In the deprecation message itself as well as in the changelog and https://wiki.wesnoth.org/CompatibilityBreakingChanges, a description must be included as to why it was deprecated or removed and how UMC authors can update their add-ons to address it.&lt;br /&gt;
&lt;br /&gt;
Lastly, it should be understood that &amp;quot;removed&amp;quot; doesn't necessarily mean that the API is entirely gone from Wesnoth's codebase. There is no maintenance burden to keeping macro or method stubs that do nothing aside from printing an error message describing what was removed and why. Keeping such stubs around is highly encouraged as it is helpful for UMC authors trying to update very old add-ons to the current version of Wesnoth.&lt;br /&gt;
&lt;br /&gt;
=== How to deprecate ===&lt;br /&gt;
Every effort should be made to create the simplest possible wrappers which will translate from an obsolete API to the updated one. Such wrappers should ideally be organized into their own compatibility file or module, and set up in such a way that the internals of the updated API will not affect how the old calls get wrapped to the new one. Essentially, the idea is to create a set-it-and-forget-it compatibility wrapper which will continue to work regardless of updates made to the newer API.&lt;br /&gt;
&lt;br /&gt;
Additionally, in all cases where it's practical, the wmllint tool must be updated to be able to automatically handle updating add-ons for anything that's been deprecated except for APIs deprecated at level 1. While developers are still heavily encouraged to add wmllint support for level 1 deprecations, it is not required as these do not show deprecation warnings by default and are expected to continue working indefinitely.&lt;br /&gt;
&lt;br /&gt;
=== Deprecation levels - When to remove deprecated features ===&lt;br /&gt;
While creating simple compatibility wrappers should be possible most of the time, it would be unreasonable to assume that this approach will be viable in absolutely every case. Wesnoth's compatibility policy therefore has four different levels of deprecation which are used to set expectations for when and if an API is expected to be removed:&lt;br /&gt;
&lt;br /&gt;
#Deprecated indefinitely &amp;amp;mdash; This deprecation level is for changes which have newer preferred alternatives but, barring any unforeseen issues, have little to no maintenance impact and should be kept indefinitely in order to reduce the work required for UMC authors to maintain their content. For example, functions or attributes which have had their names changed, macros which have been replaced with tags, or simple wrappers with little to no maintenance overhead. If future large scale changes make it impractical to maintain an API deprecated at this level then the deprecation level should be raised accordingly. This is generally the preferred deprecation level - higher deprecation levels are for APIs which are intended for eventual removal and must be weighed against the maintenance work this forces upon UMC authors.&lt;br /&gt;
#Deprecated with the intention of future removal &amp;amp;mdash; This deprecation level is for APIs which will be removed in a future version, but the version they are removed in has not yet have been decided. Barring urgent circumstances, all APIs that are deprecated with the intention of being removed '''must''' start with being deprecated at level 2 for 1-2 development cycles at minimum depending on the impact of the API's removal on UMC authors. Once the API has spent the necessary amount of time at deprecation level 2, it can be moved to deprecation level 3. It is not allowed to deprecate an API at level 2 and change it to level 3 in the same development cycle. There is no maximum amount of time that an API can remain deprecated at level 2.&lt;br /&gt;
#Deprecated for removal in a specific version &amp;amp;mdash; This deprecation level is for APIs which were previously deprecated at level 2 and now have a future version at which they will be removed. The version an API is removed in must be at least two development cycles after the API was deprecated at level 2. It is not allowed to move an API to deprecation level 3 and then remove it in the same development cycle.&lt;br /&gt;
#Removed without deprecation &amp;amp;mdash; This level should be used EXTREMELY rarely, and only in cases where it is ABSOLUTELY NECESSARY. Occasionally, an update to a feature will change the underlying architecture in such a fundamental way that the old paradigm cannot coexist with the new one no matter how much redundant code one would create. While this kind of scenario is extremely rare, and every effort should be made to find creative solutions to avoid it, there are occasionally cases where it truly is impossible to maintain both methods even in the short term. This level should only be used with broad developer consensus, after the majority of active developers familiar with the feature in question have given at least some thought to trying to deprecate gracefully and failed. This level should also be used for macro and method stubs containing only an error message describing what was removed and why, as well as if an API feature is found to have a security vulnerability which can't be fixed.&lt;br /&gt;
&lt;br /&gt;
APIs that are clearly labeled as being experimental, such as a WML tag having &amp;quot;experimental&amp;quot; in the name or a lua function having &amp;quot;experimental&amp;quot; in its name or package, can be be removed or deprecated at any level at any time. These are APIs which UMC authors use at their own risk.&lt;br /&gt;
&lt;br /&gt;
== Rationale ==&lt;br /&gt;
The above policy is the result of a large amount of thought, discussion, and debate. Following is a brief outline of the considerations and goals on both sides of the problem, why there is an inherent conflict between them, some failed approaches to resolving the conflict, and how the final policy ultimately maximizes the pursuit of both goals.&lt;br /&gt;
&lt;br /&gt;
=== The Problem - The paradox of progress ===&lt;br /&gt;
Invariably, as development on any project moves forward, developers will realize that there are better, cleaner, or more elegant ways to structure things than they had been previously. These changes can be to improve efficiency, make an API more intuitive, keep code better organized, make common tasks more straightforward, or accomplish any number of other positive things. These changes can also make the development of additional features much more viable. In short, progress is good.&lt;br /&gt;
&lt;br /&gt;
On the other hand, a content-heavy program such as Wesnoth relies on the ability of content creators to efficiently create, maintain, and update their content. Too many changes all at once will force creators of existing content to spend obscene amounts time updating their creations just to keeping them up-to-date and in working order. This can lead to a high amount of frustration, a drop in motivation, and a decline in content being created. In short, progress is bad.&lt;br /&gt;
&lt;br /&gt;
=== Backwards Compatibility - benefits and drawbacks ===&lt;br /&gt;
Most of the time, older paradigms can still be maintained in a manner in which they coexist with the newer ones. This allows for existing content to continue functioning, while at the same time allowing and encouraging new content to be created using the newer methods. However, maintaining such code can be problematic in the following ways:&lt;br /&gt;
#There may eventually be architectural changes a developer would want to make where the old method's square peg no longer fits, even forcibly, into the new method's round hole.&lt;br /&gt;
#Having compatibility code hanging around may, depending on how it is implemented, mean that any updates made to the feature, module, or subsystem in question would have to made in both the new, cleaner design, and the older, poorly structured one, adding more work for developers.&lt;br /&gt;
&lt;br /&gt;
=== The Naive Approach - Deprecate and remove everything old ===&lt;br /&gt;
One approach to balancing old and new is to deprecate the old and slate its eventual removal after either a certain amount of time has passed or a certain number of subsequent versions have been released. This sounds good in theory, but in practice, there will be many changes which are minor or cosmetic in nature and which will add up. Things like replacing macros with WML tags, updating the name of an API call or order of parameters to be more consistent with other similar functions, or switching from a functional to an object-oriented structure are very good for organizational purposes, but will result in a large amount of maintenance required on the part of content creators to keep existing code operational, and for very little real gain. This approach invariably leads to the situation where so much is being changed from one version to the next that creators turn into maintainers, forced to spend nearly all of their time trying to stay ahead of the update curve in an attempt to keep their existing content working, and leaving them very little time and motivation to create new content. And of course, by the time they're finished painstakingly updating their existing code for every little change made for the current release, whoops, there's a new release with a whole slew of new changes that need accounting for. It simply becomes unmanageable.&lt;br /&gt;
&lt;br /&gt;
=== The Naive Approach - Deprecate and remove only when necessary ===&lt;br /&gt;
The opposite approach would be to keep all existing paradigms until they actively interfere with a new architecture or create a double-maintenance problem. While this approach does cut down on the maintenance burden by ensuring that content using an older design continues to work, it hinders progress by the fact that the moment at which it first becomes clear that an architectural or double-maintenance problem will occur is exactly the same moment at which keeping the old structure around becomes problematic. Beginning a deprecation cycle at that point and then having to &amp;quot;wait out&amp;quot; the old paradigm will cause an unacceptable delay in development.&lt;br /&gt;
&lt;br /&gt;
=== The Middle Ground - Deprecate everything old, remove only when necessary ===&lt;br /&gt;
Most of the time, older APIs can be implemented in terms of their newer, cleaner counterparts through the use of things like simple wrappers, parse-translators which re-write the older paradigm's code in terms of the new one, or other relatively low-maintenance &amp;quot;set-it-and-forget-it&amp;quot; approaches. These simple wrappers are not really detrimental to making progress, don't require updating when the new APIs internals are changed, and can usually be organized into their own files and/or modules so that they don't clutter the cleaner code. Many such wrappers will never truly present either of the backwards compatibility drawbacks mentioned above. As such, there is really no detriment to keeping them around indefinitely. However, occasionally, a new idea or approach will be put forth that updates the newer paradigm in such a way that the older one can no longer cleanly wrap to it. This usually happens, as inspiration is wont to do, suddenly, unexpectedly, and without warning. As such developers need the flexibility to be able to remove outdated code as freely as possible when the situation requires. Therefore, the ideal solution would be to deprecate any API which has newer, feature-complete ways to do it, while leaving it in the codebase until such time as its presence becomes a hindrance. Essentially, deprecation need not necessarily mean &amp;quot;this WILL be removed&amp;quot; so much as &amp;quot;this is now a candidate for removal&amp;quot;. By separating the concepts of deprecation and removal, both goals can be better served.&lt;br /&gt;
&lt;br /&gt;
=== Undefined removal timeline - issues encountered ===&lt;br /&gt;
In practice however, simply stating something is a candidate for removal while providing no further information on when it will actually be removed causes multiple issues:&lt;br /&gt;
* When a deprecated API is causing a maintenance burden worthy of removal is a subjective decision, often leading to debates over removal any time a developer decides it's time for an API to be removed, initially deprecated, or moved to a higher deprecation level.&lt;br /&gt;
* Lack of developer incentive to provide good documentation and support for the deprecation of the API given there's no particular timeline for when it will actually cause issues for UMC authors.&lt;br /&gt;
* UMC authors often don't find out about deprecated APIs and so can't possibly migrate to the new APIs.&lt;br /&gt;
* UMC authors aren't provided the documentation or tooling support needed to make updating their add-ons easier.&lt;br /&gt;
* UMC authors may simply decide there's no reason to update to the new API even if they know it's deprecated and how to update their add-on. After all, why spend time updating APIs that may stick around forever?&lt;br /&gt;
* Giving a version that an API '''may''' be removed instead of a version that it '''will''' be removed is not as clear as it may seem to UMC authors who aren't already aware of how Wesnoth handles deprecation. This can lead to UMC authors ignoring deprecation warnings after seeing such warnings for APIs which provide a version that's years old.&lt;br /&gt;
&lt;br /&gt;
=== Certainty is also a benefit ===&lt;br /&gt;
Wesnoth continues to exist today in large part because of the content made by its community. As such, developers should always be sparing in what they choose to deprecate and what they choose to remove, so that UMC authors can update their content to the latest version of Wesnoth without needing to make a herculean effort every couple of years.&lt;br /&gt;
&lt;br /&gt;
But, for cases where APIs are removed, the benefit of providing certainty for when that will happen outweighs the flexibility of being able remove them at any time after they been marked as deprecated. It encourages developers to provide the documentation and tooling support to make it easier for UMC authors to update their add-ons, and it lets UMC authors know when they can expect deprecated APIs to be removed rather than it effectively happening at random when a developer decides a deprecated API needs to be dropped.&lt;br /&gt;
&lt;br /&gt;
=== More Complex Cases - One size does not fit all ===&lt;br /&gt;
There may, however, be cases where the obsolete API cannot be implemented cleanly in terms of the updated one and must be maintained separately, or, in extremely rare cases, cannot coexist at all. There needs to be some leeway for such cases as well. In the former case, it therefore makes sense to allow for a feature to be deprecated pending removal after the shortest reasonable deprecation period. In the latter case, there is obviously no choice but to make the change and remove the old immediately. Developers should, naturally, be encouraged to find creative solutions to avoid such cases, but it is inevitable that there will eventually be a few cases where no graceful transition procedure can be found. These more aggressive forms of deprecation should only be done with developer consensus, and only after all options of creating a backwards-compatible transition have been exhausted.&lt;br /&gt;
&lt;br /&gt;
=== Graphics - The exception that proves the rule ===&lt;br /&gt;
The one area where backwards compatibility should NOT be a factor is graphical changes. Any change to the terrain graphics, unit sprites, or portrait images has the potential to result in visually-incompatible custom content. Core content creators cannot be expected to maintain a deprecated visual style alongside a more modern one, as any approach toward doing so would add an unreasonable amount of bloat and overhead, and make it very difficult for core graphics to be updated without having to do double the work. In addition, add-ons will continue to function even with such visual incompatibilities present, they just won't look right. As such, it can be said that stylistic incompatibilities fall more within the realm of content than of code, and it is not unreasonable to expect a content creator to... well...  create content. Expecting the graphical style to remain backwards-compatible would be just as unreasonable as expecting stories, help descriptions, or other forms of lore to never change because they may introduce plot holes into add-on stories. Essentially, since the goal of the software portion of Wesnoth is, at its heart, merely to facilitate the creation and advancement of this kind of content, core content needs to be free to develop unhindered by considerations for add-on content.&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=CompatibilityStandardsV2&amp;diff=74994</id>
		<title>CompatibilityStandardsV2</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=CompatibilityStandardsV2&amp;diff=74994"/>
		<updated>2026-04-23T15:54:15Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* Deprecation levels - When to remove deprecated features */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;As a piece of software matures, there are often new designs, paradigms, and idioms developed which are superior to old ones. This creates an inherent conflict between the need for progress and the need for compatibility. Wesnoth is no exception. This document describes Wesnoth's approach toward resolving that conflict in a way which is most beneficial to both goals, as well as the rationale behind this approach.&lt;br /&gt;
&lt;br /&gt;
== Policy ==&lt;br /&gt;
This policy defines how the creation of new Wesnoth APIs and the deprecation and removal of old ones are to be handled. For the purposes of this document &amp;quot;API&amp;quot; means &amp;quot;any technical channel by which a content creator interacts with the game engine&amp;quot;. This includes, but is not limited to: preprocessor macros, WML tags, WFL functions, IPFs, and the Lua API. Note that this policy applies only to software APIs. Core content such as sprites, portraits, animations, lore, etc. are to be updated freely, without consideration for stylistic or literary conflicts that such content changes may present to add-ons.&lt;br /&gt;
&lt;br /&gt;
=== When to deprecate ===&lt;br /&gt;
==== Adding a better API ====&lt;br /&gt;
Any time a superior API is introduced which has '''complete feature-parity''' with an existing API, the old one should be immediately deprecated. Note that in most cases, in order to be considered to have &amp;quot;complete feature-parity&amp;quot;, the API should be available in the same language or area of the code as the obsolete one was. For example, the introduction of a more powerful API in Lua which can accomplish a superset of the functionality which had previously been available with a certain WML tag would not obsolete that WML tag. Exceptions can be made to this rule in cases where it is clear that the feature does not make a lot of sense in its current language or area, and was merely there for legacy reasons (such as the proper place for it not having been introduced yet at the time of its creation). Such exceptions should be determined by developer consensus.&lt;br /&gt;
&lt;br /&gt;
==== Preventing needed fixes or improvements ====&lt;br /&gt;
In such cases where a feature is preventing an important new feature from being added or makes it impossible to fix a problem impacting players, it can be preferable to deprecate the API to allow for the necessary changes to be made. APIs deprecated for this reason should still follow the deprecation schedule as normal, unless the fix or improvement is urgently needed.&lt;br /&gt;
&lt;br /&gt;
==== Actively harmful ====&lt;br /&gt;
If an API is found to cause significant problems for players or UMC authors, such as being prone to causing crashes while also very difficult to properly fix, corrupting saves and replays when not used correctly while being difficult to use correctly, or other similar situations, then such APIs should be deprecated and removed regardless of whether there is a replacement available.&lt;br /&gt;
&lt;br /&gt;
==== Unused ====&lt;br /&gt;
Any API that is not used in mainline and also is not used by the most recent version of an add-on on the add-ons server of the current or previous stable release can be deprecated. For example, if an add-on for 1.16 uses a deprecated API but the updated version of the add-on for 1.18 does not, then that add-on is not considered as currently using the API. Once deprecated for this reason, a new add-on being uploaded that uses the API is not a reason to undeprecate it.&lt;br /&gt;
&lt;br /&gt;
This does not need to be a passive process where developers simply check the add-ons server for whether an API is used - developers who want to deprecate an API for removal can proactively talk to and work with UMC authors to help update their add-ons to remove usage of said API. This can be anything from talking with them online about how to update to submitting updated code directly (ie: opening a PR against an add-on's public git repository).&lt;br /&gt;
&lt;br /&gt;
Deprecating and then removing APIs that are unused is the most preferred approach since their removal does not have any impact on UMC authors.&lt;br /&gt;
&lt;br /&gt;
=== When NOT to deprecate ===&lt;br /&gt;
==== Style ====&lt;br /&gt;
Deprecation should not be done purely for reasons of style. This is very subjective and prone to change as new contributors join and current contributors leave or become less active. As such, allowing deprecation for stylistic reasons would lead to entirely unnecessary work for UMC authors as developer preferences change over time.&lt;br /&gt;
&lt;br /&gt;
==== Renaming ====&lt;br /&gt;
While there can be exceptions, it is rarely a net positive to deprecate an API simply for the sake of renaming it to something else. It is preferable to either add a second name for the same function, leaving the old name as-is, or simply live with the current name rather than expecting all UMC authors using the API to update to the new name.&lt;br /&gt;
&lt;br /&gt;
==== Any other reason ====&lt;br /&gt;
Accepted reasons for deprecating APIs should be something that's discussed and agreed upon by the development team while also, ideally, including UMC authors. It should not become the norm that additional reasons to deprecate APIs are treated as exceptions and left as an increasingly forgotten discussion on Discord, IRC, or the forums - they should be added to here with the reasoning behind them.&lt;br /&gt;
&lt;br /&gt;
=== Deprecation awareness ===&lt;br /&gt;
==== Conflicting goals ====&lt;br /&gt;
When deprecating APIs there is an inherent conflict in terms of how to make UMC authors aware of the deprecation. After all, if they aren't aware something is deprecated, they can't know they may need to update their add-on. Therefore, deprecations need to be displayed in a place where they will see them and most UMC authors don't look at Wesnoth's logs unless there's some other issue they're investigating. At the same time, deprecation warnings aren't relevant to players and spamming deprecation warnings is not an effective way of communicating what the issues are.&lt;br /&gt;
&lt;br /&gt;
==== A middle ground ====&lt;br /&gt;
Each deprecated feature should make use of an appropriate deprecation function call for that language or subsystem to ensure that the appropriate deprecation notice is printed to the log output. Additionally, deprecation warnings should be displayed in-game in the following cases:&lt;br /&gt;
* If the player is running a development version, level 3 and level 4 deprecations should be shown in-game by default.&lt;br /&gt;
* If the player enables debug mode then all deprecation warnings should be shown, regardless of whether they're using a stable release or a development release.&lt;br /&gt;
&lt;br /&gt;
==== Documentation ====&lt;br /&gt;
It is also not enough to only display a warning at runtime when something deprecated is encountered. It is the responsibility of the development team to proactively make UMC authors aware of the deprecations and removals being done. To accomplish this:&lt;br /&gt;
* When an API is deprecated, and again if it's later removed, its deprecation or removal must be documented in the appropriate section of the changelog for the version it was deprecated or removed in.&lt;br /&gt;
* Likewise, it should be added to https://wiki.wesnoth.org/CompatibilityBreakingChanges&lt;br /&gt;
&lt;br /&gt;
Additionally, it is not enough to simply say that an API is deprecated. In the deprecation message itself as well as in the changelog and https://wiki.wesnoth.org/CompatibilityBreakingChanges, a description must be included as to why it was deprecated or removed and how UMC authors can update their add-ons to address it.&lt;br /&gt;
&lt;br /&gt;
Lastly, it should be understood that &amp;quot;removed&amp;quot; doesn't necessarily mean that the API is entirely gone from Wesnoth's codebase. There is no maintenance burden to keeping macro or method stubs that do nothing aside from printing an error message describing what was removed and why. Keeping such stubs around is highly encouraged as it is helpful for UMC authors trying to update very old add-ons to the current version of Wesnoth.&lt;br /&gt;
&lt;br /&gt;
=== How to deprecate ===&lt;br /&gt;
Every effort should be made to create the simplest possible wrappers which will translate from an obsolete API to the updated one. Such wrappers should ideally be organized into their own compatibility file or module, and set up in such a way that the internals of the updated API will not affect how the old calls get wrapped to the new one. Essentially, the idea is to create a set-it-and-forget-it compatibility wrapper which will continue to work regardless of updates made to the newer API.&lt;br /&gt;
&lt;br /&gt;
Additionally, in all cases where it's practical, the wmllint tool must be updated to be able to automatically handle updating add-ons for anything that's been deprecated except for APIs deprecated at level 1. While developers are still heavily encouraged to add wmllint support for level 1 deprecations, it is not required as these do not show deprecation warnings by default and are expected to continue working indefinitely.&lt;br /&gt;
&lt;br /&gt;
=== Deprecation levels - When to remove deprecated features ===&lt;br /&gt;
While creating simple compatibility wrappers should be possible most of the time, it would be unreasonable to assume that this approach will be viable in absolutely every case. Wesnoth's compatibility policy therefore has four different levels of deprecation which are used to set expectations for when and if an API is expected to be removed:&lt;br /&gt;
&lt;br /&gt;
#Deprecated indefinitely &amp;amp;mdash; This deprecation level is for changes which have newer preferred alternatives but, barring any unforeseen issues, have little to no maintenance impact and should be kept indefinitely in order to reduce the work required for UMC authors to maintain their content. For example, functions or attributes which have had their names changed, macros which have been replaced with tags, or simple wrappers with little to no maintenance overhead. If future large scale changes make it impractical to maintain an API deprecated at this level then the deprecation level should be raised accordingly. This is generally the preferred deprecation level - higher deprecation levels are for APIs which are intended for eventual removal and must be weighed against the maintenance work this forces upon UMC authors.&lt;br /&gt;
#Deprecated with the intention of future removal &amp;amp;mdash; This deprecation level is for APIs which will be removed in a future version, but the version they are removed in has not yet have been decided. Barring urgent circumstances, all APIs that are deprecated with the intention of being removed '''must''' start with being deprecated at level 2 for 1-2 development cycles at minimum depending on the impact of the API's removal on UMC authors. Once the API has spent the necessary amount of time at deprecation level 2, it can be moved to deprecation level 3. It is not allowed to deprecate an API at level 2 and change it to level 3 in the same development cycle. There is no maximum amount of time that an API can remain deprecated at level 2.&lt;br /&gt;
#Deprecated for removal in a specific version &amp;amp;mdash; This deprecation level is for APIs which were previously deprecated at level 2 and now have a future version at which they will be removed. The version an API is removed in must be at least two development cycles after the API was deprecated at level 2. It is not allowed to move an API to deprecation level 3 and then remove it in the same development cycle.&lt;br /&gt;
#Removed without deprecation &amp;amp;mdash; This level should be used EXTREMELY rarely, and only in cases where it is ABSOLUTELY NECESSARY. Occasionally, an update to a feature will change the underlying architecture in such a fundamental way that the old paradigm cannot coexist with the new one no matter how much redundant code one would create. While this kind of scenario is extremely rare, and every effort should be made to find creative solutions to avoid it, there are occasionally cases where it truly is impossible to maintain both methods even in the short term. This level should only be used with broad developer consensus, after the majority of active developers familiar with the feature in question have given at least some thought to trying to deprecate gracefully and failed. This level should also be used for macro and method stubs containing only an error message describing what was removed and why, as well as if an API feature is found to have a security vulnerability which can't be fixed.&lt;br /&gt;
&lt;br /&gt;
APIs that are clearly labeled as being experimental, such as a WML tag having &amp;quot;experimental&amp;quot; in the name or a lua function having &amp;quot;experimental&amp;quot; in its name or package, can be be removed at any time. These are APIs which UMC authors use at their own risk.&lt;br /&gt;
&lt;br /&gt;
== Rationale ==&lt;br /&gt;
The above policy is the result of a large amount of thought, discussion, and debate. Following is a brief outline of the considerations and goals on both sides of the problem, why there is an inherent conflict between them, some failed approaches to resolving the conflict, and how the final policy ultimately maximizes the pursuit of both goals.&lt;br /&gt;
&lt;br /&gt;
=== The Problem - The paradox of progress ===&lt;br /&gt;
Invariably, as development on any project moves forward, developers will realize that there are better, cleaner, or more elegant ways to structure things than they had been previously. These changes can be to improve efficiency, make an API more intuitive, keep code better organized, make common tasks more straightforward, or accomplish any number of other positive things. These changes can also make the development of additional features much more viable. In short, progress is good.&lt;br /&gt;
&lt;br /&gt;
On the other hand, a content-heavy program such as Wesnoth relies on the ability of content creators to efficiently create, maintain, and update their content. Too many changes all at once will force creators of existing content to spend obscene amounts time updating their creations just to keeping them up-to-date and in working order. This can lead to a high amount of frustration, a drop in motivation, and a decline in content being created. In short, progress is bad.&lt;br /&gt;
&lt;br /&gt;
=== Backwards Compatibility - benefits and drawbacks ===&lt;br /&gt;
Most of the time, older paradigms can still be maintained in a manner in which they coexist with the newer ones. This allows for existing content to continue functioning, while at the same time allowing and encouraging new content to be created using the newer methods. However, maintaining such code can be problematic in the following ways:&lt;br /&gt;
#There may eventually be architectural changes a developer would want to make where the old method's square peg no longer fits, even forcibly, into the new method's round hole.&lt;br /&gt;
#Having compatibility code hanging around may, depending on how it is implemented, mean that any updates made to the feature, module, or subsystem in question would have to made in both the new, cleaner design, and the older, poorly structured one, adding more work for developers.&lt;br /&gt;
&lt;br /&gt;
=== The Naive Approach - Deprecate and remove everything old ===&lt;br /&gt;
One approach to balancing old and new is to deprecate the old and slate its eventual removal after either a certain amount of time has passed or a certain number of subsequent versions have been released. This sounds good in theory, but in practice, there will be many changes which are minor or cosmetic in nature and which will add up. Things like replacing macros with WML tags, updating the name of an API call or order of parameters to be more consistent with other similar functions, or switching from a functional to an object-oriented structure are very good for organizational purposes, but will result in a large amount of maintenance required on the part of content creators to keep existing code operational, and for very little real gain. This approach invariably leads to the situation where so much is being changed from one version to the next that creators turn into maintainers, forced to spend nearly all of their time trying to stay ahead of the update curve in an attempt to keep their existing content working, and leaving them very little time and motivation to create new content. And of course, by the time they're finished painstakingly updating their existing code for every little change made for the current release, whoops, there's a new release with a whole slew of new changes that need accounting for. It simply becomes unmanageable.&lt;br /&gt;
&lt;br /&gt;
=== The Naive Approach - Deprecate and remove only when necessary ===&lt;br /&gt;
The opposite approach would be to keep all existing paradigms until they actively interfere with a new architecture or create a double-maintenance problem. While this approach does cut down on the maintenance burden by ensuring that content using an older design continues to work, it hinders progress by the fact that the moment at which it first becomes clear that an architectural or double-maintenance problem will occur is exactly the same moment at which keeping the old structure around becomes problematic. Beginning a deprecation cycle at that point and then having to &amp;quot;wait out&amp;quot; the old paradigm will cause an unacceptable delay in development.&lt;br /&gt;
&lt;br /&gt;
=== The Middle Ground - Deprecate everything old, remove only when necessary ===&lt;br /&gt;
Most of the time, older APIs can be implemented in terms of their newer, cleaner counterparts through the use of things like simple wrappers, parse-translators which re-write the older paradigm's code in terms of the new one, or other relatively low-maintenance &amp;quot;set-it-and-forget-it&amp;quot; approaches. These simple wrappers are not really detrimental to making progress, don't require updating when the new APIs internals are changed, and can usually be organized into their own files and/or modules so that they don't clutter the cleaner code. Many such wrappers will never truly present either of the backwards compatibility drawbacks mentioned above. As such, there is really no detriment to keeping them around indefinitely. However, occasionally, a new idea or approach will be put forth that updates the newer paradigm in such a way that the older one can no longer cleanly wrap to it. This usually happens, as inspiration is wont to do, suddenly, unexpectedly, and without warning. As such developers need the flexibility to be able to remove outdated code as freely as possible when the situation requires. Therefore, the ideal solution would be to deprecate any API which has newer, feature-complete ways to do it, while leaving it in the codebase until such time as its presence becomes a hindrance. Essentially, deprecation need not necessarily mean &amp;quot;this WILL be removed&amp;quot; so much as &amp;quot;this is now a candidate for removal&amp;quot;. By separating the concepts of deprecation and removal, both goals can be better served.&lt;br /&gt;
&lt;br /&gt;
=== Undefined removal timeline - issues encountered ===&lt;br /&gt;
In practice however, simply stating something is a candidate for removal while providing no further information on when it will actually be removed causes multiple issues:&lt;br /&gt;
* When a deprecated API is causing a maintenance burden worthy of removal is a subjective decision, often leading to debates over removal any time a developer decides it's time for an API to be removed, initially deprecated, or moved to a higher deprecation level.&lt;br /&gt;
* Lack of developer incentive to provide good documentation and support for the deprecation of the API given there's no particular timeline for when it will actually cause issues for UMC authors.&lt;br /&gt;
* UMC authors often don't find out about deprecated APIs and so can't possibly migrate to the new APIs.&lt;br /&gt;
* UMC authors aren't provided the documentation or tooling support needed to make updating their add-ons easier.&lt;br /&gt;
* UMC authors may simply decide there's no reason to update to the new API even if they know it's deprecated and how to update their add-on. After all, why spend time updating APIs that may stick around forever?&lt;br /&gt;
* Giving a version that an API '''may''' be removed instead of a version that it '''will''' be removed is not as clear as it may seem to UMC authors who aren't already aware of how Wesnoth handles deprecation. This can lead to UMC authors ignoring deprecation warnings after seeing such warnings for APIs which provide a version that's years old.&lt;br /&gt;
&lt;br /&gt;
=== Certainty is also a benefit ===&lt;br /&gt;
Wesnoth continues to exist today in large part because of the content made by its community. As such, developers should always be sparing in what they choose to deprecate and what they choose to remove, so that UMC authors can update their content to the latest version of Wesnoth without needing to make a herculean effort every couple of years.&lt;br /&gt;
&lt;br /&gt;
But, for cases where APIs are removed, the benefit of providing certainty for when that will happen outweighs the flexibility of being able remove them at any time after they been marked as deprecated. It encourages developers to provide the documentation and tooling support to make it easier for UMC authors to update their add-ons, and it lets UMC authors know when they can expect deprecated APIs to be removed rather than it effectively happening at random when a developer decides a deprecated API needs to be dropped.&lt;br /&gt;
&lt;br /&gt;
=== More Complex Cases - One size does not fit all ===&lt;br /&gt;
There may, however, be cases where the obsolete API cannot be implemented cleanly in terms of the updated one and must be maintained separately, or, in extremely rare cases, cannot coexist at all. There needs to be some leeway for such cases as well. In the former case, it therefore makes sense to allow for a feature to be deprecated pending removal after the shortest reasonable deprecation period. In the latter case, there is obviously no choice but to make the change and remove the old immediately. Developers should, naturally, be encouraged to find creative solutions to avoid such cases, but it is inevitable that there will eventually be a few cases where no graceful transition procedure can be found. These more aggressive forms of deprecation should only be done with developer consensus, and only after all options of creating a backwards-compatible transition have been exhausted.&lt;br /&gt;
&lt;br /&gt;
=== Graphics - The exception that proves the rule ===&lt;br /&gt;
The one area where backwards compatibility should NOT be a factor is graphical changes. Any change to the terrain graphics, unit sprites, or portrait images has the potential to result in visually-incompatible custom content. Core content creators cannot be expected to maintain a deprecated visual style alongside a more modern one, as any approach toward doing so would add an unreasonable amount of bloat and overhead, and make it very difficult for core graphics to be updated without having to do double the work. In addition, add-ons will continue to function even with such visual incompatibilities present, they just won't look right. As such, it can be said that stylistic incompatibilities fall more within the realm of content than of code, and it is not unreasonable to expect a content creator to... well...  create content. Expecting the graphical style to remain backwards-compatible would be just as unreasonable as expecting stories, help descriptions, or other forms of lore to never change because they may introduce plot holes into add-on stories. Essentially, since the goal of the software portion of Wesnoth is, at its heart, merely to facilitate the creation and advancement of this kind of content, core content needs to be free to develop unhindered by considerations for add-on content.&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=CompatibilityStandardsV2&amp;diff=74993</id>
		<title>CompatibilityStandardsV2</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=CompatibilityStandardsV2&amp;diff=74993"/>
		<updated>2026-04-23T15:33:54Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* Deprecation levels - When to remove deprecated features */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;As a piece of software matures, there are often new designs, paradigms, and idioms developed which are superior to old ones. This creates an inherent conflict between the need for progress and the need for compatibility. Wesnoth is no exception. This document describes Wesnoth's approach toward resolving that conflict in a way which is most beneficial to both goals, as well as the rationale behind this approach.&lt;br /&gt;
&lt;br /&gt;
== Policy ==&lt;br /&gt;
This policy defines how the creation of new Wesnoth APIs and the deprecation and removal of old ones are to be handled. For the purposes of this document &amp;quot;API&amp;quot; means &amp;quot;any technical channel by which a content creator interacts with the game engine&amp;quot;. This includes, but is not limited to: preprocessor macros, WML tags, WFL functions, IPFs, and the Lua API. Note that this policy applies only to software APIs. Core content such as sprites, portraits, animations, lore, etc. are to be updated freely, without consideration for stylistic or literary conflicts that such content changes may present to add-ons.&lt;br /&gt;
&lt;br /&gt;
=== When to deprecate ===&lt;br /&gt;
==== Adding a better API ====&lt;br /&gt;
Any time a superior API is introduced which has '''complete feature-parity''' with an existing API, the old one should be immediately deprecated. Note that in most cases, in order to be considered to have &amp;quot;complete feature-parity&amp;quot;, the API should be available in the same language or area of the code as the obsolete one was. For example, the introduction of a more powerful API in Lua which can accomplish a superset of the functionality which had previously been available with a certain WML tag would not obsolete that WML tag. Exceptions can be made to this rule in cases where it is clear that the feature does not make a lot of sense in its current language or area, and was merely there for legacy reasons (such as the proper place for it not having been introduced yet at the time of its creation). Such exceptions should be determined by developer consensus.&lt;br /&gt;
&lt;br /&gt;
==== Preventing needed fixes or improvements ====&lt;br /&gt;
In such cases where a feature is preventing an important new feature from being added or makes it impossible to fix a problem impacting players, it can be preferable to deprecate the API to allow for the necessary changes to be made. APIs deprecated for this reason should still follow the deprecation schedule as normal, unless the fix or improvement is urgently needed.&lt;br /&gt;
&lt;br /&gt;
==== Actively harmful ====&lt;br /&gt;
If an API is found to cause significant problems for players or UMC authors, such as being prone to causing crashes while also very difficult to properly fix, corrupting saves and replays when not used correctly while being difficult to use correctly, or other similar situations, then such APIs should be deprecated and removed regardless of whether there is a replacement available.&lt;br /&gt;
&lt;br /&gt;
==== Unused ====&lt;br /&gt;
Any API that is not used in mainline and also is not used by the most recent version of an add-on on the add-ons server of the current or previous stable release can be deprecated. For example, if an add-on for 1.16 uses a deprecated API but the updated version of the add-on for 1.18 does not, then that add-on is not considered as currently using the API. Once deprecated for this reason, a new add-on being uploaded that uses the API is not a reason to undeprecate it.&lt;br /&gt;
&lt;br /&gt;
This does not need to be a passive process where developers simply check the add-ons server for whether an API is used - developers who want to deprecate an API for removal can proactively talk to and work with UMC authors to help update their add-ons to remove usage of said API. This can be anything from talking with them online about how to update to submitting updated code directly (ie: opening a PR against an add-on's public git repository).&lt;br /&gt;
&lt;br /&gt;
Deprecating and then removing APIs that are unused is the most preferred approach since their removal does not have any impact on UMC authors.&lt;br /&gt;
&lt;br /&gt;
=== When NOT to deprecate ===&lt;br /&gt;
==== Style ====&lt;br /&gt;
Deprecation should not be done purely for reasons of style. This is very subjective and prone to change as new contributors join and current contributors leave or become less active. As such, allowing deprecation for stylistic reasons would lead to entirely unnecessary work for UMC authors as developer preferences change over time.&lt;br /&gt;
&lt;br /&gt;
==== Renaming ====&lt;br /&gt;
While there can be exceptions, it is rarely a net positive to deprecate an API simply for the sake of renaming it to something else. It is preferable to either add a second name for the same function, leaving the old name as-is, or simply live with the current name rather than expecting all UMC authors using the API to update to the new name.&lt;br /&gt;
&lt;br /&gt;
==== Any other reason ====&lt;br /&gt;
Accepted reasons for deprecating APIs should be something that's discussed and agreed upon by the development team while also, ideally, including UMC authors. It should not become the norm that additional reasons to deprecate APIs are treated as exceptions and left as an increasingly forgotten discussion on Discord, IRC, or the forums - they should be added to here with the reasoning behind them.&lt;br /&gt;
&lt;br /&gt;
=== Deprecation awareness ===&lt;br /&gt;
==== Conflicting goals ====&lt;br /&gt;
When deprecating APIs there is an inherent conflict in terms of how to make UMC authors aware of the deprecation. After all, if they aren't aware something is deprecated, they can't know they may need to update their add-on. Therefore, deprecations need to be displayed in a place where they will see them and most UMC authors don't look at Wesnoth's logs unless there's some other issue they're investigating. At the same time, deprecation warnings aren't relevant to players and spamming deprecation warnings is not an effective way of communicating what the issues are.&lt;br /&gt;
&lt;br /&gt;
==== A middle ground ====&lt;br /&gt;
Each deprecated feature should make use of an appropriate deprecation function call for that language or subsystem to ensure that the appropriate deprecation notice is printed to the log output. Additionally, deprecation warnings should be displayed in-game in the following cases:&lt;br /&gt;
* If the player is running a development version, level 3 and level 4 deprecations should be shown in-game by default.&lt;br /&gt;
* If the player enables debug mode then all deprecation warnings should be shown, regardless of whether they're using a stable release or a development release.&lt;br /&gt;
&lt;br /&gt;
==== Documentation ====&lt;br /&gt;
It is also not enough to only display a warning at runtime when something deprecated is encountered. It is the responsibility of the development team to proactively make UMC authors aware of the deprecations and removals being done. To accomplish this:&lt;br /&gt;
* When an API is deprecated, and again if it's later removed, its deprecation or removal must be documented in the appropriate section of the changelog for the version it was deprecated or removed in.&lt;br /&gt;
* Likewise, it should be added to https://wiki.wesnoth.org/CompatibilityBreakingChanges&lt;br /&gt;
&lt;br /&gt;
Additionally, it is not enough to simply say that an API is deprecated. In the deprecation message itself as well as in the changelog and https://wiki.wesnoth.org/CompatibilityBreakingChanges, a description must be included as to why it was deprecated or removed and how UMC authors can update their add-ons to address it.&lt;br /&gt;
&lt;br /&gt;
Lastly, it should be understood that &amp;quot;removed&amp;quot; doesn't necessarily mean that the API is entirely gone from Wesnoth's codebase. There is no maintenance burden to keeping macro or method stubs that do nothing aside from printing an error message describing what was removed and why. Keeping such stubs around is highly encouraged as it is helpful for UMC authors trying to update very old add-ons to the current version of Wesnoth.&lt;br /&gt;
&lt;br /&gt;
=== How to deprecate ===&lt;br /&gt;
Every effort should be made to create the simplest possible wrappers which will translate from an obsolete API to the updated one. Such wrappers should ideally be organized into their own compatibility file or module, and set up in such a way that the internals of the updated API will not affect how the old calls get wrapped to the new one. Essentially, the idea is to create a set-it-and-forget-it compatibility wrapper which will continue to work regardless of updates made to the newer API.&lt;br /&gt;
&lt;br /&gt;
Additionally, in all cases where it's practical, the wmllint tool must be updated to be able to automatically handle updating add-ons for anything that's been deprecated except for APIs deprecated at level 1. While developers are still heavily encouraged to add wmllint support for level 1 deprecations, it is not required as these do not show deprecation warnings by default and are expected to continue working indefinitely.&lt;br /&gt;
&lt;br /&gt;
=== Deprecation levels - When to remove deprecated features ===&lt;br /&gt;
While creating simple compatibility wrappers should be possible most of the time, it would be unreasonable to assume that this approach will be viable in absolutely every case. Wesnoth's compatibility policy therefore has four different levels of deprecation which are used to set expectations for when and if an API is expected to be removed:&lt;br /&gt;
&lt;br /&gt;
#Deprecated indefinitely &amp;amp;mdash; This deprecation level is for changes which have newer preferred alternatives but, barring any unforeseen issues, have little to no maintenance impact and should be kept indefinitely in order to reduce the work required for UMC authors to maintain their content. For example, functions or attributes which have had their names changed, macros which have been replaced with tags, or simple wrappers with little to no maintenance overhead. If future large scale changes make it impractical to maintain an API deprecated at this level then the deprecation level should be raised accordingly. This is generally the preferred deprecation level - higher deprecation levels are for APIs which are intended for eventual removal and must be weighed against the maintenance work this forces upon UMC authors.&lt;br /&gt;
#Deprecated with the intention of future removal &amp;amp;mdash; This deprecation level is for APIs which will be removed in a future version, but the version they are removed in has not yet have been decided. Barring urgent circumstances, all APIs that are deprecated with the intention of being removed '''must''' start with being deprecated at level 2 for 1-2 development cycles at minimum depending on the impact of the API's removal on UMC authors. Once the API has spent the necessary amount of time at deprecation level 2, it can be moved to deprecation level 3. It is not allowed to deprecate an API at level 2 and change it to level 3 in the same development cycle. There is no maximum amount of time that an API can remain deprecated at level 2.&lt;br /&gt;
#Deprecated for removal in a specific version &amp;amp;mdash; This deprecation level is for APIs which were previously deprecated at level 2 and now have a future version at which they will be removed. The version an API is removed in must be at least two development cycles after the API was deprecated at level 2. It is not allowed to move an API to deprecation level 3 and then remove it in the same development cycle.&lt;br /&gt;
#Removed without deprecation &amp;amp;mdash; This level should be used EXTREMELY rarely, and only in cases where it is ABSOLUTELY NECESSARY. Occasionally, an update to a feature will change the underlying architecture in such a fundamental way that the old paradigm cannot coexist with the new one no matter how much redundant code one would create. While this kind of scenario is extremely rare, and every effort should be made to find creative solutions to avoid it, there are occasionally cases where it truly is impossible to maintain both methods even in the short term. This level should only be used with broad developer consensus, after the majority of active developers familiar with the feature in question have given at least some thought to trying to deprecate gracefully and failed. This level should also be used for macro and method stubs containing only an error message describing what was removed and why, as well as if an API feature is found to have a security vulnerability which can't be fixed.&lt;br /&gt;
&lt;br /&gt;
== Rationale ==&lt;br /&gt;
The above policy is the result of a large amount of thought, discussion, and debate. Following is a brief outline of the considerations and goals on both sides of the problem, why there is an inherent conflict between them, some failed approaches to resolving the conflict, and how the final policy ultimately maximizes the pursuit of both goals.&lt;br /&gt;
&lt;br /&gt;
=== The Problem - The paradox of progress ===&lt;br /&gt;
Invariably, as development on any project moves forward, developers will realize that there are better, cleaner, or more elegant ways to structure things than they had been previously. These changes can be to improve efficiency, make an API more intuitive, keep code better organized, make common tasks more straightforward, or accomplish any number of other positive things. These changes can also make the development of additional features much more viable. In short, progress is good.&lt;br /&gt;
&lt;br /&gt;
On the other hand, a content-heavy program such as Wesnoth relies on the ability of content creators to efficiently create, maintain, and update their content. Too many changes all at once will force creators of existing content to spend obscene amounts time updating their creations just to keeping them up-to-date and in working order. This can lead to a high amount of frustration, a drop in motivation, and a decline in content being created. In short, progress is bad.&lt;br /&gt;
&lt;br /&gt;
=== Backwards Compatibility - benefits and drawbacks ===&lt;br /&gt;
Most of the time, older paradigms can still be maintained in a manner in which they coexist with the newer ones. This allows for existing content to continue functioning, while at the same time allowing and encouraging new content to be created using the newer methods. However, maintaining such code can be problematic in the following ways:&lt;br /&gt;
#There may eventually be architectural changes a developer would want to make where the old method's square peg no longer fits, even forcibly, into the new method's round hole.&lt;br /&gt;
#Having compatibility code hanging around may, depending on how it is implemented, mean that any updates made to the feature, module, or subsystem in question would have to made in both the new, cleaner design, and the older, poorly structured one, adding more work for developers.&lt;br /&gt;
&lt;br /&gt;
=== The Naive Approach - Deprecate and remove everything old ===&lt;br /&gt;
One approach to balancing old and new is to deprecate the old and slate its eventual removal after either a certain amount of time has passed or a certain number of subsequent versions have been released. This sounds good in theory, but in practice, there will be many changes which are minor or cosmetic in nature and which will add up. Things like replacing macros with WML tags, updating the name of an API call or order of parameters to be more consistent with other similar functions, or switching from a functional to an object-oriented structure are very good for organizational purposes, but will result in a large amount of maintenance required on the part of content creators to keep existing code operational, and for very little real gain. This approach invariably leads to the situation where so much is being changed from one version to the next that creators turn into maintainers, forced to spend nearly all of their time trying to stay ahead of the update curve in an attempt to keep their existing content working, and leaving them very little time and motivation to create new content. And of course, by the time they're finished painstakingly updating their existing code for every little change made for the current release, whoops, there's a new release with a whole slew of new changes that need accounting for. It simply becomes unmanageable.&lt;br /&gt;
&lt;br /&gt;
=== The Naive Approach - Deprecate and remove only when necessary ===&lt;br /&gt;
The opposite approach would be to keep all existing paradigms until they actively interfere with a new architecture or create a double-maintenance problem. While this approach does cut down on the maintenance burden by ensuring that content using an older design continues to work, it hinders progress by the fact that the moment at which it first becomes clear that an architectural or double-maintenance problem will occur is exactly the same moment at which keeping the old structure around becomes problematic. Beginning a deprecation cycle at that point and then having to &amp;quot;wait out&amp;quot; the old paradigm will cause an unacceptable delay in development.&lt;br /&gt;
&lt;br /&gt;
=== The Middle Ground - Deprecate everything old, remove only when necessary ===&lt;br /&gt;
Most of the time, older APIs can be implemented in terms of their newer, cleaner counterparts through the use of things like simple wrappers, parse-translators which re-write the older paradigm's code in terms of the new one, or other relatively low-maintenance &amp;quot;set-it-and-forget-it&amp;quot; approaches. These simple wrappers are not really detrimental to making progress, don't require updating when the new APIs internals are changed, and can usually be organized into their own files and/or modules so that they don't clutter the cleaner code. Many such wrappers will never truly present either of the backwards compatibility drawbacks mentioned above. As such, there is really no detriment to keeping them around indefinitely. However, occasionally, a new idea or approach will be put forth that updates the newer paradigm in such a way that the older one can no longer cleanly wrap to it. This usually happens, as inspiration is wont to do, suddenly, unexpectedly, and without warning. As such developers need the flexibility to be able to remove outdated code as freely as possible when the situation requires. Therefore, the ideal solution would be to deprecate any API which has newer, feature-complete ways to do it, while leaving it in the codebase until such time as its presence becomes a hindrance. Essentially, deprecation need not necessarily mean &amp;quot;this WILL be removed&amp;quot; so much as &amp;quot;this is now a candidate for removal&amp;quot;. By separating the concepts of deprecation and removal, both goals can be better served.&lt;br /&gt;
&lt;br /&gt;
=== Undefined removal timeline - issues encountered ===&lt;br /&gt;
In practice however, simply stating something is a candidate for removal while providing no further information on when it will actually be removed causes multiple issues:&lt;br /&gt;
* When a deprecated API is causing a maintenance burden worthy of removal is a subjective decision, often leading to debates over removal any time a developer decides it's time for an API to be removed, initially deprecated, or moved to a higher deprecation level.&lt;br /&gt;
* Lack of developer incentive to provide good documentation and support for the deprecation of the API given there's no particular timeline for when it will actually cause issues for UMC authors.&lt;br /&gt;
* UMC authors often don't find out about deprecated APIs and so can't possibly migrate to the new APIs.&lt;br /&gt;
* UMC authors aren't provided the documentation or tooling support needed to make updating their add-ons easier.&lt;br /&gt;
* UMC authors may simply decide there's no reason to update to the new API even if they know it's deprecated and how to update their add-on. After all, why spend time updating APIs that may stick around forever?&lt;br /&gt;
* Giving a version that an API '''may''' be removed instead of a version that it '''will''' be removed is not as clear as it may seem to UMC authors who aren't already aware of how Wesnoth handles deprecation. This can lead to UMC authors ignoring deprecation warnings after seeing such warnings for APIs which provide a version that's years old.&lt;br /&gt;
&lt;br /&gt;
=== Certainty is also a benefit ===&lt;br /&gt;
Wesnoth continues to exist today in large part because of the content made by its community. As such, developers should always be sparing in what they choose to deprecate and what they choose to remove, so that UMC authors can update their content to the latest version of Wesnoth without needing to make a herculean effort every couple of years.&lt;br /&gt;
&lt;br /&gt;
But, for cases where APIs are removed, the benefit of providing certainty for when that will happen outweighs the flexibility of being able remove them at any time after they been marked as deprecated. It encourages developers to provide the documentation and tooling support to make it easier for UMC authors to update their add-ons, and it lets UMC authors know when they can expect deprecated APIs to be removed rather than it effectively happening at random when a developer decides a deprecated API needs to be dropped.&lt;br /&gt;
&lt;br /&gt;
=== More Complex Cases - One size does not fit all ===&lt;br /&gt;
There may, however, be cases where the obsolete API cannot be implemented cleanly in terms of the updated one and must be maintained separately, or, in extremely rare cases, cannot coexist at all. There needs to be some leeway for such cases as well. In the former case, it therefore makes sense to allow for a feature to be deprecated pending removal after the shortest reasonable deprecation period. In the latter case, there is obviously no choice but to make the change and remove the old immediately. Developers should, naturally, be encouraged to find creative solutions to avoid such cases, but it is inevitable that there will eventually be a few cases where no graceful transition procedure can be found. These more aggressive forms of deprecation should only be done with developer consensus, and only after all options of creating a backwards-compatible transition have been exhausted.&lt;br /&gt;
&lt;br /&gt;
=== Graphics - The exception that proves the rule ===&lt;br /&gt;
The one area where backwards compatibility should NOT be a factor is graphical changes. Any change to the terrain graphics, unit sprites, or portrait images has the potential to result in visually-incompatible custom content. Core content creators cannot be expected to maintain a deprecated visual style alongside a more modern one, as any approach toward doing so would add an unreasonable amount of bloat and overhead, and make it very difficult for core graphics to be updated without having to do double the work. In addition, add-ons will continue to function even with such visual incompatibilities present, they just won't look right. As such, it can be said that stylistic incompatibilities fall more within the realm of content than of code, and it is not unreasonable to expect a content creator to... well...  create content. Expecting the graphical style to remain backwards-compatible would be just as unreasonable as expecting stories, help descriptions, or other forms of lore to never change because they may introduce plot holes into add-on stories. Essentially, since the goal of the software portion of Wesnoth is, at its heart, merely to facilitate the creation and advancement of this kind of content, core content needs to be free to develop unhindered by considerations for add-on content.&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=CompatibilityStandardsV2&amp;diff=74992</id>
		<title>CompatibilityStandardsV2</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=CompatibilityStandardsV2&amp;diff=74992"/>
		<updated>2026-04-23T15:33:26Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* When to deprecate */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;As a piece of software matures, there are often new designs, paradigms, and idioms developed which are superior to old ones. This creates an inherent conflict between the need for progress and the need for compatibility. Wesnoth is no exception. This document describes Wesnoth's approach toward resolving that conflict in a way which is most beneficial to both goals, as well as the rationale behind this approach.&lt;br /&gt;
&lt;br /&gt;
== Policy ==&lt;br /&gt;
This policy defines how the creation of new Wesnoth APIs and the deprecation and removal of old ones are to be handled. For the purposes of this document &amp;quot;API&amp;quot; means &amp;quot;any technical channel by which a content creator interacts with the game engine&amp;quot;. This includes, but is not limited to: preprocessor macros, WML tags, WFL functions, IPFs, and the Lua API. Note that this policy applies only to software APIs. Core content such as sprites, portraits, animations, lore, etc. are to be updated freely, without consideration for stylistic or literary conflicts that such content changes may present to add-ons.&lt;br /&gt;
&lt;br /&gt;
=== When to deprecate ===&lt;br /&gt;
==== Adding a better API ====&lt;br /&gt;
Any time a superior API is introduced which has '''complete feature-parity''' with an existing API, the old one should be immediately deprecated. Note that in most cases, in order to be considered to have &amp;quot;complete feature-parity&amp;quot;, the API should be available in the same language or area of the code as the obsolete one was. For example, the introduction of a more powerful API in Lua which can accomplish a superset of the functionality which had previously been available with a certain WML tag would not obsolete that WML tag. Exceptions can be made to this rule in cases where it is clear that the feature does not make a lot of sense in its current language or area, and was merely there for legacy reasons (such as the proper place for it not having been introduced yet at the time of its creation). Such exceptions should be determined by developer consensus.&lt;br /&gt;
&lt;br /&gt;
==== Preventing needed fixes or improvements ====&lt;br /&gt;
In such cases where a feature is preventing an important new feature from being added or makes it impossible to fix a problem impacting players, it can be preferable to deprecate the API to allow for the necessary changes to be made. APIs deprecated for this reason should still follow the deprecation schedule as normal, unless the fix or improvement is urgently needed.&lt;br /&gt;
&lt;br /&gt;
==== Actively harmful ====&lt;br /&gt;
If an API is found to cause significant problems for players or UMC authors, such as being prone to causing crashes while also very difficult to properly fix, corrupting saves and replays when not used correctly while being difficult to use correctly, or other similar situations, then such APIs should be deprecated and removed regardless of whether there is a replacement available.&lt;br /&gt;
&lt;br /&gt;
==== Unused ====&lt;br /&gt;
Any API that is not used in mainline and also is not used by the most recent version of an add-on on the add-ons server of the current or previous stable release can be deprecated. For example, if an add-on for 1.16 uses a deprecated API but the updated version of the add-on for 1.18 does not, then that add-on is not considered as currently using the API. Once deprecated for this reason, a new add-on being uploaded that uses the API is not a reason to undeprecate it.&lt;br /&gt;
&lt;br /&gt;
This does not need to be a passive process where developers simply check the add-ons server for whether an API is used - developers who want to deprecate an API for removal can proactively talk to and work with UMC authors to help update their add-ons to remove usage of said API. This can be anything from talking with them online about how to update to submitting updated code directly (ie: opening a PR against an add-on's public git repository).&lt;br /&gt;
&lt;br /&gt;
Deprecating and then removing APIs that are unused is the most preferred approach since their removal does not have any impact on UMC authors.&lt;br /&gt;
&lt;br /&gt;
=== When NOT to deprecate ===&lt;br /&gt;
==== Style ====&lt;br /&gt;
Deprecation should not be done purely for reasons of style. This is very subjective and prone to change as new contributors join and current contributors leave or become less active. As such, allowing deprecation for stylistic reasons would lead to entirely unnecessary work for UMC authors as developer preferences change over time.&lt;br /&gt;
&lt;br /&gt;
==== Renaming ====&lt;br /&gt;
While there can be exceptions, it is rarely a net positive to deprecate an API simply for the sake of renaming it to something else. It is preferable to either add a second name for the same function, leaving the old name as-is, or simply live with the current name rather than expecting all UMC authors using the API to update to the new name.&lt;br /&gt;
&lt;br /&gt;
==== Any other reason ====&lt;br /&gt;
Accepted reasons for deprecating APIs should be something that's discussed and agreed upon by the development team while also, ideally, including UMC authors. It should not become the norm that additional reasons to deprecate APIs are treated as exceptions and left as an increasingly forgotten discussion on Discord, IRC, or the forums - they should be added to here with the reasoning behind them.&lt;br /&gt;
&lt;br /&gt;
=== Deprecation awareness ===&lt;br /&gt;
==== Conflicting goals ====&lt;br /&gt;
When deprecating APIs there is an inherent conflict in terms of how to make UMC authors aware of the deprecation. After all, if they aren't aware something is deprecated, they can't know they may need to update their add-on. Therefore, deprecations need to be displayed in a place where they will see them and most UMC authors don't look at Wesnoth's logs unless there's some other issue they're investigating. At the same time, deprecation warnings aren't relevant to players and spamming deprecation warnings is not an effective way of communicating what the issues are.&lt;br /&gt;
&lt;br /&gt;
==== A middle ground ====&lt;br /&gt;
Each deprecated feature should make use of an appropriate deprecation function call for that language or subsystem to ensure that the appropriate deprecation notice is printed to the log output. Additionally, deprecation warnings should be displayed in-game in the following cases:&lt;br /&gt;
* If the player is running a development version, level 3 and level 4 deprecations should be shown in-game by default.&lt;br /&gt;
* If the player enables debug mode then all deprecation warnings should be shown, regardless of whether they're using a stable release or a development release.&lt;br /&gt;
&lt;br /&gt;
==== Documentation ====&lt;br /&gt;
It is also not enough to only display a warning at runtime when something deprecated is encountered. It is the responsibility of the development team to proactively make UMC authors aware of the deprecations and removals being done. To accomplish this:&lt;br /&gt;
* When an API is deprecated, and again if it's later removed, its deprecation or removal must be documented in the appropriate section of the changelog for the version it was deprecated or removed in.&lt;br /&gt;
* Likewise, it should be added to https://wiki.wesnoth.org/CompatibilityBreakingChanges&lt;br /&gt;
&lt;br /&gt;
Additionally, it is not enough to simply say that an API is deprecated. In the deprecation message itself as well as in the changelog and https://wiki.wesnoth.org/CompatibilityBreakingChanges, a description must be included as to why it was deprecated or removed and how UMC authors can update their add-ons to address it.&lt;br /&gt;
&lt;br /&gt;
Lastly, it should be understood that &amp;quot;removed&amp;quot; doesn't necessarily mean that the API is entirely gone from Wesnoth's codebase. There is no maintenance burden to keeping macro or method stubs that do nothing aside from printing an error message describing what was removed and why. Keeping such stubs around is highly encouraged as it is helpful for UMC authors trying to update very old add-ons to the current version of Wesnoth.&lt;br /&gt;
&lt;br /&gt;
=== How to deprecate ===&lt;br /&gt;
Every effort should be made to create the simplest possible wrappers which will translate from an obsolete API to the updated one. Such wrappers should ideally be organized into their own compatibility file or module, and set up in such a way that the internals of the updated API will not affect how the old calls get wrapped to the new one. Essentially, the idea is to create a set-it-and-forget-it compatibility wrapper which will continue to work regardless of updates made to the newer API.&lt;br /&gt;
&lt;br /&gt;
Additionally, in all cases where it's practical, the wmllint tool must be updated to be able to automatically handle updating add-ons for anything that's been deprecated except for APIs deprecated at level 1. While developers are still heavily encouraged to add wmllint support for level 1 deprecations, it is not required as these do not show deprecation warnings by default and are expected to continue working indefinitely.&lt;br /&gt;
&lt;br /&gt;
=== Deprecation levels - When to remove deprecated features ===&lt;br /&gt;
While creating simple compatibility wrappers should be possible most of the time, it would be unreasonable to assume that this approach will be viable in absolutely every case. Wesnoth's compatibility policy therefore has four different levels of deprecation which are used to set expectations for when and if an API is expected to be removed:&lt;br /&gt;
&lt;br /&gt;
#Deprecated indefinitely &amp;amp;mdash; This deprecation level is for changes which have newer preferred alternatives but, barring any unforeseen issues, have little to no maintenance impact and should be kept indefinitely in order to reduce the work required for UMC authors to maintain their content. For example, functions or attributes which have had their names changed, macros which have been replaced with tags, or simple wrappers with little to no maintenance overhead. If future large scale changes make it impractical to maintain an API deprecated at this level then the deprecation level should be raised accordingly. This is generally the preferred deprecation level - higher deprecation levels are for APIs which are intended for eventual removal and must be weighed against the maintenance work this forces upon UMC authors.&lt;br /&gt;
#Deprecated with the intention of future removal &amp;amp;mdash; This deprecation level is for APIs which will be removed in a future version, but the version they are removed in has not yet have been decided. Barring urgent circumstances, all APIs that are deprecated with the intention of being removed '''must''' start with being deprecated at level 2 for 1-2 development cycles at minimum depending on the impact of the API's removal on UMC authors. Once the API has spent the necessary amount of time at deprecation level 2, it can be moved to deprecation level 3. It is not allowed to deprecate an API at level 2 and change it to level 3 in the same development cycle. There is no maximum amount of time that an API can remain deprecated at level 2.&lt;br /&gt;
#Deprecated for removal in a specific version &amp;amp;mdash; This deprecation level is for APIs which were previously deprecated at level 2 and now have a future version at which they will be removed. The version an API is removed in must be at least two development cycles after the API was deprecated at level 2. It is not allowed to move an API to deprecation level 3 and then remove it in the same development cycle.&lt;br /&gt;
#Removed without deprecation &amp;amp;mdash; This level should be used EXTREMELY rarely, and only in cases where it is ABSOLUTELY NECESSARY. Occasionally, an update to a feature will change the underlying architecture in such a fundamental way that the old paradigm cannot coexist with the new one no matter how much redundant code one would create. While this kind of scenario is extremely rare, and every effort should be made to find creative solutions to avoid it, there are occasionally cases where it truly is impossible to maintain both methods even in the short term. This level should only be used with broad developer consensus, after the majority of active developers familiar with the feature in question have given at least some thought to trying to deprecate gracefully and failed. This level should also be used for macro and method stubs containing only an error message describing what was removed and why.&lt;br /&gt;
&lt;br /&gt;
== Rationale ==&lt;br /&gt;
The above policy is the result of a large amount of thought, discussion, and debate. Following is a brief outline of the considerations and goals on both sides of the problem, why there is an inherent conflict between them, some failed approaches to resolving the conflict, and how the final policy ultimately maximizes the pursuit of both goals.&lt;br /&gt;
&lt;br /&gt;
=== The Problem - The paradox of progress ===&lt;br /&gt;
Invariably, as development on any project moves forward, developers will realize that there are better, cleaner, or more elegant ways to structure things than they had been previously. These changes can be to improve efficiency, make an API more intuitive, keep code better organized, make common tasks more straightforward, or accomplish any number of other positive things. These changes can also make the development of additional features much more viable. In short, progress is good.&lt;br /&gt;
&lt;br /&gt;
On the other hand, a content-heavy program such as Wesnoth relies on the ability of content creators to efficiently create, maintain, and update their content. Too many changes all at once will force creators of existing content to spend obscene amounts time updating their creations just to keeping them up-to-date and in working order. This can lead to a high amount of frustration, a drop in motivation, and a decline in content being created. In short, progress is bad.&lt;br /&gt;
&lt;br /&gt;
=== Backwards Compatibility - benefits and drawbacks ===&lt;br /&gt;
Most of the time, older paradigms can still be maintained in a manner in which they coexist with the newer ones. This allows for existing content to continue functioning, while at the same time allowing and encouraging new content to be created using the newer methods. However, maintaining such code can be problematic in the following ways:&lt;br /&gt;
#There may eventually be architectural changes a developer would want to make where the old method's square peg no longer fits, even forcibly, into the new method's round hole.&lt;br /&gt;
#Having compatibility code hanging around may, depending on how it is implemented, mean that any updates made to the feature, module, or subsystem in question would have to made in both the new, cleaner design, and the older, poorly structured one, adding more work for developers.&lt;br /&gt;
&lt;br /&gt;
=== The Naive Approach - Deprecate and remove everything old ===&lt;br /&gt;
One approach to balancing old and new is to deprecate the old and slate its eventual removal after either a certain amount of time has passed or a certain number of subsequent versions have been released. This sounds good in theory, but in practice, there will be many changes which are minor or cosmetic in nature and which will add up. Things like replacing macros with WML tags, updating the name of an API call or order of parameters to be more consistent with other similar functions, or switching from a functional to an object-oriented structure are very good for organizational purposes, but will result in a large amount of maintenance required on the part of content creators to keep existing code operational, and for very little real gain. This approach invariably leads to the situation where so much is being changed from one version to the next that creators turn into maintainers, forced to spend nearly all of their time trying to stay ahead of the update curve in an attempt to keep their existing content working, and leaving them very little time and motivation to create new content. And of course, by the time they're finished painstakingly updating their existing code for every little change made for the current release, whoops, there's a new release with a whole slew of new changes that need accounting for. It simply becomes unmanageable.&lt;br /&gt;
&lt;br /&gt;
=== The Naive Approach - Deprecate and remove only when necessary ===&lt;br /&gt;
The opposite approach would be to keep all existing paradigms until they actively interfere with a new architecture or create a double-maintenance problem. While this approach does cut down on the maintenance burden by ensuring that content using an older design continues to work, it hinders progress by the fact that the moment at which it first becomes clear that an architectural or double-maintenance problem will occur is exactly the same moment at which keeping the old structure around becomes problematic. Beginning a deprecation cycle at that point and then having to &amp;quot;wait out&amp;quot; the old paradigm will cause an unacceptable delay in development.&lt;br /&gt;
&lt;br /&gt;
=== The Middle Ground - Deprecate everything old, remove only when necessary ===&lt;br /&gt;
Most of the time, older APIs can be implemented in terms of their newer, cleaner counterparts through the use of things like simple wrappers, parse-translators which re-write the older paradigm's code in terms of the new one, or other relatively low-maintenance &amp;quot;set-it-and-forget-it&amp;quot; approaches. These simple wrappers are not really detrimental to making progress, don't require updating when the new APIs internals are changed, and can usually be organized into their own files and/or modules so that they don't clutter the cleaner code. Many such wrappers will never truly present either of the backwards compatibility drawbacks mentioned above. As such, there is really no detriment to keeping them around indefinitely. However, occasionally, a new idea or approach will be put forth that updates the newer paradigm in such a way that the older one can no longer cleanly wrap to it. This usually happens, as inspiration is wont to do, suddenly, unexpectedly, and without warning. As such developers need the flexibility to be able to remove outdated code as freely as possible when the situation requires. Therefore, the ideal solution would be to deprecate any API which has newer, feature-complete ways to do it, while leaving it in the codebase until such time as its presence becomes a hindrance. Essentially, deprecation need not necessarily mean &amp;quot;this WILL be removed&amp;quot; so much as &amp;quot;this is now a candidate for removal&amp;quot;. By separating the concepts of deprecation and removal, both goals can be better served.&lt;br /&gt;
&lt;br /&gt;
=== Undefined removal timeline - issues encountered ===&lt;br /&gt;
In practice however, simply stating something is a candidate for removal while providing no further information on when it will actually be removed causes multiple issues:&lt;br /&gt;
* When a deprecated API is causing a maintenance burden worthy of removal is a subjective decision, often leading to debates over removal any time a developer decides it's time for an API to be removed, initially deprecated, or moved to a higher deprecation level.&lt;br /&gt;
* Lack of developer incentive to provide good documentation and support for the deprecation of the API given there's no particular timeline for when it will actually cause issues for UMC authors.&lt;br /&gt;
* UMC authors often don't find out about deprecated APIs and so can't possibly migrate to the new APIs.&lt;br /&gt;
* UMC authors aren't provided the documentation or tooling support needed to make updating their add-ons easier.&lt;br /&gt;
* UMC authors may simply decide there's no reason to update to the new API even if they know it's deprecated and how to update their add-on. After all, why spend time updating APIs that may stick around forever?&lt;br /&gt;
* Giving a version that an API '''may''' be removed instead of a version that it '''will''' be removed is not as clear as it may seem to UMC authors who aren't already aware of how Wesnoth handles deprecation. This can lead to UMC authors ignoring deprecation warnings after seeing such warnings for APIs which provide a version that's years old.&lt;br /&gt;
&lt;br /&gt;
=== Certainty is also a benefit ===&lt;br /&gt;
Wesnoth continues to exist today in large part because of the content made by its community. As such, developers should always be sparing in what they choose to deprecate and what they choose to remove, so that UMC authors can update their content to the latest version of Wesnoth without needing to make a herculean effort every couple of years.&lt;br /&gt;
&lt;br /&gt;
But, for cases where APIs are removed, the benefit of providing certainty for when that will happen outweighs the flexibility of being able remove them at any time after they been marked as deprecated. It encourages developers to provide the documentation and tooling support to make it easier for UMC authors to update their add-ons, and it lets UMC authors know when they can expect deprecated APIs to be removed rather than it effectively happening at random when a developer decides a deprecated API needs to be dropped.&lt;br /&gt;
&lt;br /&gt;
=== More Complex Cases - One size does not fit all ===&lt;br /&gt;
There may, however, be cases where the obsolete API cannot be implemented cleanly in terms of the updated one and must be maintained separately, or, in extremely rare cases, cannot coexist at all. There needs to be some leeway for such cases as well. In the former case, it therefore makes sense to allow for a feature to be deprecated pending removal after the shortest reasonable deprecation period. In the latter case, there is obviously no choice but to make the change and remove the old immediately. Developers should, naturally, be encouraged to find creative solutions to avoid such cases, but it is inevitable that there will eventually be a few cases where no graceful transition procedure can be found. These more aggressive forms of deprecation should only be done with developer consensus, and only after all options of creating a backwards-compatible transition have been exhausted.&lt;br /&gt;
&lt;br /&gt;
=== Graphics - The exception that proves the rule ===&lt;br /&gt;
The one area where backwards compatibility should NOT be a factor is graphical changes. Any change to the terrain graphics, unit sprites, or portrait images has the potential to result in visually-incompatible custom content. Core content creators cannot be expected to maintain a deprecated visual style alongside a more modern one, as any approach toward doing so would add an unreasonable amount of bloat and overhead, and make it very difficult for core graphics to be updated without having to do double the work. In addition, add-ons will continue to function even with such visual incompatibilities present, they just won't look right. As such, it can be said that stylistic incompatibilities fall more within the realm of content than of code, and it is not unreasonable to expect a content creator to... well...  create content. Expecting the graphical style to remain backwards-compatible would be just as unreasonable as expecting stories, help descriptions, or other forms of lore to never change because they may introduce plot holes into add-on stories. Essentially, since the goal of the software portion of Wesnoth is, at its heart, merely to facilitate the creation and advancement of this kind of content, core content needs to be free to develop unhindered by considerations for add-on content.&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=CompatibilityStandardsV2&amp;diff=74988</id>
		<title>CompatibilityStandardsV2</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=CompatibilityStandardsV2&amp;diff=74988"/>
		<updated>2026-04-20T22:54:45Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* Deprecation levels - When to remove deprecated features */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;As a piece of software matures, there are often new designs, paradigms, and idioms developed which are superior to old ones. This creates an inherent conflict between the need for progress and the need for compatibility. Wesnoth is no exception. This document describes Wesnoth's approach toward resolving that conflict in a way which is most beneficial to both goals, as well as the rationale behind this approach.&lt;br /&gt;
&lt;br /&gt;
== Policy ==&lt;br /&gt;
This policy defines how the creation of new Wesnoth APIs and the deprecation and removal of old ones are to be handled. For the purposes of this document &amp;quot;API&amp;quot; means &amp;quot;any technical channel by which a content creator interacts with the game engine&amp;quot;. This includes, but is not limited to: preprocessor macros, WML tags, WFL functions, IPFs, and the Lua API. Note that this policy applies only to software APIs. Core content such as sprites, portraits, animations, lore, etc. are to be updated freely, without consideration for stylistic or literary conflicts that such content changes may present to add-ons.&lt;br /&gt;
&lt;br /&gt;
=== When to deprecate ===&lt;br /&gt;
==== Adding a better API ====&lt;br /&gt;
Any time a superior API is introduced which has '''complete feature-parity''' with an existing API, the old one should be immediately deprecated. Note that in most cases, in order to be considered to have &amp;quot;complete feature-parity&amp;quot;, the API should be available in the same language or area of the code as the obsolete one was. For example, the introduction of a more powerful API in Lua which can accomplish a superset of the functionality which had previously been available with a certain WML tag would not obsolete that WML tag. Exceptions can be made to this rule in cases where it is clear that the feature does not make a lot of sense in its current language or area, and was merely there for legacy reasons (such as the proper place for it not having been introduced yet at the time of its creation). Such exceptions should be determined by developer consensus.&lt;br /&gt;
&lt;br /&gt;
==== Preventing needed fixes or improvements ====&lt;br /&gt;
In such cases where a feature is preventing an important new feature from being added or makes it impossible to fix a problem impacting players, it can be preferable to deprecate the API to allow for the necessary changes to be made. APIs deprecated for this reason should still follow the deprecation schedule as normal, unless the fix or improvement is urgently needed.&lt;br /&gt;
&lt;br /&gt;
==== Actively harmful ====&lt;br /&gt;
If an API is found to cause significant problems for players or UMC authors, such as being prone to causing crashes while also very difficult to properly fix, corrupting saves and replays when not used correctly while being difficult to use correctly, or other similar situations, then such APIs should be deprecated and removed regardless of whether there is a replacement available.&lt;br /&gt;
&lt;br /&gt;
==== Security ====&lt;br /&gt;
If an API is found to have a security flaw that can't be fixed, then it should not be deprecated, it should be immediately removed.&lt;br /&gt;
&lt;br /&gt;
==== Unused ====&lt;br /&gt;
Any API that is not used in mainline and also is not used by the most recent version of an add-on on the add-ons server of the current or previous stable release can be deprecated. For example, if an add-on for 1.16 uses a deprecated API but the updated version of the add-on for 1.18 does not, then that add-on is not considered as currently using the API. Once deprecated for this reason, a new add-on being uploaded that uses the API is not a reason to undeprecate it.&lt;br /&gt;
&lt;br /&gt;
This does not need to be a passive process where developers simply check the add-ons server for whether an API is used - developers who want to deprecate an API for removal can proactively talk to and work with UMC authors to help update their add-ons to remove usage of said API. This can be anything from talking with them online about how to update to submitting updated code directly (ie: opening a PR against an add-on's public git repository).&lt;br /&gt;
&lt;br /&gt;
Deprecating and then removing APIs that are unused is the most preferred approach since their removal does not have any impact on UMC authors.&lt;br /&gt;
&lt;br /&gt;
=== When NOT to deprecate ===&lt;br /&gt;
==== Style ====&lt;br /&gt;
Deprecation should not be done purely for reasons of style. This is very subjective and prone to change as new contributors join and current contributors leave or become less active. As such, allowing deprecation for stylistic reasons would lead to entirely unnecessary work for UMC authors as developer preferences change over time.&lt;br /&gt;
&lt;br /&gt;
==== Renaming ====&lt;br /&gt;
While there can be exceptions, it is rarely a net positive to deprecate an API simply for the sake of renaming it to something else. It is preferable to either add a second name for the same function, leaving the old name as-is, or simply live with the current name rather than expecting all UMC authors using the API to update to the new name.&lt;br /&gt;
&lt;br /&gt;
==== Any other reason ====&lt;br /&gt;
Accepted reasons for deprecating APIs should be something that's discussed and agreed upon by the development team while also, ideally, including UMC authors. It should not become the norm that additional reasons to deprecate APIs are treated as exceptions and left as an increasingly forgotten discussion on Discord, IRC, or the forums - they should be added to here with the reasoning behind them.&lt;br /&gt;
&lt;br /&gt;
=== Deprecation awareness ===&lt;br /&gt;
==== Conflicting goals ====&lt;br /&gt;
When deprecating APIs there is an inherent conflict in terms of how to make UMC authors aware of the deprecation. After all, if they aren't aware something is deprecated, they can't know they may need to update their add-on. Therefore, deprecations need to be displayed in a place where they will see them and most UMC authors don't look at Wesnoth's logs unless there's some other issue they're investigating. At the same time, deprecation warnings aren't relevant to players and spamming deprecation warnings is not an effective way of communicating what the issues are.&lt;br /&gt;
&lt;br /&gt;
==== A middle ground ====&lt;br /&gt;
Each deprecated feature should make use of an appropriate deprecation function call for that language or subsystem to ensure that the appropriate deprecation notice is printed to the log output. Additionally, deprecation warnings should be displayed in-game in the following cases:&lt;br /&gt;
* If the player is running a development version, level 3 and level 4 deprecations should be shown in-game by default.&lt;br /&gt;
* If the player enables debug mode then all deprecation warnings should be shown, regardless of whether they're using a stable release or a development release.&lt;br /&gt;
&lt;br /&gt;
==== Documentation ====&lt;br /&gt;
It is also not enough to only display a warning at runtime when something deprecated is encountered. It is the responsibility of the development team to proactively make UMC authors aware of the deprecations and removals being done. To accomplish this:&lt;br /&gt;
* When an API is deprecated, and again if it's later removed, its deprecation or removal must be documented in the appropriate section of the changelog for the version it was deprecated or removed in.&lt;br /&gt;
* Likewise, it should be added to https://wiki.wesnoth.org/CompatibilityBreakingChanges&lt;br /&gt;
&lt;br /&gt;
Additionally, it is not enough to simply say that an API is deprecated. In the deprecation message itself as well as in the changelog and https://wiki.wesnoth.org/CompatibilityBreakingChanges, a description must be included as to why it was deprecated or removed and how UMC authors can update their add-ons to address it.&lt;br /&gt;
&lt;br /&gt;
Lastly, it should be understood that &amp;quot;removed&amp;quot; doesn't necessarily mean that the API is entirely gone from Wesnoth's codebase. There is no maintenance burden to keeping macro or method stubs that do nothing aside from printing an error message describing what was removed and why. Keeping such stubs around is highly encouraged as it is helpful for UMC authors trying to update very old add-ons to the current version of Wesnoth.&lt;br /&gt;
&lt;br /&gt;
=== How to deprecate ===&lt;br /&gt;
Every effort should be made to create the simplest possible wrappers which will translate from an obsolete API to the updated one. Such wrappers should ideally be organized into their own compatibility file or module, and set up in such a way that the internals of the updated API will not affect how the old calls get wrapped to the new one. Essentially, the idea is to create a set-it-and-forget-it compatibility wrapper which will continue to work regardless of updates made to the newer API.&lt;br /&gt;
&lt;br /&gt;
Additionally, in all cases where it's practical, the wmllint tool must be updated to be able to automatically handle updating add-ons for anything that's been deprecated except for APIs deprecated at level 1. While developers are still heavily encouraged to add wmllint support for level 1 deprecations, it is not required as these do not show deprecation warnings by default and are expected to continue working indefinitely.&lt;br /&gt;
&lt;br /&gt;
=== Deprecation levels - When to remove deprecated features ===&lt;br /&gt;
While creating simple compatibility wrappers should be possible most of the time, it would be unreasonable to assume that this approach will be viable in absolutely every case. Wesnoth's compatibility policy therefore has four different levels of deprecation which are used to set expectations for when and if an API is expected to be removed:&lt;br /&gt;
&lt;br /&gt;
#Deprecated indefinitely &amp;amp;mdash; This deprecation level is for changes which have newer preferred alternatives but, barring any unforeseen issues, have little to no maintenance impact and should be kept indefinitely in order to reduce the work required for UMC authors to maintain their content. For example, functions or attributes which have had their names changed, macros which have been replaced with tags, or simple wrappers with little to no maintenance overhead. If future large scale changes make it impractical to maintain an API deprecated at this level then the deprecation level should be raised accordingly. This is generally the preferred deprecation level - higher deprecation levels are for APIs which are intended for eventual removal and must be weighed against the maintenance work this forces upon UMC authors.&lt;br /&gt;
#Deprecated with the intention of future removal &amp;amp;mdash; This deprecation level is for APIs which will be removed in a future version, but the version they are removed in has not yet have been decided. Barring urgent circumstances, all APIs that are deprecated with the intention of being removed '''must''' start with being deprecated at level 2 for 1-2 development cycles at minimum depending on the impact of the API's removal on UMC authors. Once the API has spent the necessary amount of time at deprecation level 2, it can be moved to deprecation level 3. It is not allowed to deprecate an API at level 2 and change it to level 3 in the same development cycle. There is no maximum amount of time that an API can remain deprecated at level 2.&lt;br /&gt;
#Deprecated for removal in a specific version &amp;amp;mdash; This deprecation level is for APIs which were previously deprecated at level 2 and now have a future version at which they will be removed. The version an API is removed in must be at least two development cycles after the API was deprecated at level 2. It is not allowed to move an API to deprecation level 3 and then remove it in the same development cycle.&lt;br /&gt;
#Removed without deprecation &amp;amp;mdash; This level should be used EXTREMELY rarely, and only in cases where it is ABSOLUTELY NECESSARY. Occasionally, an update to a feature will change the underlying architecture in such a fundamental way that the old paradigm cannot coexist with the new one no matter how much redundant code one would create. While this kind of scenario is extremely rare, and every effort should be made to find creative solutions to avoid it, there are occasionally cases where it truly is impossible to maintain both methods even in the short term. This level should only be used with broad developer consensus, after the majority of active developers familiar with the feature in question have given at least some thought to trying to deprecate gracefully and failed. This level should also be used for macro and method stubs containing only an error message describing what was removed and why.&lt;br /&gt;
&lt;br /&gt;
== Rationale ==&lt;br /&gt;
The above policy is the result of a large amount of thought, discussion, and debate. Following is a brief outline of the considerations and goals on both sides of the problem, why there is an inherent conflict between them, some failed approaches to resolving the conflict, and how the final policy ultimately maximizes the pursuit of both goals.&lt;br /&gt;
&lt;br /&gt;
=== The Problem - The paradox of progress ===&lt;br /&gt;
Invariably, as development on any project moves forward, developers will realize that there are better, cleaner, or more elegant ways to structure things than they had been previously. These changes can be to improve efficiency, make an API more intuitive, keep code better organized, make common tasks more straightforward, or accomplish any number of other positive things. These changes can also make the development of additional features much more viable. In short, progress is good.&lt;br /&gt;
&lt;br /&gt;
On the other hand, a content-heavy program such as Wesnoth relies on the ability of content creators to efficiently create, maintain, and update their content. Too many changes all at once will force creators of existing content to spend obscene amounts time updating their creations just to keeping them up-to-date and in working order. This can lead to a high amount of frustration, a drop in motivation, and a decline in content being created. In short, progress is bad.&lt;br /&gt;
&lt;br /&gt;
=== Backwards Compatibility - benefits and drawbacks ===&lt;br /&gt;
Most of the time, older paradigms can still be maintained in a manner in which they coexist with the newer ones. This allows for existing content to continue functioning, while at the same time allowing and encouraging new content to be created using the newer methods. However, maintaining such code can be problematic in the following ways:&lt;br /&gt;
#There may eventually be architectural changes a developer would want to make where the old method's square peg no longer fits, even forcibly, into the new method's round hole.&lt;br /&gt;
#Having compatibility code hanging around may, depending on how it is implemented, mean that any updates made to the feature, module, or subsystem in question would have to made in both the new, cleaner design, and the older, poorly structured one, adding more work for developers.&lt;br /&gt;
&lt;br /&gt;
=== The Naive Approach - Deprecate and remove everything old ===&lt;br /&gt;
One approach to balancing old and new is to deprecate the old and slate its eventual removal after either a certain amount of time has passed or a certain number of subsequent versions have been released. This sounds good in theory, but in practice, there will be many changes which are minor or cosmetic in nature and which will add up. Things like replacing macros with WML tags, updating the name of an API call or order of parameters to be more consistent with other similar functions, or switching from a functional to an object-oriented structure are very good for organizational purposes, but will result in a large amount of maintenance required on the part of content creators to keep existing code operational, and for very little real gain. This approach invariably leads to the situation where so much is being changed from one version to the next that creators turn into maintainers, forced to spend nearly all of their time trying to stay ahead of the update curve in an attempt to keep their existing content working, and leaving them very little time and motivation to create new content. And of course, by the time they're finished painstakingly updating their existing code for every little change made for the current release, whoops, there's a new release with a whole slew of new changes that need accounting for. It simply becomes unmanageable.&lt;br /&gt;
&lt;br /&gt;
=== The Naive Approach - Deprecate and remove only when necessary ===&lt;br /&gt;
The opposite approach would be to keep all existing paradigms until they actively interfere with a new architecture or create a double-maintenance problem. While this approach does cut down on the maintenance burden by ensuring that content using an older design continues to work, it hinders progress by the fact that the moment at which it first becomes clear that an architectural or double-maintenance problem will occur is exactly the same moment at which keeping the old structure around becomes problematic. Beginning a deprecation cycle at that point and then having to &amp;quot;wait out&amp;quot; the old paradigm will cause an unacceptable delay in development.&lt;br /&gt;
&lt;br /&gt;
=== The Middle Ground - Deprecate everything old, remove only when necessary ===&lt;br /&gt;
Most of the time, older APIs can be implemented in terms of their newer, cleaner counterparts through the use of things like simple wrappers, parse-translators which re-write the older paradigm's code in terms of the new one, or other relatively low-maintenance &amp;quot;set-it-and-forget-it&amp;quot; approaches. These simple wrappers are not really detrimental to making progress, don't require updating when the new APIs internals are changed, and can usually be organized into their own files and/or modules so that they don't clutter the cleaner code. Many such wrappers will never truly present either of the backwards compatibility drawbacks mentioned above. As such, there is really no detriment to keeping them around indefinitely. However, occasionally, a new idea or approach will be put forth that updates the newer paradigm in such a way that the older one can no longer cleanly wrap to it. This usually happens, as inspiration is wont to do, suddenly, unexpectedly, and without warning. As such developers need the flexibility to be able to remove outdated code as freely as possible when the situation requires. Therefore, the ideal solution would be to deprecate any API which has newer, feature-complete ways to do it, while leaving it in the codebase until such time as its presence becomes a hindrance. Essentially, deprecation need not necessarily mean &amp;quot;this WILL be removed&amp;quot; so much as &amp;quot;this is now a candidate for removal&amp;quot;. By separating the concepts of deprecation and removal, both goals can be better served.&lt;br /&gt;
&lt;br /&gt;
=== Undefined removal timeline - issues encountered ===&lt;br /&gt;
In practice however, simply stating something is a candidate for removal while providing no further information on when it will actually be removed causes multiple issues:&lt;br /&gt;
* When a deprecated API is causing a maintenance burden worthy of removal is a subjective decision, often leading to debates over removal any time a developer decides it's time for an API to be removed, initially deprecated, or moved to a higher deprecation level.&lt;br /&gt;
* Lack of developer incentive to provide good documentation and support for the deprecation of the API given there's no particular timeline for when it will actually cause issues for UMC authors.&lt;br /&gt;
* UMC authors often don't find out about deprecated APIs and so can't possibly migrate to the new APIs.&lt;br /&gt;
* UMC authors aren't provided the documentation or tooling support needed to make updating their add-ons easier.&lt;br /&gt;
* UMC authors may simply decide there's no reason to update to the new API even if they know it's deprecated and how to update their add-on. After all, why spend time updating APIs that may stick around forever?&lt;br /&gt;
* Giving a version that an API '''may''' be removed instead of a version that it '''will''' be removed is not as clear as it may seem to UMC authors who aren't already aware of how Wesnoth handles deprecation. This can lead to UMC authors ignoring deprecation warnings after seeing such warnings for APIs which provide a version that's years old.&lt;br /&gt;
&lt;br /&gt;
=== Certainty is also a benefit ===&lt;br /&gt;
Wesnoth continues to exist today in large part because of the content made by its community. As such, developers should always be sparing in what they choose to deprecate and what they choose to remove, so that UMC authors can update their content to the latest version of Wesnoth without needing to make a herculean effort every couple of years.&lt;br /&gt;
&lt;br /&gt;
But, for cases where APIs are removed, the benefit of providing certainty for when that will happen outweighs the flexibility of being able remove them at any time after they been marked as deprecated. It encourages developers to provide the documentation and tooling support to make it easier for UMC authors to update their add-ons, and it lets UMC authors know when they can expect deprecated APIs to be removed rather than it effectively happening at random when a developer decides a deprecated API needs to be dropped.&lt;br /&gt;
&lt;br /&gt;
=== More Complex Cases - One size does not fit all ===&lt;br /&gt;
There may, however, be cases where the obsolete API cannot be implemented cleanly in terms of the updated one and must be maintained separately, or, in extremely rare cases, cannot coexist at all. There needs to be some leeway for such cases as well. In the former case, it therefore makes sense to allow for a feature to be deprecated pending removal after the shortest reasonable deprecation period. In the latter case, there is obviously no choice but to make the change and remove the old immediately. Developers should, naturally, be encouraged to find creative solutions to avoid such cases, but it is inevitable that there will eventually be a few cases where no graceful transition procedure can be found. These more aggressive forms of deprecation should only be done with developer consensus, and only after all options of creating a backwards-compatible transition have been exhausted.&lt;br /&gt;
&lt;br /&gt;
=== Graphics - The exception that proves the rule ===&lt;br /&gt;
The one area where backwards compatibility should NOT be a factor is graphical changes. Any change to the terrain graphics, unit sprites, or portrait images has the potential to result in visually-incompatible custom content. Core content creators cannot be expected to maintain a deprecated visual style alongside a more modern one, as any approach toward doing so would add an unreasonable amount of bloat and overhead, and make it very difficult for core graphics to be updated without having to do double the work. In addition, add-ons will continue to function even with such visual incompatibilities present, they just won't look right. As such, it can be said that stylistic incompatibilities fall more within the realm of content than of code, and it is not unreasonable to expect a content creator to... well...  create content. Expecting the graphical style to remain backwards-compatible would be just as unreasonable as expecting stories, help descriptions, or other forms of lore to never change because they may introduce plot holes into add-on stories. Essentially, since the goal of the software portion of Wesnoth is, at its heart, merely to facilitate the creation and advancement of this kind of content, core content needs to be free to develop unhindered by considerations for add-on content.&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=CompatibilityStandardsV2&amp;diff=74987</id>
		<title>CompatibilityStandardsV2</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=CompatibilityStandardsV2&amp;diff=74987"/>
		<updated>2026-04-20T22:54:33Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* Deprecation levels - When to remove deprecated features */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;As a piece of software matures, there are often new designs, paradigms, and idioms developed which are superior to old ones. This creates an inherent conflict between the need for progress and the need for compatibility. Wesnoth is no exception. This document describes Wesnoth's approach toward resolving that conflict in a way which is most beneficial to both goals, as well as the rationale behind this approach.&lt;br /&gt;
&lt;br /&gt;
== Policy ==&lt;br /&gt;
This policy defines how the creation of new Wesnoth APIs and the deprecation and removal of old ones are to be handled. For the purposes of this document &amp;quot;API&amp;quot; means &amp;quot;any technical channel by which a content creator interacts with the game engine&amp;quot;. This includes, but is not limited to: preprocessor macros, WML tags, WFL functions, IPFs, and the Lua API. Note that this policy applies only to software APIs. Core content such as sprites, portraits, animations, lore, etc. are to be updated freely, without consideration for stylistic or literary conflicts that such content changes may present to add-ons.&lt;br /&gt;
&lt;br /&gt;
=== When to deprecate ===&lt;br /&gt;
==== Adding a better API ====&lt;br /&gt;
Any time a superior API is introduced which has '''complete feature-parity''' with an existing API, the old one should be immediately deprecated. Note that in most cases, in order to be considered to have &amp;quot;complete feature-parity&amp;quot;, the API should be available in the same language or area of the code as the obsolete one was. For example, the introduction of a more powerful API in Lua which can accomplish a superset of the functionality which had previously been available with a certain WML tag would not obsolete that WML tag. Exceptions can be made to this rule in cases where it is clear that the feature does not make a lot of sense in its current language or area, and was merely there for legacy reasons (such as the proper place for it not having been introduced yet at the time of its creation). Such exceptions should be determined by developer consensus.&lt;br /&gt;
&lt;br /&gt;
==== Preventing needed fixes or improvements ====&lt;br /&gt;
In such cases where a feature is preventing an important new feature from being added or makes it impossible to fix a problem impacting players, it can be preferable to deprecate the API to allow for the necessary changes to be made. APIs deprecated for this reason should still follow the deprecation schedule as normal, unless the fix or improvement is urgently needed.&lt;br /&gt;
&lt;br /&gt;
==== Actively harmful ====&lt;br /&gt;
If an API is found to cause significant problems for players or UMC authors, such as being prone to causing crashes while also very difficult to properly fix, corrupting saves and replays when not used correctly while being difficult to use correctly, or other similar situations, then such APIs should be deprecated and removed regardless of whether there is a replacement available.&lt;br /&gt;
&lt;br /&gt;
==== Security ====&lt;br /&gt;
If an API is found to have a security flaw that can't be fixed, then it should not be deprecated, it should be immediately removed.&lt;br /&gt;
&lt;br /&gt;
==== Unused ====&lt;br /&gt;
Any API that is not used in mainline and also is not used by the most recent version of an add-on on the add-ons server of the current or previous stable release can be deprecated. For example, if an add-on for 1.16 uses a deprecated API but the updated version of the add-on for 1.18 does not, then that add-on is not considered as currently using the API. Once deprecated for this reason, a new add-on being uploaded that uses the API is not a reason to undeprecate it.&lt;br /&gt;
&lt;br /&gt;
This does not need to be a passive process where developers simply check the add-ons server for whether an API is used - developers who want to deprecate an API for removal can proactively talk to and work with UMC authors to help update their add-ons to remove usage of said API. This can be anything from talking with them online about how to update to submitting updated code directly (ie: opening a PR against an add-on's public git repository).&lt;br /&gt;
&lt;br /&gt;
Deprecating and then removing APIs that are unused is the most preferred approach since their removal does not have any impact on UMC authors.&lt;br /&gt;
&lt;br /&gt;
=== When NOT to deprecate ===&lt;br /&gt;
==== Style ====&lt;br /&gt;
Deprecation should not be done purely for reasons of style. This is very subjective and prone to change as new contributors join and current contributors leave or become less active. As such, allowing deprecation for stylistic reasons would lead to entirely unnecessary work for UMC authors as developer preferences change over time.&lt;br /&gt;
&lt;br /&gt;
==== Renaming ====&lt;br /&gt;
While there can be exceptions, it is rarely a net positive to deprecate an API simply for the sake of renaming it to something else. It is preferable to either add a second name for the same function, leaving the old name as-is, or simply live with the current name rather than expecting all UMC authors using the API to update to the new name.&lt;br /&gt;
&lt;br /&gt;
==== Any other reason ====&lt;br /&gt;
Accepted reasons for deprecating APIs should be something that's discussed and agreed upon by the development team while also, ideally, including UMC authors. It should not become the norm that additional reasons to deprecate APIs are treated as exceptions and left as an increasingly forgotten discussion on Discord, IRC, or the forums - they should be added to here with the reasoning behind them.&lt;br /&gt;
&lt;br /&gt;
=== Deprecation awareness ===&lt;br /&gt;
==== Conflicting goals ====&lt;br /&gt;
When deprecating APIs there is an inherent conflict in terms of how to make UMC authors aware of the deprecation. After all, if they aren't aware something is deprecated, they can't know they may need to update their add-on. Therefore, deprecations need to be displayed in a place where they will see them and most UMC authors don't look at Wesnoth's logs unless there's some other issue they're investigating. At the same time, deprecation warnings aren't relevant to players and spamming deprecation warnings is not an effective way of communicating what the issues are.&lt;br /&gt;
&lt;br /&gt;
==== A middle ground ====&lt;br /&gt;
Each deprecated feature should make use of an appropriate deprecation function call for that language or subsystem to ensure that the appropriate deprecation notice is printed to the log output. Additionally, deprecation warnings should be displayed in-game in the following cases:&lt;br /&gt;
* If the player is running a development version, level 3 and level 4 deprecations should be shown in-game by default.&lt;br /&gt;
* If the player enables debug mode then all deprecation warnings should be shown, regardless of whether they're using a stable release or a development release.&lt;br /&gt;
&lt;br /&gt;
==== Documentation ====&lt;br /&gt;
It is also not enough to only display a warning at runtime when something deprecated is encountered. It is the responsibility of the development team to proactively make UMC authors aware of the deprecations and removals being done. To accomplish this:&lt;br /&gt;
* When an API is deprecated, and again if it's later removed, its deprecation or removal must be documented in the appropriate section of the changelog for the version it was deprecated or removed in.&lt;br /&gt;
* Likewise, it should be added to https://wiki.wesnoth.org/CompatibilityBreakingChanges&lt;br /&gt;
&lt;br /&gt;
Additionally, it is not enough to simply say that an API is deprecated. In the deprecation message itself as well as in the changelog and https://wiki.wesnoth.org/CompatibilityBreakingChanges, a description must be included as to why it was deprecated or removed and how UMC authors can update their add-ons to address it.&lt;br /&gt;
&lt;br /&gt;
Lastly, it should be understood that &amp;quot;removed&amp;quot; doesn't necessarily mean that the API is entirely gone from Wesnoth's codebase. There is no maintenance burden to keeping macro or method stubs that do nothing aside from printing an error message describing what was removed and why. Keeping such stubs around is highly encouraged as it is helpful for UMC authors trying to update very old add-ons to the current version of Wesnoth.&lt;br /&gt;
&lt;br /&gt;
=== How to deprecate ===&lt;br /&gt;
Every effort should be made to create the simplest possible wrappers which will translate from an obsolete API to the updated one. Such wrappers should ideally be organized into their own compatibility file or module, and set up in such a way that the internals of the updated API will not affect how the old calls get wrapped to the new one. Essentially, the idea is to create a set-it-and-forget-it compatibility wrapper which will continue to work regardless of updates made to the newer API.&lt;br /&gt;
&lt;br /&gt;
Additionally, in all cases where it's practical, the wmllint tool must be updated to be able to automatically handle updating add-ons for anything that's been deprecated except for APIs deprecated at level 1. While developers are still heavily encouraged to add wmllint support for level 1 deprecations, it is not required as these do not show deprecation warnings by default and are expected to continue working indefinitely.&lt;br /&gt;
&lt;br /&gt;
=== Deprecation levels - When to remove deprecated features ===&lt;br /&gt;
While creating simple compatibility wrappers should be possible most of the time, it would be unreasonable to assume that this approach will be viable in absolutely every case. Wesnoth's compatibility policy therefore has four different levels of deprecation which are used to set expectations for when and if an API is expected to be removed:&lt;br /&gt;
&lt;br /&gt;
#Deprecated indefinitely &amp;amp;mdash; This deprecation level is for changes which have newer preferred alternatives but, barring any unforeseen issues, have little to no maintenance impact and should be kept indefinitely in order to reduce the work required for UMC authors to maintain their content. For example, functions or attributes which have had their names changed, macros which have been replaced with tags, or simple wrappers with little to no maintenance overhead. If future large scale changes make it impractical to maintain an API deprecated at this level then the deprecation level should be raised accordingly. This is generally the preferred deprecation level - higher deprecation levels are for APIs which are intended for eventual removal and must be weighed against the maintenance work this forces upon UMC authors.&lt;br /&gt;
#Deprecated with the intention of future removal &amp;amp;mdash; This deprecation level is for APIs which will be removed in a future version, but the version they are removed in has not yet have been decided. Barring urgent circumstances, all APIs that are deprecated with the intention of being removed '''must''' start with being deprecated at level 2 for 1-2 development cycles at minimum depending on the impact of the API's removal on UMC authors. Once the API has spent the necessary amount of time at deprecation level 2, it can be moved to deprecation level 3. It is not allowed to deprecate an API at level 2 and change it to level 3 in the same development cycle. There is no maximum amount of time that an API can remain deprecated at level 2.&lt;br /&gt;
#Deprecated for removal in a specific version &amp;amp;mdash; This deprecation level is for APIs which were previously deprecated at level 2 and now have a future version at which they will be removed. The version an API is removed in must be at least two development cycles after the API was deprecated at level 2. It is not allowed to move an API to deprecation level 3 and then remove it in the same development cycle.&lt;br /&gt;
#Removed without deprecation &amp;amp;mdash; This level should be used EXTREMELY rarely, and only in cases where it is ABSOLUTELY NECESSARY. Occasionally, an update to a feature will change the underlying architecture in such a fundamental way that the old paradigm cannot coexist with the new one no matter how much redundant code one would create. While this kind of scenario is extremely rare, and every effort should be made to find creative solutions to avoid it, there are occasionally cases where it truly is impossible to maintain both methods even in the short term. This level should only be used with broad developer consensus, after the majority of active developers familiar with the feature in question have given at least some thought to trying to deprecate gracefully and failed.&lt;br /&gt;
&lt;br /&gt;
This level should also be used for macro and method stubs containing only an error message describing what was removed and why.&lt;br /&gt;
&lt;br /&gt;
== Rationale ==&lt;br /&gt;
The above policy is the result of a large amount of thought, discussion, and debate. Following is a brief outline of the considerations and goals on both sides of the problem, why there is an inherent conflict between them, some failed approaches to resolving the conflict, and how the final policy ultimately maximizes the pursuit of both goals.&lt;br /&gt;
&lt;br /&gt;
=== The Problem - The paradox of progress ===&lt;br /&gt;
Invariably, as development on any project moves forward, developers will realize that there are better, cleaner, or more elegant ways to structure things than they had been previously. These changes can be to improve efficiency, make an API more intuitive, keep code better organized, make common tasks more straightforward, or accomplish any number of other positive things. These changes can also make the development of additional features much more viable. In short, progress is good.&lt;br /&gt;
&lt;br /&gt;
On the other hand, a content-heavy program such as Wesnoth relies on the ability of content creators to efficiently create, maintain, and update their content. Too many changes all at once will force creators of existing content to spend obscene amounts time updating their creations just to keeping them up-to-date and in working order. This can lead to a high amount of frustration, a drop in motivation, and a decline in content being created. In short, progress is bad.&lt;br /&gt;
&lt;br /&gt;
=== Backwards Compatibility - benefits and drawbacks ===&lt;br /&gt;
Most of the time, older paradigms can still be maintained in a manner in which they coexist with the newer ones. This allows for existing content to continue functioning, while at the same time allowing and encouraging new content to be created using the newer methods. However, maintaining such code can be problematic in the following ways:&lt;br /&gt;
#There may eventually be architectural changes a developer would want to make where the old method's square peg no longer fits, even forcibly, into the new method's round hole.&lt;br /&gt;
#Having compatibility code hanging around may, depending on how it is implemented, mean that any updates made to the feature, module, or subsystem in question would have to made in both the new, cleaner design, and the older, poorly structured one, adding more work for developers.&lt;br /&gt;
&lt;br /&gt;
=== The Naive Approach - Deprecate and remove everything old ===&lt;br /&gt;
One approach to balancing old and new is to deprecate the old and slate its eventual removal after either a certain amount of time has passed or a certain number of subsequent versions have been released. This sounds good in theory, but in practice, there will be many changes which are minor or cosmetic in nature and which will add up. Things like replacing macros with WML tags, updating the name of an API call or order of parameters to be more consistent with other similar functions, or switching from a functional to an object-oriented structure are very good for organizational purposes, but will result in a large amount of maintenance required on the part of content creators to keep existing code operational, and for very little real gain. This approach invariably leads to the situation where so much is being changed from one version to the next that creators turn into maintainers, forced to spend nearly all of their time trying to stay ahead of the update curve in an attempt to keep their existing content working, and leaving them very little time and motivation to create new content. And of course, by the time they're finished painstakingly updating their existing code for every little change made for the current release, whoops, there's a new release with a whole slew of new changes that need accounting for. It simply becomes unmanageable.&lt;br /&gt;
&lt;br /&gt;
=== The Naive Approach - Deprecate and remove only when necessary ===&lt;br /&gt;
The opposite approach would be to keep all existing paradigms until they actively interfere with a new architecture or create a double-maintenance problem. While this approach does cut down on the maintenance burden by ensuring that content using an older design continues to work, it hinders progress by the fact that the moment at which it first becomes clear that an architectural or double-maintenance problem will occur is exactly the same moment at which keeping the old structure around becomes problematic. Beginning a deprecation cycle at that point and then having to &amp;quot;wait out&amp;quot; the old paradigm will cause an unacceptable delay in development.&lt;br /&gt;
&lt;br /&gt;
=== The Middle Ground - Deprecate everything old, remove only when necessary ===&lt;br /&gt;
Most of the time, older APIs can be implemented in terms of their newer, cleaner counterparts through the use of things like simple wrappers, parse-translators which re-write the older paradigm's code in terms of the new one, or other relatively low-maintenance &amp;quot;set-it-and-forget-it&amp;quot; approaches. These simple wrappers are not really detrimental to making progress, don't require updating when the new APIs internals are changed, and can usually be organized into their own files and/or modules so that they don't clutter the cleaner code. Many such wrappers will never truly present either of the backwards compatibility drawbacks mentioned above. As such, there is really no detriment to keeping them around indefinitely. However, occasionally, a new idea or approach will be put forth that updates the newer paradigm in such a way that the older one can no longer cleanly wrap to it. This usually happens, as inspiration is wont to do, suddenly, unexpectedly, and without warning. As such developers need the flexibility to be able to remove outdated code as freely as possible when the situation requires. Therefore, the ideal solution would be to deprecate any API which has newer, feature-complete ways to do it, while leaving it in the codebase until such time as its presence becomes a hindrance. Essentially, deprecation need not necessarily mean &amp;quot;this WILL be removed&amp;quot; so much as &amp;quot;this is now a candidate for removal&amp;quot;. By separating the concepts of deprecation and removal, both goals can be better served.&lt;br /&gt;
&lt;br /&gt;
=== Undefined removal timeline - issues encountered ===&lt;br /&gt;
In practice however, simply stating something is a candidate for removal while providing no further information on when it will actually be removed causes multiple issues:&lt;br /&gt;
* When a deprecated API is causing a maintenance burden worthy of removal is a subjective decision, often leading to debates over removal any time a developer decides it's time for an API to be removed, initially deprecated, or moved to a higher deprecation level.&lt;br /&gt;
* Lack of developer incentive to provide good documentation and support for the deprecation of the API given there's no particular timeline for when it will actually cause issues for UMC authors.&lt;br /&gt;
* UMC authors often don't find out about deprecated APIs and so can't possibly migrate to the new APIs.&lt;br /&gt;
* UMC authors aren't provided the documentation or tooling support needed to make updating their add-ons easier.&lt;br /&gt;
* UMC authors may simply decide there's no reason to update to the new API even if they know it's deprecated and how to update their add-on. After all, why spend time updating APIs that may stick around forever?&lt;br /&gt;
* Giving a version that an API '''may''' be removed instead of a version that it '''will''' be removed is not as clear as it may seem to UMC authors who aren't already aware of how Wesnoth handles deprecation. This can lead to UMC authors ignoring deprecation warnings after seeing such warnings for APIs which provide a version that's years old.&lt;br /&gt;
&lt;br /&gt;
=== Certainty is also a benefit ===&lt;br /&gt;
Wesnoth continues to exist today in large part because of the content made by its community. As such, developers should always be sparing in what they choose to deprecate and what they choose to remove, so that UMC authors can update their content to the latest version of Wesnoth without needing to make a herculean effort every couple of years.&lt;br /&gt;
&lt;br /&gt;
But, for cases where APIs are removed, the benefit of providing certainty for when that will happen outweighs the flexibility of being able remove them at any time after they been marked as deprecated. It encourages developers to provide the documentation and tooling support to make it easier for UMC authors to update their add-ons, and it lets UMC authors know when they can expect deprecated APIs to be removed rather than it effectively happening at random when a developer decides a deprecated API needs to be dropped.&lt;br /&gt;
&lt;br /&gt;
=== More Complex Cases - One size does not fit all ===&lt;br /&gt;
There may, however, be cases where the obsolete API cannot be implemented cleanly in terms of the updated one and must be maintained separately, or, in extremely rare cases, cannot coexist at all. There needs to be some leeway for such cases as well. In the former case, it therefore makes sense to allow for a feature to be deprecated pending removal after the shortest reasonable deprecation period. In the latter case, there is obviously no choice but to make the change and remove the old immediately. Developers should, naturally, be encouraged to find creative solutions to avoid such cases, but it is inevitable that there will eventually be a few cases where no graceful transition procedure can be found. These more aggressive forms of deprecation should only be done with developer consensus, and only after all options of creating a backwards-compatible transition have been exhausted.&lt;br /&gt;
&lt;br /&gt;
=== Graphics - The exception that proves the rule ===&lt;br /&gt;
The one area where backwards compatibility should NOT be a factor is graphical changes. Any change to the terrain graphics, unit sprites, or portrait images has the potential to result in visually-incompatible custom content. Core content creators cannot be expected to maintain a deprecated visual style alongside a more modern one, as any approach toward doing so would add an unreasonable amount of bloat and overhead, and make it very difficult for core graphics to be updated without having to do double the work. In addition, add-ons will continue to function even with such visual incompatibilities present, they just won't look right. As such, it can be said that stylistic incompatibilities fall more within the realm of content than of code, and it is not unreasonable to expect a content creator to... well...  create content. Expecting the graphical style to remain backwards-compatible would be just as unreasonable as expecting stories, help descriptions, or other forms of lore to never change because they may introduce plot holes into add-on stories. Essentially, since the goal of the software portion of Wesnoth is, at its heart, merely to facilitate the creation and advancement of this kind of content, core content needs to be free to develop unhindered by considerations for add-on content.&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=CompatibilityStandardsV2&amp;diff=74986</id>
		<title>CompatibilityStandardsV2</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=CompatibilityStandardsV2&amp;diff=74986"/>
		<updated>2026-04-19T02:40:35Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: Created page with &amp;quot;As a piece of software matures, there are often new designs, paradigms, and idioms developed which are superior to old ones. This creates an inherent conflict between the need...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;As a piece of software matures, there are often new designs, paradigms, and idioms developed which are superior to old ones. This creates an inherent conflict between the need for progress and the need for compatibility. Wesnoth is no exception. This document describes Wesnoth's approach toward resolving that conflict in a way which is most beneficial to both goals, as well as the rationale behind this approach.&lt;br /&gt;
&lt;br /&gt;
== Policy ==&lt;br /&gt;
This policy defines how the creation of new Wesnoth APIs and the deprecation and removal of old ones are to be handled. For the purposes of this document &amp;quot;API&amp;quot; means &amp;quot;any technical channel by which a content creator interacts with the game engine&amp;quot;. This includes, but is not limited to: preprocessor macros, WML tags, WFL functions, IPFs, and the Lua API. Note that this policy applies only to software APIs. Core content such as sprites, portraits, animations, lore, etc. are to be updated freely, without consideration for stylistic or literary conflicts that such content changes may present to add-ons.&lt;br /&gt;
&lt;br /&gt;
=== When to deprecate ===&lt;br /&gt;
==== Adding a better API ====&lt;br /&gt;
Any time a superior API is introduced which has '''complete feature-parity''' with an existing API, the old one should be immediately deprecated. Note that in most cases, in order to be considered to have &amp;quot;complete feature-parity&amp;quot;, the API should be available in the same language or area of the code as the obsolete one was. For example, the introduction of a more powerful API in Lua which can accomplish a superset of the functionality which had previously been available with a certain WML tag would not obsolete that WML tag. Exceptions can be made to this rule in cases where it is clear that the feature does not make a lot of sense in its current language or area, and was merely there for legacy reasons (such as the proper place for it not having been introduced yet at the time of its creation). Such exceptions should be determined by developer consensus.&lt;br /&gt;
&lt;br /&gt;
==== Preventing needed fixes or improvements ====&lt;br /&gt;
In such cases where a feature is preventing an important new feature from being added or makes it impossible to fix a problem impacting players, it can be preferable to deprecate the API to allow for the necessary changes to be made. APIs deprecated for this reason should still follow the deprecation schedule as normal, unless the fix or improvement is urgently needed.&lt;br /&gt;
&lt;br /&gt;
==== Actively harmful ====&lt;br /&gt;
If an API is found to cause significant problems for players or UMC authors, such as being prone to causing crashes while also very difficult to properly fix, corrupting saves and replays when not used correctly while being difficult to use correctly, or other similar situations, then such APIs should be deprecated and removed regardless of whether there is a replacement available.&lt;br /&gt;
&lt;br /&gt;
==== Security ====&lt;br /&gt;
If an API is found to have a security flaw that can't be fixed, then it should not be deprecated, it should be immediately removed.&lt;br /&gt;
&lt;br /&gt;
==== Unused ====&lt;br /&gt;
Any API that is not used in mainline and also is not used by the most recent version of an add-on on the add-ons server of the current or previous stable release can be deprecated. For example, if an add-on for 1.16 uses a deprecated API but the updated version of the add-on for 1.18 does not, then that add-on is not considered as currently using the API. Once deprecated for this reason, a new add-on being uploaded that uses the API is not a reason to undeprecate it.&lt;br /&gt;
&lt;br /&gt;
This does not need to be a passive process where developers simply check the add-ons server for whether an API is used - developers who want to deprecate an API for removal can proactively talk to and work with UMC authors to help update their add-ons to remove usage of said API. This can be anything from talking with them online about how to update to submitting updated code directly (ie: opening a PR against an add-on's public git repository).&lt;br /&gt;
&lt;br /&gt;
Deprecating and then removing APIs that are unused is the most preferred approach since their removal does not have any impact on UMC authors.&lt;br /&gt;
&lt;br /&gt;
=== When NOT to deprecate ===&lt;br /&gt;
==== Style ====&lt;br /&gt;
Deprecation should not be done purely for reasons of style. This is very subjective and prone to change as new contributors join and current contributors leave or become less active. As such, allowing deprecation for stylistic reasons would lead to entirely unnecessary work for UMC authors as developer preferences change over time.&lt;br /&gt;
&lt;br /&gt;
==== Renaming ====&lt;br /&gt;
While there can be exceptions, it is rarely a net positive to deprecate an API simply for the sake of renaming it to something else. It is preferable to either add a second name for the same function, leaving the old name as-is, or simply live with the current name rather than expecting all UMC authors using the API to update to the new name.&lt;br /&gt;
&lt;br /&gt;
==== Any other reason ====&lt;br /&gt;
Accepted reasons for deprecating APIs should be something that's discussed and agreed upon by the development team while also, ideally, including UMC authors. It should not become the norm that additional reasons to deprecate APIs are treated as exceptions and left as an increasingly forgotten discussion on Discord, IRC, or the forums - they should be added to here with the reasoning behind them.&lt;br /&gt;
&lt;br /&gt;
=== Deprecation awareness ===&lt;br /&gt;
==== Conflicting goals ====&lt;br /&gt;
When deprecating APIs there is an inherent conflict in terms of how to make UMC authors aware of the deprecation. After all, if they aren't aware something is deprecated, they can't know they may need to update their add-on. Therefore, deprecations need to be displayed in a place where they will see them and most UMC authors don't look at Wesnoth's logs unless there's some other issue they're investigating. At the same time, deprecation warnings aren't relevant to players and spamming deprecation warnings is not an effective way of communicating what the issues are.&lt;br /&gt;
&lt;br /&gt;
==== A middle ground ====&lt;br /&gt;
Each deprecated feature should make use of an appropriate deprecation function call for that language or subsystem to ensure that the appropriate deprecation notice is printed to the log output. Additionally, deprecation warnings should be displayed in-game in the following cases:&lt;br /&gt;
* If the player is running a development version, level 3 and level 4 deprecations should be shown in-game by default.&lt;br /&gt;
* If the player enables debug mode then all deprecation warnings should be shown, regardless of whether they're using a stable release or a development release.&lt;br /&gt;
&lt;br /&gt;
==== Documentation ====&lt;br /&gt;
It is also not enough to only display a warning at runtime when something deprecated is encountered. It is the responsibility of the development team to proactively make UMC authors aware of the deprecations and removals being done. To accomplish this:&lt;br /&gt;
* When an API is deprecated, and again if it's later removed, its deprecation or removal must be documented in the appropriate section of the changelog for the version it was deprecated or removed in.&lt;br /&gt;
* Likewise, it should be added to https://wiki.wesnoth.org/CompatibilityBreakingChanges&lt;br /&gt;
&lt;br /&gt;
Additionally, it is not enough to simply say that an API is deprecated. In the deprecation message itself as well as in the changelog and https://wiki.wesnoth.org/CompatibilityBreakingChanges, a description must be included as to why it was deprecated or removed and how UMC authors can update their add-ons to address it.&lt;br /&gt;
&lt;br /&gt;
Lastly, it should be understood that &amp;quot;removed&amp;quot; doesn't necessarily mean that the API is entirely gone from Wesnoth's codebase. There is no maintenance burden to keeping macro or method stubs that do nothing aside from printing an error message describing what was removed and why. Keeping such stubs around is highly encouraged as it is helpful for UMC authors trying to update very old add-ons to the current version of Wesnoth.&lt;br /&gt;
&lt;br /&gt;
=== How to deprecate ===&lt;br /&gt;
Every effort should be made to create the simplest possible wrappers which will translate from an obsolete API to the updated one. Such wrappers should ideally be organized into their own compatibility file or module, and set up in such a way that the internals of the updated API will not affect how the old calls get wrapped to the new one. Essentially, the idea is to create a set-it-and-forget-it compatibility wrapper which will continue to work regardless of updates made to the newer API.&lt;br /&gt;
&lt;br /&gt;
Additionally, in all cases where it's practical, the wmllint tool must be updated to be able to automatically handle updating add-ons for anything that's been deprecated except for APIs deprecated at level 1. While developers are still heavily encouraged to add wmllint support for level 1 deprecations, it is not required as these do not show deprecation warnings by default and are expected to continue working indefinitely.&lt;br /&gt;
&lt;br /&gt;
=== Deprecation levels - When to remove deprecated features ===&lt;br /&gt;
While creating simple compatibility wrappers should be possible most of the time, it would be unreasonable to assume that this approach will be viable in absolutely every case. Wesnoth's compatibility policy therefore has four different levels of deprecation which are used to set expectations for when and if an API is expected to be removed:&lt;br /&gt;
&lt;br /&gt;
#Deprecated indefinitely &amp;amp;mdash; This deprecation level is for changes which have newer preferred alternatives but, barring any unforeseen issues, have little to no maintenance impact and should be kept indefinitely in order to reduce the work required for UMC authors to maintain their content. For example, functions or attributes which have had their names changed, macros which have been replaced with tags, macro stubs containing only an error message describing what was removed and why, or simple wrappers with little to no maintenance overhead. If future large scale changes make it impractical to maintain an API deprecated at this level then the deprecation level should be raised accordingly. This is generally the preferred deprecation level - higher deprecation levels are for APIs which are intended for eventual removal and must be weighed against the maintenance work this forces upon UMC authors.&lt;br /&gt;
#Deprecated with the intention of future removal &amp;amp;mdash; This deprecation level is for APIs which will be removed in a future version, but the version they are removed in has not yet have been decided. Barring urgent circumstances, all APIs that are deprecated with the intention of being removed '''must''' start with being deprecated at level 2 for 1-2 development cycles at minimum depending on the impact of the API's removal on UMC authors. Once the API has spent the necessary amount of time at deprecation level 2, it can be moved to deprecation level 3. It is not allowed to deprecate an API at level 2 and change it to level 3 in the same development cycle. There is no maximum amount of time that an API can remain deprecated at level 2.&lt;br /&gt;
#Deprecated for removal in a specific version &amp;amp;mdash; This deprecation level is for APIs which were previously deprecated at level 2 and now have a future version at which they will be removed. The version an API is removed in must be at least two development cycles after the API was deprecated at level 2. It is not allowed to move an API to deprecation level 3 and then remove it in the same development cycle.&lt;br /&gt;
#Removed without deprecation &amp;amp;mdash; This level should be used EXTREMELY rarely, and only in cases where it is ABSOLUTELY NECESSARY. Occasionally, an update to a feature will change the underlying architecture in such a fundamental way that the old paradigm cannot coexist with the new one no matter how much redundant code one would create. While this kind of scenario is extremely rare, and every effort should be made to find creative solutions to avoid it, there are occasionally cases where it truly is impossible to maintain both methods even in the short term. This level should only be used with broad developer consensus, after the majority of active developers familiar with the feature in question have given at least some thought to trying to deprecate gracefully and failed.&lt;br /&gt;
&lt;br /&gt;
== Rationale ==&lt;br /&gt;
The above policy is the result of a large amount of thought, discussion, and debate. Following is a brief outline of the considerations and goals on both sides of the problem, why there is an inherent conflict between them, some failed approaches to resolving the conflict, and how the final policy ultimately maximizes the pursuit of both goals.&lt;br /&gt;
&lt;br /&gt;
=== The Problem - The paradox of progress ===&lt;br /&gt;
Invariably, as development on any project moves forward, developers will realize that there are better, cleaner, or more elegant ways to structure things than they had been previously. These changes can be to improve efficiency, make an API more intuitive, keep code better organized, make common tasks more straightforward, or accomplish any number of other positive things. These changes can also make the development of additional features much more viable. In short, progress is good.&lt;br /&gt;
&lt;br /&gt;
On the other hand, a content-heavy program such as Wesnoth relies on the ability of content creators to efficiently create, maintain, and update their content. Too many changes all at once will force creators of existing content to spend obscene amounts time updating their creations just to keeping them up-to-date and in working order. This can lead to a high amount of frustration, a drop in motivation, and a decline in content being created. In short, progress is bad.&lt;br /&gt;
&lt;br /&gt;
=== Backwards Compatibility - benefits and drawbacks ===&lt;br /&gt;
Most of the time, older paradigms can still be maintained in a manner in which they coexist with the newer ones. This allows for existing content to continue functioning, while at the same time allowing and encouraging new content to be created using the newer methods. However, maintaining such code can be problematic in the following ways:&lt;br /&gt;
#There may eventually be architectural changes a developer would want to make where the old method's square peg no longer fits, even forcibly, into the new method's round hole.&lt;br /&gt;
#Having compatibility code hanging around may, depending on how it is implemented, mean that any updates made to the feature, module, or subsystem in question would have to made in both the new, cleaner design, and the older, poorly structured one, adding more work for developers.&lt;br /&gt;
&lt;br /&gt;
=== The Naive Approach - Deprecate and remove everything old ===&lt;br /&gt;
One approach to balancing old and new is to deprecate the old and slate its eventual removal after either a certain amount of time has passed or a certain number of subsequent versions have been released. This sounds good in theory, but in practice, there will be many changes which are minor or cosmetic in nature and which will add up. Things like replacing macros with WML tags, updating the name of an API call or order of parameters to be more consistent with other similar functions, or switching from a functional to an object-oriented structure are very good for organizational purposes, but will result in a large amount of maintenance required on the part of content creators to keep existing code operational, and for very little real gain. This approach invariably leads to the situation where so much is being changed from one version to the next that creators turn into maintainers, forced to spend nearly all of their time trying to stay ahead of the update curve in an attempt to keep their existing content working, and leaving them very little time and motivation to create new content. And of course, by the time they're finished painstakingly updating their existing code for every little change made for the current release, whoops, there's a new release with a whole slew of new changes that need accounting for. It simply becomes unmanageable.&lt;br /&gt;
&lt;br /&gt;
=== The Naive Approach - Deprecate and remove only when necessary ===&lt;br /&gt;
The opposite approach would be to keep all existing paradigms until they actively interfere with a new architecture or create a double-maintenance problem. While this approach does cut down on the maintenance burden by ensuring that content using an older design continues to work, it hinders progress by the fact that the moment at which it first becomes clear that an architectural or double-maintenance problem will occur is exactly the same moment at which keeping the old structure around becomes problematic. Beginning a deprecation cycle at that point and then having to &amp;quot;wait out&amp;quot; the old paradigm will cause an unacceptable delay in development.&lt;br /&gt;
&lt;br /&gt;
=== The Middle Ground - Deprecate everything old, remove only when necessary ===&lt;br /&gt;
Most of the time, older APIs can be implemented in terms of their newer, cleaner counterparts through the use of things like simple wrappers, parse-translators which re-write the older paradigm's code in terms of the new one, or other relatively low-maintenance &amp;quot;set-it-and-forget-it&amp;quot; approaches. These simple wrappers are not really detrimental to making progress, don't require updating when the new APIs internals are changed, and can usually be organized into their own files and/or modules so that they don't clutter the cleaner code. Many such wrappers will never truly present either of the backwards compatibility drawbacks mentioned above. As such, there is really no detriment to keeping them around indefinitely. However, occasionally, a new idea or approach will be put forth that updates the newer paradigm in such a way that the older one can no longer cleanly wrap to it. This usually happens, as inspiration is wont to do, suddenly, unexpectedly, and without warning. As such developers need the flexibility to be able to remove outdated code as freely as possible when the situation requires. Therefore, the ideal solution would be to deprecate any API which has newer, feature-complete ways to do it, while leaving it in the codebase until such time as its presence becomes a hindrance. Essentially, deprecation need not necessarily mean &amp;quot;this WILL be removed&amp;quot; so much as &amp;quot;this is now a candidate for removal&amp;quot;. By separating the concepts of deprecation and removal, both goals can be better served.&lt;br /&gt;
&lt;br /&gt;
=== Undefined removal timeline - issues encountered ===&lt;br /&gt;
In practice however, simply stating something is a candidate for removal while providing no further information on when it will actually be removed causes multiple issues:&lt;br /&gt;
* When a deprecated API is causing a maintenance burden worthy of removal is a subjective decision, often leading to debates over removal any time a developer decides it's time for an API to be removed, initially deprecated, or moved to a higher deprecation level.&lt;br /&gt;
* Lack of developer incentive to provide good documentation and support for the deprecation of the API given there's no particular timeline for when it will actually cause issues for UMC authors.&lt;br /&gt;
* UMC authors often don't find out about deprecated APIs and so can't possibly migrate to the new APIs.&lt;br /&gt;
* UMC authors aren't provided the documentation or tooling support needed to make updating their add-ons easier.&lt;br /&gt;
* UMC authors may simply decide there's no reason to update to the new API even if they know it's deprecated and how to update their add-on. After all, why spend time updating APIs that may stick around forever?&lt;br /&gt;
* Giving a version that an API '''may''' be removed instead of a version that it '''will''' be removed is not as clear as it may seem to UMC authors who aren't already aware of how Wesnoth handles deprecation. This can lead to UMC authors ignoring deprecation warnings after seeing such warnings for APIs which provide a version that's years old.&lt;br /&gt;
&lt;br /&gt;
=== Certainty is also a benefit ===&lt;br /&gt;
Wesnoth continues to exist today in large part because of the content made by its community. As such, developers should always be sparing in what they choose to deprecate and what they choose to remove, so that UMC authors can update their content to the latest version of Wesnoth without needing to make a herculean effort every couple of years.&lt;br /&gt;
&lt;br /&gt;
But, for cases where APIs are removed, the benefit of providing certainty for when that will happen outweighs the flexibility of being able remove them at any time after they been marked as deprecated. It encourages developers to provide the documentation and tooling support to make it easier for UMC authors to update their add-ons, and it lets UMC authors know when they can expect deprecated APIs to be removed rather than it effectively happening at random when a developer decides a deprecated API needs to be dropped.&lt;br /&gt;
&lt;br /&gt;
=== More Complex Cases - One size does not fit all ===&lt;br /&gt;
There may, however, be cases where the obsolete API cannot be implemented cleanly in terms of the updated one and must be maintained separately, or, in extremely rare cases, cannot coexist at all. There needs to be some leeway for such cases as well. In the former case, it therefore makes sense to allow for a feature to be deprecated pending removal after the shortest reasonable deprecation period. In the latter case, there is obviously no choice but to make the change and remove the old immediately. Developers should, naturally, be encouraged to find creative solutions to avoid such cases, but it is inevitable that there will eventually be a few cases where no graceful transition procedure can be found. These more aggressive forms of deprecation should only be done with developer consensus, and only after all options of creating a backwards-compatible transition have been exhausted.&lt;br /&gt;
&lt;br /&gt;
=== Graphics - The exception that proves the rule ===&lt;br /&gt;
The one area where backwards compatibility should NOT be a factor is graphical changes. Any change to the terrain graphics, unit sprites, or portrait images has the potential to result in visually-incompatible custom content. Core content creators cannot be expected to maintain a deprecated visual style alongside a more modern one, as any approach toward doing so would add an unreasonable amount of bloat and overhead, and make it very difficult for core graphics to be updated without having to do double the work. In addition, add-ons will continue to function even with such visual incompatibilities present, they just won't look right. As such, it can be said that stylistic incompatibilities fall more within the realm of content than of code, and it is not unreasonable to expect a content creator to... well...  create content. Expecting the graphical style to remain backwards-compatible would be just as unreasonable as expecting stories, help descriptions, or other forms of lore to never change because they may introduce plot holes into add-on stories. Essentially, since the goal of the software portion of Wesnoth is, at its heart, merely to facilitate the creation and advancement of this kind of content, core content needs to be free to develop unhindered by considerations for add-on content.&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=CompatibilityBreakingChanges&amp;diff=74974</id>
		<title>CompatibilityBreakingChanges</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=CompatibilityBreakingChanges&amp;diff=74974"/>
		<updated>2026-04-14T02:07:13Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: Pentarctagon moved page User:Bssarkar/CompatibilityBreakingChanges to CompatibilityBreakingChanges&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This lists removed WML functionality and helpful hints for avoiding pitfalls.&lt;br /&gt;
&lt;br /&gt;
The previous list is available [https://forums.wesnoth.org/viewtopic.php?t=58532 here].&lt;br /&gt;
&lt;br /&gt;
== Compatibility breaking changes between 1.18 and 1.19/1.20 ==&lt;br /&gt;
&lt;br /&gt;
=== Compatibility breaking (requires updates to work) ===&lt;br /&gt;
&lt;br /&gt;
* '''[kill]''' now reduces target unit hitpoints to 0 before triggering '''last_breath, die''' events. This affects mentioned events that rely on [have_unit] conditions.&lt;br /&gt;
* WFL Removed properties '''unit.side''' and '''terrain.owner'''. Use '''unit.side_number''' and '''terrain.owner_side''' instead. They return 1-indexed side, removed properties returned 0-indexed side.&lt;br /&gt;
* Abilities with both '''add''' and '''sub''' now calculate value differently&lt;br /&gt;
* rotate_loc_around formula now works&lt;br /&gt;
* [era] with id=era_dunefolk does not exist anymore&lt;br /&gt;
* [side]'s leader attribute has been removed&lt;br /&gt;
* [side]'s [variables] tag is now used to set side variables. Use [side]'s [leader] tag to set a unit variable.&lt;br /&gt;
* Setting gold to decimal value fails with error. Previously it was converted to int automatically. As of 1.19.21, automatic conversion is only done in [gold], but not in Lua or [modify_side].&lt;br /&gt;
* The following macros were removed ([https://github.com/wesnoth/wesnoth/pull/11126 #11126])&lt;br /&gt;
** EARLY_FINISH_BONUS_NOTE&lt;br /&gt;
** NO_EARLY_FINISH_BONUS_NOTE&lt;br /&gt;
** NO_GOLD_CARRYOVER_NOTE&lt;br /&gt;
** NEW_GOLD_CARRYOVER_NOTE_100&lt;br /&gt;
** NEW_GOLD_CARRYOVER_NOTE_40&lt;br /&gt;
** NEW_GOLD_CARRYOVER_NOTE_20&lt;br /&gt;
** MISSILE_FRAME_FIREBALL&lt;br /&gt;
** MESSAGE&lt;br /&gt;
** STORY_PART_SPEECH&lt;br /&gt;
** LOYAL_UNDEAD_UNIT&lt;br /&gt;
** ON_SIGHTING&lt;br /&gt;
** MAKE_AI_SIDE_PERSISTENT&lt;br /&gt;
** DRAKE_FLYING_ANIM&lt;br /&gt;
** NO_INTERRUPT_NO_UNDO&lt;br /&gt;
** ENABLE_NIGHTBLADE&lt;br /&gt;
&lt;br /&gt;
=== Deprecations (will continue to work for now) ===&lt;br /&gt;
* rechange [experimental_filter_ability/active] and [experimental_filter_specials] to [filter_ability] and [filter_specials] and make &amp;quot;experimental_&amp;quot; deprecated.&lt;br /&gt;
&lt;br /&gt;
=== New features (help fill this list, https://wiki.wesnoth.org/Special:WhatLinksHere/Template:DevFeature1.19) ===&lt;br /&gt;
* StandardAbilityFilter, including [https://github.com/wesnoth/wesnoth/pull/7814 7814]&lt;br /&gt;
* [resistance] min_value&lt;br /&gt;
* [attack] alignment&lt;br /&gt;
* New events '''unit_hits''', '''unit_misses'''&lt;br /&gt;
* Positive '''attack_weight''' now affects weapon selection&lt;br /&gt;
* Unit attack [effect]+Lua+formula API min_range and max_range&lt;br /&gt;
* Lua unit attributes fearless and healthy&lt;br /&gt;
* wesnoth.game_config shows some color-related values&lt;br /&gt;
* wesnoth.units.rebuild&lt;br /&gt;
* stringx.ends_with, stringx.starts_with&lt;br /&gt;
* Unit formula new attributes objects, advancements_taken, traits_count, objects_count, advancements_taken_count, fearless, healthy&lt;br /&gt;
* Formula functions lerp_index, ends_with, replace_all, starts_with, get_palette&lt;br /&gt;
* Custom themes https://wiki.wesnoth.org/GUIToolkit#Custom_themes&lt;br /&gt;
* Abilities and specials now support [event] (does not work correctly due to [issue]10819[/issue])&lt;br /&gt;
* Ability &amp;amp; Weapon specials registry ([https://github.com/wesnoth/wesnoth/pull/10644 10644]) (does not work correctly if ability contains [event] due to [issue]10819[/issue])&lt;br /&gt;
* [effect][remove_specials]&lt;br /&gt;
* Expose unit pick dialog to Lua as a simple API call ([https://github.com/wesnoth/wesnoth/pull/8829 8829])&lt;br /&gt;
* [fire_event][data] can be accessed from other event as $data&lt;br /&gt;
* Unit Type Editor&lt;br /&gt;
* [era]auto_sort allows to use custom faction ordering&lt;br /&gt;
* [terrain_defaults] formula is now working as expected in case formula evaluates to null&lt;br /&gt;
* [set_menu_item] description substitutes variables when rightclick is used&lt;br /&gt;
* [defense]&lt;br /&gt;
* [resistance_defaults] is not causing OOS in random maps anymore&lt;br /&gt;
* Ability/weapon special descriptions support po variables in descriptions like '''$add''', '''$sub''' etc that contain the value of the relevant key. ([https://github.com/wesnoth/wesnoth/pull/10053 10053], [https://github.com/wesnoth/wesnoth/pull/10293 10293], [https://github.com/wesnoth/wesnoth/pull/10749 10749])&lt;br /&gt;
* Ability/weapon special help page ids now follow the format: '''ability_&amp;lt;unique_id&amp;gt;''' or '''weaponspecial_&amp;lt;unique_id&amp;gt;''' ([https://github.com/wesnoth/wesnoth/pull/11012 11012]).&lt;br /&gt;
* A lot more functions were added to https://wiki.wesnoth.org/LuaAPI/types/widget with https://wiki.wesnoth.org/index.php?title=LuaAPI%2Ftypes%2Fwidget&amp;amp;type=revision&amp;amp;diff=74843&amp;amp;oldid=74842&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=User:Bssarkar/CompatibilityBreakingChanges&amp;diff=74975</id>
		<title>User:Bssarkar/CompatibilityBreakingChanges</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=User:Bssarkar/CompatibilityBreakingChanges&amp;diff=74975"/>
		<updated>2026-04-14T02:07:13Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: Pentarctagon moved page User:Bssarkar/CompatibilityBreakingChanges to CompatibilityBreakingChanges&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[CompatibilityBreakingChanges]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=PreprocessorRef&amp;diff=74973</id>
		<title>PreprocessorRef</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=PreprocessorRef&amp;diff=74973"/>
		<updated>2026-04-13T15:42:13Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* #deprecated */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WML Tags}}&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
Wesnoth loads just one configuration file directly: '''data/_main.cfg'''. However, the '''WML preprocessor''' allows the inclusion of more files. Whenever a WML file is read by Wesnoth, it is passed through the preprocessor.&lt;br /&gt;
&lt;br /&gt;
The preprocessor can interpret a simple language of string expansions known as ''macros''. A macro should always be defined '''before''' the place where it needs to be used.&lt;br /&gt;
&lt;br /&gt;
The preprocessor is applied recursively, so included files will be parsed for macros, and after macro expansion will be parsed for macros again, and so on. As a result, you should not write a recursive macro that references itself, because it will cause errors (but, alas, not necessarily error messages).&lt;br /&gt;
&lt;br /&gt;
== Preprocessor directives ==&lt;br /&gt;
&lt;br /&gt;
The following directives are used to create and use ''macros'', i.e. shortcuts which reduce repetition of information. See [https://www.wesnoth.org/macro-reference.html the macro reference] for the list of predefined core macros.&lt;br /&gt;
&lt;br /&gt;
Macros have scoping rules such that addons have separate preprocessing contexts, meaning that they can be overridden however an author of UMC wishes to override them, without worrying about breaking other add-ons.&lt;br /&gt;
&lt;br /&gt;
The preprocessor has changed several times, so don't expect old Wesnoth versions to behave exactly the same as the current stable and development series.&lt;br /&gt;
&lt;br /&gt;
'''Note:''' In multiplayer scenarios, these directives will appear to work only for the host and not for other clients. This is because the preprocessor is run only on the host, and the clients receive the resultant WML from the server. It's particularly important to keep this in mind before using preprocessor conditionals.&lt;br /&gt;
&lt;br /&gt;
=== #define ===&lt;br /&gt;
&lt;br /&gt;
'''Syntax: #define ''symbol'' [''parameters''] ''&amp;lt;newline&amp;gt;'' ''substitution'' #enddef'''&lt;br /&gt;
&lt;br /&gt;
All subsequent occurences of '''{''symbol'' [''arguments'']}''' (see below) will be replaced by the contents of the ''substitution'' block, with all occurrences of any parameter {''parameter''} within ''substitution'' replaced by the corresponding value in ''arguments''. &amp;lt;code&amp;gt;#define-#enddef&amp;lt;/code&amp;gt; blocks cannot be nested inside another &amp;lt;code&amp;gt;#define&amp;lt;/code&amp;gt;, as of Wesnoth 1.19.&lt;br /&gt;
&lt;br /&gt;
As an example, the ENEMY_UNIT macro for the [[#Macro inclusions|macro inclusion]] example below could be defined as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;wml&amp;quot;&amp;gt;&lt;br /&gt;
#define ENEMY_UNIT TYPE X Y&lt;br /&gt;
## the ordering above is important, since the preprocessor does not distinguish&lt;br /&gt;
## data into different types; only the ordering is used to determine which&lt;br /&gt;
## arguments apply to which parameters.&lt;br /&gt;
[unit]&lt;br /&gt;
    type={TYPE} ## the unit will be of type TYPE, so different&lt;br /&gt;
                ## instantiations&lt;br /&gt;
                ## of this macro can create different units.&lt;br /&gt;
    x={X}&lt;br /&gt;
    y={Y}&lt;br /&gt;
    side=2 ## the unit will be an enemy, regardless of the parameter&lt;br /&gt;
           ## values. This reduces &amp;quot;repetition of information&amp;quot;,&lt;br /&gt;
           ## since it is no longer necessary to specify&lt;br /&gt;
           ## each created unit as an enemy.&lt;br /&gt;
[/unit]&lt;br /&gt;
#enddef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(See [[SingleUnitWML]] for further information on creating units using WML.)&lt;br /&gt;
&lt;br /&gt;
'''Important note:''' Although macros may look like they're simplifying the code, they do not help with wml bloating. Macros are very good at ''disguising'' WML bloat, but they do nothing to ''alleviate'' it. So instead of using macros to generate redundant and repetitive instructions, you should be considering how to eliminate redundancy through programming techniques of abstraction. The most popular way to improve your code is using custom [[EventWML|events]] and [[InternalActionsWML#.5Bfire_event.5D|fire_event]] tags. See also: [[Wml_optimisation|WML Optimisation]].&lt;br /&gt;
&lt;br /&gt;
==== Whitespace in Macros ====&lt;br /&gt;
&lt;br /&gt;
When expanding a macro, '''''all''''' whitespace in the definition of the macro is preserved. The &amp;lt;tt&amp;gt;#arg&amp;lt;/tt&amp;gt; declarations for optional arguments are removed including the final newline. The body of the optional argument (the default value) is similarly processed just as if it were a macro, so all whitespace is preserved.&lt;br /&gt;
&lt;br /&gt;
There are two main practical implications of these rules:&lt;br /&gt;
&lt;br /&gt;
* When using a macro to define simple constants to be used inline in the middle of an attribute value, the entire content and the &amp;lt;tt&amp;gt;#enddef&amp;lt;/tt&amp;gt; must be on one line, like so:&lt;br /&gt;
: &amp;lt;syntaxhighlight lang=wml&amp;gt;&lt;br /&gt;
#define MY_CONSTANT&lt;br /&gt;
42#enddef&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* If using a macro inside a quoted string, you should not indent the contents of the macro, as the indentation will be preserved upon macro substitution.&lt;br /&gt;
* Similarly if you use an optional argument in the middle of an attribute value, the entire content and the &amp;lt;tt&amp;gt;#endarg&amp;lt;/tt&amp;gt; must be on one line, like so:&lt;br /&gt;
: &amp;lt;syntaxhighlight lang=wml&amp;gt;&lt;br /&gt;
#define MY_MACRO&lt;br /&gt;
#arg OPTIONAL_ARG&lt;br /&gt;
default#endarg&lt;br /&gt;
  key = &amp;quot;composite {OPTIONAL_ARG} value&amp;quot;&lt;br /&gt;
#enddef&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== #arg ===&lt;br /&gt;
&lt;br /&gt;
{{DevFeature1.13|7}} &lt;br /&gt;
&lt;br /&gt;
'''Syntax: #arg ''symbol'' ''&amp;lt;newline&amp;gt;'' ''default value'' #endarg'''&lt;br /&gt;
&lt;br /&gt;
Defines an optional argument for a macro along with its default value. Optional arguments can be used to make a macro more flexible and to allow its user to specify certain parameters only when necessary.&lt;br /&gt;
&lt;br /&gt;
For example, one could define a shortcut macro for [message] with only one required argument (the text displayed), but several optional ones:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;wml&amp;quot;&amp;gt;&lt;br /&gt;
#define MESSAGE TEXT&lt;br /&gt;
&lt;br /&gt;
#arg SPEAKER_ID&lt;br /&gt;
narrator#endarg&lt;br /&gt;
&lt;br /&gt;
#arg CAPTION&lt;br /&gt;
#endarg&lt;br /&gt;
&lt;br /&gt;
#arg SOUND&lt;br /&gt;
#endarg&lt;br /&gt;
&lt;br /&gt;
#arg IMG&lt;br /&gt;
#endarg&lt;br /&gt;
&lt;br /&gt;
[message]&lt;br /&gt;
    speaker={SPEAKER_ID}&lt;br /&gt;
    message={TEXT}&lt;br /&gt;
    caption={CAPTION}&lt;br /&gt;
    sound={SOUND}&lt;br /&gt;
    image={IMG}&lt;br /&gt;
[/message]&lt;br /&gt;
#enddef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The caller of the macro can then decide which, if any, of the default values to override:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;wml&amp;quot;&amp;gt;&lt;br /&gt;
{MESSAGE _&amp;quot;Halt!&amp;quot; SPEAKER_ID=&amp;quot;Guard Captain&amp;quot;}&lt;br /&gt;
{MESSAGE _&amp;quot;Two days pass...&amp;quot; IMG=wesnoth-icon.png SOUND=ambient/morning.ogg}&lt;br /&gt;
{MESSAGE _&amp;quot;...&amp;quot;}&lt;br /&gt;
{MESSAGE _&amp;quot;Welcome!&amp;quot; CAPTION=_&amp;quot;Elóndra's shop of wonders&amp;quot; IMG=portraits/elves/shyde.png}&lt;br /&gt;
{MESSAGE _&amp;quot;*smash*&amp;quot; SPEAKER_ID=&amp;quot;Bridge Troll&amp;quot; SOUND=mace.ogg}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For a multiline optional argument defined and called as follows:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;wml&amp;quot;&amp;gt;&lt;br /&gt;
#define MY_MACRO&lt;br /&gt;
#arg MULTILINE&lt;br /&gt;
[some_tag]&lt;br /&gt;
    some_attribute = &amp;quot;some value&amp;quot;&lt;br /&gt;
[/some_tag]&lt;br /&gt;
#endarg&lt;br /&gt;
...&lt;br /&gt;
#enddef&lt;br /&gt;
&lt;br /&gt;
{MY_MACRO (MULTILINE=[other_tag]&lt;br /&gt;
    other_attribute = &amp;quot;other value&amp;quot;&lt;br /&gt;
[/other_tag])}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Note:''' As with #enddef, the final line break before #endarg is included in the default value. This means that if the symbol is used in the middle of a line, you should place the #endarg immediately after the value has ended, without a line break in between.&lt;br /&gt;
&lt;br /&gt;
=== #undef ===&lt;br /&gt;
&lt;br /&gt;
'''Syntax:''' '''#undef ''symbol'' '''&lt;br /&gt;
&lt;br /&gt;
Removes the previous definition of the macro named ''symbol''. Wesnoth expects this to be done when overriding an existing macro, and will warn you if you redefine an existing macro without an explicit #undef before it.&lt;br /&gt;
&lt;br /&gt;
=== Inclusion directive {} ===&lt;br /&gt;
&lt;br /&gt;
This directive can be used to include macros, single files or sets of files from a target directory.&lt;br /&gt;
&lt;br /&gt;
==== File/directory inclusions ====&lt;br /&gt;
&lt;br /&gt;
'''Syntax: {''path''}'''&lt;br /&gt;
&lt;br /&gt;
Includes the file with the specified ''path'', which will in turn run the preprocessor on it and perform any required substitutions or inclusions within it. The ''path'' may not contain ''..'' or the inclusion will be skipped.&lt;br /&gt;
&lt;br /&gt;
The exact location in which the ''path'' will be resolved will depend on its prefix:&lt;br /&gt;
&lt;br /&gt;
* '''{''path''}''': If ''path'' isn't a known macro (see below), the game will assume it's a relative path to a file in the main game '''data/''' directory and include it.&lt;br /&gt;
* '''{~''path''}''': As above, but instead of the game data directory, the path is resolved relative to the user '''data/''' directory, where user made add-ons can normally be found.&lt;br /&gt;
* '''{./''path''}''': The path is resolved relative to the location of the current file containing this inclusion.&lt;br /&gt;
&lt;br /&gt;
Information for locating the user data and game data directories can be found in [[EditingWesnoth]].&lt;br /&gt;
&lt;br /&gt;
Forward slashes ('''/''') should '''always''' be used as the path delimiter, even if your platform uses a different symbol such as colons (''':''') or backslashes ('''\''')! It is also very important to respect the '''actual letter case''' used to name files and directories for compatibility with case-sensitive filesystems on Unix-based operating systems.&lt;br /&gt;
&lt;br /&gt;
When ''path'' points to a directory instead of a file, the preprocessor will include all files found within with the '''.cfg''' extension, in alphabetical order; files without this extension (such as '''.map''' or '''.png''' files) are ignored.&lt;br /&gt;
&lt;br /&gt;
Some directories are handled in a special fashion according to their contents:&lt;br /&gt;
&lt;br /&gt;
* If there's a file named '''_main.cfg''' in the target directory, only that file will be included and preprocessed. It may include other files from its own directory or subdirectories within it, of course. This is used for managing WML directories as self-contained packages, like user made add-ons.&lt;br /&gt;
* If there are files named '''_main.cfg''' in subdirectories of the target and there isn't one in the target itself, they will be all preprocessed. Given the following layout:&lt;br /&gt;
 dir/&lt;br /&gt;
 dir/a/_main.cfg&lt;br /&gt;
 dir/a/other.cfg&lt;br /&gt;
 dir/b/_main.cfg&lt;br /&gt;
 dir/b/other.cfg&lt;br /&gt;
 dir/other.cfg&lt;br /&gt;
Using '''{dir}''' will cause dir/a/_main.cfg, dir/b/_main.cfg and dir/other.cfg to be included.&lt;br /&gt;
* If there's a file named '''_final.cfg''' but no '''_main.cfg''', the file is guaranteed to be included and processed ''after'' all the other files in the directory.&lt;br /&gt;
* If there's a file named '''_initial.cfg''' but no '''_main.cfg''', the file is guaranteed to be included and processed ''before'' all the other files in the directory.&lt;br /&gt;
&lt;br /&gt;
==== Macro inclusions ====&lt;br /&gt;
&lt;br /&gt;
'''Syntax: {''symbol'' [''arguments''] [''optional arguments'']}'''&lt;br /&gt;
&lt;br /&gt;
If the macro named ''symbol'' is defined, the preprocessor will replace this instruction by the expression ''symbol'' was previously defined as, using ''arguments'' as parameters. The number of normal arguments must be exactly the same as in the original definition or an error will occur. Optional arguments can only be placed '''after''' all normal arguments, however they can be specified in any order desired.&lt;br /&gt;
&lt;br /&gt;
You can create multiple word arguments by using parentheses to delimit the contents. If they are already quoted, space inside quotes will be preserved even without parentheses. For example, in '''{ENEMY_UNIT Wolf Rider 18 24}''' the four words will be interpreted as separate arguments and cause the preprocessor to fail since the macro was defined above with only three; instead, you should use '''{ENEMY_UNIT (Wolf Rider) 18 24}'''.&lt;br /&gt;
&lt;br /&gt;
Optional arguments can also be delimited by placing parentheses, however they must be placed around both the argument name '''and''' content:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;wml&amp;quot;&amp;gt;&lt;br /&gt;
{MESSAGE _&amp;quot;I'll smash you!&amp;quot; (SPEAKER_ID=Bridge Troll) } # Correct&lt;br /&gt;
{MESSAGE _&amp;quot;I'll smash you!&amp;quot; SPEAKER_ID=(Bridge Troll) } # Wrong&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This way even complex arguments can be passed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;wml&amp;quot;&amp;gt;&lt;br /&gt;
{MODIFY_UNIT (&lt;br /&gt;
    [filter_adjacent]&lt;br /&gt;
        canrecruit=yes&lt;br /&gt;
    [/filter_adjacent]&lt;br /&gt;
    ) side 2}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Using the name of an existing macro as the name of a macro argument is possible, but the argument will always take precedence over the original macro:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;wml&amp;quot;&amp;gt;&lt;br /&gt;
#define VARIABLE&lt;br /&gt;
#enddef&lt;br /&gt;
#define MACRO VARIABLE&lt;br /&gt;
    {VARIABLE} # is calling for the argument, not for the macro above&lt;br /&gt;
#enddef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== #ifdef and #ifndef ===&lt;br /&gt;
&lt;br /&gt;
Unlike the other preprocessor directives, '''#ifdef''' and '''#ifndef''' are not mere conveniences. They are often necessary to distinguish between different gameplay modes or difficulties (see [[#Built-in macros|Built-in macros]] below).&lt;br /&gt;
&lt;br /&gt;
'''Syntax:''' '''#ifdef ''symbol'' ''substitution-if-defined'' [#else ''substitution-if-not-defined'' ] #endif'''&lt;br /&gt;
&lt;br /&gt;
If ''symbol'' has been defined with '''#define''' or as a built-in macro, the whole block will be replaced by ''substitution-if-defined''.  If not, it will be replaced by ''substitution-if-not-defined'' if it is available.&lt;br /&gt;
&lt;br /&gt;
'''#ifndef''' is the exact opposite of '''#ifdef''', reversing the logic:&lt;br /&gt;
&lt;br /&gt;
'''Syntax:''' '''#ifndef ''symbol'' ''substitution-if-not-defined''  [#else ''substitution-if-defined''] #endif'''&lt;br /&gt;
&lt;br /&gt;
=== #ifhave and #ifnhave ===&lt;br /&gt;
&lt;br /&gt;
'''Syntax:''' '''#ifhave ''path'' ''substitution-if-path-exists'' [#else ''substitution-if-path-does-not-exist''] #endif'''&lt;br /&gt;
&lt;br /&gt;
Checks for the existence of a file. Uses the same relative paths as [[#Inclusion_directive_.7B.7D|include directives]].&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;wml&amp;quot;&amp;gt;&lt;br /&gt;
#ifhave ~add-ons/My_Addon/_main.cfg&lt;br /&gt;
    {MY_ADDON_MACROS}&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''#ifnhave'''  does the opposite of '''#ifhave''':&lt;br /&gt;
&lt;br /&gt;
'''Syntax:''' '''#ifnhave ''path'' ''substitution-if-path-does-not-exist'' [#else ''substitution-if-path-exists''] #endif'''&lt;br /&gt;
&lt;br /&gt;
=== #ifver and #ifnver ===&lt;br /&gt;
&lt;br /&gt;
'''Syntax:''' '''#ifver ''symbol'' ''operator'' ''version-number'' ''&amp;lt;newline&amp;gt;'' ''substitution-if-condition-met'' [#else ''substitution-if-condition-not-met''] #endif'''&lt;br /&gt;
&lt;br /&gt;
Compares a version number defined in a macro against an argument for conditional block inclusions, like ''#ifdef'' and ''#ifhave''. ''operator'' is one of ''=='' (equal), ''!='' (not equal), ''&amp;lt;'' (less), ''&amp;lt;='' (less or equal), ''&amp;gt;'' (greater), ''&amp;gt;='' (greater or equal). The specified ''symbol'' should have been previously defined as plain text without more macro inclusions within it, and it must not require any arguments.&lt;br /&gt;
&lt;br /&gt;
Versions with text suffixes are sorted in binary order and come after all versions with the same number. The most common suffixes begin with &amp;quot;+&amp;quot;, but as this represents multiple possible versions, comparing versions against it is not recommended.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;wml&amp;quot;&amp;gt;&lt;br /&gt;
#ifver WESNOTH_VERSION &amp;gt;= 1.9.7+&lt;br /&gt;
    [message]&lt;br /&gt;
        speaker=narrator&lt;br /&gt;
        message= _ &amp;quot;I’m on Wesnoth 1.9.7+, 1.9.8 or later!&amp;quot;&lt;br /&gt;
    [/message]&lt;br /&gt;
#else&lt;br /&gt;
#ifver WESNOTH_VERSION == 1.9.7&lt;br /&gt;
    [message]&lt;br /&gt;
        speaker=narrator&lt;br /&gt;
        message= _ &amp;quot;I’m on Wesnoth 1.9.7, and I’ll include some workaround code for bug #9001!&amp;quot;&lt;br /&gt;
    [/message]&lt;br /&gt;
#endif&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''#ifnver'''  does the opposite of '''#ifver''':&lt;br /&gt;
&lt;br /&gt;
'''Syntax:''' '''#ifnver ''symbol'' ''operator'' ''version-number'' ''&amp;lt;newline&amp;gt;'' ''substitution-if-condition-not-met'' [#else ''substitution-if-condition-met''] #endif'''&lt;br /&gt;
&lt;br /&gt;
=== #error ===&lt;br /&gt;
&lt;br /&gt;
'''Syntax:''' '''#error [''message'']'''&lt;br /&gt;
&lt;br /&gt;
Causes the WML preprocessor to fail unconditionally upon encountering the line. For add-ons, this will cause the game to display an error and return to the titlescreen if the add-on is required for the user's action (such as playing a campaign or loading a saved game). For core WML, this will cause the game to quit entirely.&lt;br /&gt;
&lt;br /&gt;
Please note that in spite of the example below, it is '''not''' advisable to use this mechanism in published add-ons for version or feature-checking, since the message is not displayed in a form that permits translation and the additional trace information may confuse players. This directive is only intended as a debugging aid for content creators.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;wml&amp;quot;&amp;gt;&lt;br /&gt;
#ifver WESNOTH_VERSION &amp;lt; 1.11.10&lt;br /&gt;
#error This add-on does not support Wesnoth 1.11.10!&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== #warning ===&lt;br /&gt;
&lt;br /&gt;
'''Syntax:''' '''#warning [''message'']'''&lt;br /&gt;
&lt;br /&gt;
Causes the WML preprocessor to emit a warning upon encountering the line. The message will '''only''' be relayed to stderr, not to the player in the game UI. This directive is only intended as a debugging aid for content creators.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;wml&amp;quot;&amp;gt;&lt;br /&gt;
#ifver WESNOTH_VERSION &amp;lt; 1.11.10&lt;br /&gt;
#warning On Wesnoth 1.11.9 or earlier, bug workarounds enabled!&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== #deprecated ===&lt;br /&gt;
&lt;br /&gt;
{{DevFeature1.13|11}}&lt;br /&gt;
&lt;br /&gt;
'''Syntax:''' '''#deprecated 1 [''message'']'''&lt;br /&gt;
&lt;br /&gt;
'''Syntax:''' '''#deprecated 2 ''version'' [''message'']'''&lt;br /&gt;
&lt;br /&gt;
'''Syntax:''' '''#deprecated 3 ''version'' [''message'']'''&lt;br /&gt;
&lt;br /&gt;
'''Syntax:''' '''#deprecated 4 [''message'']'''&lt;br /&gt;
&lt;br /&gt;
The first argument is the deprecation level, while ''version'' is the first Wesnoth version where this deprecated file/macro might to be removed. ''message'' is the optional deprecation message. The recommended usage is to provide ''message''.&lt;br /&gt;
&lt;br /&gt;
The effect of this directive depends on whether it appears within a macro definition.&lt;br /&gt;
&lt;br /&gt;
* If it appears at file level, outside any macro definition, then it immediately outputs a warning saying that the file is deprecated, with the provided message. Multiple '''#deprecated''' directives at toplevel in a single file will result in separate messages.&lt;br /&gt;
* If it appears inside a macro definition ('''#define ... #enddef'''), then it doesn't output anything. Instead, it marks the macro as deprecated. When that macro is later used, only then will the preprocessor output a warning saying that the macro is deprecated, with the provided message. Multiple '''#deprecated''' directives within a single macro will be merged into one message.&lt;br /&gt;
&lt;br /&gt;
Note that deprecation messages will only appear if they have been set to. {{DevFeature1.13|12}} This can be done by enabling debug mode, or by going to Advanced Preferences and setting the log-level for the deprecation logdomain. (This can also be done on the command-line.)&lt;br /&gt;
&lt;br /&gt;
If you provide a deprecation level of 2 or 3, it is required to indicate the earliest version in which the feature could be removed. However, if you provide a deprecation level of 1 or 4, any provided ''version'' will instead be parsed as part of the message, so you will probably not want to provide one at all. Other deprecation levels are not valid. See the documentation for [[InterfaceActionsWML#.5Bdeprecated_message.5D|[deprecated_message]]] for the meaning of the various ''level'' values.&lt;br /&gt;
&lt;br /&gt;
== Built-in macros ==&lt;br /&gt;
&lt;br /&gt;
The following macros are automatically defined with empty contents (unless specified otherwise) by the game engine depending on the configuration or gameplay mode.&lt;br /&gt;
&lt;br /&gt;
Note that except during an actual game, '''MULTIPLAYER''', '''EDITOR''' and previous campaign defines, are not guaranteed to be undefined, especially when coming back to the titlescreen. So it is not enough to hide contents inside of an #ifdef to prevent them from being seen by the campaign selection dialog, for example for multiplayer specific '''[campaign]'''s or '''[modification]'''s '''type=mp''' must be used. &lt;br /&gt;
&lt;br /&gt;
* A campaign define symbol (see ''define'' in [[CampaignWML]]): defined when playing a single-player campaign.&lt;br /&gt;
* A campaign difficulty level, usually '''EASY''', '''NORMAL''' or '''HARD''' (see ''difficulties'' in [[CampaignWML]]): defined according to the chosen difficulty when starting a single-player campaign, also stored in saved games.&lt;br /&gt;
* '''MULTIPLAYER''': defined when in multiplayer mode.&lt;br /&gt;
* '''EDITOR''': defined when running the built-in map editor.&lt;br /&gt;
* '''DEBUG_MODE''': defined when the game has been launched in debug mode (i.e. with '''-d''' or '''--debug''' in the command line). Can also be set by typing &amp;lt;code&amp;gt;:&amp;lt;/code&amp;gt; to bring up [[CommandMode]], then typing &amp;lt;code&amp;gt;debug&amp;lt;/code&amp;gt;, and then restarting the scenario.&lt;br /&gt;
* '''APPLE''': defined while processing the main game data when running on Mac OS X. This primarily exists to switch Control for Command in hotkeys, which is why there are no defines for other platforms.&lt;br /&gt;
* '''WESNOTH_VERSION''': defined containing just the game version number when running the WML preprocessor.&lt;br /&gt;
* '''CURRENT_FILE''': Expands to the name of the current WML file.&lt;br /&gt;
* '''CURRENT_DIRECTORY''': Expands to the preprocessor path of the parent directory for the current WML file (e.g. for &amp;lt;code&amp;gt;&amp;amp;lt;user data dir&amp;amp;gt;/data/add-ons/My_Addon/_main.cfg&amp;lt;/code&amp;gt; this evaluates to &amp;lt;code&amp;gt;~add-ons/My_Addon&amp;lt;/code&amp;gt;). &lt;br /&gt;
* '''LEFT_BRACE''': {{DevFeature1.15|2}} Expands to &amp;lt;code&amp;gt;{&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''RIGHT_BRACE''': {{DevFeature1.15|2}} Expands to &amp;lt;code&amp;gt;}&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''SCHEMA_VALIDATION''': defined if the validator is being run.&lt;br /&gt;
* '''__WMLUNITS__''': defined when the tool to create [https://units.wesnoth.org units.wesnoth.org] is being run.&lt;br /&gt;
&lt;br /&gt;
A ''very large'' number of additional macros are provided as part of the default game core WML. For a full list of those, check the [https://www.wesnoth.org/macro-reference.html macro reference].&lt;br /&gt;
&lt;br /&gt;
== Command-line preprocessor ==&lt;br /&gt;
&lt;br /&gt;
'''Syntax: --preprocess ''&amp;amp;lt;source file/directory&amp;gt;'' ''&amp;lt;target directory&amp;gt;'' '''&lt;br /&gt;
&lt;br /&gt;
Or the short form:&lt;br /&gt;
&lt;br /&gt;
'''Syntax: -p ''&amp;amp;lt;source file/directory&amp;gt;'' ''&amp;lt;target directory&amp;gt;'' '''&lt;br /&gt;
&lt;br /&gt;
You can specify a list of predefined defines with:&lt;br /&gt;
&lt;br /&gt;
'''Syntax: --preprocess-defines=DEFINE1,DEFINE2,etc'''&lt;br /&gt;
&lt;br /&gt;
comma separated list of defines to be used by '--preprocess' command. If 'SKIP_CORE' is in the define list the data/core won't be preprocessed.&lt;br /&gt;
&lt;br /&gt;
The command will first preprocess '''data/core/macros''' and '''data/core/terrain-graphics''', and afterwards the specified path. You can specify a single file to be preprocessed (if you want to preprocess multiple separate files, you'll need to run a different command line for each one), or an entire directory, which will be preprocessed according to the rules used by the inclusion directive above.&lt;br /&gt;
&lt;br /&gt;
The resulting preprocessed files will be written in the target directory. There will be two types of files: .cfg files --- the normal ones, and .plain files containing line markers and textdomain changes.&lt;br /&gt;
&lt;br /&gt;
If by chance, the simple macro define doesn't suffice, you can use:&lt;br /&gt;
&lt;br /&gt;
'''Syntax: --preprocess-input-macros &amp;lt;file&amp;gt;'''&lt;br /&gt;
&lt;br /&gt;
To import an existing file that contains macros, and they will be available in the defines database before processing the specified files.&lt;br /&gt;
&lt;br /&gt;
There is also the possibility to export the preprocessed defines/macro list with:&lt;br /&gt;
&lt;br /&gt;
'''Syntax: --preprocess-output-macros [&amp;lt;target file&amp;gt;]'''&lt;br /&gt;
&lt;br /&gt;
This file could be fed to the 'input-macros' argument next time you run it. For example, a scenario would be: parsing just the core first time, and for the intended target files, you would add SKIP_CORE but import the generated macros file - that will be faster than preprocessing the core again. If the target file is not specified, the output file will be _MACROS_.cfg in the target directory of the preprocess's command.&lt;br /&gt;
&lt;br /&gt;
If ''file/directory'' and ''target directory'' are not absolute paths, they will be considered relative to the current directory.&lt;br /&gt;
&lt;br /&gt;
Some examples:&lt;br /&gt;
&lt;br /&gt;
* Preprocess the entire tutorial dir, and write the results in the ~/result folder:&lt;br /&gt;
 -p ~/wesnoth/data/campaigns/tutorial ~/result&lt;br /&gt;
* Add the MULTIPLAYER define to the list and preprocess a scenario's config file:&lt;br /&gt;
 -p ~/.wesnoth/data/add-ons/My_Campaign/scenarios/01_First_Scenario.cfg ~/result --preprocess-defines=MULTIPLAYER&lt;br /&gt;
* Add the MY_CAMPAIGN and HARD defines before preprocessing a campaign's files:&lt;br /&gt;
 -p ~/.wesnoth/data/add-ons/My_Campaign ~/result --preprocess-defines=MY_CAMPAIGN,HARD&lt;br /&gt;
* File myfile.cfg depends on macros defined in data/gui/macros/_initial.cfg:&lt;br /&gt;
 -p data/gui/macros/_initial.cfg  /tmp --preprocess-output-macros=macros&lt;br /&gt;
 -p myfile.cfg ~/result --preprocess-input-macros=/tmp/macros&lt;br /&gt;
&lt;br /&gt;
When it starts getting complicated, such as a file that depends on multiple other files, it is probably easiest to simply &amp;quot;include&amp;quot; the dependencies:&lt;br /&gt;
&lt;br /&gt;
* File myfile.cfg depends on macros defined in data/gui/macros/_initial.cfg:&lt;br /&gt;
  Add {gui/macros/_initial.cfg} to the beginning of  myfile.cfg&lt;br /&gt;
  -p myfile.cfg ~/result&lt;br /&gt;
&lt;br /&gt;
If you want a more detailed (and potentially overwhelming) log, you can simply add the switches '''--log-debug=all''' or '''--log-info=all''' to the command line, so you can see how things are preprocessed in detail.&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&lt;br /&gt;
* [[SyntaxWML]] (explains relationship between comments and preprocessor directives)&lt;br /&gt;
* [[ReferenceWML]]&lt;br /&gt;
&lt;br /&gt;
[[Category: WML Reference]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=74962</id>
		<title>Template:DevDownload</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=74962"/>
		<updated>2026-04-09T00:19:14Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Development (1.19 branch) ==&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
==== Windows (10 1903 and later) {{{4|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.22 | filename=wesnoth-1.19.22-win64.exe |&lt;br /&gt;
hash=805a9da0b230f4334431704ba0cea6b8dcb4613da2530fcf1f315ff382ec67e2}}&lt;br /&gt;
&lt;br /&gt;
==== macOS (10.13 and later) {{{5|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.21 | filename=Wesnoth_1.19.21.dmg |&lt;br /&gt;
hash=ef44157e1056fae915df935b8cb32af88d2072eaa8e8ac2c856a4d78d40e7fd9}}&lt;br /&gt;
&lt;br /&gt;
==== Source code ====&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/blob/master/INSTALL.md Compiling Wesnoth] - How to compile the source code&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.22 | filename=wesnoth-1.19.22.tar.bz2 |&lt;br /&gt;
hash=dccf874092cf42dfbef61e30217f55d40ab4608532ff6dba3be7c052ca3e0c66}}&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Template:StableDownload&amp;diff=74961</id>
		<title>Template:StableDownload</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Template:StableDownload&amp;diff=74961"/>
		<updated>2026-04-09T00:18:43Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Stable (1.18 branch) ==&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
==== Windows (10 1903 and later, 64-bit only) {{{4|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth-1.18 |&lt;br /&gt;
version=1.18.7 | filename=wesnoth-1.18.7-win64.exe |&lt;br /&gt;
hash=1ebe433b8f7b526944b63d15caf11f42481375130431237b3f1139a517c7c7bb}}&lt;br /&gt;
&lt;br /&gt;
==== macOS (10.12 and later) {{{5|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth-1.18 |&lt;br /&gt;
version=1.18.6 | filename=Wesnoth_1.18.6.dmg |&lt;br /&gt;
hash=1b9a0ba71c11a386ea0daef357cb508f5c9dc792eed71eff1a3783c056214c93}}&lt;br /&gt;
&lt;br /&gt;
==== Source code ====&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/blob/master/INSTALL.md Compiling Wesnoth] - How to compile the source code&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth-1.18 |&lt;br /&gt;
version=1.18.7 | filename=wesnoth-1.18.7.tar.bz2 |&lt;br /&gt;
hash=d6b50cfdf4388954a1c3da66a61abacdc7643a17aa78a16196e16e720a661fc2}}&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=74953</id>
		<title>Template:DevDownload</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=74953"/>
		<updated>2026-03-30T01:57:23Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Development (1.19 branch) ==&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
==== Windows (10 1903 and later) {{{4|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.22 | filename=wesnoth-1.19.22-win64.exe |&lt;br /&gt;
hash=805a9da0b230f4334431704ba0cea6b8dcb4613da2530fcf1f315ff382ec67e2}}&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.21 | filename=wesnoth-1.19.21-win64.exe |&lt;br /&gt;
hash=7492d586fa192b5d60dfbc4265567e344993401aed6a63d03dc53db130cbbcbb}}&lt;br /&gt;
&lt;br /&gt;
==== macOS (10.13 and later) {{{5|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.21 | filename=Wesnoth_1.19.21.dmg |&lt;br /&gt;
hash=ef44157e1056fae915df935b8cb32af88d2072eaa8e8ac2c856a4d78d40e7fd9}}&lt;br /&gt;
&lt;br /&gt;
==== Source code ====&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/blob/master/INSTALL.md Compiling Wesnoth] - How to compile the source code&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.22 | filename=wesnoth-1.19.22.tar.bz2 |&lt;br /&gt;
hash=dccf874092cf42dfbef61e30217f55d40ab4608532ff6dba3be7c052ca3e0c66}}&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.21 | filename=wesnoth-1.19.21.tar.bz2 |&lt;br /&gt;
hash=d97521cda6717c0a76f3830d68e8918975ca88310e0f6d693db9b62c0585b16d}}&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=PblWML&amp;diff=74932</id>
		<title>PblWML</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=PblWML&amp;diff=74932"/>
		<updated>2026-03-29T03:38:43Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* forum_auth */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WML Tags}}&lt;br /&gt;
&lt;br /&gt;
To upload an add-on you have made, you need a '''_server.pbl''' file in your add-on's directory, at the same level as the '''_main.cfg''' file. When you upload the add-on, the entire directory and subdirectories containing the _server.pbl file will be published. Your add-on must be based entirely on these paths.&lt;br /&gt;
&lt;br /&gt;
See [[AddonStructure]] for more on setting up the add-on folder if you have not done so, and [[Distributing_content]] for more on uploading an add-on to the server with this file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Note:&amp;lt;/b&amp;gt; Be aware that translations in the .pbl-files are '''not''' used, so don't mark these strings as translatable. {{DevFeature1.15|4}} The translations in the .pbl-files are used, but they are used as a plain text instead of Gettext strings, so don't mark these strings as translatable.&lt;br /&gt;
&lt;br /&gt;
== What goes into a .pbl file? ==&lt;br /&gt;
&lt;br /&gt;
'''Note:''' ''You should '''not''' use special formatting or coloring in any of these keys when uploading to the official server.'''''&lt;br /&gt;
&lt;br /&gt;
The following keys are recognized for .pbl files:&lt;br /&gt;
&lt;br /&gt;
=== icon ===&lt;br /&gt;
: An image, displayed leftmost in the add-ons download dialog. It must be a standard Wesnoth file and '''not a custom one'''. A custom file will only work for users who already have the relevant add-on installed. This is not related to the icon used for entries in the campaigns menu -- see [[CampaignWML]] for more information.&lt;br /&gt;
&lt;br /&gt;
: If the icon is a unit with magenta team-color bits, please use [[ImagePathFunctions]] to recolor it. For example: &lt;br /&gt;
&lt;br /&gt;
::icon=&amp;quot;units/elves-wood/archer+female-sword-1.png~RC(magenta&amp;gt;brightorange)&amp;quot;&lt;br /&gt;
:or&lt;br /&gt;
::icon=&amp;quot;units/human-peasants/peasant-ranged.png~RC(magenta&amp;gt;white)~CS(24,24,24)&amp;quot;&lt;br /&gt;
:or&lt;br /&gt;
::icon=&amp;quot;units/human-peasants/ruffian.png~RC(magenta&amp;gt;green)~BLIT(units/human-peasants/woodsman.png~RC(magenta&amp;gt;lightblue),18,12)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
: Because the add-on manager's UI is dark, recoloring to light colors looks usually better. [https://irydacea.me/projects/wespal Wespal] is a tool that provides a convenient way to preview recolored unit sprites without needing to launch the game with specific WML or Lua edits.&lt;br /&gt;
&lt;br /&gt;
: {{DevFeature1.13|12}} Instead of a standard Wesnoth image, a [[DataURI]] can also be used. This way, an image can be directly included into the _server.pbl file. Take care to leave no trailing newline.&lt;br /&gt;
&lt;br /&gt;
=== title ===&lt;br /&gt;
: Displayed to the right of the icon, it is just text. It should usually be the same as the name of your add-on when it is played.&lt;br /&gt;
: '''This value is required'''.&lt;br /&gt;
&lt;br /&gt;
=== version ===&lt;br /&gt;
: Displayed to the right of the title; it is merely text. However, starting with Wesnoth 1.6, the required format is '''x.y.z''' where '''x''', '''y''' and '''z''' are numbers — and a value for '''x''' greater than ''0'' implies the add-on is complete, feature-wise. Trailing non-numeric elements are allowed, but nothing should appear before or between these numbers. The string of numbers will be modified on the server by inserting or appending zeros as neccesary to meet the required format. All this is necessary for the “Update All” button to work correctly. ([[#Version Key Examples|See Examples]])&lt;br /&gt;
: '''This value is required'''.&lt;br /&gt;
&lt;br /&gt;
=== author ===&lt;br /&gt;
: Displayed to the right of the version; it is merely text. Put your name or nickname here. If several people have contributed significantly to the add-on you may want to list all of their names.&lt;br /&gt;
&lt;br /&gt;
: {{DevFeature1.17|3}} When using forum_auth, this value is a single forum account name which will have the ability to upload new versions of the add-on, delete the add-on from the add-ons server, and update the secondary_authors field.&lt;br /&gt;
&lt;br /&gt;
: {{DevFeature1.19|7}} This value is now used the same as it was pre-forum_auth. It is only used to display in the addons manager.&lt;br /&gt;
&lt;br /&gt;
: '''This value is required'''.&lt;br /&gt;
&lt;br /&gt;
=== passphrase ===&lt;br /&gt;
: Not displayed. It prevents others from modifying the version of your add-on on the server. You do not need to input a passphrase when initially publishing a add-on; if you do not, one will be randomly generated for you and replaced in your local copy of the .pbl file.&lt;br /&gt;
: '''SECURITY NOTE:''' If you do specify a passphrase of your own, note that it is stored in '''clear text''' form in the server; '''do NOT use a password you would normally use for any other services or web sites!'''&lt;br /&gt;
&lt;br /&gt;
: {{DevFeature1.15|12}}&lt;br /&gt;
&lt;br /&gt;
: It is no longer required to keep the passphrase in the .pbl file at all. If it is not present, then Wesnoth will prompt for it to be entered when uploading or deleting an add-on.&lt;br /&gt;
&lt;br /&gt;
=== description ===&lt;br /&gt;
: This can be used to provide a brief description of your add-on, and for pre-1.0 versions, let people know how playable it is. The description can be viewed by users by clicking on the Description button in the built-in client, or by moving their mouse over the add-on's icon in the web interface.&lt;br /&gt;
: '''This value is required'''.&lt;br /&gt;
&lt;br /&gt;
=== dependencies ===&lt;br /&gt;
: An optional list of dependencies (a comma separated list of ''addon-name'' – the directory names of the needed add-ons), which should be provided if your add-on relies on other user-made content to work properly. ([[#Dependency Key Example|See Example]])&lt;br /&gt;
&lt;br /&gt;
=== tags ===&lt;br /&gt;
{{DevFeature1.13|12}}&lt;br /&gt;
: An optional string including a comma-separated list of keywords used for matching add-ons when typing terms into the Filter box on the top left of the Add-ons Manager. There are no specific requirements on the syntax of the keywords listed here, but a general recommendation is to keep them relevant for players. For example, one might include the add-on's acronym in the tags, the names or acronyms of add-ons to which it is related, and so on.&lt;br /&gt;
&lt;br /&gt;
{{DevFeature1.15|13}} The in-game add-ons manager will show all the tags in the UI, and also includes a drop-down list of tags to filter by. Not all tags are listed in the filter box, and the exact list of tags supported may change before 1.16 is released, but the list is currently:&lt;br /&gt;
&lt;br /&gt;
* '''cooperative''': All human players are on the same team, versus the AI&lt;br /&gt;
* '''cosmetic''': These make the game look different, without changing gameplay&lt;br /&gt;
* '''difficulty''': Can make campaigns easier or harder&lt;br /&gt;
* '''rng''': Modify the randomness in the combat mechanics, or remove it entirely&lt;br /&gt;
* '''survival''': Fight against waves of enemies&lt;br /&gt;
* '''terraforming''': Players can change the terrain&lt;br /&gt;
&lt;br /&gt;
For example, if an A New Land style add-on had tags ''building'', ''terraforming'', ''anl'', ''city'', and ''survival'' then it would be shown if either ''Terraforming'' or ''Survival'' was selected in the drop-down; the other tags wouldn't affect the filtering.&lt;br /&gt;
&lt;br /&gt;
=== core ===&lt;br /&gt;
{{DevFeature1.13|0}}&lt;br /&gt;
: An optional string defining the id of the core which the addon is designed for. Defaults to &amp;quot;''default''&amp;quot;. Don't specify for an addon which is of type &amp;quot;''core''&amp;quot; itself. Note: DO NOT SET this unless you know why you need it! Giving it an invalid value can lead to mysterious errors with your campaign failing to load!&lt;br /&gt;
&lt;br /&gt;
=== translate ===&lt;br /&gt;
: If set to ''true'', the add-on would have been sent to and updated with [[WesCamp|WesCamp-i18n]], if that project were still active. However, as WesCamp is no longer active, this no longer does anything.&lt;br /&gt;
&lt;br /&gt;
: You should make sure your add-on complies with some very specific [[WesCamp#Preparing_your_add-on_for_WesCamp|conventions]] required to ease the process for translators as well as technical requirements.&lt;br /&gt;
&lt;br /&gt;
: Note: WesCamp was abandoned in 2014. Instead, please refer to:&lt;br /&gt;
* [[GettextForWesnothDevelopers]]&lt;br /&gt;
* [[GettextForTranslators#For_add-ons]]&lt;br /&gt;
* forum thread: [https://r.wesnoth.org/t46366 Guide: Translating your UMC without WesCamp].&lt;br /&gt;
&lt;br /&gt;
=== type ===&lt;br /&gt;
: Indicates the type of the add-on; used to filter listings in the downloads manager dialog. Acceptable values are:&lt;br /&gt;
&lt;br /&gt;
:* ''core'': replaces the whole wml tree. {{DevFeature1.13|0}}&lt;br /&gt;
:* ''campaign'': single player campaign.&lt;br /&gt;
:* ''scenario'': single player scenario.&lt;br /&gt;
:* ''campaign_sp_mp'': hybrid campaign.&lt;br /&gt;
:* ''era'': multiplayer era.&lt;br /&gt;
:* ''faction'': multiplayer stand-alone faction, or add-on for other available era.&lt;br /&gt;
:* ''map_pack'': multiplayer map-pack.&lt;br /&gt;
:* ''campaign_mp'': multiplayer campaign.&lt;br /&gt;
:* ''scenario_mp'': multiplayer scenario. (See the note below.)&lt;br /&gt;
:* ''mod_mp'': multiplayer modification ({{DevFeature1.13|11}} can also used for single-player modifications, although it's still called ''mod_mp'').&lt;br /&gt;
:* ''media'': miscellaneous resources for UMC authors/users, for example, music packs, packages of general-purpose WML, etc. &amp;lt;small&amp;gt;Note: Shows as Resources, not Media, in the add-ons interface&amp;lt;/small&amp;gt;&lt;br /&gt;
:* ''other'': The type to use when no other type fits.&lt;br /&gt;
: '''Note:''' If your add-on contains two or more separate multiplayer scenarios, use ''map_pack''.&lt;br /&gt;
&lt;br /&gt;
: '''This value is required'''.&lt;br /&gt;
&lt;br /&gt;
=== email ===&lt;br /&gt;
: Hidden e-mail address used by the server administrators to contact content authors in case of major issues. Again, this will only be seen by the server administrators and it is required that you provide one in case you need to be contacted about your add-on.&lt;br /&gt;
&lt;br /&gt;
: '''This value is required if forum_auth is not set to true'''.&lt;br /&gt;
&lt;br /&gt;
=== forum_auth ===&lt;br /&gt;
{{DevFeature1.17|3}}&lt;br /&gt;
: When set to ''true'', you will be prompted for your forum password when uploading your add-on. The username(s) available to select will be populated from the '''author''' field (before 1.19.7) or the '''primary_authors''' field (1.19.7+). If the username dropdown is empty, make sure to confirm that either the '''author''' or '''primary_authors''' field is present and spelled correctly, depending on what version of Wesnoth you are running.&lt;br /&gt;
&lt;br /&gt;
When set to true, the ''passphrase'' and ''email'' fields are also not required.&lt;br /&gt;
&lt;br /&gt;
=== primary_authors ===&lt;br /&gt;
{{DevFeature1.19|7}}&lt;br /&gt;
: A comma-delimited list of forum accounts that are allowed to upload new versions of the add-on or delete the add-on from the add-ons server.&lt;br /&gt;
&lt;br /&gt;
=== secondary_authors ===&lt;br /&gt;
: A comma-delimited list of forum accounts that are allowed to upload new versions of the add-on, but aren't allowed to delete the add-on.&lt;br /&gt;
&lt;br /&gt;
=== [feedback] ===&lt;br /&gt;
: The [feedback] tag includes information used by the server to provide the client with a website URL for players to post feedback on an add-on and communicate with the maintainers. At this time, the official add-ons server is configured to take a single parameter described below.&lt;br /&gt;
&lt;br /&gt;
==== topic_id ====&lt;br /&gt;
: Topic id from the [http://forums.wesnoth.org/ Wesnoth.org forums] for the add-on's feedback or development topic maintained by the add-on uploader or author. For existing topics, this topic_id corresponds to the series of digits in the ''t=YYYYY'' portion of a URL like &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;http://forums.wesnoth.org/viewtopic.php?f=XX&amp;amp;t=YYYYY&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;. You must take special care to ensure this information is valid before uploading if you want players to be able to reach you!&lt;br /&gt;
&lt;br /&gt;
=== [translation] ===&lt;br /&gt;
{{DevFeature1.15|4}}&lt;br /&gt;
: Multiple [translation] tags can be used to provide the addon with a localized title and description to be seen in the addons manager. However, it should be noted that the declared translations won't influence the list of supported locales as it depends only on the presence of .mo and .po files for corresponding languages.&lt;br /&gt;
&lt;br /&gt;
==== language ====&lt;br /&gt;
: The target language code for the translation. The codes for each language are given in the big table on [https://www.wesnoth.org/gettext/] . You can use either its contracted version (like ''sv'' for Swedish) or a more precise variety (like ''zh_CN'' or ''ca_ES@valencia'').&lt;br /&gt;
: '''This value is required'''.&lt;br /&gt;
&lt;br /&gt;
==== title ====&lt;br /&gt;
: The translation of addon's title for the target language.&lt;br /&gt;
: '''This value is required'''.&lt;br /&gt;
&lt;br /&gt;
==== description ====&lt;br /&gt;
: The translation of addon's description for the target language.&lt;br /&gt;
&lt;br /&gt;
The add-on server keeps track of some other information about uploaded content, including when they were uploaded, what languages they have been at least partly translated into, how large they are on the server and the number of times they have been downloaded. For more information about this you can read [[CampaignServerWML]].&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Dependency Key Example ===&lt;br /&gt;
&lt;br /&gt;
The following dependency key could be used when the add-on needs the ''Imperial_Era'' and ''Era_of_Myths'' to be installed before it will work properly:&lt;br /&gt;
&lt;br /&gt;
 dependencies=Imperial_Era,Era_of_Myths&lt;br /&gt;
&lt;br /&gt;
=== Version Key Examples ===&lt;br /&gt;
&lt;br /&gt;
{{DevFeature1.17|13}} the schema validation rejects many of the '''good''' examples. https://github.com/wesnoth/wesnoth/issues/7396&lt;br /&gt;
&lt;br /&gt;
The following are examples of '''good''' version values:&lt;br /&gt;
&lt;br /&gt;
 version=&amp;quot;1.5&amp;quot;&lt;br /&gt;
 version=&amp;quot;0.11.4&amp;quot;&lt;br /&gt;
 version=&amp;quot;0.1.4beta&amp;quot;&lt;br /&gt;
 version=&amp;quot;1.5c&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The following are examples of '''bad''' version values:&lt;br /&gt;
&lt;br /&gt;
 version=&amp;quot;Beta1.5&amp;quot;&lt;br /&gt;
 version=&amp;quot;Incomplete (0.3.4)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
In both of the above examples the version number as read by the server will be '''0.0.0Beta1.5''' and '''0.0.0Incomplete (0.3.4)'''. You can clearly see why this will not be a good thing with the ''Update add-ons'' feature.&lt;br /&gt;
&lt;br /&gt;
Finally, here are some example version numbers and how they will be interpreted by the ''Update add-ons'' button. The number on the left will be considered an earlier number than the number on the right in each example.&lt;br /&gt;
&lt;br /&gt;
 0.5 &amp;lt; 1.0&lt;br /&gt;
 1.5 &amp;lt; 1.5c&lt;br /&gt;
 1.0 &amp;lt; 1.0.1&lt;br /&gt;
 1.0c &amp;lt; 1.0.1a&lt;br /&gt;
 1.0.1a &amp;lt; 1.0.1c&lt;br /&gt;
 1.0 Final &amp;lt; 1.0.1 Beta&lt;br /&gt;
&lt;br /&gt;
=== Example .pbl File ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=wml&amp;gt;&lt;br /&gt;
title=&amp;quot;My Campaign&amp;quot;&lt;br /&gt;
type=&amp;quot;campaign&amp;quot;&lt;br /&gt;
icon=&amp;quot;misc/ball.png&amp;quot;&lt;br /&gt;
version=&amp;quot;0.1.2&amp;quot;&lt;br /&gt;
author=&amp;quot;Me, artwork by myself&amp;quot;&lt;br /&gt;
passphrase=&amp;quot;This is like a password; see the security note in the documentation above before choosing a value of your own&amp;quot;&lt;br /&gt;
description=&amp;quot;You get to kill a lot of bad guys. But only the first map is done.&amp;quot;&lt;br /&gt;
email=&amp;quot;name@example.com&amp;quot;&lt;br /&gt;
[feedback]&lt;br /&gt;
    topic_id=12345&lt;br /&gt;
[/feedback]&lt;br /&gt;
# Note: the translation feature works on version 1.14.14, 1.15.4 and later only&lt;br /&gt;
[translation]&lt;br /&gt;
    language=&amp;quot;ru&amp;quot;&lt;br /&gt;
	title=&amp;quot;Моя Кампания&amp;quot;&lt;br /&gt;
    description=&amp;quot;Вам придётся завалить немало плохишей. Но пока что готова лишь первая карта.&amp;quot;&lt;br /&gt;
[/translation]&lt;br /&gt;
[translation]&lt;br /&gt;
    language=&amp;quot;zh_CN&amp;quot;&lt;br /&gt;
	title=&amp;quot;我的竞选&amp;quot;&lt;br /&gt;
    description=&amp;quot;你会杀死很多坏人。 但是只完成了第一张地图。(translated online)&amp;quot;&lt;br /&gt;
[/translation]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&lt;br /&gt;
* [[IGNFileFormat]]&lt;br /&gt;
* [[FancyAddonIcons]]&lt;br /&gt;
* [[ReferenceWML]]&lt;br /&gt;
* [[CampaignServerWML]]&lt;br /&gt;
&lt;br /&gt;
[[Category: WML Reference]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=74892</id>
		<title>Template:DevDownload</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=74892"/>
		<updated>2026-03-09T01:08:56Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Development (1.19 branch) ==&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
==== Windows (10 1903 and later) {{{4|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.21 | filename=wesnoth-1.19.21-win64.exe |&lt;br /&gt;
hash=7492d586fa192b5d60dfbc4265567e344993401aed6a63d03dc53db130cbbcbb}}&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.20 | filename=wesnoth-1.19.20-win64.exe |&lt;br /&gt;
hash=c55a71350f5aab18074a5cc781bc66a56a29086624df85f4e155d5a1a4e966f9}}&lt;br /&gt;
&lt;br /&gt;
==== macOS (10.13 and later) {{{5|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.21 | filename=Wesnoth_1.19.21.dmg |&lt;br /&gt;
hash=ef44157e1056fae915df935b8cb32af88d2072eaa8e8ac2c856a4d78d40e7fd9}}&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.20 | filename=Wesnoth_1.19.20.dmg |&lt;br /&gt;
hash=9a0df54edbbffb503f527a1b8007b7860eace3cfb9b94207c2ad21047171eb94}}&lt;br /&gt;
&lt;br /&gt;
==== Source code ====&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/blob/master/INSTALL.md Compiling Wesnoth] - How to compile the source code&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.21 | filename=wesnoth-1.19.21.tar.bz2 |&lt;br /&gt;
hash=d97521cda6717c0a76f3830d68e8918975ca88310e0f6d693db9b62c0585b16d}}&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.20 | filename=wesnoth-1.19.20.tar.bz2 |&lt;br /&gt;
hash=48f883f8cd3ea008f9170aeaa9fb9ebb486202dd72f455ae131363fc3d164f8f}}&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=MultiplayerServerWML&amp;diff=74861</id>
		<title>MultiplayerServerWML</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=MultiplayerServerWML&amp;diff=74861"/>
		<updated>2026-02-22T15:17:36Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* Game setup (the phase from creation to start) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the [[WML]] used to communicate with the multiplayer server for Wesnoth, [[wesnothd]].&lt;br /&gt;
&lt;br /&gt;
== The handshake ==&lt;br /&gt;
&lt;br /&gt;
The client sends four bytes, then the server replies with four bytes. To get a new connection number, the client will send these four bytes: 0x00 0x00 0x00 0x00. The server then sends back the connection number (wesnothd calls this number the &amp;quot;socket number&amp;quot;). Since 1.13+ the server no longer is using socket numbers to keep track of clients and always sends the same number to them all. Since 1.15+ client can also send 0x00 0x00 0x00 0x01 instead to request entire connection to be [https://github.com/wesnoth/wesnoth/blob/2f8136951cd77526188cf8d0fb2cf21eaa2ebe63/src/server/common/server_base.hpp#L60-L76 encapsulated in TLS] immediately '''after'''. If the handshake is successful, the server will be the first to send a data package. All packages are in [http://en.wikipedia.org/wiki/Gzip gzip] format and are preceded by four bytes that specify the size of the package to come in '''big-endian''' (network byte order). Below you'll find information about what data the (unzipped) packages contain. Unpacked WML uses utf-8 charset.&lt;br /&gt;
&lt;br /&gt;
== The login procedure ==&lt;br /&gt;
&lt;br /&gt;
* server request (optional)&lt;br /&gt;
** '''[version]'''&lt;br /&gt;
&lt;br /&gt;
* client response&lt;br /&gt;
** '''[version]'''&lt;br /&gt;
*** '''version''': The client's version string.&lt;br /&gt;
*** '''client_source''': The client's distribution info. (Steam, SourceForge, App Store, etc.)&lt;br /&gt;
&lt;br /&gt;
* server response (if the server does not accept this version)&lt;br /&gt;
** '''[redirect]'''&lt;br /&gt;
*** '''host''': The host you should connect to.&lt;br /&gt;
*** '''port''': The port you should connect to.&lt;br /&gt;
*** '''version''': A comma-separated list of globs that this server should accept (e.g. &amp;quot;1.0*,1.2*,1.4*,1.7*,1.8*&amp;quot;)&lt;br /&gt;
** or '''[reject]''' (if the version is unknown)&lt;br /&gt;
*** '''accepted_versions''': A comma-separated list of globs that this server does accept&lt;br /&gt;
&lt;br /&gt;
* server request&lt;br /&gt;
** '''[mustlogin]'''&lt;br /&gt;
&lt;br /&gt;
* client response&lt;br /&gt;
** '''[login]'''&lt;br /&gt;
*** '''username''': The username the client would like to have.&lt;br /&gt;
*** '''password''': The hashed password, created from the password and salt received from the server. More information about how this password is being generated, including a real world example, can be found in the file [http://forum.wesnoth.org/download/file.php?id=41145 HashedPasswords.pdf] (885 KiB). Since version 1.15+ if TLS was successfully established before then password will be passed as is, without hashing, relying on TLS for secrecy. Passing password hashes is no longer supported to free the client from responsibility to support all hash schemes the forum can potentially use. Client will emit error instead of trying to send password if TLS wasn't established.&lt;br /&gt;
&lt;br /&gt;
* server response&lt;br /&gt;
** '''[join_lobby]'''&lt;br /&gt;
*** '''is_moderator''': &amp;quot;yes&amp;quot; if the user is a moderator, &amp;quot;no&amp;quot; otherwise.&lt;br /&gt;
*** '''profile_url_prefix''': The external URL prefix for player profiles (empty if the server doesn't have an attached database)&lt;br /&gt;
** or '''[error]''' (server is waiting for another '''[login]''' message now)&lt;br /&gt;
*** '''message''': The error message.&lt;br /&gt;
*** '''password_request''': If not empty the server asks the client to provide a password for its desired username.&lt;br /&gt;
*** '''phpbb_encryption''': If &amp;quot;yes&amp;quot; the client will encrypt the password using phpbb's algorithm.&lt;br /&gt;
*** '''random_salt''': Random salt sent to the client for mixing with the password hash.&lt;br /&gt;
*** '''hash_seed''': Salt generated from the original hash that is required to recreate it.&lt;br /&gt;
*** '''salt''': Salt generated from the original hash that is required to recreate it.&lt;br /&gt;
*** '''force_confirmation''': Display an ok/cancel dialog with the content of the 'message' key.&lt;br /&gt;
&lt;br /&gt;
* server response&lt;br /&gt;
** '''[gamelist]'''&lt;br /&gt;
*** '''[game]''' (repeated)&lt;br /&gt;
**** '''id''': A unique id of the game.&lt;br /&gt;
**** '''name''': The title of the game.&lt;br /&gt;
**** '''mp_scenario''': The id of the scenario.&lt;br /&gt;
**** '''mp_era''': The id of the used era.&lt;br /&gt;
**** '''mp_use_map_settings''': Does the game use the map settings specified in the scenario.&lt;br /&gt;
**** '''mp_fog''': Does the game use fog.&lt;br /&gt;
**** '''mp_shroud''': Does the game use shroud.&lt;br /&gt;
**** '''mp_village_gold''': The number of gold per village.&lt;br /&gt;
**** '''experience_modifier''': The experience setting.&lt;br /&gt;
**** '''mp_countdown''': Does the game use a timer.&lt;br /&gt;
**** '''mp_countdown_reservoir_time''': Upper limit of the possibly available time.&lt;br /&gt;
**** '''mp_countdown_init_time''': Initial time.&lt;br /&gt;
**** '''mp_countdown_action_bonus''': Time bonus per action.&lt;br /&gt;
**** '''mp_countdown_turn_bonus''': Time bonus per turn.&lt;br /&gt;
**** '''map_data''': The map data. ''Notice: not sent to lobby if the game uses shroud''&lt;br /&gt;
**** '''hash''': The hash value of the map_data.&lt;br /&gt;
**** '''observer''': Are observers allowed or not.&lt;br /&gt;
**** '''human_sides''': The number of sides played by humans.&lt;br /&gt;
**** '''slots''': The number of vacant/max slots.&lt;br /&gt;
**** '''[slot_data]''' replaces '''slots''' since {{DevFeature1.13|12}}&lt;br /&gt;
***** '''max''': The number of total slots.&lt;br /&gt;
***** '''vacant''': The number of vacant slots.&lt;br /&gt;
**** '''turn''': The current turn/max turn.&lt;br /&gt;
**** '''[turn_data]''' replaces '''turn''' since {{DevFeature1.13|12}}&lt;br /&gt;
***** '''current''': The current turn number.&lt;br /&gt;
***** '''max''': The total number of turns.&lt;br /&gt;
**** '''[modification]''' Modifications used in this game. See [[ModificationWML]].&lt;br /&gt;
***** '''id''': ID of the modification.&lt;br /&gt;
***** '''name''': Name of the modification.&lt;br /&gt;
***** '''addon_id''': ID of the addon the modification is from.&lt;br /&gt;
***** '''require_modification''': A boolean value; if set to yes, all players have to have this modification installed to join the game.&lt;br /&gt;
**** '''[options]''' Options selected for this game. See [[OptionWML]].&lt;br /&gt;
***** '''[campaign|era|modification|multiplayer]'''&lt;br /&gt;
****** '''id''': ID of the addon the campaign|era|modification|multiplayer (scenario) is from.&lt;br /&gt;
****** '''[option]'''&lt;br /&gt;
******* '''id''': ID of the option.&lt;br /&gt;
******* '''value''': Value of the option.&lt;br /&gt;
** '''[user]''' (repeated)&lt;br /&gt;
*** '''name''': The username of the player.&lt;br /&gt;
*** '''game_id''': The ID of the game the player is in.&lt;br /&gt;
*** '''location''': The name of the game the player is in.&lt;br /&gt;
*** '''available''': &amp;quot;yes&amp;quot; if the player is in the lobby; &amp;quot;no&amp;quot; if in a game.&lt;br /&gt;
Many of the keys under [game] are described more indepth on the [[ScenarioWML]] page.&lt;br /&gt;
&lt;br /&gt;
== Error messages ==&lt;br /&gt;
&lt;br /&gt;
* '''[error]'''&lt;br /&gt;
** '''message''': The error message.&lt;br /&gt;
** '''password_request''': This is a response to a login attempt. The client needs to send a password on another login attempt.&lt;br /&gt;
** '''force_confirmation''': Confirmation to login even if there is an existing client with the same name. If login is continued then that existing client is getting kicked.&lt;br /&gt;
&lt;br /&gt;
== Chat (lobby and in-game) ==&lt;br /&gt;
&lt;br /&gt;
* '''[message]'''&lt;br /&gt;
** '''sender''': (optional - filled by the server) The sender of the message.&lt;br /&gt;
** '''message''': The message itself.&lt;br /&gt;
** '''room''': The room the message is from/to&lt;br /&gt;
* '''[whisper]'''&lt;br /&gt;
** '''receiver''': The receiver of the whisper&lt;br /&gt;
** '''sender''': (optional - filled by the server) The sender of the whisper.&lt;br /&gt;
** '''message''': The message itself.&lt;br /&gt;
&lt;br /&gt;
== Nickname registration related commands (lobby and in-game) ==&lt;br /&gt;
&lt;br /&gt;
* '''[nickserv]'''&lt;br /&gt;
** '''[info]''': Request info about another username.&lt;br /&gt;
*** '''name''': The username.&lt;br /&gt;
&lt;br /&gt;
== Updating the lobby state ==&lt;br /&gt;
&lt;br /&gt;
* '''[gamelist_diff]''': server message - basically a [[DiffWML|diff]] from two gamelists, which also includes the user list.&lt;br /&gt;
&lt;br /&gt;
* '''[observer]''' or '''[observer_quit]''': server message - players joining([observer_quit] - quitting the lobby &amp;quot;game&amp;quot;)/quitting([observer] - joining the lobby &amp;quot;game&amp;quot;) a game&lt;br /&gt;
** '''name''': Username of the player/observer.&lt;br /&gt;
* '''[refresh_lobby]''': Request the full gamelist.&lt;br /&gt;
&lt;br /&gt;
== Game setup (the phase from creation to start) ==&lt;br /&gt;
To create a game the client sends:&lt;br /&gt;
* '''[create_game]'''&lt;br /&gt;
** '''name''': The title of the game.&lt;br /&gt;
** '''password''': The password to use to join the game.&lt;br /&gt;
** '''ignored''': The list of ignored players from the host.&lt;br /&gt;
** '''auto_hosted''': True if this request is from a bot or a server-side queue, false otherwise.&lt;br /&gt;
** '''queue_type''': Either &amp;quot;normal&amp;quot; or &amp;quot;server_preset&amp;quot;.&lt;br /&gt;
** '''queue_id''': The ID of the queue this game is being created from.&lt;br /&gt;
&lt;br /&gt;
followed by a message with the scenario options as under [game] (see above) plus the scenario data ([time], [era], [side], etc. see [[ScenarioWML]])&lt;br /&gt;
&lt;br /&gt;
* '''[join]'''&lt;br /&gt;
** '''id''': The id of the game.&lt;br /&gt;
** '''observe''': Join the game as an observer.&lt;br /&gt;
&lt;br /&gt;
* '''[scenario_diff]''': [[DiffWML|diff]] of the [[ScenarioWML]] (side changes, etc.)&lt;br /&gt;
&lt;br /&gt;
* '''[start_game]''': sent by the host to start a game&lt;br /&gt;
* '''[leave_game]''': sent by the client when it leaves a game; sent by the server to make a client leave a game&lt;br /&gt;
** '''reason''': optional reason if sent by the server and was initiated by moderator action&lt;br /&gt;
&lt;br /&gt;
== In-game communication ==&lt;br /&gt;
&lt;br /&gt;
Normal scenario communication ([[ReplayWML]]):&lt;br /&gt;
* '''[turn]'''&lt;br /&gt;
** '''[command]''': (repeated) can contain all the tags you can find in a [[ReplayWML|replay]]: [recruit], [move], [end_turn], etc.&lt;br /&gt;
*** '''[speak]'''&lt;br /&gt;
**** '''message''': text of the message&lt;br /&gt;
**** '''id''': the sender&lt;br /&gt;
**** '''team_name''': the name of the team the message is for - empty if it's a public message&lt;br /&gt;
&lt;br /&gt;
Multiplayer specific communication:&lt;br /&gt;
* '''[request_choice]'''&lt;br /&gt;
** '''request_id''': unique ID of the choice request&lt;br /&gt;
** '''[random_seed]''': client requests a random number (used for attacks for example)&lt;br /&gt;
** '''[change_controller_wml]''': change controller request from scenario WML&lt;br /&gt;
*** '''side''': side number&lt;br /&gt;
*** '''old_controller''': old [[SideWML#controller|controller]] value&lt;br /&gt;
*** '''new_controller''': new [[SideWML#controller|controller]] value&lt;br /&gt;
* '''[store_next_scenario]''': sent by the host - the scenario data (see [[ScenarioWML]]) to advance to the next scenario&lt;br /&gt;
* '''[notify_next_scenario]''': sent by the server to tell players that the data for the next scenario is available&lt;br /&gt;
* '''[load_next_scenario]''': sent by the client to request the data for the next scenario&lt;br /&gt;
* '''[next_scenario]''': data for the next scenario (see [[ScenarioWML]]), sent by the server on request&lt;br /&gt;
&lt;br /&gt;
* '''[info]''': sent by the host on game end - info about the game state&lt;br /&gt;
** '''type''': &amp;quot;termination&amp;quot; &lt;br /&gt;
** '''condition''': the termination reason&lt;br /&gt;
&lt;br /&gt;
If a player leaves this is sent to the host for all sides he owned.&lt;br /&gt;
* '''side_drop''': The number of a side that dropped because a player left.&lt;br /&gt;
* '''controller''': The controller of that side. (&amp;quot;ai&amp;quot;, &amp;quot;network&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
Client commands:&lt;br /&gt;
* '''[change_controller]''': a player (un)droids one of his sides or assigns control to someone else (The host can assign control for any side.)&lt;br /&gt;
** '''side''': the side to change controller&lt;br /&gt;
** '''player''': the nickname of the player to take control&lt;br /&gt;
** '''controller''': the new controller: &amp;quot;human&amp;quot; or &amp;quot;human_ai&amp;quot;&lt;br /&gt;
** '''own_side''': &amp;quot;yes&amp;quot;&lt;br /&gt;
* '''[muteall]''': the host mutes/unmutes all observers - toggles&lt;br /&gt;
* '''[mute]''': the host mutes an observer - toggles&lt;br /&gt;
** '''username''': the username of the observer - if not specified the servers returns a list of muted usernames&lt;br /&gt;
* '''[kick]''' or '''[ban]''': the host kicks/bans a player/observer&lt;br /&gt;
** '''username''': the username of the player/observer&lt;br /&gt;
&lt;br /&gt;
== Game history ==&lt;br /&gt;
This is a request to query a set of 11 rows of game history data based on the provided search criteria. The official client calls this from the Match History button in the  multiplayer lobby to display 10 rows of data. The 11th row is used as a flag to indicate whether there is more data to be queried or not via the right/left arrows on the dialog.&lt;br /&gt;
&lt;br /&gt;
* '''[game_history_request]'''&lt;br /&gt;
** '''offset''': where in the result set to start returning data from. If there are 50 results and offset 10 is given, then rows 10-21 will be returned.&lt;br /&gt;
** '''search_player''': the forum username of the player to search for.&lt;br /&gt;
** '''search_game_name''': the name of the game to filter results by. Can use the * (matches any character before or after it's used) and _ (matches any single character) wildcards.&lt;br /&gt;
** '''search_content_type''': the type of content to filter by. Must be one of:&lt;br /&gt;
*** '''0''': scenario&lt;br /&gt;
*** '''1''': era&lt;br /&gt;
*** '''2''':modification&lt;br /&gt;
** '''search_content''': The content to filter by. This is the ID of the content, not the name displayed on the UI, due to the translated name getting stored in the database.&lt;br /&gt;
&lt;br /&gt;
== Queues ==&lt;br /&gt;
Queue info sent to the client on join or when the server's config is reloaded and the queue information has changed:&lt;br /&gt;
* '''[queue_update]'''&lt;br /&gt;
** '''queue_id''': The server's unique ID for the queue.&lt;br /&gt;
** '''action''': One of add/update/remove.&lt;br /&gt;
** '''display_name''': The text to show in the list of queues in the lobby. Only used by add/update.&lt;br /&gt;
** '''players_required''': How many players are required before a game is started. Only used by add/update.&lt;br /&gt;
&lt;br /&gt;
When there are enough players to start a game, the last player to join the queue is chosen as the host and their client is told to create the game with the provided settings. This skips the game creation screen and goes straight to the staging screen. The other players in the queue are then told to join that game using the normal [join] command:&lt;br /&gt;
* '''[create_game]'''&lt;br /&gt;
** '''queue_id''': The ID of the queue to create the game for.&lt;br /&gt;
** '''[game]'''&lt;br /&gt;
*** '''scenario''': The ID of the scenario to create the game for.&lt;br /&gt;
*** '''era''': The ID of the era to use.&lt;br /&gt;
*** '''fog''': Whether to have fog enabled.&lt;br /&gt;
*** '''shroud''': Whether to have shroud enabled.&lt;br /&gt;
*** '''village_gold''': How much gold each village provides.&lt;br /&gt;
*** '''village_support''': How much unit support each village provides&lt;br /&gt;
*** '''experience_modifier''': The experience modifier to use.&lt;br /&gt;
*** '''countdown''': Not currently used, set to false.&lt;br /&gt;
*** '''random_start_time''': Whether to start at a random time of day.&lt;br /&gt;
*** '''shuffle_sides''': Whether to shuffle the sides' starting positions.&lt;br /&gt;
&lt;br /&gt;
== Administrative commands ==&lt;br /&gt;
* '''[query]'''&lt;br /&gt;
** '''type''': The type of query. See [[ServerAdministration]] for details.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
[https://github.com/renom/fastbot fastbot] -  the bot for tournaments which implements the protocol, can log in into the lobby and host games. Written in Go. &lt;br /&gt;
[[Category:WML Reference]]&lt;br /&gt;
[[Category:Server Documentation]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=MultiplayerServerWML&amp;diff=74860</id>
		<title>MultiplayerServerWML</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=MultiplayerServerWML&amp;diff=74860"/>
		<updated>2026-02-22T15:14:33Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* Queues */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the [[WML]] used to communicate with the multiplayer server for Wesnoth, [[wesnothd]].&lt;br /&gt;
&lt;br /&gt;
== The handshake ==&lt;br /&gt;
&lt;br /&gt;
The client sends four bytes, then the server replies with four bytes. To get a new connection number, the client will send these four bytes: 0x00 0x00 0x00 0x00. The server then sends back the connection number (wesnothd calls this number the &amp;quot;socket number&amp;quot;). Since 1.13+ the server no longer is using socket numbers to keep track of clients and always sends the same number to them all. Since 1.15+ client can also send 0x00 0x00 0x00 0x01 instead to request entire connection to be [https://github.com/wesnoth/wesnoth/blob/2f8136951cd77526188cf8d0fb2cf21eaa2ebe63/src/server/common/server_base.hpp#L60-L76 encapsulated in TLS] immediately '''after'''. If the handshake is successful, the server will be the first to send a data package. All packages are in [http://en.wikipedia.org/wiki/Gzip gzip] format and are preceded by four bytes that specify the size of the package to come in '''big-endian''' (network byte order). Below you'll find information about what data the (unzipped) packages contain. Unpacked WML uses utf-8 charset.&lt;br /&gt;
&lt;br /&gt;
== The login procedure ==&lt;br /&gt;
&lt;br /&gt;
* server request (optional)&lt;br /&gt;
** '''[version]'''&lt;br /&gt;
&lt;br /&gt;
* client response&lt;br /&gt;
** '''[version]'''&lt;br /&gt;
*** '''version''': The client's version string.&lt;br /&gt;
*** '''client_source''': The client's distribution info. (Steam, SourceForge, App Store, etc.)&lt;br /&gt;
&lt;br /&gt;
* server response (if the server does not accept this version)&lt;br /&gt;
** '''[redirect]'''&lt;br /&gt;
*** '''host''': The host you should connect to.&lt;br /&gt;
*** '''port''': The port you should connect to.&lt;br /&gt;
*** '''version''': A comma-separated list of globs that this server should accept (e.g. &amp;quot;1.0*,1.2*,1.4*,1.7*,1.8*&amp;quot;)&lt;br /&gt;
** or '''[reject]''' (if the version is unknown)&lt;br /&gt;
*** '''accepted_versions''': A comma-separated list of globs that this server does accept&lt;br /&gt;
&lt;br /&gt;
* server request&lt;br /&gt;
** '''[mustlogin]'''&lt;br /&gt;
&lt;br /&gt;
* client response&lt;br /&gt;
** '''[login]'''&lt;br /&gt;
*** '''username''': The username the client would like to have.&lt;br /&gt;
*** '''password''': The hashed password, created from the password and salt received from the server. More information about how this password is being generated, including a real world example, can be found in the file [http://forum.wesnoth.org/download/file.php?id=41145 HashedPasswords.pdf] (885 KiB). Since version 1.15+ if TLS was successfully established before then password will be passed as is, without hashing, relying on TLS for secrecy. Passing password hashes is no longer supported to free the client from responsibility to support all hash schemes the forum can potentially use. Client will emit error instead of trying to send password if TLS wasn't established.&lt;br /&gt;
&lt;br /&gt;
* server response&lt;br /&gt;
** '''[join_lobby]'''&lt;br /&gt;
*** '''is_moderator''': &amp;quot;yes&amp;quot; if the user is a moderator, &amp;quot;no&amp;quot; otherwise.&lt;br /&gt;
*** '''profile_url_prefix''': The external URL prefix for player profiles (empty if the server doesn't have an attached database)&lt;br /&gt;
** or '''[error]''' (server is waiting for another '''[login]''' message now)&lt;br /&gt;
*** '''message''': The error message.&lt;br /&gt;
*** '''password_request''': If not empty the server asks the client to provide a password for its desired username.&lt;br /&gt;
*** '''phpbb_encryption''': If &amp;quot;yes&amp;quot; the client will encrypt the password using phpbb's algorithm.&lt;br /&gt;
*** '''random_salt''': Random salt sent to the client for mixing with the password hash.&lt;br /&gt;
*** '''hash_seed''': Salt generated from the original hash that is required to recreate it.&lt;br /&gt;
*** '''salt''': Salt generated from the original hash that is required to recreate it.&lt;br /&gt;
*** '''force_confirmation''': Display an ok/cancel dialog with the content of the 'message' key.&lt;br /&gt;
&lt;br /&gt;
* server response&lt;br /&gt;
** '''[gamelist]'''&lt;br /&gt;
*** '''[game]''' (repeated)&lt;br /&gt;
**** '''id''': A unique id of the game.&lt;br /&gt;
**** '''name''': The title of the game.&lt;br /&gt;
**** '''mp_scenario''': The id of the scenario.&lt;br /&gt;
**** '''mp_era''': The id of the used era.&lt;br /&gt;
**** '''mp_use_map_settings''': Does the game use the map settings specified in the scenario.&lt;br /&gt;
**** '''mp_fog''': Does the game use fog.&lt;br /&gt;
**** '''mp_shroud''': Does the game use shroud.&lt;br /&gt;
**** '''mp_village_gold''': The number of gold per village.&lt;br /&gt;
**** '''experience_modifier''': The experience setting.&lt;br /&gt;
**** '''mp_countdown''': Does the game use a timer.&lt;br /&gt;
**** '''mp_countdown_reservoir_time''': Upper limit of the possibly available time.&lt;br /&gt;
**** '''mp_countdown_init_time''': Initial time.&lt;br /&gt;
**** '''mp_countdown_action_bonus''': Time bonus per action.&lt;br /&gt;
**** '''mp_countdown_turn_bonus''': Time bonus per turn.&lt;br /&gt;
**** '''map_data''': The map data. ''Notice: not sent to lobby if the game uses shroud''&lt;br /&gt;
**** '''hash''': The hash value of the map_data.&lt;br /&gt;
**** '''observer''': Are observers allowed or not.&lt;br /&gt;
**** '''human_sides''': The number of sides played by humans.&lt;br /&gt;
**** '''slots''': The number of vacant/max slots.&lt;br /&gt;
**** '''[slot_data]''' replaces '''slots''' since {{DevFeature1.13|12}}&lt;br /&gt;
***** '''max''': The number of total slots.&lt;br /&gt;
***** '''vacant''': The number of vacant slots.&lt;br /&gt;
**** '''turn''': The current turn/max turn.&lt;br /&gt;
**** '''[turn_data]''' replaces '''turn''' since {{DevFeature1.13|12}}&lt;br /&gt;
***** '''current''': The current turn number.&lt;br /&gt;
***** '''max''': The total number of turns.&lt;br /&gt;
**** '''[modification]''' Modifications used in this game. See [[ModificationWML]].&lt;br /&gt;
***** '''id''': ID of the modification.&lt;br /&gt;
***** '''name''': Name of the modification.&lt;br /&gt;
***** '''addon_id''': ID of the addon the modification is from.&lt;br /&gt;
***** '''require_modification''': A boolean value; if set to yes, all players have to have this modification installed to join the game.&lt;br /&gt;
**** '''[options]''' Options selected for this game. See [[OptionWML]].&lt;br /&gt;
***** '''[campaign|era|modification|multiplayer]'''&lt;br /&gt;
****** '''id''': ID of the addon the campaign|era|modification|multiplayer (scenario) is from.&lt;br /&gt;
****** '''[option]'''&lt;br /&gt;
******* '''id''': ID of the option.&lt;br /&gt;
******* '''value''': Value of the option.&lt;br /&gt;
** '''[user]''' (repeated)&lt;br /&gt;
*** '''name''': The username of the player.&lt;br /&gt;
*** '''game_id''': The ID of the game the player is in.&lt;br /&gt;
*** '''location''': The name of the game the player is in.&lt;br /&gt;
*** '''available''': &amp;quot;yes&amp;quot; if the player is in the lobby; &amp;quot;no&amp;quot; if in a game.&lt;br /&gt;
Many of the keys under [game] are described more indepth on the [[ScenarioWML]] page.&lt;br /&gt;
&lt;br /&gt;
== Error messages ==&lt;br /&gt;
&lt;br /&gt;
* '''[error]'''&lt;br /&gt;
** '''message''': The error message.&lt;br /&gt;
** '''password_request''': This is a response to a login attempt. The client needs to send a password on another login attempt.&lt;br /&gt;
** '''force_confirmation''': Confirmation to login even if there is an existing client with the same name. If login is continued then that existing client is getting kicked.&lt;br /&gt;
&lt;br /&gt;
== Chat (lobby and in-game) ==&lt;br /&gt;
&lt;br /&gt;
* '''[message]'''&lt;br /&gt;
** '''sender''': (optional - filled by the server) The sender of the message.&lt;br /&gt;
** '''message''': The message itself.&lt;br /&gt;
** '''room''': The room the message is from/to&lt;br /&gt;
* '''[whisper]'''&lt;br /&gt;
** '''receiver''': The receiver of the whisper&lt;br /&gt;
** '''sender''': (optional - filled by the server) The sender of the whisper.&lt;br /&gt;
** '''message''': The message itself.&lt;br /&gt;
&lt;br /&gt;
== Nickname registration related commands (lobby and in-game) ==&lt;br /&gt;
&lt;br /&gt;
* '''[nickserv]'''&lt;br /&gt;
** '''[info]''': Request info about another username.&lt;br /&gt;
*** '''name''': The username.&lt;br /&gt;
&lt;br /&gt;
== Updating the lobby state ==&lt;br /&gt;
&lt;br /&gt;
* '''[gamelist_diff]''': server message - basically a [[DiffWML|diff]] from two gamelists, which also includes the user list.&lt;br /&gt;
&lt;br /&gt;
* '''[observer]''' or '''[observer_quit]''': server message - players joining([observer_quit] - quitting the lobby &amp;quot;game&amp;quot;)/quitting([observer] - joining the lobby &amp;quot;game&amp;quot;) a game&lt;br /&gt;
** '''name''': Username of the player/observer.&lt;br /&gt;
* '''[refresh_lobby]''': Request the full gamelist.&lt;br /&gt;
&lt;br /&gt;
== Game setup (the phase from creation to start) ==&lt;br /&gt;
To create a game the client sends:&lt;br /&gt;
* '''[create_game]'''&lt;br /&gt;
** '''name''': The title of the game.&lt;br /&gt;
** '''password''': The password to use to join the game.&lt;br /&gt;
** '''ignored''': The list of ignored players from the host.&lt;br /&gt;
** '''auto_hosted''': True if this request is from a bot, false otherwise.&lt;br /&gt;
&lt;br /&gt;
followed by a message with the scenario options as under [game] (see above) plus the scenario data ([time], [era], [side], etc. see [[ScenarioWML]])&lt;br /&gt;
&lt;br /&gt;
* '''[join]'''&lt;br /&gt;
** '''id''': The id of the game.&lt;br /&gt;
** '''observe''': Join the game as an observer.&lt;br /&gt;
&lt;br /&gt;
* '''[scenario_diff]''': [[DiffWML|diff]] of the [[ScenarioWML]] (side changes, etc.)&lt;br /&gt;
&lt;br /&gt;
* '''[start_game]''': sent by the host to start a game&lt;br /&gt;
* '''[leave_game]''': sent by the client when it leaves a game; sent by the server to make a client leave a game&lt;br /&gt;
** '''reason''': optional reason if sent by the server and was initiated by moderator action&lt;br /&gt;
&lt;br /&gt;
== In-game communication ==&lt;br /&gt;
&lt;br /&gt;
Normal scenario communication ([[ReplayWML]]):&lt;br /&gt;
* '''[turn]'''&lt;br /&gt;
** '''[command]''': (repeated) can contain all the tags you can find in a [[ReplayWML|replay]]: [recruit], [move], [end_turn], etc.&lt;br /&gt;
*** '''[speak]'''&lt;br /&gt;
**** '''message''': text of the message&lt;br /&gt;
**** '''id''': the sender&lt;br /&gt;
**** '''team_name''': the name of the team the message is for - empty if it's a public message&lt;br /&gt;
&lt;br /&gt;
Multiplayer specific communication:&lt;br /&gt;
* '''[request_choice]'''&lt;br /&gt;
** '''request_id''': unique ID of the choice request&lt;br /&gt;
** '''[random_seed]''': client requests a random number (used for attacks for example)&lt;br /&gt;
** '''[change_controller_wml]''': change controller request from scenario WML&lt;br /&gt;
*** '''side''': side number&lt;br /&gt;
*** '''old_controller''': old [[SideWML#controller|controller]] value&lt;br /&gt;
*** '''new_controller''': new [[SideWML#controller|controller]] value&lt;br /&gt;
* '''[store_next_scenario]''': sent by the host - the scenario data (see [[ScenarioWML]]) to advance to the next scenario&lt;br /&gt;
* '''[notify_next_scenario]''': sent by the server to tell players that the data for the next scenario is available&lt;br /&gt;
* '''[load_next_scenario]''': sent by the client to request the data for the next scenario&lt;br /&gt;
* '''[next_scenario]''': data for the next scenario (see [[ScenarioWML]]), sent by the server on request&lt;br /&gt;
&lt;br /&gt;
* '''[info]''': sent by the host on game end - info about the game state&lt;br /&gt;
** '''type''': &amp;quot;termination&amp;quot; &lt;br /&gt;
** '''condition''': the termination reason&lt;br /&gt;
&lt;br /&gt;
If a player leaves this is sent to the host for all sides he owned.&lt;br /&gt;
* '''side_drop''': The number of a side that dropped because a player left.&lt;br /&gt;
* '''controller''': The controller of that side. (&amp;quot;ai&amp;quot;, &amp;quot;network&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
Client commands:&lt;br /&gt;
* '''[change_controller]''': a player (un)droids one of his sides or assigns control to someone else (The host can assign control for any side.)&lt;br /&gt;
** '''side''': the side to change controller&lt;br /&gt;
** '''player''': the nickname of the player to take control&lt;br /&gt;
** '''controller''': the new controller: &amp;quot;human&amp;quot; or &amp;quot;human_ai&amp;quot;&lt;br /&gt;
** '''own_side''': &amp;quot;yes&amp;quot;&lt;br /&gt;
* '''[muteall]''': the host mutes/unmutes all observers - toggles&lt;br /&gt;
* '''[mute]''': the host mutes an observer - toggles&lt;br /&gt;
** '''username''': the username of the observer - if not specified the servers returns a list of muted usernames&lt;br /&gt;
* '''[kick]''' or '''[ban]''': the host kicks/bans a player/observer&lt;br /&gt;
** '''username''': the username of the player/observer&lt;br /&gt;
&lt;br /&gt;
== Game history ==&lt;br /&gt;
This is a request to query a set of 11 rows of game history data based on the provided search criteria. The official client calls this from the Match History button in the  multiplayer lobby to display 10 rows of data. The 11th row is used as a flag to indicate whether there is more data to be queried or not via the right/left arrows on the dialog.&lt;br /&gt;
&lt;br /&gt;
* '''[game_history_request]'''&lt;br /&gt;
** '''offset''': where in the result set to start returning data from. If there are 50 results and offset 10 is given, then rows 10-21 will be returned.&lt;br /&gt;
** '''search_player''': the forum username of the player to search for.&lt;br /&gt;
** '''search_game_name''': the name of the game to filter results by. Can use the * (matches any character before or after it's used) and _ (matches any single character) wildcards.&lt;br /&gt;
** '''search_content_type''': the type of content to filter by. Must be one of:&lt;br /&gt;
*** '''0''': scenario&lt;br /&gt;
*** '''1''': era&lt;br /&gt;
*** '''2''':modification&lt;br /&gt;
** '''search_content''': The content to filter by. This is the ID of the content, not the name displayed on the UI, due to the translated name getting stored in the database.&lt;br /&gt;
&lt;br /&gt;
== Queues ==&lt;br /&gt;
Queue info sent to the client on join or when the server's config is reloaded and the queue information has changed:&lt;br /&gt;
* '''[queue_update]'''&lt;br /&gt;
** '''queue_id''': The server's unique ID for the queue.&lt;br /&gt;
** '''action''': One of add/update/remove.&lt;br /&gt;
** '''display_name''': The text to show in the list of queues in the lobby. Only used by add/update.&lt;br /&gt;
** '''players_required''': How many players are required before a game is started. Only used by add/update.&lt;br /&gt;
&lt;br /&gt;
When there are enough players to start a game, the last player to join the queue is chosen as the host and their client is told to create the game with the provided settings. This skips the game creation screen and goes straight to the staging screen. The other players in the queue are then told to join that game using the normal [join] command:&lt;br /&gt;
* '''[create_game]'''&lt;br /&gt;
** '''queue_id''': The ID of the queue to create the game for.&lt;br /&gt;
** '''[game]'''&lt;br /&gt;
*** '''scenario''': The ID of the scenario to create the game for.&lt;br /&gt;
*** '''era''': The ID of the era to use.&lt;br /&gt;
*** '''fog''': Whether to have fog enabled.&lt;br /&gt;
*** '''shroud''': Whether to have shroud enabled.&lt;br /&gt;
*** '''village_gold''': How much gold each village provides.&lt;br /&gt;
*** '''village_support''': How much unit support each village provides&lt;br /&gt;
*** '''experience_modifier''': The experience modifier to use.&lt;br /&gt;
*** '''countdown''': Not currently used, set to false.&lt;br /&gt;
*** '''random_start_time''': Whether to start at a random time of day.&lt;br /&gt;
*** '''shuffle_sides''': Whether to shuffle the sides' starting positions.&lt;br /&gt;
&lt;br /&gt;
== Administrative commands ==&lt;br /&gt;
* '''[query]'''&lt;br /&gt;
** '''type''': The type of query. See [[ServerAdministration]] for details.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
[https://github.com/renom/fastbot fastbot] -  the bot for tournaments which implements the protocol, can log in into the lobby and host games. Written in Go. &lt;br /&gt;
[[Category:WML Reference]]&lt;br /&gt;
[[Category:Server Documentation]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=MultiplayerServerWML&amp;diff=74859</id>
		<title>MultiplayerServerWML</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=MultiplayerServerWML&amp;diff=74859"/>
		<updated>2026-02-22T15:13:08Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* Queues */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the [[WML]] used to communicate with the multiplayer server for Wesnoth, [[wesnothd]].&lt;br /&gt;
&lt;br /&gt;
== The handshake ==&lt;br /&gt;
&lt;br /&gt;
The client sends four bytes, then the server replies with four bytes. To get a new connection number, the client will send these four bytes: 0x00 0x00 0x00 0x00. The server then sends back the connection number (wesnothd calls this number the &amp;quot;socket number&amp;quot;). Since 1.13+ the server no longer is using socket numbers to keep track of clients and always sends the same number to them all. Since 1.15+ client can also send 0x00 0x00 0x00 0x01 instead to request entire connection to be [https://github.com/wesnoth/wesnoth/blob/2f8136951cd77526188cf8d0fb2cf21eaa2ebe63/src/server/common/server_base.hpp#L60-L76 encapsulated in TLS] immediately '''after'''. If the handshake is successful, the server will be the first to send a data package. All packages are in [http://en.wikipedia.org/wiki/Gzip gzip] format and are preceded by four bytes that specify the size of the package to come in '''big-endian''' (network byte order). Below you'll find information about what data the (unzipped) packages contain. Unpacked WML uses utf-8 charset.&lt;br /&gt;
&lt;br /&gt;
== The login procedure ==&lt;br /&gt;
&lt;br /&gt;
* server request (optional)&lt;br /&gt;
** '''[version]'''&lt;br /&gt;
&lt;br /&gt;
* client response&lt;br /&gt;
** '''[version]'''&lt;br /&gt;
*** '''version''': The client's version string.&lt;br /&gt;
*** '''client_source''': The client's distribution info. (Steam, SourceForge, App Store, etc.)&lt;br /&gt;
&lt;br /&gt;
* server response (if the server does not accept this version)&lt;br /&gt;
** '''[redirect]'''&lt;br /&gt;
*** '''host''': The host you should connect to.&lt;br /&gt;
*** '''port''': The port you should connect to.&lt;br /&gt;
*** '''version''': A comma-separated list of globs that this server should accept (e.g. &amp;quot;1.0*,1.2*,1.4*,1.7*,1.8*&amp;quot;)&lt;br /&gt;
** or '''[reject]''' (if the version is unknown)&lt;br /&gt;
*** '''accepted_versions''': A comma-separated list of globs that this server does accept&lt;br /&gt;
&lt;br /&gt;
* server request&lt;br /&gt;
** '''[mustlogin]'''&lt;br /&gt;
&lt;br /&gt;
* client response&lt;br /&gt;
** '''[login]'''&lt;br /&gt;
*** '''username''': The username the client would like to have.&lt;br /&gt;
*** '''password''': The hashed password, created from the password and salt received from the server. More information about how this password is being generated, including a real world example, can be found in the file [http://forum.wesnoth.org/download/file.php?id=41145 HashedPasswords.pdf] (885 KiB). Since version 1.15+ if TLS was successfully established before then password will be passed as is, without hashing, relying on TLS for secrecy. Passing password hashes is no longer supported to free the client from responsibility to support all hash schemes the forum can potentially use. Client will emit error instead of trying to send password if TLS wasn't established.&lt;br /&gt;
&lt;br /&gt;
* server response&lt;br /&gt;
** '''[join_lobby]'''&lt;br /&gt;
*** '''is_moderator''': &amp;quot;yes&amp;quot; if the user is a moderator, &amp;quot;no&amp;quot; otherwise.&lt;br /&gt;
*** '''profile_url_prefix''': The external URL prefix for player profiles (empty if the server doesn't have an attached database)&lt;br /&gt;
** or '''[error]''' (server is waiting for another '''[login]''' message now)&lt;br /&gt;
*** '''message''': The error message.&lt;br /&gt;
*** '''password_request''': If not empty the server asks the client to provide a password for its desired username.&lt;br /&gt;
*** '''phpbb_encryption''': If &amp;quot;yes&amp;quot; the client will encrypt the password using phpbb's algorithm.&lt;br /&gt;
*** '''random_salt''': Random salt sent to the client for mixing with the password hash.&lt;br /&gt;
*** '''hash_seed''': Salt generated from the original hash that is required to recreate it.&lt;br /&gt;
*** '''salt''': Salt generated from the original hash that is required to recreate it.&lt;br /&gt;
*** '''force_confirmation''': Display an ok/cancel dialog with the content of the 'message' key.&lt;br /&gt;
&lt;br /&gt;
* server response&lt;br /&gt;
** '''[gamelist]'''&lt;br /&gt;
*** '''[game]''' (repeated)&lt;br /&gt;
**** '''id''': A unique id of the game.&lt;br /&gt;
**** '''name''': The title of the game.&lt;br /&gt;
**** '''mp_scenario''': The id of the scenario.&lt;br /&gt;
**** '''mp_era''': The id of the used era.&lt;br /&gt;
**** '''mp_use_map_settings''': Does the game use the map settings specified in the scenario.&lt;br /&gt;
**** '''mp_fog''': Does the game use fog.&lt;br /&gt;
**** '''mp_shroud''': Does the game use shroud.&lt;br /&gt;
**** '''mp_village_gold''': The number of gold per village.&lt;br /&gt;
**** '''experience_modifier''': The experience setting.&lt;br /&gt;
**** '''mp_countdown''': Does the game use a timer.&lt;br /&gt;
**** '''mp_countdown_reservoir_time''': Upper limit of the possibly available time.&lt;br /&gt;
**** '''mp_countdown_init_time''': Initial time.&lt;br /&gt;
**** '''mp_countdown_action_bonus''': Time bonus per action.&lt;br /&gt;
**** '''mp_countdown_turn_bonus''': Time bonus per turn.&lt;br /&gt;
**** '''map_data''': The map data. ''Notice: not sent to lobby if the game uses shroud''&lt;br /&gt;
**** '''hash''': The hash value of the map_data.&lt;br /&gt;
**** '''observer''': Are observers allowed or not.&lt;br /&gt;
**** '''human_sides''': The number of sides played by humans.&lt;br /&gt;
**** '''slots''': The number of vacant/max slots.&lt;br /&gt;
**** '''[slot_data]''' replaces '''slots''' since {{DevFeature1.13|12}}&lt;br /&gt;
***** '''max''': The number of total slots.&lt;br /&gt;
***** '''vacant''': The number of vacant slots.&lt;br /&gt;
**** '''turn''': The current turn/max turn.&lt;br /&gt;
**** '''[turn_data]''' replaces '''turn''' since {{DevFeature1.13|12}}&lt;br /&gt;
***** '''current''': The current turn number.&lt;br /&gt;
***** '''max''': The total number of turns.&lt;br /&gt;
**** '''[modification]''' Modifications used in this game. See [[ModificationWML]].&lt;br /&gt;
***** '''id''': ID of the modification.&lt;br /&gt;
***** '''name''': Name of the modification.&lt;br /&gt;
***** '''addon_id''': ID of the addon the modification is from.&lt;br /&gt;
***** '''require_modification''': A boolean value; if set to yes, all players have to have this modification installed to join the game.&lt;br /&gt;
**** '''[options]''' Options selected for this game. See [[OptionWML]].&lt;br /&gt;
***** '''[campaign|era|modification|multiplayer]'''&lt;br /&gt;
****** '''id''': ID of the addon the campaign|era|modification|multiplayer (scenario) is from.&lt;br /&gt;
****** '''[option]'''&lt;br /&gt;
******* '''id''': ID of the option.&lt;br /&gt;
******* '''value''': Value of the option.&lt;br /&gt;
** '''[user]''' (repeated)&lt;br /&gt;
*** '''name''': The username of the player.&lt;br /&gt;
*** '''game_id''': The ID of the game the player is in.&lt;br /&gt;
*** '''location''': The name of the game the player is in.&lt;br /&gt;
*** '''available''': &amp;quot;yes&amp;quot; if the player is in the lobby; &amp;quot;no&amp;quot; if in a game.&lt;br /&gt;
Many of the keys under [game] are described more indepth on the [[ScenarioWML]] page.&lt;br /&gt;
&lt;br /&gt;
== Error messages ==&lt;br /&gt;
&lt;br /&gt;
* '''[error]'''&lt;br /&gt;
** '''message''': The error message.&lt;br /&gt;
** '''password_request''': This is a response to a login attempt. The client needs to send a password on another login attempt.&lt;br /&gt;
** '''force_confirmation''': Confirmation to login even if there is an existing client with the same name. If login is continued then that existing client is getting kicked.&lt;br /&gt;
&lt;br /&gt;
== Chat (lobby and in-game) ==&lt;br /&gt;
&lt;br /&gt;
* '''[message]'''&lt;br /&gt;
** '''sender''': (optional - filled by the server) The sender of the message.&lt;br /&gt;
** '''message''': The message itself.&lt;br /&gt;
** '''room''': The room the message is from/to&lt;br /&gt;
* '''[whisper]'''&lt;br /&gt;
** '''receiver''': The receiver of the whisper&lt;br /&gt;
** '''sender''': (optional - filled by the server) The sender of the whisper.&lt;br /&gt;
** '''message''': The message itself.&lt;br /&gt;
&lt;br /&gt;
== Nickname registration related commands (lobby and in-game) ==&lt;br /&gt;
&lt;br /&gt;
* '''[nickserv]'''&lt;br /&gt;
** '''[info]''': Request info about another username.&lt;br /&gt;
*** '''name''': The username.&lt;br /&gt;
&lt;br /&gt;
== Updating the lobby state ==&lt;br /&gt;
&lt;br /&gt;
* '''[gamelist_diff]''': server message - basically a [[DiffWML|diff]] from two gamelists, which also includes the user list.&lt;br /&gt;
&lt;br /&gt;
* '''[observer]''' or '''[observer_quit]''': server message - players joining([observer_quit] - quitting the lobby &amp;quot;game&amp;quot;)/quitting([observer] - joining the lobby &amp;quot;game&amp;quot;) a game&lt;br /&gt;
** '''name''': Username of the player/observer.&lt;br /&gt;
* '''[refresh_lobby]''': Request the full gamelist.&lt;br /&gt;
&lt;br /&gt;
== Game setup (the phase from creation to start) ==&lt;br /&gt;
To create a game the client sends:&lt;br /&gt;
* '''[create_game]'''&lt;br /&gt;
** '''name''': The title of the game.&lt;br /&gt;
** '''password''': The password to use to join the game.&lt;br /&gt;
** '''ignored''': The list of ignored players from the host.&lt;br /&gt;
** '''auto_hosted''': True if this request is from a bot, false otherwise.&lt;br /&gt;
&lt;br /&gt;
followed by a message with the scenario options as under [game] (see above) plus the scenario data ([time], [era], [side], etc. see [[ScenarioWML]])&lt;br /&gt;
&lt;br /&gt;
* '''[join]'''&lt;br /&gt;
** '''id''': The id of the game.&lt;br /&gt;
** '''observe''': Join the game as an observer.&lt;br /&gt;
&lt;br /&gt;
* '''[scenario_diff]''': [[DiffWML|diff]] of the [[ScenarioWML]] (side changes, etc.)&lt;br /&gt;
&lt;br /&gt;
* '''[start_game]''': sent by the host to start a game&lt;br /&gt;
* '''[leave_game]''': sent by the client when it leaves a game; sent by the server to make a client leave a game&lt;br /&gt;
** '''reason''': optional reason if sent by the server and was initiated by moderator action&lt;br /&gt;
&lt;br /&gt;
== In-game communication ==&lt;br /&gt;
&lt;br /&gt;
Normal scenario communication ([[ReplayWML]]):&lt;br /&gt;
* '''[turn]'''&lt;br /&gt;
** '''[command]''': (repeated) can contain all the tags you can find in a [[ReplayWML|replay]]: [recruit], [move], [end_turn], etc.&lt;br /&gt;
*** '''[speak]'''&lt;br /&gt;
**** '''message''': text of the message&lt;br /&gt;
**** '''id''': the sender&lt;br /&gt;
**** '''team_name''': the name of the team the message is for - empty if it's a public message&lt;br /&gt;
&lt;br /&gt;
Multiplayer specific communication:&lt;br /&gt;
* '''[request_choice]'''&lt;br /&gt;
** '''request_id''': unique ID of the choice request&lt;br /&gt;
** '''[random_seed]''': client requests a random number (used for attacks for example)&lt;br /&gt;
** '''[change_controller_wml]''': change controller request from scenario WML&lt;br /&gt;
*** '''side''': side number&lt;br /&gt;
*** '''old_controller''': old [[SideWML#controller|controller]] value&lt;br /&gt;
*** '''new_controller''': new [[SideWML#controller|controller]] value&lt;br /&gt;
* '''[store_next_scenario]''': sent by the host - the scenario data (see [[ScenarioWML]]) to advance to the next scenario&lt;br /&gt;
* '''[notify_next_scenario]''': sent by the server to tell players that the data for the next scenario is available&lt;br /&gt;
* '''[load_next_scenario]''': sent by the client to request the data for the next scenario&lt;br /&gt;
* '''[next_scenario]''': data for the next scenario (see [[ScenarioWML]]), sent by the server on request&lt;br /&gt;
&lt;br /&gt;
* '''[info]''': sent by the host on game end - info about the game state&lt;br /&gt;
** '''type''': &amp;quot;termination&amp;quot; &lt;br /&gt;
** '''condition''': the termination reason&lt;br /&gt;
&lt;br /&gt;
If a player leaves this is sent to the host for all sides he owned.&lt;br /&gt;
* '''side_drop''': The number of a side that dropped because a player left.&lt;br /&gt;
* '''controller''': The controller of that side. (&amp;quot;ai&amp;quot;, &amp;quot;network&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
Client commands:&lt;br /&gt;
* '''[change_controller]''': a player (un)droids one of his sides or assigns control to someone else (The host can assign control for any side.)&lt;br /&gt;
** '''side''': the side to change controller&lt;br /&gt;
** '''player''': the nickname of the player to take control&lt;br /&gt;
** '''controller''': the new controller: &amp;quot;human&amp;quot; or &amp;quot;human_ai&amp;quot;&lt;br /&gt;
** '''own_side''': &amp;quot;yes&amp;quot;&lt;br /&gt;
* '''[muteall]''': the host mutes/unmutes all observers - toggles&lt;br /&gt;
* '''[mute]''': the host mutes an observer - toggles&lt;br /&gt;
** '''username''': the username of the observer - if not specified the servers returns a list of muted usernames&lt;br /&gt;
* '''[kick]''' or '''[ban]''': the host kicks/bans a player/observer&lt;br /&gt;
** '''username''': the username of the player/observer&lt;br /&gt;
&lt;br /&gt;
== Game history ==&lt;br /&gt;
This is a request to query a set of 11 rows of game history data based on the provided search criteria. The official client calls this from the Match History button in the  multiplayer lobby to display 10 rows of data. The 11th row is used as a flag to indicate whether there is more data to be queried or not via the right/left arrows on the dialog.&lt;br /&gt;
&lt;br /&gt;
* '''[game_history_request]'''&lt;br /&gt;
** '''offset''': where in the result set to start returning data from. If there are 50 results and offset 10 is given, then rows 10-21 will be returned.&lt;br /&gt;
** '''search_player''': the forum username of the player to search for.&lt;br /&gt;
** '''search_game_name''': the name of the game to filter results by. Can use the * (matches any character before or after it's used) and _ (matches any single character) wildcards.&lt;br /&gt;
** '''search_content_type''': the type of content to filter by. Must be one of:&lt;br /&gt;
*** '''0''': scenario&lt;br /&gt;
*** '''1''': era&lt;br /&gt;
*** '''2''':modification&lt;br /&gt;
** '''search_content''': The content to filter by. This is the ID of the content, not the name displayed on the UI, due to the translated name getting stored in the database.&lt;br /&gt;
&lt;br /&gt;
== Queues ==&lt;br /&gt;
Queue info sent to the client on join or when the server's config is reloaded and the queue information has changed:&lt;br /&gt;
* '''[queue_update]'''&lt;br /&gt;
** '''queue_id''': The server's unique ID for the queue.&lt;br /&gt;
** '''action''': One of add/update/remove.&lt;br /&gt;
** '''display_name''': The text to show in the list of queues in the lobby. Only used by add/update.&lt;br /&gt;
** '''players_required''': How many players are required before a game is started. Only used by add/update.&lt;br /&gt;
&lt;br /&gt;
When there are enough players to start a game, the last player to join the queue is chosen as the host and their client is told to create the game with the provided settings. This skips the game creation screen and goes straight to the staging screen. The other players in the queue are then told to join that game:&lt;br /&gt;
* '''[create_game]'''&lt;br /&gt;
** '''queue_id''': The ID of the queue to create the game for.&lt;br /&gt;
** '''[game]'''&lt;br /&gt;
*** '''scenario''': The ID of the scenario to create the game for.&lt;br /&gt;
*** '''era''': The ID of the era to use.&lt;br /&gt;
*** '''fog''': Whether to have fog enabled.&lt;br /&gt;
*** '''shroud''': Whether to have shroud enabled.&lt;br /&gt;
*** '''village_gold''': How much gold each village provides.&lt;br /&gt;
*** '''village_support''': How much unit support each village provides&lt;br /&gt;
*** '''experience_modifier''': The experience modifier to use.&lt;br /&gt;
*** '''countdown''': Not currently used, set to false.&lt;br /&gt;
*** '''random_start_time''': Whether to start at a random time of day.&lt;br /&gt;
*** '''shuffle_sides''': Whether to shuffle the sides' starting positions.&lt;br /&gt;
&lt;br /&gt;
== Administrative commands ==&lt;br /&gt;
* '''[query]'''&lt;br /&gt;
** '''type''': The type of query. See [[ServerAdministration]] for details.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
[https://github.com/renom/fastbot fastbot] -  the bot for tournaments which implements the protocol, can log in into the lobby and host games. Written in Go. &lt;br /&gt;
[[Category:WML Reference]]&lt;br /&gt;
[[Category:Server Documentation]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=MultiplayerServerWML&amp;diff=74858</id>
		<title>MultiplayerServerWML</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=MultiplayerServerWML&amp;diff=74858"/>
		<updated>2026-02-22T14:46:30Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: /* Queues */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the [[WML]] used to communicate with the multiplayer server for Wesnoth, [[wesnothd]].&lt;br /&gt;
&lt;br /&gt;
== The handshake ==&lt;br /&gt;
&lt;br /&gt;
The client sends four bytes, then the server replies with four bytes. To get a new connection number, the client will send these four bytes: 0x00 0x00 0x00 0x00. The server then sends back the connection number (wesnothd calls this number the &amp;quot;socket number&amp;quot;). Since 1.13+ the server no longer is using socket numbers to keep track of clients and always sends the same number to them all. Since 1.15+ client can also send 0x00 0x00 0x00 0x01 instead to request entire connection to be [https://github.com/wesnoth/wesnoth/blob/2f8136951cd77526188cf8d0fb2cf21eaa2ebe63/src/server/common/server_base.hpp#L60-L76 encapsulated in TLS] immediately '''after'''. If the handshake is successful, the server will be the first to send a data package. All packages are in [http://en.wikipedia.org/wiki/Gzip gzip] format and are preceded by four bytes that specify the size of the package to come in '''big-endian''' (network byte order). Below you'll find information about what data the (unzipped) packages contain. Unpacked WML uses utf-8 charset.&lt;br /&gt;
&lt;br /&gt;
== The login procedure ==&lt;br /&gt;
&lt;br /&gt;
* server request (optional)&lt;br /&gt;
** '''[version]'''&lt;br /&gt;
&lt;br /&gt;
* client response&lt;br /&gt;
** '''[version]'''&lt;br /&gt;
*** '''version''': The client's version string.&lt;br /&gt;
*** '''client_source''': The client's distribution info. (Steam, SourceForge, App Store, etc.)&lt;br /&gt;
&lt;br /&gt;
* server response (if the server does not accept this version)&lt;br /&gt;
** '''[redirect]'''&lt;br /&gt;
*** '''host''': The host you should connect to.&lt;br /&gt;
*** '''port''': The port you should connect to.&lt;br /&gt;
*** '''version''': A comma-separated list of globs that this server should accept (e.g. &amp;quot;1.0*,1.2*,1.4*,1.7*,1.8*&amp;quot;)&lt;br /&gt;
** or '''[reject]''' (if the version is unknown)&lt;br /&gt;
*** '''accepted_versions''': A comma-separated list of globs that this server does accept&lt;br /&gt;
&lt;br /&gt;
* server request&lt;br /&gt;
** '''[mustlogin]'''&lt;br /&gt;
&lt;br /&gt;
* client response&lt;br /&gt;
** '''[login]'''&lt;br /&gt;
*** '''username''': The username the client would like to have.&lt;br /&gt;
*** '''password''': The hashed password, created from the password and salt received from the server. More information about how this password is being generated, including a real world example, can be found in the file [http://forum.wesnoth.org/download/file.php?id=41145 HashedPasswords.pdf] (885 KiB). Since version 1.15+ if TLS was successfully established before then password will be passed as is, without hashing, relying on TLS for secrecy. Passing password hashes is no longer supported to free the client from responsibility to support all hash schemes the forum can potentially use. Client will emit error instead of trying to send password if TLS wasn't established.&lt;br /&gt;
&lt;br /&gt;
* server response&lt;br /&gt;
** '''[join_lobby]'''&lt;br /&gt;
*** '''is_moderator''': &amp;quot;yes&amp;quot; if the user is a moderator, &amp;quot;no&amp;quot; otherwise.&lt;br /&gt;
*** '''profile_url_prefix''': The external URL prefix for player profiles (empty if the server doesn't have an attached database)&lt;br /&gt;
** or '''[error]''' (server is waiting for another '''[login]''' message now)&lt;br /&gt;
*** '''message''': The error message.&lt;br /&gt;
*** '''password_request''': If not empty the server asks the client to provide a password for its desired username.&lt;br /&gt;
*** '''phpbb_encryption''': If &amp;quot;yes&amp;quot; the client will encrypt the password using phpbb's algorithm.&lt;br /&gt;
*** '''random_salt''': Random salt sent to the client for mixing with the password hash.&lt;br /&gt;
*** '''hash_seed''': Salt generated from the original hash that is required to recreate it.&lt;br /&gt;
*** '''salt''': Salt generated from the original hash that is required to recreate it.&lt;br /&gt;
*** '''force_confirmation''': Display an ok/cancel dialog with the content of the 'message' key.&lt;br /&gt;
&lt;br /&gt;
* server response&lt;br /&gt;
** '''[gamelist]'''&lt;br /&gt;
*** '''[game]''' (repeated)&lt;br /&gt;
**** '''id''': A unique id of the game.&lt;br /&gt;
**** '''name''': The title of the game.&lt;br /&gt;
**** '''mp_scenario''': The id of the scenario.&lt;br /&gt;
**** '''mp_era''': The id of the used era.&lt;br /&gt;
**** '''mp_use_map_settings''': Does the game use the map settings specified in the scenario.&lt;br /&gt;
**** '''mp_fog''': Does the game use fog.&lt;br /&gt;
**** '''mp_shroud''': Does the game use shroud.&lt;br /&gt;
**** '''mp_village_gold''': The number of gold per village.&lt;br /&gt;
**** '''experience_modifier''': The experience setting.&lt;br /&gt;
**** '''mp_countdown''': Does the game use a timer.&lt;br /&gt;
**** '''mp_countdown_reservoir_time''': Upper limit of the possibly available time.&lt;br /&gt;
**** '''mp_countdown_init_time''': Initial time.&lt;br /&gt;
**** '''mp_countdown_action_bonus''': Time bonus per action.&lt;br /&gt;
**** '''mp_countdown_turn_bonus''': Time bonus per turn.&lt;br /&gt;
**** '''map_data''': The map data. ''Notice: not sent to lobby if the game uses shroud''&lt;br /&gt;
**** '''hash''': The hash value of the map_data.&lt;br /&gt;
**** '''observer''': Are observers allowed or not.&lt;br /&gt;
**** '''human_sides''': The number of sides played by humans.&lt;br /&gt;
**** '''slots''': The number of vacant/max slots.&lt;br /&gt;
**** '''[slot_data]''' replaces '''slots''' since {{DevFeature1.13|12}}&lt;br /&gt;
***** '''max''': The number of total slots.&lt;br /&gt;
***** '''vacant''': The number of vacant slots.&lt;br /&gt;
**** '''turn''': The current turn/max turn.&lt;br /&gt;
**** '''[turn_data]''' replaces '''turn''' since {{DevFeature1.13|12}}&lt;br /&gt;
***** '''current''': The current turn number.&lt;br /&gt;
***** '''max''': The total number of turns.&lt;br /&gt;
**** '''[modification]''' Modifications used in this game. See [[ModificationWML]].&lt;br /&gt;
***** '''id''': ID of the modification.&lt;br /&gt;
***** '''name''': Name of the modification.&lt;br /&gt;
***** '''addon_id''': ID of the addon the modification is from.&lt;br /&gt;
***** '''require_modification''': A boolean value; if set to yes, all players have to have this modification installed to join the game.&lt;br /&gt;
**** '''[options]''' Options selected for this game. See [[OptionWML]].&lt;br /&gt;
***** '''[campaign|era|modification|multiplayer]'''&lt;br /&gt;
****** '''id''': ID of the addon the campaign|era|modification|multiplayer (scenario) is from.&lt;br /&gt;
****** '''[option]'''&lt;br /&gt;
******* '''id''': ID of the option.&lt;br /&gt;
******* '''value''': Value of the option.&lt;br /&gt;
** '''[user]''' (repeated)&lt;br /&gt;
*** '''name''': The username of the player.&lt;br /&gt;
*** '''game_id''': The ID of the game the player is in.&lt;br /&gt;
*** '''location''': The name of the game the player is in.&lt;br /&gt;
*** '''available''': &amp;quot;yes&amp;quot; if the player is in the lobby; &amp;quot;no&amp;quot; if in a game.&lt;br /&gt;
Many of the keys under [game] are described more indepth on the [[ScenarioWML]] page.&lt;br /&gt;
&lt;br /&gt;
== Error messages ==&lt;br /&gt;
&lt;br /&gt;
* '''[error]'''&lt;br /&gt;
** '''message''': The error message.&lt;br /&gt;
** '''password_request''': This is a response to a login attempt. The client needs to send a password on another login attempt.&lt;br /&gt;
** '''force_confirmation''': Confirmation to login even if there is an existing client with the same name. If login is continued then that existing client is getting kicked.&lt;br /&gt;
&lt;br /&gt;
== Chat (lobby and in-game) ==&lt;br /&gt;
&lt;br /&gt;
* '''[message]'''&lt;br /&gt;
** '''sender''': (optional - filled by the server) The sender of the message.&lt;br /&gt;
** '''message''': The message itself.&lt;br /&gt;
** '''room''': The room the message is from/to&lt;br /&gt;
* '''[whisper]'''&lt;br /&gt;
** '''receiver''': The receiver of the whisper&lt;br /&gt;
** '''sender''': (optional - filled by the server) The sender of the whisper.&lt;br /&gt;
** '''message''': The message itself.&lt;br /&gt;
&lt;br /&gt;
== Nickname registration related commands (lobby and in-game) ==&lt;br /&gt;
&lt;br /&gt;
* '''[nickserv]'''&lt;br /&gt;
** '''[info]''': Request info about another username.&lt;br /&gt;
*** '''name''': The username.&lt;br /&gt;
&lt;br /&gt;
== Updating the lobby state ==&lt;br /&gt;
&lt;br /&gt;
* '''[gamelist_diff]''': server message - basically a [[DiffWML|diff]] from two gamelists, which also includes the user list.&lt;br /&gt;
&lt;br /&gt;
* '''[observer]''' or '''[observer_quit]''': server message - players joining([observer_quit] - quitting the lobby &amp;quot;game&amp;quot;)/quitting([observer] - joining the lobby &amp;quot;game&amp;quot;) a game&lt;br /&gt;
** '''name''': Username of the player/observer.&lt;br /&gt;
* '''[refresh_lobby]''': Request the full gamelist.&lt;br /&gt;
&lt;br /&gt;
== Game setup (the phase from creation to start) ==&lt;br /&gt;
To create a game the client sends:&lt;br /&gt;
* '''[create_game]'''&lt;br /&gt;
** '''name''': The title of the game.&lt;br /&gt;
** '''password''': The password to use to join the game.&lt;br /&gt;
** '''ignored''': The list of ignored players from the host.&lt;br /&gt;
** '''auto_hosted''': True if this request is from a bot, false otherwise.&lt;br /&gt;
&lt;br /&gt;
followed by a message with the scenario options as under [game] (see above) plus the scenario data ([time], [era], [side], etc. see [[ScenarioWML]])&lt;br /&gt;
&lt;br /&gt;
* '''[join]'''&lt;br /&gt;
** '''id''': The id of the game.&lt;br /&gt;
** '''observe''': Join the game as an observer.&lt;br /&gt;
&lt;br /&gt;
* '''[scenario_diff]''': [[DiffWML|diff]] of the [[ScenarioWML]] (side changes, etc.)&lt;br /&gt;
&lt;br /&gt;
* '''[start_game]''': sent by the host to start a game&lt;br /&gt;
* '''[leave_game]''': sent by the client when it leaves a game; sent by the server to make a client leave a game&lt;br /&gt;
** '''reason''': optional reason if sent by the server and was initiated by moderator action&lt;br /&gt;
&lt;br /&gt;
== In-game communication ==&lt;br /&gt;
&lt;br /&gt;
Normal scenario communication ([[ReplayWML]]):&lt;br /&gt;
* '''[turn]'''&lt;br /&gt;
** '''[command]''': (repeated) can contain all the tags you can find in a [[ReplayWML|replay]]: [recruit], [move], [end_turn], etc.&lt;br /&gt;
*** '''[speak]'''&lt;br /&gt;
**** '''message''': text of the message&lt;br /&gt;
**** '''id''': the sender&lt;br /&gt;
**** '''team_name''': the name of the team the message is for - empty if it's a public message&lt;br /&gt;
&lt;br /&gt;
Multiplayer specific communication:&lt;br /&gt;
* '''[request_choice]'''&lt;br /&gt;
** '''request_id''': unique ID of the choice request&lt;br /&gt;
** '''[random_seed]''': client requests a random number (used for attacks for example)&lt;br /&gt;
** '''[change_controller_wml]''': change controller request from scenario WML&lt;br /&gt;
*** '''side''': side number&lt;br /&gt;
*** '''old_controller''': old [[SideWML#controller|controller]] value&lt;br /&gt;
*** '''new_controller''': new [[SideWML#controller|controller]] value&lt;br /&gt;
* '''[store_next_scenario]''': sent by the host - the scenario data (see [[ScenarioWML]]) to advance to the next scenario&lt;br /&gt;
* '''[notify_next_scenario]''': sent by the server to tell players that the data for the next scenario is available&lt;br /&gt;
* '''[load_next_scenario]''': sent by the client to request the data for the next scenario&lt;br /&gt;
* '''[next_scenario]''': data for the next scenario (see [[ScenarioWML]]), sent by the server on request&lt;br /&gt;
&lt;br /&gt;
* '''[info]''': sent by the host on game end - info about the game state&lt;br /&gt;
** '''type''': &amp;quot;termination&amp;quot; &lt;br /&gt;
** '''condition''': the termination reason&lt;br /&gt;
&lt;br /&gt;
If a player leaves this is sent to the host for all sides he owned.&lt;br /&gt;
* '''side_drop''': The number of a side that dropped because a player left.&lt;br /&gt;
* '''controller''': The controller of that side. (&amp;quot;ai&amp;quot;, &amp;quot;network&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
Client commands:&lt;br /&gt;
* '''[change_controller]''': a player (un)droids one of his sides or assigns control to someone else (The host can assign control for any side.)&lt;br /&gt;
** '''side''': the side to change controller&lt;br /&gt;
** '''player''': the nickname of the player to take control&lt;br /&gt;
** '''controller''': the new controller: &amp;quot;human&amp;quot; or &amp;quot;human_ai&amp;quot;&lt;br /&gt;
** '''own_side''': &amp;quot;yes&amp;quot;&lt;br /&gt;
* '''[muteall]''': the host mutes/unmutes all observers - toggles&lt;br /&gt;
* '''[mute]''': the host mutes an observer - toggles&lt;br /&gt;
** '''username''': the username of the observer - if not specified the servers returns a list of muted usernames&lt;br /&gt;
* '''[kick]''' or '''[ban]''': the host kicks/bans a player/observer&lt;br /&gt;
** '''username''': the username of the player/observer&lt;br /&gt;
&lt;br /&gt;
== Game history ==&lt;br /&gt;
This is a request to query a set of 11 rows of game history data based on the provided search criteria. The official client calls this from the Match History button in the  multiplayer lobby to display 10 rows of data. The 11th row is used as a flag to indicate whether there is more data to be queried or not via the right/left arrows on the dialog.&lt;br /&gt;
&lt;br /&gt;
* '''[game_history_request]'''&lt;br /&gt;
** '''offset''': where in the result set to start returning data from. If there are 50 results and offset 10 is given, then rows 10-21 will be returned.&lt;br /&gt;
** '''search_player''': the forum username of the player to search for.&lt;br /&gt;
** '''search_game_name''': the name of the game to filter results by. Can use the * (matches any character before or after it's used) and _ (matches any single character) wildcards.&lt;br /&gt;
** '''search_content_type''': the type of content to filter by. Must be one of:&lt;br /&gt;
*** '''0''': scenario&lt;br /&gt;
*** '''1''': era&lt;br /&gt;
*** '''2''':modification&lt;br /&gt;
** '''search_content''': The content to filter by. This is the ID of the content, not the name displayed on the UI, due to the translated name getting stored in the database.&lt;br /&gt;
&lt;br /&gt;
== Queues ==&lt;br /&gt;
Queue info sent to the client on join or when the server's config is reloaded and the queue information has changed:&lt;br /&gt;
* '''[queue_update]'''&lt;br /&gt;
** '''queue_id''': The server's unique ID for the queue.&lt;br /&gt;
** '''action''': One of add/update/remove.&lt;br /&gt;
** '''display_name''': The text to show in the list of queues in the lobby. Only used by add/update.&lt;br /&gt;
** '''players_required''': How many players are required before a game is started. Only used by add/update.&lt;br /&gt;
&lt;br /&gt;
== Administrative commands ==&lt;br /&gt;
* '''[query]'''&lt;br /&gt;
** '''type''': The type of query. See [[ServerAdministration]] for details.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
[https://github.com/renom/fastbot fastbot] -  the bot for tournaments which implements the protocol, can log in into the lobby and host games. Written in Go. &lt;br /&gt;
[[Category:WML Reference]]&lt;br /&gt;
[[Category:Server Documentation]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=MultiplayerServerWML&amp;diff=74857</id>
		<title>MultiplayerServerWML</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=MultiplayerServerWML&amp;diff=74857"/>
		<updated>2026-02-22T14:46:00Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page describes the [[WML]] used to communicate with the multiplayer server for Wesnoth, [[wesnothd]].&lt;br /&gt;
&lt;br /&gt;
== The handshake ==&lt;br /&gt;
&lt;br /&gt;
The client sends four bytes, then the server replies with four bytes. To get a new connection number, the client will send these four bytes: 0x00 0x00 0x00 0x00. The server then sends back the connection number (wesnothd calls this number the &amp;quot;socket number&amp;quot;). Since 1.13+ the server no longer is using socket numbers to keep track of clients and always sends the same number to them all. Since 1.15+ client can also send 0x00 0x00 0x00 0x01 instead to request entire connection to be [https://github.com/wesnoth/wesnoth/blob/2f8136951cd77526188cf8d0fb2cf21eaa2ebe63/src/server/common/server_base.hpp#L60-L76 encapsulated in TLS] immediately '''after'''. If the handshake is successful, the server will be the first to send a data package. All packages are in [http://en.wikipedia.org/wiki/Gzip gzip] format and are preceded by four bytes that specify the size of the package to come in '''big-endian''' (network byte order). Below you'll find information about what data the (unzipped) packages contain. Unpacked WML uses utf-8 charset.&lt;br /&gt;
&lt;br /&gt;
== The login procedure ==&lt;br /&gt;
&lt;br /&gt;
* server request (optional)&lt;br /&gt;
** '''[version]'''&lt;br /&gt;
&lt;br /&gt;
* client response&lt;br /&gt;
** '''[version]'''&lt;br /&gt;
*** '''version''': The client's version string.&lt;br /&gt;
*** '''client_source''': The client's distribution info. (Steam, SourceForge, App Store, etc.)&lt;br /&gt;
&lt;br /&gt;
* server response (if the server does not accept this version)&lt;br /&gt;
** '''[redirect]'''&lt;br /&gt;
*** '''host''': The host you should connect to.&lt;br /&gt;
*** '''port''': The port you should connect to.&lt;br /&gt;
*** '''version''': A comma-separated list of globs that this server should accept (e.g. &amp;quot;1.0*,1.2*,1.4*,1.7*,1.8*&amp;quot;)&lt;br /&gt;
** or '''[reject]''' (if the version is unknown)&lt;br /&gt;
*** '''accepted_versions''': A comma-separated list of globs that this server does accept&lt;br /&gt;
&lt;br /&gt;
* server request&lt;br /&gt;
** '''[mustlogin]'''&lt;br /&gt;
&lt;br /&gt;
* client response&lt;br /&gt;
** '''[login]'''&lt;br /&gt;
*** '''username''': The username the client would like to have.&lt;br /&gt;
*** '''password''': The hashed password, created from the password and salt received from the server. More information about how this password is being generated, including a real world example, can be found in the file [http://forum.wesnoth.org/download/file.php?id=41145 HashedPasswords.pdf] (885 KiB). Since version 1.15+ if TLS was successfully established before then password will be passed as is, without hashing, relying on TLS for secrecy. Passing password hashes is no longer supported to free the client from responsibility to support all hash schemes the forum can potentially use. Client will emit error instead of trying to send password if TLS wasn't established.&lt;br /&gt;
&lt;br /&gt;
* server response&lt;br /&gt;
** '''[join_lobby]'''&lt;br /&gt;
*** '''is_moderator''': &amp;quot;yes&amp;quot; if the user is a moderator, &amp;quot;no&amp;quot; otherwise.&lt;br /&gt;
*** '''profile_url_prefix''': The external URL prefix for player profiles (empty if the server doesn't have an attached database)&lt;br /&gt;
** or '''[error]''' (server is waiting for another '''[login]''' message now)&lt;br /&gt;
*** '''message''': The error message.&lt;br /&gt;
*** '''password_request''': If not empty the server asks the client to provide a password for its desired username.&lt;br /&gt;
*** '''phpbb_encryption''': If &amp;quot;yes&amp;quot; the client will encrypt the password using phpbb's algorithm.&lt;br /&gt;
*** '''random_salt''': Random salt sent to the client for mixing with the password hash.&lt;br /&gt;
*** '''hash_seed''': Salt generated from the original hash that is required to recreate it.&lt;br /&gt;
*** '''salt''': Salt generated from the original hash that is required to recreate it.&lt;br /&gt;
*** '''force_confirmation''': Display an ok/cancel dialog with the content of the 'message' key.&lt;br /&gt;
&lt;br /&gt;
* server response&lt;br /&gt;
** '''[gamelist]'''&lt;br /&gt;
*** '''[game]''' (repeated)&lt;br /&gt;
**** '''id''': A unique id of the game.&lt;br /&gt;
**** '''name''': The title of the game.&lt;br /&gt;
**** '''mp_scenario''': The id of the scenario.&lt;br /&gt;
**** '''mp_era''': The id of the used era.&lt;br /&gt;
**** '''mp_use_map_settings''': Does the game use the map settings specified in the scenario.&lt;br /&gt;
**** '''mp_fog''': Does the game use fog.&lt;br /&gt;
**** '''mp_shroud''': Does the game use shroud.&lt;br /&gt;
**** '''mp_village_gold''': The number of gold per village.&lt;br /&gt;
**** '''experience_modifier''': The experience setting.&lt;br /&gt;
**** '''mp_countdown''': Does the game use a timer.&lt;br /&gt;
**** '''mp_countdown_reservoir_time''': Upper limit of the possibly available time.&lt;br /&gt;
**** '''mp_countdown_init_time''': Initial time.&lt;br /&gt;
**** '''mp_countdown_action_bonus''': Time bonus per action.&lt;br /&gt;
**** '''mp_countdown_turn_bonus''': Time bonus per turn.&lt;br /&gt;
**** '''map_data''': The map data. ''Notice: not sent to lobby if the game uses shroud''&lt;br /&gt;
**** '''hash''': The hash value of the map_data.&lt;br /&gt;
**** '''observer''': Are observers allowed or not.&lt;br /&gt;
**** '''human_sides''': The number of sides played by humans.&lt;br /&gt;
**** '''slots''': The number of vacant/max slots.&lt;br /&gt;
**** '''[slot_data]''' replaces '''slots''' since {{DevFeature1.13|12}}&lt;br /&gt;
***** '''max''': The number of total slots.&lt;br /&gt;
***** '''vacant''': The number of vacant slots.&lt;br /&gt;
**** '''turn''': The current turn/max turn.&lt;br /&gt;
**** '''[turn_data]''' replaces '''turn''' since {{DevFeature1.13|12}}&lt;br /&gt;
***** '''current''': The current turn number.&lt;br /&gt;
***** '''max''': The total number of turns.&lt;br /&gt;
**** '''[modification]''' Modifications used in this game. See [[ModificationWML]].&lt;br /&gt;
***** '''id''': ID of the modification.&lt;br /&gt;
***** '''name''': Name of the modification.&lt;br /&gt;
***** '''addon_id''': ID of the addon the modification is from.&lt;br /&gt;
***** '''require_modification''': A boolean value; if set to yes, all players have to have this modification installed to join the game.&lt;br /&gt;
**** '''[options]''' Options selected for this game. See [[OptionWML]].&lt;br /&gt;
***** '''[campaign|era|modification|multiplayer]'''&lt;br /&gt;
****** '''id''': ID of the addon the campaign|era|modification|multiplayer (scenario) is from.&lt;br /&gt;
****** '''[option]'''&lt;br /&gt;
******* '''id''': ID of the option.&lt;br /&gt;
******* '''value''': Value of the option.&lt;br /&gt;
** '''[user]''' (repeated)&lt;br /&gt;
*** '''name''': The username of the player.&lt;br /&gt;
*** '''game_id''': The ID of the game the player is in.&lt;br /&gt;
*** '''location''': The name of the game the player is in.&lt;br /&gt;
*** '''available''': &amp;quot;yes&amp;quot; if the player is in the lobby; &amp;quot;no&amp;quot; if in a game.&lt;br /&gt;
Many of the keys under [game] are described more indepth on the [[ScenarioWML]] page.&lt;br /&gt;
&lt;br /&gt;
== Error messages ==&lt;br /&gt;
&lt;br /&gt;
* '''[error]'''&lt;br /&gt;
** '''message''': The error message.&lt;br /&gt;
** '''password_request''': This is a response to a login attempt. The client needs to send a password on another login attempt.&lt;br /&gt;
** '''force_confirmation''': Confirmation to login even if there is an existing client with the same name. If login is continued then that existing client is getting kicked.&lt;br /&gt;
&lt;br /&gt;
== Chat (lobby and in-game) ==&lt;br /&gt;
&lt;br /&gt;
* '''[message]'''&lt;br /&gt;
** '''sender''': (optional - filled by the server) The sender of the message.&lt;br /&gt;
** '''message''': The message itself.&lt;br /&gt;
** '''room''': The room the message is from/to&lt;br /&gt;
* '''[whisper]'''&lt;br /&gt;
** '''receiver''': The receiver of the whisper&lt;br /&gt;
** '''sender''': (optional - filled by the server) The sender of the whisper.&lt;br /&gt;
** '''message''': The message itself.&lt;br /&gt;
&lt;br /&gt;
== Nickname registration related commands (lobby and in-game) ==&lt;br /&gt;
&lt;br /&gt;
* '''[nickserv]'''&lt;br /&gt;
** '''[info]''': Request info about another username.&lt;br /&gt;
*** '''name''': The username.&lt;br /&gt;
&lt;br /&gt;
== Updating the lobby state ==&lt;br /&gt;
&lt;br /&gt;
* '''[gamelist_diff]''': server message - basically a [[DiffWML|diff]] from two gamelists, which also includes the user list.&lt;br /&gt;
&lt;br /&gt;
* '''[observer]''' or '''[observer_quit]''': server message - players joining([observer_quit] - quitting the lobby &amp;quot;game&amp;quot;)/quitting([observer] - joining the lobby &amp;quot;game&amp;quot;) a game&lt;br /&gt;
** '''name''': Username of the player/observer.&lt;br /&gt;
* '''[refresh_lobby]''': Request the full gamelist.&lt;br /&gt;
&lt;br /&gt;
== Game setup (the phase from creation to start) ==&lt;br /&gt;
To create a game the client sends:&lt;br /&gt;
* '''[create_game]'''&lt;br /&gt;
** '''name''': The title of the game.&lt;br /&gt;
** '''password''': The password to use to join the game.&lt;br /&gt;
** '''ignored''': The list of ignored players from the host.&lt;br /&gt;
** '''auto_hosted''': True if this request is from a bot, false otherwise.&lt;br /&gt;
&lt;br /&gt;
followed by a message with the scenario options as under [game] (see above) plus the scenario data ([time], [era], [side], etc. see [[ScenarioWML]])&lt;br /&gt;
&lt;br /&gt;
* '''[join]'''&lt;br /&gt;
** '''id''': The id of the game.&lt;br /&gt;
** '''observe''': Join the game as an observer.&lt;br /&gt;
&lt;br /&gt;
* '''[scenario_diff]''': [[DiffWML|diff]] of the [[ScenarioWML]] (side changes, etc.)&lt;br /&gt;
&lt;br /&gt;
* '''[start_game]''': sent by the host to start a game&lt;br /&gt;
* '''[leave_game]''': sent by the client when it leaves a game; sent by the server to make a client leave a game&lt;br /&gt;
** '''reason''': optional reason if sent by the server and was initiated by moderator action&lt;br /&gt;
&lt;br /&gt;
== In-game communication ==&lt;br /&gt;
&lt;br /&gt;
Normal scenario communication ([[ReplayWML]]):&lt;br /&gt;
* '''[turn]'''&lt;br /&gt;
** '''[command]''': (repeated) can contain all the tags you can find in a [[ReplayWML|replay]]: [recruit], [move], [end_turn], etc.&lt;br /&gt;
*** '''[speak]'''&lt;br /&gt;
**** '''message''': text of the message&lt;br /&gt;
**** '''id''': the sender&lt;br /&gt;
**** '''team_name''': the name of the team the message is for - empty if it's a public message&lt;br /&gt;
&lt;br /&gt;
Multiplayer specific communication:&lt;br /&gt;
* '''[request_choice]'''&lt;br /&gt;
** '''request_id''': unique ID of the choice request&lt;br /&gt;
** '''[random_seed]''': client requests a random number (used for attacks for example)&lt;br /&gt;
** '''[change_controller_wml]''': change controller request from scenario WML&lt;br /&gt;
*** '''side''': side number&lt;br /&gt;
*** '''old_controller''': old [[SideWML#controller|controller]] value&lt;br /&gt;
*** '''new_controller''': new [[SideWML#controller|controller]] value&lt;br /&gt;
* '''[store_next_scenario]''': sent by the host - the scenario data (see [[ScenarioWML]]) to advance to the next scenario&lt;br /&gt;
* '''[notify_next_scenario]''': sent by the server to tell players that the data for the next scenario is available&lt;br /&gt;
* '''[load_next_scenario]''': sent by the client to request the data for the next scenario&lt;br /&gt;
* '''[next_scenario]''': data for the next scenario (see [[ScenarioWML]]), sent by the server on request&lt;br /&gt;
&lt;br /&gt;
* '''[info]''': sent by the host on game end - info about the game state&lt;br /&gt;
** '''type''': &amp;quot;termination&amp;quot; &lt;br /&gt;
** '''condition''': the termination reason&lt;br /&gt;
&lt;br /&gt;
If a player leaves this is sent to the host for all sides he owned.&lt;br /&gt;
* '''side_drop''': The number of a side that dropped because a player left.&lt;br /&gt;
* '''controller''': The controller of that side. (&amp;quot;ai&amp;quot;, &amp;quot;network&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
Client commands:&lt;br /&gt;
* '''[change_controller]''': a player (un)droids one of his sides or assigns control to someone else (The host can assign control for any side.)&lt;br /&gt;
** '''side''': the side to change controller&lt;br /&gt;
** '''player''': the nickname of the player to take control&lt;br /&gt;
** '''controller''': the new controller: &amp;quot;human&amp;quot; or &amp;quot;human_ai&amp;quot;&lt;br /&gt;
** '''own_side''': &amp;quot;yes&amp;quot;&lt;br /&gt;
* '''[muteall]''': the host mutes/unmutes all observers - toggles&lt;br /&gt;
* '''[mute]''': the host mutes an observer - toggles&lt;br /&gt;
** '''username''': the username of the observer - if not specified the servers returns a list of muted usernames&lt;br /&gt;
* '''[kick]''' or '''[ban]''': the host kicks/bans a player/observer&lt;br /&gt;
** '''username''': the username of the player/observer&lt;br /&gt;
&lt;br /&gt;
== Game history ==&lt;br /&gt;
This is a request to query a set of 11 rows of game history data based on the provided search criteria. The official client calls this from the Match History button in the  multiplayer lobby to display 10 rows of data. The 11th row is used as a flag to indicate whether there is more data to be queried or not via the right/left arrows on the dialog.&lt;br /&gt;
&lt;br /&gt;
* '''[game_history_request]'''&lt;br /&gt;
** '''offset''': where in the result set to start returning data from. If there are 50 results and offset 10 is given, then rows 10-21 will be returned.&lt;br /&gt;
** '''search_player''': the forum username of the player to search for.&lt;br /&gt;
** '''search_game_name''': the name of the game to filter results by. Can use the * (matches any character before or after it's used) and _ (matches any single character) wildcards.&lt;br /&gt;
** '''search_content_type''': the type of content to filter by. Must be one of:&lt;br /&gt;
*** '''0''': scenario&lt;br /&gt;
*** '''1''': era&lt;br /&gt;
*** '''2''':modification&lt;br /&gt;
** '''search_content''': The content to filter by. This is the ID of the content, not the name displayed on the UI, due to the translated name getting stored in the database.&lt;br /&gt;
&lt;br /&gt;
== Queues ==&lt;br /&gt;
Queue info sent to the client on join or when the server's config is reloaded and the queue information has changed:&lt;br /&gt;
* '''[queue_update]'''&lt;br /&gt;
** '''queue_id''': The server's unique ID for the queue.&lt;br /&gt;
** '''action''': One of add/update/remove.&lt;br /&gt;
** '''display_name''': The text to show in the list of queues in the lobby.&lt;br /&gt;
** '''players_required''': How many players are required before a game is started.&lt;br /&gt;
&lt;br /&gt;
== Administrative commands ==&lt;br /&gt;
* '''[query]'''&lt;br /&gt;
** '''type''': The type of query. See [[ServerAdministration]] for details.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
[https://github.com/renom/fastbot fastbot] -  the bot for tournaments which implements the protocol, can log in into the lobby and host games. Written in Go. &lt;br /&gt;
[[Category:WML Reference]]&lt;br /&gt;
[[Category:Server Documentation]]&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=74798</id>
		<title>Template:DevDownload</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=74798"/>
		<updated>2026-02-05T00:41:55Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Development (1.19 branch) ==&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
==== Windows (10 1903 and later) {{{4|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.20 | filename=wesnoth-1.19.20-win64.exe |&lt;br /&gt;
hash=c55a71350f5aab18074a5cc781bc66a56a29086624df85f4e155d5a1a4e966f9}}&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.19 | filename=wesnoth-1.19.19-win64.exe |&lt;br /&gt;
hash=1d2529daa2c585579176a88655133297b7e2b4ca634f45468bc2f13866604565}}&lt;br /&gt;
&lt;br /&gt;
==== macOS (10.13 and later) {{{5|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.20 | filename=Wesnoth_1.19.20.dmg |&lt;br /&gt;
hash=9a0df54edbbffb503f527a1b8007b7860eace3cfb9b94207c2ad21047171eb94}}&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.19 | filename=Wesnoth_1.19.19.dmg |&lt;br /&gt;
hash=ac733dd52d01026448e7127b48eedfe69ad4b0d0c6089a367592c6d2d1c8d3fc}}&lt;br /&gt;
&lt;br /&gt;
==== Source code ====&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/blob/master/INSTALL.md Compiling Wesnoth] - How to compile the source code&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.20 | filename=wesnoth-1.19.20.tar.bz2 |&lt;br /&gt;
hash=48f883f8cd3ea008f9170aeaa9fb9ebb486202dd72f455ae131363fc3d164f8f}}&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.19 | filename=wesnoth-1.19.19.tar.bz2 |&lt;br /&gt;
hash=d4fba3ecc1a92293f90d623edfef97be7ed6abe963bdcd7134de256fb6977c1d}}&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=74796</id>
		<title>Template:DevDownload</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=74796"/>
		<updated>2026-02-01T17:57:57Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Development (1.19 branch) ==&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
==== Windows (10 1903 and later) {{{4|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.20 | filename=wesnoth-1.19.20-win64.exe |&lt;br /&gt;
hash=c55a71350f5aab18074a5cc781bc66a56a29086624df85f4e155d5a1a4e966f9}}&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.19 | filename=wesnoth-1.19.19-win64.exe |&lt;br /&gt;
hash=1d2529daa2c585579176a88655133297b7e2b4ca634f45468bc2f13866604565}}&lt;br /&gt;
&lt;br /&gt;
==== macOS (10.13 and later) {{{5|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.19 | filename=Wesnoth_1.19.19.dmg |&lt;br /&gt;
hash=ac733dd52d01026448e7127b48eedfe69ad4b0d0c6089a367592c6d2d1c8d3fc}}&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.18 | filename=Wesnoth_1.19.18.dmg |&lt;br /&gt;
hash=e3c7490086c6d4200c991e54e541eb830ce2252a453a10ce07dfc0913fd3df23}}&lt;br /&gt;
&lt;br /&gt;
==== Source code ====&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/blob/master/INSTALL.md Compiling Wesnoth] - How to compile the source code&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.20 | filename=wesnoth-1.19.20.tar.bz2 |&lt;br /&gt;
hash=48f883f8cd3ea008f9170aeaa9fb9ebb486202dd72f455ae131363fc3d164f8f}}&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.19 | filename=wesnoth-1.19.19.tar.bz2 |&lt;br /&gt;
hash=d4fba3ecc1a92293f90d623edfef97be7ed6abe963bdcd7134de256fb6977c1d}}&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=74693</id>
		<title>Template:DevDownload</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Template:DevDownload&amp;diff=74693"/>
		<updated>2025-12-31T01:45:23Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Development (1.19 branch) ==&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
==== Windows (10 1903 and later) {{{4|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.19 | filename=wesnoth-1.19.19-win64.exe |&lt;br /&gt;
hash=1d2529daa2c585579176a88655133297b7e2b4ca634f45468bc2f13866604565}}&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.18 | filename=wesnoth-1.19.18-win64.exe |&lt;br /&gt;
hash=3bea895b5ac6520af3c7217283afcc7e17eb96924c3859e559fd04adb35f6fe8}}&lt;br /&gt;
&lt;br /&gt;
==== macOS (10.13 and later) {{{5|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.19 | filename=Wesnoth_1.19.19.dmg |&lt;br /&gt;
hash=ac733dd52d01026448e7127b48eedfe69ad4b0d0c6089a367592c6d2d1c8d3fc}}&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.18 | filename=Wesnoth_1.19.18.dmg |&lt;br /&gt;
hash=e3c7490086c6d4200c991e54e541eb830ce2252a453a10ce07dfc0913fd3df23}}&lt;br /&gt;
&lt;br /&gt;
==== Source code ====&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/blob/master/INSTALL.md Compiling Wesnoth] - How to compile the source code&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.19 | filename=wesnoth-1.19.19.tar.bz2 |&lt;br /&gt;
hash=d4fba3ecc1a92293f90d623edfef97be7ed6abe963bdcd7134de256fb6977c1d}}&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth |&lt;br /&gt;
version=1.19.18 | filename=wesnoth-1.19.18.tar.bz2 |&lt;br /&gt;
hash=22c788998a7555def490ca559844a8721029e25a5529d8584dfbc15f2a579b72}}&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.wesnoth.org/index.php?title=Template:StableDownload&amp;diff=74692</id>
		<title>Template:StableDownload</title>
		<link rel="alternate" type="text/html" href="https://wiki.wesnoth.org/index.php?title=Template:StableDownload&amp;diff=74692"/>
		<updated>2025-12-31T01:19:29Z</updated>

		<summary type="html">&lt;p&gt;Pentarctagon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Stable (1.18 branch) ==&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
==== Windows (10 1903 and later, 64-bit only) {{{4|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth-1.18 |&lt;br /&gt;
version=1.18.6 | filename=wesnoth-1.18.6-win64.exe |&lt;br /&gt;
hash=7365efdc70c127f6d0bfbc6a8f976e6cba1eec59f2bbfb6dc27644534670b162}}&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth-1.18 |&lt;br /&gt;
version=1.18.5 | filename=wesnoth-1.18.5-win64.exe |&lt;br /&gt;
hash=915d9c0374221782fbfaecd3ac8d8833b470c6ed6c059e6086d5082a28edf80f}}&lt;br /&gt;
&lt;br /&gt;
==== macOS (10.12 and later) {{{5|}}} ====&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth-1.18 |&lt;br /&gt;
version=1.18.6 | filename=Wesnoth_1.18.6.dmg |&lt;br /&gt;
hash=1b9a0ba71c11a386ea0daef357cb508f5c9dc792eed71eff1a3783c056214c93}}&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth-1.18 |&lt;br /&gt;
version=1.18.5 | filename=Wesnoth_1.18.5.dmg |&lt;br /&gt;
hash=73e34ee58a7c81055bdf94f1b0646a524a992235682ce227fb8d3829af4061f1}}&lt;br /&gt;
&lt;br /&gt;
==== Source code ====&lt;br /&gt;
* [https://github.com/wesnoth/wesnoth/blob/master/INSTALL.md Compiling Wesnoth] - How to compile the source code&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth-1.18 |&lt;br /&gt;
version=1.18.6 | filename=wesnoth-1.18.6.tar.bz2 |&lt;br /&gt;
hash=6bb8b17854c974bc66cb7a2574a53fe9efb4d2c138bb1373032e57788204985e}}&lt;br /&gt;
* {{DownloadItem | label={{{1|Current Version}}} | group=wesnoth-1.18 |&lt;br /&gt;
version=1.18.5 | filename=wesnoth-1.18.5.tar.bz2 |&lt;br /&gt;
hash=e15db3caf446d91d389fc275f10c1a9e7ca3c6176c3b8ce94f5ee4a7a0c81bd6}}&lt;/div&gt;</summary>
		<author><name>Pentarctagon</name></author>
		
	</entry>
</feed>