/// <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; } }