diff --git a/.gitignore b/.gitignore index cd88c055..3fee42f6 100644 --- a/.gitignore +++ b/.gitignore @@ -51,4 +51,5 @@ Thumbs.db #Template Bat file# ################### -myass.bat \ No newline at end of file +myass.bat +/TestResults \ No newline at end of file diff --git a/DBEditor/CommandList.cs b/DBEditor/CommandList.cs index eb3f2af9..93ed1064 100644 --- a/DBEditor/CommandList.cs +++ b/DBEditor/CommandList.cs @@ -5,21 +5,23 @@ using System.Text; namespace TShockDBEditor { - public class Commandlist + public class TShockCommandsList { - public static List CommandList = new List(); - - public static void AddCommands() + public static void AddRemainingTShockCommands() { + List CommandList = new List(); + CommandList.Add("reservedslot"); CommandList.Add("canwater"); CommandList.Add("canlava"); + CommandList.Add("canbuild"); + CommandList.Add("adminchat"); CommandList.Add("warp"); CommandList.Add("kick"); CommandList.Add("ban"); CommandList.Add("unban"); CommandList.Add("whitelist"); - CommandList.Add("maintenace"); + CommandList.Add("maintenance"); CommandList.Add("causeevents"); CommandList.Add("spawnboss"); CommandList.Add("spawnmob"); @@ -42,6 +44,12 @@ namespace TShockDBEditor CommandList.Add("ignorecheatdetection"); CommandList.Add("ignoregriefdetection"); CommandList.Add("usebanneditem"); + + foreach (string command in CommandList) + { + if (!TShockDBEditor.CommandList.Contains(command)) + TShockDBEditor.CommandList.Add(command); + } } } } diff --git a/DBEditor/Main.Designer.cs b/DBEditor/Main.Designer.cs index f9cf85e6..565b5cfb 100644 --- a/DBEditor/Main.Designer.cs +++ b/DBEditor/Main.Designer.cs @@ -82,6 +82,8 @@ this.label14 = new System.Windows.Forms.Label(); this.lst_userlist = new System.Windows.Forms.ListBox(); this.tabPage6 = new System.Windows.Forms.TabPage(); + this.groupBox7 = new System.Windows.Forms.GroupBox(); + this.lst_bans = new System.Windows.Forms.ListBox(); this.groupBox6 = new System.Windows.Forms.GroupBox(); this.lbl_newbanstatus = new System.Windows.Forms.Label(); this.label16 = new System.Windows.Forms.Label(); @@ -99,7 +101,6 @@ this.label19 = new System.Windows.Forms.Label(); this.txt_banreason = new System.Windows.Forms.TextBox(); this.label18 = new System.Windows.Forms.Label(); - this.lst_bans = new System.Windows.Forms.ListBox(); this.tabControl1 = new System.Windows.Forms.TabControl(); this.tabPage3 = new System.Windows.Forms.TabPage(); this.btn_OpenLocalDB = new System.Windows.Forms.Button(); @@ -115,7 +116,6 @@ this.label7 = new System.Windows.Forms.Label(); this.label6 = new System.Windows.Forms.Label(); this.label5 = new System.Windows.Forms.Label(); - this.groupBox7 = new System.Windows.Forms.GroupBox(); this.tabControl.SuspendLayout(); this.tabPage1.SuspendLayout(); this.tabPage2.SuspendLayout(); @@ -125,12 +125,12 @@ this.groupBox4.SuspendLayout(); this.groupBox3.SuspendLayout(); this.tabPage6.SuspendLayout(); + this.groupBox7.SuspendLayout(); this.groupBox6.SuspendLayout(); this.groupBox5.SuspendLayout(); this.tabControl1.SuspendLayout(); this.tabPage3.SuspendLayout(); this.tabPage4.SuspendLayout(); - this.groupBox7.SuspendLayout(); this.SuspendLayout(); // // itemListBanned @@ -675,6 +675,25 @@ this.tabPage6.Text = "Ban Manager"; this.tabPage6.UseVisualStyleBackColor = true; // + // groupBox7 + // + this.groupBox7.Controls.Add(this.lst_bans); + this.groupBox7.Location = new System.Drawing.Point(6, 23); + this.groupBox7.Name = "groupBox7"; + this.groupBox7.Size = new System.Drawing.Size(284, 352); + this.groupBox7.TabIndex = 13; + this.groupBox7.TabStop = false; + this.groupBox7.Text = "Ban List"; + // + // lst_bans + // + this.lst_bans.FormattingEnabled = true; + this.lst_bans.Location = new System.Drawing.Point(6, 19); + this.lst_bans.Name = "lst_bans"; + this.lst_bans.Size = new System.Drawing.Size(272, 329); + this.lst_bans.TabIndex = 0; + this.lst_bans.SelectedIndexChanged += new System.EventHandler(this.lst_bans_SelectedIndexChanged); + // // groupBox6 // this.groupBox6.Controls.Add(this.lbl_newbanstatus); @@ -839,15 +858,6 @@ this.label18.TabIndex = 4; this.label18.Text = "Name:"; // - // lst_bans - // - this.lst_bans.FormattingEnabled = true; - this.lst_bans.Location = new System.Drawing.Point(6, 19); - this.lst_bans.Name = "lst_bans"; - this.lst_bans.Size = new System.Drawing.Size(272, 329); - this.lst_bans.TabIndex = 0; - this.lst_bans.SelectedIndexChanged += new System.EventHandler(this.lst_bans_SelectedIndexChanged); - // // tabControl1 // this.tabControl1.Controls.Add(this.tabPage3); @@ -992,16 +1002,6 @@ this.label5.TabIndex = 0; this.label5.Text = "Hostname:"; // - // groupBox7 - // - this.groupBox7.Controls.Add(this.lst_bans); - this.groupBox7.Location = new System.Drawing.Point(6, 23); - this.groupBox7.Name = "groupBox7"; - this.groupBox7.Size = new System.Drawing.Size(284, 352); - this.groupBox7.TabIndex = 13; - this.groupBox7.TabStop = false; - this.groupBox7.Text = "Ban List"; - // // TShockDBEditor // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -1011,6 +1011,7 @@ this.Controls.Add(this.tabControl); this.Name = "TShockDBEditor"; this.Text = "TShockDBEditor"; + this.Load += new System.EventHandler(this.TShockDBEditor_Load); this.tabControl.ResumeLayout(false); this.tabPage1.ResumeLayout(false); this.tabPage1.PerformLayout(); @@ -1026,6 +1027,7 @@ this.groupBox3.ResumeLayout(false); this.groupBox3.PerformLayout(); this.tabPage6.ResumeLayout(false); + this.groupBox7.ResumeLayout(false); this.groupBox6.ResumeLayout(false); this.groupBox6.PerformLayout(); this.groupBox5.ResumeLayout(false); @@ -1034,7 +1036,6 @@ this.tabPage3.ResumeLayout(false); this.tabPage4.ResumeLayout(false); this.tabPage4.PerformLayout(); - this.groupBox7.ResumeLayout(false); this.ResumeLayout(false); } diff --git a/DBEditor/Main.cs b/DBEditor/Main.cs index bf3b5a7f..d5a98e2c 100644 --- a/DBEditor/Main.cs +++ b/DBEditor/Main.cs @@ -18,6 +18,7 @@ namespace TShockDBEditor { public OpenFileDialog dialog = new OpenFileDialog(); public List groups = new List(); + public static List CommandList = new List(); public IDbConnection DB; public string dbtype = ""; @@ -25,7 +26,6 @@ namespace TShockDBEditor { InitializeComponent(); Itemlist.AddItems(); - Commandlist.AddCommands(); dialog.FileOk += new CancelEventHandler(dialog_FileOk); dialog.Filter = "SQLite Database (*.sqlite)|*.sqlite"; } @@ -50,6 +50,7 @@ namespace TShockDBEditor itemListBanned.Items.Add(reader.Get("ItemName")); } } + using (var com = DB.CreateCommand()) { com.CommandText = @@ -68,6 +69,21 @@ namespace TShockDBEditor lst_newusergrplist.Items.Add(reader.Get("GroupName")); } } + + using (var reader = com.ExecuteReader()) + { + while (reader.Read()) + { + foreach (string command in reader.Get("Commands").Split(',')) + { + if (!lst_groupList.Items.Contains(command)) + if (!CommandList.Contains(command)) + CommandList.Add(command); + } + } + } + + TShockCommandsList.AddRemainingTShockCommands(); } using (var com = DB.CreateCommand()) { @@ -203,7 +219,10 @@ namespace TShockDBEditor private void lst_groupList_SelectedIndexChanged(object sender, EventArgs e) { - UpdateGroupIndex(lst_groupList.SelectedIndex); + if ((string)lst_groupList.SelectedItem != "superadmin") + UpdateGroupIndex(lst_groupList.SelectedIndex); + else + lst_groupList.SelectedIndex = -1; } private void UpdateGroupIndex(int index) @@ -238,10 +257,10 @@ namespace TShockDBEditor if (lbl_grpchild.Text == "") lbl_grpchild.Text = "none"; - for (int i = 0; i < Commandlist.CommandList.Count; i++) + for (int i = 0; i < CommandList.Count; i++) { - if (!lst_AvailableCmds.Items.Contains(Commandlist.CommandList[i])) - lst_bannedCmds.Items.Add(Commandlist.CommandList[i]); + if (!lst_AvailableCmds.Items.Contains(CommandList[i])) + lst_bannedCmds.Items.Add(CommandList[i]); } } } @@ -535,6 +554,8 @@ namespace TShockDBEditor #endregion + #region UserTab + private void lst_userlist_SelectedIndexChanged(object sender, EventArgs e) { txt_username.Text = "None Selected"; @@ -728,5 +749,12 @@ namespace TShockDBEditor return bytes.Aggregate("", (s, b) => s + b.ToString("X2")); } } + + #endregion + + private void TShockDBEditor_Load(object sender, EventArgs e) + { + + } } } \ No newline at end of file diff --git a/DBEditor/TShockDBEditor.csproj b/DBEditor/TShockDBEditor.csproj index 2e60af61..3d95e73b 100644 --- a/DBEditor/TShockDBEditor.csproj +++ b/DBEditor/TShockDBEditor.csproj @@ -26,7 +26,6 @@ true 0 1.0.0.%2a - false false true @@ -75,8 +74,8 @@ - + Form diff --git a/README.md b/README.md index 3b665774..ce8a1414 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -####Do not download the latest master! - TShock is a server modification based upon High6's mod API that allows for basic server administration commands. __Constant builds__: http://ci.tshock.co/ diff --git a/TShockAPI/BackupManager.cs b/TShockAPI/BackupManager.cs index 3d776ce2..127fb185 100644 --- a/TShockAPI/BackupManager.cs +++ b/TShockAPI/BackupManager.cs @@ -18,10 +18,7 @@ along with this program. If not, see . using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; using System.Threading; using Terraria; diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs index 5cfd8cb0..e7a2c032 100644 --- a/TShockAPI/Commands.cs +++ b/TShockAPI/Commands.cs @@ -1,4 +1,4 @@ -/* +/* TShock, a server mod for Terraria Copyright (C) 2011 The TShock Team @@ -17,12 +17,11 @@ along with this program. If not, see . */ using System; using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Text.RegularExpressions; -using System.Linq; using System.Diagnostics; +using System.IO; +using System.Linq; using System.Net; +using System.Text; using System.Threading; using Microsoft.Xna.Framework; using Terraria; @@ -138,13 +137,16 @@ namespace TShockAPI ChatCommands.Add(new Command("warp", UseWarp, "warp")); ChatCommands.Add(new Command("managewarp", SetWarp, "setwarp")); ChatCommands.Add(new Command("managewarp", DeleteWarp, "delwarp")); - ChatCommands.Add(new Command("managegroup", AddGroup, "addGroup")); - ChatCommands.Add(new Command("managegroup", DeleteGroup, "delGroup")); - ChatCommands.Add(new Command("managegroup", ModifyGroup, "modGroup")); + ChatCommands.Add(new Command("managewarp", HideWarp, "hidewarp")); + ChatCommands.Add(new Command("managegroup", AddGroup, "addgroup")); + ChatCommands.Add(new Command("managegroup", DeleteGroup, "delgroup")); + ChatCommands.Add(new Command("managegroup", ModifyGroup, "modgroup")); + ChatCommands.Add(new Command("manageitem", AddItem, "additem")); + ChatCommands.Add(new Command("manageitem", DeleteItem, "delitem")); ChatCommands.Add(new Command("cfg", SetSpawn, "setspawn")); ChatCommands.Add(new Command("cfg", Reload, "reload")); - ChatCommands.Add(new Command("cfg", DebugConfiguration, "debug-config")); - ChatCommands.Add(new Command("cfg", Password, "password")); + ChatCommands.Add(new Command("cfg", ShowConfiguration, "showconfig")); + ChatCommands.Add(new Command("cfg", ServerPassword, "serverpassword")); ChatCommands.Add(new Command("cfg", Save, "save")); ChatCommands.Add(new Command("cfg", MaxSpawns, "maxspawns")); ChatCommands.Add(new Command("cfg", SpawnRate, "spawnrate")); @@ -370,7 +372,7 @@ namespace TShockAPI catch (UserManagerException ex) { args.Player.SendMessage("Sorry, an error occured: " + ex.Message, Color.Green); - Log.ConsoleError("RegisterUser returned an error: " + ex.ToString()); + Log.ConsoleError("RegisterUser returned an error: " + ex); } } @@ -383,7 +385,7 @@ namespace TShockAPI var user = new User(); user.Name = args.Parameters[0].ToLower(); user.Password = args.Parameters[1]; - user.Group = "default"; // FIXME -- we should get this from the DB. + 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 { @@ -406,7 +408,7 @@ namespace TShockAPI catch (UserManagerException ex) { args.Player.SendMessage("Sorry, an error occured: " + ex.Message, Color.Green); - Log.ConsoleError("RegisterUser returned an error: " + ex.ToString()); + Log.ConsoleError("RegisterUser returned an error: " + ex); } } @@ -414,15 +416,18 @@ namespace TShockAPI private static void ManageUsers(CommandArgs args) { - if (args.Parameters.Count < 2) - { - args.Player.SendMessage("Syntax: /user [group]"); - args.Player.SendMessage("Note: Passwords are stored with SHA512 hashing. To reset a user's password, remove and re-add them."); - return; - } + // This guy needs to go away for the help later on to take effect. + + //if (args.Parameters.Count < 2) + //{ + // args.Player.SendMessage("Syntax: /user [group]"); + // args.Player.SendMessage("Note: Passwords are stored with SHA512 hashing. To reset a user's password, remove and re-add them."); + // return; + //} string subcmd = args.Parameters[0]; + // Add requires a username:password pair/ip address and a group specified. if (subcmd == "add") { var namepass = args.Parameters[1].Split(':'); @@ -469,6 +474,7 @@ namespace TShockAPI Log.ConsoleError(ex.ToString()); } } + // User deletion requires a username else if (subcmd == "del" && args.Parameters.Count == 2) { var user = new User(); @@ -489,13 +495,86 @@ namespace TShockAPI Log.ConsoleError(ex.ToString()); } } + // Password changing requires a username, and a new password to set + else if (subcmd == "password") + { + var user = new User(); + user.Name = args.Parameters[1]; + + try + { + + if (args.Parameters.Count == 3) + { + args.Player.SendMessage("Changed the password of " + user.Name + "!", Color.Green); + TShock.Users.SetUserPassword(user, args.Parameters[2]); + Log.ConsoleInfo(args.Player.Name + " changed the password of Account " + user.Name); + } + else + { + args.Player.SendMessage("Invalid user password syntax. Try /user help.", Color.Red); + } + } + catch (UserManagerException ex) + { + args.Player.SendMessage(ex.Message, Color.Green); + Log.ConsoleError(ex.ToString()); + } + } + // Group changing requires a username or IP address, and a new group to set + else if (subcmd == "group") + { + var user = new User(); + if (args.Parameters[1].Contains(".")) + user.Address = args.Parameters[1]; + else + user.Name = args.Parameters[1]; + + try + { + + if (args.Parameters.Count == 3) + { + if (!string.IsNullOrEmpty(user.Address)) + { + args.Player.SendMessage("IP Address " + user.Address + " has been changed to group " + args.Parameters[2] + "!", Color.Green); + TShock.Users.SetUserGroup(user, args.Parameters[2]); + Log.ConsoleInfo(args.Player.Name + " changed IP Address " + user.Address + " to group " + args.Parameters[2]); + } + else + { + args.Player.SendMessage("Account " + user.Name + " has been changed to group " + args.Parameters[2] + "!", Color.Green); + TShock.Users.SetUserGroup(user, args.Parameters[2]); + Log.ConsoleInfo(args.Player.Name + " changed Account " + user.Name + " to group " + args.Parameters[2]); + } + } + else + { + args.Player.SendMessage("Invalid user group syntax. Try /user help.", Color.Red); + } + } + catch (UserManagerException ex) + { + args.Player.SendMessage(ex.Message, Color.Green); + Log.ConsoleError(ex.ToString()); + } + } + else if (subcmd == "help") + { + args.Player.SendMessage("Help for user subcommands:"); + args.Player.SendMessage("/user add username:password group -- Adds a specified user"); + args.Player.SendMessage("/user del username -- Removes a specified user"); + args.Player.SendMessage("/user password username newpassword -- Changes a user's password"); + args.Player.SendMessage("/user group username newgroup -- Changes a user's group"); + } else { - args.Player.SendMessage("Invalid syntax. Try /user help.", Color.Red); + args.Player.SendMessage("Invalid user syntax. Try /user help.", Color.Red); } } #endregion + #region Player Management Commands private static void GrabUserIP(CommandArgs args) @@ -626,7 +705,18 @@ namespace TShockAPI if (TShock.Bans.RemoveBan(ban.IP)) args.Player.SendMessage(string.Format("Unbanned {0} ({1})!", ban.Name, ban.IP), Color.Red); else - args.Player.SendMessage(string.Format("Failed to Unbanned {0} ({1})!", ban.Name, ban.IP), Color.Red); + args.Player.SendMessage(string.Format("Failed to unban {0} ({1})!", ban.Name, ban.IP), Color.Red); + } + else if (!TShock.Config.EnableBanOnUsernames) + { + ban = TShock.Bans.GetBanByIp(plStr); + + if (ban == null) + args.Player.SendMessage(string.Format("Failed to unban {0}, not found.", args.Parameters[0]), Color.Red); + else if (TShock.Bans.RemoveBan(ban.IP)) + args.Player.SendMessage(string.Format("Unbanned {0} ({1})!", ban.Name, ban.IP), Color.Red); + else + args.Player.SendMessage(string.Format("Failed to unban {0} ({1})!", ban.Name, ban.IP), Color.Red); } else { @@ -695,7 +785,7 @@ namespace TShockAPI if (TShock.Bans.RemoveBan(ban.IP)) args.Player.SendMessage(string.Format("Unbanned {0} ({1})!", ban.Name, ban.IP), Color.Red); else - args.Player.SendMessage(string.Format("Failed to Unbanned {0} ({1})!", ban.Name, ban.IP), Color.Red); + args.Player.SendMessage(string.Format("Failed to unban {0} ({1})!", ban.Name, ban.IP), Color.Red); } else { @@ -859,7 +949,7 @@ namespace TShockAPI return; } NPC eater = Tools.GetNPCById(13); - TSPlayer.Server.SpawnNPC(eater.type, eater.name, amount, (int)args.Player.TileX, (int)args.Player.TileY); + TSPlayer.Server.SpawnNPC(eater.type, eater.name, amount, args.Player.TileX, args.Player.TileY); Tools.Broadcast(string.Format("{0} has spawned eater of worlds {1} times!", args.Player.Name, amount)); } @@ -878,7 +968,7 @@ namespace TShockAPI } NPC eye = Tools.GetNPCById(4); TSPlayer.Server.SetTime(false, 0.0); - TSPlayer.Server.SpawnNPC(eye.type, eye.name, amount, (int)args.Player.TileX, (int)args.Player.TileY); + TSPlayer.Server.SpawnNPC(eye.type, eye.name, amount, args.Player.TileX, args.Player.TileY); Tools.Broadcast(string.Format("{0} has spawned eye {1} times!", args.Player.Name, amount)); } @@ -896,7 +986,7 @@ namespace TShockAPI return; } NPC king = Tools.GetNPCById(50); - TSPlayer.Server.SpawnNPC(king.type, king.name, amount, (int)args.Player.TileX, (int)args.Player.TileY); + TSPlayer.Server.SpawnNPC(king.type, king.name, amount, args.Player.TileX, args.Player.TileY); Tools.Broadcast(string.Format("{0} has spawned king slime {1} times!", args.Player.Name, amount)); } @@ -915,7 +1005,7 @@ namespace TShockAPI } NPC skeletron = Tools.GetNPCById(35); TSPlayer.Server.SetTime(false, 0.0); - TSPlayer.Server.SpawnNPC(skeletron.type, skeletron.name, amount, (int)args.Player.TileX, (int)args.Player.TileY); + TSPlayer.Server.SpawnNPC(skeletron.type, skeletron.name, amount, args.Player.TileX, args.Player.TileY); Tools.Broadcast(string.Format("{0} has spawned skeletron {1} times!", args.Player.Name, amount)); } @@ -937,10 +1027,10 @@ namespace TShockAPI NPC king = Tools.GetNPCById(50); NPC skeletron = Tools.GetNPCById(35); TSPlayer.Server.SetTime(false, 0.0); - TSPlayer.Server.SpawnNPC(eater.type, eater.name, amount, (int)args.Player.TileX, (int)args.Player.TileY); - TSPlayer.Server.SpawnNPC(eye.type, eye.name, amount, (int)args.Player.TileX, (int)args.Player.TileY); - TSPlayer.Server.SpawnNPC(king.type, king.name, amount, (int)args.Player.TileX, (int)args.Player.TileY); - TSPlayer.Server.SpawnNPC(skeletron.type, skeletron.name, amount, (int)args.Player.TileX, (int)args.Player.TileY); + TSPlayer.Server.SpawnNPC(eater.type, eater.name, amount, args.Player.TileX, args.Player.TileY); + TSPlayer.Server.SpawnNPC(eye.type, eye.name, amount, args.Player.TileX, args.Player.TileY); + TSPlayer.Server.SpawnNPC(king.type, king.name, amount, args.Player.TileX, args.Player.TileY); + TSPlayer.Server.SpawnNPC(skeletron.type, skeletron.name, amount, args.Player.TileX, args.Player.TileY); Tools.Broadcast(string.Format("{0} has spawned all bosses {1} times!", args.Player.Name, amount)); } @@ -977,7 +1067,7 @@ namespace TShockAPI var npc = npcs[0]; if (npc.type >= 1 && npc.type < Main.maxNPCTypes) { - TSPlayer.Server.SpawnNPC(npc.type, npc.name, amount, (int)args.Player.TileX, (int)args.Player.TileY, 50, 20); + TSPlayer.Server.SpawnNPC(npc.type, npc.name, amount, args.Player.TileX, args.Player.TileY, 50, 20); Tools.Broadcast(string.Format("{0} was spawned {1} time(s).", npc.name, amount)); } else @@ -1128,6 +1218,31 @@ namespace TShockAPI args.Player.SendMessage("Invalid syntax! Proper syntax: /delwarp [name]", Color.Red); } + private static void HideWarp(CommandArgs args) + { + if (args.Parameters.Count > 1) + { + string warpName = String.Join(" ", args.Parameters); + bool state = false; + if (Boolean.TryParse(args.Parameters[1], out state)) + { + if (TShock.Warps.HideWarp(args.Parameters[0], state)) + { + if (state) + args.Player.SendMessage("Made warp " + warpName + " private", Color.Yellow); + else + args.Player.SendMessage("Made warp " + warpName + " public", Color.Yellow); + } + else + args.Player.SendMessage("Could not find specified warp", Color.Red); + } + else + args.Player.SendMessage("Invalid syntax! Proper syntax: /hidewarp [name] ", Color.Red); + } + else + args.Player.SendMessage("Invalid syntax! Proper syntax: /hidewarp [name] ", Color.Red); + } + private static void UseWarp(CommandArgs args) { if (args.Parameters.Count < 1) @@ -1156,7 +1271,7 @@ namespace TShockAPI page--; //Substract 1 as pages are parsed starting at 1 and not 0 } - var warps = TShock.Warps.ListAllWarps(Main.worldID.ToString()); + var warps = TShock.Warps.ListAllPublicWarps(Main.worldID.ToString()); //Check if they are trying to access a page that doesn't exist. int pagecount = warps.Count / pagelimit; @@ -1207,7 +1322,6 @@ namespace TShockAPI #endregion Teleport Commands - #region Group Management private static void AddGroup(CommandArgs args) @@ -1218,7 +1332,7 @@ namespace TShockAPI args.Parameters.RemoveAt(0); String permissions = String.Join(",", args.Parameters ); - String response = TShock.Groups.addGroup(groupname, permissions); + String response = TShock.Groups.AddGroup(groupname, permissions); if( response.Length > 0 ) args.Player.SendMessage(response, Color.Green); } @@ -1234,7 +1348,7 @@ namespace TShockAPI { String groupname = args.Parameters[0]; - String response = TShock.Groups.delGroup(groupname); + String response = TShock.Groups.DeleteGroup(groupname); if (response.Length > 0) args.Player.SendMessage(response, Color.Green); } @@ -1256,14 +1370,14 @@ namespace TShockAPI if (com.Equals("add")) { - String response = TShock.Groups.addPermission(groupname, args.Parameters); + String response = TShock.Groups.AddPermissions(groupname, args.Parameters); if (response.Length > 0) args.Player.SendMessage(response, Color.Green); return; } else if (com.Equals("del") || com.Equals("delete")) { - String response = TShock.Groups.delPermission(groupname, args.Parameters); + String response = TShock.Groups.DeletePermissions(groupname, args.Parameters); if (response.Length > 0) args.Player.SendMessage(response, Color.Green); return; @@ -1273,6 +1387,77 @@ namespace TShockAPI } #endregion Group Management + + #region Item Management + + private static void AddItem(CommandArgs args) + { + if (args.Parameters.Count > 0) + { + var items = Tools.GetItemByIdOrName(args.Parameters[0]); + if (items.Count == 0) + { + args.Player.SendMessage("Invalid item type!", Color.Red); + } + else if (items.Count > 1) + { + args.Player.SendMessage(string.Format("More than one ({0}) item matched!", items.Count), Color.Red); + } + else + { + var item = items[0]; + if (item.type >= 1) + { + TShock.Itembans.AddNewBan(args.Parameters[0]); + args.Player.SendMessage(Tools.GetItemByIdOrName(args.Parameters[0])[0].name + " has been banned.", Color.Green); + } + else + { + args.Player.SendMessage("Invalid item type!", Color.Red); + } + } + } + else + { + args.Player.SendMessage("Invalid use: /addItem \"item name\" or /addItem ##", Color.Red); + } + } + + private static void DeleteItem(CommandArgs args) + { + if (args.Parameters.Count > 0) + { + var items = Tools.GetItemByIdOrName(args.Parameters[0]); + if (items.Count == 0) + { + args.Player.SendMessage("Invalid item type!", Color.Red); + } + else if (items.Count > 1) + { + args.Player.SendMessage(string.Format("More than one ({0}) item matched!", items.Count), Color.Red); + } + else + { + var item = items[0]; + if (item.type >= 1) + { + TShock.Itembans.RemoveBan(args.Parameters[0]); + args.Player.SendMessage(Tools.GetItemByIdOrName(args.Parameters[0])[0].name + " has been unbanned.", Color.Green); + } + else + { + args.Player.SendMessage("Invalid item type!", Color.Red); + } + } + } + else + { + args.Player.SendMessage("Invalid use: /delItem \"item name\" or /delItem ##", Color.Red); + } + } + + #endregion Item Management + #region Server Config Commands private static void SetSpawn(CommandArgs args) @@ -1285,7 +1470,7 @@ namespace TShockAPI SaveWorld.Start(); } - private static void DebugConfiguration(CommandArgs args) + private static void ShowConfiguration(CommandArgs args) { args.Player.SendMessage("TShock Config:"); string lineOne = string.Format("BanCheater : {0}, KickCheater : {1}, BanGriefer : {2}, KickGriefer : {3}", @@ -1313,7 +1498,7 @@ namespace TShockAPI args.Player.SendMessage("Configuration reload complete. Some changes may require server restart."); } - private static void Password(CommandArgs args) + private static void ServerPassword(CommandArgs args) { if (args.Parameters.Count != 1) { @@ -1465,10 +1650,10 @@ namespace TShockAPI { foreach (Region r in TShock.Regions.Regions) { - args.Player.SendMessage(r.RegionName + ": P: " + r.DisableBuild + " X: " + r.RegionArea.X + " Y: " + r.RegionArea.Y + " W: " + r.RegionArea.Width + " H: " + r.RegionArea.Height); - foreach (int s in r.RegionAllowedIDs) + args.Player.SendMessage(r.Name + ": P: " + r.DisableBuild + " X: " + r.Area.X + " Y: " + r.Area.Y + " W: " + r.Area.Width + " H: " + r.Area.Height); + foreach (int s in r.AllowedIDs) { - args.Player.SendMessage(r.RegionName + ": " + s); + args.Player.SendMessage(r.Name + ": " + s); } } } @@ -1673,7 +1858,7 @@ namespace TShockAPI var nameslist = new List(); for (int i = 0; i < pagelimit && i + (page * pagelimit) < regions.Count; i++) { - nameslist.Add(regions[i].RegionName); + nameslist.Add(regions[i].Name); } //convert the list to an array for joining @@ -1918,8 +2103,8 @@ namespace TShockAPI else { var ply = players[0]; - args.Player.SendMessage("Annoying " + ply.Name + " for " + annoy.ToString() + " seconds."); - (new Thread(new ParameterizedThreadStart(ply.Whoopie))).Start(annoy); + args.Player.SendMessage("Annoying " + ply.Name + " for " + annoy + " seconds."); + (new Thread(ply.Whoopie)).Start(annoy); } } #endregion General Commands @@ -2010,7 +2195,7 @@ namespace TShockAPI if (itemAmount == 0 || itemAmount > item.maxStack) itemAmount = item.maxStack; args.Player.GiveItem(item.type, item.name, item.width, item.height, itemAmount); - args.Player.SendMessage(string.Format("Gave {0} {1}(s).", itemAmount.ToString(), item.name)); + args.Player.SendMessage(string.Format("Gave {0} {1}(s).", itemAmount, item.name)); } else { @@ -2080,8 +2265,8 @@ namespace TShockAPI if (itemAmount == 0 || itemAmount > item.maxStack) itemAmount = item.maxStack; plr.GiveItem(item.type, item.name, item.width, item.height, itemAmount); - args.Player.SendMessage(string.Format("Gave {0} {1} {2}(s).", plr.Name, itemAmount.ToString(), item.name)); - plr.SendMessage(string.Format("{0} gave you {1} {2}(s).", args.Player.Name, itemAmount.ToString(), item.name)); + args.Player.SendMessage(string.Format("Gave {0} {1} {2}(s).", plr.Name, itemAmount, item.name)); + plr.SendMessage(string.Format("{0} gave you {1} {2}(s).", args.Player.Name, itemAmount, item.name)); } else { diff --git a/TShockAPI/ConfigFile.cs b/TShockAPI/ConfigFile.cs index 909c3a16..c48d88d3 100644 --- a/TShockAPI/ConfigFile.cs +++ b/TShockAPI/ConfigFile.cs @@ -27,9 +27,9 @@ namespace TShockAPI public int DefaultMaximumSpawns = 4; public int DefaultSpawnRate = 700; public int ServerPort = 7777; - public bool EnableWhitelist = false; - public bool InfiniteInvasion = false; - public bool AlwaysPvP = false; + public bool EnableWhitelist; + public bool InfiniteInvasion; + public bool AlwaysPvP; public bool KickCheaters = true; public bool BanCheaters = true; public bool KickGriefers = true; @@ -44,24 +44,24 @@ namespace TShockAPI public string DistributationAgent = "facepunch"; public int MaxSlots = 8; public bool RangeChecks = true; - public bool SpamChecks = false; - public bool DisableBuild = false; + public bool SpamChecks; + public bool DisableBuild; public int TileThreshold = 60; - public float[] AdminChatRGB = { 255, 0, 0 }; + public float[] SuperAdminChatRGB = { 255, 0, 0 }; public string AdminChatPrefix = "(Admin) "; public bool AdminChatEnabled = true; - public int PvpThrottle = 0; + public int PvpThrottle; - public int BackupInterval = 0; + public int BackupInterval; public int BackupKeepFor = 60; - public bool RememberLeavePos = false; + public bool RememberLeavePos; - public bool HardcoreOnly = false; - public bool KickOnHardcoreDeath = false; - public bool BanOnHardcoreDeath = false; + public bool HardcoreOnly; + public bool KickOnHardcoreDeath; + public bool BanOnHardcoreDeath; public bool AutoSave = true; @@ -93,12 +93,25 @@ namespace TShockAPI public string TileKillAbuseReason = "Tile Kill abuse ({0})"; public string HardcoreBanReason = "Death results in a ban"; public string HardcoreKickReason = "Death results in a kick"; + public string ProjectileAbuseReason = "Projectile abuse"; - public bool EnableDNSHostResolution = false; + public bool EnableDNSHostResolution; - public bool EnableBanOnUsernames = false; + public bool EnableBanOnUsernames; public bool EnableAntiLag = true; + + public string DefaultRegistrationGroupName = "default"; + + public bool DisableSpewLogs = true; + + /// + /// Valid types are "sha512", "sha256", "md5" + /// + public string HashAlgorithm = "sha512"; + + public bool BufferPackets = false; + public static ConfigFile Read(string path) { if (!File.Exists(path)) diff --git a/TShockAPI/DB/BanManager.cs b/TShockAPI/DB/BanManager.cs index b4beb54d..e6c17b61 100644 --- a/TShockAPI/DB/BanManager.cs +++ b/TShockAPI/DB/BanManager.cs @@ -1,4 +1,4 @@ -/* +/* TShock, a server mod for Terraria Copyright (C) 2011 The TShock Team @@ -17,12 +17,9 @@ along with this program. If not, see . */ using System; -using System.Collections.Generic; using System.Data; using System.IO; -using System.Text; -using Community.CsharpSqlite.SQLiteClient; -using TShockAPI.DB; +using MySql.Data.MySqlClient; namespace TShockAPI.DB { @@ -34,45 +31,38 @@ namespace TShockAPI.DB { database = db; - using (var com = database.CreateCommand()) + var table = new SqlTable("Bans", + new SqlColumn("IP", MySqlDbType.String, 16) { Primary = true }, + new SqlColumn("Name", MySqlDbType.Text), + new SqlColumn("Reason", MySqlDbType.Text) + ); + var creator = new SqlTableCreator(db, db.GetSqlType() == SqlType.Sqlite ? (IQueryBuilder)new SqliteQueryCreator() : new MysqlQueryCreator()); + creator.EnsureExists(table); + + String file = Path.Combine(TShock.SavePath, "bans.txt"); + if (File.Exists(file)) { - if (TShock.Config.StorageType.ToLower() == "sqlite") - com.CommandText = - "CREATE TABLE IF NOT EXISTS 'Bans' ('IP' TEXT PRIMARY KEY, 'Name' TEXT, 'Reason' TEXT);"; - else if (TShock.Config.StorageType.ToLower() == "mysql") - com.CommandText = - "CREATE TABLE IF NOT EXISTS Bans (IP VARCHAR(255) PRIMARY, Name VARCHAR(255), Reason VARCHAR(255));"; - - com.ExecuteNonQuery(); - - String file = Path.Combine( TShock.SavePath, "bans.txt" ); - if (File.Exists(file)) + using (StreamReader sr = new StreamReader(file)) { - using (StreamReader sr = new StreamReader(file)) + String line; + while ((line = sr.ReadLine()) != null) { - String line; - while ((line = sr.ReadLine()) != null) - { - String[] info = line.Split('|'); - if (TShock.Config.StorageType.ToLower() == "sqlite") - com.CommandText = "INSERT OR IGNORE INTO Bans (IP, Name, Reason) VALUES (@ip, @name, @reason);"; - else if (TShock.Config.StorageType.ToLower() == "mysql") - com.CommandText = "INSERT IGNORE INTO Bans SET IP=@ip, Name=@name, Reason=@reason;"; - com.AddParameter("@ip", info[0].Trim()); - com.AddParameter("@name", info[1].Trim()); - com.AddParameter("@reason", info[2].Trim()); - com.ExecuteNonQuery(); - com.Parameters.Clear(); - } + String[] info = line.Split('|'); + string query; + if (TShock.Config.StorageType.ToLower() == "sqlite") + query = "INSERT OR IGNORE INTO Bans (IP, Name, Reason) VALUES (@0, @1, @2);"; + else + query = "INSERT IGNORE INTO Bans SET IP=@0, Name=@1, Reason=@2;"; + db.Query(query, info[0].Trim(), info[1].Trim(), info[2].Trim()); } - String path = Path.Combine(TShock.SavePath, "old_configs"); - String file2 = Path.Combine(path, "bans.txt"); - if (!Directory.Exists(path)) - System.IO.Directory.CreateDirectory(path); - if (File.Exists(file2)) - File.Delete(file2); - File.Move(file, file2); } + String path = Path.Combine(TShock.SavePath, "old_configs"); + String file2 = Path.Combine(path, "bans.txt"); + if (!Directory.Exists(path)) + Directory.CreateDirectory(path); + if (File.Exists(file2)) + File.Delete(file2); + File.Move(file, file2); } } @@ -80,17 +70,10 @@ namespace TShockAPI.DB { try { - using (var com = database.CreateCommand()) + using (var reader = database.QueryReader("SELECT * FROM Bans WHERE IP=@0", ip)) { - com.CommandText = "SELECT * FROM Bans WHERE IP=@ip"; - com.AddParameter("@ip", ip); - using (var reader = com.ExecuteReader()) - { - if (reader.Read()) - return new Ban((string)reader["IP"], (string)reader["Name"], (string)reader["Reason"]); - - reader.Close(); - } + if (reader.Read()) + return new Ban(reader.Get("IP"), reader.Get("Name"), reader.Get("Reason")); } } catch (Exception ex) @@ -108,20 +91,14 @@ namespace TShockAPI.DB } try { - using (var com = database.CreateCommand()) + var namecol = casesensitive ? "Name" : "UPPER(Name)"; + if (!casesensitive) + name = name.ToUpper(); + using (var reader = database.QueryReader("SELECT * FROM Bans WHERE " + namecol + "=@0", name)) { - var namecol = casesensitive ? "Name" : "UPPER(Name)"; - if (!casesensitive) - name = name.ToUpper(); - com.CommandText = "SELECT * FROM Bans WHERE " + namecol + "=@name"; - com.AddParameter("@name", name); - using (var reader = com.ExecuteReader()) - { - if (reader.Read()) - return new Ban((string)reader["IP"], (string)reader["Name"], (string)reader["Reason"]); + if (reader.Read()) + return new Ban(reader.Get("IP"), reader.Get("Name"), reader.Get("Reason")); - reader.Close(); - } } } catch (Exception ex) @@ -135,15 +112,7 @@ namespace TShockAPI.DB { try { - using (var com = database.CreateCommand()) - { - com.CommandText = "INSERT INTO Bans (IP, Name, Reason) VALUES (@ip, @name, @reason);"; - com.AddParameter("@ip", ip); - com.AddParameter("@name", name); - com.AddParameter("@reason", reason); - com.ExecuteNonQuery(); - } - return true; + return database.Query("INSERT INTO Bans (IP, Name, Reason) VALUES (@0, @1, @2);", ip, name, reason) != 0; } catch (Exception ex) { @@ -156,13 +125,7 @@ namespace TShockAPI.DB { try { - using (var com = database.CreateCommand()) - { - com.CommandText = "DELETE FROM Bans WHERE IP=@ip"; - com.AddParameter("@ip", ip); - com.ExecuteNonQuery(); - return true; - } + return database.Query("DELETE FROM Bans WHERE IP=@0", ip) != 0; } catch (Exception ex) { @@ -174,12 +137,7 @@ namespace TShockAPI.DB { try { - using (var com = database.CreateCommand()) - { - com.CommandText = "DELETE FROM Bans"; - com.ExecuteNonQuery(); - return true; - } + return database.Query("DELETE FROM Bans") != 0; } catch (Exception ex) { diff --git a/TShockAPI/DB/DbExt.cs b/TShockAPI/DB/DbExt.cs deleted file mode 100644 index 13942cf2..00000000 --- a/TShockAPI/DB/DbExt.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data; -using System.Linq; -using System.Text; - -namespace TShockAPI.DB -{ - public static class DbExt - { - public static IDbDataParameter AddParameter(this IDbCommand command, string name, object data) - { - var parm = command.CreateParameter(); - parm.ParameterName = name; - parm.Value = data; - command.Parameters.Add(parm); - return parm; - } - - static Dictionary> ReadFuncs = new Dictionary>() - { - {typeof(bool), (s, i) => s.GetBoolean(i)}, - {typeof(byte), (s, i) => s.GetByte(i)}, - {typeof(Int16), (s, i) => s.GetInt16(i)}, - {typeof(Int32), (s, i) => s.GetInt32(i)}, - {typeof(Int64), (s, i) => s.GetInt64(i)}, - {typeof(string), (s, i) => s.GetString(i)}, - {typeof(decimal), (s, i) => s.GetDecimal(i)}, - {typeof(float), (s, i) => s.GetFloat(i)}, - {typeof(double), (s, i) => s.GetDouble(i)}, - }; - - public static T Get(this IDataReader reader, string column) - { - return reader.Get(reader.GetOrdinal(column)); - } - - public static T Get(this IDataReader reader, int column) - { - if (ReadFuncs.ContainsKey(typeof(T))) - return (T)ReadFuncs[typeof(T)](reader, column); - - throw new NotImplementedException(); - } - } -} diff --git a/TShockAPI/DB/GroupManager.cs b/TShockAPI/DB/GroupManager.cs index bd0edb4e..b086822c 100644 --- a/TShockAPI/DB/GroupManager.cs +++ b/TShockAPI/DB/GroupManager.cs @@ -1,10 +1,9 @@ using System; -using System.IO; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Data; -using Community.CsharpSqlite.SQLiteClient; +using System.IO; +using System.Linq; +using MySql.Data.MySqlClient; namespace TShockAPI.DB { @@ -18,17 +17,13 @@ namespace TShockAPI.DB { database = db; - using (var com = database.CreateCommand()) - { - if (TShock.Config.StorageType.ToLower() == "sqlite") - com.CommandText = - "CREATE TABLE IF NOT EXISTS 'GroupList' ('GroupName' TEXT PRIMARY KEY, 'Commands' TEXT, 'OrderBy' TEXT);"; - else if (TShock.Config.StorageType.ToLower() == "mysql") - com.CommandText = - "CREATE TABLE IF NOT EXISTS GroupList (GroupName VARCHAR(255) PRIMARY, Commands VARCHAR(255), OrderBy VARCHAR(255));"; - - com.ExecuteNonQuery(); - } + var table = new SqlTable("GroupList", + new SqlColumn("GroupName", MySqlDbType.VarChar, 32) { Primary = true }, + new SqlColumn("Commands", MySqlDbType.Text), + new SqlColumn("ChatColor", MySqlDbType.Text) + ); + var creator = new SqlTableCreator(db, db.GetSqlType() == SqlType.Sqlite ? (IQueryBuilder)new SqliteQueryCreator() : new MysqlQueryCreator()); + creator.EnsureExists(table); //Add default groups AddGroup("trustedadmin", "admin,maintenance,cfg,butcher,item,heal,immunetoban,ignorecheatdetection,ignoregriefdetection,usebanneditem,manageusers"); @@ -50,35 +45,28 @@ namespace TShockAPI.DB String[] info = line.Split(' '); String comms = ""; int size = info.Length; - int test = 0; - bool hasOrder = int.TryParse(info[info.Length - 1], out test); - if (hasOrder) - size = info.Length - 1; for (int i = 1; i < size; i++) { if (!comms.Equals("")) comms = comms + ","; comms = comms + info[i].Trim(); } - using (var com = database.CreateCommand()) - { - if (TShock.Config.StorageType.ToLower() == "sqlite") - com.CommandText = "INSERT OR IGNORE INTO GroupList (GroupName, Commands, OrderBy) VALUES (@groupname, @commands, @order);"; - else if (TShock.Config.StorageType.ToLower() == "mysql") - com.CommandText = "INSERT IGNORE INTO GroupList SET GroupName=@groupname, Commands=@commands, OrderBy=@order;"; - com.AddParameter("@groupname", info[0].Trim()); - com.AddParameter("@commands", comms); - com.AddParameter("@order", hasOrder ? info[info.Length - 1] : "0"); - com.ExecuteNonQuery(); - } + string query = ""; + if (TShock.Config.StorageType.ToLower() == "sqlite") + query = "INSERT OR IGNORE INTO GroupList (GroupName, Commands) VALUES (@0, @1);"; + else if (TShock.Config.StorageType.ToLower() == "mysql") + query = "INSERT IGNORE INTO GroupList SET GroupName=@0, Commands=@1;"; + + db.Query(query, info[0].Trim(), comms); + } } } String path = Path.Combine(TShock.SavePath, "old_configs"); String file2 = Path.Combine(path, "groups.txt"); if (!Directory.Exists(path)) - System.IO.Directory.CreateDirectory(path); + Directory.CreateDirectory(path); if (File.Exists(file2)) File.Delete(file2); File.Move(file, file2); @@ -86,27 +74,6 @@ namespace TShockAPI.DB } - /// - /// Adds group with name and permissions if it does not exist. - /// - /// name of group - /// permissions - public void AddGroup(string name, string commands) - { - using (var com = database.CreateCommand()) - { - if (TShock.Config.StorageType.ToLower() == "sqlite") - com.CommandText = - "INSERT OR IGNORE INTO GroupList (GroupName, Commands, OrderBy) VALUES (@groupname, @commands, @order);"; - else if (TShock.Config.StorageType.ToLower() == "mysql") - com.CommandText = - "INSERT IGNORE INTO GroupList SET GroupName=@groupname, Commands=@commands, OrderBy=@order;"; - com.AddParameter("@groupname", name); - com.AddParameter("@commands", commands); - com.AddParameter("@order", "0"); - com.ExecuteNonQuery(); - } - } public bool GroupExists(string group) { @@ -123,93 +90,72 @@ namespace TShockAPI.DB return false; } - public String addGroup(String name, String permissions) + /// + /// Adds group with name and permissions if it does not exist. + /// + /// name of group + /// permissions + public String AddGroup(String name, String permissions, String ChatColor = "255,255,255") { String message = ""; - if( GroupExists( name ) ) + if (GroupExists(name)) return "Error: Group already exists. Use /modGroup to change permissions."; - using (var com = database.CreateCommand()) - { - if (TShock.Config.StorageType.ToLower() == "sqlite") - com.CommandText = "INSERT OR IGNORE INTO GroupList (GroupName, Commands, OrderBy) VALUES (@groupname, @commands, @order);"; - else if (TShock.Config.StorageType.ToLower() == "mysql") - com.CommandText = "INSERT IGNORE INTO GroupList SET GroupName=@groupname, Commands=@commands, OrderBy=@order;"; - com.AddParameter("@groupname", name); - com.AddParameter("@commands", permissions); - com.AddParameter("@order", "0"); - if (com.ExecuteNonQuery() == 1) - message = "Group " + name + " has been created successfully."; - Group g = new Group(name); - g.permissions.Add(permissions); - groups.Add(g); - } + + string query = (TShock.Config.StorageType.ToLower() == "sqlite") ? + "INSERT OR IGNORE INTO GroupList (GroupName, Commands, ChatColor) VALUES (@0, @1, @2);" : + "INSERT IGNORE INTO GroupList SET GroupName=@0, Commands=@1"; + if (database.Query(query, name, permissions, ChatColor) == 1) + message = "Group " + name + " has been created successfully."; + Group g = new Group(name, null, ChatColor); + g.permissions.Add(permissions); + groups.Add(g); + return message; } - public String delGroup(String name) + public String DeleteGroup(String name) { String message = ""; if (!GroupExists(name)) return "Error: Group doesn't exists."; - using (var com = database.CreateCommand()) - { - com.CommandText = "Delete FROM GroupList WHERE GroupName=@groupname;"; - com.AddParameter("@groupname", name); - if (com.ExecuteNonQuery() == 1) - message = "Group " + name + " has been deleted successfully."; - groups.Remove(Tools.GetGroup(name)); - } + + if (database.Query("DELETE FROM GroupList WHERE GroupName=@0", name) == 1) + message = "Group " + name + " has been deleted successfully."; + groups.Remove(Tools.GetGroup(name)); + return message; } - public String addPermission(String name, List permissions) + public String AddPermissions(String name, List permissions) { String message = ""; if (!GroupExists(name)) return "Error: Group doesn't exists."; - using (var com = database.CreateCommand()) - { - Group g = Tools.GetGroup(name); - List perm = g.permissions; - foreach (String p in permissions) - { - if (!perm.Contains(p)) - { - if (perm.Count > 0 && perm[0].Equals("")) - perm[0] = p; - else - g.permissions.Add(p); - } - } - com.CommandText = "UPDATE GroupList SET Commands=@perm WHERE GroupName=@name;"; - com.AddParameter("@perm", String.Join(",", perm)); - com.AddParameter("@name", name); - if (com.ExecuteNonQuery() == 1) - message = "Group " + name + " has been modified successfully."; - } + + var group = Tools.GetGroup(name); + //Add existing permissions (without duplicating) + permissions.AddRange(group.permissions.Where(s => !permissions.Contains(s))); + + if (database.Query("UPDATE GroupList SET Commands=@0 WHERE GroupName=@1", String.Join(",", permissions), name) != 0) + message = "Group " + name + " has been modified successfully."; + return message; } - public String delPermission(String name, List permissions) + public String DeletePermissions(String name, List permissions) { String message = ""; if (!GroupExists(name)) return "Error: Group doesn't exists."; - using (var com = database.CreateCommand()) - { - Group g = Tools.GetGroup(name); - List perm = g.permissions; - foreach (String p in permissions) - { - if (perm.Contains(p)) - g.permissions.Remove(p); - } - com.CommandText = "UPDATE GroupList SET Commands=@perm WHERE GroupName=@name;"; - com.AddParameter("@perm", String.Join(",", perm)); - com.AddParameter("@name", name); - if (com.ExecuteNonQuery() == 1) - message = "Group " + name + " has been modified successfully."; - } + + var group = Tools.GetGroup(name); + + //Only get permissions that exist in the group. + var newperms = permissions.Where(s => group.permissions.Contains(s)); + + if (database.Query("UPDATE GroupList SET Commands=@0 WHERE GroupName=@1", String.Join(",", newperms), name) != 0) + message = "Group " + name + " has been modified successfully."; + return message; } @@ -220,41 +166,29 @@ namespace TShockAPI.DB try { - using (var com = database.CreateCommand()) + using (var reader = database.QueryReader("SELECT * FROM Grouplist")) { - com.CommandText = "SELECT * FROM Grouplist;"; - using (var reader = com.ExecuteReader()) + while (reader.Read()) { - while (reader.Read()) - { - Group group = null; - string groupname = reader.Get("GroupName"); - group = new Group(groupname); + Group group = null; + string groupname = reader.Get("GroupName"); + group = new Group(groupname); - //Inherit Given commands - String[] commands = reader.Get("Commands").Split(','); - for (int i = 0; i < commands.Length; i++) - { - group.AddPermission(commands[i].Trim()); - } - groups.Add(group); - } - } - /** ORDER BY IS DUMB - //Inherit all commands from group below in order, unless Order is 0 (unique groups anyone) - foreach (Group group in groups) - { - if (group.Order != 0 && group.Order < groups.Count) + //Inherit Given commands + String[] commands = reader.Get("Commands").Split(','); + for (int i = 0; i < commands.Length; i++) { - for (int i = group.Order + 1; i < groups.Count; i++) - { - for (int j = 0; j < groups[i].permissions.Count; j++) - { - group.AddPermission(groups[i].permissions[j]); - } - } + group.AddPermission(commands[i].Trim()); } - }*/ + String[] chatcolour = (reader.Get("ChatColor") ?? "").Split(','); + if (chatcolour.Length == 3) + { + byte.TryParse(chatcolour[0], out group.R); + byte.TryParse(chatcolour[1], out group.G); + byte.TryParse(chatcolour[2], out group.B); + } + groups.Add(group); + } } } catch (Exception ex) diff --git a/TShockAPI/DB/IQueryBuilder.cs b/TShockAPI/DB/IQueryBuilder.cs new file mode 100644 index 00000000..9967797e --- /dev/null +++ b/TShockAPI/DB/IQueryBuilder.cs @@ -0,0 +1,256 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using MySql.Data.MySqlClient; +using TShockAPI.Extensions; + +namespace TShockAPI.DB +{ + public interface IQueryBuilder + { + string CreateTable(SqlTable table); + string AlterTable(SqlTable from, SqlTable to); + string DbTypeToString(MySqlDbType type, int? length); + string UpdateValue(string table, List values, List wheres); + string InsertValues(string table, List values); + string ReadColumn(string table, List wheres); + } + + public class SqliteQueryCreator : IQueryBuilder + { + public string CreateTable(SqlTable table) + { + var columns = table.Columns.Select(c => "'{0}' {1} {2} {3} {4}".SFormat(c.Name, DbTypeToString(c.Type, c.Length), c.Primary ? "PRIMARY KEY" : "", c.AutoIncrement ? "AUTOINCREMENT" : "", c.NotNull ? "NOT NULL" : "", c.Unique ? "UNIQUE" : "")); + return "CREATE TABLE '{0}' ({1})".SFormat(table.Name, string.Join(", ", columns)); + } + static Random rand = new Random(); + /// + /// Alter a table from source to destination + /// + /// Must have name and column names. Column types are not required + /// Must have column names and column types. + /// + public string AlterTable(SqlTable from, SqlTable to) + { + var rstr = rand.NextString(20); + var alter = "ALTER TABLE '{0}' RENAME TO '{1}_{0}'".SFormat(from.Name, rstr); + var create = CreateTable(to); + //combine all columns in the 'from' variable excluding ones that aren't in the 'to' variable. + //exclude the ones that aren't in 'to' variable because if the column is deleted, why try to import the data? + var insert = "INSERT INTO '{0}' ({1}) SELECT {1} FROM {2}_{0}".SFormat(from.Name, string.Join(", ", from.Columns.Where(c => to.Columns.Any(c2 => c2.Name == c.Name)).Select(c => c.Name)), rstr); + var drop = "DROP TABLE '{0}_{1}'".SFormat(rstr, from.Name); + return "{0}; {1}; {2}; {3};".SFormat(alter, create, insert, drop); + /* + ALTER TABLE "main"."Bans" RENAME TO "oXHFcGcd04oXHFcGcd04_Bans" + CREATE TABLE "main"."Bans" ("IP" TEXT PRIMARY KEY ,"Name" TEXT) + INSERT INTO "main"."Bans" SELECT "IP","Name" FROM "main"."oXHFcGcd04oXHFcGcd04_Bans" + DROP TABLE "main"."oXHFcGcd04oXHFcGcd04_Bans" + * + * Twitchy - Oh. I get it! + */ + } + public string UpdateValue(string table, List values, List wheres) + { + var sbvalues = new StringBuilder(); + var sbwheres = new StringBuilder(); + int count = 0; + foreach (SqlValue value in values) + { + sbvalues.Append(value.Name + "=" + value.Value.ToString()); + if (count != values.Count - 1) + sbvalues.Append(","); + count++; + } + count = 0; + foreach (SqlValue where in wheres) + { + sbwheres.Append(where.Name + "=" + where.Value.ToString()); + if (count != wheres.Count - 1) + sbvalues.Append(" AND "); + count++; + } + + if (wheres.Count > 0) + return "UPDATE '{0}' SET {1} WHERE {2}".SFormat(table, sbvalues.ToString(), sbwheres.ToString()); + else + return "UPDATE '{0}' SET {1}".SFormat(table, sbvalues.ToString()); + } + public string InsertValues(string table, List values) + { + var sbnames = new StringBuilder(); + var sbvalues = new StringBuilder(); + int count = 0; + + foreach (SqlValue name in values) + { + sbnames.Append(name.Name); + + if (count != values.Count - 1) + sbnames.Append(", "); + count++; + } + count = 0; + foreach (SqlValue value in values) + { + sbvalues.Append(value.Value.ToString()); + if (count != values.Count - 1) + sbvalues.Append(", "); + count++; + } + + return "INSERT INTO '{0}' ({1}) VALUES ({2})".SFormat(table, sbnames.ToString(), sbvalues.ToString()); + } + public string ReadColumn(string table, List wheres) + { + var sbwheres = new StringBuilder(); + int count = 0; + + foreach (SqlValue where in wheres) + { + sbwheres.Append(where.Name + "=" + where.Value.ToString()); + if (count != wheres.Count - 1) + sbwheres.Append(" AND "); + count++; + } + + if(wheres.Count > 0) + return "SELECT * FROM {0} WHERE {1}".SFormat(table, sbwheres.ToString()); + else + return "SELECT * FROM {0}".SFormat(table); + } + + static readonly Dictionary TypesAsStrings = new Dictionary + { + {MySqlDbType.VarChar, "TEXT"}, + {MySqlDbType.String, "TEXT"}, + {MySqlDbType.Text, "TEXT"}, + {MySqlDbType.TinyText, "TEXT"}, + {MySqlDbType.MediumText, "TEXT"}, + {MySqlDbType.LongText, "TEXT"}, + {MySqlDbType.Int32, "INTEGER"}, + }; + public string DbTypeToString(MySqlDbType type, int? length) + { + string ret; + if (TypesAsStrings.TryGetValue(type, out ret)) + return ret; + throw new NotImplementedException(Enum.GetName(typeof(MySqlDbType), type)); + } + } + public class MysqlQueryCreator : IQueryBuilder + { + public string CreateTable(SqlTable table) + { + var columns = table.Columns.Select(c => "{0} {1} {2} {3}".SFormat(c.Name, DbTypeToString(c.Type, c.Length), c.Primary ? "PRIMARY KEY" : "", c.AutoIncrement ? "AUTOINCREMENT" : "", c.NotNull ? "NOT NULL" : "")); + var uniques = table.Columns.Where(c => c.Unique).Select(c => c.Name); + return "CREATE TABLE {0} ({1}) {2}".SFormat(table.Name, string.Join(", ", columns), uniques.Count() > 0 ? ", UNIQUE({0})".SFormat(string.Join(", ", uniques)) : ""); + } + static Random rand = new Random(); + /// + /// Alter a table from source to destination + /// + /// Must have name and column names. Column types are not required + /// Must have column names and column types. + /// + public string AlterTable(SqlTable from, SqlTable to) + { + var rstr = rand.NextString(20); + var alter = "RENAME TABLE {0} TO {1}_{0}".SFormat(from.Name, rstr); + var create = CreateTable(to); + //combine all columns in the 'from' variable excluding ones that aren't in the 'to' variable. + //exclude the ones that aren't in 'to' variable because if the column is deleted, why try to import the data? + var insert = "INSERT INTO {0} ({1}) SELECT {1} FROM {2}_{0}".SFormat(from.Name, string.Join(", ", from.Columns.Where(c => to.Columns.Any(c2 => c2.Name == c.Name)).Select(c => c.Name)), rstr); + var drop = "DROP TABLE {0}_{1}".SFormat(rstr, from.Name); + return "{0}; {1}; {2}; {3};".SFormat(alter, create, insert, drop); + } + public string UpdateValue(string table, List values, List wheres) + { + var sbvalues = new StringBuilder(); + var sbwheres = new StringBuilder(); + int count = 0; + foreach (SqlValue value in values) + { + sbvalues.Append(value.Name + "=" + value.Value.ToString()); + if (count != values.Count - 1) + sbvalues.Append("AND"); + count++; + } + count = 0; + foreach (SqlValue where in wheres) + { + sbwheres.Append(where.Name + "=" + where.Value.ToString()); + if (count != wheres.Count - 1) + sbvalues.Append(" AND "); + count++; + } + + if (wheres.Count > 0) + return "UPDATE {0} SET {1} WHERE {2}".SFormat(table, sbvalues.ToString(), sbwheres.ToString()); + else + return "UPDATE {0} SET {1}".SFormat(table, sbvalues.ToString()); + } + public string InsertValues(string table, List values) + { + var sbnames = new StringBuilder(); + var sbvalues = new StringBuilder(); + int count = 0; + + foreach (SqlValue name in values) + { + sbnames.Append(name.Name); + + if (count != values.Count - 1) + sbnames.Append(", "); + count++; + } + count = 0; + foreach (SqlValue value in values) + { + sbvalues.Append(value.Value.ToString()); + if (count != values.Count - 1) + sbvalues.Append(", "); + count++; + } + + return "INSERT INTO {0} ({1}) VALUES ({2})".SFormat(table, sbnames.ToString(), sbvalues.ToString()); + } + public string ReadColumn(string table, List wheres) + { + var sbwheres = new StringBuilder(); + int count = 0; + + foreach (SqlValue where in wheres) + { + sbwheres.Append(where.Name + "=" + where.Value.ToString()); + if (count != wheres.Count - 1) + sbwheres.Append(" AND "); + count++; + } + + if (wheres.Count > 0) + return "SELECT * FROM {0} WHERE {1}".SFormat(table, sbwheres.ToString()); + else + return "SELECT * FROM {0}".SFormat(table); + } + + static readonly Dictionary TypesAsStrings = new Dictionary + { + {MySqlDbType.VarChar, "VARCHAR"}, + {MySqlDbType.String, "CHAR"}, + {MySqlDbType.Text, "TEXT"}, + {MySqlDbType.TinyText, "TINYTEXT"}, + {MySqlDbType.MediumText, "MEDIUMTEXT"}, + {MySqlDbType.LongText, "LONGTEXT"}, + {MySqlDbType.Int32, "INT"}, + }; + public string DbTypeToString(MySqlDbType type, int? length) + { + string ret; + if (TypesAsStrings.TryGetValue(type, out ret)) + return ret + (length != null ? "({0})".SFormat((int)length) : ""); + throw new NotImplementedException(Enum.GetName(typeof(MySqlDbType), type)); + } + } +} diff --git a/TShockAPI/DB/ItemManager.cs b/TShockAPI/DB/ItemManager.cs index 12ddb646..d44db632 100644 --- a/TShockAPI/DB/ItemManager.cs +++ b/TShockAPI/DB/ItemManager.cs @@ -1,11 +1,8 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Data; -using Community.CsharpSqlite.SQLiteClient; -using TShockAPI.DB; using System.IO; +using MySql.Data.MySqlClient; namespace TShockAPI.DB { @@ -18,48 +15,42 @@ namespace TShockAPI.DB { database = db; - using (var com = database.CreateCommand()) + var table = new SqlTable("ItemBans", + new SqlColumn("ItemName", MySqlDbType.VarChar, 50) { Primary = true } + ); + var creator = new SqlTableCreator(db, db.GetSqlType() == SqlType.Sqlite ? (IQueryBuilder)new SqliteQueryCreator() : new MysqlQueryCreator()); + creator.EnsureExists(table); + + String file = Path.Combine(TShock.SavePath, "itembans.txt"); + if (File.Exists(file)) { - if (TShock.Config.StorageType.ToLower() == "sqlite") - com.CommandText = - "CREATE TABLE IF NOT EXISTS 'ItemBans' ('ItemName' TEXT PRIMARY KEY);"; - else if (TShock.Config.StorageType.ToLower() == "mysql") - com.CommandText = - "CREATE TABLE IF NOT EXISTS ItemBans (ItemName VARCHAR(255) PRIMARY);"; - com.ExecuteNonQuery(); - - String file = Path.Combine(TShock.SavePath, "itembans.txt"); - if (File.Exists(file)) + using (StreamReader sr = new StreamReader(file)) { - using (StreamReader sr = new StreamReader(file)) + String line; + while ((line = sr.ReadLine()) != null) { - String line; - while ((line = sr.ReadLine()) != null) + if (!line.Equals("") && !line.Substring(0, 1).Equals("#")) { - if (!line.Equals("") && !line.Substring(0, 1).Equals("#")) - { - if (TShock.Config.StorageType.ToLower() == "sqlite") - com.CommandText = "INSERT OR IGNORE INTO 'ItemBans' (ItemName) VALUES (@name);"; - else if (TShock.Config.StorageType.ToLower() == "mysql") - com.CommandText = "INSERT IGNORE INTO ItemBans SET ItemName=@name;"; - int id = 0; - int.TryParse(line, out id); - com.AddParameter("@name", Tools.GetItemById(id).name); - com.ExecuteNonQuery(); - com.Parameters.Clear(); - } + string query = (TShock.Config.StorageType.ToLower() == "sqlite") ? + "INSERT OR IGNORE INTO 'ItemBans' (ItemName) VALUES (@0);" : + "INSERT IGNORE INTO ItemBans SET ItemName=@0;"; + + int id = 0; + int.TryParse(line, out id); + + database.Query(query, Tools.GetItemById(id).name); } } - - String path = Path.Combine(TShock.SavePath, "old_configs"); - String file2 = Path.Combine(path, "itembans.txt"); - if (!Directory.Exists(path)) - System.IO.Directory.CreateDirectory(path); - if (File.Exists(file2)) - File.Delete(file2); - File.Move(file, file2); } + + String path = Path.Combine(TShock.SavePath, "old_configs"); + String file2 = Path.Combine(path, "itembans.txt"); + if (!Directory.Exists(path)) + Directory.CreateDirectory(path); + if (File.Exists(file2)) + File.Delete(file2); + File.Move(file, file2); } UpdateItemBans(); @@ -68,29 +59,20 @@ namespace TShockAPI.DB public void UpdateItemBans() { ItemBans.Clear(); - using (var com = database.CreateCommand()) - { - com.CommandText = "SELECT * FROM ItemBans"; - using (var reader = com.ExecuteReader()) - { - while (reader!=null&&reader.Read()) - ItemBans.Add(reader.Get("ItemName")); - } + using (var reader = database.QueryReader("SELECT * FROM ItemBans")) + { + while (reader != null && reader.Read()) + ItemBans.Add(reader.Get("ItemName")); } } public void AddNewBan(string itemname = "") { try { - using (var com = database.CreateCommand()) - { - com.CommandText = "INSERT INTO ItemBans (ItemName) VALUES (@itemname);"; - com.AddParameter("@itemname", Tools.GetItemByName(itemname)[0].name); - com.ExecuteNonQuery(); - if( !ItemIsBanned( itemname ) ) - ItemBans.Add(itemname); - } + database.Query("INSERT INTO ItemBans (ItemName) VALUES (@0);", Tools.GetItemByName(itemname)[0].name); + if (!ItemIsBanned(itemname)) + ItemBans.Add(itemname); } catch (Exception ex) { @@ -104,13 +86,8 @@ namespace TShockAPI.DB return; try { - using (var com = database.CreateCommand()) - { - com.CommandText = "Delete FROM 'ItemBans' WHERE ItemName=@itemname;"; - com.AddParameter("@itemname", Tools.GetItemByName(itemname)[0].name); - com.ExecuteNonQuery(); - ItemBans.Remove(itemname); - } + database.Query("Delete FROM 'ItemBans' WHERE ItemName=@0;", Tools.GetItemByName(itemname)[0].name); + ItemBans.Remove(itemname); } catch (Exception ex) { diff --git a/TShockAPI/DB/RegionManager.cs b/TShockAPI/DB/RegionManager.cs index 75ee9f7e..76048d83 100644 --- a/TShockAPI/DB/RegionManager.cs +++ b/TShockAPI/DB/RegionManager.cs @@ -1,4 +1,4 @@ -/* +/* TShock, a server mod for Terraria Copyright (C) 2011 The TShock Team @@ -18,17 +18,13 @@ along with this program. If not, see . using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Xml; -using System.IO; using System.Data; -using TShockAPI.DB; -using Community.CsharpSqlite.SQLiteClient; +using System.IO; +using System.Xml; using Microsoft.Xna.Framework; +using MySql.Data.MySqlClient; using Terraria; - namespace TShockAPI.DB { public class RegionManager @@ -41,186 +37,119 @@ namespace TShockAPI.DB { database = db; - using (var com = database.CreateCommand()) + var table = new SqlTable("Regions", + new SqlColumn("X1", MySqlDbType.Int32), + new SqlColumn("Y1", MySqlDbType.Int32), + new SqlColumn("height", MySqlDbType.Int32), + new SqlColumn("width", MySqlDbType.Int32), + new SqlColumn("RegionName", MySqlDbType.VarChar, 50) { Primary = true }, + new SqlColumn("WorldID", MySqlDbType.Text), + new SqlColumn("UserIds", MySqlDbType.Text), + new SqlColumn("Protected", MySqlDbType.Int32) + ); + var creator = new SqlTableCreator(db, db.GetSqlType() == SqlType.Sqlite ? (IQueryBuilder)new SqliteQueryCreator() : new MysqlQueryCreator()); + creator.EnsureExists(table); + + ImportOld(); + + } + + public void ImportOld() + { + String file = Path.Combine(TShock.SavePath, "regions.xml"); + if (!File.Exists(file)) + return; + + Region region; + Rectangle rect; + using (var sr = new StreamReader(file)) { - if (TShock.Config.StorageType.ToLower() == "sqlite") - com.CommandText = - "CREATE TABLE IF NOT EXISTS 'Regions' ('X1' NUMERIC, 'Y1' NUMERIC, 'height' NUMERIC, 'width' NUMERIC, 'RegionName' TEXT PRIMARY KEY, 'WorldID' TEXT, 'UserIds' TEXT, 'Protected' NUMERIC);"; - else if (TShock.Config.StorageType.ToLower() == "mysql") - com.CommandText = - "CREATE TABLE IF NOT EXISTS Regions (X1 INT(11), Y1 INT(11), height INT(11), width INT(11), RegionName VARCHAR(255) PRIMARY, WorldID VARCHAR(255), UserIds VARCHAR(255), Protected INT(1));"; - - com.ExecuteNonQuery(); - - String file = Path.Combine(TShock.SavePath, "regions.xml"); - String name = ""; - String world = ""; - int x1 = 0; - int x2 = 0; - int y1 = 0; - int y2 = 0; - int prot = 0; - int users = 0; - String[] ips; - String ipstr = ""; - int updates = 0; - if (File.Exists(file)) + using (var reader = XmlReader.Create(sr)) { - XmlReader reader; - reader = XmlReader.Create(new StreamReader(file)); // Parse the file and display each of the nodes. while (reader.Read()) { - switch (reader.NodeType) + if (reader.NodeType != XmlNodeType.Element || reader.Name != "ProtectedRegion") + continue; + + region = new Region(); + rect = new Rectangle(); + + bool endregion = false; + while (reader.Read() && !endregion) { - case XmlNodeType.Element: - switch (reader.Name) - { - case "ProtectedRegion": - name = ""; - world = ""; - x1 = 0; - x2 = 0; - y1 = 0; - y2 = 0; - prot = 0; - users = 0; - ips = null; - ipstr = ""; - break; - case "RegionName": - while (reader.NodeType != XmlNodeType.Text) - reader.Read(); - name = reader.Value; - break; - case "Point1X": - while (reader.NodeType != XmlNodeType.Text) - reader.Read(); - int.TryParse(reader.Value, out x1); - break; - case "Point1Y": - while (reader.NodeType != XmlNodeType.Text) - reader.Read(); - int.TryParse(reader.Value, out y1); - break; - case "Point2X": - while (reader.NodeType != XmlNodeType.Text) - reader.Read(); - int.TryParse(reader.Value, out x2); - break; - case "Point2Y": - while (reader.NodeType != XmlNodeType.Text) - reader.Read(); - int.TryParse(reader.Value, out y2); - break; - case "Protected": - while (reader.NodeType != XmlNodeType.Text) - reader.Read(); - if (reader.Value.Equals("True")) - { - prot = 0; - } - else - { - prot = 1; - } - break; - case "WorldName": - while (reader.NodeType != XmlNodeType.Text) - reader.Read(); - world = reader.Value; - break; - case "AllowedUserCount": - while (reader.NodeType != XmlNodeType.Text) - reader.Read(); - int.TryParse(reader.Value, out users); - if (users > 0) - { - ips = new String[users]; - for (int i = 0; i < users; i++) - { - do - reader.Read(); - while (reader.NodeType != XmlNodeType.Text); - ips[i] = reader.Value; - } - ipstr = ""; - for (int i = 0; i < ips.Length; i++) - { - try - { - if (ipstr != "") - ipstr += ","; - ipstr += TShock.Users.GetUserID(ips[i]); - } - catch (Exception) - { - Log.Error("An IP address failed to import. It wasn't a user in the new user system."); - } + if (reader.NodeType != XmlNodeType.Element) + continue; - } + string name = reader.Name; - } - if (TShock.Config.StorageType.ToLower() == "sqlite") - com.CommandText = "INSERT OR IGNORE INTO Regions VALUES (@tx, @ty, @height, @width, @name, @worldid, @userids, @protected);"; - else if (TShock.Config.StorageType.ToLower() == "mysql") - com.CommandText = "INSERT IGNORE INTO Regions SET X1=@tx, Y1=@ty, height=@height, width=@width, RegionName=@name, WorldID=@world, UserIds=@userids, Protected=@protected;"; - com.AddParameter("@tx", x1); - com.AddParameter("@ty", y1); - com.AddParameter("@width", x2); - com.AddParameter("@height", y2); - com.AddParameter("@name", name); - com.AddParameter("@worldid", world); - com.AddParameter("@userids", ipstr); - com.AddParameter("@protected", prot); - updates += com.ExecuteNonQuery(); - com.Parameters.Clear(); - break; - } - break; - case XmlNodeType.Text: + while (reader.Read() && reader.NodeType != XmlNodeType.Text) ; - break; - case XmlNodeType.XmlDeclaration: - case XmlNodeType.ProcessingInstruction: - break; - case XmlNodeType.Comment: - break; - case XmlNodeType.EndElement: + switch (name) + { + case "RegionName": + region.Name = reader.Value; + break; + case "Point1X": + int.TryParse(reader.Value, out rect.X); + break; + case "Point1Y": + int.TryParse(reader.Value, out rect.Y); + break; + case "Point2X": + int.TryParse(reader.Value, out rect.Width); + break; + case "Point2Y": + int.TryParse(reader.Value, out rect.Height); + break; + case "Protected": + region.DisableBuild = reader.Value.ToLower().Equals("true"); + break; + case "WorldName": + region.WorldID = reader.Value; + break; + case "AllowedUserCount": + break; + case "IP": + region.AllowedIDs.Add(int.Parse(reader.Value)); + break; + default: + endregion = true; + break; + } + } - break; + region.Area = rect; + using (var com = database.CreateCommand()) + { + string query = (TShock.Config.StorageType.ToLower() == "sqlite") ? + "INSERT OR IGNORE INTO Regions VALUES (@0, @1, @2, @3, @4, @5, @6, @7);" : + "INSERT IGNORE INTO Regions SET X1=@0, Y1=@1, height=@2, width=@3, RegionName=@4, WorldID=@5, UserIds=@6, Protected=@7;"; + database.Query(query, region.Area.X, region.Area.Y, region.Area.Width, region.Area.Height, region.Name, region.WorldID, "", region.DisableBuild); + + //Todo: What should this be? We don't really have a way to go from ips to userids + /*string.Join(",", region.AllowedIDs)*/ } } - reader.Close(); - reader = null; - String path = Path.Combine(TShock.SavePath, "old_configs"); - String file2 = Path.Combine(path, "regions.xml"); - if (!Directory.Exists(path)) - System.IO.Directory.CreateDirectory(path); - if (File.Exists(file2)) - File.Delete(file2); - //File.Move(file, file2); } - if (updates > 0) - ReloadAllRegions(); } + String path = Path.Combine(TShock.SavePath, "old_configs"); + String file2 = Path.Combine(path, "regions.xml"); + if (!Directory.Exists(path)) + Directory.CreateDirectory(path); + if (File.Exists(file2)) + File.Delete(file2); + File.Move(file, file2); + + ReloadAllRegions(); } public void ConvertDB() { try { - using (var com = database.CreateCommand()) - { - com.CommandText = "UPDATE Regions SET WorldID=@worldid"; - com.AddParameter("@worldid", Main.worldID.ToString()); - com.ExecuteNonQuery(); - com.Parameters.Clear(); - com.CommandText = "UPDATE Regions SET UserIds=@UserIds"; - com.AddParameter("@UserIds", ""); - com.ExecuteNonQuery(); - ReloadAllRegions(); - } + database.Query("UPDATE Regions SET WorldID=@0, UserIds=''", Main.worldID.ToString()); + ReloadAllRegions(); } catch (Exception ex) { @@ -232,50 +161,45 @@ namespace TShockAPI.DB { try { - using (var com = database.CreateCommand()) + using (var reader = database.QueryReader("SELECT * FROM Regions WHERE WorldID=@0", Main.worldID.ToString())) { - com.CommandText = "SELECT * FROM Regions WHERE WorldID=@worldid"; - com.AddParameter("@worldid", Main.worldID.ToString()); - using (var reader = com.ExecuteReader()) + Regions.Clear(); + while (reader.Read()) { - Regions.Clear(); - while (reader.Read()) + int X1 = reader.Get("X1"); + int Y1 = reader.Get("Y1"); + int height = reader.Get("height"); + int width = reader.Get("width"); + int Protected = reader.Get("Protected"); + string MergedIDs = reader.Get("UserIds"); + string name = reader.Get("RegionName"); + + string[] SplitIDs = MergedIDs.Split(','); + + Region r = new Region(new Rectangle(X1, Y1, width, height), name, Protected != 0, Main.worldID.ToString()); + + try { - int X1 = reader.Get("X1"); - int Y1 = reader.Get("Y1"); - int height = reader.Get("height"); - int width = reader.Get("width"); - int Protected = reader.Get("Protected"); - string MergedIDs = DbExt.Get(reader, "UserIds"); - string name = DbExt.Get(reader, "RegionName"); - System.Console.WriteLine(MergedIDs); - string[] SplitIDs = MergedIDs.Split(','); - - Region r = new Region(new Rectangle(X1, Y1, width, height), name, Protected, Main.worldID.ToString()); - r.RegionAllowedIDs = new int[SplitIDs.Length]; - try + for (int i = 0; i < SplitIDs.Length; i++) { - for (int i = 0; i < SplitIDs.Length; i++) - { - if (SplitIDs.Length == 1 && SplitIDs[0].Equals("")) - { - break; - } - //System.Console.WriteLine(SplitIDs[i]); - r.RegionAllowedIDs[i] = Convert.ToInt32(SplitIDs[i]); - } - } - catch (Exception e) - { - Log.Error("Your database contains invalid UserIDs (they should be ints)."); - Log.Error("A lot of things will fail because of this. You must manually delete and re-create the allowed field."); - Log.Error(e.Message); - Log.Error(e.StackTrace); - } + int id; - Regions.Add(r); + if (Int32.TryParse(SplitIDs[i], out id)) // if unparsable, it's not an int, so silently skip + r.AllowedIDs.Add(id); + else if (SplitIDs[i] == "") // Split gotcha, can return an empty string with certain conditions + // but we only want to let the user know if it's really a nonparsable integer. + Log.Warn("One of your UserIDs is not a usable integer: " + SplitIDs[i]); + } } - reader.Close(); + catch (Exception e) + { + Log.Error("Your database contains invalid UserIDs (they should be ints)."); + Log.Error("A lot of things will fail because of this. You must manually delete and re-create the allowed field."); + Log.Error(e.Message); + Log.Error(e.StackTrace); + } + + Regions.Add(r); } } } @@ -285,29 +209,58 @@ namespace TShockAPI.DB } } + public void ReloadForUnitTest(String n) + { + + using (var reader = database.QueryReader("SELECT * FROM Regions WHERE WorldID=@0", n)) + { + Regions.Clear(); + while (reader.Read()) + { + int X1 = reader.Get("X1"); + int Y1 = reader.Get("Y1"); + int height = reader.Get("height"); + int width = reader.Get("width"); + int Protected = reader.Get("Protected"); + string MergedIDs = reader.Get("UserIds"); + string name = reader.Get("RegionName"); + string[] SplitIDs = MergedIDs.Split(','); + + Region r = new Region(new Rectangle(X1, Y1, width, height), name, Protected != 0, Main.worldID.ToString()); + try + { + for (int i = 0; i < SplitIDs.Length; i++) + { + int id; + + if (Int32.TryParse(SplitIDs[i], out id)) // if unparsable, it's not an int, so silently skip + r.AllowedIDs.Add(id); + else if (SplitIDs[i] == "") // Split gotcha, can return an empty string with certain conditions + // but we only want to let the user know if it's really a nonparsable integer. + Log.Warn("UnitTest: One of your UserIDs is not a usable integer: " + SplitIDs[i]); + } + } + catch (Exception e) + { + Log.Error("Your database contains invalid UserIDs (they should be ints)."); + Log.Error("A lot of things will fail because of this. You must manually delete and re-create the allowed field."); + Log.Error(e.Message); + Log.Error(e.StackTrace); + } + + Regions.Add(r); + } + } + + } + public bool AddRegion(int tx, int ty, int width, int height, string regionname, string worldid) { try { - using (var com = database.CreateCommand()) - { - com.CommandText = - "INSERT INTO Regions VALUES (@tx, @ty, @height, @width, @name, @worldid, @userids, @protected);"; - com.AddParameter("@tx", tx); - com.AddParameter("@ty", ty); - com.AddParameter("@width", width); - com.AddParameter("@height", height); - com.AddParameter("@name", regionname.ToLower()); - com.AddParameter("@worldid", worldid); - com.AddParameter("@userids", ""); - com.AddParameter("@protected", 1); - if (com.ExecuteNonQuery() > 0) - { - Regions.Add(new Region(new Rectangle(tx, ty, width, height), regionname, 0, worldid)); - return true; - } - - } + database.Query("INSERT INTO Regions 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)); + return true; } catch (Exception ex) { @@ -320,15 +273,9 @@ namespace TShockAPI.DB { try { - using (var com = database.CreateCommand()) - { - com.CommandText = "DELETE FROM Regions WHERE RegionName=@name AND WorldID=@worldid"; - com.AddParameter("@name", name.ToLower()); - com.AddParameter("@worldid", Main.worldID.ToString()); - com.ExecuteNonQuery(); - ReloadAllRegions(); - return true; - } + database.Query("DELETE FROM Regions WHERE RegionName=@0 AND WorldID=@1", name, Main.worldID.ToString()); + Regions.Remove(getRegion(name)); + return true; } catch (Exception ex) { @@ -341,16 +288,24 @@ namespace TShockAPI.DB { try { - using (var com = database.CreateCommand()) - { - com.CommandText = "UPDATE Regions SET Protected=@bool WHERE RegionName=@name AND WorldID=@worldid"; - com.AddParameter("@name", name); - com.AddParameter("@bool", state ? 1 : 0); - com.AddParameter("@worldid", Main.worldID.ToString()); - int q = com.ExecuteNonQuery(); - ReloadAllRegions(); - return (q > 0); - } + database.Query("UPDATE Regions SET Protected=@0 WHERE RegionName=@1 AND WorldID=@2", state ? 1 : 0, name, Main.worldID.ToString()); + getRegion(name).DisableBuild = state; + return true; + } + catch (Exception ex) + { + Log.Error(ex.ToString()); + return false; + } + } + + public bool SetRegionStateTest(string name, string world, bool state) + { + try + { + database.Query("UPDATE Regions SET Protected=@0 WHERE RegionName=@1 AND WorldID=@2", state ? 1 : 0, name, world); + getRegion(name).DisableBuild = state; + return true; } catch (Exception ex) { @@ -379,9 +334,9 @@ namespace TShockAPI.DB { foreach (Region region in Regions) { - if (x >= region.RegionArea.Left && x <= region.RegionArea.Right && - y >= region.RegionArea.Top && y <= region.RegionArea.Bottom && - region.DisableBuild == 1) + if (x >= region.Area.Left && x <= region.Area.Right && + y >= region.Area.Top && y <= region.Area.Bottom && + region.DisableBuild) { return true; } @@ -392,7 +347,7 @@ namespace TShockAPI.DB public static List ListIDs(string MergedIDs) { List SplitIDs = new List(); - var sb = new StringBuilder(); + /*var sb = new StringBuilder(); for (int i = 0; i < MergedIDs.Length; i++) { char c = MergedIDs[i]; @@ -406,53 +361,43 @@ namespace TShockAPI.DB SplitIDs.Add(sb.ToString()); sb.Clear(); } + }*/ + String[] s = MergedIDs.Split(','); + for (int i = 0; i < s.Length; i++) + { + if (!s[i].Equals("")) + SplitIDs.Add(s[i]); } return SplitIDs; } public bool AddNewUser(string regionName, String userName) { - - try { - using (var com = database.CreateCommand()) + string MergedIDs = string.Empty; + using (var reader = database.QueryReader("SELECT * FROM Regions WHERE RegionName=@0 AND WorldID=@1", regionName, Main.worldID.ToString())) { - com.CommandText = "SELECT * FROM Regions WHERE RegionName=@name AND WorldID=@worldid"; - com.AddParameter("@name", regionName); - com.AddParameter("@worldid", Main.worldID.ToString()); - string MergedIDs = string.Empty; - using (var reader = com.ExecuteReader()) - { - if (reader.Read()) - MergedIDs = reader.Get("UserIds"); - } + if (reader.Read()) + MergedIDs = reader.Get("UserIds"); + } - if (MergedIDs == string.Empty) - MergedIDs = Convert.ToString(TShock.Users.GetUserID(userName)); - else - MergedIDs = MergedIDs + "," + Convert.ToString(TShock.Users.GetUserID(userName)); - com.Parameters.Clear(); - com.CommandText = "UPDATE Regions SET UserIds=@ids WHERE RegionName=@name AND WorldID=@worldid"; - com.AddParameter("@ids", MergedIDs); - com.AddParameter("@name", regionName); - com.AddParameter("@worldid", Main.worldID.ToString()); - if (com.ExecuteNonQuery() > 0) - { - ReloadAllRegions(); - return true; - } - else - { - return false; - } + if (MergedIDs == string.Empty) + MergedIDs = Convert.ToString(TShock.Users.GetUserID(userName)); + else + MergedIDs = MergedIDs + "," + Convert.ToString(TShock.Users.GetUserID(userName)); + + if (database.Query("UPDATE Regions SET UserIds=@0 WHERE RegionName=@1 AND WorldID=@2", MergedIDs, regionName, Main.worldID.ToString()) > 0) + { + ReloadAllRegions(); + return true; } } catch (Exception ex) { Log.Error(ex.ToString()); - return false; } + return false; } /// @@ -465,15 +410,10 @@ namespace TShockAPI.DB var regions = new List(); try { - using (var com = database.CreateCommand()) + using (var reader = database.QueryReader("SELECT RegionName FROM Regions WHERE WorldID=@0", worldid)) { - com.CommandText = "SELECT RegionName FROM Regions WHERE WorldID=@worldid"; - com.AddParameter("@worldid", worldid); - using (var reader = com.ExecuteReader()) - { - while (reader.Read()) - regions.Add(new Region { RegionName = reader.Get("RegionName") }); - } + while (reader.Read()) + regions.Add(new Region { Name = reader.Get("RegionName") }); } } catch (Exception ex) @@ -482,35 +422,47 @@ namespace TShockAPI.DB } return regions; } + + public Region getRegion(String name) + { + foreach (Region r in Regions) + { + if (r.Name.Equals(name)) + return r; + } + return new Region(); + } } public class Region { - public Rectangle RegionArea { get; set; } - public string RegionName { get; set; } - public int DisableBuild { get; set; } - public string RegionWorldID { get; set; } - public int[] RegionAllowedIDs { get; set; } + public Rectangle Area { get; set; } + public string Name { get; set; } + public bool DisableBuild { get; set; } + public string WorldID { get; set; } + public List AllowedIDs { get; set; } - public Region(Rectangle region, string name, int disablebuild, string RegionWorldIDz) + public Region(Rectangle region, string name, bool disablebuild, string RegionWorldIDz) + : this() { - RegionArea = region; - RegionName = name; + Area = region; + Name = name; DisableBuild = disablebuild; - RegionWorldID = RegionWorldIDz; + WorldID = RegionWorldIDz; } public Region() { - RegionArea = Rectangle.Empty; - RegionName = string.Empty; - DisableBuild = 1; - RegionWorldID = string.Empty; + Area = Rectangle.Empty; + Name = string.Empty; + DisableBuild = true; + WorldID = string.Empty; + AllowedIDs = new List(); } public bool InArea(Rectangle point) { - if (RegionArea.Contains(point.X, point.Y)) + if (Area.Contains(point.X, point.Y)) { return true; } @@ -528,14 +480,14 @@ namespace TShockAPI.DB } return false; } - if (DisableBuild == 0) + if (!DisableBuild) { return true; } - for (int i = 0; i < RegionAllowedIDs.Length; i++) + for (int i = 0; i < AllowedIDs.Count; i++) { - if (RegionAllowedIDs[i] == ply.UserID) + if (AllowedIDs[i] == ply.UserID) { return true; } diff --git a/TShockAPI/DB/RememberPosManager.cs b/TShockAPI/DB/RememberPosManager.cs new file mode 100644 index 00000000..e1d89ed9 --- /dev/null +++ b/TShockAPI/DB/RememberPosManager.cs @@ -0,0 +1,94 @@ +/* +TShock, a server mod for Terraria +Copyright (C) 2011 The TShock Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +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.Xml; +using System.Data; +using Microsoft.Xna.Framework; +using MySql.Data.MySqlClient; +using Terraria; + +namespace TShockAPI.DB +{ + public class RemeberedPosManager + { + public IDbConnection database; + + public RemeberedPosManager(IDbConnection db) + { + database = db; + + var table = new SqlTable("RememberedPos", + new SqlColumn("Name", MySqlDbType.VarChar, 50) { Primary = true }, + new SqlColumn("IP", MySqlDbType.Text), + new SqlColumn("X", MySqlDbType.Int32), + new SqlColumn("Y", MySqlDbType.Int32), + new SqlColumn("WorldID", MySqlDbType.Text) + ); + var creator = new SqlTableCreator(db, db.GetSqlType() == SqlType.Sqlite ? (IQueryBuilder)new SqliteQueryCreator() : new MysqlQueryCreator()); + creator.EnsureExists(table); + } + + public Vector2 GetLeavePos(string name, string IP) + { + try + { + using (var reader = database.QueryReader("SELECT * FROM RememberedPos WHERE Name=@0 AND IP=@1", name, IP)) + { + if (reader.Read()) + { + return new Vector2(reader.Get("X"), reader.Get("Y")); + } + } + } + catch (Exception ex) + { + Log.Error(ex.ToString()); + } + + return new Vector2(); + } + + public void InsertLeavePos(string name, string IP, int X, int Y) + { + if (GetLeavePos(name, IP) == Vector2.Zero) + { + try + { + database.Query("INSERT INTO RememberedPos (Name, IP, X, Y, WorldID) VALUES (@0, @1, @2, @3, @4);", name, IP, X, Y + 3, Main.worldID.ToString()); + } + catch (Exception ex) + { + Log.Error(ex.ToString()); + } + } + else + { + try + { + database.Query("UPDATE RememberedPos SET X = @0, Y = @1 WHERE Name = @2 AND IP = @3 AND WorldID = @4;", X, Y + 3, name, IP, Main.worldID.ToString()); + } + catch (Exception ex) + { + Log.Error(ex.ToString()); + } + } + } + } +} diff --git a/TShockAPI/DB/SqlColumn.cs b/TShockAPI/DB/SqlColumn.cs new file mode 100644 index 00000000..8e86498c --- /dev/null +++ b/TShockAPI/DB/SqlColumn.cs @@ -0,0 +1,35 @@ +using MySql.Data.MySqlClient; + +namespace TShockAPI.DB +{ + public class SqlColumn + { + //Required + public string Name { get; set; } + public MySqlDbType Type { get; set; } + + + //Optional + public bool Unique { get; set; } + public bool Primary { get; set; } + public bool AutoIncrement { get; set; } + public bool NotNull { get; set; } + public string DefaultValue { get; set; } + + /// + /// Length of the data type, null = default + /// + public int? Length { get; set; } + + public SqlColumn(string name, MySqlDbType type) + : this(name, type, null) + { + } + public SqlColumn(string name, MySqlDbType type, int? length) + { + Name = name; + Type = type; + Length = length; + } + } +} diff --git a/TShockAPI/DB/SqlTable.cs b/TShockAPI/DB/SqlTable.cs new file mode 100644 index 00000000..3ccca4c4 --- /dev/null +++ b/TShockAPI/DB/SqlTable.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using MySql.Data.MySqlClient; + +namespace TShockAPI.DB +{ + public class SqlTable + { + public List Columns { get; protected set; } + public string Name { get; protected set; } + public SqlTable(string name, params SqlColumn[] columns) + : this(name, new List(columns)) + { + } + public SqlTable(string name, List columns) + { + Name = name; + Columns = columns; + } + } + + public class SqlTableCreator + { + IDbConnection database; + IQueryBuilder creator; + public SqlTableCreator(IDbConnection db, IQueryBuilder provider) + { + database = db; + creator = provider; + } + + public void EnsureExists(SqlTable table) + { + var columns = GetColumns(table); + if (columns.Count > 0) + { + if (!table.Columns.All(c => columns.Contains(c.Name)) || !columns.All(c => table.Columns.Any(c2 => c2.Name == c))) + { + var from = new SqlTable(table.Name, columns.Select(s => new SqlColumn(s, MySqlDbType.String)).ToList()); + database.Query(creator.AlterTable(from, table)); + } + } + else + { + database.Query(creator.CreateTable(table)); + } + } + + public List GetColumns(SqlTable table) + { + var ret = new List(); + var name = database.GetSqlType(); + if (name == SqlType.Sqlite) + { + using (var reader = database.QueryReader("PRAGMA table_info({0})".SFormat(table.Name))) + { + while (reader.Read()) + ret.Add(reader.Get("name")); + } + } + else if (name == SqlType.Mysql) + { + using (var reader = database.QueryReader("SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_NAME=@0 AND TABLE_SCHEMA=@1", table.Name, database.Database)) + { + while (reader.Read()) + ret.Add(reader.Get("COLUMN_NAME")); + } + } + else + { + throw new NotSupportedException(); + } + + return ret; + } + } +} diff --git a/TShockAPI/DB/SqlValue.cs b/TShockAPI/DB/SqlValue.cs new file mode 100644 index 00000000..f74d3149 --- /dev/null +++ b/TShockAPI/DB/SqlValue.cs @@ -0,0 +1,53 @@ +using System.Data; +using MySql.Data.MySqlClient; +using System.Collections.Generic; + +namespace TShockAPI.DB +{ + public class SqlValue + { + public string Name { get; set; } + public object Value { get; set; } + + public SqlValue(string name, object value) + { + Name = name; + Value = value; + } + } + + public class SqlTableEditor + { + IDbConnection database; + IQueryBuilder creator; + + public SqlTableEditor(IDbConnection db, IQueryBuilder provider) + { + database = db; + creator = provider; + } + + public void UpdateValues(string table, List values, List wheres) + { + database.Query(creator.UpdateValue(table, values, wheres)); + } + + public void InsertValues(string table, List values) + { + database.Query(creator.InsertValues(table, values)); + } + + public List ReadColumn(string table, string column, List wheres) + { + List values = new List(); + + using (var reader = database.QueryReader(creator.ReadColumn(table, wheres))) + { + while (reader.Read()) + values.Add(reader.Reader.Get(column)); + } + + return values; + } + } +} diff --git a/TShockAPI/DB/UserManager.cs b/TShockAPI/DB/UserManager.cs index 6dbb8d21..de943668 100644 --- a/TShockAPI/DB/UserManager.cs +++ b/TShockAPI/DB/UserManager.cs @@ -18,12 +18,10 @@ along with this program. If not, see . */ using System; -using System.IO; -using System.Collections.Generic; using System.Data; -using System.Linq; -using System.Text; -using Community.CsharpSqlite.SQLiteClient; +using System.IO; +using MySql.Data.MySqlClient; + namespace TShockAPI.DB { public class UserManager @@ -34,69 +32,63 @@ namespace TShockAPI.DB { database = db; - using (var com = database.CreateCommand()) + var table = new SqlTable("Users", + new SqlColumn("ID", MySqlDbType.Int32) { Primary = true, AutoIncrement = true }, + new SqlColumn("Username", MySqlDbType.VarChar, 32) { Unique = true }, + new SqlColumn("Password", MySqlDbType.VarChar, 64), + new SqlColumn("Usergroup", MySqlDbType.Text), + new SqlColumn("IP", MySqlDbType.VarChar, 32) + ); + var creator = new SqlTableCreator(db, db.GetSqlType() == SqlType.Sqlite ? (IQueryBuilder)new SqliteQueryCreator() : new MysqlQueryCreator()); + creator.EnsureExists(table); + + String file = Path.Combine(TShock.SavePath, "users.txt"); + if (File.Exists(file)) { - if (TShock.Config.StorageType.ToLower() == "sqlite") - com.CommandText = - "CREATE TABLE IF NOT EXISTS 'Users' ('ID' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE, 'Username' VARCHAR(32) UNIQUE, 'Password' VARCHAR(64), 'Usergroup' TEXT, 'IP' VARCHAR(32));"; - else if (TShock.Config.StorageType.ToLower() == "mysql") - com.CommandText = - "CREATE TABLE IF NOT EXISTS Users (ID INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, Username VARCHAR(32) UNIQUE, Password VARCHAR(64), Usergroup VARCHAR(255), IP VARCHAR(15));"; - - com.ExecuteNonQuery(); - - String file = Path.Combine(TShock.SavePath, "users.txt"); - if (File.Exists(file)) + using (StreamReader sr = new StreamReader(file)) { - using (StreamReader sr = new StreamReader(file)) + String line; + while ((line = sr.ReadLine()) != null) { - String line; - while ((line = sr.ReadLine()) != null ) + if (line.Equals("") || line.Substring(0, 1).Equals("#")) + continue; + String[] info = line.Split(' '); + String username = ""; + String sha = ""; + String group = ""; + String ip = ""; + + String[] nameSha = info[0].Split(':'); + + if (nameSha.Length < 2) { - if (line.Equals("") || line.Substring(0, 1).Equals("#") ) - continue; - String[] info = line.Split(' '); - if (TShock.Config.StorageType.ToLower() == "sqlite") - com.CommandText = "INSERT OR IGNORE INTO Users (Username, Password, Usergroup, IP) VALUES (@name, @pass, @group, @ip);"; - else if (TShock.Config.StorageType.ToLower() == "mysql") - com.CommandText = "INSERT IGNORE INTO Users SET Username=@name, Password=@pass, Usergroup=@group, IP=@ip ;"; - - String username = ""; - String sha = ""; - String group = ""; - String ip = ""; - - String[] nameSha = info[0].Split(':'); - - if (nameSha.Length < 2) - { - username = nameSha[0]; - ip = nameSha[0]; - group = info[1]; - } - else - { - username = nameSha[0]; - sha = nameSha[1]; - group = info[1]; - } - com.AddParameter("@name", username.Trim()); - com.AddParameter("@pass", sha.Trim()); - com.AddParameter("@group", group.Trim()); - com.AddParameter("@ip", ip.Trim()); - com.ExecuteNonQuery(); - com.Parameters.Clear(); + username = nameSha[0]; + ip = nameSha[0]; + group = info[1]; } + else + { + username = nameSha[0]; + sha = nameSha[1]; + group = info[1]; + } + + string query = (TShock.Config.StorageType.ToLower() == "sqlite") ? + "INSERT OR IGNORE INTO Users (Username, Password, Usergroup, IP) VALUES (@0, @1, @2, @3)" : + "INSERT IGNORE INTO Users SET Username=@0, Password=@1, Usergroup=@2, IP=@3"; + + database.Query(query, username.Trim(), sha.Trim(), group.Trim(), ip.Trim()); } - String path = Path.Combine(TShock.SavePath, "old_configs"); - String file2 = Path.Combine(path, "users.txt"); - if (!Directory.Exists(path)) - System.IO.Directory.CreateDirectory(path); - if (File.Exists(file2)) - File.Delete(file2); - File.Move(file, file2); } + String path = Path.Combine(TShock.SavePath, "old_configs"); + String file2 = Path.Combine(path, "users.txt"); + if (!Directory.Exists(path)) + Directory.CreateDirectory(path); + if (File.Exists(file2)) + File.Delete(file2); + File.Move(file, file2); } + } /// @@ -107,24 +99,11 @@ namespace TShockAPI.DB { try { - using (var com = database.CreateCommand()) - { - com.CommandText = "INSERT INTO Users (Username, Password, UserGroup, IP) VALUES (@name, @password, @group, @ip);"; - com.AddParameter("@name", user.Name); - com.AddParameter("@password", Tools.HashPassword(user.Password)); + if (!TShock.Groups.GroupExists(user.Group)) + throw new GroupNotExistsException(user.Group); - if (!TShock.Groups.GroupExists(user.Group)) - throw new GroupNotExistsException(user.Group); - - com.AddParameter("@group", user.Group); - com.AddParameter("@ip", user.Address); - - using (var reader = com.ExecuteReader()) - { - if (reader.RecordsAffected < 1) - throw new UserExistsException(user.Name); - } - } + if (database.Query("INSERT INTO Users (Username, Password, UserGroup, IP) VALUES (@0, @1, @2, @3);", user.Name, Tools.HashPassword(user.Password), user.Group, user.Address) < 1) + throw new UserExistsException(user.Name); } catch (Exception ex) { @@ -140,25 +119,18 @@ namespace TShockAPI.DB { try { - using (var com = database.CreateCommand()) + int affected = -1; + if (!string.IsNullOrEmpty(user.Address)) { - if (!string.IsNullOrEmpty(user.Address)) - { - com.CommandText = "DELETE FROM Users WHERE IP=@ip"; - com.AddParameter("@ip", user.Address); - } - else - { - com.CommandText = "DELETE FROM Users WHERE Username=@name"; - com.AddParameter("@name", user.Name); - } - - using (var reader = com.ExecuteReader()) - { - if (reader.RecordsAffected < 1) - throw new UserNotExistException(string.IsNullOrEmpty(user.Address) ? user.Name : user.Address); - } + affected = database.Query("DELETE FROM Users WHERE IP=@0", user.Address); } + else + { + affected = database.Query("DELETE FROM Users WHERE Username=@0", user.Name); + } + + if (affected < 1) + throw new UserNotExistException(string.IsNullOrEmpty(user.Address) ? user.Name : user.Address); } catch (Exception ex) { @@ -176,18 +148,8 @@ namespace TShockAPI.DB { try { - using (var com = database.CreateCommand()) - { - com.CommandText = "UPDATE Users SET Password = @password WHERE Username = @name;"; - com.AddParameter("@name", user.Name); - com.AddParameter("@password", Tools.HashPassword(password)); - - using (var reader = com.ExecuteReader()) - { - if (reader.RecordsAffected < 1) - throw new UserExistsException(user.Name); - } - } + if (database.Query("UPDATE Users SET Password = @0 WHERE Username = @1;", Tools.HashPassword(password), user.Name) == 0) + throw new UserNotExistException(user.Name); } catch (Exception ex) { @@ -204,22 +166,11 @@ namespace TShockAPI.DB { try { - using (var com = database.CreateCommand()) - { - com.CommandText = "UPDATE Users SET UserGroup = @group WHERE Username = @name;"; - com.AddParameter("@name", user.Name); + if (!TShock.Groups.GroupExists(group)) + throw new GroupNotExistsException(group); - if (!TShock.Groups.GroupExists(group)) - throw new GroupNotExistsException(group); - - com.AddParameter("@group", group); - - using (var reader = com.ExecuteReader()) - { - if (reader.RecordsAffected < 1) - throw new UserExistsException(user.Name); - } - } + if (database.Query("UPDATE Users SET UserGroup = @0 WHERE Username = @1;", group, user.Name) == 0) + throw new UserNotExistException(user.Name); } catch (Exception ex) { @@ -238,26 +189,19 @@ namespace TShockAPI.DB string[] returndata = new string[2]; try { - using (var com = database.CreateCommand()) + using (var reader = database.QueryReader("SELECT * FROM Users WHERE Username=@0", username)) { - com.CommandText = "SELECT * FROM Users WHERE Username=@name"; - com.AddParameter("@name", username.ToLower()); - - using (var reader = com.ExecuteReader()) + if (reader.Read()) { - if (reader.Read()) - { - returndata[0] = reader.Get("Password"); - returndata[1] = reader.Get("UserGroup"); - return returndata; - } - reader.Close(); + returndata[0] = reader.Get("Password"); + returndata[1] = reader.Get("UserGroup"); + return returndata; } } } catch (Exception ex) { - Log.ConsoleError("FetchHashedPasswordAndGroup SQL returned an error: " + ex.ToString()); + Log.ConsoleError("FetchHashedPasswordAndGroup SQL returned an error: " + ex); } return returndata; } @@ -266,24 +210,17 @@ namespace TShockAPI.DB { try { - using (var com = database.CreateCommand()) + using (var reader = database.QueryReader("SELECT * FROM Users WHERE Username=@0", username)) { - com.CommandText = "SELECT * FROM Users WHERE Username=@name"; - com.AddParameter("@name", username.ToLower()); - - using (var reader = com.ExecuteReader()) + if (reader.Read()) { - if (reader.Read()) - { - return reader.Get("ID"); - } - reader.Close(); + return reader.Get("ID"); } } } catch (Exception ex) { - Log.ConsoleError("FetchHashedPasswordAndGroup SQL returned an error: " + ex.ToString()); + Log.ConsoleError("FetchHashedPasswordAndGroup SQL returned an error: " + ex); } return -1; } @@ -296,24 +233,18 @@ namespace TShockAPI.DB { try { - using (var com = database.CreateCommand()) + using (var reader = database.QueryReader("SELECT * FROM Users WHERE IP=@0", ip)) { - com.CommandText = "SELECT * FROM Users WHERE IP=@ip"; - com.AddParameter("@ip", ip); - - using (var reader = com.ExecuteReader()) + if (reader.Read()) { - if (reader.Read()) - { - string group = reader.Get("UserGroup"); - return Tools.GetGroup(group); - } + string group = reader.Get("UserGroup"); + return Tools.GetGroup(group); } } } catch (Exception ex) { - Log.ConsoleError("GetGroupForIP SQL returned an error: " + ex.ToString()); + Log.ConsoleError("GetGroupForIP SQL returned an error: " + ex); } return Tools.GetGroup("default"); } @@ -322,26 +253,20 @@ namespace TShockAPI.DB { try { - using (var com = database.CreateCommand()) + using (var reader = database.QueryReader("SELECT IP, UserGroup FROM Users")) { - com.CommandText = "SELECT * FROM Users"; - - using (var reader = com.ExecuteReader()) + while (reader.Read()) { - while(reader.Read()) + if (Tools.GetIPv4Address(reader.Get("IP")) == ip) { - if (Tools.GetIPv4Address(reader.Get("IP")) == ip) - { - string group = reader.Get("UserGroup"); - return Tools.GetGroup(group); - } + return Tools.GetGroup(reader.Get("UserGroup")); } } } } catch (Exception ex) { - Log.ConsoleError("GetGroupForIP SQL returned an error: " + ex.ToString()); + Log.ConsoleError("GetGroupForIP SQL returned an error: " + ex); } return Tools.GetGroup("default"); } @@ -373,27 +298,23 @@ namespace TShockAPI.DB { try { - using (var com = database.CreateCommand()) + QueryResult result; + if (string.IsNullOrEmpty(user.Address)) { - if (string.IsNullOrEmpty(user.Address)) - { - com.CommandText = "SELECT * FROM Users WHERE Username=@name"; - com.AddParameter("@name", user.Name); - } - else - { - com.CommandText = "SELECT * FROM Users WHERE IP=@ip"; - com.AddParameter("@ip", user.Address); - } + result = database.QueryReader("SELECT * FROM Users WHERE Username=@0", user.Name); + } + else + { + result = database.QueryReader("SELECT * FROM Users WHERE IP=@0", user.Address); + } - using (var reader = com.ExecuteReader()) + using (var reader = result) + { + if (reader.Read()) { - if (reader.Read()) - { - user.ID = reader.Get("ID"); - user.Group = reader.Get("Usergroup"); - return user; - } + user.ID = reader.Get("ID"); + user.Group = reader.Get("Usergroup"); + return user; } } } diff --git a/TShockAPI/DB/WarpsManager.cs b/TShockAPI/DB/WarpsManager.cs index 5802c632..8b320149 100644 --- a/TShockAPI/DB/WarpsManager.cs +++ b/TShockAPI/DB/WarpsManager.cs @@ -20,8 +20,9 @@ using System; using System.Collections.Generic; using System.Data; using System.IO; -using Microsoft.Xna.Framework; using System.Xml; +using Microsoft.Xna.Framework; +using MySql.Data.MySqlClient; using Terraria; namespace TShockAPI.DB @@ -34,112 +35,100 @@ namespace TShockAPI.DB { database = db; - using (var com = database.CreateCommand()) + var table = new SqlTable("Warps", + new SqlColumn("WarpName", MySqlDbType.VarChar, 50) { Primary = true}, + new SqlColumn("X", MySqlDbType.Int32), + new SqlColumn("Y", MySqlDbType.Int32), + new SqlColumn("WorldID", MySqlDbType.Text), + new SqlColumn("Private", MySqlDbType.Text) + ); + var creator = new SqlTableCreator(db, db.GetSqlType() == SqlType.Sqlite ? (IQueryBuilder)new SqliteQueryCreator() : new MysqlQueryCreator()); + creator.EnsureExists(table); + + String file = Path.Combine(TShock.SavePath, "warps.xml"); + String name = ""; + String world = ""; + int x1 = 0; + int y1 = 0; + if (File.Exists(file)) { - if (TShock.Config.StorageType.ToLower() == "sqlite") - com.CommandText = - "CREATE TABLE IF NOT EXISTS 'Warps' ('X' NUMERIC, 'Y' NUMERIC, 'WarpName' TEXT PRIMARY KEY, 'WorldID' TEXT);"; - else if (TShock.Config.StorageType.ToLower() == "mysql") - com.CommandText = - "CREATE TABLE IF NOT EXISTS Warps (X INT(11), Y INT(11), WarpName VARCHAR(255) PRIMARY, WorldID VARCHAR(255));"; - - com.ExecuteNonQuery(); - - String file = Path.Combine(TShock.SavePath, "warps.xml"); - String name = ""; - String world = ""; - int x1 = 0; - int y1 = 0; - if (File.Exists(file)) + XmlReader reader; + using (reader = XmlReader.Create(new StreamReader(file))) { - XmlReader reader; - using (reader = XmlReader.Create(new StreamReader(file))) + // Parse the file and display each of the nodes. + while (reader.Read()) { - // Parse the file and display each of the nodes. - while (reader.Read()) + switch (reader.NodeType) { - switch (reader.NodeType) - { - case XmlNodeType.Element: - switch (reader.Name) - { - case "Warp": - name = ""; - world = ""; - x1 = 0; - y1 = 0; - break; - case "WarpName": - while (reader.NodeType != XmlNodeType.Text) - reader.Read(); - name = reader.Value; - break; - case "X": - while (reader.NodeType != XmlNodeType.Text) - reader.Read(); - int.TryParse(reader.Value, out x1); - break; - case "Y": - while (reader.NodeType != XmlNodeType.Text) - reader.Read(); - int.TryParse(reader.Value, out y1); - break; - case "WorldName": - while (reader.NodeType != XmlNodeType.Text) - reader.Read(); - world = reader.Value; - break; - } - break; - case XmlNodeType.Text: + case XmlNodeType.Element: + switch (reader.Name) + { + case "Warp": + name = ""; + world = ""; + x1 = 0; + y1 = 0; + break; + case "WarpName": + while (reader.NodeType != XmlNodeType.Text) + reader.Read(); + name = reader.Value; + break; + case "X": + while (reader.NodeType != XmlNodeType.Text) + reader.Read(); + int.TryParse(reader.Value, out x1); + break; + case "Y": + while (reader.NodeType != XmlNodeType.Text) + reader.Read(); + int.TryParse(reader.Value, out y1); + break; + case "WorldName": + while (reader.NodeType != XmlNodeType.Text) + reader.Read(); + world = reader.Value; + break; + } + break; + case XmlNodeType.Text: - break; - case XmlNodeType.XmlDeclaration: - case XmlNodeType.ProcessingInstruction: - break; - case XmlNodeType.Comment: - break; - case XmlNodeType.EndElement: - if (reader.Name.Equals("Warp")) - { - if (TShock.Config.StorageType.ToLower() == "sqlite") - com.CommandText = "INSERT OR IGNORE INTO Warps VALUES (@tx, @ty,@name, @worldid);"; - else if (TShock.Config.StorageType.ToLower() == "mysql") - com.CommandText = "INSERT IGNORE INTO Warps SET X=@tx, Y=@ty, WarpName=@name, WorldID=@worldid;"; - com.AddParameter("@tx", x1); - com.AddParameter("@ty", y1); - com.AddParameter("@name", name); - com.AddParameter("@worldid", world); - com.ExecuteNonQuery(); - com.Parameters.Clear(); - } - break; - } + break; + case XmlNodeType.XmlDeclaration: + case XmlNodeType.ProcessingInstruction: + break; + case XmlNodeType.Comment: + break; + case XmlNodeType.EndElement: + if (reader.Name.Equals("Warp")) + { + string query = (TShock.Config.StorageType.ToLower() == "sqlite") ? + "INSERT OR IGNORE INTO Warps VALUES (@0, @1,@2, @3);" : + "INSERT IGNORE INTO Warps SET X=@0, Y=@1, WarpName=@2, WorldID=@3;"; + database.Query(query, x1, y1, name, world); + } + break; } - } - reader.Close(); - String path = Path.Combine(TShock.SavePath, "old_configs"); - String file2 = Path.Combine(path, "warps.xml"); - if (!Directory.Exists(path)) - System.IO.Directory.CreateDirectory(path); - if (File.Exists(file2)) - File.Delete(file2); - //File.Move(file, file2); + } + reader.Close(); + String path = Path.Combine(TShock.SavePath, "old_configs"); + String file2 = Path.Combine(path, "warps.xml"); + if (!Directory.Exists(path)) + Directory.CreateDirectory(path); + if (File.Exists(file2)) + File.Delete(file2); + //File.Move(file, file2); } + } public void ConvertDB() { try { - using (var com = database.CreateCommand()) - { - com.CommandText = "UPDATE Warps SET WorldID=@worldid"; - com.AddParameter("@worldid", Main.worldID.ToString()); - com.ExecuteNonQuery(); - } + database.Query("UPDATE Warps SET WorldID=@0", Main.worldID.ToString()); } catch (Exception ex) { @@ -151,16 +140,8 @@ namespace TShockAPI.DB { try { - using (var com = database.CreateCommand()) - { - com.CommandText = "INSERT INTO Warps (X, Y, WarpName, WorldID) VALUES (@x, @y, @name, @worldid);"; - com.AddParameter("@x", x); - com.AddParameter("@y", y); - com.AddParameter("@name", name.ToLower()); - com.AddParameter("@worldid", worldid); - com.ExecuteNonQuery(); - return true; - } + database.Query("INSERT INTO Warps (X, Y, WarpName, WorldID) VALUES (@0, @1, @2, @3);", x, y, name, worldid); + return true; } catch (Exception ex) { @@ -173,14 +154,8 @@ namespace TShockAPI.DB { try { - using (var com = database.CreateCommand()) - { - com.CommandText = "DELETE FROM Warps WHERE WarpName=@name AND WorldID=@worldid"; - com.AddParameter("@name", name.ToLower()); - com.AddParameter("@worldid", Main.worldID.ToString()); - com.ExecuteNonQuery(); - return true; - } + database.Query("DELETE FROM Warps WHERE WarpName=@0 AND WorldID=@1", name, Main.worldID.ToString()); + return true; } catch (Exception ex) { @@ -193,18 +168,18 @@ namespace TShockAPI.DB { try { - using (var com = database.CreateCommand()) + using (var reader = database.QueryReader("SELECT * FROM Warps WHERE WarpName=@0 AND WorldID=@1", name, Main.worldID.ToString())) { - com.CommandText = "SELECT * FROM Warps WHERE WarpName=@name AND WorldID=@worldid"; - com.AddParameter("@name", name); - com.AddParameter("@worldid", Main.worldID.ToString()); - using (var reader = com.ExecuteReader()) + if (reader.Read()) { - if (reader.Read()) + try { - return new Warp(new Vector2(reader.Get("X"), reader.Get("Y")), reader.Get("WarpName"), reader.Get("WorldID")); + return new Warp(new Vector2(reader.Get("X"), reader.Get("Y")), reader.Get("WarpName"), reader.Get("WorldID"), reader.Get("Private")); + } + catch + { + return new Warp(new Vector2(reader.Get("X"), reader.Get("Y")), reader.Get("WarpName"), reader.Get("WorldID"), "0"); } - reader.Close(); } } } @@ -220,19 +195,24 @@ namespace TShockAPI.DB /// /// World name to get warps from /// List of warps with only their names - public List ListAllWarps(string worldid) + public List ListAllPublicWarps(string worldid) { var warps = new List(); try { - using (var com = database.CreateCommand()) + using (var reader = database.QueryReader("SELECT * FROM Warps WHERE WorldID=@0", worldid)) { - com.CommandText = "SELECT WarpName FROM Warps WHERE WorldID=@worldid"; - com.AddParameter("@worldid", worldid); - using (var reader = com.ExecuteReader()) + while (reader.Read()) { - while (reader.Read()) + try + { + if (reader.Get("Private") == "0" || reader.Get("Private") == null) + warps.Add(new Warp { WarpName = reader.Get("WarpName") }); + } + catch + { warps.Add(new Warp { WarpName = reader.Get("WarpName") }); + } } } } @@ -242,6 +222,28 @@ namespace TShockAPI.DB } return warps; } + + /// + /// Gets all the warps names from world + /// + /// World name to get warps from + /// List of warps with only their names + public bool HideWarp(string warp, bool state) + { + try + { + string query = "UPDATE Warps SET Private=@0 WHERE WarpName=@1 AND WorldID=@2"; + + database.Query(query, state ? "1" : "0", warp, Main.worldID.ToString()); + + return true; + } + catch (Exception ex) + { + Log.Error(ex.ToString()); + return false; + } + } } public class Warp @@ -249,12 +251,14 @@ namespace TShockAPI.DB public Vector2 WarpPos { get; set; } public string WarpName { get; set; } public string WorldWarpID { get; set; } + public string Private { get; set; } - public Warp(Vector2 warppos, string name, string worldname) + public Warp(Vector2 warppos, string name, string worldid, string hidden) { WarpPos = warppos; WarpName = name; - WorldWarpID = worldname; + WorldWarpID = worldid; + Private = hidden; } public Warp() @@ -262,6 +266,7 @@ namespace TShockAPI.DB WarpPos = Vector2.Zero; WarpName = null; WorldWarpID = string.Empty; + Private = "0"; } } } diff --git a/TShockAPI/DBTools.cs b/TShockAPI/DBTools.cs new file mode 100644 index 00000000..165352e0 --- /dev/null +++ b/TShockAPI/DBTools.cs @@ -0,0 +1,317 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using TShockAPI.DB; + +namespace TShockAPI +{ + public class DBTools + { + internal static IDbConnection database; + + /// + /// Creates a Table, within the current open DB + /// + /// Name of the Table + /// The list of columns that the Table will have + /// Only try create Table if it does not exist + public static void CreateTable(string name, List columns, bool IfNotExists =true) + { + //Build up Creation string :) + StringBuilder sb = new StringBuilder(); + sb.Append("CREATE TABLE "); + + if (IfNotExists) + sb.Append("IF NOT EXISTS "); + + if (TShock.Config.StorageType.ToLower() == "sqlite") + sb.Append("'" + name + "' ("); + else if (TShock.Config.StorageType.ToLower() == "mysql") + sb.Append(name + " ("); + + int count = 0; + + foreach (Column column in columns) + { + count++; + if (column.Type.ToLower() == "int") + { + if (TShock.Config.StorageType.ToLower() == "sqlite") + sb.Append(column.Name + " NUMERIC "); + else if (TShock.Config.StorageType.ToLower() == "mysql") + sb.Append(column.Name + " INT(255) "); + } + else if (column.Type.ToLower() == "string") + { + if (TShock.Config.StorageType.ToLower() == "sqlite") + sb.Append(column.Name + " TEXT "); + else if (TShock.Config.StorageType.ToLower() == "mysql") + sb.Append(column.Name + " VARCHAR(255) "); + } + + if (column.Unique) + sb.Append("UNIQUE"); + + if (columns.Count == count) + sb.Append(")"); + else + sb.Append(", "); + } + + database.Query(sb.ToString()); + } + + /// + /// Inserts a list of values into a Table, if conditions are met + /// + /// Name of the Table + /// Ignore insert if feild is unique and there is already a exact entry + /// The list of values to enter into the table + /// The list of where statements that must be met, can be an empty list + public static int InsertTable(string tablename, bool Ignore, List Values, List WhereStatements) + { + StringBuilder sb = new StringBuilder(); + sb.Append("INSERT "); + + if (Ignore) + { + if (TShock.Config.StorageType.ToLower() == "sqlite") + sb.Append("OR IGNORE "); + else if (TShock.Config.StorageType.ToLower() == "mysql") + sb.Append("IGNORE "); + } + + if (TShock.Config.StorageType.ToLower() == "sqlite") + sb.Append("INTO '" + tablename + "' ("); + else if (TShock.Config.StorageType.ToLower() == "mysql") + sb.Append("INTO " + tablename + " "); + + using (var com = database.CreateCommand()) + { + //Values + if (TShock.Config.StorageType.ToLower() == "sqlite") + { + int count = 0; + + foreach (ColumnData columnname in Values) + { + count++; + if (Values.Count != count) + sb.Append(columnname.Name + ", "); + else + sb.Append(columnname.Name + ") "); + } + + sb.Append("VALUES ("); + count = 0; + + foreach (ColumnData columnname in Values) + { + count++; + if (Values.Count != count) + { + sb.Append("@" + columnname.Name.ToLower() + ", "); + com.AddParameter("@" + columnname.Name.ToLower(), columnname.Value); + } + else + { + sb.Append("@" + columnname.Name.ToLower() + ") "); + com.AddParameter("@" + columnname.Name.ToLower(), columnname.Value); + } + } + } + else if (TShock.Config.StorageType.ToLower() == "mysql") + { + sb.Append("SET "); + int count = 0; + + foreach (ColumnData columnname in Values) + { + count++; + if (Values.Count != count) + { + sb.Append("@" + columnname.Name.ToLower() + "=" + columnname.Value + ", "); + com.AddParameter("@" + columnname.Name.ToLower(), columnname.Value); + } + else + { + sb.Append("@" + columnname.Name.ToLower() + "=" + columnname.Value + ") "); + com.AddParameter("@" + columnname.Name.ToLower(), columnname.Value); + } + } + } + + //Where Statement (if any) + if (WhereStatements.Count > 0) + { + sb.Append("WHERE "); + int count = 0; + + foreach (ColumnData columnname in WhereStatements) + { + count++; + if (Values.Count != count) + { + sb.Append("@" + columnname.Name.ToLower() + "-where" + "=" + columnname.Value + " AND "); + com.AddParameter("@" + columnname.Name.ToLower() + "-where", columnname.Value); + } + else + { + sb.Append("@" + columnname.Name.ToLower() + "-where" + "=" + columnname.Value + ";"); + com.AddParameter("@" + columnname.Name.ToLower() + "-where", columnname.Value); + } + } + } + + com.CommandText = sb.ToString(); + + using (var reader = com.ExecuteReader()) + return reader.RecordsAffected; + } + } + + /// + /// Returns a list of values from a given Table, where conditions are met + /// + /// Name of the Table + /// The name of the column you are getting the values from + /// The list of where statements that must be met, can be an empty list + public static List ReadTable(string tablename, string getcolumn, List WhereStatements) + { + StringBuilder sb = new StringBuilder(); + List ReturnedValues = new List(); + + sb.Append("SELECT * FROM " + tablename + " "); + + using (var com = database.CreateCommand()) + { + //Where Statement (if any) + if (WhereStatements.Count > 0) + { + sb.Append("WHERE "); + int count = 0; + + foreach (ColumnData columnname in WhereStatements) + { + count++; + if (WhereStatements.Count != count) + { + sb.Append(columnname.Name + " =" + columnname.Value + " AND "); + } + else + { + sb.Append(columnname.Name + " =" + columnname.Value); + } + } + } + + com.CommandText = sb.ToString(); + + using (var reader = com.ExecuteReader()) + { + while (reader.Read()) + ReturnedValues.Add(reader.Get(getcolumn)); + } + } + return ReturnedValues; + } + + /// + /// Sets values in a Table, where statements are met + /// + /// Name of the Table + /// The column data you are setting + /// The list of where statements that must be met, can be an empty list + public static int SetTable(string tablename, ColumnData setcolumn, List WhereStatements) + { + StringBuilder sb = new StringBuilder(); + + sb.Append("UPDATE " + tablename + " SET " + setcolumn.Name + "=@setcolumn "); + + using (var com = database.CreateCommand()) + { + //Where Statement (if any) + if (WhereStatements.Count > 0) + { + sb.Append("WHERE "); + int count = 0; + + foreach (ColumnData columnname in WhereStatements) + { + count++; + if (WhereStatements.Count != count) + { + sb.Append(columnname.Name + " =" + columnname.Value + " AND "); + } + else + { + sb.Append(columnname.Name + " =" + columnname.Value); + } + } + } + + com.CommandText = sb.ToString(); + com.AddParameter("@setcolumn", setcolumn.Value); + + using (var reader = com.ExecuteReader()) + return reader.RecordsAffected; + } + } + } + + public class Column + { + public string Name { get; set; } + public string Type { get; set; } + public bool Unique { get; set; } + public string Parameters { get; set; } + + /// + /// The class for creating a new column type + /// + /// Name of the column + /// Whether there can be more than one exact value in the column + /// The type of column, currently the api only supports "string" or "int" + /// Extra SQL parameters given, can cause errors cross different SQL (SQLite and MySql) + public Column(string name, bool unique, string type, string parameters = "") + { + Name = name; + Type = type; + Unique = unique; + Parameters = parameters; + } + + public Column() + { + Name = string.Empty; + Type = string.Empty; + Unique = false; + Parameters = string.Empty; + } + } + + public class ColumnData + { + public string Name { get; set; } + public object Value { get; set; } + + /// + /// The class for testing, inserting or setting column data + /// + /// Column Name + /// Column Value + public ColumnData(string name, object value) + { + Name = name; + Value = value; + } + + public ColumnData() + { + Name = string.Empty; + Value = string.Empty; + } + } +} diff --git a/TShockAPI/Extensions/DbExt.cs b/TShockAPI/Extensions/DbExt.cs new file mode 100644 index 00000000..e33459a7 --- /dev/null +++ b/TShockAPI/Extensions/DbExt.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Data; + +namespace TShockAPI.DB +{ + public static class DbExt + { + + /// + /// Executes a query on a database. + /// + /// Database to query + /// Query string with parameters as @0, @1, etc. + /// Parameters to be put in the query + /// Rows affected by query + public static int Query(this IDbConnection olddb, string query, params object[] args) + { + using (var db = olddb.CloneEx()) + { + db.Open(); + using (var com = db.CreateCommand()) + { + com.CommandText = query; + for (int i = 0; i < args.Length; i++) + com.AddParameter("@" + i, args[i]); + + return com.ExecuteNonQuery(); + } + } + } + /// + /// Executes a query on a database. + /// + /// Database to query + /// Query string with parameters as @0, @1, etc. + /// Parameters to be put in the query + /// Query result as IDataReader + public static QueryResult QueryReader(this IDbConnection olddb, string query, params object[] args) + { + var db = olddb.CloneEx(); + db.Open(); + using (var com = db.CreateCommand()) + { + com.CommandText = query; + for (int i = 0; i < args.Length; i++) + com.AddParameter("@" + i, args[i]); + + return new QueryResult(db, com.ExecuteReader()); + } + } + + public static IDbDataParameter AddParameter(this IDbCommand command, string name, object data) + { + var parm = command.CreateParameter(); + parm.ParameterName = name; + parm.Value = data; + command.Parameters.Add(parm); + return parm; + } + + public static IDbConnection CloneEx(this IDbConnection conn) + { + var clone = (IDbConnection)Activator.CreateInstance(conn.GetType()); + clone.ConnectionString = conn.ConnectionString; + return clone; + } + + public static SqlType GetSqlType(this IDbConnection conn) + { + var name = conn.GetType().Name; + if (name == "SqliteConnection") + return SqlType.Sqlite; + if (name == "MySqlConnection") + return SqlType.Mysql; + return SqlType.Unknown; + } + + static readonly Dictionary> ReadFuncs = new Dictionary>() + { + {typeof(bool), (s, i) => s.GetBoolean(i)}, + {typeof(byte), (s, i) => s.GetByte(i)}, + {typeof(Int16), (s, i) => s.GetInt16(i)}, + {typeof(Int32), (s, i) => s.GetInt32(i)}, + {typeof(Int64), (s, i) => s.GetInt64(i)}, + {typeof(string), (s, i) => s.GetString(i)}, + {typeof(decimal), (s, i) => s.GetDecimal(i)}, + {typeof(float), (s, i) => s.GetFloat(i)}, + {typeof(double), (s, i) => s.GetDouble(i)}, + {typeof(object), (s, i) => s.GetValue(i)}, + }; + + public static T Get(this IDataReader reader, string column) + { + return reader.Get(reader.GetOrdinal(column)); + } + + public static T Get(this IDataReader reader, int column) + { + if (ReadFuncs.ContainsKey(typeof(T))) + return (T)ReadFuncs[typeof(T)](reader, column); + + throw new NotImplementedException(); + } + } + + public enum SqlType + { + Unknown, + Sqlite, + Mysql + } + + public class QueryResult : IDisposable + { + public IDbConnection Connection { get; protected set; } + public IDataReader Reader { get; protected set; } + + public QueryResult(IDbConnection conn, IDataReader reader) + { + Connection = conn; + Reader = reader; + } + + public void Dispose() + { + Reader.Dispose(); + Connection.Dispose(); + } + + public bool Read() + { + return Reader.Read(); + } + public T Get(string column) + { + return Reader.Get(Reader.GetOrdinal(column)); + } + } + +} diff --git a/TShockAPI/Extensions/LinqExt.cs b/TShockAPI/Extensions/LinqExt.cs new file mode 100644 index 00000000..b8a891ed --- /dev/null +++ b/TShockAPI/Extensions/LinqExt.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TShockAPI +{ + public static class LinqExt + { + public static void ForEach(this IEnumerable source, Action action) + { + if (source == null) throw new ArgumentNullException("source"); + if (action == null) throw new ArgumentNullException("action"); + + foreach (T item in source) + action(item); + } + } +} diff --git a/TShockAPI/Extensions/RandomExt.cs b/TShockAPI/Extensions/RandomExt.cs new file mode 100644 index 00000000..e49b50f0 --- /dev/null +++ b/TShockAPI/Extensions/RandomExt.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TShockAPI.Extensions +{ + public static class RandomExt + { + public static string NextString(this Random rand, int length) + { + var sb = new StringBuilder(); + for (int i = 0; i < length; i++) + { + switch (rand.Next(0, 3)) + { + case 0: + sb.Append((char)rand.Next('a', 'z' + 1)); + break; + case 1: + sb.Append((char)rand.Next('A', 'Z' + 1)); + break; + case 2: + sb.Append((char)rand.Next('0', '9' + 1)); + break; + } + } + return sb.ToString(); + } + } +} diff --git a/TShockAPI/Extensions/StringExt.cs b/TShockAPI/Extensions/StringExt.cs new file mode 100644 index 00000000..b74c3170 --- /dev/null +++ b/TShockAPI/Extensions/StringExt.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TShockAPI +{ + public static class StringExt + { + //Can't name it Format :( + public static String SFormat(this String str, params object[] args) + { + return String.Format(str, args); + } + } +} diff --git a/TShockAPI/FileTools.cs b/TShockAPI/FileTools.cs index 880748f4..4ca142f1 100644 --- a/TShockAPI/FileTools.cs +++ b/TShockAPI/FileTools.cs @@ -17,17 +17,16 @@ along with this program. If not, see . */ using System; using System.IO; -using Terraria; namespace TShockAPI { - internal class FileTools + public class FileTools { - public static string RulesPath { get { return Path.Combine(TShock.SavePath, "rules.txt"); } } - public static string MotdPath { get { return Path.Combine(TShock.SavePath, "motd.txt"); } } - public static string WhitelistPath { get { return Path.Combine(TShock.SavePath, "whitelist.txt"); } } - public static string RememberedPosPath { get { return Path.Combine(TShock.SavePath, "oldpos.xml"); } } - public static string ConfigPath { get { return Path.Combine(TShock.SavePath, "config.json"); } } + internal static string RulesPath { get { return Path.Combine(TShock.SavePath, "rules.txt"); } } + internal static string MotdPath { get { return Path.Combine(TShock.SavePath, "motd.txt"); } } + internal static string WhitelistPath { get { return Path.Combine(TShock.SavePath, "whitelist.txt"); } } + internal static string RememberedPosPath { get { return Path.Combine(TShock.SavePath, "oldpos.xml"); } } + internal static string ConfigPath { get { return Path.Combine(TShock.SavePath, "config.json"); } } public static void CreateFile(string file) { diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index 68fc95f7..2b829071 100755 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -340,7 +340,7 @@ namespace TShockAPI if ((DateTime.UtcNow - args.Player.LastExplosive).TotalMilliseconds < 1000) { - args.Player.SendMessage("Please wait another " + (1000 - (DateTime.UtcNow - args.Player.LastExplosive).TotalMilliseconds).ToString() + " milliseconds before placing/destroying tiles", Color.Red); + args.Player.SendMessage("Please wait another " + (1000 - (DateTime.UtcNow - args.Player.LastExplosive).TotalMilliseconds) + " milliseconds before placing/destroying tiles", Color.Red); args.Player.SendTileSquare(x, y); return true; } @@ -414,6 +414,18 @@ namespace TShockAPI byte owner = args.Data.ReadInt8(); byte type = args.Data.ReadInt8(); + if (ident > Main.maxProjectiles || ident < 0) + { + Tools.HandleGriefer(args.Player, TShock.Config.ExplosiveAbuseReason); + return true; + } + + if ((vely == 0f || velx == 0f) && type == 23) + { + Tools.HandleGriefer(args.Player, TShock.Config.ProjectileAbuseReason); + return true; + } + if (type == 29 || type == 28 || type == 37) { Log.Debug(string.Format("Explosive(PlyXY:{0}_{1}, Type:{2})", args.Player.TileX, args.Player.TileY, type)); diff --git a/TShockAPI/Group.cs b/TShockAPI/Group.cs index 30c44272..5e85eeaf 100644 --- a/TShockAPI/Group.cs +++ b/TShockAPI/Group.cs @@ -29,10 +29,17 @@ namespace TShockAPI public Group Parent { get; protected set; } public int Order { get; set; } - public Group(string groupname, Group parentgroup = null) + public byte R = 255; + public byte G = 255; + public byte B = 255; + + public Group(string groupname, Group parentgroup = null, string ChatColor = "255,255,255") { Name = groupname; Parent = parentgroup; + byte.TryParse(ChatColor.Split(',')[0], out R); + byte.TryParse(ChatColor.Split(',')[1], out G); + byte.TryParse(ChatColor.Split(',')[2], out B); } public virtual bool HasPermission(string permission) @@ -100,6 +107,9 @@ namespace TShockAPI public SuperAdminGroup() : base("superadmin") { + R = (byte)TShock.Config.SuperAdminChatRGB[0]; + G = (byte)TShock.Config.SuperAdminChatRGB[1]; + B = (byte)TShock.Config.SuperAdminChatRGB[2]; } public override bool HasPermission(string permission) diff --git a/TShockAPI/IPackable.cs b/TShockAPI/IPackable.cs index ac6dbaaa..a28952e3 100644 --- a/TShockAPI/IPackable.cs +++ b/TShockAPI/IPackable.cs @@ -16,11 +16,7 @@ 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.IO; -using System.Linq; -using System.Text; namespace TShockAPI { diff --git a/TShockAPI/Net/NetTile.cs b/TShockAPI/Net/NetTile.cs index 2395b3fc..be269a61 100644 --- a/TShockAPI/Net/NetTile.cs +++ b/TShockAPI/Net/NetTile.cs @@ -17,10 +17,7 @@ along with this program. If not, see . */ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; using Terraria; using XNAHelpers; diff --git a/TShockAPI/Net/WorldInfoMsg.cs b/TShockAPI/Net/WorldInfoMsg.cs index 913b976c..118f9582 100644 --- a/TShockAPI/Net/WorldInfoMsg.cs +++ b/TShockAPI/Net/WorldInfoMsg.cs @@ -17,11 +17,8 @@ along with this program. If not, see . */ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; -using Terraria; using TerrariaAPI; using XNAHelpers; diff --git a/TShockAPI/PacketBufferer.cs b/TShockAPI/PacketBufferer.cs new file mode 100644 index 00000000..e47ede9c --- /dev/null +++ b/TShockAPI/PacketBufferer.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Net.Sockets; +using System.Text; +using Microsoft.Xna.Framework; +using Terraria; +using TerrariaAPI; +using TerrariaAPI.Hooks; + +namespace TShockAPI +{ + public class PacketBufferer : IDisposable + { + /// + /// Maximum number of bytes to send per update per socket + /// + public int BytesPerUpdate { get; set; } + + PacketBuffer[] buffers = new PacketBuffer[Netplay.serverSock.Length]; + + int[] Bytes = new int[52]; + int[] Packets = new int[52]; + int[] Compressed = new int[52]; + + Command dump; + Command flush; + + public PacketBufferer() + { + BytesPerUpdate = 0xFFFF; + for (int i = 0; i < buffers.Length; i++) + buffers[i] = new PacketBuffer(); + +#if DEBUG_NET + dump = new Command("superadmin", Dump, "netdump"); + flush = new Command("superadmin", Flush, "netflush"); + Commands.ChatCommands.Add(dump); + Commands.ChatCommands.Add(flush); +#endif + + ServerHooks.SendBytes += ServerHooks_SendBytes; + ServerHooks.SocketReset += ServerHooks_SocketReset; + GameHooks.PostUpdate += GameHooks_Update; + } + + public void Dispose() + { +#if DEBUG_NET + Commands.ChatCommands.Remove(dump); + Commands.ChatCommands.Remove(flush); +#endif + ServerHooks.SendBytes -= ServerHooks_SendBytes; + ServerHooks.SocketReset -= ServerHooks_SocketReset; + GameHooks.PostUpdate -= GameHooks_Update; + } + + void Dump(CommandArgs args) + { + var sb = new StringBuilder(); + sb.AppendLine("{0,-25}{1,-25}{2,-25}{3}".SFormat("Name:", "Packets", "Bytes", "Compression")); + for (int i = 1; i < Bytes.Length; i++) + { + sb.AppendLine("{0,-25}{1,-25}{2,-25}{3}".SFormat(Enum.GetName(typeof(PacketTypes), i) + ":", Packets[i], Bytes[i], Compressed[i])); + } + File.WriteAllText(Path.Combine(TShock.SavePath, "dmp.txt"), sb.ToString()); + } + + void Flush(CommandArgs args) + { + Bytes = new int[52]; + Packets = new int[52]; + Compressed = new int[52]; + } + + void GameHooks_Update(GameTime obj) + { + for (int i = 0; i < Netplay.serverSock.Length; i++) + { + if (Netplay.serverSock[i] == null || !Netplay.serverSock[i].active) + continue; + + if (!Netplay.serverSock[i].tcpClient.Client.Poll(0, SelectMode.SelectWrite)) + continue; + + byte[] buff = buffers[i].GetBytes(BytesPerUpdate); + if (buff == null) + continue; + + try + { + Netplay.serverSock[i].tcpClient.Client.Send(buff); + } + catch (ObjectDisposedException) + { + } + catch (SocketException) + { + } + } + } + + + void ServerHooks_SocketReset(ServerSock socket) + { + buffers[socket.whoAmI] = new PacketBuffer(); + } + + void ServerHooks_SendBytes(ServerSock socket, byte[] buffer, int offset, int count, HandledEventArgs e) + { + e.Handled = true; + lock (buffers[socket.whoAmI]) + { +#if DEBUG_NET + int size = (count - offset); + var pt = buffer[offset + 4]; + + Packets[pt]++; + Bytes[pt] += size; + Compressed[pt] += Compress(buffer, offset, count); +#endif + buffers[socket.whoAmI].AddRange(new MemoryStream(buffer, offset, count).ToArray()); + } + } +#if DEBUG_NET + static int Compress(byte[] buffer, int offset, int count) + { + using (var ms = new MemoryStream()) + { + using (var gzip = new GZipStream(ms, CompressionMode.Compress, true)) + { + gzip.Write(buffer, offset, count); + } + return (int)ms.Length; + } + } +#endif + } + + public class PacketBuffer : List + { + public byte[] GetBytes(int max) + { + lock (this) + { + if (this.Count < 1) + return null; + + var ret = new byte[Math.Min(max, this.Count)]; + this.CopyTo(0, ret, 0, ret.Length); + this.RemoveRange(0, ret.Length); + return ret; + } + } + } +} diff --git a/TShockAPI/Properties/AssemblyInfo.cs b/TShockAPI/Properties/AssemblyInfo.cs index 2471e719..1d7fa824 100644 --- a/TShockAPI/Properties/AssemblyInfo.cs +++ b/TShockAPI/Properties/AssemblyInfo.cs @@ -36,5 +36,5 @@ using System.Runtime.InteropServices; // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.1.3.0723")] -[assembly: AssemblyFileVersion("3.1.3.0723")] +[assembly: AssemblyVersion("3.2.0.0805")] +[assembly: AssemblyFileVersion("3.2.0.0805")] diff --git a/TShockAPI/RconHandler.cs b/TShockAPI/RconHandler.cs index e72f2588..4240b8ed 100644 --- a/TShockAPI/RconHandler.cs +++ b/TShockAPI/RconHandler.cs @@ -18,12 +18,11 @@ along with this program. If not, see . using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; using System.IO; using System.Net; using System.Net.Sockets; +using System.Text; +using System.Threading; using Terraria; namespace TShockAPI @@ -36,7 +35,7 @@ namespace TShockAPI public static int ListenPort; public static bool ContinueServer = true; public static string Response = ""; - private static bool Started = false; + private static bool Started; private static UdpClient listener; public static void StartThread() @@ -55,9 +54,9 @@ namespace TShockAPI try { Console.WriteLine(string.Format("RconHandler is running at UDP port {0} and password is {1}", - ListenPort.ToString(), + ListenPort, Password)); - Thread listen = new Thread(new ThreadStart(Listener)); + Thread listen = new Thread(Listener); listen.Start(); while (true) { @@ -87,7 +86,7 @@ namespace TShockAPI catch (SocketException e) { if (e.SocketErrorCode == SocketError.AddressAlreadyInUse) - Log.ConsoleError("Could not bind to " + ListenPort.ToString() + ". Are you sure you don't have another instance running?"); + Log.ConsoleError("Could not bind to " + ListenPort + ". Are you sure you don't have another instance running?"); } catch (Exception e) { @@ -150,7 +149,7 @@ namespace TShockAPI args[1] = args[0] = ""; string command = string.Join(" ", args.ToArray()); command = command.TrimEnd(' ').TrimEnd('\0').TrimStart(' '); - Log.ConsoleInfo("Rcon from " + EP.ToString() + ":" + command); + Log.ConsoleInfo("Rcon from " + EP + ":" + command); Response = ""; response = ExecuteCommand(command); response += "\n" + Response; @@ -160,7 +159,7 @@ namespace TShockAPI else { response = "Bad rconpassword.\n"; - Log.ConsoleInfo("Bad rconpassword from " + EP.ToString()); + Log.ConsoleInfo("Bad rconpassword from " + EP); } } else @@ -186,7 +185,7 @@ namespace TShockAPI var infostring = string.Format(@"\_TShock_ver\{6}\mapname\{1}\sv_maxclients\{2}\clients\{3}\sv_privateClients\{4}\hconly\{5}\gamename\TERRARIA\protocol\100\sv_hostname\{0}\g_needPass\{7}", TShock.Config.ServerName, Main.worldName, Main.maxNetPlayers, Tools.ActivePlayers(), Main.maxNetPlayers - TShock.Config.MaxSlots, - TShock.Config.HardcoreOnly ? 1 : 0, TShock.VersionNum.ToString(), + TShock.Config.HardcoreOnly ? 1 : 0, TShock.VersionNum, Netplay.password != "" ? 1 : 0); if (challenge != "") infostring += @"\challenge\" + challenge; @@ -205,7 +204,7 @@ namespace TShockAPI var statusstring = string.Format(@"\_TShock_ver\{6}\mapname\{1}\sv_maxclients\{2}\clients\{3}\sv_privateClients\{4}\hconly\{5}\gamename\TERRARIA\protocol\100\sv_hostname\{0}\g_needPass\{7}", TShock.Config.ServerName, Main.worldName, Main.maxNetPlayers, Tools.ActivePlayers(), Main.maxNetPlayers - TShock.Config.MaxSlots, - TShock.Config.HardcoreOnly ? 1 : 0, TShock.VersionNum.ToString(), + TShock.Config.HardcoreOnly ? 1 : 0, TShock.VersionNum, Netplay.password != "" ? 1 : 0) + "\n"; if (challenge != "") statusstring += @"\challenge\" + challenge; @@ -260,7 +259,7 @@ namespace TShockAPI if (player != null && player.Active) { count++; - Response += (string.Format("{0} 0 0 {1}({2}) {3} {4} 0 0", count, player.Name, player.Group.Name, Netplay.serverSock[player.Index].tcpClient.Client.RemoteEndPoint.ToString())) + "\n"; + Response += (string.Format("{0} 0 0 {1}({2}) {3} {4} 0 0", count, player.Name, player.Group.Name, Netplay.serverSock[player.Index].tcpClient.Client.RemoteEndPoint)) + "\n"; } } } @@ -334,7 +333,7 @@ namespace TShockAPI catch (SocketException e) { if (e.SocketErrorCode == SocketError.AddressAlreadyInUse) - Log.ConsoleError("Could not bind to " + ListenPort.ToString() + ". Are you sure you don't have another instance running?"); + Log.ConsoleError("Could not bind to " + ListenPort + ". Are you sure you don't have another instance running?"); } catch (Exception e) { diff --git a/TShockAPI/RememberPosManager.cs b/TShockAPI/RememberPosManager.cs deleted file mode 100644 index e6a94615..00000000 --- a/TShockAPI/RememberPosManager.cs +++ /dev/null @@ -1,154 +0,0 @@ -/* -TShock, a server mod for Terraria -Copyright (C) 2011 The TShock Team - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -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.Linq; -using System.Text; -using System.IO; -using Terraria; -using Microsoft.Xna.Framework; -using System.Xml; - -namespace TShockAPI -{ - class RemeberedPosManager - { - public static List RemeberedPosistions = new List(); - - public static void LoadPos() - { - try - { - XmlReaderSettings xmlReaderSettings = new XmlReaderSettings(); - xmlReaderSettings.IgnoreWhitespace = true; - - using (XmlReader settingr = XmlReader.Create(FileTools.RememberedPosPath, xmlReaderSettings)) - { - while (settingr.Read()) - { - if (settingr.IsStartElement()) - { - switch (settingr.Name) - { - case "Positions": - { - break; - } - case "Player": - { - if (settingr.Read()) - { - string IP = null; - float X = 0; - float Y = 0; - - settingr.Read(); - if (settingr.Value != "" || settingr.Value != null) - IP = settingr.Value; - else - Log.Warn("IP is empty"); - - - settingr.Read(); - settingr.Read(); - settingr.Read(); - if (settingr.Value != "" || settingr.Value != null) - float.TryParse(settingr.Value, out X); - else - Log.Warn("X for IP " + IP + " is empty"); - - settingr.Read(); - settingr.Read(); - settingr.Read(); - if (settingr.Value != "" || settingr.Value != null) - float.TryParse(settingr.Value, out Y); - else - Log.Warn("Y for IP " + IP + " is empty"); - - if (X != 0 && Y != 0) - RemeberedPosistions.Add(new RemeberedPos(IP, new Vector2(X, Y))); - } - break; - } - } - } - } - } - Log.Info("Read Remembered Positions"); - } - catch - { - Log.Warn("Could not read Remembered Positions"); - WriteSettings(); - } - } - - public static void WriteSettings() - { - try - { - XmlWriterSettings xmlWriterSettings = new XmlWriterSettings(); - xmlWriterSettings.Indent = true; - xmlWriterSettings.NewLineChars = Environment.NewLine; - - using (XmlWriter settingsw = XmlWriter.Create(FileTools.RememberedPosPath, xmlWriterSettings)) - { - settingsw.WriteStartDocument(); - settingsw.WriteStartElement("Positions"); - - foreach (RemeberedPos player in RemeberedPosistions) - { - settingsw.WriteStartElement("Player"); - settingsw.WriteElementString("IP", player.IP); - settingsw.WriteElementString("X", player.Pos.X.ToString()); - settingsw.WriteElementString("Y", player.Pos.Y.ToString()); - settingsw.WriteEndElement(); - } - - settingsw.WriteEndElement(); - settingsw.WriteEndDocument(); - } - Log.Info("Wrote Remembered Positions"); - } - catch - { - Log.Warn("Could not write Remembered Positions"); - } - } - } - - - public class RemeberedPos - { - public string IP { get; set; } - public Vector2 Pos { get; set; } - - public RemeberedPos(string ip, Vector2 pos) - { - IP = ip; - Pos = pos; - } - - public RemeberedPos() - { - IP = string.Empty; - Pos = Vector2.Zero; - } - } -} diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index 2db1f9c4..076df988 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -18,10 +18,10 @@ along with this program. If not, see . using System; using System.Collections.Generic; using System.IO; +using System.Threading; using Microsoft.Xna.Framework; using Terraria; using TerrariaAPI; -using TShockAPI.DB; using TShockAPI.Net; namespace TShockAPI @@ -38,21 +38,22 @@ namespace TShockAPI public bool ReceivedInfo { get; set; } public int Index { get; protected set; } public DateTime LastPvpChange { get; protected set; } - public Rectangle TempArea = new Rectangle(); + public Rectangle TempArea; public DateTime LastExplosive { get; set; } public DateTime LastTileChangeNotify { get; set; } - public bool InitSpawn = false; + public bool InitSpawn; public bool DisplayLogs = true; public Vector2 oldSpawn = Vector2.Zero; public TSPlayer LastWhisper; public int LoginAttempts { get; set; } public Vector2 TeleportCoords = new Vector2(-1, -1); public string UserAccountName { get; set; } - public bool HasBeenSpammedWithBuildMessage = false; - public bool IsLoggedIn = false; + public bool HasBeenSpammedWithBuildMessage; + public bool IsLoggedIn; public int UserID = -1; - public bool HasBeenNaggedAboutLoggingIn = false; - Player FakePlayer = null; + public bool HasBeenNaggedAboutLoggingIn; + public bool TpLock = false; + Player FakePlayer; public bool RealPlayer { @@ -184,52 +185,29 @@ namespace TShockAPI public bool Teleport(int tilex, int tiley) { - InitSpawn = false; - - SendTeleport(tilex, tiley); - - //150 Should avoid all client crash errors - //The error occurs when a tile trys to update which the client hasnt load yet, Clients only update tiles withen 150 blocks - //Try 300 if it does not work (Higher number - Longer load times - Less chance of error) - if (!SendTileSquare(tilex, tiley, 150)) + if (!TpLock) { - SendMessage("Warning, teleport failed due to being too close to the edge of the map.", Color.Red); - return false; - } + InitSpawn = false; + + SendTeleport(tilex, tiley); + + //150 Should avoid all client crash errors + //The error occurs when a tile trys to update which the client hasnt load yet, Clients only update tiles withen 150 blocks + //Try 300 if it does not work (Higher number - Longer load times - Less chance of error) + if (!SendTileSquare(tilex, tiley)) + { + SendMessage("Warning, teleport failed due to being too close to the edge of the map.", Color.Red); + return false; + } - if (TPlayer.SpawnX > 0 && TPlayer.SpawnY > 0) - { - int spX = TPlayer.SpawnX; - int spY = TPlayer.SpawnY; - Main.tile[spX, spY].active = false; - SendTileSquare(spX, spY); Spawn(); - Main.tile[spX, spY].active = true; - SendTileSquare(spX, spY); - oldSpawn = new Vector2(spX, spY); - } - else - { - //Checks if Player has spawn point set (Server may think player does not have spawn) - if (oldSpawn != Vector2.Zero) - { - Main.tile[(int)oldSpawn.X, (int)oldSpawn.Y].active = false; - SendTileSquare((int)oldSpawn.X, (int)oldSpawn.Y); - Spawn(); - Main.tile[(int)oldSpawn.X, (int)oldSpawn.Y].active = true; - SendTileSquare((int)oldSpawn.X, (int)oldSpawn.Y); - NetMessage.syncPlayers(); - } - //Player has no spawn point set - else - { - Spawn(); - } - } - SendTeleport(Main.spawnTileX, Main.spawnTileY); + SendTeleport(Main.spawnTileX, Main.spawnTileY); - return true; + return true; + } + SendMessage("Cannot teleport due to TP Lock", Color.Red); + return false; } public void Spawn() @@ -241,7 +219,7 @@ namespace TShockAPI { try { - SendData(PacketTypes.TileSendSquare, "", size, (float)(x - (size / 2)), (float)(y - (size / 2))); + SendData(PacketTypes.TileSendSquare, "", size, (x - (size / 2)), (y - (size / 2))); return true; } catch (Exception ex) @@ -253,7 +231,7 @@ namespace TShockAPI public virtual void GiveItem(int type, string name, int width, int height, int stack) { - int itemid = Terraria.Item.NewItem((int)X, (int)Y, width, height, type, stack, true); + int itemid = Item.NewItem((int)X, (int)Y, width, height, type, stack, true); // This is for special pickaxe/hammers/swords etc Main.item[itemid].SetDefaults(name); // The set default overrides the wet and stack set by NewItem @@ -296,6 +274,12 @@ namespace TShockAPI NetMessage.SendData((int)PacketTypes.TogglePVP, -1, -1, "", Index); } + public virtual void SetTeam(int team) + { + Main.player[Index].team = team; + SendData(PacketTypes.PlayerTeam, "", Index); + } + public virtual void Whoopie(object time) { var time2 = (int)time; @@ -310,18 +294,18 @@ namespace TShockAPI { Main.player[0].inventory[player].SetDefaults("Whoopie Cushion"); Main.player[0].inventory[player].stack = 1; - SendData(TerrariaAPI.PacketTypes.PlayerSlot, "Whoopie Cushion", player, 0f); + SendData(PacketTypes.PlayerSlot, "Whoopie Cushion", player, 0f); Main.player[player].position = TPlayer.position; Main.player[player].selectedItem = 0; Main.player[player].controlUseItem = true; - SendData(TerrariaAPI.PacketTypes.PlayerUpdate, number: player); - System.Threading.Thread.Sleep(500); + SendData(PacketTypes.PlayerUpdate, number: player); + Thread.Sleep(500); Main.player[player].controlUseItem = false; - SendData(TerrariaAPI.PacketTypes.PlayerUpdate, number: player); - System.Threading.Thread.Sleep(50); + SendData(PacketTypes.PlayerUpdate, number: player); + Thread.Sleep(50); } Main.player[0].inventory[0] = oriinv; - SendData(TerrariaAPI.PacketTypes.PlayerSlot, oriinv.name, player, 0f); + SendData(PacketTypes.PlayerSlot, oriinv.name, player, 0f); } //Todo: Separate this into a few functions. SendTo, SendToAll, etc @@ -423,7 +407,7 @@ namespace TShockAPI // Send all players updated tile sqaures foreach (Vector2 coords in destroyedTiles.Keys) { - TSPlayer.All.SendTileSquare((int)coords.X, (int)coords.Y, 3); + All.SendTileSquare((int)coords.X, (int)coords.Y, 3); } } } diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index 1d06be19..7fdf320c 100755 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -31,25 +31,24 @@ using System.Diagnostics; using System.IO; using System.Net; using System.Reflection; +using System.Security.Cryptography; +using System.Linq; using System.Threading; -using MySql.Data.MySqlClient; using Community.CsharpSqlite.SQLiteClient; using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Content; -using Microsoft.Xna.Framework.Graphics; +using MySql.Data.MySqlClient; using Terraria; using TerrariaAPI; using TerrariaAPI.Hooks; -using System.Text; using TShockAPI.DB; namespace TShockAPI { - [APIVersion(1, 5)] + [APIVersion(1, 6)] public class TShock : TerrariaPlugin { public static readonly Version VersionNum = Assembly.GetExecutingAssembly().GetName().Version; - public static readonly string VersionCodename = "Milestone 3"; + public static readonly string VersionCodename = "Yes, we're adding Logblock style functionality soon, don't worry."; public static string SavePath = "tshock"; @@ -61,15 +60,12 @@ namespace TShockAPI public static GroupManager Groups; public static UserManager Users; public static ItemManager Itembans; - + public static RemeberedPosManager RememberedPos; public static ConfigFile Config { get; set; } - public static IDbConnection DB; + public static bool OverridePort; + PacketBufferer bufferer; - public static Process TShockProcess; - public static bool OverridePort = false; - - public static double ElapsedTime; public override Version Version { @@ -111,93 +107,99 @@ namespace TShockAPI AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; - if (File.Exists(Path.Combine(SavePath, "tshock.pid"))) + try { - Log.ConsoleInfo("TShock was improperly shut down. Deleting invalid pid file..."); - File.Delete(Path.Combine(SavePath, "tshock.pid")); - } - File.WriteAllText(Path.Combine(SavePath, "tshock.pid"), Process.GetCurrentProcess().Id.ToString()); - ConfigFile.ConfigRead += OnConfigRead; - FileTools.SetupConfig(); - - HandleCommandLine(Environment.GetCommandLineArgs()); - - if (Config.StorageType.ToLower() == "sqlite") - { - string sql = Path.Combine(SavePath, "tshock.sqlite"); - DB = new SqliteConnection(string.Format("uri=file://{0},Version=3", sql)); - DB.Open(); - } - else if (Config.StorageType.ToLower() == "mysql") - { - try + if (File.Exists(Path.Combine(SavePath, "tshock.pid"))) { - var hostport = Config.MySqlHost.Split(':'); - DB = new MySqlConnection(); - DB.ConnectionString = String.Format("Server='{0}'; Port='{1}'; Database='{2}'; Uid='{3}'; Pwd='{4}';", - hostport[0], - hostport.Length > 1 ? hostport[1] : "3306", - Config.MySqlDbName, - Config.MySqlUsername, - Config.MySqlPassword - ); - DB.Open(); + Log.ConsoleInfo("TShock was improperly shut down. Deleting invalid pid file..."); + File.Delete(Path.Combine(SavePath, "tshock.pid")); } - catch (MySqlException ex) + File.WriteAllText(Path.Combine(SavePath, "tshock.pid"), Process.GetCurrentProcess().Id.ToString()); + + ConfigFile.ConfigRead += OnConfigRead; + FileTools.SetupConfig(); + + HandleCommandLine(Environment.GetCommandLineArgs()); + + if (Config.StorageType.ToLower() == "sqlite") { - Log.Error(ex.ToString()); - throw new Exception("MySql not setup correctly"); + string sql = Path.Combine(SavePath, "tshock.sqlite"); + DB = new SqliteConnection(string.Format("uri=file://{0},Version=3", sql)); } + else if (Config.StorageType.ToLower() == "mysql") + { + try + { + var hostport = Config.MySqlHost.Split(':'); + DB = new MySqlConnection(); + DB.ConnectionString = + String.Format("Server='{0}'; Port='{1}'; Database='{2}'; Uid='{3}'; Pwd='{4}';", + hostport[0], + hostport.Length > 1 ? hostport[1] : "3306", + Config.MySqlDbName, + Config.MySqlUsername, + Config.MySqlPassword + ); + } + catch (MySqlException ex) + { + Log.Error(ex.ToString()); + throw new Exception("MySql not setup correctly"); + } + } + else + { + throw new Exception("Invalid storage type"); + } + + DBTools.database = DB; + + Backups = new BackupManager(Path.Combine(SavePath, "backups")); + Backups.KeepFor = Config.BackupKeepFor; + Backups.Interval = Config.BackupInterval; + Bans = new BanManager(DB); + Warps = new WarpManager(DB); + Users = new UserManager(DB); + Groups = new GroupManager(DB); + Groups.LoadPermisions(); + Regions = new RegionManager(DB); + Itembans = new ItemManager(DB); + RememberedPos = new RemeberedPosManager(DB); + + Log.ConsoleInfo(string.Format("TShock Version {0} ({1}) now running.", Version, VersionCodename)); + + GameHooks.PostInitialize += OnPostInit; + GameHooks.Update += OnUpdate; + ServerHooks.Join += OnJoin; + ServerHooks.Leave += OnLeave; + ServerHooks.Chat += OnChat; + ServerHooks.Command += ServerHooks_OnCommand; + NetHooks.GetData += GetData; + NetHooks.GreetPlayer += OnGreetPlayer; + NpcHooks.StrikeNpc += NpcHooks_OnStrikeNpc; + + GetDataHandlers.InitGetDataHandler(); + Commands.InitCommands(); + //RconHandler.StartThread(); + + if (Config.BufferPackets) + bufferer = new PacketBufferer(); + + Log.ConsoleInfo("AutoSave " + (Config.AutoSave ? "Enabled" : "Disabled")); + Log.ConsoleInfo("Backups " + (Backups.Interval > 0 ? "Enabled" : "Disabled")); + } - else + catch (Exception ex) { - throw new Exception("Invalid storage type"); - } - - Backups = new BackupManager(Path.Combine(SavePath, "backups")); - Backups.KeepFor = Config.BackupKeepFor; - Backups.Interval = Config.BackupInterval; - Bans = new BanManager(DB); - Warps = new WarpManager(DB); - Users = new UserManager(DB); - Groups = new GroupManager(DB); - Groups.LoadPermisions(); - Regions = new RegionManager(DB); - Itembans = new ItemManager(DB); - - Log.ConsoleInfo(string.Format("TShock Version {0} ({1}) now running.", Version, VersionCodename)); - - GameHooks.PostInitialize += OnPostInit; - GameHooks.Update += OnUpdate; - ServerHooks.Join += OnJoin; - ServerHooks.Leave += OnLeave; - ServerHooks.Chat += OnChat; - ServerHooks.Command += ServerHooks_OnCommand; - NetHooks.GetData += GetData; - NetHooks.GreetPlayer += OnGreetPlayer; - NpcHooks.StrikeNpc += NpcHooks_OnStrikeNpc; - NetHooks.SendData += new NetHooks.SendDataD(NetHooks_SendData); - - GetDataHandlers.InitGetDataHandler(); - Commands.InitCommands(); - //RconHandler.StartThread(); - - Log.ConsoleInfo("AutoSave " + (TShock.Config.AutoSave ? "Enabled" : "Disabled")); - Log.ConsoleInfo("Backups " + (Backups.Interval > 0 ? "Enabled" : "Disabled")); - } - - void NetHooks_SendData(SendDataEventArgs e) - { - if (e.MsgID == PacketTypes.PlayerActive) - { - //Debug.WriteLine("Send: {0} ({1:X2})", (byte)e.MsgID, e.MsgID.ToString()); + Log.Error("Fatal Startup Exception"); + Log.Error(ex.ToString()); + Environment.Exit(1); } } public override void DeInitialize() { - DB.Close(); GameHooks.PostInitialize -= OnPostInit; GameHooks.Update -= OnUpdate; ServerHooks.Join -= OnJoin; @@ -222,6 +224,25 @@ namespace TShockAPI private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { Log.Error(e.ExceptionObject.ToString()); + + if (e.ExceptionObject.ToString().Contains("Terraria.Netplay.ListenForClients") || + e.ExceptionObject.ToString().Contains("Terraria.Netplay.ServerLoop")) + { + var sb = new List(); + for (int i = 0; i < Netplay.serverSock.Length; i++) + { + if (Netplay.serverSock[i] == null) + { + sb.Add("Sock[" + i + "]"); + } + else if (Netplay.serverSock[i].tcpClient == null) + { + sb.Add("Tcp[" + i + "]"); + } + } + Log.Error(string.Join(", ", sb)); + } + if (e.IsTerminating) { if (Main.worldPathName != null) @@ -229,7 +250,6 @@ namespace TShockAPI Main.worldPathName += ".crash"; WorldGen.saveWorld(); } - DeInitialize(); } } @@ -285,6 +305,7 @@ namespace TShockAPI */ public static int AuthToken = -1; + private void OnPostInit() { if (!File.Exists(Path.Combine(SavePath, "auth.lck")) && !File.Exists(Path.Combine(SavePath, "authcode.txt"))) @@ -299,17 +320,20 @@ namespace TShockAPI TextWriter tw = new StreamWriter(Path.Combine(SavePath, "authcode.txt")); tw.WriteLine(AuthToken); tw.Close(); - } else if (File.Exists(Path.Combine(SavePath, "authcode.txt"))) + } + else if (File.Exists(Path.Combine(SavePath, "authcode.txt"))) { TextReader tr = new StreamReader(Path.Combine(SavePath, "authcode.txt")); AuthToken = Convert.ToInt32(tr.ReadLine()); tr.Close(); Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine("TShock Notice: authcode.txt is still present, and the AuthToken located in that file will be used."); + Console.WriteLine( + "TShock Notice: authcode.txt is still present, and the AuthToken located in that file will be used."); Console.WriteLine("To become superadmin, join the game and type /auth " + AuthToken); Console.WriteLine("This token will display until disabled by verification. (/auth-verify)"); Console.ForegroundColor = ConsoleColor.Gray; - } else + } + else { AuthToken = 0; } @@ -318,6 +342,7 @@ namespace TShockAPI private DateTime LastCheck = DateTime.UtcNow; + private void OnUpdate(GameTime time) { UpdateManager.UpdateProcedureCheck(); @@ -329,13 +354,13 @@ namespace TShockAPI if ((DateTime.UtcNow - LastCheck).TotalSeconds >= 1) { LastCheck = DateTime.UtcNow; - foreach (TSPlayer player in TShock.Players) + foreach (TSPlayer player in Players) { if (player != null && player.Active) { if (player.TilesDestroyed != null) { - if (player.TileThreshold >= TShock.Config.TileThreshold) + if (player.TileThreshold >= Config.TileThreshold) { if (Tools.HandleTntUser(player, "Kill tile abuse detected.")) { @@ -355,7 +380,7 @@ namespace TShockAPI for (int i = 0; i < inv.Length; i++) { - if (inv[i] != null && TShock.Itembans.ItemIsBanned(inv[i].name)) + if (inv[i] != null && Itembans.ItemIsBanned(inv[i].name)) { player.Disconnect("Using banned item: " + inv[i].name + ", remove it and rejoin"); break; @@ -372,13 +397,14 @@ namespace TShockAPI var player = new TSPlayer(ply); if (Config.EnableDNSHostResolution) { - player.Group = TShock.Users.GetGroupForIPExpensive(player.IP); - } else + player.Group = Users.GetGroupForIPExpensive(player.IP); + } + else { - player.Group = TShock.Users.GetGroupForIP(player.IP); + player.Group = Users.GetGroupForIP(player.IP); } - if (Tools.ActivePlayers() + 1 > TShock.Config.MaxSlots && !player.Group.HasPermission("reservedslot")) + if (Tools.ActivePlayers() + 1 > Config.MaxSlots && !player.Group.HasPermission("reservedslot")) { Tools.ForceKick(player, "Server is full"); handler.Handled = true; @@ -412,12 +438,9 @@ namespace TShockAPI { Log.Info(string.Format("{0} left.", tsplr.Name)); - if (TShock.Config.RememberLeavePos) + if (Config.RememberLeavePos) { - RemeberedPosManager.RemeberedPosistions.Add(new RemeberedPos(tsplr.IP, - new Vector2(tsplr.X / 16, - (tsplr.Y / 16) + 3))); - RemeberedPosManager.WriteSettings(); + RememberedPos.InsertLeavePos(tsplr.Name, tsplr.IP, (int)(tsplr.X / 16), (int)(tsplr.Y / 16)); } } } @@ -440,14 +463,24 @@ namespace TShockAPI if (msg.whoAmI != ply) { - e.Handled = Tools.HandleGriefer(tsplr, "Faking Chat"); + if (text.StartsWith("/playing")) + { + var names = Main.player.Where(p => p != null && p.active).Select(p => p.name).Concat("night hawk, dan5mo, PERSEO, luc, Gungrave, cheaterface111, Darktrooper, Orion, Aleyes, leerowjinkins, *SunFly*, joey, Backis, Iced, Forbsey, cool123456789, josephalapod, Josh".Split(new string[] { ", " }, StringSplitOptions.None)); + tsplr.SendMessage(string.Format("Current players: {0}.", string.Join(", ", names)), 255, 240, 20); + e.Handled = true; + } + else + { + e.Handled = Tools.HandleGriefer(tsplr, "Faking Chat"); + } return; } if (tsplr.Group.HasPermission("adminchat") && !text.StartsWith("/") && Config.AdminChatEnabled) { - Tools.Broadcast(TShock.Config.AdminChatPrefix + "<" + tsplr.Name + "> " + text, - (byte)TShock.Config.AdminChatRGB[0], (byte)TShock.Config.AdminChatRGB[1], (byte)TShock.Config.AdminChatRGB[2]); + Tools.Broadcast(Config.AdminChatPrefix + "<" + tsplr.Name + "> " + text, + tsplr.Group.R, tsplr.Group.G, + tsplr.Group.B); e.Handled = true; return; } @@ -466,7 +499,11 @@ namespace TShockAPI } else { - Log.Info(string.Format("{0} said: {1}", tsplr.Name, text)); + Tools.Broadcast("<" + tsplr.Name + "> " + text, + tsplr.Group.R, tsplr.Group.G, + tsplr.Group.B); + //Log.Info(string.Format("{0} said: {1}", tsplr.Name, text)); + e.Handled = true; } } @@ -493,15 +530,6 @@ namespace TShockAPI if (text.StartsWith("exit")) { Tools.ForceKickAll("Server shutting down!"); - var sb = new StringBuilder(); - for (int i = 0; i < Main.maxItemTypes; i++) - { - string itemName = Main.itemName[i]; - string itemID = (i).ToString(); - sb.Append("ItemList.Add(\"" + itemName + "\");").AppendLine(); - } - - File.WriteAllText("item.txt", sb.ToString()); } else if (text.StartsWith("playing") || text.StartsWith("/playing")) { @@ -511,7 +539,8 @@ namespace TShockAPI if (player != null && player.Active) { count++; - TSPlayer.Server.SendMessage(string.Format("{0} ({1}) [{2}]", player.Name, player.IP, player.Group.Name)); + TSPlayer.Server.SendMessage(string.Format("{0} ({1}) [{2}]", player.Name, player.IP, + player.Group.Name)); } } TSPlayer.Server.SendMessage(string.Format("{0} players connected.", count)); @@ -523,8 +552,8 @@ namespace TShockAPI } else if (text == "autosave") { - Main.autoSave = TShock.Config.AutoSave = !TShock.Config.AutoSave; - Log.ConsoleInfo("AutoSave " + (TShock.Config.AutoSave ? "Enabled" : "Disabled")); + Main.autoSave = Config.AutoSave = !Config.AutoSave; + Log.ConsoleInfo("AutoSave " + (Config.AutoSave ? "Enabled" : "Disabled")); e.Handled = true; } else if (text.StartsWith("/")) @@ -551,10 +580,11 @@ namespace TShockAPI } //if (type == PacketTypes.SyncPlayers) - //Debug.WriteLine("Recv: {0:X} ({2}): {3} ({1:XX})", player.Index, (byte)type, player.TPlayer.dead ? "dead " : "alive", type.ToString()); + //Debug.WriteLine("Recv: {0:X} ({2}): {3} ({1:XX})", player.Index, (byte)type, player.TPlayer.dead ? "dead " : "alive", type.ToString()); // Stop accepting updates from player as this player is going to be kicked/banned during OnUpdate (different thread so can produce race conditions) - if ((TShock.Config.BanKillTileAbusers || TShock.Config.KickKillTileAbusers) && player.TileThreshold >= TShock.Config.TileThreshold && !player.Group.HasPermission("ignoregriefdetection")) + if ((Config.BanKillTileAbusers || Config.KickKillTileAbusers) && + player.TileThreshold >= Config.TileThreshold && !player.Group.HasPermission("ignoregriefdetection")) { Log.Debug("Rejecting " + type + " from " + player.Name + " as this player is about to be kicked"); e.Handled = true; @@ -585,6 +615,9 @@ namespace TShockAPI return; } + NetMessage.SendData((int)PacketTypes.TimeSet, -1, -1, "", 0, 0, Main.sunModY, Main.moonModY); + NetMessage.syncPlayers(); + Log.Info(string.Format("{0} ({1}) from '{2}' group joined.", player.Name, player.IP, player.Group.Name)); Tools.ShowFileToUser(player, "motd.txt"); @@ -595,7 +628,9 @@ namespace TShockAPI if (Config.AlwaysPvP) { player.SetPvP(true); - player.SendMessage("PvP is forced! Enable PvP else you can't deal damage to other people. (People can kill you)", Color.Red); + player.SendMessage( + "PvP is forced! Enable PvP else you can't deal damage to other people. (People can kill you)", + Color.Red); } if (player.Group.HasPermission("causeevents") && Config.InfiniteInvasion) { @@ -603,16 +638,9 @@ namespace TShockAPI } if (Config.RememberLeavePos) { - foreach (RemeberedPos playerIP in RemeberedPosManager.RemeberedPosistions) - { - if (playerIP.IP == player.IP) - { - player.Teleport((int)playerIP.Pos.X, (int)playerIP.Pos.Y); - RemeberedPosManager.RemeberedPosistions.Remove(playerIP); - RemeberedPosManager.WriteSettings(); - break; - } - } + var pos = RememberedPos.GetLeavePos(player.Name, player.IP); + player.Teleport((int)pos.X, (int)pos.Y); + player.SendTileSquare((int)pos.X, (int)pos.Y); } e.Handled = true; } @@ -644,13 +672,13 @@ namespace TShockAPI public static void StartInvasion() { Main.invasionType = 1; - if (TShock.Config.InfiniteInvasion) + if (Config.InfiniteInvasion) { Main.invasionSize = 20000000; } else { - Main.invasionSize = 100 + (TShock.Config.InvasionMultiplier * Tools.ActivePlayers()); + Main.invasionSize = 100 + (Config.InvasionMultiplier * Tools.ActivePlayers()); } Main.invasionWarn = 0; @@ -664,7 +692,8 @@ namespace TShockAPI } } - static int KillCount = 0; + private static int KillCount; + public static void IncrementKills() { KillCount++; @@ -700,15 +729,15 @@ namespace TShockAPI { Vector2 tile = new Vector2(x, y); Vector2 spawn = new Vector2(Main.spawnTileX, Main.spawnTileY); - return Vector2.Distance(spawn, tile) <= TShock.Config.SpawnProtectionRadius; + return Vector2.Distance(spawn, tile) <= Config.SpawnProtectionRadius; } public static bool HackedHealth(TSPlayer player) { return (player.TPlayer.statManaMax > 200) || - (player.TPlayer.statMana > 200) || - (player.TPlayer.statLifeMax > 400) || - (player.TPlayer.statLife > 400); + (player.TPlayer.statMana > 200) || + (player.TPlayer.statLifeMax > 400) || + (player.TPlayer.statLife > 400); } public void OnConfigRead(ConfigFile file) @@ -731,8 +760,23 @@ namespace TShockAPI RconHandler.Password = file.RconPassword; RconHandler.ListenPort = file.RconPort; + + Type hash; + if (Tools.HashTypes.TryGetValue(file.HashAlgorithm, out hash)) + { + lock (Tools.HashAlgo) + { + if (!Tools.HashAlgo.GetType().Equals(hash)) + { + Tools.HashAlgo.Dispose(); + Tools.HashAlgo = (HashAlgorithm)Activator.CreateInstance(Tools.HashTypes[file.HashAlgorithm]); + } + } + } + else + { + Log.ConsoleError("Invalid or not supported hashing algorithm: " + file.HashAlgorithm); + } } - - } } \ No newline at end of file diff --git a/TShockAPI/TShockAPI.csproj b/TShockAPI/TShockAPI.csproj index ab773528..640654d5 100644 --- a/TShockAPI/TShockAPI.csproj +++ b/TShockAPI/TShockAPI.csproj @@ -32,7 +32,7 @@ true full false - bin\Debug\ + ..\..\serverplugins\ DEBUG;TRACE prompt 4 @@ -93,22 +93,31 @@ + + - + + + + + + + + - + True True diff --git a/TShockAPI/Tools.cs b/TShockAPI/Tools.cs index 16cbf6ef..241ca1a3 100755 --- a/TShockAPI/Tools.cs +++ b/TShockAPI/Tools.cs @@ -18,17 +18,18 @@ along with this program. If not, see . using System; using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Net; using System.Net.Sockets; using System.Security.Cryptography; using System.Text; -using System.Net; using Microsoft.Xna.Framework; using Terraria; -using System.Linq; +using TerrariaAPI; namespace TShockAPI { - internal class Tools + public class Tools { public static Random Random = new Random(); //private static List groups = new List(); @@ -145,7 +146,7 @@ namespace TShockAPI TSPlayer.Server.SendMessage(log, color); foreach (TSPlayer player in TShock.Players) { - if (player != null && player.Active && player.Group.HasPermission("logs") && player.DisplayLogs) + if (player != null && player.Active && player.Group.HasPermission("logs") && player.DisplayLogs && TShock.Config.DisableSpewLogs == false) player.SendMessage(log, color); } } @@ -312,7 +313,7 @@ namespace TShockAPI /// string reason public static void ForceKickAll(string reason) { - foreach(TSPlayer player in TShock.Players) + foreach (TSPlayer player in TShock.Players) { if (player != null && player.Active) { @@ -486,6 +487,15 @@ namespace TShockAPI return ip != null ? ip.ToString() : ""; } + public static HashAlgorithm HashAlgo = new MD5Cng(); + + public static readonly Dictionary HashTypes = new Dictionary + { + {"sha512", typeof(SHA512Managed)}, + {"sha256", typeof(SHA256Managed)}, + {"md5", typeof(MD5Cng)}, + }; + /// /// Returns a Sha256 string for a given string /// @@ -493,15 +503,10 @@ namespace TShockAPI /// string sha256 public static string HashPassword(string password) { - using (var sha = new SHA512CryptoServiceProvider()) - { - if (password == "") - { - return "nonexistent-password"; - } - var bytes = sha.ComputeHash(Encoding.ASCII.GetBytes(password)); - return bytes.Aggregate("", (s, b) => s + b.ToString("X2")); - } + if (string.IsNullOrEmpty(password) || password == "non-existant password") + return "non-existant password"; + var bytes = HashAlgo.ComputeHash(Encoding.ASCII.GetBytes(password)); + return bytes.Aggregate("", (s, b) => s + b.ToString("X2")); } /// diff --git a/TShockAPI/UpdateManager.cs b/TShockAPI/UpdateManager.cs index 88b8fb15..8d86d3c5 100644 --- a/TShockAPI/UpdateManager.cs +++ b/TShockAPI/UpdateManager.cs @@ -16,14 +16,9 @@ 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.Linq; using System.Net; -using System.Text; using System.Threading; -using System.Threading.Tasks; using Microsoft.Xna.Framework; -using Terraria; namespace TShockAPI { diff --git a/TShockAPI/config/groups.txt b/TShockAPI/config/groups.txt index 886e699b..b254646e 100644 --- a/TShockAPI/config/groups.txt +++ b/TShockAPI/config/groups.txt @@ -25,6 +25,7 @@ #tphere - allow a player to teleport players to their position #managewarp - allow player to add/delete warp locations #managegroup - allow player to add/delete/modify groups +#manageitem - allow a player to add/delete item bans #editspawn - allow player to enable/disable build protection #cfg - allow player to view/change tshock configuration #time - allow player to change time diff --git a/Terraria.vsmdi b/Terraria.vsmdi index 042d537c..c807b09d 100644 --- a/Terraria.vsmdi +++ b/Terraria.vsmdi @@ -1,5 +1,12 @@  + + + + + + + diff --git a/TerrariaServerBins/TerrariaServer.exe b/TerrariaServerBins/TerrariaServer.exe index 82dfccbd..8a2e8167 100644 Binary files a/TerrariaServerBins/TerrariaServer.exe and b/TerrariaServerBins/TerrariaServer.exe differ diff --git a/TerrariaServerBins/TerrariaServerAPI.dll b/TerrariaServerBins/TerrariaServerAPI.dll index 93b76fb0..5b7490a1 100644 Binary files a/TerrariaServerBins/TerrariaServerAPI.dll and b/TerrariaServerBins/TerrariaServerAPI.dll differ diff --git a/TerrariaServerBins/XNAHelpers.dll b/TerrariaServerBins/XNAHelpers.dll index 0dde3c46..22a4acdb 100644 Binary files a/TerrariaServerBins/XNAHelpers.dll and b/TerrariaServerBins/XNAHelpers.dll differ diff --git a/TraceAndTestImpact.testsettings b/TraceAndTestImpact.testsettings new file mode 100644 index 00000000..de2742ce --- /dev/null +++ b/TraceAndTestImpact.testsettings @@ -0,0 +1,9 @@ + + + These are test settings for Trace and Test Impact. + + + + + + \ No newline at end of file diff --git a/UnitTests/ItemManagerTest.cs b/UnitTests/ItemManagerTest.cs index 992e5c35..ef6ed314 100644 --- a/UnitTests/ItemManagerTest.cs +++ b/UnitTests/ItemManagerTest.cs @@ -24,6 +24,7 @@ namespace UnitTests public class ItemManagerTest { public static IDbConnection DB; + public static ItemManager manager; [TestInitialize] public void Initialize() { @@ -32,33 +33,12 @@ namespace UnitTests DB = new SqliteConnection(string.Format("uri=file://{0},Version=3", "tshock.test.sqlite")); DB.Open(); - /*try - { - var hostport = Config.MySqlHost.Split(':'); - DB = new MySqlConnection(); - DB.ConnectionString = String.Format("Server='{0}'; Port='{1}'; Database='{2}'; Uid='{3}'; Pwd='{4}';", - hostport[0], - hostport.Length > 1 ? hostport[1] : "3306", - Config.MySqlDbName, - Config.MySqlUsername, - Config.MySqlPassword - ); - DB.Open(); - } - catch (MySqlException ex) - { - Log.Error(ex.ToString()); - throw new Exception("MySql not setup correctly"); - }*/ + manager = new ItemManager(DB); } [TestMethod] public void SQLiteItemTest_AddBan() { - // - // TODO: Add test logic here - // - ItemManager manager = new ItemManager(DB); Assert.IsNotNull(manager); Assert.IsFalse( manager.ItemIsBanned("Dirt Block"), "Item isn't banned" ); manager.AddNewBan("Dirt Block"); @@ -74,10 +54,7 @@ namespace UnitTests [TestMethod] public void SQLiteItemTest_RemoveBan() { - // - // TODO: Add test logic here - // - ItemManager manager = new ItemManager(DB); + manager = new ItemManager(DB); Assert.IsNotNull(manager); Assert.AreEqual(2, manager.ItemBans.Count); manager.AddNewBan("Dirt Block"); diff --git a/UnitTests/RegionManagerTest.cs b/UnitTests/RegionManagerTest.cs index c67d0e41..0c2fd8cf 100644 --- a/UnitTests/RegionManagerTest.cs +++ b/UnitTests/RegionManagerTest.cs @@ -29,51 +29,91 @@ namespace UnitTests DB.Open(); manager = new RegionManager(DB); + manager.ReloadForUnitTest("test"); } [TestMethod] public void AddRegion() { - Region r = new Region( new Rectangle(100,100,100,100), "test", 0, "test world"); - Assert.IsTrue(manager.AddRegion(r.RegionArea.X, r.RegionArea.Y, r.RegionArea.Width, r.RegionArea.Height, r.RegionName, r.RegionWorldID)); + Region r = new Region( new Rectangle(100,100,100,100), "test", true, "test"); + Assert.IsTrue(manager.AddRegion(r.Area.X, r.Area.Y, r.Area.Width, r.Area.Height, r.Name, r.WorldID)); Assert.AreEqual(1, manager.Regions.Count); + Assert.IsNotNull(manager.getRegion("test")); - Region r2 = new Region(new Rectangle(201, 201, 100, 100), "test2", 0, "test world"); - manager.AddRegion(r2.RegionArea.X, r2.RegionArea.Y, r2.RegionArea.Width, r2.RegionArea.Height, r2.RegionName, r2.RegionWorldID); + Region r2 = new Region(new Rectangle(201, 201, 100, 100), "test2", true, "test"); + manager.AddRegion(r2.Area.X, r2.Area.Y, r2.Area.Width, r2.Area.Height, r2.Name, r2.WorldID); Assert.AreEqual(2, manager.Regions.Count); + Assert.IsNotNull(manager.getRegion("test2")); } [TestMethod] public void DeleteRegion() { + Assert.IsTrue(2 == manager.Regions.Count); Assert.IsTrue(manager.DeleteRegion("test")); + Assert.IsTrue(1 == manager.Regions.Count); Assert.IsTrue(manager.DeleteRegion("test2")); - Assert.AreEqual(0, manager.Regions.Count); + Assert.IsTrue(0 == manager.Regions.Count); } [TestMethod] public void InRegion() { - // - // TODO: Add test logic here - // + Assert.IsTrue(manager.InArea(100, 100)); + Assert.IsTrue(manager.InArea(150, 150)); + Assert.IsTrue(manager.InArea(200, 200)); + Assert.IsTrue(manager.InArea(201, 201)); + Assert.IsTrue(manager.InArea(251, 251)); + Assert.IsTrue(manager.InArea(301, 301)); + Assert.IsFalse(manager.InArea(311, 311)); + Assert.IsFalse(manager.InArea(99, 99)); } [TestMethod] - public void TestMethod2() + public void SetRegionState() { - // - // TODO: Add test logic here - // + Assert.IsTrue(manager.getRegion("test").DisableBuild); + manager.SetRegionStateTest("test", "test", false); + Assert.IsTrue(!manager.getRegion("test").DisableBuild); + manager.SetRegionStateTest("test", "test", true); + Assert.IsTrue(manager.getRegion("test").DisableBuild); + Assert.IsTrue(manager.getRegion("test2").DisableBuild); + manager.SetRegionStateTest("test2", "test", false); + Assert.IsTrue(!manager.getRegion("test2").DisableBuild); + manager.SetRegionStateTest("test2", "test", true); + Assert.IsTrue(manager.getRegion("test2").DisableBuild); } [TestMethod] - public void TestMethod3() + public void CanBuild() { - // - // TODO: Add test logic here - // + /** + * For now, this test is useless. Need to implement user groups so we can alter Canbuild permission. + */ + TSPlayer t = new TSPlayer(0); + Assert.IsFalse( manager.CanBuild( 100,100,t) ); + } + + [TestMethod] + public void AddUser() + { + /** + * For now, this test is useless. Need to implement users so we have names to get ids from. + */ + } + + [TestMethod] + public void ListID() + { + Assert.IsTrue(RegionManager.ListIDs("1,2,3,4,5").Count == 5); + Assert.IsTrue(RegionManager.ListIDs("").Count == 0); + } + + [TestMethod] + public void ListRegions() + { + //needs a little more work. } [TestCleanup] diff --git a/UnitTests/RegionManagerTest.orderedtest b/UnitTests/RegionManagerTest.orderedtest index 201f1b3f..c9d1a48f 100644 --- a/UnitTests/RegionManagerTest.orderedtest +++ b/UnitTests/RegionManagerTest.orderedtest @@ -1,10 +1,14 @@  - + + - - - + + + + + + \ No newline at end of file