diff --git a/.gitignore b/.gitignore index 3fee42f6..ea69dfeb 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,6 @@ ehthumbs.db Icon? Thumbs.db - # Visual Studio shit Motherfucka # ################################## *.suo @@ -51,5 +50,10 @@ Thumbs.db #Template Bat file# ################### -myass.bat -/TestResults \ No newline at end of file +postbuild.bat +/TestResults + +# JetBrains Crap # +################## +*.dotCover +_ReSharper.* \ No newline at end of file diff --git a/DBEditor/CommandList.cs b/DBEditor/CommandList.cs index 4b47951a..e1c24f64 100644 --- a/DBEditor/CommandList.cs +++ b/DBEditor/CommandList.cs @@ -43,6 +43,13 @@ namespace TShockDBEditor CommandList.Add("immunetokick"); CommandList.Add("immunetoban"); CommandList.Add("usebanneditem"); + + CommandList.Add("canregister"); + CommandList.Add("canlogin"); + CommandList.Add("canchangepassword"); + CommandList.Add("canpartychat"); + CommandList.Add("cantalkinthird"); + CommandList.Add("candisplayplaying"); foreach (string command in CommandList) { diff --git a/TShockAPI/BackupManager.cs b/TShockAPI/BackupManager.cs index 857570ef..50f8e2cc 100644 --- a/TShockAPI/BackupManager.cs +++ b/TShockAPI/BackupManager.cs @@ -66,6 +66,7 @@ namespace TShockAPI TShock.Utils.Broadcast("Server map saving, potential lag spike"); Console.WriteLine("Backing up world..."); + Thread SaveWorld = new Thread(TShock.Utils.SaveWorld); SaveWorld.Start(); diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs index 5703cdc1..81b7804c 100644 --- a/TShockAPI/Commands.cs +++ b/TShockAPI/Commands.cs @@ -120,8 +120,8 @@ namespace TShockAPI add(Permissions.ban, UnBanIP, "unbanip"); add(Permissions.maintenance, ClearBans, "clearbans"); add(Permissions.whitelist, Whitelist, "whitelist"); - add(Permissions.maintenance, Off, "off"); - add(Permissions.maintenance, OffNoSave, "off-nosave"); + add(Permissions.maintenance, Off, "off", "exit"); + add(Permissions.maintenance, OffNoSave, "off-nosave", "exit-nosave"); add(Permissions.maintenance, CheckUpdates, "checkupdates"); add(Permissions.causeevents, DropMeteor, "dropmeteor"); add(Permissions.causeevents, Star, "star"); @@ -167,19 +167,20 @@ namespace TShockAPI add(Permissions.manageregion, Region, "region"); add(Permissions.manageregion, DebugRegions, "debugreg"); add(null, Help, "help"); - add(null, Playing, "playing", "online", "who"); + add(null, Playing, "playing", "online", "who", "version"); add(null, AuthToken, "auth"); - add(null, ThirdPerson, "me"); - add(null, PartyChat, "p"); + add(Permissions.cantalkinthird, ThirdPerson, "me"); + add(Permissions.canpartychat, PartyChat, "p"); add(null, Motd, "motd"); add(null, Rules, "rules"); + add(Permissions.mute, Mute, "mute", "unmute"); add(Permissions.logs, DisplayLogs, "displaylogs"); - ChatCommands.Add(new Command(PasswordUser, "password") { DoLog = false }); - ChatCommands.Add(new Command(RegisterUser, "register") { DoLog = false }); + ChatCommands.Add(new Command(Permissions.canchangepassword, PasswordUser, "password") { DoLog = false }); + ChatCommands.Add(new Command(Permissions.canregister, RegisterUser, "register") { DoLog = false }); ChatCommands.Add(new Command(Permissions.rootonly, ManageUsers, "user") { DoLog = false }); add(Permissions.rootonly, GrabUserUserInfo, "userinfo", "ui"); add(Permissions.rootonly, AuthVerify, "auth-verify"); - ChatCommands.Add(new Command(AttemptLogin, "login") { DoLog = false }); + ChatCommands.Add(new Command(Permissions.canlogin, AttemptLogin, "login") { DoLog = false }); add(Permissions.cfg, Broadcast, "broadcast", "bc"); add(Permissions.whisper, Whisper, "whisper", "w", "tell"); add(Permissions.whisper, Reply, "reply", "r"); @@ -215,7 +216,10 @@ namespace TShockAPI Command cmd = ChatCommands.FirstOrDefault(c => c.HasAlias(cmdName)); if (cmd == null) - return false; + { + player.SendMessage("Invalid Command Entered. Type /help for a list of valid Commands.", Color.Red); + return true; + } if (!cmd.CanRun(player)) { @@ -344,12 +348,21 @@ namespace TShockAPI if (TShock.Config.ServerSideInventory) { - if (!TShock.CheckInventory(args.Player)) + if (args.Player.Group.HasPermission(Permissions.bypassinventorychecks)) { - args.Player.SendMessage("Login Failed, Please fix the above errors then log back in.", Color.Cyan); + args.Player.IgnoreActionsForInventory = false; + args.Player.IgnoreActionsForClearingTrashCan = false; + } + else if (!TShock.CheckInventory(args.Player)) + { + args.Player.SendMessage("Login Failed, Please fix the above errors then /login again.", Color.Cyan); + args.Player.IgnoreActionsForClearingTrashCan = true; return; } } + + if (args.Player.Group.HasPermission(Permissions.ignorestackhackdetection)) + args.Player.IgnoreActionsForCheating = "none"; args.Player.Group = TShock.Utils.GetGroup(user.Group); args.Player.UserAccountName = args.Parameters[0]; @@ -358,7 +371,7 @@ namespace TShockAPI args.Player.IgnoreActionsForInventory = false; args.Player.PlayerData.CopyInventory(args.Player); - TShock.InventoryDB.InsertPlayerData(args.Player, args.Player.UserID); + TShock.InventoryDB.InsertPlayerData(args.Player); args.Player.SendMessage("Authenticated as " + args.Parameters[0] + " successfully.", Color.LimeGreen); Log.ConsoleInfo(args.Player.Name + " authenticated successfully as user: " + args.Parameters[0]); @@ -414,29 +427,37 @@ namespace TShockAPI { try { - if (args.Parameters.Count == 2) + var user = new User(); + + if (args.Parameters.Count == 1) + { + user.Name = args.Player.Name; + user.Password = args.Parameters[0]; + } + else if (args.Parameters.Count == 2 && TShock.Config.AllowRegisterAnyUsername) { - var user = new User(); user.Name = args.Parameters[0]; user.Password = args.Parameters[1]; - user.Group = TShock.Config.DefaultRegistrationGroupName; // FIXME -- we should get this from the DB. - - if (TShock.Users.GetUserByName(user.Name) == null) // Cheap way of checking for existance of a user - { - args.Player.SendMessage("Account " + user.Name + " has been registered.", Color.Green); - TShock.Users.AddUser(user); - Log.ConsoleInfo(args.Player.Name + " registered an Account: " + user.Name); - } - else - { - args.Player.SendMessage("Account " + user.Name + " has already been registered.", Color.Green); - Log.ConsoleInfo(args.Player.Name + " failed to register an existing Account: " + user.Name); - } - } else { - args.Player.SendMessage("Invalid syntax! Proper syntax: /register ", Color.Red); + args.Player.SendMessage("Invalid syntax! Proper syntax: /register ", Color.Red); + return; + } + + user.Group = TShock.Config.DefaultRegistrationGroupName; // FIXME -- we should get this from the DB. + + if (TShock.Users.GetUserByName(user.Name) == null) // Cheap way of checking for existance of a user + { + args.Player.SendMessage("Account " + user.Name + " has been registered.", Color.Green); + args.Player.SendMessage("Your password is " + user.Password); + TShock.Users.AddUser(user); + Log.ConsoleInfo(args.Player.Name + " registered an Account: " + user.Name); + } + else + { + args.Player.SendMessage("Account " + user.Name + " has already been registered.", Color.Green); + Log.ConsoleInfo(args.Player.Name + " failed to register an existing Account: " + user.Name); } } catch (UserManagerException ex) @@ -1249,12 +1270,20 @@ namespace TShockAPI { switch (Main.tile[x, y].type) { + case 22: case 25: Main.tile[x, y].type = 117; break; case 23: Main.tile[x, y].type = 109; break; + case 32: + Main.tile[x, y].type = 0; + Main.tile[x, y].active = false; + break; + case 24: + Main.tile[x, y].type = 110; + break; case 112: Main.tile[x, y].type = 116; break; @@ -1762,7 +1791,7 @@ namespace TShockAPI return; } string passwd = args.Parameters[0]; - Netplay.password = passwd; + TShock.Config.ServerPassword = passwd; args.Player.SendMessage(string.Format("Server password changed to: {0}", passwd)); } @@ -1971,7 +2000,7 @@ namespace TShockAPI var width = Math.Abs(args.Player.TempPoints[0].X - args.Player.TempPoints[1].X); var height = Math.Abs(args.Player.TempPoints[0].Y - args.Player.TempPoints[1].Y); - if (TShock.Regions.AddRegion(x, y, width, height, regionName, Main.worldID.ToString())) + if (TShock.Regions.AddRegion(x, y, width, height, regionName, args.Player.UserAccountName, Main.worldID.ToString())) { args.Player.TempPoints[0] = Point.Zero; args.Player.TempPoints[1] = Point.Zero; @@ -2107,8 +2136,78 @@ namespace TShockAPI } } else - args.Player.SendMessage("Invalid syntax! Proper syntax: /region allow [name] [region]", Color.Red); + args.Player.SendMessage("Invalid syntax! Proper syntax: /region remove [name] [region]", Color.Red); break; + case "allowg": + { + if (args.Parameters.Count > 2) + { + string group = args.Parameters[1]; + string regionName = ""; + + for (int i = 2; i < args.Parameters.Count; i++) + { + if (regionName == "") + { + regionName = args.Parameters[2]; + } + else + { + regionName = regionName + " " + args.Parameters[i]; + } + } + if (TShock.Groups.GroupExists(group)) + { + if (TShock.Regions.AllowGroup(regionName, group)) + { + args.Player.SendMessage("Added group " + group + " to " + regionName, Color.Yellow); + } + else + args.Player.SendMessage("Region " + regionName + " not found", Color.Red); + } + else + { + args.Player.SendMessage("Group " + group + " not found", Color.Red); + } + } + else + args.Player.SendMessage("Invalid syntax! Proper syntax: /region allow [group] [region]", Color.Red); + break; + } + case "removeg": + if (args.Parameters.Count > 2) + { + string group = args.Parameters[1]; + string regionName = ""; + + for (int i = 2; i < args.Parameters.Count; i++) + { + if (regionName == "") + { + regionName = args.Parameters[2]; + } + else + { + regionName = regionName + " " + args.Parameters[i]; + } + } + if (TShock.Groups.GroupExists(group)) + { + if (TShock.Regions.RemoveGroup(regionName, group)) + { + args.Player.SendMessage("Removed group " + group + " from " + regionName, Color.Yellow); + } + else + args.Player.SendMessage("Region " + regionName + " not found", Color.Red); + } + else + { + args.Player.SendMessage("Group " + group + " not found", Color.Red); + } + } + else + args.Player.SendMessage("Invalid syntax! Proper syntax: /region removeg [group] [region]", Color.Red); + break; case "list": { //How many regions per page @@ -2314,6 +2413,7 @@ namespace TShockAPI private static void Playing(CommandArgs args) { args.Player.SendMessage(string.Format("Current players: {0}.", TShock.Utils.GetPlayers()), 255, 240, 20); + args.Player.SendMessage(string.Format("TShock: {0} ({1}): ({2}/{3})", TShock.VersionNum, TShock.VersionCodename, TShock.Utils.ActivePlayers(), TShock.Config.MaxSlots)); } private static void AuthToken(CommandArgs args) @@ -2393,7 +2493,10 @@ namespace TShockAPI args.Player.SendMessage("Invalid syntax! Proper syntax: /me ", Color.Red); return; } - TShock.Utils.Broadcast(string.Format("*{0} {1}", args.Player.Name, String.Join(" ", args.Parameters)), 205, 133, 63); + if (args.Player.mute) + args.Player.SendMessage("You are muted."); + else + TShock.Utils.Broadcast(string.Format("*{0} {1}", args.Player.Name, String.Join(" ", args.Parameters)), 205, 133, 63); } private static void PartyChat(CommandArgs args) @@ -2404,7 +2507,10 @@ namespace TShockAPI return; } int playerTeam = args.Player.Team; - if (playerTeam != 0) + + if (args.Player.mute) + args.Player.SendMessage("You are muted."); + else if (playerTeam != 0) { string msg = string.Format("<{0}> {1}", args.Player.Name, String.Join(" ", args.Parameters)); foreach (TSPlayer player in TShock.Players) @@ -2414,11 +2520,42 @@ namespace TShockAPI } } else - { args.Player.SendMessage("You are not in a party!", 255, 240, 20); - } } - + + private static void Mute(CommandArgs args) + { + if (args.Parameters.Count < 1) + { + args.Player.SendMessage("Invalid syntax! Proper syntax: /mute ", Color.Red); + return; + } + + string plStr = String.Join(" ", args.Parameters); + var players = TShock.Utils.FindPlayer(plStr); + if (players.Count == 0) + args.Player.SendMessage("Invalid player!", Color.Red); + else if (players.Count > 1) + args.Player.SendMessage("More than one player matched!", Color.Red); + else if (players[0].mute && !players[0].Group.HasPermission(Permissions.mute)) + { + var plr = players[0]; + plr.mute = false; + plr.SendMessage("You have been unmuted."); + TShock.Utils.Broadcast(plr.Name + " has been unmuted by " + args.Player.Name, Color.Yellow); + } + else if (!players[0].Group.HasPermission(Permissions.mute)) + { + var plr = players[0]; + plr.mute = true; + plr.SendMessage("You have been muted."); + TShock.Utils.Broadcast(plr.Name + " has been muted by " + args.Player.Name, Color.Yellow); + } + else + args.Player.SendMessage("You cannot mute this player."); + + } + private static void Motd(CommandArgs args) { TShock.Utils.ShowFileToUser(args.Player, "motd.txt"); @@ -2446,6 +2583,8 @@ namespace TShockAPI { args.Player.SendMessage("More than one player matched!", Color.Red); } + else if (args.Player.mute) + args.Player.SendMessage("You are muted."); else { var plr = players[0]; @@ -2459,7 +2598,9 @@ namespace TShockAPI private static void Reply(CommandArgs args) { - if (args.Player.LastWhisper != null) + if (args.Player.mute) + args.Player.SendMessage("You are muted."); + else if (args.Player.LastWhisper != null) { var msg = string.Join(" ", args.Parameters); args.Player.LastWhisper.SendMessage("(Whisper From)" + "<" + args.Player.Name + ">" + msg, Color.MediumPurple); diff --git a/TShockAPI/ConfigFile.cs b/TShockAPI/ConfigFile.cs index 4ec36764..9ccc2f4c 100644 --- a/TShockAPI/ConfigFile.cs +++ b/TShockAPI/ConfigFile.cs @@ -16,7 +16,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ using System; -using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; @@ -39,8 +38,8 @@ namespace TShockAPI public bool EnableWhitelist; [Description("Enable the ability for invaison size to never decrease. Make sure to run /invade, and note that this adds 2 million+ goblins to the spawn que for the map.")] public bool InfiniteInvasion; - [Description("Enable or disable perma pvp.")] - public bool AlwaysPvP = false; + [Description("Set the server pvp mode. Vaild types are, \"normal\", \"always\", \"disabled\"")] + public string PvPMode = "normal"; [Description("Prevents tiles from being placed within SpawnProtectionRadius of the default spawn.")] public bool SpawnProtection = true; [Description("Radius from spawn tile for SpawnProtection.")] @@ -51,14 +50,12 @@ namespace TShockAPI public bool RangeChecks = true; [Description("Disables any building; placing of blocks")] public bool DisableBuild; - [Description("Kick a player if they exceed this number of tile kills within 1 second.")] - public int TileThreshold = 120; [Description("#.#.#. = Red/Blue/Green - RGB Colors for the Admin Chat Color. Max value: 255")] public float[] SuperAdminChatRGB = { 255, 0, 0 }; - [Description("The Chat Prefix before an admin speaks. eg. *The prefix was set to \"(Admin) \", so.. (Admin) : Hi! Note: If you put a space after the prefix, it will look like this: (Admin) ("GroupName"); var group = new Group(groupname); + group.Prefix = reader.Get("Prefix"); + group.Suffix= reader.Get("Suffix"); + //Inherit Given commands String[] commands = reader.Get("Commands").Split(','); foreach (var t in commands) diff --git a/TShockAPI/DB/IQueryBuilder.cs b/TShockAPI/DB/IQueryBuilder.cs index e88d7c9c..96e71b8c 100644 --- a/TShockAPI/DB/IQueryBuilder.cs +++ b/TShockAPI/DB/IQueryBuilder.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Data; using System.Linq; using System.Text; using MySql.Data.MySqlClient; diff --git a/TShockAPI/DB/InventoryManager.cs b/TShockAPI/DB/InventoryManager.cs index 0ea940f2..97feb6b4 100644 --- a/TShockAPI/DB/InventoryManager.cs +++ b/TShockAPI/DB/InventoryManager.cs @@ -18,9 +18,7 @@ along with this program. If not, see . using System; using System.Data; - using MySql.Data.MySqlClient; -using Terraria; namespace TShockAPI.DB { @@ -54,7 +52,6 @@ namespace TShockAPI.DB { playerData.exists = true; playerData.maxHealth = reader.Get("MaxHealth"); - playerData.maxMana = reader.Get("MaxMana"); playerData.inventory = NetItem.Parse(reader.Get("Inventory")); return playerData; } @@ -68,15 +65,18 @@ namespace TShockAPI.DB return playerData; } - public bool InsertPlayerData(TSPlayer player, int acctid) + public bool InsertPlayerData(TSPlayer player) { PlayerData playerData = player.PlayerData; - if (!GetPlayerData(player, acctid).exists) + if (!player.IsLoggedIn) + return false; + + if (!GetPlayerData(player, player.UserID).exists) { try { - database.Query("INSERT INTO Inventory (Account, MaxHealth, MaxMana, Inventory) VALUES (@0, @1, @2, @3);", acctid, playerData.maxHealth, playerData.maxMana, NetItem.ToString(playerData.inventory)); + database.Query("INSERT INTO Inventory (Account, MaxHealth, Inventory) VALUES (@0, @1, @2);", player.UserID, playerData.maxHealth, NetItem.ToString(playerData.inventory)); return true; } catch (Exception ex) @@ -88,7 +88,7 @@ namespace TShockAPI.DB { try { - database.Query("UPDATE Inventory SET MaxHealth = @0, MaxMana = @1, Inventory = @2 WHERE Account = @3;", playerData.maxHealth, playerData.maxMana, NetItem.ToString(playerData.inventory), acctid); + database.Query("UPDATE Inventory SET MaxHealth = @0, Inventory = @1 WHERE Account = @2;", playerData.maxHealth, NetItem.ToString(playerData.inventory), player.UserID); return true; } catch (Exception ex) diff --git a/TShockAPI/DB/ItemManager.cs b/TShockAPI/DB/ItemManager.cs index d0beb4ab..960e8b0c 100644 --- a/TShockAPI/DB/ItemManager.cs +++ b/TShockAPI/DB/ItemManager.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Data; using System.IO; +using System.Linq; using MySql.Data.MySqlClient; namespace TShockAPI.DB @@ -9,14 +10,15 @@ namespace TShockAPI.DB public class ItemManager { private IDbConnection database; - public List ItemBans = new List(); + public List ItemBans = new List(); public ItemManager(IDbConnection db) { database = db; var table = new SqlTable("ItemBans", - new SqlColumn("ItemName", MySqlDbType.VarChar, 50) { Primary = true } + new SqlColumn("ItemName", MySqlDbType.VarChar, 50) { Primary = true }, + new SqlColumn("AllowedGroups", MySqlDbType.Text ) ); var creator = new SqlTableCreator(db, db.GetSqlType() == SqlType.Sqlite ? (IQueryBuilder)new SqliteQueryCreator() : new MysqlQueryCreator()); creator.EnsureExists(table); @@ -33,13 +35,13 @@ namespace TShockAPI.DB { string query = (TShock.Config.StorageType.ToLower() == "sqlite") ? - "INSERT OR IGNORE INTO 'ItemBans' (ItemName) VALUES (@0);" : - "INSERT IGNORE INTO ItemBans SET ItemName=@0;"; + "INSERT OR IGNORE INTO 'ItemBans' (ItemName, AllowedGroups) VALUES (@0, @1);" : + "INSERT IGNORE INTO ItemBans SET ItemName=@0,AllowedGroups=@1 ;"; int id = 0; int.TryParse(line, out id); - database.Query(query, TShock.Utils.GetItemById(id).name); + database.Query(query, TShock.Utils.GetItemById(id).name, ""); } } } @@ -62,17 +64,22 @@ namespace TShockAPI.DB using (var reader = database.QueryReader("SELECT * FROM ItemBans")) { - while (reader != null && reader.Read()) - ItemBans.Add(reader.Get("ItemName")); + while (reader != null && reader.Read()) + { + ItemBan ban = new ItemBan(reader.Get("ItemName")); + ban.SetAllowedGroups( reader.Get("AllowedGroups") ); + ItemBans.Add(ban); + } } } + public void AddNewBan(string itemname = "") { try { - database.Query("INSERT INTO ItemBans (ItemName) VALUES (@0);", TShock.Utils.GetItemByName(itemname)[0].name); - if (!ItemIsBanned(itemname)) - ItemBans.Add(itemname); + database.Query("INSERT INTO ItemBans (ItemName, AllowedGroups) VALUES (@0, @1);", TShock.Utils.GetItemByName(itemname)[0].name, ""); + if (!ItemIsBanned(itemname, null)) + ItemBans.Add( new ItemBan(itemname) ); } catch (Exception ex) { @@ -82,12 +89,12 @@ namespace TShockAPI.DB public void RemoveBan(string itemname) { - if (!ItemIsBanned(itemname)) + if (!ItemIsBanned(itemname, null)) return; try { database.Query("Delete FROM 'ItemBans' WHERE ItemName=@0;", TShock.Utils.GetItemByName(itemname)[0].name); - ItemBans.Remove(itemname); + ItemBans.Remove( new ItemBan(itemname) ); } catch (Exception ex) { @@ -97,10 +104,123 @@ namespace TShockAPI.DB public bool ItemIsBanned(string name) { - if (ItemBans.Contains(name)) + if (ItemBans.Contains(new ItemBan(name))) + { return true; - + } return false; } + + public bool ItemIsBanned(string name, TSPlayer ply) + { + if (ItemBans.Contains( new ItemBan(name) ) ) + { + ItemBan b = GetItemBanByName(name); + return !b.HasPermissionToUseItem(ply); + } + return false; + } + + public bool AllowGroup(string item, string name) + { + string groupsNew = ""; + ItemBan b = GetItemBanByName(item); + if (b != null) + { + groupsNew = String.Join(",", b.AllowedGroups); + if (groupsNew.Length > 0) + groupsNew += ","; + groupsNew += name; + b.SetAllowedGroups(groupsNew); + + int q = database.Query("UPDATE ItemBans SET AllowedGroups=@0 WHERE ItemName=@1", groupsNew, + item); + + return q > 0; + } + + return false; + } + + public bool RemoveGroup(string item, string group) + { + ItemBan b = GetItemBanByName(item); + if (b != null) + { + b.RemoveGroup(group); + string groups = string.Join(",", b.AllowedGroups); + int q = database.Query("UPDATE ItemBans SET AllowedGroups=@0 WHERE ItemName=@1", groups, + item); + if (q > 0) + return true; + } + return false; + } + + public ItemBan GetItemBanByName(String name) + { + foreach (ItemBan b in ItemBans) + { + if (b.Name == name) + { + return b; + } + } + return null; + } } + + public class ItemBan : IEquatable + { + public string Name { get; set; } + public List AllowedGroups { get; set; } + + public ItemBan(string name) + : this() + { + Name = name; + AllowedGroups = new List(); + } + + public ItemBan() + { + Name = ""; + AllowedGroups = new List(); + } + + public bool Equals(ItemBan other) + { + return Name == other.Name; + } + + public bool HasPermissionToUseItem(TSPlayer ply) + { + if (ply == null) + return false; + Console.WriteLine(ply.Group.Name); + return AllowedGroups.Contains(ply.Group.Name); // could add in the other permissions in this class instead of a giant if switch. + } + + public void SetAllowedGroups( String groups ) + { + // prevent null pointer exceptions + if (!string.IsNullOrEmpty(groups)) + { + List groupArr = groups.Split(',').ToList(); + + for (int i = 0; i < groupArr.Count; i++) + { + groupArr[i] = groupArr[i].Trim(); + Console.WriteLine(groupArr[i]); + } + AllowedGroups = groupArr; + } + } + + public bool RemoveGroup(string groupName) + { + return AllowedGroups.Remove(groupName); + } + } + } diff --git a/TShockAPI/DB/RegionManager.cs b/TShockAPI/DB/RegionManager.cs index 5c78241b..64155671 100644 --- a/TShockAPI/DB/RegionManager.cs +++ b/TShockAPI/DB/RegionManager.cs @@ -38,7 +38,6 @@ namespace TShockAPI.DB public RegionManager(IDbConnection db) { database = db; - var table = new SqlTable("Regions", new SqlColumn("X1", MySqlDbType.Int32), new SqlColumn("Y1", MySqlDbType.Int32), @@ -47,7 +46,10 @@ namespace TShockAPI.DB new SqlColumn("RegionName", MySqlDbType.VarChar, 50) { Primary = true }, new SqlColumn("WorldID", MySqlDbType.Text), new SqlColumn("UserIds", MySqlDbType.Text), - new SqlColumn("Protected", MySqlDbType.Int32) + new SqlColumn("Protected", MySqlDbType.Int32), + new SqlColumn("Groups", MySqlDbType.Text), + new SqlColumn("Owner", MySqlDbType.VarChar, 50) + ); var creator = new SqlTableCreator(db, db.GetSqlType() == SqlType.Sqlite ? (IQueryBuilder)new SqliteQueryCreator() : new MysqlQueryCreator()); creator.EnsureExists(table); @@ -180,11 +182,13 @@ namespace TShockAPI.DB int Protected = reader.Get("Protected"); string mergedids = reader.Get("UserIds"); string name = reader.Get("RegionName"); + string owner = reader.Get("Owner"); + string groups = reader.Get("Groups"); string[] splitids = mergedids.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - Region r = new Region(new Rectangle(X1, Y1, width, height), name, Protected != 0, Main.worldID.ToString()); - + Region r = new Region(new Rectangle(X1, Y1, width, height), name, owner, Protected != 0, Main.worldID.ToString()); + r.SetAllowedGroups( groups ); try { for (int i = 0; i < splitids.Length; i++) @@ -231,8 +235,11 @@ namespace TShockAPI.DB string MergedIDs = reader.Get("UserIds"); string name = reader.Get("RegionName"); string[] SplitIDs = MergedIDs.Split(','); + string owner = reader.Get("Owner"); + string groups = reader.Get("Groups"); - Region r = new Region(new Rectangle(X1, Y1, width, height), name, Protected != 0, Main.worldID.ToString()); + Region r = new Region(new Rectangle(X1, Y1, width, height), name, owner, Protected != 0, Main.worldID.ToString()); + r.SetAllowedGroups( groups ); try { for (int i = 0; i < SplitIDs.Length; i++) @@ -260,7 +267,7 @@ namespace TShockAPI.DB } - public bool AddRegion(int tx, int ty, int width, int height, string regionname, string worldid) + public bool AddRegion(int tx, int ty, int width, int height, string regionname, string owner, string worldid) { if (GetRegionByName(regionname) != null) { @@ -268,9 +275,9 @@ namespace TShockAPI.DB } try { - database.Query("INSERT INTO Regions (X1, Y1, width, height, RegionName, WorldID, UserIds, Protected) VALUES (@0, @1, @2, @3, @4, @5, @6, @7);", - tx, ty, width, height, regionname, worldid, "", 1); - Regions.Add(new Region(new Rectangle(tx, ty, width, height), regionname, true, worldid)); + database.Query("INSERT INTO Regions (X1, Y1, width, height, RegionName, WorldID, UserIds, Protected, Groups, Owner) VALUES (@0, @1, @2, @3, @4, @5, @6, @7, @8, @9);", + tx, ty, width, height, regionname, worldid, "", 1, "", owner); + Regions.Add(new Region(new Rectangle(tx, ty, width, height), regionname, owner, true, worldid)); return true; } catch (Exception ex) @@ -524,23 +531,84 @@ namespace TShockAPI.DB } return null; } + + public bool ChangeOwner( string regionName, string newOwner ) + { + var region = GetRegionByName(regionName); + if( region != null ) + { + region.Owner = newOwner; + int q = database.Query("UPDATE Regions SET Owner=@0 WHERE RegionName=@1 AND WorldID=@2", newOwner, + regionName, Main.worldID.ToString()); + if (q > 0) + return true; + } + return false; + } + + public bool AllowGroup( string regionName, string groups) + { + string groupsNew = ""; + using (var reader = database.QueryReader("SELECT * FROM Regions WHERE RegionName=@0 AND WorldID=@1", regionName, Main.worldID.ToString())) + { + if (reader.Read()) + groupsNew = reader.Get("Groups"); + } + if (groupsNew != "") + groupsNew += ","; + groupsNew += groups; + + int q = database.Query("UPDATE Regions SET Groups=@0 WHERE RegionName=@1 AND WorldID=@2", groupsNew, + regionName, Main.worldID.ToString()); + + Region r = GetRegionByName(regionName); + if( r != null ) + { + r.SetAllowedGroups( groupsNew ); + } + else + { + return false; + } + + return q > 0; + } + + public bool RemoveGroup( string regionName, string group ) + { + Region r = GetRegionByName(regionName); + if (r != null) + { + r.RemoveGroup(group); + string groups = string.Join(",", r.AllowedGroups); + int q = database.Query("UPDATE Regions SET Groups=@0 WHERE RegionName=@1 AND WorldID=@2", groups, + regionName, Main.worldID.ToString()); + if (q > 0) + return true; + } + return false; + } } public class Region { public Rectangle Area { get; set; } public string Name { get; set; } + public string Owner { get; set; } public bool DisableBuild { get; set; } public string WorldID { get; set; } public List AllowedIDs { get; set; } + public List AllowedGroups { get; set; } - public Region(Rectangle region, string name, bool disablebuild, string RegionWorldIDz) + public Region(Rectangle region, string name, string owner, bool disablebuild, string RegionWorldIDz) : this() { Area = region; Name = name; - DisableBuild = disablebuild; + Owner = owner; + DisableBuild = disablebuild; WorldID = RegionWorldIDz; + } public Region() @@ -550,6 +618,7 @@ namespace TShockAPI.DB DisableBuild = true; WorldID = string.Empty; AllowedIDs = new List(); + AllowedGroups = new List(); } public bool InArea(Rectangle point) @@ -577,14 +646,7 @@ namespace TShockAPI.DB return true; } - for (int i = 0; i < AllowedIDs.Count; i++) - { - if (AllowedIDs[i] == ply.UserID) - { - return true; - } - } - return false; + return AllowedIDs.Contains(ply.UserID) || AllowedGroups.Contains(ply.Group.Name) || Owner == ply.UserAccountName || ply.Group.HasPermission("manageregion"); } public void setAllowedIDs(String ids) @@ -601,6 +663,20 @@ namespace TShockAPI.DB AllowedIDs = id_list; } + public void SetAllowedGroups( String groups ) + { + // prevent null pointer exceptions + if (!string.IsNullOrEmpty(groups)) + { + List groupArr = groups.Split(',').ToList(); + + for (int i = 0; i < groupArr.Count; i++) + groupArr[i] = groupArr[i].Trim(); + + AllowedGroups = groupArr; + } + } + public void RemoveID(int id) { var index = -1; @@ -614,5 +690,10 @@ namespace TShockAPI.DB } AllowedIDs.RemoveAt(index); } + + public bool RemoveGroup(string groupName) + { + return AllowedGroups.Remove(groupName); + } } } diff --git a/TShockAPI/DB/SqlTable.cs b/TShockAPI/DB/SqlTable.cs index 21d289e1..b2610538 100644 --- a/TShockAPI/DB/SqlTable.cs +++ b/TShockAPI/DB/SqlTable.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Data; using System.Linq; -using System.Text; using MySql.Data.MySqlClient; namespace TShockAPI.DB diff --git a/TShockAPI/DB/SqlValue.cs b/TShockAPI/DB/SqlValue.cs index f74d3149..a5520236 100644 --- a/TShockAPI/DB/SqlValue.cs +++ b/TShockAPI/DB/SqlValue.cs @@ -1,5 +1,4 @@ using System.Data; -using MySql.Data.MySqlClient; using System.Collections.Generic; namespace TShockAPI.DB diff --git a/TShockAPI/DB/UserManager.cs b/TShockAPI/DB/UserManager.cs index 91b1dd93..3c450674 100644 --- a/TShockAPI/DB/UserManager.cs +++ b/TShockAPI/DB/UserManager.cs @@ -218,7 +218,7 @@ namespace TShockAPI.DB { Log.ConsoleError("GetGroupForIP SQL returned an error: " + ex); } - return TShock.Utils.GetGroup("default"); + return TShock.Utils.GetGroup(TShock.Config.DefaultGuestGroupName); } public Group GetGroupForIPExpensive(string ip) @@ -240,7 +240,7 @@ namespace TShockAPI.DB { Log.ConsoleError("GetGroupForIP SQL returned an error: " + ex); } - return TShock.Utils.GetGroup("default"); + return TShock.Utils.GetGroup(TShock.Config.DefaultGuestGroupName); } diff --git a/TShockAPI/Extensions/LinqExt.cs b/TShockAPI/Extensions/LinqExt.cs index b8a891ed..bd8384fc 100644 --- a/TShockAPI/Extensions/LinqExt.cs +++ b/TShockAPI/Extensions/LinqExt.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; namespace TShockAPI { diff --git a/TShockAPI/Extensions/RandomExt.cs b/TShockAPI/Extensions/RandomExt.cs index e49b50f0..517e445b 100644 --- a/TShockAPI/Extensions/RandomExt.cs +++ b/TShockAPI/Extensions/RandomExt.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Text; namespace TShockAPI.Extensions diff --git a/TShockAPI/Extensions/StringExt.cs b/TShockAPI/Extensions/StringExt.cs index b74c3170..569cd1f8 100644 --- a/TShockAPI/Extensions/StringExt.cs +++ b/TShockAPI/Extensions/StringExt.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace TShockAPI { diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index a7423bb5..cf9b4e17 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -17,7 +17,6 @@ along with this program. If not, see . */ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Text; @@ -25,7 +24,6 @@ using Terraria; using TShockAPI.Net; using System.IO.Streams; -using System.Net; namespace TShockAPI { @@ -90,6 +88,9 @@ namespace TShockAPI {PacketTypes.NpcStrike, HandleNpcStrike}, {PacketTypes.NpcSpecial, HandleSpecial}, {PacketTypes.PlayerAnimation, HandlePlayerAnimation}, + {PacketTypes.PlayerBuff, HandlePlayerBuffUpdate}, + {PacketTypes.PasswordSend, HandlePassword}, + {PacketTypes.ContinueConnecting2, HandleConnecting} }; } @@ -137,9 +138,9 @@ namespace TShockAPI item.netDefaults(type); item.Prefix(prefix); - if (stack > item.maxStack && type != 0) + if (stack > item.maxStack && type != 0 && args.Player.IgnoreActionsForCheating != "none" && !args.Player.Group.HasPermission(Permissions.ignorestackhackdetection)) { - args.Player.IgnoreActionsForCheating = true; + args.Player.IgnoreActionsForCheating = "Item Hack: " + item.name + " (" + stack + ") exceeds max stack of " + item.maxStack; } if (args.Player.IsLoggedIn) @@ -156,9 +157,13 @@ namespace TShockAPI int cur = args.Data.ReadInt16(); int max = args.Data.ReadInt16(); - if (cur > 600 || max > 600) + if (args.Player.FirstMaxHP == 0) + args.Player.FirstMaxHP = max; + + if (max > 400 && max > args.Player.FirstMaxHP) { - args.Player.IgnoreActionsForCheating = true; + TShock.Utils.ForceKick(args.Player, "Hacked Client Detected."); + return false; } if (args.Player.IsLoggedIn) @@ -175,14 +180,13 @@ namespace TShockAPI int cur = args.Data.ReadInt16(); int max = args.Data.ReadInt16(); - if (cur > 600 || max > 600) - { - args.Player.IgnoreActionsForCheating = true; - } + if (args.Player.FirstMaxMP == 0) + args.Player.FirstMaxMP = max; - if (args.Player.IsLoggedIn) + if (max > 400 && max > args.Player.FirstMaxMP) { - args.Player.PlayerData.maxMana = max; + TShock.Utils.ForceKick(args.Player, "Hacked Client Detected."); + return false; } return false; @@ -236,21 +240,126 @@ namespace TShockAPI args.TPlayer.name = name; args.Player.ReceivedInfo = true; + return false; + } + + private static bool HandleConnecting(GetDataHandlerArgs args) + { + var user = TShock.Users.GetUserByName(args.Player.Name); + if (user != null && !TShock.Config.DisableLoginBeforeJoin) + { + args.Player.RequiresPassword = true; + NetMessage.SendData((int)PacketTypes.PasswordRequired, args.Player.Index); + return true; + } + else if (!string.IsNullOrEmpty(TShock.Config.ServerPassword)) + { + args.Player.RequiresPassword = true; + NetMessage.SendData((int)PacketTypes.PasswordRequired, args.Player.Index); + return true; + } + + if (args.Player.State == 1) + args.Player.State = 2; + NetMessage.SendData((int)PacketTypes.WorldInfo, args.Player.Index); + return true; + } + + private static bool HandlePassword(GetDataHandlerArgs args) + { + if (!args.Player.RequiresPassword) + return true; + + string password = Encoding.ASCII.GetString(args.Data.ReadBytes((int)(args.Data.Length - args.Data.Position - 1))); + var user = TShock.Users.GetUserByName(args.Player.Name); + if (user != null) + { + string encrPass = TShock.Utils.HashPassword(password); + if (user.Password.ToUpper() == encrPass.ToUpper()) + { + args.Player.RequiresPassword = false; + args.Player.PlayerData = TShock.InventoryDB.GetPlayerData(args.Player, TShock.Users.GetUserID(args.Player.Name)); + + if (args.Player.State == 1) + args.Player.State = 2; + NetMessage.SendData((int)PacketTypes.WorldInfo, args.Player.Index); + + if (TShock.Config.ServerSideInventory) + { + if (!TShock.CheckInventory(args.Player)) + { + args.Player.SendMessage("Login Failed, Please fix the above errors then /login again.", Color.Cyan); + args.Player.IgnoreActionsForClearingTrashCan = true; + return true; + } + } + + args.Player.Group = TShock.Utils.GetGroup(user.Group); + args.Player.UserAccountName = args.Player.Name; + args.Player.UserID = TShock.Users.GetUserID(args.Player.UserAccountName); + args.Player.IsLoggedIn = true; + args.Player.IgnoreActionsForInventory = false; + + args.Player.PlayerData.CopyInventory(args.Player); + TShock.InventoryDB.InsertPlayerData(args.Player); + + args.Player.SendMessage("Authenticated as " + args.Player.Name + " successfully.", Color.LimeGreen); + Log.ConsoleInfo(args.Player.Name + " authenticated successfully as user: " + args.Player.Name); + return true; + } + TShock.Utils.ForceKick(args.Player, "Incorrect User Account Password"); + return true; + } + if (!string.IsNullOrEmpty(TShock.Config.ServerPassword)) + { + if(TShock.Config.ServerPassword == password) + { + args.Player.RequiresPassword = false; + if (args.Player.State == 1) + args.Player.State = 2; + NetMessage.SendData((int)PacketTypes.WorldInfo, args.Player.Index); + return true; + } + TShock.Utils.ForceKick(args.Player, "Incorrect Server Password"); + return true; + } + + TShock.Utils.ForceKick(args.Player, "Bad Password Attempt"); + return true; + } + + private static bool HandleGetSection(GetDataHandlerArgs args) + { + if (args.Player.RequestedSection) + return true; + + args.Player.RequestedSection = true; + if (TShock.HackedHealth(args.Player) && !args.Player.Group.HasPermission(Permissions.ignorestathackdetection)) + { + TShock.Utils.ForceKick(args.Player, "You have Hacked Health/Mana, Please use a different character."); + } + + if (!args.Player.Group.HasPermission(Permissions.ignorestackhackdetection)) + { + TShock.HackedInventory(args.Player); + } + + if (TShock.Utils.ActivePlayers() + 1 > TShock.Config.MaxSlots && !args.Player.Group.HasPermission(Permissions.reservedslot)) + { + TShock.Utils.ForceKick(args.Player, TShock.Config.ServerFullReason); + return true; + } + NetMessage.SendData((int)PacketTypes.TimeSet, -1, -1, "", 0, 0, Main.sunModY, Main.moonModY); if (TShock.Config.EnableGeoIP && TShock.Geo != null) { - var code = TShock.Geo.TryGetCountryCode(IPAddress.Parse(args.Player.IP)); - args.Player.Country = code == null ? "N/A" : MaxMind.GeoIPCountry.GetCountryNameByCode(code); - if (code == "A1") - if (TShock.Config.KickProxyUsers) - TShock.Utils.Kick(args.Player, "Proxies are not allowed"); - Log.Info(string.Format("{0} ({1}) from '{2}' group from '{3}' joined.", args.Player.Name, args.Player.IP, args.Player.Group.Name, args.Player.Country)); + Log.Info(string.Format("{0} ({1}) from '{2}' group from '{3}' joined. ({4}/{5})", args.Player.Name, args.Player.IP, args.Player.Group.Name, args.Player.Country, TShock.Utils.ActivePlayers(), TShock.Config.MaxSlots)); TShock.Utils.Broadcast(args.Player.Name + " has joined from the " + args.Player.Country, Color.Yellow); } else { - Log.Info(string.Format("{0} ({1}) from '{2}' group joined.", args.Player.Name, args.Player.IP, args.Player.Group.Name)); + Log.Info(string.Format("{0} ({1}) from '{2}' group joined. ({3}/{4})", args.Player.Name, args.Player.IP, args.Player.Group.Name, TShock.Utils.ActivePlayers(), TShock.Config.MaxSlots)); TShock.Utils.Broadcast(args.Player.Name + " has joined", Color.Yellow); } @@ -308,6 +417,10 @@ namespace TShockAPI var tile = Main.tile[realx, realy]; var newtile = tiles[x, y]; if(TShock.CheckTilePermission(args.Player, x, y)) + { + continue; + } + if(TShock.CheckRangePermission(args.Player, x, y)) { continue; } @@ -430,6 +543,10 @@ namespace TShockAPI TSPlayer.All.SendTileSquare(tileX, tileY, size); WorldGen.RangeFrame(tileX, tileY, tileX + size, tileY + size); } + else + { + args.Player.SendTileSquare(tileX, tileY, size); + } return true; } @@ -474,7 +591,13 @@ namespace TShockAPI { return true; } - if (tiletype == 48 && !args.Player.Group.HasPermission(Permissions.usebanneditem) && TShock.Itembans.ItemIsBanned("Spikes")) + if (tiletype == 29 && tiletype == 97 && TShock.Config.ServerSideInventory) + { + args.Player.SendMessage("You cannot place this tile, Server side inventory is enabled.", Color.Red); + args.Player.SendTileSquare(tileX, tileY); + return true; + } + if (tiletype == 48 && !args.Player.Group.HasPermission(Permissions.usebanneditem) && TShock.Itembans.ItemIsBanned("Spike", args.Player)) { args.Player.SendTileSquare(tileX, tileY); return true; @@ -485,7 +608,7 @@ namespace TShockAPI args.Player.SendTileSquare(tileX, tileY); return true; } - if (tiletype == 141 && !args.Player.Group.HasPermission(Permissions.usebanneditem) && TShock.Itembans.ItemIsBanned("Explosives")) + if (tiletype == 141 && !args.Player.Group.HasPermission(Permissions.usebanneditem) && TShock.Itembans.ItemIsBanned("Explosives", args.Player)) { args.Player.SendTileSquare(tileX, tileY); return true; @@ -504,12 +627,29 @@ namespace TShockAPI return true; } - if (type == 0 && Main.tileSolid[Main.tile[tileX, tileY].type] && args.Player.Active) + if ((tiletype == 127 || Main.tileCut[tiletype]) && type == 0) //Ice Block Kill, Bypass range checks and such { - args.Player.TileThreshold++; - var coords = new Vector2(tileX, tileY); - if (!args.Player.TilesDestroyed.ContainsKey(coords)) - args.Player.TilesDestroyed.Add(coords, Main.tile[tileX, tileY]); + return false; + } + + if (TShock.CheckRangePermission(args.Player, tileX, tileY)) + { + args.Player.SendTileSquare(tileX, tileY); + return true; + } + + if (args.Player.TileKillThreshold >= TShock.Config.TileKillThreshold) + { + args.Player.LastThreat = DateTime.UtcNow; + args.Player.SendTileSquare(tileX, tileY); + return true; + } + + if (args.Player.TilePlaceThreshold >= TShock.Config.TilePlaceThreshold) + { + args.Player.LastThreat = DateTime.UtcNow; + args.Player.SendTileSquare(tileX, tileY); + return true; } if ((DateTime.UtcNow - args.Player.LastThreat).TotalMilliseconds < 5000) @@ -518,6 +658,22 @@ namespace TShockAPI return true; } + if (type == 1 && !args.Player.Group.HasPermission(Permissions.ignoreplacetiledetection)) + { + args.Player.TilePlaceThreshold++; + var coords = new Vector2(tileX, tileY); + if (!args.Player.TilesCreated.ContainsKey(coords)) + args.Player.TilesCreated.Add(coords, Main.tile[tileX, tileY].Data); + } + + if ((type == 0 || type == 4) && Main.tileSolid[Main.tile[tileX, tileY].type] && !args.Player.Group.HasPermission(Permissions.ignorekilltiledetection)) + { + args.Player.TileKillThreshold++; + var coords = new Vector2(tileX, tileY); + if (!args.Player.TilesDestroyed.ContainsKey(coords)) + args.Player.TilesDestroyed.Add(coords, Main.tile[tileX, tileY].Data); + } + return false; } @@ -543,7 +699,7 @@ namespace TShockAPI args.TPlayer.hostile = pvp; - if (TShock.Config.AlwaysPvP) + if (TShock.Config.PvPMode == "always") { if (pvp == true) args.Player.IgnoreActionsForPvP = false; @@ -572,40 +728,107 @@ namespace TShockAPI return true; } - if ((control & 32) == 32) - { - if (!args.Player.Group.HasPermission(Permissions.usebanneditem) && TShock.Itembans.ItemIsBanned(args.TPlayer.inventory[item].name)) - { - args.Player.LastThreat = DateTime.UtcNow; - args.Player.SendMessage(string.Format("You cannot use {0} on this server. Your actions are being ignored.", args.TPlayer.inventory[item].name), Color.Red); - return true; - } - } - if (!pos.Equals(args.Player.LastNetPosition)) { float distance = Vector2.Distance(new Vector2((pos.X / 16f), (pos.Y / 16f)), new Vector2(Main.spawnTileX, Main.spawnTileY)); - if (TShock.CheckIgnores(args.Player) && distance > 6f) + if (TShock.CheckIgnores(args.Player) && distance > TShock.Config.MaxRangeForDisabled) { - if (args.Player.IgnoreActionsForCheating) + if(args.Player.IgnoreActionsForCheating != "none") { - args.Player.SendMessage("You have been disabled for cheating! Please login with a new character!", Color.Red); + args.Player.SendMessage("Disabled for cheating: " + args.Player.IgnoreActionsForCheating, Color.Red); } - else if (TShock.Config.ServerSideInventory && !args.Player.IsLoggedIn) + else if (TShock.Config.RequireLogin && !args.Player.IsLoggedIn) + { + args.Player.SendMessage("Please /register or /login to play!", Color.Red); + } + else if (args.Player.IgnoreActionsForInventory) { args.Player.SendMessage("Server Side Inventory is enabled! Please /register or /login to play!", Color.Red); } - else if (TShock.Config.AlwaysPvP && !args.TPlayer.hostile) + else if (args.Player.IgnoreActionsForClearingTrashCan) + { + args.Player.SendMessage("You need to rejoin to ensure your trash can is cleared!", Color.Red); + } + else if (args.Player.IgnoreActionsForPvP) { args.Player.SendMessage("PvP is forced! Enable PvP else you can't move or do anything!", Color.Red); } args.Player.Spawn(); return true; } - } + if (args.Player.Dead) + { + return true; + } + + if (!args.Player.Group.HasPermission(Permissions.ignorenoclipdetection) && Collision.SolidCollision(pos, args.TPlayer.width, args.TPlayer.height)) + { + int lastTileX = (int)(args.Player.LastNetPosition.X / 16f); + int lastTileY = (int)(args.Player.LastNetPosition.Y / 16f); + if (!args.Player.Teleport(lastTileX, lastTileY + 3)) + { + args.Player.SendMessage("You got stuck in a solid object, Sent to spawn point."); + args.Player.Spawn(); + } + return true; + } + } args.Player.LastNetPosition = pos; - return false; + + if ((control & 32) == 32) + { + if (!args.Player.Group.HasPermission(Permissions.usebanneditem) && TShock.Itembans.ItemIsBanned(args.TPlayer.inventory[item].name, args.Player)) + { + control -= 32; + args.Player.LastThreat = DateTime.UtcNow; + args.Player.SendMessage(string.Format("You cannot use {0} on this server. Your actions are being ignored.", args.TPlayer.inventory[item].name), Color.Red); + } + } + + args.TPlayer.selectedItem = item; + args.TPlayer.position = pos; + args.TPlayer.velocity = vel; + args.TPlayer.oldVelocity = args.TPlayer.velocity; + args.TPlayer.fallStart = (int)(pos.Y / 16f); + args.TPlayer.controlUp = false; + args.TPlayer.controlDown = false; + args.TPlayer.controlLeft = false; + args.TPlayer.controlRight = false; + args.TPlayer.controlJump = false; + args.TPlayer.controlUseItem = false; + args.TPlayer.direction = -1; + if ((control & 1) == 1) + { + args.TPlayer.controlUp = true; + } + if ((control & 2) == 2) + { + args.TPlayer.controlDown = true; + } + if ((control & 4) == 4) + { + args.TPlayer.controlLeft = true; + } + if ((control & 8) == 8) + { + args.TPlayer.controlRight = true; + } + if ((control & 16) == 16) + { + args.TPlayer.controlJump = true; + } + if ((control & 32) == 32) + { + args.TPlayer.controlUseItem = true; + } + if ((control & 64) == 64) + { + args.TPlayer.direction = 1; + } + NetMessage.SendData((int)PacketTypes.PlayerUpdate, -1, args.Player.Index, "", args.Player.Index); + + return true; } private static bool HandleProjectileNew(GetDataHandlerArgs args) @@ -627,12 +850,14 @@ namespace TShockAPI if (args.Player.Index != owner) { + args.Player.LastThreat = DateTime.UtcNow; args.Player.SendData(PacketTypes.ProjectileNew, "", index); return true; } if (dmg > 175) { + args.Player.LastThreat = DateTime.UtcNow; args.Player.SendData(PacketTypes.ProjectileNew, "", index); return true; } @@ -650,6 +875,24 @@ namespace TShockAPI return true; } + if (args.Player.ProjectileThreshold >= TShock.Config.ProjectileThreshold) + { + args.Player.LastThreat = DateTime.UtcNow; + args.Player.SendData(PacketTypes.ProjectileNew, "", index); + return true; + } + + if ((DateTime.UtcNow - args.Player.LastThreat).TotalMilliseconds < 5000) + { + args.Player.SendData(PacketTypes.ProjectileNew, "", index); + return true; + } + + if (!args.Player.Group.HasPermission(Permissions.ignoreprojectiledetection)) + { + args.Player.ProjectileThreshold++; + } + return false; } @@ -660,6 +903,7 @@ namespace TShockAPI if (args.Player.Index != owner) { + args.Player.LastThreat = DateTime.UtcNow; return true; } @@ -674,6 +918,7 @@ namespace TShockAPI if (args.Player.Index != Main.projectile[index].owner) { + args.Player.LastThreat = DateTime.UtcNow; args.Player.SendData(PacketTypes.ProjectileNew, "", index); return true; } @@ -691,6 +936,12 @@ namespace TShockAPI return true; } + if ((DateTime.UtcNow - args.Player.LastThreat).TotalMilliseconds < 5000) + { + args.Player.SendData(PacketTypes.ProjectileNew, "", index); + return true; + } + return false; } @@ -713,9 +964,7 @@ namespace TShockAPI } args.Player.LastDeath = DateTime.Now; - - if (args.Player.Difficulty != 2) - args.Player.ForceSpawn = true; + args.Player.Dead = true; return false; } @@ -740,43 +989,59 @@ namespace TShockAPI return true; } - if ((DateTime.UtcNow - args.Player.LastThreat).TotalMilliseconds < 5000) + if (args.Player.TileLiquidThreshold >= TShock.Config.TileLiquidThreshold) { + args.Player.LastThreat = DateTime.UtcNow; args.Player.SendTileSquare(tileX, tileY); return true; } - bool bucket = false; - for (int i = 0; i < 49; i++) + if (!args.Player.Group.HasPermission(Permissions.ignoreliquidsetdetection)) { - if (args.TPlayer.inventory[i].type >= 205 && args.TPlayer.inventory[i].type <= 207) - { - bucket = true; - break; - } + args.Player.TileLiquidThreshold++; } - if (!bucket) + + int bucket = 0; + if (args.TPlayer.inventory[args.TPlayer.selectedItem].type == 206) { + bucket = 1; + } + else if (args.TPlayer.inventory[args.TPlayer.selectedItem].type == 207) + { + bucket = 2; + } + + if (lava && bucket != 2 && !args.Player.Group.HasPermission(Permissions.usebanneditem) && TShock.Itembans.ItemIsBanned("Lava Bucket", args.Player)) + { + args.Player.LastThreat = DateTime.UtcNow; args.Player.SendTileSquare(tileX, tileY); return true; } - if (lava && !args.Player.Group.HasPermission(Permissions.usebanneditem) && TShock.Itembans.ItemIsBanned("Lava Bucket")) + if (!lava && bucket != 1 && !args.Player.Group.HasPermission(Permissions.usebanneditem) && TShock.Itembans.ItemIsBanned("Water Bucket", args.Player)) { + args.Player.LastThreat = DateTime.UtcNow; args.Player.SendTileSquare(tileX, tileY); return true; } - if (!lava && !args.Player.Group.HasPermission(Permissions.usebanneditem) && TShock.Itembans.ItemIsBanned("Water Bucket")) - { - args.Player.SendTileSquare(tileX, tileY); - return true; - } if (TShock.CheckTilePermission(args.Player, tileX, tileY)) { args.Player.SendTileSquare(tileX, tileY); return true; } + + if (TShock.CheckRangePermission(args.Player, tileX, tileY, 16)) + { + args.Player.SendTileSquare(tileX, tileY); + return true; + } + + if ((DateTime.UtcNow - args.Player.LastThreat).TotalMilliseconds < 5000) + { + args.Player.SendTileSquare(tileX, tileY); + return true; + } return false; } @@ -797,6 +1062,7 @@ namespace TShockAPI if (Main.tile[tileX, tileY].type != 0x15 && (!TShock.Utils.MaxChests() && Main.tile[tileX, tileY].type != 0)) //Chest { + args.Player.LastThreat = DateTime.UtcNow; args.Player.SendTileSquare(tileX, tileY); return true; } @@ -807,6 +1073,12 @@ namespace TShockAPI return true; } + if (TShock.CheckRangePermission(args.Player, tileX, tileY)) + { + args.Player.SendTileSquare(tileX, tileY); + return true; + } + return false; } @@ -838,6 +1110,7 @@ namespace TShockAPI else args.Player.InitSpawn = true; + args.Player.Dead = false; return false; } @@ -851,10 +1124,16 @@ namespace TShockAPI return true; } - if (TShock.Config.RangeChecks && ((Math.Abs(args.Player.TileX - x) > 32) || (Math.Abs(args.Player.TileY - y) > 32))) + if (TShock.CheckRangePermission(args.Player, x, y)) { return true; } + + if (TShock.CheckTilePermission(args.Player, x, y) && TShock.Config.RegionProtectChests) + { + return true; + } + return false; } @@ -879,15 +1158,18 @@ namespace TShockAPI Item item = new Item(); item.netDefaults(type); - if (stacks > item.maxStack || TShock.Itembans.ItemIsBanned(item.name)) + if (stacks > item.maxStack || TShock.Itembans.ItemIsBanned(item.name, args.Player)) { - args.Player.SendData(PacketTypes.ChestItem, "", id, slot); return false; } - if (TShock.CheckTilePermission(args.Player, Main.chest[id].x, Main.chest[id].y)) + if (TShock.CheckTilePermission(args.Player, Main.chest[id].x, Main.chest[id].y) && TShock.Config.RegionProtectChests) + { + return false; + } + + if (TShock.CheckRangePermission(args.Player, Main.chest[id].x, Main.chest[id].y)) { - args.Player.SendData(PacketTypes.ChestItem, "", id, slot); return false; } @@ -905,15 +1187,12 @@ namespace TShockAPI args.Player.SendData(PacketTypes.SignNew, "", id); return true; } - return false; - } - private static bool HandleGetSection(GetDataHandlerArgs args) - { - if (args.Player.RequestedSection) + if (TShock.CheckRangePermission(args.Player, x, y)) + { + args.Player.SendData(PacketTypes.SignNew, "", id); return true; - - args.Player.RequestedSection = true; + } return false; } @@ -936,6 +1215,12 @@ namespace TShockAPI args.Player.SendData(PacketTypes.UpdateNPCHome, "", id, Main.npc[id].homeTileX, Main.npc[id].homeTileY, Convert.ToByte(Main.npc[id].homeless)); return true; } + + if (TShock.CheckRangePermission(args.Player, x, y)) + { + args.Player.SendData(PacketTypes.UpdateNPCHome, "", id, Main.npc[id].homeTileX, Main.npc[id].homeTileY, Convert.ToByte(Main.npc[id].homeless)); + return true; + } return false; } @@ -955,7 +1240,7 @@ namespace TShockAPI args.Player.SendData(PacketTypes.PlayerBuff, "", id); return true; } - if (TShock.Config.RangeChecks && ((Math.Abs(args.Player.TileX - TShock.Players[id].TileX) > 64) || (Math.Abs(args.Player.TileY - TShock.Players[id].TileY) > 64))) + if (TShock.CheckRangePermission(args.Player, TShock.Players[id].TileX, TShock.Players[id].TileY, 50)) { args.Player.SendData(PacketTypes.PlayerBuff, "", id); return true; @@ -989,7 +1274,7 @@ namespace TShockAPI return false; } - if (TShock.Config.RangeChecks && ((Math.Abs(args.Player.TileX - (pos.X / 16f)) > 64) || (Math.Abs(args.Player.TileY - (pos.Y / 16f)) > 64))) + if (TShock.CheckRangePermission(args.Player, (int)(pos.X / 16f), (int)(pos.Y / 16f))) { args.Player.SendData(PacketTypes.ItemDrop, "", id); return true; @@ -997,7 +1282,7 @@ namespace TShockAPI Item item = new Item(); item.netDefaults(type); - if (stacks > item.maxStack || TShock.Itembans.ItemIsBanned(item.name)) + if (stacks > item.maxStack || TShock.Itembans.ItemIsBanned(item.name, args.Player)) { args.Player.SendData(PacketTypes.ItemDrop, "", id); return true; @@ -1055,7 +1340,7 @@ namespace TShockAPI return true; } - if (TShock.Config.RangeChecks && ((Math.Abs(args.Player.TileX - TShock.Players[id].TileX) > 128) || (Math.Abs(args.Player.TileY - TShock.Players[id].TileY) > 128))) + if (TShock.CheckRangePermission(args.Player, TShock.Players[id].TileX, TShock.Players[id].TileY, 100)) { args.Player.SendData(PacketTypes.PlayerHp, "", id); args.Player.SendData(PacketTypes.PlayerUpdate, "", id); @@ -1101,7 +1386,7 @@ namespace TShockAPI return true; } - if (TShock.Config.RangeChecks && ((Math.Abs(args.Player.TileX - (Main.npc[id].position.X / 16f)) > 128) || (Math.Abs(args.Player.TileY - (Main.npc[id].position.Y / 16f)) > 128))) + if (TShock.Config.RangeChecks && TShock.CheckRangePermission(args.Player, (int)(Main.npc[id].position.X / 16f), (int)(Main.npc[id].position.Y / 16f), 100)) { args.Player.SendData(PacketTypes.NpcUpdate, "", id); return true; @@ -1147,5 +1432,34 @@ namespace TShockAPI return false; } + + private static bool HandlePlayerBuffUpdate(GetDataHandlerArgs args) + { + var id = args.Data.ReadInt8(); + for (int i = 0; i < 10; i++) + { + var buff = args.Data.ReadInt8(); + + if (buff == 10) + { + if (!args.Player.Group.HasPermission(Permissions.usebanneditem) && TShock.Itembans.ItemIsBanned("Invisibility Potion", args.Player) ) + buff = 0; + else if (TShock.Config.DisableInvisPvP && args.TPlayer.hostile) + buff = 0; + } + + args.TPlayer.buffType[i] = buff; + if (args.TPlayer.buffType[i] > 0) + { + args.TPlayer.buffTime[i] = 60; + } + else + { + args.TPlayer.buffTime[i] = 0; + } + } + NetMessage.SendData((int)PacketTypes.PlayerBuff, -1, args.Player.Index, "", args.Player.Index); + return true; + } } } diff --git a/TShockAPI/Group.cs b/TShockAPI/Group.cs index c089ff16..78877c42 100644 --- a/TShockAPI/Group.cs +++ b/TShockAPI/Group.cs @@ -28,6 +28,8 @@ namespace TShockAPI public string Name { get; set; } public Group Parent { get; set; } public int Order { get; set; } + public string Prefix { get; set; } + public string Suffix { get; set; } public byte R = 255; public byte G = 255; @@ -91,6 +93,9 @@ namespace TShockAPI R = (byte)TShock.Config.SuperAdminChatRGB[0]; G = (byte)TShock.Config.SuperAdminChatRGB[1]; B = (byte)TShock.Config.SuperAdminChatRGB[2]; + Prefix = TShock.Config.SuperAdminChatPrefix; + Suffix = TShock.Config.SuperAdminChatSuffix; + } public override bool HasPermission(string permission) diff --git a/TShockAPI/Net/BaseMsg.cs b/TShockAPI/Net/BaseMsg.cs index 38e9c055..5e41e2ee 100644 --- a/TShockAPI/Net/BaseMsg.cs +++ b/TShockAPI/Net/BaseMsg.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; using System.IO.Streams; -using System.Linq; -using System.Text; namespace TShockAPI.Net { diff --git a/TShockAPI/Permissions.cs b/TShockAPI/Permissions.cs index b9b620c4..8ff95f71 100644 --- a/TShockAPI/Permissions.cs +++ b/TShockAPI/Permissions.cs @@ -35,14 +35,28 @@ namespace TShockAPI [Description("Prevents you from being reverted by kill tile abuse detection")] public static readonly string ignorekilltiledetection; + [Description("Prevents you from being reverted by place tile abuse detection")] + public static readonly string ignoreplacetiledetection; + + [Description("Prevents you from being disabled by liquid set abuse detection")] + public static readonly string ignoreliquidsetdetection; + + [Description("Prevents you from being disabled by liquid set abuse detection")] + public static readonly string ignoreprojectiledetection; + + [Description("Prevents you from being reverted by no clip detection")] + public static readonly string ignorenoclipdetection; + + [Description("Prevents you from being disabled by stack hack detection")] + public static readonly string ignorestackhackdetection; + + [Description("Prevents you from being kicked by hacked health detection")] + public static readonly string ignorestathackdetection; + [Description("Specific log messages are sent to users with this permission")] public static readonly string logs; - [Description("User gets the admin prefix/color in chat")] - public static readonly string adminchat; - - [Todo] - [Description("Not currently working")] + [Description("Allows you to bypass the max slots for up to 5 slots above your max")] public static readonly string reservedslot; [Description("User is notified when an update is available")] @@ -141,6 +155,28 @@ namespace TShockAPI [Description("User can convert hallow into corruption and vice-versa")] public static readonly string converthardmode; + [Description("User can mute and unmute users")] + public static readonly string mute; + + [Description("User can register account in game")] + public static readonly string canregister; + + [Description("User can login in game")] + public static readonly string canlogin; + + [Description("User can change password in game")] + public static readonly string canchangepassword; + + [Description("User can use party chat in game")] + public static readonly string canpartychat; + + [Description("User can talk in third person")] + public static readonly string cantalkinthird; + + [Description("Bypass Server Side Inventory checks")] + public static readonly string bypassinventorychecks; + + static Permissions() { foreach (var field in typeof(Permissions).GetFields()) diff --git a/TShockAPI/Properties/AssemblyInfo.cs b/TShockAPI/Properties/AssemblyInfo.cs index d534f527..740ad4e0 100644 --- a/TShockAPI/Properties/AssemblyInfo.cs +++ b/TShockAPI/Properties/AssemblyInfo.cs @@ -8,9 +8,9 @@ using System.Runtime.InteropServices; [assembly: AssemblyTitle("TShockAPI")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyCompany("Nyx Team")] [assembly: AssemblyProduct("TShockAPI")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyCopyright("Copyright © Nyx Team 2011")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -35,6 +35,5 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.4.1.1221")] -[assembly: AssemblyFileVersion("3.4.1.1221")] - +[assembly: AssemblyVersion("3.4.2.1232")] +[assembly: AssemblyFileVersion("3.4.2.1232")] \ No newline at end of file diff --git a/TShockAPI/Rest/RestManager.cs b/TShockAPI/Rest/RestManager.cs index cf20d8ef..b43e6542 100644 --- a/TShockAPI/Rest/RestManager.cs +++ b/TShockAPI/Rest/RestManager.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using HttpServer; using Rests; using Terraria; diff --git a/TShockAPI/Rest/SecureRest.cs b/TShockAPI/Rest/SecureRest.cs index db12f1fe..4563f4b6 100644 --- a/TShockAPI/Rest/SecureRest.cs +++ b/TShockAPI/Rest/SecureRest.cs @@ -2,9 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Net; -using System.Text; using HttpServer; -using TShockAPI; namespace Rests { diff --git a/TShockAPI/StatTracker.cs b/TShockAPI/StatTracker.cs new file mode 100644 index 00000000..87ba219e --- /dev/null +++ b/TShockAPI/StatTracker.cs @@ -0,0 +1,75 @@ +using System; +using System.IO; +using System.Net; +using System.Threading; +using Terraria; + +namespace TShockAPI +{ + public class StatTracker + { + Utils Utils = TShock.Utils; + public DateTime lastcheck = DateTime.MinValue; + readonly int checkinFrequency = 5; + + public void checkin() + { + if ((DateTime.Now - lastcheck).TotalMinutes >= checkinFrequency) + { + ThreadPool.QueueUserWorkItem(callHome); + lastcheck = DateTime.Now; + } + } + + private void callHome(object state) + { + string fp; + string lolpath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/.tshock/"; + if (!Directory.Exists(lolpath)) + { + Directory.CreateDirectory(lolpath); + } + if (!File.Exists(Path.Combine(lolpath, Netplay.serverPort + ".fingerprint"))) + { + fp = ""; + int random = Utils.Random.Next(500000, 1000000); + fp += random; + + fp = Utils.HashPassword(Netplay.serverIP + fp + Netplay.serverPort + Netplay.serverListenIP); + TextWriter tw = new StreamWriter(Path.Combine(lolpath, Netplay.serverPort + ".fingerprint")); + tw.Write(fp); + tw.Close(); + } + else + { + fp = ""; + TextReader tr = new StreamReader(Path.Combine(lolpath, Netplay.serverPort + ".fingerprint")); + fp = tr.ReadToEnd(); + tr.Close(); + } + + using (var client = new WebClient()) + { + client.Headers.Add("user-agent", + "TShock (" + TShock.VersionNum + ")"); + try + { + string response; + if (TShock.Config.DisablePlayerCountReporting) + { + response = client.DownloadString("http://tshock.co/tickto.php?do=log&fp=" + fp + "&ver=" + TShock.VersionNum + "&os=" + System.Environment.OSVersion.ToString() + "&mono=" + Main.runningMono + "&port=" + Netplay.serverPort + "&plcount=0"); + } + else + { + response = client.DownloadString("http://tshock.co/tickto.php?do=log&fp=" + fp + "&ver=" + TShock.VersionNum + "&os=" + System.Environment.OSVersion.ToString() + "&mono=" + Main.runningMono + "&port=" + Netplay.serverPort + "&plcount=" + TShock.Utils.ActivePlayers()); + } + Log.ConsoleInfo("Stat Tracker: " + response + "\n"); + } + catch (Exception e) + { + Log.Error(e.ToString()); + } + } + } + } +} diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index f706badf..92e69dbf 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -30,10 +30,14 @@ namespace TShockAPI { public static readonly TSServerPlayer Server = new TSServerPlayer(); public static readonly TSPlayer All = new TSPlayer("All"); - public int TileThreshold { get; set; } - public Dictionary TilesDestroyed { get; protected set; } - public bool SyncHP { get; set; } - public bool SyncMP { get; set; } + public int TileKillThreshold { get; set; } + public int TilePlaceThreshold { get; set; } + public int TileLiquidThreshold { get; set; } + public int ProjectileThreshold { get; set; } + public Dictionary TilesDestroyed { get; protected set; } + public Dictionary TilesCreated { get; protected set; } + public int FirstMaxHP { get; set; } + public int FirstMaxMP { get; set; } public Group Group { get; set; } public bool ReceivedInfo { get; set; } public int Index { get; protected set; } @@ -56,18 +60,21 @@ namespace TShockAPI public int UserID = -1; public bool HasBeenNaggedAboutLoggingIn; public bool TPAllow = true; + public bool mute = false; public bool TpLock = false; Player FakePlayer; public bool RequestedSection = false; public DateTime LastDeath { get; set; } - public bool ForceSpawn = false; + public bool Dead = false; public string Country = "??"; public int Difficulty; private string CacheIP; public bool IgnoreActionsForPvP = false; public bool IgnoreActionsForInventory = false; - public bool IgnoreActionsForCheating = false; + public string IgnoreActionsForCheating = "none"; + public bool IgnoreActionsForClearingTrashCan = false; public PlayerData PlayerData; + public bool RequiresPassword = false; public bool RealPlayer { @@ -77,6 +84,13 @@ namespace TShockAPI { get { return RealPlayer && (Netplay.serverSock[Index] != null && Netplay.serverSock[Index].active && !Netplay.serverSock[Index].kill); } } + + public int State + { + get { return Netplay.serverSock[Index].state; } + set { Netplay.serverSock[Index].state = value; } + } + public string IP { get @@ -154,17 +168,19 @@ namespace TShockAPI public TSPlayer(int index) { - TilesDestroyed = new Dictionary(); + TilesDestroyed = new Dictionary(); + TilesCreated = new Dictionary(); Index = index; - Group = new Group("null"); + Group = new Group(TShock.Config.DefaultGuestGroupName); } protected TSPlayer(String playerName) { - TilesDestroyed = new Dictionary(); + TilesDestroyed = new Dictionary(); + TilesCreated = new Dictionary(); Index = -1; FakePlayer = new Player { name = playerName, whoAmi = -1 }; - Group = new Group("null"); + Group = new Group(TShock.Config.DefaultGuestGroupName); } public virtual void Disconnect(string reason) @@ -375,7 +391,7 @@ namespace TShockAPI public override void SendMessage(string msg, byte red, byte green, byte blue) { Console.WriteLine(msg); - RconHandler.Response += msg + "\n"; + //RconHandler.Response += msg + "\n"; } public void SetFullMoon(bool fullmoon) @@ -417,17 +433,15 @@ namespace TShockAPI NetMessage.SendData((int)PacketTypes.NpcStrike, -1, -1, "", npcid, damage, knockBack, hitDirection); } - public void RevertKillTile(Dictionary destroyedTiles) + public void RevertTiles(Dictionary tiles) { // Update Main.Tile first so that when tile sqaure is sent it is correct - foreach (KeyValuePair entry in destroyedTiles) + foreach (KeyValuePair entry in tiles) { - Main.tile[(int)entry.Key.X, (int)entry.Key.Y] = entry.Value; - Log.Debug(string.Format("Reverted DestroyedTile(TileXY:{0}_{1}, Type:{2})", - entry.Key.X, entry.Key.Y, Main.tile[(int)entry.Key.X, (int)entry.Key.Y].type)); + Main.tile[(int)entry.Key.X, (int)entry.Key.Y].Data = entry.Value; } // Send all players updated tile sqaures - foreach (Vector2 coords in destroyedTiles.Keys) + foreach (Vector2 coords in tiles.Keys) { All.SendTileSquare((int)coords.X, (int)coords.Y, 3); } @@ -438,7 +452,7 @@ namespace TShockAPI { public NetItem[] inventory = new NetItem[NetItem.maxNetInventory]; public int maxHealth = 100; - public int maxMana = 100; + //public int maxMana = 100; public bool exists = false; public PlayerData(TSPlayer player) @@ -479,7 +493,6 @@ namespace TShockAPI public void CopyInventory(TSPlayer player) { this.maxHealth = player.TPlayer.statLifeMax; - this.maxMana = player.TPlayer.statManaMax; Item[] inventory = player.TPlayer.inventory; Item[] armor = player.TPlayer.armor; for (int i = 0; i < NetItem.maxNetInventory; i++) diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index ff7a6eda..0edbc025 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -32,12 +32,10 @@ using System.Diagnostics; using System.IO; using System.Net; using System.Reflection; -using System.Runtime.InteropServices; using System.Threading; using Mono.Data.Sqlite; using Hooks; using MySql.Data.MySqlClient; -using Newtonsoft.Json; using Rests; using Terraria; using TShockAPI.DB; @@ -71,7 +69,7 @@ namespace TShockAPI public static SecureRest RestApi; public static RestManager RestManager; public static Utils Utils = new Utils(); - + public static StatTracker StatTracker = new StatTracker(); /// /// Called after TShock is initialized. Useful for plugins that needs hooks before tshock but also depend on tshock being loaded. /// @@ -189,10 +187,12 @@ namespace TShockAPI if (Config.EnableGeoIP && File.Exists(geoippath)) Geo = new MaxMind.GeoIPCountry(geoippath); + Console.Title = string.Format("TerrariaShock Version {0} ({1})", Version, VersionCodename); Log.ConsoleInfo(string.Format("TerrariaShock Version {0} ({1}) now running.", Version, VersionCodename)); GameHooks.PostInitialize += OnPostInit; GameHooks.Update += OnUpdate; + ServerHooks.Connect += OnConnect; ServerHooks.Join += OnJoin; ServerHooks.Leave += OnLeave; ServerHooks.Chat += OnChat; @@ -226,49 +226,7 @@ namespace TShockAPI } - private void callHome() - { - string fp; - string lolpath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/.tshock/"; - if (!Directory.Exists(lolpath)) - { - Directory.CreateDirectory(lolpath); - } - if (!File.Exists(Path.Combine(lolpath, Netplay.serverPort + ".fingerprint"))) - { - fp = ""; - int random = Utils.Random.Next(500000, 1000000); - fp += random; - - fp = Utils.HashPassword(Netplay.serverIP + fp + Netplay.serverPort + Netplay.serverListenIP); - TextWriter tw = new StreamWriter(Path.Combine(lolpath, Netplay.serverPort + ".fingerprint")); - tw.Write(fp); - tw.Close(); - } else - { - fp = ""; - TextReader tr = new StreamReader(Path.Combine(lolpath, Netplay.serverPort + ".fingerprint")); - fp = tr.ReadToEnd(); - tr.Close(); - } - - using (var client = new WebClient()) - { - client.Headers.Add("user-agent", - "TShock (" + VersionNum + ")"); - try - { - string response = client.DownloadString("http://tshock.co/tickto.php?do=log&fp=" + fp + "&ver=" + VersionNum + "&port=" + Netplay.serverPort); - Console.ForegroundColor = ConsoleColor.Cyan; - Console.WriteLine("\nRegistered with stat tracker: " + response + "\n"); - Console.ForegroundColor = ConsoleColor.Gray; - } - catch (Exception e) - { - Log.Error(e.ToString()); - } - } - } + RestObject RestApi_Verify(string username, string password) { @@ -455,54 +413,131 @@ namespace TShockAPI if (Config.RestApiEnabled) RestApi.Start(); - Thread t = new Thread(callHome); - t.Start(); + StatTracker.checkin(); + FixChestStacks(); + + } + + private void FixChestStacks() + { + foreach(Chest chest in Main.chest) + { + if (chest != null) + { + foreach (Item item in chest.item) + { + if (item != null && item.stack > item.maxStack) + item.stack = item.maxStack; + } + } + } } private DateTime LastCheck = DateTime.UtcNow; + private DateTime LastSave = DateTime.UtcNow; private void OnUpdate() { UpdateManager.UpdateProcedureCheck(); - + StatTracker.checkin(); if (Backups.IsBackupTime) Backups.Backup(); //call these every second, not every update if ((DateTime.UtcNow - LastCheck).TotalSeconds >= 1) { + OnSecondUpdate(); LastCheck = DateTime.UtcNow; - foreach (TSPlayer player in Players) + } + + if ((DateTime.UtcNow - LastSave).TotalMinutes >= 15) + { + foreach (TSPlayer player in TShock.Players) { - if (player != null && player.Active) + // prevent null point exceptions + if (player != null && player.IsLoggedIn) { - if (player.TilesDestroyed != null) - { - if (player.TileThreshold >= Config.TileThreshold) - { - TSPlayer.Server.RevertKillTile(player.TilesDestroyed); - } - if (player.TileThreshold > 0) - { - player.TileThreshold = 0; - player.TilesDestroyed.Clear(); - } - } - /*if (CheckPlayerCollision(player.TileX, player.TileY)) - player.SendMessage("You are currently nocliping!", Color.Red);*/ - if (player.ForceSpawn && (DateTime.Now - player.LastDeath).Seconds >= 3) - { - player.Spawn(); - player.ForceSpawn = false; - } + TShock.InventoryDB.InsertPlayerData(player); } } + LastSave = DateTime.UtcNow; } } - private void OnJoin(int ply, HandledEventArgs handler) + private void OnSecondUpdate() + { + if (Config.ForceTime != "normal") + { + switch(Config.ForceTime) + { + case "day": + TSPlayer.Server.SetTime(true, 27000.0); + break; + case "night": + TSPlayer.Server.SetTime(false, 16200.0); + break; + } + } + int count = 0; + foreach (TSPlayer player in Players) + { + if (player != null && player.Active) + { + count++; + if (player.TilesDestroyed != null) + { + if (player.TileKillThreshold >= Config.TileKillThreshold) + { + player.LastThreat = DateTime.UtcNow; + TSPlayer.Server.RevertTiles(player.TilesDestroyed); + player.TilesDestroyed.Clear(); + } + } + if (player.TileKillThreshold > 0) + { + player.TileKillThreshold = 0; + } + if (player.TilesCreated != null) + { + if (player.TilePlaceThreshold >= Config.TilePlaceThreshold) + { + player.LastThreat = DateTime.UtcNow; + TSPlayer.Server.RevertTiles(player.TilesCreated); + player.TilesCreated.Clear(); + } + } + if (player.TilePlaceThreshold > 0) + { + player.TilePlaceThreshold = 0; + } + if(player.TileLiquidThreshold >= Config.TileLiquidThreshold) + { + player.LastThreat = DateTime.UtcNow; + } + if (player.TileLiquidThreshold > 0) + { + player.TileLiquidThreshold = 0; + } + if (player.ProjectileThreshold >= Config.ProjectileThreshold) + { + player.LastThreat = DateTime.UtcNow; + } + if (player.ProjectileThreshold > 0) + { + player.ProjectileThreshold = 0; + } + if (player.Dead && (DateTime.Now - player.LastDeath).Seconds >= 3 && player.Difficulty != 2) + { + player.Spawn(); + } + } + } + Console.Title = string.Format("TerrariaShock Version {0} ({1}) ({2}/{3})", Version, VersionCodename, count, Config.MaxSlots); + } + + private void OnConnect(int ply, HandledEventArgs handler) { var player = new TSPlayer(ply); if (Config.EnableDNSHostResolution) @@ -514,20 +549,17 @@ namespace TShockAPI player.Group = Users.GetGroupForIP(player.IP); } - if (TShock.Utils.ActivePlayers() + 1 > Config.MaxSlots && !player.Group.HasPermission(Permissions.reservedslot)) + if (TShock.Utils.ActivePlayers() + 1 > Config.MaxSlots + 20) { - TShock.Utils.ForceKick(player, Config.ServerFullReason); + TShock.Utils.ForceKick(player, Config.ServerFullNoReservedReason); handler.Handled = true; return; } var ipban = Bans.GetBanByIp(player.IP); - var nameban = Bans.GetBanByName(player.Name); Ban ban = null; if (ipban != null && Config.EnableIPBans) ban = ipban; - else if (nameban != null && Config.EnableIPBans) - ban = nameban; if (ban != null) { @@ -543,9 +575,45 @@ namespace TShockAPI return; } + if (TShock.Geo != null) + { + var code = TShock.Geo.TryGetCountryCode(IPAddress.Parse(player.IP)); + player.Country = code == null ? "N/A" : MaxMind.GeoIPCountry.GetCountryNameByCode(code); + if (code == "A1") + { + if (TShock.Config.KickProxyUsers) + { + TShock.Utils.ForceKick(player, "Proxies are not allowed"); + handler.Handled = true; + return; + } + } + } Players[ply] = player; } + private void OnJoin(int ply, HandledEventArgs handler) + { + var player = Players[ply]; + if (player == null) + { + handler.Handled = true; + return; + } + + var nameban = Bans.GetBanByName(player.Name); + Ban ban = null; + if (nameban != null && Config.EnableBanOnUsernames) + ban = nameban; + + if (ban != null) + { + TShock.Utils.ForceKick(player, string.Format("You are banned: {0}", ban.Reason)); + handler.Handled = true; + return; + } + } + private void OnLeave(int ply) { var tsplr = Players[ply]; @@ -559,7 +627,7 @@ namespace TShockAPI if (tsplr.IsLoggedIn) { tsplr.PlayerData.CopyInventory(tsplr); - InventoryDB.InsertPlayerData(tsplr, tsplr.UserID); + InventoryDB.InsertPlayerData(tsplr); } if (Config.RememberLeavePos) @@ -587,15 +655,6 @@ namespace TShockAPI return; } - if (tsplr.Group.HasPermission(Permissions.adminchat) && !text.StartsWith("/") && Config.AdminChatEnabled) - { - TShock.Utils.Broadcast(Config.AdminChatPrefix + "<" + tsplr.Name + "> " + text, - tsplr.Group.R, tsplr.Group.G, - tsplr.Group.B); - e.Handled = true; - return; - } - if (text.StartsWith("/")) { try @@ -608,12 +667,14 @@ namespace TShockAPI Log.Error(ex.ToString()); } } - else + else if (!tsplr.mute) { - TShock.Utils.Broadcast("{2}<{0}> {1}".SFormat(tsplr.Name, text, Config.ChatDisplayGroup ? "[{0}] ".SFormat(tsplr.Group.Name) : ""), - tsplr.Group.R, tsplr.Group.G, - tsplr.Group.B); - //Log.Info(string.Format("{0} said: {1}", tsplr.Name, text)); + TShock.Utils.Broadcast(String.Format(TShock.Config.ChatFormat, tsplr.Group.Name, tsplr.Group.Prefix, tsplr.Name, tsplr.Group.Suffix, text), tsplr.Group.R, tsplr.Group.G, tsplr.Group.B); + e.Handled = true; + } + else if (tsplr.mute) + { + tsplr.SendMessage("You are muted!"); e.Handled = true; } } @@ -655,7 +716,6 @@ namespace TShockAPI } } TSPlayer.Server.SendMessage(string.Format("{0} players connected.", count)); - e.Handled = true; } else if (text.StartsWith("say ")) { @@ -665,13 +725,12 @@ namespace TShockAPI { Main.autoSave = Config.AutoSave = !Config.AutoSave; Log.ConsoleInfo("AutoSave " + (Config.AutoSave ? "Enabled" : "Disabled")); - e.Handled = true; } - else if (text.StartsWith("/")) + else { - if (Commands.HandleCommand(TSPlayer.Server, text)) - e.Handled = true; + Commands.HandleCommand(TSPlayer.Server, text); } + e.Handled = true; } private void OnGetData(GetDataEventArgs e) @@ -696,24 +755,28 @@ namespace TShockAPI return; } - // Stop accepting updates from player as this player is going to be kicked/banned during OnUpdate (different thread so can produce race conditions) - if (player.TileThreshold >= Config.TileThreshold && !player.Group.HasPermission(Permissions.ignorekilltiledetection)) + if (player.RequiresPassword && type != PacketTypes.PasswordSend) { e.Handled = true; + return; } - else + + if ((player.State < 10 || player.Dead) && (int)type > 12 && (int)type != 16 && (int)type != 42 && (int)type != 50 && (int)type != 38) { - using (var data = new MemoryStream(e.Msg.readBuffer, e.Index, e.Length)) + e.Handled = true; + return; + } + + using (var data = new MemoryStream(e.Msg.readBuffer, e.Index, e.Length)) + { + try { - try - { - if (GetDataHandlers.HandlerGetData(type, player, data)) - e.Handled = true; - } - catch (Exception ex) - { - Log.Error(ex.ToString()); - } + if (GetDataHandlers.HandlerGetData(type, player, data)) + e.Handled = true; + } + catch (Exception ex) + { + Log.Error(ex.ToString()); } } } @@ -728,40 +791,41 @@ namespace TShockAPI } TShock.Utils.ShowFileToUser(player, "motd.txt"); - if (HackedHealth(player)) - { - player.IgnoreActionsForCheating = true; - player.SendMessage("You are using a health/mana cheat. Please choose a different character."); - } - if (HackedInventory(player)) - { - player.IgnoreActionsForCheating = true; - } - - NetMessage.syncPlayers(); - - if (Config.ServerSideInventory && !player.IsLoggedIn) - { - player.IgnoreActionsForInventory = true; - player.SendMessage("Server Side Inventory is enabled! Please /register or /login to play!", Color.Red); - } - - if (Config.AlwaysPvP && !player.TPlayer.hostile) + if (Config.PvPMode == "always" && !player.TPlayer.hostile) { player.IgnoreActionsForPvP = true; player.SendMessage("PvP is forced! Enable PvP else you can't move or do anything!", Color.Red); } + + if (!player.IsLoggedIn) + { + if (Config.RequireLogin) + { + player.SendMessage("Please /register or /login to play!", Color.Red); + } + else if (Config.ServerSideInventory) + { + player.SendMessage("Server Side Inventory is enabled! Please /register or /login to play!", Color.Red); + } + + if (Config.ServerSideInventory) + { + player.IgnoreActionsForInventory = true; + } + } + if (player.Group.HasPermission(Permissions.causeevents) && Config.InfiniteInvasion) { StartInvasion(); } + if (Config.RememberLeavePos) { var pos = RememberedPos.GetLeavePos(player.Name, player.IP); - player.Teleport((int)pos.X, (int)pos.Y); - player.SendTileSquare((int)pos.X, (int)pos.Y); + player.Teleport((int) pos.X, (int) pos.Y); } + e.Handled = true; } @@ -782,6 +846,21 @@ namespace TShockAPI if (e.Info == 43) if (Config.DisableTombstones) e.Object.SetDefaults(0); + if (e.Info == 75) + if (Config.DisableClownBombs) + e.Object.SetDefaults(0); + if (e.Info == 109) + if (Config.DisableSnowBalls) + e.Object.SetDefaults(0); + + } + + void OnNpcSetDefaults(SetDefaultsEventArgs e) + { + if (TShock.Itembans.ItemIsBanned(e.Object.name, null) ) + { + e.Object.SetDefaults(0); + } } /// @@ -937,12 +1016,12 @@ namespace TShockAPI return true; } - if (type == 17 && !player.Group.HasPermission(Permissions.usebanneditem) && TShock.Itembans.ItemIsBanned("Dirt Wand")) //Dirt Wand Projectile + if (type == 17 && !player.Group.HasPermission(Permissions.usebanneditem) && TShock.Itembans.ItemIsBanned("Dirt Rod", player)) //Dirt Rod Projectile { return true; } - if ((type == 42 || type == 65 || type == 68) && !player.Group.HasPermission(Permissions.usebanneditem) && TShock.Itembans.ItemIsBanned("Sandgun")) //Sandgun Projectiles + if ((type == 42 || type == 65 || type == 68) && !player.Group.HasPermission(Permissions.usebanneditem) && TShock.Itembans.ItemIsBanned("Sandgun", player)) //Sandgun Projectiles { return true; } @@ -950,7 +1029,7 @@ namespace TShockAPI Projectile proj = new Projectile(); proj.SetDefaults(type); - if (!player.Group.HasPermission(Permissions.usebanneditem) && TShock.Itembans.ItemIsBanned(proj.name)) + if (!player.Group.HasPermission(Permissions.usebanneditem) && TShock.Itembans.ItemIsBanned(proj.name, player)) { return true; } @@ -963,12 +1042,17 @@ namespace TShockAPI return false; } - public static bool CheckTilePermission(TSPlayer player, int tileX, int tileY) + public static bool CheckRangePermission(TSPlayer player, int x, int y, int range = 32) { - if (TShock.Config.RangeChecks && ((Math.Abs(player.TileX - tileX) > 32) || (Math.Abs(player.TileY - tileY) > 32))) + if (TShock.Config.RangeChecks && ((Math.Abs(player.TileX - x) > 32) || (Math.Abs(player.TileY - y) > 32))) { return true; } + return false; + } + + public static bool CheckTilePermission(TSPlayer player, int tileX, int tileY) + { if (!player.Group.HasPermission(Permissions.canbuild)) { player.SendMessage("You do not have permission to build!", Color.Red); @@ -1078,12 +1162,6 @@ namespace TShockAPI check = false; } - if (player.TPlayer.statManaMax > playerData.maxMana) - { - player.SendMessage("Error: Your max mana exceeded (" + playerData.maxMana + ") which is stored on server", Color.Cyan); - check = false; - } - Item[] inventory = player.TPlayer.inventory; Item[] armor = player.TPlayer.armor; for (int i = 0; i < NetItem.maxNetInventory; i++) @@ -1164,30 +1242,13 @@ namespace TShockAPI check = true; if (player.IgnoreActionsForInventory) check = true; - if (player.IgnoreActionsForCheating) + if (player.IgnoreActionsForCheating != "none") + check = true; + if (!player.IsLoggedIn && Config.RequireLogin) check = true; return check; } - public static bool CheckPlayerCollision(int x, int y) - { - if (x + 1 <= Main.maxTilesX && y + 3 <= Main.maxTilesY - && x >= 0 && y >= 0) - { - for (int i = x; i < x + 2; i++) - { - for (int h = y; h < y + 4; h++) - { - if (!Main.tile[i, h].active || !Main.tileSolid[Main.tile[i, h].type]) - return false; - } - } - } - else - return false; - return true; - } - public void OnConfigRead(ConfigFile file) { NPC.defaultMaxSpawns = file.DefaultMaximumSpawns; @@ -1204,6 +1265,10 @@ namespace TShockAPI Netplay.serverPort = file.ServerPort; } + if (file.MaxSlots > 235) + file.MaxSlots = 235; + Main.maxNetPlayers = file.MaxSlots + 20; + Netplay.password = ""; Netplay.spamCheck = false; RconHandler.Password = file.RconPassword; diff --git a/TShockAPI/TShockAPI.csproj b/TShockAPI/TShockAPI.csproj index 3d024668..dbdb91bc 100644 --- a/TShockAPI/TShockAPI.csproj +++ b/TShockAPI/TShockAPI.csproj @@ -125,6 +125,7 @@ + @@ -181,7 +182,7 @@ - +