/// <summary> Creates a new Rank object from given XML definition.
/// XML schema is expected to match the output of Rank.Serialize() </summary>
/// <param name="el"> XML element to parse as a rank. </param>
/// <exception cref="ArgumentNullException"> el is null </exception>
/// <exception cref="RankDefinitionException"> Given XML element could not be parsed as a rank definition. </exception>
public Rank([NotNull] XElement el)
: this()
{
if (el == null)
{
throw new ArgumentNullException("el");
}
// Name
XAttribute attr = el.Attribute("name");
if (attr == null)
{
throw new RankDefinitionException(null, "Rank definition with no name was ignored.");
}
else if (!IsValidRankName(attr.Value.Trim()))
{
throw new RankDefinitionException(Name,
"Invalid name specified for rank \"{0}\". " +
"Rank names can only contain letters, digits, and underscores. " +
"Rank definition was ignored.",
Name);
}
else
{
// duplicate Name check is done in RankManager.AddRank()
Name = attr.Value.Trim();
}
// ID
attr = el.Attribute("id");
if (attr == null)
{
Id = RankManager.GenerateId();
Logger.Log(LogType.Warning,
"Rank({0}): No ID specified; issued a new unique ID: {1}",
Name,
Id);
}
else if (!IsValidID(attr.Value.Trim()))
{
Id = RankManager.GenerateId();
Logger.Log(LogType.Warning,
"Rank({0}): Invalid ID specified (must be alphanumeric, and exactly 16 characters long); issued a new unique ID: {1}",
Name,
Id);
}
else
{
Id = attr.Value.Trim();
// duplicate ID check is done in RankManager.AddRank()
}
FullName = Name + "#" + Id;
// Color (optional)
if ((attr = el.Attribute("color")) != null)
{
string color = ChatColor.Parse(attr.Value);
if (color == null)
{
Logger.Log(LogType.Warning,
"Rank({0}): Could not parse rank color. Assuming default (none).",
Name);
Color = ChatColor.White;
}
else
{
Color = color;
}
}
else
{
Color = ChatColor.White;
}
// Prefix (optional)
if ((attr = el.Attribute("prefix")) != null)
{
if (IsValidPrefix(attr.Value))
{
Prefix = attr.Value;
}
else
{
Logger.Log(LogType.Warning,
"Rank({0}): Invalid prefix format. Expecting 1 character.",
Name);
}
}
// AntiGrief block limit (assuming unlimited if not given)
int value;
XAttribute agBlocks = el.Attribute("antiGriefBlocks");
XAttribute agSeconds = el.Attribute("antiGriefSeconds");
if (agBlocks != null && agSeconds != null)
{
if (Int32.TryParse(agBlocks.Value, out value))
{
if (value >= 0 && value < 1000)
{
AntiGriefBlocks = value;
}
else
{
Logger.Log(LogType.Warning,
"Rank({0}): Value for antiGriefBlocks is not within valid range (0-1000). Assuming default ({1}).",
Name,
AntiGriefBlocks);
}
}
else
{
Logger.Log(LogType.Warning,
"Rank({0}): Could not parse the value for antiGriefBlocks. Assuming default ({1}).",
Name,
AntiGriefBlocks);
}
if (Int32.TryParse(agSeconds.Value, out value))
{
if (value >= 0 && value < 100)
{
AntiGriefSeconds = value;
}
else
{
Logger.Log(LogType.Warning,
"Rank({0}): Value for antiGriefSeconds is not within valid range (0-100). Assuming default ({1}).",
Name,
AntiGriefSeconds);
}
}
else
{
Logger.Log(LogType.Warning,
"Rank({0}): Could not parse the value for antiGriefSeconds. Assuming default ({1}).",
Name,
AntiGriefSeconds);
}
}
// Draw command limit, in number-of-blocks (assuming unlimited if not given)
if ((attr = el.Attribute("drawLimit")) != null)
{
if (Int32.TryParse(attr.Value, out value))
{
if (value >= 0 && value < 100000000)
{
DrawLimit = value;
}
else
{
Logger.Log(LogType.Warning,
"Rank({0}): Value for drawLimit is not within valid range (0-100000000). Assuming default ({1}).",
Name,
DrawLimit);
}
}
else
{
Logger.Log(LogType.Warning,
"Rank({0}): Could not parse the value for drawLimit. Assuming default ({1}).",
Name,
DrawLimit);
}
}
// Idle kick timer, in minutes. (assuming 'never' if not given)
if ((attr = el.Attribute("idleKickAfter")) != null)
{
if (!Int32.TryParse(attr.Value, out IdleKickTimer))
{
Logger.Log(LogType.Warning,
"Rank({0}): Could not parse the value for idleKickAfter. Assuming 0 (never).",
Name);
IdleKickTimer = 0;
}
}
else
{
IdleKickTimer = 0;
}
// Reserved slot. (assuming 'no' if not given)
if ((attr = el.Attribute("reserveSlot")) != null)
{
if (!Boolean.TryParse(attr.Value, out HasReservedSlot))
{
Logger.Log(LogType.Warning,
"Rank({0}): Could not parse value for reserveSlot. Assuming \"false\".",
Name);
HasReservedSlot = false;
}
}
else
{
HasReservedSlot = false;
}
// Security circumvention. (assuming 'no' if not given)
if ((attr = el.Attribute("allowSecurityCircumvention")) != null)
{
if (!Boolean.TryParse(attr.Value, out AllowSecurityCircumvention))
{
Logger.Log(LogType.Warning,
"Rank({0}): Could not parse the value for allowSecurityCircumvention. Assuming \"false\".",
Name);
AllowSecurityCircumvention = false;
}
}
else
{
AllowSecurityCircumvention = false;
}
// Copy slots (assuming default 2 if not given)
if ((attr = el.Attribute("copySlots")) != null)
{
if (Int32.TryParse(attr.Value, out value))
{
if (value > 0 && value < 256)
{
CopySlots = value;
}
else
{
Logger.Log(LogType.Warning,
"Rank({0}): Value for copySlots is not within valid range (1-255). Assuming default ({1}).",
Name,
CopySlots);
}
}
else
{
Logger.Log(LogType.Warning,
"Rank({0}): Could not parse the value for copySlots. Assuming default ({1}).",
Name,
CopySlots);
}
}
// Fill limit (assuming default 32 if not given)
if ((attr = el.Attribute("fillLimit")) != null)
{
if (Int32.TryParse(attr.Value, out value))
{
if (value < 1)
{
Logger.Log(LogType.Warning,
"Rank({0}): Value for fillLimit may not be negative. Assuming default ({1}).",
Name,
FillLimit);
}
else if (value > 2048)
{
FillLimit = 2048;
}
else
{
FillLimit = value;
}
}
else
{
Logger.Log(LogType.Warning,
"Rank({0}): Could not parse the value for fillLimit. Assuming default ({1}).",
Name,
FillLimit);
}
}
// Permissions
for (int i = 0; i < PermissionNames.Length; i++)
{
string permission = PermissionNames[i];
XElement temp;
if ((temp = el.Element(permission)) != null)
{
Permissions[i] = true;
if ((attr = temp.Attribute("max")) != null)
{
permissionLimitStrings[i] = attr.Value;
}
}
}
// check consistency of ban permissions
if (!Can(Permission.Ban) && (Can(Permission.BanAll) || Can(Permission.BanIP)))
{
Logger.Log(LogType.Warning,
"Rank({0}): Rank is allowed to BanIP and/or BanAll but not allowed to Ban. " +
"Assuming that all ban permissions were meant to be off.",
Name);
Permissions[(int)Permission.BanIP] = false;
Permissions[(int)Permission.BanAll] = false;
}
// check consistency of patrol permissions
if (!Can(Permission.Teleport) && Can(Permission.Patrol))
{
Logger.Log(LogType.Warning,
"Rank({0}): Rank is allowed to Patrol but not allowed to Teleport. " +
"Assuming that Patrol permission was meant to be off.",
Name);
Permissions[(int)Permission.Patrol] = false;
}
// check consistency of draw permissions
if (!Can(Permission.Draw) && Can(Permission.DrawAdvanced))
{
Logger.Log(LogType.Warning,
"Rank({0}): Rank is allowed to DrawAdvanced but not allowed to Draw. " +
"Assuming that DrawAdvanced permission was meant to be off.",
Name);
Permissions[(int)Permission.DrawAdvanced] = false;
}
// check consistency of Undo permissions
if (!Can(Permission.UndoOthersActions) && Can(Permission.UndoAll))
{
Logger.Log(LogType.Warning,
"Rank({0}): Rank is allowed to UndoAll but not allowed to UndoOthersActions. " +
"Assuming that UndoAll permission was meant to be off.",
Name);
Permissions[(int)Permission.UndoAll] = false;
}
}