diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..026167bb --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +* text=auto +*.cs text eol=crlf +*.sln text eol=crlf +*.csproj text eol=crlf +*.vsmdi text eol=crlf diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs index 43f43112..127fe7af 100644 --- a/TShockAPI/Commands.cs +++ b/TShockAPI/Commands.cs @@ -244,8 +244,8 @@ namespace TShockAPI add(Permissions.savessi, SaveSSI, "savessi"); add(Permissions.savessi, OverrideSSI, "overridessi", "ossi"); add(Permissions.xmas, ForceXmas, "forcexmas"); - add(Permissions.settempgroup, TempGroup, "tempgroup"); - add(null, Aliases, "aliases"); + add(Permissions.settempgroup, TempGroup, "tempgroup"); + add(null, Aliases, "aliases"); add(Rests.RestPermissions.restmanage, ManageRest, "rest"); //add(null, TestCallbackCommand, "test"); @@ -1284,11 +1284,11 @@ namespace TShockAPI private static void Broadcast(CommandArgs args) { - string message = string.Join(" ", args.Parameters); + string message = string.Join(" ", args.Parameters); - TShock.Utils.Broadcast( - "(Server Broadcast) " + message, - Convert.ToByte(TShock.Config.BroadcastRGB[0]), Convert.ToByte(TShock.Config.BroadcastRGB[1]), + TShock.Utils.Broadcast( + "(Server Broadcast) " + message, + Convert.ToByte(TShock.Config.BroadcastRGB[0]), Convert.ToByte(TShock.Config.BroadcastRGB[1]), Convert.ToByte(TShock.Config.BroadcastRGB[2])); } @@ -1318,7 +1318,7 @@ namespace TShockAPI } else { - string reason = ((args.Parameters.Count > 0) ? "Server shutting down: " + String.Join(" ", args.Parameters) : "Server shutting down!"); + string reason = ((args.Parameters.Count > 0) ? "Server shutting down: " + String.Join(" ", args.Parameters) : "Server shutting down!"); TShock.Utils.RestartServer(true, reason); } } @@ -1340,57 +1340,57 @@ namespace TShockAPI args.Player.SendInfoMessage("Starting plugin update process:"); args.Player.SendInfoMessage("This may take a while, do not turn off the server!"); new PluginUpdaterThread(args.Player); - } - - private static void ManageRest(CommandArgs args) - { - string subCommand = "help"; - if (args.Parameters.Count > 0) - subCommand = args.Parameters[0]; + } - switch(subCommand.ToLower()) - { - case "listusers": - { - int pageNumber; - if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber)) - return; - - Dictionary restUsersTokens = new Dictionary(); - foreach (Rests.SecureRest.TokenData tokenData in TShock.RestApi.Tokens.Values) - { - if (restUsersTokens.ContainsKey(tokenData.Username)) - restUsersTokens[tokenData.Username]++; - else - restUsersTokens.Add(tokenData.Username, 1); - } - - List restUsers = new List( - restUsersTokens.Select(ut => string.Format("{0} ({1} tokens)", ut.Key, ut.Value))); - - PaginationTools.SendPage( - args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(restUsers), new PaginationTools.Settings { - NothingToDisplayString = "There are currently no active REST users.", - HeaderFormat = "Active REST Users ({0}/{1}):", - FooterFormat = "Type /rest listusers {0} for more." - } - ); - - break; - } - case "destroytokens": - { - TShock.RestApi.Tokens.Clear(); - args.Player.SendSuccessMessage("All REST tokens have been destroyed."); - break; - } - default: - { - args.Player.SendInfoMessage("Available REST Sub-Commands:"); - args.Player.SendMessage("listusers - Lists all REST users and their current active tokens.", Color.White); - args.Player.SendMessage("destroytokens - Destroys all current REST tokens.", Color.White); - break; - } + private static void ManageRest(CommandArgs args) + { + string subCommand = "help"; + if (args.Parameters.Count > 0) + subCommand = args.Parameters[0]; + + switch(subCommand.ToLower()) + { + case "listusers": + { + int pageNumber; + if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber)) + return; + + Dictionary restUsersTokens = new Dictionary(); + foreach (Rests.SecureRest.TokenData tokenData in TShock.RestApi.Tokens.Values) + { + if (restUsersTokens.ContainsKey(tokenData.Username)) + restUsersTokens[tokenData.Username]++; + else + restUsersTokens.Add(tokenData.Username, 1); + } + + List restUsers = new List( + restUsersTokens.Select(ut => string.Format("{0} ({1} tokens)", ut.Key, ut.Value))); + + PaginationTools.SendPage( + args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(restUsers), new PaginationTools.Settings { + NothingToDisplayString = "There are currently no active REST users.", + HeaderFormat = "Active REST Users ({0}/{1}):", + FooterFormat = "Type /rest listusers {0} for more." + } + ); + + break; + } + case "destroytokens": + { + TShock.RestApi.Tokens.Clear(); + args.Player.SendSuccessMessage("All REST tokens have been destroyed."); + break; + } + default: + { + args.Player.SendInfoMessage("Available REST Sub-Commands:"); + args.Player.SendMessage("listusers - Lists all REST users and their current active tokens.", Color.White); + args.Player.SendMessage("destroytokens - Destroys all current REST tokens.", Color.White); + break; + } } } @@ -2044,8 +2044,8 @@ namespace TShockAPI } } #endregion - return; - + return; + case "parent": #region Parent { @@ -2053,45 +2053,45 @@ namespace TShockAPI { args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /group parent [new parent group name]"); return; - } - - string groupName = args.Parameters[1]; - Group group = TShock.Groups.GetGroupByName(groupName); - if (group == null) - { - args.Player.SendErrorMessage("No such group \"{0}\".", groupName); - return; - } - - if (args.Parameters.Count > 2) - { - string newParentGroupName = string.Join(" ", args.Parameters.Skip(2)); - if (!string.IsNullOrWhiteSpace(newParentGroupName) && !TShock.Groups.GroupExists(newParentGroupName)) - { - args.Player.SendErrorMessage("No such group \"{0}\".", newParentGroupName); - return; - } - - try - { - TShock.Groups.UpdateGroup(groupName, newParentGroupName, group.Permissions, group.ChatColor); - - if (!string.IsNullOrWhiteSpace(newParentGroupName)) - args.Player.SendSuccessMessage("Parent of group \"{0}\" set to \"{1}\".", groupName, newParentGroupName); - else - args.Player.SendSuccessMessage("Removed parent of group \"{0}\".", groupName); - } - catch (GroupManagerException ex) - { - args.Player.SendErrorMessage(ex.Message); - } - } - else - { - if (group.Parent != null) - args.Player.SendSuccessMessage("Parent of \"{0}\" is \"{1}\".", group.Name, group.Parent.Name); - else - args.Player.SendSuccessMessage("Group \"{0}\" has no parent.", group.Name); + } + + string groupName = args.Parameters[1]; + Group group = TShock.Groups.GetGroupByName(groupName); + if (group == null) + { + args.Player.SendErrorMessage("No such group \"{0}\".", groupName); + return; + } + + if (args.Parameters.Count > 2) + { + string newParentGroupName = string.Join(" ", args.Parameters.Skip(2)); + if (!string.IsNullOrWhiteSpace(newParentGroupName) && !TShock.Groups.GroupExists(newParentGroupName)) + { + args.Player.SendErrorMessage("No such group \"{0}\".", newParentGroupName); + return; + } + + try + { + TShock.Groups.UpdateGroup(groupName, newParentGroupName, group.Permissions, group.ChatColor); + + if (!string.IsNullOrWhiteSpace(newParentGroupName)) + args.Player.SendSuccessMessage("Parent of group \"{0}\" set to \"{1}\".", groupName, newParentGroupName); + else + args.Player.SendSuccessMessage("Removed parent of group \"{0}\".", groupName); + } + catch (GroupManagerException ex) + { + args.Player.SendErrorMessage(ex.Message); + } + } + else + { + if (group.Parent != null) + args.Player.SendSuccessMessage("Parent of \"{0}\" is \"{1}\".", group.Name, group.Parent.Name); + else + args.Player.SendSuccessMessage("Group \"{0}\" has no parent.", group.Name); } } #endregion @@ -2202,7 +2202,7 @@ namespace TShockAPI }); } #endregion - return; + return; case "help": args.Player.SendInfoMessage("Syntax: /group [arguments]"); args.Player.SendInfoMessage("Commands: add, addperm, parent, del, delperm, list, listperm"); @@ -2413,7 +2413,7 @@ namespace TShockAPI private static void Reload(CommandArgs args) { - TShock.Utils.Reload(args.Player); + TShock.Utils.Reload(args.Player); args.Player.SendSuccessMessage( "Configuration, permissions, and regions reload complete. Some changes may require a server restart."); @@ -3457,8 +3457,8 @@ namespace TShockAPI args.Player.SendSuccessMessage("Annoying " + ply.Name + " for " + annoy + " seconds."); (new Thread(ply.Whoopie)).Start(annoy); } - } - + } + private static void Aliases(CommandArgs args) { if (args.Parameters.Count < 1) @@ -3466,31 +3466,31 @@ namespace TShockAPI args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /aliases "); return; } - - string givenCommandName = string.Join(" ", args.Parameters); - if (string.IsNullOrWhiteSpace(givenCommandName)) { - args.Player.SendErrorMessage("Please enter a proper command name or alias."); - return; - } - - string commandName; - if (givenCommandName[0] == '/') - commandName = givenCommandName.Substring(1); - else - commandName = givenCommandName; - - bool didMatch = false; - foreach (Command matchingCommand in ChatCommands.Where(cmd => cmd.Names.IndexOf(commandName) != -1)) { - if (matchingCommand.Names.Count > 1) - args.Player.SendInfoMessage( - "Aliases of /{0}: /{1}", matchingCommand.Name, string.Join(", /", matchingCommand.Names.Skip(1))); - else - args.Player.SendInfoMessage("/{0} defines no aliases.", matchingCommand.Name); - - didMatch = true; - } - - if (!didMatch) + + string givenCommandName = string.Join(" ", args.Parameters); + if (string.IsNullOrWhiteSpace(givenCommandName)) { + args.Player.SendErrorMessage("Please enter a proper command name or alias."); + return; + } + + string commandName; + if (givenCommandName[0] == '/') + commandName = givenCommandName.Substring(1); + else + commandName = givenCommandName; + + bool didMatch = false; + foreach (Command matchingCommand in ChatCommands.Where(cmd => cmd.Names.IndexOf(commandName) != -1)) { + if (matchingCommand.Names.Count > 1) + args.Player.SendInfoMessage( + "Aliases of /{0}: /{1}", matchingCommand.Name, string.Join(", /", matchingCommand.Names.Skip(1))); + else + args.Player.SendInfoMessage("/{0} defines no aliases.", matchingCommand.Name); + + didMatch = true; + } + + if (!didMatch) args.Player.SendErrorMessage("No command or command alias matching \"{0}\" found.", givenCommandName); } diff --git a/TShockAPI/ConfigFile.cs b/TShockAPI/ConfigFile.cs index db5a6488..915fcaf7 100644 --- a/TShockAPI/ConfigFile.cs +++ b/TShockAPI/ConfigFile.cs @@ -251,9 +251,9 @@ namespace TShockAPI [Description("The path of the directory where logs should be written into.")] public string LogPath = "tshock"; - [Description("Prevents players from placing tiles with an invalid style.")] public bool PreventInvalidPlaceStyle = true; - - [Description("#.#.#. = Red/Blue/Green - RGB Colors for broadcasts. Max value: 255.")] public float[] BroadcastRGB = + [Description("Prevents players from placing tiles with an invalid style.")] public bool PreventInvalidPlaceStyle = true; + + [Description("#.#.#. = Red/Blue/Green - RGB Colors for broadcasts. Max value: 255.")] public float[] BroadcastRGB = {127,255,212}; // TODO: Get rid of this when the old REST permission model is removed. diff --git a/TShockAPI/DB/GroupManager.cs b/TShockAPI/DB/GroupManager.cs index ad6bdf44..1618b8f8 100644 --- a/TShockAPI/DB/GroupManager.cs +++ b/TShockAPI/DB/GroupManager.cs @@ -18,8 +18,8 @@ along with this program. If not, see . using System; using System.Collections; using System.Collections.Generic; -using System.Data; -using System.Diagnostics; +using System.Data; +using System.Diagnostics; using System.IO; using System.Linq; using MySql.Data.MySqlClient; @@ -166,7 +166,7 @@ namespace TShockAPI.DB /// permissions /// chatcolor public void UpdateGroup(string name, string parentname, string permissions, string chatcolor) - { + { Group group = GetGroupByName(name); if (group == null) throw new GroupNotExistException(name); @@ -176,22 +176,22 @@ namespace TShockAPI.DB { parent = GetGroupByName(parentname); if (parent == null || parent == group) - throw new GroupManagerException("Invalid parent \"{0}\" for group \"{1}\".".SFormat(parentname, name)); - - // Check if the new parent would cause loops. - List groupChain = new List { group, parent }; - Group checkingGroup = parent.Parent; - while (checkingGroup != null) - { - if (groupChain.Contains(checkingGroup)) - throw new GroupManagerException( - string.Format("Invalid parent \"{0}\" for group \"{1}\" would cause loops in the parent chain.", parentname, name)); - - groupChain.Add(checkingGroup); - checkingGroup = checkingGroup.Parent; - } - } - + throw new GroupManagerException("Invalid parent \"{0}\" for group \"{1}\".".SFormat(parentname, name)); + + // Check if the new parent would cause loops. + List groupChain = new List { group, parent }; + Group checkingGroup = parent.Parent; + while (checkingGroup != null) + { + if (groupChain.Contains(checkingGroup)) + throw new GroupManagerException( + string.Format("Invalid parent \"{0}\" for group \"{1}\" would cause loops in the parent chain.", parentname, name)); + + groupChain.Add(checkingGroup); + checkingGroup = checkingGroup.Parent; + } + } + // Ensure any group validation is also persisted to the DB. var newGroup = new Group(name, parent, chatcolor, permissions); string query = "UPDATE GroupList SET Parent=@0, Commands=@1, ChatColor=@2 WHERE GroupName=@3"; @@ -265,107 +265,107 @@ namespace TShockAPI.DB } public void LoadPermisions() - { - try - { - List newGroups = new List(groups.Count); - Dictionary newGroupParents = new Dictionary(groups.Count); + { + try + { + List newGroups = new List(groups.Count); + Dictionary newGroupParents = new Dictionary(groups.Count); using (var reader = database.QueryReader("SELECT * FROM GroupList")) { while (reader.Read()) - { - string groupName = reader.Get("GroupName"); - if (groupName == "superadmin") - { - Log.ConsoleInfo("WARNING: Group \"superadmin\" is defined in the database even though it's a reserved group name."); - continue; - } + { + string groupName = reader.Get("GroupName"); + if (groupName == "superadmin") + { + Log.ConsoleInfo("WARNING: Group \"superadmin\" is defined in the database even though it's a reserved group name."); + continue; + } newGroups.Add(new Group(groupName, null, reader.Get("ChatColor"), reader.Get("Commands")) { Prefix = reader.Get("Prefix"), - Suffix = reader.Get("Suffix"), - }); - - try - { - newGroupParents.Add(groupName, reader.Get("Parent")); - } - catch (ArgumentException) - { - // Just in case somebody messed with the unique primary key. - Log.ConsoleError("ERROR: Group name \"{0}\" occurs more than once. Keeping current group settings."); - return; + Suffix = reader.Get("Suffix"), + }); + + try + { + newGroupParents.Add(groupName, reader.Get("Parent")); + } + catch (ArgumentException) + { + // Just in case somebody messed with the unique primary key. + Log.ConsoleError("ERROR: Group name \"{0}\" occurs more than once. Keeping current group settings."); + return; } } - } - - try - { - // Get rid of deleted groups. - for (int i = 0; i < groups.Count; i++) - if (newGroups.All(g => g.Name != groups[i].Name)) - groups.RemoveAt(i--); - - // Apply changed group settings while keeping the current instances and add new groups. - foreach (Group newGroup in newGroups) - { - Group currentGroup = groups.FirstOrDefault(g => g.Name == newGroup.Name); - if (currentGroup != null) - newGroup.AssignTo(currentGroup); - else - groups.Add(newGroup); - } - - // Resolve parent groups. - Debug.Assert(newGroups.Count == newGroupParents.Count); - for (int i = 0; i < groups.Count; i++) - { - Group group = groups[i]; - string parentGroupName; - if (!newGroupParents.TryGetValue(group.Name, out parentGroupName) || string.IsNullOrEmpty(parentGroupName)) - continue; - - group.Parent = groups.FirstOrDefault(g => g.Name == parentGroupName); - if (group.Parent == null) - { - Log.ConsoleError( - "ERROR: Group \"{0}\" is referencing non existent parent group \"{1}\", parent reference was removed.", - group.Name, parentGroupName); - } - else - { - if (group.Parent == group) - Log.ConsoleInfo( - "WARNING: Group \"{0}\" is referencing itself as parent group, parent reference was removed.", group.Name); - - List groupChain = new List { group }; - Group checkingGroup = group; - while (checkingGroup.Parent != null) - { - if (groupChain.Contains(checkingGroup.Parent)) - { - Log.ConsoleError( - "ERROR: Group \"{0}\" is referencing parent group \"{1}\" which is already part of the parent chain. Parent reference removed.", - checkingGroup.Name, checkingGroup.Parent.Name); - - checkingGroup.Parent = null; - break; - } - groupChain.Add(checkingGroup); - checkingGroup = checkingGroup.Parent; - } - } - } - } - finally - { - if (!groups.Any(g => g is SuperAdminGroup)) - groups.Add(new SuperAdminGroup()); - } - } - catch (Exception ex) - { - Log.ConsoleError("Error on reloading groups: " + ex); + } + + try + { + // Get rid of deleted groups. + for (int i = 0; i < groups.Count; i++) + if (newGroups.All(g => g.Name != groups[i].Name)) + groups.RemoveAt(i--); + + // Apply changed group settings while keeping the current instances and add new groups. + foreach (Group newGroup in newGroups) + { + Group currentGroup = groups.FirstOrDefault(g => g.Name == newGroup.Name); + if (currentGroup != null) + newGroup.AssignTo(currentGroup); + else + groups.Add(newGroup); + } + + // Resolve parent groups. + Debug.Assert(newGroups.Count == newGroupParents.Count); + for (int i = 0; i < groups.Count; i++) + { + Group group = groups[i]; + string parentGroupName; + if (!newGroupParents.TryGetValue(group.Name, out parentGroupName) || string.IsNullOrEmpty(parentGroupName)) + continue; + + group.Parent = groups.FirstOrDefault(g => g.Name == parentGroupName); + if (group.Parent == null) + { + Log.ConsoleError( + "ERROR: Group \"{0}\" is referencing non existent parent group \"{1}\", parent reference was removed.", + group.Name, parentGroupName); + } + else + { + if (group.Parent == group) + Log.ConsoleInfo( + "WARNING: Group \"{0}\" is referencing itself as parent group, parent reference was removed.", group.Name); + + List groupChain = new List { group }; + Group checkingGroup = group; + while (checkingGroup.Parent != null) + { + if (groupChain.Contains(checkingGroup.Parent)) + { + Log.ConsoleError( + "ERROR: Group \"{0}\" is referencing parent group \"{1}\" which is already part of the parent chain. Parent reference removed.", + checkingGroup.Name, checkingGroup.Parent.Name); + + checkingGroup.Parent = null; + break; + } + groupChain.Add(checkingGroup); + checkingGroup = checkingGroup.Parent; + } + } + } + } + finally + { + if (!groups.Any(g => g is SuperAdminGroup)) + groups.Add(new SuperAdminGroup()); + } + } + catch (Exception ex) + { + Log.ConsoleError("Error on reloading groups: " + ex); } } } diff --git a/TShockAPI/DB/RegionManager.cs b/TShockAPI/DB/RegionManager.cs index 78e7deaf..ea0acf1d 100644 --- a/TShockAPI/DB/RegionManager.cs +++ b/TShockAPI/DB/RegionManager.cs @@ -394,10 +394,10 @@ namespace TShockAPI.DB mergedIDs = reader.Get("UserIds"); } - string userIdToAdd = Convert.ToString(TShock.Users.GetUserID(userName)); - string[] ids = mergedIDs.Split(','); - // Is the user already allowed to the region? - if (ids.Contains(userIdToAdd)) + string userIdToAdd = Convert.ToString(TShock.Users.GetUserID(userName)); + string[] ids = mergedIDs.Split(','); + // Is the user already allowed to the region? + if (ids.Contains(userIdToAdd)) return true; if (string.IsNullOrEmpty(mergedIDs)) @@ -484,9 +484,9 @@ namespace TShockAPI.DB mergedGroups = reader.Get("Groups"); } - string[] groups = mergedGroups.Split(','); - // Is the group already allowed to the region? - if (groups.Contains(groupName)) + string[] groups = mergedGroups.Split(','); + // Is the group already allowed to the region? + if (groups.Contains(groupName)) return true; if (mergedGroups != "") diff --git a/TShockAPI/Group.cs b/TShockAPI/Group.cs index 9e869dfe..e662d445 100644 --- a/TShockAPI/Group.cs +++ b/TShockAPI/Group.cs @@ -270,26 +270,26 @@ namespace TShockAPI return; } permissions.Remove(permission); - } - + } + /// /// Assigns all fields of this instance to another. /// - /// The other instance. - public void AssignTo(Group otherGroup) - { - otherGroup.Name = Name; - otherGroup.Parent = Parent; - otherGroup.Prefix = Prefix; - otherGroup.Suffix = Suffix; - otherGroup.R = R; - otherGroup.G = G; - otherGroup.B = B; - otherGroup.Permissions = Permissions; - } - - public override string ToString() { - return this.Name; + /// The other instance. + public void AssignTo(Group otherGroup) + { + otherGroup.Name = Name; + otherGroup.Parent = Parent; + otherGroup.Prefix = Prefix; + otherGroup.Suffix = Suffix; + otherGroup.R = R; + otherGroup.G = G; + otherGroup.B = B; + otherGroup.Permissions = Permissions; + } + + public override string ToString() { + return this.Name; } } diff --git a/TShockAPI/Log.cs b/TShockAPI/Log.cs index b04bc4c9..0d5a10ef 100644 --- a/TShockAPI/Log.cs +++ b/TShockAPI/Log.cs @@ -70,12 +70,12 @@ namespace TShockAPI public static void Data(String message) { Write(message, LogLevel.Data); - } - + } + /// /// Writes data to the log file. /// - /// The format of the message to be written. + /// The format of the message to be written. /// The format arguments. public static void Data(String format, params String[] args) { @@ -89,12 +89,12 @@ namespace TShockAPI public static void Error(String message) { Write(message, LogLevel.Error); - } - + } + /// /// Writes an error to the log file. /// - /// The format of the message to be written. + /// The format of the message to be written. /// The format arguments. public static void Error(String format, params String[] args) { @@ -111,12 +111,12 @@ namespace TShockAPI Console.WriteLine(message); Console.ForegroundColor = ConsoleColor.Gray; Write(message, LogLevel.Error); - } - + } + /// /// Writes an error to the log file. /// - /// The format of the message to be written. + /// The format of the message to be written. /// The format arguments. public static void ConsoleError(String format, params String[] args) { @@ -130,12 +130,12 @@ namespace TShockAPI public static void Warn(String message) { Write(message, LogLevel.Warning); - } - + } + /// /// Writes a warning to the log file. /// - /// The format of the message to be written. + /// The format of the message to be written. /// The format arguments. public static void Warn(String format, params String[] args) { @@ -149,12 +149,12 @@ namespace TShockAPI public static void Info(String message) { Write(message, LogLevel.Info); - } - + } + /// /// Writes an informative string to the log file. /// - /// The format of the message to be written. + /// The format of the message to be written. /// The format arguments. public static void Info(String format, params String[] args) { @@ -171,12 +171,12 @@ namespace TShockAPI Console.WriteLine(message); Console.ForegroundColor = ConsoleColor.Gray; Write(message, LogLevel.Info); - } - + } + /// /// Writes an informative string to the log file. Also outputs to the console. /// - /// The format of the message to be written. + /// The format of the message to be written. /// The format arguments. public static void ConsoleInfo(String format, params String[] args) { @@ -190,12 +190,12 @@ namespace TShockAPI public static void Debug(String message) { Write(message, LogLevel.Debug); - } - + } + /// /// Writes a debug string to the log file. /// - /// The format of the message to be written. + /// The format of the message to be written. /// The format arguments. public static void Debug(String format, params String[] args) { diff --git a/TShockAPI/Permissions.cs b/TShockAPI/Permissions.cs index e7dee2b7..fd01522a 100644 --- a/TShockAPI/Permissions.cs +++ b/TShockAPI/Permissions.cs @@ -174,8 +174,8 @@ namespace TShockAPI [Description("User can elevate other users' groups temporarily.")] public static readonly string settempgroup; - [Description("User can download updates to plugins that are currently running.")] public static readonly string updateplugins; - + [Description("User can download updates to plugins that are currently running.")] public static readonly string updateplugins; + static Permissions() { foreach (var field in typeof (Permissions).GetFields()) diff --git a/TShockAPI/Rest/Rest.cs b/TShockAPI/Rest/Rest.cs index 0aaa1ad4..ddf1ec5c 100644 --- a/TShockAPI/Rest/Rest.cs +++ b/TShockAPI/Rest/Rest.cs @@ -36,13 +36,13 @@ namespace Rests /// Parameters in the url /// {x} in urltemplate /// Response object or null to not handle request - public delegate object RestCommandD(RestVerbs verbs, IParameterCollection parameters); - + public delegate object RestCommandD(RestVerbs verbs, IParameterCollection parameters); + /// /// Secure Rest command delegate including token data. /// /// Parameters in the url - /// {x} in urltemplate + /// {x} in urltemplate /// The data of stored for the provided token. /// Response object or null to not handle request public delegate object SecureRestCommandD(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData); @@ -189,32 +189,32 @@ namespace Rests protected virtual object ExecuteCommand(RestCommand cmd, RestVerbs verbs, IParameterCollection parms) { - object result = cmd.Execute(verbs, parms); - if (cmd.DoLog) - Log.ConsoleInfo("Anonymous requested REST endpoint: " + BuildRequestUri(cmd, verbs, parms, false)); - + object result = cmd.Execute(verbs, parms); + if (cmd.DoLog) + Log.ConsoleInfo("Anonymous requested REST endpoint: " + BuildRequestUri(cmd, verbs, parms, false)); + return result; - } - - protected virtual string BuildRequestUri( - RestCommand cmd, RestVerbs verbs, IParameterCollection parms, bool includeToken = true - ) { - StringBuilder requestBuilder = new StringBuilder(cmd.UriTemplate); - char separator = '?'; - foreach (IParameter paramImpl in parms) - { - Parameter param = (paramImpl as Parameter); - if (param == null || (!includeToken && param.Name.Equals("token", StringComparison.InvariantCultureIgnoreCase))) - continue; - - requestBuilder.Append(separator); - requestBuilder.Append(param.Name); - requestBuilder.Append('='); - requestBuilder.Append(param.Value); - separator = '&'; - } - - return requestBuilder.ToString(); + } + + protected virtual string BuildRequestUri( + RestCommand cmd, RestVerbs verbs, IParameterCollection parms, bool includeToken = true + ) { + StringBuilder requestBuilder = new StringBuilder(cmd.UriTemplate); + char separator = '?'; + foreach (IParameter paramImpl in parms) + { + Parameter param = (paramImpl as Parameter); + if (param == null || (!includeToken && param.Name.Equals("token", StringComparison.InvariantCultureIgnoreCase))) + continue; + + requestBuilder.Append(separator); + requestBuilder.Append(param.Name); + requestBuilder.Append('='); + requestBuilder.Append(param.Value); + separator = '&'; + } + + return requestBuilder.ToString(); } #region Dispose diff --git a/TShockAPI/Rest/RestCommand.cs b/TShockAPI/Rest/RestCommand.cs index afe96d33..c66ce20c 100644 --- a/TShockAPI/Rest/RestCommand.cs +++ b/TShockAPI/Rest/RestCommand.cs @@ -16,9 +16,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ using System.Linq; -using System.Text.RegularExpressions; -using HttpServer; - +using System.Text.RegularExpressions; +using HttpServer; + namespace Rests { public class RestCommand @@ -27,10 +27,10 @@ namespace Rests public string UriTemplate { get; protected set; } public string UriVerbMatch { get; protected set; } public string[] UriVerbs { get; protected set; } - public virtual bool RequiresToken { get { return false; } } + public virtual bool RequiresToken { get { return false; } } public bool DoLog { get; set; } - - private RestCommandD callback; + + private RestCommandD callback; /// /// @@ -45,7 +45,7 @@ namespace Rests UriVerbMatch = string.Format("^{0}$", string.Join("([^/]*)", Regex.Split(uritemplate, "\\{[^\\{\\}]*\\}"))); var matches = Regex.Matches(uritemplate, "\\{([^\\{\\}]*)\\}"); UriVerbs = (from Match match in matches select match.Groups[1].Value).ToArray(); - this.callback = callback; + this.callback = callback; DoLog = true; } @@ -62,44 +62,44 @@ namespace Rests public bool HasVerbs { get { return UriVerbs.Length > 0; } - } - - public virtual object Execute(RestVerbs verbs, IParameterCollection parameters) - { - return callback(verbs, parameters); } - } - - public class SecureRestCommand: RestCommand - { - public override bool RequiresToken { get { return true; } } - public string[] Permissions { get; set; } - - private SecureRestCommandD callback; - - public SecureRestCommand(string name, string uritemplate, SecureRestCommandD callback, params string[] permissions) + + public virtual object Execute(RestVerbs verbs, IParameterCollection parameters) + { + return callback(verbs, parameters); + } + } + + public class SecureRestCommand: RestCommand + { + public override bool RequiresToken { get { return true; } } + public string[] Permissions { get; set; } + + private SecureRestCommandD callback; + + public SecureRestCommand(string name, string uritemplate, SecureRestCommandD callback, params string[] permissions) : base(name, uritemplate, null) - { - this.callback = callback; + { + this.callback = callback; Permissions = permissions; } public SecureRestCommand(string uritemplate, SecureRestCommandD callback, params string[] permissions) : this(string.Empty, uritemplate, callback, permissions) { - } - - public override object Execute(RestVerbs verbs, IParameterCollection parameters) - { - return new RestObject("401") { Error = "Not authorized. The specified API endpoint requires a token." }; - } - - public object Execute(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData) - { - if (tokenData.Equals(SecureRest.TokenData.None)) - return new RestObject("401") { Error = "Not authorized. The specified API endpoint requires a token." }; - - return callback(verbs, parameters, tokenData); - } + } + + public override object Execute(RestVerbs verbs, IParameterCollection parameters) + { + return new RestObject("401") { Error = "Not authorized. The specified API endpoint requires a token." }; + } + + public object Execute(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData) + { + if (tokenData.Equals(SecureRest.TokenData.None)) + return new RestObject("401") { Error = "Not authorized. The specified API endpoint requires a token." }; + + return callback(verbs, parameters, tokenData); + } } } \ No newline at end of file diff --git a/TShockAPI/Rest/RestManager.cs b/TShockAPI/Rest/RestManager.cs index 23f284bc..0cf096a4 100644 --- a/TShockAPI/Rest/RestManager.cs +++ b/TShockAPI/Rest/RestManager.cs @@ -17,8 +17,8 @@ along with this program. If not, see . */ using System; using System.Collections; -using System.Collections.Generic; -using System.IO; +using System.Collections.Generic; +using System.IO; using System.Linq; using HttpServer; using Rests; @@ -38,27 +38,27 @@ namespace TShockAPI public void RegisterRestfulCommands() { - // Server Commands - if (TShock.Config.EnableTokenEndpointAuthentication) - { + // Server Commands + if (TShock.Config.EnableTokenEndpointAuthentication) + { Rest.Register(new SecureRestCommand("/v2/server/status", ServerStatusV2)); - Rest.Register(new SecureRestCommand("/status", ServerStatus)); - Rest.Register(new SecureRestCommand("/v3/server/motd", ServerMotd)); - Rest.Register(new SecureRestCommand("/v3/server/rules", ServerRules)); - } - else - { + Rest.Register(new SecureRestCommand("/status", ServerStatus)); + Rest.Register(new SecureRestCommand("/v3/server/motd", ServerMotd)); + Rest.Register(new SecureRestCommand("/v3/server/rules", ServerRules)); + } + else + { Rest.Register(new RestCommand("/v2/server/status", (a, b) => this.ServerStatusV2(a, b, SecureRest.TokenData.None))); - Rest.Register(new RestCommand("/status", (a, b) => this.ServerStatusV2(a, b, SecureRest.TokenData.None))); - Rest.Register(new RestCommand("/v3/server/motd", (a, b) => this.ServerMotd(a, b, SecureRest.TokenData.None))); - Rest.Register(new RestCommand("/v3/server/rules", (a, b) => this.ServerRules(a, b, SecureRest.TokenData.None))); - } + Rest.Register(new RestCommand("/status", (a, b) => this.ServerStatusV2(a, b, SecureRest.TokenData.None))); + Rest.Register(new RestCommand("/v3/server/motd", (a, b) => this.ServerMotd(a, b, SecureRest.TokenData.None))); + Rest.Register(new RestCommand("/v3/server/rules", (a, b) => this.ServerRules(a, b, SecureRest.TokenData.None))); + } - Rest.Register(new SecureRestCommand("/v2/server/broadcast", ServerBroadcast)); - Rest.Register(new SecureRestCommand("/v3/server/reload", ServerReload, RestPermissions.restcfg)); - Rest.Register(new SecureRestCommand("/v2/server/off", ServerOff, RestPermissions.restmaintenance)); + Rest.Register(new SecureRestCommand("/v2/server/broadcast", ServerBroadcast)); + Rest.Register(new SecureRestCommand("/v3/server/reload", ServerReload, RestPermissions.restcfg)); + Rest.Register(new SecureRestCommand("/v2/server/off", ServerOff, RestPermissions.restmaintenance)); Rest.Register(new SecureRestCommand("/v3/server/restart", ServerRestart, RestPermissions.restmaintenance)); - Rest.Register(new SecureRestCommand("/v2/server/rawcmd", ServerCommand, RestPermissions.restrawcommand)); + Rest.Register(new SecureRestCommand("/v2/server/rawcmd", ServerCommand, RestPermissions.restrawcommand)); Rest.Register(new SecureRestCommand("/v3/server/rawcmd", ServerCommandV3, RestPermissions.restrawcommand)); Rest.Register(new SecureRestCommand("/tokentest", ServerTokenTest)); @@ -107,35 +107,35 @@ namespace TShockAPI private object ServerCommand(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData) { if (string.IsNullOrWhiteSpace(parameters["cmd"])) - return RestMissingParam("cmd"); - - Group restPlayerGroup; + return RestMissingParam("cmd"); + + Group restPlayerGroup; // TODO: Get rid of this when the old REST permission model is removed. - if (TShock.Config.RestUseNewPermissionModel) - restPlayerGroup = TShock.Groups.GetGroupByName(tokenData.UserGroupName); - else - restPlayerGroup = new SuperAdminGroup(); + if (TShock.Config.RestUseNewPermissionModel) + restPlayerGroup = TShock.Groups.GetGroupByName(tokenData.UserGroupName); + else + restPlayerGroup = new SuperAdminGroup(); TSRestPlayer tr = new TSRestPlayer(tokenData.Username, restPlayerGroup); Commands.HandleCommand(tr, parameters["cmd"]); return RestResponse(string.Join("\n", tr.GetCommandOutput())); - } - + } + private object ServerCommandV3(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData) { if (string.IsNullOrWhiteSpace(parameters["cmd"])) - return RestMissingParam("cmd"); + return RestMissingParam("cmd"); - Group restPlayerGroup; + Group restPlayerGroup; // TODO: Get rid of this when the old REST permission model is removed. - if (TShock.Config.RestUseNewPermissionModel) - restPlayerGroup = TShock.Groups.GetGroupByName(tokenData.UserGroupName); - else - restPlayerGroup = new SuperAdminGroup(); - + if (TShock.Config.RestUseNewPermissionModel) + restPlayerGroup = TShock.Groups.GetGroupByName(tokenData.UserGroupName); + else + restPlayerGroup = new SuperAdminGroup(); + TSRestPlayer tr = new TSRestPlayer(tokenData.Username, restPlayerGroup); Commands.HandleCommand(tr, parameters["cmd"]); - return new RestObject() + return new RestObject() { {"response", tr.GetCommandOutput()} }; @@ -151,8 +151,8 @@ namespace TShockAPI TShock.Utils.StopServer(!GetBool(parameters["nosave"], false), reason); return RestResponse("The server is shutting down"); - } - + } + private object ServerRestart(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData) { if (!GetBool(parameters["confirm"], false)) @@ -163,10 +163,10 @@ namespace TShockAPI TShock.Utils.RestartServer(!GetBool(parameters["nosave"], false), reason); return RestResponse("The server is shutting down and will attempt to restart"); - } - + } + private object ServerReload(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData) - { + { TShock.Utils.Reload(new TSRestPlayer(tokenData.Username, TShock.Groups.GetGroupByName(tokenData.UserGroupName))); return RestResponse("Configuration, permissions, and regions reload complete. Some changes may require a server restart."); @@ -179,29 +179,29 @@ namespace TShockAPI return RestMissingParam("msg"); TShock.Utils.Broadcast(msg); return RestResponse("The message was broadcasted successfully"); - } - + } + private object ServerMotd(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData) - { - string motdFilePath = Path.Combine(TShock.SavePath, "motd.txt"); - if (!File.Exists(motdFilePath)) - return this.RestError("The motd.txt was not found.", "500"); + { + string motdFilePath = Path.Combine(TShock.SavePath, "motd.txt"); + if (!File.Exists(motdFilePath)) + return this.RestError("The motd.txt was not found.", "500"); - return new RestObject() - { - {"motd", File.ReadAllLines(motdFilePath)} + return new RestObject() + { + {"motd", File.ReadAllLines(motdFilePath)} }; - } - - private object ServerRules(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData) - { - string rulesFilePath = Path.Combine(TShock.SavePath, "rules.txt"); - if (!File.Exists(rulesFilePath)) - return this.RestError("The rules.txt was not found.", "500"); + } - return new RestObject() - { - {"rules", File.ReadAllLines(rulesFilePath)} + private object ServerRules(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData) + { + string rulesFilePath = Path.Combine(TShock.SavePath, "rules.txt"); + if (!File.Exists(rulesFilePath)) + return this.RestError("The rules.txt was not found.", "500"); + + return new RestObject() + { + {"rules", File.ReadAllLines(rulesFilePath)} }; } @@ -225,8 +225,8 @@ namespace TShockAPI {"port", TShock.Config.ServerPort}, {"playercount", Main.player.Where(p => null != p && p.active).Count()}, {"maxplayers", TShock.Config.MaxSlots}, - {"world", Main.worldName}, - {"uptime", (DateTime.Now - System.Diagnostics.Process.GetCurrentProcess().StartTime).ToString(@"d'.'hh':'mm':'ss")}, + {"world", Main.worldName}, + {"uptime", (DateTime.Now - System.Diagnostics.Process.GetCurrentProcess().StartTime).ToString(@"d'.'hh':'mm':'ss")}, {"serverpassword", !string.IsNullOrEmpty(TShock.Config.ServerPassword)} }; @@ -256,7 +256,7 @@ namespace TShockAPI rules.Add("HardcoreOnly", TShock.Config.HardcoreOnly); rules.Add("PvPMode", TShock.Config.PvPMode); rules.Add("SpawnProtection", TShock.Config.SpawnProtection); - rules.Add("SpawnProtectionRadius", TShock.Config.SpawnProtectionRadius); + rules.Add("SpawnProtectionRadius", TShock.Config.SpawnProtectionRadius); rules.Add("ServerSideInventory", TShock.Config.ServerSideInventory); ret.Add("rules", rules); @@ -265,10 +265,10 @@ namespace TShockAPI } private object ServerTokenTest(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData) - { - return new RestObject() + { + return new RestObject() { - {"response", "Token is valid and was passed through correctly."}, + {"response", "Token is valid and was passed through correctly."}, {"associateduser", tokenData.Username} }; } diff --git a/TShockAPI/Rest/RestPermissions.cs b/TShockAPI/Rest/RestPermissions.cs index 4662276d..5379b71c 100644 --- a/TShockAPI/Rest/RestPermissions.cs +++ b/TShockAPI/Rest/RestPermissions.cs @@ -14,80 +14,80 @@ 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.ComponentModel; -using System.Linq; - -namespace Rests -{ - public static class RestPermissions - { +*/ +using System; +using System.ComponentModel; +using System.Linq; + +namespace Rests +{ + public static class RestPermissions + { [Description("User can create REST tokens.")] - public static readonly string restapi; - + public static readonly string restapi; + [Description("User or REST user can destroy all REST tokens.")] - public static readonly string restmanage; - - - [Description("REST user can turn off / restart the server.")] - public static readonly string restmaintenance; - - [Description("REST user can reload configurations, save the world and set auto save settings.")] - public static readonly string restcfg; - - - [Description("REST user can list and get detailed information about users.")] - public static readonly string restviewusers; - - [Description("REST user can alter users.")] - public static readonly string restmanageusers; - - [Description("REST user can list and get detailed information about bans.")] - public static readonly string restviewbans; - - [Description("REST user can alter bans.")] - public static readonly string restmanagebans; - - [Description("REST user can list and get detailed information about groups.")] - public static readonly string restviewgroups; - - [Description("REST user can alter groups.")] - public static readonly string restmanagegroups; - - - [Description("REST user can get user information.")] - public static readonly string restuserinfo; - - [Description("REST user can kick players.")] - public static readonly string restkick; - - [Description("REST user can ban players.")] - public static readonly string restban; - - [Description("REST user can mute and unmute players.")] - public static readonly string restmute; - - [Description("REST user can kill players.")] - public static readonly string restkill; - - - [Description("REST user can drop meteors or change bloodmoon.")] - public static readonly string restcauseevents; - - [Description("REST user can butcher npcs.")] - public static readonly string restbutcher; - - - [Description("REST user can run raw TShock commands (the raw command permissions are also checked though).")] - public static readonly string restrawcommand; - + public static readonly string restmanage; + + + [Description("REST user can turn off / restart the server.")] + public static readonly string restmaintenance; + + [Description("REST user can reload configurations, save the world and set auto save settings.")] + public static readonly string restcfg; + + + [Description("REST user can list and get detailed information about users.")] + public static readonly string restviewusers; + + [Description("REST user can alter users.")] + public static readonly string restmanageusers; + + [Description("REST user can list and get detailed information about bans.")] + public static readonly string restviewbans; + + [Description("REST user can alter bans.")] + public static readonly string restmanagebans; + + [Description("REST user can list and get detailed information about groups.")] + public static readonly string restviewgroups; + + [Description("REST user can alter groups.")] + public static readonly string restmanagegroups; + + + [Description("REST user can get user information.")] + public static readonly string restuserinfo; + + [Description("REST user can kick players.")] + public static readonly string restkick; + + [Description("REST user can ban players.")] + public static readonly string restban; + + [Description("REST user can mute and unmute players.")] + public static readonly string restmute; + + [Description("REST user can kill players.")] + public static readonly string restkill; + + + [Description("REST user can drop meteors or change bloodmoon.")] + public static readonly string restcauseevents; + + [Description("REST user can butcher npcs.")] + public static readonly string restbutcher; + + + [Description("REST user can run raw TShock commands (the raw command permissions are also checked though).")] + public static readonly string restrawcommand; + static RestPermissions() { foreach (var field in typeof (RestPermissions).GetFields()) { field.SetValue(null, field.Name); } - } - } -} + } + } +} diff --git a/TShockAPI/Rest/SecureRest.cs b/TShockAPI/Rest/SecureRest.cs index cedeaafe..24dd98ac 100644 --- a/TShockAPI/Rest/SecureRest.cs +++ b/TShockAPI/Rest/SecureRest.cs @@ -18,35 +18,35 @@ along with this program. If not, see . using System; using System.Collections.Generic; using System.Linq; -using System.Net; -using System.Text; -using HttpServer; -using TShockAPI; -using TShockAPI.DB; - +using System.Net; +using System.Text; +using HttpServer; +using TShockAPI; +using TShockAPI.DB; + namespace Rests { public class SecureRest : Rest - { - public struct TokenData - { - public static readonly TokenData None = default(TokenData); - - public string Username { get; set; } - public string UserGroupName { get; set; } - } + { + public struct TokenData + { + public static readonly TokenData None = default(TokenData); + + public string Username { get; set; } + public string UserGroupName { get; set; } + } public Dictionary Tokens { get; protected set; } public SecureRest(IPAddress ip, int port) : base(ip, port) { - Tokens = new Dictionary(); + Tokens = new Dictionary(); Register(new RestCommand("/token/create/{username}/{password}", NewToken) { DoLog = false }); Register(new RestCommand("/v2/token/create/{password}", NewTokenV2) { DoLog = false }); - Register(new SecureRestCommand("/token/destroy/{token}", DestroyToken)); - Register(new SecureRestCommand("/v3/token/destroy/all", DestroyAllTokens, RestPermissions.restmanage)); + Register(new SecureRestCommand("/token/destroy/{token}", DestroyToken)); + Register(new SecureRestCommand("/v3/token/destroy/all", DestroyAllTokens, RestPermissions.restmanage)); foreach (KeyValuePair t in TShockAPI.TShock.RESTStartupTokens) { @@ -55,7 +55,7 @@ namespace Rests // TODO: Get rid of this when the old REST permission model is removed. if (!TShock.Config.RestUseNewPermissionModel) - { + { string warningMessage = string.Concat( "You're using the old REST permission model which is highly vulnerable in matter of security. ", "The old model will be removed with the next maintenance release of TShock. In order to switch to the new model, ", @@ -64,16 +64,16 @@ namespace Rests Log.Warn(warningMessage); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(warningMessage); - Console.ForegroundColor = ConsoleColor.Gray; + Console.ForegroundColor = ConsoleColor.Gray; } else - { + { string warningMessage = string.Concat( "You're using the new more secure REST permission model which can lead to compatibility problems ", "with existing REST services. If compatibility problems occur, you can switch back to the unsecure permission ", "model by changing the config setting \"RestUseNewPermissionModel\" to false, which is not recommended." ); - Log.ConsoleInfo(warningMessage); + Log.ConsoleInfo(warningMessage); } } @@ -91,11 +91,11 @@ namespace Rests } return new RestObject() { Response = "Requested token was successfully destroyed." }; - } - + } + private object DestroyAllTokens(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData) { - Tokens.Clear(); + Tokens.Clear(); return new RestObject() { Response = "All tokens were successfully destroyed." }; @@ -114,13 +114,13 @@ namespace Rests var user = verbs["username"]; var pass = verbs["password"]; - RestObject response = this.NewTokenInternal(user, pass); - response["deprecated"] = "This endpoint is depracted and will be removed in the future."; + RestObject response = this.NewTokenInternal(user, pass); + response["deprecated"] = "This endpoint is depracted and will be removed in the future."; return response; } - - private RestObject NewTokenInternal(string username, string password) - { + + private RestObject NewTokenInternal(string username, string password) + { User userAccount = TShock.Users.GetUserByName(username); if (userAccount == null || !string.IsNullOrWhiteSpace(userAccount.Address)) return new RestObject("401") @@ -129,12 +129,12 @@ namespace Rests if (!TShock.Utils.HashPassword(password).Equals(userAccount.Password, StringComparison.InvariantCultureIgnoreCase)) return new RestObject("401") { Error = "Invalid username/password combination provided. Please re-submit your query with a correct pair." }; - + Group userGroup = TShock.Utils.GetGroup(userAccount.Group); if (!userGroup.HasPermission(RestPermissions.restapi) && userAccount.Group != "superadmin") return new RestObject("403") - { Error = "Although your account was successfully found and identified, your account lacks the permission required to use the API. (restapi)" }; - + { Error = "Although your account was successfully found and identified, your account lacks the permission required to use the API. (restapi)" }; + string tokenHash; var rand = new Random(); var randbytes = new byte[32]; @@ -145,53 +145,53 @@ namespace Rests } while (Tokens.ContainsKey(tokenHash)); Tokens.Add(tokenHash, new TokenData { Username = userAccount.Name, UserGroupName = userGroup.Name }); - + RestObject response = new RestObject() { Response = "Successful login" }; response["token"] = tokenHash; - return response; + return response; } protected override object ExecuteCommand(RestCommand cmd, RestVerbs verbs, IParameterCollection parms) { - if (!cmd.RequiresToken) + if (!cmd.RequiresToken) return base.ExecuteCommand(cmd, verbs, parms); var token = parms["token"]; if (token == null) return new RestObject("401") { Error = "Not authorized. The specified API endpoint requires a token." }; - + SecureRestCommand secureCmd = (SecureRestCommand)cmd; TokenData tokenData; if (!Tokens.TryGetValue(token, out tokenData)) return new RestObject("403") - { Error = "Not authorized. The specified API endpoint requires a token, but the provided token was not valid." }; - + { Error = "Not authorized. The specified API endpoint requires a token, but the provided token was not valid." }; + // TODO: Get rid of this when the old REST permission model is removed. - if (TShock.Config.RestUseNewPermissionModel) { - Group userGroup = TShock.Groups.GetGroupByName(tokenData.UserGroupName); - if (userGroup == null) - { - Tokens.Remove(token); - - return new RestObject("403") - { Error = "Not authorized. The provided token became invalid due to group changes, please create a new token." }; - } - - if (secureCmd.Permissions.Length > 0 && secureCmd.Permissions.All(perm => !userGroup.HasPermission(perm))) - { - return new RestObject("403") - { Error = string.Format("Not authorized. User \"{0}\" has no access to use the specified API endpoint.", tokenData.Username) }; - } - } - - object result = secureCmd.Execute(verbs, parms, tokenData); - if (cmd.DoLog) - TShock.Utils.SendLogs(string.Format( - "\"{0}\" requested REST endpoint: {1}", tokenData.Username, this.BuildRequestUri(cmd, verbs, parms, false)), - Color.PaleVioletRed); - - return result; + if (TShock.Config.RestUseNewPermissionModel) { + Group userGroup = TShock.Groups.GetGroupByName(tokenData.UserGroupName); + if (userGroup == null) + { + Tokens.Remove(token); + + return new RestObject("403") + { Error = "Not authorized. The provided token became invalid due to group changes, please create a new token." }; + } + + if (secureCmd.Permissions.Length > 0 && secureCmd.Permissions.All(perm => !userGroup.HasPermission(perm))) + { + return new RestObject("403") + { Error = string.Format("Not authorized. User \"{0}\" has no access to use the specified API endpoint.", tokenData.Username) }; + } + } + + object result = secureCmd.Execute(verbs, parms, tokenData); + if (cmd.DoLog) + TShock.Utils.SendLogs(string.Format( + "\"{0}\" requested REST endpoint: {1}", tokenData.Username, this.BuildRequestUri(cmd, verbs, parms, false)), + Color.PaleVioletRed); + + return result; } } } \ No newline at end of file diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index b8443a16..81c53321 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -1,1076 +1,1076 @@ -/* -TShock, a server mod for Terraria -Copyright (C) 2011-2012 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.Diagnostics; -using System.IO; -using System.Threading; -using Terraria; -using TShockAPI.Net; - -namespace TShockAPI -{ - public class TSPlayer - { - /// - /// This represents the server as a player. - /// - public static readonly TSServerPlayer Server = new TSServerPlayer(); - - /// - /// This player represents all the players. - /// - public static readonly TSPlayer All = new TSPlayer("All"); - - /// - /// The amount of tiles that the player has killed in the last second. - /// - public int TileKillThreshold { get; set; } - - /// - /// The amount of tiles the player has placed in the last second. - /// - public int TilePlaceThreshold { get; set; } - - /// - /// The amount of liquid( in tiles ) that the player has placed in the last second. - /// - public int TileLiquidThreshold { get; set; } - - /// - /// The number of projectiles created by the player in the last second. - /// - public int ProjectileThreshold { get; set; } - - /// - /// A queue of tiles destroyed by the player for reverting. - /// - public Dictionary TilesDestroyed { get; protected set; } - - /// - /// A queue of tiles placed by the player for reverting. - /// - public Dictionary TilesCreated { get; protected set; } - - public int FirstMaxHP { get; set; } - - public int FirstMaxMP { get; set; } - - /// - /// The player's group. - /// - public Group Group - { - get - { - if (tempGroup != null) - return tempGroup; - return group; - } - set { group = value; } - } - - /// - /// The player's temporary group. This overrides the user's actual group. - /// - public Group tempGroup = null; - - private Group group = null; - - public bool ReceivedInfo { get; set; } - - /// - /// The players index in the player array( Main.players[] ). - /// - public int Index { get; protected set; } - - /// - /// The last time the player changed their team or pvp status. - /// - public DateTime LastPvpChange; - - /// - /// Temp points for use in regions and other plugins. - /// - public Point[] TempPoints = new Point[2]; - - /// - /// Whether the player is waiting to place/break a tile to set as a temp point. - /// - public int AwaitingTempPoint { get; set; } - - /// - /// A list of command callbacks indexed by the command they need to do. - /// - public Dictionary> AwaitingResponse; - - public bool AwaitingName { get; set; } - - public string[] AwaitingNameParameters { get; set; } - - /// - /// The last time a player broke a grief check. - /// - public DateTime LastThreat { get; set; } - - /// - /// Not used, can be removed. - /// - public DateTime LastTileChangeNotify { get; set; } - - public bool InitSpawn; - - /// - /// Whether the player should see logs. - /// - public bool DisplayLogs = true; - - public Vector2 oldSpawn = Vector2.Zero; - - /// - /// The last player that the player whispered with( to or from ). - /// - public TSPlayer LastWhisper; - - /// - /// The number of unsuccessful login attempts. - /// - public int LoginAttempts { get; set; } - - public Vector2 TeleportCoords = new Vector2(-1, -1); - - public Vector2 LastNetPosition = Vector2.Zero; - - /// - /// The player's login name. - /// - public string UserAccountName { get; set; } - - /// - /// Whether the player performed a valid login attempt (i.e. entered valid user name and password) but is still blocked - /// from logging in because of SSI. - /// - public bool LoginFailsBySsi { get; set; } - - /// - /// Whether the player is logged in or not. - /// - public bool IsLoggedIn; - - /// - /// Whether the player has sent their whole inventory to the server while connecting. - /// - public bool HasSentInventory { get; set; } - - /// - /// The player's user id( from the db ). - /// - public int UserID = -1; - - /// - /// Whether the player has been nagged about logging in. - /// - public bool HasBeenNaggedAboutLoggingIn; - - public bool TPAllow = true; - - /// - /// Whether the player is muted or not. - /// - public bool mute; - - public bool TpLock; - - private Player FakePlayer; - - public bool RequestedSection; - - /// - /// The last time the player died. - /// - public DateTime LastDeath { get; set; } - - /// - /// Whether the player is dead or not. - /// - public bool Dead; - - public string Country = "??"; - - /// - /// The players difficulty( normal[softcore], mediumcore, hardcore ). - /// - public int Difficulty; - - private string CacheIP; - - public string IgnoreActionsForInventory = "none"; - - public string IgnoreActionsForCheating = "none"; - - public string IgnoreActionsForDisabledArmor = "none"; - - public bool IgnoreActionsForClearingTrashCan; - - /// - /// The player's server side inventory data. - /// - public PlayerData PlayerData; - - /// - /// Whether the player needs to specify a password upon connection( either server or user account ). - /// - public bool RequiresPassword; - - public bool SilentKickInProgress; - - public bool SilentJoinInProgress; - - /// - /// A list of points where ice tiles have been placed. - /// - public List IceTiles; - - /// - /// Unused, can be removed. - /// - public long RPm = 1; - - /// - /// World protection message cool down. - /// - public long WPm = 1; - - /// - /// Spawn protection message cool down. - /// - public long SPm = 1; - - /// - /// Permission to build message cool down. - /// - public long BPm = 1; - - /// - /// The time in ms when the player has logged in. - /// - public long LoginMS; - - /// - /// Whether the player has been harrassed about logging in due to server side inventory or forced login. - /// - public bool LoginHarassed = false; - - /// - /// Whether the player is a real, human, player on the server. - /// - public bool RealPlayer - { - get { return Index >= 0 && Index < Main.maxNetPlayers && Main.player[Index] != null; } - } - - public bool ConnectionAlive - { - get - { - return RealPlayer && - (Netplay.serverSock[Index] != null && Netplay.serverSock[Index].active && !Netplay.serverSock[Index].kill); - } - } - - public int State - { - get { return Netplay.serverSock[Index].state; } - set { Netplay.serverSock[Index].state = value; } - } - - public string IP - { - get - { - if (string.IsNullOrEmpty(CacheIP)) - return - CacheIP = - RealPlayer - ? (Netplay.serverSock[Index].tcpClient.Connected - ? TShock.Utils.GetRealIP(Netplay.serverSock[Index].tcpClient.Client.RemoteEndPoint.ToString()) - : "") - : ""; - else - return CacheIP; - } - } - - /// - /// Saves the player's inventory to SSI - /// - /// bool - True/false if it saved successfully - public bool SaveServerInventory() - { - if (!TShock.Config.ServerSideInventory) - { - return false; - } - try - { - PlayerData.CopyInventory(this); - TShock.InventoryDB.InsertPlayerData(this); - return true; - } catch (Exception e) - { - Log.Error(e.Message); - return false; - } - - } - - /// - /// Terraria Player - /// - public Player TPlayer - { - get { return FakePlayer ?? Main.player[Index]; } - } - - public string Name - { - get { return TPlayer.name; } - } - - public bool Active - { - get { return TPlayer != null && TPlayer.active; } - } - - public int Team - { - get { return TPlayer.team; } - } - - public float X - { - get { return RealPlayer ? TPlayer.position.X : Main.spawnTileX*16; } - } - - public float Y - { - get { return RealPlayer ? TPlayer.position.Y : Main.spawnTileY*16; } - } - - public int TileX - { - get { return (int) (X/16); } - } - - public int TileY - { - get { return (int) (Y/16); } - } - - public bool InventorySlotAvailable - { - get - { - bool flag = false; - if (RealPlayer) - { - for (int i = 0; i < 40; i++) //41 is trash can, 42-45 is coins, 46-49 is ammo - { - if (TPlayer.inventory[i] == null || !TPlayer.inventory[i].active || TPlayer.inventory[i].name == "") - { - flag = true; - break; - } - } - } - return flag; - } - } - - public TSPlayer(int index) - { - TilesDestroyed = new Dictionary(); - TilesCreated = new Dictionary(); - Index = index; - Group = new Group(TShock.Config.DefaultGuestGroupName); - IceTiles = new List(); - AwaitingResponse = new Dictionary>(); - } - - protected TSPlayer(String playerName) - { - TilesDestroyed = new Dictionary(); - TilesCreated = new Dictionary(); - Index = -1; - FakePlayer = new Player {name = playerName, whoAmi = -1}; - Group = new Group(TShock.Config.DefaultGuestGroupName); - AwaitingResponse = new Dictionary>(); - } - - public virtual void Disconnect(string reason) - { - SendData(PacketTypes.Disconnect, reason); - } - - public virtual void Flush() - { - var sock = Netplay.serverSock[Index]; - if (sock == null) - return; - - TShock.PacketBuffer.Flush(sock); - } - - - public void SendWorldInfo(int tilex, int tiley, bool fakeid) - { - using (var ms = new MemoryStream()) - { - var msg = new WorldInfoMsg - { - Time = (int) Main.time, - DayTime = Main.dayTime, - MoonPhase = (byte) Main.moonPhase, - BloodMoon = Main.bloodMoon, - MaxTilesX = Main.maxTilesX, - MaxTilesY = Main.maxTilesY, - SpawnX = tilex, - SpawnY = tiley, - WorldSurface = (int) Main.worldSurface, - RockLayer = (int) Main.rockLayer, - //Sending a fake world id causes the client to not be able to find a stored spawnx/y. - //This fixes the bed spawn point bug. With a fake world id it wont be able to find the bed spawn. - WorldID = !fakeid ? Main.worldID : -1, - WorldFlags = (WorldGen.shadowOrbSmashed ? WorldInfoFlag.OrbSmashed : WorldInfoFlag.None) | - (NPC.downedBoss1 ? WorldInfoFlag.DownedBoss1 : WorldInfoFlag.None) | - (NPC.downedBoss2 ? WorldInfoFlag.DownedBoss2 : WorldInfoFlag.None) | - (NPC.downedBoss3 ? WorldInfoFlag.DownedBoss3 : WorldInfoFlag.None) | - (Main.hardMode ? WorldInfoFlag.HardMode : WorldInfoFlag.None) | - (NPC.downedClown ? WorldInfoFlag.DownedClown : WorldInfoFlag.None), - WorldName = TShock.Config.UseServerName ? TShock.Config.ServerName : Main.worldName - }; - msg.PackFull(ms); - SendRawData(ms.ToArray()); - } - } - - public bool Teleport(int tilex, int tiley) - { - InitSpawn = false; - - SendWorldInfo(tilex, tiley, true); - - //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) - //Should we properly send sections so that clients don't get tiles twice? - SendTileSquare(tilex, tiley, 150); - -/* //We shouldn't need this section anymore -it can prevent otherwise acceptable teleportation under some circumstances. - - if (!SendTileSquare(tilex, tiley, 150)) - { - InitSpawn = true; - SendWorldInfo(Main.spawnTileX, Main.spawnTileY, false); - return false; - } - -*/ - Spawn(-1, -1); - - SendWorldInfo(Main.spawnTileX, Main.spawnTileY, false); - - TPlayer.position.X = (float)(tilex * 16 + 8 - TPlayer.width /2); - TPlayer.position.Y = (float)(tiley * 16 - TPlayer.height); - //We need to send the tile data again to prevent clients from thinking they *really* destroyed blocks just now. - - SendTileSquare(tilex, tiley, 10); - - return true; - } - - public void Spawn() - { - Spawn(TPlayer.SpawnX, TPlayer.SpawnY); - } - - public void Spawn(int tilex, int tiley) - { - using (var ms = new MemoryStream()) - { - var msg = new SpawnMsg - { - PlayerIndex = (byte) Index, - TileX = tilex, - TileY = tiley - }; - msg.PackFull(ms); - SendRawData(ms.ToArray()); - } - } - - public void RemoveProjectile(int index, int owner) - { - using (var ms = new MemoryStream()) - { - var msg = new ProjectileRemoveMsg - { - Index = (short) index, - Owner = (byte) owner - }; - msg.PackFull(ms); - SendRawData(ms.ToArray()); - } - } - - public virtual bool SendTileSquare(int x, int y, int size = 10) - { - try - { - int num = (size - 1)/2; - int m_x=0; - int m_y=0; - - if (x - num <0){ - m_x=0; - }else{ - m_x = x - num; - } - - if (y - num <0){ - m_y=0; - }else{ - m_y = y - num; - } - - if (m_x + size > Main.maxTilesX){ - m_x=Main.maxTilesX - size; - } - - if (m_y + size > Main.maxTilesY){ - m_y=Main.maxTilesY - size; - } - - SendData(PacketTypes.TileSendSquare, "", size, m_x, m_y); - return true; - } - catch (IndexOutOfRangeException) - { - - // This is expected if square exceeds array. - } - catch (Exception ex) - { - Log.Error(ex.ToString()); - } - return false; - } - - public bool GiveItemCheck(int type, string name, int width, int height, int stack, int prefix = 0) - { - if ((TShock.Itembans.ItemIsBanned(name) && TShock.Config.PreventBannedItemSpawn) && - (TShock.Itembans.ItemIsBanned(name, this) || !TShock.Config.AllowAllowedGroupsToSpawnBannedItems)) - return false; - - GiveItem(type,name,width,height,stack,prefix); - return true; - } - - public virtual void GiveItem(int type, string name, int width, int height, int stack, int prefix = 0) - { - int itemid = Item.NewItem((int) X, (int) Y, width, height, type, stack, true, prefix); - - // This is for special pickaxe/hammers/swords etc - Main.item[itemid].SetDefaults(name); - // The set default overrides the wet and stack set by NewItem - Main.item[itemid].wet = Collision.WetCollision(Main.item[itemid].position, Main.item[itemid].width, - Main.item[itemid].height); - Main.item[itemid].stack = stack; - Main.item[itemid].owner = Index; - Main.item[itemid].prefix = (byte) prefix; - NetMessage.SendData((int) PacketTypes.ItemDrop, -1, -1, "", itemid, 0f, 0f, 0f); - NetMessage.SendData((int) PacketTypes.ItemOwner, -1, -1, "", itemid, 0f, 0f, 0f); - } - - public virtual void SendInfoMessage(string msg) - { - SendMessage(msg, Color.Yellow); - } - - public void SendInfoMessage(string format, params object[] args) - { - SendInfoMessage(string.Format(format, args)); - } - - public virtual void SendSuccessMessage(string msg) - { - SendMessage(msg, Color.Green); - } - - public void SendSuccessMessage(string format, params object[] args) - { - SendSuccessMessage(string.Format(format, args)); - } - - public virtual void SendWarningMessage(string msg) - { - SendMessage(msg, Color.OrangeRed); - } - - public void SendWarningMessage(string format, params object[] args) - { - SendWarningMessage(string.Format(format, args)); - } - - public virtual void SendErrorMessage(string msg) - { - SendMessage(msg, Color.Red); - } - - public void SendErrorMessage(string format, params object[] args) - { - SendErrorMessage(string.Format(format, args)); - } - - [Obsolete("Use SendErrorMessage, SendInfoMessage, or SendWarningMessage, or a custom color instead.")] - public virtual void SendMessage(string msg) - { - SendMessage(msg, 0, 255, 0); - } - - public virtual void SendMessage(string msg, Color color) - { - SendMessage(msg, color.R, color.G, color.B); - } - - public virtual void SendMessage(string msg, byte red, byte green, byte blue) - { - SendData(PacketTypes.ChatText, msg, 255, red, green, blue); - } - - public virtual void SendMessageFromPlayer(string msg, byte red, byte green, byte blue, int ply) - { - SendDataFromPlayer(PacketTypes.ChatText, ply, msg, red, green, blue, 0); - } - - public virtual void DamagePlayer(int damage) - { - NetMessage.SendData((int) PacketTypes.PlayerDamage, -1, -1, "", Index, ((new Random()).Next(-1, 1)), damage, - (float) 0); - } - - public virtual void SetTeam(int team) - { - Main.player[Index].team = team; - SendData(PacketTypes.PlayerTeam, "", Index); - } - - public virtual void Disable(string reason = "") - { - LastThreat = DateTime.UtcNow; - SetBuff(33, 330, true); //Weak - SetBuff(32, 330, true); //Slow - SetBuff(23, 330, true); //Cursed - if (!string.IsNullOrEmpty(reason)) - Log.ConsoleInfo(string.Format("Player {0} has been disabled for {1}.", Name, reason)); - - var trace = new StackTrace(); - StackFrame frame = null; - frame = trace.GetFrame(1); - if (frame != null && frame.GetMethod().DeclaringType != null) - Log.Debug(frame.GetMethod().DeclaringType.Name + " called Disable()."); - } - - public virtual void Whoopie(object time) - { - var time2 = (int) time; - var launch = DateTime.UtcNow; - var startname = Name; - SendMessage("You are now being annoyed.", Color.Red); - while ((DateTime.UtcNow - launch).TotalSeconds < time2 && startname == Name) - { - SendData(PacketTypes.NpcSpecial, number: Index, number2: 2f); - Thread.Sleep(50); - } - } - - public virtual void SetBuff(int type, int time = 3600, bool bypass = false) - { - if ((DateTime.UtcNow - LastThreat).TotalMilliseconds < 5000 && !bypass) - return; - - SendData(PacketTypes.PlayerAddBuff, number: Index, number2: type, number3: time); - } - - //Todo: Separate this into a few functions. SendTo, SendToAll, etc - public virtual void SendData(PacketTypes msgType, string text = "", int number = 0, float number2 = 0f, - float number3 = 0f, float number4 = 0f, int number5 = 0) - { - if (RealPlayer && !ConnectionAlive) - return; - - NetMessage.SendData((int) msgType, Index, -1, text, number, number2, number3, number4, number5); - } - - public virtual void SendDataFromPlayer(PacketTypes msgType, int ply, string text = "", float number2 = 0f, float number3 = 0f, float number4 = 0f, int number5 = 0) - { - if (RealPlayer && !ConnectionAlive) - return; - - NetMessage.SendData((int) msgType, Index, -1, text, ply, number2, number3, number4, number5); - } - - public virtual bool SendRawData(byte[] data) - { - if (!RealPlayer || !ConnectionAlive) - return false; - - return TShock.SendBytes(Netplay.serverSock[Index], data); - } - - /// - /// Adds a command callback to a specified command string. - /// - /// The string representing the command i.e "yes" == /yes - /// The method that will be executed on confirmation ie user accepts - public void AddResponse( string name, Action callback) - { - if( AwaitingResponse.ContainsKey(name)) - { - AwaitingResponse.Remove(name); - } - - AwaitingResponse.Add(name, callback); - } - } - - public class TSRestPlayer : TSPlayer - { - internal List CommandOutput = new List(); - - public TSRestPlayer(string playerName, Group playerGroup): base(playerName) - { - Group = playerGroup; - AwaitingResponse = new Dictionary>(); - } - - public override void SendMessage(string msg) - { - SendMessage(msg, 0, 255, 0); - } - - public override void SendMessage(string msg, Color color) - { - SendMessage(msg, color.R, color.G, color.B); - } - - public override void SendMessage(string msg, byte red, byte green, byte blue) - { - this.CommandOutput.Add(msg); - } - - public override void SendInfoMessage(string msg) - { - SendMessage(msg, Color.Yellow); - } - - public override void SendSuccessMessage(string msg) - { - SendMessage(msg, Color.Green); - } - - public override void SendWarningMessage(string msg) - { - SendMessage(msg, Color.OrangeRed); - } - - public override void SendErrorMessage(string msg) - { - SendMessage(msg, Color.Red); - } - - public List GetCommandOutput() - { - return this.CommandOutput; - } - } - - public class TSServerPlayer : TSPlayer - { - public TSServerPlayer() - : base("Server") - { - Group = new SuperAdminGroup(); - } - - public override void SendErrorMessage(string msg) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine(msg); - Console.ResetColor(); - } - - public override void SendInfoMessage(string msg) - { - Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine(msg); - Console.ResetColor(); - } - - public override void SendSuccessMessage(string msg) - { - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine(msg); - Console.ResetColor(); - } - - public override void SendWarningMessage(string msg) - { - Console.ForegroundColor = ConsoleColor.DarkRed; - Console.WriteLine(msg); - Console.ResetColor(); - } - - public override void SendMessage(string msg) - { - SendMessage(msg, 0, 255, 0); - } - - public override void SendMessage(string msg, Color color) - { - SendMessage(msg, color.R, color.G, color.B); - } - - public override void SendMessage(string msg, byte red, byte green, byte blue) - { - Console.WriteLine(msg); - //RconHandler.Response += msg + "\n"; - } - - public void SetFullMoon(bool fullmoon) - { - Main.moonPhase = 0; - SetTime(false, 0); - } - - public void SetBloodMoon(bool bloodMoon) - { - Main.bloodMoon = bloodMoon; - SetTime(false, 0); - } - - public void SetTime(bool dayTime, double time) - { - Main.dayTime = dayTime; - Main.time = time; - NetMessage.SendData((int) PacketTypes.TimeSet, -1, -1, "", 0, 0, Main.sunModY, Main.moonModY); - NetMessage.syncPlayers(); - } - - public void SpawnNPC(int type, string name, int amount, int startTileX, int startTileY, int tileXRange = 100, - int tileYRange = 50) - { - for (int i = 0; i < amount; i++) - { - int spawnTileX; - int spawnTileY; - TShock.Utils.GetRandomClearTileWithInRange(startTileX, startTileY, tileXRange, tileYRange, out spawnTileX, - out spawnTileY); - int npcid = NPC.NewNPC(spawnTileX*16, spawnTileY*16, type, 0); - // This is for special slimes - Main.npc[npcid].SetDefaults(name); - } - } - - public void StrikeNPC(int npcid, int damage, float knockBack, int hitDirection) - { - // Main.rand is thread static. - if (Main.rand == null) - Main.rand = new Random(); - - Main.npc[npcid].StrikeNPC(damage, knockBack, hitDirection); - NetMessage.SendData((int) PacketTypes.NpcStrike, -1, -1, "", npcid, damage, knockBack, hitDirection); - } - - public void RevertTiles(Dictionary tiles) - { - // Update Main.Tile first so that when tile sqaure is sent it is correct - foreach (KeyValuePair entry in tiles) - { - Main.tile[(int) entry.Key.X, (int) entry.Key.Y].Data = entry.Value; - } - // Send all players updated tile sqaures - foreach (Vector2 coords in tiles.Keys) - { - All.SendTileSquare((int) coords.X, (int) coords.Y, 3); - } - } - } - - public class PlayerData - { - public NetItem[] inventory = new NetItem[NetItem.maxNetInventory]; - public int maxHealth = 100; - //public int maxMana = 100; - public bool exists; - - public PlayerData(TSPlayer player) - { - for (int i = 0; i < NetItem.maxNetInventory; i++) - { - this.inventory[i] = new NetItem(); - } - this.inventory[0].netID = -15; - this.inventory[0].stack = 1; - if (player.TPlayer.inventory[0] != null && player.TPlayer.inventory[0].netID == -15) - this.inventory[0].prefix = player.TPlayer.inventory[0].prefix; - this.inventory[1].netID = -13; - this.inventory[1].stack = 1; - if (player.TPlayer.inventory[1] != null && player.TPlayer.inventory[1].netID == -13) - this.inventory[1].prefix = player.TPlayer.inventory[1].prefix; - this.inventory[2].netID = -16; - this.inventory[2].stack = 1; - if (player.TPlayer.inventory[2] != null && player.TPlayer.inventory[2].netID == -16) - this.inventory[2].prefix = player.TPlayer.inventory[2].prefix; - } - - public void StoreSlot(int slot, int netID, int prefix, int stack) - { - if(slot > (this.inventory.Length - 1)) //if the slot is out of range then dont save - { - return; - } - - this.inventory[slot].netID = netID; - if (this.inventory[slot].netID != 0) - { - this.inventory[slot].stack = stack; - this.inventory[slot].prefix = prefix; - } - else - { - this.inventory[slot].stack = 0; - this.inventory[slot].prefix = 0; - } - } - - public void CopyInventory(TSPlayer player) - { - this.maxHealth = player.TPlayer.statLifeMax; - Item[] inventory = player.TPlayer.inventory; - Item[] armor = player.TPlayer.armor; - for (int i = 0; i < NetItem.maxNetInventory; i++) - { - if (i < 49) - { - if (player.TPlayer.inventory[i] != null) - { - this.inventory[i].netID = inventory[i].netID; - } - else - { - this.inventory[i].netID = 0; - } - - if (this.inventory[i].netID != 0) - { - this.inventory[i].stack = inventory[i].stack; - this.inventory[i].prefix = inventory[i].prefix; - } - else - { - this.inventory[i].stack = 0; - this.inventory[i].prefix = 0; - } - } - else - { - if (player.TPlayer.armor[i - 48] != null) - { - this.inventory[i].netID = armor[i - 48].netID; - } - else - { - this.inventory[i].netID = 0; - } - - if (this.inventory[i].netID != 0) - { - this.inventory[i].stack = armor[i - 48].stack; - this.inventory[i].prefix = armor[i - 48].prefix; - } - else - { - this.inventory[i].stack = 0; - this.inventory[i].prefix = 0; - } - } - } - } - } - - public class NetItem - { - public static int maxNetInventory = 59; - public int netID; - public int stack; - public int prefix; - - public static string ToString(NetItem[] inventory) - { - string inventoryString = ""; - for (int i = 0; i < maxNetInventory; i++) - { - if (i != 0) - inventoryString += "~"; - inventoryString += inventory[i].netID; - if (inventory[i].netID != 0) - { - inventoryString += "," + inventory[i].stack; - inventoryString += "," + inventory[i].prefix; - } - else - { - inventoryString += ",0,0"; - } - } - return inventoryString; - } - - public static NetItem[] Parse(string data) - { - NetItem[] inventory = new NetItem[maxNetInventory]; - int i; - for (i = 0; i < maxNetInventory; i++) - { - inventory[i] = new NetItem(); - } - string[] items = data.Split('~'); - i = 0; - foreach (string item in items) - { - string[] idata = item.Split(','); - inventory[i].netID = int.Parse(idata[0]); - inventory[i].stack = int.Parse(idata[1]); - inventory[i].prefix = int.Parse(idata[2]); - i++; - } - return inventory; - } - } -} +/* +TShock, a server mod for Terraria +Copyright (C) 2011-2012 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.Diagnostics; +using System.IO; +using System.Threading; +using Terraria; +using TShockAPI.Net; + +namespace TShockAPI +{ + public class TSPlayer + { + /// + /// This represents the server as a player. + /// + public static readonly TSServerPlayer Server = new TSServerPlayer(); + + /// + /// This player represents all the players. + /// + public static readonly TSPlayer All = new TSPlayer("All"); + + /// + /// The amount of tiles that the player has killed in the last second. + /// + public int TileKillThreshold { get; set; } + + /// + /// The amount of tiles the player has placed in the last second. + /// + public int TilePlaceThreshold { get; set; } + + /// + /// The amount of liquid( in tiles ) that the player has placed in the last second. + /// + public int TileLiquidThreshold { get; set; } + + /// + /// The number of projectiles created by the player in the last second. + /// + public int ProjectileThreshold { get; set; } + + /// + /// A queue of tiles destroyed by the player for reverting. + /// + public Dictionary TilesDestroyed { get; protected set; } + + /// + /// A queue of tiles placed by the player for reverting. + /// + public Dictionary TilesCreated { get; protected set; } + + public int FirstMaxHP { get; set; } + + public int FirstMaxMP { get; set; } + + /// + /// The player's group. + /// + public Group Group + { + get + { + if (tempGroup != null) + return tempGroup; + return group; + } + set { group = value; } + } + + /// + /// The player's temporary group. This overrides the user's actual group. + /// + public Group tempGroup = null; + + private Group group = null; + + public bool ReceivedInfo { get; set; } + + /// + /// The players index in the player array( Main.players[] ). + /// + public int Index { get; protected set; } + + /// + /// The last time the player changed their team or pvp status. + /// + public DateTime LastPvpChange; + + /// + /// Temp points for use in regions and other plugins. + /// + public Point[] TempPoints = new Point[2]; + + /// + /// Whether the player is waiting to place/break a tile to set as a temp point. + /// + public int AwaitingTempPoint { get; set; } + + /// + /// A list of command callbacks indexed by the command they need to do. + /// + public Dictionary> AwaitingResponse; + + public bool AwaitingName { get; set; } + + public string[] AwaitingNameParameters { get; set; } + + /// + /// The last time a player broke a grief check. + /// + public DateTime LastThreat { get; set; } + + /// + /// Not used, can be removed. + /// + public DateTime LastTileChangeNotify { get; set; } + + public bool InitSpawn; + + /// + /// Whether the player should see logs. + /// + public bool DisplayLogs = true; + + public Vector2 oldSpawn = Vector2.Zero; + + /// + /// The last player that the player whispered with( to or from ). + /// + public TSPlayer LastWhisper; + + /// + /// The number of unsuccessful login attempts. + /// + public int LoginAttempts { get; set; } + + public Vector2 TeleportCoords = new Vector2(-1, -1); + + public Vector2 LastNetPosition = Vector2.Zero; + + /// + /// The player's login name. + /// + public string UserAccountName { get; set; } + + /// + /// Whether the player performed a valid login attempt (i.e. entered valid user name and password) but is still blocked + /// from logging in because of SSI. + /// + public bool LoginFailsBySsi { get; set; } + + /// + /// Whether the player is logged in or not. + /// + public bool IsLoggedIn; + + /// + /// Whether the player has sent their whole inventory to the server while connecting. + /// + public bool HasSentInventory { get; set; } + + /// + /// The player's user id( from the db ). + /// + public int UserID = -1; + + /// + /// Whether the player has been nagged about logging in. + /// + public bool HasBeenNaggedAboutLoggingIn; + + public bool TPAllow = true; + + /// + /// Whether the player is muted or not. + /// + public bool mute; + + public bool TpLock; + + private Player FakePlayer; + + public bool RequestedSection; + + /// + /// The last time the player died. + /// + public DateTime LastDeath { get; set; } + + /// + /// Whether the player is dead or not. + /// + public bool Dead; + + public string Country = "??"; + + /// + /// The players difficulty( normal[softcore], mediumcore, hardcore ). + /// + public int Difficulty; + + private string CacheIP; + + public string IgnoreActionsForInventory = "none"; + + public string IgnoreActionsForCheating = "none"; + + public string IgnoreActionsForDisabledArmor = "none"; + + public bool IgnoreActionsForClearingTrashCan; + + /// + /// The player's server side inventory data. + /// + public PlayerData PlayerData; + + /// + /// Whether the player needs to specify a password upon connection( either server or user account ). + /// + public bool RequiresPassword; + + public bool SilentKickInProgress; + + public bool SilentJoinInProgress; + + /// + /// A list of points where ice tiles have been placed. + /// + public List IceTiles; + + /// + /// Unused, can be removed. + /// + public long RPm = 1; + + /// + /// World protection message cool down. + /// + public long WPm = 1; + + /// + /// Spawn protection message cool down. + /// + public long SPm = 1; + + /// + /// Permission to build message cool down. + /// + public long BPm = 1; + + /// + /// The time in ms when the player has logged in. + /// + public long LoginMS; + + /// + /// Whether the player has been harrassed about logging in due to server side inventory or forced login. + /// + public bool LoginHarassed = false; + + /// + /// Whether the player is a real, human, player on the server. + /// + public bool RealPlayer + { + get { return Index >= 0 && Index < Main.maxNetPlayers && Main.player[Index] != null; } + } + + public bool ConnectionAlive + { + get + { + return RealPlayer && + (Netplay.serverSock[Index] != null && Netplay.serverSock[Index].active && !Netplay.serverSock[Index].kill); + } + } + + public int State + { + get { return Netplay.serverSock[Index].state; } + set { Netplay.serverSock[Index].state = value; } + } + + public string IP + { + get + { + if (string.IsNullOrEmpty(CacheIP)) + return + CacheIP = + RealPlayer + ? (Netplay.serverSock[Index].tcpClient.Connected + ? TShock.Utils.GetRealIP(Netplay.serverSock[Index].tcpClient.Client.RemoteEndPoint.ToString()) + : "") + : ""; + else + return CacheIP; + } + } + + /// + /// Saves the player's inventory to SSI + /// + /// bool - True/false if it saved successfully + public bool SaveServerInventory() + { + if (!TShock.Config.ServerSideInventory) + { + return false; + } + try + { + PlayerData.CopyInventory(this); + TShock.InventoryDB.InsertPlayerData(this); + return true; + } catch (Exception e) + { + Log.Error(e.Message); + return false; + } + + } + + /// + /// Terraria Player + /// + public Player TPlayer + { + get { return FakePlayer ?? Main.player[Index]; } + } + + public string Name + { + get { return TPlayer.name; } + } + + public bool Active + { + get { return TPlayer != null && TPlayer.active; } + } + + public int Team + { + get { return TPlayer.team; } + } + + public float X + { + get { return RealPlayer ? TPlayer.position.X : Main.spawnTileX*16; } + } + + public float Y + { + get { return RealPlayer ? TPlayer.position.Y : Main.spawnTileY*16; } + } + + public int TileX + { + get { return (int) (X/16); } + } + + public int TileY + { + get { return (int) (Y/16); } + } + + public bool InventorySlotAvailable + { + get + { + bool flag = false; + if (RealPlayer) + { + for (int i = 0; i < 40; i++) //41 is trash can, 42-45 is coins, 46-49 is ammo + { + if (TPlayer.inventory[i] == null || !TPlayer.inventory[i].active || TPlayer.inventory[i].name == "") + { + flag = true; + break; + } + } + } + return flag; + } + } + + public TSPlayer(int index) + { + TilesDestroyed = new Dictionary(); + TilesCreated = new Dictionary(); + Index = index; + Group = new Group(TShock.Config.DefaultGuestGroupName); + IceTiles = new List(); + AwaitingResponse = new Dictionary>(); + } + + protected TSPlayer(String playerName) + { + TilesDestroyed = new Dictionary(); + TilesCreated = new Dictionary(); + Index = -1; + FakePlayer = new Player {name = playerName, whoAmi = -1}; + Group = new Group(TShock.Config.DefaultGuestGroupName); + AwaitingResponse = new Dictionary>(); + } + + public virtual void Disconnect(string reason) + { + SendData(PacketTypes.Disconnect, reason); + } + + public virtual void Flush() + { + var sock = Netplay.serverSock[Index]; + if (sock == null) + return; + + TShock.PacketBuffer.Flush(sock); + } + + + public void SendWorldInfo(int tilex, int tiley, bool fakeid) + { + using (var ms = new MemoryStream()) + { + var msg = new WorldInfoMsg + { + Time = (int) Main.time, + DayTime = Main.dayTime, + MoonPhase = (byte) Main.moonPhase, + BloodMoon = Main.bloodMoon, + MaxTilesX = Main.maxTilesX, + MaxTilesY = Main.maxTilesY, + SpawnX = tilex, + SpawnY = tiley, + WorldSurface = (int) Main.worldSurface, + RockLayer = (int) Main.rockLayer, + //Sending a fake world id causes the client to not be able to find a stored spawnx/y. + //This fixes the bed spawn point bug. With a fake world id it wont be able to find the bed spawn. + WorldID = !fakeid ? Main.worldID : -1, + WorldFlags = (WorldGen.shadowOrbSmashed ? WorldInfoFlag.OrbSmashed : WorldInfoFlag.None) | + (NPC.downedBoss1 ? WorldInfoFlag.DownedBoss1 : WorldInfoFlag.None) | + (NPC.downedBoss2 ? WorldInfoFlag.DownedBoss2 : WorldInfoFlag.None) | + (NPC.downedBoss3 ? WorldInfoFlag.DownedBoss3 : WorldInfoFlag.None) | + (Main.hardMode ? WorldInfoFlag.HardMode : WorldInfoFlag.None) | + (NPC.downedClown ? WorldInfoFlag.DownedClown : WorldInfoFlag.None), + WorldName = TShock.Config.UseServerName ? TShock.Config.ServerName : Main.worldName + }; + msg.PackFull(ms); + SendRawData(ms.ToArray()); + } + } + + public bool Teleport(int tilex, int tiley) + { + InitSpawn = false; + + SendWorldInfo(tilex, tiley, true); + + //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) + //Should we properly send sections so that clients don't get tiles twice? + SendTileSquare(tilex, tiley, 150); + +/* //We shouldn't need this section anymore -it can prevent otherwise acceptable teleportation under some circumstances. + + if (!SendTileSquare(tilex, tiley, 150)) + { + InitSpawn = true; + SendWorldInfo(Main.spawnTileX, Main.spawnTileY, false); + return false; + } + +*/ + Spawn(-1, -1); + + SendWorldInfo(Main.spawnTileX, Main.spawnTileY, false); + + TPlayer.position.X = (float)(tilex * 16 + 8 - TPlayer.width /2); + TPlayer.position.Y = (float)(tiley * 16 - TPlayer.height); + //We need to send the tile data again to prevent clients from thinking they *really* destroyed blocks just now. + + SendTileSquare(tilex, tiley, 10); + + return true; + } + + public void Spawn() + { + Spawn(TPlayer.SpawnX, TPlayer.SpawnY); + } + + public void Spawn(int tilex, int tiley) + { + using (var ms = new MemoryStream()) + { + var msg = new SpawnMsg + { + PlayerIndex = (byte) Index, + TileX = tilex, + TileY = tiley + }; + msg.PackFull(ms); + SendRawData(ms.ToArray()); + } + } + + public void RemoveProjectile(int index, int owner) + { + using (var ms = new MemoryStream()) + { + var msg = new ProjectileRemoveMsg + { + Index = (short) index, + Owner = (byte) owner + }; + msg.PackFull(ms); + SendRawData(ms.ToArray()); + } + } + + public virtual bool SendTileSquare(int x, int y, int size = 10) + { + try + { + int num = (size - 1)/2; + int m_x=0; + int m_y=0; + + if (x - num <0){ + m_x=0; + }else{ + m_x = x - num; + } + + if (y - num <0){ + m_y=0; + }else{ + m_y = y - num; + } + + if (m_x + size > Main.maxTilesX){ + m_x=Main.maxTilesX - size; + } + + if (m_y + size > Main.maxTilesY){ + m_y=Main.maxTilesY - size; + } + + SendData(PacketTypes.TileSendSquare, "", size, m_x, m_y); + return true; + } + catch (IndexOutOfRangeException) + { + + // This is expected if square exceeds array. + } + catch (Exception ex) + { + Log.Error(ex.ToString()); + } + return false; + } + + public bool GiveItemCheck(int type, string name, int width, int height, int stack, int prefix = 0) + { + if ((TShock.Itembans.ItemIsBanned(name) && TShock.Config.PreventBannedItemSpawn) && + (TShock.Itembans.ItemIsBanned(name, this) || !TShock.Config.AllowAllowedGroupsToSpawnBannedItems)) + return false; + + GiveItem(type,name,width,height,stack,prefix); + return true; + } + + public virtual void GiveItem(int type, string name, int width, int height, int stack, int prefix = 0) + { + int itemid = Item.NewItem((int) X, (int) Y, width, height, type, stack, true, prefix); + + // This is for special pickaxe/hammers/swords etc + Main.item[itemid].SetDefaults(name); + // The set default overrides the wet and stack set by NewItem + Main.item[itemid].wet = Collision.WetCollision(Main.item[itemid].position, Main.item[itemid].width, + Main.item[itemid].height); + Main.item[itemid].stack = stack; + Main.item[itemid].owner = Index; + Main.item[itemid].prefix = (byte) prefix; + NetMessage.SendData((int) PacketTypes.ItemDrop, -1, -1, "", itemid, 0f, 0f, 0f); + NetMessage.SendData((int) PacketTypes.ItemOwner, -1, -1, "", itemid, 0f, 0f, 0f); + } + + public virtual void SendInfoMessage(string msg) + { + SendMessage(msg, Color.Yellow); + } + + public void SendInfoMessage(string format, params object[] args) + { + SendInfoMessage(string.Format(format, args)); + } + + public virtual void SendSuccessMessage(string msg) + { + SendMessage(msg, Color.Green); + } + + public void SendSuccessMessage(string format, params object[] args) + { + SendSuccessMessage(string.Format(format, args)); + } + + public virtual void SendWarningMessage(string msg) + { + SendMessage(msg, Color.OrangeRed); + } + + public void SendWarningMessage(string format, params object[] args) + { + SendWarningMessage(string.Format(format, args)); + } + + public virtual void SendErrorMessage(string msg) + { + SendMessage(msg, Color.Red); + } + + public void SendErrorMessage(string format, params object[] args) + { + SendErrorMessage(string.Format(format, args)); + } + + [Obsolete("Use SendErrorMessage, SendInfoMessage, or SendWarningMessage, or a custom color instead.")] + public virtual void SendMessage(string msg) + { + SendMessage(msg, 0, 255, 0); + } + + public virtual void SendMessage(string msg, Color color) + { + SendMessage(msg, color.R, color.G, color.B); + } + + public virtual void SendMessage(string msg, byte red, byte green, byte blue) + { + SendData(PacketTypes.ChatText, msg, 255, red, green, blue); + } + + public virtual void SendMessageFromPlayer(string msg, byte red, byte green, byte blue, int ply) + { + SendDataFromPlayer(PacketTypes.ChatText, ply, msg, red, green, blue, 0); + } + + public virtual void DamagePlayer(int damage) + { + NetMessage.SendData((int) PacketTypes.PlayerDamage, -1, -1, "", Index, ((new Random()).Next(-1, 1)), damage, + (float) 0); + } + + public virtual void SetTeam(int team) + { + Main.player[Index].team = team; + SendData(PacketTypes.PlayerTeam, "", Index); + } + + public virtual void Disable(string reason = "") + { + LastThreat = DateTime.UtcNow; + SetBuff(33, 330, true); //Weak + SetBuff(32, 330, true); //Slow + SetBuff(23, 330, true); //Cursed + if (!string.IsNullOrEmpty(reason)) + Log.ConsoleInfo(string.Format("Player {0} has been disabled for {1}.", Name, reason)); + + var trace = new StackTrace(); + StackFrame frame = null; + frame = trace.GetFrame(1); + if (frame != null && frame.GetMethod().DeclaringType != null) + Log.Debug(frame.GetMethod().DeclaringType.Name + " called Disable()."); + } + + public virtual void Whoopie(object time) + { + var time2 = (int) time; + var launch = DateTime.UtcNow; + var startname = Name; + SendMessage("You are now being annoyed.", Color.Red); + while ((DateTime.UtcNow - launch).TotalSeconds < time2 && startname == Name) + { + SendData(PacketTypes.NpcSpecial, number: Index, number2: 2f); + Thread.Sleep(50); + } + } + + public virtual void SetBuff(int type, int time = 3600, bool bypass = false) + { + if ((DateTime.UtcNow - LastThreat).TotalMilliseconds < 5000 && !bypass) + return; + + SendData(PacketTypes.PlayerAddBuff, number: Index, number2: type, number3: time); + } + + //Todo: Separate this into a few functions. SendTo, SendToAll, etc + public virtual void SendData(PacketTypes msgType, string text = "", int number = 0, float number2 = 0f, + float number3 = 0f, float number4 = 0f, int number5 = 0) + { + if (RealPlayer && !ConnectionAlive) + return; + + NetMessage.SendData((int) msgType, Index, -1, text, number, number2, number3, number4, number5); + } + + public virtual void SendDataFromPlayer(PacketTypes msgType, int ply, string text = "", float number2 = 0f, float number3 = 0f, float number4 = 0f, int number5 = 0) + { + if (RealPlayer && !ConnectionAlive) + return; + + NetMessage.SendData((int) msgType, Index, -1, text, ply, number2, number3, number4, number5); + } + + public virtual bool SendRawData(byte[] data) + { + if (!RealPlayer || !ConnectionAlive) + return false; + + return TShock.SendBytes(Netplay.serverSock[Index], data); + } + + /// + /// Adds a command callback to a specified command string. + /// + /// The string representing the command i.e "yes" == /yes + /// The method that will be executed on confirmation ie user accepts + public void AddResponse( string name, Action callback) + { + if( AwaitingResponse.ContainsKey(name)) + { + AwaitingResponse.Remove(name); + } + + AwaitingResponse.Add(name, callback); + } + } + + public class TSRestPlayer : TSPlayer + { + internal List CommandOutput = new List(); + + public TSRestPlayer(string playerName, Group playerGroup): base(playerName) + { + Group = playerGroup; + AwaitingResponse = new Dictionary>(); + } + + public override void SendMessage(string msg) + { + SendMessage(msg, 0, 255, 0); + } + + public override void SendMessage(string msg, Color color) + { + SendMessage(msg, color.R, color.G, color.B); + } + + public override void SendMessage(string msg, byte red, byte green, byte blue) + { + this.CommandOutput.Add(msg); + } + + public override void SendInfoMessage(string msg) + { + SendMessage(msg, Color.Yellow); + } + + public override void SendSuccessMessage(string msg) + { + SendMessage(msg, Color.Green); + } + + public override void SendWarningMessage(string msg) + { + SendMessage(msg, Color.OrangeRed); + } + + public override void SendErrorMessage(string msg) + { + SendMessage(msg, Color.Red); + } + + public List GetCommandOutput() + { + return this.CommandOutput; + } + } + + public class TSServerPlayer : TSPlayer + { + public TSServerPlayer() + : base("Server") + { + Group = new SuperAdminGroup(); + } + + public override void SendErrorMessage(string msg) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(msg); + Console.ResetColor(); + } + + public override void SendInfoMessage(string msg) + { + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine(msg); + Console.ResetColor(); + } + + public override void SendSuccessMessage(string msg) + { + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine(msg); + Console.ResetColor(); + } + + public override void SendWarningMessage(string msg) + { + Console.ForegroundColor = ConsoleColor.DarkRed; + Console.WriteLine(msg); + Console.ResetColor(); + } + + public override void SendMessage(string msg) + { + SendMessage(msg, 0, 255, 0); + } + + public override void SendMessage(string msg, Color color) + { + SendMessage(msg, color.R, color.G, color.B); + } + + public override void SendMessage(string msg, byte red, byte green, byte blue) + { + Console.WriteLine(msg); + //RconHandler.Response += msg + "\n"; + } + + public void SetFullMoon(bool fullmoon) + { + Main.moonPhase = 0; + SetTime(false, 0); + } + + public void SetBloodMoon(bool bloodMoon) + { + Main.bloodMoon = bloodMoon; + SetTime(false, 0); + } + + public void SetTime(bool dayTime, double time) + { + Main.dayTime = dayTime; + Main.time = time; + NetMessage.SendData((int) PacketTypes.TimeSet, -1, -1, "", 0, 0, Main.sunModY, Main.moonModY); + NetMessage.syncPlayers(); + } + + public void SpawnNPC(int type, string name, int amount, int startTileX, int startTileY, int tileXRange = 100, + int tileYRange = 50) + { + for (int i = 0; i < amount; i++) + { + int spawnTileX; + int spawnTileY; + TShock.Utils.GetRandomClearTileWithInRange(startTileX, startTileY, tileXRange, tileYRange, out spawnTileX, + out spawnTileY); + int npcid = NPC.NewNPC(spawnTileX*16, spawnTileY*16, type, 0); + // This is for special slimes + Main.npc[npcid].SetDefaults(name); + } + } + + public void StrikeNPC(int npcid, int damage, float knockBack, int hitDirection) + { + // Main.rand is thread static. + if (Main.rand == null) + Main.rand = new Random(); + + Main.npc[npcid].StrikeNPC(damage, knockBack, hitDirection); + NetMessage.SendData((int) PacketTypes.NpcStrike, -1, -1, "", npcid, damage, knockBack, hitDirection); + } + + public void RevertTiles(Dictionary tiles) + { + // Update Main.Tile first so that when tile sqaure is sent it is correct + foreach (KeyValuePair entry in tiles) + { + Main.tile[(int) entry.Key.X, (int) entry.Key.Y].Data = entry.Value; + } + // Send all players updated tile sqaures + foreach (Vector2 coords in tiles.Keys) + { + All.SendTileSquare((int) coords.X, (int) coords.Y, 3); + } + } + } + + public class PlayerData + { + public NetItem[] inventory = new NetItem[NetItem.maxNetInventory]; + public int maxHealth = 100; + //public int maxMana = 100; + public bool exists; + + public PlayerData(TSPlayer player) + { + for (int i = 0; i < NetItem.maxNetInventory; i++) + { + this.inventory[i] = new NetItem(); + } + this.inventory[0].netID = -15; + this.inventory[0].stack = 1; + if (player.TPlayer.inventory[0] != null && player.TPlayer.inventory[0].netID == -15) + this.inventory[0].prefix = player.TPlayer.inventory[0].prefix; + this.inventory[1].netID = -13; + this.inventory[1].stack = 1; + if (player.TPlayer.inventory[1] != null && player.TPlayer.inventory[1].netID == -13) + this.inventory[1].prefix = player.TPlayer.inventory[1].prefix; + this.inventory[2].netID = -16; + this.inventory[2].stack = 1; + if (player.TPlayer.inventory[2] != null && player.TPlayer.inventory[2].netID == -16) + this.inventory[2].prefix = player.TPlayer.inventory[2].prefix; + } + + public void StoreSlot(int slot, int netID, int prefix, int stack) + { + if(slot > (this.inventory.Length - 1)) //if the slot is out of range then dont save + { + return; + } + + this.inventory[slot].netID = netID; + if (this.inventory[slot].netID != 0) + { + this.inventory[slot].stack = stack; + this.inventory[slot].prefix = prefix; + } + else + { + this.inventory[slot].stack = 0; + this.inventory[slot].prefix = 0; + } + } + + public void CopyInventory(TSPlayer player) + { + this.maxHealth = player.TPlayer.statLifeMax; + Item[] inventory = player.TPlayer.inventory; + Item[] armor = player.TPlayer.armor; + for (int i = 0; i < NetItem.maxNetInventory; i++) + { + if (i < 49) + { + if (player.TPlayer.inventory[i] != null) + { + this.inventory[i].netID = inventory[i].netID; + } + else + { + this.inventory[i].netID = 0; + } + + if (this.inventory[i].netID != 0) + { + this.inventory[i].stack = inventory[i].stack; + this.inventory[i].prefix = inventory[i].prefix; + } + else + { + this.inventory[i].stack = 0; + this.inventory[i].prefix = 0; + } + } + else + { + if (player.TPlayer.armor[i - 48] != null) + { + this.inventory[i].netID = armor[i - 48].netID; + } + else + { + this.inventory[i].netID = 0; + } + + if (this.inventory[i].netID != 0) + { + this.inventory[i].stack = armor[i - 48].stack; + this.inventory[i].prefix = armor[i - 48].prefix; + } + else + { + this.inventory[i].stack = 0; + this.inventory[i].prefix = 0; + } + } + } + } + } + + public class NetItem + { + public static int maxNetInventory = 59; + public int netID; + public int stack; + public int prefix; + + public static string ToString(NetItem[] inventory) + { + string inventoryString = ""; + for (int i = 0; i < maxNetInventory; i++) + { + if (i != 0) + inventoryString += "~"; + inventoryString += inventory[i].netID; + if (inventory[i].netID != 0) + { + inventoryString += "," + inventory[i].stack; + inventoryString += "," + inventory[i].prefix; + } + else + { + inventoryString += ",0,0"; + } + } + return inventoryString; + } + + public static NetItem[] Parse(string data) + { + NetItem[] inventory = new NetItem[maxNetInventory]; + int i; + for (i = 0; i < maxNetInventory; i++) + { + inventory[i] = new NetItem(); + } + string[] items = data.Split('~'); + i = 0; + foreach (string item in items) + { + string[] idata = item.Split(','); + inventory[i].netID = int.Parse(idata[0]); + inventory[i].stack = int.Parse(idata[1]); + inventory[i].prefix = int.Parse(idata[2]); + i++; + } + return inventory; + } + } +} diff --git a/TShockAPI/TShockAPI.csproj b/TShockAPI/TShockAPI.csproj index e21ddc8a..94909ef4 100644 --- a/TShockAPI/TShockAPI.csproj +++ b/TShockAPI/TShockAPI.csproj @@ -1,205 +1,205 @@ - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {49606449-072B-4CF5-8088-AA49DA586694} - Library - Properties - TShockAPI - TShockAPI - v4.0 - 512 - false - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - true - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - true - bin\Debug\TShockAPI.XML - - - pdbonly - true - bin\Release\ - TRACE;COMPAT_SIGS - prompt - 4 - true - bin\Release\TShockAPI.XML - - - - ..\HttpBins\HttpServer.dll - - - ..\SqlBins\Mono.Data.Sqlite.dll - - - False - ..\SqlBins\MySql.Data.dll - True - - - False - ..\SqlBins\MySql.Web.dll - True - - - .\Newtonsoft.Json.dll - - - - - - - - - - ..\TerrariaServerBins\TerrariaServer.exe - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - Resources.resx - - - - - - - - - - - - - - - - - - - - - - - - - ResXFileCodeGenerator - Designer - Resources.Designer.cs - - - - - False - Microsoft .NET Framework 4 %28x86 and x64%29 - true - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - false - - - False - Windows Installer 3.1 - true - - - - - - - - - - - - - "$(ProjectDir)postbuild.bat" - - - - - - + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {49606449-072B-4CF5-8088-AA49DA586694} + Library + Properties + TShockAPI + TShockAPI + v4.0 + 512 + false + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + bin\Debug\TShockAPI.XML + + + pdbonly + true + bin\Release\ + TRACE;COMPAT_SIGS + prompt + 4 + true + bin\Release\TShockAPI.XML + + + + ..\HttpBins\HttpServer.dll + + + ..\SqlBins\Mono.Data.Sqlite.dll + + + False + ..\SqlBins\MySql.Data.dll + True + + + False + ..\SqlBins\MySql.Web.dll + True + + + .\Newtonsoft.Json.dll + + + + + + + + + + ..\TerrariaServerBins\TerrariaServer.exe + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + + + + + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Designer + Resources.Designer.cs + + + + + False + Microsoft .NET Framework 4 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 3.1 + true + + + + + + + + + + + + + "$(ProjectDir)postbuild.bat" + + + + + + + --> \ No newline at end of file diff --git a/TShockAPI/Utils.cs b/TShockAPI/Utils.cs index c51e3d5e..2a0c6c45 100644 --- a/TShockAPI/Utils.cs +++ b/TShockAPI/Utils.cs @@ -22,8 +22,8 @@ using System.Linq; using System.Net; using System.Net.Sockets; using System.Security.Cryptography; -using System.Text; -using TShockAPI.DB; +using System.Text; +using TShockAPI.DB; using Terraria; namespace TShockAPI @@ -221,7 +221,7 @@ namespace TShockAPI /// Sends message to all players with 'logs' permission. /// /// Message to send - /// Color of the message + /// Color of the message /// The player to not send the message to. public void SendLogs(string log, Color color, TSPlayer excludedPlayer = null) { @@ -229,7 +229,7 @@ namespace TShockAPI TSPlayer.Server.SendMessage(log, color); foreach (TSPlayer player in TShock.Players) { - if (player != null && player != excludedPlayer && player.Active && player.Group.HasPermission(Permissions.logs) && + if (player != null && player != excludedPlayer && player.Active && player.Group.HasPermission(Permissions.logs) && player.DisplayLogs && TShock.Config.DisableSpewLogs == false) player.SendMessage(log, color); } @@ -560,10 +560,10 @@ namespace TShockAPI // Disconnect after kick as that signifies server is exiting and could cause a race Netplay.disconnect = true; - } - + } + /// - /// Stops the server after kicking all players with a reason message, and optionally saving the world then attempts to + /// Stops the server after kicking all players with a reason message, and optionally saving the world then attempts to /// restart it. /// /// bool perform a world save before stop (default: true) @@ -573,23 +573,23 @@ namespace TShockAPI if (TShock.Config.ServerSideInventory) foreach (TSPlayer player in TShock.Players) if (player != null && player.IsLoggedIn && !player.IgnoreActionsForClearingTrashCan) - TShock.InventoryDB.InsertPlayerData(player); + TShock.InventoryDB.InsertPlayerData(player); StopServer(true, reason); System.Diagnostics.Process.Start(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase); Environment.Exit(0); - } - + } + /// /// Reloads all configuration settings, groups, regions and raises the reload event. - /// - public void Reload(TSPlayer player) - { + /// + public void Reload(TSPlayer player) + { FileTools.SetupConfig(); TShock.HandleCommandLinePostConfigLoad(Environment.GetCommandLineArgs()); TShock.Groups.LoadPermisions(); - TShock.Regions.ReloadAllRegions(); - Hooks.GeneralHooks.OnReloadEvent(player); + TShock.Regions.ReloadAllRegions(); + Hooks.GeneralHooks.OnReloadEvent(player); } #if COMPAT_SIGS @@ -879,19 +879,19 @@ namespace TShockAPI /// /// The region's area to enumerate through. /// The enumerated boundary points. - public IEnumerable EnumerateRegionBoundaries(Rectangle regionArea) - { - for (int x = 0; x < regionArea.Width + 1; x++) - { - yield return new Point(regionArea.Left + x, regionArea.Top); - yield return new Point(regionArea.Left + x, regionArea.Bottom); - } - - for (int y = 1; y < regionArea.Height; y++) - { - yield return new Point(regionArea.Left, regionArea.Top + y); - yield return new Point(regionArea.Right, regionArea.Top + y); - } + public IEnumerable EnumerateRegionBoundaries(Rectangle regionArea) + { + for (int x = 0; x < regionArea.Width + 1; x++) + { + yield return new Point(regionArea.Left + x, regionArea.Top); + yield return new Point(regionArea.Left + x, regionArea.Bottom); + } + + for (int y = 1; y < regionArea.Height; y++) + { + yield return new Point(regionArea.Left, regionArea.Top + y); + yield return new Point(regionArea.Right, regionArea.Top + y); + } } } }