/**
 *	UI Lib
 *
 *	Available modules:
 *	- TimeGap -> display the time gap between the players
 *	- SmallScoresTable -> display the player ranking at the end of the round
 *	- Chrono -> display a chrono at the bottom of the screen
 *	- CheckpointTime -> time displayed when crossing a checkpoint
 *	- SpeedAndDistance -> display the car speed and distance travelled
 *	- Countdown -> display the time remaining before the end of the map
 *	- Laps -> display the number of laps to do and the number of laps done
 *	- PrevBestTime -> display the previous time and the best time
 *	- MapRanking -> display the player's ranking on the map
 *	- CheckpointRanking -> display the player's ranking at the latest checkpoint
 *	- MapInfo -> display informations about the map
 *	- LiveInfo -> display informations about live events
 *	- SpectatorInfo -> display informations about the player being spectated
 *	- ViewersCount -> display the number of players currently spectating us
 */
			
#Const Version		"2017-12-20"
#Const ScriptName	"UI.Script.txt"

// ---------------------------------- //
// Libraries
// ---------------------------------- //
#Include "TextLib" as TL
#Include "Libs/Nadeo/Semver.Script.txt" as Semver
#Include "Libs/Nadeo/XmlRpc2.Script.txt" as XmlRpc
#Include "Libs/Nadeo/TrackMania/WarmUp3.Script.txt" as WarmUp
#Include "ManiaApps/Nadeo/TrackMania/UI_Server.Script.txt" as UI_Server
#Include "ManiaApps/Nadeo/TrackMania/UI_Common.Script.txt" as UI_Common

// ---------------------------------- //
// Constants
// ---------------------------------- //
// Deprecated positions, use the ones in UI_Common instead
#Const C_LibUI_TimeGapPos						<48., -52., 5.>
#Const C_LibUI_SmallScoresTablePos		<-158.5, 40., 150.>
#Const C_LibUI_ChronoPos							<0., -80., -5.>
#Const C_LibUI_CheckpointTimePos			<0., 3., -10.>
#Const C_LibUI_PrevBestTimePos				<157., -24., 5.>
#Const C_LibUI_SpeedAndDistancePos		<137., -69., 5.>
#Const C_LibUI_CountdownPos					<153., -7., 5.>
#Const C_LibUI_LapsPos								<140., 84., 5.>
#Const C_LibUI_MapRankingPos					<150.5, -28., 5.>
#Const C_LibUI_CheckpointRankingPos	<0., 84., 5.>
#Const C_LibUI_MapInfoPos						<-160., 80., 150.>
#Const C_LibUI_LiveInfoPos						<-159., 84., 5.>
#Const C_LibUI_SpectatorInfoPos			<0., -68., 5.>
// XmlRpc
#Const C_Callback_Properties "Trackmania.UI.Properties"
#Const C_Method_GetProperties "Trackmania.UI.GetProperties"
#Const C_Method_GetDefaultProperties "Trackmania.UI.GetDefaultProperties"
#Const C_Method_SetProperties "Trackmania.UI.SetProperties"
#Const C_Method_ResetProperties "Trackmania.UI.ResetProperties"
#Const C_Callback_Property "Trackmania.UI.Property"
#Const C_Method_GetProperty "Trackmania.UI.GetProperty"
#Const C_Method_GetDefaultProperty "Trackmania.UI.GetDefaultProperty"
#Const C_Method_SetProperty "Trackmania.UI.SetProperty"
#Const C_Method_ResetProperty "Trackmania.UI.ResetProperty"
/// Formats
#Const C_Format_Xml	0
#Const C_Format_Json	1

// ---------------------------------- //
// Globales
// ---------------------------------- //
declare Text[] G_LibUI_ModulesLoaded;
declare Ident[Text] G_LibUI_LayersIds;
declare Boolean[Text] G_LibUI_ModuleVisibility;
declare Vec3[Text] G_LibUI_ModulePosition;
declare Integer G_LibUI_PrevCutOffTimeLimit;

// ---------------------------------- //
// Functions
// ---------------------------------- //
// ---------------------------------- //
// Private
// ---------------------------------- //
// ---------------------------------- //
/** Check if a module is loaded
 *
 *	@param	_ModuleId									The module to check
 *
 *	@return														True if the given module is loaded, False otherwise
 */
Boolean Private_ModuleIsLoaded(Text _ModuleId) {
	return (G_LibUI_ModulesLoaded.exists(_ModuleId));
}

// ---------------------------------- //
/** Convert a Boolean to a Text
 *
 *	@param	_Boolean									The Boolean to convert
 *
 *	@return														The Text
 */
Text Private_BooleanToText(Boolean _Boolean) {
	if (_Boolean) return "true";
	return "false";
}

// ---------------------------------- //
/** Convert a Vec2 to a Text
 *
 *	@param	_Vec2											The Vec2 to convert
 *
 *	@return														The Text
 */
Text Private_Vec2ToText(Vec2 _Vec2) {
	return _Vec2.X^" "^_Vec2.Y;
}

// ---------------------------------- //
/** Convert a Vec3 to a Text
 *
 *	@param	_Vec3											The Vec3 to convert
 *
 *	@return														The Text
 */
Text Private_Vec3ToText(Vec3 _Vec3) {
	return _Vec3.X^" "^_Vec3.Y^" "^_Vec3.Z;
}

// ---------------------------------- //
/** Check if the scores table module is visible
 *
 *	@return														The scores table module visibility if found
 *																		True otherwise
 */
Boolean Private_ScoresTableModuleIsVisible() {
	if (Hud != Null && Hud.ScoresTable != Null) {
		//return Hud.ScoresTable.IsVisible(UIManager.UIAll);
		declare LIbUI2_ScoreTableOnlyManialink for This = UIManager.UIAll.ScoreTableOnlyManialink;
		return LIbUI2_ScoreTableOnlyManialink;
	} else {
		return !UIManager.UIAll.ScoreTableOnlyManialink;
	}
	return True;
}

// ---------------------------------- //
/** Hide or show the scores table module
 *
 *	@param	_Visible									True to show
 *																		False to hide
 */
Void Private_ScoresTableModuleSetVisibility(Boolean _Visible) {
	if (Hud != Null && Hud.ScoresTable != Null) {
		// Module visibility
		if (_Visible) Hud.ScoresTable.Show();
		else Hud.ScoresTable.Hide();
		// Save module visibility
		declare LIbUI2_ScoreTableOnlyManialink for This = UIManager.UIAll.ScoreTableOnlyManialink;
		LIbUI2_ScoreTableOnlyManialink = _Visible;
		// Force hide the default scores table
		UIManager.UIAll.ScoreTableOnlyManialink = True;
	} else {
		UIManager.UIAll.ScoreTableOnlyManialink = !_Visible;
	}
}

// ---------------------------------- //
// Public
// ---------------------------------- //
// ---------------------------------- //
/** Return the version number of the script
 *
 *	@return														The version number of the script
 */
Text GetScriptVersion() {
	return Version;
}

// ---------------------------------- //
/** Return the name of the script
 *
 *	@return														The name of the script
 */
Text GetScriptName() {
	return ScriptName;
}

// ---------------------------------- //
/** Display a module
 *
 *	@param	_ModuleName								The name of the module to set
 *	@param	_Display									Show or not the module
 */
Void SetModuleVisibility(Text _ModuleName, Boolean _Display) {
	if (Private_ModuleIsLoaded(_ModuleName)) {
		switch (_ModuleName) {
			case UI_Common::C_Module_PrevBestTime			: UIManager.UIAll.OverlayHidePersonnalBestAndRank = True;
			case UI_Common::C_Module_TimeGap						: UIManager.UIAll.OverlayHideCheckPointList = True;
			case UI_Common::C_Module_SmallScoresTable	: UIManager.UIAll.OverlayHideRoundScores = True;
			case UI_Common::C_Module_Chrono						: UIManager.UIAll.OverlayHideChrono = True;
			case UI_Common::C_Module_CheckpointTime		: UIManager.UIAll.OverlayHideCheckPointTime = True;
			case UI_Common::C_Module_SpeedAndDistance	: UIManager.UIAll.OverlayHideSpeedAndDist = True;
			case UI_Common::C_Module_Countdown					: UIManager.UIAll.OverlayHideCountdown = True;
			case UI_Common::C_Module_Laps							: UIManager.UIAll.OverlayHideMultilapInfos = True;
			case UI_Common::C_Module_MapRanking				: UIManager.UIAll.OverlayHidePosition = True;
			//case UI_Common::C_Module_CheckpointRanking: UIManager.UIAll.OverlayHideMultilapInfos = True;
			case UI_Common::C_Module_MapInfo						: UIManager.UIAll.OverlayHideMapInfo = True;
			//case UI_Common::C_Module_LiveInfo				:
			case UI_Common::C_Module_SpectatorInfo			: UIManager.UIAll.OverlayHideSpectatorInfos = True;
			//case UI_Common::ViewersCount							:
		}
		UI_Server::UpdateSetting(_ModuleName^"_Display", TL::ToText(_Display));
	} else {
		switch (_ModuleName) {
			case UI_Common::C_Module_PrevBestTime			: UIManager.UIAll.OverlayHidePersonnalBestAndRank = !_Display;
			case UI_Common::C_Module_TimeGap						: UIManager.UIAll.OverlayHideCheckPointList = !_Display;
			case UI_Common::C_Module_SmallScoresTable	: UIManager.UIAll.OverlayHideRoundScores = !_Display;
			case UI_Common::C_Module_Chrono						: UIManager.UIAll.OverlayHideChrono = !_Display;
			case UI_Common::C_Module_CheckpointTime		: UIManager.UIAll.OverlayHideCheckPointTime = !_Display;
			case UI_Common::C_Module_SpeedAndDistance	: UIManager.UIAll.OverlayHideSpeedAndDist = !_Display;
			case UI_Common::C_Module_Countdown					: UIManager.UIAll.OverlayHideCountdown = !_Display;
			case UI_Common::C_Module_Laps							: UIManager.UIAll.OverlayHideMultilapInfos = !_Display;
			case UI_Common::C_Module_MapRanking				: UIManager.UIAll.OverlayHidePosition = !_Display;
			//case UI_Common::C_Module_CheckpointRanking: UIManager.UIAll.OverlayHideMultilapInfos = !_Display;
			case UI_Common::C_Module_MapInfo						: UIManager.UIAll.OverlayHideMapInfo = !_Display;
			//case UI_Common::C_Module_LiveInfo				: 
			case UI_Common::C_Module_SpectatorInfo			: UIManager.UIAll.OverlayHideSpectatorInfos = !_Display;
			//case UI_Common::C_Module_ViewersCount		:
		}
	}
	G_LibUI_ModuleVisibility[_ModuleName] = _Display;
}

// ---------------------------------- //
/** Get the visibility of a module
 *
 *	@param	_ModuleName								The name of the module to get
 *
 *	@return														True if the module is visible, False otherwise
 */
Boolean GetModuleVisibility(Text _ModuleName) {
	if (Private_ModuleIsLoaded(_ModuleName) && G_LibUI_ModuleVisibility.existskey(_ModuleName)) {
		return G_LibUI_ModuleVisibility[_ModuleName];
	}
	
	switch (_ModuleName) {
		case UI_Common::C_Module_PrevBestTime			: return !UIManager.UIAll.OverlayHidePersonnalBestAndRank;
		case UI_Common::C_Module_TimeGap						: return !UIManager.UIAll.OverlayHideCheckPointList;
		case UI_Common::C_Module_SmallScoresTable	: return !UIManager.UIAll.OverlayHideRoundScores;
		case UI_Common::C_Module_Chrono						: return !UIManager.UIAll.OverlayHideChrono;
		case UI_Common::C_Module_CheckpointTime		: return !UIManager.UIAll.OverlayHideCheckPointTime;
		case UI_Common::C_Module_SpeedAndDistance	: return !UIManager.UIAll.OverlayHideSpeedAndDist;
		case UI_Common::C_Module_Countdown					: return !UIManager.UIAll.OverlayHideCountdown;
		case UI_Common::C_Module_Laps							: return !UIManager.UIAll.OverlayHideMultilapInfos;
		case UI_Common::C_Module_MapRanking				: return !UIManager.UIAll.OverlayHidePosition;
		//case UI_Common::C_Module_CheckpointRanking: return !UIManager.UIAll.OverlayHideMultilapInfos;
		case UI_Common::C_Module_MapInfo						: return !UIManager.UIAll.OverlayHideMapInfo;
		case UI_Common::C_Module_LiveInfo					: return False;
		case UI_Common::C_Module_SpectatorInfo			: return !UIManager.UIAll.OverlayHideSpectatorInfos;
		//case UI_Common::C_Module_ViewersCount		:
	}
	
	return False;
}

// ---------------------------------- //
/** Set the position of a module
 *
 *	@param	_ModuleName								The name of the module to set
 *	@param	_Pos											The new position of the module
 */
Void SetModulePosition(Text _ModuleName, Vec3 _Pos) {
	if (Private_ModuleIsLoaded(_ModuleName)) {
		UI_Server::UpdateSetting(_ModuleName^"_Position", Private_Vec3ToText(_Pos));
	}
	G_LibUI_ModulePosition[_ModuleName] = _Pos;
}

// ---------------------------------- //
/** Get the position of a module
 *
 *	@param	_ModuleName								The name of the module to get
 *
 *	@return														The position of the module
 */
Vec3 GetModulePosition(Text _ModuleName) {
	if (Private_ModuleIsLoaded(_ModuleName) && G_LibUI_ModulePosition.existskey(_ModuleName)) {
		return G_LibUI_ModulePosition[_ModuleName];
	}
	
	switch (_ModuleName) {
		case UI_Common::C_Module_PrevBestTime			: return UI_Common::C_Position_PrevBestTime;
		case UI_Common::C_Module_TimeGap						: return UI_Common::C_Position_TimeGap;
		case UI_Common::C_Module_SmallScoresTable	: return UI_Common::C_Position_SmallScoresTable;
		case UI_Common::C_Module_Chrono						: return UI_Common::C_Position_Chrono;
		case UI_Common::C_Module_CheckpointTime		: return UI_Common::C_Position_CheckpointTime;
		case UI_Common::C_Module_SpeedAndDistance	: return UI_Common::C_Position_SpeedAndDistance;
		case UI_Common::C_Module_Countdown					: return UI_Common::C_Position_Countdown;
		case UI_Common::C_Module_Laps							: return UI_Common::C_Position_Laps;
		case UI_Common::C_Module_MapRanking				: return UI_Common::C_Position_MapRanking;
		case UI_Common::C_Module_CheckpointRanking	: return UI_Common::C_Position_CheckpointRanking;
		case UI_Common::C_Module_MapInfo						: return UI_Common::C_Position_MapInfo;
		case UI_Common::C_Module_LiveInfo					: return UI_Common::C_Position_LiveInfo;
		case UI_Common::C_Module_SpectatorInfo			: return UI_Common::C_Position_SpectatorInfo;
		case UI_Common::C_Module_ViewersCount			: return UI_Common::C_Position_ViewersCount;
	}
	
	return <0., 0., 0.>;
}

// ---------------------------------- //
/** Send a live event to a player
 *
 *	@param	_Player										The receiver of the live event
 *	@param	_Message									The message
 *	@param	_Image										The path to the image
 */
Void SendLiveEvent(CPlayer _Player, Text _Message, Text _Image) {
	UI_Server::SendLiveEvent(_Player, _Message, _Image);
}

// ---------------------------------- //
/** Show or hide the speed gauge around
 *	the speedometer
 *
 *	@param	_Visible									True to show, False to hide
 */
Void DisplaySpeedGauge(Boolean _Visible) {
	if (_Visible) {
		UI_Server::UpdateSetting(UI_Common::C_Module_SpeedAndDistance^"_DisplaySpeedGauge", "True");
	} else {
		UI_Server::UpdateSetting(UI_Common::C_Module_SpeedAndDistance^"_DisplaySpeedGauge", "False");
	}
}

// ---------------------------------- //
/** Set the time gap mode for the TM time gap module
 *
 *	@param	_Mode											The mode to use between "BestRace" and "CurRace"
 *																		- BestRace: compare the best time of the players
 *																		- CurRace: compare the times of the current race of the players
 */
Void SetTimeGapMode(Text _Mode) {
	if (_Mode != "BestRace" && _Mode != "CurRace") return;
	UI_Server::UpdateSetting(UI_Common::C_Module_TimeGap^"_Mode", _Mode);
}

// ---------------------------------- //
/** Enable or disable the team mode
 *	for the TM time gap module
 *
 *	@param	_OnlyTeam									True to enable
 *																		False to disable
 */
Void SetTimeGapOnlyTeam(Boolean _OnlyTeam) {
	declare OnlyTeam = "0";
	if (_OnlyTeam) OnlyTeam = "1";
	UI_Server::UpdateSetting(UI_Common::C_Module_TimeGap^"_OnlyTeam", OnlyTeam);
}

// ---------------------------------- //
/** Set the time gap mode for the TM time gap module
 *
 *	@param	_Mode											The mode to use between "BestRace" and "CurRace"
 *																		- BestRace: compare the best time of the players
 *																		- CurRace: compare the times of the current race of the players
 */
Void SetCheckpointTimeMode(Text _Mode) {
	if (_Mode != "BestRace" && _Mode != "CurRace") return;
	UI_Server::UpdateSetting(UI_Common::C_Module_CheckpointTime^"_Mode", _Mode);
}

// ---------------------------------- //
/** Display the time diff in the checkpoint time module
 *
 *	@param	_Display									Show or not the time diff
 */
Void DisplayTimeDiff(Boolean _Display) {
	UI_Server::UpdateSetting(UI_Common::C_Module_CheckpointTime^"_DisplayTimeDiff", TL::ToText(_Display));
}

// ---------------------------------- //
/** Display the lap time diff in the checkpoint time module
 *
 *	@param	_Display									Show or not the time diff
 */
Void DisplayLapTimeDiff(Boolean _Display) {
	UI_Server::UpdateSetting(UI_Common::C_Module_CheckpointTime^"_DisplayLapTimeDiff", TL::ToText(_Display));
}

// ---------------------------------- //
/** Set if the mode has IndependantLaps or not
 *
 *	@param	_IndependantLaps					True if the mode has IndependantLaps, false otherwise
 */
Void SetIndependantLaps(Boolean _IndependantLaps) {
	UI_Server::UpdateSetting("IndependantLaps", TL::ToText(_IndependantLaps));
}

// ---------------------------------- //
/** Set the CutOffTimeLimit
 *
 *	@param	_CutOffTimeLimit					The new value of CutOffTimeLimit
 */
Void SetCutOffTimeLimit(Integer _CutOffTimeLimit) {
	UI_Server::UpdateSetting(UI_Common::C_Module_Countdown^"_CutOffTimeLimit", TL::ToText(_CutOffTimeLimit));
}

// ---------------------------------- //
/** Set the number of lines of the chat
 *
 *	@param	_LineCount								The number of lines of the chat
 */
Void SetChatLineCount(Integer _LineCount) {
	if (_LineCount >= 0 && _LineCount <= 40) UIManager.UIAll.OverlayChatLineCount = _LineCount;
	else if (_LineCount < 0) UIManager.UIAll.OverlayChatLineCount = 0;
	else if (_LineCount > 40) UIManager.UIAll.OverlayChatLineCount = 40;
}

// ---------------------------------- //
/** Get the number of lines of the chat
 *
 *	@return														The number of lines of the chat
 */
Integer GetChatLineCount() {
	return UIManager.UIAll.OverlayChatLineCount;
}

// ---------------------------------- //
/** Set the visibility of the UI overlays
 *
 *	@param	_Name											The name of the overlay
 *	@param	_Visible									The visibility of the overlay
 */
Void Private_SetVisibility(Text _Name, Text _Visible) {
	if (_Visible == "") return;
	
	declare Hide = False;
	if (_Visible == "False" || _Visible == "false" || _Visible == "0") Hide = True;
	
	switch (_Name) {
		case UI_Common::C_ModuleXmlRpc_MapInfo 					: SetModuleVisibility(UI_Common::C_Module_MapInfo, !Hide);
		case UI_Common::C_ModuleXmlRpc_LiveInfo  				: SetModuleVisibility(UI_Common::C_Module_LiveInfo, !Hide);
		case UI_Common::C_ModuleXmlRpc_SpectatorInfo 		: SetModuleVisibility(UI_Common::C_Module_SpectatorInfo, !Hide);
		case UI_Common::C_ModuleXmlRpc_OpponentsInfo 		: UIManager.UIAll.OverlayHideOpponentsInfo = Hide;
		case UI_Common::C_ModuleXmlRpc_Chat							: UIManager.UIAll.OverlayHideChat = Hide;
		case UI_Common::C_ModuleXmlRpc_Countdown					: SetModuleVisibility(UI_Common::C_Module_Countdown, !Hide);
		case UI_Common::C_ModuleXmlRpc_Go								: UIManager.UIAll.OverlayHide321Go = Hide;
		case UI_Common::C_ModuleXmlRpc_SpeedAndDistance	: SetModuleVisibility(UI_Common::C_Module_SpeedAndDistance, !Hide);
		case UI_Common::C_ModuleXmlRpc_MapRanking				: SetModuleVisibility(UI_Common::C_Module_MapRanking, !Hide);
		case UI_Common::C_ModuleXmlRpc_ChatAvatar				: UIManager.UIAll.OverlayChatHideAvatar = Hide;
		case UI_Common::C_ModuleXmlRpc_TimeGap						: SetModuleVisibility(UI_Common::C_Module_TimeGap, !Hide);
		case UI_Common::C_ModuleXmlRpc_SmallScoresTable	: SetModuleVisibility(UI_Common::C_Module_SmallScoresTable, !Hide);
		case UI_Common::C_ModuleXmlRpc_Chrono						: SetModuleVisibility(UI_Common::C_Module_Chrono, !Hide);
		case UI_Common::C_ModuleXmlRpc_CheckpointTime		: SetModuleVisibility(UI_Common::C_Module_CheckpointTime, !Hide);
		case UI_Common::C_ModuleXmlRpc_PrevBestTime			: SetModuleVisibility(UI_Common::C_Module_PrevBestTime, !Hide);
		case UI_Common::C_ModuleXmlRpc_WarmUp						: WarmUp::SetUIVisibility(!Hide);
		case UI_Common::C_ModuleXmlRpc_LadderRecap				: UIManager.UIAll.OverlayHideEndMapLadderRecap = Hide;
		case UI_Common::C_ModuleXmlRpc_Laps							: SetModuleVisibility(UI_Common::C_Module_Laps, !Hide);
		case UI_Common::C_ModuleXmlRpc_CheckpointRanking	: SetModuleVisibility(UI_Common::C_Module_CheckpointRanking, !Hide);
		case UI_Common::C_ModuleXmlRpc_ScoresTable				: Private_ScoresTableModuleSetVisibility(!Hide);
		case UI_Common::C_ModuleXmlRpc_ScoresTableAlt		: {
			UIManager.UIAll.AltMenuNoDefaultScores = Hide;
			UIManager.UIAll.AltMenuNoCustomScores = Hide;
		}
		case UI_Common::C_ModuleXmlRpc_ViewersCount			: SetModuleVisibility(UI_Common::C_Module_ViewersCount, !Hide);
	}
}

// ---------------------------------- //
/** Set the visibility of the UI overlays
 *
 *	@param	_Name											The name of the overlay
 *	@param	_Position									The position of the overlay
 */
Void Private_SetPosition(Text _Name, Text _Position) {
	if (_Position == "") return;
	
	declare Vec3 Position;
	declare PositionSplit = TL::Split(" ", _Position);
	if (PositionSplit.existskey(0)) Position.X = TL::ToReal(PositionSplit[0]);
	if (PositionSplit.existskey(1)) Position.Y = TL::ToReal(PositionSplit[1]);
	if (PositionSplit.existskey(2)) Position.Z = TL::ToReal(PositionSplit[2]);
	
	switch (_Name) {
		case UI_Common::C_ModuleXmlRpc_TimeGap						: SetModulePosition(UI_Common::C_Module_TimeGap, Position);
		case UI_Common::C_ModuleXmlRpc_SmallScoresTable	: SetModulePosition(UI_Common::C_Module_SmallScoresTable, Position);
		case UI_Common::C_ModuleXmlRpc_Chrono						: SetModulePosition(UI_Common::C_Module_Chrono, Position);
		case UI_Common::C_ModuleXmlRpc_PrevBestTime			: SetModulePosition(UI_Common::C_Module_PrevBestTime, Position);
		case UI_Common::C_ModuleXmlRpc_CheckpointTime		: SetModulePosition(UI_Common::C_Module_CheckpointTime, Position);
		case UI_Common::C_ModuleXmlRpc_SpeedAndDistance	: SetModulePosition(UI_Common::C_Module_SpeedAndDistance, Position);
		case UI_Common::C_ModuleXmlRpc_Chat							: UIManager.UIAll.OverlayChatOffset = <Position.X, Position.Y>;
		case UI_Common::C_ModuleXmlRpc_Countdown					: SetModulePosition(UI_Common::C_Module_Countdown, Position);
		case UI_Common::C_ModuleXmlRpc_WarmUp						: WarmUp::SetUIPosition(Position);
		case UI_Common::C_ModuleXmlRpc_Laps							: SetModulePosition(UI_Common::C_Module_Laps, Position);
		case UI_Common::C_ModuleXmlRpc_MapRanking				: SetModulePosition(UI_Common::C_Module_MapRanking, Position);
		case UI_Common::C_ModuleXmlRpc_CheckpointRanking	: SetModulePosition(UI_Common::C_Module_CheckpointRanking, Position);
		case UI_Common::C_ModuleXmlRpc_MapInfo 					: SetModulePosition(UI_Common::C_Module_MapInfo, Position);
		case UI_Common::C_ModuleXmlRpc_LiveInfo 					: SetModulePosition(UI_Common::C_Module_LiveInfo, Position);
		case UI_Common::C_ModuleXmlRpc_SpectatorInfo 		: SetModulePosition(UI_Common::C_Module_SpectatorInfo, Position);
		case UI_Common::C_ModuleXmlRpc_ViewersCount			: SetModulePosition(UI_Common::C_Module_ViewersCount, Position);
	}
}

// ---------------------------------- //
/** Get the current UI properties
 *	in the specified format
 *
 *	@param	_Format										The format of the properties
 *
 *	@return		The UI properties
 */
Text Private_GetProperties(Integer _Format) {
	if (_Format == C_Format_Json) {
		declare Append = "";
		if (Semver::Compare(XmlRpc::GetApiVersion(), ">=", "2.3.0")) {
			Append ^= """,
	"{{{UI_Common::C_ModuleXmlRpc_ViewersCount}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_ViewersCount))}}},
		"pos": {{{XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_ViewersCount))}}}
	}""";
		}
		declare ScoresTableVisibility = "";
		if (Semver::Compare(XmlRpc::GetApiVersion(), ">=", "2.4.0")) {
			ScoresTableVisibility = """,
		"visible": {{{XmlRpc::JsonGetBoolean(Private_ScoresTableModuleIsVisible())}}}""";
		}
		
		return """{
	"{{{UI_Common::C_ModuleXmlRpc_MapInfo}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_MapInfo))}}},
		"pos": {{{XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_MapInfo))}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_LiveInfo}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_LiveInfo))}}},
		"pos": {{{XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_LiveInfo))}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_SpectatorInfo}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_SpectatorInfo))}}},
		"pos": {{{XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_SpectatorInfo))}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_OpponentsInfo}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(!UIManager.UIAll.OverlayHideOpponentsInfo)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_Chat}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(!UIManager.UIAll.OverlayHideChat)}}},
		"offset": {{{XmlRpc::JsonGetVec2(UIManager.UIAll.OverlayChatOffset)}}},
		"linecount": {{{XmlRpc::JsonGetInteger(GetChatLineCount())}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_TimeGap}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_TimeGap))}}},
		"pos": {{{XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_TimeGap))}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_SmallScoresTable}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_SmallScoresTable))}}},
		"pos": {{{XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_SmallScoresTable))}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_Countdown}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_Countdown))}}},
		"pos": {{{XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_Countdown))}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_Go}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(!UIManager.UIAll.OverlayHide321Go)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_Chrono}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_Chrono))}}},
		"pos": {{{XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_Chrono))}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_SpeedAndDistance}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_SpeedAndDistance))}}},
		"pos": {{{XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_SpeedAndDistance))}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_PrevBestTime}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_PrevBestTime))}}},
		"pos": {{{XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_PrevBestTime))}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_MapRanking}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_MapRanking))}}},
		"pos": {{{XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_MapRanking))}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_CheckpointTime}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_CheckpointTime))}}},
		"pos": {{{XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_CheckpointTime))}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_ChatAvatar}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(!UIManager.UIAll.OverlayChatHideAvatar)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_WarmUp}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(WarmUp::GetUIVisibility())}}},
		"pos": {{{XmlRpc::JsonGetVec3(WarmUp::GetUIPosition())}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_LadderRecap}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(!UIManager.UIAll.OverlayHideEndMapLadderRecap)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_Laps}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_Laps))}}},
		"pos": {{{XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_Laps))}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_CheckpointRanking}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_CheckpointRanking))}}},
		"pos": {{{XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_CheckpointRanking))}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_ScoresTable}}}": {
		"alt_visible": {{{XmlRpc::JsonGetBoolean(!UIManager.UIAll.AltMenuNoDefaultScores)}}}{{{ScoresTableVisibility}}}
	}{{{Append}}}
}""";
	}
	
	declare Append = "";
	if (Semver::Compare(XmlRpc::GetApiVersion(), ">=", "2.3.0")) {
		Append ^= """
	<{{{UI_Common::C_ModuleXmlRpc_ViewersCount}}} visible="{{{Private_BooleanToText(GetModuleVisibility(UI_Common::C_Module_ViewersCount))}}}" pos="{{{Private_Vec3ToText(GetModulePosition(UI_Common::C_Module_ViewersCount))}}}" />""";
	}
	declare ScoresTableVisibility = "";
	if (Semver::Compare(XmlRpc::GetApiVersion(), ">=", "2.4.0")) {
		ScoresTableVisibility = """visible="{{{Private_BooleanToText(Private_ScoresTableModuleIsVisible())}}}" """;
	}
	
	return """
<ui_properties>
	<{{{UI_Common::C_ModuleXmlRpc_MapInfo}}} visible="{{{Private_BooleanToText(GetModuleVisibility(UI_Common::C_Module_MapInfo))}}}" pos="{{{Private_Vec3ToText(GetModulePosition(UI_Common::C_Module_MapInfo))}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_LiveInfo}}} visible="{{{Private_BooleanToText(GetModuleVisibility(UI_Common::C_Module_LiveInfo))}}}" pos="{{{Private_Vec3ToText(GetModulePosition(UI_Common::C_Module_LiveInfo))}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_SpectatorInfo}}} visible="{{{Private_BooleanToText(GetModuleVisibility(UI_Common::C_Module_SpectatorInfo))}}}" pos="{{{Private_Vec3ToText(GetModulePosition(UI_Common::C_Module_SpectatorInfo))}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_OpponentsInfo}}} visible="{{{Private_BooleanToText(!UIManager.UIAll.OverlayHideOpponentsInfo)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_Chat}}} visible="{{{Private_BooleanToText(!UIManager.UIAll.OverlayHideChat)}}}" offset="{{{Private_Vec2ToText(UIManager.UIAll.OverlayChatOffset)}}}" linecount="{{{GetChatLineCount()}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_TimeGap}}} visible="{{{Private_BooleanToText(GetModuleVisibility(UI_Common::C_Module_TimeGap))}}}" pos="{{{Private_Vec3ToText(GetModulePosition(UI_Common::C_Module_TimeGap))}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_SmallScoresTable}}} visible="{{{Private_BooleanToText(GetModuleVisibility(UI_Common::C_Module_SmallScoresTable))}}}" pos="{{{Private_Vec3ToText(GetModulePosition(UI_Common::C_Module_SmallScoresTable))}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_Countdown}}} visible="{{{Private_BooleanToText(GetModuleVisibility(UI_Common::C_Module_Countdown))}}}" pos="{{{Private_Vec3ToText(GetModulePosition(UI_Common::C_Module_Countdown))}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_Go}}} visible="{{{Private_BooleanToText(!UIManager.UIAll.OverlayHide321Go)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_Chrono}}} visible="{{{Private_BooleanToText(GetModuleVisibility(UI_Common::C_Module_Chrono))}}}" pos="{{{Private_Vec3ToText(GetModulePosition(UI_Common::C_Module_Chrono))}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_SpeedAndDistance}}} visible="{{{Private_BooleanToText(GetModuleVisibility(UI_Common::C_Module_SpeedAndDistance))}}}" pos="{{{Private_Vec3ToText(GetModulePosition(UI_Common::C_Module_SpeedAndDistance))}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_PrevBestTime}}} visible="{{{Private_BooleanToText(GetModuleVisibility(UI_Common::C_Module_PrevBestTime))}}}" pos="{{{Private_Vec3ToText(GetModulePosition(UI_Common::C_Module_PrevBestTime))}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_MapRanking}}} visible="{{{Private_BooleanToText(GetModuleVisibility(UI_Common::C_Module_MapRanking))}}}" pos="{{{Private_Vec3ToText(GetModulePosition(UI_Common::C_Module_MapRanking))}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_CheckpointTime}}} visible="{{{Private_BooleanToText(GetModuleVisibility(UI_Common::C_Module_CheckpointTime))}}}" pos="{{{Private_Vec3ToText(GetModulePosition(UI_Common::C_Module_CheckpointTime))}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_ChatAvatar}}} visible="{{{Private_BooleanToText(!UIManager.UIAll.OverlayChatHideAvatar)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_WarmUp}}} visible="{{{Private_BooleanToText(WarmUp::GetUIVisibility())}}}" pos="{{{Private_Vec3ToText(WarmUp::GetUIPosition())}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_LadderRecap}}} visible="{{{Private_BooleanToText(!UIManager.UIAll.OverlayHideEndMapLadderRecap)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_Laps}}} visible="{{{Private_BooleanToText(GetModuleVisibility(UI_Common::C_Module_Laps))}}}" pos="{{{Private_Vec3ToText(GetModulePosition(UI_Common::C_Module_Laps))}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_CheckpointRanking}}} visible="{{{Private_BooleanToText(GetModuleVisibility(UI_Common::C_Module_CheckpointRanking))}}}" pos="{{{Private_Vec3ToText(GetModulePosition(UI_Common::C_Module_CheckpointRanking))}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_ScoresTable}}} alt_visible="{{{Private_BooleanToText(!UIManager.UIAll.AltMenuNoDefaultScores)}}}" {{{ScoresTableVisibility}}}/>{{{Append}}}
</ui_properties>""";
}

// ---------------------------------- //
/** Get the default UI properties
 *	in the specified format
 *
 *	@param	_Format										The format of the properties
 *
 *	@return		The default UI properties
 */
Text Private_GetDefaultProperties(Integer _Format) {
	if (_Format == C_Format_Json) {
		declare Append = "";
		/*
		if (Semver::Compare(XmlRpc::GetApiVersion(), ">=", "2.3.0")) {
			Append ^= """""";
		}
		*/
		declare ScoresTableVisibility = "";
		if (Semver::Compare(XmlRpc::GetApiVersion(), ">=", "2.4.0")) {
			ScoresTableVisibility = """,
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}}""";
		}
		
		return """{
	"{{{UI_Common::C_ModuleXmlRpc_MapInfo}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}},
		"pos": {{{XmlRpc::JsonGetVec3(UI_Common::C_Position_MapInfo)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_LiveInfo}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}},
		"pos": {{{XmlRpc::JsonGetVec3(UI_Common::C_Position_LiveInfo)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_SpectatorInfo}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}},
		"pos": {{{XmlRpc::JsonGetVec3(UI_Common::C_Position_SpectatorInfo)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_OpponentsInfo}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_Chat}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}},
		"offset": {{{XmlRpc::JsonGetVec2(UI_Common::C_Position_Chat)}}},
		"linecount": {{{XmlRpc::JsonGetInteger(0)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_TimeGap}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}},
		"pos": {{{XmlRpc::JsonGetVec3(UI_Common::C_Position_TimeGap)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_SmallScoresTable}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}},
		"pos": {{{XmlRpc::JsonGetVec3(UI_Common::C_Position_SmallScoresTable)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_Countdown}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}},
		"pos": {{{XmlRpc::JsonGetVec3(UI_Common::C_Position_Countdown)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_Go}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_Chrono}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}},
		"pos": {{{XmlRpc::JsonGetVec3(UI_Common::C_Position_Chrono)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_SpeedAndDistance}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}},
		"pos": {{{XmlRpc::JsonGetVec3(UI_Common::C_Position_SpeedAndDistance)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_PrevBestTime}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}},
		"pos": {{{XmlRpc::JsonGetVec3(UI_Common::C_Position_PrevBestTime)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_MapRanking}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}},
		"pos": {{{XmlRpc::JsonGetVec3(UI_Common::C_Position_MapRanking)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_CheckpointTime}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}},
		"pos": {{{XmlRpc::JsonGetVec3(UI_Common::C_Position_CheckpointTime)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_ChatAvatar}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_WarmUp}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}},
		"pos": {{{XmlRpc::JsonGetVec3(WarmUp::C_LibWU3_LayerPosition)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_LadderRecap}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_Laps}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}},
		"pos": {{{XmlRpc::JsonGetVec3(UI_Common::C_Position_Laps)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_CheckpointRanking}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}},
		"pos": {{{XmlRpc::JsonGetVec3(UI_Common::C_Position_CheckpointRanking)}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_ScoresTable}}}": {
		"alt_visible": {{{XmlRpc::JsonGetBoolean(True)}}}{{{ScoresTableVisibility}}}
	},
	"{{{UI_Common::C_ModuleXmlRpc_ViewersCount}}}": {
		"visible": {{{XmlRpc::JsonGetBoolean(True)}}},
		"pos": {{{XmlRpc::JsonGetVec3(UI_Common::C_Position_ViewersCount)}}}
	}{{{Append}}}
}""";
	}
	
	declare Append = "";
	if (Semver::Compare(XmlRpc::GetApiVersion(), ">=", "2.3.0")) {
		Append ^= """
	<{{{UI_Common::C_ModuleXmlRpc_ViewersCount}}} visible="{{{Private_BooleanToText(True)}}}" pos="{{{Private_Vec3ToText(UI_Common::C_Position_ViewersCount)}}}" />""";
	}
	declare ScoresTableVisibility = "";
	if (Semver::Compare(XmlRpc::GetApiVersion(), ">=", "2.4.0")) {
		ScoresTableVisibility = """visible="{{{Private_BooleanToText(True)}}}" """;
	}
	
	return """
<ui_properties>
	<{{{UI_Common::C_ModuleXmlRpc_MapInfo}}} visible="{{{Private_BooleanToText(True)}}}" pos="{{{Private_Vec3ToText(UI_Common::C_Position_MapInfo)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_LiveInfo}}} visible="{{{Private_BooleanToText(True)}}}" pos="{{{Private_Vec3ToText(UI_Common::C_Position_LiveInfo)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_SpectatorInfo}}} visible="{{{Private_BooleanToText(True)}}}" pos="{{{Private_Vec3ToText(UI_Common::C_Position_SpectatorInfo)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_OpponentsInfo}}} visible="{{{Private_BooleanToText(True)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_Chat}}} visible="{{{Private_BooleanToText(True)}}}" offset="{{{Private_Vec2ToText(UI_Common::C_Position_Chat)}}}" linecount="0" />
	<{{{UI_Common::C_ModuleXmlRpc_TimeGap}}} visible="{{{Private_BooleanToText(True)}}}" pos="{{{Private_Vec3ToText(UI_Common::C_Position_TimeGap)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_SmallScoresTable}}} visible="{{{Private_BooleanToText(True)}}}" pos="{{{Private_Vec3ToText(UI_Common::C_Position_SmallScoresTable)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_Countdown}}} visible="{{{Private_BooleanToText(True)}}}" pos="{{{Private_Vec3ToText(UI_Common::C_Position_Countdown)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_Go}}} visible="{{{Private_BooleanToText(True)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_Chrono}}} visible="{{{Private_BooleanToText(True)}}}" pos="{{{Private_Vec3ToText(UI_Common::C_Position_Chrono)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_SpeedAndDistance}}} visible="{{{Private_BooleanToText(True)}}}" pos="{{{Private_Vec3ToText(UI_Common::C_Position_SpeedAndDistance)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_PrevBestTime}}} visible="{{{Private_BooleanToText(True)}}}" pos="{{{Private_Vec3ToText(UI_Common::C_Position_PrevBestTime)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_MapRanking}}} visible="{{{Private_BooleanToText(True)}}}" pos="{{{Private_Vec3ToText(UI_Common::C_Position_MapRanking)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_CheckpointTime}}} visible="{{{Private_BooleanToText(True)}}}" pos="{{{Private_Vec3ToText(UI_Common::C_Position_CheckpointTime)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_ChatAvatar}}} visible="{{{Private_BooleanToText(True)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_WarmUp}}} visible="{{{Private_BooleanToText(True)}}}" pos="{{{Private_Vec3ToText(WarmUp::C_LibWU3_LayerPosition)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_LadderRecap}}} visible="{{{Private_BooleanToText(True)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_Laps}}} visible="{{{Private_BooleanToText(True)}}}" pos="{{{Private_Vec3ToText(UI_Common::C_Position_Laps)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_CheckpointRanking}}} visible="{{{Private_BooleanToText(True)}}}" pos="{{{Private_Vec3ToText(UI_Common::C_Position_CheckpointRanking)}}}" />
	<{{{UI_Common::C_ModuleXmlRpc_ScoresTable}}} alt_visible="{{{Private_BooleanToText(True)}}}" {{{ScoresTableVisibility}}}/>{{{Append}}}
</ui_properties>""";
}

// ---------------------------------- //
/** Get the current properties xml
 *
 *	@return														The properties xml
 */
Text Private_GetProperties() {
	return Private_GetProperties(C_Format_Xml);
}

// ---------------------------------- //
/** Get the value of one of the 
 *	UI module property
 *
 *	@param	_Name											The name of the module
 *	@param	_Property									The property to update
 */
Text Private_GetProperty(Text _Name, Text _Property) {
	declare Value = XmlRpc::JsonGetText("");
	
	switch (_Name) {
		case UI_Common::C_ModuleXmlRpc_TimeGap: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_TimeGap));
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_TimeGap));
		}
		case UI_Common::C_ModuleXmlRpc_SmallScoresTable: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_SmallScoresTable));
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_SmallScoresTable));
		}
		case UI_Common::C_ModuleXmlRpc_Chrono: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_Chrono));
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_Chrono));
		}
		case UI_Common::C_ModuleXmlRpc_CheckpointTime: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_CheckpointTime));
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_CheckpointTime));
		}
		case UI_Common::C_ModuleXmlRpc_PrevBestTime: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_PrevBestTime));
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_PrevBestTime));
		}
		case UI_Common::C_ModuleXmlRpc_SpeedAndDistance: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_SpeedAndDistance));
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_SpeedAndDistance));
		}
		case UI_Common::C_ModuleXmlRpc_Chat: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(!UIManager.UIAll.OverlayHideChat);
			else if (_Property == "offset") Value = XmlRpc::JsonGetVec2(UIManager.UIAll.OverlayChatOffset);
			else if (_Property == "linecount") Value = XmlRpc::JsonGetInteger(GetChatLineCount());
		}
		case UI_Common::C_ModuleXmlRpc_Countdown: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_Countdown));
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_Countdown));
		}
		case UI_Common::C_ModuleXmlRpc_WarmUp: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(WarmUp::GetUIVisibility());
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(WarmUp::GetUIPosition());
		}
		case UI_Common::C_ModuleXmlRpc_Laps: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_Laps));
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_Laps));
		}
		case UI_Common::C_ModuleXmlRpc_MapRanking: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_MapRanking));
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_MapRanking));
		}
		case UI_Common::C_ModuleXmlRpc_CheckpointRanking: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_CheckpointRanking));
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_CheckpointRanking));
		}
		case UI_Common::C_ModuleXmlRpc_MapInfo: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_MapInfo));
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_MapInfo));
		}
		case UI_Common::C_ModuleXmlRpc_LiveInfo: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_LiveInfo));
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_LiveInfo));
		}
		case UI_Common::C_ModuleXmlRpc_SpectatorInfo: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_SpectatorInfo));
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_SpectatorInfo));
		}
		case UI_Common::C_ModuleXmlRpc_ViewersCount: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(GetModuleVisibility(UI_Common::C_Module_ViewersCount));
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(GetModulePosition(UI_Common::C_Module_ViewersCount));
		}
		case UI_Common::C_ModuleXmlRpc_Go: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(!UIManager.UIAll.OverlayHide321Go);
		}
		case UI_Common::C_ModuleXmlRpc_ChatAvatar: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(!UIManager.UIAll.OverlayChatHideAvatar);
		}
		case UI_Common::C_ModuleXmlRpc_LadderRecap: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(!UIManager.UIAll.OverlayHideEndMapLadderRecap);
		}
		case UI_Common::C_ModuleXmlRpc_ScoresTable: {
			if (_Property == "alt_visible") Value = XmlRpc::JsonGetBoolean(!UIManager.UIAll.AltMenuNoDefaultScores);
			else if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(Private_ScoresTableModuleIsVisible());
		}
		case UI_Common::C_ModuleXmlRpc_ScoresTableAlt: {
			if (_Property == "alt_visible") Value = XmlRpc::JsonGetBoolean(!UIManager.UIAll.AltMenuNoDefaultScores);
		}
		case UI_Common::C_ModuleXmlRpc_OpponentsInfo: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(!UIManager.UIAll.OverlayHideOpponentsInfo);
		}
	}
	
	return Value;
}

// ---------------------------------- //
/** Get the default value of one of the 
 *	UI module property
 *
 *	@param	_Name											The name of the module
 *	@param	_Property									The property to update
 */
Text Private_GetDefaultProperty(Text _Name, Text _Property) {
	declare Value = XmlRpc::JsonGetText("");
	
	switch (_Name) {
		case UI_Common::C_ModuleXmlRpc_TimeGap: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(UI_Common::C_Position_TimeGap);
		}
		case UI_Common::C_ModuleXmlRpc_SmallScoresTable: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(UI_Common::C_Position_SmallScoresTable);
		}
		case UI_Common::C_ModuleXmlRpc_Chrono: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(UI_Common::C_Position_Chrono);
		}
		case UI_Common::C_ModuleXmlRpc_CheckpointTime: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(UI_Common::C_Position_CheckpointTime);
		}
		case UI_Common::C_ModuleXmlRpc_PrevBestTime: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(UI_Common::C_Position_PrevBestTime);
		}
		case UI_Common::C_ModuleXmlRpc_SpeedAndDistance: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(UI_Common::C_Position_SpeedAndDistance);
		}
		case UI_Common::C_ModuleXmlRpc_Chat: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
			else if (_Property == "offset") Value = XmlRpc::JsonGetVec2(UI_Common::C_Position_Chat);
			else if (_Property == "linecount") Value = XmlRpc::JsonGetInteger(0);
		}
		case UI_Common::C_ModuleXmlRpc_Countdown: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(UI_Common::C_Position_Countdown);
		}
		case UI_Common::C_ModuleXmlRpc_WarmUp: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(WarmUp::C_LibWU3_LayerPosition);
		}
		case UI_Common::C_ModuleXmlRpc_Laps: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(UI_Common::C_Position_Laps);
		}
		case UI_Common::C_ModuleXmlRpc_MapRanking: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(UI_Common::C_Position_MapRanking);
		}
		case UI_Common::C_ModuleXmlRpc_CheckpointRanking: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(UI_Common::C_Position_CheckpointRanking);
		}
		case UI_Common::C_ModuleXmlRpc_MapInfo: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(UI_Common::C_Position_MapInfo);
		}
		case UI_Common::C_ModuleXmlRpc_LiveInfo: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(UI_Common::C_Position_LiveInfo);
		}
		case UI_Common::C_ModuleXmlRpc_SpectatorInfo: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(UI_Common::C_Position_SpectatorInfo);
		}
		case UI_Common::C_ModuleXmlRpc_ViewersCount: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
			else if (_Property == "pos") Value = XmlRpc::JsonGetVec3(UI_Common::C_Position_ViewersCount);
		}
		case UI_Common::C_ModuleXmlRpc_Go: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
		}
		case UI_Common::C_ModuleXmlRpc_ChatAvatar: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
		}
		case UI_Common::C_ModuleXmlRpc_LadderRecap: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
		}
		case UI_Common::C_ModuleXmlRpc_ScoresTable: {
			if (_Property == "alt_visible") Value = XmlRpc::JsonGetBoolean(True);
			else if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
		}
		case UI_Common::C_ModuleXmlRpc_ScoresTableAlt: {
			if (_Property == "alt_visible") Value = XmlRpc::JsonGetBoolean(True);
		}
		case UI_Common::C_ModuleXmlRpc_OpponentsInfo: {
			if (_Property == "visible") Value = XmlRpc::JsonGetBoolean(True);
		}
	}
	
	return Value;
}

// ---------------------------------- //
/** Set the value of one of the 
 *	UI module property
 *
 *	@param	_Name											The name of the module
 *	@param	_Property									The property to update
 *	@param	_Value										The value of the property
 */
Void Private_SetProperty(Text _Name, Text _Property, Text _Value) {
	if (_Property == "visible") Private_SetVisibility(_Name, _Value);
	else if (_Property == "pos") Private_SetPosition(_Name, _Value);
	if (_Name == UI_Common::C_ModuleXmlRpc_Chat) {
		if (_Property == "linecount") SetChatLineCount(TL::ToInteger(_Value));
		else if (_Property == "offset") Private_SetPosition(_Name, _Value);
	} else if (_Name == UI_Common::C_ModuleXmlRpc_ScoresTable || _Name == UI_Common::C_ModuleXmlRpc_ScoresTableAlt) {
		if (_Property == "alt_visible") Private_SetVisibility(UI_Common::C_ModuleXmlRpc_ScoresTableAlt, _Value);
	}
	
	declare LibUI_PropertiesBackUp for This = "";
	LibUI_PropertiesBackUp = Private_GetProperties(C_Format_Xml);
}

// ---------------------------------- //
/** Reset one property of an UI module
 *
 *	@param	_Name											The name of the module
 *	@param	_Property									The property to reset
 *	@param	_UpdateSave								Update the properties save
 */
Void Private_ResetProperty(Text _Name, Text _Property, Boolean _UpdateSave) {
	switch (_Name) {
		case UI_Common::C_ModuleXmlRpc_TimeGap: {
			if (_Property == "visible") SetModuleVisibility(UI_Common::C_Module_TimeGap, True);
			else if (_Property == "pos") SetModulePosition(UI_Common::C_Module_TimeGap, UI_Common::C_Position_TimeGap);
		}
		case UI_Common::C_ModuleXmlRpc_SmallScoresTable: {
			if (_Property == "visible") SetModuleVisibility(UI_Common::C_Module_SmallScoresTable, True);
			else if (_Property == "pos") SetModulePosition(UI_Common::C_Module_SmallScoresTable, UI_Common::C_Position_SmallScoresTable);
		}
		case UI_Common::C_ModuleXmlRpc_Chrono: {
			if (_Property == "visible") SetModuleVisibility(UI_Common::C_Module_Chrono, True);
			else if (_Property == "pos") SetModulePosition(UI_Common::C_Module_Chrono, UI_Common::C_Position_Chrono);
		}
		case UI_Common::C_ModuleXmlRpc_CheckpointTime: {
			if (_Property == "visible") SetModuleVisibility(UI_Common::C_Module_CheckpointTime, True);
			else if (_Property == "pos") SetModulePosition(UI_Common::C_Module_CheckpointTime, UI_Common::C_Position_CheckpointTime);
		}
		case UI_Common::C_ModuleXmlRpc_PrevBestTime: {
			if (_Property == "visible") SetModuleVisibility(UI_Common::C_Module_PrevBestTime, True);
			else if (_Property == "pos") SetModulePosition(UI_Common::C_Module_PrevBestTime, UI_Common::C_Position_PrevBestTime);
		}
		case UI_Common::C_ModuleXmlRpc_SpeedAndDistance: {
			if (_Property == "visible") SetModuleVisibility(UI_Common::C_Module_SpeedAndDistance, True);
			else if (_Property == "pos") SetModulePosition(UI_Common::C_Module_SpeedAndDistance, UI_Common::C_Position_SpeedAndDistance);
		}
		case UI_Common::C_ModuleXmlRpc_Chat: {
			if (_Property == "visible") UIManager.UIAll.OverlayHideChat = False;
			else if (_Property == "offset") UIManager.UIAll.OverlayChatOffset = UI_Common::C_Position_Chat;
			else if (_Property == "linecount") SetChatLineCount(0);
		}
		case UI_Common::C_ModuleXmlRpc_Countdown: {
			if (_Property == "visible") SetModuleVisibility(UI_Common::C_Module_Countdown, True);
			else if (_Property == "pos") SetModulePosition(UI_Common::C_Module_Countdown, UI_Common::C_Position_Countdown);
		}
		case UI_Common::C_ModuleXmlRpc_WarmUp: {
			if (_Property == "visible") WarmUp::SetUIVisibility(True);
			else if (_Property == "pos") WarmUp::SetUIPosition(WarmUp::C_LibWU3_LayerPosition);
		}
		case UI_Common::C_ModuleXmlRpc_Laps: {
			if (_Property == "visible") SetModuleVisibility(UI_Common::C_Module_Laps, True);
			else if (_Property == "pos") SetModulePosition(UI_Common::C_Module_Laps, UI_Common::C_Position_Laps);
		}
		case UI_Common::C_ModuleXmlRpc_MapRanking: {
			if (_Property == "visible") SetModuleVisibility(UI_Common::C_Module_MapRanking, True);
			else if (_Property == "pos") SetModulePosition(UI_Common::C_Module_MapRanking, UI_Common::C_Position_MapRanking);
		}
		case UI_Common::C_ModuleXmlRpc_CheckpointRanking: {
			if (_Property == "visible") SetModuleVisibility(UI_Common::C_Module_CheckpointRanking, True);
			else if (_Property == "pos") SetModulePosition(UI_Common::C_Module_CheckpointRanking, UI_Common::C_Position_CheckpointRanking);
		}
		case UI_Common::C_ModuleXmlRpc_MapInfo: {
			if (_Property == "visible") SetModuleVisibility(UI_Common::C_Module_MapInfo, True);
			else if (_Property == "pos") SetModulePosition(UI_Common::C_Module_MapInfo, UI_Common::C_Position_MapInfo);
		}
		case UI_Common::C_ModuleXmlRpc_LiveInfo: {
			if (_Property == "visible") SetModuleVisibility(UI_Common::C_Module_LiveInfo, True);
			else if (_Property == "pos") SetModulePosition(UI_Common::C_Module_LiveInfo, UI_Common::C_Position_LiveInfo);
		}
		case UI_Common::C_ModuleXmlRpc_SpectatorInfo: {
			if (_Property == "visible") SetModuleVisibility(UI_Common::C_Module_SpectatorInfo, True);
			else if (_Property == "pos") SetModulePosition(UI_Common::C_Module_SpectatorInfo, UI_Common::C_Position_SpectatorInfo);
		}
		case UI_Common::C_ModuleXmlRpc_ViewersCount: {
			if (_Property == "visible") SetModuleVisibility(UI_Common::C_Module_ViewersCount, True);
			else if (_Property == "pos") SetModulePosition(UI_Common::C_Module_ViewersCount, UI_Common::C_Position_ViewersCount);
		}
		case UI_Common::C_ModuleXmlRpc_Go: {
			if (_Property == "visible") UIManager.UIAll.OverlayHide321Go = False;
		}
		case UI_Common::C_ModuleXmlRpc_ChatAvatar: {
			if (_Property == "visible") UIManager.UIAll.OverlayChatHideAvatar = False;
		}
		case UI_Common::C_ModuleXmlRpc_LadderRecap: {
			if (_Property == "visible") UIManager.UIAll.OverlayHideEndMapLadderRecap = False;
		}
		case UI_Common::C_ModuleXmlRpc_ScoresTable: {
			if (_Property == "alt_visible") {
				UIManager.UIAll.AltMenuNoDefaultScores = False;
				UIManager.UIAll.AltMenuNoCustomScores = False;
			}
			else if (_Property == "visible") Private_ScoresTableModuleSetVisibility(True);
		}
		case UI_Common::C_ModuleXmlRpc_ScoresTableAlt: {
			if (_Property == "alt_visible") {
				UIManager.UIAll.AltMenuNoDefaultScores = False;
				UIManager.UIAll.AltMenuNoCustomScores = False;
			}
		}
		case UI_Common::C_ModuleXmlRpc_OpponentsInfo: {
			if (_Property == "visible") UIManager.UIAll.OverlayHideOpponentsInfo = False;
		}
	}
	
	if (_UpdateSave) {
		declare LibUI_PropertiesBackUp for This = "";
		LibUI_PropertiesBackUp = Private_GetProperties(C_Format_Xml);
	}
}

// ---------------------------------- //
/** Reset one property of an UI module
 *
 *	@param	_Name											The name of the module
 *	@param	_Property									The property to reset
 */
Void Private_ResetProperty(Text _Name, Text _Property) {
	Private_ResetProperty(_Name, _Property, True);
}

// ---------------------------------- //
/// Reset the value of all UI module properties
Void Private_ResetProperties() {
	foreach (Name in UI_Common::C_ModulesXmlRpc) {
		Private_ResetProperty(Name, "pos", False);
		Private_ResetProperty(Name, "visible", False);
		if (Name == UI_Common::C_ModuleXmlRpc_Chat) {
			Private_ResetProperty(Name, "linecount", False);
			Private_ResetProperty(Name, "offset", False);
		} else if (Name == UI_Common::C_ModuleXmlRpc_ScoresTableAlt) {
			Private_ResetProperty(Name, "alt_visible", False);
		}
	}
	
	declare LibUI_PropertiesBackUp for This = "";
	LibUI_PropertiesBackUp = Private_GetProperties(C_Format_Xml);
}

// ---------------------------------- //
/** Parse the properties xml
 *
 *	@param	_Xml											The xml to parse
 */
Void Private_SetProperties(Text _Xml) {
	if (_Xml == "") return;
	declare XmlDoc <=> Xml.Create(_Xml);
	if (XmlDoc == Null) {
		Xml.Destroy(XmlDoc);
		return;
	}
	if (XmlDoc.Root.Name != "ui_properties") {
		Xml.Destroy(XmlDoc);
		return;
	}
	
	foreach (Node in XmlDoc.Root.Children) {
		Private_SetVisibility(Node.Name, Node.GetAttributeText("visible", ""));
		Private_SetPosition(Node.Name, Node.GetAttributeText("pos", ""));
		if (Node.Name == UI_Common::C_ModuleXmlRpc_Chat) {
			SetChatLineCount(Node.GetAttributeInteger("linecount", 0));
			Private_SetPosition(Node.Name, Node.GetAttributeText("offset", ""));
		}
		if (Node.Name == UI_Common::C_ModuleXmlRpc_ScoresTable) {
			Private_SetVisibility(UI_Common::C_ModuleXmlRpc_ScoresTableAlt, Node.GetAttributeText("alt_visible", ""));
		}
	}
	
	Xml.Destroy(XmlDoc);
	
	declare LibUI_PropertiesBackUp for This = "";
	LibUI_PropertiesBackUp = _Xml;
}

// ---------------------------------- //
/** Send ui properties
 *
 *	@param	_ResponseId								Id to insert in the response callback
 */
Void XmlRpc_SendUIProperties(Text _ResponseId) {
	declare Json = """{
	"responseid": {{{dump(_ResponseId)}}}
}""";
	declare PropsXml = Private_GetProperties(C_Format_Xml);
	declare PropsJson = Private_GetProperties(C_Format_Json);

	XmlRpc::SendCallback(C_Callback_Properties, [Json, PropsXml, PropsJson]);
}

// ---------------------------------- //
/** Send default ui properties
 *
 *	@param	_ResponseId								Id to insert in the response callback
 */
Void XmlRpc_SendDefaultUIProperties(Text _ResponseId) {
	declare Json = """{
	"responseid": {{{dump(_ResponseId)}}}
}""";
	declare PropsXml = Private_GetDefaultProperties(C_Format_Xml);
	declare PropsJson = Private_GetDefaultProperties(C_Format_Json);

	XmlRpc::SendCallback(C_Callback_Properties, [Json, PropsXml, PropsJson]);
}

// ---------------------------------- //
/** Send ui property
 *
 *	@param	_ModuleId									The UI module to check
 *	@param	_Property									The property to get
 *	@param	_ResponseId								Id to insert in the response callback
 */
Void XmlRpc_SendUIProperty(Text _ModuleId, Text _Property, Text _ResponseId) {
	declare Value = Private_GetProperty(_ModuleId, _Property);
	
	declare Json = """{
	"responseid": {{{XmlRpc::JsonGetText(_ResponseId)}}},
	"module": {{{XmlRpc::JsonGetText(_ModuleId)}}},
	"property": {{{XmlRpc::JsonGetText(_Property)}}},
	"value": {{{Value}}}
}""";

	XmlRpc::SendCallback(C_Callback_Property, [Json]);
}

// ---------------------------------- //
/** Send default ui property
 *
 *	@param	_ModuleId									The UI module to check
 *	@param	_Property									The property to get
 *	@param	_ResponseId								Id to insert in the response callback
 */
Void XmlRpc_SendDefaultUIProperty(Text _ModuleId, Text _Property, Text _ResponseId) {
	declare Value = Private_GetDefaultProperty(_ModuleId, _Property);
	
	declare Json = """{
	"responseid": {{{XmlRpc::JsonGetText(_ResponseId)}}},
	"module": {{{XmlRpc::JsonGetText(_ModuleId)}}},
	"property": {{{XmlRpc::JsonGetText(_Property)}}},
	"value": {{{Value}}}
}""";

	XmlRpc::SendCallback(C_Callback_Property, [Json]);
}

// ---------------------------------- //
/// Update UI library
Void Yield() {
	if (G_LibUI_PrevCutOffTimeLimit != CutOffTimeLimit) {
		G_LibUI_PrevCutOffTimeLimit = CutOffTimeLimit;
		SetCutOffTimeLimit(CutOffTimeLimit);
	}
	
	foreach (Event in XmlRpc.PendingEvents) {
		if (Event.Type == CXmlRpcEvent::EType::CallbackArray) {
			switch (Event.ParamArray1) {
				case C_Method_SetProperties: {
					if (Event.ParamArray2.count > 0) {
						Private_SetProperties(Event.ParamArray2[0]);
					}
				}
				case C_Method_GetProperties: {
					declare ResponseId = "";
					if (Event.ParamArray2.existskey(0)) ResponseId = Event.ParamArray2[0];
					XmlRpc_SendUIProperties(ResponseId);
				}
				case C_Method_GetDefaultProperties: {
					if (Semver::Compare(XmlRpc::GetApiVersion(), ">=", "2.3.0")) {
						declare ResponseId = "";
						if (Event.ParamArray2.existskey(0)) ResponseId = Event.ParamArray2[0];
						XmlRpc_SendDefaultUIProperties(ResponseId);
					}
				}
				case C_Method_ResetProperties: {
					if (Semver::Compare(XmlRpc::GetApiVersion(), ">=", "2.3.0")) {
						Private_ResetProperties();
					}
				}
				case C_Method_GetProperty: {
					if (Semver::Compare(XmlRpc::GetApiVersion(), ">=", "2.3.0")) {
						declare Name = "";
						declare Property = "";
						declare ResponseId = "";
						if (Event.ParamArray2.existskey(0)) Name = Event.ParamArray2[0];
						if (Event.ParamArray2.existskey(1)) Property = Event.ParamArray2[1];
						if (Event.ParamArray2.existskey(2)) ResponseId = Event.ParamArray2[2];
						XmlRpc_SendUIProperty(Name, Property, ResponseId);
					}
				}
				case C_Method_GetDefaultProperty: {
					if (Semver::Compare(XmlRpc::GetApiVersion(), ">=", "2.3.0")) {
						declare Name = "";
						declare Property = "";
						declare ResponseId = "";
						if (Event.ParamArray2.existskey(0)) Name = Event.ParamArray2[0];
						if (Event.ParamArray2.existskey(1)) Property = Event.ParamArray2[1];
						if (Event.ParamArray2.existskey(2)) ResponseId = Event.ParamArray2[2];
						XmlRpc_SendDefaultUIProperty(Name, Property, ResponseId);
					}
				}
				case C_Method_SetProperty: {
					if (Semver::Compare(XmlRpc::GetApiVersion(), ">=", "2.3.0")) {
						declare Name = "";
						declare Property = "";
						declare Value = "";
						if (Event.ParamArray2.existskey(0)) Name = Event.ParamArray2[0];
						if (Event.ParamArray2.existskey(1)) Property = Event.ParamArray2[1];
						if (Event.ParamArray2.existskey(2)) Value = Event.ParamArray2[2];
						Private_SetProperty(Name, Property, Value);
					}
				}
				case C_Method_ResetProperty: {
					if (Semver::Compare(XmlRpc::GetApiVersion(), ">=", "2.3.0")) {
						declare Name = "";
						declare Property = "";
						if (Event.ParamArray2.existskey(0)) Name = Event.ParamArray2[0];
						if (Event.ParamArray2.existskey(1)) Property = Event.ParamArray2[1];
						Private_ResetProperty(Name, Property);
					}
				}
			}
		}
	}
	
	// Update live info module with default events
	if (Private_ModuleIsLoaded(UI_Common::C_Module_LiveInfo)) {
		foreach (Event in PendingEvents) {
			if (Event.Type == CTmModeEvent::EType::OnPlayerAdded) {
				if (Event.Player != Null && Event.Player.User != Null) {
					//L16N [UI] Message displayed when a player joins the server
					declare Message = TL::Compose(_("$<%1$> joined the game"), Event.Player.User.Name);
					declare Image = """file://Avatars/{{{Event.Player.User.Login}}}/Default""";
					foreach (Player in AllPlayers) {
						SendLiveEvent(Player, Message, Image);
					}
				}
			} else if (Event.Type == CTmModeEvent::EType::OnPlayerRemoved) {
				if (Event.User != Null) {
					//L16N [UI] Message displayed when a player leaves the server
					declare Message = TL::Compose(_("$<%1$> left the game"), Event.User.Name);
					declare Image = """file://Avatars/{{{Event.User.Login}}}/Default""";
					foreach (Player in AllPlayers) {
						SendLiveEvent(Player, Message, Image);
					}
				}
			}
		}
	}
}

// ---------------------------------- //
/** Load a module
 *
 *	@param	_ModuleId									The name of the module to load
 */
Void LoadModule(Text _ModuleId) {
	if (Private_ModuleIsLoaded(_ModuleId)) return;
	
	G_LibUI_ModulesLoaded.add(_ModuleId);
			
	switch (_ModuleId) {
		case UI_Common::C_Module_TimeGap: {
			UI_Server::LoadModule(UI_Common::C_Module_TimeGap);
			SetTimeGapMode("CurRace");
			SetModuleVisibility(UI_Common::C_Module_TimeGap, True);
			SetModulePosition(UI_Common::C_Module_TimeGap, UI_Common::C_Position_TimeGap);
		}
		case UI_Common::C_Module_SmallScoresTable: {
			UI_Server::LoadModule(UI_Common::C_Module_SmallScoresTable);
			SetModuleVisibility(UI_Common::C_Module_SmallScoresTable, True);
			SetModulePosition(UI_Common::C_Module_SmallScoresTable, UI_Common::C_Position_SmallScoresTable);
		}
		case UI_Common::C_Module_Chrono: {
			UI_Server::LoadModule(UI_Common::C_Module_Chrono);
			SetModuleVisibility(UI_Common::C_Module_Chrono, True);
			SetModulePosition(UI_Common::C_Module_Chrono, UI_Common::C_Position_Chrono);
		}
		case UI_Common::C_Module_CheckpointTime: {
			UI_Server::LoadModule(UI_Common::C_Module_CheckpointTime);
			SetCheckpointTimeMode("CurRace");
			DisplayTimeDiff(True);
			DisplayLapTimeDiff(True);
			SetModuleVisibility(UI_Common::C_Module_CheckpointTime, True);
			SetModulePosition(UI_Common::C_Module_CheckpointTime, UI_Common::C_Position_CheckpointTime);
		}
		case UI_Common::C_Module_PrevBestTime: {
			UI_Server::LoadModule(UI_Common::C_Module_PrevBestTime);
			SetModuleVisibility(UI_Common::C_Module_PrevBestTime, True);
			SetModulePosition(UI_Common::C_Module_PrevBestTime, UI_Common::C_Position_PrevBestTime);
		}
		case UI_Common::C_Module_SpeedAndDistance: {
			UI_Server::LoadModule(UI_Common::C_Module_SpeedAndDistance);
			SetModuleVisibility(UI_Common::C_Module_SpeedAndDistance, True);
			SetModulePosition(UI_Common::C_Module_SpeedAndDistance, UI_Common::C_Position_SpeedAndDistance);
			DisplaySpeedGauge(False);
		}
		case UI_Common::C_Module_Countdown: {
			UI_Server::LoadModule(UI_Common::C_Module_Countdown);
			SetModuleVisibility(UI_Common::C_Module_Countdown, True);
			SetModulePosition(UI_Common::C_Module_Countdown, UI_Common::C_Position_Countdown);
		}
		case UI_Common::C_Module_Laps: {
			UI_Server::LoadModule(UI_Common::C_Module_Laps);
			SetModuleVisibility(UI_Common::C_Module_Laps, True);
			SetModulePosition(UI_Common::C_Module_Laps, UI_Common::C_Position_Laps);
		}
		case UI_Common::C_Module_MapRanking: {
			UI_Server::LoadModule(UI_Common::C_Module_MapRanking);
			SetModuleVisibility(UI_Common::C_Module_MapRanking, True);
			SetModulePosition(UI_Common::C_Module_MapRanking, UI_Common::C_Position_MapRanking);
		}
		case UI_Common::C_Module_CheckpointRanking: {
			UI_Server::LoadModule(UI_Common::C_Module_CheckpointRanking);
			SetModuleVisibility(UI_Common::C_Module_CheckpointRanking, True);
			SetModulePosition(UI_Common::C_Module_CheckpointRanking, UI_Common::C_Position_CheckpointRanking);
		}
		case UI_Common::C_Module_MapInfo: {
			UI_Server::LoadModule(UI_Common::C_Module_MapInfo);
			SetModuleVisibility(UI_Common::C_Module_MapInfo, True);
			SetModulePosition(UI_Common::C_Module_MapInfo, UI_Common::C_Position_MapInfo);
		}
		case UI_Common::C_Module_LiveInfo: {
			UI_Server::LoadModule(UI_Common::C_Module_LiveInfo);
			SetModuleVisibility(UI_Common::C_Module_LiveInfo, True);
			SetModulePosition(UI_Common::C_Module_LiveInfo, UI_Common::C_Position_LiveInfo);
		}
		case UI_Common::C_Module_SpectatorInfo: {
			UI_Server::LoadModule(UI_Common::C_Module_SpectatorInfo);
			SetModuleVisibility(UI_Common::C_Module_SpectatorInfo, True);
			SetModulePosition(UI_Common::C_Module_SpectatorInfo, UI_Common::C_Position_SpectatorInfo);
		}
		case UI_Common::C_Module_ViewersCount: {
			UI_Server::LoadModule(UI_Common::C_Module_ViewersCount);
			SetModuleVisibility(UI_Common::C_Module_ViewersCount, True);
			SetModulePosition(UI_Common::C_Module_ViewersCount, UI_Common::C_Position_ViewersCount);
		}
	}
	
	// Try to load the latest properties
	declare LibUI_PropertiesBackUp for This = "";
	Private_SetProperties(LibUI_PropertiesBackUp);
}

// ---------------------------------- //
/** Load several modules
 *
 *	@param	_ModulesIds								A list of modules to load with the library
 */
Void LoadModules(Text[] _ModulesIds) {
	foreach (ModuleId in _ModulesIds) {
		LoadModule(ModuleId);
	}
}

// ---------------------------------- //
/** Unload a module
 *
 *	@param	_ModuleId									The name of the module to unload
 */
Void UnloadModule(Text _ModuleId) {
	declare Removed = G_LibUI_ModulesLoaded.remove(_ModuleId);
	
	if (Removed) {
		// Remove module layer
		UI_Server::UnloadModule(_ModuleId);
		SetModuleVisibility(_ModuleId, True);
	}
}

// ---------------------------------- //
/// Unload the library
Void Unload() {
	foreach (ModuleId in UI_Common::C_Modules) {
		UnloadModule(ModuleId);		
	}
	
	G_LibUI_ModulesLoaded.clear();
	G_LibUI_LayersIds.clear();
	G_LibUI_ModuleVisibility.clear();
	G_LibUI_ModulePosition.clear();
	G_LibUI_PrevCutOffTimeLimit = -1;
	
	XmlRpc::UnregisterCallback(C_Callback_Properties);
	XmlRpc::UnregisterCallback(C_Callback_Property);
	XmlRpc::UnregisterMethod(C_Method_SetProperties);
	XmlRpc::UnregisterMethod(C_Method_GetProperties);
	XmlRpc::UnregisterMethod(C_Method_GetDefaultProperties);
	XmlRpc::UnregisterMethod(C_Method_ResetProperties);
	XmlRpc::UnregisterMethod(C_Method_GetProperty);
	XmlRpc::UnregisterMethod(C_Method_GetDefaultProperty);
	XmlRpc::UnregisterMethod(C_Method_SetProperty);
	XmlRpc::UnregisterMethod(C_Method_ResetProperty);
	
	UI_Server::Unload();
}

// ---------------------------------- //
/// Load the library
Void Load() {
	Unload();
	
	UI_Server::Load();
	
	// Try to load the latest properties
	declare LibUI_PropertiesBackUp for This = "";
	Private_SetProperties(LibUI_PropertiesBackUp);
	
	// Register callbacks
	XmlRpc::RegisterCallback(C_Callback_Properties, """
* Name: {{{C_Callback_Properties}}}
* Type: CallbackArray
* Description: Information about the default UI components of Maniaplanet (map info, chat, ladder recap, ...).
* Data:
	- Version >=2.0.0: 
	```
	[
		"{
			"responseid": "xyz" //< Facultative id passed by a script event
		}",
		"
		<ui_properties>
			<!-- The map name and author displayed in the top right of the screen when viewing the scores table -->
			<{{{UI_Common::C_ModuleXmlRpc_MapInfo}}} visible="true" pos="-160. 80. 150." />
			<!-- Information about live envent displayed in the top right of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_LiveInfo}}} visible="true" pos="-159. 84. 5." />
			<!-- Information about the spectated player displayed in the bottom of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_SpectatorInfo}}} visible="true" pos="0. -68. 5." />
			<!-- Only visible in solo modes, it hides the medal/ghost selection UI -->
			<{{{UI_Common::C_ModuleXmlRpc_OpponentsInfo}}} visible="true" />
			<!--
				The server chat displayed on the bottom left of the screen
				The offset values range from 0. to -3.2 for x and from 0. to 1.8 for y
				The linecount property must be between 0 and 40
			-->
			<{{{UI_Common::C_ModuleXmlRpc_Chat}}} visible="true" offset="0. 0." linecount="7" />
			<!-- Time of the players at the current checkpoint displayed at the bottom of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_TimeGap}}} visible="true" pos="48. -52. 5." />
			<!-- Small scores table displayed at the end of race of the round based modes (Rounds, Cup, ...) on the right of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_SmallScoresTable}}} visible="true" pos="-158.5 40. 150." />
			<!-- Race time left displayed at the bottom right of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_Countdown}}} visible="true" pos="153. -7. 5." />
			<!-- 3, 2, 1, Go! message displayed on the middle of the screen when spawning -->
			<{{{UI_Common::C_ModuleXmlRpc_Go}}} visible="true" />
			<!-- Current race chrono displayed at the bottom center of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_Chrono}}} visible="true" pos="0. -80. -5." />
			<!-- Speed and distance raced displayed in the bottom right of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_SpeedAndDistance}}} visible="true" pos="137. -69. 5." />
			<!-- Previous and best times displayed at the bottom right of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_PrevBestTime}}} visible="true" pos="157. -24. 5." />
			<!-- Current position in the map ranking displayed at the bottom right of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_MapRanking}}} visible="true" pos="150.5 -28. 5." />
			<!-- Checkpoint time information displayed in the middle of the screen when crossing a checkpoint -->
			<{{{UI_Common::C_ModuleXmlRpc_CheckpointTime}}} visible="true" pos="0. 3. -10." />
			<!-- The avatar of the last player speaking in the chat displayed above the chat -->
			<{{{UI_Common::C_ModuleXmlRpc_ChatAvatar}}} visible="true" />
			<!-- Warm-up progression displayed on the right of the screen during warm-up -->
			<{{{UI_Common::C_ModuleXmlRpc_WarmUp}}} visible="true" pos="153. 13. 0." />
			<!-- Ladder progression box displayed on the top of the screen at the end of the map -->
			<{{{UI_Common::C_ModuleXmlRpc_LadderRecap}}} visible="true" />
			<!-- Laps count displayed on the right of the screen on multilaps map -->
			<{{{UI_Common::C_ModuleXmlRpc_Laps}}} visible="true" pos="140. 84. 5." />
			<!-- Player's ranking at the latest checkpoint -->
			<{{{UI_Common::C_ModuleXmlRpc_CheckpointRanking}}} visible="false" pos="0. 84. 5." />
			<!-- Scores table displayed in the middle of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_ScoresTable}}} alt_visible="true" />
		</ui_properties>
		",
		"{
			"{{{UI_Common::C_ModuleXmlRpc_MapInfo}}}": { //< The map name and author displayed in the top right of the screen when viewing the scores table
				"visible": true,
				"pos": { "x": -160.0, "y": 80.0, "z": 150.0 }
			},
			"{{{UI_Common::C_ModuleXmlRpc_LiveInfo}}}": { //< Information about live envent displayed in the top right of the screen
				"visible": true,
				"pos": { "x": -159.0, "y": 84.0, "z": 5.0 }
			},
			"{{{UI_Common::C_ModuleXmlRpc_SpectatorInfo}}}": { //< Information about the spectated player displayed in the bottom of the screen
				"visible": true,
				"pos": { "x": 0.0, "y": -68.0, "z": 5.0 }
			},
			"{{{UI_Common::C_ModuleXmlRpc_OpponentsInfo}}}": { //< Only visible in solo modes, it hides the medal/ghost selection UI
				"visible": true
			},
			"{{{UI_Common::C_ModuleXmlRpc_Chat}}}": { //< The server chat displayed on the bottom left of the screen
				"visible": true,
				"offset": { "x": 0.0, "y": 0.0 }, //< The offset values range from 0. to -3.2 for x and from 0. to 1.8 for y
				"linecount": 7 //< The linecount property must be between 0 and 40
			},
			"{{{UI_Common::C_ModuleXmlRpc_TimeGap}}}": { //< Time of the players at the current checkpoint displayed at the bottom of the screen
				"visible": true,
				"pos": { "x": 48.0, "y": -52.0, "z": 5.0 }
			},
			"{{{UI_Common::C_ModuleXmlRpc_SmallScoresTable}}}": { //< Small scores table displayed at the end of race of the round based modes (Rounds, Cup, ...) on the right of the screen
				"visible": true,
				"pos": { "x": -158.5, "y": 40.0, "z": 150.0 }
			},
			"{{{UI_Common::C_ModuleXmlRpc_Countdown}}}": { //< Race time left displayed at the bottom right of the screen
				"visible": true,
				"pos": { "x": 153.0, "y": -7.0, "z": 5.0 }
			},
			"{{{UI_Common::C_ModuleXmlRpc_Go}}}": { //< 3, 2, 1, Go! message displayed on the middle of the screen when spawning
				"visible": true
			},
			"{{{UI_Common::C_ModuleXmlRpc_Chrono}}}": { //< Current race chrono displayed at the bottom center of the screen
				"visible": true,
				"pos": { "x": 0.0, "y": -80.0, "z": -5.0 }
			},
			"{{{UI_Common::C_ModuleXmlRpc_SpeedAndDistance}}}": { //< Speed and distance raced displayed in the bottom right of the screen
				"visible": true,
				"pos": { "x": 137.0, "y": -69.0, "z": 5.0 }
			},
			"{{{UI_Common::C_ModuleXmlRpc_PrevBestTime}}}": { //< Previous and best times displayed at the bottom right of the screen
				"visible": true,
				"pos": { "x": 157.0, "y": -24.0, "z": 5.0 }
			},
			"{{{UI_Common::C_ModuleXmlRpc_MapRanking}}}": { //< Current position in the map ranking displayed at the bottom right of the screen
				"visible": true,
				"pos": { "x": 150.5, "y": -28.0, "z": 5.0 }
			},
			"{{{UI_Common::C_ModuleXmlRpc_CheckpointTime}}}": { //< Checkpoint time information displayed in the middle of the screen when crossing a checkpoint
				"visible": true,
				"pos": { "x": 0.0, "y": 3.0, "z": -10.0 }
			},
			"{{{UI_Common::C_ModuleXmlRpc_ChatAvatar}}}": { //< The avatar of the last player speaking in the chat displayed above the chat
				"visible": true
			},
			"{{{UI_Common::C_ModuleXmlRpc_WarmUp}}}": { //< Warm-up progression displayed on the right of the screen during warm-up
				"visible": true,
				"pos": { "x": 153.0, "y": 13.0, "z": 0.0 }
			},
			"{{{UI_Common::C_ModuleXmlRpc_LadderRecap}}}": { //< Ladder progression box displayed on the top of the screen at the end of the map
				"visible": true
			},
			"{{{UI_Common::C_ModuleXmlRpc_Laps}}}": { //< Laps count displayed on the right of the screen on multilaps map
				"visible": true,
				"pos": { "x": 140.0, "y": 84.0, "z": 5.0 }
			},
			"{{{UI_Common::C_ModuleXmlRpc_CheckpointRanking}}}": { //< Player's ranking at the latest checkpoint
				"visible": true,
				"pos": { "x": 0.0, "y": 84.0, "z": 5.0 }
			},
			"{{{UI_Common::C_ModuleXmlRpc_ScoresTable}}}": { //< Scores table displayed in the middle of the screen
				"alt_visible": true
			}
		}"
	]
	```
	- Version >=2.3.0: 
	A new module "{{{UI_Common::C_ModuleXmlRpc_ViewersCount}}}" is available.
	```
	[
		"{
			"responseid": "xyz" //< Facultative id passed by a script event
		}",
		"
		<ui_properties>
			...
			<!-- Number of players spectating us displayed at the bottom right of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_ViewersCount}}} visible="true" pos="157. -40. 5." />
		</ui_properties>
		",
		"{
			...
			"{{{UI_Common::C_ModuleXmlRpc_ViewersCount}}}": { //< Number of players spectating us displayed at the bottom right of the screen
				"visible": true,
				"pos": { "x": 157.0, "y": -40.0, "z": 5.0 }
			}
		}"
	]
	```
	- Version >=2.4.0: 
	The scores table module has a new visible property
	```
	[
		"{
			"responseid": "xyz" //< Facultative id passed by a script event
		}",
		"
		<ui_properties>
			...
			<!-- Scores table displayed in the middle of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_ScoresTable}}} alt_visible="true" visible="true" />
		</ui_properties>
		",
		"{
			...
			"{{{UI_Common::C_ModuleXmlRpc_ScoresTable}}}": { //< Scores table displayed in the middle of the screen
				"alt_visible": true,
				"visible": true
			}
		}"
	]
	```
	""");
	XmlRpc::RegisterCallback(C_Callback_Property, """
* Name: {{{C_Callback_Property}}}
* Type: CallbackArray
* Description: Value of one property of a UI module.
* Data:
	- Version >=2.3.0: 
	```
	[
		"{
			"responseid": "xyz", //< Facultative id passed by a script event
			"module": "{{{UI_Common::C_ModuleXmlRpc_MapInfo}}}", //< The module
			"property": "visible", //< The name of the property
			"value": true //< The value of the property. Its type can change depending on the property.
		}"
	]
	```
	""");
	
	// Register methods
	XmlRpc::RegisterMethod(C_Method_GetProperties, """
* Name: {{{C_Method_GetProperties}}}
* Type: TriggerModeScriptEventArray
* Description: Request the current ui properties. This method will trigger the "{{{C_Callback_Properties}}}" callback.
* Data:
	- Version >=2.0.0:
	```
	[
		"responseid" //< Facultative id that will be passed to the "{{{C_Callback_Properties}}}" callback.
	]
	```
	""");
	XmlRpc::RegisterMethod(C_Method_GetDefaultProperties, """
* Name: {{{C_Method_GetDefaultProperties}}}
* Type: TriggerModeScriptEventArray
* Description: Request the default ui properties. This method will trigger the "{{{C_Callback_Properties}}}" callback.
* Data:
	- Version >=2.3.0:
	```
	[
		"responseid" //< Facultative id that will be passed to the "{{{C_Callback_Properties}}}" callback.
	]
	```
	""");
	XmlRpc::RegisterMethod(C_Method_SetProperties, """
* Name: {{{C_Method_SetProperties}}}
* Type: TriggerModeScriptEventArray
* Description: Update the ui properties.
* Data:
	- Version >=2.0.0:
	```
	[
		"
		<!--
		  Each node is optional and can be omitted.
		  If it's the case then the previous value will be kept.
		-->
		<ui_properties>
			<!-- The map name and author displayed in the top right of the screen when viewing the scores table -->
			<{{{UI_Common::C_ModuleXmlRpc_MapInfo}}} visible="true" pos="-160. 80. 150." />
			<!-- Information about live envent displayed in the top right of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_LiveInfo}}} visible="true" pos="-159. 84. 5." />
			<!-- Information about the spectated player displayed in the bottom of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_SpectatorInfo}}} visible="true" pos="0. -68. 5." />
			<!-- Only visible in solo modes, it hides the medal/ghost selection UI -->
			<{{{UI_Common::C_ModuleXmlRpc_OpponentsInfo}}} visible="true" />
			<!--
				The server chat displayed on the bottom left of the screen
				The offset values range from 0. to -3.2 for x and from 0. to 1.8 for y
				The linecount property must be between 0 and 40
			-->
			<{{{UI_Common::C_ModuleXmlRpc_Chat}}} visible="true" offset="0. 0." linecount="7" />
			<!-- Time of the players at the current checkpoint displayed at the bottom of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_TimeGap}}} visible="true" pos="48. -52. 5." />
			<!-- Small scores table displayed at the end of race of the round based modes (Rounds, Cup, ...) on the right of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_SmallScoresTable}}} visible="true" pos="-158.5 40. 150." />
			<!-- Race time left displayed at the bottom right of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_Countdown}}} visible="true" pos="153. -7. 5." />
			<!-- 3, 2, 1, Go! message displayed on the middle of the screen when spawning -->
			<{{{UI_Common::C_ModuleXmlRpc_Go}}} visible="true" />
			<!-- Current race chrono displayed at the bottom center of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_Chrono}}} visible="true" pos="0. -80. -5." />
			<!-- Speed and distance raced displayed in the bottom right of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_SpeedAndDistance}}} visible="true" pos="137. -69. 5." />
			<!-- Previous and best times displayed at the bottom right of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_PrevBestTime}}} visible="true" pos="157. -24. 5." />
			<!-- Current position in the map ranking displayed at the bottom right of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_MapRanking}}} visible="true" pos="150.5 -28. 5." />
			<!-- Checkpoint time information displayed in the middle of the screen when crossing a checkpoint -->
			<{{{UI_Common::C_ModuleXmlRpc_CheckpointTime}}} visible="true" pos="0. 3. -10." />
			<!-- The avatar of the last player speaking in the chat displayed above the chat -->
			<{{{UI_Common::C_ModuleXmlRpc_ChatAvatar}}} visible="true" />
			<!-- Warm-up progression displayed on the right of the screen during warm-up -->
			<{{{UI_Common::C_ModuleXmlRpc_WarmUp}}} visible="true" pos="153. 13. 0." />
			<!-- Ladder progression box displayed on the top of the screen at the end of the map -->
			<{{{UI_Common::C_ModuleXmlRpc_LadderRecap}}} visible="true" />
			<!-- Laps count displayed on the right of the screen on multilaps map -->
			<{{{UI_Common::C_ModuleXmlRpc_Laps}}} visible="true" pos="140. 84. 5." />
			<!-- Player's ranking at the latest checkpoint -->
			<{{{UI_Common::C_ModuleXmlRpc_CheckpointRanking}}} visible="false" pos="0. 84. 5." />
			<!-- Scores table displayed in the middle of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_ScoresTable}}} alt_visible="true" />
		</ui_properties>
		"
	]
	```
	- Version >=2.3.0:
	A new module "{{{UI_Common::C_ModuleXmlRpc_ViewersCount}}}" is available.
	```
	[
		"
		<!--
		  Each node is optional and can be omitted.
		  If it's the case then the previous value will be kept.
		-->
		<ui_properties>
			...
			<!-- Number of players spectating us displayed at the bottom right of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_ViewersCount}}} visible="true" pos="157. -40. 5." />
		</ui_properties>
		"
	]
	```
	- Version >=2.4.0: 
	The scores table module has a new visible property
	```
	[
		"
		<!--
		  Each node is optional and can be omitted.
		  If it's the case then the previous value will be kept.
		-->
		<ui_properties>
			...
			<!-- Scores table displayed in the middle of the screen -->
			<{{{UI_Common::C_ModuleXmlRpc_ScoresTable}}} alt_visible="true" visible="true" />
		</ui_properties>
		"
	]
	```
	""");
	XmlRpc::RegisterMethod(C_Method_ResetProperties, """
* Name: {{{C_Method_ResetProperties}}}
* Type: TriggerModeScriptEventArray
* Description: Reset the ui properties to their default value.
* Data:
	- Version >=2.3.0:
	```
	[]
	```
	""");
	XmlRpc::RegisterMethod(C_Method_GetProperty, """
* Name: {{{C_Method_GetProperty}}}
* Type: TriggerModeScriptEventArray
* Description: Request the value of a module property. This method will trigger the "{{{C_Callback_Property}}}" callback.
* Data:
	- Version >=2.3.0:
	```
	[
		"{{{UI_Common::C_ModuleXmlRpc_MapInfo}}}", //< The name of the module
		"visible", //< The name of the property
		"responseid" //< Facultative id that will be passed to the "{{{C_Callback_Property}}}" callback.
	]
	```
	""");
	XmlRpc::RegisterMethod(C_Method_GetDefaultProperty, """
* Name: {{{C_Method_GetDefaultProperty}}}
* Type: TriggerModeScriptEventArray
* Description: Request the default value of a module property. This method will trigger the "{{{C_Callback_Property}}}" callback.
* Data:
	- Version >=2.3.0:
	```
	[
		"{{{UI_Common::C_ModuleXmlRpc_MapInfo}}}", //< The name of the module
		"visible", //< The name of the property
		"responseid" //< Facultative id that will be passed to the "{{{C_Callback_Property}}}" callback.
	]
	```
	""");
	XmlRpc::RegisterMethod(C_Method_SetProperty, """
* Name: {{{C_Method_SetProperty}}}
* Type: TriggerModeScriptEventArray
* Description: Set the value of a module property.
* Data:
	- Version >=2.3.0:
	```
	[
		"{{{UI_Common::C_ModuleXmlRpc_MapInfo}}}", //< The name of the module
		"visible", //< The name of the property
		"true" //< The value of the property
	]
	```
	""");
	XmlRpc::RegisterMethod(C_Method_ResetProperty, """
* Name: {{{C_Method_ResetProperty}}}
* Type: TriggerModeScriptEventArray
* Description: Reset a module property to its default value.
* Data:
	- Version >=2.3.0:
	```
	[
		"{{{UI_Common::C_ModuleXmlRpc_MapInfo}}}", //< The name of the module
		"visible" //< The name of the property
	]
	```
	""");
}

// ---------------------------------- //
/** (Overload) Load the library with some modules
 *
 *	@param	_AutoLoadModules					A list of modules to load with the library
 */
Void Load(Text[] _AutoLoadModules) {
	Load();
	
	LoadModules(_AutoLoadModules);
}