From 84d8049ac699f0549bbcff956f1c1a62d0144022 Mon Sep 17 00:00:00 2001 From: Zack Piispanen Date: Fri, 11 Apr 2014 13:44:47 -0400 Subject: [PATCH] Add projectile bans and the commands needed to set them up. --- TShockAPI/Commands.cs | 186 +++++++++++++++++++++- TShockAPI/DB/ProjectileManager.cs | 253 ++++++++++++++++++++++++++++++ TShockAPI/GetDataHandlers.cs | 10 +- TShockAPI/Permissions.cs | 6 + TShockAPI/TShock.cs | 2 + TShockAPI/TShockAPI.csproj | 3 +- 6 files changed, 456 insertions(+), 4 deletions(-) create mode 100644 TShockAPI/DB/ProjectileManager.cs diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs index 547b0711..261df60c 100755 --- a/TShockAPI/Commands.cs +++ b/TShockAPI/Commands.cs @@ -235,6 +235,10 @@ namespace TShockAPI { HelpText = "Manages item bans." }); + add(new Command(Permissions.manageprojectile, ProjectileBan, "projban") + { + HelpText = "Manages item bans." + }); add(new Command(Permissions.manageregion, Region, "region") { HelpText = "Manages regions." @@ -2769,9 +2773,187 @@ namespace TShockAPI } #endregion Item Management - #region Server Config Commands + #region Projectile Management - private static void SetSpawn(CommandArgs args) + private static void ProjectileBan(CommandArgs args) + { + if (args.Parameters.Count == 0) + { + args.Player.SendInfoMessage("Invalid syntax! Proper syntax: /projban [arguments]"); + args.Player.SendInfoMessage("Commands: add, allow, del, disallow, list"); + args.Player.SendInfoMessage("Arguments: add , allow "); + args.Player.SendInfoMessage("Arguments: del , disallow , list [page]"); + return; + } + + switch (args.Parameters[0].ToLower()) + { + case "add": + #region Add projectile + { + if (args.Parameters.Count != 2) + { + args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /projban add "); + return; + } + short id; + if (Int16.TryParse(args.Parameters[1], out id)) + { + TShock.ProjectileBans.AddNewBan(id); + args.Player.SendSuccessMessage("Banned Projectile: " + id + "."); + return; + } + else + { + args.Player.SendErrorMessage("Invalid syntax! Projectile Id must be a number."); + return; + } + + } + #endregion + return; + case "allow": + #region Allow group to projectile + { + if (args.Parameters.Count != 3) + { + args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /projban allow "); + return; + } + + short id; + if (Int16.TryParse(args.Parameters[1], out id)) + { + if (!TShock.Groups.GroupExists(args.Parameters[2])) + { + args.Player.SendErrorMessage("Invalid group."); + return; + } + + ProjectileBan ban = TShock.ProjectileBans.GetBanById(id); + if (ban == null) + { + args.Player.SendErrorMessage("Projectile " + id + " is not banned."); + return; + } + if (!ban.AllowedGroups.Contains(args.Parameters[2])) + { + TShock.ProjectileBans.AllowGroup(id, args.Parameters[2]); + args.Player.SendSuccessMessage(String.Format("{0} has been allowed to use projectile {1}.", args.Parameters[2], id)); + return; + } + else + { + args.Player.SendWarningMessage(String.Format("{0} is already allowed to use projectile {1}.", args.Parameters[2], id)); + return; + } + } + else + { + args.Player.SendErrorMessage("Invalid syntax! Projectile Id must be a number."); + return; + } + } + #endregion + + case "del": + #region Delete item + { + if (args.Parameters.Count != 2) + { + args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /projban del "); + return; + } + + short id; + if (Int16.TryParse(args.Parameters[1], out id)) + { + TShock.ProjectileBans.RemoveBan(id); + args.Player.SendSuccessMessage("Unbanned Projectile: " + id + "."); + return; + } + else + { + args.Player.SendErrorMessage("Invalid syntax! Projectile Id must be a number."); + return; + } + } + #endregion + return; + case "disallow": + #region Allow group to item + { + if (args.Parameters.Count != 3) + { + args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /projban disallow "); + return; + } + + short id; + if (Int16.TryParse(args.Parameters[1], out id)) + { + if (!TShock.Groups.GroupExists(args.Parameters[2])) + { + args.Player.SendErrorMessage("Invalid group."); + return; + } + + ProjectileBan ban = TShock.ProjectileBans.GetBanById(id); + if (ban == null) + { + args.Player.SendErrorMessage("Projectile " + id + " is not banned."); + return; + } + if (ban.AllowedGroups.Contains(args.Parameters[2])) + { + TShock.ProjectileBans.RemoveGroup(id, args.Parameters[2]); + args.Player.SendSuccessMessage(String.Format("{0} has been disallowed from using projectile {1}.", args.Parameters[2], id)); + return; + } + else + { + args.Player.SendWarningMessage(String.Format("{0} is already prevented from using projectile {1}.", args.Parameters[2], id)); + return; + } + } + else + { + args.Player.SendErrorMessage("Invalid syntax! Projectile Id must be a number."); + return; + } + } + #endregion + return; + case "help": + args.Player.SendInfoMessage("Syntax: /projban [arguments]"); + args.Player.SendInfoMessage("Commands: add, allow, del, disallow, list"); + args.Player.SendInfoMessage("Arguments: add , allow "); + args.Player.SendInfoMessage("Arguments: del , disallow , list [page]"); + return; + case "list": + #region List items + int pageNumber; + if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber)) + return; + IEnumerable projectileIds = from projectileBan in TShock.ProjectileBans.ProjectileBans + select projectileBan.ID; + PaginationTools.SendPage(args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(projectileIds), + new PaginationTools.Settings + { + HeaderFormat = "Projectile bans ({0}/{1}):", + FooterFormat = "Type /Projectile list {0} for more.", + NothingToDisplayString = "There are currently no banned projectiles." + }); + #endregion + return; + } + } + + #endregion Projectile Management + + #region Server Config Commands + + private static void SetSpawn(CommandArgs args) { Main.spawnTileX = args.Player.TileX + 1; Main.spawnTileY = args.Player.TileY + 3; diff --git a/TShockAPI/DB/ProjectileManager.cs b/TShockAPI/DB/ProjectileManager.cs new file mode 100644 index 00000000..961baacf --- /dev/null +++ b/TShockAPI/DB/ProjectileManager.cs @@ -0,0 +1,253 @@ +/* +TShock, a server mod for Terraria +Copyright (C) 2011-2013 Nyx Studios (fka. The TShock Team) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using MySql.Data.MySqlClient; + +namespace TShockAPI.DB +{ + public class ProjectileManagager + { + private IDbConnection database; + public List ProjectileBans = new List(); + + public ProjectileManagager(IDbConnection db) + { + database = db; + + var table = new SqlTable("ProjectileBans", + new SqlColumn("ProjectileID", MySqlDbType.Int32) { Primary = true }, + new SqlColumn("AllowedGroups", MySqlDbType.Text) + ); + var creator = new SqlTableCreator(db, + db.GetSqlType() == SqlType.Sqlite + ? (IQueryBuilder)new SqliteQueryCreator() + : new MysqlQueryCreator()); + creator.EnsureExists(table); + UpdateBans(); + } + + public void UpdateBans() + { + ProjectileBans.Clear(); + + using (var reader = database.QueryReader("SELECT * FROM ProjectileBans")) + { + while (reader != null && reader.Read()) + { + ProjectileBan ban = new ProjectileBan((short)reader.Get("ProjectileID")); + ban.SetAllowedGroups(reader.Get("AllowedGroups")); + ProjectileBans.Add(ban); + } + } + } + + public void AddNewBan(short id = 0) + { + try + { + database.Query("INSERT INTO ProjectileBans (ProjectileID, AllowedGroups) VALUES (@0, @1);", + id, ""); + + if (!ProjectileIsBanned(id, null)) + ProjectileBans.Add(new ProjectileBan(id)); + } + catch (Exception ex) + { + Log.Error(ex.ToString()); + } + } + + public void RemoveBan(short id) + { + if (!ProjectileIsBanned(id, null)) + return; + try + { + database.Query("DELETE FROM ProjectileBans WHERE ProjectileId=@0;", id); + ProjectileBans.Remove(new ProjectileBan(id)); + } + catch (Exception ex) + { + Log.Error(ex.ToString()); + } + } + + public bool ProjectileIsBanned(short id) + { + if (ProjectileBans.Contains(new ProjectileBan(id))) + { + return true; + } + return false; + } + + public bool ProjectileIsBanned(short id, TSPlayer ply) + { + if (ProjectileBans.Contains(new ProjectileBan(id))) + { + ProjectileBan b = GetBanById(id); + return !b.HasPermissionToCreateProjectile(ply); + } + return false; + } + + public bool AllowGroup(short id, string name) + { + string groupsNew = ""; + ProjectileBan b = GetBanById(id); + if (b != null) + { + try + { + groupsNew = String.Join(",", b.AllowedGroups); + if (groupsNew.Length > 0) + groupsNew += ","; + groupsNew += name; + b.SetAllowedGroups(groupsNew); + + int q = database.Query("UPDATE ProjectileBans SET AllowedGroups=@0 WHERE ProjectileId=@1", groupsNew, + id); + + return q > 0; + } + catch (Exception ex) + { + Log.Error(ex.ToString()); + } + } + + return false; + } + + public bool RemoveGroup(short id, string group) + { + ProjectileBan b = GetBanById(id); + if (b != null) + { + try + { + b.RemoveGroup(group); + string groups = string.Join(",", b.AllowedGroups); + int q = database.Query("UPDATE ProjectileBans SET AllowedGroups=@0 WHERE ProjectileId=@1", groups, + id); + + if (q > 0) + return true; + } + catch (Exception ex) + { + Log.Error(ex.ToString()); + } + } + return false; + } + + public ProjectileBan GetBanById(short id) + { + foreach (ProjectileBan b in ProjectileBans) + { + if (b.ID == id) + { + return b; + } + } + return null; + } + } + + public class ProjectileBan : IEquatable + { + public short ID { get; set; } + public List AllowedGroups { get; set; } + + public ProjectileBan(short id) + : this() + { + ID = id; + AllowedGroups = new List(); + } + + public ProjectileBan() + { + ID = 0; + AllowedGroups = new List(); + } + + public bool Equals(ProjectileBan other) + { + return ID == other.ID; + } + + public bool HasPermissionToCreateProjectile(TSPlayer ply) + { + if (ply == null) + return false; + + if (ply.Group.HasPermission(Permissions.canusebannedprojectiles)) + return true; + + var cur = ply.Group; + var traversed = new List(); + while (cur != null) + { + if (AllowedGroups.Contains(cur.Name)) + { + return true; + } + if (traversed.Contains(cur)) + { + throw new InvalidOperationException("Infinite group parenting ({0})".SFormat(cur.Name)); + } + traversed.Add(cur); + cur = cur.Parent; + } + return false; + // could add in the other permissions in this class instead of a giant if switch. + } + + public void SetAllowedGroups(String groups) + { + // prevent null pointer exceptions + if (!string.IsNullOrEmpty(groups)) + { + List groupArr = groups.Split(',').ToList(); + + for (int i = 0; i < groupArr.Count; i++) + { + groupArr[i] = groupArr[i].Trim(); + //Console.WriteLine(groupArr[i]); + } + AllowedGroups = groupArr; + } + } + + public bool RemoveGroup(string groupName) + { + return AllowedGroups.Remove(groupName); + } + + public override string ToString() + { + return ID + (AllowedGroups.Count > 0 ? " (" + String.Join(",", AllowedGroups) + ")" : ""); + } + } +} \ No newline at end of file diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index ae11532a..4c5e40b1 100755 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -2393,9 +2393,17 @@ namespace TShockAPI if (index > Main.maxProjectiles || index < 0) { - return false; + args.Player.RemoveProjectile(ident, owner); + return true; } + if (TShock.ProjectileBans.ProjectileIsBanned(type, args.Player)) + { + args.Player.Disable("Player does not have permission to create that projectile.", true); + args.Player.SendErrorMessage("You do not have permission to create that projectile."); + args.Player.RemoveProjectile(ident, owner); + return false; + } // Server now checks owner + ident, if owner is different, server will create new projectile. /*if (args.Player.Index != owner) { diff --git a/TShockAPI/Permissions.cs b/TShockAPI/Permissions.cs index 4f7d68f4..32d85331 100644 --- a/TShockAPI/Permissions.cs +++ b/TShockAPI/Permissions.cs @@ -64,6 +64,9 @@ namespace TShockAPI [Description("User can manage item bans.")] public static readonly string manageitem = "tshock.admin.itemban"; + [Description("User can manage projectile bans.")] + public static readonly string manageprojectile = "tshock.admin.projectileban"; + [Description("User can manage groups.")] public static readonly string managegroup = "tshock.admin.group"; @@ -322,6 +325,9 @@ namespace TShockAPI [Description("Player can chat")] public static readonly string canchat = "tshock.canchat"; + [Description("Player can use banned projectiles.")] + public static readonly string canusebannedprojectiles = "tshock.projectiles.usebanned"; + /// /// Lists all commands associated with a given permission /// diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index e1481fd4..aee1cac3 100755 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -62,6 +62,7 @@ namespace TShockAPI public static GroupManager Groups; public static UserManager Users; public static ItemManager Itembans; + public static ProjectileManagager ProjectileBans; public static RememberedPosManager RememberedPos; public static CharacterManager CharacterDB; public static ConfigFile Config { get; set; } @@ -223,6 +224,7 @@ namespace TShockAPI Users = new UserManager(DB); Groups = new GroupManager(DB); Itembans = new ItemManager(DB); + ProjectileBans = new ProjectileManagager(DB); RememberedPos = new RememberedPosManager(DB); CharacterDB = new CharacterManager(DB); RestApi = new SecureRest(Netplay.serverListenIP, Config.RestApiPort); diff --git a/TShockAPI/TShockAPI.csproj b/TShockAPI/TShockAPI.csproj index 3ebc9d93..6b931512 100644 --- a/TShockAPI/TShockAPI.csproj +++ b/TShockAPI/TShockAPI.csproj @@ -72,6 +72,7 @@ + @@ -179,7 +180,7 @@ - +