diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..598dd721
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,9 @@
+root = true
+
+[*]
+end_of_line = crlf
+insert_final_newline = true
+
+[*.cs]
+indent_style = tab
+trim_trailing_whitespace = true
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/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..22e6ce9b
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,9 @@
+language: c
+install:
+- sudo apt-get install mono-devel mono-gmcs nunit-console
+script:
+- xbuild ./TShockAPI/TShockAPI.csproj
+notifications:
+ irc: irc.rizon.net#tshock
+ hipchat:
+ secure: hpRLWiHF2j6O2qJOVs++aqAmryN6G5kY0SF26/rKCpQ7klhMlDZIgI1V1dbkKqlculFtW1neS0EBJyV9lmcV5b26H+KhlZYGN0j7q1VcOTM3rvtU6wW0Ap22uRLl2RrnA4kEsgDAsNouPOkyLZ19hlHAISlsId6G4+Rfqg6k+zQ=
\ No newline at end of file
diff --git a/CONTRIBUTING b/CONTRIBUTING
new file mode 100644
index 00000000..8f36fd52
--- /dev/null
+++ b/CONTRIBUTING
@@ -0,0 +1,36 @@
+### Issue Guidelines
+Please follow these simple requirements before posting an issue:
+
+1. TShock version number
+2. Any stack traces that may have happened when the issue occurred
+3. How to reproduce the issue
+
+### Pull Request Dev Guidelines
+
+These guidelines are for contributors. If you do not follow these guidelines your commits will be reverted.
+
+Required:
+- Follow the code style. We generally use microsofts except for m_ infront of private variables.
+- Do not push unfinished features to the master branch, instead create a remote branch and push to that.
+- Do not push untested code to the master branch, instead push to the test branch.
+- Document all compatibility issues in the COMPATIBILITY file. (IE file formats changing)
+- DO NOT MASS COMMIT. Commit changes as you go (without pushing). That way when you push we don't get a thousand changes with a 1-3 line commit message.
+
+Optional:
+- Build Version Increment (http://autobuildversion.codeplex.com/).
+
+----
+
+### Dev Team Guidelines
+
+These guidelines are to be followed by all developers with commit level access to this repository:
+
+- Do not, for any reason, submit code to the master branch before it hits the development branch first. If the development branch is far ahead, and a new bug fix is going out, branch master, then merge with master and remove your branch.
+ - If you are found to do this, you will be the person merging and rebasing your code to fit general-devel.
+- Prior to posting any version on the website, you must tick the version in AssemblyInfo.cs. This is the versioning formula:
+ - Major.Minor.Revision.BuildDate (tick Revision if you're fixing prior to an actual planned release)
+- Do not release any development builds on the forums without consulting another developer first.
+- __Document code prior to marking it done in JIRA__
+- Move any un-tested code to the "Needs Validation" section on JIRA prior to marking it as done.
+- Do not push changes to any branch without a proper issue being assigned in JIRA. If a feature isn't planned for this release, __it shouldn't be in the repo about to be released__.
+- Submit all pull requests to the general-devel branch prior to the master branch, or you will be ignored.
\ No newline at end of file
diff --git a/README.md b/README.md
index 741aca81..f394cf6c 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# TShock
+# TShock [](https://travis-ci.org/NyxStudios/TShock)
TShock is a server modification for Terraria, written in C#, and based upon the [Terraria Server API](https://github.com/Deathmax/TerrariaAPI-Server). It uses JSON for configuration management, and offers several features not present in the Terraria Server normally.
@@ -24,4 +24,3 @@ Feeling like helping out? Want to find an awesome server? Some awesome plugins?
* [Github Releases](https://github.com/TShock/TShock/releases)
* [Download Archive](https://github.com/TShock/TShock/downloads)
-* [Latest Version (4.0.5)](https://s3.amazonaws.com/tshock/TShock+4.0.5.zip)
diff --git a/TShockAPI/BackupManager.cs b/TShockAPI/BackupManager.cs
index 4cb41e87..bc5cec6a 100644
--- a/TShockAPI/BackupManager.cs
+++ b/TShockAPI/BackupManager.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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.IO;
using System.Threading;
diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs
index 81893761..7ff6516d 100644
--- a/TShockAPI/Commands.cs
+++ b/TShockAPI/Commands.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,18 +15,18 @@ 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.Collections.ObjectModel;
using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
-using System.Net;
using System.Text;
using System.Threading;
+using TShockAPI.PluginUpdater;
using Terraria;
using TShockAPI.DB;
-using System.Reflection;
namespace TShockAPI
{
@@ -66,34 +66,54 @@ namespace TShockAPI
public List Names { get; protected set; }
public bool AllowServer { get; set; }
public bool DoLog { get; set; }
- public string Permission { get; protected set; }
- private CommandDelegate command;
+ public List Permissions { get; protected set; }
+
+ private CommandDelegate commandDelegate;
+ public CommandDelegate CommandDelegate
+ {
+ get { return commandDelegate; }
+ set
+ {
+ if (value == null)
+ throw new ArgumentNullException();
+
+ commandDelegate = value;
+ }
+ }
+
+ public Command(List permissionsneeded, CommandDelegate cmd, params string[] names)
+ : this(cmd, names)
+ {
+ Permissions = permissionsneeded;
+ }
public Command(string permissionneeded, CommandDelegate cmd, params string[] names)
: this(cmd, names)
{
- Permission = permissionneeded;
+ Permissions = new List { permissionneeded };
}
public Command(CommandDelegate cmd, params string[] names)
{
+ if (cmd == null)
+ throw new ArgumentNullException("cmd");
if (names == null || names.Length < 1)
- throw new NotSupportedException();
- Permission = null;
+ throw new ArgumentException("names");
+ Permissions = new List();
Names = new List(names);
- command = cmd;
+ CommandDelegate = cmd;
AllowServer = true;
DoLog = true;
}
public bool Run(string msg, TSPlayer ply, List parms)
{
- if (!ply.Group.HasPermission(Permission))
+ if (!CanRun(ply))
return false;
try
{
- command(new CommandArgs(msg, ply, parms));
+ CommandDelegate(new CommandArgs(msg, ply, parms));
}
catch (Exception e)
{
@@ -111,42 +131,57 @@ namespace TShockAPI
public bool CanRun(TSPlayer ply)
{
- return ply.Group.HasPermission(Permission);
+ if (Permissions == null || Permissions.Count < 1)
+ return true;
+ foreach (var Permission in Permissions)
+ {
+ if (ply.Group.HasPermission(Permission))
+ return true;
+ }
+ return false;
}
}
public static class Commands
{
public static List ChatCommands = new List();
+ public static ReadOnlyCollection TShockCommands = new ReadOnlyCollection(new List());
private delegate void AddChatCommand(string permission, CommandDelegate command, params string[] names);
public static void InitCommands()
{
- AddChatCommand add = (p, c, n) => ChatCommands.Add(new Command(p, c, n));
- ChatCommands.Add(new Command(AuthToken, "auth") { AllowServer = false });
- ChatCommands.Add(new Command(Permissions.canchangepassword, PasswordUser, "password") { AllowServer = false, DoLog = false });
- ChatCommands.Add(new Command(Permissions.canregister, RegisterUser, "register") { AllowServer = false, DoLog = false });
- ChatCommands.Add(new Command(Permissions.rootonly, ManageUsers, "user") { DoLog = false });
- ChatCommands.Add(new Command(Permissions.canlogin, AttemptLogin, "login") { AllowServer = false, DoLog = false });
- ChatCommands.Add(new Command(Permissions.buff, Buff, "buff") { AllowServer = false });
- ChatCommands.Add(new Command(Permissions.cfg, SetSpawn, "setspawn") { AllowServer = false });
- ChatCommands.Add(new Command(Permissions.grow, Grow, "grow") { AllowServer = false });
- ChatCommands.Add(new Command(Permissions.item, Item, "item", "i") { AllowServer = false });
- ChatCommands.Add(new Command(Permissions.tp, Home, "home") { AllowServer = false });
- ChatCommands.Add(new Command(Permissions.canpartychat, PartyChat, "p") { AllowServer = false });
- ChatCommands.Add(new Command(Permissions.tp, Spawn, "spawn") { AllowServer = false });
- ChatCommands.Add(new Command(Permissions.tp, TP, "tp") { AllowServer = false });
- ChatCommands.Add(new Command(Permissions.tp, TPHere, "tphere") { AllowServer = false });
- ChatCommands.Add(new Command(Permissions.tpallow, TPAllow, "tpallow") { AllowServer = false });
+ List tshockCommands = new List(100);
+ Action add2 = (cmd) =>
+ {
+ tshockCommands.Add(cmd);
+ ChatCommands.Add(cmd);
+ };
+ AddChatCommand add = (p, c, n) => add2(new Command(p, c, n));
+
+ add2(new Command(AuthToken, "auth") { AllowServer = false });
+ add2(new Command(Permissions.canchangepassword, PasswordUser, "password") { AllowServer = false, DoLog = false });
+ add2(new Command(Permissions.canregister, RegisterUser, "register") { AllowServer = false, DoLog = false });
+ add2(new Command(Permissions.user, ManageUsers, "user") { DoLog = false });
+ add2(new Command(Permissions.canlogin, AttemptLogin, "login") { AllowServer = false, DoLog = false });
+ add2(new Command(Permissions.buff, Buff, "buff") { AllowServer = false });
+ add2(new Command(Permissions.worldspawn, SetSpawn, "setspawn") { AllowServer = false });
+ add2(new Command(Permissions.grow, Grow, "grow") { AllowServer = false });
+ add2(new Command(Permissions.item, Item, "item", "i") { AllowServer = false });
+ add2(new Command(Permissions.home, Home, "home") { AllowServer = false });
+ add2(new Command(Permissions.canpartychat, PartyChat, "p") { AllowServer = false });
+ add2(new Command(Permissions.spawn, Spawn, "spawn") { AllowServer = false });
+ add2(new Command(Permissions.tp, TP, "tp") { AllowServer = false });
+ add2(new Command(Permissions.tphere, TPHere, "tphere") { AllowServer = false });
+ add2(new Command(Permissions.tpallow, TPAllow, "tpallow") { AllowServer = false });
add(Permissions.kick, Kick, "kick");
- add(Permissions.ban, DeprecateBans, "banip", "listbans", "unban", "unbanip", "clearbans");
add(Permissions.ban, Ban, "ban");
add(Permissions.whitelist, Whitelist, "whitelist");
add(Permissions.maintenance, Off, "off", "exit");
- add(Permissions.maintenance, Restart, "restart"); //Added restart command
+ add(Permissions.maintenance, Restart, "restart");
add(Permissions.maintenance, OffNoSave, "off-nosave", "exit-nosave");
add(Permissions.maintenance, CheckUpdates, "checkupdates");
+ add(Permissions.updateplugins, UpdatePlugins, "updateplugins");
add(Permissions.causeevents, DropMeteor, "dropmeteor");
add(Permissions.causeevents, Star, "star");
add(Permissions.causeevents, Fullmoon, "fullmoon");
@@ -163,26 +198,21 @@ namespace TShockAPI
add(Permissions.spawnboss, Hardcore, "hardcore");
add(Permissions.spawnmob, SpawnMob, "spawnmob", "sm");
add(Permissions.warp, Warp, "warp");
- add(null, DeprecateWarp, "setwarp", "sendwarp", "delwarp", "sw");
- add(Permissions.managegroup, AddGroup, "addgroup");
- add(Permissions.managegroup, DeleteGroup, "delgroup");
- add(Permissions.managegroup, ModifyGroup, "modgroup");
- add(Permissions.managegroup, ViewGroups, "group");
- add(Permissions.manageitem, AddItem, "additem", "banitem");
- add(Permissions.manageitem, DeleteItem, "delitem", "unbanitem");
- add(Permissions.manageitem, ListItems, "listitems", "listbanneditems");
- add(Permissions.manageitem, AddItemGroup, "additemgroup");
- add(Permissions.manageitem, DeleteItemGroup, "delitemgroup");
+ add(Permissions.managegroup, Group, "group");
+ add(Permissions.managegroup, GroupDeprecated, "addgroup", "delgroup", "modgroup");
+ add(Permissions.manageitem, ItemBan, "itemban");
+ add(Permissions.manageitem, ItemBanDeprecated,
+ "additem", "additemgroup", "banitem", "delitem", "delitemgroup", "listitems", "listbanneditems", "unbanitem");
add(Permissions.manageregion, Region, "region");
add(Permissions.manageregion, DebugRegions, "debugreg");
- add(Permissions.cfg, Reload, "reload");
- add(Permissions.cfg, ServerPassword, "serverpassword");
- add(Permissions.cfg, Save, "save");
- add(Permissions.cfg, Settle, "settle");
- add(Permissions.cfg, MaxSpawns, "maxspawns");
- add(Permissions.cfg, SpawnRate, "spawnrate");
+ add(Permissions.cfgreload, Reload, "reload");
+ add(Permissions.cfgpassword, ServerPassword, "serverpassword");
+ add(Permissions.worldsave, Save, "save");
+ add(Permissions.worldsettle, Settle, "settle");
+ add(Permissions.cfgmaxspawns, MaxSpawns, "maxspawns");
+ add(Permissions.cfgspawnrate, SpawnRate, "spawnrate");
add(Permissions.time, Time, "time");
- add(Permissions.pvpfun, Slap, "slap");
+ add(Permissions.slap, Slap, "slap");
add(Permissions.editspawn, ToggleAntiBuild, "antibuild");
add(Permissions.editspawn, ProtectSpawn, "protectspawn");
add(Permissions.maintenance, GetVersion, "version");
@@ -194,8 +224,8 @@ namespace TShockAPI
add(Permissions.mute, Mute, "mute", "unmute");
add(Permissions.logs, DisplayLogs, "displaylogs");
add(Permissions.userinfo, GrabUserUserInfo, "userinfo", "ui");
- add(Permissions.rootonly, AuthVerify, "auth-verify");
- add(Permissions.cfg, Broadcast, "broadcast", "bc", "say");
+ add(Permissions.authverify, AuthVerify, "auth-verify");
+ add(Permissions.broadcast, Broadcast, "broadcast", "bc", "say");
add(Permissions.whisper, Whisper, "whisper", "w", "tell");
add(Permissions.whisper, Reply, "reply", "r");
add(Permissions.annoy, Annoy, "annoy");
@@ -207,12 +237,17 @@ namespace TShockAPI
add(Permissions.buffplayer, GBuff, "gbuff", "buffplayer");
add(Permissions.hardmode, StartHardMode, "hardmode");
add(Permissions.hardmode, DisableHardMode, "stophardmode", "disablehardmode");
- add(Permissions.cfg, ServerInfo, "stats");
- add(Permissions.cfg, WorldInfo, "world");
+ add(Permissions.serverinfo, ServerInfo, "stats");
+ add(Permissions.worldinfo, WorldInfo, "world");
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(Rests.RestPermissions.restmanage, ManageRest, "rest");
//add(null, TestCallbackCommand, "test");
+
+ TShockCommands = new ReadOnlyCollection(tshockCommands);
}
public static bool HandleCommand(TSPlayer player, string text)
@@ -226,6 +261,9 @@ namespace TShockAPI
string cmdName = args[0].ToLower();
args.RemoveAt(0);
+ if (Hooks.PlayerHooks.OnPlayerCommand(player, cmdName, cmdText, args))
+ return true;
+
IEnumerable cmds = ChatCommands.Where(c => c.HasAlias(cmdName));
if (cmds.Count() == 0)
@@ -244,7 +282,7 @@ namespace TShockAPI
{
if (!cmd.CanRun(player))
{
- TShock.Utils.SendLogs(string.Format("{0} tried to execute /{1}.", player.Name, cmdText), Color.Red);
+ TShock.Utils.SendLogs(string.Format("{0} tried to execute /{1}.", player.Name, cmdText), Color.PaleVioletRed, player);
player.SendErrorMessage("You do not have access to that command.");
}
else if (!cmd.AllowServer && !player.RealPlayer)
@@ -254,7 +292,7 @@ namespace TShockAPI
else
{
if (cmd.DoLog)
- TShock.Utils.SendLogs(string.Format("{0} executed: /{1}.", player.Name, cmdText), Color.Red);
+ TShock.Utils.SendLogs(string.Format("{0} executed: /{1}.", player.Name, cmdText), Color.PaleVioletRed, player);
cmd.Run(cmdText, player, args);
}
}
@@ -352,7 +390,7 @@ namespace TShockAPI
#region Account commands
- public static void AttemptLogin(CommandArgs args)
+ private static void AttemptLogin(CommandArgs args)
{
if (args.Player.LoginAttempts > TShock.Config.MaximumLoginAttempts && (TShock.Config.MaximumLoginAttempts != -1))
{
@@ -361,17 +399,22 @@ namespace TShockAPI
TShock.Utils.Kick(args.Player, "Too many invalid login attempts.");
return;
}
-
+
User user = TShock.Users.GetUserByName(args.Player.Name);
string encrPass = "";
if (args.Parameters.Count == 1)
{
+ if (Hooks.PlayerHooks.OnPlayerPreLogin(args.Player, args.Player.Name, args.Parameters[0]))
+ return;
user = TShock.Users.GetUserByName(args.Player.Name);
encrPass = TShock.Utils.HashPassword(args.Parameters[0]);
}
else if (args.Parameters.Count == 2 && TShock.Config.AllowLoginAnyUsername)
{
+ if (Hooks.PlayerHooks.OnPlayerPreLogin(args.Player, args.Parameters[0], args.Parameters[1]))
+ return;
+
user = TShock.Users.GetUserByName(args.Parameters[0]);
encrPass = TShock.Utils.HashPassword(args.Parameters[1]);
if (String.IsNullOrEmpty(args.Parameters[0]))
@@ -382,7 +425,7 @@ namespace TShockAPI
}
else
{
- args.Player.SendErrorMessage(String.Format("Syntax: /login{0} ", TShock.Config.AllowLoginAnyUsername ? " " : " [username]"));
+ args.Player.SendErrorMessage(String.Format("Syntax: /login{0} ", TShock.Config.AllowLoginAnyUsername ? " [username]" : " "));
args.Player.SendErrorMessage("If you forgot your password, there is no way to recover it.");
return;
}
@@ -406,11 +449,13 @@ namespace TShockAPI
}
else if (!TShock.CheckInventory(args.Player))
{
+ args.Player.LoginFailsBySsi = true;
args.Player.SendErrorMessage("Login failed. Please fix the above errors then /login again.");
args.Player.IgnoreActionsForClearingTrashCan = true;
return;
}
}
+ args.Player.LoginFailsBySsi = false;
if (group.HasPermission(Permissions.ignorestackhackdetection))
args.Player.IgnoreActionsForCheating = "none";
@@ -419,6 +464,7 @@ namespace TShockAPI
args.Player.IgnoreActionsForDisabledArmor = "none";
args.Player.Group = group;
+ args.Player.tempGroup = null;
args.Player.UserAccountName = user.Name;
args.Player.UserID = TShock.Users.GetUserID(args.Player.UserAccountName);
args.Player.IsLoggedIn = true;
@@ -443,7 +489,7 @@ namespace TShockAPI
}
- Hooks.PlayerLoginEvent.OnPlayerLogin(args.Player);
+ Hooks.PlayerHooks.OnPlayerPostLogin(args.Player);
}
else
{
@@ -482,7 +528,7 @@ namespace TShockAPI
}
else
{
- args.Player.SendErrorMessage("Not logged in or invalid syntax! Syntax: /password ");
+ args.Player.SendErrorMessage("Not logged in or invalid syntax! Proper syntax: /password ");
}
}
catch (UserManagerException ex)
@@ -516,7 +562,7 @@ namespace TShockAPI
user.Group = TShock.Config.DefaultRegistrationGroupName; // FIXME -- we should get this from the DB. --Why?
- if (TShock.Users.GetUserByName(user.Name) == null) // Cheap way of checking for existance of a user
+ if (TShock.Users.GetUserByName(user.Name) == null && user.Name != TSServerPlayer.AccountName) // Cheap way of checking for existance of a user
{
args.Player.SendSuccessMessage("Account " + user.Name + " has been registered.");
args.Player.SendSuccessMessage("Your password is " + user.Password);
@@ -547,41 +593,22 @@ namespace TShockAPI
string subcmd = args.Parameters[0];
- // Add requires a username:password pair/ip address and a group specified.
+ // Add requires a username, password, and a group specified.
if (subcmd == "add")
{
- var namepass = args.Parameters[1].Split(':');
var user = new User();
try
{
- if (args.Parameters.Count > 2)
+ if (args.Parameters.Count == 4)
{
- if (namepass.Length == 2)
- {
- user.Name = namepass[0];
- user.Password = namepass[1];
- user.Group = args.Parameters[2];
- }
- else if (namepass.Length == 1)
- {
- user.Address = namepass[0];
- user.Group = args.Parameters[2];
- user.Name = user.Address;
- }
- if (!string.IsNullOrEmpty(user.Address))
- {
- args.Player.SendSuccessMessage("IP address admin added. If they're logged in, tell them to rejoin.");
- args.Player.SendSuccessMessage("WARNING: This is insecure! It would be better to use a user account instead.");
- TShock.Users.AddUser(user);
- Log.ConsoleInfo(args.Player.Name + " added IP " + user.Address + " to group " + user.Group);
- }
- else
- {
- args.Player.SendSuccessMessage("Account " + user.Name + " has been added to group " + user.Group + "!");
- TShock.Users.AddUser(user);
- Log.ConsoleInfo(args.Player.Name + " added Account " + user.Name + " to group " + user.Group);
- }
+ user.Name = args.Parameters[1];
+ user.Password = args.Parameters[2];
+ user.Group = args.Parameters[3];
+
+ args.Player.SendSuccessMessage("Account " + user.Name + " has been added to group " + user.Group + "!");
+ TShock.Users.AddUser(user);
+ Log.ConsoleInfo(args.Player.Name + " added Account " + user.Name + " to group " + user.Group);
}
else
{
@@ -598,13 +625,7 @@ namespace TShockAPI
else if (subcmd == "del" && args.Parameters.Count == 2)
{
var user = new User();
- if (args.Parameters[1].Split('.').Count() ==4)
-
- // changed to support dot character in usernames
- // if (args.Parameters[1].Contains("."))
- user.Address = args.Parameters[1];
- else
- user.Name = args.Parameters[1];
+ user.Name = args.Parameters[1];
try
{
@@ -646,32 +667,16 @@ namespace TShockAPI
// Group changing requires a username or IP address, and a new group to set
else if (subcmd == "group")
{
- var user = new User();
- if (args.Parameters[1].Split('.').Count()==4)
-
- //changed to support dot character in usernames
- //if (args.Parameters[1].Contains("."))
-
- user.Address = args.Parameters[1];
- else
- user.Name = args.Parameters[1];
+ var user = new User();
+ user.Name = args.Parameters[1];
try
{
if (args.Parameters.Count == 3)
{
- if (!string.IsNullOrEmpty(user.Address))
- {
- args.Player.SendSuccessMessage("IP address " + user.Address + " has been changed to group " + args.Parameters[2] + "!");
- TShock.Users.SetUserGroup(user, args.Parameters[2]);
- Log.ConsoleInfo(args.Player.Name + " changed IP address " + user.Address + " to group " + args.Parameters[2] + ".");
- }
- else
- {
- args.Player.SendSuccessMessage("Account " + user.Name + " has been changed to group " + args.Parameters[2] + "!");
- TShock.Users.SetUserGroup(user, args.Parameters[2]);
- Log.ConsoleInfo(args.Player.Name + " changed account " + user.Name + " to group " + args.Parameters[2] + ".");
- }
+ args.Player.SendSuccessMessage("Account " + user.Name + " has been changed to group " + args.Parameters[2] + "!");
+ TShock.Users.SetUserGroup(user, args.Parameters[2]);
+ Log.ConsoleInfo(args.Player.Name + " changed account " + user.Name + " to group " + args.Parameters[2] + ".");
}
else
{
@@ -687,7 +692,7 @@ namespace TShockAPI
else if (subcmd == "help")
{
args.Player.SendInfoMessage("Use command help:");
- args.Player.SendInfoMessage("/user add username:password group -- Adds a specified user");
+ args.Player.SendInfoMessage("/user add username password group -- Adds a specified user");
args.Player.SendInfoMessage("/user del username -- Removes a specified user");
args.Player.SendInfoMessage("/user password username newpassword -- Changes a user's password");
args.Player.SendInfoMessage("/user group username newgroup -- Changes a user's group");
@@ -702,7 +707,7 @@ namespace TShockAPI
#region Stupid commands
- public static void ServerInfo(CommandArgs args)
+ private static void ServerInfo(CommandArgs args)
{
args.Player.SendInfoMessage("Memory usage: " + Process.GetCurrentProcess().WorkingSet64);
args.Player.SendInfoMessage("Allocated memory: " + Process.GetCurrentProcess().VirtualMemorySize64);
@@ -712,9 +717,10 @@ namespace TShockAPI
args.Player.SendInfoMessage("Machine name: " + Environment.MachineName);
}
- public static void WorldInfo(CommandArgs args)
+ private static void WorldInfo(CommandArgs args)
{
args.Player.SendInfoMessage("World name: " + Main.worldName);
+ args.Player.SendInfoMessage("World size: {0}x{1}", Main.maxTilesX, Main.maxTilesY);
args.Player.SendInfoMessage("World ID: " + Main.worldID);
}
@@ -805,21 +811,10 @@ namespace TShockAPI
}
}
- private static void DeprecateBans(CommandArgs args)
- {
- args.Player.SendInfoMessage("All ban commands were merged into one in TShock 4.0.");
- args.Player.SendInfoMessage("Syntax: /ban [option] [arguments]");
- args.Player.SendInfoMessage("Options: list, listip, clear, add, addip, del, delip");
- args.Player.SendInfoMessage("Arguments: list, listip, clear [code], add [name], addip [ip], del [name], delip [name]");
- args.Player.SendInfoMessage("In addition, a reason may be provided for all new bans after the arguments.");
- return;
- }
-
private static void Ban(CommandArgs args)
{
if (args.Parameters.Count == 0 || args.Parameters[0].ToLower() == "help")
{
- args.Player.SendInfoMessage("All ban commands were merged into one in TShock 4.0.");
args.Player.SendInfoMessage("Syntax: /ban [option] [arguments]");
args.Player.SendInfoMessage("Options: list, listip, clear, add, addip, del, delip");
args.Player.SendInfoMessage("Arguments: list, listip, clear [code], add [name], addip [ip], del [name], delip [name]");
@@ -984,7 +979,7 @@ namespace TShockAPI
string reason = args.Parameters.Count > 2
? String.Join(" ", args.Parameters.GetRange(2, args.Parameters.Count - 2))
: "Misbehavior.";
- if (!TShock.Utils.Ban(players[0], reason, !args.Player.RealPlayer, args.Player.Name))
+ if (!TShock.Utils.Ban(players[0], reason, !args.Player.RealPlayer, args.Player.UserAccountName))
{
args.Player.SendErrorMessage("You can't ban another admin!");
}
@@ -999,7 +994,7 @@ namespace TShockAPI
string reason = args.Parameters.Count > 2
? String.Join(" ", args.Parameters.GetRange(2, args.Parameters.Count - 2))
: "Manually added IP address ban.";
- TShock.Bans.AddBan(ip, "", reason);
+ TShock.Bans.AddBan(ip, "", reason, false, args.Player.UserAccountName);
args.Player.SendSuccessMessage(ip + " banned.");
return;
#endregion Add ip ban
@@ -1092,7 +1087,7 @@ namespace TShockAPI
private static int ClearBansCode = -1;
- public static void Whitelist(CommandArgs args)
+ private static void Whitelist(CommandArgs args)
{
if (args.Parameters.Count == 1)
{
@@ -1104,13 +1099,13 @@ namespace TShockAPI
}
}
- public static void DisplayLogs(CommandArgs args)
+ private static void DisplayLogs(CommandArgs args)
{
args.Player.DisplayLogs = (!args.Player.DisplayLogs);
args.Player.SendSuccessMessage("You will " + (args.Player.DisplayLogs ? "now" : "no longer") + " receive logs.");
}
- public static void SaveSSI(CommandArgs args )
+ private static void SaveSSI(CommandArgs args)
{
if (TShock.Config.ServerSideInventory)
{
@@ -1125,31 +1120,51 @@ namespace TShockAPI
}
}
- public static void OverrideSSI( CommandArgs args )
+ private static void OverrideSSI(CommandArgs args)
{
+ if (!TShock.Config.ServerSideInventory)
+ {
+ args.Player.SendErrorMessage("Server Side Inventory is disabled.");
+ return;
+ }
if( args.Parameters.Count < 1 )
{
- args.Player.SendErrorMessage("Correct usage: /overridessi(/ossi) ");
+ args.Player.SendErrorMessage("Correct usage: /overridessi|/ossi ");
return;
}
- var players = TShock.Utils.FindPlayer(args.Parameters[0]);
- if( players.Count < 1 )
+ string playerNameToMatch = string.Join(" ", args.Parameters);
+ var matchedPlayers = TShock.Utils.FindPlayer(playerNameToMatch);
+ if( matchedPlayers.Count < 1 )
{
- args.Player.SendErrorMessage("No players match " + args.Parameters[0] + "!");
+ args.Player.SendErrorMessage("No players matched \"{0}\".", playerNameToMatch);
+ return;
}
- else if( players.Count > 1 )
+ else if( matchedPlayers.Count > 1 )
{
- args.Player.SendErrorMessage( players.Count + " players matched " + args.Parameters[0] + "!");
+ args.Player.SendErrorMessage("{0} players matched \"{1}\".", matchedPlayers.Count, playerNameToMatch);
+ return;
}
- else if (TShock.Config.ServerSideInventory)
+
+ TSPlayer matchedPlayer = matchedPlayers[0];
+ if (matchedPlayer.IsLoggedIn)
{
- if( players[0] != null && players[0].IsLoggedIn && !players[0].IgnoreActionsForClearingTrashCan)
- {
- args.Player.SendSuccessMessage( players[0].Name + " has been exempted and updated.");
- TShock.InventoryDB.InsertPlayerData(players[0]);
- }
+ args.Player.SendErrorMessage("Player \"{0}\" is already logged in.", matchedPlayer.Name);
+ return;
}
+ if (!matchedPlayer.LoginFailsBySsi)
+ {
+ args.Player.SendErrorMessage("Player \"{0}\" has to perform a /login attempt first.", matchedPlayer.Name);
+ return;
+ }
+ if (matchedPlayer.IgnoreActionsForClearingTrashCan)
+ {
+ args.Player.SendErrorMessage("Player \"{0}\" has to reconnect first.", matchedPlayer.Name);
+ return;
+ }
+
+ TShock.InventoryDB.InsertPlayerData(matchedPlayer);
+ args.Player.SendSuccessMessage("SSI of player \"{0}\" has been overriden.", matchedPlayer.Name);
}
private static void ForceXmas(CommandArgs args)
@@ -1184,21 +1199,54 @@ namespace TShockAPI
(TShock.Config.ForceXmas ? "in" : "not in")));
}
+ private static void TempGroup(CommandArgs args)
+ {
+ if (args.Parameters.Count < 2)
+ {
+ args.Player.SendInfoMessage("Invalid usage");
+ args.Player.SendInfoMessage("Usage: /tempgroup ");
+ return;
+ }
+
+ List ply = TShock.Utils.FindPlayer(args.Parameters[0]);
+ if(ply.Count < 1)
+ {
+ args.Player.SendErrorMessage(string.Format("Could not find player {0}.", args.Parameters[0]));
+ return;
+ }
+
+ if (ply.Count > 1)
+ {
+ args.Player.SendErrorMessage(string.Format("Found more than one match for {0}.", args.Parameters[0]));
+ return;
+ }
+
+ if(!TShock.Groups.GroupExists(args.Parameters[1]))
+ {
+ args.Player.SendErrorMessage(string.Format("Could not find group {0}", args.Parameters[1]));
+ return;
+ }
+
+ Group g = TShock.Utils.GetGroup(args.Parameters[1]);
+
+ ply[0].tempGroup = g;
+
+ args.Player.SendSuccessMessage(string.Format("You have changed {0}'s group to {1}", ply[0].Name, g.Name));
+ ply[0].SendSuccessMessage(string.Format("Your group has temporarily been changed to {0}", g.Name));
+ }
+
#endregion Player Management Commands
#region Server Maintenence Commands
private static void Broadcast(CommandArgs args)
{
- string message = "";
+ string message = string.Join(" ", args.Parameters);
- for (int i = 0; i < args.Parameters.Count; i++)
- {
- message += " " + args.Parameters[i];
- }
-
- TShock.Utils.Broadcast("(Server Broadcast)" + message, Color.Red);
- return;
+ TShock.Utils.Broadcast(
+ "(Server Broadcast) " + message,
+ Convert.ToByte(TShock.Config.BroadcastRGB[0]), Convert.ToByte(TShock.Config.BroadcastRGB[1]),
+ Convert.ToByte(TShock.Config.BroadcastRGB[2]));
}
private static void Off(CommandArgs args)
@@ -1218,7 +1266,7 @@ namespace TShockAPI
string reason = ((args.Parameters.Count > 0) ? "Server shutting down: " + String.Join(" ", args.Parameters) : "Server shutting down!");
TShock.Utils.StopServer(true, reason);
}
- //Added restart command
+
private static void Restart(CommandArgs args)
{
if (Main.runningMono)
@@ -1227,21 +1275,8 @@ namespace TShockAPI
}
else
{
- if (TShock.Config.ServerSideInventory)
- {
- foreach (TSPlayer player in TShock.Players)
- {
- if (player != null && player.IsLoggedIn && !player.IgnoreActionsForClearingTrashCan)
- {
- TShock.InventoryDB.InsertPlayerData(player);
- }
- }
- }
-
string reason = ((args.Parameters.Count > 0) ? "Server shutting down: " + String.Join(" ", args.Parameters) : "Server shutting down!");
- TShock.Utils.StopServer(true, reason);
- System.Diagnostics.Process.Start(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
- Environment.Exit(0);
+ TShock.Utils.RestartServer(true, reason);
}
}
@@ -1257,6 +1292,65 @@ namespace TShockAPI
ThreadPool.QueueUserWorkItem(UpdateManager.CheckUpdate);
}
+ private static void UpdatePlugins(CommandArgs args)
+ {
+ 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;
+ }
+ }
+ }
+
#endregion Server Maintenence Commands
#region Cause Events and Spawn Monsters Commands
@@ -1667,25 +1761,6 @@ namespace TShockAPI
args.Player.TPAllow = !args.Player.TPAllow;
}
- private static void DeprecateWarp(CommandArgs args)
- {
- if (args.Player.Group.HasPermission(Permissions.managewarp))
- {
- args.Player.SendInfoMessage("All warp commands were merged into one in TShock 4.0.");
- args.Player.SendInfoMessage("Previous warps with spaces should be wrapped in single quotes.");
- args.Player.SendInfoMessage("Invalid syntax. Syntax: /warp [command] [arguments]");
- args.Player.SendInfoMessage("Commands: add, del, hide, list, send, [warpname]");
- args.Player.SendInfoMessage("Arguments: add [warp name], del [warp name], list [page]");
- args.Player.SendInfoMessage("Arguments: send [player] [warp name], hide [warp name] [Enable(true/false)]");
- args.Player.SendInfoMessage("Examples: /warp add foobar, /warp hide foobar true, /warp foobar");
- }
- else
- {
- args.Player.SendErrorMessage("Invalid syntax. Syntax: /warp [name] or /warp list ");
- args.Player.SendErrorMessage("Previous warps with spaces should be wrapped in single quotes.");
- }
- }
-
private static void Warp(CommandArgs args)
{
bool hasManageWarpPermission = args.Player.Group.HasPermission(Permissions.managewarp);
@@ -1693,9 +1768,7 @@ namespace TShockAPI
{
if (hasManageWarpPermission)
{
- args.Player.SendInfoMessage("All warp commands were merged into one in TShock 4.0.");
- args.Player.SendInfoMessage("Previous warps with spaces should be wrapped in single quotes.");
- args.Player.SendInfoMessage("Invalid syntax. Syntax: /warp [command] [arguments]");
+ args.Player.SendInfoMessage("Invalid syntax! Proper syntax: /warp [command] [arguments]");
args.Player.SendInfoMessage("Commands: add, del, hide, list, send, [warpname]");
args.Player.SendInfoMessage("Arguments: add [warp name], del [warp name], list [page]");
args.Player.SendInfoMessage("Arguments: send [player] [warp name], hide [warp name] [Enable(true/false)]");
@@ -1704,65 +1777,26 @@ namespace TShockAPI
}
else
{
- args.Player.SendErrorMessage("Invalid syntax. Syntax: /warp [name] or /warp list ");
- args.Player.SendErrorMessage("Previous warps with spaces should be wrapped in single quotes.");
-
+ args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /warp [name] or /warp list ");
return;
}
}
if (args.Parameters[0].Equals("list"))
{
- #region
- //How many warps per page
- const int pagelimit = 15;
- //How many warps per line
- const int perline = 5;
- //Pages start at 0 but are displayed and parsed at 1
- int page = 0;
-
-
- if (args.Parameters.Count > 1)
- {
- if (!int.TryParse(args.Parameters[1], out page) || page < 1)
- {
- args.Player.SendErrorMessage(string.Format("Invalid page number ({0})", page));
- return;
- }
- page--; //Substract 1 as pages are parsed starting at 1 and not 0
- }
-
- var warps = TShock.Warps.ListAllPublicWarps(Main.worldID.ToString());
-
- //Check if they are trying to access a page that doesn't exist.
- int pagecount = warps.Count/pagelimit;
- if (page > pagecount)
- {
- args.Player.SendErrorMessage(string.Format("Page number exceeds pages ({0}/{1}).", page + 1, pagecount + 1));
+ #region List warps
+ int pageNumber;
+ if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
return;
- }
-
- //Display the current page and the number of pages.
- args.Player.SendSuccessMessage(string.Format("Current warps ({0}/{1}):", page + 1, pagecount + 1));
-
- //Add up to pagelimit names to a list
- var nameslist = new List();
- for (int i = (page*pagelimit); (i < ((page*pagelimit) + pagelimit)) && i < warps.Count; i++)
- {
- nameslist.Add(warps[i].WarpName);
- }
-
- //convert the list to an array for joining
- var names = nameslist.ToArray();
- for (int i = 0; i < names.Length; i += perline)
- {
- args.Player.SendInfoMessage(string.Join(", ", names, i, Math.Min(names.Length - i, perline)));
- }
-
- if (page < pagecount)
- {
- args.Player.SendInfoMessage(string.Format("Type /warp list {0} for more warps.", (page + 2)));
- }
+ IEnumerable warpNames = from warp in TShock.Warps.ListAllPublicWarps(Main.worldID.ToString())
+ select warp.WarpName;
+ PaginationTools.SendPage(args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(warpNames),
+ new PaginationTools.Settings
+ {
+ HeaderFormat = "Warps ({0}/{1}):",
+ FooterFormat = "Type /warp list {0} for more.",
+ NothingToDisplayString = "There are currently no warps defined."
+ });
#endregion
}
else if (args.Parameters[0].ToLower() == "add" && hasManageWarpPermission)
@@ -1787,7 +1821,6 @@ namespace TShockAPI
else
args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /warp add [name]");
#endregion
-
}
else if (args.Parameters[0].ToLower() == "del" && hasManageWarpPermission)
{
@@ -1803,7 +1836,6 @@ namespace TShockAPI
else
args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /warp del [name]");
#endregion
-
}
else if (args.Parameters[0].ToLower() == "hide" && hasManageWarpPermission)
{
@@ -1867,7 +1899,6 @@ namespace TShockAPI
args.Player.SendErrorMessage("Specified warp not found.");
}
#endregion
-
}
else
{
@@ -1889,341 +1920,442 @@ namespace TShockAPI
#region Group Management
- private static void AddGroup(CommandArgs args)
+ private static void GroupDeprecated(CommandArgs args)
{
- if (args.Parameters.Count > 0)
- {
- String groupname = args.Parameters[0];
- args.Parameters.RemoveAt(0);
- String permissions = String.Join(",", args.Parameters);
-
- String response = TShock.Groups.AddGroup(groupname, permissions);
- if (response.Length > 0)
- args.Player.SendSuccessMessage(response);
- }
- else
- {
- args.Player.SendErrorMessage("Incorrect format: /addgroup [optional permissions]");
- }
+ args.Player.SendInfoMessage("The group commands were merged into /group in TShock 4.1; check /group help.");
}
- private static void DeleteGroup(CommandArgs args)
+ private static void Group(CommandArgs args)
{
- if (args.Parameters.Count > 0)
+ if (args.Parameters.Count == 0)
{
- String groupname = args.Parameters[0];
-
- String response = TShock.Groups.DeleteGroup(groupname);
- if (response.Length > 0)
- args.Player.SendSuccessMessage(response);
+ args.Player.SendInfoMessage("Invalid syntax! Proper syntax: /group [arguments]");
+ args.Player.SendInfoMessage("Commands: add, addperm, del, delperm, list, listperm");
+ args.Player.SendInfoMessage("Arguments: add , addperm , del ");
+ args.Player.SendInfoMessage("Arguments: delperm , list [page], listperm [page]");
+ return;
}
- else
+
+ switch (args.Parameters[0].ToLower())
{
- args.Player.SendErrorMessage("Incorrect format: /delgroup ");
- }
- }
-
- private static void ModifyGroup(CommandArgs args)
- {
- if (args.Parameters.Count > 2)
- {
- String com = args.Parameters[0];
- args.Parameters.RemoveAt(0);
-
- String groupname = args.Parameters[0];
- args.Parameters.RemoveAt(0);
-
- string response = "";
- if (com.Equals("add"))
- {
- if( groupname == "*" )
+ case "add":
+ #region Add group
{
- int count = 0;
- foreach( Group g in TShock.Groups )
+ if (args.Parameters.Count < 2)
{
- response = TShock.Groups.AddPermissions(g.Name, args.Parameters);
- if (!response.StartsWith("Error:"))
- count++;
- }
- args.Player.SendSuccessMessage(String.Format("{0} groups were modified.", count ));
- return;
- }
- response = TShock.Groups.AddPermissions(groupname, args.Parameters);
- if (response.Length > 0)
- args.Player.SendSuccessMessage(response);
- return;
- }
-
- if (com.Equals("del") || com.Equals("delete"))
- {
- if (groupname == "*")
- {
- int count = 0;
- foreach (Group g in TShock.Groups)
- {
- response = TShock.Groups.DeletePermissions(g.Name, args.Parameters);
- if (!response.StartsWith("Error:"))
- count++;
- }
- args.Player.SendSuccessMessage(String.Format("{0} groups were modified.", count));
- return;
- }
- response = TShock.Groups.DeletePermissions(groupname, args.Parameters);
- if (response.Length > 0)
- args.Player.SendSuccessMessage(response);
- return;
- }
- }
- args.Player.SendErrorMessage("Incorrect format: /modgroup add|del ");
- }
-
- private static void ViewGroups(CommandArgs args)
- {
- if (args.Parameters.Count > 0)
- {
- String com = args.Parameters[0];
-
- if( com == "list" )
- {
- string ret = "Groups: ";
- foreach( Group g in TShock.Groups.groups )
- {
- if (ret.Length > 50)
- {
- args.Player.SendSuccessMessage(ret);
- ret = "";
- }
-
- if( ret != "" )
- {
- ret += ", ";
- }
-
- ret += g.Name;
- }
-
- if (ret.Length > 0)
- {
- args.Player.SendSuccessMessage(ret);
- }
- return;
- }
- else if( com == "perm")
- {
- if (args.Parameters.Count > 1)
- {
- String groupname = args.Parameters[1];
-
- if( TShock.Groups.GroupExists( groupname ) )
- {
- string ret = String.Format("Permissions for {0}: ", groupname);
- foreach (string p in TShock.Utils.GetGroup( groupname ).permissions)
- {
- if (ret.Length > 50)
- {
- args.Player.SendSuccessMessage(ret);
- ret = "";
- }
-
- if (ret != "")
- {
- ret += ", ";
- }
-
- ret += p;
- }
- if (ret.Length > 0)
- {
- args.Player.SendSuccessMessage(ret);
- }
-
+ args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /group add [permissions]");
return;
}
+
+ string groupName = args.Parameters[1];
+ args.Parameters.RemoveRange(0, 2);
+ string permissions = String.Join(",", args.Parameters);
+
+ try
+ {
+ string response = TShock.Groups.AddGroup(groupName, permissions);
+ if (response.Length > 0)
+ {
+ args.Player.SendSuccessMessage(response);
+ }
+ }
+ catch (GroupManagerException ex)
+ {
+ args.Player.SendErrorMessage(ex.ToString());
+ }
+ }
+ #endregion
+ return;
+ case "addperm":
+ #region Add permissions
+ {
+ if (args.Parameters.Count < 3)
+ {
+ args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /group addperm ");
+ return;
+ }
+
+ string groupName = args.Parameters[1];
+ args.Parameters.RemoveRange(0, 2);
+ if (groupName == "*")
+ {
+ foreach (Group g in TShock.Groups)
+ {
+ TShock.Groups.AddPermissions(g.Name, args.Parameters);
+ }
+ args.Player.SendSuccessMessage("Modified all groups.");
+ return;
+ }
+ try
+ {
+ string response = TShock.Groups.AddPermissions(groupName, args.Parameters);
+ if (response.Length > 0)
+ {
+ args.Player.SendSuccessMessage(response);
+ }
+ return;
+ }
+ catch (GroupManagerException ex)
+ {
+ args.Player.SendErrorMessage(ex.ToString());
+ }
+ }
+ #endregion
+ return;
+
+ case "parent":
+ #region Parent
+ {
+ if (args.Parameters.Count < 2)
+ {
+ 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
{
- args.Player.SendErrorMessage("Group does not exist.");
- return;
+ 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);
}
}
- }
- }
- args.Player.SendErrorMessage("Incorrect format: /group list");
- args.Player.SendErrorMessage(" /group perm ");
- }
+ #endregion
+ return;
+ case "del":
+ #region Delete group
+ {
+ if (args.Parameters.Count != 2)
+ {
+ args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /group del ");
+ return;
+ }
+ try
+ {
+ string response = TShock.Groups.DeleteGroup(args.Parameters[1]);
+ if (response.Length > 0)
+ {
+ args.Player.SendSuccessMessage(response);
+ }
+ }
+ catch (GroupManagerException ex)
+ {
+ args.Player.SendErrorMessage(ex.ToString());
+ }
+ }
+ #endregion
+ return;
+ case "delperm":
+ #region Delete permissions
+ {
+ if (args.Parameters.Count < 3)
+ {
+ args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /group delperm ");
+ return;
+ }
+
+ string groupName = args.Parameters[1];
+ args.Parameters.RemoveRange(0, 2);
+ if (groupName == "*")
+ {
+ foreach (Group g in TShock.Groups)
+ {
+ TShock.Groups.DeletePermissions(g.Name, args.Parameters);
+ }
+ args.Player.SendSuccessMessage("Modified all groups.");
+ return;
+ }
+ try
+ {
+ string response = TShock.Groups.DeletePermissions(groupName, args.Parameters);
+ if (response.Length > 0)
+ {
+ args.Player.SendSuccessMessage(response);
+ }
+ return;
+ }
+ catch (GroupManagerException ex)
+ {
+ args.Player.SendErrorMessage(ex.ToString());
+ }
+ }
+ #endregion
+ return;
+ case "list":
+ #region List groups
+ {
+ int pageNumber;
+ if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
+ return;
+ IEnumerable groupNames = from grp in TShock.Groups.groups
+ select grp.Name;
+ PaginationTools.SendPage(args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(groupNames),
+ new PaginationTools.Settings
+ {
+ HeaderFormat = "Groups ({0}/{1}):",
+ FooterFormat = "Type /group list {0} for more."
+ });
+ }
+ #endregion
+ return;
+ case "listperm":
+ #region List permissions
+ {
+ if (args.Parameters.Count == 1)
+ {
+ args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /group listperm [page]");
+ return;
+ }
+ int pageNumber;
+ if (!PaginationTools.TryParsePageNumber(args.Parameters, 2, args.Player, out pageNumber))
+ return;
+
+ if (!TShock.Groups.GroupExists(args.Parameters[1]))
+ {
+ args.Player.SendErrorMessage("Invalid group.");
+ return;
+ }
+ Group grp = TShock.Utils.GetGroup(args.Parameters[1]);
+ List permissions = grp.TotalPermissions;
+
+ PaginationTools.SendPage(args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(permissions),
+ new PaginationTools.Settings
+ {
+ HeaderFormat = "Permissions for " + grp.Name + " ({0}/{1}):",
+ FooterFormat = "Type /group permlist " + grp.Name + " {0} for more.",
+ NothingToDisplayString = "There are currently no permissions for " + grp.Name + "."
+ });
+ }
+ #endregion
+ return;
+ case "help":
+ args.Player.SendInfoMessage("Syntax: /group [arguments]");
+ args.Player.SendInfoMessage("Commands: add, addperm, parent, del, delperm, list, listperm");
+ args.Player.SendInfoMessage("Arguments: add , addperm , del ");
+ args.Player.SendInfoMessage("Arguments: delperm , list [page], listperm [page]");
+ return;
+ }
+ }
#endregion Group Management
#region Item Management
- private static void AddItem(CommandArgs args)
+ private static void ItemBanDeprecated(CommandArgs args)
{
- if (args.Parameters.Count == 1)
- {
- var items = TShock.Utils.GetItemByIdOrName(args.Parameters[0]);
- if (items.Count == 0)
- {
- args.Player.SendErrorMessage("Invalid item type!");
- }
- else if (items.Count > 1)
- {
- args.Player.SendErrorMessage(string.Format("More than one ({0}) item matched!", items.Count));
- }
- else
- {
- var item = items[0];
- if (item.type >= 1)
- {
- TShock.Itembans.AddNewBan(item.name);
- args.Player.SendErrorMessage(item.name + " has been banned.");
- }
- else
- {
- args.Player.SendErrorMessage("Invalid item type!");
- }
- }
- }
- else
- {
- args.Player.SendErrorMessage("Invalid use: /additem \"item name\" or /additem ##.");
- }
+ args.Player.SendInfoMessage("The item ban commands were merged into /itemban in TShock 4.1; check /itemban help.");
}
- private static void DeleteItem(CommandArgs args)
+ private static void ItemBan(CommandArgs args)
{
- if (args.Parameters.Count == 1)
+ if (args.Parameters.Count == 0)
{
- var items = TShock.Utils.GetItemByIdOrName(args.Parameters[0]);
- if (items.Count == 0)
- {
- args.Player.SendErrorMessage("Invalid item type!");
- }
- else if (items.Count > 1)
- {
- args.Player.SendErrorMessage(string.Format("More than one ({0}) item matched!", items.Count));
- }
- else
- {
- var item = items[0];
- if (item.type >= 1)
- {
- TShock.Itembans.RemoveBan(item.name);
- args.Player.SendSuccessMessage(item.name + " has been unbanned.");
- }
- else
- {
- args.Player.SendErrorMessage("Invalid item type!");
- }
- }
+ args.Player.SendInfoMessage("Invalid syntax! Proper syntax: /itemban [arguments]");
+ args.Player.SendInfoMessage("Commands: add, allow, del, disallow, list");
+ args.Player.SendInfoMessage("Arguments: add , allow ");
+ args.Player.SendInfoMessage("Arguments: del , disallow , list [page]");
+ return;
}
- else
+
+ switch (args.Parameters[0].ToLower())
{
- args.Player.SendErrorMessage("Invalid use: /delitem \"item name\" or /delitem ##");
- }
- }
-
- private static void ListItems(CommandArgs args)
- {
- args.Player.SendInfoMessage("The banned items are: " + String.Join(",", TShock.Itembans.ItemBans) + ".");
- }
-
- private static void AddItemGroup(CommandArgs args)
- {
- if (args.Parameters.Count == 2)
- {
- var items = TShock.Utils.GetItemByIdOrName(args.Parameters[0]);
- if (items.Count == 0)
- {
- args.Player.SendErrorMessage("Invalid item type!");
- }
- else if (items.Count > 1)
- {
- args.Player.SendErrorMessage(string.Format("More than one ({0}) item matched!", items.Count));
- }
- else
- {
- var item = items[0];
- if (item.type >= 1)
+ case "add":
+ #region Add item
{
- if(TShock.Groups.GroupExists(args.Parameters[1]))
+ if (args.Parameters.Count != 2)
{
- ItemBan ban = TShock.Itembans.GetItemBanByName(item.name);
-
- if(!ban.AllowedGroups.Contains(args.Parameters[1]))
- {
- TShock.Itembans.AllowGroup(item.name, args.Parameters[1]);
- args.Player.SendSuccessMessage("Banned item " + item.name + " has been allowed for group " + args.Parameters[1] + ".");
- }
- else
- {
- args.Player.SendWarningMessage("Banned item " + item.name + " is already allowed for group " + args.Parameters[1] + "!");
- }
+ args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /itemban add ");
+ return;
+ }
+
+ List items = TShock.Utils.GetItemByIdOrName(args.Parameters[1]);
+ if (items.Count == 0)
+ {
+ args.Player.SendErrorMessage("Invalid item.");
+ }
+ else if (items.Count > 1)
+ {
+ args.Player.SendErrorMessage(string.Format("More than one ({0}) item matched.", items.Count));
}
else
{
- args.Player.SendErrorMessage("Group " + args.Parameters[1] + " not found!");
+ TShock.Itembans.AddNewBan(items[0].name);
+ args.Player.SendSuccessMessage("Banned " + items[0].name + ".");
}
}
- else
+ #endregion
+ return;
+ case "allow":
+ #region Allow group to item
{
- args.Player.SendErrorMessage("Invalid item type!");
- }
- }
- }
- else
- {
- args.Player.SendErrorMessage("Invalid use: /additemgroup \"item name\" \"group name\"");
- }
- }
-
- private static void DeleteItemGroup(CommandArgs args)
- {
- if (args.Parameters.Count == 2)
- {
- var items = TShock.Utils.GetItemByIdOrName(args.Parameters[0]);
- if (items.Count == 0)
- {
- args.Player.SendErrorMessage("Invalid item type!");
- }
- else if (items.Count > 1)
- {
- args.Player.SendErrorMessage(string.Format("More than one ({0}) item matched!", items.Count));
- }
- else
- {
- var item = items[0];
- if (item.type >= 1)
- {
- if(TShock.Groups.GroupExists(args.Parameters[1]))
+ if (args.Parameters.Count != 3)
{
- ItemBan ban = TShock.Itembans.GetItemBanByName(item.name);
-
- if(ban.AllowedGroups.Contains(args.Parameters[1]))
- {
- TShock.Itembans.RemoveGroup(item.name, args.Parameters[1]);
- args.Player.SendSuccessMessage("Removed access for group " + args.Parameters[1] + " to banned item " + item.name + ".");
- }
- else
- {
- args.Player.SendWarningMessage("Group " + args.Parameters[1] + " did not have access to banned item " + item.name + "!");
- }
+ args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /itemban allow ");
+ return;
+ }
+
+ List items = TShock.Utils.GetItemByIdOrName(args.Parameters[1]);
+ if (items.Count == 0)
+ {
+ args.Player.SendErrorMessage("Invalid item.");
+ }
+ else if (items.Count > 1)
+ {
+ args.Player.SendErrorMessage(string.Format("More than one ({0}) item matched.", items.Count));
}
else
{
- args.Player.SendErrorMessage("Group " + args.Parameters[1] + " not found!");
+ if (!TShock.Groups.GroupExists(args.Parameters[2]))
+ {
+ args.Player.SendErrorMessage("Invalid group.");
+ return;
+ }
+
+ ItemBan ban = TShock.Itembans.GetItemBanByName(items[0].name);
+ if (ban == null)
+ {
+ args.Player.SendErrorMessage(items[0].name + " is not banned.");
+ return;
+ }
+ if (!ban.AllowedGroups.Contains(args.Parameters[2]))
+ {
+ TShock.Itembans.AllowGroup(items[0].name, args.Parameters[2]);
+ args.Player.SendSuccessMessage(String.Format("{0} has been allowed to use {1}.", args.Parameters[2], items[0].name));
+ }
+ else
+ {
+ args.Player.SendWarningMessage(String.Format("{0} is already allowed to use {1}.", args.Parameters[2], items[0].name));
+ }
}
}
- else
+ #endregion
+ return;
+ case "del":
+ #region Delete item
{
- args.Player.SendErrorMessage("Invalid item type!");
+ if (args.Parameters.Count != 2)
+ {
+ args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /itemban del ");
+ return;
+ }
+
+ List items = TShock.Utils.GetItemByIdOrName(args.Parameters[1]);
+ if (items.Count == 0)
+ {
+ args.Player.SendErrorMessage("Invalid item.");
+ }
+ else if (items.Count > 1)
+ {
+ args.Player.SendErrorMessage(string.Format("More than one ({0}) item matched.", items.Count));
+ }
+ else
+ {
+ TShock.Itembans.RemoveBan(items[0].name);
+ args.Player.SendSuccessMessage("Unbanned " + items[0].name + ".");
+ }
}
- }
- }
- else
- {
- args.Player.SendErrorMessage("Invalid use: /delitemgroup \"item name\" \"group name\"");
+ #endregion
+ return;
+ case "disallow":
+ #region Allow group to item
+ {
+ if (args.Parameters.Count != 3)
+ {
+ args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /itemban disallow ");
+ return;
+ }
+
+ List items = TShock.Utils.GetItemByIdOrName(args.Parameters[1]);
+ if (items.Count == 0)
+ {
+ args.Player.SendErrorMessage("Invalid item.");
+ }
+ else if (items.Count > 1)
+ {
+ args.Player.SendErrorMessage(string.Format("More than one ({0}) item matched.", items.Count));
+ }
+ else
+ {
+ if (!TShock.Groups.GroupExists(args.Parameters[2]))
+ {
+ args.Player.SendErrorMessage("Invalid group.");
+ return;
+ }
+
+ ItemBan ban = TShock.Itembans.GetItemBanByName(items[0].name);
+ if (ban == null)
+ {
+ args.Player.SendErrorMessage(items[0].name + " is not banned.");
+ return;
+ }
+ if (ban.AllowedGroups.Contains(args.Parameters[2]))
+ {
+ TShock.Itembans.RemoveGroup(items[0].name, args.Parameters[2]);
+ args.Player.SendSuccessMessage(String.Format("{0} has been disallowed to use {1}.", args.Parameters[2], items[0].name));
+ }
+ else
+ {
+ args.Player.SendWarningMessage(String.Format("{0} is already disallowed to use {1}.", args.Parameters[2], items[0].name));
+ }
+ }
+ }
+ #endregion
+ return;
+ case "help":
+ args.Player.SendInfoMessage("Syntax: /itemban [arguments]");
+ args.Player.SendInfoMessage("Commands: add, allow, del, disallow, list");
+ args.Player.SendInfoMessage("Arguments: add , allow ");
+ args.Player.SendInfoMessage("Arguments: del , disallow , list [page]");
+ return;
+ case "list":
+ #region List items
+ int pageNumber;
+ if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
+ return;
+ IEnumerable itemNames = from itemBan in TShock.Itembans.ItemBans
+ select itemBan.Name;
+ PaginationTools.SendPage(args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(itemNames),
+ new PaginationTools.Settings
+ {
+ HeaderFormat = "Item bans ({0}/{1}):",
+ FooterFormat = "Type /itemban list {0} for more.",
+ NothingToDisplayString = "There are currently no banned items."
+ });
+ #endregion
+ return;
}
}
-
#endregion Item Management
#region Server Config Commands
@@ -2238,11 +2370,8 @@ namespace TShockAPI
private static void Reload(CommandArgs args)
{
- FileTools.SetupConfig();
- TShock.HandleCommandLinePostConfigLoad(Environment.GetCommandLineArgs());
- TShock.Groups.LoadPermisions();
- //todo: Create an event for reloads to propegate to plugins.
- TShock.Regions.ReloadAllRegions();
+ TShock.Utils.Reload(args.Player);
+
args.Player.SendSuccessMessage(
"Configuration, permissions, and regions reload complete. Some changes may require a server restart.");
}
@@ -2467,6 +2596,7 @@ namespace TShockAPI
{
args.Player.SendMessage("Hit a block to get the name of the region", Color.Yellow);
args.Player.AwaitingName = true;
+ args.Player.AwaitingNameParameters = args.Parameters.Skip(1).ToArray();
}
break;
}
@@ -2482,7 +2612,7 @@ namespace TShockAPI
}
else
{
- args.Player.SendMessage("Invalid syntax! Proper syntax: /region set [1/2]", Color.Red);
+ args.Player.SendMessage("Invalid syntax! Proper syntax: /region set <1/2>", Color.Red);
}
break;
}
@@ -2516,7 +2646,7 @@ namespace TShockAPI
}
}
else
- args.Player.SendMessage("Invalid syntax! Proper syntax: /region define [name]", Color.Red);
+ args.Player.SendMessage("Invalid syntax! Proper syntax: /region define ", Color.Red);
break;
}
case "protect":
@@ -2539,10 +2669,10 @@ namespace TShockAPI
args.Player.SendMessage("Could not find specified region", Color.Red);
}
else
- args.Player.SendMessage("Invalid syntax! Proper syntax: /region protect [name] [true/false]", Color.Red);
+ args.Player.SendMessage("Invalid syntax! Proper syntax: /region protect ", Color.Red);
}
else
- args.Player.SendMessage("Invalid syntax! Proper syntax: /region protect [name] [true/false]", Color.Red);
+ args.Player.SendMessage("Invalid syntax! Proper syntax: /region protect ", Color.Red);
break;
}
case "delete":
@@ -2556,7 +2686,7 @@ namespace TShockAPI
args.Player.SendMessage("Could not find specified region", Color.Red);
}
else
- args.Player.SendMessage("Invalid syntax! Proper syntax: /region delete [name]", Color.Red);
+ args.Player.SendMessage("Invalid syntax! Proper syntax: /region delete ", Color.Red);
break;
}
case "clear":
@@ -2600,7 +2730,7 @@ namespace TShockAPI
}
}
else
- args.Player.SendMessage("Invalid syntax! Proper syntax: /region allow [name] [region]", Color.Red);
+ args.Player.SendMessage("Invalid syntax! Proper syntax: /region allow ", Color.Red);
break;
}
case "remove":
@@ -2635,7 +2765,7 @@ namespace TShockAPI
}
}
else
- args.Player.SendMessage("Invalid syntax! Proper syntax: /region remove [name] [region]", Color.Red);
+ args.Player.SendMessage("Invalid syntax! Proper syntax: /region remove ", Color.Red);
break;
case "allowg":
{
@@ -2670,7 +2800,7 @@ namespace TShockAPI
}
}
else
- args.Player.SendMessage("Invalid syntax! Proper syntax: /region allow [group] [region]", Color.Red);
+ args.Player.SendMessage("Invalid syntax! Proper syntax: /region allowg ", Color.Red);
break;
}
case "removeg":
@@ -2705,93 +2835,132 @@ namespace TShockAPI
}
}
else
- args.Player.SendMessage("Invalid syntax! Proper syntax: /region removeg [group] [region]", Color.Red);
+ args.Player.SendMessage("Invalid syntax! Proper syntax: /region removeg ", Color.Red);
break;
- case "list":
- {
- //How many regions per page
- const int pagelimit = 15;
- //How many regions per line
- const int perline = 5;
- //Pages start at 0 but are displayed and parsed at 1
- int page = 0;
+ case "list":
+ {
+ int pageNumber;
+ if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
+ return;
-
- if (args.Parameters.Count > 1)
- {
- if (!int.TryParse(args.Parameters[1], out page) || page < 1)
- {
- args.Player.SendMessage(string.Format("Invalid page number ({0})", page), Color.Red);
- return;
- }
- page--; //Substract 1 as pages are parsed starting at 1 and not 0
- }
-
- var regions = TShock.Regions.ListAllRegions(Main.worldID.ToString());
-
- // Are there even any regions to display?
- if (regions.Count == 0)
- {
- args.Player.SendMessage("There are currently no regions defined.", Color.Red);
- return;
- }
-
- //Check if they are trying to access a page that doesn't exist.
- int pagecount = regions.Count / pagelimit;
- if (page > pagecount)
- {
- args.Player.SendMessage(string.Format("Page number exceeds pages ({0}/{1})", page + 1, pagecount + 1), Color.Red);
- return;
- }
-
- //Display the current page and the number of pages.
- args.Player.SendMessage(string.Format("Current Regions ({0}/{1}):", page + 1, pagecount + 1), Color.Green);
-
- //Add up to pagelimit names to a list
- var nameslist = new List();
- for (int i = (page * pagelimit); (i < ((page * pagelimit) + pagelimit)) && i < regions.Count; i++)
- {
- nameslist.Add(regions[i].Name);
- }
-
- //convert the list to an array for joining
- var names = nameslist.ToArray();
- for (int i = 0; i < names.Length; i += perline)
- {
- args.Player.SendMessage(string.Join(", ", names, i, Math.Min(names.Length - i, perline)), Color.Yellow);
- }
-
- if (page < pagecount)
- {
- args.Player.SendMessage(string.Format("Type /region list {0} for more regions.", (page + 2)), Color.Yellow);
- }
-
- break;
- }
+ IEnumerable regionNames = from region in TShock.Regions.Regions
+ where region.WorldID == Main.worldID.ToString()
+ select region.Name;
+ PaginationTools.SendPage(args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(regionNames),
+ new PaginationTools.Settings
+ {
+ HeaderFormat = "Regions ({0}/{1}):",
+ FooterFormat = "Type /region list {0} for more.",
+ NothingToDisplayString = "There are currently no regions defined."
+ });
+ break;
+ }
case "info":
{
- if (args.Parameters.Count > 1)
+ if (args.Parameters.Count == 1 || args.Parameters.Count > 4)
{
- string regionName = String.Join(" ", args.Parameters.GetRange(1, args.Parameters.Count - 1));
- Region r = TShock.Regions.GetRegionByName(regionName);
+ args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /region info [-d] [page]");
+ break;
+ }
- if (r == null)
- {
- args.Player.SendMessage("Region {0} does not exist");
- break;
- }
+ string regionName = args.Parameters[1];
+ bool displayBoundaries = args.Parameters.Skip(2).Any(
+ p => p.Equals("-d", StringComparison.InvariantCultureIgnoreCase)
+ );
- args.Player.SendMessage(r.Name + ": P: " + r.DisableBuild + " X: " + r.Area.X + " Y: " + r.Area.Y + " W: " +
- r.Area.Width + " H: " + r.Area.Height);
- foreach (int s in r.AllowedIDs)
+ Region region = TShock.Regions.GetRegionByName(regionName);
+ if (region == null)
+ {
+ args.Player.SendErrorMessage("Region \"{0}\" does not exist.", regionName);
+ break;
+ }
+
+ int pageNumberIndex = displayBoundaries ? 3 : 2;
+ int pageNumber;
+ if (!PaginationTools.TryParsePageNumber(args.Parameters, pageNumberIndex, args.Player, out pageNumber))
+ break;
+
+ List lines = new List
+ {
+ string.Format("X: {0}; Y: {1}; W: {2}; H: {3}, Z: {4}", region.Area.X, region.Area.Y, region.Area.Width, region.Area.Height, region.Z),
+ string.Concat("Owner: ", region.Owner),
+ string.Concat("Protected: ", region.DisableBuild.ToString()),
+ };
+
+ if (region.AllowedIDs.Count > 0)
+ {
+ IEnumerable sharedUsersSelector = region.AllowedIDs.Select(userId =>
{
- var user = TShock.Users.GetUserByID(s);
- args.Player.SendMessage(r.Name + ": " + (user != null ? user.Name : "Unknown"));
- }
+ User user = TShock.Users.GetUserByID(userId);
+ if (user != null)
+ return user.Name;
+ else
+ return string.Concat("{ID: ", userId, "}");
+ });
+ List extraLines = PaginationTools.BuildLinesFromTerms(sharedUsersSelector.Distinct());
+ extraLines[0] = "Shared with: " + extraLines[0];
+ lines.AddRange(extraLines);
}
else
{
- args.Player.SendMessage("Invalid syntax! Proper syntax: /region info [name]", Color.Red);
+ lines.Add("Region is not shared with any users.");
+ }
+
+ if (region.AllowedGroups.Count > 0)
+ {
+ List extraLines = PaginationTools.BuildLinesFromTerms(region.AllowedGroups.Distinct());
+ extraLines[0] = "Shared with groups: " + extraLines[0];
+ lines.AddRange(extraLines);
+ }
+ else
+ {
+ lines.Add("Region is not shared with any groups.");
+ }
+
+ PaginationTools.SendPage(
+ args.Player, pageNumber, lines, new PaginationTools.Settings
+ {
+ HeaderFormat = string.Format("Information About Region \"{0}\" ({{0}}/{{1}}):", region.Name),
+ FooterFormat = string.Format("Type /region info {0} {{0}} for more information.", regionName)
+ }
+ );
+
+ if (displayBoundaries)
+ {
+ Rectangle regionArea = region.Area;
+ foreach (Point boundaryPoint in Utils.Instance.EnumerateRegionBoundaries(regionArea))
+ {
+ // Preferring dotted lines as those should easily be distinguishable from actual wires.
+ if ((boundaryPoint.X + boundaryPoint.Y & 1) == 0)
+ {
+ // Could be improved by sending raw tile data to the client instead but not really
+ // worth the effort as chances are very low that overwriting the wire for a few
+ // nanoseconds will cause much trouble.
+ Tile tile = Main.tile[boundaryPoint.X, boundaryPoint.Y];
+ bool oldWireState = tile.wire;
+ tile.wire = true;
+
+ try {
+ args.Player.SendTileSquare(boundaryPoint.X, boundaryPoint.Y, 1);
+ } finally {
+ tile.wire = oldWireState;
+ }
+ }
+ }
+
+ Timer boundaryHideTimer = null;
+ boundaryHideTimer = new Timer((state) => {
+ foreach (Point boundaryPoint in Utils.Instance.EnumerateRegionBoundaries(regionArea))
+ if ((boundaryPoint.X + boundaryPoint.Y & 1) == 0)
+ args.Player.SendTileSquare(boundaryPoint.X, boundaryPoint.Y, 1);
+
+ // ReSharper disable AccessToModifiedClosure
+ Debug.Assert(boundaryHideTimer != null);
+ boundaryHideTimer.Dispose();
+ // ReSharper restore AccessToModifiedClosure
+ },
+ null, 5000, Timeout.Infinite
+ );
}
break;
@@ -2810,10 +2979,10 @@ namespace TShockAPI
args.Player.SendMessage("Could not find specified region", Color.Red);
}
else
- args.Player.SendMessage("Invalid syntax! Proper syntax: /region z [name] [#]", Color.Red);
+ args.Player.SendMessage("Invalid syntax! Proper syntax: /region z <#>", Color.Red);
}
else
- args.Player.SendMessage("Invalid syntax! Proper syntax: /region z [name] [#]", Color.Red);
+ args.Player.SendMessage("Invalid syntax! Proper syntax: /region z <#>", Color.Red);
break;
}
case "resize":
@@ -2863,27 +3032,79 @@ namespace TShockAPI
}
else
{
- args.Player.SendMessage("Invalid syntax! Proper syntax: /region resize [regionname] [u/d/l/r] [amount]",
+ args.Player.SendMessage("Invalid syntax! Proper syntax: /region resize ",
Color.Red);
}
}
else
{
- args.Player.SendMessage("Invalid syntax! Proper syntax: /region resize [regionname] [u/d/l/r] [amount]1",
+ args.Player.SendMessage("Invalid syntax! Proper syntax: /region resize ",
Color.Red);
}
break;
}
+ case "tp":
+ {
+ if (!args.Player.Group.HasPermission(Permissions.tp))
+ {
+ args.Player.SendErrorMessage("You don't have the necessary permission to do that.");
+ break;
+ }
+ if (args.Parameters.Count <= 1)
+ {
+ args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /region tp .");
+ break;
+ }
+
+ string regionName = string.Join(" ", args.Parameters.Skip(1));
+ Region region = TShock.Regions.GetRegionByName(regionName);
+ if (region == null)
+ {
+ args.Player.SendErrorMessage("Region \"{0}\" does not exist.", regionName);
+ break;
+ }
+
+ args.Player.Teleport(region.Area.Center.X, region.Area.Center.Y + 3);
+
+ break;
+ }
case "help":
default:
{
- args.Player.SendMessage("Avialable region commands:", Color.Green);
- args.Player.SendMessage("/region set [1/2] /region define [name] /region protect [name] [true/false]",
- Color.Yellow);
- args.Player.SendMessage("/region name (provides region name)", Color.Yellow);
- args.Player.SendMessage("/region delete [name] /region clear (temporary region)", Color.Yellow);
- args.Player.SendMessage("/region allow [name] [regionname]", Color.Yellow);
- args.Player.SendMessage("/region resize [regionname] [u/d/l/r] [amount]", Color.Yellow);
+ int pageNumber;
+ int pageParamIndex = 0;
+ if (args.Parameters.Count > 1)
+ pageParamIndex = 1;
+ if (!PaginationTools.TryParsePageNumber(args.Parameters, pageParamIndex, args.Player, out pageNumber))
+ return;
+
+ List lines = new List {
+ "set <1/2> - Sets the temporary region points.",
+ "clear - Clears the temporary region points.",
+ "define - Defines the region with the given name.",
+ "delete - Deletes the given region.",
+ "name [-u][-z][-p] - Shows the name of the region at the given point.",
+ "list - Lists all regions.",
+ "resize - Resizes a region.",
+ "allow - Allows a user to a region.",
+ "remove - Removes a user from a region.",
+ "allowg - Allows a user group to a region.",
+ "removeg - Removes a user group from a region.",
+ "info [-d] - Displays several information about the given region.",
+ "protect - Sets whether the tiles inside the region are protected or not.",
+ "z <#> - Sets the z-order of the region.",
+ };
+ if (args.Player.Group.HasPermission(Permissions.tp))
+ lines.Add("tp - Teleports you to the given region's center.");
+
+ PaginationTools.SendPage(
+ args.Player, pageNumber, lines,
+ new PaginationTools.Settings
+ {
+ HeaderFormat = "Available Region Sub-Commands ({0}/{1}):",
+ FooterFormat = "Type /region {0} for more sub-commands."
+ }
+ );
break;
}
}
@@ -2911,42 +3132,18 @@ namespace TShockAPI
private static void Help(CommandArgs args)
{
- args.Player.SendInfoMessage("TShock Commands:");
- int page = 1;
- if (args.Parameters.Count > 0)
- int.TryParse(args.Parameters[0], out page);
- var cmdlist = new List();
- for (int j = 0; j < ChatCommands.Count; j++)
- {
- if (ChatCommands[j].CanRun(args.Player))
+ int pageNumber;
+ if (!PaginationTools.TryParsePageNumber(args.Parameters, 0, args.Player, out pageNumber))
+ return;
+ IEnumerable cmdNames = from cmd in ChatCommands
+ where cmd.CanRun(args.Player) && (cmd.Name != "auth" || TShock.AuthToken != 0)
+ select "/" + cmd.Name;
+ PaginationTools.SendPage(args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(cmdNames),
+ new PaginationTools.Settings
{
- cmdlist.Add(ChatCommands[j]);
- }
- }
- var sb = new StringBuilder();
- if (cmdlist.Count > (15*(page - 1)))
- {
- for (int j = (15*(page - 1)); j < (15*page); j++)
- {
- if (sb.Length != 0)
- sb.Append(", ");
- sb.Append("/").Append(cmdlist[j].Name);
- if (j == cmdlist.Count - 1)
- {
- args.Player.SendInfoMessage(sb.ToString());
- break;
- }
- if ((j + 1)%5 == 0)
- {
- args.Player.SendInfoMessage(sb.ToString());
- sb.Clear();
- }
- }
- }
- if (cmdlist.Count > (15*page))
- {
- args.Player.SendInfoMessage(string.Format("Type /help {0} for more commands.", (page + 1)));
- }
+ HeaderFormat = "Commands ({0}/{1}):",
+ FooterFormat = "Type /help {0} for more."
+ });
}
private static void GetVersion(CommandArgs args)
@@ -2957,59 +3154,47 @@ namespace TShockAPI
private static void ListConnectedPlayers(CommandArgs args)
{
- //How many players per page
- const int pagelimit = 15;
- //How many players per line
- const int perline = 5;
- //Pages start at 0 but are displayed and parsed at 1
- int page = 0;
+ bool invalidUsage = (args.Parameters.Count > 2);
-
- if (args.Parameters.Count > 0)
+ bool displayIdsRequested = false;
+ int pageNumber = 1;
+ if (!invalidUsage)
{
- if (!int.TryParse(args.Parameters[0], out page) || page < 1)
+ foreach (string parameter in args.Parameters)
{
- args.Player.SendErrorMessage(string.Format("Invalid page number ({0})", page));
- return;
+ if (parameter.Equals("-i", StringComparison.InvariantCultureIgnoreCase))
+ {
+ displayIdsRequested = true;
+ continue;
+ }
+
+ if (!int.TryParse(parameter, out pageNumber))
+ {
+ invalidUsage = true;
+ break;
+ }
}
- page--; //Substract 1 as pages are parsed starting at 1 and not 0
}
-
- var playerList = args.Player.Group.HasPermission(Permissions.seeids)
- ? TShock.Utils.GetPlayers(true)
- : TShock.Utils.GetPlayers(false);
-
- //Check if they are trying to access a page that doesn't exist.
- int pagecount = playerList.Count / pagelimit;
- if (page > pagecount)
+ if (invalidUsage)
{
- args.Player.SendErrorMessage(string.Format("Page number exceeds pages ({0}/{1})", page + 1, pagecount + 1));
+ args.Player.SendErrorMessage("Invalid usage, proper usage: /who [-i] [pagenumber]");
+ return;
+ }
+ if (displayIdsRequested && !args.Player.Group.HasPermission(Permissions.seeids))
+ {
+ args.Player.SendErrorMessage("You don't have the required permission to list player ids.");
return;
}
- //Display the current page and the number of pages.
- args.Player.SendSuccessMessage(string.Format("Players: {0}/{1}",
- TShock.Utils.ActivePlayers(), TShock.Config.MaxSlots));
- args.Player.SendSuccessMessage(string.Format("Current players page {0}/{1}:", page + 1, pagecount + 1));
-
- //Add up to pagelimit names to a list
- var nameslist = new List();
- for (int i = (page * pagelimit); (i < ((page * pagelimit) + pagelimit)) && i < playerList.Count; i++)
- {
- nameslist.Add(playerList[i]);
- }
-
- //convert the list to an array for joining
- var names = nameslist.ToArray();
- for (int i = 0; i < names.Length; i += perline)
- {
- args.Player.SendInfoMessage(string.Join(", ", names, i, Math.Min(names.Length - i, perline)));
- }
-
- if (page < pagecount)
- {
- args.Player.SendInfoMessage(string.Format("Type /who {0} for more players.", (page + 2)));
- }
+ args.Player.SendSuccessMessage("Online Players ({0}/{1})", TShock.Utils.ActivePlayers(), TShock.Config.MaxSlots);
+ PaginationTools.SendPage(
+ args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(TShock.Utils.GetPlayers(displayIdsRequested)),
+ new PaginationTools.Settings
+ {
+ IncludeHeader = false,
+ FooterFormat = string.Format("Type /who {0}{{0}} for more.", displayIdsRequested ? "-i " : string.Empty)
+ }
+ );
}
private static void AuthToken(CommandArgs args)
@@ -3025,10 +3210,9 @@ namespace TShockAPI
{
try
{
- TShock.Users.AddUser(new User(args.Player.IP, "", "", "superadmin"));
args.Player.Group = TShock.Utils.GetGroup("superadmin");
- args.Player.SendInfoMessage("This IP address is now superadmin. Please perform the following command:");
- args.Player.SendInfoMessage("/user add : superadmin");
+ args.Player.SendInfoMessage("You are now superadmin, please do the following to finish your install:");
+ args.Player.SendInfoMessage("/user add superadmin");
args.Player.SendInfoMessage("Creates: with the password as part of the superadmin group.");
args.Player.SendInfoMessage("Please use /login to login from now on.");
args.Player.SendInfoMessage("If you understand, please /login now, and type /auth-verify.");
@@ -3044,9 +3228,7 @@ namespace TShockAPI
if (args.Player.Group.Name == "superadmin")
{
args.Player.SendInfoMessage("Please disable the auth system! If you need help, consult the forums. http://tshock.co/");
- args.Player.SendInfoMessage("This IP address is now superadmin. Please perform the following command:");
- args.Player.SendInfoMessage("/user add : superadmin");
- args.Player.SendInfoMessage("Creates: with the password as part of the superadmin group.");
+ args.Player.SendInfoMessage("This account is superadmin, please do the following to finish your install:");
args.Player.SendInfoMessage("Please use /login to login from now on.");
args.Player.SendInfoMessage("If you understand, please /login now, and type /auth-verify.");
return;
@@ -3065,15 +3247,6 @@ namespace TShockAPI
return;
}
- if (!args.Player.IsLoggedIn)
- {
- args.Player.SendWarningMessage("You must be logged in to disable the auth system.");
- args.Player.SendWarningMessage("This is a security measure designed to prevent insecure administration setups.");
- args.Player.SendWarningMessage("Please re-run /auth and read the instructions!");
- args.Player.SendWarningMessage("If you're still confused, consult the forums: http://tshock.co/");
- return;
- }
-
args.Player.SendSuccessMessage("Your new account has been verified, and the /auth system has been turned off.");
args.Player.SendSuccessMessage("You can always use the /user command to manage players. Don't just delete the auth.lck.");
args.Player.SendSuccessMessage("Thank you for using TShock! http://tshock.co/ & http://github.com/TShock/TShock");
@@ -3231,6 +3404,41 @@ namespace TShockAPI
}
}
+ private static void Aliases(CommandArgs args)
+ {
+ if (args.Parameters.Count < 1)
+ {
+ 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)
+ args.Player.SendErrorMessage("No command or command alias matching \"{0}\" found.", givenCommandName);
+ }
+
#endregion General Commands
#region Cheat Commands
@@ -3293,59 +3501,87 @@ namespace TShockAPI
args.Player.SendErrorMessage("Invalid syntax! Proper syntax: /item [item amount] [prefix id/name]");
return;
}
- if (args.Parameters[0].Length == 0)
- {
- args.Player.SendErrorMessage("Missing an item name/id.");
- return;
- }
+
+ int amountParamIndex = -1;
int itemAmount = 0;
- int prefix = 0;
- if (args.Parameters.Count == 2)
- int.TryParse(args.Parameters[1], out itemAmount);
- else if (args.Parameters.Count == 3)
+ for (int i = 1; i < args.Parameters.Count; i++)
{
- int.TryParse(args.Parameters[1], out itemAmount);
- var found = TShock.Utils.GetPrefixByIdOrName(args.Parameters[2]);
- if (found.Count == 1)
- prefix = found[0];
+ if (int.TryParse(args.Parameters[i], out itemAmount))
+ {
+ amountParamIndex = i;
+ break;
+ }
}
- var items = TShock.Utils.GetItemByIdOrName(args.Parameters[0]);
- if (items.Count == 0)
+
+ string itemNameOrId;
+ if (amountParamIndex == -1)
+ itemNameOrId = string.Join(" ", args.Parameters);
+ else
+ itemNameOrId = string.Join(" ", args.Parameters.Take(amountParamIndex));
+
+ Item item;
+ List matchedItems = TShock.Utils.GetItemByIdOrName(itemNameOrId);
+ if (matchedItems.Count == 0)
{
args.Player.SendErrorMessage("Invalid item type!");
+ return;
}
- else if (items.Count > 1)
+ else if (matchedItems.Count > 1)
{
- args.Player.SendErrorMessage(string.Format("More than one ({0}) item matched!", items.Count));
+ args.Player.SendErrorMessage("More than one item matched:");
+ args.Player.SendErrorMessage(string.Join(", ", matchedItems.Select(i => i.name)));
+ return;
}
else
{
- var item = items[0];
- if (item.type >= 1 && item.type < Main.maxItemTypes)
+ item = matchedItems[0];
+ }
+ if (item.type < 1 && item.type >= Main.maxItemTypes)
+ {
+ args.Player.SendErrorMessage("The item type {0} is invalid.", itemNameOrId);
+ return;
+ }
+
+ int prefixId = 0;
+ if (amountParamIndex != -1 && args.Parameters.Count > amountParamIndex + 1)
+ {
+ string prefixidOrName = args.Parameters[amountParamIndex + 1];
+ List matchedPrefixIds = TShock.Utils.GetPrefixByIdOrName(prefixidOrName);
+ if (matchedPrefixIds.Count > 1)
{
- if (args.Player.InventorySlotAvailable || item.name.Contains("Coin"))
- {
- if (itemAmount == 0 || itemAmount > item.maxStack)
- itemAmount = item.maxStack;
- if (args.Player.GiveItemCheck(item.type, item.name, item.width, item.height, itemAmount, prefix))
- {
- args.Player.SendSuccessMessage(string.Format("Gave {0} {1}(s).", itemAmount, item.name));
- }
- else
- {
- args.Player.SendErrorMessage("The item is banned and the config prevents you from spawning banned items.");
- }
- }
- else
- {
- args.Player.SendErrorMessage("You don't have free slots!");
- }
+ args.Player.SendErrorMessage("More than one ({0}) prefixes matched \"{1}\".", matchedPrefixIds.Count, prefixidOrName);
+ return;
+ }
+ else if (matchedPrefixIds.Count == 0)
+ {
+ args.Player.SendErrorMessage("No prefix matched \"{0}\".", prefixidOrName);
+ return;
}
else
{
- args.Player.SendErrorMessage("Invalid item type!");
+ prefixId = matchedPrefixIds[0];
}
}
+
+ if (args.Player.InventorySlotAvailable || item.name.Contains("Coin"))
+ {
+ if (itemAmount == 0 || itemAmount > item.maxStack)
+ itemAmount = item.maxStack;
+
+ if (args.Player.GiveItemCheck(item.type, item.name, item.width, item.height, itemAmount, prefixId))
+ {
+ item.prefix = (byte)prefixId;
+ args.Player.SendSuccessMessage("Gave {0} {1}(s).", itemAmount, item.AffixName());
+ }
+ else
+ {
+ args.Player.SendErrorMessage("The item is banned and the config prevents you from spawning banned items.");
+ }
+ }
+ else
+ {
+ args.Player.SendErrorMessage("Your inventory seems full.");
+ }
}
private static void Give(CommandArgs args)
@@ -3435,7 +3671,7 @@ namespace TShockAPI
}
}
- public static void ClearItems(CommandArgs args)
+ private static void ClearItems(CommandArgs args)
{
int radius = 50;
if (args.Parameters.Count > 0)
diff --git a/TShockAPI/ConfigFile.cs b/TShockAPI/ConfigFile.cs
index 8ad3a8d4..9079f3e1 100644
--- a/TShockAPI/ConfigFile.cs
+++ b/TShockAPI/ConfigFile.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,12 +15,15 @@ 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.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
+using Rests;
namespace TShockAPI
{
@@ -129,7 +132,7 @@ namespace TShockAPI
[Description("This will announce a player's location on join")] public bool EnableGeoIP;
- [Description("This will turn on a token requirement for the /status API endpoint.")] public bool
+ [Description("This will turn on token requirement for the public REST API endpoints.")] public bool
EnableTokenEndpointAuthentication;
[Description("Deprecated. Use ServerName instead.")] public string ServerNickname = "TShock Server";
@@ -158,8 +161,6 @@ namespace TShockAPI
[Description("Time, in milliseconds, to disallow discarding items after logging in when ServerSideInventory is ON.")] public int LogonDiscardThreshold=250;
- [Description("Disables reporting of playercount to the stat system.")] public bool DisablePlayerCountReporting;
-
[Description("Disables clown bomb projectiles from spawning.")] public bool DisableClownBombs;
[Description("Disables snow ball projectiles from spawning.")] public bool DisableSnowBalls;
@@ -236,15 +237,37 @@ namespace TShockAPI
[Description("Prevent banks on SSI.")] public bool DisablePiggybanksOnSSI = false;
[Description("Prevent players from interacting with the world if dead.")] public bool PreventDeadModification =
- false;
+ true;
[Description("Displays chat messages above players' heads, but will disable chat prefixes to compensate.")] public
bool EnableChatAboveHeads = false;
- [Description("Hide stat tracker console messages.")] public bool HideStatTrackerDebugMessages = true;
-
[Description("Force Christmas only events to occur all year.")] public bool ForceXmas = false;
+ [Description("Allows groups on the banned item allowed list to spawn banned items.")] public bool AllowAllowedGroupsToSpawnBannedItems = false;
+
+ [Description("Allows stacks in chests to be beyond the stack limit")] public bool IgnoreChestStacksOnLoad = false;
+
+ [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 =
+ {127,255,212};
+
+ // TODO: Get rid of this when the old REST permission model is removed.
+ [Description(
+ "Whether the REST API should use the new permission model. Note: The old permission model will become depracted in the future."
+ )] public bool RestUseNewPermissionModel = true;
+
+ [Description("A dictionary of REST tokens that external applications may use to make queries to your server.")]
+ public Dictionary ApplicationRestTokens = new Dictionary();
+
+ [Description("The maximum value that a character may have for health.")] public int MaxHealth = 400;
+
+ [Description("The maximum value that a character may have for health.")] public int MaxMana = 400;
+
+ [Description("The number of reserved slots past your max server slot that can be joined by reserved players")] public int ReservedSlots = 20;
///
/// Reads a configuration file from a given path
///
diff --git a/TShockAPI/DB/BanManager.cs b/TShockAPI/DB/BanManager.cs
index 7ace1584..c2fef99e 100644
--- a/TShockAPI/DB/BanManager.cs
+++ b/TShockAPI/DB/BanManager.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,10 +15,10 @@ 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.Data;
-using System.IO;
using MySql.Data.MySqlClient;
namespace TShockAPI.DB
@@ -34,7 +34,10 @@ namespace TShockAPI.DB
var table = new SqlTable("Bans",
new SqlColumn("IP", MySqlDbType.String, 16) {Primary = true},
new SqlColumn("Name", MySqlDbType.Text),
- new SqlColumn("Reason", MySqlDbType.Text)
+ new SqlColumn("Reason", MySqlDbType.Text),
+ new SqlColumn("BanningUser", MySqlDbType.Text),
+ new SqlColumn("Date", MySqlDbType.Text),
+ new SqlColumn("Expiration", MySqlDbType.Text)
);
var creator = new SqlTableCreator(db,
db.GetSqlType() == SqlType.Sqlite
@@ -58,7 +61,7 @@ namespace TShockAPI.DB
using (var reader = database.QueryReader("SELECT * FROM Bans WHERE IP=@0", ip))
{
if (reader.Read())
- return new Ban(reader.Get("IP"), reader.Get("Name"), reader.Get("Reason"));
+ return new Ban(reader.Get("IP"), reader.Get("Name"), reader.Get("Reason"), reader.Get("BanningUser"), reader.Get("Date"), reader.Get("Expiration"));
}
}
catch (Exception ex)
@@ -77,7 +80,7 @@ namespace TShockAPI.DB
{
while (reader.Read())
{
- banlist.Add(new Ban(reader.Get("IP"), reader.Get("Name"), reader.Get("Reason")));
+ banlist.Add(new Ban(reader.Get("IP"), reader.Get("Name"), reader.Get("Reason"), reader.Get("BanningUser"), reader.Get("Date"), reader.Get("Expiration")));
}
return banlist;
}
@@ -100,7 +103,7 @@ namespace TShockAPI.DB
using (var reader = database.QueryReader("SELECT * FROM Bans WHERE " + namecol + "=@0", name))
{
if (reader.Read())
- return new Ban(reader.Get("IP"), reader.Get("Name"), reader.Get("Reason"));
+ return new Ban(reader.Get("IP"), reader.Get("Name"), reader.Get("Reason"), reader.Get("BanningUser"), reader.Get("Date"), reader.Get("Expiration"));
}
}
catch (Exception ex)
@@ -114,14 +117,14 @@ namespace TShockAPI.DB
[Obsolete("This method is for signature compatibility for external code only")]
public bool AddBan(string ip, string name, string reason)
{
- return AddBan(ip, name, reason, false);
+ return AddBan(ip, name, reason, false, "", "");
}
#endif
- public bool AddBan(string ip, string name = "", string reason = "", bool exceptions = false)
+ public bool AddBan(string ip, string name = "", string reason = "", bool exceptions = false, string banner = "", string expiration = "")
{
try
{
- return database.Query("INSERT INTO Bans (IP, Name, Reason) VALUES (@0, @1, @2);", ip, name, reason) != 0;
+ return database.Query("INSERT INTO Bans (IP, Name, Reason, BanningUser, Date, Expiration) VALUES (@0, @1, @2, @3, @4, @5);", ip, name, reason, banner, DateTime.Now.ToString("G"), expiration) != 0;
}
catch (Exception ex)
{
@@ -180,11 +183,20 @@ namespace TShockAPI.DB
public string Reason { get; set; }
- public Ban(string ip, string name, string reason)
+ public string BanningUser { get; set; }
+
+ public string Date { get; set; }
+
+ public string Expiration { get; set; }
+
+ public Ban(string ip, string name, string reason, string banner, string date, string exp)
{
IP = ip;
Name = name;
Reason = reason;
+ BanningUser = banner;
+ Date = date;
+ Expiration = exp;
}
public Ban()
@@ -192,6 +204,9 @@ namespace TShockAPI.DB
IP = string.Empty;
Name = string.Empty;
Reason = string.Empty;
+ BanningUser = "";
+ Date = "";
+ Expiration = "";
}
}
}
diff --git a/TShockAPI/DB/GroupManager.cs b/TShockAPI/DB/GroupManager.cs
index 5d962eb0..c7646d57 100644
--- a/TShockAPI/DB/GroupManager.cs
+++ b/TShockAPI/DB/GroupManager.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,11 +15,12 @@ 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;
using System.Collections.Generic;
using System.Data;
-using System.IO;
+using System.Diagnostics;
using System.Linq;
using MySql.Data.MySqlClient;
@@ -52,13 +53,29 @@ namespace TShockAPI.DB
LoadPermisions();
// Add default groups if they don't exist
- AddDefaultGroup("guest", "", "canbuild,canregister,canlogin,canpartychat,cantalkinthird");
- AddDefaultGroup("default", "guest", "warp,canchangepassword");
- AddDefaultGroup("newadmin", "default", "kick,editspawn,reservedslot");
+ AddDefaultGroup(TShock.Config.DefaultGuestGroupName, "",
+ string.Join(",", Permissions.canbuild, Permissions.canregister, Permissions.canlogin, Permissions.canpartychat,
+ Permissions.cantalkinthird));
+
+ AddDefaultGroup("default", TShock.Config.DefaultGuestGroupName,
+ string.Join(",", Permissions.warp, Permissions.canchangepassword));
+
+ AddDefaultGroup("newadmin", "default",
+ string.Join(",", Permissions.kick, Permissions.editspawn, Permissions.reservedslot));
+
AddDefaultGroup("admin", "newadmin",
- "ban,unban,whitelist,causeevents,spawnboss,spawnmob,managewarp,time,tp,pvpfun,kill,logs,immunetokick,tphere");
- AddDefaultGroup("trustedadmin", "admin", "maintenance,cfg,butcher,item,heal,immunetoban,usebanneditem,manageusers");
- AddDefaultGroup("vip", "default", "reservedslot");
+ string.Join(",", Permissions.ban, Permissions.whitelist, Permissions.causeevents, Permissions.spawnboss,
+ Permissions.spawnmob, Permissions.managewarp, Permissions.time, Permissions.tp, Permissions.slap,
+ Permissions.kill, Permissions.logs,
+ Permissions.immunetokick, Permissions.tphere));
+
+ AddDefaultGroup("trustedadmin", "admin",
+ string.Join(",", Permissions.maintenance, "tshock.cfg.*", "tshock.world.*", Permissions.butcher, Permissions.item,
+ Permissions.heal, Permissions.immunetoban, Permissions.usebanneditem));
+
+ AddDefaultGroup("vip", "default", string.Join(",", Permissions.reservedslot));
+
+ Group.DefaultGroup = GetGroupByName(TShock.Config.DefaultGuestGroupName);
}
private void AddDefaultGroup(string name, string parent, string permissions)
@@ -114,7 +131,7 @@ namespace TShockAPI.DB
if (!string.IsNullOrWhiteSpace(parentname))
{
var parent = groups.FirstOrDefault(gp => gp.Name == parentname);
- if (parent == null)
+ if (parent == null || name == parentname)
{
var error = "Invalid parent {0} for group {1}".SFormat(parentname, group.Name);
if (exceptions)
@@ -166,27 +183,40 @@ namespace TShockAPI.DB
/// chatcolor
public void UpdateGroup(string name, string parentname, string permissions, string chatcolor)
{
- if (!GroupExists(name))
+ Group group = GetGroupByName(name);
+ if (group == null)
throw new GroupNotExistException(name);
Group parent = null;
if (!string.IsNullOrWhiteSpace(parentname))
{
- parent = groups.FirstOrDefault(gp => gp.Name == parentname);
- if (null == parent)
- throw new GroupManagerException("Invalid parent {0} for group {1}".SFormat(parentname, name));
+ 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;
+ }
}
- // NOTE: we use newgroup.XYZ to ensure any validation is also persisted to the DB
- var newgroup = new Group(name, parent, chatcolor, permissions);
+ // 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";
- if (database.Query(query, parentname, newgroup.Permissions, string.Format("{0},{1},{2}", newgroup.R, newgroup.G, newgroup.B), name) != 1)
- throw new GroupManagerException("Failed to update group '" + name + "'");
+ if (database.Query(query, parentname, newGroup.Permissions, string.Format("{0},{1},{2}", newGroup.R, newGroup.G, newGroup.B), name) != 1)
+ throw new GroupManagerException(string.Format("Failed to update group \"{0}\".", name));
- Group group = TShock.Utils.GetGroup(name);
group.ChatColor = chatcolor;
group.Permissions = permissions;
- group.Parent = TShock.Utils.GetGroup(parentname);
+ group.Parent = parent;
}
#if COMPAT_SIGS
@@ -252,53 +282,106 @@ namespace TShockAPI.DB
public void LoadPermisions()
{
- // Create a temporary list so if there is an error it doesn't override the currently loaded groups with broken groups.
- var tempgroups = new List();
- tempgroups.Add(new SuperAdminGroup());
-
- if (groups == null || groups.Count < 2)
- {
- groups.Clear();
- groups.AddRange(tempgroups);
- }
-
try
{
- var groupsparents = new List>();
+ List newGroups = new List(groups.Count);
+ Dictionary newGroupParents = new Dictionary(groups.Count);
using (var reader = database.QueryReader("SELECT * FROM GroupList"))
{
while (reader.Read())
{
- var group = new Group(reader.Get("GroupName"), null, reader.Get("ChatColor"), reader.Get("Commands"));
- group.Prefix = reader.Get("Prefix");
- group.Suffix = reader.Get("Suffix");
- groupsparents.Add(Tuple.Create(group, reader.Get("Parent")));
- }
- }
-
- foreach (var t in groupsparents)
- {
- var group = t.Item1;
- var parentname = t.Item2;
- if (!string.IsNullOrWhiteSpace(parentname))
- {
- var parent = groupsparents.FirstOrDefault(gp => gp.Item1.Name == parentname);
- if (parent == null)
+ string groupName = reader.Get("GroupName");
+ if (groupName == "superadmin")
{
- Log.ConsoleError("Invalid parent {0} for group {1}".SFormat(parentname, group.Name));
+ 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;
}
- group.Parent = parent.Item1;
}
- tempgroups.Add(group);
}
- groups.Clear();
- groups.AddRange(tempgroups);
+ 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.Error(ex.ToString());
+ Log.ConsoleError("Error on reloading groups: " + ex);
}
}
}
diff --git a/TShockAPI/DB/IQueryBuilder.cs b/TShockAPI/DB/IQueryBuilder.cs
index 2d4a8662..d65af44a 100644
--- a/TShockAPI/DB/IQueryBuilder.cs
+++ b/TShockAPI/DB/IQueryBuilder.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
+
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/TShockAPI/DB/InventoryManager.cs b/TShockAPI/DB/InventoryManager.cs
index 92f8a6bf..9cbff2f4 100644
--- a/TShockAPI/DB/InventoryManager.cs
+++ b/TShockAPI/DB/InventoryManager.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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.Data;
using MySql.Data.MySqlClient;
diff --git a/TShockAPI/DB/ItemManager.cs b/TShockAPI/DB/ItemManager.cs
index 6ef11b6b..b95bd886 100644
--- a/TShockAPI/DB/ItemManager.cs
+++ b/TShockAPI/DB/ItemManager.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,10 +15,10 @@ 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.Data;
-using System.IO;
using System.Linq;
using MySql.Data.MySqlClient;
diff --git a/TShockAPI/DB/RegionManager.cs b/TShockAPI/DB/RegionManager.cs
index 31519726..f9cce5c8 100644
--- a/TShockAPI/DB/RegionManager.cs
+++ b/TShockAPI/DB/RegionManager.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,13 +15,11 @@ 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.Data;
-using System.Diagnostics.CodeAnalysis;
-using System.IO;
using System.Linq;
-using System.Xml;
using MySql.Data.MySqlClient;
using Terraria;
@@ -238,25 +236,21 @@ namespace TShockAPI.DB
return false;
}
Region top = null;
- for (int i = 0; i < Regions.Count; i++)
- {
- if (Regions[i].InArea(x,y) )
- {
- if (top == null)
- top = Regions[i];
- else
- {
- if (Regions[i].Z > top.Z)
- top = Regions[i];
- }
- }
- }
+
+ foreach (Region region in Regions.ToList())
+ {
+ if (region.InArea(x, y))
+ {
+ if (top == null || region.Z > top.Z)
+ top = region;
+ }
+ }
return top == null || top.HasPermissionToBuildInRegion(ply);
}
public bool InArea(int x, int y)
{
- foreach (Region region in Regions)
+ foreach (Region region in Regions.ToList())
{
if (x >= region.Area.Left && x <= region.Area.Right &&
y >= region.Area.Top && y <= region.Area.Bottom &&
@@ -271,7 +265,7 @@ namespace TShockAPI.DB
public List InAreaRegionName(int x, int y)
{
List regions = new List() { };
- foreach (Region region in Regions)
+ foreach (Region region in Regions.ToList())
{
if (x >= region.Area.Left && x <= region.Area.Right &&
y >= region.Area.Top && y <= region.Area.Bottom &&
@@ -286,7 +280,7 @@ namespace TShockAPI.DB
public List InAreaRegion(int x, int y)
{
List regions = new List() { };
- foreach (Region region in Regions)
+ foreach (Region region in Regions.ToList())
{
if (x >= region.Area.Left && x <= region.Area.Right &&
y >= region.Area.Top && y <= region.Area.Bottom &&
@@ -385,30 +379,36 @@ namespace TShockAPI.DB
return false;
}
- public bool AddNewUser(string regionName, String userName)
+ public bool AddNewUser(string regionName, string userName)
{
try
{
- string MergedIDs = string.Empty;
+ string mergedIDs = string.Empty;
using (
- var reader = database.QueryReader("SELECT * FROM Regions WHERE RegionName=@0 AND WorldID=@1", regionName,
+ var reader = database.QueryReader("SELECT UserIds FROM Regions WHERE RegionName=@0 AND WorldID=@1", regionName,
Main.worldID.ToString()))
{
if (reader.Read())
- MergedIDs = reader.Get("UserIds");
+ mergedIDs = reader.Get("UserIds");
}
- if (string.IsNullOrEmpty(MergedIDs))
- MergedIDs = Convert.ToString(TShock.Users.GetUserID(userName));
- else
- MergedIDs = MergedIDs + "," + Convert.ToString(TShock.Users.GetUserID(userName));
+ 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;
- int q = database.Query("UPDATE Regions SET UserIds=@0 WHERE RegionName=@1 AND WorldID=@2", MergedIDs,
+ if (string.IsNullOrEmpty(mergedIDs))
+ mergedIDs = userIdToAdd;
+ else
+ mergedIDs = string.Concat(mergedIDs, ",", userIdToAdd);
+
+ int q = database.Query("UPDATE Regions SET UserIds=@0 WHERE RegionName=@1 AND WorldID=@2", mergedIDs,
regionName, Main.worldID.ToString());
foreach (var r in Regions)
{
if (r.Name == regionName && r.WorldID == Main.worldID.ToString())
- r.setAllowedIDs(MergedIDs);
+ r.setAllowedIDs(mergedIDs);
}
return q != 0;
}
@@ -471,27 +471,33 @@ namespace TShockAPI.DB
return false;
}
- public bool AllowGroup(string regionName, string groups)
+ public bool AllowGroup(string regionName, string groupName)
{
- string groupsNew = "";
+ string mergedGroups = "";
using (
- var reader = database.QueryReader("SELECT * FROM Regions WHERE RegionName=@0 AND WorldID=@1", regionName,
+ var reader = database.QueryReader("SELECT Groups FROM Regions WHERE RegionName=@0 AND WorldID=@1", regionName,
Main.worldID.ToString()))
{
if (reader.Read())
- groupsNew = reader.Get("Groups");
+ mergedGroups = reader.Get("Groups");
}
- if (groupsNew != "")
- groupsNew += ",";
- groupsNew += groups;
- int q = database.Query("UPDATE Regions SET Groups=@0 WHERE RegionName=@1 AND WorldID=@2", groupsNew,
+ string[] groups = mergedGroups.Split(',');
+ // Is the group already allowed to the region?
+ if (groups.Contains(groupName))
+ return true;
+
+ if (mergedGroups != "")
+ mergedGroups += ",";
+ mergedGroups += groupName;
+
+ int q = database.Query("UPDATE Regions SET Groups=@0 WHERE RegionName=@1 AND WorldID=@2", mergedGroups,
regionName, Main.worldID.ToString());
Region r = GetRegionByName(regionName);
if (r != null)
{
- r.SetAllowedGroups(groupsNew);
+ r.SetAllowedGroups(mergedGroups);
}
else
{
diff --git a/TShockAPI/DB/RememberedPosManager.cs b/TShockAPI/DB/RememberedPosManager.cs
index 88d57215..604743ff 100644
--- a/TShockAPI/DB/RememberedPosManager.cs
+++ b/TShockAPI/DB/RememberedPosManager.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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.Data;
using MySql.Data.MySqlClient;
@@ -112,7 +113,7 @@ namespace TShockAPI.DB
try
{
if ((X != 0) && ( Y !=0)) //invalid pos!
- database.Query("UPDATE RememberedPos SET X = @0, Y = @1, IP = @2 WHERE Name = @3 AND WorldID = @4;", X, Y, IP, name, Main.worldID.ToString());
+ database.Query("UPDATE RememberedPos SET X = @0, Y = @1, IP = @2, WorldID = @3 WHERE Name = @4;", X, Y, IP, Main.worldID.ToString(), name);
}
catch (Exception ex)
{
diff --git a/TShockAPI/DB/SqlColumn.cs b/TShockAPI/DB/SqlColumn.cs
index efc94649..9b59c89e 100644
--- a/TShockAPI/DB/SqlColumn.cs
+++ b/TShockAPI/DB/SqlColumn.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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 MySql.Data.MySqlClient;
namespace TShockAPI.DB
diff --git a/TShockAPI/DB/SqlTable.cs b/TShockAPI/DB/SqlTable.cs
index 0b4024c6..08275ef9 100644
--- a/TShockAPI/DB/SqlTable.cs
+++ b/TShockAPI/DB/SqlTable.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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.Data;
diff --git a/TShockAPI/DB/SqlValue.cs b/TShockAPI/DB/SqlValue.cs
index 0efd78bf..04161e90 100644
--- a/TShockAPI/DB/SqlValue.cs
+++ b/TShockAPI/DB/SqlValue.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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.Collections.Generic;
using System.Data;
diff --git a/TShockAPI/DB/UserManager.cs b/TShockAPI/DB/UserManager.cs
index db360518..c8af324c 100644
--- a/TShockAPI/DB/UserManager.cs
+++ b/TShockAPI/DB/UserManager.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,9 +15,9 @@ 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.Data;
-using System.IO;
using System.Collections.Generic;
using System.Linq;
using MySql.Data.MySqlClient;
@@ -38,7 +38,8 @@ namespace TShockAPI.DB
new SqlColumn("Username", MySqlDbType.VarChar, 32) {Unique = true},
new SqlColumn("Password", MySqlDbType.VarChar, 128),
new SqlColumn("Usergroup", MySqlDbType.Text),
- new SqlColumn("IP", MySqlDbType.VarChar, 16)
+ new SqlColumn("LastAccessed", MySqlDbType.Text),
+ new SqlColumn("KnownIPs", MySqlDbType.Text)
);
var creator = new SqlTableCreator(db,
db.GetSqlType() == SqlType.Sqlite
@@ -59,8 +60,8 @@ namespace TShockAPI.DB
int ret;
try
{
- ret = database.Query("INSERT INTO Users (Username, Password, UserGroup, IP) VALUES (@0, @1, @2, @3);", user.Name,
- TShock.Utils.HashPassword(user.Password), user.Group, user.Address);
+ ret = database.Query("INSERT INTO Users (Username, Password, UserGroup) VALUES (@0, @1, @2);", user.Name,
+ TShock.Utils.HashPassword(user.Password), user.Group);
}
catch (Exception ex)
{
@@ -82,18 +83,10 @@ namespace TShockAPI.DB
{
try
{
- int affected = -1;
- if (!string.IsNullOrEmpty(user.Address))
- {
- affected = database.Query("DELETE FROM Users WHERE IP=@0", user.Address);
- }
- else
- {
- affected = database.Query("DELETE FROM Users WHERE Username=@0", user.Name);
- }
+ int affected = database.Query("DELETE FROM Users WHERE Username=@0", user.Name);
if (affected < 1)
- throw new UserNotExistException(string.IsNullOrEmpty(user.Address) ? user.Name : user.Address);
+ throw new UserNotExistException(user.Name);
}
catch (Exception ex)
{
@@ -101,7 +94,6 @@ namespace TShockAPI.DB
}
}
-
///
/// Sets the Hashed Password for a given username
///
@@ -150,6 +142,19 @@ namespace TShockAPI.DB
}
}
+ public void UpdateLogin(User user)
+ {
+ try
+ {
+ if (database.Query("UPDATE Users SET LastAccessed = @0, KnownIps = @1 WHERE Username = @2;", DateTime.Now.ToString("G"), user.KnownIps, user.Name) == 0)
+ throw new UserNotExistException(user.Name);
+ }
+ catch (Exception ex)
+ {
+ throw new UserManagerException("UpdateLogin SQL returned an error", ex);
+ }
+ }
+
public int GetUserID(string username)
{
try
@@ -169,53 +174,6 @@ namespace TShockAPI.DB
return -1;
}
- ///
- /// Returns a Group for a ip from the database
- ///
- /// string ip
- public Group GetGroupForIP(string ip)
- {
- try
- {
- using (var reader = database.QueryReader("SELECT * FROM Users WHERE IP=@0", ip))
- {
- if (reader.Read())
- {
- string group = reader.Get("UserGroup");
- return TShock.Utils.GetGroup(group);
- }
- }
- }
- catch (Exception ex)
- {
- Log.ConsoleError("GetGroupForIP SQL returned an error: " + ex);
- }
- return TShock.Utils.GetGroup(TShock.Config.DefaultGuestGroupName);
- }
-
- public Group GetGroupForIPExpensive(string ip)
- {
- try
- {
- using (var reader = database.QueryReader("SELECT IP, UserGroup FROM Users"))
- {
- while (reader.Read())
- {
- if (TShock.Utils.GetIPv4Address(reader.Get("IP")) == ip)
- {
- return TShock.Utils.GetGroup(reader.Get("UserGroup"));
- }
- }
- }
- }
- catch (Exception ex)
- {
- Log.ConsoleError("GetGroupForIP SQL returned an error: " + ex);
- }
- return TShock.Utils.GetGroup(TShock.Config.DefaultGuestGroupName);
- }
-
-
public User GetUserByName(string name)
{
try
@@ -240,18 +198,6 @@ namespace TShockAPI.DB
}
}
- public User GetUserByIP(string ip)
- {
- try
- {
- return GetUser(new User {Address = ip});
- }
- catch (UserManagerException)
- {
- return null;
- }
- }
-
public User GetUser(User user)
{
bool multiple = false;
@@ -264,18 +210,12 @@ namespace TShockAPI.DB
arg = user.ID;
type = "id";
}
- else if (string.IsNullOrEmpty(user.Address))
+ else
{
query = "SELECT * FROM Users WHERE Username=@0";
arg = user.Name;
type = "name";
}
- else
- {
- query = "SELECT * FROM Users WHERE IP=@0";
- arg = user.Address;
- type = "ip";
- }
try
{
@@ -298,7 +238,7 @@ namespace TShockAPI.DB
if (multiple)
throw new UserManagerException(String.Format("Multiple users found for {0} '{1}'", type, arg));
- throw new UserNotExistException(string.IsNullOrEmpty(user.Address) ? user.Name : user.Address);
+ throw new UserNotExistException(user.Name);
}
public List GetUsers()
@@ -328,7 +268,8 @@ namespace TShockAPI.DB
user.Group = result.Get("Usergroup");
user.Password = result.Get("Password");
user.Name = result.Get("Username");
- user.Address = result.Get("IP");
+ user.LastAccessed = result.Get("LastAccessed");
+ user.KnownIps = result.Get("KnownIps");
return user;
}
}
@@ -339,22 +280,25 @@ namespace TShockAPI.DB
public string Name { get; set; }
public string Password { get; set; }
public string Group { get; set; }
- public string Address { get; set; }
+ public string LastAccessed { get; set; }
+ public string KnownIps { get; set; }
- public User(string ip, string name, string pass, string group)
+ public User(string name, string pass, string group, string last, string known)
{
- Address = ip;
Name = name;
Password = pass;
Group = group;
+ LastAccessed = last;
+ KnownIps = known;
}
public User()
{
- Address = "";
Name = "";
Password = "";
Group = "";
+ LastAccessed = "";
+ KnownIps = "";
}
}
diff --git a/TShockAPI/DB/WarpsManager.cs b/TShockAPI/DB/WarpsManager.cs
index 67d1afe8..9ece9591 100644
--- a/TShockAPI/DB/WarpsManager.cs
+++ b/TShockAPI/DB/WarpsManager.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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.Data;
diff --git a/TShockAPI/Extensions/DbExt.cs b/TShockAPI/Extensions/DbExt.cs
index 42bcc6f1..3f1db5c7 100644
--- a/TShockAPI/Extensions/DbExt.cs
+++ b/TShockAPI/Extensions/DbExt.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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.Data;
diff --git a/TShockAPI/Extensions/LinqExt.cs b/TShockAPI/Extensions/LinqExt.cs
index 25d06b7e..86b639bd 100644
--- a/TShockAPI/Extensions/LinqExt.cs
+++ b/TShockAPI/Extensions/LinqExt.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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;
diff --git a/TShockAPI/Extensions/RandomExt.cs b/TShockAPI/Extensions/RandomExt.cs
index 2e4d2681..e0a866bf 100644
--- a/TShockAPI/Extensions/RandomExt.cs
+++ b/TShockAPI/Extensions/RandomExt.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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.Text;
diff --git a/TShockAPI/Extensions/StringExt.cs b/TShockAPI/Extensions/StringExt.cs
index e70be35b..ea67e801 100644
--- a/TShockAPI/Extensions/StringExt.cs
+++ b/TShockAPI/Extensions/StringExt.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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;
namespace TShockAPI
diff --git a/TShockAPI/FileTools.cs b/TShockAPI/FileTools.cs
index 59072016..581fe393 100644
--- a/TShockAPI/FileTools.cs
+++ b/TShockAPI/FileTools.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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.IO;
diff --git a/TShockAPI/GeoIPCountry.cs b/TShockAPI/GeoIPCountry.cs
index c09ccffc..4ca30777 100644
--- a/TShockAPI/GeoIPCountry.cs
+++ b/TShockAPI/GeoIPCountry.cs
@@ -1,4 +1,22 @@
-/* GeoIPCountry.cs
+/*
+TShock, a server mod for Terraria
+Copyright (C) 2011-2013 Nyx Studios (fka. 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 .
+*/
+
+/* GeoIPCountry.cs
*
* Copyright (C) 2008 MaxMind, Inc. All Rights Reserved.
*
@@ -16,24 +34,6 @@
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-
-/*
-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.IO;
using System.Net;
diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs
index c085a31b..0b128ba9 100644
--- a/TShockAPI/GetDataHandlers.cs
+++ b/TShockAPI/GetDataHandlers.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,13 +15,16 @@ 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.ComponentModel;
+using System.Diagnostics;
using System.IO;
using System.IO.Streams;
using System.Linq;
using System.Text;
+using TShockAPI.DB;
using Terraria;
using TShockAPI.Net;
@@ -86,13 +89,18 @@ namespace TShockAPI
/// Did the tile get destroyed successfully.
///
public bool Fail { get; set; }
+
+ ///
+ /// Used when a tile is placed to denote a subtype of tile. (e.g. for tile id 21: Chest = 0, Gold Chest = 1)
+ ///
+ public byte Style { get; set; }
}
///
/// TileEdit - called when a tile is placed or destroyed
///
public static HandlerList TileEdit;
- private static bool OnTileEdit(TSPlayer ply, int x, int y, byte type, byte editType, bool fail)
+ private static bool OnTileEdit(TSPlayer ply, int x, int y, byte type, byte editType, bool fail, byte style)
{
if (TileEdit == null)
return false;
@@ -104,7 +112,8 @@ namespace TShockAPI
Y = y,
Type = type,
EditType = editType,
- Fail = fail
+ Fail = fail,
+ Style = style
};
TileEdit.Invoke(null, args);
return args.Handled;
@@ -1189,6 +1198,14 @@ namespace TShockAPI
byte prefix = args.Data.ReadInt8();
short type = args.Data.ReadInt16();
+ // Players send a slot update packet for each inventory slot right after they've joined.
+ bool bypassTrashCanCheck = false;
+ if (plr == args.Player.Index && !args.Player.HasSentInventory && slot == NetItem.maxNetInventory)
+ {
+ args.Player.HasSentInventory = true;
+ bypassTrashCanCheck = true;
+ }
+
if (OnPlayerSlot(plr, slot, stack, prefix, type))
return true;
@@ -1202,6 +1219,7 @@ namespace TShockAPI
return true;
}
+ // Garabage? Or will it cause some internal initialization or whatever?
var item = new Item();
item.netDefaults(type);
item.Prefix(prefix);
@@ -1210,6 +1228,13 @@ namespace TShockAPI
{
args.Player.PlayerData.StoreSlot(slot, type, prefix, stack);
}
+ else if (
+ TShock.Config.ServerSideInventory && TShock.Config.DisableLoginBeforeJoin && !bypassTrashCanCheck &&
+ args.Player.HasSentInventory && !args.Player.Group.HasPermission(Permissions.bypassinventorychecks)
+ ) {
+ // The player might have moved an item to their trash can before they performed a single login attempt yet.
+ args.Player.IgnoreActionsForClearingTrashCan = true;
+ }
return false;
}
@@ -1226,7 +1251,7 @@ namespace TShockAPI
if (args.Player.FirstMaxHP == 0)
args.Player.FirstMaxHP = max;
- if (max > 400 && max > args.Player.FirstMaxHP)
+ if (max > TShock.Config.MaxHealth && max > args.Player.FirstMaxHP)
{
TShock.Utils.ForceKick(args.Player, "Hacked Client Detected.", true);
return false;
@@ -1252,7 +1277,7 @@ namespace TShockAPI
if (args.Player.FirstMaxMP == 0)
args.Player.FirstMaxMP = max;
- if (max > 400 && max > args.Player.FirstMaxMP)
+ if (max > TShock.Config.MaxMana && max > args.Player.FirstMaxMP)
{
TShock.Utils.ForceKick(args.Player, "Hacked Client Detected.", true);
return false;
@@ -1281,12 +1306,6 @@ namespace TShockAPI
TShock.Utils.ForceKick(args.Player, "Empty Name.", true);
return true;
}
- var ban = TShock.Bans.GetBanByName(name);
- if (ban != null)
- {
- TShock.Utils.ForceKick(args.Player, string.Format("You are banned: {0}", ban.Reason), true);
- return true;
- }
if (args.Player.ReceivedInfo)
{
return true;
@@ -1336,6 +1355,10 @@ namespace TShockAPI
return true;
string password = Encoding.UTF8.GetString(args.Data.ReadBytes((int) (args.Data.Length - args.Data.Position - 1)));
+
+ if (Hooks.PlayerHooks.OnPlayerPreLogin(args.Player, args.Player.Name, password))
+ return true;
+
var user = TShock.Users.GetUserByName(args.Player.Name);
if (user != null && !TShock.Config.DisableLoginBeforeJoin)
{
@@ -1359,11 +1382,13 @@ namespace TShockAPI
}
else if (!TShock.CheckInventory(args.Player))
{
+ args.Player.LoginFailsBySsi = true;
args.Player.SendMessage("Login Failed, Please fix the above errors then /login again.", Color.Cyan);
args.Player.IgnoreActionsForClearingTrashCan = true;
return true;
}
}
+ args.Player.LoginFailsBySsi = false;
if (group.HasPermission(Permissions.ignorestackhackdetection))
args.Player.IgnoreActionsForCheating = "none";
@@ -1372,6 +1397,7 @@ namespace TShockAPI
args.Player.IgnoreActionsForDisabledArmor = "none";
args.Player.Group = group;
+ args.Player.tempGroup = null;
args.Player.UserAccountName = args.Player.Name;
args.Player.UserID = TShock.Users.GetUserID(args.Player.UserAccountName);
args.Player.IsLoggedIn = true;
@@ -1384,7 +1410,7 @@ namespace TShockAPI
}
args.Player.SendMessage("Authenticated as " + args.Player.Name + " successfully.", Color.LimeGreen);
Log.ConsoleInfo(args.Player.Name + " authenticated successfully as user " + args.Player.Name + ".");
- Hooks.PlayerLoginEvent.OnPlayerLogin(args.Player);
+ Hooks.PlayerHooks.OnPlayerPostLogin(args.Player);
return true;
}
TShock.Utils.ForceKick(args.Player, "Invalid user account password.", true);
@@ -1418,7 +1444,7 @@ namespace TShockAPI
TShock.Utils.ForceKick(args.Player, "Blank name.", true);
return true;
}
- if (TShock.HackedHealth(args.Player) && !args.Player.Group.HasPermission(Permissions.ignorestathackdetection))
+ if (TShock.HackedStats(args.Player) && !args.Player.Group.HasPermission(Permissions.ignorestathackdetection))
{
TShock.Utils.ForceKick(args.Player, "You have hacked health/mana, please use a different character.", true);
return true;
@@ -1443,13 +1469,15 @@ namespace TShockAPI
Log.Info(string.Format("{0} ({1}) from '{2}' group from '{3}' joined. ({4}/{5})", args.Player.Name, args.Player.IP,
args.Player.Group.Name, args.Player.Country, TShock.Utils.ActivePlayers(),
TShock.Config.MaxSlots));
- TShock.Utils.Broadcast(string.Format("{0} ({1}) has joined.", args.Player.Name, args.Player.Country), Color.Yellow);
+ if (!args.Player.SilentJoinInProgress)
+ TShock.Utils.Broadcast(string.Format("{0} ({1}) has joined.", args.Player.Name, args.Player.Country), Color.Yellow);
}
else
{
Log.Info(string.Format("{0} ({1}) from '{2}' group joined. ({3}/{4})", args.Player.Name, args.Player.IP,
args.Player.Group.Name, TShock.Utils.ActivePlayers(), TShock.Config.MaxSlots));
- TShock.Utils.Broadcast(args.Player.Name + " has joined.", Color.Yellow);
+ if (!args.Player.SilentJoinInProgress)
+ TShock.Utils.Broadcast(args.Player.Name + " has joined.", Color.Yellow);
}
if (TShock.Config.DisplayIPToAdmins)
@@ -1651,10 +1679,12 @@ namespace TShockAPI
var tileX = args.Data.ReadInt32();
var tileY = args.Data.ReadInt32();
var tiletype = args.Data.ReadInt8();
- var fail = args.Data.ReadBoolean();
- if (OnTileEdit(args.Player, tileX, tileY, tiletype, type, fail))
+ var fail = tiletype == 1;
+ var style = args.Data.ReadInt8();
+
+ if (OnTileEdit(args.Player, tileX, tileY, tiletype, type, fail, style))
return true;
- if (!TShock.Utils.TileValid(tileX, tileY))
+ if (!TShock.Utils.TilePlacementValid(tileX, tileY))
return false;
if (args.Player.Dead && TShock.Config.PreventDeadModification)
@@ -1662,18 +1692,63 @@ namespace TShockAPI
if (args.Player.AwaitingName)
{
- var protectedregions = TShock.Regions.InAreaRegionName(tileX, tileY);
- if (protectedregions.Count == 0)
+ Debug.Assert(args.Player.AwaitingNameParameters != null);
+
+ bool includeUnprotected = false;
+ bool includeZIndexes = false;
+ bool persistentMode = false;
+ foreach (string parameter in args.Player.AwaitingNameParameters)
{
- args.Player.SendMessage("Region is not protected", Color.Yellow);
+ if (parameter.Equals("-u", StringComparison.InvariantCultureIgnoreCase))
+ includeUnprotected = true;
+ if (parameter.Equals("-z", StringComparison.InvariantCultureIgnoreCase))
+ includeZIndexes = true;
+ if (parameter.Equals("-p", StringComparison.InvariantCultureIgnoreCase))
+ persistentMode = true;
+ }
+
+ List outputRegions = new List();
+ foreach (Region region in TShock.Regions.Regions.OrderBy(r => r.Z).Reverse())
+ {
+ if (!includeUnprotected && !region.DisableBuild)
+ continue;
+ if (tileX < region.Area.Left || tileX > region.Area.Right)
+ continue;
+ if (tileY < region.Area.Top || tileY > region.Area.Bottom)
+ continue;
+
+ string format = "{1}";
+ if (includeZIndexes)
+ format = "{1} (z:{0})";
+
+ outputRegions.Add(string.Format(format, region.Z, region.Name));
+ }
+
+ if (outputRegions.Count == 0)
+ {
+ if (includeUnprotected)
+ args.Player.SendMessage("There are no regions at this point.", Color.Yellow);
+ else
+ args.Player.SendMessage("There are no regions at this point or they are not protected.", Color.Yellow);
}
else
{
- string regionlist = string.Join(",", protectedregions.ToArray());
- args.Player.SendMessage("Region Name(s): " + regionlist, Color.Yellow);
+ if (includeUnprotected)
+ args.Player.SendSuccessMessage("Regions at this point:");
+ else
+ args.Player.SendSuccessMessage("Protected regions at this point:");
+
+ foreach (string line in PaginationTools.BuildLinesFromTerms(outputRegions))
+ args.Player.SendMessage(line, Color.White);
}
+
+ if (!persistentMode)
+ {
+ args.Player.AwaitingName = false;
+ args.Player.AwaitingNameParameters = null;
+ }
+
args.Player.SendTileSquare(tileX, tileY);
- args.Player.AwaitingName = false;
return true;
}
@@ -1687,22 +1762,63 @@ namespace TShockAPI
return true;
}
- if (type == 1 || type == 3)
+ byte[] rightClickKill = new byte[] { 4, 13, 33, 49, 50, 128};
+ Item selectedItem = args.TPlayer.inventory[args.TPlayer.selectedItem];
+ if (type == 0 && Main.tile[tileX, tileY].type != 127 && !Main.tileCut[Main.tile[tileX, tileY].type] && !rightClickKill.Contains(Main.tile[tileX, tileY].type))
{
- if (tiletype >= ((type == 1) ? Main.maxTileSets : Main.maxWallTypes))
+ // If the tile is an axe tile and they aren't selecting an axe, they're hacking.
+ if (Main.tileAxe[Main.tile[tileX, tileY].type] && selectedItem.axe == 0)
{
+ args.Player.SendTileSquare(tileX, tileY);
+ return true;
+ }
+ // If the tile is a hammer tile and they aren't selecting an hammer, they're hacking.
+ else if (Main.tileHammer[Main.tile[tileX, tileY].type] && selectedItem.hammer == 0)
+ {
+ args.Player.SendTileSquare(tileX, tileY);
+ return true;
+ }
+ // If the tile is a pickaxe tile and they aren't selecting an pickaxe, they're hacking.
+ else if ((!Main.tileAxe[Main.tile[tileX, tileY].type] && !Main.tileHammer[Main.tile[tileX, tileY].type]) && selectedItem.pick == 0)
+ {
+ args.Player.SendTileSquare(tileX, tileY);
+ return true;
+ }
+ }
+ else if (type == 2)
+ {
+ // If they aren't selecting an hammer, they're hacking.
+ if (selectedItem.hammer == 0)
+ {
+ args.Player.SendTileSquare(tileX, tileY);
+ return true;
+ }
+ }
+ else if (type == 1 || type == 3)
+ {
+ if (type == 1 && TShock.Config.PreventInvalidPlaceStyle && ((tiletype == 4 && style > 8) ||
+ (tiletype == 13 && style > 4) || (tiletype == 15 && style > 1) || (tiletype == 21 && style > 6) ||
+ (tiletype == 82 && style > 5) || (tiletype == 91 && style > 3) || (tiletype == 105 && style > 42) ||
+ (tiletype == 135 && style > 3) || (tiletype == 139 && style > 12) || (tiletype == 144 && style > 2) ||
+ (tiletype == 149 && style > 2)))
+ {
+ args.Player.SendTileSquare(tileX, tileY);
+ return true;
+ }
+ // If they aren't selecting the item which creates the tile or wall, they're hacking.
+ if (tiletype != 127 && tiletype != (type == 1 ? selectedItem.createTile : selectedItem.createWall))
+ {
+ args.Player.SendTileSquare(tileX, tileY);
+ return true;
+ }
+ if (TShock.Itembans.ItemIsBanned(selectedItem.name, args.Player) || tiletype >= (type == 1 ? Main.maxTileSets : Main.maxWallTypes))
+ {
+ args.Player.SendTileSquare(tileX, tileY);
return true;
}
if (type == 1 && (tiletype == 29 || tiletype == 97) && TShock.Config.ServerSideInventory && TShock.Config.DisablePiggybanksOnSSI)
{
- args.Player.SendMessage("You cannot place this tile, server side inventory is enabled.", Color.Red);
- args.Player.SendTileSquare(tileX, tileY);
- return true;
- }
- if (tiletype == 48 && !args.Player.Group.HasPermission(Permissions.usebanneditem) &&
- TShock.Itembans.ItemIsBanned("Spike", args.Player))
- {
- args.Player.Disable("Used banned spikes without permission.");
+ args.Player.SendMessage("You cannot place this tile because server side inventory is enabled.", Color.Red);
args.Player.SendTileSquare(tileX, tileY);
return true;
}
@@ -1710,21 +1826,32 @@ namespace TShockAPI
{
if (TShock.Utils.MaxChests())
{
- args.Player.SendMessage("Reached the world's max chest limit, unable to place more.", Color.Red);
+ args.Player.SendMessage("The world's chest limit has been reached - unable to place more.", Color.Red);
args.Player.SendTileSquare(tileX, tileY);
return true;
}
- if ((TShock.Utils.TileValid(tileX, tileY + 1) && Main.tile[tileX, tileY + 1].type == 138) ||
- (TShock.Utils.TileValid(tileX + 1, tileY + 1) && Main.tile[tileX + 1, tileY + 1].type == 138))
+ if ((TShock.Utils.TilePlacementValid(tileX, tileY + 1) && Main.tile[tileX, tileY + 1].type == 138) ||
+ (TShock.Utils.TilePlacementValid(tileX + 1, tileY + 1) && Main.tile[tileX + 1, tileY + 1].type == 138))
{
args.Player.SendTileSquare(tileX, tileY);
return true;
}
}
- if (tiletype == 141 && !args.Player.Group.HasPermission(Permissions.usebanneditem) &&
- TShock.Itembans.ItemIsBanned("Explosives", args.Player))
+ }
+ else if (type == 5)
+ {
+ // If they aren't selecting the wrench, they're hacking.
+ if (args.TPlayer.inventory[args.TPlayer.selectedItem].type != 509)
+ {
+ args.Player.SendTileSquare(tileX, tileY);
+ return true;
+ }
+ }
+ else if (type == 6)
+ {
+ // If they aren't selecting the wire cutter, they're hacking.
+ if (args.TPlayer.inventory[args.TPlayer.selectedItem].type != 510)
{
- args.Player.Disable("Used banned explosives tile without permission.");
args.Player.SendTileSquare(tileX, tileY);
return true;
}
@@ -2040,17 +2167,18 @@ namespace TShockAPI
return true;
}
- if (!TShock.Config.IgnoreProjUpdate && TShock.CheckProjectilePermission(args.Player, index, type))
+ bool hasPermission = !TShock.CheckProjectilePermission(args.Player, index, type);
+ if (!TShock.Config.IgnoreProjUpdate && !hasPermission)
{
- if (type == 100)
- { //fix for skele prime
- Log.Debug("Skeletron Prime's death laser ignored for cheat detection..");
- }
- else
- {
- args.Player.Disable("Does not have projectile permission to update projectile.");
- args.Player.RemoveProjectile(ident, owner);
- }
+ if (type == 100)
+ { //fix for skele prime
+ Log.Debug("Skeletron Prime's death laser ignored for cheat detection..");
+ }
+ else
+ {
+ args.Player.Disable("Does not have projectile permission to update projectile.");
+ args.Player.RemoveProjectile(ident, owner);
+ }
return true;
}
@@ -2069,14 +2197,22 @@ namespace TShockAPI
if (!args.Player.Group.HasPermission(Permissions.ignoreprojectiledetection))
{
- if ((type ==90) && (TShock.Config.ProjIgnoreShrapnel))// ignore shrapnel
- {
- Log.Debug("Ignoring shrapnel per config..");
- }
- else
- {
- args.Player.ProjectileThreshold++;
- }
+ if ((type == 90) && (TShock.Config.ProjIgnoreShrapnel))// ignore shrapnel
+ {
+ Log.Debug("Ignoring shrapnel per config..");
+ }
+ else
+ {
+ args.Player.ProjectileThreshold++;
+ }
+ }
+
+ // force all explosives server-side.
+ if (hasPermission && (type == 28 || type == 29 || type == 37))
+ {
+ args.Player.RemoveProjectile(ident, owner);
+ Projectile.NewProjectile(pos.X, pos.Y, vel.X, vel.Y, type, dmg, knockback);
+ return true;
}
return false;
@@ -2192,18 +2328,36 @@ namespace TShockAPI
bucket = 2;
}
- if (lava && bucket != 2 && !args.Player.Group.HasPermission(Permissions.usebanneditem) &&
- TShock.Itembans.ItemIsBanned("Lava Bucket", args.Player))
- {
- args.Player.Disable("Using banned lava bucket without permissions.");
- args.Player.SendTileSquare(tileX, tileY);
- return true;
- }
+ if(lava && bucket != 2)
+ {
+ args.Player.SendErrorMessage("You do not have permission to perform this action.");
+ args.Player.Disable("Spreading lava without holding a lava bucket");
+ args.Player.SendTileSquare(tileX, tileY);
+ return true;
+ }
+
+ if(lava && (!args.Player.Group.HasPermission(Permissions.usebanneditem) &&
+ TShock.Itembans.ItemIsBanned("Lava Bucket", args.Player)))
+ {
+ args.Player.SendErrorMessage("You do not have permission to perform this action.");
+ args.Player.Disable("Using banned lava bucket without permissions");
+ args.Player.SendTileSquare(tileX, tileY);
+ return true;
+ }
- if (!lava && bucket != 1 && !args.Player.Group.HasPermission(Permissions.usebanneditem) &&
- TShock.Itembans.ItemIsBanned("Water Bucket", args.Player))
+ if (!lava && bucket != 1)
+ {
+ args.Player.SendErrorMessage("You do not have permission to perform this action.");
+ args.Player.Disable("Spreading water without holding a water bucket");
+ args.Player.SendTileSquare(tileX, tileY);
+ return true;
+ }
+
+ if (!lava && (!args.Player.Group.HasPermission(Permissions.usebanneditem) &&
+ TShock.Itembans.ItemIsBanned("Water Bucket", args.Player)))
{
- args.Player.Disable("Using banned water bucket without permissions.");
+ args.Player.SendErrorMessage("You do not have permission to perform this action.");
+ args.Player.Disable("Using banned water bucket without permissions");
args.Player.SendTileSquare(tileX, tileY);
return true;
}
@@ -2286,7 +2440,7 @@ namespace TShockAPI
{
if (TShock.Config.BanOnMediumcoreDeath)
{
- if (!TShock.Utils.Ban(args.Player, TShock.Config.MediumcoreBanReason))
+ if (!TShock.Utils.Ban(args.Player, TShock.Config.MediumcoreBanReason, false, "mediumcore-death"))
TShock.Utils.ForceKick(args.Player, "Death results in a ban, but can't ban you.", true);
}
else
@@ -2782,7 +2936,7 @@ namespace TShockAPI
break;
}
- TShock.Utils.SendLogs(string.Format("{0} summoned {1}", args.Player.Name, boss), Color.Red);
+ TShock.Utils.SendLogs(string.Format("{0} summoned {1}", args.Player.Name, boss), Color.PaleVioletRed, args.Player);
return false;
}
}
diff --git a/TShockAPI/Group.cs b/TShockAPI/Group.cs
index d525ee54..08a2aa9b 100644
--- a/TShockAPI/Group.cs
+++ b/TShockAPI/Group.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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.Linq;
using System.Collections.Generic;
@@ -28,7 +29,7 @@ namespace TShockAPI
///
/// Default chat color.
///
- public const string defaultChatColor = "255.255.255";
+ public const string defaultChatColor = "255,255,255";
///
/// List of permissions available to the group.
@@ -151,6 +152,7 @@ namespace TShockAPI
public byte G = 255;
public byte B = 255;
+ public static Group DefaultGroup = null;
#if COMPAT_SIGS
[Obsolete("This constructor is for signature compatibility for external code only")]
public Group(string groupname, Group parentgroup, string chatcolor)
@@ -204,7 +206,7 @@ namespace TShockAPI
return true;
if (traversed.Contains(cur))
{
- throw new Exception("Infinite group parenting ({0})".SFormat(cur.Name));
+ throw new InvalidOperationException("Infinite group parenting ({0})".SFormat(cur.Name));
}
traversed.Add(cur);
cur = cur.Parent;
@@ -271,6 +273,26 @@ namespace TShockAPI
}
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;
+ }
}
///
@@ -298,4 +320,4 @@ namespace TShockAPI
return true;
}
}
-}
\ No newline at end of file
+}
diff --git a/TShockAPI/HandlerList.cs b/TShockAPI/HandlerList.cs
index 12822962..128e6c5e 100644
--- a/TShockAPI/HandlerList.cs
+++ b/TShockAPI/HandlerList.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,11 +15,11 @@ 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.ComponentModel;
using System.Linq;
-using System.Text;
namespace TShockAPI
{
diff --git a/TShockAPI/Hooks/GeneralHooks.cs b/TShockAPI/Hooks/GeneralHooks.cs
new file mode 100644
index 00000000..f84efabd
--- /dev/null
+++ b/TShockAPI/Hooks/GeneralHooks.cs
@@ -0,0 +1,43 @@
+/*
+TShock, a server mod for Terraria
+Copyright (C) 2011-2013 Nyx Studios (fka. 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 .
+*/
+
+namespace TShockAPI.Hooks
+{
+ public class ReloadEventArgs
+ {
+ public TSPlayer Player { get; set; }
+ public ReloadEventArgs(TSPlayer ply)
+ {
+ Player = ply;
+ }
+ }
+
+ public class GeneralHooks
+ {
+ public delegate void ReloadEventD(ReloadEventArgs e);
+ public static event ReloadEventD ReloadEvent;
+
+ public static void OnReloadEvent(TSPlayer ply)
+ {
+ if(ReloadEvent == null)
+ return;
+
+ ReloadEvent(new ReloadEventArgs(ply));
+ }
+ }
+}
diff --git a/TShockAPI/Hooks/PlayerHooks.cs b/TShockAPI/Hooks/PlayerHooks.cs
new file mode 100644
index 00000000..047fca50
--- /dev/null
+++ b/TShockAPI/Hooks/PlayerHooks.cs
@@ -0,0 +1,98 @@
+/*
+TShock, a server mod for Terraria
+Copyright (C) 2011-2013 Nyx Studios (fka. 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.Collections.Generic;
+using System.ComponentModel;
+
+namespace TShockAPI.Hooks
+{
+ public class PlayerPostLoginEventArgs
+ {
+ public TSPlayer Player { get; set; }
+ public PlayerPostLoginEventArgs(TSPlayer ply)
+ {
+ Player = ply;
+ }
+ }
+
+ public class PlayerPreLoginEventArgs : HandledEventArgs
+ {
+ public TSPlayer Player { get; set; }
+ public string LoginName { get; set; }
+ public string Password { get; set; }
+ }
+
+ public class PlayerCommandEventArgs : HandledEventArgs
+ {
+ public TSPlayer Player { get; set; }
+ public string CommandName { get; set; }
+ public string CommandText { get; set; }
+ public List Parameters { get; set; }
+ }
+
+ public static class PlayerHooks
+ {
+ public delegate void PlayerPostLoginD(PlayerPostLoginEventArgs e);
+ public static event PlayerPostLoginD PlayerPostLogin;
+
+ public delegate void PlayerPreLoginD(PlayerPreLoginEventArgs e);
+ public static event PlayerPreLoginD PlayerPreLogin;
+
+ public delegate void PlayerCommandD(PlayerCommandEventArgs e);
+ public static event PlayerCommandD PlayerCommand;
+
+ public static void OnPlayerPostLogin(TSPlayer ply)
+ {
+ if(PlayerPostLogin == null)
+ {
+ return;
+ }
+
+ PlayerPostLoginEventArgs args = new PlayerPostLoginEventArgs(ply);
+ PlayerPostLogin(args);
+ }
+
+ public static bool OnPlayerCommand(TSPlayer player, string cmdName, string cmdText, List args)
+ {
+ if (PlayerCommand == null)
+ {
+ return false;
+ }
+ PlayerCommandEventArgs playerCommandEventArgs = new PlayerCommandEventArgs()
+ {
+ Player = player,
+ CommandName = cmdName,
+ CommandText = cmdText,
+ Parameters = args
+
+ };
+ PlayerCommand(playerCommandEventArgs);
+ return playerCommandEventArgs.Handled;
+ }
+
+ public static bool OnPlayerPreLogin(TSPlayer ply, string name, string pass)
+ {
+ if (PlayerPreLogin == null)
+ return false;
+
+ var args = new PlayerPreLoginEventArgs {Player = ply, LoginName = name, Password = pass};
+ PlayerPreLogin(args);
+ return args.Handled;
+ }
+ }
+}
diff --git a/TShockAPI/Hooks/PlayerLoginEvent.cs b/TShockAPI/Hooks/PlayerLoginEvent.cs
deleted file mode 100644
index b533720f..00000000
--- a/TShockAPI/Hooks/PlayerLoginEvent.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace TShockAPI.Hooks
-{
- class PlayerLoginEventArgs
- {
- public TSPlayer Player { get; set; }
- public PlayerLoginEventArgs(TSPlayer ply)
- {
- Player = ply;
- }
- }
-
- class PlayerLoginEvent
- {
- public delegate void PlayerLoginD(PlayerLoginEventArgs e);
- public static event PlayerLoginD PlayerLogin;
- public static void OnPlayerLogin(TSPlayer ply)
- {
- if(PlayerLogin == null)
- {
- return;
- }
-
- PlayerLoginEventArgs args = new PlayerLoginEventArgs(ply);
- PlayerLogin(args);
- }
- }
-}
diff --git a/TShockAPI/IPackable.cs b/TShockAPI/IPackable.cs
index 8523aeff..b44bedc8 100644
--- a/TShockAPI/IPackable.cs
+++ b/TShockAPI/IPackable.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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.IO;
namespace TShockAPI
diff --git a/TShockAPI/Log.cs b/TShockAPI/Log.cs
index 9e54c53a..1e286b09 100644
--- a/TShockAPI/Log.cs
+++ b/TShockAPI/Log.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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.Diagnostics;
using System.Globalization;
@@ -72,6 +73,16 @@ namespace TShockAPI
Write(message, LogLevel.Data);
}
+ ///
+ /// Writes data to the log file.
+ ///
+ /// The format of the message to be written.
+ /// The format arguments.
+ public static void Data(String format, params String[] args)
+ {
+ Data(String.Format(format, args));
+ }
+
///
/// Writes an error to the log file.
///
@@ -81,6 +92,16 @@ namespace TShockAPI
Write(message, LogLevel.Error);
}
+ ///
+ /// Writes an error to the log file.
+ ///
+ /// The format of the message to be written.
+ /// The format arguments.
+ public static void Error(String format, params String[] args)
+ {
+ Error(String.Format(format, args));
+ }
+
///
/// Writes an error to the log file.
///
@@ -93,6 +114,16 @@ namespace TShockAPI
Write(message, LogLevel.Error);
}
+ ///
+ /// Writes an error to the log file.
+ ///
+ /// The format of the message to be written.
+ /// The format arguments.
+ public static void ConsoleError(String format, params String[] args)
+ {
+ ConsoleError(String.Format(format, args));
+ }
+
///
/// Writes a warning to the log file.
///
@@ -102,6 +133,16 @@ namespace TShockAPI
Write(message, LogLevel.Warning);
}
+ ///
+ /// Writes a warning to the log file.
+ ///
+ /// The format of the message to be written.
+ /// The format arguments.
+ public static void Warn(String format, params String[] args)
+ {
+ Warn(String.Format(format, args));
+ }
+
///
/// Writes an informative string to the log file.
///
@@ -111,6 +152,16 @@ namespace TShockAPI
Write(message, LogLevel.Info);
}
+ ///
+ /// Writes an informative string to the log file.
+ ///
+ /// The format of the message to be written.
+ /// The format arguments.
+ public static void Info(String format, params String[] args)
+ {
+ Info(String.Format(format, args));
+ }
+
///
/// Writes an informative string to the log file. Also outputs to the console.
///
@@ -123,6 +174,16 @@ namespace TShockAPI
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 arguments.
+ public static void ConsoleInfo(String format, params String[] args)
+ {
+ ConsoleInfo(String.Format(format, args));
+ }
+
///
/// Writes a debug string to the log file.
///
@@ -132,6 +193,16 @@ namespace TShockAPI
Write(message, LogLevel.Debug);
}
+ ///
+ /// Writes a debug string to the log file.
+ ///
+ /// The format of the message to be written.
+ /// The format arguments.
+ public static void Debug(String format, params String[] args)
+ {
+ Debug(String.Format(format, args));
+ }
+
///
/// Disposes objects that are being used.
///
diff --git a/TShockAPI/Net/BaseMsg.cs b/TShockAPI/Net/BaseMsg.cs
index 8e0ce232..5db8814e 100644
--- a/TShockAPI/Net/BaseMsg.cs
+++ b/TShockAPI/Net/BaseMsg.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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.IO;
using System.IO.Streams;
diff --git a/TShockAPI/Net/DisconnectMsg.cs b/TShockAPI/Net/DisconnectMsg.cs
index d62775a4..eaaa6df8 100644
--- a/TShockAPI/Net/DisconnectMsg.cs
+++ b/TShockAPI/Net/DisconnectMsg.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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.IO;
using System.IO.Streams;
using System.Text;
diff --git a/TShockAPI/Net/NetTile.cs b/TShockAPI/Net/NetTile.cs
index 78d53be7..e3cc30f4 100644
--- a/TShockAPI/Net/NetTile.cs
+++ b/TShockAPI/Net/NetTile.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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.IO;
using System.IO.Streams;
diff --git a/TShockAPI/Net/ProjectileRemoveMsg.cs b/TShockAPI/Net/ProjectileRemoveMsg.cs
index bc119dcc..d8775313 100644
--- a/TShockAPI/Net/ProjectileRemoveMsg.cs
+++ b/TShockAPI/Net/ProjectileRemoveMsg.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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.IO;
using System.IO.Streams;
diff --git a/TShockAPI/Net/SpawnMsg.cs b/TShockAPI/Net/SpawnMsg.cs
index 36258396..849e5627 100644
--- a/TShockAPI/Net/SpawnMsg.cs
+++ b/TShockAPI/Net/SpawnMsg.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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.IO;
using System.IO.Streams;
diff --git a/TShockAPI/Net/WorldInfoMsg.cs b/TShockAPI/Net/WorldInfoMsg.cs
index 7b64f2a8..d95a8bf9 100644
--- a/TShockAPI/Net/WorldInfoMsg.cs
+++ b/TShockAPI/Net/WorldInfoMsg.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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.IO;
using System.IO.Streams;
diff --git a/TShockAPI/PacketBufferer.cs b/TShockAPI/PacketBufferer.cs
index e2ea8e94..d18ac47f 100644
--- a/TShockAPI/PacketBufferer.cs
+++ b/TShockAPI/PacketBufferer.cs
@@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
-Copyright (C) 2011-2012 The TShock Team
+Copyright (C) 2011-2013 Nyx Studios (fka. 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
@@ -15,6 +15,7 @@ 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.ComponentModel;
@@ -121,24 +122,24 @@ namespace TShockAPI
public bool Flush(ServerSock socket)
{
- try
- {
- if (socket == null || !socket.active)
- return false;
+ try
+ {
+ if (socket == null || !socket.active)
+ return false;
- if (buffers[socket.whoAmI].Count < 1)
- return false;
+ if (buffers[socket.whoAmI].Count < 1)
+ return false;
- byte[] buff = buffers[socket.whoAmI].GetBytes(BytesPerUpdate);
- if (buff == null)
- return false;
+ byte[] buff = buffers[socket.whoAmI].GetBytes(BytesPerUpdate);
+ if (buff == null)
+ return false;
- if (SendBytes(socket, buff))
- {
- buffers[socket.whoAmI].Pop(buff.Length);
- return true;
- }
- }
+ if (SendBytes(socket, buff))
+ {
+ buffers[socket.whoAmI].Pop(buff.Length);
+ return true;
+ }
+ }
catch (Exception e)
{
Log.ConsoleError(e.ToString());
@@ -200,7 +201,14 @@ namespace TShockAPI
}
catch (SocketException e)
{
- Log.Warn(e.ToString());
+ switch ((uint)e.ErrorCode)
+ {
+ case 0x80004005:
+ break;
+ default:
+ Log.Warn(e.ToString());
+ break;
+ }
}
catch (IOException e)
{
diff --git a/TShockAPI/PaginationTools.cs b/TShockAPI/PaginationTools.cs
new file mode 100644
index 00000000..e64d3e2a
--- /dev/null
+++ b/TShockAPI/PaginationTools.cs
@@ -0,0 +1,316 @@
+/*
+TShock, a server mod for Terraria
+Copyright (C) 2011-2013 Nyx Studios (fka. 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;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TShockAPI
+{
+ public static class PaginationTools
+ {
+ public delegate Tuple LineFormatterDelegate(object lineData, int lineIndex, int pageNumber);
+
+ #region [Nested: Settings Class]
+ public class Settings
+ {
+ public bool IncludeHeader { get; set; }
+
+ private string headerFormat;
+ public string HeaderFormat
+ {
+ get { return this.headerFormat; }
+ set
+ {
+ if (value == null)
+ throw new ArgumentNullException();
+
+ this.headerFormat = value;
+ }
+ }
+
+ public Color HeaderTextColor { get; set; }
+ public bool IncludeFooter { get; set; }
+
+ private string footerFormat;
+ public string FooterFormat
+ {
+ get { return this.footerFormat; }
+ set
+ {
+ if (value == null)
+ throw new ArgumentNullException();
+
+ this.footerFormat = value;
+ }
+ }
+
+ public Color FooterTextColor { get; set; }
+ public string NothingToDisplayString { get; set; }
+ public LineFormatterDelegate LineFormatter { get; set; }
+ public Color LineTextColor { get; set; }
+
+ private int maxLinesPerPage;
+
+ public int MaxLinesPerPage
+ {
+ get { return this.maxLinesPerPage; }
+ set
+ {
+ if (value <= 0)
+ throw new ArgumentException("The value has to be greater than zero.");
+
+ this.maxLinesPerPage = value;
+ }
+ }
+
+ private int pageLimit;
+
+ public int PageLimit
+ {
+ get { return this.pageLimit; }
+ set
+ {
+ if (value < 0)
+ throw new ArgumentException("The value has to be greater than or equal to zero.");
+
+ this.pageLimit = value;
+ }
+ }
+
+
+ public Settings()
+ {
+ this.IncludeHeader = true;
+ this.headerFormat = "Page {0} of {1}";
+ this.HeaderTextColor = Color.Green;
+ this.IncludeFooter = true;
+ this.footerFormat = "Type / {0} for more.";
+ this.FooterTextColor = Color.Yellow;
+ this.NothingToDisplayString = null;
+ this.LineFormatter = null;
+ this.LineTextColor = Color.White;
+ this.maxLinesPerPage = 4;
+ this.pageLimit = 0;
+ }
+ }
+ #endregion
+
+ public static void SendPage(
+ TSPlayer player, int pageNumber, IEnumerable dataToPaginate, int dataToPaginateCount, Settings settings = null)
+ {
+ if (settings == null)
+ settings = new Settings();
+
+ if (dataToPaginateCount == 0)
+ {
+ if (settings.NothingToDisplayString != null)
+ {
+ if (player is TSServerPlayer)
+ {
+ player.SendSuccessMessage(settings.NothingToDisplayString);
+ }
+ else
+ {
+ player.SendMessage(settings.NothingToDisplayString, settings.HeaderTextColor);
+ }
+ }
+ return;
+ }
+
+ int pageCount = ((dataToPaginateCount - 1) / settings.MaxLinesPerPage) + 1;
+ if (settings.PageLimit > 0 && pageCount > settings.PageLimit)
+ pageCount = settings.PageLimit;
+ if (pageNumber > pageCount)
+ pageNumber = pageCount;
+
+ if (settings.IncludeHeader)
+ {
+ if (player is TSServerPlayer)
+ {
+ player.SendSuccessMessage(string.Format(settings.HeaderFormat, pageNumber, pageCount));
+ }
+ else
+ {
+ player.SendMessage(string.Format(settings.HeaderFormat, pageNumber, pageCount), settings.HeaderTextColor);
+ }
+ }
+
+ int listOffset = (pageNumber - 1) * settings.MaxLinesPerPage;
+ int offsetCounter = 0;
+ int lineCounter = 0;
+ foreach (object lineData in dataToPaginate)
+ {
+ if (lineData == null)
+ continue;
+ if (offsetCounter++ < listOffset)
+ continue;
+ if (lineCounter++ == settings.MaxLinesPerPage)
+ break;
+
+ string lineMessage;
+ Color lineColor = settings.LineTextColor;
+ if (lineData is Tuple)
+ {
+ var lineFormat = (Tuple)lineData;
+ lineMessage = lineFormat.Item1;
+ lineColor = lineFormat.Item2;
+ }
+ else if (settings.LineFormatter != null)
+ {
+ try
+ {
+ Tuple lineFormat = settings.LineFormatter(lineData, offsetCounter, pageNumber);
+ if (lineFormat == null)
+ continue;
+
+ lineMessage = lineFormat.Item1;
+ lineColor = lineFormat.Item2;
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidOperationException(
+ "The method referenced by LineFormatter has thrown an exception. See inner exception for details.", ex);
+ }
+ }
+ else
+ {
+ lineMessage = lineData.ToString();
+ }
+
+ if (lineMessage != null)
+ {
+ if (player is TSServerPlayer)
+ {
+ Console.WriteLine(lineMessage);
+ }
+ else
+ {
+ player.SendMessage(lineMessage, lineColor);
+ }
+ }
+ }
+
+ if (lineCounter == 0)
+ {
+ if (settings.NothingToDisplayString != null)
+ {
+ if (player is TSServerPlayer)
+ {
+ player.SendSuccessMessage(settings.NothingToDisplayString);
+ }
+ else
+ {
+ player.SendMessage(settings.NothingToDisplayString, settings.HeaderTextColor);
+ }
+ }
+ }
+ else if (settings.IncludeFooter && pageNumber + 1 <= pageCount)
+ {
+ if (player is TSServerPlayer)
+ {
+ player.SendInfoMessage(string.Format(settings.FooterFormat, pageNumber + 1, pageNumber, pageCount));
+ }
+ else
+ {
+ player.SendMessage(string.Format(settings.FooterFormat, pageNumber + 1, pageNumber, pageCount), settings.FooterTextColor);
+ }
+ }
+ }
+
+ public static void SendPage(TSPlayer player, int pageNumber, IList dataToPaginate, Settings settings = null)
+ {
+ PaginationTools.SendPage(player, pageNumber, dataToPaginate, dataToPaginate.Count, settings);
+ }
+
+ public static List BuildLinesFromTerms(
+ IEnumerable terms, Func