Example #1
0
        private void DeathFix(GetDataEventArgs args)
        {
            if (args.MsgID == PacketTypes.PlayerDeathV2)
            {
                using (BinaryReader reader =
                           new BinaryReader(new MemoryStream(args.Msg.readBuffer, args.Index, args.Length)))
                {
                    reader.ReadByte();
                    PlayerDeathReason playerDeathReason = PlayerDeathReason.FromReader(reader);
                    var      dmg       = reader.ReadInt16();
                    var      direction = (byte)(reader.ReadByte() - 1);
                    BitsByte bits      = reader.ReadByte();
                    bool     pvp       = bits[0];


                    TShock.Players[args.Msg.whoAmI].TPlayer.KillMe(playerDeathReason, dmg, direction, pvp);
                    TShock.Players[args.Msg.whoAmI].Dead         = true;
                    TShock.Players[args.Msg.whoAmI].TPlayer.dead = true;

                    NetMessage.SendPlayerDeath(args.Msg.whoAmI, playerDeathReason, dmg, direction, pvp);
                    NetMessage.SendPlayerDeath(args.Msg.whoAmI, playerDeathReason, dmg, direction, pvp, args.Msg.whoAmI);

                    args.Handled = true;
                }
            }
        }
Example #2
0
        private void OnGetData(GetDataEventArgs e)
        {
            if (!e.Handled)
            {
                if (e.MsgID == PacketTypes.PlayerDeathV2)
                {
                    using (BinaryReader br = new BinaryReader(new MemoryStream(e.Msg.readBuffer, e.Index, e.Length)))
                    {
                        byte   who    = br.ReadByte();
                        var    reason = PlayerDeathReason.FromReader(br);
                        int    damage = br.ReadInt16();
                        byte   dir    = br.ReadByte();
                        byte   flag   = br.ReadByte();
                        string d      = TShock.Players[who].UUID;
                        switch (flag)
                        {
                        case 0:
                            block(d).IncreaseValue("pveDeaths", 1);
                            break;

                        case 1:
                            block(d).IncreaseValue("pvpDeaths", 1);
                            block(TShock.Players[reason.SourcePlayerIndex].UUID).IncreaseValue("Kills", 1);
                            break;

                        default:
                            break;
                        }
                    }
                }
            }
        }
Example #3
0
 public PlayerDeathArgs(GetDataEventArgs args, MemoryStream data, TSPlayer player) : base(player)
 {
     data.ReadByte(); // Player ID
     PlayerDeathReason = PlayerDeathReason.FromReader(new BinaryReader(data));
     Damage            = data.ReadInt16();
     HitDirection      = (byte)data.ReadByte();
     Flags             = (byte)data.ReadByte();
 }
        private void OnDeathEvent(TSPlayer player, MemoryStream data)
        {
            // Read in Byte
            data.ReadByte();
            PlayerDeathReason death  = PlayerDeathReason.FromReader(new BinaryReader(data));
            string            reason = death.GetDeathText(player.TPlayer.name).ToString();

            // Update player last death event
            lastPlaceOfDeathData.UpdateLocationUser(player.UUID, new Vector2(player.X, player.Y), reason);
        }
 public PlayerHurtArgs(GetDataEventArgs args, MemoryStream data, TSPlayer player) : base(player)
 {
     Args            = args;
     PlayerID        = data.ReadByte();
     PlayerHitReason = PlayerDeathReason.FromReader(new BinaryReader(data));
     Damage          = data.ReadInt16();
     HitDirection    = (byte)data.ReadByte();
     Flags           = (byte)data.ReadByte();
     CooldownCounter = (sbyte)data.ReadByte();
 }
        private void OnGetData(GetDataEventArgs args)
        {
            if (args.MsgID == PacketTypes.PlayerDeathV2) //Checks the packet type for the playerdeath type, 118
            {
                using (MemoryStream data = new MemoryStream(args.Msg.readBuffer, args.Index, args.Length))
                {
                    // You can see how packets are handled in MessageBuffer.cs and how they are sent in NetMessage.cs
                    // TShock's GetDataHandler.cs can be used as an example on deserializing packets, same with other plugins
                    // Some custom types from terraria have their own methods to read from a binary reader (you can see this in use by tshock & terraria's messagebuffer)
                    // See https://tshock.readme.io/docs/multiplayer-packet-structure for more packet information
                    int player = data.ReadByte();                                                         //Player ID
                    PlayerDeathReason deathReason = PlayerDeathReason.FromReader(new BinaryReader(data)); //Terraria.DataStructures.PlayerDeathReason
                    int  damage    = data.ReadInt16();                                                    //Damage taken
                    int  direction = data.ReadByte() - 1;                                                 //Direction, left or right, the player's body parts fly off to
                    bool pvp       = ((BitsByte)data.ReadByte())[0];

                    args.Handled = true;                //Prevents the game from processing this event

                    string customDeathReason = "";      //start of a custom death reason

                    Projectile proj = new Projectile(); //a projectile object used to get the name of a projectile given the projectile id/type
                    proj.SetDefaults(deathReason._sourceProjectileType);

                    if (pvp) //conditions could be cleaner
                    {
                        customDeathReason = (deathReason._sourcePlayerIndex == 0) ? $"[c/00ccff:{TShock.Players[player].Name}] was dealt [c/afef2a:{damage}] mortal damage by their own [i/p{deathReason._sourceItemPrefix}:{deathReason._sourceItemType}]" : $"[c/00ccff:{TShock.Players[player].Name}] was dealt [c/afef2a:{damage}] fatal damage by {TShock.Players[deathReason._sourcePlayerIndex].Name}'s [i/p{deathReason._sourceItemPrefix}:{deathReason._sourceItemType}]";
                    }
                    else if (deathReason._sourceNPCIndex != -1)
                    {
                        customDeathReason = $"[c/00ccff:{TShock.Players[player].Name}] was dealt [c/afef2a:{damage}] mortal damage by [c/00ff00:{Terraria.Main.npc[deathReason._sourceNPCIndex].TypeName}]";
                    }
                    else if (deathReason._sourceOtherIndex == -1 && proj.hostile)
                    {
                        customDeathReason = $"[c/00ccff:{TShock.Players[player].Name}] was dealt [c/afef2a:{damage}] mortal damage by [c/00ff00:{proj.Name}]";
                    }
                    else if (deathReason._sourceOtherIndex == 4)
                    {
                        string evil = WorldGen.crimson ? "Crimson" : "Demon";
                        customDeathReason = $"[c/00ccff:{TShock.Players[player].Name}] was driven insane trying to smash a [c/00ff00:{evil} altar]";
                    }

                    if (customDeathReason != "")
                    {
                        deathReason = PlayerDeathReason.ByCustomReason(customDeathReason);
                    }

                    Terraria.Main.player[player].KillMe(deathReason, damage, direction, pvp); //Terraria method used in MessageBuffer.cs (where packets are dealt with) to kill the player and send out the death reason to all clients
                }
            }
        }
Example #7
0
        public bool ExtractData(GetDataEventArgs args, MemoryStream data, PvPPlayer attacker, out PlayerHurtArgs arg)
        {
            arg = null;
            int       targetId        = data.ReadByte();
            var       playerHitReason = PlayerDeathReason.FromReader(new BinaryReader(data));
            PvPPlayer target;

            if (targetId > -1)
            {
                target = PvPModifier.PvPers[targetId];
                if (target == null || !target.ConnectionAlive || !target.Active)
                {
                    return(false);
                }

                if (attacker == target)
                {
                    return(false);
                }
            }
            else
            {
                return(false);
            }

            var projectile = playerHitReason._sourceProjectileIndex == -1
                ? null
                : attacker.ProjTracker.Projectiles[(int)playerHitReason.SourceProjectileType];
            var weapon = projectile?.ItemOriginated ?? attacker.HeldItem;

            target.LastHitBy         = attacker;
            target.LastHitWeapon     = Weapon;
            target.LastHitProjectile = Projectile;

            arg = new PlayerHurtArgs {
                Args            = args,
                Attacker        = attacker,
                Target          = target,
                Projectile      = projectile,
                PlayerHitReason = playerHitReason,
                Weapon          = weapon,
                InflictedDamage = data.ReadInt16(),
                DamageReceived  = target.DamageReceived(InflictedDamage),
                HitDirection    = data.ReadByte() - 1,
                Flag            = data.ReadByte()
            };

            return(true);
        }
Example #8
0
        private void GetData(GetDataEventArgs args)
        {
            if (args.MsgID == PacketTypes.PlayerHurtV2)
            {
                //Collects all necessary data to alter pvp
                //The person who would be sending data is the attacking player
                var attackingplayer = TShock.Players[args.Msg.whoAmI];
                var data            = new MemoryStream(args.Msg.readBuffer, args.Index, args.Length);
                var damagedplayer   = TShock.Players[data.ReadByte()];
                PlayerDeathReason playerHitReason = PlayerDeathReason.FromReader(new BinaryReader(data));

                if (attackingplayer == null || !attackingplayer.ConnectionAlive || !attackingplayer.Active)
                {
                    return;
                }
                if (damagedplayer == null || !damagedplayer.ConnectionAlive || !damagedplayer.Active)
                {
                    return;
                }
                if (!attackingplayer.TPlayer.hostile || !damagedplayer.TPlayer.hostile)
                {
                    return;
                }
                if (playerHitReason.SourcePlayerIndex == -1)
                {
                    return;
                }

                int damage    = data.ReadInt16();
                int knockback = data.ReadByte() - 1;

                //Cancels client damage handling and makes it so the server does all the damage handling
                args.Handled = true;

                //Send the damage packet as long as they're not immune themselves
                if (!attackingplayer.TPlayer.immune)
                {
                    if (Math.Abs(iframeTime[damagedplayer.Index] - DateTime.Now.ToFileTimeUtc()) >= config.iframeTime)
                    {
                        NetMessage.SendPlayerHurt(damagedplayer.Index, playerHitReason,
                                                  damage, knockback, false, true, 5);

                        iframeTime[damagedplayer.Index] = DateTime.Now.ToFileTimeUtc();
                    }
                }
            }
        }
Example #9
0
 private void NetHooks_GetData(GetDataEventArgs args)
 {
     if (args.MsgID == PacketTypes.PlayerDeathV2)
     {
         args.Msg.reader.BaseStream.Position = args.Index;
         var playerId    = args.Msg.whoAmI;
         var tPlayer     = TShock.Players[playerId];
         var p           = Main.player[playerId];
         var deathReason = "";
         using (var reader = new BinaryReader(new MemoryStream(args.Msg.readBuffer, args.Index, args.Length)))
         {
             reader.ReadByte();
             var reason = PlayerDeathReason.FromReader(reader);
             deathReason = Convert.ToString(reason.GetDeathText(p.name));
             if (!TShock.ServerSideCharacterConfig.Enabled)
             {
                 return;
             }
             InventoryDrop(Main.player[playerId], Main.player[playerId].inventory, 0, InventoryDDR, InventoryDR);
             ArmDrop(Main.player[playerId], Main.player[playerId].armor, 59, ArmDDR, ArmDR);
             MiscItemDrop(Main.player[playerId], Main.player[playerId].miscEquips, 89, MEDDR, MEDR);
         }
     }
     if (args.MsgID == PacketTypes.PlayerUpdate)
     {
         var playerId = args.Msg.whoAmI;
         var tPlayer  = TShock.Players[playerId];
         var p        = Main.player[playerId];
         if (!TShock.ServerSideCharacterConfig.Enabled || tPlayer.User == null)
         {
             return;
         }
         else
         {
             var ppos = DB.GetPos(tPlayer.User.ID);
             if (ppos.indatabase)
             {
                 if (tPlayer.Teleport(ppos.x, ppos.y))
                 {
                     DB.UpdateStatus(false, tPlayer.User.ID);
                 }
             }
         }
     }
 }
        public PlayerHurtArgs(GetDataEventArgs args, MemoryStream data, PvPPlayer attacker)
        {
            Args = args;

            var target          = PvPController.PvPers[data.ReadByte()];
            var playerHitReason = PlayerDeathReason.FromReader(new BinaryReader(data));

            if (target == null || !target.ConnectionAlive || !target.Active)
            {
                IsPvPDamage = false;
                return;
            }

            if (playerHitReason.SourcePlayerIndex == -1)
            {
                IsPvPDamage      = false;
                target.LastHitBy = null;
                return;
            }

            Projectile = playerHitReason.SourceProjectileIndex == -1 ?
                         null : attacker.ProjTracker.Projectiles[playerHitReason.SourceProjectileType];

            int int1 = data.ReadInt16(); //damage
            int int2 = data.ReadByte();  //knockback

            target.LastHitBy         = attacker;
            target.LastHitWeapon     = Weapon;
            target.LastHitProjectile = Projectile;

            Attacker = attacker;
            Target   = target;

            Weapon          = Projectile == null ? attacker.GetPlayerItem : Projectile.ItemOriginated;
            InflictedDamage = PvPController.Config.EnableDamageChanges ? target.GetDamageDealt(attacker, Weapon, Projectile) : int1;
            DamageReceived  = target.GetDamageReceived(InflictedDamage);
            HitDirection    = int2 - 1;
            Crit            = attacker.GetCrit(Weapon);
            PlayerHitReason = playerHitReason;
            IsPvPDamage     = true;
        }
Example #11
0
        private bool RecordPlayerDeath(Player player, MemoryStream data)
        {
            // Unused initial ID, read byte so FromReader is properly aligned in stream.
            data.ReadByte();
            PlayerDeathReason playerDeathReason = PlayerDeathReason.FromReader(new BinaryReader(data));

            // Record failure in current death repository.
            string killerName      = GetNameOfKiller(player, playerDeathReason);
            int    totalDeathCount = deathRecords.RecordDeath(player.name, killerName);

            // Log objects for reference.
            string deathText = playerDeathReason.GetDeathText(player.name).ToString();

            logWriter.ServerWriteLine(JsonConvert.SerializeObject(playerDeathReason), TraceLevel.Info);
            logWriter.ServerWriteLine(string.Format("{0} {1}->{2} ({3})", player.name, deathText, killerName, totalDeathCount), TraceLevel.Info);

            // Broadcast message to server.
            string serverMessage = GetServerMessage(player, killerName, totalDeathCount);

            TShockAPI.Utils.Instance.Broadcast(serverMessage, 255, 0, 0);

            return(true);
        }
Example #12
0
        void OnGetData(GetDataEventArgs e)
        {
            if (!e.Handled && TShock.Players[e.Msg.whoAmI].GetData <bool>("buildmode"))
            {
                Player   plr   = Main.player[e.Msg.whoAmI];
                TSPlayer tsplr = TShock.Players[e.Msg.whoAmI];

                switch (e.MsgID)
                {
                case PacketTypes.PlayerHurtV2:
                    using (MemoryStream data = new MemoryStream(e.Msg.readBuffer, e.Index, e.Length))
                    {
                        data.ReadByte();
                        PlayerDeathReason.FromReader(new BinaryReader(data));
                        int damage = data.ReadInt16();
                        tsplr.Heal((int)Terraria.Main.CalculateDamagePlayersTake(damage, plr.statDefense) + 2);
                    }
                    break;

                case PacketTypes.Teleport:
                    if ((e.Msg.readBuffer[3] == 0))
                    {
                        List <int> buffs = new List <int>(plr.buffType);
                        if (buffs.Contains(Terraria.ID.BuffID.ChaosState) && plr.inventory[plr.selectedItem].netID == Terraria.ID.ItemID.RodofDiscord)                             //rod 1326
                        {
                            tsplr.Heal(plr.statLifeMax2 / 7);
                        }
                    }
                    break;

                case PacketTypes.PaintTile:
                case PacketTypes.PaintWall:                        //0%
                {
                    int           count    = 0;
                    int           type     = e.Msg.readBuffer[7];
                    Terraria.Item lastItem = null;
                    foreach (Item i in plr.inventory)
                    {
                        if (i.paint == type)
                        {
                            lastItem = i;
                            count   += i.stack;
                        }
                    }
                    if (count <= 10 && lastItem != null)
                    {
                        tsplr.GiveItem(lastItem.type, lastItem.maxStack + 1 - count);
                    }
                }
                break;

                case PacketTypes.Tile:                        //1%
                {
                    int count = 0;
                    int type  = e.Msg.readBuffer[e.Index];
                    switch (type)
                    {
                    case 1:                                                                             //PlaceTile
                    case 3:                                                                             //PlaceWall
                    case 21:                                                                            //ReplaceTile
                    case 22:                                                                            //ReplaceWall
                        if (plr.inventory[plr.selectedItem].type != Terraria.ID.ItemID.StaffofRegrowth) //230
                        {
                            int tile = e.Msg.readBuffer[e.Index + 5];
                            if (tsplr.SelectedItem.tileWand > 0)
                            {
                                tile = tsplr.SelectedItem.tileWand;
                            }
                            Item lastItem = null;
                            foreach (Item i in plr.inventory)
                            {
                                if (i.createTile == tile || i.createWall == tile)
                                {
                                    lastItem = i;
                                    count   += i.stack;
                                }
                            }
                            if (count <= 10 && lastItem != null)
                            {
                                tsplr.GiveItem(lastItem.type, lastItem.maxStack + 1 - count);
                            }
                        }
                        break;

                    // Placing wires
                    case 5:                                              //Red
                    case 10:                                             //Blue
                    case 12:                                             //Green
                    case 16:                                             //Yellow
                        foreach (Item i in plr.inventory)
                        {
                            if (i.type == Terraria.ID.ItemID.Wire)                                             //530
                            {
                                count += i.stack;
                            }
                        }
                        if (count <= 10)
                        {
                            tsplr.GiveItem(Terraria.ID.ItemID.Wire, 1000 - count);
                        }
                        break;

                    case 8:                                             //Place Actuator
                        foreach (Item i in plr.inventory)
                        {
                            if (i.type == Terraria.ID.ItemID.Actuator)                                             //849
                            {
                                count += i.stack;
                            }
                        }
                        if (count <= 10)
                        {
                            tsplr.GiveItem(Terraria.ID.ItemID.Actuator, 1000 - count);
                        }
                        break;
                    }
                }
                break;
                }
            }
        }
Example #13
0
        private void OnNetGetData(GetDataEventArgs args)
        {
            if (args.Handled)
            {
                return;
            }

            //      if (args.MsgID == PacketTypes.NpcItemStrike || args.MsgID == PacketTypes.NpcStrike)
            //      {
            //          var player = TShock.Players[args.Msg.whoAmI];
            //          using (var reader = new BinaryReader(new MemoryStream(args.Msg.readBuffer, args.Index, args.Length)))
            //          {
            //              var npcIndex = reader.ReadInt16();

            //              void DoStrike(double damage, bool isCritical)
            //              {
            //                  if (damage < 1.0)
            //                  {
            //                      return;
            //                  }

            //                  var npc = Main.npc[npcIndex];
            //                  var defense = npc.defense;
            //                  defense -= npc.ichor ? 20 : 0;
            //                  defense -= npc.betsysCurse ? 40 : 0;
            //                  defense = Math.Max(0, defense);

            //                  damage = Main.CalculateDamage((int)damage, defense);
            //                  damage *= isCritical ? 2.0 : 1.0;
            //                  damage *= Math.Max(1.0, npc.takenDamageMultiplier);

            //                  var damages = _npcDamages.GetOrCreateValue(npc);
            //                  damages[player] = damages.Get(player) + (int)damage;

            ////Debug.Print($"Leveling - DoStrike! Damage: {damage}, Critical: {isCritical}");

            //if (npc.life <= damage)
            //                  {
            //                      KillNpc(npc);
            //                  }
            //              }

            //              if (args.MsgID == PacketTypes.NpcItemStrike)
            //              {
            //                  DoStrike(player.SelectedItem.damage, false);
            //              }
            //              else
            //              {
            //                  var damage = reader.ReadInt16();
            //                  reader.ReadSingle();
            //                  reader.ReadByte();
            //                  var isCritical = reader.ReadByte() == 1;
            //                  DoStrike(damage, isCritical);
            //              }
            //          }
            //      }
            //else if (args.MsgID == PacketTypes.PlayerDeathV2)
            if (args.MsgID == PacketTypes.PlayerDeathV2)
            {
                var player  = TShock.Players[args.Msg.whoAmI];
                var session = GetOrCreateSession(player);
                using (var reader = new BinaryReader(new MemoryStream(args.Msg.readBuffer, args.Index, args.Length)))
                {
                    reader.ReadByte();
                    var deathReason = PlayerDeathReason.FromReader(reader);
                    reader.ReadInt16();
                    reader.ReadByte();
                    var wasPvP = ((BitsByte)reader.ReadByte())[0];
                    if (wasPvP)
                    {
                        var otherPlayer = deathReason.SourcePlayerIndex >= 0
                            ? TShock.Players[deathReason.SourcePlayerIndex]
                            : null;
                        if (otherPlayer == player)
                        {
                            return;
                        }

                        var expLoss = (long)Math.Round(Math.Max(
                                                           Config.Instance.DeathPenaltyPvPMultiplier * session.Exp,
                                                           Config.Instance.DeathPenaltyMinimum));
                        session.GiveExp(-expLoss);
                        session.AddExpToReport(-expLoss);

                        if (otherPlayer != null)
                        {
                            var otherSession = GetOrCreateSession(otherPlayer);
                            otherSession.GiveExp(expLoss);
                            otherSession.AddExpToReport(expLoss);
                        }
                    }
                    else
                    {
                        var expLoss = (long)Math.Round(Math.Max(
                                                           Config.Instance.DeathPenaltyMultiplier *
                                                           (session.Class.DeathPenaltyMultiplierOverride ?? 1.0) *
                                                           session.Exp,
                                                           Config.Instance.DeathPenaltyMinimum));
                        session.GiveExp(-expLoss);
                        session.AddExpToReport(-expLoss);
                    }
                }
            }
        }
Example #14
0
        private void GetData(GetDataEventArgs args)
        {
            MemoryStream data     = new MemoryStream(args.Msg.readBuffer, args.Index, args.Length);
            PvPPlayer    attacker = pvpers[args.Msg.whoAmI];

            if (attacker == null || !attacker.TPlayer.active || !attacker.ConnectionAlive)
            {
                return;
            }
            if (!attacker.TPlayer.hostile)
            {
                return;
            }

            switch (args.MsgID)
            {
            case PacketTypes.PlayerHurtV2:
                PvPPlayer         target          = pvpers[data.ReadByte()];
                PlayerDeathReason playerHitReason = PlayerDeathReason.FromReader(new BinaryReader(data));
                if (target == null || !target.ConnectionAlive || !target.Active)
                {
                    return;
                }
                if (playerHitReason.SourcePlayerIndex == -1)
                {
                    target.lastHitBy = null;
                    return;
                }

                PvPProjectile projectile = playerHitReason.SourceProjectileIndex == -1 ? null : projectiles[playerHitReason.SourceProjectileIndex];
                PvPItem       weapon     = projectile == null?attacker.GetPlayerItem() : projectile.itemOriginated;

                int inflictedDamage = PvPController.config.enableDamageChanges ? target.GetDamageDealt(attacker, weapon, projectile) : data.ReadByte();
                int damageReceived  = target.GetDamageReceived(inflictedDamage);
                data.ReadByte(); data.ReadByte();
                int knockback = data.ReadByte() - 1;

                target.lastHitBy         = attacker;
                target.lastHitWeapon     = weapon;
                target.lastHitProjectile = projectile;

                DataHandler.OnPlayerHurtted(args, attacker, target, weapon, projectile, playerHitReason,
                                            inflictedDamage, damageReceived, knockback);

                break;

            case PacketTypes.TogglePvp:
                DataHandler.OnPvPToggled(attacker);
                break;

            case PacketTypes.PlayerSlot:
                data.ReadByte();
                int slot = data.ReadByte();
                DataHandler.OnPlayerSlotUpdated(attacker, slot);
                break;

            case PacketTypes.PlayerDeathV2:
                DataHandler.OnPlayerDead(attacker);
                break;

            case PacketTypes.ProjectileDestroy:
                DataHandler.OnProjectileDestroyed(data);
                break;

            case PacketTypes.PlayerUpdate:
                DataHandler.OnPlayerUpdated(data, attacker);
                break;
            }
        }
        private void NetHooks_GetData(GetDataEventArgs e)
        {
            if (e == null || this.isDisposed || e.Handled)
            {
                return;
            }

            TSPlayer player = TShock.Players[e.Msg.whoAmI];

            if (player == null)
            {
                return;
            }

            try {
                switch (e.MsgID)
                {
                case PacketTypes.Tile: {
                    if (this.TileEdit == null)
                    {
                        break;
                    }

                    int editType = e.Msg.readBuffer[e.Index];
                    int x        = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 1);
                    int y        = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 3);

                    if (!TerrariaUtils.Tiles.IsValidCoord(x, y))
                    {
                        return;
                    }

                    int blockType   = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 5);
                    int objectStyle = e.Msg.readBuffer[e.Index + 7];

                    e.Handled = this.OnTileEdit(
                        new TileEditEventArgs(player, (TileEditType)editType, new DPoint(x, y), blockType, objectStyle)
                        );
                    break;
                }

                case PacketTypes.PlaceObject: {
                    if (this.ObjectPlacement == null && this.TileEdit == null)
                    {
                        break;
                    }

                    int x = BitConverter.ToInt16(e.Msg.readBuffer, e.Index);
                    int y = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 2);

                    if (!TerrariaUtils.Tiles.IsValidCoord(x, y))
                    {
                        return;
                    }

                    int  blockType   = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 4);
                    int  objectStyle = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 6);
                    int  alternative = e.Msg.readBuffer[e.Index + 8];
                    int  random      = ((sbyte)e.Msg.readBuffer[e.Index + 9]);
                    bool direction   = BitConverter.ToBoolean(e.Msg.readBuffer, e.Index + 10);

                    if (this.InvokeTileOnObjectPlacement)
                    {
                        e.Handled = this.OnTileEdit(
                            new TileEditEventArgs(player, TileEditType.PlaceTile, new DPoint(x, y), blockType, objectStyle
                                                  ));
                    }

                    if (!e.Handled)
                    {
                        e.Handled = this.OnObjectPlacement(
                            new ObjectPlacementEventArgs(player, new DPoint(x, y), blockType, objectStyle, alternative, random, direction
                                                         ));
                    }

                    break;
                }

                // Note: As for TileKill and TileKillNoItem, blockId will be of "1" if the player attempted to destroy
                // a tile but didn't succeed yet, and will be of "0" as the tile is actually destroyed.
                // However, there's one exception with Chests, they will never send their actual destroy packet, except a hack
                // tool is used, it seems.
                case PacketTypes.TileKill: {
                    int type = e.Msg.readBuffer[e.Index];
                    int x    = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 1);
                    int y    = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 3);

                    if (!TerrariaUtils.Tiles.IsValidCoord(x, y))
                    {
                        break;
                    }

                    int style = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 5);

                    if (type == 0 || type == 2 || type == 4) // Chest placement / Dresser Placement / Chest2 placement
                    {
                        e.Handled = this.OnChestPlace(new ChestPlaceEventArgs(player, new DPoint(x, y), type, style));
                    }
                    else // Chest or Dresser or Chest2 kill
                    {
                        int tileType = TerrariaUtils.Tiles[x, y].type;
                        if (tileType != TileID.Containers && tileType != TileID.Dressers)
                        {
                            break;
                        }

                        if (this.InvokeTileEditOnChestKill)
                        {
                            e.Handled = this.OnTileEdit(new TileEditEventArgs(player, TileEditType.TileKill, new DPoint(x, y), 0, 0));
                        }

                        if (!e.Handled)
                        {
                            e.Handled = this.OnChestKill(new TileLocationEventArgs(player, new DPoint(x, y)));
                        }
                    }

                    break;
                }

                case PacketTypes.ChestOpen: {
                    if (this.ChestOpen == null && this.ChestRename == null)
                    {
                        break;
                    }

                    int chestIndex = BitConverter.ToInt16(e.Msg.readBuffer, e.Index);
                    int x          = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 2);
                    int y          = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 4);

                    if (!TerrariaUtils.Tiles.IsValidCoord(x, y))
                    {
                        break;
                    }

                    int nameLength = e.Msg.readBuffer[e.Index + 6];

                    string newName = string.Empty;
                    if ((nameLength > 0 && nameLength <= 20) || nameLength == 255) // Name change requested?
                    {
                        if (nameLength != 255)
                        {
                            newName = Encoding.UTF8.GetString(e.Msg.readBuffer, e.Index + 8, nameLength);
                        }

                        e.Handled = this.OnChestRename(new ChestRenameEventArgs(player, chestIndex, newName));
                    }

                    if (!e.Handled)
                    {
                        e.Handled = this.OnChestOpen(new ChestOpenEventArgs(player, chestIndex, new DPoint(x, y)));
                    }

                    break;
                }

                case PacketTypes.ChestGetContents: {
                    if (this.ChestGetContents == null)
                    {
                        break;
                    }

                    int x = BitConverter.ToInt16(e.Msg.readBuffer, e.Index);
                    int y = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 2);

                    if (!TerrariaUtils.Tiles.IsValidCoord(x, y) || !Main.tile[x, y].active())
                    {
                        return;
                    }

                    e.Handled = this.OnChestGetContents(new TileLocationEventArgs(player, new DPoint(x, y)));
                    break;
                }

                case PacketTypes.ChestItem: {
                    if (this.ChestModifySlot == null)
                    {
                        break;
                    }

                    int chestIndex    = BitConverter.ToInt16(e.Msg.readBuffer, e.Index);
                    int slotIndex     = e.Msg.readBuffer[e.Index + 2];
                    int itemStackSize = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 3);
                    int itemPrefix    = e.Msg.readBuffer[e.Index + 5];
                    int itemType      = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 6);

                    if (chestIndex >= Main.chest.Length || slotIndex > 39)
                    {
                        break;
                    }

                    e.Handled = this.OnChestModifySlot(new ChestModifySlotEventArgs(
                                                           player, chestIndex, slotIndex, new ItemData(itemPrefix, itemType, itemStackSize)
                                                           ));
                    break;
                }

                case PacketTypes.SignNew: {
                    if (this.SignEdit == null)
                    {
                        break;
                    }

                    int    signIndex = BitConverter.ToInt16(e.Msg.readBuffer, e.Index);
                    int    x         = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 2);
                    int    y         = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 4);
                    string newText;
                    using (MemoryStream stream = new MemoryStream(e.Msg.readBuffer, e.Index + 6, e.Length - 7))
                        newText = new BinaryReader(stream).ReadString();

                    if (!TerrariaUtils.Tiles.IsValidCoord(x, y) || !Main.tile[x, y].active())
                    {
                        return;
                    }

                    e.Handled = this.OnSignEdit(new SignEditEventArgs(player, signIndex, new DPoint(x, y), newText));
                    break;
                }

                case PacketTypes.SignRead: {
                    if (this.SignRead == null)
                    {
                        break;
                    }

                    int x = BitConverter.ToInt16(e.Msg.readBuffer, e.Index);
                    int y = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 2);

                    if (!TerrariaUtils.Tiles.IsValidCoord(x, y))
                    {
                        break;
                    }

                    e.Handled = this.OnSignRead(new TileLocationEventArgs(player, new DPoint(x, y)));
                    break;
                }

                case PacketTypes.HitSwitch: {
                    if (this.HitSwitch == null)
                    {
                        break;
                    }

                    int x = BitConverter.ToInt16(e.Msg.readBuffer, e.Index);
                    int y = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 2);

                    if (!TerrariaUtils.Tiles.IsValidCoord(x, y) || !Main.tile[x, y].active())
                    {
                        return;
                    }

                    // For some reason, TShock doesn't handle this packet so we just do our own checks.
                    if (TShock.CheckIgnores(player))
                    {
                        return;
                    }
                    if (TShock.CheckRangePermission(player, x, y, 32))
                    {
                        return;
                    }

                    e.Handled = this.OnHitSwitch(new TileLocationEventArgs(player, new DPoint(x, y)));
                    break;
                }

                case PacketTypes.SpawnBossorInvasion: {
                    if (this.BossSpawn == null)
                    {
                        break;
                    }

                    //int playerIndex = BitConverter.ToInt32(e.Msg.readBuffer, e.Index);
                    int bossType = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 2);

                    e.Handled = this.OnBossSpawn(new BossSpawnEventArgs(player, (BossType)bossType));
                    break;
                }

                case PacketTypes.ItemDrop:
                case PacketTypes.UpdateItemDrop: {
                    if (this.ItemUpdate == null)
                    {
                        break;
                    }

                    int   itemIndex     = BitConverter.ToInt16(e.Msg.readBuffer, e.Index);
                    float x             = BitConverter.ToSingle(e.Msg.readBuffer, e.Index + 2);
                    float y             = BitConverter.ToSingle(e.Msg.readBuffer, e.Index + 6);
                    float velocityX     = BitConverter.ToSingle(e.Msg.readBuffer, e.Index + 10);
                    float velocityY     = BitConverter.ToSingle(e.Msg.readBuffer, e.Index + 14);
                    int   itemStackSize = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 18);
                    int   itemPrefix    = e.Msg.readBuffer[e.Index + 20];
                    bool  noDelay       = (e.Msg.readBuffer[e.Index + 21] != 0);
                    int   itemType      = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 22);

                    // If it is actually an item pick up, then ensure a valid item index.
                    if (itemType == 0 && (itemIndex < 0 || itemIndex >= Main.item.Length))
                    {
                        break;
                    }

                    e.Handled = this.OnItemUpdate(new ItemUpdateEventArgs(
                                                      player, itemIndex, new Vector2(x, y), new Vector2(velocityX, velocityY),
                                                      noDelay, new ItemData(itemPrefix, itemType, itemStackSize)
                                                      ));
                    break;
                }

                case PacketTypes.ItemOwner: {
                    if (this.ItemOwner == null)
                    {
                        break;
                    }

                    int      itemIndex           = BitConverter.ToInt16(e.Msg.readBuffer, e.Index);
                    int      newOwnerPlayerIndex = e.Msg.readBuffer[e.Index + 2];
                    TSPlayer newOwner;
                    if (newOwnerPlayerIndex < 255)
                    {
                        newOwner = TShock.Players[newOwnerPlayerIndex];
                    }
                    else
                    {
                        break;
                    }

                    e.Handled = this.OnItemOwner(new ItemOwnerEventArgs(player, itemIndex, newOwner));
                    break;
                }

                case PacketTypes.ForceItemIntoNearestChest: {
                    if (this.QuickStackNearby == null)
                    {
                        break;
                    }

                    int slotIndex = e.Msg.readBuffer[e.Index];
                    if (slotIndex >= TSPlayer.Server.TPlayer.inventory.Length)
                    {
                        break;
                    }

                    e.Handled = this.OnQuickStackNearby(new PlayerSlotEventArgs(player, slotIndex));
                    break;
                }

                case PacketTypes.PlayerSlot: {
                    if (this.PlayerModifySlot == null)
                    {
                        break;
                    }

                    //byte playerIndex = e.Msg.readBuffer[e.Index];
                    int slotIndex     = e.Msg.readBuffer[e.Index + 1];
                    int itemStackSize = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 2);
                    int itemPrefix    = e.Msg.readBuffer[e.Index + 4];
                    int itemType      = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 5);

                    Player tServerPlayer = TSPlayer.Server.TPlayer;
                    if (slotIndex >= tServerPlayer.inventory.Length + tServerPlayer.bank.item.Length + tServerPlayer.bank2.item.Length)
                    {
                        break;
                    }

                    e.Handled = this.OnPlayerModifySlot(new PlayerModifySlotEventArgs(
                                                            player, slotIndex, new ItemData(itemPrefix, itemType, itemStackSize)
                                                            ));
                    break;
                }

                case PacketTypes.LiquidSet: {
                    if (this.LiquidSet == null)
                    {
                        break;
                    }

                    int x = BitConverter.ToInt16(e.Msg.readBuffer, e.Index);
                    int y = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 2);

                    if (!TerrariaUtils.Tiles.IsValidCoord(x, y))
                    {
                        break;
                    }

                    int        liquidAmount = e.Msg.readBuffer[e.Index + 4];
                    LiquidKind liquidKind   = (LiquidKind)e.Msg.readBuffer[e.Index + 5];

                    e.Handled = this.OnLiquidSet(new LiquidSetEventArgs(player, new DPoint(x, y), liquidAmount, liquidKind));
                    break;
                }

                case PacketTypes.DoorUse: {
                    if (this.DoorUse == null)
                    {
                        break;
                    }

                    byte action = e.Msg.readBuffer[e.Index];
                    int  x      = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 1);
                    int  y      = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 3);

                    if (!TerrariaUtils.Tiles.IsValidCoord(x, y))
                    {
                        break;
                    }

                    int direction = e.Msg.readBuffer[e.Index + 5];

                    Direction actualDirection = Direction.Right;
                    if (direction == 0)
                    {
                        actualDirection = Direction.Left;
                    }

                    e.Handled = this.OnDoorUse(new DoorUseEventArgs(player, new DPoint(x, y), (DoorAction)action, actualDirection));
                    break;
                }

                case PacketTypes.PlayerSpawn: {
                    if (this.PlayerSpawn == null)
                    {
                        break;
                    }

                    int playerIndex = e.Msg.readBuffer[e.Index];
                    int spawnX      = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 1);
                    int spawnY      = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 3);

                    if (!TerrariaUtils.Tiles.IsValidCoord(spawnX, spawnY))
                    {
                        break;
                    }

                    e.Handled = this.OnPlayerSpawn(new PlayerSpawnEventArgs(player, new DPoint(spawnX, spawnY)));
                    break;
                }

                // Note: Also door unlock
                case PacketTypes.ChestUnlock: {
                    if (this.ChestUnlock == null)
                    {
                        break;
                    }

                    UnlockType unlockType = (UnlockType)e.Msg.readBuffer[e.Index];
                    int        chestX     = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 1);
                    int        chestY     = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 3);

                    if (!TerrariaUtils.Tiles.IsValidCoord(chestX, chestY))
                    {
                        break;
                    }

                    e.Handled = this.OnChestUnlock(new UnlockEventArgs(player, new DPoint(chestX, chestY), unlockType));
                    break;
                }

                case (PacketTypes)25: {
                    if (this.ChatText == null)
                    {
                        break;
                    }

                    short playerIndex = e.Msg.readBuffer[e.Index];
                    if (playerIndex != e.Msg.whoAmI)
                    {
                        break;
                    }

                    int    colorR = e.Msg.readBuffer[e.Index + 1];
                    int    colorG = e.Msg.readBuffer[e.Index + 2];
                    int    colorB = e.Msg.readBuffer[e.Index + 3];
                    string text   = Encoding.UTF8.GetString(e.Msg.readBuffer, e.Index + 4, e.Length - 5);

                    e.Handled = this.OnChatText(new ChatTextEventArgs(player, new Color(colorR, colorG, colorB), text));
                    break;
                }

                case PacketTypes.TileSendSquare: {
                    if (this.SendTileSquare == null)
                    {
                        break;
                    }

                    int size  = BitConverter.ToInt16(e.Msg.readBuffer, e.Index);
                    int tileX = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 2);
                    int tileY = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 4);

                    if (!TerrariaUtils.Tiles.IsValidCoord(tileX, tileY))
                    {
                        break;
                    }

                    e.Handled = this.OnSendTileSquare(new SendTileSquareEventArgs(player, new DPoint(tileX, tileY), size));
                    break;
                }

                case PacketTypes.PaintTile: {
                    if (this.TilePaint == null)
                    {
                        break;
                    }

                    int tileX = BitConverter.ToInt16(e.Msg.readBuffer, e.Index);
                    int tileY = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 2);

                    if (!TerrariaUtils.Tiles.IsValidCoord(tileX, tileY))
                    {
                        break;
                    }

                    int color = e.Msg.readBuffer[e.Index + 8];

                    e.Handled = this.OnTilePaint(new TilePaintEventArgs(player, new DPoint(tileX, tileY), (PaintColor)color));
                    break;
                }

                case PacketTypes.PlayerDeathV2: {
                    if (this.PlayerDeath == null)
                    {
                        break;
                    }

                    using (BinaryReader reader = new BinaryReader(new MemoryStream(e.Msg.readBuffer, e.Index, e.Length, false))) {
                        int  playerIndex = reader.ReadByte();
                        var  deathReason = PlayerDeathReason.FromReader(reader);
                        int  damage      = reader.ReadInt16();
                        int  direction   = reader.ReadByte() - 1;
                        bool pvp         = reader.ReadByte() != 0;

                        e.Handled = this.OnPlayerDeath(new PlayerDeathEventArgs(player, deathReason, direction, damage, pvp));
                    }

                    break;
                }

                case PacketTypes.Teleport: {
                    if (this.Teleport == null)
                    {
                        break;
                    }

                    BitsByte flags        = e.Msg.readBuffer[e.Index];
                    int      playerIndex  = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 1);
                    float    x            = BitConverter.ToSingle(e.Msg.readBuffer, e.Index + 3);
                    float    y            = BitConverter.ToSingle(e.Msg.readBuffer, e.Index + 7);
                    Vector2  destLocation = new Vector2(x, y);

                    TeleportType tpType = TeleportType.PlayerToPos;
                    if (flags[0])
                    {
                        tpType = TeleportType.NpcToPos;
                    }
                    if (flags[1])
                    {
                        if (flags[0])
                        {
                            tpType = TeleportType.Unknown;
                        }
                        else
                        {
                            tpType = TeleportType.PlayerNearPlayerWormhole;
                        }
                    }

                    e.Handled = this.OnTeleport(new TeleportEventArgs(player, destLocation, tpType));
                    break;
                }

                case PacketTypes.NpcStrike: {
                    if (this.NpcTookDamage == null)
                    {
                        break;
                    }

                    int   npcIndex     = BitConverter.ToInt16(e.Msg.readBuffer, e.Index);
                    int   damage       = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 2);
                    float knockback    = BitConverter.ToSingle(e.Msg.readBuffer, e.Index + 4);
                    int   hitDirection = e.Msg.readBuffer[e.Index + 8] - 1; // 1 left, -1 right
                    bool  isCritical   = (e.Msg.readBuffer[e.Index + 9] == 1);

                    e.Handled = this.OnNpcTookDamage(new NpcTookDamageEventArgs(player, npcIndex, damage, knockback, hitDirection, isCritical));
                    break;
                }

                case PacketTypes.MassWireOperation: {
                    if (this.MassWireOperation == null && this.TileEdit == null)
                    {
                        break;
                    }

                    int x1 = BitConverter.ToInt16(e.Msg.readBuffer, e.Index);
                    int y1 = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 2);
                    int x2 = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 4);
                    int y2 = BitConverter.ToInt16(e.Msg.readBuffer, e.Index + 6);
                    WiresUI.Settings.MultiToolMode toolMode = (WiresUI.Settings.MultiToolMode)e.Msg.readBuffer[e.Index + 7];

                    DPoint startLocation = new DPoint(x1, y1);
                    DPoint endLocation   = new DPoint(x2, y2);
                    e.Handled = this.OnMassWireOperation(new MassWireOperationEventArgs(player, startLocation, endLocation, toolMode));
                    if (!e.Handled && this.InvokeTileEditOnMasswireOperation != MassWireOpTileEditInvokeType.DontInvoke)
                    {
                        e.Handled = this.RaiseTileEditDependingOnToolMode(player, startLocation, toolMode);

                        if (startLocation != endLocation)
                        {
                            e.Handled = this.RaiseTileEditDependingOnToolMode(player, endLocation, toolMode) || e.Handled;
                        }
                    }
                    break;
                }
                }
            } catch (Exception ex) {
                ServerApi.LogWriter.PluginWriteLine(
                    this.Plugin, $"Internal error on handling data packet {e.MsgID}. Exception details: \n{ex}", TraceLevel.Error
                    );
            }
        }
Example #16
0
        private void OnGetData(GetDataEventArgs args)
        {
            var player = TShock.Players[args.Msg.whoAmI];

            if (args.Handled || player == null || !player.ContainsData("buildmode") || !player.GetData <bool>("buildmode"))
            {
                return;
            }

            switch (args.MsgID)
            {
            case PacketTypes.Teleport:
                if (player.TPlayer.FindBuffIndex(88) > -1)
                {
                    player.Heal(100);
                }
                break;

            case PacketTypes.PaintTile:
            case PacketTypes.PaintWall:
                int count = 0;
                int type  = args.Msg.readBuffer[args.Index + 4];

                Item lastItem = null;
                foreach (Item i in player.TPlayer.inventory)
                {
                    if (i.paint == type)
                    {
                        lastItem = i;
                        count   += i.stack;
                    }
                }
                if (count <= 5 && lastItem != null)
                {
                    player.GiveItem(lastItem.type, lastItem.stack);
                }
                break;

            case PacketTypes.Tile:
                count = 0;
                type  = args.Msg.readBuffer[args.Index];
                if ((type == 1 || type == 3 || type == 21 || type == 22) && player.TPlayer.inventory[player.TPlayer.selectedItem].type != 213)     // Tile or Wall or ReplaceTile or ReplaceWall
                {
                    short tile = args.Msg.readBuffer[args.Index + 5];
                    if (player.SelectedItem.tileWand > 0)
                    {
                        tile = (short)player.SelectedItem.tileWand;
                    }
                    lastItem = null;
                    foreach (Item i in player.TPlayer.inventory)
                    {
                        if (((type == 1 || type == 21) && i.createTile == tile) || ((type == 3 || type == 22) && i.createWall == tile))
                        {
                            lastItem = i;
                            count   += i.stack;
                        }
                    }
                    if (count <= 5 && lastItem != null)
                    {
                        player.GiveItem(lastItem.type, lastItem.maxStack - count);
                    }
                }
                else if (type == 5 || type == 10 || type == 12 || type == 16)     // Wire, Wire2, Wire3, Wire4
                {
                    foreach (Item i in player.TPlayer.inventory)
                    {
                        if (i.type == 530)
                        {
                            count += i.stack;
                        }
                    }
                    if (count <= 5)
                    {
                        player.GiveItem(530, 1000 - count);
                    }
                }
                else if (type == 8)     // Actuator
                {
                    foreach (Item i in player.TPlayer.inventory)
                    {
                        if (i.type == 849)
                        {
                            count += i.stack;
                        }
                    }
                    if (count <= 5)
                    {
                        player.GiveItem(849, 1000 - count);
                    }
                }
                break;

            case PacketTypes.PlayerHurtV2:
                using (var reader = new BinaryReader(new MemoryStream(args.Msg.readBuffer, args.Index, args.Length)))
                {
                    reader.ReadByte();
                    PlayerDeathReason.FromReader(reader);

                    short damage = reader.ReadInt16();
                    player.Heal(damage);
                    player.TPlayer.statLife = player.TPlayer.statLifeMax2;
                }
                break;
            }
        }
        private void OnNetGetData(GetDataEventArgs args)
        {
            if (args.Handled)
            {
                return;
            }

            switch (args.MsgID)
            {
            case PacketTypes.Tile:
                using (var reader = new BinaryReader(new MemoryStream(args.Msg.readBuffer, args.Index, args.Length)))
                {
                    var action = reader.ReadByte();

                    if (action >= 0 && action <= 3)                        //0 kill, 1 place tile, 2 kill, 3 place wall
                    {
                        var tileX = reader.ReadInt16();
                        var tileY = reader.ReadInt16();
                        var var1  = reader.ReadInt16();                               //kill tile status
                        var var2  = reader.ReadInt16();                               //place tile

                        //Debug.Print($"action: {action}");
                        //Debug.Print($"tileX: {tileX}");
                        //Debug.Print($"tileY: {tileY}");
                        //Debug.Print($"var1: {var1}");
                        //Debug.Print($"var2: {var2}");

                        var           player = TShock.Players[args.Msg.whoAmI];
                        TileSubTarget tileSubTarget;

                        if (action < 2)
                        {
                            tileSubTarget = TileSubTarget.Tile;
                        }
                        else
                        {
                            tileSubTarget = TileSubTarget.Wall;
                        }

                        if ((action == 0 || action == 2) && var1 == 0)
                        {
                            var tile = Main.tile[tileX, tileY];

                            //kill tile
                            if (action == 2 || tile.collisionType > 0)                                 //ignore grass
                            {
                                OnTileKilled(new TileChangedEventArgs(player, tileX, tileY, tile.type, tile.wall, tileSubTarget));
                                return;
                            }
                        }
                        else if (action == 1 || action == 3)                           // && var1 > 0)
                        {
                            //var1 should hold the type of the tile or wall we placed.

                            //place tile
                            OnTilePlaced(new TileChangedEventArgs(player, tileX, tileY, (ushort)var1, (byte)var1, tileSubTarget));
                            return;
                        }
                    }
                }

                break;

            case PacketTypes.PlayerDeathV2:
                //based off of MarioE's original code from the Leveling plugin...
                using (var reader = new BinaryReader(new MemoryStream(args.Msg.readBuffer, args.Index, args.Length)))
                {
                    var player = TShock.Players[args.Msg.whoAmI];

                    reader.ReadByte();
                    var deathReason = PlayerDeathReason.FromReader(reader);
                    reader.ReadInt16();
                    reader.ReadByte();
                    var wasPvP = ((BitsByte)reader.ReadByte())[0];
                    if (wasPvP)
                    {
                        var otherPlayer = deathReason.SourcePlayerIndex >= 0
                                                                ? TShock.Players[deathReason.SourcePlayerIndex]
                                                                : null;
                        if (otherPlayer == player)
                        {
                            return;
                        }

                        if (otherPlayer != null)
                        {
                            var reward = new DeathReward(player.Name, RewardReason.DeathPvP, otherPlayer.Name);
                            RewardDistributor.EnqueueReward(reward);
                        }
                        else
                        {
                            var reward = new DeathReward(player.Name, RewardReason.DeathPvP, "");
                            RewardDistributor.EnqueueReward(reward);
                        }
                    }
                    else
                    {
                        var reward = new DeathReward(player.Name, RewardReason.Death, "");
                        RewardDistributor.EnqueueReward(reward);
                    }
                }

                break;

            case PacketTypes.ProjectileNew:
                //Debug.Print("ProjectileNew!");

                using (var reader = new BinaryReader(new MemoryStream(args.Msg.readBuffer, args.Index, args.Length)))
                {
                    var projectileId = reader.ReadInt16();
                    reader.ReadSingle();
                    reader.ReadSingle();
                    reader.ReadSingle();
                    reader.ReadSingle();
                    reader.ReadSingle();
                    reader.ReadInt16();
                    var playerId = reader.ReadByte();
                    var type     = reader.ReadInt16();
                    //var aiFlags = reader.ReadByte();
                    //var ai0 = reader.ReadSingle();
                    //var ai1 = reader.ReadSingle();
                    //var projUUID = reader.ReadSingle();

                    PlayerFishingTracker.TryBeginFishing(playerId, projectileId, type);
                }

                break;

            case PacketTypes.ProjectileDestroy:
                //Debug.Print("ProjectileDestroy!");
                using (var reader = new BinaryReader(new MemoryStream(args.Msg.readBuffer, args.Index, args.Length)))
                {
                    var projectileId = reader.ReadInt16();
                    var ownerId      = reader.ReadByte();

                    PlayerFishingTracker.TryEndFishing(ownerId, projectileId);
                }

                break;

            case PacketTypes.PlayerSlot:
                //Debug.Print("PlayerSlot!");
                using (var reader = new BinaryReader(new MemoryStream(args.Msg.readBuffer, args.Index, args.Length)))
                {
                    var playerId = reader.ReadByte();
                    var slotId   = reader.ReadByte();
                    var stack    = reader.ReadInt16();
                    var prefix   = reader.ReadByte();
                    var itemId   = reader.ReadInt16();

                    if (PlayerFishingTracker.IsItemFromFishing(playerId))                           //, stack, prefix, itemId))
                    {
                        var player = TShock.Players[args.Msg.whoAmI];
                        var reward = new FishingReward(player.Name, stack, prefix, itemId);
                        RewardDistributor.EnqueueReward(reward);
                    }
                }

                break;

            default:
                break;
            }
        }