From aa419283a962ed95e011223d75797cdb0008363a Mon Sep 17 00:00:00 2001 From: Enerdy Date: Mon, 11 Jan 2016 14:33:44 +0000 Subject: [PATCH] Add PlayerPermission hook, replace p.Group.HasPermission by p.HasPermission everywhere possible. --- TShockAPI/Commands.cs | 58 ++++---- TShockAPI/DB/CharacterManager.cs | 14 +- TShockAPI/DB/ItemManager.cs | 48 +++--- TShockAPI/DB/ProjectileManager.cs | 2 +- TShockAPI/DB/RegionManager.cs | 2 +- TShockAPI/DB/TileManager.cs | 2 +- TShockAPI/GetDataHandlers.cs | 62 ++++---- TShockAPI/Group.cs | 239 +++++++++++++++++------------- TShockAPI/Hooks/PlayerHooks.cs | 190 ++++++++++++++++++++++++ TShockAPI/TSPlayer.cs | 52 ++++--- TShockAPI/TShock.cs | 22 +-- TShockAPI/UpdateManager.cs | 2 +- TShockAPI/Utils.cs | 6 +- 13 files changed, 467 insertions(+), 232 deletions(-) diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs index 4ab71eb0..5934dd16 100755 --- a/TShockAPI/Commands.cs +++ b/TShockAPI/Commands.cs @@ -175,7 +175,7 @@ namespace TShockAPI return true; foreach (var Permission in Permissions) { - if (ply.Group.HasPermission(Permission)) + if (ply.HasPermission(Permission)) return true; } return false; @@ -795,7 +795,7 @@ namespace TShockAPI if (Main.ServerSideCharacter) { - if (group.HasPermission(Permissions.bypassssc)) + if (args.Player.HasPermission(Permissions.bypassssc)) { args.Player.IgnoreActionsForClearingTrashCan = false; } @@ -803,10 +803,10 @@ namespace TShockAPI } args.Player.LoginFailsBySsi = false; - if (group.HasPermission(Permissions.ignorestackhackdetection)) + if (args.Player.HasPermission(Permissions.ignorestackhackdetection)) args.Player.IgnoreActionsForCheating = "none"; - if (group.HasPermission(Permissions.usebanneditem)) + if (args.Player.HasPermission(Permissions.usebanneditem)) args.Player.IgnoreActionsForDisabledArmor = "none"; args.Player.Group = group; @@ -1381,7 +1381,7 @@ namespace TShockAPI TShock.Utils.SendMultipleMatchError(args.Player, players.Select(p => p.Name)); else { - if (args.Player.RealPlayer && players[0].Group.HasPermission(Permissions.immunetoban)) + if (args.Player.RealPlayer && players[0].HasPermission(Permissions.immunetoban)) { args.Player.SendErrorMessage("You can't ban {0}!", players[0].Name); return; @@ -2237,7 +2237,7 @@ namespace TShockAPI { if (args.Parameters.Count != 1 && args.Parameters.Count != 2) { - if (args.Player.Group.HasPermission(Permissions.tpothers)) + if (args.Player.HasPermission(Permissions.tpothers)) args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}tp [player 2]", Specifier); else args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}tp ", Specifier); @@ -2254,7 +2254,7 @@ namespace TShockAPI else { var target = players[0]; - if (!target.TPAllow && !args.Player.Group.HasPermission(Permissions.tpoverride)) + if (!target.TPAllow && !args.Player.HasPermission(Permissions.tpoverride)) { args.Player.SendErrorMessage("{0} has disabled players from teleporting.", target.Name); return; @@ -2262,14 +2262,14 @@ namespace TShockAPI if (args.Player.Teleport(target.TPlayer.position.X, target.TPlayer.position.Y)) { args.Player.SendSuccessMessage("Teleported to {0}.", target.Name); - if (!args.Player.Group.HasPermission(Permissions.tpsilent)) + if (!args.Player.HasPermission(Permissions.tpsilent)) target.SendInfoMessage("{0} teleported to you.", args.Player.Name); } } } else { - if (!args.Player.Group.HasPermission(Permissions.tpothers)) + if (!args.Player.HasPermission(Permissions.tpothers)) { args.Player.SendErrorMessage("You do not have access to this command."); return; @@ -2286,7 +2286,7 @@ namespace TShockAPI { if (args.Parameters[0] == "*") { - if (!args.Player.Group.HasPermission(Permissions.tpallothers)) + if (!args.Player.HasPermission(Permissions.tpallothers)) { args.Player.SendErrorMessage("You do not have access to this command."); return; @@ -2295,22 +2295,22 @@ namespace TShockAPI var target = players2[0]; foreach (var source in TShock.Players.Where(p => p != null && p != args.Player)) { - if (!target.TPAllow && !args.Player.Group.HasPermission(Permissions.tpoverride)) + if (!target.TPAllow && !args.Player.HasPermission(Permissions.tpoverride)) continue; if (source.Teleport(target.TPlayer.position.X, target.TPlayer.position.Y)) { if (args.Player != source) { - if (args.Player.Group.HasPermission(Permissions.tpsilent)) + if (args.Player.HasPermission(Permissions.tpsilent)) source.SendSuccessMessage("You were teleported to {0}.", target.Name); else source.SendSuccessMessage("{0} teleported you to {1}.", args.Player.Name, target.Name); } if (args.Player != target) { - if (args.Player.Group.HasPermission(Permissions.tpsilent)) + if (args.Player.HasPermission(Permissions.tpsilent)) target.SendInfoMessage("{0} was teleported to you.", source.Name); - if (!args.Player.Group.HasPermission(Permissions.tpsilent)) + if (!args.Player.HasPermission(Permissions.tpsilent)) target.SendInfoMessage("{0} teleported {1} to you.", args.Player.Name, source.Name); } } @@ -2325,13 +2325,13 @@ namespace TShockAPI else { var source = players1[0]; - if (!source.TPAllow && !args.Player.Group.HasPermission(Permissions.tpoverride)) + if (!source.TPAllow && !args.Player.HasPermission(Permissions.tpoverride)) { args.Player.SendErrorMessage("{0} has disabled players from teleporting.", source.Name); return; } var target = players2[0]; - if (!target.TPAllow && !args.Player.Group.HasPermission(Permissions.tpoverride)) + if (!target.TPAllow && !args.Player.HasPermission(Permissions.tpoverride)) { args.Player.SendErrorMessage("{0} has disabled players from teleporting.", target.Name); return; @@ -2341,16 +2341,16 @@ namespace TShockAPI { if (args.Player != source) { - if (args.Player.Group.HasPermission(Permissions.tpsilent)) + if (args.Player.HasPermission(Permissions.tpsilent)) source.SendSuccessMessage("You were teleported to {0}.", target.Name); else source.SendSuccessMessage("{0} teleported you to {1}.", args.Player.Name, target.Name); } if (args.Player != target) { - if (args.Player.Group.HasPermission(Permissions.tpsilent)) + if (args.Player.HasPermission(Permissions.tpsilent)) target.SendInfoMessage("{0} was teleported to you.", source.Name); - if (!args.Player.Group.HasPermission(Permissions.tpsilent)) + if (!args.Player.HasPermission(Permissions.tpsilent)) target.SendInfoMessage("{0} teleported {1} to you.", args.Player.Name, source.Name); } } @@ -2362,7 +2362,7 @@ namespace TShockAPI { if (args.Parameters.Count < 1) { - if (args.Player.Group.HasPermission(Permissions.tpallothers)) + if (args.Player.HasPermission(Permissions.tpallothers)) args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}tphere ", Specifier); else args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}tphere ", Specifier); @@ -2375,7 +2375,7 @@ namespace TShockAPI { if (playerName == "*") { - if (!args.Player.Group.HasPermission(Permissions.tpallothers)) + if (!args.Player.HasPermission(Permissions.tpallothers)) { args.Player.SendErrorMessage("You do not have permission to use this command."); return; @@ -2500,7 +2500,7 @@ namespace TShockAPI private static void Warp(CommandArgs args) { - bool hasManageWarpPermission = args.Player.Group.HasPermission(Permissions.managewarp); + bool hasManageWarpPermission = args.Player.HasPermission(Permissions.managewarp); if (args.Parameters.Count < 1) { if (hasManageWarpPermission) @@ -2603,7 +2603,7 @@ namespace TShockAPI args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}warp hide [name] ", Specifier); #endregion } - else if (args.Parameters[0].ToLower() == "send" && args.Player.Group.HasPermission(Permissions.tpothers)) + else if (args.Parameters[0].ToLower() == "send" && args.Player.HasPermission(Permissions.tpothers)) { #region Warp send if (args.Parameters.Count < 3) @@ -3852,7 +3852,7 @@ namespace TShockAPI { int.TryParse(args.Parameters[1], out damage); } - if (!args.Player.Group.HasPermission(Permissions.kill)) + if (!args.Player.HasPermission(Permissions.kill)) { damage = TShock.Utils.Clamp(damage, 15, 0); } @@ -4349,7 +4349,7 @@ namespace TShockAPI } case "tp": { - if (!args.Player.Group.HasPermission(Permissions.tp)) + if (!args.Player.HasPermission(Permissions.tp)) { args.Player.SendErrorMessage("You don't have the necessary permission to do that."); break; @@ -4397,7 +4397,7 @@ namespace TShockAPI "protect - Sets whether the tiles inside the region are protected or not.", "z <#> - Sets the z-order of the region.", }; - if (args.Player.Group.HasPermission(Permissions.tp)) + if (args.Player.HasPermission(Permissions.tp)) lines.Add("tp - Teleports you to the given region's center."); PaginationTools.SendPage( @@ -4526,7 +4526,7 @@ namespace TShockAPI args.Player.SendErrorMessage("Invalid usage, proper usage: {0}who [-i] [pagenumber]", Specifier); return; } - if (displayIdsRequested && !args.Player.Group.HasPermission(Permissions.seeids)) + if (displayIdsRequested && !args.Player.HasPermission(Permissions.seeids)) { args.Player.SendErrorMessage("You don't have the required permission to list player ids."); return; @@ -4658,7 +4658,7 @@ namespace TShockAPI { TShock.Utils.SendMultipleMatchError(args.Player, players.Select(p => p.Name)); } - else if (players[0].Group.HasPermission(Permissions.mute)) + else if (players[0].HasPermission(Permissions.mute)) { args.Player.SendErrorMessage("You cannot mute this player."); } @@ -5497,7 +5497,7 @@ namespace TShockAPI TSPlayer playerToGod; if (args.Parameters.Count > 0) { - if (!args.Player.Group.HasPermission(Permissions.godmodeother)) + if (!args.Player.HasPermission(Permissions.godmodeother)) { args.Player.SendErrorMessage("You do not have permission to god mode another player!"); return; diff --git a/TShockAPI/DB/CharacterManager.cs b/TShockAPI/DB/CharacterManager.cs index 3da05b1b..f1e5a5fc 100755 --- a/TShockAPI/DB/CharacterManager.cs +++ b/TShockAPI/DB/CharacterManager.cs @@ -162,13 +162,13 @@ namespace TShockAPI.DB if (!player.IsLoggedIn) return false; - - if ((player.tempGroup != null && player.tempGroup.HasPermission(Permissions.bypassssc)) || player.Group.HasPermission(Permissions.bypassssc)) - { - TShock.Log.ConsoleInfo("Skipping SSC Backup for " + player.User.Name); // Debug code - return true; - } - + + if (player.HasPermission(Permissions.bypassssc)) + { + TShock.Log.ConsoleInfo("Skipping SSC Backup for " + player.User.Name); // Debug code + return true; + } + if (!GetPlayerData(player, player.User.ID).exists) { try diff --git a/TShockAPI/DB/ItemManager.cs b/TShockAPI/DB/ItemManager.cs index 99ff89e3..ebff94bc 100755 --- a/TShockAPI/DB/ItemManager.cs +++ b/TShockAPI/DB/ItemManager.cs @@ -192,32 +192,32 @@ namespace TShockAPI.DB return Name == other.Name; } - public bool HasPermissionToUseItem(TSPlayer ply) - { - if (ply == null) - return false; + public bool HasPermissionToUseItem(TSPlayer ply) + { + if (ply == null) + return false; - if (ply.Group.HasPermission(Permissions.usebanneditem)) - return true; + if (ply.HasPermission(Permissions.usebanneditem)) + 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. - } + 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) { diff --git a/TShockAPI/DB/ProjectileManager.cs b/TShockAPI/DB/ProjectileManager.cs index ee955a2f..c52baf3b 100755 --- a/TShockAPI/DB/ProjectileManager.cs +++ b/TShockAPI/DB/ProjectileManager.cs @@ -202,7 +202,7 @@ namespace TShockAPI.DB if (ply == null) return false; - if (ply.Group.HasPermission(Permissions.canusebannedprojectiles)) + if (ply.HasPermission(Permissions.canusebannedprojectiles)) return true; var cur = ply.Group; diff --git a/TShockAPI/DB/RegionManager.cs b/TShockAPI/DB/RegionManager.cs index 5bd58d5f..622b94e2 100755 --- a/TShockAPI/DB/RegionManager.cs +++ b/TShockAPI/DB/RegionManager.cs @@ -210,7 +210,7 @@ namespace TShockAPI.DB public bool CanBuild(int x, int y, TSPlayer ply) { - if (!ply.Group.HasPermission(Permissions.canbuild)) + if (!ply.HasPermission(Permissions.canbuild)) { return false; } diff --git a/TShockAPI/DB/TileManager.cs b/TShockAPI/DB/TileManager.cs index bf7f28dc..a16f30fb 100755 --- a/TShockAPI/DB/TileManager.cs +++ b/TShockAPI/DB/TileManager.cs @@ -202,7 +202,7 @@ namespace TShockAPI.DB if (ply == null) return false; - if (ply.Group.HasPermission(Permissions.canusebannedtiles)) + if (ply.HasPermission(Permissions.canusebannedtiles)) return true; var cur = ply.Group; diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index ef67b844..4b8e3b83 100755 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -1305,7 +1305,7 @@ namespace TShockAPI args.Player.PlayerData.StoreSlot(slot, type, prefix, stack); } else if (Main.ServerSideCharacter && TShock.Config.DisableLoginBeforeJoin && !bypassTrashCanCheck && - args.Player.HasSentInventory && !args.Player.Group.HasPermission(Permissions.bypassssc)) + args.Player.HasSentInventory && !args.Player.HasPermission(Permissions.bypassssc)) { // The player might have moved an item to their trash can before they performed a single login attempt yet. args.Player.IgnoreActionsForClearingTrashCan = true; @@ -1329,7 +1329,7 @@ namespace TShockAPI if (OnPlayerHP(plr, cur, max) || cur <= 0 || max <= 0 || args.Player.IgnoreSSCPackets) return true; - if (max > TShock.Config.MaxHP && !args.Player.Group.HasPermission(Permissions.ignorehp)) + if (max > TShock.Config.MaxHP && !args.Player.HasPermission(Permissions.ignorehp)) { args.Player.Disable("Maximum HP beyond limit", DisableFlags.WriteToLogAndConsole); return true; @@ -1358,7 +1358,7 @@ namespace TShockAPI if (OnPlayerMana(plr, cur, max) || cur < 0 || max < 0 || args.Player.IgnoreSSCPackets) return true; - if (max > TShock.Config.MaxMP && !args.Player.Group.HasPermission(Permissions.ignoremp)) + if (max > TShock.Config.MaxMP && !args.Player.HasPermission(Permissions.ignoremp)) { args.Player.Disable("Maximum MP beyond limit", DisableFlags.WriteToLogAndConsole); return true; @@ -1476,17 +1476,17 @@ namespace TShockAPI if (Main.ServerSideCharacter) { - if (!group.HasPermission(Permissions.bypassssc)) + if (!args.Player.HasPermission(Permissions.bypassssc)) { args.Player.PlayerData.RestoreCharacter(args.Player); } } args.Player.LoginFailsBySsi = false; - if (group.HasPermission(Permissions.ignorestackhackdetection)) + if (args.Player.HasPermission(Permissions.ignorestackhackdetection)) args.Player.IgnoreActionsForCheating = "none"; - if (group.HasPermission(Permissions.usebanneditem)) + if (args.Player.HasPermission(Permissions.usebanneditem)) args.Player.IgnoreActionsForDisabledArmor = "none"; args.Player.Group = group; @@ -1551,7 +1551,7 @@ namespace TShockAPI if (Main.ServerSideCharacter) { - if (group.HasPermission(Permissions.bypassssc)) + if (args.Player.HasPermission(Permissions.bypassssc)) { args.Player.IgnoreActionsForClearingTrashCan = false; } @@ -1559,10 +1559,10 @@ namespace TShockAPI } args.Player.LoginFailsBySsi = false; - if (group.HasPermission(Permissions.ignorestackhackdetection)) + if (args.Player.HasPermission(Permissions.ignorestackhackdetection)) args.Player.IgnoreActionsForCheating = "none"; - if (group.HasPermission(Permissions.usebanneditem)) + if (args.Player.HasPermission(Permissions.usebanneditem)) args.Player.IgnoreActionsForDisabledArmor = "none"; args.Player.Group = group; @@ -1614,13 +1614,13 @@ namespace TShockAPI return true; } - if (!args.Player.Group.HasPermission(Permissions.ignorestackhackdetection)) + if (!args.Player.HasPermission(Permissions.ignorestackhackdetection)) { TShock.HackedInventory(args.Player); } if (TShock.Utils.ActivePlayers() + 1 > TShock.Config.MaxSlots && - !args.Player.Group.HasPermission(Permissions.reservedslot)) + !args.Player.HasPermission(Permissions.reservedslot)) { TShock.Utils.ForceKick(args.Player, TShock.Config.ServerFullReason, true); return true; @@ -1681,7 +1681,7 @@ namespace TShockAPI isTrapdoor = true; } - if (args.Player.Group.HasPermission(Permissions.allowclientsideworldedit) && !isTrapdoor) + if (args.Player.HasPermission(Permissions.allowclientsideworldedit) && !isTrapdoor) return false; if (OnSendTileSquare(size, tileX, tileY)) @@ -2133,7 +2133,7 @@ namespace TShockAPI return true; } - if ((action == EditAction.PlaceTile || action == EditAction.PlaceWall) && !args.Player.Group.HasPermission(Permissions.ignoreplacetiledetection)) + if ((action == EditAction.PlaceTile || action == EditAction.PlaceWall) && !args.Player.HasPermission(Permissions.ignoreplacetiledetection)) { args.Player.TilePlaceThreshold++; var coords = new Vector2(tileX, tileY); @@ -2142,7 +2142,7 @@ namespace TShockAPI } if ((action == EditAction.KillTile || action == EditAction.KillTileNoItem || action == EditAction.KillWall) && Main.tileSolid[Main.tile[tileX, tileY].type] && - !args.Player.Group.HasPermission(Permissions.ignorekilltiledetection)) + !args.Player.HasPermission(Permissions.ignorekilltiledetection)) { args.Player.TileKillThreshold++; var coords = new Vector2(tileX, tileY); @@ -2235,7 +2235,7 @@ namespace TShockAPI return true; } - if (!args.Player.Group.HasPermission(Permissions.ignoreplacetiledetection)) + if (!args.Player.HasPermission(Permissions.ignoreplacetiledetection)) { args.Player.TilePlaceThreshold++; var coords = new Vector2(x, y); @@ -2437,7 +2437,7 @@ namespace TShockAPI return true; } - if (!args.Player.Group.HasPermission(Permissions.ignorenoclipdetection) && + if (!args.Player.HasPermission(Permissions.ignorenoclipdetection) && TSCheckNoclip(pos, args.TPlayer.width, args.TPlayer.height) && !TShock.Config.IgnoreNoClip && !args.TPlayer.tongued) { @@ -2617,7 +2617,7 @@ namespace TShockAPI return true; } - if (dmg > TShock.Config.MaxProjDamage && !args.Player.Group.HasPermission(Permissions.ignoredamagecap)) + if (dmg > TShock.Config.MaxProjDamage && !args.Player.HasPermission(Permissions.ignoredamagecap)) { args.Player.Disable(String.Format("Projectile damage is higher than {0}.", TShock.Config.MaxProjDamage), DisableFlags.WriteToLogAndConsole); args.Player.RemoveProjectile(ident, owner); @@ -2631,7 +2631,7 @@ namespace TShockAPI } bool hasPermission = !TShock.CheckProjectilePermission(args.Player, index, type); - if (!TShock.Config.IgnoreProjUpdate && !hasPermission && !args.Player.Group.HasPermission(Permissions.ignoreprojectiledetection)) + if (!TShock.Config.IgnoreProjUpdate && !hasPermission && !args.Player.HasPermission(Permissions.ignoreprojectiledetection)) { if (type == ProjectileID.BlowupSmokeMoonlord || type == ProjectileID.PhantasmalEye @@ -2668,7 +2668,7 @@ namespace TShockAPI return true; } - if (!args.Player.Group.HasPermission(Permissions.ignoreprojectiledetection)) + if (!args.Player.HasPermission(Permissions.ignoreprojectiledetection)) { if (type == ProjectileID.CrystalShard && TShock.Config.ProjIgnoreShrapnel) // Ignore crystal shards { @@ -2831,7 +2831,7 @@ namespace TShockAPI return true; } - if (!args.Player.Group.HasPermission(Permissions.ignoreliquidsetdetection)) + if (!args.Player.HasPermission(Permissions.ignoreliquidsetdetection)) { args.Player.TileLiquidThreshold++; } @@ -3149,7 +3149,7 @@ namespace TShockAPI if (OnUpdateNPCHome(id, x, y, homeless)) return true; - if (!args.Player.Group.HasPermission(Permissions.movenpc)) + if (!args.Player.HasPermission(Permissions.movenpc)) { args.Player.SendErrorMessage("You do not have permission to relocate NPCs."); args.Player.SendData(PacketTypes.UpdateNPCHome, "", id, Main.npc[id].homeTileX, Main.npc[id].homeTileY, @@ -3281,7 +3281,7 @@ namespace TShockAPI Item item = new Item(); item.netDefaults(type); - if ((stacks > item.maxStack || stacks <= 0) || (TShock.Itembans.ItemIsBanned(item.name, args.Player) && !args.Player.Group.HasPermission(Permissions.allowdroppingbanneditems))) + if ((stacks > item.maxStack || stacks <= 0) || (TShock.Itembans.ItemIsBanned(item.name, args.Player) && !args.Player.HasPermission(Permissions.allowdroppingbanneditems))) { args.Player.SendData(PacketTypes.ItemDrop, "", id); return true; @@ -3338,7 +3338,7 @@ namespace TShockAPI return true; } - if (dmg > TShock.Config.MaxDamage && !args.Player.Group.HasPermission(Permissions.ignoredamagecap) && id != args.Player.Index) + if (dmg > TShock.Config.MaxDamage && !args.Player.HasPermission(Permissions.ignoredamagecap) && id != args.Player.Index) { if (TShock.Config.KickOnDamageThresholdBroken) { @@ -3404,7 +3404,7 @@ namespace TShockAPI if (Main.npc[id] == null) return true; - if (dmg > TShock.Config.MaxDamage && !args.Player.Group.HasPermission(Permissions.ignoredamagecap)) + if (dmg > TShock.Config.MaxDamage && !args.Player.HasPermission(Permissions.ignoredamagecap)) { if (TShock.Config.KickOnDamageThresholdBroken) { @@ -3425,7 +3425,7 @@ namespace TShockAPI return true; } - if (Main.npc[id].townNPC && !args.Player.Group.HasPermission(Permissions.hurttownnpc)) + if (Main.npc[id].townNPC && !args.Player.HasPermission(Permissions.hurttownnpc)) { args.Player.SendErrorMessage("You do not have permission to hurt this NPC."); args.Player.SendData(PacketTypes.NpcUpdate, "", id); @@ -3588,12 +3588,12 @@ namespace TShockAPI break; } } - if (spawnboss && !args.Player.Group.HasPermission(Permissions.summonboss)) + if (spawnboss && !args.Player.HasPermission(Permissions.summonboss)) { args.Player.SendErrorMessage("You don't have permission to summon a boss."); return true; } - if (invasion && !args.Player.Group.HasPermission(Permissions.startinvasion)) + if (invasion && !args.Player.HasPermission(Permissions.startinvasion)) { args.Player.SendErrorMessage("You don't have permission to start an invasion."); return true; @@ -3670,7 +3670,7 @@ namespace TShockAPI return true; } - if (!args.Player.Group.HasPermission(Permissions.ignorepaintdetection)) + if (!args.Player.HasPermission(Permissions.ignorepaintdetection)) { args.Player.PaintThreshold++; } @@ -3714,7 +3714,7 @@ namespace TShockAPI return true; } - if (!args.Player.Group.HasPermission(Permissions.ignorepaintdetection)) + if (!args.Player.HasPermission(Permissions.ignorepaintdetection)) { args.Player.PaintThreshold++; } @@ -3753,7 +3753,7 @@ namespace TShockAPI } //Rod of Discord teleport (usually (may be used by modded clients to teleport)) - if (type == 0 && !args.Player.Group.HasPermission(Permissions.rod)) + if (type == 0 && !args.Player.HasPermission(Permissions.rod)) { args.Player.SendErrorMessage("You do not have permission to teleport."); args.Player.Teleport(args.TPlayer.position.X, args.TPlayer.position.Y); @@ -3774,7 +3774,7 @@ namespace TShockAPI return true; } - if (!args.Player.Group.HasPermission(Permissions.wormhole)) + if (!args.Player.HasPermission(Permissions.wormhole)) { args.Player.SendErrorMessage("You do not have permission to teleport."); args.Player.Teleport(args.TPlayer.position.X, args.TPlayer.position.Y); diff --git a/TShockAPI/Group.cs b/TShockAPI/Group.cs index 1f953dff..8c05d64c 100644 --- a/TShockAPI/Group.cs +++ b/TShockAPI/Group.cs @@ -22,56 +22,59 @@ using System.Collections.Generic; namespace TShockAPI { + /// + /// A class used to group multiple users' permissions and settings. + /// public class Group { // NOTE: Using a const still suffers from needing to recompile to change the default // ideally we would use a static but this means it can't be used for the default parameter :( - /// - /// Default chat color. - /// + /// + /// Default chat color. + /// public const string defaultChatColor = "255,255,255"; - /// - /// List of permissions available to the group. - /// + /// + /// List of permissions available to the group. + /// public readonly List permissions = new List(); - /// - /// List of permissions that the group is explicitly barred from. - /// + /// + /// List of permissions that the group is explicitly barred from. + /// public readonly List negatedpermissions = new List(); - /// - /// The group's name. - /// + /// + /// The group's name. + /// public string Name { get; set; } - /// - /// The group that this group inherits permissions from. - /// + /// + /// The group that this group inherits permissions from. + /// public Group Parent { get; set; } - /// - /// The chat prefix for this group. - /// + /// + /// The chat prefix for this group. + /// public string Prefix { get; set; } - /// - /// The chat suffix for this group. - /// + /// + /// The chat suffix for this group. + /// public string Suffix { get; set; } - /// - /// The name of the parent, not particularly sure why this is here. - /// We can use group.Parent.Name and not have this second reference. - /// This was added for rest, so a discussion with Shank is necessary. - /// + /// + /// The name of the parent, not particularly sure why this is here. + /// We can use group.Parent.Name and not have this second reference. + /// This was added for rest, so a discussion with Shank is necessary. + /// public string ParentName { get { return (null == Parent) ? "" : Parent.Name; } } - /// - /// The chat color of the group. - /// Returns "255,255,255", sets "255,255,255" - /// + /// + /// The chat color of the group. + /// Returns "255,255,255", sets "255,255,255" + /// public string ChatColor { get { return string.Format("{0},{1},{2}", R.ToString("D3"), G.ToString("D3"), B.ToString("D3")); } @@ -95,9 +98,9 @@ namespace TShockAPI } } - /// - /// The permissions of the user in string form. - /// + /// + /// The permissions of the user in string form. + /// public string Permissions { get @@ -115,9 +118,9 @@ namespace TShockAPI } } - /// - /// The permissions of this group and all that it inherits from. - /// + /// + /// The permissions of this group and all that it inherits from. + /// public virtual List TotalPermissions { get @@ -148,12 +151,31 @@ namespace TShockAPI } } + /// + /// The group's chat color red byte. + /// public byte R = 255; + /// + /// The group's chat color green byte. + /// public byte G = 255; + /// + /// The group's chat color blue byte. + /// public byte B = 255; - public static Group DefaultGroup = null; + /// + /// The default group attributed to unregistered users. + /// + public static Group DefaultGroup = null; + /// + /// Initializes a new instance of the group class. + /// + /// The name of the group. + /// The parent group, if any. + /// The chat color, in "RRR,GGG,BBB" format. + /// The list of permissions associated with this group, separated by commas. public Group(string groupname, Group parentgroup = null, string chatcolor = "255,255,255", string permissions = null) { Name = groupname; @@ -162,21 +184,21 @@ namespace TShockAPI Permissions = permissions; } - /// - /// Checks to see if a group has a specified permission. - /// - /// The permission to check. - /// Returns true if the user has that permission. + /// + /// Checks to see if a group has a specified permission. + /// + /// The permission to check. + /// True if the group has that permission. public virtual bool HasPermission(string permission) - { - bool negated = false; + { + bool negated = false; if (String.IsNullOrEmpty(permission) || (RealHasPermission(permission, ref negated) && !negated)) { return true; } - if (negated) - return false; + if (negated) + return false; string[] nodes = permission.Split('.'); for (int i = nodes.Length - 1; i >= 0; i--) @@ -189,37 +211,37 @@ namespace TShockAPI } return false; } - private bool RealHasPermission(string permission, ref bool negated) - { - negated = false; - if (string.IsNullOrEmpty(permission)) - return true; + private bool RealHasPermission(string permission, ref bool negated) + { + negated = false; + if (string.IsNullOrEmpty(permission)) + return true; - var cur = this; - var traversed = new List(); - while (cur != null) - { - if (cur.negatedpermissions.Contains(permission)) - { - negated = true; - return false; - } - if (cur.permissions.Contains(permission)) - return true; - if (traversed.Contains(cur)) - { - throw new InvalidOperationException("Infinite group parenting ({0})".SFormat(cur.Name)); - } - traversed.Add(cur); - cur = cur.Parent; - } - return false; - } + var cur = this; + var traversed = new List(); + while (cur != null) + { + if (cur.negatedpermissions.Contains(permission)) + { + negated = true; + return false; + } + if (cur.permissions.Contains(permission)) + return true; + if (traversed.Contains(cur)) + { + throw new InvalidOperationException("Infinite group parenting ({0})".SFormat(cur.Name)); + } + traversed.Add(cur); + cur = cur.Parent; + } + return false; + } - /// - /// Adds a permission to the list of negated permissions. - /// - /// The permission to negate. + /// + /// Adds a permission to the list of negated permissions. + /// + /// The permission to negate. public void NegatePermission(string permission) { // Avoid duplicates @@ -230,10 +252,10 @@ namespace TShockAPI } } - /// - /// Adds a permission to the list of permissions. - /// - /// The permission to add. + /// + /// Adds a permission to the list of permissions. + /// + /// The permission to add. public void AddPermission(string permission) { if (permission.StartsWith("!")) @@ -249,11 +271,11 @@ namespace TShockAPI } } - /// - /// Clears the permission list and sets it to the list provided, - /// will parse "!permssion" and add it to the negated permissions. - /// - /// + /// + /// Clears the permission list and sets it to the list provided, + /// will parse "!permssion" and add it to the negated permissions. + /// + /// The new list of permissions to associate with the group. public void SetPermission(List permission) { permissions.Clear(); @@ -261,11 +283,11 @@ namespace TShockAPI permission.ForEach(p => AddPermission(p)); } - /// - /// Will remove a permission from the respective list, - /// where "!permission" will remove a negated permission. - /// - /// + /// + /// Will remove a permission from the respective list, + /// where "!permission" will remove a negated permission. + /// + /// public void RemovePermission(string permission) { if (permission.StartsWith("!")) @@ -277,9 +299,9 @@ namespace TShockAPI } /// - /// Assigns all fields of this instance to another. - /// - /// The other instance. + /// Assigns all fields of this instance to another. + /// + /// The other instance. public void AssignTo(Group otherGroup) { otherGroup.Name = Name; @@ -292,35 +314,44 @@ namespace TShockAPI otherGroup.Permissions = Permissions; } - public override string ToString() { + public override string ToString() + { return this.Name; } } - /// - /// This class is the SuperAdminGroup, which has access to everything. - /// + /// + /// This class is the SuperAdminGroup, which has access to everything. + /// public class SuperAdminGroup : Group { + /// + /// The superadmin class has every permission, represented by '*'. + /// public override List TotalPermissions { get { return new List { "*" }; } } + + /// + /// Initializes a new instance of the SuperAdminGroup class with the configured parameters. + /// Those can be changed in the config file. + /// public SuperAdminGroup() : base("superadmin") { - R = (byte) TShock.Config.SuperAdminChatRGB[0]; - G = (byte) TShock.Config.SuperAdminChatRGB[1]; - B = (byte) TShock.Config.SuperAdminChatRGB[2]; + R = (byte)TShock.Config.SuperAdminChatRGB[0]; + G = (byte)TShock.Config.SuperAdminChatRGB[1]; + B = (byte)TShock.Config.SuperAdminChatRGB[2]; Prefix = TShock.Config.SuperAdminChatPrefix; Suffix = TShock.Config.SuperAdminChatSuffix; } - /// - /// Override to allow access to everything. - /// - /// The permission - /// True + /// + /// Override to allow access to everything. + /// + /// The permission + /// True public override bool HasPermission(string permission) { return true; diff --git a/TShockAPI/Hooks/PlayerHooks.cs b/TShockAPI/Hooks/PlayerHooks.cs index d8801399..473d2d6d 100644 --- a/TShockAPI/Hooks/PlayerHooks.cs +++ b/TShockAPI/Hooks/PlayerHooks.cs @@ -21,66 +21,214 @@ using System.ComponentModel; namespace TShockAPI.Hooks { + /// + /// EventArgs used for the event. + /// public class PlayerPostLoginEventArgs { + /// + /// The player who fired the event. + /// public TSPlayer Player { get; set; } + + /// + /// Initializes a new instance of the PlayerPostLoginEventArgs class. + /// + /// The player who fired the event. public PlayerPostLoginEventArgs(TSPlayer ply) { Player = ply; } } + /// + /// EventArgs used for the event. + /// public class PlayerPreLoginEventArgs : HandledEventArgs { + /// + /// The player who fired the event. + /// public TSPlayer Player { get; set; } + + /// + /// The player's login name. + /// public string LoginName { get; set; } + + /// + /// The player's raw password. + /// public string Password { get; set; } } + /// + /// EventArgs used for the event. + /// public class PlayerLogoutEventArgs { + /// + /// The player who fired the event. + /// public TSPlayer Player { get; set; } + /// + /// Initializes a new instance of the PlayerLogoutEventArgs class. + /// + /// The player who fired the event. public PlayerLogoutEventArgs(TSPlayer player) { Player = player; } } + /// + /// EventArgs used for the event. + /// public class PlayerCommandEventArgs : HandledEventArgs { + /// + /// The player who fired the event. + /// public TSPlayer Player { get; set; } + + /// + /// The command's name that follows the . + /// public string CommandName { get; set; } + + /// + /// The command's full text. + /// public string CommandText { get; set; } + + /// + /// The command's parameters extracted from . + /// public List Parameters { get; set; } + + /// + /// The full list of server commands. + /// public IEnumerable CommandList { get; set; } + + /// + /// The prefix used to send the command (either or ). + /// public string CommandPrefix { get; set; } } + /// + /// EventArgs used for the event. + /// public class PlayerChatEventArgs : HandledEventArgs { + /// + /// The player who fired the event. + /// public TSPlayer Player { get; set; } + + /// + /// The raw chat text as received by the server. + /// public string RawText { get; set; } + + /// + /// The string after being formatted by TShock as specified in the config file. + /// public string TShockFormattedText { get; set; } } + /// + /// EventArgs used for the event. + /// + public class PlayerPermissionEventArgs : HandledEventArgs + { + /// + /// The player who fired the event. + /// + public TSPlayer Player { get; set; } + + /// + /// Initializes a new instance of the PlayerPermissionEventArgs class. + /// + /// + public PlayerPermissionEventArgs(TSPlayer player) + { + Player = player; + } + } + + /// + /// A collection of events fired by players that can be hooked to. + /// public static class PlayerHooks { + /// + /// The delegate of the event. + /// + /// The EventArgs for this event. public delegate void PlayerPostLoginD(PlayerPostLoginEventArgs e); + /// + /// Fired by players after they've successfully logged in to a user account. + /// public static event PlayerPostLoginD PlayerPostLogin; + /// + /// The delegate of the event. + /// + /// The EventArgs for this event. public delegate void PlayerPreLoginD(PlayerPreLoginEventArgs e); + /// + /// Fired by players when sending login credentials to the server. + /// public static event PlayerPreLoginD PlayerPreLogin; + /// + /// The delegate of the event. + /// + /// The EventArgs for this event. public delegate void PlayerLogoutD(PlayerLogoutEventArgs e); + /// + /// Fired by players upon logging out from a user account. + /// public static event PlayerLogoutD PlayerLogout; + /// + /// The delegate of the event. + /// + /// The EventArgs for this event. public delegate void PlayerCommandD(PlayerCommandEventArgs e); + /// + /// Fired by players when using a command. + /// public static event PlayerCommandD PlayerCommand; + /// + /// The delegate of the event. + /// + /// The EventArgs for this event. public delegate void PlayerChatD(PlayerChatEventArgs e); + /// + /// Fired by players when they send a chat message packet to the server + /// and before it is transmitted to the rest of the players. + /// public static event PlayerChatD PlayerChat; + /// + /// The delegate of the event. + /// + /// The EventArgs for this event. + public delegate void PlayerPermissionD(PlayerPermissionEventArgs e); + /// + /// Fired by players every time a permission check involving them occurs. + /// + public static event PlayerPermissionD PlayerPermission; + + /// + /// Fires the event. + /// + /// The player firing the event. public static void OnPlayerPostLogin(TSPlayer ply) { if (PlayerPostLogin == null) @@ -92,6 +240,16 @@ namespace TShockAPI.Hooks PlayerPostLogin(args); } + /// + /// Fires the event. + /// + /// The player firing the event. + /// The command name. + /// The raw command text. + /// The command args extracted from the command text. + /// The list of commands. + /// The command specifier used. + /// True if the event has been handled. public static bool OnPlayerCommand(TSPlayer player, string cmdName, string cmdText, List args, ref IEnumerable commands, string cmdPrefix) { if (PlayerCommand == null) @@ -111,6 +269,13 @@ namespace TShockAPI.Hooks return playerCommandEventArgs.Handled; } + /// + /// Fires the event. + /// + /// The player firing the event. + /// The user name. + /// The password. + /// True if the event has been handled. public static bool OnPlayerPreLogin(TSPlayer ply, string name, string pass) { if (PlayerPreLogin == null) @@ -121,6 +286,10 @@ namespace TShockAPI.Hooks return args.Handled; } + /// + /// Fires the event. + /// + /// The player firing the event. public static void OnPlayerLogout(TSPlayer ply) { if (PlayerLogout == null) @@ -130,6 +299,12 @@ namespace TShockAPI.Hooks PlayerLogout(args); } + /// + /// Fires the event. + /// + /// The player firing the event. + /// The raw chat text sent by the player. + /// The chat text after being formatted. public static void OnPlayerChat(TSPlayer ply, string rawtext, ref string tshockText) { if (PlayerChat == null) @@ -139,5 +314,20 @@ namespace TShockAPI.Hooks PlayerChat(args); tshockText = args.TShockFormattedText; } + + /// + /// Fires the event. + /// + /// The player firing the event. + /// True if the event has been handled. + public static bool OnPlayerPermission(TSPlayer player) + { + if (PlayerPermission == null) + return false; + + var args = new PlayerPermissionEventArgs(player); + PlayerPermission(args); + return args.Handled; + } } } diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index 4c4c475e..9baa358d 100755 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -26,6 +26,7 @@ using System.Timers; using Terraria; using Terraria.ID; using TShockAPI.DB; +using TShockAPI.Hooks; using TShockAPI.Net; using Timer = System.Timers.Timer; @@ -430,11 +431,11 @@ namespace TShockAPI } try { - if ((tempGroup != null && tempGroup.HasPermission(Permissions.bypassssc)) || Group.HasPermission(Permissions.bypassssc)) - { - TShock.Log.ConsoleInfo("Skipping SSC Backup for " + User.Name); // Debug Code - return true; - } + if (HasPermission(Permissions.bypassssc)) + { + TShock.Log.ConsoleInfo("Skipping SSC Backup for " + User.Name); // Debug Code + return true; + } PlayerData.CopyCharacter(this); TShock.CharacterDB.InsertPlayerData(this); return true; @@ -875,22 +876,22 @@ namespace TShockAPI } } - /* - * Calling new StackTrace() is incredibly expensive, and must be disabled - * in release builds. Use a conditional call instead. - */ - LogStackFrame(); + /* + * Calling new StackTrace() is incredibly expensive, and must be disabled + * in release builds. Use a conditional call instead. + */ + LogStackFrame(); } - [Conditional("DEBUG")] - private void LogStackFrame() - { - var trace = new StackTrace(); - StackFrame frame = null; - frame = trace.GetFrame(1); - if (frame != null && frame.GetMethod().DeclaringType != null) - TShock.Log.Debug(frame.GetMethod().DeclaringType.Name + " called Disable()."); - } + [Conditional("DEBUG")] + private void LogStackFrame() + { + var trace = new StackTrace(); + StackFrame frame = null; + frame = trace.GetFrame(1); + if (frame != null && frame.GetMethod().DeclaringType != null) + TShock.Log.Debug(frame.GetMethod().DeclaringType.Name + " called Disable()."); + } public virtual void Whoopie(object time) { @@ -954,6 +955,19 @@ namespace TShockAPI AwaitingResponse.Add(name, callback); } + + /// + /// Checks to see if a player or its associated group/temporary group has a specified permission. + /// + /// The permission to check. + /// True if the player has that permission. + public bool HasPermission(string permission) + { + if (PlayerHooks.OnPlayerPermission(this)) + return true; + + return (tempGroup != null && tempGroup.HasPermission(permission)) || Group.HasPermission(permission); + } } public class TSRestPlayer : TSPlayer diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index c8a3ed11..977554f9 100755 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -989,7 +989,7 @@ namespace TShockAPI string check = "none"; foreach (Item item in player.TPlayer.inventory) { - if (!player.Group.HasPermission(Permissions.ignorestackhackdetection) && (item.stack > item.maxStack || item.stack < 0) && + if (!player.HasPermission(Permissions.ignorestackhackdetection) && (item.stack > item.maxStack || item.stack < 0) && item.type != 0) { check = "Remove item " + item.name + " (" + item.stack + ") exceeds max stack of " + item.maxStack; @@ -1334,7 +1334,7 @@ namespace TShockAPI } else { - if (!tsplr.Group.HasPermission(Permissions.canchat)) + if (!tsplr.HasPermission(Permissions.canchat)) { args.Handled = true; } @@ -1652,7 +1652,7 @@ namespace TShockAPI /// bool - True if the player should not be able to modify a tile. public static bool CheckTilePermission(TSPlayer player, int tileX, int tileY, short tileType, GetDataHandlers.EditAction actionType) { - if (!player.Group.HasPermission(Permissions.canbuild)) + if (!player.HasPermission(Permissions.canbuild)) { if (TShock.Config.AllowIce && actionType != GetDataHandlers.EditAction.PlaceTile) { @@ -1687,7 +1687,7 @@ namespace TShockAPI return true; } - if (!player.Group.HasPermission(Permissions.editregion) && !Regions.CanBuild(tileX, tileY, player) && + if (!player.HasPermission(Permissions.editregion) && !Regions.CanBuild(tileX, tileY, player) && Regions.InArea(tileX, tileY)) { if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.RPm) > 2000) @@ -1700,7 +1700,7 @@ namespace TShockAPI if (Config.DisableBuild) { - if (!player.Group.HasPermission(Permissions.antibuild)) + if (!player.HasPermission(Permissions.antibuild)) { if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.WPm) > 2000) { @@ -1713,7 +1713,7 @@ namespace TShockAPI if (Config.SpawnProtection) { - if (!player.Group.HasPermission(Permissions.editspawn)) + if (!player.HasPermission(Permissions.editspawn)) { if (CheckSpawn(tileX, tileY)) { @@ -1737,8 +1737,8 @@ namespace TShockAPI /// bool - True if the player should not be able to modify the tile. public static bool CheckTilePermission(TSPlayer player, int tileX, int tileY, bool paint = false) { - if ((!paint && !player.Group.HasPermission(Permissions.canbuild)) || - (paint && !player.Group.HasPermission(Permissions.canpaint))) + if ((!paint && !player.HasPermission(Permissions.canbuild)) || + (paint && !player.HasPermission(Permissions.canpaint))) { if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.BPm) > 2000) { @@ -1755,7 +1755,7 @@ namespace TShockAPI return true; } - if (!player.Group.HasPermission(Permissions.editregion) && !Regions.CanBuild(tileX, tileY, player) && + if (!player.HasPermission(Permissions.editregion) && !Regions.CanBuild(tileX, tileY, player) && Regions.InArea(tileX, tileY)) { if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.RPm) > 2000) @@ -1768,7 +1768,7 @@ namespace TShockAPI if (Config.DisableBuild) { - if (!player.Group.HasPermission(Permissions.antibuild)) + if (!player.HasPermission(Permissions.antibuild)) { if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.WPm) > 2000) { @@ -1781,7 +1781,7 @@ namespace TShockAPI if (Config.SpawnProtection) { - if (!player.Group.HasPermission(Permissions.editspawn)) + if (!player.HasPermission(Permissions.editspawn)) { if (CheckSpawn(tileX, tileY)) { diff --git a/TShockAPI/UpdateManager.cs b/TShockAPI/UpdateManager.cs index 5ccc1c85..e782095c 100755 --- a/TShockAPI/UpdateManager.cs +++ b/TShockAPI/UpdateManager.cs @@ -107,7 +107,7 @@ namespace TShockAPI NotifyAdministrator(TSPlayer.Server, changes); foreach (TSPlayer player in TShock.Players) { - if (player != null && player.Active && player.Group.HasPermission(Permissions.maintenance)) + if (player != null && player.Active && player.HasPermission(Permissions.maintenance)) { NotifyAdministrator(player, changes); } diff --git a/TShockAPI/Utils.cs b/TShockAPI/Utils.cs index dc02286b..ff91b74f 100644 --- a/TShockAPI/Utils.cs +++ b/TShockAPI/Utils.cs @@ -191,7 +191,7 @@ namespace TShockAPI TSPlayer.Server.SendMessage(log, color); foreach (TSPlayer player in TShock.Players) { - if (player != null && player != excludedPlayer && player.Active && player.Group.HasPermission(Permissions.logs) && + if (player != null && player != excludedPlayer && player.Active && player.HasPermission(Permissions.logs) && player.DisplayLogs && TShock.Config.DisableSpewLogs == false) player.SendMessage(log, color); } @@ -610,7 +610,7 @@ namespace TShockAPI { if (!player.ConnectionAlive) return true; - if (force || !player.Group.HasPermission(Permissions.immunetokick)) + if (force || !player.HasPermission(Permissions.immunetokick)) { string playerName = player.Name; player.SilentKickInProgress = silent; @@ -642,7 +642,7 @@ namespace TShockAPI { if (!player.ConnectionAlive) return true; - if (force || !player.Group.HasPermission(Permissions.immunetoban)) + if (force || !player.HasPermission(Permissions.immunetoban)) { string ip = player.IP; string uuid = player.UUID;