private bool RaiseTileEditDependingOnToolMode(TSPlayer player, DPoint tileLocation, WiresUI.Settings.MultiToolMode toolMode)
        {
            bool handled = false;
            bool isPlace = (toolMode & WiresUI.Settings.MultiToolMode.Cutter) == 0;

            if (this.InvokeTileEditOnMasswireOperation == MassWireOpTileEditInvokeType.ForEach)
            {
                if ((toolMode & WiresUI.Settings.MultiToolMode.Red) != 0)
                {
                    handled = this.OnTileEdit(new TileEditEventArgs(player, isPlace ? TileEditType.PlaceWire : TileEditType.DestroyWire, tileLocation, 0, 0));
                }
                if (!handled && (toolMode & WiresUI.Settings.MultiToolMode.Blue) != 0)
                {
                    handled = this.OnTileEdit(new TileEditEventArgs(player, isPlace ? TileEditType.PlaceWireBlue : TileEditType.DestroyWireBlue, tileLocation, 0, 0));
                }
                if (!handled && (toolMode & WiresUI.Settings.MultiToolMode.Green) != 0)
                {
                    handled = this.OnTileEdit(new TileEditEventArgs(player, isPlace ? TileEditType.PlaceWireGreen : TileEditType.DestroyWireGreen, tileLocation, 0, 0));
                }
                if (!handled && (toolMode & WiresUI.Settings.MultiToolMode.Yellow) != 0)
                {
                    handled = this.OnTileEdit(new TileEditEventArgs(player, isPlace ? TileEditType.PlaceWireYellow : TileEditType.DestroyWireYellow, tileLocation, 0, 0));
                }
                if (!handled && (toolMode & WiresUI.Settings.MultiToolMode.Actuator) != 0)
                {
                    handled = this.OnTileEdit(new TileEditEventArgs(player, isPlace ? TileEditType.PlaceActuator : TileEditType.DestroyActuator, tileLocation, 0, 0));
                }
            }
            else if (this.InvokeTileEditOnMasswireOperation == MassWireOpTileEditInvokeType.AlwaysPlaceWire)
            {
                handled = this.OnTileEdit(new TileEditEventArgs(player, TileEditType.PlaceWire, tileLocation, 0, 0));
            }

            return(handled);
        }
        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
                    );
            }
        }
 public MassWireOperationEventArgs(TSPlayer player, DPoint startLocation, DPoint endLocation, WiresUI.Settings.MultiToolMode toolMode) : base(player)
 {
     this.StartLocation = startLocation;
     this.EndLocation   = endLocation;
     this.ToolMode      = toolMode;
 }