void OnGetData(GetDataEventArgs e)
        {
            if (!e.Handled)
            {
                using (var reader = new BinaryReader(new MemoryStream(e.Msg.readBuffer, e.Index, e.Length)))
                {
                    switch (e.MsgID)
                    {
                    case PacketTypes.SignNew:
                    {
                        reader.ReadInt16();
                        int    x    = reader.ReadInt16();
                        int    y    = reader.ReadInt16();
                        string text = reader.ReadString();
                        Task.Factory.StartNew(() => ModSign(x, y, e.Msg.whoAmI, text));
                        e.Handled = true;
                    }
                    break;

                    case PacketTypes.SignRead:
                    {
                        int x = reader.ReadInt16();
                        int y = reader.ReadInt16();
                        Task.Factory.StartNew(() => GetSign(x, y, e.Msg.whoAmI));
                        e.Handled = true;
                    }
                    break;

                    case PacketTypes.Tile:
                    {
                        byte   action = reader.ReadByte();
                        int    x      = reader.ReadInt16();
                        int    y      = reader.ReadInt16();
                        ushort type   = reader.ReadUInt16();

                        if (x < 0 || y < 0 || x >= Main.maxTilesX || y >= Main.maxTilesY)
                        {
                            return;
                        }

                        if (action == 0 && type == 0)
                        {
                            if (Sign.Nearby(x, y))
                            {
                                Task.Factory.StartNew(() => KillSign(x, y, e.Msg.whoAmI));
                                e.Handled = true;
                            }
                        }
                        else if (action == 1 && (type == 55 || type == 85))
                        {
                            if (TShock.Regions.CanBuild(x, y, TShock.Players[e.Msg.whoAmI]))
                            {
                                WorldGen.PlaceSign(x, y, type);
                                NetMessage.SendData(17, -1, e.Msg.whoAmI, "", 1, x, y, type);
                                if (Main.tile[x, y].frameY != 0)
                                {
                                    y--;
                                }
                                if (Main.tile[x, y].frameX % 36 != 0)
                                {
                                    x--;
                                }
                                Task.Factory.StartNew(() => PlaceSign(x, y, e.Msg.whoAmI));
                                e.Handled = true;
                            }
                        }
                    }
                    break;
                    }
                }
            }
        }
        void ModSign(int x, int y, int plr, string text)
        {
            Sign sign = null;

            using (QueryResult reader = Database.QueryReader("SELECT Account, Flags, ID, Password FROM Signs WHERE X = @0 AND Y = @1 AND WorldID = @2",
                                                             x, y, Main.worldID))
            {
                if (reader.Read())
                {
                    sign = new Sign
                    {
                        Account        = reader.Get <string>("Account"),
                        Flags          = (SignFlags)reader.Get <int>("Flags"),
                        HashedPassword = reader.Get <string>("Password"),
                        ID             = reader.Get <int>("ID")
                    };
                }
            }

            var info   = Infos[plr];
            var player = TShock.Players[plr];

            if (sign != null)
            {
                Console.WriteLine("IsRegion: {0}", sign.IsRegion);
                bool isFree   = String.IsNullOrEmpty(sign.Account);
                bool isOwner  = sign.Account == player.UserAccountName || player.Group.HasPermission("infsigns.admin.editall");
                bool isRegion = sign.IsRegion && TShock.Regions.CanBuild(x, y, player);
                if (!isFree && !isOwner && !isRegion)
                {
                    if (String.IsNullOrEmpty(sign.HashedPassword))
                    {
                        player.SendErrorMessage("This sign is protected.");
                        return;
                    }
                    else if (TShock.Utils.HashPassword(info.Password) != sign.HashedPassword)
                    {
                        player.SendErrorMessage("This sign is password protected.");
                        return;
                    }
                    else
                    {
                        player.SendSuccessMessage("Sign unlocked.");
                        info.Password = "";
                    }
                }

                Database.Query("UPDATE Signs SET Text = @0 WHERE ID = @1", text, sign.ID);

                byte[] raw = null;
                using (var writer = new BinaryWriter(new MemoryStream()))
                {
                    writer.Write((short)0);
                    writer.Write((byte)47);
                    writer.Write((short)(info.SignIndex ? 1 : 0));
                    writer.Write((short)x);
                    writer.Write((short)y);
                    writer.Write(sign.Text);

                    short length = (short)writer.BaseStream.Position;
                    writer.BaseStream.Position = 0;
                    writer.Write(length);
                    raw = ((MemoryStream)writer.BaseStream).ToArray();
                }

                foreach (var info2 in Infos.Where(i => i.X == info.X && i.Y == info.Y && i != info))
                {
                    raw[2] = (byte)(info2.SignIndex ? 1 : 0);
                    TShock.Players[info2.Index].SendRawData(raw);
                }
            }
        }
        void GetSign(int x, int y, int plr)
        {
            Sign sign = null;

            using (QueryResult reader = Database.QueryReader("SELECT Account, Flags, ID, Text FROM Signs WHERE X = @0 AND Y = @1 AND WorldID = @2",
                                                             x, y, Main.worldID))
            {
                if (reader.Read())
                {
                    sign = new Sign
                    {
                        Account = reader.Get <string>("Account"),
                        Flags   = (SignFlags)reader.Get <int>("Flags"),
                        ID      = reader.Get <int>("ID"),
                        Text    = reader.Get <string>("Text")
                    };
                }
            }

            var info   = Infos[plr];
            var player = TShock.Players[plr];

            if (sign != null)
            {
                switch (info.Action)
                {
                case SignAction.Info:
                    player.SendInfoMessage("X: {0} Y: {1} Account: {2} Region: {3}", x, y, sign.Account ?? "N/A", sign.IsRegion);
                    break;

                case SignAction.Protect:
                    if (!String.IsNullOrEmpty(sign.Account))
                    {
                        player.SendErrorMessage("This sign is protected.");
                        break;
                    }
                    Database.Query("UPDATE Signs SET Account = @0 WHERE ID = @1", player.UserAccountName, sign.ID);
                    player.SendInfoMessage("This sign is now protected.");
                    break;

                case SignAction.SetPassword:
                    if (String.IsNullOrEmpty(sign.Account))
                    {
                        player.SendErrorMessage("This sign is not protected.");
                        break;
                    }
                    if (sign.Account != player.UserAccountName && !player.Group.HasPermission("infsigns.admin.editall"))
                    {
                        player.SendErrorMessage("This sign is not yours.");
                        break;
                    }
                    if (String.Equals(info.Password, "remove", StringComparison.CurrentCultureIgnoreCase))
                    {
                        Database.Query("UPDATE Signs SET Password = NULL WHERE ID = @0", sign.ID);
                        player.SendSuccessMessage("This sign is no longer password protected.");
                    }
                    else
                    {
                        Database.Query("UPDATE Signs SET Password = @0 WHERE ID = @1", TShock.Utils.HashPassword(info.Password), sign.ID);
                        player.SendSuccessMessage("This sign is now password protected with password '{0}'.", info.Password);
                    }
                    break;

                case SignAction.TogglePublic:
                    if (String.IsNullOrEmpty(sign.Account))
                    {
                        player.SendErrorMessage("This sign is not protected.");
                        break;
                    }
                    if (sign.Account != player.UserAccountName && !player.Group.HasPermission("infsigns.admin.editall"))
                    {
                        player.SendErrorMessage("This sign is not yours.");
                        break;
                    }
                    Database.Query("UPDATE Signs SET Flags = (~(Flags & 1)) & (Flags | 1) WHERE ID = @0", sign.ID);
                    player.SendInfoMessage("This sign is no{0} public.", sign.IsRegion ? " longer" : "w");
                    break;

                case SignAction.ToggleRegion:
                    if (String.IsNullOrEmpty(sign.Account))
                    {
                        player.SendErrorMessage("This sign is not protected.");
                        break;
                    }
                    if (sign.Account != player.UserAccountName && !player.Group.HasPermission("infsigns.admin.editall"))
                    {
                        player.SendErrorMessage("This sign is not yours.");
                        break;
                    }
                    Database.Query("UPDATE Signs SET Flags = (~(Flags & 2)) & (Flags | 2) WHERE ID = @0", sign.ID);
                    player.SendInfoMessage("This sign is no{0} region shared.", sign.IsRegion ? " longer" : "w");
                    break;

                case SignAction.Unprotect:
                    if (String.IsNullOrEmpty(sign.Account))
                    {
                        player.SendErrorMessage("This sign is not protected.");
                        break;
                    }
                    if (sign.Account != player.UserAccountName && !player.Group.HasPermission("infsigns.admin.editall"))
                    {
                        player.SendErrorMessage("This sign is not yours.");
                        break;
                    }
                    Database.Query("UPDATE Signs SET Account = NULL, Flags = 0, Password = NULL WHERE ID = @0", sign.ID);
                    player.SendInfoMessage("This sign is now unprotected.");
                    break;

                default:
                    sign.Text = sign.Text.Replace("\0", "");
                    using (var writer = new BinaryWriter(new MemoryStream()))
                    {
                        writer.Write((short)0);
                        writer.Write((byte)47);
                        writer.Write((short)(info.SignIndex ? 1 : 0));
                        writer.Write((short)x);
                        writer.Write((short)y);
                        writer.Write(sign.Text);

                        short length = (short)writer.BaseStream.Position;
                        writer.BaseStream.Position = 0;
                        writer.Write(length);
                        player.SendRawData(((MemoryStream)writer.BaseStream).ToArray());
                    }
                    info.SignIndex = !info.SignIndex;
                    info.X         = x;
                    info.Y         = y;
                    break;
                }
                info.Action = SignAction.None;
            }
        }
        void KillSign(int x, int y, int plr)
        {
            TSPlayer player = TShock.Players[plr];

            bool[] attached     = new bool[4];
            bool[] attachedNext = new bool[4];
            var    positions    = new List <Point>();

            if (Main.tile[x, y].IsSign())
            {
                if (TryKillSign(Sign.GetSign(x, y).X, Sign.GetSign(x, y).Y, plr))
                {
                    WorldGen.KillTile(x, y);
                    TSPlayer.All.SendTileSquare(x, y, 3);
                    return;
                }
                else
                {
                    player.SendErrorMessage("This sign is protected.");
                    player.SendTileSquare(x, y, 5);
                    return;
                }
            }
            else
            {
                if (TileValid(x - 1, y) && Main.tile[x - 1, y].IsSign())
                {
                    positions.Add(Sign.GetSign(x - 1, y));
                }
                if (TileValid(x + 1, y) && Main.tile[x + 1, y].IsSign())
                {
                    positions.Add(Sign.GetSign(x + 1, y));
                }
                if (TileValid(x, y - 1) && Main.tile[x, y - 1].IsSign())
                {
                    positions.Add(Sign.GetSign(x, y - 1));
                }
                if (TileValid(x, y + 1) && Main.tile[x, y + 1].IsSign())
                {
                    positions.Add(Sign.GetSign(x, y + 1));
                }
                foreach (Point p in positions)
                {
                    attached[0] = TileValid(p.X, p.Y + 2) && Main.tile[p.X, p.Y + 2].IsSolid() &&
                                  TileValid(p.X + 1, p.Y + 2) && Main.tile[p.X + 1, p.Y + 2].IsSolid();
                    attached[1] = TileValid(p.X, p.Y - 1) && Main.tile[p.X, p.Y - 1].IsSolid() &&
                                  TileValid(p.X + 1, p.Y - 1) && Main.tile[p.X + 1, p.Y - 1].IsSolid();
                    attached[2] = TileValid(p.X - 1, p.Y) && Main.tile[p.X - 1, p.Y].IsSolid() &&
                                  TileValid(p.X - 1, p.Y + 1) && Main.tile[p.X - 1, p.Y + 1].IsSolid();
                    attached[3] = TileValid(p.X + 2, p.Y) && Main.tile[p.X + 2, p.Y].IsSolid() &&
                                  TileValid(p.X + 2, p.Y + 1) && Main.tile[p.X + 2, p.Y + 1].IsSolid();
                    bool prev = Main.tile[x, y].active();
                    Main.tile[x, y].active(false);
                    attachedNext[0] = TileValid(p.X, p.Y + 2) && Main.tile[p.X, p.Y + 2].IsSolid() &&
                                      TileValid(p.X + 1, p.Y + 2) && Main.tile[p.X + 1, p.Y + 2].IsSolid();
                    attachedNext[1] = TileValid(p.X, p.Y - 1) && Main.tile[p.X, p.Y - 1].IsSolid() &&
                                      TileValid(p.X + 1, p.Y - 1) && Main.tile[p.X + 1, p.Y - 1].IsSolid();
                    attachedNext[2] = TileValid(p.X - 1, p.Y) && Main.tile[p.X - 1, p.Y].IsSolid() &&
                                      TileValid(p.X - 1, p.Y + 1) && Main.tile[p.X - 1, p.Y + 1].IsSolid();
                    attachedNext[3] = TileValid(p.X + 2, p.Y) && Main.tile[p.X + 2, p.Y].IsSolid() &&
                                      TileValid(p.X + 2, p.Y + 1) && Main.tile[p.X + 2, p.Y + 1].IsSolid();
                    Main.tile[x, y].active(prev);
                    if (attached.Count(b => b) > 1 || attached.Count(b => b) == attachedNext.Count(b => b))
                    {
                        continue;
                    }
                    if (TryKillSign(p.X, p.Y, plr))
                    {
                        WorldGen.KillTile(p.X, p.Y);
                        TSPlayer.All.SendTileSquare(p.X, p.Y, 3);
                    }
                    else
                    {
                        player.SendErrorMessage("This sign is protected.");
                        player.SendTileSquare(x, y, 5);
                    }
                }
            }
        }
示例#5
0
        void OnGetData(GetDataEventArgs e)
        {
            if (!e.Handled)
            {
                switch (e.MsgID)
                {
                case PacketTypes.SignNew:
                {
                    int    X    = BitConverter.ToInt32(e.Msg.readBuffer, e.Index + 2);
                    int    Y    = BitConverter.ToInt32(e.Msg.readBuffer, e.Index + 6);
                    string text = Encoding.UTF8.GetString(e.Msg.readBuffer, e.Index + 10, e.Length - 11);
                    ModSign(X, Y, e.Msg.whoAmI, text);
                    e.Handled = true;
                }
                break;

                case PacketTypes.SignRead:
                {
                    int X = BitConverter.ToInt32(e.Msg.readBuffer, e.Index);
                    int Y = BitConverter.ToInt32(e.Msg.readBuffer, e.Index + 4);
                    GetSign(X, Y, e.Msg.whoAmI);
                    e.Handled = true;
                }
                break;

                case PacketTypes.Tile:
                {
                    int X = BitConverter.ToInt32(e.Msg.readBuffer, e.Index + 1);
                    int Y = BitConverter.ToInt32(e.Msg.readBuffer, e.Index + 5);
                    if (X < 0 || Y < 0 || X >= Main.maxTilesX || Y >= Main.maxTilesY)
                    {
                        return;
                    }
                    if (e.Msg.readBuffer[e.Index] == 0 && Main.tile[X, Y].IsSign())
                    {
                        if (SignHit != null)
                        {
                            Sign sign    = null;
                            var  signPos = Sign.GetSign(X, Y);
                            using (QueryResult reader = Database.QueryReader("SELECT Account, Text FROM Signs WHERE X = @0 AND Y = @1 AND WorldID = @2", signPos.X, signPos.Y, Main.worldID))
                            {
                                if (reader.Read())
                                {
                                    sign = new Sign {
                                        account = reader.Get <string>("Account"), text = reader.Get <string>("Text")
                                    }
                                }
                                ;
                            }
                            if (sign != null)
                            {
                                SignEventArgs signargs = new SignEventArgs(X, Y, sign.text, sign.account);
                                SignHit(signargs);
                            }
                        }
                    }
                    if (e.Msg.readBuffer[e.Index] == 0 && e.Msg.readBuffer[e.Index + 9] == 0)
                    {
                        if (Sign.Nearby(X, Y))
                        {
                            KillSign(X, Y, e.Msg.whoAmI);
                            e.Handled = true;
                        }
                    }
                    else if (e.Msg.readBuffer[e.Index] == 1 && (e.Msg.readBuffer[e.Index + 9] == 55 || e.Msg.readBuffer[e.Index + 9] == 85))
                    {
                        if (TShock.Regions.CanBuild(X, Y, TShock.Players[e.Msg.whoAmI]))
                        {
                            WorldGen.PlaceSign(X, Y, e.Msg.readBuffer[e.Index + 9]);
                            if (Main.tile[X, Y].frameY != 0)
                            {
                                Y--;
                            }
                            if (Main.tile[X, Y].frameX % 36 != 0)
                            {
                                X--;
                            }
                            PlaceSign(X, Y, e.Msg.whoAmI);
                            e.Handled = true;
                        }
                    }
                }
                break;
                }
            }
        }

        void OnInitialize()
        {
            Commands.ChatCommands.Add(new Command("maintenance", ConvertSigns, "convsigns"));
            Commands.ChatCommands.Add(new Command("protectsign", Deselect, "scset"));
            Commands.ChatCommands.Add(new Command("showsigninfo", Info, "sinfo"));
            Commands.ChatCommands.Add(new Command("protectsign", Protect, "sset"));
            Commands.ChatCommands.Add(new Command("protectsign", Unprotect, "sunset"));

            switch (TShock.Config.StorageType.ToLower())
            {
            case "mysql":
                string[] host = TShock.Config.MySqlHost.Split(':');
                Database = new MySqlConnection()
                {
                    ConnectionString = string.Format("Server={0}; Port={1}; Database={2}; Uid={3}; Pwd={4};",
                                                     host[0],
                                                     host.Length == 1 ? "3306" : host[1],
                                                     TShock.Config.MySqlDbName,
                                                     TShock.Config.MySqlUsername,
                                                     TShock.Config.MySqlPassword)
                };
                break;

            case "sqlite":
                string sql = Path.Combine(TShock.SavePath, "signs.sqlite");
                Database = new SqliteConnection(string.Format("uri=file://{0},Version=3", sql));
                break;
            }
            SqlTableCreator sqlcreator = new SqlTableCreator(Database,
                                                             Database.GetSqlType() == SqlType.Sqlite ? (IQueryBuilder) new SqliteQueryCreator() : new MysqlQueryCreator());

            sqlcreator.EnsureExists(new SqlTable("Signs",
                                                 new SqlColumn("X", MySqlDbType.Int32),
                                                 new SqlColumn("Y", MySqlDbType.Int32),
                                                 new SqlColumn("Account", MySqlDbType.Text),
                                                 new SqlColumn("Text", MySqlDbType.Text),
                                                 new SqlColumn("WorldID", MySqlDbType.Int32)));
        }

        void OnLeave(int index)
        {
            Action[index]  = SignAction.NONE;
            SignNum[index] = false;
        }

        void GetSign(int X, int Y, int plr)
        {
            Sign sign = null;

            using (QueryResult reader = Database.QueryReader("SELECT Account, Text FROM Signs WHERE X = @0 AND Y = @1 AND WorldID = @2",
                                                             X, Y, Main.worldID))
            {
                if (reader.Read())
                {
                    sign = new Sign {
                        account = reader.Get <string>("Account"), text = reader.Get <string>("Text")
                    };
                }
            }
            TSPlayer player = TShock.Players[plr];

            if (sign != null)
            {
                switch (Action[plr])
                {
                case SignAction.INFO:
                    player.SendMessage(string.Format("X: {0} Y: {1} Account: {2}", X, Y, sign.account == "" ? "N/A" : sign.account), Color.Yellow);
                    break;

                case SignAction.PROTECT:
                    if (sign.account != "")
                    {
                        player.SendMessage("This sign is protected.", Color.Red);
                        break;
                    }
                    Database.Query("UPDATE Signs SET Account = @0 WHERE X = @1 AND Y = @2 AND WorldID = @3",
                                   player.UserAccountName, X, Y, Main.worldID);
                    player.SendMessage("This sign is now protected.");
                    break;

                case SignAction.UNPROTECT:
                    if (sign.account == "")
                    {
                        player.SendMessage("This sign is not protected.", Color.Red);
                        break;
                    }
                    if (sign.account != player.UserAccountName && !player.Group.HasPermission("removesignprotection"))
                    {
                        player.SendMessage("This sign is not yours.", Color.Red);
                        break;
                    }
                    Database.Query("UPDATE Signs SET Account = '' WHERE X = @0 AND Y = @1 AND WorldID = @2",
                                   X, Y, Main.worldID);
                    player.SendMessage("This sign is now unprotected.");
                    break;

                default:
                    if (sign.text.Length > 0 && sign.text[sign.text.Length - 1] == '\0')
                    {
                        sign.text = sign.text.Substring(0, sign.text.Length - 1);
                    }
                    byte[] utf8 = Encoding.UTF8.GetBytes(sign.text);
                    byte[] raw  = new byte[15 + utf8.Length];
                    Buffer.BlockCopy(BitConverter.GetBytes(utf8.Length + 11), 0, raw, 0, 4);
                    if (SignNum[plr])
                    {
                        raw[5] = 1;
                    }
                    raw[4] = 47;
                    Buffer.BlockCopy(BitConverter.GetBytes(X), 0, raw, 7, 4);
                    Buffer.BlockCopy(BitConverter.GetBytes(Y), 0, raw, 11, 4);
                    Buffer.BlockCopy(utf8, 0, raw, 15, utf8.Length);
                    SignNum[plr] = !SignNum[plr];
                    SignEventArgs signargs = new SignEventArgs(X, Y, sign.text, sign.account);
                    if (SignRead != null)
                    {
                        SignRead(signargs);
                    }
                    if (!signargs.Handled)
                    {
                        player.SendRawData(raw);
                    }
                    break;
                }
                Action[plr] = SignAction.NONE;
            }
        }

        void KillSign(int X, int Y, int plr)
        {
            TSPlayer player = TShock.Players[plr];

            bool[]       attached     = new bool[4];
            bool[]       attachedNext = new bool[4];
            List <Point> positions    = new List <Point>();

            if (Main.tile[X, Y].IsSign())
            {
                positions.Add(Sign.GetSign(X, Y));
                if (TryKillSign(positions[0].X, positions[0].Y, plr))
                {
                    WorldGen.KillTile(X, Y);
                    TSPlayer.All.SendTileSquare(X, Y, 3);
                }
                else
                {
                    player.SendMessage("This sign is protected.", Color.Red);
                    player.SendTileSquare(X, Y, 5);
                }
            }
            else
            {
                if (Main.tile.Valid(X - 1, Y) && Main.tile[X - 1, Y].IsSign())
                {
                    positions.Add(Sign.GetSign(X - 1, Y));
                }
                if (Main.tile.Valid(X + 1, Y) && Main.tile[X + 1, Y].IsSign())
                {
                    positions.Add(Sign.GetSign(X + 1, Y));
                }
                if (Main.tile.Valid(X, Y - 1) && Main.tile[X, Y - 1].IsSign())
                {
                    positions.Add(Sign.GetSign(X, Y - 1));
                }
                if (Main.tile.Valid(X, Y + 1) && Main.tile[X, Y + 1].IsSign())
                {
                    positions.Add(Sign.GetSign(X, Y + 1));
                }
                bool killTile = true;
                foreach (Point p in positions)
                {
                    attached[0] = Main.tile.Valid(p.X, p.Y + 2) && Main.tile[p.X, p.Y + 2].IsSolid() &&
                                  Main.tile.Valid(p.X + 1, p.Y + 2) && Main.tile[p.X + 1, p.Y + 2].IsSolid();
                    attached[1] = Main.tile.Valid(p.X, p.Y - 1) && Main.tile[p.X, p.Y - 1].IsSolid() &&
                                  Main.tile.Valid(p.X + 1, p.Y - 1) && Main.tile[p.X + 1, p.Y - 1].IsSolid();
                    attached[2] = Main.tile.Valid(p.X - 1, p.Y) && Main.tile[p.X - 1, p.Y].IsSolid() &&
                                  Main.tile.Valid(p.X - 1, p.Y + 1) && Main.tile[p.X - 1, p.Y + 1].IsSolid();
                    attached[3] = Main.tile.Valid(p.X + 2, p.Y) && Main.tile[p.X + 2, p.Y].IsSolid() &&
                                  Main.tile.Valid(p.X + 2, p.Y + 1) && Main.tile[p.X + 2, p.Y + 1].IsSolid();
                    bool prev = Main.tile[X, Y].active;
                    Main.tile[X, Y].active = false;
                    attachedNext[0]        = Main.tile.Valid(p.X, p.Y + 2) && Main.tile[p.X, p.Y + 2].IsSolid() &&
                                             Main.tile.Valid(p.X + 1, p.Y + 2) && Main.tile[p.X + 1, p.Y + 2].IsSolid();
                    attachedNext[1] = Main.tile.Valid(p.X, p.Y - 1) && Main.tile[p.X, p.Y - 1].IsSolid() &&
                                      Main.tile.Valid(p.X + 1, p.Y - 1) && Main.tile[p.X + 1, p.Y - 1].IsSolid();
                    attachedNext[2] = Main.tile.Valid(p.X - 1, p.Y) && Main.tile[p.X - 1, p.Y].IsSolid() &&
                                      Main.tile.Valid(p.X - 1, p.Y + 1) && Main.tile[p.X - 1, p.Y + 1].IsSolid();
                    attachedNext[3] = Main.tile.Valid(p.X + 2, p.Y) && Main.tile[p.X + 2, p.Y].IsSolid() &&
                                      Main.tile.Valid(p.X + 2, p.Y + 1) && Main.tile[p.X + 2, p.Y + 1].IsSolid();
                    Main.tile[X, Y].active = prev;
                    if (attached.Count(b => b) > 1 || attached.Count(b => b) == attachedNext.Count(b => b))
                    {
                        continue;
                    }
                    if (TryKillSign(p.X, p.Y, plr))
                    {
                        WorldGen.KillTile(p.X, p.Y);
                        TSPlayer.All.SendTileSquare(p.X, p.Y, 3);
                    }
                    else
                    {
                        player.SendMessage("This sign is protected.", Color.Red);
                        player.SendTileSquare(X, Y, 5);
                        killTile = false;
                    }
                }
                if (killTile)
                {
                    WorldGen.KillTile(X, Y);
                    TSPlayer.All.SendTileSquare(X, Y, 1);
                }
            }
        }

        void ModSign(int X, int Y, int plr, string text)
        {
            Sign sign = null;

            using (QueryResult reader = Database.QueryReader("SELECT Account FROM Signs WHERE X = @0 AND Y = @1 AND WorldID = @2",
                                                             X, Y, Main.worldID))
            {
                if (reader.Read())
                {
                    sign = new Sign {
                        account = reader.Get <string>("Account")
                    };
                }
            }
            TSPlayer player = TShock.Players[plr];

            if (sign != null)
            {
                if (sign.account != player.UserAccountName && sign.account != "" && !player.Group.HasPermission("editallsigns"))
                {
                    player.SendMessage("This sign is protected.", Color.Red);
                }
                else
                {
                    SignEventArgs signargs = new SignEventArgs(X, Y, sign.text, sign.account);
                    if (SignEdit != null)
                    {
                        SignEdit(signargs);
                    }
                    if (signargs.Handled)
                    {
                        player.SendMessage("Another plugin is preventing the sign to be edited.", Color.Red);
                    }
                    else
                    {
                        Database.Query("UPDATE Signs SET Text = @0 WHERE X = @1 AND Y = @2 AND WorldID = @3", text, X, Y, Main.worldID);
                    }
                }
            }
        }

        void PlaceSign(int X, int Y, int plr)
        {
            TSPlayer player = TShock.Players[plr];

            Database.Query("INSERT INTO Signs (X, Y, Account, Text, WorldID) VALUES (@0, @1, @2, '', @3)",
                           X, Y, player.IsLoggedIn ? player.UserAccountName : "", Main.worldID);
            Main.sign[999] = null;
        }

        bool TryKillSign(int X, int Y, int plr)
        {
            Sign sign = null;

            using (QueryResult reader = Database.QueryReader("SELECT Account, Text FROM Signs WHERE X = @0 AND Y = @1 AND WorldID = @2",
                                                             X, Y, Main.worldID))
            {
                if (reader.Read())
                {
                    sign = new Sign {
                        account = reader.Get <string>("Account"), text = reader.Get <string>("Text")
                    };
                }
            }
            if (sign != null)
            {
                if (sign.account != TShock.Players[plr].UserAccountName && sign.account != "")
                {
                    return(false);
                }
                else
                {
                    if (SignKill != null)
                    {
                        SignEventArgs signargs = new SignEventArgs(X, Y, sign.text, sign.account);
                        SignKill(signargs);
                        if (signargs.Handled)
                        {
                            return(false);
                        }
                    }
                }
            }
            Database.Query("DELETE FROM Signs WHERE X = @0 AND Y = @1 AND WorldID = @2", X, Y, Main.worldID);
            return(true);
        }

        void ConvertSigns(CommandArgs e)
        {
            Database.Query("DELETE FROM Signs WHERE WorldID = @0", Main.worldID);
            int converted = 0;

            foreach (Terraria.Sign s in Main.sign)
            {
                if (s != null)
                {
                    Database.Query("INSERT INTO Signs (X, Y, Text, WorldID) VALUES (@0, @1, @2, @3)",
                                   s.x, s.y, s.text, Main.worldID);
                    converted++;
                }
            }
            e.Player.SendMessage("Converted " + converted + " signs.");
        }

        void Deselect(CommandArgs e)
        {
            Action[e.Player.Index] = SignAction.NONE;
            e.Player.SendMessage("Stopped selecting a sign.");
        }

        void Info(CommandArgs e)
        {
            Action[e.Player.Index] = SignAction.INFO;
            e.Player.SendMessage("Read a sign to get its info.");
        }

        void Protect(CommandArgs e)
        {
            Action[e.Player.Index] = SignAction.PROTECT;
            e.Player.SendMessage("Read a sign to protect it.");
        }

        void Unprotect(CommandArgs e)
        {
            Action[e.Player.Index] = SignAction.UNPROTECT;
            e.Player.SendMessage("Read a sign to unprotect it.");
        }
    }