From 207925ddf1f0119f36d7aa67322e97506f5a831e Mon Sep 17 00:00:00 2001 From: Chris Nord Date: Tue, 2 Oct 2018 16:35:46 -0700 Subject: [PATCH 01/19] Update Manager now uses TLS. Solves issue #1633 --- CHANGELOG.md | 1 + TShockAPI/UpdateManager.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4a9b410..2382417d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when adding new entries; sign your name off when you add or change something. This should primarily be things like user changes, not necessarily codebase changes unless it's really relevant or large. ## Upcoming Changes +* Update tracker now uses TLS (@pandabear41) * API: Added return in OnNameCollision if hook has been handled. (@Patrikkk) * API: Added hooks for item, projectile and tile bans (@deadsurgeon42) * API: Changed `PlayerHooks` permission hook mechanisms to allow negation from hooks (@deadsurgeon42) diff --git a/TShockAPI/UpdateManager.cs b/TShockAPI/UpdateManager.cs index bbf72a84..db8b4234 100644 --- a/TShockAPI/UpdateManager.cs +++ b/TShockAPI/UpdateManager.cs @@ -33,7 +33,7 @@ namespace TShockAPI /// public class UpdateManager { - private const string UpdateUrl = "http://update.tshock.co/latest/"; + private const string UpdateUrl = "https://update.tshock.co/latest/"; private HttpClient _client = new HttpClient(); /// From ce6dc577cb4dcde91f1a12b7134da66cbceb9e59 Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Thu, 4 Oct 2018 07:49:26 -0700 Subject: [PATCH 02/19] Reorder changelog for consistency --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2382417d..52da3276 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,6 @@ This is the rolling changelog for TShock for Terraria. Use past tense when adding new entries; sign your name off when you add or change something. This should primarily be things like user changes, not necessarily codebase changes unless it's really relevant or large. ## Upcoming Changes -* Update tracker now uses TLS (@pandabear41) * API: Added return in OnNameCollision if hook has been handled. (@Patrikkk) * API: Added hooks for item, projectile and tile bans (@deadsurgeon42) * API: Changed `PlayerHooks` permission hook mechanisms to allow negation from hooks (@deadsurgeon42) @@ -89,6 +88,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin * `Utils.TryParseTime` can now take spaces (e.g., `3d 5h 2m 3s`) (@QuiCM) * Enabled banning unregistered users (@QuiCM) * Added filtering and validation on packet 96 (Teleport player through portal) (@QuiCM) +* Update tracker now uses TLS (@pandabear41) ## TShock 4.3.25 * Fixed a critical exploit in the Terraria protocol that could cause massive unpreventable world corruption as well as a number of other problems. Thanks to @bartico6 for reporting. Fixed by the efforts of @QuiCM, @hakusaro, and tips in the right directioon from @bartico6. From f5ccf925bd74a1f7d8da51e7469a343148ff0566 Mon Sep 17 00:00:00 2001 From: Enerdy Date: Mon, 15 Oct 2018 14:33:32 +0100 Subject: [PATCH 03/19] Logout players when their user account is removed --- TShockAPI/DB/UserManager.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/TShockAPI/DB/UserManager.cs b/TShockAPI/DB/UserManager.cs index 0c0dc5ff..4aa09dee 100644 --- a/TShockAPI/DB/UserManager.cs +++ b/TShockAPI/DB/UserManager.cs @@ -95,13 +95,17 @@ namespace TShockAPI.DB { try { - var tempuser = GetUserAccount(account); + // Logout any player logged in as the account to be removed + TShock.Players.Where(p => p?.IsLoggedIn == true && p.Account.Name == account.Name).ForEach(p => p.Logout()); + + UserAccount tempuser = GetUserAccount(account); int affected = _database.Query("DELETE FROM Users WHERE Username=@0", account.Name); if (affected < 1) throw new UserAccountNotExistException(account.Name); Hooks.AccountHooks.OnAccountDelete(tempuser); + } catch (Exception ex) { From 659e1ebd613317e0ecce0470042d942dcd9c1e5a Mon Sep 17 00:00:00 2001 From: Rodrigo Rente Date: Mon, 15 Oct 2018 15:20:21 +0100 Subject: [PATCH 04/19] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52da3276..79f36e88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -89,6 +89,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin * Enabled banning unregistered users (@QuiCM) * Added filtering and validation on packet 96 (Teleport player through portal) (@QuiCM) * Update tracker now uses TLS (@pandabear41) +* When deleting an user account, any player logged in to that account is now logged out properly (@Enerdy) ## TShock 4.3.25 * Fixed a critical exploit in the Terraria protocol that could cause massive unpreventable world corruption as well as a number of other problems. Thanks to @bartico6 for reporting. Fixed by the efforts of @QuiCM, @hakusaro, and tips in the right directioon from @bartico6. From bcbea04002d1a1fe3a7db774f1ee04b371189992 Mon Sep 17 00:00:00 2001 From: AxeelAnder <1491773534@qq.com> Date: Sat, 20 Oct 2018 17:48:50 +0800 Subject: [PATCH 05/19] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79f36e88..e97338de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -90,6 +90,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin * Added filtering and validation on packet 96 (Teleport player through portal) (@QuiCM) * Update tracker now uses TLS (@pandabear41) * When deleting an user account, any player logged in to that account is now logged out properly (@Enerdy) +* Add NPCAddBuff data handler and bouncer (@AxeelAnder) ## TShock 4.3.25 * Fixed a critical exploit in the Terraria protocol that could cause massive unpreventable world corruption as well as a number of other problems. Thanks to @bartico6 for reporting. Fixed by the efforts of @QuiCM, @hakusaro, and tips in the right directioon from @bartico6. From 6b8f346868fb98d664710f5e83dc7512b90c1239 Mon Sep 17 00:00:00 2001 From: AxeelAnder <1491773534@qq.com> Date: Sat, 20 Oct 2018 12:48:05 +0800 Subject: [PATCH 06/19] add NPCAddBuff GetDataHandler --- TShockAPI/GetDataHandlers.cs | 54 ++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index c59ce1a7..b3769e4e 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -1003,6 +1003,46 @@ namespace TShockAPI NPCHome.Invoke(null, args); return args.Handled; } + + /// + /// For use in a NPCAddBuff event + /// + public class NPCAddBuffEventArgs : GetDataHandledEventArgs + { + /// + /// The ID of the npc + /// + public short ID { get; set; } + /// + /// Buff Type + /// + public byte Type { get; set; } + /// + /// Time the buff lasts + /// + public short Time { get; set; } + } + /// + /// NPCAddBuff - Called when a npc is buffed + /// + public static HandlerList NPCAddBuff = new HandlerList(); + + private static bool OnNPCAddBuff(TSPlayer player, MemoryStream data, short id, byte type, short time) + { + if (NPCAddBuff == null) + return false; + + var args = new NPCAddBuffEventArgs + { + Player = player, + Data = data, + ID = id, + Type = type, + Time = time + }; + NPCAddBuff.Invoke(null, args); + return args.Handled; + } /// /// For use in a PlayerBuff event @@ -1455,6 +1495,7 @@ namespace TShockAPI { PacketTypes.PlayerSlot, HandlePlayerSlot }, { PacketTypes.TileGetSection, HandleGetSection }, { PacketTypes.UpdateNPCHome, UpdateNPCHome }, + { PacketTypes.NpcAddBuff, HandleNPCAddBuff }, { PacketTypes.PlayerAddBuff, HandlePlayerAddBuff }, { PacketTypes.ItemDrop, HandleItemDrop }, { PacketTypes.UpdateItemDrop, HandleItemDrop }, @@ -2627,6 +2668,19 @@ namespace TShockAPI } return false; } + + private static bool HandleNPCAddBuff(GetDataHandlerArgs args) + { + var id = args.Data.ReadInt16(); + var type = args.Data.ReadInt8(); + var time = args.Data.ReadInt16(); + + if (OnNPCAddBuff(args.Player, args.Data, id, type, time)) + return true; + + args.Player.SendData(PacketTypes.NpcAddBuff, "", id); + return true; + } private static bool HandlePlayerAddBuff(GetDataHandlerArgs args) { From 87915b70b0e6be7484263bdebc19b5f208750ff8 Mon Sep 17 00:00:00 2001 From: AxeelAnder <1491773534@qq.com> Date: Sat, 20 Oct 2018 14:41:58 +0800 Subject: [PATCH 07/19] add ignoreNpcAddBuffPermission --- TShockAPI/Permissions.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TShockAPI/Permissions.cs b/TShockAPI/Permissions.cs index 7733f110..dec457d3 100644 --- a/TShockAPI/Permissions.cs +++ b/TShockAPI/Permissions.cs @@ -169,6 +169,9 @@ namespace TShockAPI [Description("Prevents you from being disabled by abnormal MP.")] public static readonly string ignoremp = "tshock.ignore.mp"; + + [Description("Prevents you from being disabled by add buffs to npcs abnormally.")] + public static readonly string ignorenpcaddbuffdetection = "tshock.ignore.npcaddbuff"; // tshock.item nodes From fc38521dbd4538cd0e644265799a141d9b6ba815 Mon Sep 17 00:00:00 2001 From: AxeelAnder <1491773534@qq.com> Date: Sat, 20 Oct 2018 13:21:38 +0800 Subject: [PATCH 08/19] add NPCAddBuff bouncer --- TShockAPI/Bouncer.cs | 32 ++++++++++++++++++++++++++++++++ TShockAPI/GetDataHandlers.cs | 3 +-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 223de05b..b71c7de8 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -49,6 +49,7 @@ namespace TShockAPI GetDataHandlers.PlayerAnimation += OnPlayerAnimation; GetDataHandlers.NPCStrike += OnNPCStrike; GetDataHandlers.ItemDrop += OnItemDrop; + GetDataHandlers.NPCAddBuff += OnNPCAddBuff; GetDataHandlers.PlayerBuff += OnPlayerBuff; GetDataHandlers.ChestItemChange += OnChestItemChange; GetDataHandlers.NPCHome += OnUpdateNPCHome; @@ -453,6 +454,37 @@ namespace TShockAPI return; } } + + /// Handles NPCAddBuff events. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnNPCAddBuff(object sender, GetDataHandlers.NPCAddBuffEventArgs args) + { + short id = args.ID; + byte type = args.Type; + short time = args.Time; + + if (id >= Main.npc.Length) + { + args.Handled = true; + return; + } + + NPC npc = Main.npc[id]; + + if (npc == null) + { + args.Handled = true; + return; + } + + if (args.Player.IsBeingDisabled()) + { + args.Player.SendData(PacketTypes.NpcAddBuff, "", id); + args.Handled = true; + return; + } + } /// Handles Buff events. /// The object that triggered the event. diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index b3769e4e..6673dd64 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -2677,8 +2677,7 @@ namespace TShockAPI if (OnNPCAddBuff(args.Player, args.Data, id, type, time)) return true; - - args.Player.SendData(PacketTypes.NpcAddBuff, "", id); + return true; } From b9dedd77dcb03a0e47ae0beeb12a7f19568beaad Mon Sep 17 00:00:00 2001 From: AxeelAnder <1491773534@qq.com> Date: Sat, 20 Oct 2018 17:44:34 +0800 Subject: [PATCH 09/19] add some anti-cheat code --- TShockAPI/Bouncer.cs | 60 +++++++++++++++++++++++++++++++++++- TShockAPI/GetDataHandlers.cs | 2 +- TShockAPI/TShock.cs | 32 +++++++++++++++++++ 3 files changed, 92 insertions(+), 2 deletions(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index b71c7de8..79dce7d1 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -480,10 +480,68 @@ namespace TShockAPI if (args.Player.IsBeingDisabled()) { - args.Player.SendData(PacketTypes.NpcAddBuff, "", id); args.Handled = true; return; } + + if (!args.Player.HasPermission(Permissions.ignorenpcaddbuffdetection)) + { + bool cheat = false; + + if(TShock.NPCAddBuffTimeMax.ContainsKey(type)) + { + if(time > TShock.NPCAddBuffTimeMax[type]) + { + cheat = true; + } + + if(npc.townNPC && npc.netID != NPCID.Guide && npc.netID != NPCID.Clothier) + { + if(type != BuffID.Lovestruck && type != BuffID.Stinky && type != BuffID.DryadsWard && + type != BuffID.Wet && type != BuffID.Slimed) + { + cheat = true; + } + } + // Want to check voodoo doll but it may be wrong. + //else if(npc.netID == NPCID.Guide) + //{ + // bool hasDoll = false; + // foreach (var item in args.Player.Accessories) + // { + // if (item.netID == ItemID.GuideVoodooDoll) + // { + // hasDoll = true; + // break; + // } + // } + // cheat = !hasDoll; + //} + //else if (npc.netID == NPCID.Clothier) + //{ + // bool hasDoll = false; + // foreach (var item in args.Player.Accessories) + // { + // if (item.netID == ItemID.ClothierVoodooDoll) + // { + // hasDoll = true; + // break; + // } + // } + // cheat = !hasDoll; + //} + } + else + { + cheat = true; + } + + if (cheat) + { + args.Player.Disable("Add buff to NPC abnormally.", DisableFlags.WriteToLogAndConsole); + args.Handled = true; + } + } } /// Handles Buff events. diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index 6673dd64..a7792569 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -2678,7 +2678,7 @@ namespace TShockAPI if (OnNPCAddBuff(args.Player, args.Data, id, type, time)) return true; - return true; + return false; } private static bool HandlePlayerAddBuff(GetDataHandlerArgs args) diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index f839019d..18cff28e 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -131,6 +131,9 @@ namespace TShockAPI /// public static Dictionary RESTStartupTokens = new Dictionary(); + /// The longest time of buffs players can add to NPCs. + public static Dictionary NPCAddBuffTimeMax; + /// The TShock anti-cheat/anti-exploit system. internal Bouncer Bouncer; @@ -323,6 +326,7 @@ namespace TShockAPI RestApi = new SecureRest(Netplay.ServerIP, Config.RestApiPort); RestManager = new RestManager(RestApi); RestManager.RegisterRestfulCommands(); + NPCAddBuffTimeMax = InitNPCAddBuffTimeMax(); Bouncer = new Bouncer(); RegionSystem = new RegionHandler(Regions); @@ -385,6 +389,34 @@ namespace TShockAPI } } + private Dictionary InitNPCAddBuffTimeMax() + { + var dict = new Dictionary(); + + dict.Add(BuffID.Poisoned, 3600); + dict.Add(BuffID.OnFire, 1200); + dict.Add(BuffID.CursedInferno, 420); + dict.Add(BuffID.Frostburn, 900); + dict.Add(BuffID.Ichor, 1200); + dict.Add(BuffID.Venom, 1260); + dict.Add(BuffID.Midas, 120); + dict.Add(BuffID.Wet, 1500); + dict.Add(BuffID.Slimed, 1500); + dict.Add(BuffID.Lovestruck, 1800); + dict.Add(BuffID.Stinky, 1800); + dict.Add(BuffID.SoulDrain, 30); + dict.Add(BuffID.ShadowFlame, 660); + dict.Add(BuffID.DryadsWard, 120); + dict.Add(BuffID.BoneJavelin, 900); + dict.Add(BuffID.StardustMinionBleed, 900); + dict.Add(BuffID.DryadsWardDebuff, 120); + dict.Add(BuffID.Daybreak, 300); + dict.Add(BuffID.BetsysCurse, 600); + dict.Add(BuffID.Oiled, 540); + + return dict; + } + protected void CrashReporter_HeapshotRequesting(object sender, EventArgs e) { foreach (TSPlayer player in TShock.Players) From 6efa0d2bb6d0c53cc2094ca681236261429788b7 Mon Sep 17 00:00:00 2001 From: AxeelAnder <1491773534@qq.com> Date: Sat, 20 Oct 2018 20:07:04 +0800 Subject: [PATCH 10/19] better code --- TShockAPI/Bouncer.cs | 97 +++++++++++++++++++--------------------- TShockAPI/Permissions.cs | 3 -- TShockAPI/TShock.cs | 32 ------------- 3 files changed, 46 insertions(+), 86 deletions(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 79dce7d1..ba7c2c85 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -36,6 +36,31 @@ namespace TShockAPI /// Bouncer is the TShock anti-hack and anti-cheat system. internal sealed class Bouncer { + static Dictionary NPCAddBuffTimeMax = new Dictionary() + { + { BuffID.Poisoned, 3600 }, + { BuffID.OnFire, 1200 }, + { BuffID.CursedInferno, 420 }, + { BuffID.Frostburn, 900 }, + { BuffID.Ichor, 1200 }, + { BuffID.Venom, 1260 }, + { BuffID.Midas, 120 }, + { BuffID.Wet, 1500 }, + { BuffID.Slimed, 1500 }, + { BuffID.Lovestruck, 1800 }, + { BuffID.Stinky, 1800 }, + { BuffID.SoulDrain, 30 }, + { BuffID.ShadowFlame, 660 }, + { BuffID.DryadsWard, 120 }, + { BuffID.BoneJavelin, 900 }, + { BuffID.StardustMinionBleed, 900 }, + { BuffID.DryadsWardDebuff, 120 }, + { BuffID.Daybreak, 300 }, + { BuffID.BetsysCurse, 600 }, + { BuffID.Oiled, 540 } + }; + + /// Constructor call initializes Bouncer and related functionality. /// A new Bouncer. internal Bouncer() @@ -454,7 +479,7 @@ namespace TShockAPI return; } } - + /// Handles NPCAddBuff events. /// The object that triggered the event. /// The packet arguments that the event has. @@ -484,64 +509,34 @@ namespace TShockAPI return; } - if (!args.Player.HasPermission(Permissions.ignorenpcaddbuffdetection)) + bool cheat = false; + + if (NPCAddBuffTimeMax.ContainsKey(type)) { - bool cheat = false; - - if(TShock.NPCAddBuffTimeMax.ContainsKey(type)) - { - if(time > TShock.NPCAddBuffTimeMax[type]) - { - cheat = true; - } - - if(npc.townNPC && npc.netID != NPCID.Guide && npc.netID != NPCID.Clothier) - { - if(type != BuffID.Lovestruck && type != BuffID.Stinky && type != BuffID.DryadsWard && - type != BuffID.Wet && type != BuffID.Slimed) - { - cheat = true; - } - } - // Want to check voodoo doll but it may be wrong. - //else if(npc.netID == NPCID.Guide) - //{ - // bool hasDoll = false; - // foreach (var item in args.Player.Accessories) - // { - // if (item.netID == ItemID.GuideVoodooDoll) - // { - // hasDoll = true; - // break; - // } - // } - // cheat = !hasDoll; - //} - //else if (npc.netID == NPCID.Clothier) - //{ - // bool hasDoll = false; - // foreach (var item in args.Player.Accessories) - // { - // if (item.netID == ItemID.ClothierVoodooDoll) - // { - // hasDoll = true; - // break; - // } - // } - // cheat = !hasDoll; - //} - } - else + if (time > NPCAddBuffTimeMax[type]) { cheat = true; } - if (cheat) + if (npc.townNPC && npc.netID != NPCID.Guide && npc.netID != NPCID.Clothier) { - args.Player.Disable("Add buff to NPC abnormally.", DisableFlags.WriteToLogAndConsole); - args.Handled = true; + if (type != BuffID.Lovestruck && type != BuffID.Stinky && type != BuffID.DryadsWard && + type != BuffID.Wet && type != BuffID.Slimed) + { + cheat = true; + } } } + else + { + cheat = true; + } + + if (cheat) + { + args.Player.Disable("Add buff to NPC abnormally.", DisableFlags.WriteToLogAndConsole); + args.Handled = true; + } } /// Handles Buff events. diff --git a/TShockAPI/Permissions.cs b/TShockAPI/Permissions.cs index dec457d3..7733f110 100644 --- a/TShockAPI/Permissions.cs +++ b/TShockAPI/Permissions.cs @@ -169,9 +169,6 @@ namespace TShockAPI [Description("Prevents you from being disabled by abnormal MP.")] public static readonly string ignoremp = "tshock.ignore.mp"; - - [Description("Prevents you from being disabled by add buffs to npcs abnormally.")] - public static readonly string ignorenpcaddbuffdetection = "tshock.ignore.npcaddbuff"; // tshock.item nodes diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index 18cff28e..f839019d 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -131,9 +131,6 @@ namespace TShockAPI /// public static Dictionary RESTStartupTokens = new Dictionary(); - /// The longest time of buffs players can add to NPCs. - public static Dictionary NPCAddBuffTimeMax; - /// The TShock anti-cheat/anti-exploit system. internal Bouncer Bouncer; @@ -326,7 +323,6 @@ namespace TShockAPI RestApi = new SecureRest(Netplay.ServerIP, Config.RestApiPort); RestManager = new RestManager(RestApi); RestManager.RegisterRestfulCommands(); - NPCAddBuffTimeMax = InitNPCAddBuffTimeMax(); Bouncer = new Bouncer(); RegionSystem = new RegionHandler(Regions); @@ -389,34 +385,6 @@ namespace TShockAPI } } - private Dictionary InitNPCAddBuffTimeMax() - { - var dict = new Dictionary(); - - dict.Add(BuffID.Poisoned, 3600); - dict.Add(BuffID.OnFire, 1200); - dict.Add(BuffID.CursedInferno, 420); - dict.Add(BuffID.Frostburn, 900); - dict.Add(BuffID.Ichor, 1200); - dict.Add(BuffID.Venom, 1260); - dict.Add(BuffID.Midas, 120); - dict.Add(BuffID.Wet, 1500); - dict.Add(BuffID.Slimed, 1500); - dict.Add(BuffID.Lovestruck, 1800); - dict.Add(BuffID.Stinky, 1800); - dict.Add(BuffID.SoulDrain, 30); - dict.Add(BuffID.ShadowFlame, 660); - dict.Add(BuffID.DryadsWard, 120); - dict.Add(BuffID.BoneJavelin, 900); - dict.Add(BuffID.StardustMinionBleed, 900); - dict.Add(BuffID.DryadsWardDebuff, 120); - dict.Add(BuffID.Daybreak, 300); - dict.Add(BuffID.BetsysCurse, 600); - dict.Add(BuffID.Oiled, 540); - - return dict; - } - protected void CrashReporter_HeapshotRequesting(object sender, EventArgs e) { foreach (TSPlayer player in TShock.Players) From e9372be276a79eefba10187ce6aa8a94bce35dfc Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Sat, 20 Oct 2018 09:14:38 -0700 Subject: [PATCH 11/19] Rename cheat var to detectedNPCBuffTimeCheat. --- TShockAPI/Bouncer.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index ba7c2c85..395ecc36 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -509,13 +509,13 @@ namespace TShockAPI return; } - bool cheat = false; + bool detectedNPCBuffTimeCheat = false; if (NPCAddBuffTimeMax.ContainsKey(type)) { if (time > NPCAddBuffTimeMax[type]) { - cheat = true; + detectedNPCBuffTimeCheat = true; } if (npc.townNPC && npc.netID != NPCID.Guide && npc.netID != NPCID.Clothier) @@ -523,18 +523,18 @@ namespace TShockAPI if (type != BuffID.Lovestruck && type != BuffID.Stinky && type != BuffID.DryadsWard && type != BuffID.Wet && type != BuffID.Slimed) { - cheat = true; + detectedNPCBuffTimeCheat = true; } } } else { - cheat = true; + detectedNPCBuffTimeCheat = true; } - if (cheat) + if (detectedNPCBuffTimeCheat) { - args.Player.Disable("Add buff to NPC abnormally.", DisableFlags.WriteToLogAndConsole); + args.Player.Disable("Added buff to NPC abnormally.", DisableFlags.WriteToLogAndConsole); args.Handled = true; } } From f715d91dc6682991c11a941c6367af39d1bb1f53 Mon Sep 17 00:00:00 2001 From: AxeelAnder <1491773534@qq.com> Date: Sun, 21 Oct 2018 11:01:08 +0800 Subject: [PATCH 12/19] disable -> kick --- TShockAPI/Bouncer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 395ecc36..7287924f 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -534,7 +534,7 @@ namespace TShockAPI if (detectedNPCBuffTimeCheat) { - args.Player.Disable("Added buff to NPC abnormally.", DisableFlags.WriteToLogAndConsole); + args.Player.Kick("Added buff to NPC abnormally.", true); args.Handled = true; } } From 447d801dbca210eb909f274b221221469f5a1a4e Mon Sep 17 00:00:00 2001 From: Enerdy Date: Sun, 21 Oct 2018 19:52:46 +0100 Subject: [PATCH 13/19] Clean up xml comments and descriptions in ConfigFile.cs Remove property name from start of xml comments xml comments now almost always match the property's description Improve grammar in both comments and descriptions overall --- TShockAPI/ConfigFile.cs | 444 ++++++++++++++++++++-------------------- 1 file changed, 222 insertions(+), 222 deletions(-) diff --git a/TShockAPI/ConfigFile.cs b/TShockAPI/ConfigFile.cs index c68c2fbc..863260b7 100644 --- a/TShockAPI/ConfigFile.cs +++ b/TShockAPI/ConfigFile.cs @@ -27,497 +27,497 @@ using Rests; namespace TShockAPI { - /// ConfigFile - The config file class, which contains the configuration for a server that is serialized into JSON and deserialized on load. + /// The config file class, which contains the configuration for a server that is serialized into JSON and deserialized on load. public class ConfigFile { - /// InvasionMultiplier - The equation for calculating invasion size = 100 + (multiplier * (number of active players > 200 hp)). - [Description("The equation for calculating invasion size is 100 + (multiplier * (number of active players with greater than 200 health)).")] + /// Determines the size of invasion events. The equation for calculating invasion size = 100 + (multiplier * (number of active players > 200 hp)). + [Description("Determines the size of invasion events.\nThe equation for calculating invasion size is 100 + (multiplier * (number of active players with greater than 200 health)).")] public int InvasionMultiplier = 1; - /// DefaultMaximumSpawns - The default max spawns per wave. - [Description("The default maximum mobs that will spawn per wave. Higher means more mobs in that wave.")] + /// The default maximum number of mobs that will spawn per wave. Higher means more mobs in that wave. + [Description("The default maximum number of mobs that will spawn per wave. Higher means more mobs in that wave.")] public int DefaultMaximumSpawns = 5; - /// DefaultSpawnRate - The default spawn rate. + /// The delay between waves. Lower values lead to more mobs. [Description("The delay between waves. Lower values lead to more mobs.")] public int DefaultSpawnRate = 600; - /// ServerPort - The configured server port. + /// The port the server runs on. [Description("The port the server runs on.")] public int ServerPort = 7777; - /// EnableWhitelist - boolean if the whitelist functionality should be turned on. - [Description("Enable or disable the whitelist based on IP addresses in whitelist.txt.")] + /// Enable or disable the whitelist based on IP addresses in the whitelist.txt file. + [Description("Enable or disable the whitelist based on IP addresses in the whitelist.txt file.")] public bool EnableWhitelist; - /// InfiniteInvasion - Whether or not infinite invasion mode should be on. - [Description("Enable the ability for invasion size to never decrease. Make sure to run /invade, and note that this adds 2 million+ goblins to the spawn queue for the map.")] + /// Enables never-ending invasion events. You still need to start the event. + [Description("Enables never ending invasion events. You still need to start the event, such as with the /invade command.")] public bool InfiniteInvasion; - /// PvPMode - The server PvP mode (normal, always, or disabled). - [Description("Set the server pvp mode. Valid types are: \"normal\", \"always\" and \"disabled\".")] + /// Sets the PvP mode. Valid types are: "normal", "always", "disabled". + [Description("Sets the PvP mode. Valid types are: \"normal\", \"always\" and \"disabled\".")] public string PvPMode = "normal"; - /// SpawnProtection - Enables the spawn protection system. + /// Prevents tiles from being placed within SpawnProtectionRadius of the default spawn. [Description("Prevents tiles from being placed within SpawnProtectionRadius of the default spawn.")] public bool SpawnProtection = true; - /// SpawnProtectionRadius - The spawn protection tile radius. - [Description("Radius from spawn tile for SpawnProtection.")] + /// The tile radius around the spawn tile that is protected by the SpawnProtection setting. + [Description("The tile radius around the spawn tile that is protected by the SpawnProtection setting.")] public int SpawnProtectionRadius = 10; - /// MaxSlots - The server's max slots. - [Description("Max slots for the server. If you want people to be kicked with \"Server is full\" set this to how many players you want max and then set Terraria max players to 2 higher.")] + /// Maximum number of clients connected at once. If lower than Terraria's setting, the server will kick excess connections. + [Description("Maximum number of clients connected at once.\nIf you want people to be kicked with \"Server is full\" set this to how many players you want max and then set Terraria max players to 2 higher.")] public int MaxSlots = 8; - /// RangeChecks - Whether or not the anti-grief system based on range should be enabled. - [Description("Global protection agent for any block distance based anti-grief check.")] + /// Enable or disable anti-cheat range checks based on distance between the player and their block placements. + [Description("Enable or disable anti-cheat range checks based on distance between the player and their block placements.")] public bool RangeChecks = true; - /// DisableBuild - Whether or not building should be enabled. - [Description("Disables any building / placing of blocks.")] + /// Disables any placing, or removal of blocks. + [Description("Disables any placing, or removal of blocks.")] public bool DisableBuild; - /// SuperAdminChatRGB - The chat color for the superadmin group. - [Description("#.#.# = Red/Blue/Green - RGB Colors for the Admin Chat Color. Max value: 255.")] + /// The chat color for the superadmin group. + [Description("The chat color for the superadmin group.\n#.#.# = Red/Blue/Green\nMax value: 255")] public int[] SuperAdminChatRGB = { 255, 255, 255 }; - /// SuperAdminChatPrefix - The superadmin chat prefix. - [Description("Super admin group chat prefix.")] + /// The superadmin chat prefix. + [Description("The superadmin chat prefix.")] public string SuperAdminChatPrefix = "(Super Admin) "; - /// SuperAdminChatSuffix - The superadmin chat suffix. - [Description("Super admin group chat suffix.")] + /// The superadmin chat suffix. + [Description("The superadmin chat suffix.")] public string SuperAdminChatSuffix = ""; - /// BackupInterval - The backup frequency in minutes. - [Description("Backup frequency in minutes. So, a value of 60 = 60 minutes. Backups are stored in the \\tshock\\backups folder.")] + /// The interval between backups, in minutes. Backups are stored in the tshock/backups folder. + [Description("The interval between backups, in minutes. Backups are stored in the tshock/backups folder.")] public int BackupInterval; - /// BackupKeepFor - Backup max age in minutes. - [Description("How long backups are kept in minutes. 2880 = 2 days.")] + /// For how long backups are kept in minutes. + [Description("For how long backups are kept in minutes.\neg. 2880 = 2 days.")] public int BackupKeepFor = 60; - /// RememberLeavePos - Whether or not to remember where an IP player was when they left. - [Description("Remembers where a player left off. It works by remembering the IP, NOT the character.\neg. When you try to disconnect, and reconnect to be automatically placed at spawn, you'll be at your last location. Note: Won't save after server restarts.")] + /// Remembers where a player left off, based on their IP. Does not persist through server restarts. + [Description("Remembers where a player left off, based on their IP. Does not persist through server restarts.\neg. When you try to disconnect, and reconnect to be automatically placed at spawn, you'll be at your last location.")] public bool RememberLeavePos; - /// HardcoreOnly - Whether or not HardcoreOnly should be enabled. - [Description("Hardcore players ONLY. This means softcore players cannot join.")] + /// Prevents non-hardcore players from connecting. + [Description("Prevents non-hardcore players from connecting.")] public bool HardcoreOnly; - /// MediumcoreOnly - Whether or not MediumCore only players should be enabled. - [Description("Mediumcore players ONLY. This means softcore players cannot join.")] + /// Prevents softcore players from connecting. + [Description("Prevents softcore players from connecting.")] public bool MediumcoreOnly; - /// KickOnMediumcoreDeath - Whether or not to kick mediumcore players on death. - [Description("Kicks a mediumcore player on death.")] + /// Whether or not to kick mediumcore players on death. + [Description("Whether or not to kick mediumcore players on death.")] public bool KickOnMediumcoreDeath; - /// BanOnMediumcoreDeath - Whether or not to ban mediumcore players on death. - [Description("Bans a mediumcore player on death.")] + /// Whether or not to ban mediumcore players on death. + [Description("Whether or not to ban mediumcore players on death.")] public bool BanOnMediumcoreDeath; - /// AutoSave - Whether or not to use Terraria's built-in world auto save. - [Description("Enable/disable Terraria's built in auto save.")] + /// Enable or disable Terraria's built-in world auto save. + [Description("Enable or disable Terraria's built-in world auto save.")] public bool AutoSave = true; - /// AnnounceSave - Whether or not to broadcast world saves. - [Description("Enable/disable save announcements.")] + /// Enable or disable world save announcements. + [Description("Enable or disable world save announcements.")] public bool AnnounceSave = true; - /// MaximumLoginAttempts - Number of failed login attempts before kicking a player. + /// Number of failed login attempts before kicking the player. [Description("Number of failed login attempts before kicking the player.")] public int MaximumLoginAttempts = 3; - /// ServerName - Used when replying to a REST /status request or sent to the client. + /// Replaces the world name during a session if UseServerName is true. [Description("Replaces the world name during a session if UseServerName is true.")] public string ServerName = ""; - /// UseServerName - Whether or not to use ServerName in place of the world name. - [Description("Sends ServerName in place of the world name to clients.")] + /// Whether or not to use ServerName in place of the world name. + [Description("Whether or not to use ServerName in place of the world name.")] public bool UseServerName = false; - /// StorageType - The type of SQL database to use when storing data (either "sqlite" or "mysql"). - [Description("Valid types are \"sqlite\" and \"mysql\".")] + /// The type of database to use when storing data (either "sqlite" or "mysql"). + [Description("The type of database to use when storing data (either \"sqlite\" or \"mysql\").")] public string StorageType = "sqlite"; - /// MySqlHost - The hostname and port to to use when connecting to a MySQL database. + /// The MySQL hostname and port to direct connections to. [Description("The MySQL hostname and port to direct connections to.")] public string MySqlHost = "localhost:3306"; - /// MySqlDbName - The database name to use when connecting to a MySQL database. - [Description("Database name to connect to.")] + /// The database name to connect to when using MySQL as the database type. + [Description("The database name to connect to when using MySQL as the database type.")] public string MySqlDbName = ""; - /// MySqlUsername - The username for the login credentials used when connecting to a MySQL database. - [Description("Database username to connect with.")] + /// The username used when connecting to a MySQL database. + [Description("The username used when connecting to a MySQL database.")] public string MySqlUsername = ""; - /// MySqlPassword - The password for the login credentials used when connecting to a MySQL database. - [Description("Database password to connect with.")] + /// The password used when connecting to a MySQL database. + [Description("The password used when connecting to a MySQL database.")] public string MySqlPassword = ""; - /// MediumcoreBanReason - The reason given if banning mediumcore players on death. - [Description("The reason given when banning a mediumcore player on death if BanOnMediumcoreDeath is set to true.")] + /// The reason given if banning a mediumcore player on death. + [Description("The reason given if banning a mediumcore player on death.")] public string MediumcoreBanReason = "Death results in a ban"; - /// MediumcoreKickReason - The reason given if kicking mediumcore players on death. - [Description("The reason given when kicking a mediumcore player on death if KickOnMediumcoreDeath is set to true.")] + /// The reason given if kicking a mediumcore players on death. + [Description("The reason given if kicking a mediumcore players on death.")] public string MediumcoreKickReason = "Death results in a kick"; - /// EnableIPBans - Whether or not to kick players on join that match a banned IP address. - [Description("Enables kicking of banned users by matching their IP Address.")] + /// Enables kicking banned users by matching their IP Address. + [Description("Enables kicking banned users by matching their IP Address.")] public bool EnableIPBans = true; - /// EnableUUIDBans - Whether or not to kick players on join that match a banned UUID. - [Description("Enables kicking of banned users by matching their client UUID.")] + /// Enables kicking banned users by matching their client UUID. + [Description("Enables kicking banned users by matching their client UUID.")] public bool EnableUUIDBans = true; - /// EnableBanOnUsernames - Whether or not to kick players on join that match a banned character name. - [Description("Enables kicking of banned users by matching their Character Name.")] + /// Enables kicking banned users by matching their Character Name. + [Description("Enables kicking banned users by matching their Character Name.")] public bool EnableBanOnUsernames; - /// DefaultRegistrationGroupName - The default group name to place newly registered users under. - [Description("Selects the default group name to place new registrants under.")] + /// The default group name to place newly registered users under. + [Description("The default group name to place newly registered users under.")] public string DefaultRegistrationGroupName = "default"; - /// DefaultGuestGroupName - The default group name to place unregistered players under. - [Description("Selects the default group name to place unregistered players under.")] + /// The default group name to place unregistered players under. + [Description("The default group name to place unregistered players under.")] public string DefaultGuestGroupName = "guest"; - /// DisableSpewLogs - Whether or not to send logs as messages to players with the log permission. - [Description("Force-disable printing logs to players with the log permission.")] + /// Disables sending logs as messages to players with the log permission. + [Description("Disables sending logs as messages to players with the log permission.")] public bool DisableSpewLogs = true; - /// DisableSecondUpdateLogs - Prevents OnSecondUpdate() checks from writing to the log file. + /// Prevents OnSecondUpdate checks from writing to the log file. [Description("Prevents OnSecondUpdate checks from writing to the log file.")] public bool DisableSecondUpdateLogs = false; - /// HashAlgorithm - The hash algorithm used to encrypt user passwords. + /// The hash algorithm used to encrypt user passwords. /// Valid types: "sha512", "sha256" and "md5". Append with "-xp" for the xp supported algorithms. [Description("The hash algorithm used to encrypt user passwords. Valid types: \"sha512\", \"sha256\" and \"md5\". Append with \"-xp\" for the xp supported algorithms.")] public string HashAlgorithm = "sha512"; - /// ServerFullReason - The reason given when kicking players when the server is full. - [Description("String that is used when kicking people when the server is full.")] + /// The reason given when kicking players that attempt to join while the server is full. + [Description("The reason given when kicking players that attempt to join while the server is full.")] public string ServerFullReason = "Server is full"; - /// WhitelistKickReason - The reason given when kicking players for not being on the whitelist. - [Description("String that is used when a user is kicked due to not being on the whitelist.")] + /// The reason given when kicking players for not being on the whitelist. + [Description("The reason given when kicking players for not being on the whitelist.")] public string WhitelistKickReason = "You are not on the whitelist."; - /// ServerFullNoReservedReason - The reason given when kicking players when the server is full and there are no reserved slots open. - [Description("String that is used when kicking people when the server is full with no reserved slots.")] + /// The reason given when kicking players that attempt to join while the server is full with no reserved slots available. + [Description("The reason given when kicking players that attempt to join while the server is full with no reserved slots available.")] public string ServerFullNoReservedReason = "Server is full. No reserved slots open."; - /// SaveWorldOnCrash - Attempts to save world in the server crashes due to an unhandled exception. - [Description("This will save the world if Terraria crashes from an unhandled exception.")] + /// Whether or not to save the world if the server crashes from an unhandled exception. + [Description("Whether or not to save the world if the server crashes from an unhandled exception.")] public bool SaveWorldOnCrash = true; - /// EnableGeoIP - Whether or not to announce a player's location on join. - [Description("This will announce a player's location on join.")] + /// Whether or not to announce a player's geographic location on join, based on their IP. + [Description("Whether or not to announce a player's geographic location on join, based on their IP.")] public bool EnableGeoIP; - /// EnableTokenEndpointAuthentication - Whether or not to require token authentication for the public REST API endpoints. - [Description("This will turn on token requirement for the public REST API endpoints.")] + /// Whether or not to require token authentication to use the public REST API endpoints. + [Description("Whether or not to require token authentication to use the public REST API endpoints.")] public bool EnableTokenEndpointAuthentication; - /// RestApiEnabled - Enable/disable the REST API. - [Description("Enable/disable the REST API.")] + /// Enable or disable the REST API. + [Description("Enable or disable the REST API.")] public bool RestApiEnabled; - /// RestApiPort - The port used by the REST API. - [Description("This is the port which the REST API will listen on.")] + /// The port used by the REST API. + [Description("The port used by the REST API.")] public int RestApiPort = 7878; - /// DisableTombstones - Disable tombstone dropping during death for all players. - [Description("Disable tombstone dropping during death for all players.")] + /// Disables tombstone dropping during death for all players. + [Description("Disables tombstone dropping during death for all players.")] public bool DisableTombstones = true; - /// DisplayIPToAdmins - Displays a player's IP on join to everyone with the log permission. - [Description("Displays a player's IP on join to everyone who has the log permission.")] + /// Displays a player's IP on join to users with the log permission. + [Description("Displays a player's IP on join to users with the log permission.")] public bool DisplayIPToAdmins; - /// KickProxyUsers - If the GeoIP service is running, this will kick users under a proxy. - [Description("Kicks users using a proxy as identified with the GeoIP database.")] + /// If GeoIP is enabled, this will kick users identified as being under a proxy. + [Description("If GeoIP is enabled, this will kick users identified as being under a proxy.")] public bool KickProxyUsers = true; - /// DisableHardmode - If set to true, hardmode will not be activated by the Wall of Flesh or the /starthardmode command. - [Description("Disables hardmode, can't never be activated. Overrides /starthardmode.")] + /// If enabled, hardmode will not be activated by the Wall of Flesh or the /starthardmode command. + [Description("If enabled, hardmode will not be activated by the Wall of Flesh or the /starthardmode command.")] public bool DisableHardmode; - /// DisableDungeonGuardian - Disables the dungeon guardian from being spawned while sending players to their spawn point instead. - [Description("Disables the dungeon guardian from being spawned while sending players to their spawn point instead.")] + /// Prevents the dungeon guardian from being spawned while sending players to their spawn point instead. + [Description("Prevents the dungeon guardian from being spawned while sending players to their spawn point instead.")] public bool DisableDungeonGuardian; - /// DisableClownBombs - Disables clown bomb projectiles from spawning. + /// Disables clown bomb projectiles from spawning. [Description("Disables clown bomb projectiles from spawning.")] public bool DisableClownBombs; - /// DisableSnowBalls - Disables snow ball projectiles from spawning. + /// Disables snow ball projectiles from spawning. [Description("Disables snow ball projectiles from spawning.")] public bool DisableSnowBalls; - /// ChatFormat - Controls the in-game chat format. {0} = Group Name, {1} = Group Prefix, {2} = Player Name, {3} = Group Suffix, {4} = Chat Message. + /// Changes in-game chat format: {0} = Group Name, {1} = Group Prefix, {2} = Player Name, {3} = Group Suffix, {4} = Chat Message. [Description("Changes in-game chat format: {0} = Group Name, {1} = Group Prefix, {2} = Player Name, {3} = Group Suffix, {4} = Chat Message.")] public string ChatFormat = "{1}{2}{3}: {4}"; - /// ChatAboveHeadsFormat - Modifies the player name when using chat above heads. Same formatting options as ChatFormat. - [Description("Change the player name when using chat above heads. This begins with a player name wrapped in brackets, as per Terraria's formatting. Same formatting as ChatFormat(minus the text aka {4}).")] + /// Changes the player name when using chat above heads. Starts with a player name wrapped in brackets, as per Terraria's formatting.\nSame formatting as ChatFormat without the message. + [Description("Changes the player name when using chat above heads. Starts with a player name wrapped in brackets, as per Terraria's formatting.\nSame formatting as ChatFormat without the message.")] public string ChatAboveHeadsFormat = "{2}"; - /// ForceTime - Can be either "normal", "day" or "night". When set to one of the latter two, the blocks other. - [Description("Force the world time to be normal, day, or night.")] + /// Forces the world time to be normal, day, or night. + [Description("Forces the world time to be normal, day, or night.")] public string ForceTime = "normal"; - /// TileKillThreshold - Disables/reverts a player if this number of tile kills is exceeded within 1 second. - [Description("Disables/reverts a player if this number of tile kills is exceeded within 1 second.")] + /// Disables a player and reverts their actions if this number of tile kills is exceeded within 1 second. + [Description("Disables a player and reverts their actions if this number of tile kills is exceeded within 1 second.")] public int TileKillThreshold = 60; - /// TilePlaceThreshold - Disables/reverts a player if this number of tile placements is exceeded within 1 second. - [Description("Disables/reverts a player if this number of tile places is exceeded within 1 second.")] + /// Disables a player and reverts their actions if this number of tile places is exceeded within 1 second. + [Description("Disables a player and reverts their actions if this number of tile places is exceeded within 1 second.")] public int TilePlaceThreshold = 20; - /// TileLiquidThreshold - Disables a player if this number of liquid sets is exceeded within 1 second. + /// Disables a player if this number of liquid sets is exceeded within 1 second. [Description("Disables a player if this number of liquid sets is exceeded within 1 second.")] public int TileLiquidThreshold = 15; - /// ProjectileThreshold - Disables a player if this number of projectiles is created within 1 second. + /// Disable a player if this number of projectiles is created within 1 second. [Description("Disable a player if this number of projectiles is created within 1 second.")] public int ProjectileThreshold = 50; - /// HealOtherThreshold - Disables a player if this number of HealOtherPlayer packets is sent within 1 second. + /// Disables a player if this number of HealOtherPlayer packets is sent within 1 second. [Description("Disables a player if this number of HealOtherPlayer packets is sent within 1 second.")] public int HealOtherThreshold = 50; - /// ProjIgnoreShrapnel - Whether or not to ignore shrapnel from crystal bullets for the projectile threshold count. - [Description("Ignore shrapnel from crystal bullets for projectile threshold.")] + /// Whether or not to ignore shrapnel from crystal bullets for the projectile threshold count. + [Description("Whether or not to ignore shrapnel from crystal bullets for the projectile threshold count.")] public bool ProjIgnoreShrapnel = true; - /// RequireLogin - Requires all players to register or login before being allowed to play. - [Description("Requires all players to register or login before being allowed to play.")] + /// Require all players to register or login before being allowed to play. + [Description("Require all players to register or login before being allowed to play.")] public bool RequireLogin; - /// DisableInvisPvP - Whether or not to turn a player invisible if using invisibility potions during PvP. - [Description("Disables invisibility potions from being used in PvP (Note, can be used in the client, but the effect isn't sent to the rest of the server).")] + /// Disables the effect of invisibility potions while PvP is enabled by turning the player visible to the other clients. + [Description("Disables the effect of invisibility potions while PvP is enabled by turning the player visible to the other clients.")] public bool DisableInvisPvP; - /// MaxRangeForDisabled - The maximum distance, in tiles, that disabled players can move from. - [Description("The maximum distance players disabled for various reasons can move from.")] + /// The maximum distance, in tiles, that disabled players can move from. + [Description("The maximum distance, in tiles, that disabled players can move from.")] public int MaxRangeForDisabled = 10; - /// ServerPassword - The server password required to join the server. - [Description("Server password required to join the server.")] + /// The server password required to join the server. + [Description("The server password required to join the server.")] public string ServerPassword = ""; - /// RegionProtectChests - Whether or not region protection should apply to chests. - [Description("Protect chests with region and build permissions.")] + /// Whether or not region protection should apply to chests. + [Description("Whether or not region protection should apply to chests.")] public bool RegionProtectChests; - /// RegionProtectGemLocks - Whether or not region protection should apply to gem locks. - [Description("Protect gem locks with region and build permissions.")] + /// Whether or not region protection should apply to gem locks. + [Description("Whether or not region protection should apply to gem locks.")] public bool RegionProtectGemLocks = true; - /// DisableLoginBeforeJoin - This will prevent users from being able to login before connecting. - [Description("Disable users from being able to login with account password when joining.")] + /// Prevents users from being able to login before they finish connecting. + [Description("Prevents users from being able to login before they finish connecting.")] public bool DisableLoginBeforeJoin; - /// DisableUUIDLogin - This will disable automatic login through a saved client UUID. - [Description("Disable users from being able to login with their client UUID.")] + /// Prevents users from being able to login with their client UUID. + [Description("Prevents users from being able to login with their client UUID.")] public bool DisableUUIDLogin; - /// KickEmptyUUID - Kick clients that don't send a UUID to the server. - [Description("Kick clients that don't send a UUID to the server.")] + /// Kick clients that don't send their UUID to the server. + [Description("Kick clients that don't send their UUID to the server.")] public bool KickEmptyUUID; - /// AllowRegisterAnyUsername - Allows users to register a username that doesn't necessarily match their character name. - [Description("Allows users to register any username with /register.")] + /// Allows users to register a username that doesn't necessarily match their character name. + [Description("Allows users to register a username that doesn't necessarily match their character name.")] public bool AllowRegisterAnyUsername; - /// AllowLoginAnyUsername - Allows users to login to any account even if the username doesn't match their character name. - [Description("Allows users to login with any username with /login.")] + /// Allows users to login to any account even if the username doesn't match their character name. + [Description("Allows users to login to any account even if the username doesn't match their character name.")] public bool AllowLoginAnyUsername = true; /// The maximum damage a player/NPC can inflict. - [Description("The maximum damage a player/npc can inflict.")] + [Description("The maximum damage a player/NPC can inflict.")] public int MaxDamage = 1175; /// The maximum damage a projectile can inflict. [Description("The maximum damage a projectile can inflict.")] public int MaxProjDamage = 1175; - /// KickOnDamageThresholdBroken - Whether or not to kick users when they surpass the MaxDamage threshold. - [Description("Kicks a user if set to true, if they inflict more damage then the max damage.")] + /// Whether or not to kick users when they surpass the MaxDamage threshold. + [Description("Whether or not to kick users when they surpass the MaxDamage threshold.")] public bool KickOnDamageThresholdBroken = false; - /// IgnoreProjUpdate - Ignores checking to see if player 'can' update a projectile. - [Description("Ignores checking to see if player 'can' update a projectile.")] + /// Ignores checks to see if a player 'can' update a projectile. + [Description("Ignores checks to see if a player 'can' update a projectile.")] public bool IgnoreProjUpdate = false; - /// IgnoreProjKill - Ignores checking to see if player 'can' kill a projectile. - [Description("Ignores checking to see if player 'can' kill a projectile.")] + /// Ignores checks to see if a player 'can' kill a projectile. + [Description("Ignores checks to see if a player 'can' kill a projectile.")] public bool IgnoreProjKill = false; - /// AlllowIce - Allows ice placement even where a user cannot usually build. - [Description("Allow ice placement even when user does not have canbuild.")] + /// Allows ice placement even where a user cannot usually build. + [Description("Allows ice placement even where a user cannot usually build.")] public bool AllowIce = false; - /// AllowCrimsonCreep - Enables or disables crimson to spread when a world is in hardmode. - [Description("Allows crimson to spread when a world is hardmode.")] + /// Allows the crimson to spread when a world is in hardmode. + [Description("Allows the crimson to spread when a world is in hardmode.")] public bool AllowCrimsonCreep = true; - /// AllowCorruptionCreep - Enables or disables corruption to spread when a world is in hardmode. - [Description("Allows corruption to spread when a world is hardmode.")] + /// Allows the corruption to spread when a world is in hardmode. + [Description("Allows the corruption to spread when a world is in hardmode.")] public bool AllowCorruptionCreep = true; - /// AllowHallowCreep - Enables or disables hallow to spread when a world is in hardmode. - [Description("Allows hallow to spread when a world is hardmode.")] + /// Allows the hallow to spread when a world is in hardmode. + [Description("Allows the hallow to spread when a world is in hardmode.")] public bool AllowHallowCreep = true; - /// StatueSpawn200 - How many NPCs a statue can spawn within 200 pixels(?) before it stops spawning. - [Description("How many things a statue can spawn within 200 pixels(?) before it stops spawning. Default = 3.")] + /// How many NPCs a statue can spawn within 200 pixels(?) before it stops spawning. + [Description("How many NPCs a statue can spawn within 200 pixels(?) before it stops spawning.\nDefault = 3.")] public int StatueSpawn200 = 3; - /// StatueSpawn600 - How many NPCs a statue can spawn within 600 pixels(?) before it stops spawning. - [Description("How many things a statue can spawn within 600 pixels(?) before it stops spawning. Default = 6.")] + /// How many NPCs a statue can spawn within 600 pixels(?) before it stops spawning. + [Description("How many NPCs a statue can spawn within 600 pixels(?) before it stops spawning.\nDefault = 6.")] public int StatueSpawn600 = 6; - /// StatueSpawnWorld - How many NPCs a statue can spawn before it stops spawning. - [Description("How many things a statue can spawn before it stops spawning. Default = 10.")] + /// How many NPCs a statue can spawn before it stops spawning. + [Description("How many NPCs a statue can spawn before it stops spawning.\nDefault = 10.")] public int StatueSpawnWorld = 10; - /// PreventBannedItemSpawn - Prevents banned items from being spawned with commands. - [Description("Prevent banned items from being /i or /give.")] + /// Prevent banned items from being spawned or given with commands. + [Description("Prevent banned items from being spawned or given with commands.")] public bool PreventBannedItemSpawn = false; - /// PreventDeadModification - Prevent players from interacting with the world if dead. - [Description("Prevent players from interacting with the world if dead.")] + /// Prevent players from interacting with the world while they are dead. + [Description("Prevent players from interacting with the world while they are dead.")] public bool PreventDeadModification = true; - /// EnableChatAboveHeads - Whether or not to display chat messages above players' heads. - [Description("Displays chat messages above players' heads, but will disable chat prefixes to compensate.")] + /// Whether or not to display chat messages above players' heads. + [Description("Whether or not to display chat messages above players' heads.")] public bool EnableChatAboveHeads = false; - /// ForceXmas - Force Christmas-only events to occur all year. - [Description("Force Christmas-only events to occur all year.")] + /// Forces Christmas-only events to occur all year. + [Description("Forces Christmas-only events to occur all year.")] public bool ForceXmas = false; - /// AllowAllowedGroupsToSpawnBannedItems - Allows groups on the banned item allowed list to spawn banned items even if is set to true. - [Description("Allows groups on the banned item allowed list to spawn banned items.")] + /// Allows groups on the banned item allowed list to spawn banned items even if PreventBannedItemSpawn is set to true. + [Description("Allows groups on the banned item allowed list to spawn banned items even if PreventBannedItemSpawn is set to true.")] public bool AllowAllowedGroupsToSpawnBannedItems = false; - /// IgnoreChestStacksOnLoad - Allows stacks in chests to be beyond the stack limit during world load. - [Description("Allows stacks in chests to be beyond the stack limit.")] + /// Allows stacks in chests to go beyond the stack limit during world loading. + [Description("Allows stacks in chests to go beyond the stack limit during world loading.")] public bool IgnoreChestStacksOnLoad = false; - /// LogPath - The path of the directory where logs should be written to. - [Description("The path of the directory where logs should be written into.")] + /// The path to the directory where logs should be written to. + [Description("The path to the directory where logs should be written to.")] public string LogPath = "tshock"; - /// UseSqlLogs - Whether or not to save logs to a SQL database instead of a text file. - [Description("Save logs to an SQL database instead of a text file. Default = false.")] + /// Whether or not to save logs to the SQL database instead of a text file. + [Description("Whether or not to save logs to the SQL database instead of a text file.\nDefault = false.")] public bool UseSqlLogs = false; - /// RevertToTextLogsOnSqlFailures - Number of times the SQL log must fail to insert logs before falling back to the text log. + /// Number of times the SQL log must fail to insert logs before falling back to the text log. [Description("Number of times the SQL log must fail to insert logs before falling back to the text log.")] public int RevertToTextLogsOnSqlFailures = 10; - /// PreventInvalidPlaceStyle - Prevents players from placing tiles with an invalid style. + /// Prevents players from placing tiles with an invalid style. [Description("Prevents players from placing tiles with an invalid style.")] public bool PreventInvalidPlaceStyle = true; - /// BroadCastRGB - The RGB values used for the color of broadcast messages. - [Description("#.#.# = Red/Blue/Green - RGB Colors for broadcasts. Max value: 255.")] + /// The RGB values used for the color of broadcast messages. + [Description("The RGB values used for the color of broadcast messages.\n#.#.# = Red/Blue/Green\nMax value: 255")] public int[] BroadcastRGB = { 127, 255, 212 }; - /// ApplicationRestTokens - A dictionary of REST tokens that external applications may use to make queries to your server. + /// A dictionary of REST tokens that external applications may use to make queries to your server. [Description("A dictionary of REST tokens that external applications may use to make queries to your server.")] public Dictionary ApplicationRestTokens = new Dictionary(); - /// ReservedSlots - The number of reserved slots past your max server slot that can be joined by reserved players. - [Description("The number of reserved slots past your max server slot that can be joined by reserved players.")] + /// The number of reserved slots past your max server slots that can be joined by reserved players. + [Description("The number of reserved slots past your max server slots that can be joined by reserved players.")] public int ReservedSlots = 20; - /// LogRest - Whether or not to log REST API connections. - [Description("Enable/disable the REST API connection log.")] + /// Whether or not to log REST API connections. + [Description("Whether or not to log REST API connections.")] public bool LogRest = false; - /// RespawnSeconds - The number of seconds a player must wait before being respawned. + /// The number of seconds a player must wait before being respawned. [Description("The number of seconds a player must wait before being respawned.")] public int RespawnSeconds = 5; - /// RespawnBossSeconds - "The number of seconds a player must wait before being respawned if there is a boss nearby. + /// The number of seconds a player must wait before being respawned if there is a boss nearby. [Description("The number of seconds a player must wait before being respawned if there is a boss nearby.")] public int RespawnBossSeconds = 10; - /// TilePaintThreshold - Disables a player if this number of tiles is painted within 1 second. + /// Disables a player if this number of tiles is painted within 1 second. [Description("Disables a player if this number of tiles is painted within 1 second.")] public int TilePaintThreshold = 15; - /// ForceHalloween - Forces Halloween-only events to occur all year. - [Description("Forces your world to be in Halloween mode regardless of the data.")] + /// Forces Halloween-only events to occur all year. + [Description("Forces Halloween-only events to occur all year.")] public bool ForceHalloween = false; - /// AllowCutTilesAndBreakables - Allows players to break temporary tiles (grass, pots, etc) even if they cannot typically build in a region. - [Description("Allows anyone to break grass, pots, etc.")] + /// Allows players to break temporary tiles (grass, pots, etc) where they cannot usually build. + [Description("Allows players to break temporary tiles (grass, pots, etc) where they cannot usually build.")] public bool AllowCutTilesAndBreakables = false; - /// CommandSpecifier - Specifies which string starts a command. + /// Specifies which string starts a command. /// Note: Will not function properly if the string length is bigger than 1. - [Description("Specifies which string starts a command.")] + [Description("Specifies which string starts a command.\nNote: Will not function properly if the string length is bigger than 1.")] public string CommandSpecifier = "/"; - /// CommandSilentSpecifier - Specifies which string starts a command silently. + /// Specifies which string starts a command silently. /// Note: Will not function properly if the string length is bigger than 1. - [Description("Specifies which string starts a command silently.")] + [Description("Specifies which string starts a command silently.\nNote: Will not function properly if the string length is bigger than 1.")] public string CommandSilentSpecifier = "."; - /// KickOnHardcoreDeath - Whether or not to kick a hardcore player on death. - [Description("Kicks a hardcore player on death.")] + /// Whether or not to kick hardcore players on death. + [Description("Whether or not to kick hardcore players on death.")] public bool KickOnHardcoreDeath; - /// BanOnHardcoreDeath - Whether or not to ban a hardcore player on death. - [Description("Bans a hardcore player on death.")] + /// Whether or not to ban hardcore players on death. + [Description("Whether or not to ban hardcore players on death.")] public bool BanOnHardcoreDeath; - /// HardcoreBanReason - The reason given when banning a hardcore player on death. - [Description("Bans a hardcore player on death.")] + /// The reason given when banning hardcore players on death. + [Description("The reason given when banning hardcore players on death.")] public string HardcoreBanReason = "Death results in a ban"; - /// HardcoreKickReason - The reason given when kicking a hardcore player on death. - [Description("Kicks a hardcore player on death.")] + /// The reason given when kicking hardcore players on death. + [Description("The reason given when kicking hardcore players on death.")] public string HardcoreKickReason = "Death results in a kick"; - /// AnonymousBossInvasions - Whether or not to announce boss spawning or invasion starts. - [Description("Whether bosses or invasions should be anonymously spawned.")] + /// Whether or not to announce boss spawning or invasion starts. + [Description("Whether or not to announce boss spawning or invasion starts.")] public bool AnonymousBossInvasions = true; - /// MaxHP - The maximum allowable HP, before equipment buffs. - [Description("The maximum allowable HP, before equipment buffs.")] + /// The maximum HP a player can have, before equipment buffs. + [Description("The maximum HP a player can have, before equipment buffs.")] public int MaxHP = 500; - /// MaxMP - The maximum allowable MP, before equipment buffs. - [Description("The maximum allowable MP, before equipment buffs.")] + /// The maximum MP a player can have, before equipment buffs. + [Description("The maximum MP a player can have, before equipment buffs.")] public int MaxMP = 200; - /// SaveWorldOnLastPlayerExit - Whether or not to save the world when the last player disconnects. - [Description("Determines if the server should save the world if the last player exits.")] + /// Whether or not to save the world when the last player disconnects. + [Description("Whether or not to save the world when the last player disconnects.")] public bool SaveWorldOnLastPlayerExit = true; - /// BCryptWorkFactor - Determines the BCrypt work factor to use. If increased, all passwords will be upgraded to new work-factor on verify. + /// Determines the BCrypt work factor to use. If increased, all passwords will be upgraded to new work-factor on verify. /// The number of computational rounds is 2^n. Increase with caution. Range: 5-31. [Description("Determines the BCrypt work factor to use. If increased, all passwords will be upgraded to new work-factor on verify. The number of computational rounds is 2^n. Increase with caution. Range: 5-31.")] public int BCryptWorkFactor = 7; - /// MinimumPasswordLength - The minimum password length for new user accounts. - [Description("The minimum password length for new user accounts. Minimum value is 4.")] + /// The minimum password length for new user accounts. Can never be lower than 4. + [Description("The minimum password length for new user accounts. Can never be lower than 4.")] public int MinimumPasswordLength = 4; - /// RESTMaximumRequestsPerInterval - The maximum REST requests in the bucket before denying requests. + /// The maximum REST requests in the bucket before denying requests. Minimum value is 5. [Description("The maximum REST requests in the bucket before denying requests. Minimum value is 5.")] public int RESTMaximumRequestsPerInterval = 5; - /// RESTRequestBucketDecreaseIntervalMinutes - How often in minutes the REST requests bucket is decreased by one. + /// How often in minutes the REST requests bucket is decreased by one. Minimum value is 1 minute. [Description("How often in minutes the REST requests bucket is decreased by one. Minimum value is 1 minute.")] public int RESTRequestBucketDecreaseIntervalMinutes = 1; - /// ShowBackupAutosaveMessages - Whether or not to show backup auto save messages. - [Description("Show backup autosave messages.")] + /// Whether or not to show backup auto save messages. + [Description("Whether or not to show backup auto save messages.")] public bool ShowBackupAutosaveMessages = true; /// @@ -613,4 +613,4 @@ namespace TShockAPI File.WriteAllText("ConfigDescriptions.txt", sb.ToString()); } } -} \ No newline at end of file +} From 944cac1fd30ce1a53ba94146cae9898f0da6e89e Mon Sep 17 00:00:00 2001 From: Rodrigo Rente Date: Sun, 21 Oct 2018 20:12:59 +0100 Subject: [PATCH 14/19] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e97338de..4c548cac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,6 +91,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin * Update tracker now uses TLS (@pandabear41) * When deleting an user account, any player logged in to that account is now logged out properly (@Enerdy) * Add NPCAddBuff data handler and bouncer (@AxeelAnder) +* Improved config file documentation (@Enerdy) ## TShock 4.3.25 * Fixed a critical exploit in the Terraria protocol that could cause massive unpreventable world corruption as well as a number of other problems. Thanks to @bartico6 for reporting. Fixed by the efforts of @QuiCM, @hakusaro, and tips in the right directioon from @bartico6. From 2ea9a0161bc7d812f8dcc2779c1fe8e678ba8019 Mon Sep 17 00:00:00 2001 From: AxeelAnder Date: Tue, 23 Oct 2018 17:14:42 +0800 Subject: [PATCH 15/19] added PlayerZone handler and bouncer, detect lunar towers cheat --- TShockAPI/Bouncer.cs | 35 ++++++++++++++++++ TShockAPI/GetDataHandlers.cs | 70 ++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 7287924f..bb9937a6 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -83,6 +83,7 @@ namespace TShockAPI GetDataHandlers.LiquidSet += OnLiquidSet; GetDataHandlers.ProjectileKill += OnProjectileKill; GetDataHandlers.PlayerUpdate += OnPlayerUpdate; + GetDataHandlers.PlayerZone += OnPlayerZone; GetDataHandlers.KillMe += OnKillMe; GetDataHandlers.NewProjectile += OnNewProjectile; GetDataHandlers.PlaceObject += OnPlaceObject; @@ -1000,6 +1001,40 @@ namespace TShockAPI return; } + /// Handles PlayerZone events for preventing spawning NPC maliciously. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnPlayerZone(object sender, GetDataHandlers.PlayerZoneEventArgs args) + { + bool hasSolarTower = false; + bool hasVortexTower = false; + bool hasNebulaTower = false; + bool hasStardustTower = false; + + // May need to optimize + foreach (var npc in Main.npc) + { + if (npc.netID == NPCID.LunarTowerSolar) + hasSolarTower = true; + else if (npc.netID == NPCID.LunarTowerVortex) + hasVortexTower = true; + else if (npc.netID == NPCID.LunarTowerNebula) + hasNebulaTower = true; + else if (npc.netID == NPCID.LunarTowerStardust) + hasStardustTower = true; + } + + if((args.Zone2[1] && !hasSolarTower) + || (args.Zone2[2] && !hasVortexTower) + || (args.Zone2[3] && !hasNebulaTower) + || (args.Zone2[4] && !hasStardustTower) + ) + { + args.Handled = true; + return; + } + } + /// Bouncer's KillMe hook stops crash exploits from out of bounds values. /// The object that triggered the event. /// The packet arguments that the event has. diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index a7792569..dd18e502 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -550,6 +550,56 @@ namespace TShockAPI return args.Handled; } + /// + /// For use in a PlayerZone event + /// + public class PlayerZoneEventArgs : GetDataHandledEventArgs + { + /// + /// The Terraria playerID of the player + /// + public byte PlayerId { get; set; } + /// + /// 0 = Dungeon, 1 = Corruption,2 =Holy, 3 = Meteor, 4 = Jungle, 5 = Snow, 6 = Crimson, 7 = Water Candle + /// + public BitsByte Zone1 { get; set; } + /// + /// 0 = Peace Candle, 1 = Solar Tower, 2 = Vortex Tower, 3 = Nebula Tower, 4 = Stardust Tower, 5 = Desert, 6 = Glowshroom, 7 = Underground Desert + /// + public BitsByte Zone2 { get; set; } + /// + /// 0 = Overworld, 1 = Dirt Layer, 2 = Rock Layer, 3 = Underworld, 4 = Beach, 5 = Rain, 6 = Sandstorm + /// + public BitsByte Zone3 { get; set; } + /// + /// 0 = Old One's Army + /// + public BitsByte Zone4 { get; set; } + } + /// + /// PlayerZone - When the player sends it's zone/biome information to the server + /// + public static HandlerList PlayerZone = new HandlerList(); + + private static bool OnPlayerZone(TSPlayer player, MemoryStream data, byte plr, BitsByte zone1, BitsByte zone2, BitsByte zone3, BitsByte zone4) + { + if (PlayerZone == null) + return false; + + var args = new PlayerZoneEventArgs + { + Player = player, + Data = data, + PlayerId = plr, + Zone1 = zone1, + Zone2 = zone2, + Zone3 = zone3, + Zone4 = zone4 + }; + PlayerZone.Invoke(null, args); + return args.Handled; + } + /// The event args object for the HealOtherPlayer event public class HealOtherPlayerEventArgs : GetDataHandledEventArgs { @@ -1479,6 +1529,7 @@ namespace TShockAPI { { PacketTypes.PlayerInfo, HandlePlayerInfo }, { PacketTypes.PlayerUpdate, HandlePlayerUpdate }, + { PacketTypes.Zones, HandlePlayerZone }, { PacketTypes.Tile, HandleTile }, { PacketTypes.PlaceObject, HandlePlaceObject }, { PacketTypes.TileSendSquare, HandleSendTileSquare }, @@ -2402,6 +2453,25 @@ namespace TShockAPI return true; } + private static bool HandlePlayerZone(GetDataHandlerArgs args) + { + if (args.Player == null || args.TPlayer == null || args.Data == null) + { + return true; + } + + var plr = args.Data.ReadInt8(); + BitsByte zone1 = args.Data.ReadInt8(); + BitsByte zone2 = args.Data.ReadInt8(); + BitsByte zone3 = args.Data.ReadInt8(); + BitsByte zone4 = args.Data.ReadInt8(); + + if (OnPlayerZone(args.Player, args.Data, plr, zone1, zone2, zone3, zone4)) + return true; + + return false; + } + private static bool HandleProjectileNew(GetDataHandlerArgs args) { short ident = args.Data.ReadInt16(); From 42e413012dbe6819643741499bce37bea795aff1 Mon Sep 17 00:00:00 2001 From: AxeelAnder Date: Wed, 24 Oct 2018 15:56:24 +0800 Subject: [PATCH 16/19] added filter: only check when zones contains towers --- TShockAPI/Bouncer.cs | 53 +++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index bb9937a6..d38a249f 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -1006,32 +1006,35 @@ namespace TShockAPI /// The packet arguments that the event has. internal void OnPlayerZone(object sender, GetDataHandlers.PlayerZoneEventArgs args) { - bool hasSolarTower = false; - bool hasVortexTower = false; - bool hasNebulaTower = false; - bool hasStardustTower = false; - - // May need to optimize - foreach (var npc in Main.npc) + if(args.Zone2[1] || args.Zone2[2] || args.Zone2[3] || args.Zone2[4]) { - if (npc.netID == NPCID.LunarTowerSolar) - hasSolarTower = true; - else if (npc.netID == NPCID.LunarTowerVortex) - hasVortexTower = true; - else if (npc.netID == NPCID.LunarTowerNebula) - hasNebulaTower = true; - else if (npc.netID == NPCID.LunarTowerStardust) - hasStardustTower = true; - } - - if((args.Zone2[1] && !hasSolarTower) - || (args.Zone2[2] && !hasVortexTower) - || (args.Zone2[3] && !hasNebulaTower) - || (args.Zone2[4] && !hasStardustTower) - ) - { - args.Handled = true; - return; + bool hasSolarTower = false; + bool hasVortexTower = false; + bool hasNebulaTower = false; + bool hasStardustTower = false; + + // May need to optimize + foreach (var npc in Main.npc) + { + if (npc.netID == NPCID.LunarTowerSolar) + hasSolarTower = true; + else if (npc.netID == NPCID.LunarTowerVortex) + hasVortexTower = true; + else if (npc.netID == NPCID.LunarTowerNebula) + hasNebulaTower = true; + else if (npc.netID == NPCID.LunarTowerStardust) + hasStardustTower = true; + } + + if ((args.Zone2[1] && !hasSolarTower) + || (args.Zone2[2] && !hasVortexTower) + || (args.Zone2[3] && !hasNebulaTower) + || (args.Zone2[4] && !hasStardustTower) + ) + { + args.Handled = true; + return; + } } } From b2033d9eac9db08a049ba262c1bddbeee2c27cf7 Mon Sep 17 00:00:00 2001 From: AxeelAnder Date: Thu, 25 Oct 2018 22:10:33 +0800 Subject: [PATCH 17/19] update changelog --- CHANGELOG.md | 1 + TShockAPI/Bouncer.cs | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c548cac..4a47f26b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -92,6 +92,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin * When deleting an user account, any player logged in to that account is now logged out properly (@Enerdy) * Add NPCAddBuff data handler and bouncer (@AxeelAnder) * Improved config file documentation (@Enerdy) +* Add PlayerZone data handler and bouncer (@AxeelAnder) ## TShock 4.3.25 * Fixed a critical exploit in the Terraria protocol that could cause massive unpreventable world corruption as well as a number of other problems. Thanks to @bartico6 for reporting. Fixed by the efforts of @QuiCM, @hakusaro, and tips in the right directioon from @bartico6. diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index d38a249f..f31f9dcc 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -1012,8 +1012,7 @@ namespace TShockAPI bool hasVortexTower = false; bool hasNebulaTower = false; bool hasStardustTower = false; - - // May need to optimize + foreach (var npc in Main.npc) { if (npc.netID == NPCID.LunarTowerSolar) From 210c95e5b1238c747dd9b4946727b30493734f4c Mon Sep 17 00:00:00 2001 From: AxeelAnder Date: Fri, 26 Oct 2018 14:06:46 +0800 Subject: [PATCH 18/19] sorted Bouncer and GetDataHandlers, ordered by packet type enum value --- TShockAPI/Bouncer.cs | 2400 ++++++++++----------- TShockAPI/GetDataHandlers.cs | 3806 +++++++++++++++++----------------- 2 files changed, 3072 insertions(+), 3134 deletions(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index f31f9dcc..087e092a 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -36,64 +36,39 @@ namespace TShockAPI /// Bouncer is the TShock anti-hack and anti-cheat system. internal sealed class Bouncer { - static Dictionary NPCAddBuffTimeMax = new Dictionary() - { - { BuffID.Poisoned, 3600 }, - { BuffID.OnFire, 1200 }, - { BuffID.CursedInferno, 420 }, - { BuffID.Frostburn, 900 }, - { BuffID.Ichor, 1200 }, - { BuffID.Venom, 1260 }, - { BuffID.Midas, 120 }, - { BuffID.Wet, 1500 }, - { BuffID.Slimed, 1500 }, - { BuffID.Lovestruck, 1800 }, - { BuffID.Stinky, 1800 }, - { BuffID.SoulDrain, 30 }, - { BuffID.ShadowFlame, 660 }, - { BuffID.DryadsWard, 120 }, - { BuffID.BoneJavelin, 900 }, - { BuffID.StardustMinionBleed, 900 }, - { BuffID.DryadsWardDebuff, 120 }, - { BuffID.Daybreak, 300 }, - { BuffID.BetsysCurse, 600 }, - { BuffID.Oiled, 540 } - }; - - /// Constructor call initializes Bouncer and related functionality. /// A new Bouncer. internal Bouncer() { // Setup hooks - GetDataHandlers.GetSection += OnGetSection; - GetDataHandlers.PlaceItemFrame += OnPlaceItemFrame; - GetDataHandlers.GemLockToggle += OnGemLockToggle; - GetDataHandlers.PlaceTileEntity += OnPlaceTileEntity; - GetDataHandlers.PlayerAnimation += OnPlayerAnimation; - GetDataHandlers.NPCStrike += OnNPCStrike; + GetDataHandlers.PlayerUpdate += OnPlayerUpdate; + GetDataHandlers.TileEdit += OnTileEdit; + GetDataHandlers.SendTileSquare += OnSendTileSquare; GetDataHandlers.ItemDrop += OnItemDrop; - GetDataHandlers.NPCAddBuff += OnNPCAddBuff; - GetDataHandlers.PlayerBuff += OnPlayerBuff; + GetDataHandlers.NewProjectile += OnNewProjectile; + GetDataHandlers.NPCStrike += OnNPCStrike; + GetDataHandlers.ProjectileKill += OnProjectileKill; GetDataHandlers.ChestItemChange += OnChestItemChange; - GetDataHandlers.NPCHome += OnUpdateNPCHome; GetDataHandlers.ChestOpen += OnChestOpen; GetDataHandlers.PlaceChest += OnPlaceChest; - GetDataHandlers.LiquidSet += OnLiquidSet; - GetDataHandlers.ProjectileKill += OnProjectileKill; - GetDataHandlers.PlayerUpdate += OnPlayerUpdate; GetDataHandlers.PlayerZone += OnPlayerZone; - GetDataHandlers.KillMe += OnKillMe; - GetDataHandlers.NewProjectile += OnNewProjectile; - GetDataHandlers.PlaceObject += OnPlaceObject; - GetDataHandlers.SendTileSquare += OnSendTileSquare; + GetDataHandlers.PlayerAnimation += OnPlayerAnimation; + GetDataHandlers.LiquidSet += OnLiquidSet; + GetDataHandlers.PlayerBuff += OnPlayerBuff; + GetDataHandlers.NPCAddBuff += OnNPCAddBuff; + GetDataHandlers.NPCHome += OnUpdateNPCHome; GetDataHandlers.HealOtherPlayer += OnHealOtherPlayer; - GetDataHandlers.TileEdit += OnTileEdit; - GetDataHandlers.MassWireOperation += OnMassWireOperation; + GetDataHandlers.PlaceObject += OnPlaceObject; + GetDataHandlers.PlaceTileEntity += OnPlaceTileEntity; + GetDataHandlers.PlaceItemFrame += OnPlaceItemFrame; GetDataHandlers.PortalTeleport += OnPlayerPortalTeleport; + GetDataHandlers.GemLockToggle += OnGemLockToggle; + GetDataHandlers.MassWireOperation += OnMassWireOperation; + GetDataHandlers.PlayerDamage += OnPlayerDamage; + GetDataHandlers.KillMe += OnKillMe; } - + internal void OnGetSection(object sender, GetDataHandlers.GetSectionEventArgs args) { if (args.Player.RequestedSection) @@ -116,804 +91,6 @@ namespace TShockAPI } } - /// Fired when an item frame is placed for anti-cheat detection. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnPlaceItemFrame(object sender, GetDataHandlers.PlaceItemFrameEventArgs args) - { - if (args.Player.IsBeingDisabled()) - { - NetMessage.SendData((int)PacketTypes.UpdateTileEntity, -1, -1, NetworkText.Empty, args.ItemFrame.ID, 0, 1); - args.Handled = true; - return; - } - - if (!args.Player.HasBuildPermission(args.X, args.Y)) - { - NetMessage.SendData((int)PacketTypes.UpdateTileEntity, -1, -1, NetworkText.Empty, args.ItemFrame.ID, 0, 1); - args.Handled = true; - return; - } - - if (!args.Player.IsInRange(args.X, args.Y)) - { - NetMessage.SendData((int)PacketTypes.UpdateTileEntity, -1, -1, NetworkText.Empty, args.ItemFrame.ID, 0, 1); - args.Handled = true; - return; - } - } - - /// Handles the anti-cheat components of gem lock toggles. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnGemLockToggle(object sender, GetDataHandlers.GemLockToggleEventArgs args) - { - if (args.X < 0 || args.Y < 0 || args.X >= Main.maxTilesX || args.Y >= Main.maxTilesY) - { - args.Handled = true; - return; - } - - if (!TShock.Utils.TilePlacementValid(args.X, args.Y) || (args.Player.Dead && TShock.Config.PreventDeadModification)) - { - args.Handled = true; - return; - } - - if (args.Player.IsBeingDisabled()) - { - args.Handled = true; - return; - } - - if (!args.Player.HasBuildPermission(args.X, args.Y)) - { - args.Handled = true; - return; - } - } - - /// Fired when a PlaceTileEntity occurs for basic anti-cheat on perms and range. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnPlaceTileEntity(object sender, GetDataHandlers.PlaceTileEntityEventArgs args) - { - if (args.Player.IsBeingDisabled()) - { - args.Handled = true; - return; - } - - if (!args.Player.HasBuildPermission(args.X, args.Y)) - { - args.Handled = true; - return; - } - - if (!args.Player.IsInRange(args.X, args.Y)) - { - args.Handled = true; - return; - } - } - - /// Handles validation of of basic anti-cheat on mass wire operations. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnMassWireOperation(object sender, GetDataHandlers.MassWireOperationEventArgs args) - { - short startX = args.StartX; - short startY = args.StartY; - short endX = args.EndX; - short endY = args.EndY; - - List points = Utils.Instance.GetMassWireOperationRange( - new Point(startX, startY), - new Point(endX, endY), - args.Player.TPlayer.direction == 1); - - int x; - int y; - foreach (Point p in points) - { - /* Perform similar checks to TileKill - * The server-side nature of this packet removes the need to use SendTileSquare - * Range checks are currently ignored here as the items that send this seem to have infinite range */ - - x = p.X; - y = p.Y; - - if (!TShock.Utils.TilePlacementValid(x, y) || (args.Player.Dead && TShock.Config.PreventDeadModification)) - { - args.Handled = true; - return; - } - - if (args.Player.IsBeingDisabled()) - { - args.Handled = true; - return; - } - - if (!args.Player.HasBuildPermission(x, y)) - { - args.Handled = true; - return; - } - } - } - - /// Handles basic animation throttling for disabled players. - /// sender - /// args - internal void OnPlayerAnimation(object sender, GetDataHandlers.PlayerAnimationEventArgs args) - { - if (args.Player.IsBeingDisabled()) - { - args.Player.SendData(PacketTypes.PlayerAnimation, "", args.Player.Index); - args.Handled = true; - return; - } - - if (args.Player.IsBouncerThrottled()) - { - args.Player.SendData(PacketTypes.PlayerAnimation, "", args.Player.Index); - args.Handled = true; - return; - } - } - - /// Handles the NPC Strike event for Bouncer. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnNPCStrike(object sender, GetDataHandlers.NPCStrikeEventArgs args) - { - short id = args.ID; - byte direction = args.Direction; - short damage = args.Damage; - float knockback = args.Knockback; - byte crit = args.Critical; - - if (Main.npc[id] == null) - { - args.Handled = true; - return; - } - - if (damage > TShock.Config.MaxDamage && !args.Player.HasPermission(Permissions.ignoredamagecap)) - { - if (TShock.Config.KickOnDamageThresholdBroken) - { - args.Player.Kick(string.Format("NPC damage exceeded {0}.", TShock.Config.MaxDamage)); - args.Handled = true; - return; - } - else - { - args.Player.Disable(String.Format("NPC damage exceeded {0}.", TShock.Config.MaxDamage), DisableFlags.WriteToLogAndConsole); - } - args.Player.SendData(PacketTypes.NpcUpdate, "", id); - args.Handled = true; - return; - } - - if (args.Player.IsBeingDisabled()) - { - args.Player.SendData(PacketTypes.NpcUpdate, "", id); - args.Handled = true; - return; - } - - if (TShock.Config.RangeChecks && - !args.Player.IsInRange((int)(Main.npc[id].position.X / 16f), (int)(Main.npc[id].position.Y / 16f), 128)) - { - args.Player.SendData(PacketTypes.NpcUpdate, "", id); - args.Handled = true; - return; - } - - if (args.Player.IsBouncerThrottled()) - { - args.Player.SendData(PacketTypes.NpcUpdate, "", id); - args.Handled = true; - return; - } - } - - /// Called when a player is damaged. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnPlayerDamage(object sender, GetDataHandlers.PlayerDamageEventArgs args) - { - byte id = args.ID; - short damage = args.Damage; - bool pvp = args.PVP; - bool crit = args.Critical; - byte direction = args.Direction; - - if (id >= Main.maxPlayers || TShock.Players[id] == null) - { - args.Handled = true; - return; - } - - if (damage > TShock.Config.MaxDamage && !args.Player.HasPermission(Permissions.ignoredamagecap) && id != args.Player.Index) - { - if (TShock.Config.KickOnDamageThresholdBroken) - { - args.Player.Kick(string.Format("Player damage exceeded {0}.", TShock.Config.MaxDamage)); - args.Handled = true; - return; - } - else - { - args.Player.Disable(String.Format("Player damage exceeded {0}.", TShock.Config.MaxDamage), DisableFlags.WriteToLogAndConsole); - } - args.Player.SendData(PacketTypes.PlayerHp, "", id); - args.Player.SendData(PacketTypes.PlayerUpdate, "", id); - args.Handled = true; - return; - } - - if (!TShock.Players[id].TPlayer.hostile && pvp && id != args.Player.Index) - { - args.Player.SendData(PacketTypes.PlayerHp, "", id); - args.Player.SendData(PacketTypes.PlayerUpdate, "", id); - args.Handled = true; - return; - } - - if (args.Player.IsBeingDisabled()) - { - args.Player.SendData(PacketTypes.PlayerHp, "", id); - args.Player.SendData(PacketTypes.PlayerUpdate, "", id); - args.Handled = true; - return; - } - - if (!args.Player.IsInRange(TShock.Players[id].TileX, TShock.Players[id].TileY, 100)) - { - args.Player.SendData(PacketTypes.PlayerHp, "", id); - args.Player.SendData(PacketTypes.PlayerUpdate, "", id); - args.Handled = true; - return; - } - - if (args.Player.IsBouncerThrottled()) - { - args.Player.SendData(PacketTypes.PlayerHp, "", id); - args.Player.SendData(PacketTypes.PlayerUpdate, "", id); - args.Handled = true; - return; - } - - } - - /// Registered when items fall to the ground to prevent cheating. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnItemDrop(object sender, GetDataHandlers.ItemDropEventArgs args) - { - short id = args.ID; - Vector2 pos = args.Position; - Vector2 vel = args.Velocity; - short stacks = args.Stacks; - short prefix = args.Prefix; - bool noDelay = args.NoDelay; - short type = args.Type; - - // player is attempting to crash clients - if (type < -48 || type >= Main.maxItemTypes) - { - // Causes item duplications. Will be re added later if necessary - //args.Player.SendData(PacketTypes.ItemDrop, "", id); - args.Handled = true; - return; - } - - // make sure the prefix is a legit value - // Note: Not checking if prefix is less than 1 because if it is, this check - // will break item pickups on the client. - if (prefix > PrefixID.Count) - { - args.Player.SendData(PacketTypes.ItemDrop, "", id); - args.Handled = true; - return; - } - - //Item removed, let client do this to prevent item duplication - // client side (but only if it passed the range check) (i.e., return false) - if (type == 0) - { - if (!args.Player.IsInRange((int)(Main.item[id].position.X / 16f), (int)(Main.item[id].position.Y / 16f))) - { - // Causes item duplications. Will be re added if necessary - //args.Player.SendData(PacketTypes.ItemDrop, "", id); - args.Handled = true; - return; - } - - args.Handled = false; - return; - } - - if (!args.Player.IsInRange((int)(pos.X / 16f), (int)(pos.Y / 16f))) - { - args.Player.SendData(PacketTypes.ItemDrop, "", id); - args.Handled = true; - return; - } - - // stop the client from changing the item type of a drop but - // only if the client isn't picking up the item - if (Main.item[id].active && Main.item[id].netID != type) - { - args.Player.SendData(PacketTypes.ItemDrop, "", id); - args.Handled = true; - return; - } - - Item item = new Item(); - item.netDefaults(type); - if ((stacks > item.maxStack || stacks <= 0) || (TShock.Itembans.ItemIsBanned(EnglishLanguage.GetItemNameById(item.type), args.Player) && !args.Player.HasPermission(Permissions.allowdroppingbanneditems))) - { - args.Player.SendData(PacketTypes.ItemDrop, "", id); - args.Handled = true; - return; - } - - // TODO: Remove item ban part of this check - if ((Main.ServerSideCharacter) && (DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond - args.Player.LoginMS < TShock.ServerSideCharacterConfig.LogonDiscardThreshold)) - { - //Player is probably trying to sneak items onto the server in their hands!!! - TShock.Log.ConsoleInfo("Player {0} tried to sneak {1} onto the server!", args.Player.Name, item.Name); - args.Player.SendData(PacketTypes.ItemDrop, "", id); - args.Handled = true; - return; - - } - - if (args.Player.IsBeingDisabled()) - { - args.Player.SendData(PacketTypes.ItemDrop, "", id); - args.Handled = true; - return; - } - } - - /// Handles NPCAddBuff events. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnNPCAddBuff(object sender, GetDataHandlers.NPCAddBuffEventArgs args) - { - short id = args.ID; - byte type = args.Type; - short time = args.Time; - - if (id >= Main.npc.Length) - { - args.Handled = true; - return; - } - - NPC npc = Main.npc[id]; - - if (npc == null) - { - args.Handled = true; - return; - } - - if (args.Player.IsBeingDisabled()) - { - args.Handled = true; - return; - } - - bool detectedNPCBuffTimeCheat = false; - - if (NPCAddBuffTimeMax.ContainsKey(type)) - { - if (time > NPCAddBuffTimeMax[type]) - { - detectedNPCBuffTimeCheat = true; - } - - if (npc.townNPC && npc.netID != NPCID.Guide && npc.netID != NPCID.Clothier) - { - if (type != BuffID.Lovestruck && type != BuffID.Stinky && type != BuffID.DryadsWard && - type != BuffID.Wet && type != BuffID.Slimed) - { - detectedNPCBuffTimeCheat = true; - } - } - } - else - { - detectedNPCBuffTimeCheat = true; - } - - if (detectedNPCBuffTimeCheat) - { - args.Player.Kick("Added buff to NPC abnormally.", true); - args.Handled = true; - } - } - - /// Handles Buff events. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnPlayerBuff(object sender, GetDataHandlers.PlayerBuffEventArgs args) - { - byte id = args.ID; - byte type = args.Type; - int time = args.Time; - - if (TShock.Players[id] == null) - { - args.Handled = true; - return; - } - - if (args.Player.IsBeingDisabled()) - { - args.Player.SendData(PacketTypes.PlayerAddBuff, "", id); - args.Handled = true; - return; - } - - if (id >= Main.maxPlayers) - { - args.Player.SendData(PacketTypes.PlayerAddBuff, "", id); - args.Handled = true; - return; - } - - if (!TShock.Players[id].TPlayer.hostile || !Main.pvpBuff[type]) - { - args.Player.SendData(PacketTypes.PlayerAddBuff, "", id); - args.Handled = true; - return; - } - - if (!args.Player.IsInRange(TShock.Players[id].TileX, TShock.Players[id].TileY, 50)) - { - args.Player.SendData(PacketTypes.PlayerAddBuff, "", id); - args.Handled = true; - return; - } - - if (args.Player.IsBouncerThrottled()) - { - args.Player.SendData(PacketTypes.PlayerAddBuff, "", id); - args.Handled = true; - return; - } - - if (WhitelistBuffMaxTime[type] > 0 && time <= WhitelistBuffMaxTime[type]) - { - args.Handled = false; - return; - } - } - - /// Handles when a chest item is changed. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnChestItemChange(object sender, GetDataHandlers.ChestItemEventArgs args) - { - short id = args.ID; - byte slot = args.Slot; - short stacks = args.Stacks; - byte prefix = args.Prefix; - short type = args.Type; - - if (args.Player.TPlayer.chest != id) - { - args.Handled = true; - return; - } - - if (args.Player.IsBeingDisabled()) - { - args.Player.SendData(PacketTypes.ChestItem, "", id, slot); - args.Handled = true; - return; - } - - if (!args.Player.HasBuildPermission(Main.chest[id].x, Main.chest[id].y) && TShock.Config.RegionProtectChests) - { - args.Handled = true; - return; - } - - if (!args.Player.IsInRange(Main.chest[id].x, Main.chest[id].y)) - { - args.Handled = true; - return; - } - } - - /// The Bouncer handler for when an NPC is rehomed. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnUpdateNPCHome(object sender, GetDataHandlers.NPCHomeChangeEventArgs args) - { - int id = args.ID; - short x = args.X; - short y = args.Y; - byte homeless = args.Homeless; - - if (!args.Player.HasBuildPermission(x, y)) - { - args.Player.SendData(PacketTypes.UpdateNPCHome, "", id, Main.npc[id].homeTileX, Main.npc[id].homeTileY, - Convert.ToByte(Main.npc[id].homeless)); - args.Handled = true; - return; - } - - if (!args.Player.IsInRange(x, y)) - { - args.Player.SendData(PacketTypes.UpdateNPCHome, "", id, Main.npc[id].homeTileX, Main.npc[id].homeTileY, - Convert.ToByte(Main.npc[id].homeless)); - args.Handled = true; - return; - } - } - - /// The Bouncer handler for when chests are opened. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnChestOpen(object sender, GetDataHandlers.ChestOpenEventArgs args) - { - if (args.Player.IsBeingDisabled()) - { - args.Handled = true; - return; - } - - if (!args.Player.IsInRange(args.X, args.Y)) - { - args.Handled = true; - return; - } - - if (!args.Player.HasBuildPermission(args.X, args.Y) && TShock.Config.RegionProtectChests) - { - args.Handled = true; - return; - } - - int id = Chest.FindChest(args.X, args.Y); - args.Player.ActiveChest = id; - } - - /// The place chest event that Bouncer hooks to prevent accidental damage. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnPlaceChest(object sender, GetDataHandlers.PlaceChestEventArgs args) - { - int tileX = args.TileX; - int tileY = args.TileY; - int flag = args.Flag; - - if (!TShock.Utils.TilePlacementValid(tileX, tileY) || (args.Player.Dead && TShock.Config.PreventDeadModification)) - { - args.Handled = true; - return; - } - - if (args.Player.IsBeingDisabled()) - { - args.Player.SendTileSquare(tileX, tileY, 3); - args.Handled = true; - return; - } - - if (flag != 0 && flag != 4 // if no container or container2 placement - && Main.tile[tileX, tileY].type != TileID.Containers - && Main.tile[tileX, tileY].type != TileID.Dressers - && Main.tile[tileX, tileY].type != TileID.Containers2 - && (!TShock.Utils.HasWorldReachedMaxChests() && Main.tile[tileX, tileY].type != TileID.Dirt)) //Chest - { - args.Player.SendTileSquare(tileX, tileY, 3); - args.Handled = true; - return; - } - - if (flag == 2) //place dresser - { - if ((TShock.Utils.TilePlacementValid(tileX, tileY + 1) && Main.tile[tileX, tileY + 1].type == TileID.Teleporter) || - (TShock.Utils.TilePlacementValid(tileX + 1, tileY + 1) && Main.tile[tileX + 1, tileY + 1].type == TileID.Teleporter)) - { - //Prevent a dresser from being placed on a teleporter, as this can cause client and server crashes. - args.Player.SendTileSquare(tileX, tileY, 3); - args.Handled = true; - return; - } - } - - if (!args.Player.HasBuildPermission(tileX, tileY)) - { - args.Player.SendTileSquare(tileX, tileY, 3); - args.Handled = true; - return; - } - - if (!args.Player.IsInRange(tileX, tileY)) - { - args.Player.SendTileSquare(tileX, tileY, 3); - args.Handled = true; - return; - } - } - - /// Handles Bouncer's liquid set anti-cheat. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnLiquidSet(object sender, GetDataHandlers.LiquidSetEventArgs args) - { - int tileX = args.TileX; - int tileY = args.TileY; - byte amount = args.Amount; - byte type = args.Type; - - if (!TShock.Utils.TilePlacementValid(tileX, tileY) || (args.Player.Dead && TShock.Config.PreventDeadModification)) - { - args.Handled = true; - return; - } - - if (args.Player.IsBeingDisabled()) - { - args.Player.SendTileSquare(tileX, tileY, 1); - args.Handled = true; - return; - } - - if (args.Player.TileLiquidThreshold >= TShock.Config.TileLiquidThreshold) - { - args.Player.Disable("Reached TileLiquid threshold.", DisableFlags.WriteToLogAndConsole); - args.Player.SendTileSquare(tileX, tileY, 1); - args.Handled = true; - return; - } - - if (!args.Player.HasPermission(Permissions.ignoreliquidsetdetection)) - { - args.Player.TileLiquidThreshold++; - } - - // Liquid anti-cheat - // Arguably the banned buckets bit should be in the item bans system - if (amount != 0) - { - int bucket = -1; - if (args.Player.TPlayer.inventory[args.Player.TPlayer.selectedItem].type == ItemID.EmptyBucket) - { - bucket = 0; - } - else if (args.Player.TPlayer.inventory[args.Player.TPlayer.selectedItem].type == ItemID.WaterBucket) - { - bucket = 1; - } - else if (args.Player.TPlayer.inventory[args.Player.TPlayer.selectedItem].type == ItemID.LavaBucket) - { - bucket = 2; - } - else if (args.Player.TPlayer.inventory[args.Player.TPlayer.selectedItem].type == ItemID.HoneyBucket) - { - bucket = 3; - } - else if (args.Player.TPlayer.inventory[args.Player.TPlayer.selectedItem].type == ItemID.BottomlessBucket || - args.Player.TPlayer.inventory[args.Player.TPlayer.selectedItem].type == ItemID.SuperAbsorbantSponge) - { - bucket = 4; - } - - if (type == 1 && !(bucket == 2 || bucket == 0)) - { - args.Player.SendErrorMessage("You do not have permission to perform this action."); - args.Player.Disable("Spreading lava without holding a lava bucket", DisableFlags.WriteToLogAndConsole); - args.Player.SendTileSquare(tileX, tileY, 1); - args.Handled = true; - return; - } - - if (type == 1 && TShock.Itembans.ItemIsBanned("Lava Bucket", args.Player)) - { - args.Player.SendErrorMessage("You do not have permission to perform this action."); - args.Player.Disable("Using banned lava bucket without permissions", DisableFlags.WriteToLogAndConsole); - args.Player.SendTileSquare(tileX, tileY, 1); - args.Handled = true; - return; - } - - if (type == 0 && !(bucket == 1 || bucket == 0 || bucket == 4)) - { - args.Player.SendErrorMessage("You do not have permission to perform this action."); - args.Player.Disable("Spreading water without holding a water bucket", DisableFlags.WriteToLogAndConsole); - args.Player.SendTileSquare(tileX, tileY, 1); - args.Handled = true; - return; - } - - if (type == 0 && TShock.Itembans.ItemIsBanned("Water Bucket", args.Player)) - { - args.Player.SendErrorMessage("You do not have permission to perform this action."); - args.Player.Disable("Using banned water bucket without permissions", DisableFlags.WriteToLogAndConsole); - args.Player.SendTileSquare(tileX, tileY, 1); - args.Handled = true; - return; - } - - if (type == 2 && !(bucket == 3 || bucket == 0)) - { - args.Player.SendErrorMessage("You do not have permission to perform this action."); - args.Player.Disable("Spreading honey without holding a honey bucket", DisableFlags.WriteToLogAndConsole); - args.Player.SendTileSquare(tileX, tileY, 1); - args.Handled = true; - return; - } - - if (type == 2 && TShock.Itembans.ItemIsBanned("Honey Bucket", args.Player)) - { - args.Player.SendErrorMessage("You do not have permission to perform this action."); - args.Player.Disable("Using banned honey bucket without permissions", DisableFlags.WriteToLogAndConsole); - args.Player.SendTileSquare(tileX, tileY, 1); - args.Handled = true; - return; - } - } - - if (!args.Player.HasBuildPermission(tileX, tileY)) - { - args.Player.SendTileSquare(tileX, tileY, 1); - args.Handled = true; - return; - } - - if (!args.Player.IsInRange(tileX, tileY, 16)) - { - args.Player.SendTileSquare(tileX, tileY, 1); - args.Handled = true; - return; - } - - if (args.Player.IsBouncerThrottled()) - { - args.Player.SendTileSquare(tileX, tileY, 1); - args.Handled = true; - return; - } - } - - /// Handles ProjectileKill events for throttling and out of bounds projectiles. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnProjectileKill(object sender, GetDataHandlers.ProjectileKillEventArgs args) - { - if (args.ProjectileIndex < 0) - { - args.Handled = true; - return; - } - - if (args.Player.IsBeingDisabled()) - { - args.Player.RemoveProjectile(args.ProjectileIdentity, args.ProjectileOwner); - args.Handled = true; - return; - } - - if (args.Player.IsBouncerThrottled()) - { - args.Player.RemoveProjectile(args.ProjectileIdentity, args.ProjectileOwner); - args.Handled = true; - return; - } - } - /// Handles disabling enforcement and minor anti-exploit stuff /// The object that triggered the event. /// The packet arguments that the event has. @@ -1001,319 +178,6 @@ namespace TShockAPI return; } - /// Handles PlayerZone events for preventing spawning NPC maliciously. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnPlayerZone(object sender, GetDataHandlers.PlayerZoneEventArgs args) - { - if(args.Zone2[1] || args.Zone2[2] || args.Zone2[3] || args.Zone2[4]) - { - bool hasSolarTower = false; - bool hasVortexTower = false; - bool hasNebulaTower = false; - bool hasStardustTower = false; - - foreach (var npc in Main.npc) - { - if (npc.netID == NPCID.LunarTowerSolar) - hasSolarTower = true; - else if (npc.netID == NPCID.LunarTowerVortex) - hasVortexTower = true; - else if (npc.netID == NPCID.LunarTowerNebula) - hasNebulaTower = true; - else if (npc.netID == NPCID.LunarTowerStardust) - hasStardustTower = true; - } - - if ((args.Zone2[1] && !hasSolarTower) - || (args.Zone2[2] && !hasVortexTower) - || (args.Zone2[3] && !hasNebulaTower) - || (args.Zone2[4] && !hasStardustTower) - ) - { - args.Handled = true; - return; - } - } - } - - /// Bouncer's KillMe hook stops crash exploits from out of bounds values. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnKillMe(object sender, GetDataHandlers.KillMeEventArgs args) - { - short damage = args.Damage; - short id = args.PlayerId; - PlayerDeathReason playerDeathReason = args.PlayerDeathReason; - - if (damage > 20000) //Abnormal values have the potential to cause infinite loops in the server. - { - args.Player.Kick("Failed to shade polygon normals.", true, true); - TShock.Log.ConsoleError("Death Exploit Attempt: Damage {0}", damage); - args.Handled = true; - return; - } - - if (id >= Main.maxPlayers) - { - args.Handled = true; - return; - } - - // This was formerly marked as a crash check; does not actually crash on this specific packet. - if (playerDeathReason != null) - { - if (playerDeathReason.GetDeathText(TShock.Players[id].Name).ToString().Length > 500) - { - TShock.Players[id].Kick("Death reason outside of normal bounds.", true); - args.Handled = true; - return; - } - } - } - - /// Bouncer's projectile trigger hook stops world damaging projectiles from destroying the world. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventArgs args) - { - short ident = args.Identity; - Vector2 pos = args.Position; - Vector2 vel = args.Velocity; - float knockback = args.Knockback; - short damage = args.Damage; - byte owner = args.Owner; - short type = args.Type; - int index = args.Index; - - if (index > Main.maxProjectiles) - { - args.Player.RemoveProjectile(ident, owner); - args.Handled = true; - return; - } - - if (TShock.ProjectileBans.ProjectileIsBanned(type, args.Player)) - { - args.Player.Disable("Player does not have permission to create that projectile.", DisableFlags.WriteToLogAndConsole); - args.Player.SendErrorMessage("You do not have permission to create that projectile."); - args.Player.RemoveProjectile(ident, owner); - args.Handled = true; - return; - } - - if (damage > 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); - args.Handled = true; - return; - } - - if (args.Player.IsBeingDisabled()) - { - args.Player.RemoveProjectile(ident, owner); - args.Handled = true; - return; - } - - bool hasPermission = args.Player.HasProjectilePermission(index, type); - if (!TShock.Config.IgnoreProjUpdate && !hasPermission && !args.Player.HasPermission(Permissions.ignoreprojectiledetection)) - { - if (type == ProjectileID.BlowupSmokeMoonlord - || type == ProjectileID.PhantasmalEye - || type == ProjectileID.CultistBossIceMist - || (type >= ProjectileID.MoonlordBullet && type <= ProjectileID.MoonlordTurretLaser) - || type == ProjectileID.DeathLaser || type == ProjectileID.Landmine - || type == ProjectileID.BulletDeadeye || type == ProjectileID.BoulderStaffOfEarth - || (type > ProjectileID.ConfettiMelee && type < ProjectileID.SpiritHeal) - || (type >= ProjectileID.FlamingWood && type <= ProjectileID.GreekFire3) - || (type >= ProjectileID.PineNeedleHostile && type <= ProjectileID.Spike) - || (type >= ProjectileID.MartianTurretBolt && type <= ProjectileID.RayGunnerLaser) - || type == ProjectileID.CultistBossLightningOrb) - { - TShock.Log.Debug("Certain projectiles have been ignored for cheat detection."); - } - else - { - args.Player.Disable(String.Format("Does not have projectile permission to update projectile. ({0})", type), DisableFlags.WriteToLogAndConsole); - args.Player.RemoveProjectile(ident, owner); - } - args.Handled = true; - return; - } - - if (args.Player.ProjectileThreshold >= TShock.Config.ProjectileThreshold) - { - args.Player.Disable("Reached projectile update threshold.", DisableFlags.WriteToLogAndConsole); - args.Player.RemoveProjectile(ident, owner); - args.Handled = true; - return; - } - - if (args.Player.IsBouncerThrottled()) - { - args.Player.RemoveProjectile(ident, owner); - args.Handled = true; - return; - } - - if (!args.Player.HasPermission(Permissions.ignoreprojectiledetection)) - { - if (type == ProjectileID.CrystalShard && TShock.Config.ProjIgnoreShrapnel) // Ignore crystal shards - { - TShock.Log.Debug("Ignoring shrapnel per config.."); - } - else if (!Main.projectile[index].active) - { - args.Player.ProjectileThreshold++; // Creating new projectile - } - } - - if (hasPermission && - (type == ProjectileID.Bomb - || type == ProjectileID.Dynamite - || type == ProjectileID.StickyBomb - || type == ProjectileID.StickyDynamite)) - { - // Denotes that the player has recently set a fuse - used for cheat detection. - args.Player.RecentFuse = 10; - } - } - - /// Bouncer's PlaceObject hook reverts malicious tile placement. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnPlaceObject(object sender, GetDataHandlers.PlaceObjectEventArgs args) - { - short x = args.X; - short y = args.Y; - short type = args.Type; - short style = args.Style; - byte alternate = args.Alternate; - bool direction = args.Direction; - - if (type < 0 || type >= Main.maxTileSets) - { - args.Handled = true; - return; - } - - if (x < 0 || x >= Main.maxTilesX) - { - args.Handled = true; - return; - } - - if (y < 0 || y >= Main.maxTilesY) - { - args.Handled = true; - return; - } - - //style 52 and 53 are used by ItemID.Fake_newchest1 and ItemID.Fake_newchest2 - //These two items cause localised lag and rendering issues - if (type == TileID.FakeContainers && (style == 52 || style == 53)) - { - args.Player.SendTileSquare(x, y, 4); - args.Handled = true; - return; - } - - // TODO: REMOVE. This does NOT look like Bouncer code. - if (TShock.TileBans.TileIsBanned(type, args.Player)) - { - args.Player.SendTileSquare(x, y, 1); - args.Player.SendErrorMessage("You do not have permission to place this tile."); - args.Handled = true; - return; - } - - if (!TShock.Utils.TilePlacementValid(x, y)) - { - args.Player.SendTileSquare(x, y, 1); - args.Handled = true; - return; - } - - if (args.Player.Dead && TShock.Config.PreventDeadModification) - { - args.Player.SendTileSquare(x, y, 4); - args.Handled = true; - return; - } - - if (args.Player.IsBeingDisabled()) - { - args.Player.SendTileSquare(x, y, 4); - args.Handled = true; - return; - } - - // This is neccessary to check in order to prevent special tiles such as - // queen bee larva, paintings etc that use this packet from being placed - // without selecting the right item. - if (type != args.Player.TPlayer.inventory[args.Player.TPlayer.selectedItem].createTile) - { - args.Player.SendTileSquare(x, y, 4); - args.Handled = true; - return; - } - - TileObjectData tileData = TileObjectData.GetTileData(type, style, 0); - if (tileData == null) - { - args.Handled = true; - return; - } - - x -= tileData.Origin.X; - y -= tileData.Origin.Y; - - for (int i = x; i < x + tileData.Width; i++) - { - for (int j = y; j < y + tileData.Height; j++) - { - if (!args.Player.HasModifiedIceSuccessfully(i, j, type, EditAction.PlaceTile) - && !args.Player.HasBuildPermission(i, j)) - { - args.Player.SendTileSquare(i, j, 4); - args.Handled = true; - return; - } - } - } - - // Ignore rope placement range - if ((type != TileID.Rope - || type != TileID.SilkRope - || type != TileID.VineRope - || type != TileID.WebRope) - && !args.Player.IsInRange(x, y)) - { - args.Player.SendTileSquare(x, y, 4); - args.Handled = true; - return; - } - - if (args.Player.TilePlaceThreshold >= TShock.Config.TilePlaceThreshold) - { - args.Player.Disable("Reached TilePlace threshold.", DisableFlags.WriteToLogAndConsole); - args.Player.SendTileSquare(x, y, 4); - args.Handled = true; - return; - } - - if (!args.Player.HasPermission(Permissions.ignoreplacetiledetection)) - { - args.Player.TilePlaceThreshold++; - var coords = new Vector2(x, y); - lock (args.Player.TilesCreated) - if (!args.Player.TilesCreated.ContainsKey(coords)) - args.Player.TilesCreated.Add(coords, Main.tile[x, y]); - } - } - /// Bouncer's TileEdit hook is used to revert malicious tile changes. /// The object that triggered the event. /// The packet arguments that the event has. @@ -1347,7 +211,7 @@ namespace TShockAPI args.Handled = false; return; } - + if (args.Player.Dead && TShock.Config.PreventDeadModification) { args.Player.SendTileSquare(tileX, tileY, 4); @@ -1608,49 +472,7 @@ namespace TShockAPI return; } } - - /// Bouncer's HealOther handler prevents gross misuse of HealOther packets by hackers. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnHealOtherPlayer(object sender, GetDataHandlers.HealOtherPlayerEventArgs args) - { - short amount = args.Amount; - byte plr = args.TargetPlayerIndex; - - if (amount <= 0 || Main.player[plr] == null || !Main.player[plr].active) - { - args.Handled = true; - return; - } - - // Why 0.2? - // @bartico6: Because heal other player only happens when you are using the spectre armor with the hood, - // and the healing you can do with that is 20% of your damage. - if (amount > TShock.Config.MaxDamage * 0.2) - { - args.Player.Disable("HealOtherPlayer cheat attempt!", DisableFlags.WriteToLogAndConsole); - args.Handled = true; - return; - } - - if (args.Player.HealOtherThreshold > TShock.Config.HealOtherThreshold) - { - args.Player.Disable("Reached HealOtherPlayer threshold.", DisableFlags.WriteToLogAndConsole); - args.Handled = true; - return; - } - - if (args.Player.IsBeingDisabled() || args.Player.IsBouncerThrottled()) - { - args.Handled = true; - return; - } - - args.Player.HealOtherThreshold++; - args.Handled = false; - return; - } - + /// Bouncer's SendTileSquare hook halts large scope world destruction. /// The object that triggered the event. /// The packet arguments that the event has. @@ -1815,7 +637,7 @@ namespace TShockAPI if ((tile.type == TileID.TrapdoorClosed && (newtile.Type == TileID.TrapdoorOpen || !newtile.Active)) || (tile.type == TileID.TrapdoorOpen && (newtile.Type == TileID.TrapdoorClosed || !newtile.Active)) || - (!tile.active() && newtile.Active && (newtile.Type == TileID.TrapdoorOpen||newtile.Type == TileID.TrapdoorClosed))) + (!tile.active() && newtile.Active && (newtile.Type == TileID.TrapdoorOpen || newtile.Type == TileID.TrapdoorClosed))) { Main.tile[realx, realy].type = newtile.Type; Main.tile[realx, realy].frameX = newtile.FrameX; @@ -1843,7 +665,980 @@ namespace TShockAPI args.Handled = true; } + + /// Registered when items fall to the ground to prevent cheating. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnItemDrop(object sender, GetDataHandlers.ItemDropEventArgs args) + { + short id = args.ID; + Vector2 pos = args.Position; + Vector2 vel = args.Velocity; + short stacks = args.Stacks; + short prefix = args.Prefix; + bool noDelay = args.NoDelay; + short type = args.Type; + // player is attempting to crash clients + if (type < -48 || type >= Main.maxItemTypes) + { + // Causes item duplications. Will be re added later if necessary + //args.Player.SendData(PacketTypes.ItemDrop, "", id); + args.Handled = true; + return; + } + + // make sure the prefix is a legit value + // Note: Not checking if prefix is less than 1 because if it is, this check + // will break item pickups on the client. + if (prefix > PrefixID.Count) + { + args.Player.SendData(PacketTypes.ItemDrop, "", id); + args.Handled = true; + return; + } + + //Item removed, let client do this to prevent item duplication + // client side (but only if it passed the range check) (i.e., return false) + if (type == 0) + { + if (!args.Player.IsInRange((int)(Main.item[id].position.X / 16f), (int)(Main.item[id].position.Y / 16f))) + { + // Causes item duplications. Will be re added if necessary + //args.Player.SendData(PacketTypes.ItemDrop, "", id); + args.Handled = true; + return; + } + + args.Handled = false; + return; + } + + if (!args.Player.IsInRange((int)(pos.X / 16f), (int)(pos.Y / 16f))) + { + args.Player.SendData(PacketTypes.ItemDrop, "", id); + args.Handled = true; + return; + } + + // stop the client from changing the item type of a drop but + // only if the client isn't picking up the item + if (Main.item[id].active && Main.item[id].netID != type) + { + args.Player.SendData(PacketTypes.ItemDrop, "", id); + args.Handled = true; + return; + } + + Item item = new Item(); + item.netDefaults(type); + if ((stacks > item.maxStack || stacks <= 0) || (TShock.Itembans.ItemIsBanned(EnglishLanguage.GetItemNameById(item.type), args.Player) && !args.Player.HasPermission(Permissions.allowdroppingbanneditems))) + { + args.Player.SendData(PacketTypes.ItemDrop, "", id); + args.Handled = true; + return; + } + + // TODO: Remove item ban part of this check + if ((Main.ServerSideCharacter) && (DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond - args.Player.LoginMS < TShock.ServerSideCharacterConfig.LogonDiscardThreshold)) + { + //Player is probably trying to sneak items onto the server in their hands!!! + TShock.Log.ConsoleInfo("Player {0} tried to sneak {1} onto the server!", args.Player.Name, item.Name); + args.Player.SendData(PacketTypes.ItemDrop, "", id); + args.Handled = true; + return; + + } + + if (args.Player.IsBeingDisabled()) + { + args.Player.SendData(PacketTypes.ItemDrop, "", id); + args.Handled = true; + return; + } + } + + /// Bouncer's projectile trigger hook stops world damaging projectiles from destroying the world. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventArgs args) + { + short ident = args.Identity; + Vector2 pos = args.Position; + Vector2 vel = args.Velocity; + float knockback = args.Knockback; + short damage = args.Damage; + byte owner = args.Owner; + short type = args.Type; + int index = args.Index; + + if (index > Main.maxProjectiles) + { + args.Player.RemoveProjectile(ident, owner); + args.Handled = true; + return; + } + + if (TShock.ProjectileBans.ProjectileIsBanned(type, args.Player)) + { + args.Player.Disable("Player does not have permission to create that projectile.", DisableFlags.WriteToLogAndConsole); + args.Player.SendErrorMessage("You do not have permission to create that projectile."); + args.Player.RemoveProjectile(ident, owner); + args.Handled = true; + return; + } + + if (damage > 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); + args.Handled = true; + return; + } + + if (args.Player.IsBeingDisabled()) + { + args.Player.RemoveProjectile(ident, owner); + args.Handled = true; + return; + } + + bool hasPermission = args.Player.HasProjectilePermission(index, type); + if (!TShock.Config.IgnoreProjUpdate && !hasPermission && !args.Player.HasPermission(Permissions.ignoreprojectiledetection)) + { + if (type == ProjectileID.BlowupSmokeMoonlord + || type == ProjectileID.PhantasmalEye + || type == ProjectileID.CultistBossIceMist + || (type >= ProjectileID.MoonlordBullet && type <= ProjectileID.MoonlordTurretLaser) + || type == ProjectileID.DeathLaser || type == ProjectileID.Landmine + || type == ProjectileID.BulletDeadeye || type == ProjectileID.BoulderStaffOfEarth + || (type > ProjectileID.ConfettiMelee && type < ProjectileID.SpiritHeal) + || (type >= ProjectileID.FlamingWood && type <= ProjectileID.GreekFire3) + || (type >= ProjectileID.PineNeedleHostile && type <= ProjectileID.Spike) + || (type >= ProjectileID.MartianTurretBolt && type <= ProjectileID.RayGunnerLaser) + || type == ProjectileID.CultistBossLightningOrb) + { + TShock.Log.Debug("Certain projectiles have been ignored for cheat detection."); + } + else + { + args.Player.Disable(String.Format("Does not have projectile permission to update projectile. ({0})", type), DisableFlags.WriteToLogAndConsole); + args.Player.RemoveProjectile(ident, owner); + } + args.Handled = true; + return; + } + + if (args.Player.ProjectileThreshold >= TShock.Config.ProjectileThreshold) + { + args.Player.Disable("Reached projectile update threshold.", DisableFlags.WriteToLogAndConsole); + args.Player.RemoveProjectile(ident, owner); + args.Handled = true; + return; + } + + if (args.Player.IsBouncerThrottled()) + { + args.Player.RemoveProjectile(ident, owner); + args.Handled = true; + return; + } + + if (!args.Player.HasPermission(Permissions.ignoreprojectiledetection)) + { + if (type == ProjectileID.CrystalShard && TShock.Config.ProjIgnoreShrapnel) // Ignore crystal shards + { + TShock.Log.Debug("Ignoring shrapnel per config.."); + } + else if (!Main.projectile[index].active) + { + args.Player.ProjectileThreshold++; // Creating new projectile + } + } + + if (hasPermission && + (type == ProjectileID.Bomb + || type == ProjectileID.Dynamite + || type == ProjectileID.StickyBomb + || type == ProjectileID.StickyDynamite)) + { + // Denotes that the player has recently set a fuse - used for cheat detection. + args.Player.RecentFuse = 10; + } + } + + /// Handles the NPC Strike event for Bouncer. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnNPCStrike(object sender, GetDataHandlers.NPCStrikeEventArgs args) + { + short id = args.ID; + byte direction = args.Direction; + short damage = args.Damage; + float knockback = args.Knockback; + byte crit = args.Critical; + + if (Main.npc[id] == null) + { + args.Handled = true; + return; + } + + if (damage > TShock.Config.MaxDamage && !args.Player.HasPermission(Permissions.ignoredamagecap)) + { + if (TShock.Config.KickOnDamageThresholdBroken) + { + args.Player.Kick(string.Format("NPC damage exceeded {0}.", TShock.Config.MaxDamage)); + args.Handled = true; + return; + } + else + { + args.Player.Disable(String.Format("NPC damage exceeded {0}.", TShock.Config.MaxDamage), DisableFlags.WriteToLogAndConsole); + } + args.Player.SendData(PacketTypes.NpcUpdate, "", id); + args.Handled = true; + return; + } + + if (args.Player.IsBeingDisabled()) + { + args.Player.SendData(PacketTypes.NpcUpdate, "", id); + args.Handled = true; + return; + } + + if (TShock.Config.RangeChecks && + !args.Player.IsInRange((int)(Main.npc[id].position.X / 16f), (int)(Main.npc[id].position.Y / 16f), 128)) + { + args.Player.SendData(PacketTypes.NpcUpdate, "", id); + args.Handled = true; + return; + } + + if (args.Player.IsBouncerThrottled()) + { + args.Player.SendData(PacketTypes.NpcUpdate, "", id); + args.Handled = true; + return; + } + } + + /// Handles ProjectileKill events for throttling and out of bounds projectiles. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnProjectileKill(object sender, GetDataHandlers.ProjectileKillEventArgs args) + { + if (args.ProjectileIndex < 0) + { + args.Handled = true; + return; + } + + if (args.Player.IsBeingDisabled()) + { + args.Player.RemoveProjectile(args.ProjectileIdentity, args.ProjectileOwner); + args.Handled = true; + return; + } + + if (args.Player.IsBouncerThrottled()) + { + args.Player.RemoveProjectile(args.ProjectileIdentity, args.ProjectileOwner); + args.Handled = true; + return; + } + } + + /// Handles when a chest item is changed. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnChestItemChange(object sender, GetDataHandlers.ChestItemEventArgs args) + { + short id = args.ID; + byte slot = args.Slot; + short stacks = args.Stacks; + byte prefix = args.Prefix; + short type = args.Type; + + if (args.Player.TPlayer.chest != id) + { + args.Handled = true; + return; + } + + if (args.Player.IsBeingDisabled()) + { + args.Player.SendData(PacketTypes.ChestItem, "", id, slot); + args.Handled = true; + return; + } + + if (!args.Player.HasBuildPermission(Main.chest[id].x, Main.chest[id].y) && TShock.Config.RegionProtectChests) + { + args.Handled = true; + return; + } + + if (!args.Player.IsInRange(Main.chest[id].x, Main.chest[id].y)) + { + args.Handled = true; + return; + } + } + + /// The Bouncer handler for when chests are opened. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnChestOpen(object sender, GetDataHandlers.ChestOpenEventArgs args) + { + if (args.Player.IsBeingDisabled()) + { + args.Handled = true; + return; + } + + if (!args.Player.IsInRange(args.X, args.Y)) + { + args.Handled = true; + return; + } + + if (!args.Player.HasBuildPermission(args.X, args.Y) && TShock.Config.RegionProtectChests) + { + args.Handled = true; + return; + } + + int id = Chest.FindChest(args.X, args.Y); + args.Player.ActiveChest = id; + } + + /// The place chest event that Bouncer hooks to prevent accidental damage. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnPlaceChest(object sender, GetDataHandlers.PlaceChestEventArgs args) + { + int tileX = args.TileX; + int tileY = args.TileY; + int flag = args.Flag; + + if (!TShock.Utils.TilePlacementValid(tileX, tileY) || (args.Player.Dead && TShock.Config.PreventDeadModification)) + { + args.Handled = true; + return; + } + + if (args.Player.IsBeingDisabled()) + { + args.Player.SendTileSquare(tileX, tileY, 3); + args.Handled = true; + return; + } + + if (flag != 0 && flag != 4 // if no container or container2 placement + && Main.tile[tileX, tileY].type != TileID.Containers + && Main.tile[tileX, tileY].type != TileID.Dressers + && Main.tile[tileX, tileY].type != TileID.Containers2 + && (!TShock.Utils.HasWorldReachedMaxChests() && Main.tile[tileX, tileY].type != TileID.Dirt)) //Chest + { + args.Player.SendTileSquare(tileX, tileY, 3); + args.Handled = true; + return; + } + + if (flag == 2) //place dresser + { + if ((TShock.Utils.TilePlacementValid(tileX, tileY + 1) && Main.tile[tileX, tileY + 1].type == TileID.Teleporter) || + (TShock.Utils.TilePlacementValid(tileX + 1, tileY + 1) && Main.tile[tileX + 1, tileY + 1].type == TileID.Teleporter)) + { + //Prevent a dresser from being placed on a teleporter, as this can cause client and server crashes. + args.Player.SendTileSquare(tileX, tileY, 3); + args.Handled = true; + return; + } + } + + if (!args.Player.HasBuildPermission(tileX, tileY)) + { + args.Player.SendTileSquare(tileX, tileY, 3); + args.Handled = true; + return; + } + + if (!args.Player.IsInRange(tileX, tileY)) + { + args.Player.SendTileSquare(tileX, tileY, 3); + args.Handled = true; + return; + } + } + + /// Handles PlayerZone events for preventing spawning NPC maliciously. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnPlayerZone(object sender, GetDataHandlers.PlayerZoneEventArgs args) + { + if (args.Zone2[1] || args.Zone2[2] || args.Zone2[3] || args.Zone2[4]) + { + bool hasSolarTower = false; + bool hasVortexTower = false; + bool hasNebulaTower = false; + bool hasStardustTower = false; + + foreach (var npc in Main.npc) + { + if (npc.netID == NPCID.LunarTowerSolar) + hasSolarTower = true; + else if (npc.netID == NPCID.LunarTowerVortex) + hasVortexTower = true; + else if (npc.netID == NPCID.LunarTowerNebula) + hasNebulaTower = true; + else if (npc.netID == NPCID.LunarTowerStardust) + hasStardustTower = true; + } + + if ((args.Zone2[1] && !hasSolarTower) + || (args.Zone2[2] && !hasVortexTower) + || (args.Zone2[3] && !hasNebulaTower) + || (args.Zone2[4] && !hasStardustTower) + ) + { + args.Handled = true; + return; + } + } + } + + /// Handles basic animation throttling for disabled players. + /// sender + /// args + internal void OnPlayerAnimation(object sender, GetDataHandlers.PlayerAnimationEventArgs args) + { + if (args.Player.IsBeingDisabled()) + { + args.Player.SendData(PacketTypes.PlayerAnimation, "", args.Player.Index); + args.Handled = true; + return; + } + + if (args.Player.IsBouncerThrottled()) + { + args.Player.SendData(PacketTypes.PlayerAnimation, "", args.Player.Index); + args.Handled = true; + return; + } + } + + /// Handles Bouncer's liquid set anti-cheat. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnLiquidSet(object sender, GetDataHandlers.LiquidSetEventArgs args) + { + int tileX = args.TileX; + int tileY = args.TileY; + byte amount = args.Amount; + byte type = args.Type; + + if (!TShock.Utils.TilePlacementValid(tileX, tileY) || (args.Player.Dead && TShock.Config.PreventDeadModification)) + { + args.Handled = true; + return; + } + + if (args.Player.IsBeingDisabled()) + { + args.Player.SendTileSquare(tileX, tileY, 1); + args.Handled = true; + return; + } + + if (args.Player.TileLiquidThreshold >= TShock.Config.TileLiquidThreshold) + { + args.Player.Disable("Reached TileLiquid threshold.", DisableFlags.WriteToLogAndConsole); + args.Player.SendTileSquare(tileX, tileY, 1); + args.Handled = true; + return; + } + + if (!args.Player.HasPermission(Permissions.ignoreliquidsetdetection)) + { + args.Player.TileLiquidThreshold++; + } + + // Liquid anti-cheat + // Arguably the banned buckets bit should be in the item bans system + if (amount != 0) + { + int bucket = -1; + if (args.Player.TPlayer.inventory[args.Player.TPlayer.selectedItem].type == ItemID.EmptyBucket) + { + bucket = 0; + } + else if (args.Player.TPlayer.inventory[args.Player.TPlayer.selectedItem].type == ItemID.WaterBucket) + { + bucket = 1; + } + else if (args.Player.TPlayer.inventory[args.Player.TPlayer.selectedItem].type == ItemID.LavaBucket) + { + bucket = 2; + } + else if (args.Player.TPlayer.inventory[args.Player.TPlayer.selectedItem].type == ItemID.HoneyBucket) + { + bucket = 3; + } + else if (args.Player.TPlayer.inventory[args.Player.TPlayer.selectedItem].type == ItemID.BottomlessBucket || + args.Player.TPlayer.inventory[args.Player.TPlayer.selectedItem].type == ItemID.SuperAbsorbantSponge) + { + bucket = 4; + } + + if (type == 1 && !(bucket == 2 || bucket == 0)) + { + args.Player.SendErrorMessage("You do not have permission to perform this action."); + args.Player.Disable("Spreading lava without holding a lava bucket", DisableFlags.WriteToLogAndConsole); + args.Player.SendTileSquare(tileX, tileY, 1); + args.Handled = true; + return; + } + + if (type == 1 && TShock.Itembans.ItemIsBanned("Lava Bucket", args.Player)) + { + args.Player.SendErrorMessage("You do not have permission to perform this action."); + args.Player.Disable("Using banned lava bucket without permissions", DisableFlags.WriteToLogAndConsole); + args.Player.SendTileSquare(tileX, tileY, 1); + args.Handled = true; + return; + } + + if (type == 0 && !(bucket == 1 || bucket == 0 || bucket == 4)) + { + args.Player.SendErrorMessage("You do not have permission to perform this action."); + args.Player.Disable("Spreading water without holding a water bucket", DisableFlags.WriteToLogAndConsole); + args.Player.SendTileSquare(tileX, tileY, 1); + args.Handled = true; + return; + } + + if (type == 0 && TShock.Itembans.ItemIsBanned("Water Bucket", args.Player)) + { + args.Player.SendErrorMessage("You do not have permission to perform this action."); + args.Player.Disable("Using banned water bucket without permissions", DisableFlags.WriteToLogAndConsole); + args.Player.SendTileSquare(tileX, tileY, 1); + args.Handled = true; + return; + } + + if (type == 2 && !(bucket == 3 || bucket == 0)) + { + args.Player.SendErrorMessage("You do not have permission to perform this action."); + args.Player.Disable("Spreading honey without holding a honey bucket", DisableFlags.WriteToLogAndConsole); + args.Player.SendTileSquare(tileX, tileY, 1); + args.Handled = true; + return; + } + + if (type == 2 && TShock.Itembans.ItemIsBanned("Honey Bucket", args.Player)) + { + args.Player.SendErrorMessage("You do not have permission to perform this action."); + args.Player.Disable("Using banned honey bucket without permissions", DisableFlags.WriteToLogAndConsole); + args.Player.SendTileSquare(tileX, tileY, 1); + args.Handled = true; + return; + } + } + + if (!args.Player.HasBuildPermission(tileX, tileY)) + { + args.Player.SendTileSquare(tileX, tileY, 1); + args.Handled = true; + return; + } + + if (!args.Player.IsInRange(tileX, tileY, 16)) + { + args.Player.SendTileSquare(tileX, tileY, 1); + args.Handled = true; + return; + } + + if (args.Player.IsBouncerThrottled()) + { + args.Player.SendTileSquare(tileX, tileY, 1); + args.Handled = true; + return; + } + } + + /// Handles Buff events. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnPlayerBuff(object sender, GetDataHandlers.PlayerBuffEventArgs args) + { + byte id = args.ID; + byte type = args.Type; + int time = args.Time; + + if (TShock.Players[id] == null) + { + args.Handled = true; + return; + } + + if (args.Player.IsBeingDisabled()) + { + args.Player.SendData(PacketTypes.PlayerAddBuff, "", id); + args.Handled = true; + return; + } + + if (id >= Main.maxPlayers) + { + args.Player.SendData(PacketTypes.PlayerAddBuff, "", id); + args.Handled = true; + return; + } + + if (!TShock.Players[id].TPlayer.hostile || !Main.pvpBuff[type]) + { + args.Player.SendData(PacketTypes.PlayerAddBuff, "", id); + args.Handled = true; + return; + } + + if (!args.Player.IsInRange(TShock.Players[id].TileX, TShock.Players[id].TileY, 50)) + { + args.Player.SendData(PacketTypes.PlayerAddBuff, "", id); + args.Handled = true; + return; + } + + if (args.Player.IsBouncerThrottled()) + { + args.Player.SendData(PacketTypes.PlayerAddBuff, "", id); + args.Handled = true; + return; + } + + if (WhitelistBuffMaxTime[type] > 0 && time <= WhitelistBuffMaxTime[type]) + { + args.Handled = false; + return; + } + } + + /// Handles NPCAddBuff events. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnNPCAddBuff(object sender, GetDataHandlers.NPCAddBuffEventArgs args) + { + short id = args.ID; + byte type = args.Type; + short time = args.Time; + + if (id >= Main.npc.Length) + { + args.Handled = true; + return; + } + + NPC npc = Main.npc[id]; + + if (npc == null) + { + args.Handled = true; + return; + } + + if (args.Player.IsBeingDisabled()) + { + args.Handled = true; + return; + } + + bool detectedNPCBuffTimeCheat = false; + + if (NPCAddBuffTimeMax.ContainsKey(type)) + { + if (time > NPCAddBuffTimeMax[type]) + { + detectedNPCBuffTimeCheat = true; + } + + if (npc.townNPC && npc.netID != NPCID.Guide && npc.netID != NPCID.Clothier) + { + if (type != BuffID.Lovestruck && type != BuffID.Stinky && type != BuffID.DryadsWard && + type != BuffID.Wet && type != BuffID.Slimed) + { + detectedNPCBuffTimeCheat = true; + } + } + } + else + { + detectedNPCBuffTimeCheat = true; + } + + if (detectedNPCBuffTimeCheat) + { + args.Player.Kick("Added buff to NPC abnormally.", true); + args.Handled = true; + } + } + + /// The Bouncer handler for when an NPC is rehomed. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnUpdateNPCHome(object sender, GetDataHandlers.NPCHomeChangeEventArgs args) + { + int id = args.ID; + short x = args.X; + short y = args.Y; + byte homeless = args.Homeless; + + if (!args.Player.HasBuildPermission(x, y)) + { + args.Player.SendData(PacketTypes.UpdateNPCHome, "", id, Main.npc[id].homeTileX, Main.npc[id].homeTileY, + Convert.ToByte(Main.npc[id].homeless)); + args.Handled = true; + return; + } + + if (!args.Player.IsInRange(x, y)) + { + args.Player.SendData(PacketTypes.UpdateNPCHome, "", id, Main.npc[id].homeTileX, Main.npc[id].homeTileY, + Convert.ToByte(Main.npc[id].homeless)); + args.Handled = true; + return; + } + } + + /// Bouncer's HealOther handler prevents gross misuse of HealOther packets by hackers. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnHealOtherPlayer(object sender, GetDataHandlers.HealOtherPlayerEventArgs args) + { + short amount = args.Amount; + byte plr = args.TargetPlayerIndex; + + if (amount <= 0 || Main.player[plr] == null || !Main.player[plr].active) + { + args.Handled = true; + return; + } + + // Why 0.2? + // @bartico6: Because heal other player only happens when you are using the spectre armor with the hood, + // and the healing you can do with that is 20% of your damage. + if (amount > TShock.Config.MaxDamage * 0.2) + { + args.Player.Disable("HealOtherPlayer cheat attempt!", DisableFlags.WriteToLogAndConsole); + args.Handled = true; + return; + } + + if (args.Player.HealOtherThreshold > TShock.Config.HealOtherThreshold) + { + args.Player.Disable("Reached HealOtherPlayer threshold.", DisableFlags.WriteToLogAndConsole); + args.Handled = true; + return; + } + + if (args.Player.IsBeingDisabled() || args.Player.IsBouncerThrottled()) + { + args.Handled = true; + return; + } + + args.Player.HealOtherThreshold++; + args.Handled = false; + return; + } + + /// Bouncer's PlaceObject hook reverts malicious tile placement. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnPlaceObject(object sender, GetDataHandlers.PlaceObjectEventArgs args) + { + short x = args.X; + short y = args.Y; + short type = args.Type; + short style = args.Style; + byte alternate = args.Alternate; + bool direction = args.Direction; + + if (type < 0 || type >= Main.maxTileSets) + { + args.Handled = true; + return; + } + + if (x < 0 || x >= Main.maxTilesX) + { + args.Handled = true; + return; + } + + if (y < 0 || y >= Main.maxTilesY) + { + args.Handled = true; + return; + } + + //style 52 and 53 are used by ItemID.Fake_newchest1 and ItemID.Fake_newchest2 + //These two items cause localised lag and rendering issues + if (type == TileID.FakeContainers && (style == 52 || style == 53)) + { + args.Player.SendTileSquare(x, y, 4); + args.Handled = true; + return; + } + + // TODO: REMOVE. This does NOT look like Bouncer code. + if (TShock.TileBans.TileIsBanned(type, args.Player)) + { + args.Player.SendTileSquare(x, y, 1); + args.Player.SendErrorMessage("You do not have permission to place this tile."); + args.Handled = true; + return; + } + + if (!TShock.Utils.TilePlacementValid(x, y)) + { + args.Player.SendTileSquare(x, y, 1); + args.Handled = true; + return; + } + + if (args.Player.Dead && TShock.Config.PreventDeadModification) + { + args.Player.SendTileSquare(x, y, 4); + args.Handled = true; + return; + } + + if (args.Player.IsBeingDisabled()) + { + args.Player.SendTileSquare(x, y, 4); + args.Handled = true; + return; + } + + // This is neccessary to check in order to prevent special tiles such as + // queen bee larva, paintings etc that use this packet from being placed + // without selecting the right item. + if (type != args.Player.TPlayer.inventory[args.Player.TPlayer.selectedItem].createTile) + { + args.Player.SendTileSquare(x, y, 4); + args.Handled = true; + return; + } + + TileObjectData tileData = TileObjectData.GetTileData(type, style, 0); + if (tileData == null) + { + args.Handled = true; + return; + } + + x -= tileData.Origin.X; + y -= tileData.Origin.Y; + + for (int i = x; i < x + tileData.Width; i++) + { + for (int j = y; j < y + tileData.Height; j++) + { + if (!args.Player.HasModifiedIceSuccessfully(i, j, type, EditAction.PlaceTile) + && !args.Player.HasBuildPermission(i, j)) + { + args.Player.SendTileSquare(i, j, 4); + args.Handled = true; + return; + } + } + } + + // Ignore rope placement range + if ((type != TileID.Rope + || type != TileID.SilkRope + || type != TileID.VineRope + || type != TileID.WebRope) + && !args.Player.IsInRange(x, y)) + { + args.Player.SendTileSquare(x, y, 4); + args.Handled = true; + return; + } + + if (args.Player.TilePlaceThreshold >= TShock.Config.TilePlaceThreshold) + { + args.Player.Disable("Reached TilePlace threshold.", DisableFlags.WriteToLogAndConsole); + args.Player.SendTileSquare(x, y, 4); + args.Handled = true; + return; + } + + if (!args.Player.HasPermission(Permissions.ignoreplacetiledetection)) + { + args.Player.TilePlaceThreshold++; + var coords = new Vector2(x, y); + lock (args.Player.TilesCreated) + if (!args.Player.TilesCreated.ContainsKey(coords)) + args.Player.TilesCreated.Add(coords, Main.tile[x, y]); + } + } + + /// Fired when a PlaceTileEntity occurs for basic anti-cheat on perms and range. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnPlaceTileEntity(object sender, GetDataHandlers.PlaceTileEntityEventArgs args) + { + if (args.Player.IsBeingDisabled()) + { + args.Handled = true; + return; + } + + if (!args.Player.HasBuildPermission(args.X, args.Y)) + { + args.Handled = true; + return; + } + + if (!args.Player.IsInRange(args.X, args.Y)) + { + args.Handled = true; + return; + } + } + + /// Fired when an item frame is placed for anti-cheat detection. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnPlaceItemFrame(object sender, GetDataHandlers.PlaceItemFrameEventArgs args) + { + if (args.Player.IsBeingDisabled()) + { + NetMessage.SendData((int)PacketTypes.UpdateTileEntity, -1, -1, NetworkText.Empty, args.ItemFrame.ID, 0, 1); + args.Handled = true; + return; + } + + if (!args.Player.HasBuildPermission(args.X, args.Y)) + { + NetMessage.SendData((int)PacketTypes.UpdateTileEntity, -1, -1, NetworkText.Empty, args.ItemFrame.ID, 0, 1); + args.Handled = true; + return; + } + + if (!args.Player.IsInRange(args.X, args.Y)) + { + NetMessage.SendData((int)PacketTypes.UpdateTileEntity, -1, -1, NetworkText.Empty, args.ItemFrame.ID, 0, 1); + args.Handled = true; + return; + } + } + internal void OnPlayerPortalTeleport(object sender, GetDataHandlers.TeleportThroughPortalEventArgs args) { //Packet 96 (player teleport through portal) has no validation on whether or not the player id provided @@ -1871,7 +1666,212 @@ namespace TShockAPI return; } } + + /// Handles the anti-cheat components of gem lock toggles. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnGemLockToggle(object sender, GetDataHandlers.GemLockToggleEventArgs args) + { + if (args.X < 0 || args.Y < 0 || args.X >= Main.maxTilesX || args.Y >= Main.maxTilesY) + { + args.Handled = true; + return; + } + if (!TShock.Utils.TilePlacementValid(args.X, args.Y) || (args.Player.Dead && TShock.Config.PreventDeadModification)) + { + args.Handled = true; + return; + } + + if (args.Player.IsBeingDisabled()) + { + args.Handled = true; + return; + } + + if (!args.Player.HasBuildPermission(args.X, args.Y)) + { + args.Handled = true; + return; + } + } + + /// Handles validation of of basic anti-cheat on mass wire operations. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnMassWireOperation(object sender, GetDataHandlers.MassWireOperationEventArgs args) + { + short startX = args.StartX; + short startY = args.StartY; + short endX = args.EndX; + short endY = args.EndY; + + List points = Utils.Instance.GetMassWireOperationRange( + new Point(startX, startY), + new Point(endX, endY), + args.Player.TPlayer.direction == 1); + + int x; + int y; + foreach (Point p in points) + { + /* Perform similar checks to TileKill + * The server-side nature of this packet removes the need to use SendTileSquare + * Range checks are currently ignored here as the items that send this seem to have infinite range */ + + x = p.X; + y = p.Y; + + if (!TShock.Utils.TilePlacementValid(x, y) || (args.Player.Dead && TShock.Config.PreventDeadModification)) + { + args.Handled = true; + return; + } + + if (args.Player.IsBeingDisabled()) + { + args.Handled = true; + return; + } + + if (!args.Player.HasBuildPermission(x, y)) + { + args.Handled = true; + return; + } + } + } + + /// Called when a player is damaged. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnPlayerDamage(object sender, GetDataHandlers.PlayerDamageEventArgs args) + { + byte id = args.ID; + short damage = args.Damage; + bool pvp = args.PVP; + bool crit = args.Critical; + byte direction = args.Direction; + + if (id >= Main.maxPlayers || TShock.Players[id] == null) + { + args.Handled = true; + return; + } + + if (damage > TShock.Config.MaxDamage && !args.Player.HasPermission(Permissions.ignoredamagecap) && id != args.Player.Index) + { + if (TShock.Config.KickOnDamageThresholdBroken) + { + args.Player.Kick(string.Format("Player damage exceeded {0}.", TShock.Config.MaxDamage)); + args.Handled = true; + return; + } + else + { + args.Player.Disable(String.Format("Player damage exceeded {0}.", TShock.Config.MaxDamage), DisableFlags.WriteToLogAndConsole); + } + args.Player.SendData(PacketTypes.PlayerHp, "", id); + args.Player.SendData(PacketTypes.PlayerUpdate, "", id); + args.Handled = true; + return; + } + + if (!TShock.Players[id].TPlayer.hostile && pvp && id != args.Player.Index) + { + args.Player.SendData(PacketTypes.PlayerHp, "", id); + args.Player.SendData(PacketTypes.PlayerUpdate, "", id); + args.Handled = true; + return; + } + + if (args.Player.IsBeingDisabled()) + { + args.Player.SendData(PacketTypes.PlayerHp, "", id); + args.Player.SendData(PacketTypes.PlayerUpdate, "", id); + args.Handled = true; + return; + } + + if (!args.Player.IsInRange(TShock.Players[id].TileX, TShock.Players[id].TileY, 100)) + { + args.Player.SendData(PacketTypes.PlayerHp, "", id); + args.Player.SendData(PacketTypes.PlayerUpdate, "", id); + args.Handled = true; + return; + } + + if (args.Player.IsBouncerThrottled()) + { + args.Player.SendData(PacketTypes.PlayerHp, "", id); + args.Player.SendData(PacketTypes.PlayerUpdate, "", id); + args.Handled = true; + return; + } + + } + + /// Bouncer's KillMe hook stops crash exploits from out of bounds values. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnKillMe(object sender, GetDataHandlers.KillMeEventArgs args) + { + short damage = args.Damage; + short id = args.PlayerId; + PlayerDeathReason playerDeathReason = args.PlayerDeathReason; + + if (damage > 20000) //Abnormal values have the potential to cause infinite loops in the server. + { + args.Player.Kick("Failed to shade polygon normals.", true, true); + TShock.Log.ConsoleError("Death Exploit Attempt: Damage {0}", damage); + args.Handled = true; + return; + } + + if (id >= Main.maxPlayers) + { + args.Handled = true; + return; + } + + // This was formerly marked as a crash check; does not actually crash on this specific packet. + if (playerDeathReason != null) + { + if (playerDeathReason.GetDeathText(TShock.Players[id].Name).ToString().Length > 500) + { + TShock.Players[id].Kick("Death reason outside of normal bounds.", true); + args.Handled = true; + return; + } + } + } + + + private static Dictionary NPCAddBuffTimeMax = new Dictionary() + { + { BuffID.Poisoned, 3600 }, + { BuffID.OnFire, 1200 }, + { BuffID.CursedInferno, 420 }, + { BuffID.Frostburn, 900 }, + { BuffID.Ichor, 1200 }, + { BuffID.Venom, 1260 }, + { BuffID.Midas, 120 }, + { BuffID.Wet, 1500 }, + { BuffID.Slimed, 1500 }, + { BuffID.Lovestruck, 1800 }, + { BuffID.Stinky, 1800 }, + { BuffID.SoulDrain, 30 }, + { BuffID.ShadowFlame, 660 }, + { BuffID.DryadsWard, 120 }, + { BuffID.BoneJavelin, 900 }, + { BuffID.StardustMinionBleed, 900 }, + { BuffID.DryadsWardDebuff, 120 }, + { BuffID.Daybreak, 300 }, + { BuffID.BetsysCurse, 600 }, + { BuffID.Oiled, 540 } + }; + /// /// Tile IDs that can be oriented: /// Cannon, diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index dd18e502..4a126d56 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -74,136 +74,148 @@ namespace TShockAPI public static class GetDataHandlers { private static Dictionary GetDataHandlerDelegates; + public static int[] WhitelistBuffMaxTime; + + public static void InitGetDataHandler() + { + #region Blacklists + + WhitelistBuffMaxTime = new int[Main.maxBuffTypes]; + WhitelistBuffMaxTime[20] = 600; + WhitelistBuffMaxTime[0x18] = 1200; + WhitelistBuffMaxTime[0x1f] = 120; + WhitelistBuffMaxTime[0x27] = 420; + + #endregion Blacklists + + GetDataHandlerDelegates = new Dictionary + { + { PacketTypes.PlayerInfo, HandlePlayerInfo }, + { PacketTypes.PlayerSlot, HandlePlayerSlot }, + { PacketTypes.ContinueConnecting2, HandleConnecting }, + { PacketTypes.TileGetSection, HandleGetSection }, + { PacketTypes.PlayerSpawn, HandleSpawn }, + { PacketTypes.PlayerUpdate, HandlePlayerUpdate }, + { PacketTypes.PlayerHp, HandlePlayerHp }, + { PacketTypes.Tile, HandleTile }, + { PacketTypes.DoorUse, HandleDoorUse }, + { PacketTypes.TileSendSquare, HandleSendTileSquare }, + { PacketTypes.ItemDrop, HandleItemDrop }, + { PacketTypes.ItemOwner, HandleItemOwner }, + { PacketTypes.ProjectileNew, HandleProjectileNew }, + { PacketTypes.NpcStrike, HandleNpcStrike }, + { PacketTypes.ProjectileDestroy, HandleProjectileKill }, + { PacketTypes.TogglePvp, HandleTogglePvp }, + { PacketTypes.ChestGetContents, HandleChestOpen }, + { PacketTypes.ChestItem, HandleChestItem }, + { PacketTypes.ChestOpen, HandleChestActive }, + { PacketTypes.PlaceChest, HandlePlaceChest }, + { PacketTypes.Zones, HandlePlayerZone }, + { PacketTypes.PasswordSend, HandlePassword }, + { PacketTypes.PlayerAnimation, HandlePlayerAnimation }, + { PacketTypes.PlayerMana, HandlePlayerMana }, + { PacketTypes.PlayerTeam, HandlePlayerTeam }, + { PacketTypes.SignNew, HandleSign }, + { PacketTypes.LiquidSet, HandleLiquidSet }, + { PacketTypes.PlayerBuff, HandlePlayerBuffList }, + { PacketTypes.NpcSpecial, HandleSpecial }, + { PacketTypes.NpcAddBuff, HandleNPCAddBuff }, + { PacketTypes.PlayerAddBuff, HandlePlayerAddBuff }, + { PacketTypes.UpdateNPCHome, UpdateNPCHome }, + { PacketTypes.SpawnBossorInvasion, HandleSpawnBoss }, + { PacketTypes.PaintTile, HandlePaintTile }, + { PacketTypes.PaintWall, HandlePaintWall }, + { PacketTypes.Teleport, HandleTeleport }, + { PacketTypes.PlayerHealOther, HandleHealOther }, + { PacketTypes.CatchNPC, HandleCatchNpc }, + { PacketTypes.CompleteAnglerQuest, HandleCompleteAnglerQuest }, + { PacketTypes.NumberOfAnglerQuestsCompleted, HandleNumberOfAnglerQuestsCompleted }, + { PacketTypes.PlaceObject, HandlePlaceObject }, + { PacketTypes.LoadNetModule, HandleLoadNetModule }, + { PacketTypes.PlaceTileEntity, HandlePlaceTileEntity }, + { PacketTypes.PlaceItemFrame, HandlePlaceItemFrame }, + { PacketTypes.UpdateItemDrop, HandleItemDrop }, + { PacketTypes.SyncExtraValue, HandleSyncExtraValue }, + { PacketTypes.KillPortal, HandleKillPortal }, + { PacketTypes.PlayerTeleportPortal, HandlePlayerPortalTeleport }, + { PacketTypes.NpcTeleportPortal, HandleNpcTeleportPortal }, + { PacketTypes.GemLockToggle, HandleGemLockToggle }, + { PacketTypes.MassWireOperation, HandleMassWireOperation }, + { PacketTypes.ToggleParty, HandleToggleParty }, + { PacketTypes.CrystalInvasionStart, HandleOldOnesArmy }, + { PacketTypes.PlayerHurtV2, HandlePlayerDamageV2 }, + { PacketTypes.PlayerDeathV2, HandlePlayerKillMeV2 } + }; + } + + public static bool HandlerGetData(PacketTypes type, TSPlayer player, MemoryStream data) + { + GetDataHandlerDelegate handler; + if (GetDataHandlerDelegates.TryGetValue(type, out handler)) + { + try + { + return handler(new GetDataHandlerArgs(player, data)); + } + catch (Exception ex) + { + TShock.Log.Error(ex.ToString()); + return true; + } + } + return false; + } + #region Events - /// - /// Used when a TileEdit event is called. - /// - public class TileEditEventArgs : GetDataHandledEventArgs + public class PlayerInfoEventArgs : GetDataHandledEventArgs { /// - /// The tile coordinate on the X plane - /// - public int X { get; set; } - - /// - /// The tile coordinate on the Y plane - /// - public int Y { get; set; } - - /// - /// The Tile ID being edited. - /// - public short EditData { get; set; } - /// - /// The EditType. - /// (KillTile = 0, PlaceTile = 1, KillWall = 2, PlaceWall = 3, KillTileNoItem = 4, PlaceWire = 5, KillWire = 6) - /// - public EditAction Action { get; set; } - - /// - /// Did the tile get destroyed successfully. - /// - public EditType editDetail { get; set; } - - /// - /// Used when a tile is placed to denote a subtype of tile. (e.g. for tile id 21: Chest = 0, Gold Chest = 1) - /// - public byte Style { get; set; } - } - - /// - /// TileEdit - called when a tile is placed or destroyed - /// - public static HandlerList TileEdit = new HandlerList(); - private static bool OnTileEdit(TSPlayer ply, MemoryStream data, int x, int y, EditAction action, EditType editDetail, short editData, byte style) - { - if (TileEdit == null) - return false; - - var args = new TileEditEventArgs - { - Player = ply, - Data = data, - X = x, - Y = y, - Action = action, - EditData = editData, - editDetail = editDetail, - Style = style - }; - TileEdit.Invoke(null, args); - return args.Handled; - } - /// - /// For use in a TogglePvp event - /// - public class TogglePvpEventArgs : GetDataHandledEventArgs - { - /// - /// The Terraria player ID of the player + /// The Terraria playerID of the player /// public byte PlayerId { get; set; } /// - /// Enable/disable pvp? + /// Hair color /// - public bool Pvp { get; set; } + public byte Hair { get; set; } + /// + /// Clothing style. 0-3 are for male characters, and 4-7 are for female characters. + /// + public int Style { get; set; } + /// + /// Character difficulty + /// + public byte Difficulty { get; set; } + /// + /// Player/character name + /// + public string Name { get; set; } } /// - /// TogglePvp - called when a player toggles pvp + /// PlayerInfo - called at a PlayerInfo event + /// If this is cancelled, the server will kick the player. If this should be changed in the future, let someone know. /// - public static HandlerList TogglePvp = new HandlerList(); - private static bool OnPvpToggled(TSPlayer player, MemoryStream data, byte _id, bool _pvp) + public static HandlerList PlayerInfo = new HandlerList(); + private static bool OnPlayerInfo(TSPlayer player, MemoryStream data, byte _plrid, byte _hair, int _style, byte _difficulty, string _name) { - if (TogglePvp == null) + if (PlayerInfo == null) return false; - var args = new TogglePvpEventArgs + var args = new PlayerInfoEventArgs { Player = player, Data = data, - PlayerId = _id, - Pvp = _pvp, + PlayerId = _plrid, + Hair = _hair, + Style = _style, + Difficulty = _difficulty, + Name = _name, }; - TogglePvp.Invoke(null, args); + PlayerInfo.Invoke(null, args); return args.Handled; } - - /// - /// For use in a PlayerTeam event - /// - public class PlayerTeamEventArgs : GetDataHandledEventArgs - { - /// - /// The Terraria player ID of the player - /// - public byte PlayerId { get; set; } - /// - /// Enable/disable pvp? - /// - public byte Team { get; set; } - } - /// - /// TogglePvp - called when a player toggles pvp - /// - public static HandlerList PlayerTeam = new HandlerList(); - private static bool OnPlayerTeam(TSPlayer player, MemoryStream data, byte _id, byte _team) - { - if (PlayerTeam == null) - return false; - - var args = new PlayerTeamEventArgs - { - Player = player, - Data = data, - PlayerId = _id, - Team = _team, - }; - PlayerTeam.Invoke(null, args); - return args.Handled; - } - + /// /// For use in a PlayerSlot event /// @@ -252,251 +264,35 @@ namespace TShockAPI PlayerSlot.Invoke(null, args); return args.Handled; } - - /// - /// For use in a PlayerHP event - /// - public class PlayerHPEventArgs : GetDataHandledEventArgs + + /// The arguments to a GetSection packet. + public class GetSectionEventArgs : GetDataHandledEventArgs { - /// - /// The Terraria playerID of the player - /// - public byte PlayerId { get; set; } - /// - /// Current HP - /// - public short Current { get; set; } - /// - /// Maximum HP - /// - public short Max { get; set; } + /// The X position requested. Or -1 for spawn. + public int X { get; set; } + + /// The Y position requested. Or -1 for spawn. + public int Y { get; set; } } - /// - /// PlayerHP - called at a PlayerHP event - /// - public static HandlerList PlayerHP = new HandlerList(); - - private static bool OnPlayerHP(TSPlayer player, MemoryStream data, byte _plr, short _cur, short _max) + /// The hook for a GetSection event. + public static HandlerList GetSection = new HandlerList(); + private static bool OnGetSection(TSPlayer player, MemoryStream data, int x, int y) { - if (PlayerHP == null) + if (GetSection == null) return false; - var args = new PlayerHPEventArgs + var args = new GetSectionEventArgs { Player = player, Data = data, - PlayerId = _plr, - Current = _cur, - Max = _max, - }; - PlayerHP.Invoke(null, args); - return args.Handled; - } - - /// - /// For use in a PlayerMana event - /// - public class PlayerManaEventArgs : GetDataHandledEventArgs - { - public byte PlayerId { get; set; } - public short Current { get; set; } - public short Max { get; set; } - } - /// - /// PlayerMana - called at a PlayerMana event - /// - public static HandlerList PlayerMana = new HandlerList(); - - private static bool OnPlayerMana(TSPlayer player, MemoryStream data, byte _plr, short _cur, short _max) - { - if (PlayerMana == null) - return false; - - var args = new PlayerManaEventArgs - { - Player = player, - Data = data, - PlayerId = _plr, - Current = _cur, - Max = _max, - }; - PlayerMana.Invoke(null, args); - return args.Handled; - } - - public class PlayerInfoEventArgs : GetDataHandledEventArgs - { - /// - /// The Terraria playerID of the player - /// - public byte PlayerId { get; set; } - /// - /// Hair color - /// - public byte Hair { get; set; } - /// - /// Clothing style. 0-3 are for male characters, and 4-7 are for female characters. - /// - public int Style { get; set; } - /// - /// Character difficulty - /// - public byte Difficulty { get; set; } - /// - /// Player/character name - /// - public string Name { get; set; } - } - /// - /// PlayerInfo - called at a PlayerInfo event - /// If this is cancelled, the server will kick the player. If this should be changed in the future, let someone know. - /// - public static HandlerList PlayerInfo = new HandlerList(); - - private static bool OnPlayerInfo(TSPlayer player, MemoryStream data, byte _plrid, byte _hair, int _style, byte _difficulty, string _name) - { - if (PlayerInfo == null) - return false; - - var args = new PlayerInfoEventArgs - { - Player = player, - Data = data, - PlayerId = _plrid, - Hair = _hair, - Style = _style, - Difficulty = _difficulty, - Name = _name, - }; - PlayerInfo.Invoke(null, args); - return args.Handled; - } - - /// - /// For use in a PlaceChest event - /// - public class PlaceChestEventArgs : GetDataHandledEventArgs - { - /// What the packet is doing (see MP packet docs). - public int Flag { get; set; } - /// - /// The X coordinate - /// - public int TileX { get; set; } - /// - /// The Y coordinate - /// - public int TileY { get; set; } - } - /// - /// When a chest is added or removed from the world. - /// - public static HandlerList PlaceChest = new HandlerList(); - - private static bool OnPlaceChest(TSPlayer player, MemoryStream data, int flag, int tilex, int tiley) - { - if (PlaceChest == null) - return false; - - var args = new PlaceChestEventArgs - { - Player = player, - Data = data, - Flag = flag, - TileX = tilex, - TileY = tiley, - }; - PlaceChest.Invoke(null, args); - return args.Handled; - } - - /// The arguments to the ProjectileKill packet. - public class ProjectileKillEventArgs : GetDataHandledEventArgs - { - /// The projectile's identity...? - public int ProjectileIdentity; - /// The the player index of the projectile's owner (Main.players). - public byte ProjectileOwner; - /// The index of the projectile in Main.projectile. - public int ProjectileIndex; - } - - /// The event fired when a projectile kill packet is received. - public static HandlerList ProjectileKill = new HandlerList(); - - /// Fires the ProjectileKill event. - /// The TSPlayer that caused the event. - /// The MemoryStream containing the raw event data. - /// The projectile identity (from the packet). - /// The projectile's owner (from the packet). - /// The projectile's index (from Main.projectiles). - /// bool - private static bool OnProjectileKill(TSPlayer player, MemoryStream data, int identity, byte owner, int index) - { - if (ProjectileKill == null) - return false; - - var args = new ProjectileKillEventArgs - { - Player = player, - Data = data, - ProjectileIdentity = identity, - ProjectileOwner = owner, - ProjectileIndex = index, + X = x, + Y = y, }; - ProjectileKill.Invoke(null, args); + GetSection.Invoke(null, args); return args.Handled; } - - /// - /// For use in a KillMe event - /// - public class KillMeEventArgs : GetDataHandledEventArgs - { - /// - /// The Terraria playerID of the player - /// - public byte PlayerId { get; set; } - /// - /// The direction the damage is coming from (?) - /// - public byte Direction { get; set; } - /// - /// Amount of damage delt - /// - public short Damage { get; set; } - /// - /// Player's current pvp setting - /// - public bool Pvp { get; set; } - /// The reason the player died. - public PlayerDeathReason PlayerDeathReason { get; set; } - } - /// - /// KillMe - Terraria's crappy way of handling damage from players - /// - public static HandlerList KillMe = new HandlerList(); - - private static bool OnKillMe(TSPlayer player, MemoryStream data, byte plr, byte direction, short damage, bool pvp, PlayerDeathReason playerDeathReason) - { - if (KillMe == null) - return false; - - var args = new KillMeEventArgs - { - Player = player, - Data = data, - PlayerId = plr, - Direction = direction, - Damage = damage, - Pvp = pvp, - PlayerDeathReason = playerDeathReason, - }; - KillMe.Invoke(null, args); - return args.Handled; - } - + /// /// For use in a PlayerUpdate event /// @@ -529,7 +325,6 @@ namespace TShockAPI /// PlayerUpdate - When the player sends it's updated information to the server /// public static HandlerList PlayerUpdate = new HandlerList(); - private static bool OnPlayerUpdate(TSPlayer player, MemoryStream data, byte plr, byte control, byte item, Vector2 position, Vector2 velocity, byte pulley) { if (PlayerUpdate == null) @@ -549,93 +344,105 @@ namespace TShockAPI PlayerUpdate.Invoke(null, args); return args.Handled; } - + /// - /// For use in a PlayerZone event + /// For use in a PlayerHP event /// - public class PlayerZoneEventArgs : GetDataHandledEventArgs + public class PlayerHPEventArgs : GetDataHandledEventArgs { /// /// The Terraria playerID of the player /// public byte PlayerId { get; set; } /// - /// 0 = Dungeon, 1 = Corruption,2 =Holy, 3 = Meteor, 4 = Jungle, 5 = Snow, 6 = Crimson, 7 = Water Candle + /// Current HP /// - public BitsByte Zone1 { get; set; } + public short Current { get; set; } /// - /// 0 = Peace Candle, 1 = Solar Tower, 2 = Vortex Tower, 3 = Nebula Tower, 4 = Stardust Tower, 5 = Desert, 6 = Glowshroom, 7 = Underground Desert + /// Maximum HP /// - public BitsByte Zone2 { get; set; } - /// - /// 0 = Overworld, 1 = Dirt Layer, 2 = Rock Layer, 3 = Underworld, 4 = Beach, 5 = Rain, 6 = Sandstorm - /// - public BitsByte Zone3 { get; set; } - /// - /// 0 = Old One's Army - /// - public BitsByte Zone4 { get; set; } + public short Max { get; set; } } /// - /// PlayerZone - When the player sends it's zone/biome information to the server + /// PlayerHP - called at a PlayerHP event /// - public static HandlerList PlayerZone = new HandlerList(); + public static HandlerList PlayerHP = new HandlerList(); + private static bool OnPlayerHP(TSPlayer player, MemoryStream data, byte _plr, short _cur, short _max) + { + if (PlayerHP == null) + return false; + + var args = new PlayerHPEventArgs + { + Player = player, + Data = data, + PlayerId = _plr, + Current = _cur, + Max = _max, + }; + PlayerHP.Invoke(null, args); + return args.Handled; + } - private static bool OnPlayerZone(TSPlayer player, MemoryStream data, byte plr, BitsByte zone1, BitsByte zone2, BitsByte zone3, BitsByte zone4) + /// + /// Used when a TileEdit event is called. + /// + public class TileEditEventArgs : GetDataHandledEventArgs { - if (PlayerZone == null) + /// + /// The tile coordinate on the X plane + /// + public int X { get; set; } + + /// + /// The tile coordinate on the Y plane + /// + public int Y { get; set; } + + /// + /// The Tile ID being edited. + /// + public short EditData { get; set; } + /// + /// The EditType. + /// (KillTile = 0, PlaceTile = 1, KillWall = 2, PlaceWall = 3, KillTileNoItem = 4, PlaceWire = 5, KillWire = 6) + /// + public EditAction Action { get; set; } + + /// + /// Did the tile get destroyed successfully. + /// + public EditType editDetail { get; set; } + + /// + /// Used when a tile is placed to denote a subtype of tile. (e.g. for tile id 21: Chest = 0, Gold Chest = 1) + /// + public byte Style { get; set; } + } + /// + /// TileEdit - called when a tile is placed or destroyed + /// + public static HandlerList TileEdit = new HandlerList(); + private static bool OnTileEdit(TSPlayer ply, MemoryStream data, int x, int y, EditAction action, EditType editDetail, short editData, byte style) + { + if (TileEdit == null) return false; - var args = new PlayerZoneEventArgs + var args = new TileEditEventArgs { - Player = player, + Player = ply, Data = data, - PlayerId = plr, - Zone1 = zone1, - Zone2 = zone2, - Zone3 = zone3, - Zone4 = zone4 + X = x, + Y = y, + Action = action, + EditData = editData, + editDetail = editDetail, + Style = style }; - PlayerZone.Invoke(null, args); + TileEdit.Invoke(null, args); return args.Handled; } - - /// The event args object for the HealOtherPlayer event - public class HealOtherPlayerEventArgs : GetDataHandledEventArgs - { - /// The Terraria player index of the target player - public byte TargetPlayerIndex { get; set; } - - /// The amount to heal by - public short Amount { get; set; } - } - - /// When a player heals another player - public static HandlerList HealOtherPlayer = new HandlerList(); - - /// Fires the HealOtherPlayer event - /// The TSPlayer that caused the event. - /// The MemoryStream containing the raw event data. - /// The Terraria player index that the event targets - /// The amount to heal - /// bool - private static bool OnHealOtherPlayer(TSPlayer player, MemoryStream data, byte targetPlayerIndex, short amount) - { - if (HealOtherPlayer == null) - return false; - - var args = new HealOtherPlayerEventArgs - { - Player = player, - Data = data, - TargetPlayerIndex = targetPlayerIndex, - Amount = amount, - }; - - HealOtherPlayer.Invoke(null, args); - return args.Handled; - } - + /// /// For use in a SendTileSquare event /// @@ -660,7 +467,6 @@ namespace TShockAPI /// When the player sends a tile square /// public static HandlerList SendTileSquare = new HandlerList(); - private static bool OnSendTileSquare(TSPlayer player, MemoryStream data, short size, int tilex, int tiley) { if (SendTileSquare == null) @@ -678,64 +484,67 @@ namespace TShockAPI SendTileSquare.Invoke(null, args); return args.Handled; } - - /// The arguments to the PlaceObject hook. - public class PlaceObjectEventArgs : GetDataHandledEventArgs + + /// + /// For use in an ItemDrop event + /// + public class ItemDropEventArgs : GetDataHandledEventArgs { - /// The X location where the object was placed. - public short X { get; set ; } - - /// The Y location where the object was placed. - public short Y { get; set; } - - /// The type of object that was placed. + /// + /// ID of the item. + /// If below 400 and NetID(Type) is 0 Then Set Null. If ItemID is 400 Then New Item + /// + public short ID { get; set; } + /// + /// Position of the item + /// + public Vector2 Position { get; set; } + /// + /// Velocity at which the item is deployed + /// + public Vector2 Velocity { get; set; } + /// + /// Stacks + /// + public short Stacks { get; set; } + /// + /// Prefix of the item + /// + public byte Prefix { get; set; } + /// + /// No Delay on pickup + /// + public bool NoDelay { get; set; } + /// + /// Item type + /// public short Type { get; set; } - - /// The style of the object was placed. - public short Style { get; set; } - - /// Alternate variation of the object placed. - public byte Alternate { get; set; } - - /// The direction the object was placed. - public bool Direction { get; set; } } - - /// Fired when an object is placed in the world. - public static HandlerList PlaceObject = new HandlerList(); - - /// Fires the PlaceObject hook. To be called when an object is placed in the world. - /// The TSPlayer that caused the event. - /// The MemoryStream containing the raw event data. - /// The x position where the object is placed. - /// The y position where the object is placed. - /// The type of object. - /// The object's style data. - /// The object's alternate data. - /// The direction of the object. - /// bool - private static bool OnPlaceObject(TSPlayer player, MemoryStream data, short x, short y, short type, short style, byte alternate, bool direction) + /// + /// ItemDrop - Called when an item is dropped + /// + public static HandlerList ItemDrop = new HandlerList(); + private static bool OnItemDrop(TSPlayer player, MemoryStream data, short id, Vector2 pos, Vector2 vel, short stacks, byte prefix, bool noDelay, short type) { - if (PlaceObject == null) + if (ItemDrop == null) return false; - var args = new PlaceObjectEventArgs + var args = new ItemDropEventArgs { Player = player, Data = data, - X = x, - Y = y, + ID = id, + Position = pos, + Velocity = vel, + Stacks = stacks, + Prefix = prefix, + NoDelay = noDelay, Type = type, - Style = style, - Alternate = alternate, - Direction = direction }; - - PlaceObject.Invoke(null, args); + ItemDrop.Invoke(null, args); return args.Handled; } - - + /// /// For use in a NewProjectile event /// @@ -778,7 +587,6 @@ namespace TShockAPI /// NewProjectile - Called when a client creates a new projectile /// public static HandlerList NewProjectile = new HandlerList(); - private static bool OnNewProjectile(MemoryStream data, short ident, Vector2 pos, Vector2 vel, float knockback, short dmg, byte owner, short type, int index, TSPlayer player) { if (NewProjectile == null) @@ -800,51 +608,127 @@ namespace TShockAPI NewProjectile.Invoke(null, args); return args.Handled; } - + /// - /// For use in a LiquidSet event + /// For use with a NPCStrike event /// - public class LiquidSetEventArgs : GetDataHandledEventArgs + public class NPCStrikeEventArgs : GetDataHandledEventArgs { /// - /// X location of the tile + /// ??? /// - public int TileX { get; set; } + public short ID { get; set; } /// - /// Y location of the tile + /// Direction the damage occurred from /// - public int TileY { get; set; } + public byte Direction { get; set; } /// - /// Amount of liquid + /// Amount of damage /// - public byte Amount { get; set; } + public short Damage { get; set; } /// - /// Type of Liquid: 0=water, 1=lave, 2=honey + /// Knockback /// - public byte Type { get; set; } + public float Knockback { get; set; } + /// + /// Critical? + /// + public byte Critical { get; set; } } /// - /// LiquidSet - When ever a liquid is set + /// NPCStrike - Called when an NPC is attacked /// - public static HandlerList LiquidSet = new HandlerList(); - - private static bool OnLiquidSet(TSPlayer player, MemoryStream data, int tilex, int tiley, byte amount, byte type) + public static HandlerList NPCStrike = new HandlerList(); + private static bool OnNPCStrike(TSPlayer player, MemoryStream data, short id, byte dir, short dmg, float knockback, byte crit) { - if (LiquidSet == null) + if (NPCStrike == null) return false; - var args = new LiquidSetEventArgs + var args = new NPCStrikeEventArgs { Player = player, Data = data, - TileX = tilex, - TileY = tiley, - Amount = amount, - Type = type, + ID = id, + Direction = dir, + Damage = dmg, + Knockback = knockback, + Critical = crit, }; - LiquidSet.Invoke(null, args); + NPCStrike.Invoke(null, args); return args.Handled; } + + /// The arguments to the ProjectileKill packet. + public class ProjectileKillEventArgs : GetDataHandledEventArgs + { + /// The projectile's identity...? + public int ProjectileIdentity; + /// The the player index of the projectile's owner (Main.players). + public byte ProjectileOwner; + /// The index of the projectile in Main.projectile. + public int ProjectileIndex; + } + /// The event fired when a projectile kill packet is received. + public static HandlerList ProjectileKill = new HandlerList(); + /// Fires the ProjectileKill event. + /// The TSPlayer that caused the event. + /// The MemoryStream containing the raw event data. + /// The projectile identity (from the packet). + /// The projectile's owner (from the packet). + /// The projectile's index (from Main.projectiles). + /// bool + private static bool OnProjectileKill(TSPlayer player, MemoryStream data, int identity, byte owner, int index) + { + if (ProjectileKill == null) + return false; + + var args = new ProjectileKillEventArgs + { + Player = player, + Data = data, + ProjectileIdentity = identity, + ProjectileOwner = owner, + ProjectileIndex = index, + }; + + ProjectileKill.Invoke(null, args); + return args.Handled; + } + + /// + /// For use in a TogglePvp event + /// + public class TogglePvpEventArgs : GetDataHandledEventArgs + { + /// + /// The Terraria player ID of the player + /// + public byte PlayerId { get; set; } + /// + /// Enable/disable pvp? + /// + public bool Pvp { get; set; } + } + /// + /// TogglePvp - called when a player toggles pvp + /// + public static HandlerList TogglePvp = new HandlerList(); + private static bool OnPvpToggled(TSPlayer player, MemoryStream data, byte _id, bool _pvp) + { + if (TogglePvp == null) + return false; + + var args = new TogglePvpEventArgs + { + Player = player, + Data = data, + PlayerId = _id, + Pvp = _pvp, + }; + TogglePvp.Invoke(null, args); + return args.Handled; + } + /// /// For use in a PlayerSpawn event /// @@ -867,7 +751,6 @@ namespace TShockAPI /// PlayerSpawn - When a player spawns /// public static HandlerList PlayerSpawn = new HandlerList(); - private static bool OnPlayerSpawn(TSPlayer player, MemoryStream data, byte pid, int spawnX, int spawnY) { if (PlayerSpawn == null) @@ -884,41 +767,7 @@ namespace TShockAPI PlayerSpawn.Invoke(null, args); return args.Handled; } - /// - /// For use with a ChestOpen event - /// - public class ChestOpenEventArgs : GetDataHandledEventArgs - { - /// - /// X location of said chest - /// - public int X { get; set; } - /// - /// Y location of said chest - /// - public int Y { get; set; } - } - /// - /// ChestOpen - Called when any chest is opened - /// - public static HandlerList ChestOpen = new HandlerList(); - - private static bool OnChestOpen(MemoryStream data, int x, int y, TSPlayer player) - { - if (ChestOpen == null) - return false; - - var args = new ChestOpenEventArgs - { - Data = data, - X = x, - Y = y, - Player = player, - }; - ChestOpen.Invoke(null, args); - return args.Handled; - } - + /// /// For use in a ChestItemChange event /// @@ -949,7 +798,6 @@ namespace TShockAPI /// ChestItemChange - Called when an item in a chest changes /// public static HandlerList ChestItemChange = new HandlerList(); - private static bool OnChestItemChange(TSPlayer player, MemoryStream data, short id, byte slot, short stacks, byte prefix, short type) { if (ChestItemChange == null) @@ -968,7 +816,213 @@ namespace TShockAPI ChestItemChange.Invoke(null, args); return args.Handled; } + + /// + /// For use with a ChestOpen event + /// + public class ChestOpenEventArgs : GetDataHandledEventArgs + { + /// + /// X location of said chest + /// + public int X { get; set; } + /// + /// Y location of said chest + /// + public int Y { get; set; } + } + /// + /// ChestOpen - Called when any chest is opened + /// + public static HandlerList ChestOpen = new HandlerList(); + private static bool OnChestOpen(MemoryStream data, int x, int y, TSPlayer player) + { + if (ChestOpen == null) + return false; + var args = new ChestOpenEventArgs + { + Data = data, + X = x, + Y = y, + Player = player, + }; + ChestOpen.Invoke(null, args); + return args.Handled; + } + + /// + /// For use in a PlaceChest event + /// + public class PlaceChestEventArgs : GetDataHandledEventArgs + { + /// What the packet is doing (see MP packet docs). + public int Flag { get; set; } + /// + /// The X coordinate + /// + public int TileX { get; set; } + /// + /// The Y coordinate + /// + public int TileY { get; set; } + } + /// + /// When a chest is added or removed from the world. + /// + public static HandlerList PlaceChest = new HandlerList(); + private static bool OnPlaceChest(TSPlayer player, MemoryStream data, int flag, int tilex, int tiley) + { + if (PlaceChest == null) + return false; + + var args = new PlaceChestEventArgs + { + Player = player, + Data = data, + Flag = flag, + TileX = tilex, + TileY = tiley, + }; + PlaceChest.Invoke(null, args); + return args.Handled; + } + + /// + /// For use in a PlayerZone event + /// + public class PlayerZoneEventArgs : GetDataHandledEventArgs + { + /// + /// The Terraria playerID of the player + /// + public byte PlayerId { get; set; } + /// + /// 0 = Dungeon, 1 = Corruption,2 =Holy, 3 = Meteor, 4 = Jungle, 5 = Snow, 6 = Crimson, 7 = Water Candle + /// + public BitsByte Zone1 { get; set; } + /// + /// 0 = Peace Candle, 1 = Solar Tower, 2 = Vortex Tower, 3 = Nebula Tower, 4 = Stardust Tower, 5 = Desert, 6 = Glowshroom, 7 = Underground Desert + /// + public BitsByte Zone2 { get; set; } + /// + /// 0 = Overworld, 1 = Dirt Layer, 2 = Rock Layer, 3 = Underworld, 4 = Beach, 5 = Rain, 6 = Sandstorm + /// + public BitsByte Zone3 { get; set; } + /// + /// 0 = Old One's Army + /// + public BitsByte Zone4 { get; set; } + } + /// + /// PlayerZone - When the player sends it's zone/biome information to the server + /// + public static HandlerList PlayerZone = new HandlerList(); + private static bool OnPlayerZone(TSPlayer player, MemoryStream data, byte plr, BitsByte zone1, BitsByte zone2, BitsByte zone3, BitsByte zone4) + { + if (PlayerZone == null) + return false; + + var args = new PlayerZoneEventArgs + { + Player = player, + Data = data, + PlayerId = plr, + Zone1 = zone1, + Zone2 = zone2, + Zone3 = zone3, + Zone4 = zone4 + }; + PlayerZone.Invoke(null, args); + return args.Handled; + } + + /// + /// For use with a PlayerAnimation event + /// + public class PlayerAnimationEventArgs : GetDataHandledEventArgs { } + /// + /// PlayerAnimation - Called when a player animates + /// + public static HandlerList PlayerAnimation = new HandlerList(); + private static bool OnPlayerAnimation(TSPlayer player, MemoryStream data) + { + if (PlayerAnimation == null) + return false; + + var args = new PlayerAnimationEventArgs + { + Player = player, + Data = data, + }; + PlayerAnimation.Invoke(null, args); + return args.Handled; + } + + /// + /// For use in a PlayerMana event + /// + public class PlayerManaEventArgs : GetDataHandledEventArgs + { + public byte PlayerId { get; set; } + public short Current { get; set; } + public short Max { get; set; } + } + /// + /// PlayerMana - called at a PlayerMana event + /// + public static HandlerList PlayerMana = new HandlerList(); + private static bool OnPlayerMana(TSPlayer player, MemoryStream data, byte _plr, short _cur, short _max) + { + if (PlayerMana == null) + return false; + + var args = new PlayerManaEventArgs + { + Player = player, + Data = data, + PlayerId = _plr, + Current = _cur, + Max = _max, + }; + PlayerMana.Invoke(null, args); + return args.Handled; + } + + /// + /// For use in a PlayerTeam event + /// + public class PlayerTeamEventArgs : GetDataHandledEventArgs + { + /// + /// The Terraria player ID of the player + /// + public byte PlayerId { get; set; } + /// + /// Enable/disable pvp? + /// + public byte Team { get; set; } + } + /// + /// TogglePvp - called when a player toggles pvp + /// + public static HandlerList PlayerTeam = new HandlerList(); + private static bool OnPlayerTeam(TSPlayer player, MemoryStream data, byte _id, byte _team) + { + if (PlayerTeam == null) + return false; + + var args = new PlayerTeamEventArgs + { + Player = player, + Data = data, + PlayerId = _id, + Team = _team, + }; + PlayerTeam.Invoke(null, args); + return args.Handled; + } + /// /// For use in a Sign event /// @@ -991,7 +1045,6 @@ namespace TShockAPI /// Sign - Called when a sign is changed /// public static HandlerList Sign = new HandlerList(); - private static bool OnSignEvent(TSPlayer player, MemoryStream data, short id, int x, int y) { if (Sign == null) @@ -1008,7 +1061,192 @@ namespace TShockAPI Sign.Invoke(null, args); return args.Handled; } + + /// + /// For use in a LiquidSet event + /// + public class LiquidSetEventArgs : GetDataHandledEventArgs + { + /// + /// X location of the tile + /// + public int TileX { get; set; } + /// + /// Y location of the tile + /// + public int TileY { get; set; } + /// + /// Amount of liquid + /// + public byte Amount { get; set; } + /// + /// Type of Liquid: 0=water, 1=lave, 2=honey + /// + public byte Type { get; set; } + } + /// + /// LiquidSet - When ever a liquid is set + /// + public static HandlerList LiquidSet = new HandlerList(); + private static bool OnLiquidSet(TSPlayer player, MemoryStream data, int tilex, int tiley, byte amount, byte type) + { + if (LiquidSet == null) + return false; + var args = new LiquidSetEventArgs + { + Player = player, + Data = data, + TileX = tilex, + TileY = tiley, + Amount = amount, + Type = type, + }; + LiquidSet.Invoke(null, args); + return args.Handled; + } + + /// + /// For use in a PlayerBuffUpdate event + /// + public class PlayerBuffUpdateEventArgs : GetDataHandledEventArgs + { + /// + /// The Terraria playerID of the player + /// + public byte ID { get; set; } + } + /// + /// PlayerBuffUpdate - Called when a player updates buffs + /// + public static HandlerList PlayerBuffUpdate = new HandlerList(); + private static bool OnPlayerBuffUpdate(TSPlayer player, MemoryStream data, byte id) + { + if (PlayerBuffUpdate == null) + return false; + + var args = new PlayerBuffUpdateEventArgs + { + Player = player, + Data = data, + ID = id, + }; + PlayerBuffUpdate.Invoke(null, args); + return args.Handled; + } + + /// + /// For use with a NPCSpecial event + /// + public class NPCSpecialEventArgs : GetDataHandledEventArgs + { + /// + /// ??? + /// + public byte ID { get; set; } + /// + /// Type...? + /// + public byte Type { get; set; } + } + /// + /// NPCSpecial - Called at some point + /// + public static HandlerList NPCSpecial = new HandlerList(); + private static bool OnNPCSpecial(TSPlayer player, MemoryStream data, byte id, byte type) + { + if (NPCSpecial == null) + return false; + + var args = new NPCSpecialEventArgs + { + Player = player, + Data = data, + ID = id, + Type = type, + }; + NPCSpecial.Invoke(null, args); + return args.Handled; + } + + /// + /// For use in a NPCAddBuff event + /// + public class NPCAddBuffEventArgs : GetDataHandledEventArgs + { + /// + /// The ID of the npc + /// + public short ID { get; set; } + /// + /// Buff Type + /// + public byte Type { get; set; } + /// + /// Time the buff lasts + /// + public short Time { get; set; } + } + /// + /// NPCAddBuff - Called when a npc is buffed + /// + public static HandlerList NPCAddBuff = new HandlerList(); + private static bool OnNPCAddBuff(TSPlayer player, MemoryStream data, short id, byte type, short time) + { + if (NPCAddBuff == null) + return false; + + var args = new NPCAddBuffEventArgs + { + Player = player, + Data = data, + ID = id, + Type = type, + Time = time + }; + NPCAddBuff.Invoke(null, args); + return args.Handled; + } + + /// + /// For use in a PlayerBuff event + /// + public class PlayerBuffEventArgs : GetDataHandledEventArgs + { + /// + /// The Terraria playerID of the player + /// + public byte ID { get; set; } + /// + /// Buff Type + /// + public byte Type { get; set; } + /// + /// Time the buff lasts + /// + public int Time { get; set; } + } + /// + /// PlayerBuff - Called when a player is buffed + /// + public static HandlerList PlayerBuff = new HandlerList(); + private static bool OnPlayerBuff(TSPlayer player, MemoryStream data, byte id, byte type, int time) + { + if (PlayerBuff == null) + return false; + + var args = new PlayerBuffEventArgs + { + Player = player, + Data = data, + ID = id, + Type = type, + Time = time + }; + PlayerBuff.Invoke(null, args); + return args.Handled; + } + /// /// For use in a NPCHome event /// @@ -1035,7 +1273,6 @@ namespace TShockAPI /// NPCHome - Called when an NPC's home is changed /// public static HandlerList NPCHome = new HandlerList(); - private static bool OnUpdateNPCHome(TSPlayer player, MemoryStream data, short id, short x, short y, byte homeless) { if (NPCHome == null) @@ -1053,145 +1290,397 @@ namespace TShockAPI NPCHome.Invoke(null, args); return args.Handled; } - + /// - /// For use in a NPCAddBuff event + /// For use with a PaintTile event /// - public class NPCAddBuffEventArgs : GetDataHandledEventArgs + public class PaintTileEventArgs : GetDataHandledEventArgs { /// - /// The ID of the npc + /// X Location /// - public short ID { get; set; } + public Int32 X { get; set; } /// - /// Buff Type + /// Y Location /// - public byte Type { get; set; } + public Int32 Y { get; set; } /// - /// Time the buff lasts + /// Type /// - public short Time { get; set; } + public byte type { get; set; } } /// - /// NPCAddBuff - Called when a npc is buffed + /// NPCStrike - Called when an NPC is attacked /// - public static HandlerList NPCAddBuff = new HandlerList(); - - private static bool OnNPCAddBuff(TSPlayer player, MemoryStream data, short id, byte type, short time) + public static HandlerList PaintTile = new HandlerList(); + private static bool OnPaintTile(TSPlayer player, MemoryStream data, Int32 x, Int32 y, byte t) { - if (NPCAddBuff == null) + if (PaintTile == null) return false; - var args = new NPCAddBuffEventArgs + var args = new PaintTileEventArgs { Player = player, Data = data, - ID = id, - Type = type, - Time = time + X = x, + Y = y, + type = t }; - NPCAddBuff.Invoke(null, args); + PaintTile.Invoke(null, args); return args.Handled; } /// - /// For use in a PlayerBuff event + /// For use with a PaintWall event /// - public class PlayerBuffEventArgs : GetDataHandledEventArgs + public class PaintWallEventArgs : GetDataHandledEventArgs { /// - /// The Terraria playerID of the player + /// X Location /// - public byte ID { get; set; } + public Int32 X { get; set; } /// - /// Buff Type + /// Y Location /// - public byte Type { get; set; } + public Int32 Y { get; set; } /// - /// Time the buff lasts + /// Type /// - public int Time { get; set; } + public byte type { get; set; } } /// - /// PlayerBuff - Called when a player is buffed + /// Called When a wall is painted /// - public static HandlerList PlayerBuff = new HandlerList(); - - private static bool OnPlayerBuff(TSPlayer player, MemoryStream data, byte id, byte type, int time) + public static HandlerList PaintWall = new HandlerList(); + private static bool OnPaintWall(TSPlayer player, MemoryStream data, Int32 x, Int32 y, byte t) { - if (PlayerBuff == null) + if (PaintWall == null) return false; - var args = new PlayerBuffEventArgs + var args = new PaintWallEventArgs { Player = player, Data = data, - ID = id, - Type = type, - Time = time + X = x, + Y = y, + type = t }; - PlayerBuff.Invoke(null, args); + PaintWall.Invoke(null, args); return args.Handled; } /// - /// For use in an ItemDrop event + /// For use with a NPCStrike event /// - public class ItemDropEventArgs : GetDataHandledEventArgs + public class TeleportEventArgs : GetDataHandledEventArgs { /// - /// ID of the item. - /// If below 400 and NetID(Type) is 0 Then Set Null. If ItemID is 400 Then New Item + /// ??? /// - public short ID { get; set; } + public Int16 ID { get; set; } /// - /// Position of the item + /// Flag is a bit field + /// if the first bit is set -> 0 = player, 1 = NPC + /// if the second bit is set, ignore this packet + /// if the third bit is set, style +1 + /// if the fourth bit is set, style +1 /// - public Vector2 Position { get; set; } + public byte Flag { get; set; } /// - /// Velocity at which the item is deployed + /// X Location /// - public Vector2 Velocity { get; set; } + public float X { get; set; } /// - /// Stacks - /// - public short Stacks { get; set; } - /// - /// Prefix of the item - /// - public byte Prefix { get; set; } - /// - /// No Delay on pickup - /// - public bool NoDelay { get; set; } - /// - /// Item type + /// Y Location /// + public float Y { get; set; } + } + /// + /// NPCStrike - Called when an NPC is attacked + /// + public static HandlerList Teleport = new HandlerList(); + private static bool OnTeleport(TSPlayer player, MemoryStream data, Int16 id, byte f, float x, float y) + { + if (Teleport == null) + return false; + + var args = new TeleportEventArgs + { + Player = player, + Data = data, + ID = id, + Flag = f, + X = x, + Y = y + }; + Teleport.Invoke(null, args); + return args.Handled; + } + + /// The event args object for the HealOtherPlayer event + public class HealOtherPlayerEventArgs : GetDataHandledEventArgs + { + /// The Terraria player index of the target player + public byte TargetPlayerIndex { get; set; } + + /// The amount to heal by + public short Amount { get; set; } + } + /// When a player heals another player + public static HandlerList HealOtherPlayer = new HandlerList(); + private static bool OnHealOtherPlayer(TSPlayer player, MemoryStream data, byte targetPlayerIndex, short amount) + { + if (HealOtherPlayer == null) + return false; + + var args = new HealOtherPlayerEventArgs + { + Player = player, + Data = data, + TargetPlayerIndex = targetPlayerIndex, + Amount = amount, + }; + + HealOtherPlayer.Invoke(null, args); + return args.Handled; + } + + /// The arguments to the PlaceObject hook. + public class PlaceObjectEventArgs : GetDataHandledEventArgs + { + /// The X location where the object was placed. + public short X { get; set; } + + /// The Y location where the object was placed. + public short Y { get; set; } + + /// The type of object that was placed. public short Type { get; set; } - } - /// - /// ItemDrop - Called when an item is dropped - /// - public static HandlerList ItemDrop = new HandlerList(); - private static bool OnItemDrop(TSPlayer player, MemoryStream data, short id, Vector2 pos, Vector2 vel, short stacks, byte prefix, bool noDelay, short type) + /// The style of the object was placed. + public short Style { get; set; } + + /// Alternate variation of the object placed. + public byte Alternate { get; set; } + + /// The direction the object was placed. + public bool Direction { get; set; } + } + /// Fired when an object is placed in the world. + public static HandlerList PlaceObject = new HandlerList(); + private static bool OnPlaceObject(TSPlayer player, MemoryStream data, short x, short y, short type, short style, byte alternate, bool direction) { - if (ItemDrop == null) + if (PlaceObject == null) return false; - var args = new ItemDropEventArgs + var args = new PlaceObjectEventArgs { Player = player, Data = data, - ID = id, - Position = pos, - Velocity = vel, - Stacks = stacks, - Prefix = prefix, - NoDelay = noDelay, + X = x, + Y = y, Type = type, + Style = style, + Alternate = alternate, + Direction = direction }; - ItemDrop.Invoke(null, args); + + PlaceObject.Invoke(null, args); + return args.Handled; + } + + /// For use in a PlaceTileEntity event. + public class PlaceTileEntityEventArgs : GetDataHandledEventArgs + { + /// The X coordinate of the event. + public short X { get; set; } + + /// The Y coordinate of the event. + public short Y { get; set; } + + /// The Type of event. + public byte Type { get; set; } + } + /// Fired when a PlaceTileEntity event occurs. + public static HandlerList PlaceTileEntity = new HandlerList(); + private static bool OnPlaceTileEntity(TSPlayer player, MemoryStream data, short x, short y, byte type) + { + if (PlaceTileEntity == null) + return false; + + var args = new PlaceTileEntityEventArgs + { + Player = player, + Data = data, + X = x, + Y = y, + Type = type + }; + + PlaceTileEntity.Invoke(null, args); + return args.Handled; + } + + /// The arguments to the PlaceItemFrame event. + public class PlaceItemFrameEventArgs : GetDataHandledEventArgs + { + /// The X coordinate of the item frame. + public short X { get; set; } + + /// The Y coordinate of the item frame. + public short Y { get; set; } + + /// The ItemID of the item frame. + public short ItemID { get; set; } + + /// The prefix. + public byte Prefix { get; set; } + + /// The stack. + public short Stack { get; set; } + + /// The ItemFrame object associated with this event. + public TEItemFrame ItemFrame { get; set; } + } + /// Fired when an ItemFrame is placed. + public static HandlerList PlaceItemFrame = new HandlerList(); + private static bool OnPlaceItemFrame(TSPlayer player, MemoryStream data, short x, short y, short itemID, byte prefix, short stack, TEItemFrame itemFrame) + { + if (PlaceItemFrame == null) + return false; + + var args = new PlaceItemFrameEventArgs + { + Player = player, + Data = data, + X = x, + Y = y, + ItemID = itemID, + Prefix = prefix, + Stack = stack, + ItemFrame = itemFrame, + }; + + PlaceItemFrame.Invoke(null, args); + return args.Handled; + } + + /// The event args object for the PortalTeleport event + public class TeleportThroughPortalEventArgs : GetDataHandledEventArgs + { + /// The Terraria player index of the target player + public byte TargetPlayerIndex { get; set; } + + /// + /// The position the target player will be at after going through the portal + /// + public Vector2 NewPosition { get; set; } + + /// + /// The velocity the target player will have after going through the portal + /// + public Vector2 NewVelocity { get; set; } + + /// + /// Index of the portal's color (for use with ) + /// + public int PortalColorIndex { get; set; } + } + /// When a player passes through a portal + public static HandlerList PortalTeleport = new HandlerList(); + private static bool OnPlayerTeleportThroughPortal(TSPlayer sender, byte targetPlayerIndex, MemoryStream data, Vector2 position, Vector2 velocity, int colorIndex) + { + TeleportThroughPortalEventArgs args = new TeleportThroughPortalEventArgs + { + TargetPlayerIndex = targetPlayerIndex, + Data = data, + Player = sender, + NewPosition = position, + NewVelocity = velocity, + PortalColorIndex = colorIndex + }; + + PortalTeleport.Invoke(null, args); + + return args.Handled; + } + + /// + /// For use with a ToggleGemLock event + /// + public class GemLockToggleEventArgs : GetDataHandledEventArgs + { + /// + /// X Location + /// + public short X { get; set; } + /// + /// Y Location + /// + public short Y { get; set; } + /// + /// On status + /// + public bool On { get; set; } + } + /// + /// GemLockToggle - Called when a gem lock is switched + /// + public static HandlerList GemLockToggle = new HandlerList(); + private static bool OnGemLockToggle(TSPlayer player, MemoryStream data, short x, short y, bool on) + { + if (GemLockToggle == null) + return false; + + var args = new GemLockToggleEventArgs + { + Player = player, + Data = data, + X = x, + Y = y, + On = on + }; + GemLockToggle.Invoke(null, args); + return args.Handled; + } + + /// The arguments to the MassWireOperation event. + public class MassWireOperationEventArgs : GetDataHandledEventArgs + { + /// The start X point in the operation. + public short StartX { get; set; } + + /// The start Y point in the operation. + public short StartY { get; set; } + + /// The end X point in the operation. + public short EndX { get; set; } + + /// The end Y point in the operation. + public short EndY { get; set; } + + /// ToolMode + public byte ToolMode { get; set; } + } + /// Fired on a mass wire edit operation. + public static HandlerList MassWireOperation = new HandlerList(); + private static bool OnMassWireOperation(TSPlayer player, MemoryStream data, short startX, short startY, short endX, short endY, byte toolMode) + { + if (MassWireOperation == null) + return false; + + var args = new MassWireOperationEventArgs + { + Player = player, + Data = data, + StartX = startX, + StartY = startY, + EndX = endX, + EndY = endY, + ToolMode = toolMode, + }; + + MassWireOperation.Invoke(null, args); return args.Handled; } @@ -1227,7 +1716,6 @@ namespace TShockAPI /// PlayerDamage - Called when a player is damaged /// public static HandlerList PlayerDamage = new HandlerList(); - private static bool OnPlayerDamage(TSPlayer player, MemoryStream data, byte id, byte dir, short dmg, bool pvp, bool crit, PlayerDeathReason playerDeathReason) { if (PlayerDamage == null) @@ -1249,535 +1737,54 @@ namespace TShockAPI } /// - /// For use with a NPCStrike event + /// For use in a KillMe event /// - public class NPCStrikeEventArgs : GetDataHandledEventArgs - { - /// - /// ??? - /// - public short ID { get; set; } - /// - /// Direction the damage occurred from - /// - public byte Direction { get; set; } - /// - /// Amount of damage - /// - public short Damage { get; set; } - /// - /// Knockback - /// - public float Knockback { get; set; } - /// - /// Critical? - /// - public byte Critical { get; set; } - } - /// - /// NPCStrike - Called when an NPC is attacked - /// - public static HandlerList NPCStrike = new HandlerList(); - - private static bool OnNPCStrike(TSPlayer player, MemoryStream data, short id, byte dir, short dmg, float knockback, byte crit) - { - if (NPCStrike == null) - return false; - - var args = new NPCStrikeEventArgs - { - Player = player, - Data = data, - ID = id, - Direction = dir, - Damage = dmg, - Knockback = knockback, - Critical = crit, - }; - NPCStrike.Invoke(null, args); - return args.Handled; - } - - /// The arguments to the MassWireOperation event. - public class MassWireOperationEventArgs : GetDataHandledEventArgs - { - /// The start X point in the operation. - public short StartX { get; set; } - - /// The start Y point in the operation. - public short StartY { get; set; } - - /// The end X point in the operation. - public short EndX { get; set; } - - /// The end Y point in the operation. - public short EndY { get; set; } - - /// ToolMode - public byte ToolMode { get; set; } - } - - /// Fired on a mass wire edit operation. - public static HandlerList MassWireOperation = new HandlerList(); - - private static bool OnMassWireOperation(TSPlayer player, MemoryStream data, short startX, short startY, short endX, short endY, byte toolMode) - { - if (MassWireOperation == null) - return false; - - var args = new MassWireOperationEventArgs - { - Player = player, - Data = data, - StartX = startX, - StartY = startY, - EndX = endX, - EndY = endY, - ToolMode = toolMode, - }; - - MassWireOperation.Invoke(null, args); - return args.Handled; - } - - /// For use in a PlaceTileEntity event. - public class PlaceTileEntityEventArgs : GetDataHandledEventArgs - { - /// The X coordinate of the event. - public short X { get; set; } - - /// The Y coordinate of the event. - public short Y { get; set; } - - /// The Type of event. - public byte Type { get; set; } - } - - /// Fired when a PlaceTileEntity event occurs. - public static HandlerList PlaceTileEntity = new HandlerList(); - - private static bool OnPlaceTileEntity(TSPlayer player, MemoryStream data, short x, short y, byte type) - { - if (PlaceTileEntity == null) - return false; - - var args = new PlaceTileEntityEventArgs - { - Player = player, - Data = data, - X = x, - Y = y, - Type = type - }; - - PlaceTileEntity.Invoke(null, args); - return args.Handled; - } - - /// - /// For use with a NPCSpecial event - /// - public class NPCSpecialEventArgs : GetDataHandledEventArgs - { - /// - /// ??? - /// - public byte ID { get; set; } - /// - /// Type...? - /// - public byte Type { get; set; } - } - /// - /// NPCSpecial - Called at some point - /// - public static HandlerList NPCSpecial = new HandlerList(); - - private static bool OnNPCSpecial(TSPlayer player, MemoryStream data, byte id, byte type) - { - if (NPCSpecial == null) - return false; - - var args = new NPCSpecialEventArgs - { - Player = player, - Data = data, - ID = id, - Type = type, - }; - NPCSpecial.Invoke(null, args); - return args.Handled; - } - - /// - /// For use with a PlayerAnimation event - /// - public class PlayerAnimationEventArgs : GetDataHandledEventArgs { } - - /// - /// PlayerAnimation - Called when a player animates - /// - public static HandlerList PlayerAnimation = new HandlerList(); - - private static bool OnPlayerAnimation(TSPlayer player, MemoryStream data) - { - if (PlayerAnimation == null) - return false; - - var args = new PlayerAnimationEventArgs - { - Player = player, - Data = data, - }; - PlayerAnimation.Invoke(null, args); - return args.Handled; - } - - /// - /// For use in a PlayerBuffUpdate event - /// - public class PlayerBuffUpdateEventArgs : GetDataHandledEventArgs + public class KillMeEventArgs : GetDataHandledEventArgs { /// /// The Terraria playerID of the player /// - public byte ID { get; set; } + public byte PlayerId { get; set; } + /// + /// The direction the damage is coming from (?) + /// + public byte Direction { get; set; } + /// + /// Amount of damage delt + /// + public short Damage { get; set; } + /// + /// Player's current pvp setting + /// + public bool Pvp { get; set; } + /// The reason the player died. + public PlayerDeathReason PlayerDeathReason { get; set; } } /// - /// PlayerBuffUpdate - Called when a player updates buffs + /// KillMe - Terraria's crappy way of handling damage from players /// - public static HandlerList PlayerBuffUpdate = new HandlerList(); - - private static bool OnPlayerBuffUpdate(TSPlayer player, MemoryStream data, byte id) + public static HandlerList KillMe = new HandlerList(); + private static bool OnKillMe(TSPlayer player, MemoryStream data, byte plr, byte direction, short damage, bool pvp, PlayerDeathReason playerDeathReason) { - if (PlayerBuffUpdate == null) + if (KillMe == null) return false; - var args = new PlayerBuffUpdateEventArgs + var args = new KillMeEventArgs { Player = player, Data = data, - ID = id, + PlayerId = plr, + Direction = direction, + Damage = damage, + Pvp = pvp, + PlayerDeathReason = playerDeathReason, }; - PlayerBuffUpdate.Invoke(null, args); - return args.Handled; - } - - /// - /// For use with a NPCStrike event - /// - public class TeleportEventArgs : GetDataHandledEventArgs - { - /// - /// ??? - /// - public Int16 ID { get; set; } - /// - /// Flag is a bit field - /// if the first bit is set -> 0 = player, 1 = NPC - /// if the second bit is set, ignore this packet - /// if the third bit is set, style +1 - /// if the fourth bit is set, style +1 - /// - public byte Flag { get; set; } - /// - /// X Location - /// - public float X { get; set; } - /// - /// Y Location - /// - public float Y { get; set; } - } - /// - /// NPCStrike - Called when an NPC is attacked - /// - public static HandlerList Teleport = new HandlerList(); - - private static bool OnTeleport(TSPlayer player, MemoryStream data, Int16 id, byte f, float x, float y) - { - if (Teleport == null) - return false; - - var args = new TeleportEventArgs - { - Player = player, - Data = data, - ID = id, - Flag = f, - X = x, - Y = y - }; - Teleport.Invoke(null, args); + KillMe.Invoke(null, args); return args.Handled; } #endregion - public static void InitGetDataHandler() - { - #region Blacklists - - WhitelistBuffMaxTime = new int[Main.maxBuffTypes]; - WhitelistBuffMaxTime[20] = 600; - WhitelistBuffMaxTime[0x18] = 1200; - WhitelistBuffMaxTime[0x1f] = 120; - WhitelistBuffMaxTime[0x27] = 420; - - #endregion Blacklists - - GetDataHandlerDelegates = new Dictionary - { - { PacketTypes.PlayerInfo, HandlePlayerInfo }, - { PacketTypes.PlayerUpdate, HandlePlayerUpdate }, - { PacketTypes.Zones, HandlePlayerZone }, - { PacketTypes.Tile, HandleTile }, - { PacketTypes.PlaceObject, HandlePlaceObject }, - { PacketTypes.TileSendSquare, HandleSendTileSquare }, - { PacketTypes.ProjectileNew, HandleProjectileNew }, - { PacketTypes.TogglePvp, HandleTogglePvp }, - { PacketTypes.PlayerTeam, HandlePlayerTeam }, - { PacketTypes.PlaceChest, HandlePlaceChest }, - { PacketTypes.LiquidSet, HandleLiquidSet }, - { PacketTypes.PlayerSpawn, HandleSpawn }, - { PacketTypes.ChestGetContents, HandleChestOpen }, - { PacketTypes.ChestOpen, HandleChestActive }, - { PacketTypes.ChestItem, HandleChestItem }, - { PacketTypes.SignNew, HandleSign }, - { PacketTypes.PlayerSlot, HandlePlayerSlot }, - { PacketTypes.TileGetSection, HandleGetSection }, - { PacketTypes.UpdateNPCHome, UpdateNPCHome }, - { PacketTypes.NpcAddBuff, HandleNPCAddBuff }, - { PacketTypes.PlayerAddBuff, HandlePlayerAddBuff }, - { PacketTypes.ItemDrop, HandleItemDrop }, - { PacketTypes.UpdateItemDrop, HandleItemDrop }, - { PacketTypes.ItemOwner, HandleItemOwner }, - { PacketTypes.PlayerHp, HandlePlayerHp }, - { PacketTypes.PlayerMana, HandlePlayerMana }, - { PacketTypes.NpcStrike, HandleNpcStrike }, - { PacketTypes.NpcSpecial, HandleSpecial }, - { PacketTypes.PlayerAnimation, HandlePlayerAnimation }, - { PacketTypes.PlayerBuff, HandlePlayerBuffList }, - { PacketTypes.PasswordSend, HandlePassword }, - { PacketTypes.ContinueConnecting2, HandleConnecting }, - { PacketTypes.ProjectileDestroy, HandleProjectileKill }, - { PacketTypes.SpawnBossorInvasion, HandleSpawnBoss }, - { PacketTypes.Teleport, HandleTeleport }, - { PacketTypes.PaintTile, HandlePaintTile }, - { PacketTypes.PaintWall, HandlePaintWall }, - { PacketTypes.DoorUse, HandleDoorUse }, - { PacketTypes.CompleteAnglerQuest, HandleCompleteAnglerQuest }, - { PacketTypes.NumberOfAnglerQuestsCompleted, HandleNumberOfAnglerQuestsCompleted }, - { PacketTypes.MassWireOperation, HandleMassWireOperation }, - { PacketTypes.GemLockToggle, HandleGemLockToggle }, - { PacketTypes.CatchNPC, HandleCatchNpc }, - { PacketTypes.NpcTeleportPortal, HandleNpcTeleportPortal }, - { PacketTypes.KillPortal, HandleKillPortal }, - { PacketTypes.PlaceTileEntity, HandlePlaceTileEntity }, - { PacketTypes.PlaceItemFrame, HandlePlaceItemFrame }, - { PacketTypes.SyncExtraValue, HandleSyncExtraValue }, - { PacketTypes.LoadNetModule, HandleLoadNetModule }, - { PacketTypes.ToggleParty, HandleToggleParty }, - { PacketTypes.PlayerHealOther, HandleHealOther }, - { PacketTypes.CrystalInvasionStart, HandleOldOnesArmy }, - { PacketTypes.PlayerHurtV2, HandlePlayerDamageV2 }, - { PacketTypes.PlayerDeathV2, HandlePlayerKillMeV2 }, - { PacketTypes.PlayerTeleportPortal, HandlePlayerPortalTeleport } - }; - } - - public static bool HandlerGetData(PacketTypes type, TSPlayer player, MemoryStream data) - { - GetDataHandlerDelegate handler; - if (GetDataHandlerDelegates.TryGetValue(type, out handler)) - { - try - { - return handler(new GetDataHandlerArgs(player, data)); - } - catch (Exception ex) - { - TShock.Log.Error(ex.ToString()); - return true; - } - } - return false; - } - - /// The event args object for the PortalTeleport event - public class TeleportThroughPortalEventArgs : GetDataHandledEventArgs - { - /// The Terraria player index of the target player - public byte TargetPlayerIndex { get; set; } - - /// - /// The position the target player will be at after going through the portal - /// - public Vector2 NewPosition { get; set; } - - /// - /// The velocity the target player will have after going through the portal - /// - public Vector2 NewVelocity { get; set; } - - /// - /// Index of the portal's color (for use with ) - /// - public int PortalColorIndex { get; set; } - } - - /// When a player passes through a portal - public static HandlerList PortalTeleport = new HandlerList(); - - private static bool OnPlayerTeleportThroughPortal(TSPlayer sender, byte targetPlayerIndex, MemoryStream data, Vector2 position, Vector2 velocity, int colorIndex) - { - TeleportThroughPortalEventArgs args = new TeleportThroughPortalEventArgs - { - TargetPlayerIndex = targetPlayerIndex, - Data = data, - Player = sender, - NewPosition = position, - NewVelocity = velocity, - PortalColorIndex = colorIndex - }; - - PortalTeleport.Invoke(null, args); - - return args.Handled; - } - - private static bool HandlePlayerPortalTeleport(GetDataHandlerArgs args) - { - byte plr = args.Data.ReadInt8(); - short portalColorIndex = args.Data.ReadInt16(); - float newPositionX = args.Data.ReadSingle(); - float newPositionY = args.Data.ReadSingle(); - float newVelocityX = args.Data.ReadSingle(); - float newVelocityY = args.Data.ReadSingle(); - - return OnPlayerTeleportThroughPortal( - args.Player, - plr, - args.Data, - new Vector2(newPositionX, newPositionY), - new Vector2(newVelocityX, newVelocityY), - portalColorIndex - ); - } - - private static bool HandleHealOther(GetDataHandlerArgs args) - { - byte plr = args.Data.ReadInt8(); - short amount = args.Data.ReadInt16(); - - if (OnHealOtherPlayer(args.Player, args.Data, plr, amount)) - return true; - - return false; - } - - private static bool HandlePlayerSlot(GetDataHandlerArgs args) - { - byte plr = args.Data.ReadInt8(); - byte slot = args.Data.ReadInt8(); - short stack = args.Data.ReadInt16(); - byte prefix = args.Data.ReadInt8(); - short type = args.Data.ReadInt16(); - - // Players send a slot update packet for each inventory slot right after they've joined. - bool bypassTrashCanCheck = false; - if (plr == args.Player.Index && !args.Player.HasSentInventory && slot == NetItem.MaxInventory) - { - args.Player.HasSentInventory = true; - bypassTrashCanCheck = true; - } - - if (OnPlayerSlot(args.Player, args.Data, plr, slot, stack, prefix, type) || plr != args.Player.Index || slot < 0 || - slot > NetItem.MaxInventory) - return true; - if (args.Player.IgnoreSSCPackets) - { - args.Player.SendData(PacketTypes.PlayerSlot, "", args.Player.Index, slot, prefix); - return true; - } - - // Garabage? Or will it cause some internal initialization or whatever? - var item = new Item(); - item.netDefaults(type); - item.Prefix(prefix); - - if (args.Player.IsLoggedIn) - { - args.Player.PlayerData.StoreSlot(slot, type, prefix, stack); - } - else if (Main.ServerSideCharacter && TShock.Config.DisableLoginBeforeJoin && !bypassTrashCanCheck && - 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.IsDisabledPendingTrashRemoval = true; - } - - if (slot == 58) //this is the hand - { - item.stack = stack; - args.Player.ItemInHand = item; - } - - return false; - } - - public static bool HandlePlayerHp(GetDataHandlerArgs args) - { - var plr = args.Data.ReadInt8(); - var cur = args.Data.ReadInt16(); - var max = args.Data.ReadInt16(); - - if (OnPlayerHP(args.Player, args.Data, plr, cur, max) || cur <= 0 || max <= 0 || args.Player.IgnoreSSCPackets) - return true; - - if (max > TShock.Config.MaxHP && !args.Player.HasPermission(Permissions.ignorehp)) - { - args.Player.Disable("Maximum HP beyond limit", DisableFlags.WriteToLogAndConsole); - return true; - } - - if (args.Player.IsLoggedIn) - { - args.Player.TPlayer.statLife = cur; - args.Player.TPlayer.statLifeMax = max; - args.Player.PlayerData.maxHealth = max; - } - - if (args.Player.GodMode && (cur < max)) - { - args.Player.Heal(args.TPlayer.statLifeMax2); - } - return false; - } - - private static bool HandlePlayerMana(GetDataHandlerArgs args) - { - var plr = args.Data.ReadInt8(); - var cur = args.Data.ReadInt16(); - var max = args.Data.ReadInt16(); - - if (OnPlayerMana(args.Player, args.Data, plr, cur, max) || cur < 0 || max < 0 || args.Player.IgnoreSSCPackets) - return true; - - if (max > TShock.Config.MaxMP && !args.Player.HasPermission(Permissions.ignoremp)) - { - args.Player.Disable("Maximum MP beyond limit", DisableFlags.WriteToLogAndConsole); - return true; - } - - if (args.Player.IsLoggedIn) - { - args.Player.TPlayer.statMana = cur; - args.Player.TPlayer.statManaMax = max; - args.Player.PlayerData.maxMana = max; - } - return false; - } - + private static bool HandlePlayerInfo(GetDataHandlerArgs args) { byte playerid = args.Data.ReadInt8(); @@ -1863,6 +1870,56 @@ namespace TShockAPI return false; } + private static bool HandlePlayerSlot(GetDataHandlerArgs args) + { + byte plr = args.Data.ReadInt8(); + byte slot = args.Data.ReadInt8(); + short stack = args.Data.ReadInt16(); + byte prefix = args.Data.ReadInt8(); + short type = args.Data.ReadInt16(); + + // Players send a slot update packet for each inventory slot right after they've joined. + bool bypassTrashCanCheck = false; + if (plr == args.Player.Index && !args.Player.HasSentInventory && slot == NetItem.MaxInventory) + { + args.Player.HasSentInventory = true; + bypassTrashCanCheck = true; + } + + if (OnPlayerSlot(args.Player, args.Data, plr, slot, stack, prefix, type) || plr != args.Player.Index || slot < 0 || + slot > NetItem.MaxInventory) + return true; + if (args.Player.IgnoreSSCPackets) + { + args.Player.SendData(PacketTypes.PlayerSlot, "", args.Player.Index, slot, prefix); + return true; + } + + // Garabage? Or will it cause some internal initialization or whatever? + var item = new Item(); + item.netDefaults(type); + item.Prefix(prefix); + + if (args.Player.IsLoggedIn) + { + args.Player.PlayerData.StoreSlot(slot, type, prefix, stack); + } + else if (Main.ServerSideCharacter && TShock.Config.DisableLoginBeforeJoin && !bypassTrashCanCheck && + 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.IsDisabledPendingTrashRemoval = true; + } + + if (slot == 58) //this is the hand + { + item.stack = stack; + args.Player.ItemInHand = item; + } + + return false; + } + private static bool HandleConnecting(GetDataHandlerArgs args) { var account = TShock.UserAccounts.GetUserAccountByName(args.Player.Name); @@ -1928,126 +1985,14 @@ namespace TShockAPI NetMessage.SendData((int)PacketTypes.WorldInfo, args.Player.Index); return true; } - - private static bool HandlePassword(GetDataHandlerArgs args) - { - if (!args.Player.RequiresPassword) - return true; - - string password = args.Data.ReadString(); - - if (Hooks.PlayerHooks.OnPlayerPreLogin(args.Player, args.Player.Name, password)) - return true; - - var account = TShock.UserAccounts.GetUserAccountByName(args.Player.Name); - if (account != null && !TShock.Config.DisableLoginBeforeJoin) - { - if (account.VerifyPassword(password)) - { - args.Player.RequiresPassword = false; - args.Player.PlayerData = TShock.CharacterDB.GetPlayerData(args.Player, account.ID); - - if (args.Player.State == 1) - args.Player.State = 2; - NetMessage.SendData((int)PacketTypes.WorldInfo, args.Player.Index); - - var group = TShock.Groups.GetGroupByName(account.Group); - - args.Player.Group = group; - args.Player.tempGroup = null; - args.Player.Account = account; - args.Player.IsLoggedIn = true; - args.Player.IsDisabledForSSC = false; - - if (Main.ServerSideCharacter) - { - if (args.Player.HasPermission(Permissions.bypassssc)) - { - args.Player.PlayerData.CopyCharacter(args.Player); - TShock.CharacterDB.InsertPlayerData(args.Player); - } - args.Player.PlayerData.RestoreCharacter(args.Player); - } - args.Player.LoginFailsBySsi = false; - - if (args.Player.HasPermission(Permissions.ignorestackhackdetection)) - args.Player.IsDisabledForStackDetection = false; - - if (args.Player.HasPermission(Permissions.usebanneditem)) - args.Player.IsDisabledForBannedWearable = false; - - - args.Player.SendMessage("Authenticated as " + args.Player.Name + " successfully.", Color.LimeGreen); - TShock.Log.ConsoleInfo(args.Player.Name + " authenticated successfully as user " + args.Player.Name + "."); - TShock.UserAccounts.SetUserAccountUUID(account, args.Player.UUID); - Hooks.PlayerHooks.OnPlayerPostLogin(args.Player); - return true; - } - args.Player.Kick("Your password did not match this character's password.", true, true); - return true; - } - - if (!string.IsNullOrEmpty(TShock.Config.ServerPassword)) - { - if (TShock.Config.ServerPassword == password) - { - args.Player.RequiresPassword = false; - if (args.Player.State == 1) - args.Player.State = 2; - NetMessage.SendData((int)PacketTypes.WorldInfo, args.Player.Index); - return true; - } - args.Player.Kick("Invalid server password.", true, true); - return true; - } - - args.Player.Kick("You have been Bounced.", true, true); - return true; - } - - /// The arguments to a GetSection packet. - public class GetSectionEventArgs : GetDataHandledEventArgs - { - /// The X position requested. Or -1 for spawn. - public int X { get; set; } - - /// The Y position requested. Or -1 for spawn. - public int Y { get; set; } - } - - /// The hook for a GetSection event. - public static HandlerList GetSection = new HandlerList(); - - /// Fires a GetSection event. - /// The TSPlayer that caused the GetSection. - /// The raw MP protocol data. - /// The x coordinate requested or -1 for spawn. - /// The y coordinate requested or -1 for spawn. - /// bool - private static bool OnGetSection(TSPlayer player, MemoryStream data, int x, int y) - { - if (GetSection == null) - return false; - - var args = new GetSectionEventArgs - { - Player = player, - Data = data, - X = x, - Y = y, - }; - - GetSection.Invoke(null, args); - return args.Handled; - } - + private static bool HandleGetSection(GetDataHandlerArgs args) { if (OnGetSection(args.Player, args.Data, args.Data.ReadInt32(), args.Data.ReadInt32())) return true; if (TShock.Utils.GetActivePlayerCount() + 1 > TShock.Config.MaxSlots && - !args.Player.HasPermission(Permissions.reservedslot)) + !args.Player.HasPermission(Permissions.reservedslot)) { args.Player.Kick(TShock.Config.ServerFullReason, true, true); return true; @@ -2056,259 +2001,33 @@ namespace TShockAPI NetMessage.SendData((int)PacketTypes.TimeSet, -1, -1, NetworkText.Empty, Main.dayTime ? 1 : 0, (int)Main.time, Main.sunModY, Main.moonModY); return false; } - - private static bool HandleSendTileSquare(GetDataHandlerArgs args) + + private static bool HandleSpawn(GetDataHandlerArgs args) { - var player = args.Player; - var size = args.Data.ReadInt16(); - var tileX = args.Data.ReadInt16(); - var tileY = args.Data.ReadInt16(); - var data = args.Data; + var player = args.Data.ReadInt8(); + var spawnx = args.Data.ReadInt16(); + var spawny = args.Data.ReadInt16(); - if (OnSendTileSquare(player, data, size, tileX, tileY)) + if (OnPlayerSpawn(args.Player, args.Data, player, spawnx, spawny)) return true; - return false; - } - - public enum EditAction - { - KillTile = 0, - PlaceTile, - KillWall, - PlaceWall, - KillTileNoItem, - PlaceWire, - KillWire, - PoundTile, - PlaceActuator, - KillActuator, - PlaceWire2, - KillWire2, - PlaceWire3, - KillWire3, - SlopeTile, - FrameTrack, - PlaceWire4, - KillWire4 - } - public enum EditType - { - Fail = 0, - Type, - Slope, - } - - /// - /// Tiles that can be broken without any pickaxes/etc. - /// - internal static int[] breakableTiles = new int[] - { - TileID.Books, - TileID.Bottles, - TileID.BreakableIce, - TileID.Candles, - TileID.CorruptGrass, - TileID.Dirt, - TileID.FleshGrass, - TileID.Grass, - TileID.HallowedGrass, - TileID.MagicalIceBlock, - TileID.Mannequin, - TileID.Torches, - TileID.WaterCandle, - TileID.Womannequin, - }; - /// - /// The maximum place styles for each tile. - /// - public static Dictionary MaxPlaceStyles = new Dictionary(); - /// - /// These projectiles create tiles on death. - /// - internal static Dictionary projectileCreatesTile = new Dictionary - { - { ProjectileID.DirtBall, TileID.Dirt }, - { ProjectileID.SandBallGun, TileID.Sand }, - { ProjectileID.EbonsandBallGun, TileID.Ebonsand }, - { ProjectileID.PearlSandBallGun, TileID.Pearlsand }, - { ProjectileID.CrimsandBallGun, TileID.Crimsand }, - }; - - internal static Dictionary ropeCoilPlacements = new Dictionary - { - {ItemID.RopeCoil, TileID.Rope}, - {ItemID.SilkRopeCoil, TileID.SilkRope}, - {ItemID.VineRopeCoil, TileID.VineRope}, - {ItemID.WebRopeCoil, TileID.WebRope} - }; - - /// - /// Extra place style limits for strange hardcoded values in Terraria - /// - internal static Dictionary ExtraneousPlaceStyles = new Dictionary - { - {TileID.MinecartTrack, 3} - }; - - private static bool HandleTile(GetDataHandlerArgs args) - { - EditAction action = (EditAction)args.Data.ReadInt8(); - var tileX = args.Data.ReadInt16(); - var tileY = args.Data.ReadInt16(); - var editData = args.Data.ReadInt16(); - EditType type = (action == EditAction.KillTile || action == EditAction.KillWall || - action == EditAction.KillTileNoItem) - ? EditType.Fail - : (action == EditAction.PlaceTile || action == EditAction.PlaceWall) - ? EditType.Type - : EditType.Slope; - - var style = args.Data.ReadInt8(); - - if (OnTileEdit(args.Player, args.Data, tileX, tileY, action, type, editData, style)) - return true; - - return false; - } - - /// - /// Handle PlaceObject event - /// - private static bool HandlePlaceObject(GetDataHandlerArgs args) - { - short x = args.Data.ReadInt16(); - short y = args.Data.ReadInt16(); - short type = args.Data.ReadInt16(); - short style = args.Data.ReadInt16(); - byte alternate = args.Data.ReadInt8(); - bool direction = args.Data.ReadBoolean(); - - if (OnPlaceObject(args.Player, args.Data, x, y, type, style, alternate, direction)) - return true; - - return false; - } - - /// - /// For use with a PaintTile event - /// - public class PaintTileEventArgs : GetDataHandledEventArgs - { - /// - /// X Location - /// - public Int32 X { get; set; } - /// - /// Y Location - /// - public Int32 Y { get; set; } - /// - /// Type - /// - public byte type { get; set; } - } - /// - /// NPCStrike - Called when an NPC is attacked - /// - public static HandlerList PaintTile = new HandlerList(); - - private static bool OnPaintTile(TSPlayer player, MemoryStream data, Int32 x, Int32 y, byte t) - { - if (PaintTile == null) - return false; - - var args = new PaintTileEventArgs + if ((Main.ServerSideCharacter) && (args.Player.sX > 0) && (args.Player.sY > 0) && (args.TPlayer.SpawnX > 0) && ((args.TPlayer.SpawnX != args.Player.sX) && (args.TPlayer.SpawnY != args.Player.sY))) { - Player = player, - Data = data, - X = x, - Y = y, - type = t - }; - PaintTile.Invoke(null, args); - return args.Handled; - } + args.Player.sX = args.TPlayer.SpawnX; + args.Player.sY = args.TPlayer.SpawnY; - /// - /// For use with a PaintWall event - /// - public class PaintWallEventArgs : GetDataHandledEventArgs - { - /// - /// X Location - /// - public Int32 X { get; set; } - /// - /// Y Location - /// - public Int32 Y { get; set; } - /// - /// Type - /// - public byte type { get; set; } - } - /// - /// Called When a wall is painted - /// - public static HandlerList PaintWall = new HandlerList(); - - private static bool OnPaintWall(TSPlayer player, MemoryStream data, Int32 x, Int32 y, byte t) - { - if (PaintWall == null) - return false; - - var args = new PaintWallEventArgs - { - Player = player, - Data = data, - X = x, - Y = y, - type = t - }; - PaintWall.Invoke(null, args); - return args.Handled; - } - - private static bool HandleTogglePvp(GetDataHandlerArgs args) - { - byte id = args.Data.ReadInt8(); - bool pvp = args.Data.ReadBoolean(); - if (OnPvpToggled(args.Player, args.Data, id, pvp)) - return true; - - if (id != args.Player.Index) - return true; - - string pvpMode = TShock.Config.PvPMode.ToLowerInvariant(); - if (pvpMode == "disabled" || pvpMode == "always" || (DateTime.UtcNow - args.Player.LastPvPTeamChange).TotalSeconds < 5) - { - args.Player.SendData(PacketTypes.TogglePvp, "", id); - return true; + if (((Main.tile[args.Player.sX, args.Player.sY - 1].active() && Main.tile[args.Player.sX, args.Player.sY - 1].type == 79)) && (WorldGen.StartRoomCheck(args.Player.sX, args.Player.sY - 1))) + args.Player.Teleport(args.Player.sX * 16, (args.Player.sY * 16) - 48); } - args.Player.LastPvPTeamChange = DateTime.UtcNow; - return false; - } - - private static bool HandlePlayerTeam(GetDataHandlerArgs args) - { - byte id = args.Data.ReadInt8(); - byte team = args.Data.ReadInt8(); - if (OnPlayerTeam(args.Player, args.Data, id, team)) - return true; - - if (id != args.Player.Index) - return true; - - if ((DateTime.UtcNow - args.Player.LastPvPTeamChange).TotalSeconds < 5) + else if ((Main.ServerSideCharacter) && (args.Player.sX > 0) && (args.Player.sY > 0)) { - args.Player.SendData(PacketTypes.PlayerTeam, "", id); - return true; + if (((Main.tile[args.Player.sX, args.Player.sY - 1].active() && Main.tile[args.Player.sX, args.Player.sY - 1].type == 79)) && (WorldGen.StartRoomCheck(args.Player.sX, args.Player.sY - 1))) + args.Player.Teleport(args.Player.sX * 16, (args.Player.sY * 16) - 48); } - args.Player.LastPvPTeamChange = DateTime.UtcNow; + args.Player.Dead = false; return false; } @@ -2453,241 +2172,78 @@ namespace TShockAPI return true; } - private static bool HandlePlayerZone(GetDataHandlerArgs args) + private static bool HandlePlayerHp(GetDataHandlerArgs args) { - if (args.Player == null || args.TPlayer == null || args.Data == null) - { - return true; - } - var plr = args.Data.ReadInt8(); - BitsByte zone1 = args.Data.ReadInt8(); - BitsByte zone2 = args.Data.ReadInt8(); - BitsByte zone3 = args.Data.ReadInt8(); - BitsByte zone4 = args.Data.ReadInt8(); + var cur = args.Data.ReadInt16(); + var max = args.Data.ReadInt16(); - if (OnPlayerZone(args.Player, args.Data, plr, zone1, zone2, zone3, zone4)) + if (OnPlayerHP(args.Player, args.Data, plr, cur, max) || cur <= 0 || max <= 0 || args.Player.IgnoreSSCPackets) return true; - + + if (max > TShock.Config.MaxHP && !args.Player.HasPermission(Permissions.ignorehp)) + { + args.Player.Disable("Maximum HP beyond limit", DisableFlags.WriteToLogAndConsole); + return true; + } + + if (args.Player.IsLoggedIn) + { + args.Player.TPlayer.statLife = cur; + args.Player.TPlayer.statLifeMax = max; + args.Player.PlayerData.maxHealth = max; + } + + if (args.Player.GodMode && (cur < max)) + { + args.Player.Heal(args.TPlayer.statLifeMax2); + } return false; } - private static bool HandleProjectileNew(GetDataHandlerArgs args) + private static bool HandleTile(GetDataHandlerArgs args) { - short ident = args.Data.ReadInt16(); - var pos = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle()); - var vel = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle()); - float knockback = args.Data.ReadSingle(); - short dmg = args.Data.ReadInt16(); - byte owner = args.Data.ReadInt8(); - short type = args.Data.ReadInt16(); - BitsByte bits = args.Data.ReadInt8(); - //owner = (byte)args.Player.Index; - float[] ai = new float[Projectile.maxAI]; + EditAction action = (EditAction)args.Data.ReadInt8(); + var tileX = args.Data.ReadInt16(); + var tileY = args.Data.ReadInt16(); + var editData = args.Data.ReadInt16(); + EditType type = (action == EditAction.KillTile || action == EditAction.KillWall || + action == EditAction.KillTileNoItem) + ? EditType.Fail + : (action == EditAction.PlaceTile || action == EditAction.PlaceWall) + ? EditType.Type + : EditType.Slope; - for (int i = 0; i < Projectile.maxAI; i++) - { - if (bits[i]) - ai[i] = args.Data.ReadSingle(); - else - ai[i] = 0f; - } + var style = args.Data.ReadInt8(); - var index = TShock.Utils.SearchProjectile(ident, owner); - - if (OnNewProjectile(args.Data, ident, pos, vel, knockback, dmg, owner, type, index, args.Player)) + if (OnTileEdit(args.Player, args.Data, tileX, tileY, action, type, editData, style)) return true; return false; } - private static bool HandleProjectileKill(GetDataHandlerArgs args) + private static bool HandleDoorUse(GetDataHandlerArgs args) { - var ident = args.Data.ReadInt16(); - var owner = args.Data.ReadInt8(); - owner = (byte)args.Player.Index; - var index = TShock.Utils.SearchProjectile(ident, owner); + byte type = (byte)args.Data.ReadByte(); + short x = args.Data.ReadInt16(); + short y = args.Data.ReadInt16(); + args.Data.ReadByte(); //Ignore direction - if (OnProjectileKill(args.Player, args.Data, ident, owner, index)) + if (x >= Main.maxTilesX || y >= Main.maxTilesY || x < 0 || y < 0) // Check for out of range { return true; } - var type = Main.projectile[index].type; - - // TODO: This needs to be moved somewhere else. - if (!args.Player.HasProjectilePermission(index, type) && type != 102 && type != 100 && !TShock.Config.IgnoreProjKill) + if (type < 0 || type > 5) { - args.Player.Disable("Does not have projectile permission to kill projectile.", DisableFlags.WriteToLogAndConsole); - args.Player.RemoveProjectile(ident, owner); return true; } - args.Player.LastKilledProjectile = type; + ushort tileType = Main.tile[x, y].type; - return false; - } - - private static bool HandlePlayerKillMeV2(GetDataHandlerArgs args) - { - var id = args.Data.ReadInt8(); - PlayerDeathReason playerDeathReason = PlayerDeathReason.FromReader(new BinaryReader(args.Data)); - var dmg = args.Data.ReadInt16(); - var direction = (byte)(args.Data.ReadInt8() - 1); - BitsByte bits = (BitsByte)args.Data.ReadByte(); - bool pvp = bits[0]; - - if (OnKillMe(args.Player, args.Data, id, direction, dmg, pvp, playerDeathReason)) - return true; - - args.Player.Dead = true; - args.Player.RespawnTimer = TShock.Config.RespawnSeconds; - - foreach (NPC npc in Main.npc) - { - if (npc.active && (npc.boss || npc.type == 13 || npc.type == 14 || npc.type == 15) && - Math.Abs(args.TPlayer.Center.X - npc.Center.X) + Math.Abs(args.TPlayer.Center.Y - npc.Center.Y) < 4000f) - { - args.Player.RespawnTimer = TShock.Config.RespawnBossSeconds; - break; - } - } - - // Handle kicks/bans on mediumcore/hardcore deaths. - if (args.TPlayer.difficulty != 0) // Player is not softcore - { - bool mediumcore = args.TPlayer.difficulty == 1; - bool shouldBan = mediumcore ? TShock.Config.BanOnMediumcoreDeath : TShock.Config.BanOnHardcoreDeath; - bool shouldKick = mediumcore ? TShock.Config.KickOnMediumcoreDeath : TShock.Config.KickOnHardcoreDeath; - string banReason = mediumcore ? TShock.Config.MediumcoreBanReason : TShock.Config.HardcoreBanReason; - string kickReason = mediumcore ? TShock.Config.MediumcoreKickReason : TShock.Config.HardcoreKickReason; - - if(shouldBan) { - if (!args.Player.Ban(banReason, false, "TShock")) - args.Player.Kick("You died! Normally, you'd be banned.", true, true); - } - else if(shouldKick) { - args.Player.Kick(kickReason, true, true, null, false); - } - } - - if (args.TPlayer.difficulty == 2 && Main.ServerSideCharacter && args.Player.IsLoggedIn) - { - if (TShock.CharacterDB.RemovePlayer(args.Player.Account.ID)) - { - args.Player.SendErrorMessage("You have fallen in hardcore mode, and your items have been lost forever."); - TShock.CharacterDB.SeedInitialData(args.Player.Account); - } - } - - return false; - } - - private static bool HandleLiquidSet(GetDataHandlerArgs args) - { - int tileX = args.Data.ReadInt16(); - int tileY = args.Data.ReadInt16(); - byte amount = args.Data.ReadInt8(); - byte type = args.Data.ReadInt8(); - - if (OnLiquidSet(args.Player, args.Data, tileX, tileY, amount, type)) - return true; - - return false; - } - - private static bool HandlePlaceChest(GetDataHandlerArgs args) - { - int flag = args.Data.ReadByte(); - int tileX = args.Data.ReadInt16(); - int tileY = args.Data.ReadInt16(); - args.Data.ReadInt16(); // Ignore style - - if (OnPlaceChest(args.Player, args.Data, flag, tileX, tileY)) - return true; - - return false; - } - - private static bool HandleSpawn(GetDataHandlerArgs args) - { - var player = args.Data.ReadInt8(); - var spawnx = args.Data.ReadInt16(); - var spawny = args.Data.ReadInt16(); - - if (OnPlayerSpawn(args.Player, args.Data, player, spawnx, spawny)) - return true; - - if ((Main.ServerSideCharacter) && (args.Player.sX > 0) && (args.Player.sY > 0) && (args.TPlayer.SpawnX > 0) && ((args.TPlayer.SpawnX != args.Player.sX) && (args.TPlayer.SpawnY != args.Player.sY))) - { - - args.Player.sX = args.TPlayer.SpawnX; - args.Player.sY = args.TPlayer.SpawnY; - - if (((Main.tile[args.Player.sX, args.Player.sY - 1].active() && Main.tile[args.Player.sX, args.Player.sY - 1].type == 79)) && (WorldGen.StartRoomCheck(args.Player.sX, args.Player.sY - 1))) - args.Player.Teleport(args.Player.sX * 16, (args.Player.sY * 16) - 48); - } - - else if ((Main.ServerSideCharacter) && (args.Player.sX > 0) && (args.Player.sY > 0)) - { - if (((Main.tile[args.Player.sX, args.Player.sY - 1].active() && Main.tile[args.Player.sX, args.Player.sY - 1].type == 79)) && (WorldGen.StartRoomCheck(args.Player.sX, args.Player.sY - 1))) - args.Player.Teleport(args.Player.sX * 16, (args.Player.sY * 16) - 48); - } - - args.Player.Dead = false; - return false; - } - - private static bool HandleChestOpen(GetDataHandlerArgs args) - { - var x = args.Data.ReadInt16(); - var y = args.Data.ReadInt16(); - - if (OnChestOpen(args.Data, x, y, args.Player)) - return true; - - return false; - } - - private static bool HandleChestActive(GetDataHandlerArgs args) - { - //chest ID - var id = args.Data.ReadInt16(); - //chest x - var x = args.Data.ReadInt16(); - //chest y - var y = args.Data.ReadInt16(); - //chest name length - var nameLen = args.Data.ReadInt8(); - - if (nameLen != 0 && nameLen <= 20) - args.Data.ReadString(); // Ignore the name - - args.Player.ActiveChest = id; - - if (!args.Player.HasBuildPermission(x, y) && TShock.Config.RegionProtectChests) - { - args.Player.SendData(PacketTypes.ChestOpen, "", -1); - return true; - } - - return false; - } - - private static bool HandleChestItem(GetDataHandlerArgs args) - { - var id = args.Data.ReadInt16(); - var slot = args.Data.ReadInt8(); - var stacks = args.Data.ReadInt16(); - var prefix = args.Data.ReadInt8(); - var type = args.Data.ReadInt16(); - - if (OnChestItemChange(args.Player, args.Data, id, slot, stacks, prefix, type)) - return true; - - Item item = new Item(); - item.netDefaults(type); - if (stacks > item.maxStack || TShock.Itembans.ItemIsBanned(EnglishLanguage.GetItemNameById(item.type), args.Player)) + if (tileType != TileID.ClosedDoor && tileType != TileID.OpenDoor + && tileType != TileID.TallGateClosed && tileType != TileID.TallGateOpen + && tileType != TileID.TrapdoorClosed && tileType != TileID.TrapdoorOpen) { return true; } @@ -2695,75 +2251,20 @@ namespace TShockAPI return false; } - private static bool HandleSign(GetDataHandlerArgs args) + private static bool HandleSendTileSquare(GetDataHandlerArgs args) { - var id = args.Data.ReadInt16(); - var x = args.Data.ReadInt16(); - var y = args.Data.ReadInt16(); - args.Data.ReadString(); // Ignore sign text + var player = args.Player; + var size = args.Data.ReadInt16(); + var tileX = args.Data.ReadInt16(); + var tileY = args.Data.ReadInt16(); + var data = args.Data; - if (OnSignEvent(args.Player, args.Data, id, x, y)) + if (OnSendTileSquare(player, data, size, tileX, tileY)) return true; - if (!args.Player.HasBuildPermission(x, y)) - { - args.Player.SendData(PacketTypes.SignNew, "", id); - return true; - } - - if (!args.Player.IsInRange(x, y)) - { - args.Player.SendData(PacketTypes.SignNew, "", id); - return true; - } return false; } - private static bool UpdateNPCHome(GetDataHandlerArgs args) - { - var id = args.Data.ReadInt16(); - var x = args.Data.ReadInt16(); - var y = args.Data.ReadInt16(); - var homeless = args.Data.ReadInt8(); - - if (OnUpdateNPCHome(args.Player, args.Data, id, x, y, homeless)) - return true; - - 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, - Convert.ToByte(Main.npc[id].homeless)); - return true; - } - return false; - } - - private static bool HandleNPCAddBuff(GetDataHandlerArgs args) - { - var id = args.Data.ReadInt16(); - var type = args.Data.ReadInt8(); - var time = args.Data.ReadInt16(); - - if (OnNPCAddBuff(args.Player, args.Data, id, type, time)) - return true; - - return false; - } - - private static bool HandlePlayerAddBuff(GetDataHandlerArgs args) - { - var id = args.Data.ReadInt8(); - var type = args.Data.ReadInt8(); - var time = args.Data.ReadInt32(); - - if (OnPlayerBuff(args.Player, args.Data, id, type, time)) - return true; - - args.Player.SendData(PacketTypes.PlayerAddBuff, "", id); - return true; - } - private static bool HandleItemDrop(GetDataHandlerArgs args) { var id = args.Data.ReadInt16(); @@ -2797,24 +2298,32 @@ namespace TShockAPI return false; } - private static bool HandlePlayerDamageV2(GetDataHandlerArgs args) + private static bool HandleProjectileNew(GetDataHandlerArgs args) { - var id = args.Data.ReadInt8(); - PlayerDeathReason playerDeathReason = PlayerDeathReason.FromReader(new BinaryReader(args.Data)); - var dmg = args.Data.ReadInt16(); - var direction = (byte)(args.Data.ReadInt8() - 1); - var bits = (BitsByte)(args.Data.ReadByte()); - var crit = bits[0]; - var pvp = bits[1]; + short ident = args.Data.ReadInt16(); + var pos = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle()); + var vel = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle()); + float knockback = args.Data.ReadSingle(); + short dmg = args.Data.ReadInt16(); + byte owner = args.Data.ReadInt8(); + short type = args.Data.ReadInt16(); + BitsByte bits = args.Data.ReadInt8(); + //owner = (byte)args.Player.Index; + float[] ai = new float[Projectile.maxAI]; - if (OnPlayerDamage(args.Player, args.Data, id, direction, dmg, pvp, crit, playerDeathReason)) - return true; - - if (TShock.Players[id].GodMode) + for (int i = 0; i < Projectile.maxAI; i++) { - TShock.Players[id].Heal(args.TPlayer.statLifeMax); + if (bits[i]) + ai[i] = args.Data.ReadSingle(); + else + ai[i] = 0f; } + var index = TShock.Utils.SearchProjectile(ident, owner); + + if (OnNewProjectile(args.Data, ident, pos, vel, knockback, dmg, owner, type, index, args.Player)) + return true; + return false; } @@ -2839,30 +2348,219 @@ namespace TShockAPI return false; } - private static bool HandleSpecial(GetDataHandlerArgs args) + private static bool HandleProjectileKill(GetDataHandlerArgs args) { - var id = args.Data.ReadInt8(); - var type = args.Data.ReadInt8(); + var ident = args.Data.ReadInt16(); + var owner = args.Data.ReadInt8(); + owner = (byte)args.Player.Index; + var index = TShock.Utils.SearchProjectile(ident, owner); - if (OnNPCSpecial(args.Player, args.Data, id, type)) - return true; - - if (type == 1 && TShock.Config.DisableDungeonGuardian) + if (OnProjectileKill(args.Player, args.Data, ident, owner, index)) { - args.Player.SendMessage("The Dungeon Guardian returned you to your spawn point", Color.Purple); - args.Player.Spawn(); return true; } - if (type == 3 & !args.Player.HasPermission(Permissions.usesundial)) + var type = Main.projectile[index].type; + + // TODO: This needs to be moved somewhere else. + if (!args.Player.HasProjectilePermission(index, type) && type != 102 && type != 100 && !TShock.Config.IgnoreProjKill) + { + args.Player.Disable("Does not have projectile permission to kill projectile.", DisableFlags.WriteToLogAndConsole); + args.Player.RemoveProjectile(ident, owner); + return true; + } + + args.Player.LastKilledProjectile = type; + + return false; + } + + private static bool HandleTogglePvp(GetDataHandlerArgs args) + { + byte id = args.Data.ReadInt8(); + bool pvp = args.Data.ReadBoolean(); + if (OnPvpToggled(args.Player, args.Data, id, pvp)) + return true; + + if (id != args.Player.Index) + return true; + + string pvpMode = TShock.Config.PvPMode.ToLowerInvariant(); + if (pvpMode == "disabled" || pvpMode == "always" || (DateTime.UtcNow - args.Player.LastPvPTeamChange).TotalSeconds < 5) + { + args.Player.SendData(PacketTypes.TogglePvp, "", id); + return true; + } + + args.Player.LastPvPTeamChange = DateTime.UtcNow; + return false; + } + + private static bool HandleChestOpen(GetDataHandlerArgs args) + { + var x = args.Data.ReadInt16(); + var y = args.Data.ReadInt16(); + + if (OnChestOpen(args.Data, x, y, args.Player)) + return true; + + return false; + } + + private static bool HandleChestItem(GetDataHandlerArgs args) + { + var id = args.Data.ReadInt16(); + var slot = args.Data.ReadInt8(); + var stacks = args.Data.ReadInt16(); + var prefix = args.Data.ReadInt8(); + var type = args.Data.ReadInt16(); + + if (OnChestItemChange(args.Player, args.Data, id, slot, stacks, prefix, type)) + return true; + + Item item = new Item(); + item.netDefaults(type); + if (stacks > item.maxStack || TShock.Itembans.ItemIsBanned(EnglishLanguage.GetItemNameById(item.type), args.Player)) { - args.Player.SendErrorMessage("You do not have permission to use the Enchanted Sundial!"); return true; } return false; } + private static bool HandleChestActive(GetDataHandlerArgs args) + { + //chest ID + var id = args.Data.ReadInt16(); + //chest x + var x = args.Data.ReadInt16(); + //chest y + var y = args.Data.ReadInt16(); + //chest name length + var nameLen = args.Data.ReadInt8(); + + if (nameLen != 0 && nameLen <= 20) + args.Data.ReadString(); // Ignore the name + + args.Player.ActiveChest = id; + + if (!args.Player.HasBuildPermission(x, y) && TShock.Config.RegionProtectChests) + { + args.Player.SendData(PacketTypes.ChestOpen, "", -1); + return true; + } + + return false; + } + + private static bool HandlePlaceChest(GetDataHandlerArgs args) + { + int flag = args.Data.ReadByte(); + int tileX = args.Data.ReadInt16(); + int tileY = args.Data.ReadInt16(); + args.Data.ReadInt16(); // Ignore style + + if (OnPlaceChest(args.Player, args.Data, flag, tileX, tileY)) + return true; + + return false; + } + + private static bool HandlePlayerZone(GetDataHandlerArgs args) + { + if (args.Player == null || args.TPlayer == null || args.Data == null) + { + return true; + } + + var plr = args.Data.ReadInt8(); + BitsByte zone1 = args.Data.ReadInt8(); + BitsByte zone2 = args.Data.ReadInt8(); + BitsByte zone3 = args.Data.ReadInt8(); + BitsByte zone4 = args.Data.ReadInt8(); + + if (OnPlayerZone(args.Player, args.Data, plr, zone1, zone2, zone3, zone4)) + return true; + + return false; + } + + private static bool HandlePassword(GetDataHandlerArgs args) + { + if (!args.Player.RequiresPassword) + return true; + + string password = args.Data.ReadString(); + + if (Hooks.PlayerHooks.OnPlayerPreLogin(args.Player, args.Player.Name, password)) + return true; + + var account = TShock.UserAccounts.GetUserAccountByName(args.Player.Name); + if (account != null && !TShock.Config.DisableLoginBeforeJoin) + { + if (account.VerifyPassword(password)) + { + args.Player.RequiresPassword = false; + args.Player.PlayerData = TShock.CharacterDB.GetPlayerData(args.Player, account.ID); + + if (args.Player.State == 1) + args.Player.State = 2; + NetMessage.SendData((int)PacketTypes.WorldInfo, args.Player.Index); + + var group = TShock.Groups.GetGroupByName(account.Group); + + args.Player.Group = group; + args.Player.tempGroup = null; + args.Player.Account = account; + args.Player.IsLoggedIn = true; + args.Player.IsDisabledForSSC = false; + + if (Main.ServerSideCharacter) + { + if (args.Player.HasPermission(Permissions.bypassssc)) + { + args.Player.PlayerData.CopyCharacter(args.Player); + TShock.CharacterDB.InsertPlayerData(args.Player); + } + args.Player.PlayerData.RestoreCharacter(args.Player); + } + args.Player.LoginFailsBySsi = false; + + if (args.Player.HasPermission(Permissions.ignorestackhackdetection)) + args.Player.IsDisabledForStackDetection = false; + + if (args.Player.HasPermission(Permissions.usebanneditem)) + args.Player.IsDisabledForBannedWearable = false; + + + args.Player.SendMessage("Authenticated as " + args.Player.Name + " successfully.", Color.LimeGreen); + TShock.Log.ConsoleInfo(args.Player.Name + " authenticated successfully as user " + args.Player.Name + "."); + TShock.UserAccounts.SetUserAccountUUID(account, args.Player.UUID); + Hooks.PlayerHooks.OnPlayerPostLogin(args.Player); + return true; + } + args.Player.Kick("Your password did not match this character's password.", true, true); + return true; + } + + if (!string.IsNullOrEmpty(TShock.Config.ServerPassword)) + { + if (TShock.Config.ServerPassword == password) + { + args.Player.RequiresPassword = false; + if (args.Player.State == 1) + args.Player.State = 2; + NetMessage.SendData((int)PacketTypes.WorldInfo, args.Player.Index); + return true; + } + args.Player.Kick("Invalid server password.", true, true); + return true; + } + + args.Player.Kick("You have been Bounced.", true, true); + return true; + } + private static bool HandlePlayerAnimation(GetDataHandlerArgs args) { if (OnPlayerAnimation(args.Player, args.Data)) @@ -2871,6 +2569,87 @@ namespace TShockAPI return false; } + private static bool HandlePlayerMana(GetDataHandlerArgs args) + { + var plr = args.Data.ReadInt8(); + var cur = args.Data.ReadInt16(); + var max = args.Data.ReadInt16(); + + if (OnPlayerMana(args.Player, args.Data, plr, cur, max) || cur < 0 || max < 0 || args.Player.IgnoreSSCPackets) + return true; + + if (max > TShock.Config.MaxMP && !args.Player.HasPermission(Permissions.ignoremp)) + { + args.Player.Disable("Maximum MP beyond limit", DisableFlags.WriteToLogAndConsole); + return true; + } + + if (args.Player.IsLoggedIn) + { + args.Player.TPlayer.statMana = cur; + args.Player.TPlayer.statManaMax = max; + args.Player.PlayerData.maxMana = max; + } + return false; + } + + private static bool HandlePlayerTeam(GetDataHandlerArgs args) + { + byte id = args.Data.ReadInt8(); + byte team = args.Data.ReadInt8(); + if (OnPlayerTeam(args.Player, args.Data, id, team)) + return true; + + if (id != args.Player.Index) + return true; + + if ((DateTime.UtcNow - args.Player.LastPvPTeamChange).TotalSeconds < 5) + { + args.Player.SendData(PacketTypes.PlayerTeam, "", id); + return true; + } + + args.Player.LastPvPTeamChange = DateTime.UtcNow; + return false; + } + + private static bool HandleSign(GetDataHandlerArgs args) + { + var id = args.Data.ReadInt16(); + var x = args.Data.ReadInt16(); + var y = args.Data.ReadInt16(); + args.Data.ReadString(); // Ignore sign text + + if (OnSignEvent(args.Player, args.Data, id, x, y)) + return true; + + if (!args.Player.HasBuildPermission(x, y)) + { + args.Player.SendData(PacketTypes.SignNew, "", id); + return true; + } + + if (!args.Player.IsInRange(x, y)) + { + args.Player.SendData(PacketTypes.SignNew, "", id); + return true; + } + return false; + } + + private static bool HandleLiquidSet(GetDataHandlerArgs args) + { + int tileX = args.Data.ReadInt16(); + int tileY = args.Data.ReadInt16(); + byte amount = args.Data.ReadInt8(); + byte type = args.Data.ReadInt8(); + + if (OnLiquidSet(args.Player, args.Data, tileX, tileY, amount, type)) + return true; + + return false; + } + private static bool HandlePlayerBuffList(GetDataHandlerArgs args) { var id = args.Data.ReadInt8(); @@ -2904,6 +2683,75 @@ namespace TShockAPI return true; } + private static bool HandleSpecial(GetDataHandlerArgs args) + { + var id = args.Data.ReadInt8(); + var type = args.Data.ReadInt8(); + + if (OnNPCSpecial(args.Player, args.Data, id, type)) + return true; + + if (type == 1 && TShock.Config.DisableDungeonGuardian) + { + args.Player.SendMessage("The Dungeon Guardian returned you to your spawn point", Color.Purple); + args.Player.Spawn(); + return true; + } + + if (type == 3 & !args.Player.HasPermission(Permissions.usesundial)) + { + args.Player.SendErrorMessage("You do not have permission to use the Enchanted Sundial!"); + return true; + } + + return false; + } + + private static bool HandleNPCAddBuff(GetDataHandlerArgs args) + { + var id = args.Data.ReadInt16(); + var type = args.Data.ReadInt8(); + var time = args.Data.ReadInt16(); + + if (OnNPCAddBuff(args.Player, args.Data, id, type, time)) + return true; + + return false; + } + + private static bool HandlePlayerAddBuff(GetDataHandlerArgs args) + { + var id = args.Data.ReadInt8(); + var type = args.Data.ReadInt8(); + var time = args.Data.ReadInt32(); + + if (OnPlayerBuff(args.Player, args.Data, id, type, time)) + return true; + + args.Player.SendData(PacketTypes.PlayerAddBuff, "", id); + return true; + } + + private static bool UpdateNPCHome(GetDataHandlerArgs args) + { + var id = args.Data.ReadInt16(); + var x = args.Data.ReadInt16(); + var y = args.Data.ReadInt16(); + var homeless = args.Data.ReadInt8(); + + if (OnUpdateNPCHome(args.Player, args.Data, id, x, y, homeless)) + return true; + + 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, + Convert.ToByte(Main.npc[id].homeless)); + return true; + } + return false; + } + private static bool HandleSpawnBoss(GetDataHandlerArgs args) { if (args.Player.IsBouncerThrottled()) @@ -3164,159 +3012,13 @@ namespace TShockAPI return false; } - private static bool HandleDoorUse(GetDataHandlerArgs args) + private static bool HandleHealOther(GetDataHandlerArgs args) { - byte type = (byte)args.Data.ReadByte(); - short x = args.Data.ReadInt16(); - short y = args.Data.ReadInt16(); - args.Data.ReadByte(); //Ignore direction + byte plr = args.Data.ReadInt8(); + short amount = args.Data.ReadInt16(); - if (x >= Main.maxTilesX || y >= Main.maxTilesY || x < 0 || y < 0) // Check for out of range - { + if (OnHealOtherPlayer(args.Player, args.Data, plr, amount)) return true; - } - - if (type < 0 || type > 5) - { - return true; - } - - ushort tileType = Main.tile[x, y].type; - - if (tileType != TileID.ClosedDoor && tileType != TileID.OpenDoor - && tileType != TileID.TallGateClosed && tileType != TileID.TallGateOpen - && tileType != TileID.TrapdoorClosed && tileType != TileID.TrapdoorOpen) - { - return true; - } - - return false; - } - - private static bool HandleCompleteAnglerQuest(GetDataHandlerArgs args) - { - // Since packet 76 is NEVER sent to us, we actually have to rely on this to get the true count - args.TPlayer.anglerQuestsFinished++; - return false; - } - - private static bool HandleNumberOfAnglerQuestsCompleted(GetDataHandlerArgs args) - { - // Never sent by vanilla client, ignore this - return true; - } - - private static bool HandleMassWireOperation(GetDataHandlerArgs args) - { - short startX = args.Data.ReadInt16(); - short startY = args.Data.ReadInt16(); - short endX = args.Data.ReadInt16(); - short endY = args.Data.ReadInt16(); - byte toolMode = (byte) args.Data.ReadByte(); - - if (OnMassWireOperation(args.Player, args.Data, startX, startY, endX, endY, toolMode)) - return true; - - return false; - } - - /// The arguments to the PlaceItemFrame event. - public class PlaceItemFrameEventArgs : GetDataHandledEventArgs - { - /// The X coordinate of the item frame. - public short X { get; set; } - - /// The Y coordinate of the item frame. - public short Y { get; set; } - - /// The ItemID of the item frame. - public short ItemID { get; set; } - - /// The prefix. - public byte Prefix { get; set; } - - /// The stack. - public short Stack { get; set; } - - /// The ItemFrame object associated with this event. - public TEItemFrame ItemFrame { get; set; } - } - - /// Fired when an ItemFrame is placed. - public static HandlerList PlaceItemFrame = new HandlerList(); - - private static bool OnPlaceItemFrame(TSPlayer player, MemoryStream data, short x, short y, short itemID, byte prefix, short stack, TEItemFrame itemFrame) - { - if (PlaceItemFrame == null) - return false; - - var args = new PlaceItemFrameEventArgs - { - Player = player, - Data = data, - X = x, - Y = y, - ItemID = itemID, - Prefix = prefix, - Stack = stack, - ItemFrame = itemFrame, - }; - - PlaceItemFrame.Invoke(null, args); - return args.Handled; - } - - /// - /// For use with a ToggleGemLock event - /// - public class GemLockToggleEventArgs : GetDataHandledEventArgs - { - /// - /// X Location - /// - public short X { get; set; } - /// - /// Y Location - /// - public short Y { get; set; } - /// - /// On status - /// - public bool On { get; set; } - } - - /// - /// GemLockToggle - Called when a gem lock is switched - /// - public static HandlerList GemLockToggle = new HandlerList(); - - private static bool OnGemLockToggle(TSPlayer player, MemoryStream data, short x, short y, bool on) - { - if (GemLockToggle == null) - return false; - - var args = new GemLockToggleEventArgs - { - Player = player, - Data = data, - X = x, - Y = y, - On = on - }; - GemLockToggle.Invoke(null, args); - return args.Handled; - } - - private static bool HandleGemLockToggle(GetDataHandlerArgs args) - { - var x = args.Data.ReadInt16(); - var y = args.Data.ReadInt16(); - var on = args.Data.ReadBoolean(); - - if (OnGemLockToggle(args.Player, args.Data, x, y, on)) - { - return true; - } return false; } @@ -3336,50 +3038,45 @@ namespace TShockAPI return false; } - private static bool HandleNpcTeleportPortal(GetDataHandlerArgs args) + private static bool HandleCompleteAnglerQuest(GetDataHandlerArgs args) { - var npcIndex = args.Data.ReadByte(); - var portalColorIndex = args.Data.ReadInt16(); - var newPosition = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle()); - var velocity = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle()); - var projectile = Main.projectile.FirstOrDefault(p => p.position.X == newPosition.X && p.position.Y == newPosition.Y); // Check for projectiles at this location + // Since packet 76 is NEVER sent to us, we actually have to rely on this to get the true count + args.TPlayer.anglerQuestsFinished++; + return false; + } - if (projectile == null || !projectile.active) - { - NetMessage.SendData((int)PacketTypes.NpcUpdate, -1, -1, NetworkText.Empty, npcIndex); - return true; - } + private static bool HandleNumberOfAnglerQuestsCompleted(GetDataHandlerArgs args) + { + // Never sent by vanilla client, ignore this + return true; + } - if (projectile.type != ProjectileID.PortalGunGate) - { - NetMessage.SendData((int)PacketTypes.NpcUpdate, -1, -1, NetworkText.Empty, npcIndex); + private static bool HandlePlaceObject(GetDataHandlerArgs args) + { + short x = args.Data.ReadInt16(); + short y = args.Data.ReadInt16(); + short type = args.Data.ReadInt16(); + short style = args.Data.ReadInt16(); + byte alternate = args.Data.ReadInt8(); + bool direction = args.Data.ReadBoolean(); + + if (OnPlaceObject(args.Player, args.Data, x, y, type, style, alternate, direction)) return true; - } return false; } - private static bool HandleKillPortal(GetDataHandlerArgs args) + private static bool HandleLoadNetModule(GetDataHandlerArgs args) { - short projectileIndex = args.Data.ReadInt16(); - - Projectile projectile = Main.projectile[projectileIndex]; - if (projectile != null && projectile.active) - { - if (projectile.owner != args.TPlayer.whoAmI) - { - return true; - } - } - - return false; + // Since this packet is never actually sent to us, every attempt at sending it can be considered as a liquid exploit attempt + return true; } private static bool HandlePlaceTileEntity(GetDataHandlerArgs args) { var x = args.Data.ReadInt16(); var y = args.Data.ReadInt16(); - var type = (byte) args.Data.ReadByte(); + var type = (byte)args.Data.ReadByte(); if (OnPlaceTileEntity(args.Player, args.Data, x, y, type)) { @@ -3414,7 +3111,7 @@ namespace TShockAPI return false; } - + private static bool HandleSyncExtraValue(GetDataHandlerArgs args) { var npcIndex = args.Data.ReadInt16(); @@ -3438,11 +3135,91 @@ namespace TShockAPI return false; } - - private static bool HandleLoadNetModule(GetDataHandlerArgs args) + + private static bool HandleKillPortal(GetDataHandlerArgs args) { - // Since this packet is never actually sent to us, every attempt at sending it can be considered as a liquid exploit attempt - return true; + short projectileIndex = args.Data.ReadInt16(); + + Projectile projectile = Main.projectile[projectileIndex]; + if (projectile != null && projectile.active) + { + if (projectile.owner != args.TPlayer.whoAmI) + { + return true; + } + } + + return false; + } + + private static bool HandlePlayerPortalTeleport(GetDataHandlerArgs args) + { + byte plr = args.Data.ReadInt8(); + short portalColorIndex = args.Data.ReadInt16(); + float newPositionX = args.Data.ReadSingle(); + float newPositionY = args.Data.ReadSingle(); + float newVelocityX = args.Data.ReadSingle(); + float newVelocityY = args.Data.ReadSingle(); + + return OnPlayerTeleportThroughPortal( + args.Player, + plr, + args.Data, + new Vector2(newPositionX, newPositionY), + new Vector2(newVelocityX, newVelocityY), + portalColorIndex + ); + } + + private static bool HandleNpcTeleportPortal(GetDataHandlerArgs args) + { + var npcIndex = args.Data.ReadByte(); + var portalColorIndex = args.Data.ReadInt16(); + var newPosition = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle()); + var velocity = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle()); + var projectile = Main.projectile.FirstOrDefault(p => p.position.X == newPosition.X && p.position.Y == newPosition.Y); // Check for projectiles at this location + + if (projectile == null || !projectile.active) + { + NetMessage.SendData((int)PacketTypes.NpcUpdate, -1, -1, NetworkText.Empty, npcIndex); + return true; + } + + if (projectile.type != ProjectileID.PortalGunGate) + { + NetMessage.SendData((int)PacketTypes.NpcUpdate, -1, -1, NetworkText.Empty, npcIndex); + return true; + } + + return false; + } + + private static bool HandleGemLockToggle(GetDataHandlerArgs args) + { + var x = args.Data.ReadInt16(); + var y = args.Data.ReadInt16(); + var on = args.Data.ReadBoolean(); + + if (OnGemLockToggle(args.Player, args.Data, x, y, on)) + { + return true; + } + + return false; + } + + private static bool HandleMassWireOperation(GetDataHandlerArgs args) + { + short startX = args.Data.ReadInt16(); + short startY = args.Data.ReadInt16(); + short endX = args.Data.ReadInt16(); + short endY = args.Data.ReadInt16(); + byte toolMode = (byte)args.Data.ReadByte(); + + if (OnMassWireOperation(args.Player, args.Data, startX, startY, endX, endY, toolMode)) + return true; + + return false; } private static bool HandleToggleParty(GetDataHandlerArgs args) @@ -3475,5 +3252,166 @@ namespace TShockAPI TShock.Utils.Broadcast(string.Format("{0} started the Old One's Army event!", args.Player.Name), 175, 75, 255); return false; } + + private static bool HandlePlayerDamageV2(GetDataHandlerArgs args) + { + var id = args.Data.ReadInt8(); + PlayerDeathReason playerDeathReason = PlayerDeathReason.FromReader(new BinaryReader(args.Data)); + var dmg = args.Data.ReadInt16(); + var direction = (byte)(args.Data.ReadInt8() - 1); + var bits = (BitsByte)(args.Data.ReadByte()); + var crit = bits[0]; + var pvp = bits[1]; + + if (OnPlayerDamage(args.Player, args.Data, id, direction, dmg, pvp, crit, playerDeathReason)) + return true; + + if (TShock.Players[id].GodMode) + { + TShock.Players[id].Heal(args.TPlayer.statLifeMax); + } + + return false; + } + + private static bool HandlePlayerKillMeV2(GetDataHandlerArgs args) + { + var id = args.Data.ReadInt8(); + PlayerDeathReason playerDeathReason = PlayerDeathReason.FromReader(new BinaryReader(args.Data)); + var dmg = args.Data.ReadInt16(); + var direction = (byte)(args.Data.ReadInt8() - 1); + BitsByte bits = (BitsByte)args.Data.ReadByte(); + bool pvp = bits[0]; + + if (OnKillMe(args.Player, args.Data, id, direction, dmg, pvp, playerDeathReason)) + return true; + + args.Player.Dead = true; + args.Player.RespawnTimer = TShock.Config.RespawnSeconds; + + foreach (NPC npc in Main.npc) + { + if (npc.active && (npc.boss || npc.type == 13 || npc.type == 14 || npc.type == 15) && + Math.Abs(args.TPlayer.Center.X - npc.Center.X) + Math.Abs(args.TPlayer.Center.Y - npc.Center.Y) < 4000f) + { + args.Player.RespawnTimer = TShock.Config.RespawnBossSeconds; + break; + } + } + + // Handle kicks/bans on mediumcore/hardcore deaths. + if (args.TPlayer.difficulty != 0) // Player is not softcore + { + bool mediumcore = args.TPlayer.difficulty == 1; + bool shouldBan = mediumcore ? TShock.Config.BanOnMediumcoreDeath : TShock.Config.BanOnHardcoreDeath; + bool shouldKick = mediumcore ? TShock.Config.KickOnMediumcoreDeath : TShock.Config.KickOnHardcoreDeath; + string banReason = mediumcore ? TShock.Config.MediumcoreBanReason : TShock.Config.HardcoreBanReason; + string kickReason = mediumcore ? TShock.Config.MediumcoreKickReason : TShock.Config.HardcoreKickReason; + + if (shouldBan) + { + if (!args.Player.Ban(banReason, false, "TShock")) + args.Player.Kick("You died! Normally, you'd be banned.", true, true); + } + else if (shouldKick) + { + args.Player.Kick(kickReason, true, true, null, false); + } + } + + if (args.TPlayer.difficulty == 2 && Main.ServerSideCharacter && args.Player.IsLoggedIn) + { + if (TShock.CharacterDB.RemovePlayer(args.Player.Account.ID)) + { + args.Player.SendErrorMessage("You have fallen in hardcore mode, and your items have been lost forever."); + TShock.CharacterDB.SeedInitialData(args.Player.Account); + } + } + + return false; + } + + + public enum EditAction + { + KillTile = 0, + PlaceTile, + KillWall, + PlaceWall, + KillTileNoItem, + PlaceWire, + KillWire, + PoundTile, + PlaceActuator, + KillActuator, + PlaceWire2, + KillWire2, + PlaceWire3, + KillWire3, + SlopeTile, + FrameTrack, + PlaceWire4, + KillWire4 + } + public enum EditType + { + Fail = 0, + Type, + Slope, + } + + /// + /// The maximum place styles for each tile. + /// + public static Dictionary MaxPlaceStyles = new Dictionary(); + + /// + /// Tiles that can be broken without any pickaxes/etc. + /// + internal static int[] breakableTiles = new int[] + { + TileID.Books, + TileID.Bottles, + TileID.BreakableIce, + TileID.Candles, + TileID.CorruptGrass, + TileID.Dirt, + TileID.FleshGrass, + TileID.Grass, + TileID.HallowedGrass, + TileID.MagicalIceBlock, + TileID.Mannequin, + TileID.Torches, + TileID.WaterCandle, + TileID.Womannequin, + }; + + /// + /// These projectiles create tiles on death. + /// + internal static Dictionary projectileCreatesTile = new Dictionary + { + { ProjectileID.DirtBall, TileID.Dirt }, + { ProjectileID.SandBallGun, TileID.Sand }, + { ProjectileID.EbonsandBallGun, TileID.Ebonsand }, + { ProjectileID.PearlSandBallGun, TileID.Pearlsand }, + { ProjectileID.CrimsandBallGun, TileID.Crimsand }, + }; + + internal static Dictionary ropeCoilPlacements = new Dictionary + { + {ItemID.RopeCoil, TileID.Rope}, + {ItemID.SilkRopeCoil, TileID.SilkRope}, + {ItemID.VineRopeCoil, TileID.VineRope}, + {ItemID.WebRopeCoil, TileID.WebRope} + }; + + /// + /// Extra place style limits for strange hardcoded values in Terraria + /// + internal static Dictionary ExtraneousPlaceStyles = new Dictionary + { + {TileID.MinecartTrack, 3} + }; } } From eae1ba5a5c1a856993c1a96a5249b1309e8e116b Mon Sep 17 00:00:00 2001 From: AxeelAnder Date: Wed, 31 Oct 2018 18:02:53 +0800 Subject: [PATCH 19/19] concrete reason when create banned projectile --- TShockAPI/Bouncer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 087e092a..f4ee7d5f 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -781,7 +781,7 @@ namespace TShockAPI if (TShock.ProjectileBans.ProjectileIsBanned(type, args.Player)) { - args.Player.Disable("Player does not have permission to create that projectile.", DisableFlags.WriteToLogAndConsole); + args.Player.Disable(String.Format("Player does not have permission to create projectile {0}.", type), DisableFlags.WriteToLogAndConsole); args.Player.SendErrorMessage("You do not have permission to create that projectile."); args.Player.RemoveProjectile(ident, owner); args.Handled = true;