diff --git a/HouseRegion/Config.cs b/HouseRegion/Config.cs new file mode 100644 index 00000000..9525a280 --- /dev/null +++ b/HouseRegion/Config.cs @@ -0,0 +1,67 @@ +using Newtonsoft.Json; + +namespace HouseRegion +{ + public class Config + { + [JsonProperty("进出房屋提示")] + public bool HouseRegion = true; + + [JsonProperty("房屋嘴大大小")] + public int HouseMaxSize = 1000; + + [JsonProperty("房屋最小宽度")] + public int MinWidth = 30; + + [JsonProperty("房屋最小高度")] + public int MinHeight = 30; + + [JsonProperty("房屋最大数量")] + public int HouseMaxNumber = 1; + + [JsonProperty("禁止锁房屋")] + public bool LimitLockHouse = false; + + [JsonProperty("保护宝石锁")] + public bool ProtectiveGemstoneLock = false; + + [JsonProperty("始终保护箱子")] + public bool ProtectiveChest = true; + + [JsonProperty("冻结警告破坏者")] + public bool WarningSpoiler = true; + + [JsonProperty("禁止分享所有者7")] + public bool ProhibitSharingOwner = false; + + [JsonProperty("禁止分享使用者")] + public bool ProhibitSharingUser = false; + + [JsonProperty("禁止所有者修改使用者")] + public bool ProhibitOwnerModifyingUser = true; + + public static Config Read(string Path)//给定文件进行读 + { + if (!File.Exists(Path)) return new Config(); + using var fs = new FileStream(Path, FileMode.Open, FileAccess.Read, FileShare.Read); + return Read(fs); + } + public static Config Read(Stream stream)//给定流文件进行读取 + { + using var sr = new StreamReader(stream); + var cf = JsonConvert.DeserializeObject(sr.ReadToEnd()); + return cf ?? new(); + } + public void Write(string Path)//给定路径进行写 + { + using var fs = new FileStream(Path, FileMode.Create, FileAccess.Write, FileShare.Write); + Write(fs); + } + public void Write(Stream stream)//给定流文件写 + { + var str = JsonConvert.SerializeObject(this, Formatting.Indented); + using var sw = new StreamWriter(stream); + sw.Write(str); + } + } +} \ No newline at end of file diff --git a/HouseRegion/Customize.cs b/HouseRegion/Customize.cs new file mode 100644 index 00000000..4e9a60bc --- /dev/null +++ b/HouseRegion/Customize.cs @@ -0,0 +1,40 @@ +using Microsoft.Xna.Framework; + +namespace HouseRegion +{ + public class LPlayer//本地玩家类 + { + public int Who { get; set; } + public int TileX { get; set; }//上次瓷砖X位置目的用来判定玩家是否步入房子,若上次瓷砖位置在房子内表示已在房子内 + public int TileY { get; set; }//上次瓷砖Y位置 + public bool Look { get; set; } + + public LPlayer(int who, int lasttileX, int lasttileY)//类初始化时 + { + Who = who; + TileX = lasttileX; + TileY = lasttileY; + Look = false; + } + } + + public class House//本地房子类 + { + public Rectangle HouseArea { get; set; } + public string Author { get; set; } + public List Owners { get; set; } + public string Name { get; set; } + public bool Locked { get; set; } + public List Users { get; set; } + + public House(Rectangle housearea, string author, List owners, string name, bool locked, List users)//类初始化时 + { + HouseArea = housearea; + Author = author; + Owners = owners; + Name = name; + Locked = locked; + Users = users; + } + } +} diff --git a/HouseRegion/HouseRegion.csproj b/HouseRegion/HouseRegion.csproj new file mode 100644 index 00000000..5277c439 --- /dev/null +++ b/HouseRegion/HouseRegion.csproj @@ -0,0 +1,4 @@ + + + + diff --git a/HouseRegion/PacketReceive.cs b/HouseRegion/PacketReceive.cs new file mode 100644 index 00000000..f173c84d --- /dev/null +++ b/HouseRegion/PacketReceive.cs @@ -0,0 +1,323 @@ +using Microsoft.Xna.Framework; +using System.IO.Streams; +using Terraria; +using Terraria.DataStructures; +using Terraria.GameContent.Tile_Entities; +using Terraria.Localization; +using TShockAPI; +using TShockAPI.Net; + +namespace HouseRegion; + +public delegate bool GetDataHandlerDelegate(GetDataHandlerArgs args);//设置代理 +public class GetDataHandlerArgs : EventArgs//要让这个数据在数据的基础上再多两个信息,用户和解析后的数据 +{ + public TSPlayer Player { get; private set; } + public MemoryStream Data { get; private set; } + public Player TPlayer { get { return Player.TPlayer; } } + public GetDataHandlerArgs(TSPlayer player, MemoryStream data) { Player = player; Data = data; } +} +public static class GetDataHandlers +{ + static string EditHouse = "house.edit"; + private static Dictionary GetDataHandlerDelegates;//创建词典 + public static void InitGetDataHandler() + { + GetDataHandlerDelegates = new Dictionary + { {PacketTypes.Tile, HandleTile},//修改砖放或敲 + {PacketTypes.DoorUse,HandleDoorUse},//使用门 + {PacketTypes.TileSendSquare, HandleSendTileSquareCentered},//地区改变 + {PacketTypes.ChestGetContents, HandleChestOpen },//打开箱子 + {PacketTypes.ChestItem, HandleChestItem },//更新箱子物品 + {PacketTypes.ChestOpen, HandleChestActive },//修改箱子 + {PacketTypes.PlaceChest, HandlePlaceChest },//放置箱子 + {PacketTypes.SignRead, HandleSignRead },//读标牌 + {PacketTypes.SignNew, HandleSign },//修改标牌 + {PacketTypes.LiquidSet, HandleLiquidSet},//放水//PacketTypes.ChestUnlock解锁箱子 + {PacketTypes.HitSwitch,HandleHitSwitch},//点击开关 + {PacketTypes.PaintTile, HandlePaintTile},//油漆块 + {PacketTypes.PaintWall, HandlePaintWall},//油漆墙体 + {PacketTypes.Teleport, HandleTeleport},//NPC或玩家传送处理//仿佛没有必要 + {PacketTypes.PlaceObject, HandlePlaceObject },//放置物体//PacketTypes.ForceItemIntoNearestChest堆物品 + {PacketTypes.PlaceTileEntity, HandlePlaceTileEntity },//放置实体 + {PacketTypes.PlaceItemFrame, HandlePlaceItemFrame },//放置物品框//PacketTypes.KillPortal杀传送门 + //{ PacketTypes.SyncExtraValue, HandleSyncExtraValue },同步附加价值? + {PacketTypes.GemLockToggle, HandleGemLockToggle },//宝石锁启动 + {PacketTypes.MassWireOperation, HandleMassWireOperation },//大规模电路 + }; + } + public static bool HandlerGetData(PacketTypes type, TSPlayer player, MemoryStream data) + { + GetDataHandlerDelegate handler; + if (GetDataHandlerDelegates.TryGetValue(type, out handler)) + { + try { return handler(new GetDataHandlerArgs(player, data)); } + catch (Exception ex) { TShock.Log.Error("房屋插件错误调用事件时出错:" + ex.ToString()); } + } + return false; + } + private static bool HandleTile(GetDataHandlerArgs args)//17修改砖放或敲 + { + int action = args.Data.ReadInt8();//类型 + int x = args.Data.ReadInt16(); + int y = args.Data.ReadInt16(); + ITile tile = Main.tile[x, y]; + if (Main.tileCut[tile.type]) return false;//如果是草不阻止破坏 + var house = Utils.InAreaHouse(x, y);//直接读出敲的房子 + if (HousingPlugin.LPlayers[args.Player.Index].Look)//敲击砖块确认房屋名 + { + if (house == null) args.Player.SendMessage("敲击处不属于任何房子。", Color.Yellow); + else + { + string AuthorNames = ""; + try { AuthorNames = TShock.UserAccounts.GetUserAccountByID(Convert.ToInt32(house.Author)).Name; } + catch (Exception ex) { TShock.Log.Error("房屋插件错误超标错误:" + ex.ToString()); } + args.Player.SendMessage("敲击处为 " + AuthorNames + " 的房子: " + house.Name + " 状态: " + (!house.Locked || HousingPlugin.LConfig.LimitLockHouse ? "未上锁" : "已上锁"), Color.Yellow); + } + args.Player.SendTileSquareCentered(x, y); + HousingPlugin.LPlayers[args.Player.Index].Look = false; + return true; + } + if (args.Player.AwaitingTempPoint > 0)//设置点位 + { + args.Player.TempPoints[args.Player.AwaitingTempPoint - 1].X = x; + args.Player.TempPoints[args.Player.AwaitingTempPoint - 1].Y = y; + if (args.Player.AwaitingTempPoint == 1) args.Player.SendMessage("保护区左上角已设置!", Color.Yellow); + if (args.Player.AwaitingTempPoint == 2) args.Player.SendMessage("保护区右下角已设置!", Color.Yellow); + args.Player.SendTileSquareCentered(x, y); + args.Player.AwaitingTempPoint = 0; + return true; + } + if (house == null) return false; + if (args.Player.Group.HasPermission(EditHouse) || args.Player.Account.ID.ToString() == house.Author || Utils.OwnsHouse(args.Player.Account.ID.ToString(), house)) return false; + if (HousingPlugin.LConfig.WarningSpoiler) args.Player.Disable("无权修改房子保护!"); + args.Player.SendErrorMessage("你没有权力损坏被房子保护的地区。"); + args.Player.SendTileSquareCentered(x, y); + return true;//假表示允许修改//真表示禁止修改 + } + private static bool HandleDoorUse(GetDataHandlerArgs args)//19使用门 + { + args.Data.ReadInt8();//跳过对于我们的目的没影响的类型 + int x = args.Data.ReadInt16(); + int y = args.Data.ReadInt16(); + var house = Utils.InAreaHouse(x, y);//直接读出房子 + if (house == null) return false; + if (!house.Locked || HousingPlugin.LConfig.LimitLockHouse) return false;//没锁,那随便开 + if (args.Player.Group.HasPermission(EditHouse) || args.Player.Account.ID.ToString() == house.Author || Utils.OwnsHouse(args.Player.Account.ID.ToString(), house) || Utils.CanUseHouse(args.Player.Account.ID.ToString(), house)) return false; + if (HousingPlugin.LConfig.WarningSpoiler) args.Player.Disable("无权修改门!"); + args.Player.SendErrorMessage("你没有权力修改被房子保护的地区的门。"); + args.Player.SendTileSquareCentered(x, y); + return true;//假表示允许修改//真表示禁止修改 + } + private static bool HandleSendTileSquareCentered(GetDataHandlerArgs args)//20地区改变,比如腐化血腥花草什么的 + { + return false;//此处虽然可以阻止地形改变但是由于资源耗费巨大不建议使用 + var size = args.Data.ReadInt16(); + int tileX = args.Data.ReadInt16(); + int tileY = args.Data.ReadInt16(); + //Console.WriteLine("地区改变{0} X{1} Y{2}" ,size, tileX, tileY); + if (size > 1) return false;//腐化血腥的生成好像都是1 + ITile tile = Main.tile[tileX, tileY]; + if (Main.tileCut[tile.type]) return false;//如果是草不阻止破坏 + var newtile = new NetTile(args.Data);//新瓷砖 + if (Main.tileCut[newtile.Type]) return false;//如果是草不阻止生成 + var house = Utils.InAreaHouse(tileX, tileY);//确定是否保护区 + if (house == null) return false; + if (args.Player.Group.HasPermission(EditHouse) || args.Player.Account.ID.ToString() == house.Author || Utils.OwnsHouse(args.Player.Account.ID.ToString(), house)) return false; + args.Player.SendTileSquareCentered(tileX, tileY); + return true; + } + private static bool HandleChestOpen(GetDataHandlerArgs args)//31打开箱子 + { + int x = args.Data.ReadInt16(); + int y = args.Data.ReadInt16(); + var house = Utils.InAreaHouse(x, y);//直接读出房子 + if (house == null) return false; + if ((!house.Locked || HousingPlugin.LConfig.LimitLockHouse) && !HousingPlugin.LConfig.ProtectiveChest) return false;//没锁,且不保护箱子,那随便开 + if (args.Player.Group.HasPermission(EditHouse) || args.Player.Account.ID.ToString() == house.Author || Utils.OwnsHouse(args.Player.Account.ID.ToString(), house) || Utils.CanUseHouse(args.Player.Account.ID.ToString(), house)) return false; + if (HousingPlugin.LConfig.WarningSpoiler) args.Player.Disable("无权打开箱子!"); + args.Player.SendErrorMessage("你没有权力打开被房子保护的地区的箱子。"); + return true;//假表示允许修改//真表示禁止修改 + } + private static bool HandleChestItem(GetDataHandlerArgs args)//32更新箱子 + { + var id = args.Data.ReadInt16(); + var x = Main.chest[id].x; + var y = Main.chest[id].y; + var house = Utils.InAreaHouse(x, y);//直接读出房子 + if (house == null) return false; + if ((!house.Locked || HousingPlugin.LConfig.LimitLockHouse) && !HousingPlugin.LConfig.ProtectiveChest) return false;//没锁,且不保护箱子,那随便开 + if (args.Player.Group.HasPermission(EditHouse) || args.Player.Account.ID.ToString() == house.Author || Utils.OwnsHouse(args.Player.Account.ID.ToString(), house) || Utils.CanUseHouse(args.Player.Account.ID.ToString(), house)) return false; + if (HousingPlugin.LConfig.WarningSpoiler) args.Player.Disable("无权更新箱子!"); + args.Player.SendErrorMessage("你没有权力更新被房子保护的地区的箱子。"); + return true;//假表示允许修改//真表示禁止修改 + } + private static bool HandleChestActive(GetDataHandlerArgs args)//33修改箱子 + { + args.Data.ReadInt16();//跳过编号 + int x = args.Data.ReadInt16(); + int y = args.Data.ReadInt16(); + var house = Utils.InAreaHouse(x, y);//直接读出房子 + if (house == null) return false; + if ((!house.Locked || HousingPlugin.LConfig.LimitLockHouse) && !HousingPlugin.LConfig.ProtectiveChest) return false;//没锁,且不保护箱子,那随便开 + if (args.Player.Group.HasPermission(EditHouse) || args.Player.Account.ID.ToString() == house.Author || Utils.OwnsHouse(args.Player.Account.ID.ToString(), house) || Utils.CanUseHouse(args.Player.Account.ID.ToString(), house)) return false; + if (HousingPlugin.LConfig.WarningSpoiler) args.Player.Disable("无权修改箱子!"); + args.Player.SendErrorMessage("你没有权力修改被房子保护的地区的箱子。"); + args.Player.SendData(PacketTypes.ChestOpen, "", -1); + return true;//假表示允许修改//真表示禁止修改 + } + private static bool HandlePlaceChest(GetDataHandlerArgs args)//34放置箱子 + { + args.Data.ReadByte(); + int tileX = args.Data.ReadInt16(); + int tileY = args.Data.ReadInt16(); + var rect = new Rectangle(tileX, tileY, 3, 3);//创造个同样大小的范围虽然箱子是2*2但是梳妆台却是3因此就大不就小 + for (int i = 0; i < HousingPlugin.Houses.Count; i++) + { + var house = HousingPlugin.Houses[i]; if (house == null) continue; + if (house.HouseArea.Intersects(rect) && !(args.Player.Group.HasPermission(EditHouse) || args.Player.Account.ID.ToString() == house.Author || Utils.OwnsHouse(args.Player.Account.ID.ToString(), house))) + { + if (HousingPlugin.LConfig.WarningSpoiler) args.Player.Disable("无权放置家具!"); + args.Player.SendErrorMessage("你没有权力放置被房子保护的地区的家具。"); + args.Player.SendTileSquareCentered(tileX, tileY, 3); + return true;//假表示允许修改//真表示禁止修改 + } + } + return false; + } + private static bool HandleSignRead(GetDataHandlerArgs args)//46读标牌,应该不影响读吧? + { + return false; + } + private static bool HandleSign(GetDataHandlerArgs args)//46写标牌 + { + var id = args.Data.ReadInt16(); + var x = args.Data.ReadInt16(); + var y = args.Data.ReadInt16(); + var house = Utils.InAreaHouse(x, y);//直接读出房子 + if (house == null) return false; + if (args.Player.Group.HasPermission(EditHouse) || args.Player.Account.ID.ToString() == house.Author || Utils.OwnsHouse(args.Player.Account.ID.ToString(), house) || Utils.CanUseHouse(args.Player.Account.ID.ToString(), house)) return false; + if (HousingPlugin.LConfig.WarningSpoiler) args.Player.Disable("无权修改标牌!"); + args.Player.SendErrorMessage("你没有权力修改被房子保护的地区的标牌。"); + args.Player.SendData(PacketTypes.SignNew, "", id); + return true;//假表示允许修改//真表示禁止修改 + } + private static bool HandleLiquidSet(GetDataHandlerArgs args)//48放水 + { + int tileX = args.Data.ReadInt16(); + int tileY = args.Data.ReadInt16(); + var house = Utils.InAreaHouse(tileX, tileY);//直接读出房子 + if (house == null) return false; + if (args.Player.Group.HasPermission(EditHouse) || args.Player.Account.ID.ToString() == house.Author || Utils.OwnsHouse(args.Player.Account.ID.ToString(), house)) return false; + if (HousingPlugin.LConfig.WarningSpoiler) args.Player.Disable("无权放水!"); + args.Player.SendErrorMessage("你没有权力在被房子保护的地区放水。"); + args.Player.SendTileSquareCentered(tileX, tileY); + return true;//假表示允许修改//真表示禁止修改 + } + private static bool HandleHitSwitch(GetDataHandlerArgs args)//59点击开关,暂时无意义 + { + return false; + int tileX = args.Data.ReadInt16(); + int tileY = args.Data.ReadInt16(); + //Console.WriteLine("点开关X{0} Y{1}}" , tileX, tileY); + } + private static bool HandlePaintTile(GetDataHandlerArgs args)//63油漆块 + { + var X = args.Data.ReadInt16(); + var Y = args.Data.ReadInt16(); + var house = Utils.InAreaHouse(X, Y);//直接读出房子 + if (house == null) return false; + if (args.Player.Group.HasPermission(EditHouse) || args.Player.Account.ID.ToString() == house.Author || Utils.OwnsHouse(args.Player.Account.ID.ToString(), house)) return false; + if (HousingPlugin.LConfig.WarningSpoiler) args.Player.Disable("无权油漆砖!"); + args.Player.SendErrorMessage("你没有权力在被房子保护的地区油漆砖。"); + args.Player.SendData(PacketTypes.PaintTile, "", X, Y, Main.tile[X, Y].color()); + return true;//假表示允许修改//真表示禁止修改 + } + private static bool HandlePaintWall(GetDataHandlerArgs args)//64油漆墙 + { + var X = args.Data.ReadInt16(); + var Y = args.Data.ReadInt16(); + var house = Utils.InAreaHouse(X, Y);//直接读出房子 + if (house == null) return false; + if (args.Player.Group.HasPermission(EditHouse) || args.Player.Account.ID.ToString() == house.Author || Utils.OwnsHouse(args.Player.Account.ID.ToString(), house)) return false; + if (HousingPlugin.LConfig.WarningSpoiler) args.Player.Disable("无权油漆墙!"); + args.Player.SendErrorMessage("你没有权力在被房子保护的地区油漆墙。"); + args.Player.SendData(PacketTypes.PaintWall, "", X, Y, Main.tile[X, Y].wallColor()); + return true;//假表示允许修改//真表示禁止修改 + } + private static bool HandleTeleport(GetDataHandlerArgs args)//65处理玩家传送,暂时无意义 + { + return false; + //if (HousingPlugin.LConfig.禁止传送进房子) + var Flags = args.Data.ReadInt8(); + var ID = args.Data.ReadInt16(); + var X = args.Data.ReadSingle(); + var Y = args.Data.ReadSingle(); + } + private static bool HandlePlaceObject(GetDataHandlerArgs args)//79放置物块 + { + int x = args.Data.ReadInt16(); + int y = args.Data.ReadInt16(); + var house = Utils.InAreaHouse(x, y);//直接读出放置房子 + if (house == null) return false; + if (args.Player.Group.HasPermission(EditHouse) || args.Player.Account.ID.ToString() == house.Author || Utils.OwnsHouse(args.Player.Account.ID.ToString(), house)) return false; + if (HousingPlugin.LConfig.WarningSpoiler) args.Player.Disable("无权修改房子保护!"); + args.Player.SendErrorMessage("你没有权力修改被房子保护的地区。"); + args.Player.SendTileSquareCentered(x, y); + return true;//假表示允许修改//真表示禁止修改 + } + private static bool HandlePlaceTileEntity(GetDataHandlerArgs args)//87放置实体 + { + var x = args.Data.ReadInt16(); + var y = args.Data.ReadInt16(); + var house = Utils.InAreaHouse(x, y);//直接读出放置房子 + if (house == null) return false; + if (args.Player.Group.HasPermission(EditHouse) || args.Player.Account.ID.ToString() == house.Author || Utils.OwnsHouse(args.Player.Account.ID.ToString(), house)) return false; + if (HousingPlugin.LConfig.WarningSpoiler) args.Player.Disable("无权修改房子保护!"); + args.Player.SendErrorMessage("你没有权力修改被房子保护的地区。"); + args.Player.SendTileSquareCentered(x, y); + return true;//假表示允许修改//真表示禁止修改 + } + private static bool HandlePlaceItemFrame(GetDataHandlerArgs args)//89放置物品框 + { + var x = args.Data.ReadInt16(); + var y = args.Data.ReadInt16(); + var itemFrame = (TEItemFrame)TileEntity.ByID[TEItemFrame.Find(x, y)]; + var house = Utils.InAreaHouse(x, y);//直接读出放置房子 + if (house == null) return false; + if (args.Player.Group.HasPermission(EditHouse) || args.Player.Account.ID.ToString() == house.Author || Utils.OwnsHouse(args.Player.Account.ID.ToString(), house) || Utils.CanUseHouse(args.Player.Account.ID.ToString(), house)) return false; + if (HousingPlugin.LConfig.WarningSpoiler) args.Player.Disable("无权修改房子保护的物品!"); + args.Player.SendErrorMessage("你没有权力修改被房子保护的物品。"); + NetMessage.SendData((int)PacketTypes.UpdateTileEntity, -1, -1, NetworkText.Empty, itemFrame.ID, 0, 1); + return true; + } + private static bool HandleGemLockToggle(GetDataHandlerArgs args)//105宝石锁 + { + var x = (int)args.Data.ReadInt16(); + var y = (int)args.Data.ReadInt16(); + if (!HousingPlugin.LConfig.ProtectiveGemstoneLock) return false; + var house = Utils.InAreaHouse(x, y);//直接读出放置房子 + if (house == null) return false; + if (args.Player.Group.HasPermission(EditHouse) || args.Player.Account.ID.ToString() == house.Author || Utils.OwnsHouse(args.Player.Account.ID.ToString(), house) || Utils.CanUseHouse(args.Player.Account.ID.ToString(), house)) return false; + if (HousingPlugin.LConfig.WarningSpoiler) args.Player.Disable("无权触发房子保护的宝石锁!"); + args.Player.SendErrorMessage("你没有权力触发被房子保护的宝石锁。"); + return true; + } + private static bool HandleMassWireOperation(GetDataHandlerArgs args)//109规模电路 + { + int x1 = args.Data.ReadInt16(); + int y1 = args.Data.ReadInt16(); + int x2 = args.Data.ReadInt16(); + int y2 = args.Data.ReadInt16(); + Rectangle A = new Rectangle(Math.Min(x1, x2), args.TPlayer.direction != 1 ? y1 : y2, Math.Abs(x2 - x1) + 1, 1); + Rectangle B = new Rectangle(args.TPlayer.direction != 1 ? x2 : x1, Math.Min(y1, y2), 1, Math.Abs(y2 - y1) + 1); + for (int i = 0; i < HousingPlugin.Houses.Count; i++) + { + var house = HousingPlugin.Houses[i]; if (house == null) continue; + if (house.HouseArea.Intersects(A) || house.HouseArea.Intersects(B)) + if (!(args.Player.Group.HasPermission(EditHouse) || args.Player.Account.ID.ToString() == house.Author || Utils.OwnsHouse(args.Player.Account.ID.ToString(), house) || Utils.CanUseHouse(args.Player.Account.ID.ToString(), house))) + return true; + } + return false; + } +} diff --git a/HouseRegion/Plugin.cs b/HouseRegion/Plugin.cs new file mode 100644 index 00000000..957c5cf8 --- /dev/null +++ b/HouseRegion/Plugin.cs @@ -0,0 +1,598 @@ +using Microsoft.Xna.Framework; +using MySql.Data.MySqlClient; +using System.Timers; +using Terraria; +using TerrariaApi.Server; +using TShockAPI; +using TShockAPI.DB; + + +namespace HouseRegion +{ + [ApiVersion(2, 1)]//api版本 + public class HousingPlugin : TerrariaPlugin + { + public override string Author => "GK 阁下 改良"; + public override string Description => "一个著名的用于保护房屋的插件。"; + public override string Name => "HousingDistricts"; + public override Version Version => new Version(1, 0, 0, 3); + public HousingPlugin(Main game) : base(game) { LPlayers = new LPlayer[256]; Order = 5; LConfig = new Config(); } + public static Config LConfig { get; set; } + internal static string LConfigPath { get { return Path.Combine(TShock.SavePath, "HouseRegion.json"); } } + public static LPlayer[] LPlayers { get; set; }//L表示local本地的意思 + public static List Houses = new(); + static readonly System.Timers.Timer Update = new(1100);//创建一个1.1秒的时钟 + public static bool ULock = false; + + #region 读取配置 + private void RC()//读取/重读配置文件 + { + try + { + if (File.Exists(LConfigPath)) + { + LConfig = Config.Read(LConfigPath);// 读取配置并且补全配置 + } + else + { + TShock.Log.ConsoleError("未找到房屋配置文件,已为您创建!" + + "修改配置后重启服务器可以应用新的配置。"); + } + LConfig.Write(LConfigPath); + } + catch (Exception ex) + { + TShock.Log.ConsoleError("房屋插件错误配置读取错误:" + ex.ToString()); + } + } + private void RD()//读取 + { + var table = new SqlTable("HousingDistrict", + new SqlColumn("ID", MySqlDbType.Int32) { Primary = true, AutoIncrement = true },//主键id自增 + new SqlColumn("Name", MySqlDbType.VarChar, 255) { Unique = true },//唯一的 + new SqlColumn("TopX", MySqlDbType.Int32), + new SqlColumn("TopY", MySqlDbType.Int32), + new SqlColumn("BottomX", MySqlDbType.Int32), + new SqlColumn("BottomY", MySqlDbType.Int32),//以上四个为两点的XY坐标 + new SqlColumn("Author", MySqlDbType.VarChar, 32),//作者 + new SqlColumn("Owners", MySqlDbType.Text),//拥有者 + new SqlColumn("WorldID", MySqlDbType.Text),//世界代号 + new SqlColumn("Locked", MySqlDbType.Int32),//是否上锁 + new SqlColumn("Users", MySqlDbType.Text));//使用者 + var SQLWriter = new SqlTableCreator(TShock.DB, + TShock.DB.GetSqlType() == SqlType.Sqlite ? + new SqliteQueryCreator() : new MysqlQueryCreator()); + SQLWriter.EnsureTableStructure(table); + } + private void RH()//读取房屋 + { + var SQLWriter = TShock.DB.QueryReader("SELECT* FROM HousingDistrict");//开始读取数据库 + Houses.Clear();//清除全部 + while (SQLWriter.Read())//判断循环 + { + //Console.WriteLine("读入1个 " + Main.worldID.ToString()); + if (SQLWriter.Get("WorldID") != Main.worldID.ToString()) continue;//世界id不一致到循环尾巴 + string author = SQLWriter.Get("Author");//读到房屋的作者 + List owners = SQLWriter.Get("Owners").Split(',').ToList();//找到拥有者列表 + bool locked = SQLWriter.Get("Locked") == 1;//读取上锁 + List user = SQLWriter.Get("Users").Split(',').ToList();//使用者列表 + Houses.Add(new House(new Rectangle(SQLWriter.Get("TopX"), + SQLWriter.Get("TopY"), + SQLWriter.Get("BottomX"), + SQLWriter.Get("BottomY")), + author, + owners, SQLWriter.Get("Name"), locked, user));//添加到组 + } + } + #endregion + + #region 插件的各种初始化 + public override void Initialize()// 插件启动时,用于初始化各种狗子 + { + RC(); RD(); GetDataHandlers.InitGetDataHandler();//初始化配置值,RH要放在服务器开成后再读不然世界ID读不出 + ServerApi.Hooks.GameInitialize.Register(this, OnInitialize);//钩住游戏初始化时 + ServerApi.Hooks.NetGetData.Register(this, GetData);//收到数据 + ServerApi.Hooks.NetGreetPlayer.Register(this, OnGreetPlayer);//玩家进入服务器 + ServerApi.Hooks.ServerLeave.Register(this, OnLeave);//玩家退出服务器 + ServerApi.Hooks.GamePostInitialize.Register(this, PostInitialize);//地图读入后 + } + protected override void Dispose(bool disposing)// 插件关闭时 + { + if (disposing) + { + ServerApi.Hooks.GameInitialize.Deregister(this, OnInitialize);//销毁游戏初始化狗子 + ServerApi.Hooks.NetGetData.Deregister(this, GetData);//收到数据 + ServerApi.Hooks.NetGreetPlayer.Deregister(this, OnGreetPlayer);//玩家进入服务器 + ServerApi.Hooks.ServerLeave.Deregister(this, OnLeave);//玩家退出服务器 + ServerApi.Hooks.GamePostInitialize.Deregister(this, PostInitialize);//地图读入后 + Update.Elapsed -= OnUpdate; Update.Stop();//销毁时钟 + } + base.Dispose(disposing); + } + private void OnGreetPlayer(GreetPlayerEventArgs e)//进入游戏时 + { + lock (LPlayers) + LPlayers[e.Who] = new LPlayer(e.Who, TShock.Players[e.Who].TileX, TShock.Players[e.Who].TileY); + } + private void OnLeave(LeaveEventArgs e) + { + lock (LPlayers) + if (LPlayers[e.Who] != null) LPlayers[e.Who] = null; + } + private void OnInitialize(EventArgs args)//游戏初始化 + { + Commands.ChatCommands.Add(new Command("house.use", HCommands, "house") { HelpText = "输入/house help可以显示与房子相关的操作提示。" }); + } + public void PostInitialize(EventArgs e) + { + RH(); Update.Elapsed += OnUpdate; Update.Start();//只有读入地图后才能取得地图ID否则地图ID为空//启动时钟 + } + #endregion + + #region 插件指令处理 + private void HCommands(CommandArgs args) + { + if (!args.Player.IsLoggedIn || args.Player.Account == null || args.Player.Account.ID == 0) + { args.Player.SendErrorMessage("你必须登录才能使用房子插件。"); return; } + string cmd = "help"; + const string AdminHouse = "house.admin";//定义管理权限和定义默认指令即如果指令后面什么都没跟的时候的指令 + if (args.Parameters.Count > 0) cmd = args.Parameters[0].ToLower(); + switch (cmd) + { + case "name"://确认房子名字 + { + args.Player.SendMessage("请敲击一个块查看它属于哪个房子。", Color.Yellow); + LPlayers[args.Player.Index].Look = true; break; + } + case "set"://设置点 + { + int choice = 0; + if (args.Parameters.Count == 2 && + int.TryParse(args.Parameters[1], out choice) && + choice >= 1 && choice <= 2) + { + if (choice == 1) args.Player.SendMessage("现在请敲击要保护的区域的左上角。", Color.Yellow); + if (choice == 2) args.Player.SendMessage("现在请敲击要保护的区域的右下角。", Color.Yellow); + args.Player.AwaitingTempPoint = choice; + } + else args.Player.SendErrorMessage("指令错误! 正确指令: /house set [1/2]"); break; + } + case "add"://添加房子 + { + if (args.Parameters.Count > 1) + { + var maxHouses = Utils.MaxCount(args.Player); int authorHouses = 0; + for (int i = 0; i < Houses.Count; i++) + { if (Houses[i].Author == args.Player.Account.ID.ToString()) authorHouses++; } + if (authorHouses < maxHouses || args.Player.Group.HasPermission("house.bypasscount")) + { + if (!args.Player.TempPoints.Any(p => p == Point.Zero)) + { + string houseName = string.Join(" ", args.Parameters.GetRange(1, args.Parameters.Count - 1)); + if (string.IsNullOrEmpty(houseName)) { args.Player.SendErrorMessage("房屋名称不能为空。"); return; } + var x = Math.Min(args.Player.TempPoints[0].X, args.Player.TempPoints[1].X); + var y = Math.Min(args.Player.TempPoints[0].Y, args.Player.TempPoints[1].Y); + var width = Math.Abs(args.Player.TempPoints[0].X - args.Player.TempPoints[1].X) + 1; + var height = Math.Abs(args.Player.TempPoints[0].Y - args.Player.TempPoints[1].Y) + 1; + var maxSize = Utils.MaxSize(args.Player); + if (width * height <= maxSize && width >= LConfig.MinWidth && height >= LConfig.MinHeight || args.Player.Group.HasPermission("house.bypasssize")) + { + Rectangle newHouseR = new Rectangle(x, y, width, height); + for (int i = 0; i < Houses.Count; i++) + { + if (newHouseR.Intersects(Houses[i].HouseArea)) + { + args.Player.TempPoints[0] = Point.Zero; args.Player.TempPoints[1] = Point.Zero;//清除点 + args.Player.SendErrorMessage("你选择的区域与其他房子存在重叠,这是不允许的。"); + return; + } + } + if (newHouseR.Intersects(new Rectangle(Main.spawnTileX, Main.spawnTileY, TShock.Config.Settings.SpawnProtectionRadius, TShock.Config.Settings.SpawnProtectionRadius))) + { + args.Player.TempPoints[0] = Point.Zero; args.Player.TempPoints[1] = Point.Zero;//清除点 + args.Player.SendErrorMessage("你选择的区域与出生保护范围重叠,这是不允许的。"); + return; + } + for (int i = 0; i < TShock.Regions.Regions.Count; i++) + { + if (newHouseR.Intersects(TShock.Regions.Regions[i].Area)) + { + args.Player.TempPoints[0] = Point.Zero; args.Player.TempPoints[1] = Point.Zero;//清除点 + args.Player.SendErrorMessage("你选择的区域与Tshock区域 {0} 重叠,这是不允许的。", TShock.Regions.Regions[i].Name); + return; + } + } + if (HouseManager.AddHouse(x, y, width, height, houseName, args.Player.Account.ID.ToString())) + { + args.Player.SendMessage("你建造了新房子 " + houseName, Color.Yellow); + TShock.Log.ConsoleInfo("{0} 建了新房子: {1}", args.Player.Account.Name, houseName); + } + else { args.Player.SendErrorMessage("房子 " + houseName + " 已存在!"); return; }//此情况不清除点 + } + else + { + args.Player.SendErrorMessage("您设置的房屋宽:{0} 高:{1} 面积:{2} 需重新设置。", width, height, width * height); + if (width * height >= maxSize) args.Player.SendErrorMessage("因为您的房子总面积超过了最大限制 " + maxSize.ToString() + " 格块。"); + if (width < LConfig.MinWidth) args.Player.SendErrorMessage("因为您的房屋宽度小于最小限制 " + LConfig.MinWidth.ToString() + " 格块。"); + if (height < LConfig.MinHeight) args.Player.SendErrorMessage("因为您的房屋高度小于最小限制 " + LConfig.MinHeight.ToString() + " 格块。"); + } + } + else args.Player.SendErrorMessage("未设置完整的房屋点,建议先使用指令: /house help"); + } + else args.Player.SendErrorMessage("房屋添加失败:您只能添加{0}个房屋!", maxHouses); + args.Player.TempPoints[0] = Point.Zero; args.Player.TempPoints[1] = Point.Zero;//清除已设置的点 + } + else args.Player.SendErrorMessage("语法错误! 正确语法: /house add [屋名]"); break;//第二分号是顺序执行 + } + case "allow"://添加所有者 + { + if (args.Parameters.Count > 2) + { + string playerName = args.Parameters[1]; + UserAccount playerID; + string housename = string.Join(" ", args.Parameters.GetRange(2, args.Parameters.Count - 2)); + var house = Utils.GetHouseByName(housename); + if (house == null) { args.Player.SendErrorMessage("没有找到这个房子!"); return; } + if (house.Author == args.Player.Account.ID.ToString() || args.Player.Group.HasPermission(AdminHouse)) + { + if ((playerID = TShock.UserAccounts.GetUserAccountByName(playerName)) != null) + { + if (LConfig.ProhibitSharingOwner) { args.Player.SendErrorMessage("无法使用,因为服务器禁止了分享所有者功能。"); return; } + if (playerID.ID.ToString() != house.Author && !Utils.OwnsHouse(playerID.ID.ToString(), house)) + { + if (HouseManager.AddNewOwner(house.Name, playerID.ID.ToString())) + { + args.Player.SendMessage("成功为 " + playerName + " 添加房屋 " + house.Name + " 的拥有权!", Color.Yellow); + TShock.Log.ConsoleInfo("{0} 添加 {1} 为房屋 {2} 的拥有者。", args.Player.Account.Name, playerID.Name, house.Name); + } + else args.Player.SendErrorMessage("添加用户权力失败。"); + } + else args.Player.SendErrorMessage("用户 " + playerName + " 已拥有此房屋权限。"); + } + else args.Player.SendErrorMessage("用户 " + playerName + " 不存在。"); + } + else args.Player.SendErrorMessage("你没有权力分享这个房子!");//只有房子的作者可以 + } + else args.Player.SendErrorMessage("语法错误! 正确语法: /house allow [名字] [屋名]"); break; + } + case "disallow": + { + if (args.Parameters.Count > 2) + { + string playerName = args.Parameters[1]; UserAccount playerID; + var house = Utils.GetHouseByName(string.Join(" ", args.Parameters.GetRange(2, args.Parameters.Count - 2))); + if (house == null) { args.Player.SendErrorMessage("没有找到这个房子!"); return; } + if (house.Author == args.Player.Account.ID.ToString() || args.Player.Group.HasPermission(AdminHouse)) + { + if ((playerID = TShock.UserAccounts.GetUserAccountByName(playerName)) != null) + { + if (!Utils.OwnsHouse(playerID.ID.ToString(), house)) { args.Player.SendErrorMessage("目标非此房屋拥有者。"); return; } + if (HouseManager.DeleteOwner(house.Name, playerID.ID.ToString())) + { + args.Player.SendMessage("成功移除 " + playerName + " 的房屋 " + house.Name + "的拥有权!", Color.Yellow); + TShock.Log.ConsoleInfo("{0} 移除 {1} 的房屋 {2} 的拥有者。", args.Player.Account.Name, playerID.Name, house.Name); + } + else args.Player.SendErrorMessage("移除用户权力失败。"); + } + else args.Player.SendErrorMessage("用户 " + playerName + " 不存在。"); + } + else args.Player.SendErrorMessage("你没有权力管理这个房子!");//只有房子的作者可以 + } + else args.Player.SendErrorMessage("语法错误! 正确语法: /house disallow [名字] [屋名]"); break; + } + case "adduser"://添加使用者 + { + if (args.Parameters.Count > 2) + { + string playerName = args.Parameters[1]; UserAccount playerID; + string housename = string.Join(" ", args.Parameters.GetRange(2, args.Parameters.Count - 2)); + var house = Utils.GetHouseByName(housename); + if (house == null) { args.Player.SendErrorMessage("没有找到这个房子!"); return; } + if (house.Author == args.Player.Account.ID.ToString() || args.Player.Group.HasPermission(AdminHouse) || Utils.OwnsHouse(args.Player.Account, house)) + { + if (house.Author != args.Player.Account.ID.ToString() && !args.Player.Group.HasPermission(AdminHouse) && LConfig.ProhibitOwnerModifyingUser) + { args.Player.SendErrorMessage("无法使用,因为服务器禁止了所有者分享使用者功能。"); return; } + if ((playerID = TShock.UserAccounts.GetUserAccountByName(playerName)) != null) + { + if (LConfig.ProhibitSharingUser) { args.Player.SendErrorMessage("无法使用,因为服务器禁止了分享使用者功能。"); return; } + if (playerID.ID.ToString() != house.Author && !Utils.OwnsHouse(playerID.ID.ToString(), house) && !Utils.CanUseHouse(playerID.ID.ToString(), house)) + { + if (HouseManager.AddNewUser(house.Name, playerID.ID.ToString())) + { + args.Player.SendMessage("成功为 " + playerName + " 添加房屋 " + house.Name + " 的使用权!", Color.Yellow); + TShock.Log.ConsoleInfo("{0} 添加 {1} 为房屋 {2} 的使用者。", args.Player.Account.Name, playerID.Name, house.Name); + } + else args.Player.SendErrorMessage("添加用户权力失败。"); + } + else args.Player.SendErrorMessage("用户 " + playerName + " 已拥有此房屋权限。"); + } + else args.Player.SendErrorMessage("用户 " + playerName + " 不存在。"); + } + else args.Player.SendErrorMessage("你没有权力分享这个房子!");//只有房子的作者可以 + } + else args.Player.SendErrorMessage("语法错误! 正确语法: /house adduser [名字] [屋名]"); break; + } + case "deluser"://删除使用者 + { + if (args.Parameters.Count > 2) + { + string playerName = args.Parameters[1]; UserAccount playerID; + var house = Utils.GetHouseByName(string.Join(" ", args.Parameters.GetRange(2, args.Parameters.Count - 2))); + if (house == null) { args.Player.SendErrorMessage("没有找到这个房子!"); return; } + if (house.Author == args.Player.Account.ID.ToString() || args.Player.Group.HasPermission(AdminHouse) || Utils.OwnsHouse(args.Player.Account, house)) + { + if (house.Author != args.Player.Account.ID.ToString() && !args.Player.Group.HasPermission(AdminHouse) && LConfig.ProhibitOwnerModifyingUser) + { args.Player.SendErrorMessage("无法使用,因为服务器禁止了所有者修改使用者功能。"); return; } + if ((playerID = TShock.UserAccounts.GetUserAccountByName(playerName)) != null) + { + if (!Utils.CanUseHouse(playerID.ID.ToString(), house)) { args.Player.SendErrorMessage("目标非此房屋使用者。"); return; } + if (HouseManager.DeleteUser(house.Name, playerID.ID.ToString())) + { + args.Player.SendMessage("成功移除 " + playerName + " 的房屋 " + house.Name + "的使用权!", Color.Yellow); + TShock.Log.ConsoleInfo("{0} 移除 {1} 的房屋 {2} 的使用者。", args.Player.Account.Name, playerID.Name, house.Name); + } + else args.Player.SendErrorMessage("移除用户权力失败。"); + } + else args.Player.SendErrorMessage("用户 " + playerName + " 不存在。"); + } + else args.Player.SendErrorMessage("你没有权力管理这个房子!");//只有房子的作者可以 + } + else args.Player.SendErrorMessage("语法错误! 正确语法: /house deluser [名字] [屋名]"); break; + } + case "delete": + { + if (args.Parameters.Count > 1) + { + string houseName = string.Join(" ", args.Parameters.GetRange(1, args.Parameters.Count - 1)); + var house = Utils.GetHouseByName(houseName); + if (house == null) { args.Player.SendErrorMessage("没有找到这个房子!"); return; } + if (house.Author == args.Player.Account.ID.ToString() || args.Player.Group.HasPermission(AdminHouse)) + { + try + { + TShock.DB.Query("DELETE FROM HousingDistrict WHERE Name=@0", house.Name); + } + catch (Exception ex) + { + TShock.Log.Error("房屋插件错误删除错误" + ex.ToString()); + args.Player.SendErrorMessage("房屋删除失败!"); + return; + } + Houses.Remove(house); + args.Player.SendMessage("房屋: " + house.Name + " 删除成功!", Color.Yellow); + TShock.Log.ConsoleInfo("{0} 删除房屋: {1}", args.Player.Account.Name, house.Name); + } + else args.Player.SendErrorMessage("你没有权力删除这个房子!");//只有房子的作者可以 + } + else args.Player.SendErrorMessage("语法错误! 正确语法: /house delete [屋名]"); break; + } + case "clear": + { + args.Player.TempPoints[0] = Point.Zero; + args.Player.TempPoints[1] = Point.Zero; + args.Player.AwaitingTempPoint = 0; + args.Player.SendMessage("临时敲击点清除完毕!", Color.Yellow); + break; + } + case "list": + { + const int pagelimit = 15; const int perline = 5; int page = 0;//每页上限每行上限和当前页码 + if (args.Parameters.Count > 1) + { + if (!int.TryParse(args.Parameters[1], out page) || page < 1) + { args.Player.SendErrorMessage(string.Format("无效页码 ({0})", page)); return; } + page--; //从0开始而不是1 + } + List Lhouses = new List(); + for (int i = 0; i < Houses.Count; i++) + { + if (args.Player.Group.HasPermission(AdminHouse) || args.Player.Account.ID.ToString() == Houses[i].Author || Utils.OwnsHouse(args.Player.Account.ID.ToString(), Houses[i]) || Utils.CanUseHouse(args.Player.Account.ID.ToString(), Houses[i])) + Lhouses.Add(Houses[i]); + } + if (Lhouses.Count == 0) { args.Player.SendMessage("您目前还没有已定义房屋。", Color.Yellow); return; } + int pagecount = Lhouses.Count / pagelimit; + if (page > pagecount) { args.Player.SendErrorMessage("页码超过最大页数 ({0}/{1})", page + 1, pagecount + 1); return; } + args.Player.SendMessage(string.Format("目前的房屋 ({0}/{1}) 页:", page + 1, pagecount + 1), Color.Green); + var nameslist = new List(); + for (int i = page * pagelimit; i < page * pagelimit + pagelimit && i < Lhouses.Count; i++) nameslist.Add(Lhouses[i].Name); + var names = nameslist.ToArray(); + for (int i = 0; i < names.Length; i += perline) + args.Player.SendMessage(string.Join(", ", names, i, Math.Min(names.Length - i, perline)), Color.Yellow); + if (page < pagecount) args.Player.SendMessage(string.Format("输入 /house list {0} 查看更多房屋。", page + 2), Color.Yellow); + break; + } + case "redefine"://重新定义 + { + if (args.Parameters.Count > 1) + { + if (!args.Player.TempPoints.Any(p => p == Point.Zero)) + { + string houseName = string.Join(" ", args.Parameters.GetRange(1, args.Parameters.Count - 1)); + var house = Utils.GetHouseByName(houseName); + if (house == null) { args.Player.SendErrorMessage("没有找到这个房子!"); return; }//此时不清楚点 + if (house.Author == args.Player.Account.ID.ToString() || args.Player.Group.HasPermission(AdminHouse)) + { + var x = Math.Min(args.Player.TempPoints[0].X, args.Player.TempPoints[1].X); + var y = Math.Min(args.Player.TempPoints[0].Y, args.Player.TempPoints[1].Y); + var width = Math.Abs(args.Player.TempPoints[0].X - args.Player.TempPoints[1].X) + 1; + var height = Math.Abs(args.Player.TempPoints[0].Y - args.Player.TempPoints[1].Y) + 1; + var maxSize = Utils.MaxSize(args.Player); + if (width * height <= maxSize && width >= LConfig.MinWidth && height >= LConfig.MinHeight || args.Player.Group.HasPermission("house.bypasssize")) + { + Rectangle newHouseR = new Rectangle(x, y, width, height); + for (int i = 0; i < Houses.Count; i++) + { + if (newHouseR.Intersects(Houses[i].HouseArea) && Houses[i].Name != house.Name)//如果发现重叠并且不是本房屋 + { + args.Player.TempPoints[0] = Point.Zero; args.Player.TempPoints[1] = Point.Zero;//清除点 + args.Player.SendErrorMessage("你选择的区域与其他房子存在重叠,这是不允许的。"); + return; + } + } + if (newHouseR.Intersects(new Rectangle(Main.spawnTileX, Main.spawnTileY, TShock.Config.Settings.SpawnProtectionRadius, TShock.Config.Settings.SpawnProtectionRadius))) + { + args.Player.TempPoints[0] = Point.Zero; args.Player.TempPoints[1] = Point.Zero;//清除点 + args.Player.SendErrorMessage("你选择的区域与出生保护范围重叠,这是不允许的。"); + return; + } + for (int i = 0; i < TShock.Regions.Regions.Count; i++) + { + if (newHouseR.Intersects(TShock.Regions.Regions[i].Area)) + { + args.Player.TempPoints[0] = Point.Zero; args.Player.TempPoints[1] = Point.Zero;//清除点 + args.Player.SendErrorMessage("你选择的区域与Tshock区域 {0} 重叠,这是不允许的。", TShock.Regions.Regions[i].Name); + return; + } + } + if (HouseManager.RedefineHouse(x, y, width, height, houseName)) + { + args.Player.SendMessage("重新定义了房子 " + houseName, Color.Yellow); + TShock.Log.ConsoleInfo("{0} 重新定义的房子: {1}", args.Player.Account.Name, houseName); + } + else { args.Player.SendErrorMessage("重新定义房屋时出错!"); return; }//此情况不清除点 + } + else + { + args.Player.SendErrorMessage("您设置的房屋宽:{0} 高:{1} 面积:{2} 需重新设置。", width, height, width * height); + if (width * height >= maxSize) args.Player.SendErrorMessage("因为您的房子总面积超过了最大限制 " + maxSize.ToString() + " 格块。"); + if (width < LConfig.MinWidth) args.Player.SendErrorMessage("因为您的房屋宽度小于最小限制 " + LConfig.MinWidth.ToString() + " 格块。"); + if (height < LConfig.MinHeight) args.Player.SendErrorMessage("因为您的房屋高度小于最小限制 " + LConfig.MinHeight.ToString() + " 格块。"); + } + } + else args.Player.SendErrorMessage("你没有权力修改这个房子!");//只有房子的作者可以 + } + else args.Player.SendErrorMessage("未设置完整的房屋点,建议先使用指令: /house help"); + args.Player.TempPoints[0] = Point.Zero; args.Player.TempPoints[1] = Point.Zero;//清除已设置的点 + } + else args.Player.SendErrorMessage("语法错误! 正确语法: /house redefine [屋名]"); break; + } + case "info": + { + if (args.Parameters.Count > 1) + { + var house = Utils.GetHouseByName(args.Parameters[1]); + if (house == null) { args.Player.SendErrorMessage("没有找到这个房子!"); return; } + if (args.Player.Group.HasPermission(AdminHouse) || args.Player.Account.ID.ToString() == house.Author || Utils.OwnsHouse(args.Player.Account.ID.ToString(), house) || Utils.CanUseHouse(args.Player.Account.ID.ToString(), house)) + { + string OwnerNames = ""; string UserNames = ""; string AuthorNames = ""; + try { AuthorNames = TShock.UserAccounts.GetUserAccountByID(Convert.ToInt32(house.Author)).Name; } + catch (Exception ex) { TShock.Log.Error("房屋插件错误超标错误:" + ex.ToString()); } + for (int i = 0; i < house.Owners.Count; i++) + { + var ID = house.Owners[i]; + try { OwnerNames += (string.IsNullOrEmpty(OwnerNames) ? "" : ", ") + TShock.UserAccounts.GetUserAccountByID(Convert.ToInt32(ID)).Name; } + catch (Exception ex) { TShock.Log.Error("房屋插件错误超标错误:" + ex.ToString()); } + } + for (int i = 0; i < house.Users.Count; i++) + { + var ID = house.Users[i]; + try { UserNames += (string.IsNullOrEmpty(UserNames) ? "" : ", ") + TShock.UserAccounts.GetUserAccountByID(Convert.ToInt32(ID)).Name; } + catch (Exception ex) { TShock.Log.Error("房屋插件错误超标错误:" + ex.ToString()); } + } + args.Player.SendMessage("房屋 " + house.Name + " 的信息:", Color.LawnGreen); + args.Player.SendMessage("作者: " + AuthorNames, Color.LawnGreen); + args.Player.SendMessage("状态: " + (!house.Locked || LConfig.LimitLockHouse ? "未上锁" : "已上锁"), Color.LawnGreen); + args.Player.SendMessage("拥有者: " + OwnerNames, Color.LawnGreen); + args.Player.SendMessage("使用者: " + UserNames, Color.LawnGreen); + } + else args.Player.SendErrorMessage("你没有权力查看这个房子的信息!"); + } + else args.Player.SendErrorMessage("语法错误! 正确语法: /house info [屋名]"); break; + } + case "lock": + { + if (args.Parameters.Count > 1) + { + string houseName = string.Join(" ", args.Parameters.GetRange(1, args.Parameters.Count - 1)); + var house = Utils.GetHouseByName(houseName); + if (house == null) { args.Player.SendErrorMessage("没有找到这个房子!"); return; } + if (LConfig.LimitLockHouse) { args.Player.SendErrorMessage("无法使用,因为服务器关闭了锁房屋功能。"); return; } + if (args.Player.Group.HasPermission(AdminHouse) || args.Player.Account.ID.ToString() == house.Author || Utils.OwnsHouse(args.Player.Account.ID.ToString(), house) || Utils.CanUseHouse(args.Player.Account.ID.ToString(), house)) + { + if (HouseManager.ChangeLock(house)) + { + args.Player.SendMessage("房子: " + house.Name + (house.Locked ? " 上锁" : " 开锁"), Color.Yellow); + TShock.Log.ConsoleInfo("{0} 修改锁状态: {1}", args.Player.Account.Name, house.Name); + } + else args.Player.SendErrorMessage("修改房屋锁失败!"); + } + else args.Player.SendErrorMessage("你没有权力修改这个房子的锁!"); + } + else args.Player.SendErrorMessage("语法错误! 正确语法: /house lock [屋名]"); break; + } + default: + { + args.Player.SendMessage("要创建房屋,请使用以下命令:", Color.Lime); + args.Player.SendMessage("/house set 1", Color.Lime); + args.Player.SendMessage("/house set 2", Color.Lime); + args.Player.SendMessage("/house add 房屋名字", Color.Lime); + args.Player.SendMessage("其他命令: list, allow, disallow, redefine, name, delete, clear, info, adduser, deluser, lock", Color.Lime); + break; + } + }//循环的括号 + }//方法的括号 + #endregion + + #region 收到数据 + private void GetData(GetDataEventArgs args)//收到数据的狗子程序 + { + if (args.Handled) return; + //Console.WriteLine("收到数据"+ args.MsgID); + var user = TShock.Players[args.Msg.whoAmI]; + if (user == null) { args.Handled = true; return; }//若用户不存在直接返回真 + if (!user.ConnectionAlive) { args.Handled = true; return; }//若已丢失连接直接返回 + using (var data = new MemoryStream(args.Msg.readBuffer, args.Index, args.Length)) + { + try { if (GetDataHandlers.HandlerGetData(args.MsgID, user, data)) args.Handled = true; } + catch (Exception ex) { TShock.Log.Error("房屋插件错误传递时出错:" + ex.ToString()); } + } + } + #endregion + + #region 时钟事件 + public void OnUpdate(object sender, ElapsedEventArgs e)//时钟更新数据时 + { + if (!LConfig.HouseRegion || ULock) return;//若关闭了提示或时钟正在执行过程中,则这次放弃执行 + ULock = true; var Start = DateTime.Now;//Console.WriteLine("时钟正常启动" ); + lock (LPlayers) + foreach (LPlayer user in LPlayers) + { + if (user == null) continue;//空白跳过 + if (Timeout(Start)) return;//检测耗时超时 + var player = TShock.Players[user.Who]; + if (player == null) { ULock = false; return; }//玩家组变动,此时不计入 + var house = Utils.InAreaHouse(player.TileX, player.TileY);//当前所在的房子 + var Lhouse = Utils.InAreaHouse(user.TileX, user.TileY);//之前所在的房子 + LPlayers[user.Who].TileX = player.TileX; LPlayers[user.Who].TileY = player.TileY;//使此为前 + if (Lhouse == null && house == null) continue; + if (Lhouse == null && house != null)//此乃进入了房子 + { + if (player.Account.ID.ToString() == house.Author || Utils.OwnsHouse(player.Account.ID.ToString(), house) || Utils.CanUseHouse(player.Account.ID.ToString(), house)) + player.SendMessage("你进入了你的房子: " + house.Name, Color.LightSeaGreen); + else player.SendMessage("你进入了房子: " + house.Name, Color.LightSeaGreen); + } + else if (Lhouse != null && house == null)//此乃离开了房子 + { + if (player.Account.ID.ToString() == Lhouse.Author || Utils.OwnsHouse(player.Account.ID.ToString(), Lhouse) || Utils.CanUseHouse(player.Account.ID.ToString(), Lhouse)) + player.SendMessage("你离开了你的房子: " + Lhouse.Name, Color.LightSeaGreen); + else player.SendMessage("你离开了房子: " + Lhouse.Name, Color.LightSeaGreen); + } + else//此乃离开了一个房子又进入另外一个房子 + { + if (Lhouse.Name == house.Name) continue;//仍然在原来的房子里 + if (player.Account.ID.ToString() == house.Author || Utils.OwnsHouse(player.Account.ID.ToString(), house) || Utils.CanUseHouse(player.Account.ID.ToString(), house)) + player.SendMessage("你进入了你的房子: " + house.Name, Color.LightSeaGreen); + else player.SendMessage("你进入了房子: " + house.Name, Color.LightSeaGreen); + } + } + ULock = false; + } + public static bool Timeout(DateTime Start, bool warn = true, int ms = 500)//参数1启始时间,参数2通告,3,超时时间 + { + bool ret = (DateTime.Now - Start).TotalMilliseconds >= ms; if (ret) ULock = false;//超时自动解锁 + if (warn && ret) { TShock.Log.Error("房子插件提示处理超时,已抛弃部分提示!"); } + return ret; + } + #endregion + } +} diff --git a/HouseRegion/README.md b/HouseRegion/README.md new file mode 100644 index 00000000..8d33a0a7 --- /dev/null +++ b/HouseRegion/README.md @@ -0,0 +1,54 @@ +# ShowArmors 发送装备 + +- 作者: GK +- 出处: 无 +- 更好的圈地插件 + +## 更新日志 + +``` +暂无 +``` + +## 指令 + +| 语法 | 别名 | 权限 | 说明 | +| ----------------------------- | :--: | :-----------------------: | :------------: | +| /house set 1 | 无 | house.use | 敲击左上角 | +| /house set 2 | 无 | house.use | 敲击右下角 | +| /house clear | 无 | house.use | 重置临时敲击点 | +| /house allow [玩家] [房屋] | 无 | `house.use` `house.admin` | 添加所有者 | +| /house disallow [玩家] [房屋] | 无 | `house.use` `house.admin` | 移除所有者 | +| /house adduser [玩家] [房屋] | 无 | `house.use` `house.admin` | 添加使用者 | +| /house deluser [玩家] [房屋] | 无 | `house.use` `house.admin` | 删除使用者 | +| /house delete [屋名] | 无 | `house.use` `house.admin` | 删除房屋 | +| /house list [页码] | 无 | `house.use` `house.admin` | 查看房屋列表 | +| /house redefine [屋名] | 无 | `house.use` `house.admin` | 重新定义房屋 | +| /house info [屋名] | 无 | `house.use` `house.admin` | 房屋信息 | +| /house lock [屋名] | 无 | `house.use` `house.admin` | 房屋锁 | + +## 配置 + +> HouseRegion.json + +```json +{ + "进出房屋提示": true, + "房屋嘴大大小": 1000, + "房屋最小宽度": 30, + "房屋最小高度": 30, + "房屋最大数量": 1, + "禁止锁房屋": false, + "保护宝石锁": false, + "始终保护箱子": false, + "冻结警告破坏者": true, + "禁止分享所有者7": false, + "禁止分享使用者": false, + "禁止所有者修改使用者": true +} +``` + +## 反馈 + +- 共同维护的插件库:https://github.com/Controllerdestiny/TShockPlugin +- 国内社区 trhub.cn 或 TShock 官方群等 diff --git a/HouseRegion/Utils.cs b/HouseRegion/Utils.cs new file mode 100644 index 00000000..baaa5426 --- /dev/null +++ b/HouseRegion/Utils.cs @@ -0,0 +1,230 @@ +using Microsoft.Xna.Framework; +using System.Text; +using System.Text.RegularExpressions; +using Terraria; +using TShockAPI; +using TShockAPI.DB; + + +namespace HouseRegion; + +class Utils//专属工具包 +{ + public static int MaxCount(TSPlayer ply)//通过正则获取权限中自定义房屋数量最大值 + { + for (int i = 0; i < ply.Group.permissions.Count; i++) + { + var perm = ply.Group.permissions[i]; + Match Match = Regex.Match(perm, @"^house\.count\.(\d{1,9})$"); + if (Match.Success) return Convert.ToInt32(Match.Groups[1].Value); + } + return HousingPlugin.LConfig.HouseMaxNumber; + } + public static int MaxSize(TSPlayer ply)//取最大尺寸 + { + for (int i = 0; i < ply.Group.permissions.Count; i++) + { + var perm = ply.Group.permissions[i]; + Match Match = Regex.Match(perm, @"^house\.size\.(\d{1,9})$");//正则 + if (Match.Success) return Convert.ToInt32(Match.Groups[1].Value); + } + return HousingPlugin.LConfig.HouseMaxSize;//没有权限指定则返回配置的 内容 + } + public static House GetHouseByName(string name)//取得指定名字的房子 + { + if (string.IsNullOrEmpty(name)) return null; + for (int i = 0; i < HousingPlugin.Houses.Count; i++) + { + var house = HousingPlugin.Houses[i]; + if (house == null) continue; + if (house.Name == name) return house; + } + return null; + } + + public static bool OwnsHouse(UserAccount U, string housename)//判断是否为房屋的所有者 + { + if (U == null) return false; + return OwnsHouse(U.ID.ToString(), housename); + } + + public static bool OwnsHouse(UserAccount U, House house)//判断是否为房屋的所有者 + { + if (U == null) return false; + return OwnsHouse(U.ID.ToString(), house); + } + + public static bool OwnsHouse(string UserID, string housename)//判断是否为房屋的所有者 + { + if (string.IsNullOrWhiteSpace(UserID) || UserID == "0" || string.IsNullOrEmpty(housename)) return false; + House H = GetHouseByName(housename);//各种排错之后看看这个房子在不在 + if (H == null) return false; + return OwnsHouse(UserID, H); + } + public static bool OwnsHouse(string UserID, House house)//判断是否为房屋的所有者 + { + if (!string.IsNullOrEmpty(UserID) && UserID != "0" && house != null) + { + try { if (house.Owners.Contains(UserID)) return true; else return false; } + catch (Exception ex) { TShock.Log.Error("房屋插件错误超标错误:" + ex.ToString()); return false; } + } + return false; + } + public static bool CanUseHouse(string UserID, House house)//是否为使用者 + { + return !string.IsNullOrEmpty(UserID) && UserID != "0" && house.Users.Contains(UserID); + } + + public static bool CanUseHouse(UserAccount U, House house)//形式不一样 + { + return U != null && U.ID != 0 && house.Users.Contains(U.ID.ToString()); + } + public static string InAreaHouseName(int x, int y)//指定位置的房子名字 + { + for (int i = 0; i < HousingPlugin.Houses.Count; i++) + { + var house = HousingPlugin.Houses[i]; + if (house == null) continue; + if (x >= house.HouseArea.Left && x < house.HouseArea.Right && + y >= house.HouseArea.Top && y < house.HouseArea.Bottom) + return house.Name; + } + return null; + } + public static House InAreaHouse(int x, int y)//指定位置的房子 + { + for (int i = 0; i < HousingPlugin.Houses.Count; i++) + { + var house = HousingPlugin.Houses[i]; + if (house == null) continue; + if (x >= house.HouseArea.Left && x < house.HouseArea.Right && + y >= house.HouseArea.Top && y < house.HouseArea.Bottom) + return house; + } + return null; + } +} +public class HouseManager//房屋管理 +{ + const string cols = "Name, TopX, TopY, BottomX, BottomY, Author, Owners, WorldID, Locked, Users"; + public static bool AddHouse(int tx, int ty, int width, int height, string housename, string author) + { + int locked = 1; + if (Utils.GetHouseByName(housename) != null) return false; + try + { + TShock.DB.Query("INSERT INTO HousingDistrict (" + cols + ") VALUES (@0, @1, @2, @3, @4, @5, @6, @7, @8, @9);", housename, tx, ty, width, height, author, "", Main.worldID.ToString(), locked, ""); + } + catch (Exception ex) { TShock.Log.Error("房屋插件错误数据库写入错误:" + ex.ToString()); return false; } + HousingPlugin.Houses.Add(new House(new Rectangle(tx, ty, width, height), author, new List(), housename, locked == 1, new List())); + return true; + } + public static bool AddNewOwner(string houseName, string id) + { + var house = Utils.GetHouseByName(houseName); + if (house == null) return false; + StringBuilder sb = new StringBuilder();//某些人 + int count = 0; house.Owners.Add(id); + for (int i = 0; i < house.Owners.Count; i++) + { + var owner = house.Owners[i]; + count++; sb.Append(owner); + if (count != house.Owners.Count) sb.Append(",");//添加分隔符 + } + try + { + string query = "UPDATE HousingDistrict SET Owners=@0 WHERE Name=@1"; + TShock.DB.Query(query, sb.ToString(), houseName); + } + catch (Exception ex) { TShock.Log.Error("房屋插件错误数据库修改错误:" + ex.ToString()); return false; } + return true; + } + public static bool AddNewUser(string houseName, string id)//添加使用者 + { + var house = Utils.GetHouseByName(houseName); + if (house == null) return false; + StringBuilder sb = new StringBuilder();//某些人 + int count = 0; house.Users.Add(id); + for (int i = 0; i < house.Users.Count; i++) + { + var user = house.Users[i]; + count++; sb.Append(user); + if (count != house.Users.Count) sb.Append(",");//添加分隔符 + } + try + { + string query = "UPDATE HousingDistrict SET Users=@0 WHERE Name=@1"; + TShock.DB.Query(query, sb.ToString(), houseName); + } + catch (Exception ex) { TShock.Log.Error("房屋插件错误数据库修改错误:" + ex.ToString()); return false; } + return true; + } + public static bool DeleteOwner(string houseName, string id)//删除所有者 + { + var house = Utils.GetHouseByName(houseName); + if (house == null) return false; + StringBuilder sb = new StringBuilder(); + int count = 0; house.Owners.Remove(id); + for (int i = 0; i < house.Owners.Count; i++) + { + var owner = house.Owners[i]; + count++; sb.Append(owner); + if (count != house.Owners.Count) sb.Append(","); + } + try + { + string query = "UPDATE HousingDistrict SET Owners=@0 WHERE Name=@1"; + TShock.DB.Query(query, sb.ToString(), houseName); + } + catch (Exception ex) { TShock.Log.Error("房屋插件错误数据库修改错误:" + ex.ToString()); return false; } + return true; + } + public static bool DeleteUser(string houseName, string id)//删除使用者 + { + var house = Utils.GetHouseByName(houseName); + if (house == null) return false; + StringBuilder sb = new StringBuilder(); + int count = 0; house.Users.Remove(id); + for (int i = 0; i < house.Users.Count; i++) + { + var users = house.Users[i]; + count++; sb.Append(users); + if (count != house.Users.Count) sb.Append(","); + } + try + { + string query = "UPDATE HousingDistrict SET Users=@0 WHERE Name=@1"; + TShock.DB.Query(query, sb.ToString(), houseName); + } + catch (Exception ex) { TShock.Log.Error("房屋插件错误数据库修改错误:" + ex.ToString()); return false; } + return true; + } + public static bool RedefineHouse(int tx, int ty, int width, int height, string housename) + { + try + { + var house = Utils.GetHouseByName(housename); + var houseName = house.Name; + try + { + string query = "UPDATE HousingDistrict SET TopX=@0, TopY=@1, BottomX=@2, BottomY=@3, WorldID=@4 WHERE Name=@5"; + TShock.DB.Query(query, tx, ty, width, height, Main.worldID.ToString(), house.Name); + } + catch (Exception ex) { TShock.Log.Error("房屋插件错误数据库修改错误:" + ex.ToString()); return false; } + house.HouseArea = new Rectangle(tx, ty, width, height); + } + catch (Exception ex) { TShock.Log.Error("房屋插件错误重新定义房屋时出错:" + ex.ToString()); return false; } + return true; + } + public static bool ChangeLock(House house) + { + if (house.Locked) house.Locked = false; else house.Locked = true; + try + { + string query = "UPDATE HousingDistrict SET Locked=@0 WHERE Name=@1"; + TShock.DB.Query(query, house.Locked ? 1 : 0, house.Name); + } + catch (Exception ex) { TShock.Log.Error("房屋插件错误修改锁房屋时出错:" + ex.ToString()); return false; } + return true; + } +} diff --git a/Plugin.sln b/Plugin.sln index 1e1475d7..53440c4e 100644 --- a/Plugin.sln +++ b/Plugin.sln @@ -148,6 +148,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SourceGen", "Yaaiomni\Sourc EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CaiBot", "CaiBot\CaiBot.csproj", "{78EA1806-A757-47F3-A1C5-DF2FC3B8288B}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HouseRegion", "HouseRegion\HouseRegion.csproj", "{4FEDE121-FE96-4B51-B598-0CDD3C5AA078}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -716,6 +718,14 @@ Global {78EA1806-A757-47F3-A1C5-DF2FC3B8288B}.Release|Any CPU.Build.0 = Release|Any CPU {78EA1806-A757-47F3-A1C5-DF2FC3B8288B}.Release|x64.ActiveCfg = Release|Any CPU {78EA1806-A757-47F3-A1C5-DF2FC3B8288B}.Release|x64.Build.0 = Release|Any CPU + {4FEDE121-FE96-4B51-B598-0CDD3C5AA078}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4FEDE121-FE96-4B51-B598-0CDD3C5AA078}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4FEDE121-FE96-4B51-B598-0CDD3C5AA078}.Debug|x64.ActiveCfg = Debug|Any CPU + {4FEDE121-FE96-4B51-B598-0CDD3C5AA078}.Debug|x64.Build.0 = Debug|Any CPU + {4FEDE121-FE96-4B51-B598-0CDD3C5AA078}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4FEDE121-FE96-4B51-B598-0CDD3C5AA078}.Release|Any CPU.Build.0 = Release|Any CPU + {4FEDE121-FE96-4B51-B598-0CDD3C5AA078}.Release|x64.ActiveCfg = Release|Any CPU + {4FEDE121-FE96-4B51-B598-0CDD3C5AA078}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/README.md b/README.md index 4d9bc7c3..45c61405 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,7 @@ | [WikiLangPackLoader](WikiLangPackLoader/README.md) | 为服务器加载 Wiki 语言包 | 无 | | [HelpPlus](HelpPlus/README.md) | 修复和增强 Help 命令 | 无 | | [CaiBot](CaiBot/README.md) | CaiBot 适配插件 | 自带前置 | +| [HouseRegion](HouseRegion/README.md) | 圈地插件 | | ## 代码贡献