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