(61d00a474) v0.9.7.1

This commit is contained in:
Regalis
2020-03-04 13:04:10 +01:00
parent 3c50efa5c9
commit 3c09ebe02f
5086 changed files with 786063 additions and 295871 deletions
@@ -0,0 +1,17 @@
using System.Collections.Generic;
namespace Barotrauma
{
interface ISerializableEntity
{
string Name
{
get;
}
Dictionary<string, SerializableProperty> SerializableProperties
{
get;
}
}
}
@@ -0,0 +1,729 @@
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
namespace Barotrauma
{
[AttributeUsage(AttributeTargets.Property)]
class Editable : Attribute
{
public int MaxLength;
public int DecimalCount = 1;
public int MinValueInt = int.MinValue, MaxValueInt = int.MaxValue;
public float MinValueFloat = float.MinValue, MaxValueFloat = float.MaxValue;
public float ValueStep;
/// <summary>
/// Currently implemented only for int fields. TODO: implement the remaining types (SerializableEntityEditor)
/// </summary>
public bool ReadOnly;
public Editable(int maxLength = 20)
{
MaxLength = maxLength;
}
public Editable(int minValue, int maxValue)
{
MinValueInt = minValue;
MaxValueInt = maxValue;
}
public Editable(float minValue, float maxValue, int decimals = 1)
{
MinValueFloat = minValue;
MaxValueFloat = maxValue;
DecimalCount = decimals;
}
}
[AttributeUsage(AttributeTargets.Property)]
class InGameEditable : Editable
{
}
[AttributeUsage(AttributeTargets.Property)]
public class Serialize : Attribute
{
public object defaultValue;
public bool isSaveable;
public string translationTextTag;
public string Description;
/// <summary>
/// Makes the property serializable to/from XML
/// </summary>
/// <param name="defaultValue">The property is set to this value during deserialization if the value is not defined in XML.</param>
/// <param name="isSaveable">Is the value saved to XML when serializing.</param>
/// <param name="translationTextTag">If set to anything else than null, SerializableEntityEditors will show what the text gets translated to or warn if the text is not found in the language files.
/// Setting the value to a non-empty string will let the user select the text from one whose tag starts with the given string (e.g. RoomName. would show all texts with a RoomName.* tag)</param>
public Serialize(object defaultValue, bool isSaveable, string description = "", string translationTextTag = null)
{
this.defaultValue = defaultValue;
this.isSaveable = isSaveable;
this.translationTextTag = translationTextTag;
this.Description = description;
}
}
class SerializableProperty
{
private static Dictionary<Type, string> supportedTypes = new Dictionary<Type, string>
{
{ typeof(bool), "bool" },
{ typeof(int), "int" },
{ typeof(float), "float" },
{ typeof(string), "string" },
{ typeof(Point), "point" },
{ typeof(Vector2), "vector2" },
{ typeof(Vector3), "vector3" },
{ typeof(Vector4), "vector4" },
{ typeof(Rectangle), "rectangle" },
{ typeof(Color), "color" },
};
private static readonly Dictionary<Type, Dictionary<string, SerializableProperty>> cachedProperties =
new Dictionary<Type, Dictionary<string, SerializableProperty>>();
public readonly string Name;
public readonly string NameToLowerInvariant;
public readonly AttributeCollection Attributes;
public readonly Type PropertyType;
public PropertyInfo PropertyInfo { get; private set; }
public SerializableProperty(PropertyDescriptor property)
{
Name = property.Name;
NameToLowerInvariant = Name.ToLowerInvariant();
PropertyInfo = property.ComponentType.GetProperty(property.Name);
PropertyType = property.PropertyType;
Attributes = property.Attributes;
}
public T GetAttribute<T>() where T : Attribute
{
foreach (Attribute a in Attributes)
{
if (a is T) return (T)a;
}
return default(T);
}
public void SetValue(object parentObject, object val)
{
PropertyInfo.SetValue(parentObject, val);
}
public bool TrySetValue(object parentObject, string value)
{
if (value == null) { return false; }
if (!supportedTypes.TryGetValue(PropertyType, out string typeName))
{
if (PropertyType.IsEnum)
{
object enumVal;
try
{
enumVal = Enum.Parse(PropertyInfo.PropertyType, value, true);
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject + "\" to " + value + " (not a valid " + PropertyInfo.PropertyType + ")", e);
return false;
}
try
{
PropertyInfo.SetValue(parentObject, enumVal);
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject.ToString() + "\" to " + value.ToString(), e);
return false;
}
}
else
{
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject + "\" to " + value);
DebugConsole.ThrowError("(Type not supported)");
return false;
}
}
try
{
switch (typeName)
{
case "bool":
bool boolValue = value == "true" || value == "True";
if (TrySetValueWithoutReflection(parentObject, boolValue)) { return true; }
PropertyInfo.SetValue(parentObject, boolValue, null);
break;
case "int":
if (int.TryParse(value, out int intVal))
{
if (TrySetValueWithoutReflection(parentObject, intVal)) { return true; }
PropertyInfo.SetValue(parentObject, intVal, null);
}
else
{
return false;
}
break;
case "float":
if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out float floatVal))
{
if (TrySetValueWithoutReflection(parentObject, floatVal)) { return true; }
PropertyInfo.SetValue(parentObject, floatVal, null);
}
else
{
return false;
}
break;
case "string":
PropertyInfo.SetValue(parentObject, value, null);
break;
case "point":
PropertyInfo.SetValue(parentObject, XMLExtensions.ParsePoint(value));
break;
case "vector2":
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseVector2(value));
break;
case "vector3":
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseVector3(value));
break;
case "vector4":
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseVector4(value));
break;
case "color":
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseColor(value));
break;
case "rectangle":
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseRect(value, true));
break;
}
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject.ToString() + "\" to " + value.ToString(), e);
return false;
}
return true;
}
public bool TrySetValue(object parentObject, object value)
{
if (value == null || parentObject == null || PropertyInfo == null) return false;
try
{
if (!supportedTypes.TryGetValue(PropertyType, out string typeName))
{
if (PropertyType.IsEnum)
{
object enumVal;
try
{
enumVal = Enum.Parse(PropertyInfo.PropertyType, value.ToString(), true);
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject + "\" to " + value + " (not a valid " + PropertyInfo.PropertyType + ")", e);
return false;
}
PropertyInfo.SetValue(parentObject, enumVal);
return true;
}
else
{
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject + "\" to " + value);
DebugConsole.ThrowError("(Type not supported)");
return false;
}
}
try
{
if (value.GetType() == typeof(string))
{
switch (typeName)
{
case "string":
PropertyInfo.SetValue(parentObject, value, null);
return true;
case "point":
PropertyInfo.SetValue(parentObject, XMLExtensions.ParsePoint((string)value));
return true;
case "vector2":
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseVector2((string)value));
return true;
case "vector3":
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseVector3((string)value));
return true;
case "vector4":
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseVector4((string)value));
return true;
case "color":
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseColor((string)value));
return true;
case "rectangle":
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseRect((string)value, false));
return true;
default:
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject.ToString() + "\" to " + value.ToString());
DebugConsole.ThrowError("(Cannot convert a string to a " + PropertyType.ToString() + ")");
return false;
}
}
else if (PropertyType != value.GetType())
{
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject.ToString() + "\" to " + value.ToString());
DebugConsole.ThrowError("(Non-matching type, should be " + PropertyType + " instead of " + value.GetType() + ")");
return false;
}
PropertyInfo.SetValue(parentObject, value, null);
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject.ToString() + "\" to " + value.ToString(), e);
return false;
}
return true;
}
catch (Exception e)
{
DebugConsole.ThrowError("Error in SerializableProperty.TrySetValue", e);
return false;
}
}
public bool TrySetValue(object parentObject, float value)
{
try
{
if (TrySetValueWithoutReflection(parentObject, value)) { return true; }
PropertyInfo.SetValue(parentObject, value, null);
}
catch (TargetInvocationException e)
{
DebugConsole.ThrowError("Exception thrown by the target of SerializableProperty.TrySetValue", e.InnerException);
return false;
}
catch (Exception e)
{
DebugConsole.ThrowError("Error in SerializableProperty.TrySetValue", e);
return false;
}
return true;
}
public bool TrySetValue(object parentObject, bool value)
{
try
{
if (TrySetValueWithoutReflection(parentObject, value)) { return true; }
PropertyInfo.SetValue(parentObject, value, null);
}
catch (TargetInvocationException e)
{
DebugConsole.ThrowError("Exception thrown by the target of SerializableProperty.TrySetValue", e.InnerException);
return false;
}
catch (Exception e)
{
DebugConsole.ThrowError("Error in SerializableProperty.TrySetValue", e);
return false;
}
return true;
}
public bool TrySetValue(object parentObject, int value)
{
try
{
PropertyInfo.SetValue(parentObject, value, null);
}
catch (TargetInvocationException e)
{
DebugConsole.ThrowError("Exception thrown by the target of SerializableProperty.TrySetValue", e.InnerException);
return false;
}
catch (Exception e)
{
DebugConsole.ThrowError("Error in SerializableProperty.TrySetValue", e);
return false;
}
return true;
}
public object GetValue(object parentObject)
{
if (parentObject == null || PropertyInfo == null) { return false; }
var value = TryGetValueWithoutReflection(parentObject);
if (value != null) { return value; }
try
{
return PropertyInfo.GetValue(parentObject, null);
}
catch (TargetInvocationException e)
{
DebugConsole.ThrowError("Exception thrown by the target of SerializableProperty.GetValue", e.InnerException);
return false;
}
catch (Exception e)
{
DebugConsole.ThrowError("Error in SerializableProperty.TrySetValue", e);
return false;
}
}
public static string GetSupportedTypeName(Type type)
{
if (type.IsEnum) return "Enum";
if (!supportedTypes.TryGetValue(type, out string typeName))
{
return null;
}
return typeName;
}
/// <summary>
/// Try getting the values of some commonly used properties directly without reflection
/// </summary>
private object TryGetValueWithoutReflection(object parentObject)
{
switch (Name)
{
case "Voltage":
if (parentObject is Powered powered) { return powered.Voltage; }
break;
case "Charge":
if (parentObject is PowerContainer powerContainer) { return powerContainer.Charge; }
break;
case "Overload":
if (parentObject is PowerTransfer powerTransfer) { return powerTransfer.Overload; }
break;
case "AvailableFuel":
{ if (parentObject is Reactor reactor) { return reactor.AvailableFuel; } }
break;
case "FissionRate":
{ if (parentObject is Reactor reactor) { return reactor.FissionRate; } }
break;
case "OxygenFlow":
if (parentObject is Vent vent) { return vent.OxygenFlow; }
break;
case "CurrFlow":
if (parentObject is Pump pump) { return pump.CurrFlow; }
if (parentObject is OxygenGenerator oxygenGenerator) { return oxygenGenerator.CurrFlow; }
break;
case "CurrentVolume":
if (parentObject is Engine engine) { return engine.CurrentVolume; }
break;
case "MotionDetected":
if (parentObject is MotionSensor motionSensor) { return motionSensor.MotionDetected; }
break;
case "Oxygen":
{ if (parentObject is Character character) { return character.Oxygen; } }
break;
case "Health":
{ if (parentObject is Character character) { return character.Health; } }
break;
case "OxygenAvailable":
{ if (parentObject is Character character) { return character.OxygenAvailable; } }
break;
case "PressureProtection":
{ if (parentObject is Character character) { return character.PressureProtection; } }
break;
case "IsDead":
{ if (parentObject is Character character) { return character.IsDead; } }
break;
case "IsOn":
{ if (parentObject is LightComponent lightComponent) { return lightComponent.IsOn; } }
break;
case "Condition":
{
if (parentObject is Item item) { return item.Condition; }
}
break;
case "ContainerIdentifier":
{
if (parentObject is Item item) { return item.ContainerIdentifier; }
}
break;
case "PhysicsBodyActive":
{
if (parentObject is Item item) { return item.PhysicsBodyActive; }
}
break;
}
return null;
}
/// <summary>
/// Try setting the values of some commonly used properties directly without reflection
/// </summary>
private bool TrySetValueWithoutReflection(object parentObject, object value)
{
switch (Name)
{
case "Condition":
if (parentObject is Item item && value is float) { item.Condition = (float)value; return true; }
break;
case "Voltage":
if (parentObject is Powered powered && value is float) { powered.Voltage = (float)value; return true; }
break;
case "Charge":
if (parentObject is PowerContainer powerContainer && value is float) { powerContainer.Charge = (float)value; return true; }
break;
case "AvailableFuel":
if (parentObject is Reactor reactor && value is float) { reactor.AvailableFuel = (float)value; return true; }
break;
case "Oxygen":
{ if (parentObject is Character character && value is float) { character.Oxygen = (float)value; return true; } }
break;
case "HideFace":
{ if (parentObject is Character character && value is bool) { character.HideFace = (bool)value; return true; } }
break;
case "OxygenAvailable":
{ if (parentObject is Character character && value is float) { character.OxygenAvailable = (float)value; return true; } }
break;
case "ObstructVision":
{ if (parentObject is Character character && value is bool) { character.ObstructVision = (bool)value; return true; } }
break;
case "PressureProtection":
{ if (parentObject is Character character && value is float) { character.PressureProtection = (float)value; return true; } }
break;
case "LowPassMultiplier":
{ if (parentObject is Character character && value is float) { character.LowPassMultiplier = (float)value; return true; } }
break;
case "SpeedMultiplier":
{ if (parentObject is Character character && value is float) { character.StackSpeedMultiplier((float)value); return true; } }
break;
case "IsOn":
{ if (parentObject is LightComponent lightComponent && value is bool) { lightComponent.IsOn = (bool)value; return true; } }
break;
}
return false;
}
public static List<SerializableProperty> GetProperties<T>(ISerializableEntity obj)
{
List<SerializableProperty> editableProperties = new List<SerializableProperty>();
foreach (var property in obj.SerializableProperties.Values)
{
if (property.Attributes.OfType<T>().Any()) editableProperties.Add(property);
}
return editableProperties;
}
public static Dictionary<string, SerializableProperty> GetProperties(object obj)
{
Type objType = obj.GetType();
if (cachedProperties.ContainsKey(objType))
{
return cachedProperties[objType];
}
var properties = TypeDescriptor.GetProperties(obj.GetType()).Cast<PropertyDescriptor>();
Dictionary<string, SerializableProperty> dictionary = new Dictionary<string, SerializableProperty>();
foreach (var property in properties)
{
var serializableProperty = new SerializableProperty(property);
dictionary.Add(serializableProperty.NameToLowerInvariant, serializableProperty);
}
cachedProperties[objType] = dictionary;
return dictionary;
}
public static Dictionary<string, SerializableProperty> DeserializeProperties(object obj, XElement element = null)
{
Dictionary<string, SerializableProperty> dictionary = GetProperties(obj);
foreach (var property in dictionary.Values)
{
//set the value of the property to the default value if there is one
foreach (var ini in property.Attributes.OfType<Serialize>())
{
property.TrySetValue(obj, ini.defaultValue);
break;
}
}
if (element != null)
{
//go through all the attributes in the xml element
//and set the value of the matching property if it is initializable
foreach (XAttribute attribute in element.Attributes())
{
if (!dictionary.TryGetValue(attribute.Name.ToString().ToLowerInvariant(), out SerializableProperty property)) { continue; }
if (!property.Attributes.OfType<Serialize>().Any()) { continue; }
property.TrySetValue(obj, attribute.Value);
}
}
return dictionary;
}
public static void SerializeProperties(ISerializableEntity obj, XElement element, bool saveIfDefault = false)
{
var saveProperties = GetProperties<Serialize>(obj);
foreach (var property in saveProperties)
{
object value = property.GetValue(obj);
if (value == null) continue;
if (!saveIfDefault)
{
//only save
// - if the attribute is saveable and it's different from the default value
// - or can be changed in-game or in the editor
bool save = false;
foreach (var attribute in property.Attributes.OfType<Serialize>())
{
if ((attribute.isSaveable && !attribute.defaultValue.Equals(value)) ||
property.Attributes.OfType<Editable>().Any())
{
save = true;
break;
}
}
if (!save) continue;
}
string stringValue;
if (!supportedTypes.TryGetValue(value.GetType(), out string typeName))
{
if (property.PropertyType.IsEnum)
{
stringValue = value.ToString();
}
else
{
DebugConsole.ThrowError("Failed to serialize the property \"" + property.Name + "\" of \"" + obj + "\" (type " + property.PropertyType + " not supported)");
continue;
}
}
else
{
switch (typeName)
{
case "float":
//make sure the decimal point isn't converted to a comma or anything else
stringValue = ((float)value).ToString("G", CultureInfo.InvariantCulture);
break;
case "point":
stringValue = XMLExtensions.PointToString((Point)value);
break;
case "vector2":
stringValue = XMLExtensions.Vector2ToString((Vector2)value);
break;
case "vector3":
stringValue = XMLExtensions.Vector3ToString((Vector3)value);
break;
case "vector4":
stringValue = XMLExtensions.Vector4ToString((Vector4)value);
break;
case "color":
stringValue = XMLExtensions.ColorToString((Color)value);
break;
case "rectangle":
stringValue = XMLExtensions.RectToString((Rectangle)value);
break;
default:
stringValue = value.ToString();
break;
}
}
element.Attribute(property.Name)?.Remove();
element.SetAttributeValue(property.NameToLowerInvariant, stringValue);
}
}
/// <summary>
/// Upgrade the properties of an entity saved with an older version of the game. Properties that should be upgraded are defined using "Upgrade" elements in the config file.
/// for example, <Upgrade gameversion="0.9.2.0" scale="0.5"/> would force the scale of the entity to 0.5 if it was saved with a version prior to 0.9.2.0.
/// </summary>
/// <param name="entity">The entity to upgrade</param>
/// <param name="configElement">The XML element to get the upgrade instructions from (e.g. the config of an item prefab)</param>
/// <param name="savedVersion">The game version the entity was saved with</param>
public static void UpgradeGameVersion(ISerializableEntity entity, XElement configElement, Version savedVersion)
{
foreach (XElement subElement in configElement.Elements())
{
if (!subElement.Name.ToString().Equals("upgrade", StringComparison.OrdinalIgnoreCase)) { continue; }
var upgradeVersion = new Version(subElement.GetAttributeString("gameversion", "0.0.0.0"));
if (savedVersion >= upgradeVersion) { continue; }
foreach (XAttribute attribute in subElement.Attributes())
{
string attributeName = attribute.Name.ToString().ToLowerInvariant();
if (attributeName == "gameversion") { continue; }
if (entity.SerializableProperties.TryGetValue(attributeName, out SerializableProperty property))
{
property.TrySetValue(entity, attribute.Value);
}
else if (entity is Item item1)
{
foreach (ISerializableEntity component in item1.AllPropertyObjects)
{
if (component.SerializableProperties.TryGetValue(attributeName, out SerializableProperty componentProperty))
{
componentProperty.TrySetValue(component, attribute.Value);
}
}
}
}
if (entity is Item item2)
{
XElement componentElement = subElement.FirstElement();
if (componentElement == null) continue;
ItemComponent itemComponent = item2.Components.First(c => c.Name == componentElement.Name.ToString());
if (itemComponent == null) continue;
foreach (XElement element in componentElement.Elements())
{
switch (element.Name.ToString().ToLowerInvariant())
{
case "requireditem":
case "requireditems":
itemComponent.requiredItems.Clear();
itemComponent.DisabledRequiredItems.Clear();
itemComponent.SetRequiredItems(element);
break;
}
}
}
}
}
}
}
@@ -0,0 +1,666 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
public static class XMLExtensions
{
public static string ParseContentPathFromUri(this XObject element) => ToolBox.ConvertAbsoluteToRelativePath(element.BaseUri);
public static XDocument TryLoadXml(string filePath)
{
XDocument doc;
try
{
ToolBox.IsProperFilenameCase(filePath);
doc = XDocument.Load(filePath, LoadOptions.SetBaseUri);
}
catch (Exception e)
{
DebugConsole.ThrowError("Couldn't load xml document \"" + filePath + "\"!", e);
return null;
}
if (doc?.Root == null)
{
DebugConsole.ThrowError("File \"" + filePath + "\" could not be loaded: Document or the root element is invalid!");
return null;
}
return doc;
}
public static XDocument LoadXml(string filePath)
{
XDocument doc = null;
ToolBox.IsProperFilenameCase(filePath);
if (File.Exists(filePath))
{
try
{
doc = XDocument.Load(filePath, LoadOptions.SetBaseUri);
}
catch
{
return null;
}
if (doc.Root == null) return null;
}
return doc;
}
public static object GetAttributeObject(XAttribute attribute)
{
if (attribute == null) return null;
return ParseToObject(attribute.Value.ToString());
}
public static object ParseToObject(string value)
{
float floatVal;
int intVal;
if (value.Contains(".") && Single.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out floatVal))
{
return floatVal;
}
if (Int32.TryParse(value, out intVal))
{
return intVal;
}
string lowerTrimmedVal = value.ToLowerInvariant().Trim();
if (lowerTrimmedVal == "true")
{
return true;
}
if (lowerTrimmedVal == "false")
{
return false;
}
return value;
}
public static string GetAttributeString(this XElement element, string name, string defaultValue)
{
if (element?.Attribute(name) == null) return defaultValue;
return GetAttributeString(element.Attribute(name), defaultValue);
}
private static string GetAttributeString(XAttribute attribute, string defaultValue)
{
string value = attribute.Value;
return String.IsNullOrEmpty(value) ? defaultValue : value;
}
public static string[] GetAttributeStringArray(this XElement element, string name, string[] defaultValue, bool trim = true, bool convertToLowerInvariant = false)
{
if (element?.Attribute(name) == null) return defaultValue;
string stringValue = element.Attribute(name).Value;
if (string.IsNullOrEmpty(stringValue)) return defaultValue;
string[] splitValue = stringValue.Split(',', '');
if (convertToLowerInvariant)
{
for (int i = 0; i < splitValue.Length; i++)
{
splitValue[i] = splitValue[i].ToLowerInvariant();
}
}
if (trim)
{
for (int i = 0; i < splitValue.Length; i++)
{
splitValue[i] = splitValue[i].Trim();
}
}
return splitValue;
}
public static float GetAttributeFloat(this XElement element, float defaultValue, params string[] matchingAttributeName)
{
if (element == null) return defaultValue;
foreach (string name in matchingAttributeName)
{
if (element.Attribute(name) == null) continue;
float val;
try
{
if (!Single.TryParse(element.Attribute(name).Value, NumberStyles.Float, CultureInfo.InvariantCulture, out val))
{
continue;
}
}
catch (Exception e)
{
DebugConsole.ThrowError("Error in " + element + "!", e);
continue;
}
return val;
}
return defaultValue;
}
public static float GetAttributeFloat(this XElement element, string name, float defaultValue)
{
if (element?.Attribute(name) == null) return defaultValue;
float val = defaultValue;
try
{
if (!Single.TryParse(element.Attribute(name).Value, NumberStyles.Float, CultureInfo.InvariantCulture, out val))
{
return defaultValue;
}
}
catch (Exception e)
{
DebugConsole.ThrowError("Error in " + element + "!", e);
}
return val;
}
public static float GetAttributeFloat(this XAttribute attribute, float defaultValue)
{
if (attribute == null) return defaultValue;
float val = defaultValue;
try
{
val = Single.Parse(attribute.Value, CultureInfo.InvariantCulture);
}
catch (Exception e)
{
DebugConsole.ThrowError("Error in " + attribute + "! ", e);
}
return val;
}
public static float[] GetAttributeFloatArray(this XElement element, string name, float[] defaultValue)
{
if (element?.Attribute(name) == null) return defaultValue;
string stringValue = element.Attribute(name).Value;
if (string.IsNullOrEmpty(stringValue)) return defaultValue;
string[] splitValue = stringValue.Split(',');
float[] floatValue = new float[splitValue.Length];
for (int i = 0; i < splitValue.Length; i++)
{
try
{
float val = Single.Parse(splitValue[i], CultureInfo.InvariantCulture);
floatValue[i] = val;
}
catch (Exception e)
{
DebugConsole.ThrowError("Error in " + element + "! ", e);
}
}
return floatValue;
}
public static int GetAttributeInt(this XElement element, string name, int defaultValue)
{
if (element?.Attribute(name) == null) return defaultValue;
int val = defaultValue;
try
{
val = Int32.Parse(element.Attribute(name).Value);
}
catch (Exception e)
{
DebugConsole.ThrowError("Error in " + element + "! ", e);
}
return val;
}
public static uint GetAttributeUInt(this XElement element, string name, uint defaultValue)
{
if (element?.Attribute(name) == null) return defaultValue;
uint val = defaultValue;
try
{
val = UInt32.Parse(element.Attribute(name).Value);
}
catch (Exception e)
{
DebugConsole.ThrowError("Error in " + element + "! ", e);
}
return val;
}
public static UInt64 GetAttributeUInt64(this XElement element, string name, UInt64 defaultValue)
{
if (element?.Attribute(name) == null) return defaultValue;
UInt64 val = defaultValue;
try
{
val = UInt64.Parse(element.Attribute(name).Value);
}
catch (Exception e)
{
DebugConsole.ThrowError("Error in " + element + "! ", e);
}
return val;
}
public static UInt64 GetAttributeSteamID(this XElement element, string name, UInt64 defaultValue)
{
if (element?.Attribute(name) == null) return defaultValue;
UInt64 val = defaultValue;
try
{
val = Steam.SteamManager.SteamIDStringToUInt64(element.Attribute(name).Value);
}
catch (Exception e)
{
DebugConsole.ThrowError("Error in " + element + "! ", e);
}
return val;
}
public static int[] GetAttributeIntArray(this XElement element, string name, int[] defaultValue)
{
if (element?.Attribute(name) == null) return defaultValue;
string stringValue = element.Attribute(name).Value;
if (string.IsNullOrEmpty(stringValue)) return defaultValue;
string[] splitValue = stringValue.Split(',');
int[] intValue = new int[splitValue.Length];
for (int i = 0; i < splitValue.Length; i++)
{
try
{
int val = Int32.Parse(splitValue[i]);
intValue[i] = val;
}
catch (Exception e)
{
DebugConsole.ThrowError("Error in " + element + "! ", e);
}
}
return intValue;
}
public static ushort[] GetAttributeUshortArray(this XElement element, string name, ushort[] defaultValue)
{
if (element?.Attribute(name) == null) return defaultValue;
string stringValue = element.Attribute(name).Value;
if (string.IsNullOrEmpty(stringValue)) return defaultValue;
string[] splitValue = stringValue.Split(',');
ushort[] ushortValue = new ushort[splitValue.Length];
for (int i = 0; i < splitValue.Length; i++)
{
try
{
ushort val = ushort.Parse(splitValue[i]);
ushortValue[i] = val;
}
catch (Exception e)
{
DebugConsole.ThrowError("Error in " + element + "! ", e);
}
}
return ushortValue;
}
public static bool GetAttributeBool(this XElement element, string name, bool defaultValue)
{
if (element?.Attribute(name) == null) return defaultValue;
return element.Attribute(name).GetAttributeBool(defaultValue);
}
public static bool GetAttributeBool(this XAttribute attribute, bool defaultValue)
{
if (attribute == null) return defaultValue;
string val = attribute.Value.ToLowerInvariant().Trim();
if (val == "true")
{
return true;
}
if (val == "false")
{
return false;
}
DebugConsole.ThrowError("Error in " + attribute.Value.ToString() + "! \"" + val + "\" is not a valid boolean value");
return false;
}
public static Point GetAttributePoint(this XElement element, string name, Point defaultValue)
{
if (element?.Attribute(name) == null) return defaultValue;
return ParsePoint(element.Attribute(name).Value);
}
public static Vector2 GetAttributeVector2(this XElement element, string name, Vector2 defaultValue)
{
if (element?.Attribute(name) == null) return defaultValue;
return ParseVector2(element.Attribute(name).Value);
}
public static Vector3 GetAttributeVector3(this XElement element, string name, Vector3 defaultValue)
{
if (element == null || element.Attribute(name) == null) return defaultValue;
return ParseVector3(element.Attribute(name).Value);
}
public static Vector4 GetAttributeVector4(this XElement element, string name, Vector4 defaultValue)
{
if (element == null || element.Attribute(name) == null) return defaultValue;
return ParseVector4(element.Attribute(name).Value);
}
public static Color GetAttributeColor(this XElement element, string name, Color defaultValue)
{
if (element == null || element.Attribute(name) == null) return defaultValue;
return ParseColor(element.Attribute(name).Value);
}
public static Color? GetAttributeColor(this XElement element, string name)
{
if (element == null || element.Attribute(name) == null) { return null; }
return ParseColor(element.Attribute(name).Value);
}
public static Color[] GetAttributeColorArray(this XElement element, string name, Color[] defaultValue)
{
if (element?.Attribute(name) == null) return defaultValue;
string stringValue = element.Attribute(name).Value;
if (string.IsNullOrEmpty(stringValue)) return defaultValue;
string[] splitValue = stringValue.Split(';');
Color[] colorValue = new Color[splitValue.Length];
for (int i = 0; i < splitValue.Length; i++)
{
try
{
Color val = ParseColor(splitValue[i], true);
colorValue[i] = val;
}
catch (Exception e)
{
DebugConsole.ThrowError("Error in " + element + "! ", e);
}
}
return colorValue;
}
public static Rectangle GetAttributeRect(this XElement element, string name, Rectangle defaultValue)
{
if (element == null || element.Attribute(name) == null) return defaultValue;
return ParseRect(element.Attribute(name).Value, false);
}
public static string ElementInnerText(this XElement el)
{
StringBuilder str = new StringBuilder();
foreach (XNode element in el.DescendantNodes().Where(x => x.NodeType == XmlNodeType.Text))
{
str.Append(element.ToString());
}
return str.ToString();
}
public static string PointToString(Point point)
{
return point.X.ToString() + "," + point.Y.ToString();
}
public static string Vector2ToString(Vector2 vector)
{
return vector.X.ToString("G", CultureInfo.InvariantCulture) + "," + vector.Y.ToString("G", CultureInfo.InvariantCulture);
}
public static string Vector3ToString(Vector3 vector, string format = "G")
{
return vector.X.ToString(format, CultureInfo.InvariantCulture) + "," +
vector.Y.ToString(format, CultureInfo.InvariantCulture) + "," +
vector.Z.ToString(format, CultureInfo.InvariantCulture);
}
public static string Vector4ToString(Vector4 vector, string format = "G")
{
return vector.X.ToString(format, CultureInfo.InvariantCulture) + "," +
vector.Y.ToString(format, CultureInfo.InvariantCulture) + "," +
vector.Z.ToString(format, CultureInfo.InvariantCulture) + "," +
vector.W.ToString(format, CultureInfo.InvariantCulture);
}
public static string ColorToString(Color color)
{
return color.R + "," + color.G + "," + color.B + "," + color.A;
}
public static string RectToString(Rectangle rect)
{
return rect.X + "," + rect.Y + "," + rect.Width + "," + rect.Height;
}
public static Point ParsePoint(string stringPoint, bool errorMessages = true)
{
string[] components = stringPoint.Split(',');
Point point = Point.Zero;
if (components.Length != 2)
{
if (!errorMessages) return point;
DebugConsole.ThrowError("Failed to parse the string \"" + stringPoint + "\" to Vector2");
return point;
}
int.TryParse(components[0], NumberStyles.Any, CultureInfo.InvariantCulture, out point.X);
int.TryParse(components[1], NumberStyles.Any, CultureInfo.InvariantCulture, out point.Y);
return point;
}
public static Vector2 ParseVector2(string stringVector2, bool errorMessages = true)
{
string[] components = stringVector2.Split(',');
Vector2 vector = Vector2.Zero;
if (components.Length != 2)
{
if (!errorMessages) return vector;
DebugConsole.ThrowError("Failed to parse the string \"" + stringVector2 + "\" to Vector2");
return vector;
}
float.TryParse(components[0], NumberStyles.Any, CultureInfo.InvariantCulture, out vector.X);
float.TryParse(components[1], NumberStyles.Any, CultureInfo.InvariantCulture, out vector.Y);
return vector;
}
public static Vector3 ParseVector3(string stringVector3, bool errorMessages = true)
{
string[] components = stringVector3.Split(',');
Vector3 vector = Vector3.Zero;
if (components.Length != 3)
{
if (!errorMessages) return vector;
DebugConsole.ThrowError("Failed to parse the string \"" + stringVector3 + "\" to Vector3");
return vector;
}
Single.TryParse(components[0], NumberStyles.Any, CultureInfo.InvariantCulture, out vector.X);
Single.TryParse(components[1], NumberStyles.Any, CultureInfo.InvariantCulture, out vector.Y);
Single.TryParse(components[2], NumberStyles.Any, CultureInfo.InvariantCulture, out vector.Z);
return vector;
}
public static Vector4 ParseVector4(string stringVector4, bool errorMessages = true)
{
string[] components = stringVector4.Split(',');
Vector4 vector = Vector4.Zero;
if (components.Length < 3)
{
if (errorMessages) DebugConsole.ThrowError("Failed to parse the string \"" + stringVector4 + "\" to Vector4");
return vector;
}
Single.TryParse(components[0], NumberStyles.Float, CultureInfo.InvariantCulture, out vector.X);
Single.TryParse(components[1], NumberStyles.Float, CultureInfo.InvariantCulture, out vector.Y);
Single.TryParse(components[2], NumberStyles.Float, CultureInfo.InvariantCulture, out vector.Z);
if (components.Length > 3)
Single.TryParse(components[3], NumberStyles.Float, CultureInfo.InvariantCulture, out vector.W);
return vector;
}
public static Color ParseColor(string stringColor, bool errorMessages = true)
{
string[] strComponents = stringColor.Split(',');
Color color = Color.White;
float[] components = new float[4] { 1.0f, 1.0f, 1.0f, 1.0f };
if (strComponents.Length == 1)
{
bool hexFailed = true;
stringColor = stringColor.Trim();
if (stringColor.Length > 0 && stringColor[0] == '#')
{
stringColor = stringColor.Substring(1);
int colorInt = 0;
if (int.TryParse(stringColor, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out colorInt))
{
if (stringColor.Length == 6)
{
colorInt = (colorInt << 8) | 0xff;
}
components[0] = ((float)((colorInt & 0xff000000) >> 24)) / 255.0f;
components[1] = ((float)((colorInt & 0x00ff0000) >> 16)) / 255.0f;
components[2] = ((float)((colorInt & 0x0000ff00) >> 8)) / 255.0f;
components[3] = ((float)(colorInt & 0x000000ff)) / 255.0f;
hexFailed = false;
}
}
if (hexFailed)
{
if (errorMessages) DebugConsole.ThrowError("Failed to parse the string \"" + stringColor + "\" to Color");
return Color.White;
}
}
else
{
for (int i = 0; i < 4 && i < strComponents.Length; i++)
{
float.TryParse(strComponents[i], NumberStyles.Float, CultureInfo.InvariantCulture, out components[i]);
}
if (components.Any(c => c > 1.0f))
{
for (int i = 0; i < 4; i++)
{
components[i] = components[i] / 255.0f;
}
//alpha defaults to 1.0 if not given
if (strComponents.Length < 4) components[3] = 1.0f;
}
}
return new Color(components[0], components[1], components[2], components[3]);
}
public static Rectangle ParseRect(string stringRect, bool requireSize, bool errorMessages = true)
{
string[] strComponents = stringRect.Split(',');
if ((strComponents.Length < 3 && requireSize) || strComponents.Length < 2)
{
if (errorMessages) DebugConsole.ThrowError("Failed to parse the string \"" + stringRect + "\" to Rectangle");
return new Rectangle(0, 0, 0, 0);
}
int[] components = new int[4] { 0, 0, 0, 0 };
for (int i = 0; i < 4 && i < strComponents.Length; i++)
{
int.TryParse(strComponents[i], out components[i]);
}
return new Rectangle(components[0], components[1], components[2], components[3]);
}
public static float[] ParseFloatArray(string[] stringArray)
{
if (stringArray == null || stringArray.Length == 0) return null;
float[] floatArray = new float[stringArray.Length];
for (int i = 0; i < floatArray.Length; i++)
{
floatArray[i] = 0.0f;
Single.TryParse(stringArray[i], NumberStyles.Float, CultureInfo.InvariantCulture, out floatArray[i]);
}
return floatArray;
}
public static bool IsOverride(this XElement element) => element.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase);
public static XElement FirstElement(this XElement element) => element.Elements().FirstOrDefault();
/// <summary>
/// Returns the first child element that matches the name using the provided comparison method.
/// </summary>
public static XElement GetChildElement(this XContainer container, string name, StringComparison comparisonMethod = StringComparison.OrdinalIgnoreCase) => container.Elements().FirstOrDefault(e => e.Name.ToString().Equals(name, comparisonMethod));
/// <summary>
/// Returns all child elements that match the name using the provided comparison method.
/// </summary>
public static IEnumerable<XElement> GetChildElements(this XContainer container, string name, StringComparison comparisonMethod = StringComparison.OrdinalIgnoreCase) => container.Elements().Where(e => e.Name.ToString().Equals(name, comparisonMethod));
}
}