Merge pull request #490 from NyxStudios/general-devel

Feature Freeze! Fixes will be applied here until 12:00AM on Friday. After that, new changes will be held to Tuesday.
This commit is contained in:
Lucas Nicodemus 2013-09-26 01:18:43 -07:00
commit 49dec8c05c
65 changed files with 3514 additions and 1658 deletions

9
.editorconfig Normal file
View file

@ -0,0 +1,9 @@
root = true
[*]
end_of_line = crlf
insert_final_newline = true
[*.cs]
indent_style = tab
trim_trailing_whitespace = true

5
.gitattributes vendored Normal file
View file

@ -0,0 +1,5 @@
* text=auto
*.cs text eol=crlf
*.sln text eol=crlf
*.csproj text eol=crlf
*.vsmdi text eol=crlf

9
.travis.yml Normal file
View file

@ -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=

36
CONTRIBUTING Normal file
View file

@ -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.

View file

@ -1,4 +1,4 @@
# TShock
# TShock [![Build Status](https://travis-ci.org/NyxStudios/TShock.png?branch=general-devel)](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)

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.IO;
using System.Threading;

File diff suppressed because it is too large Load diff

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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<string, SecureRest.TokenData> ApplicationRestTokens = new Dictionary<string, SecureRest.TokenData>();
[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;
/// <summary>
/// Reads a configuration file from a given path
/// </summary>

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("Reason"));
return new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration"));
}
}
catch (Exception ex)
@ -77,7 +80,7 @@ namespace TShockAPI.DB
{
while (reader.Read())
{
banlist.Add(new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("Reason")));
banlist.Add(new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("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<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("Reason"));
return new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("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 = "";
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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
/// <param name="chatcolor">chatcolor</param>
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<Group> groupChain = new List<Group> { 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<Group>();
tempgroups.Add(new SuperAdminGroup());
if (groups == null || groups.Count < 2)
{
groups.Clear();
groups.AddRange(tempgroups);
}
try
{
var groupsparents = new List<Tuple<Group, string>>();
List<Group> newGroups = new List<Group>(groups.Count);
Dictionary<string,string> newGroupParents = new Dictionary<string, string>(groups.Count);
using (var reader = database.QueryReader("SELECT * FROM GroupList"))
{
while (reader.Read())
{
var group = new Group(reader.Get<String>("GroupName"), null, reader.Get<String>("ChatColor"), reader.Get<String>("Commands"));
group.Prefix = reader.Get<String>("Prefix");
group.Suffix = reader.Get<String>("Suffix");
groupsparents.Add(Tuple.Create(group, reader.Get<string>("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<string>("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<string>("ChatColor"), reader.Get<string>("Commands")) {
Prefix = reader.Get<string>("Prefix"),
Suffix = reader.Get<string>("Suffix"),
});
try
{
newGroupParents.Add(groupName, reader.Get<string>("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<Group> groupChain = new List<Group> { 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);
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Linq;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Data;
using MySql.Data.MySqlClient;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using MySql.Data.MySqlClient;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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<string> InAreaRegionName(int x, int y)
{
List<string> regions = new List<string>() { };
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<Region> InAreaRegion(int x, int y)
{
List<Region> regions = new List<Region>() { };
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<string>("UserIds");
mergedIDs = reader.Get<string>("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<string>("Groups");
mergedGroups = reader.Get<string>("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
{

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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)
{

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using MySql.Data.MySqlClient;
namespace TShockAPI.DB

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Data;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System.Collections.Generic;
using System.Data;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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
}
}
/// <summary>
/// Sets the Hashed Password for a given username
/// </summary>
@ -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;
}
/// <summary>
/// Returns a Group for a ip from the database
/// </summary>
/// <param name="ply">string ip</param>
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<string>("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<string>("IP")) == ip)
{
return TShock.Utils.GetGroup(reader.Get<string>("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<User> GetUsers()
@ -328,7 +268,8 @@ namespace TShockAPI.DB
user.Group = result.Get<string>("Usergroup");
user.Password = result.Get<string>("Password");
user.Name = result.Get<string>("Username");
user.Address = result.Get<string>("IP");
user.LastAccessed = result.Get<string>("LastAccessed");
user.KnownIps = result.Get<string>("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 = "";
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Data;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Data;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Text;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
namespace TShockAPI

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.IO;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
/* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.IO;
using System.Net;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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.
/// </summary>
public bool Fail { get; set; }
/// <summary>
/// Used when a tile is placed to denote a subtype of tile. (e.g. for tile id 21: Chest = 0, Gold Chest = 1)
/// </summary>
public byte Style { get; set; }
}
/// <summary>
/// TileEdit - called when a tile is placed or destroyed
/// </summary>
public static HandlerList<TileEditEventArgs> 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<string> outputRegions = new List<string>();
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;
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Linq;
using System.Collections.Generic;
@ -28,7 +29,7 @@ namespace TShockAPI
/// <summary>
/// Default chat color.
/// </summary>
public const string defaultChatColor = "255.255.255";
public const string defaultChatColor = "255,255,255";
/// <summary>
/// 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);
}
/// <summary>
/// Assigns all fields of this instance to another.
/// </summary>
/// <param name="otherGroup">The other instance.</param>
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;
}
}
/// <summary>
@ -298,4 +320,4 @@ namespace TShockAPI
return true;
}
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace TShockAPI
{

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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));
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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<string> 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<string> 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;
}
}
}

View file

@ -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);
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System.IO;
namespace TShockAPI

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Diagnostics;
using System.Globalization;
@ -72,6 +73,16 @@ namespace TShockAPI
Write(message, LogLevel.Data);
}
/// <summary>
/// Writes data to the log file.
/// </summary>
/// <param name="format">The format of the message to be written.</param>
/// <param name="args">The format arguments.</param>
public static void Data(String format, params String[] args)
{
Data(String.Format(format, args));
}
/// <summary>
/// Writes an error to the log file.
/// </summary>
@ -81,6 +92,16 @@ namespace TShockAPI
Write(message, LogLevel.Error);
}
/// <summary>
/// Writes an error to the log file.
/// </summary>
/// <param name="format">The format of the message to be written.</param>
/// <param name="args">The format arguments.</param>
public static void Error(String format, params String[] args)
{
Error(String.Format(format, args));
}
/// <summary>
/// Writes an error to the log file.
/// </summary>
@ -93,6 +114,16 @@ namespace TShockAPI
Write(message, LogLevel.Error);
}
/// <summary>
/// Writes an error to the log file.
/// </summary>
/// <param name="format">The format of the message to be written.</param>
/// <param name="args">The format arguments.</param>
public static void ConsoleError(String format, params String[] args)
{
ConsoleError(String.Format(format, args));
}
/// <summary>
/// Writes a warning to the log file.
/// </summary>
@ -102,6 +133,16 @@ namespace TShockAPI
Write(message, LogLevel.Warning);
}
/// <summary>
/// Writes a warning to the log file.
/// </summary>
/// <param name="format">The format of the message to be written.</param>
/// <param name="args">The format arguments.</param>
public static void Warn(String format, params String[] args)
{
Warn(String.Format(format, args));
}
/// <summary>
/// Writes an informative string to the log file.
/// </summary>
@ -111,6 +152,16 @@ namespace TShockAPI
Write(message, LogLevel.Info);
}
/// <summary>
/// Writes an informative string to the log file.
/// </summary>
/// <param name="format">The format of the message to be written.</param>
/// <param name="args">The format arguments.</param>
public static void Info(String format, params String[] args)
{
Info(String.Format(format, args));
}
/// <summary>
/// Writes an informative string to the log file. Also outputs to the console.
/// </summary>
@ -123,6 +174,16 @@ namespace TShockAPI
Write(message, LogLevel.Info);
}
/// <summary>
/// Writes an informative string to the log file. Also outputs to the console.
/// </summary>
/// <param name="format">The format of the message to be written.</param>
/// <param name="args">The format arguments.</param>
public static void ConsoleInfo(String format, params String[] args)
{
ConsoleInfo(String.Format(format, args));
}
/// <summary>
/// Writes a debug string to the log file.
/// </summary>
@ -132,6 +193,16 @@ namespace TShockAPI
Write(message, LogLevel.Debug);
}
/// <summary>
/// Writes a debug string to the log file.
/// </summary>
/// <param name="format">The format of the message to be written.</param>
/// <param name="args">The format arguments.</param>
public static void Debug(String format, params String[] args)
{
Debug(String.Format(format, args));
}
/// <summary>
/// Disposes objects that are being used.
/// </summary>

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.IO;
using System.IO.Streams;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System.IO;
using System.IO.Streams;
using System.Text;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.IO;
using System.IO.Streams;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System.IO;
using System.IO.Streams;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System.IO;
using System.IO.Streams;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.IO;
using System.IO.Streams;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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)
{

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace TShockAPI
{
public static class PaginationTools
{
public delegate Tuple<string, Color> 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 /<command> {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<string, Color>)
{
var lineFormat = (Tuple<string, Color>)lineData;
lineMessage = lineFormat.Item1;
lineColor = lineFormat.Item2;
}
else if (settings.LineFormatter != null)
{
try
{
Tuple<string, Color> 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<string> BuildLinesFromTerms(
IEnumerable terms, Func<object, string> termFormatter = null, string separator = ", ", int maxCharsPerLine = 80)
{
List<string> lines = new List<string>();
StringBuilder lineBuilder = new StringBuilder();
foreach (object term in terms)
{
if (term == null && termFormatter == null)
continue;
string termString;
if (termFormatter != null)
{
try
{
termString = termFormatter(term);
if (termString == null)
continue;
}
catch (Exception ex)
{
throw new ArgumentException(
"The method represented by termFormatter has thrown an exception. See inner exception for details.", ex);
}
}
else
{
termString = term.ToString();
}
bool goesOnNextLine = (lineBuilder.Length + termString.Length > maxCharsPerLine);
if (!goesOnNextLine)
{
if (lineBuilder.Length > 0)
lineBuilder.Append(separator);
lineBuilder.Append(termString);
}
else
{
// A separator should always be at the end of a line as we know it is followed by another line.
lineBuilder.Append(separator);
lines.Add(lineBuilder.ToString());
lineBuilder.Clear();
lineBuilder.Append(termString);
}
}
if (lineBuilder.Length > 0)
lines.Add(lineBuilder.ToString());
return lines;
}
public static bool TryParsePageNumber(
List<string> commandParameters, int expectedParamterIndex, TSPlayer errorMessageReceiver, out int pageNumber)
{
pageNumber = 1;
if (commandParameters.Count <= expectedParamterIndex)
return true;
string pageNumberRaw = commandParameters[expectedParamterIndex];
if (!int.TryParse(pageNumberRaw, out pageNumber) || pageNumber < 1)
{
if (errorMessageReceiver != null)
errorMessageReceiver.SendErrorMessage(string.Format("\"{0}\" is not a valid page number.", pageNumberRaw));
pageNumber = 1;
return false;
}
return true;
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
@ -26,161 +27,258 @@ namespace TShockAPI
{
public static class Permissions
{
//Permissions with blank descriptions basically means its described by the commands it gives access to.
// tshock.account nodes
[Description("Allows player to get user info")] public static readonly string userinfo;
[Description("User can register account in game")]
public static readonly string canregister = "tshock.account.register";
[Description("")] public static readonly string causeevents;
[Description("User can login in game")]
public static readonly string canlogin = "tshock.account.login";
[Description("Required to be able to build (modify tiles and liquid)")] public static readonly string canbuild;
[Description("User can change password in game")]
public static readonly string canchangepassword = "tshock.account.changepassword";
[Description("")] public static readonly string kill;
// tshock.admin nodes
[Description("Allows you to use banned items")] public static readonly string usebanneditem;
[Description("Prevents you from being kicked.")]
public static readonly string immunetokick = "tshock.admin.nokick";
[Description("Allows you to edit the spawn")] public static readonly string editspawn;
[Description("Prevents you from being banned.")]
public static readonly string immunetoban = "tshock.admin.noban";
[Description("Prevents you from being kicked")] public static readonly string immunetokick;
[Description("Specific log messages are sent to users with this permission.")]
public static readonly string logs = "tshock.admin.viewlogs";
[Description("Prevents you from being banned")] public static readonly string immunetoban;
[Description("User can kick others.")]
public static readonly string kick = "tshock.admin.kick";
[Description("Prevents you from being reverted by kill tile abuse detection")] public static readonly string
ignorekilltiledetection;
[Description("User can ban others.")]
public static readonly string ban = "tshock.admin.ban";
[Description("Prevents you from being reverted by place tile abuse detection")] public static readonly string
ignoreplacetiledetection;
[Description("User can manage warps.")]
public static readonly string managewarp = "tshock.admin.warp";
[Description("Prevents you from being disabled by liquid set abuse detection")] public static readonly string
ignoreliquidsetdetection;
[Description("User can manage item bans.")]
public static readonly string manageitem = "tshock.admin.itemban";
[Description("Prevents you from being disabled by liquid set abuse detection")] public static readonly string
ignoreprojectiledetection;
[Description("User can manage groups.")]
public static readonly string managegroup = "tshock.admin.group";
[Description("Prevents you from being reverted by no clip detection")] public static readonly string
ignorenoclipdetection;
[Description("User can manage regions.")]
public static readonly string manageregion = "tshock.admin.region";
[Description("Prevents you from being disabled by stack hack detection")] public static readonly string
ignorestackhackdetection;
[Description("User can mute and unmute users")]
public static readonly string mute = "tshock.admin.mute";
[Description("Prevents you from being kicked by hacked health detection")] public static readonly string
ignorestathackdetection;
[Description("User can see the id of players with /who")]
public static readonly string seeids = "tshock.admin.seeplayerids";
[Description("Prevents your actions from being ignored if damage is too high")] public static readonly string
ignoredamagecap;
[Description("User can save all the players SSI state.")]
public static readonly string savessi = "tshock.admin.savessi";
[Description("Specific log messages are sent to users with this permission")] public static readonly string logs;
[Description("User can elevate other users' groups temporarily.")]
public static readonly string settempgroup = "tshock.admin.tempgroup";
[Description("Allows you to bypass the max slots for up to 5 slots above your max")] public static readonly string
reservedslot;
[Description("User can broadcast messages.")]
public static readonly string broadcast = "tshock.admin.broadcast";
[Description("User is notified when an update is available")] public static readonly string maintenance;
[Description("User can get other users' info.")]
public static readonly string userinfo = "tshock.admin.userinfo";
[Description("User can kick others")] public static readonly string kick;
// tshock.buff nodes
[Description("User can ban others")] public static readonly string ban;
[Description("User can buff self.")]
public static readonly string buff = "tshock.buff.self";
[Description("User can modify the whitelist")] public static readonly string whitelist;
[Description("User can buff other players.")]
public static readonly string buffplayer = "tshock.buff.others";
[Description("User can spawn bosses")] public static readonly string spawnboss;
// tshock.cfg nodes
[Description("User can spawn npcs")] public static readonly string spawnmob;
[Description("User is notified when an update is available, user can turn off / restart the server.")]
public static readonly string maintenance = "tshock.cfg.maintenance";
[Description("User can teleport")] public static readonly string tp;
[Description("User can modify the whitelist.")]
public static readonly string whitelist = "tshock.cfg.whitelist";
[Description("User can teleport people to them")] public static readonly string tphere;
[Description("User can edit the server password.")]
public static readonly string cfgpassword = "tshock.cfg.password";
[Description("User can use warps")] public static readonly string warp;
[Description("User can reload the configurations file.")]
public static readonly string cfgreload = "tshock.cfg.reload";
[Description("User can manage warps")] public static readonly string managewarp;
[Description("User can edit the max spawns.")]
public static readonly string cfgmaxspawns = "tshock.cfg.maxspawns";
[Description("User can manage item bans")] public static readonly string manageitem;
[Description("User can edit the spawnrate.")]
public static readonly string cfgspawnrate = "tshock.cfg.spawnrate";
[Description("User can manage groups")] public static readonly string managegroup;
[Description("User can download updates to plugins that are currently running.")]
public static readonly string updateplugins = "tshock.cfg.updateplugins";
[Description("User can edit sevrer configurations")] public static readonly string cfg;
// tshock.ignore nodes
[Description("")] public static readonly string time;
[Description("Prevents you from being reverted by kill tile abuse detection.")]
public static readonly string ignorekilltiledetection = "tshock.ignore.removetile";
[Description("")] public static readonly string pvpfun;
[Description("Prevents you from being reverted by place tile abuse detection.")]
public static readonly string ignoreplacetiledetection = "tshock.ignore.placetile";
[Description("User can edit regions")] public static readonly string manageregion;
[Description("Prevents you from being disabled by liquid set abuse detection.")]
public static readonly string ignoreliquidsetdetection = "tshock.ignore.liquid";
[Description("Meant for super admins only")] public static readonly string rootonly;
[Description("Prevents you from being disabled by projectile abuse detection.")]
public static readonly string ignoreprojectiledetection = "tshock.ignore.projectile";
[Description("User can whisper to others")] public static readonly string whisper;
[Description("Prevents you from being reverted by no clip detection.")]
public static readonly string ignorenoclipdetection = "tshock.ignore.noclip";
[Description("")] public static readonly string annoy;
[Description("Prevents you from being disabled by stack hack detection.")]
public static readonly string ignorestackhackdetection = "tshock.ignore.itemstack";
[Description("User can kill all enemy npcs")] public static readonly string butcher;
[Description("Prevents you from being kicked by hacked health detection.")]
public static readonly string ignorestathackdetection = "tshock.ignore.stats";
[Description("User can spawn items")] public static readonly string item;
[Description("Prevents your actions from being ignored if damage is too high.")]
public static readonly string ignoredamagecap = "tshock.ignore.damage";
[Description("User can clear item drops.")] public static readonly string clearitems;
[Description("Bypass server side inventory checks")]
public static readonly string bypassinventorychecks = "tshock.ignore.ssi";
[Description("")] public static readonly string heal;
[Description("Allow unrestricted SendTileSquare usage, for client side world editing.")]
public static readonly string allowclientsideworldedit = "tshock.ignore.sendtilesquare";
[Description("User can buff self")] public static readonly string buff;
// tshock.item nodes
[Description("User can buff other players")] public static readonly string buffplayer;
[Description("User can spawn items.")]
public static readonly string item = "tshock.item.spawn";
[Description("")] public static readonly string grow;
[Description("User can clear items.")]
public static readonly string clearitems = "tshock.item.clear";
[Description("User can change hardmode state.")] public static readonly string hardmode;
[Description("Allows you to use banned items.")]
public static readonly string usebanneditem = "tshock.item.usebanned";
[Description("User can change the homes of NPCs.")] public static readonly string movenpc;
// tshock.npc nodes
[Description("Users can stop people from teleporting to them")] public static readonly string tpallow;
[Description("User can spawn bosses.")]
public static readonly string spawnboss = "tshock.npc.spawnboss";
[Description("Users can tp to anyone")] public static readonly string tpall;
[Description("User can spawn npcs.")]
public static readonly string spawnmob = "tshock.npc.spawnmob";
[Description("Users can tp to people without showing a notice")] public static readonly string tphide;
[Description("User can kill all enemy npcs.")]
public static readonly string butcher = "tshock.npc.butcher";
[Description("User can convert hallow into corruption and vice-versa")] public static readonly string converthardmode;
[Description("User can summon bosses using items")]
public static readonly string summonboss = "tshock.npc.summonboss";
[Description("User can mute and unmute users")] public static readonly string mute;
[Description("User can start invasions (Goblin/Snow Legion) using items")]
public static readonly string startinvasion = "tshock.npc.startinvasion";
[Description("User can register account in game")] public static readonly string canregister;
// tshock.superadmin nodes
[Description("User can login in game")] public static readonly string canlogin;
[Description("Meant for super admins only.")]
public static readonly string authverify = "tshock.superadmin.authverify";
[Description("User can change password in game")] public static readonly string canchangepassword;
[Description("Meant for super admins only.")]
public static readonly string user = "tshock.superadmin.user";
[Description("User can use party chat in game")] public static readonly string canpartychat;
// tshock.tp nodes
[Description("User can talk in third person")] public static readonly string cantalkinthird;
[Description("User can teleport to others.")]
public static readonly string tp = "tshock.tp.self";
[Description("Bypass server side inventory checks")] public static readonly string bypassinventorychecks;
[Description("User can teleport people to them.")]
public static readonly string tphere = "tshock.tp.others";
[Description("Allow unrestricted SendTileSquare usage, for client side world editing.")] public static readonly
string allowclientsideworldedit;
[Description("Users can stop people from teleporting to them")]
public static readonly string tpallow = "tshock.tp.block";
[Description("User can summon bosses using items")]
public static readonly string summonboss;
[Description("Users can tp to anyone")]
public static readonly string tpall = "tshock.tp.toall";
[Description("User can start invasions (Goblin/Snow Legion) using items")]
public static readonly string startinvasion;
[Description("Users can tp to people without showing a notice")]
public static readonly string tphide = "tshock.tp.silent";
[Description("User can see the id of players with /who")]
public static readonly string seeids;
[Description("User can use /home.")]
public static readonly string home = "tshock.tp.home";
[Description("User can save all the players SSI state.")]
public static readonly string savessi;
[Description("User can use /spawn.")]
public static readonly string spawn = "tshock.tp.spawn";
[Description("User can use rest api calls.")]
public static readonly string restapi;
// tshock.world nodes
[Description("User can force the server to Christmas mode.")] public static readonly string xmas;
[Description("Allows you to edit the spawn.")]
public static readonly string editspawn = "tshock.world.editspawn";
static Permissions()
{
foreach (var field in typeof (Permissions).GetFields())
{
field.SetValue(null, field.Name);
}
[Description("User can set the time.")]
public static readonly string time = "tshock.world.settime";
//Backwards compatability.
restapi = "api";
}
[Description("User can grow plants.")]
public static readonly string grow = "tshock.world.grow";
[Description("User can change hardmode state.")]
public static readonly string hardmode = "tshock.world.hardmode";
[Description("User can change the homes of NPCs.")]
public static readonly string movenpc = "tshock.world.movenpc";
[Description("User can convert hallow into corruption and vice-versa")]
public static readonly string converthardmode = "tshock.world.converthardmode";
[Description("User can force the server to Christmas mode.")]
public static readonly string xmas = "tshock.world.setxmas";
[Description("User can save the world.")]
public static readonly string worldsave = "tshock.world.save";
[Description("User can settle liquids.")]
public static readonly string worldsettle = "tshock.world.settleliquids";
[Description("User can get the world info.")]
public static readonly string worldinfo = "tshock.world.info";
[Description("User can set the world spawn.")]
public static readonly string worldspawn = "tshock.world.setspawn";
[Description("User can cause some events.")]
public static readonly string causeevents = "tshock.world.causeevents";
[Description("User can modify the world.")]
public static readonly string canbuild = "tshock.world.modify";
// Non-grouped
[Description("User can kill others.")]
public static readonly string kill = "tshock.kill";
[Description("Allows you to bypass the max slots for up to 5 slots above your max.")]
public static readonly string reservedslot = "tshock.reservedslot";
[Description("User can use warps.")]
public static readonly string warp = "tshock.warp";
[Description("User can slap others.")]
public static readonly string slap = "tshock.slap";
[Description("User can whisper to others.")]
public static readonly string whisper = "tshock.whisper";
[Description("User can annoy others.")]
public static readonly string annoy = "tshock.annoy";
[Description("User can heal players.")]
public static readonly string heal = "tshock.heal";
[Description("User can use party chat in game")]
public static readonly string canpartychat = "tshock.partychat";
[Description("User can talk in third person")]
public static readonly string cantalkinthird = "tshock.thirdperson";
[Description("User can get the server info.")]
public static readonly string serverinfo = "tshock.info";
/// <summary>
/// Lists all commands associated with a given permission
@ -191,7 +289,7 @@ namespace TShockAPI
{
if (Commands.ChatCommands.Count < 1)
Commands.InitCommands();
return Commands.ChatCommands.Where(c => c.Permission == perm).ToList();
return Commands.ChatCommands.Where(c => c.Permissions.Contains(perm)).ToList();
}
/// <summary>

View file

@ -0,0 +1,87 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.IO;
using System.Threading;
using Terraria;
namespace TShockAPI.PluginUpdater
{
class PluginUpdaterThread
{
private TSPlayer invoker;
public PluginUpdaterThread(TSPlayer player)
{
invoker = player;
PluginVersionCheck.PluginUpdate += PluginUpdate;
HandleUpdate();
}
private void HandleUpdate()
{
foreach(PluginContainer cont in ProgramServer.Plugins)
new Thread(PluginVersionCheck.CheckPlugin).Start(cont.Plugin);
}
private int Updates = 0;
private void PluginUpdate(UpdateArgs args)
{
Updates++;
if(args.Success && String.IsNullOrEmpty(args.Error))
{
invoker.SendSuccessMessage(String.Format("{0} was downloaded successfully.", args.Plugin.Name));
}
else if(args.Success)
{
invoker.SendSuccessMessage(String.Format("{0} was skipped. Reason: {1}", args.Plugin.Name, args.Error));
}
else
{
invoker.SendSuccessMessage(String.Format("{0} failed to downloaded. Error: {1}", args.Plugin.Name, args.Error));
}
if(Updates >= Terraria.ProgramServer.Plugins.Count)
{
PluginVersionCheck.PluginUpdate -= PluginUpdate;
invoker.SendSuccessMessage("All plugins have been downloaded. Now copying them to the plugin folder...");
string folder = Path.Combine(TShock.SavePath, "UpdatedPlugins");
string dest = Path.Combine(TShock.SavePath, "..", "ServerPlugins");
foreach (string dir in Directory.GetDirectories(folder, "*", System.IO.SearchOption.AllDirectories))
{
string new_folder = dest + dir.Substring(folder.Length);
if (!Directory.Exists(new_folder))
Directory.CreateDirectory(new_folder);
}
foreach (string file_name in Directory.GetFiles(folder, "*.*", System.IO.SearchOption.AllDirectories))
{
TSPlayer.Server.SendSuccessMessage(String.Format("Copied {0}", file_name));
File.Copy(file_name, dest + file_name.Substring(folder.Length), true);
}
Directory.Delete(folder, true);
invoker.SendSuccessMessage("All plugins have been processed. Restart the server to have access to the new plugins.");
}
}
}
}

View file

@ -0,0 +1,127 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using JsonLoader;
using Newtonsoft.Json;
namespace TShockAPI.PluginUpdater
{
public class PluginVersionCheck
{
public delegate void PluginUpdateD(UpdateArgs e);
public static event PluginUpdateD PluginUpdate;
public static void OnPluginUpdate(UpdateArgs args)
{
if (PluginUpdate == null)
{
return;
}
PluginUpdate(args);
}
public static void CheckPlugin(object p)
{
TerrariaPlugin plugin = (TerrariaPlugin)p;
UpdateArgs args = new UpdateArgs {Plugin = plugin, Success = true, Error = ""};
List<string> files = new List<string>();
try
{
if (!String.IsNullOrEmpty(plugin.UpdateURL))
{
var request = HttpWebRequest.Create(plugin.UpdateURL);
VersionInfo vi;
request.Timeout = 5000;
using (var response = request.GetResponse())
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
vi = JsonConvert.DeserializeObject<VersionInfo>(reader.ReadToEnd());
}
}
System.Version v = System.Version.Parse((vi.version.ToString()));
if (!v.Equals(plugin.Version))
{
DownloadPackage pkg;
request = HttpWebRequest.Create(vi.url);
request.Timeout = 5000;
using (var response = request.GetResponse())
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
pkg = JsonConvert.DeserializeObject<DownloadPackage>(reader.ReadToEnd());
}
}
foreach (PluginFile f in pkg.files)
{
using (WebClient Client = new WebClient())
{
string dir = Path.Combine(TShock.SavePath, "UpdatedPlugins");
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
Client.DownloadFile(f.url,
Path.Combine(dir, f.destination));
files.Add(Path.Combine(dir, f.destination));
}
}
}
else
{
args.Error = "Plugin is up to date.";
}
}
else
{
args.Error = "Plugin has no updater recorded.";
}
}
catch(Exception e)
{
args.Success = false;
args.Error = e.Message;
if(files.Count > 0)
{
foreach(string s in files)
{
File.Delete(s);
}
}
}
OnPluginUpdate(args);
}
}
public class UpdateArgs
{
public TerrariaPlugin Plugin { get; set; }
public bool Success { get; set; }
public string Error { get; set; }
}
}

View file

@ -0,0 +1,87 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
namespace JsonLoader
{
class VersionInfo
{
public Version version;
public string url;
}
public class Version
{
public int Major;
public int Minor;
public int Build;
public int Revision;
public int MajorRevision;
public int MinorRevision;
public Version()
{
SetVersion(0,0,0,0);
}
public Version(int m)
{
SetVersion(m, 0, 0, 0);
}
public Version(int ma, int mi)
{
SetVersion(ma, mi, 0, 0);
}
public Version(int ma, int mi, int b)
{
SetVersion(ma, mi, b, 0);
}
public Version(int ma, int mi, int b, int r)
{
SetVersion(ma, mi, b, r);
}
private void SetVersion(int ma, int mi, int b, int r)
{
Major = ma;
Minor = mi;
Build = b;
Revision = r;
}
public string ToString()
{
return String.Format("{0}.{1}.{2}.{3}", Major, Minor, Build, Revision);
}
}
class DownloadPackage
{
public List<PluginFile> files;
}
class PluginFile
{
public string url;
public string destination = "";
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System.Reflection;
using System.Runtime.InteropServices;
@ -25,9 +26,9 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTitle("TShock for Terraria")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Nyx Team")]
[assembly: AssemblyCompany("Nyx Studios & TShock Contributors")]
[assembly: AssemblyProduct("TShockAPI")]
[assembly: AssemblyCopyright("Copyright © Nyx Team 2012")]
[assembly: AssemblyCopyright("Copyright © Nyx Studios 2011-2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@ -48,5 +49,5 @@ using System.Runtime.InteropServices;
// Build Number
// MMdd of the build
[assembly: AssemblyVersion("4.0.0.0923")]
[assembly: AssemblyFileVersion("4.0.0.0923")]
[assembly: AssemblyVersion("4.1.0.0926")]
[assembly: AssemblyFileVersion("4.1.0.0926")]

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.IO;

View file

@ -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
@ -27,9 +27,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------
namespace TShockAPI {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
@ -38,6 +39,15 @@ namespace Rests
/// <returns>Response object or null to not handle request</returns>
public delegate object RestCommandD(RestVerbs verbs, IParameterCollection parameters);
/// <summary>
/// Secure Rest command delegate including token data.
/// </summary>
/// <param name="parameters">Parameters in the url</param>
/// <param name="verbs">{x} in urltemplate</param>
/// <param name="tokenData">The data of stored for the provided token.</param>
/// <returns>Response object or null to not handle request</returns>
public delegate object SecureRestCommandD(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData);
public class Rest : IDisposable
{
private readonly List<RestCommand> commands = new List<RestCommand>();
@ -170,24 +180,47 @@ namespace Rests
}
catch (Exception exception)
{
return new Dictionary<string, string>
return new RestObject("500")
{
{"status", "500"},
{"error", "Internal server error."},
{"errormsg", exception.Message},
{"stacktrace", exception.StackTrace},
};
}
return new Dictionary<string, string>
return new RestObject("404")
{
{"status", "404"},
{"error", "Specified API endpoint doesn't exist. Refer to the documentation for a list of valid endpoints."}
};
}
protected virtual object ExecuteCommand(RestCommand cmd, RestVerbs verbs, IParameterCollection parms)
{
return cmd.Callback(verbs, parms);
object result = cmd.Execute(verbs, parms);
if (cmd.DoLog)
Log.ConsoleInfo("Anonymous requested REST endpoint: " + BuildRequestUri(cmd, verbs, parms, false));
return result;
}
protected virtual string BuildRequestUri(
RestCommand cmd, RestVerbs verbs, IParameterCollection parms, bool includeToken = true
) {
StringBuilder requestBuilder = new StringBuilder(cmd.UriTemplate);
char separator = '?';
foreach (IParameter paramImpl in parms)
{
Parameter param = (paramImpl as Parameter);
if (param == null || (!includeToken && param.Name.Equals("token", StringComparison.InvariantCultureIgnoreCase)))
continue;
requestBuilder.Append(separator);
requestBuilder.Append(param.Name);
requestBuilder.Append('=');
requestBuilder.Append(param.Value);
separator = '&';
}
return requestBuilder.ToString();
}
#region Dispose

View file

@ -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,8 +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 <http://www.gnu.org/licenses/>.
*/
using System.Linq;
using System.Text.RegularExpressions;
using HttpServer;
namespace Rests
{
@ -26,8 +28,10 @@ namespace Rests
public string UriTemplate { get; protected set; }
public string UriVerbMatch { get; protected set; }
public string[] UriVerbs { get; protected set; }
public RestCommandD Callback { get; protected set; }
public bool RequiresToken { get; set; }
public virtual bool RequiresToken { get { return false; } }
public bool DoLog { get; set; }
private RestCommandD callback;
/// <summary>
///
@ -42,8 +46,8 @@ namespace Rests
UriVerbMatch = string.Format("^{0}$", string.Join("([^/]*)", Regex.Split(uritemplate, "\\{[^\\{\\}]*\\}")));
var matches = Regex.Matches(uritemplate, "\\{([^\\{\\}]*)\\}");
UriVerbs = (from Match match in matches select match.Groups[1].Value).ToArray();
Callback = callback;
RequiresToken = true;
this.callback = callback;
DoLog = true;
}
/// <summary>
@ -60,5 +64,43 @@ namespace Rests
{
get { return UriVerbs.Length > 0; }
}
public virtual object Execute(RestVerbs verbs, IParameterCollection parameters)
{
return callback(verbs, parameters);
}
}
public class SecureRestCommand: RestCommand
{
public override bool RequiresToken { get { return true; } }
public string[] Permissions { get; set; }
private SecureRestCommandD callback;
public SecureRestCommand(string name, string uritemplate, SecureRestCommandD callback, params string[] permissions)
: base(name, uritemplate, null)
{
this.callback = callback;
Permissions = permissions;
}
public SecureRestCommand(string uritemplate, SecureRestCommandD callback, params string[] permissions)
: this(string.Empty, uritemplate, callback, permissions)
{
}
public override object Execute(RestVerbs verbs, IParameterCollection parameters)
{
return new RestObject("401") { Error = "Not authorized. The specified API endpoint requires a token." };
}
public object Execute(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
if (tokenData.Equals(SecureRest.TokenData.None))
return new RestObject("401") { Error = "Not authorized. The specified API endpoint requires a token." };
return callback(verbs, parameters, tokenData);
}
}
}

View file

@ -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,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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using HttpServer;
using Rests;
@ -38,78 +40,140 @@ namespace TShockAPI
public void RegisterRestfulCommands()
{
// Server Commands
Rest.Register(new RestCommand("/v2/server/broadcast", ServerBroadcast));
Rest.Register(new RestCommand("/v2/server/off", ServerOff));
Rest.Register(new RestCommand("/v2/server/rawcmd", ServerCommand));
Rest.Register(new RestCommand("/v2/server/status", ServerStatusV2) { RequiresToken = false });
Rest.Register(new RestCommand("/tokentest", ServerTokenTest));
Rest.Register(new RestCommand("/status", ServerStatus) { RequiresToken = false });
if (TShock.Config.EnableTokenEndpointAuthentication)
{
Rest.Register(new SecureRestCommand("/v2/server/status", ServerStatusV2));
Rest.Register(new SecureRestCommand("/status", ServerStatus));
Rest.Register(new SecureRestCommand("/v3/server/motd", ServerMotd));
Rest.Register(new SecureRestCommand("/v3/server/rules", ServerRules));
}
else
{
Rest.Register(new RestCommand("/v2/server/status", (a, b) => this.ServerStatusV2(a, b, SecureRest.TokenData.None)));
Rest.Register(new RestCommand("/status", (a, b) => this.ServerStatusV2(a, b, SecureRest.TokenData.None)));
Rest.Register(new RestCommand("/v3/server/motd", (a, b) => this.ServerMotd(a, b, SecureRest.TokenData.None)));
Rest.Register(new RestCommand("/v3/server/rules", (a, b) => this.ServerRules(a, b, SecureRest.TokenData.None)));
}
Rest.Register(new SecureRestCommand("/v2/server/broadcast", ServerBroadcast));
Rest.Register(new SecureRestCommand("/v3/server/reload", ServerReload, RestPermissions.restcfg));
Rest.Register(new SecureRestCommand("/v2/server/off", ServerOff, RestPermissions.restmaintenance));
Rest.Register(new SecureRestCommand("/v3/server/restart", ServerRestart, RestPermissions.restmaintenance));
Rest.Register(new SecureRestCommand("/v2/server/rawcmd", ServerCommand, RestPermissions.restrawcommand));
Rest.Register(new SecureRestCommand("/v3/server/rawcmd", ServerCommandV3, RestPermissions.restrawcommand));
Rest.Register(new SecureRestCommand("/tokentest", ServerTokenTest));
// User Commands
Rest.Register(new RestCommand("/v2/users/activelist", UserActiveListV2));
Rest.Register(new RestCommand("/v2/users/create", UserCreateV2));
Rest.Register(new RestCommand("/v2/users/list", UserListV2));
Rest.Register(new RestCommand("/v2/users/read", UserInfoV2));
Rest.Register(new RestCommand("/v2/users/destroy", UserDestroyV2));
Rest.Register(new RestCommand("/v2/users/update", UserUpdateV2));
Rest.Register(new SecureRestCommand("/v2/users/activelist", UserActiveListV2, RestPermissions.restviewusers));
Rest.Register(new SecureRestCommand("/v2/users/create", UserCreateV2, RestPermissions.restmanageusers) { DoLog = false });
Rest.Register(new SecureRestCommand("/v2/users/list", UserListV2, RestPermissions.restviewusers));
Rest.Register(new SecureRestCommand("/v2/users/read", UserInfoV2, RestPermissions.restviewusers));
Rest.Register(new SecureRestCommand("/v2/users/destroy", UserDestroyV2, RestPermissions.restmanageusers));
Rest.Register(new SecureRestCommand("/v2/users/update", UserUpdateV2, RestPermissions.restmanageusers) { DoLog = false });
// Ban Commands
Rest.Register(new RestCommand("/bans/create", BanCreate));
Rest.Register(new RestCommand("/v2/bans/list", BanListV2));
Rest.Register(new RestCommand("/v2/bans/read", BanInfoV2));
Rest.Register(new RestCommand("/v2/bans/destroy", BanDestroyV2));
Rest.Register(new SecureRestCommand("/bans/create", BanCreate, RestPermissions.restmanagebans));
Rest.Register(new SecureRestCommand("/v2/bans/list", BanListV2, RestPermissions.restviewbans));
Rest.Register(new SecureRestCommand("/v2/bans/read", BanInfoV2, RestPermissions.restviewbans));
Rest.Register(new SecureRestCommand("/v2/bans/destroy", BanDestroyV2, RestPermissions.restmanagebans));
// World Commands
Rest.Register(new RestCommand("/world/read", WorldRead));
Rest.Register(new RestCommand("/world/meteor", WorldMeteor));
Rest.Register(new RestCommand("/world/bloodmoon/{bool}", WorldBloodmoon));
Rest.Register(new RestCommand("/v2/world/save", WorldSave));
Rest.Register(new RestCommand("/v2/world/autosave/state/{bool}", WorldChangeSaveSettings));
Rest.Register(new RestCommand("/v2/world/butcher", WorldButcher));
Rest.Register(new SecureRestCommand("/world/read", WorldRead));
Rest.Register(new SecureRestCommand("/world/meteor", WorldMeteor, RestPermissions.restcauseevents));
Rest.Register(new SecureRestCommand("/world/bloodmoon/{bool}", WorldBloodmoon, RestPermissions.restcauseevents));
Rest.Register(new SecureRestCommand("/v2/world/save", WorldSave, RestPermissions.restcfg));
Rest.Register(new SecureRestCommand("/v2/world/autosave/state/{bool}", WorldChangeSaveSettings, RestPermissions.restcfg));
Rest.Register(new SecureRestCommand("/v2/world/butcher", WorldButcher, RestPermissions.restbutcher));
// Player Commands
Rest.Register(new RestCommand("/lists/players", PlayerList));
Rest.Register(new RestCommand("/v2/players/list", PlayerListV2));
Rest.Register(new RestCommand("/v2/players/read", PlayerReadV2));
Rest.Register(new RestCommand("/v2/players/kick", PlayerKickV2));
Rest.Register(new RestCommand("/v2/players/ban", PlayerBanV2));
Rest.Register(new RestCommand("/v2/players/kill", PlayerKill));
Rest.Register(new RestCommand("/v2/players/mute", PlayerMute));
Rest.Register(new RestCommand("/v2/players/unmute", PlayerUnMute));
Rest.Register(new SecureRestCommand("/lists/players", PlayerList));
Rest.Register(new SecureRestCommand("/v2/players/list", PlayerListV2));
Rest.Register(new SecureRestCommand("/v2/players/read", PlayerReadV2, RestPermissions.restuserinfo));
Rest.Register(new SecureRestCommand("/v2/players/kick", PlayerKickV2, RestPermissions.restkick));
Rest.Register(new SecureRestCommand("/v2/players/ban", PlayerBanV2, RestPermissions.restban, RestPermissions.restmanagebans));
Rest.Register(new SecureRestCommand("/v2/players/kill", PlayerKill, RestPermissions.restkill));
Rest.Register(new SecureRestCommand("/v2/players/mute", PlayerMute, RestPermissions.restmute));
Rest.Register(new SecureRestCommand("/v2/players/unmute", PlayerUnMute, RestPermissions.restmute));
// Group Commands
Rest.Register(new RestCommand("/v2/groups/list", GroupList));
Rest.Register(new RestCommand("/v2/groups/read", GroupInfo));
Rest.Register(new RestCommand("/v2/groups/destroy", GroupDestroy));
Rest.Register(new RestCommand("/v2/groups/create", GroupCreate));
Rest.Register(new RestCommand("/v2/groups/update", GroupUpdate));
Rest.Register(new SecureRestCommand("/v2/groups/list", GroupList, RestPermissions.restviewgroups));
Rest.Register(new SecureRestCommand("/v2/groups/read", GroupInfo, RestPermissions.restviewgroups));
Rest.Register(new SecureRestCommand("/v2/groups/destroy", GroupDestroy, RestPermissions.restmanagegroups));
Rest.Register(new SecureRestCommand("/v2/groups/create", GroupCreate, RestPermissions.restmanagegroups));
Rest.Register(new SecureRestCommand("/v2/groups/update", GroupUpdate, RestPermissions.restmanagegroups));
}
#region RestServerMethods
private object ServerCommand(RestVerbs verbs, IParameterCollection parameters)
private object ServerCommand(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
if (string.IsNullOrWhiteSpace(parameters["cmd"]))
return RestMissingParam("cmd");
TSRestPlayer tr = new TSRestPlayer();
Group restPlayerGroup;
// TODO: Get rid of this when the old REST permission model is removed.
if (TShock.Config.RestUseNewPermissionModel)
restPlayerGroup = TShock.Groups.GetGroupByName(tokenData.UserGroupName);
else
restPlayerGroup = new SuperAdminGroup();
TSRestPlayer tr = new TSRestPlayer(tokenData.Username, restPlayerGroup);
Commands.HandleCommand(tr, parameters["cmd"]);
return RestResponse(string.Join("\n", tr.GetCommandOutput()));
}
private object ServerOff(RestVerbs verbs, IParameterCollection parameters)
private object ServerCommandV3(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
if (string.IsNullOrWhiteSpace(parameters["cmd"]))
return RestMissingParam("cmd");
Group restPlayerGroup;
// TODO: Get rid of this when the old REST permission model is removed.
if (TShock.Config.RestUseNewPermissionModel)
restPlayerGroup = TShock.Groups.GetGroupByName(tokenData.UserGroupName);
else
restPlayerGroup = new SuperAdminGroup();
TSRestPlayer tr = new TSRestPlayer(tokenData.Username, restPlayerGroup);
Commands.HandleCommand(tr, parameters["cmd"]);
return new RestObject()
{
{"response", tr.GetCommandOutput()}
};
}
private object ServerOff(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
if (!GetBool(parameters["confirm"], false))
return RestInvalidParam("confirm");
// Inform players the server is shutting down
var msg = string.IsNullOrWhiteSpace(parameters["message"]) ? "Server is shutting down" : parameters["message"];
TShock.Utils.StopServer(!GetBool(parameters["nosave"], false), msg);
var reason = string.IsNullOrWhiteSpace(parameters["message"]) ? "Server is shutting down" : parameters["message"];
TShock.Utils.StopServer(!GetBool(parameters["nosave"], false), reason);
return RestResponse("The server is shutting down");
}
private object ServerBroadcast(RestVerbs verbs, IParameterCollection parameters)
private object ServerRestart(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
if (!GetBool(parameters["confirm"], false))
return RestInvalidParam("confirm");
// Inform players the server is shutting down
var reason = string.IsNullOrWhiteSpace(parameters["message"]) ? "Server is restarting" : parameters["message"];
TShock.Utils.RestartServer(!GetBool(parameters["nosave"], false), reason);
return RestResponse("The server is shutting down and will attempt to restart");
}
private object ServerReload(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
TShock.Utils.Reload(new TSRestPlayer(tokenData.Username, TShock.Groups.GetGroupByName(tokenData.UserGroupName)));
return RestResponse("Configuration, permissions, and regions reload complete. Some changes may require a server restart.");
}
private object ServerBroadcast(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var msg = parameters["msg"];
if (string.IsNullOrWhiteSpace(msg))
@ -118,11 +182,32 @@ namespace TShockAPI
return RestResponse("The message was broadcasted successfully");
}
private object ServerStatus(RestVerbs verbs, IParameterCollection parameters)
private object ServerMotd(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
if (TShock.Config.EnableTokenEndpointAuthentication)
return RestError("Server settings require a token for this API call");
string motdFilePath = Path.Combine(TShock.SavePath, "motd.txt");
if (!File.Exists(motdFilePath))
return this.RestError("The motd.txt was not found.", "500");
return new RestObject()
{
{"motd", File.ReadAllLines(motdFilePath)}
};
}
private object ServerRules(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
string rulesFilePath = Path.Combine(TShock.SavePath, "rules.txt");
if (!File.Exists(rulesFilePath))
return this.RestError("The rules.txt was not found.", "500");
return new RestObject()
{
{"rules", File.ReadAllLines(rulesFilePath)}
};
}
private object ServerStatus(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var activeplayers = Main.player.Where(p => null != p && p.active).ToList();
return new RestObject()
{
@ -133,18 +218,17 @@ namespace TShockAPI
};
}
private object ServerStatusV2(RestVerbs verbs, IParameterCollection parameters)
private object ServerStatusV2(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
if (TShock.Config.EnableTokenEndpointAuthentication)
return RestError("Server settings require a token for this API call");
var ret = new RestObject()
{
{"name", TShock.Config.ServerName},
{"port", TShock.Config.ServerPort},
{"playercount", Main.player.Where(p => null != p && p.active).Count()},
{"maxplayers", TShock.Config.MaxSlots},
{"world", Main.worldName}
{"world", Main.worldName},
{"uptime", (DateTime.Now - System.Diagnostics.Process.GetCurrentProcess().StartTime).ToString(@"d'.'hh':'mm':'ss")},
{"serverpassword", !string.IsNullOrEmpty(TShock.Config.ServerPassword)}
};
if (GetBool(parameters["players"], false))
@ -174,52 +258,56 @@ namespace TShockAPI
rules.Add("PvPMode", TShock.Config.PvPMode);
rules.Add("SpawnProtection", TShock.Config.SpawnProtection);
rules.Add("SpawnProtectionRadius", TShock.Config.SpawnProtectionRadius);
rules.Add("ServerSideInventory", TShock.Config.ServerSideInventory);
ret.Add("rules", rules);
}
return ret;
}
private object ServerTokenTest(RestVerbs verbs, IParameterCollection parameters)
private object ServerTokenTest(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
return RestResponse("Token is valid and was passed through correctly");
return new RestObject()
{
{"response", "Token is valid and was passed through correctly."},
{"associateduser", tokenData.Username}
};
}
#endregion
#region RestUserMethods
private object UserActiveListV2(RestVerbs verbs, IParameterCollection parameters)
private object UserActiveListV2(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
return new RestObject() { { "activeusers", string.Join("\t", TShock.Players.Where(p => null != p && null != p.UserAccountName && p.Active).Select(p => p.UserAccountName)) } };
}
private object UserListV2(RestVerbs verbs, IParameterCollection parameters)
private object UserListV2(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
return new RestObject() { { "users", TShock.Users.GetUsers().Select(p => new Dictionary<string,object>(){
{"name", p.Name},
{"id", p.ID},
{"group", p.Group},
{"ip", p.Address},
}) } };
}
private object UserCreateV2(RestVerbs verbs, IParameterCollection parameters)
private object UserCreateV2(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var username = parameters["user"];
if (string.IsNullOrWhiteSpace(username))
return RestMissingParam("user");
var group = parameters["group"];
if (string.IsNullOrWhiteSpace(group))
return RestMissingParam("group");
if (string.IsNullOrWhiteSpace(group))
group = TShock.Config.DefaultRegistrationGroupName;
var password = parameters["password"];
if (string.IsNullOrWhiteSpace(password))
return RestMissingParam("password");
// NOTE: ip can be blank
User user = new User(parameters["ip"], username, password, group);
User user = new User(username, password, group, "", "");
try
{
TShock.Users.AddUser(user);
@ -232,7 +320,7 @@ namespace TShockAPI
return RestResponse("User was successfully created");
}
private object UserUpdateV2(RestVerbs verbs, IParameterCollection parameters)
private object UserUpdateV2(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var ret = UserFind(parameters);
if (ret is RestObject)
@ -274,7 +362,7 @@ namespace TShockAPI
return response;
}
private object UserDestroyV2(RestVerbs verbs, IParameterCollection parameters)
private object UserDestroyV2(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var ret = UserFind(parameters);
if (ret is RestObject)
@ -292,7 +380,7 @@ namespace TShockAPI
return RestResponse("User deleted successfully");
}
private object UserInfoV2(RestVerbs verbs, IParameterCollection parameters)
private object UserInfoV2(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var ret = UserFind(parameters);
if (ret is RestObject)
@ -306,7 +394,7 @@ namespace TShockAPI
#region RestBanMethods
private object BanCreate(RestVerbs verbs, IParameterCollection parameters)
private object BanCreate(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var ip = parameters["ip"];
var name = parameters["name"];
@ -316,7 +404,7 @@ namespace TShockAPI
try
{
TShock.Bans.AddBan(ip, name, parameters["reason"], true);
TShock.Bans.AddBan(ip, name, parameters["reason"], true, tokenData.Username);
}
catch (Exception e)
{
@ -325,7 +413,7 @@ namespace TShockAPI
return RestResponse("Ban created successfully");
}
private object BanDestroyV2(RestVerbs verbs, IParameterCollection parameters)
private object BanDestroyV2(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var ret = BanFind(parameters);
if (ret is RestObject)
@ -357,7 +445,7 @@ namespace TShockAPI
return RestResponse("Ban deleted successfully");
}
private object BanInfoV2(RestVerbs verbs, IParameterCollection parameters)
private object BanInfoV2(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var ret = BanFind(parameters);
if (ret is RestObject)
@ -371,7 +459,7 @@ namespace TShockAPI
};
}
private object BanListV2(RestVerbs verbs, IParameterCollection parameters)
private object BanListV2(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var banList = new ArrayList();
foreach (var ban in TShock.Bans.GetBans())
@ -393,7 +481,7 @@ namespace TShockAPI
#region RestWorldMethods
private object WorldChangeSaveSettings(RestVerbs verbs, IParameterCollection parameters)
private object WorldChangeSaveSettings(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
bool autoSave;
if (!bool.TryParse(verbs["bool"], out autoSave))
@ -403,14 +491,14 @@ namespace TShockAPI
return RestResponse("AutoSave has been set to " + autoSave);
}
private object WorldSave(RestVerbs verbs, IParameterCollection parameters)
private object WorldSave(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
SaveManager.Instance.SaveWorld();
return RestResponse("World saved");
}
private object WorldButcher(RestVerbs verbs, IParameterCollection parameters)
private object WorldButcher(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
bool killFriendly;
if (!bool.TryParse(parameters["killfriendly"], out killFriendly))
@ -432,7 +520,7 @@ namespace TShockAPI
return RestResponse(killcount + " NPCs have been killed");
}
private object WorldRead(RestVerbs verbs, IParameterCollection parameters)
private object WorldRead(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
return new RestObject()
{
@ -445,7 +533,7 @@ namespace TShockAPI
};
}
private object WorldMeteor(RestVerbs verbs, IParameterCollection parameters)
private object WorldMeteor(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
if (null == WorldGen.genRand)
WorldGen.genRand = new Random();
@ -453,7 +541,7 @@ namespace TShockAPI
return RestResponse("Meteor has been spawned");
}
private object WorldBloodmoon(RestVerbs verbs, IParameterCollection parameters)
private object WorldBloodmoon(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
bool bloodmoon;
if (!bool.TryParse(verbs["bool"], out bloodmoon))
@ -467,23 +555,23 @@ namespace TShockAPI
#region RestPlayerMethods
private object PlayerUnMute(RestVerbs verbs, IParameterCollection parameters)
private object PlayerUnMute(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
return PlayerSetMute(parameters, false);
}
private object PlayerMute(RestVerbs verbs, IParameterCollection parameters)
private object PlayerMute(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
return PlayerSetMute(parameters, true);
}
private object PlayerList(RestVerbs verbs, IParameterCollection parameters)
private object PlayerList(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var activeplayers = Main.player.Where(p => null != p && p.active).ToList();
return new RestObject() { { "players", string.Join(", ", activeplayers.Select(p => p.name)) } };
}
private object PlayerListV2(RestVerbs verbs, IParameterCollection parameters)
private object PlayerListV2(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var playerList = new ArrayList();
foreach (TSPlayer tsPlayer in TShock.Players.Where(p => null != p))
@ -495,7 +583,7 @@ namespace TShockAPI
return new RestObject() { { "players", playerList } };
}
private object PlayerReadV2(RestVerbs verbs, IParameterCollection parameters)
private object PlayerReadV2(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var ret = PlayerFind(parameters);
if (ret is RestObject)
@ -515,7 +603,7 @@ namespace TShockAPI
};
}
private object PlayerKickV2(RestVerbs verbs, IParameterCollection parameters)
private object PlayerKickV2(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var ret = PlayerFind(parameters);
if (ret is RestObject)
@ -526,7 +614,7 @@ namespace TShockAPI
return RestResponse("Player " + player.Name + " was kicked");
}
private object PlayerBanV2(RestVerbs verbs, IParameterCollection parameters)
private object PlayerBanV2(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var ret = PlayerFind(parameters);
if (ret is RestObject)
@ -539,7 +627,7 @@ namespace TShockAPI
return RestResponse("Player " + player.Name + " was banned");
}
private object PlayerKill(RestVerbs verbs, IParameterCollection parameters)
private object PlayerKill(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var ret = PlayerFind(parameters);
if (ret is RestObject)
@ -556,7 +644,7 @@ namespace TShockAPI
#region RestGroupMethods
private object GroupList(RestVerbs verbs, IParameterCollection parameters)
private object GroupList(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var groups = new ArrayList();
foreach (Group group in TShock.Groups)
@ -566,7 +654,7 @@ namespace TShockAPI
return new RestObject() { { "groups", groups } };
}
private object GroupInfo(RestVerbs verbs, IParameterCollection parameters)
private object GroupInfo(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var ret = GroupFind(parameters);
if (ret is RestObject)
@ -583,7 +671,7 @@ namespace TShockAPI
};
}
private object GroupDestroy(RestVerbs verbs, IParameterCollection parameters)
private object GroupDestroy(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var ret = GroupFind(parameters);
if (ret is RestObject)
@ -602,7 +690,7 @@ namespace TShockAPI
return RestResponse("Group '" + group.Name + "' deleted successfully");
}
private object GroupCreate(RestVerbs verbs, IParameterCollection parameters)
private object GroupCreate(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var name = parameters["group"];
if (string.IsNullOrWhiteSpace(name))
@ -619,7 +707,7 @@ namespace TShockAPI
return RestResponse("Group '" + name + "' created successfully");
}
private object GroupUpdate(RestVerbs verbs, IParameterCollection parameters)
private object GroupUpdate(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var ret = GroupFind(parameters);
if (ret is RestObject)
@ -713,10 +801,6 @@ namespace TShockAPI
break;
case "id":
user = TShock.Users.GetUserByID(Convert.ToInt32(name));
break;
case "ip":
user = TShock.Users.GetUserByIP(name);
break;
default:
return RestError("Invalid Type: '" + type + "'");

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;

View file

@ -0,0 +1,87 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
using System.ComponentModel;
namespace Rests
{
public static class RestPermissions
{
// tshock.rest.bans nodes
[Description("REST user can list and get detailed information about bans.")]
public static readonly string restviewbans = "tshock.rest.bans.view";
[Description("REST user can alter bans.")]
public static readonly string restmanagebans = "tshock.rest.bans.manage";
// tshock.rest.groups nodes
[Description("REST user can list and get detailed information about groups.")]
public static readonly string restviewgroups = "tshock.rest.groups.view";
[Description("REST user can alter groups.")]
public static readonly string restmanagegroups = "tshock.rest.groups.manage";
// tshock.rest.users nodes
[Description("REST user can list and get detailed information about users.")]
public static readonly string restviewusers = "tshock.rest.users.view";
[Description("REST user can alter users.")]
public static readonly string restmanageusers = "tshock.rest.users.manage";
[Description("REST user can get user information.")]
public static readonly string restuserinfo = "tshock.rest.users.info";
// Non-grouped nodes
[Description("User can create REST tokens.")]
public static readonly string restapi = "tshock.rest.useapi";
[Description("User or REST user can destroy all REST tokens.")]
public static readonly string restmanage = "tshock.rest.manage";
[Description("REST user can turn off / restart the server.")]
public static readonly string restmaintenance = "tshock.rest.maintenance";
[Description("REST user can reload configurations, save the world and set auto save settings.")]
public static readonly string restcfg = "tshock.rest.cfg";
[Description("REST user can kick players.")]
public static readonly string restkick = "tshock.rest.kick";
[Description("REST user can ban players.")]
public static readonly string restban = "tshock.rest.ban";
[Description("REST user can mute and unmute players.")]
public static readonly string restmute = "tshock.rest.mute";
[Description("REST user can kill players.")]
public static readonly string restkill = "tshock.rest.kill";
[Description("REST user can drop meteors or change bloodmoon.")]
public static readonly string restcauseevents = "tshock.rest.causeevents";
[Description("REST user can butcher npcs.")]
public static readonly string restbutcher = "tshock.rest.butcher";
[Description("REST user can run raw TShock commands (the raw command permissions are also checked though).")]
public static readonly string restrawcommand = "tshock.rest.command";
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;

View file

@ -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,41 +15,76 @@ 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using HttpServer;
using TShockAPI;
using TShockAPI.DB;
namespace Rests
{
/// <summary>
///
/// </summary>
/// <param name="username">Username to verify</param>
/// <param name="password">Password to verify</param>
/// <returns>Returning a restobject with a null error means a successful verification.</returns>
public delegate RestObject VerifyD(string username, string password);
public class SecureRest : Rest
{
public Dictionary<string, object> Tokens { get; protected set; }
public event VerifyD Verify;
public struct TokenData
{
public static readonly TokenData None = default(TokenData);
public string Username { get; set; }
public string UserGroupName { get; set; }
}
public Dictionary<string,TokenData> Tokens { get; protected set; }
public Dictionary<string, TokenData> AppTokens { get; protected set; }
public SecureRest(IPAddress ip, int port)
: base(ip, port)
{
Tokens = new Dictionary<string, object>();
Register(new RestCommand("/token/create/{username}/{password}", NewToken) {RequiresToken = false});
Register(new RestCommand("/v2/token/create/{password}", NewTokenV2) { RequiresToken = false });
Register(new RestCommand("/token/destroy/{token}", DestroyToken) {RequiresToken = true});
foreach (KeyValuePair<string, string> t in TShockAPI.TShock.RESTStartupTokens)
Tokens = new Dictionary<string, TokenData>();
AppTokens = new Dictionary<string, TokenData>();
Register(new RestCommand("/token/create/{username}/{password}", NewToken) { DoLog = false });
Register(new RestCommand("/v2/token/create/{password}", NewTokenV2) { DoLog = false });
Register(new SecureRestCommand("/token/destroy/{token}", DestroyToken));
Register(new SecureRestCommand("/v3/token/destroy/all", DestroyAllTokens, RestPermissions.restmanage));
foreach (KeyValuePair<string, TokenData> t in TShockAPI.TShock.RESTStartupTokens)
{
Tokens.Add(t.Key, t.Value);
AppTokens.Add(t.Key, t.Value);
}
foreach (KeyValuePair<string, TokenData> t in TShock.Config.ApplicationRestTokens)
{
AppTokens.Add(t.Key, t.Value);
}
// TODO: Get rid of this when the old REST permission model is removed.
if (TShock.Config.RestApiEnabled && !TShock.Config.RestUseNewPermissionModel)
{
string warningMessage = string.Concat(
"You're using the old REST permission model which is highly vulnerable in matter of security. ",
"The old model will be removed with the next maintenance release of TShock. In order to switch to the new model, ",
"change the config setting \"RestUseNewPermissionModel\" to true."
);
Log.Warn(warningMessage);
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(warningMessage);
Console.ForegroundColor = ConsoleColor.Gray;
}
else if (TShock.Config.RestApiEnabled)
{
string warningMessage = string.Concat(
"You're using the new more secure REST permission model which can lead to compatibility problems ",
"with existing REST services. If compatibility problems occur, you can switch back to the unsecure permission ",
"model by changing the config setting \"RestUseNewPermissionModel\" to false, which is not recommended."
);
Log.ConsoleInfo(warningMessage);
}
}
private object DestroyToken(RestVerbs verbs, IParameterCollection parameters)
private object DestroyToken(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
var token = verbs["token"];
try
@ -58,11 +93,19 @@ namespace Rests
}
catch (Exception)
{
return new Dictionary<string, string>
{{"status", "400"}, {"error", "The specified token queued for destruction failed to be deleted."}};
return new RestObject("400")
{ Error = "The specified token queued for destruction failed to be deleted." };
}
return new Dictionary<string, string>
{{"status", "200"}, {"response", "Requested token was successfully destroyed."}};
return new RestObject()
{ Response = "Requested token was successfully destroyed." };
}
private object DestroyAllTokens(RestVerbs verbs, IParameterCollection parameters, SecureRest.TokenData tokenData)
{
Tokens.Clear();
return new RestObject()
{ Response = "All tokens were successfully destroyed." };
}
private object NewTokenV2(RestVerbs verbs, IParameterCollection parameters)
@ -70,29 +113,7 @@ namespace Rests
var user = parameters["username"];
var pass = verbs["password"];
RestObject obj = null;
if (Verify != null)
obj = Verify(user, pass);
if (obj == null)
obj = new RestObject("401") { Error = "Invalid username/password combination provided. Please re-submit your query with a correct pair." };
if (obj.Error != null)
return obj;
string hash;
var rand = new Random();
var randbytes = new byte[32];
do
{
rand.NextBytes(randbytes);
hash = randbytes.Aggregate("", (s, b) => s + b.ToString("X2"));
} while (Tokens.ContainsKey(hash));
Tokens.Add(hash, user);
obj["token"] = hash;
return obj;
return this.NewTokenInternal(user, pass);
}
private object NewToken(RestVerbs verbs, IParameterCollection parameters)
@ -100,55 +121,83 @@ namespace Rests
var user = verbs["username"];
var pass = verbs["password"];
RestObject obj = null;
if (Verify != null)
obj = Verify(user, pass);
RestObject response = this.NewTokenInternal(user, pass);
response["deprecated"] = "This endpoint is depracted and will be removed in the future.";
return response;
}
if (obj == null)
obj = new RestObject("401")
{Error = "Invalid username/password combination provided. Please re-submit your query with a correct pair."};
private RestObject NewTokenInternal(string username, string password)
{
User userAccount = TShock.Users.GetUserByName(username);
if (userAccount == null)
return new RestObject("401") { Error = "Invalid username/password combination provided. Please re-submit your query with a correct pair." };
if (!TShock.Utils.HashPassword(password).Equals(userAccount.Password, StringComparison.InvariantCultureIgnoreCase))
return new RestObject("401")
{ Error = "Invalid username/password combination provided. Please re-submit your query with a correct pair." };
if (obj.Error != null)
return obj;
string hash;
Group userGroup = TShock.Utils.GetGroup(userAccount.Group);
if (!userGroup.HasPermission(RestPermissions.restapi) && userAccount.Group != "superadmin")
return new RestObject("403")
{ Error = "Although your account was successfully found and identified, your account lacks the permission required to use the API. (restapi)" };
string tokenHash;
var rand = new Random();
var randbytes = new byte[32];
do
{
rand.NextBytes(randbytes);
hash = randbytes.Aggregate("", (s, b) => s + b.ToString("X2"));
} while (Tokens.ContainsKey(hash));
tokenHash = randbytes.Aggregate("", (s, b) => s + b.ToString("X2"));
} while (Tokens.ContainsKey(tokenHash));
Tokens.Add(hash, user);
Tokens.Add(tokenHash, new TokenData { Username = userAccount.Name, UserGroupName = userGroup.Name });
obj["token"] = hash;
obj["deprecated"] = "This method will be removed from TShock in 3.6.";
return obj;
RestObject response = new RestObject() { Response = "Successful login" };
response["token"] = tokenHash;
return response;
}
protected override object ExecuteCommand(RestCommand cmd, RestVerbs verbs, IParameterCollection parms)
{
if (cmd.RequiresToken)
{
var strtoken = parms["token"];
if (strtoken == null)
return new Dictionary<string, string>
{{"status", "401"}, {"error", "Not authorized. The specified API endpoint requires a token."}};
if (!cmd.RequiresToken)
return base.ExecuteCommand(cmd, verbs, parms);
var token = parms["token"];
if (token == null)
return new RestObject("401")
{ Error = "Not authorized. The specified API endpoint requires a token." };
object token;
if (!Tokens.TryGetValue(strtoken, out token))
return new Dictionary<string, string>
{
{"status", "403"},
{
"error",
"Not authorized. The specified API endpoint requires a token, but the provided token was not valid."
}
};
SecureRestCommand secureCmd = (SecureRestCommand)cmd;
TokenData tokenData;
if (!Tokens.TryGetValue(token, out tokenData) && !AppTokens.TryGetValue(token, out tokenData))
return new RestObject("403")
{ Error = "Not authorized. The specified API endpoint requires a token, but the provided token was not valid." };
// TODO: Get rid of this when the old REST permission model is removed.
if (TShock.Config.RestUseNewPermissionModel) {
Group userGroup = TShock.Groups.GetGroupByName(tokenData.UserGroupName);
if (userGroup == null)
{
Tokens.Remove(token);
return new RestObject("403")
{ Error = "Not authorized. The provided token became invalid due to group changes, please create a new token." };
}
if (secureCmd.Permissions.Length > 0 && secureCmd.Permissions.All(perm => !userGroup.HasPermission(perm)))
{
return new RestObject("403")
{ Error = string.Format("Not authorized. User \"{0}\" has no access to use the specified API endpoint.", tokenData.Username) };
}
}
return base.ExecuteCommand(cmd, verbs, parms);
object result = secureCmd.Execute(verbs, parms, tokenData);
if (cmd.DoLog)
TShock.Utils.SendLogs(string.Format(
"\"{0}\" requested REST endpoint: {1}", tokenData.Username, this.BuildRequestUri(cmd, verbs, parms, false)),
Color.PaleVioletRed);
return result;
}
}
}

View file

@ -1,8 +1,24 @@
using System;
/*
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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
using System.Diagnostics;
using Terraria;
namespace TShockAPI

View file

@ -1,99 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.IO;
using System.Net;
using System.Threading;
using Terraria;
namespace TShockAPI
{
public class StatTracker
{
private Utils Utils = TShock.Utils;
public DateTime lastcheck = DateTime.MinValue;
private readonly int checkinFrequency = 5;
public void CheckIn()
{
if ((DateTime.Now - lastcheck).TotalMinutes >= checkinFrequency)
{
ThreadPool.QueueUserWorkItem(CallHome);
lastcheck = DateTime.Now;
}
}
private void CallHome(object state)
{
string fp;
string lolpath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/.tshock/";
if (!Directory.Exists(lolpath))
{
Directory.CreateDirectory(lolpath);
}
if (!File.Exists(Path.Combine(lolpath, Netplay.serverPort + ".fingerprint")))
{
fp = "";
int random = Utils.Random.Next(500000, 1000000);
fp += random;
fp = Utils.HashPassword(Netplay.serverIP + fp + Netplay.serverPort + Netplay.serverListenIP);
TextWriter tw = new StreamWriter(Path.Combine(lolpath, Netplay.serverPort + ".fingerprint"));
tw.Write(fp);
tw.Close();
}
else
{
fp = "";
TextReader tr = new StreamReader(Path.Combine(lolpath, Netplay.serverPort + ".fingerprint"));
fp = tr.ReadToEnd();
tr.Close();
}
using (var client = new WebClient())
{
client.Headers.Add("user-agent",
"TShock (" + TShock.VersionNum + ")");
try
{
string response;
if (TShock.Config.DisablePlayerCountReporting)
{
response =
client.DownloadString("http://tshock.co/tickto.php?do=log&fp=" + fp + "&ver=" + TShock.VersionNum + "&os=" +
Environment.OSVersion + "&mono=" + Main.runningMono + "&port=" + Netplay.serverPort +
"&plcount=0");
}
else
{
response =
client.DownloadString("http://tshock.co/tickto.php?do=log&fp=" + fp + "&ver=" + TShock.VersionNum + "&os=" +
Environment.OSVersion + "&mono=" + Main.runningMono + "&port=" + Netplay.serverPort +
"&plcount=" + TShock.Utils.ActivePlayers());
}
if (!TShock.Config.HideStatTrackerDebugMessages)
Log.ConsoleInfo("Stat Tracker: " + response);
}
catch (Exception e)
{
Log.Error(e.ToString());
}
}
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
@ -71,10 +72,26 @@ namespace TShockAPI
public int FirstMaxMP { get; set; }
/// <summary>
/// The player's group.
/// </summary>
public Group Group { get; set; }
/// <summary>
/// The player's group.
/// </summary>
public Group Group
{
get
{
if (tempGroup != null)
return tempGroup;
return group;
}
set { group = value; }
}
/// <summary>
/// The player's temporary group. This overrides the user's actual group.
/// </summary>
public Group tempGroup = null;
private Group group = null;
public bool ReceivedInfo { get; set; }
@ -105,6 +122,8 @@ namespace TShockAPI
public bool AwaitingName { get; set; }
public string[] AwaitingNameParameters { get; set; }
/// <summary>
/// The last time a player broke a grief check.
/// </summary>
@ -144,15 +163,21 @@ namespace TShockAPI
public string UserAccountName { get; set; }
/// <summary>
/// Unused can be removed.
/// Whether the player performed a valid login attempt (i.e. entered valid user name and password) but is still blocked
/// from logging in because of SSI.
/// </summary>
public bool HasBeenSpammedWithBuildMessage;
public bool LoginFailsBySsi { get; set; }
/// <summary>
/// Whether the player is logged in or not.
/// </summary>
public bool IsLoggedIn;
/// <summary>
/// Whether the player has sent their whole inventory to the server while connecting.
/// </summary>
public bool HasSentInventory { get; set; }
/// <summary>
/// The player's user id( from the db ).
/// </summary>
@ -215,6 +240,8 @@ namespace TShockAPI
public bool SilentKickInProgress;
public bool SilentJoinInProgress;
/// <summary>
/// A list of points where ice tiles have been placed.
/// </summary>
@ -381,7 +408,7 @@ namespace TShockAPI
TilesDestroyed = new Dictionary<Vector2, TileData>();
TilesCreated = new Dictionary<Vector2, TileData>();
Index = index;
Group = new Group(TShock.Config.DefaultGuestGroupName);
Group = Group.DefaultGroup;
IceTiles = new List<Point>();
AwaitingResponse = new Dictionary<string, Action<object>>();
}
@ -392,7 +419,7 @@ namespace TShockAPI
TilesCreated = new Dictionary<Vector2, TileData>();
Index = -1;
FakePlayer = new Player {name = playerName, whoAmi = -1};
Group = new Group(TShock.Config.DefaultGuestGroupName);
Group = Group.DefaultGroup;
AwaitingResponse = new Dictionary<string, Action<object>>();
}
@ -557,7 +584,8 @@ namespace TShockAPI
public bool GiveItemCheck(int type, string name, int width, int height, int stack, int prefix = 0)
{
if (TShock.Itembans.ItemIsBanned(name) && TShock.Config.PreventBannedItemSpawn)
if ((TShock.Itembans.ItemIsBanned(name) && TShock.Config.PreventBannedItemSpawn) &&
(TShock.Itembans.ItemIsBanned(name, this) || !TShock.Config.AllowAllowedGroupsToSpawnBannedItems))
return false;
GiveItem(type,name,width,height,stack,prefix);
@ -585,21 +613,41 @@ namespace TShockAPI
SendMessage(msg, Color.Yellow);
}
public void SendInfoMessage(string format, params object[] args)
{
SendInfoMessage(string.Format(format, args));
}
public virtual void SendSuccessMessage(string msg)
{
SendMessage(msg, Color.Green);
}
public void SendSuccessMessage(string format, params object[] args)
{
SendSuccessMessage(string.Format(format, args));
}
public virtual void SendWarningMessage(string msg)
{
SendMessage(msg, Color.OrangeRed);
}
public void SendWarningMessage(string format, params object[] args)
{
SendWarningMessage(string.Format(format, args));
}
public virtual void SendErrorMessage(string msg)
{
SendMessage(msg, Color.Red);
}
public void SendErrorMessage(string format, params object[] args)
{
SendErrorMessage(string.Format(format, args));
}
[Obsolete("Use SendErrorMessage, SendInfoMessage, or SendWarningMessage, or a custom color instead.")]
public virtual void SendMessage(string msg)
{
@ -712,14 +760,14 @@ namespace TShockAPI
}
}
public class TSRestPlayer : TSServerPlayer
public class TSRestPlayer : TSPlayer
{
internal List<string> CommandReturn = new List<string>();
internal List<string> CommandOutput = new List<string>();
public TSRestPlayer()
public TSRestPlayer(string playerName, Group playerGroup): base(playerName)
{
Group = new SuperAdminGroup();
AwaitingResponse = new Dictionary<string, Action<object>>();
Group = playerGroup;
AwaitingResponse = new Dictionary<string, Action<object>>();
}
public override void SendMessage(string msg)
@ -734,21 +782,43 @@ namespace TShockAPI
public override void SendMessage(string msg, byte red, byte green, byte blue)
{
CommandReturn.Add(msg);
this.CommandOutput.Add(msg);
}
public override void SendInfoMessage(string msg)
{
SendMessage(msg, Color.Yellow);
}
public override void SendSuccessMessage(string msg)
{
SendMessage(msg, Color.Green);
}
public override void SendWarningMessage(string msg)
{
SendMessage(msg, Color.OrangeRed);
}
public override void SendErrorMessage(string msg)
{
SendMessage(msg, Color.Red);
}
public List<string> GetCommandOutput()
{
return CommandReturn;
return this.CommandOutput;
}
}
public class TSServerPlayer : TSPlayer
{
public static string AccountName = "ServerConsole";
public TSServerPlayer()
: base("Server")
{
Group = new SuperAdminGroup();
UserAccountName = AccountName;
}
public override void SendErrorMessage(string msg)
@ -832,6 +902,10 @@ namespace TShockAPI
public void StrikeNPC(int npcid, int damage, float knockBack, int hitDirection)
{
// Main.rand is thread static.
if (Main.rand == null)
Main.rand = new Random();
Main.npc[npcid].StrikeNPC(damage, knockBack, hitDirection);
NetMessage.SendData((int) PacketTypes.NpcStrike, -1, -1, "", npcid, damage, knockBack, hitDirection);
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
@ -23,13 +24,14 @@ using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Threading;
using Hooks;
using MaxMind;
using Mono.Data.Sqlite;
using MySql.Data.MySqlClient;
using Newtonsoft.Json;
using Rests;
using Terraria;
using TShockAPI.DB;
@ -37,16 +39,18 @@ using TShockAPI.Net;
namespace TShockAPI
{
[APIVersion(1, 12)]
[APIVersion(1, 13)]
public class TShock : TerrariaPlugin
{
private const string LogFormatDefault = "yyyy-MM-dd_HH-mm-ss";
private static string LogFormat = LogFormatDefault;
private static bool LogClear = false;
public static readonly Version VersionNum = Assembly.GetExecutingAssembly().GetName().Version;
public static readonly string VersionCodename = "Welcome to the future.";
public static readonly string VersionCodename = "And the great beast rose from its slumber, ready to take on the world again.";
public static string SavePath = "tshock";
private const string LogFormatDefault = "yyyy-MM-dd_HH-mm-ss";
private static string LogFormat = LogFormatDefault;
private const string LogPathDefault = "tshock";
private static string LogPath = LogPathDefault;
private static bool LogClear = false;
public static TSPlayer[] Players = new TSPlayer[Main.maxPlayers];
public static BanManager Bans;
@ -66,11 +70,10 @@ namespace TShockAPI
public static SecureRest RestApi;
public static RestManager RestManager;
public static Utils Utils = Utils.Instance;
public static StatTracker StatTracker = new StatTracker();
/// <summary>
/// Used for implementing REST Tokens prior to the REST system starting up.
/// </summary>
public static Dictionary<string, string> RESTStartupTokens = new Dictionary<string, string>();
public static Dictionary<string, SecureRest.TokenData> RESTStartupTokens = new Dictionary<string, SecureRest.TokenData>();
/// <summary>
/// Called after TShock is initialized. Useful for plugins that needs hooks before tshock but also depend on tshock being loaded.
@ -97,6 +100,10 @@ namespace TShockAPI
get { return "The administration modification of the future."; }
}
public override string UpdateURL
{
get { return ""; }
}
public TShock(Main game)
: base(game)
{
@ -108,35 +115,57 @@ namespace TShockAPI
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
public override void Initialize()
{
HandleCommandLine(Environment.GetCommandLineArgs());
if (!Directory.Exists(SavePath))
Directory.CreateDirectory(SavePath);
DateTime now = DateTime.Now;
string logFilename;
try
{
logFilename = Path.Combine(SavePath, now.ToString(LogFormat)+".log");
}
catch(Exception)
{
// Problem with the log format use the default
logFilename = Path.Combine(SavePath, now.ToString(LogFormatDefault) + ".log");
}
HandleCommandLine(Environment.GetCommandLineArgs());
if (Version.Major >= 4)
getTShockAscii();
if (!Directory.Exists(SavePath))
Directory.CreateDirectory(SavePath);
ConfigFile.ConfigRead += OnConfigRead;
FileTools.SetupConfig();
DateTime now = DateTime.Now;
string logFilename;
string logPathSetupWarning = null;
// Log path was not already set by the command line parameter?
if (LogPath == LogPathDefault)
LogPath = Config.LogPath;
try
{
logFilename = Path.Combine(LogPath, now.ToString(LogFormat)+".log");
if (!Directory.Exists(LogPath))
Directory.CreateDirectory(LogPath);
}
catch(Exception ex)
{
logPathSetupWarning = "Could not apply the given log path / log format, defaults will be used. Exception details:\n" + ex;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(logPathSetupWarning);
Console.ForegroundColor = ConsoleColor.Gray;
// Problem with the log path or format use the default
logFilename = Path.Combine(LogPathDefault, now.ToString(LogFormatDefault) + ".log");
}
#if DEBUG
Log.Initialize(logFilename, LogLevel.All, false);
Log.Initialize(logFilename, LogLevel.All, false);
#else
Log.Initialize(logFilename, LogLevel.All & ~LogLevel.Debug, LogClear);
Log.Initialize(logFilename, LogLevel.All & ~LogLevel.Debug, LogClear);
#endif
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
if (Version.Major >= 4)
{
getTShockAscii();
}
if (logPathSetupWarning != null)
Log.Warn(logPathSetupWarning);
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
}
catch(Exception ex)
{
// Will be handled by the server api and written to its crashlog.txt.
throw new Exception("Fatal TShock initialization exception. See inner exception for details.", ex);
}
// Further exceptions are written to TShock's log from now on.
try
{
if (File.Exists(Path.Combine(SavePath, "tshock.pid")))
@ -147,9 +176,6 @@ namespace TShockAPI
}
File.WriteAllText(Path.Combine(SavePath, "tshock.pid"), Process.GetCurrentProcess().Id.ToString(CultureInfo.InvariantCulture));
ConfigFile.ConfigRead += OnConfigRead;
FileTools.SetupConfig();
HandleCommandLinePostConfigLoad(Environment.GetCommandLineArgs());
if (Config.StorageType.ToLower() == "sqlite")
@ -195,7 +221,6 @@ namespace TShockAPI
RememberedPos = new RememberedPosManager(DB);
InventoryDB = new InventoryManager(DB);
RestApi = new SecureRest(Netplay.serverListenIP, Config.RestApiPort);
RestApi.Verify += RestApi_Verify;
RestApi.Port = Config.RestApiPort;
RestManager = new RestManager(RestApi);
RestManager.RegisterRestfulCommands();
@ -225,6 +250,7 @@ namespace TShockAPI
WorldHooks.SaveWorld += SaveManager.Instance.OnSaveWorld;
WorldHooks.ChristmasCheck += OnXmasCheck;
NetHooks.NameCollision += NetHooks_NameCollision;
TShockAPI.Hooks.PlayerHooks.PlayerPostLogin += OnPlayerLogin;
GetDataHandlers.InitGetDataHandler();
Commands.InitCommands();
@ -269,33 +295,6 @@ namespace TShockAPI
// ReSharper restore LocalizableElement
}
private RestObject RestApi_Verify(string username, string password)
{
var userAccount = Users.GetUserByName(username);
if (userAccount == null)
{
return new RestObject("401")
{Error = "Invalid username/password combination provided. Please re-submit your query with a correct pair."};
}
if (Utils.HashPassword(password).ToUpper() != userAccount.Password.ToUpper())
{
return new RestObject("401")
{Error = "Invalid username/password combination provided. Please re-submit your query with a correct pair."};
}
if (!Utils.GetGroup(userAccount.Group).HasPermission(Permissions.restapi) && userAccount.Group != "superadmin")
{
return new RestObject("403")
{
Error =
"Although your account was successfully found and identified, your account lacks the permission required to use the API. (api)"
};
}
return new RestObject("200") {Response = "Successful login"}; //Maybe return some user info too?
}
protected override void Dispose(bool disposing)
{
if (disposing)
@ -326,6 +325,7 @@ namespace TShockAPI
WorldHooks.SaveWorld -= SaveManager.Instance.OnSaveWorld;
WorldHooks.ChristmasCheck -= OnXmasCheck;
NetHooks.NameCollision -= NetHooks_NameCollision;
TShockAPI.Hooks.PlayerHooks.PlayerPostLogin -= OnPlayerLogin;
if (File.Exists(Path.Combine(SavePath, "tshock.pid")))
{
@ -338,7 +338,31 @@ namespace TShockAPI
base.Dispose(disposing);
}
void NetHooks_NameCollision(int who, string name, HandledEventArgs e)
private void OnPlayerLogin(Hooks.PlayerPostLoginEventArgs args)
{
User u = Users.GetUserByName(args.Player.UserAccountName);
List<String> KnownIps = new List<string>();
if (!string.IsNullOrWhiteSpace(u.KnownIps))
{
KnownIps = JsonConvert.DeserializeObject<List<String>>(u.KnownIps);
}
bool found = KnownIps.Any(s => s.Equals(args.Player.IP));
if (!found)
{
if (KnownIps.Count == 100)
{
KnownIps.RemoveAt(0);
}
KnownIps.Add(args.Player.IP);
}
u.KnownIps = JsonConvert.SerializeObject(KnownIps, Formatting.Indented);
Users.UpdateLogin(u);
}
private void NetHooks_NameCollision(int who, string name, HandledEventArgs e)
{
string ip = TShock.Utils.GetRealIP(Netplay.serverSock[who].tcpClient.Client.RemoteEndPoint.ToString());
foreach (TSPlayer ply in TShock.Players)
@ -369,7 +393,7 @@ namespace TShockAPI
return;
}
void OnXmasCheck(ChristmasCheckEventArgs args)
private void OnXmasCheck(ChristmasCheckEventArgs args)
{
if (args.Handled)
return;
@ -442,9 +466,13 @@ namespace TShockAPI
}
break;
case "-dump":
ConfigFile.DumpDescriptions();
Permissions.DumpDescriptions();
case "-logpath":
path = parms[++i];
if (path.IndexOfAny(Path.GetInvalidPathChars()) == -1)
{
LogPath = path;
Log.ConsoleInfo("Log path has been set to " + path);
}
break;
case "-logformat":
@ -454,6 +482,11 @@ namespace TShockAPI
case "-logclear":
bool.TryParse(parms[++i], out LogClear);
break;
case "-dump":
ConfigFile.DumpDescriptions();
Permissions.DumpDescriptions();
break;
}
}
}
@ -473,7 +506,7 @@ namespace TShockAPI
break;
case "-rest-token":
string token = Convert.ToString(parms[++i]);
RESTStartupTokens.Add(token, "null");
RESTStartupTokens.Add(token, new SecureRest.TokenData { Username = "null", UserGroupName = "superadmin" });
Console.WriteLine("Startup parameter overrode REST token.");
break;
case "-rest-enabled":
@ -537,7 +570,7 @@ namespace TShockAPI
Regions.ReloadAllRegions();
StatTracker.CheckIn();
Lighting.lightMode = 2;
FixChestStacks();
@ -545,6 +578,9 @@ namespace TShockAPI
private void FixChestStacks()
{
if (Config.IgnoreChestStacksOnLoad)
return;
foreach (Chest chest in Main.chest)
{
if (chest != null)
@ -564,7 +600,6 @@ namespace TShockAPI
private void OnUpdate()
{
UpdateManager.UpdateProcedureCheck();
StatTracker.CheckIn();
if (Backups.IsBackupTime)
Backups.Backup();
//call these every second, not every update
@ -735,16 +770,8 @@ namespace TShockAPI
private void OnConnect(int ply, HandledEventArgs handler)
{
var player = new TSPlayer(ply);
if (Config.EnableDNSHostResolution)
{
player.Group = Users.GetGroupForIPExpensive(player.IP);
}
else
{
player.Group = Users.GetGroupForIP(player.IP);
}
if (Utils.ActivePlayers() + 1 > Config.MaxSlots + 20)
if (Utils.ActivePlayers() + 1 > Config.MaxSlots + Config.ReservedSlots)
{
Utils.ForceKick(player, Config.ServerFullNoReservedReason, true, false);
handler.Handled = true;
@ -758,9 +785,14 @@ namespace TShockAPI
if (ban != null)
{
Utils.ForceKick(player, string.Format("You are banned: {0}", ban.Reason), true, false);
handler.Handled = true;
return;
if (!Utils.HasBanExpired(ban))
{
DateTime exp;
string duration = DateTime.TryParse(ban.Expiration, out exp) ? String.Format("until {0}", exp.ToString("G")) : "forever";
Utils.ForceKick(player, string.Format("You are banned {0}: {1}", duration, ban.Reason), true, false);
handler.Handled = true;
return;
}
}
if (!FileTools.OnWhitelist(player.IP))
@ -811,9 +843,13 @@ namespace TShockAPI
if (ban != null)
{
Utils.ForceKick(player, string.Format("You are banned: {0}", ban.Reason), true, false);
handler.Handled = true;
return;
if (!Utils.HasBanExpired(ban))
{
DateTime exp;
string duration = DateTime.TryParse(ban.Expiration, out exp) ? String.Format("until {0}", exp.ToString("G")) : "forever";
Utils.ForceKick(player, string.Format("You are banned {0}: {1}", duration, ban.Reason), true, false);
handler.Handled = true;
}
}
}
@ -825,12 +861,9 @@ namespace TShockAPI
if (tsplr != null && tsplr.ReceivedInfo)
{
if (!tsplr.SilentKickInProgress || tsplr.State > 1)
if (!tsplr.SilentKickInProgress && tsplr.State >= 3)
{
if (tsplr.State >= 2)
{
Utils.Broadcast(tsplr.Name + " left", Color.Yellow);
}
Utils.Broadcast(tsplr.Name + " left", Color.Yellow);
}
Log.Info(string.Format("{0} disconnected.", tsplr.Name));
@ -1451,12 +1484,12 @@ namespace TShockAPI
return (float) Math.Sqrt(num3);
}
public static bool HackedHealth(TSPlayer player)
public static bool HackedStats(TSPlayer player)
{
return (player.TPlayer.statManaMax > 400) ||
(player.TPlayer.statMana > 400) ||
(player.TPlayer.statLifeMax > 400) ||
(player.TPlayer.statLife > 400);
return (player.TPlayer.statManaMax > TShock.Config.MaxMana) ||
(player.TPlayer.statMana > TShock.Config.MaxMana) ||
(player.TPlayer.statLifeMax > TShock.Config.MaxHealth) ||
(player.TPlayer.statLife > TShock.Config.MaxHealth);
}
public static bool HackedInventory(TSPlayer player)

View file

@ -61,32 +61,27 @@
<HintPath>..\SqlBins\MySql.Data.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="MySql.Web, Version=6.3.6.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\SqlBins\MySql.Web.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>.\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="TerrariaServer, Version=1.0.4.0, Culture=neutral, processorArchitecture=x86">
<Reference Include="TerrariaServer, Version=0.0.0.0, Culture=neutral, processorArchitecture=x86">
<SpecificVersion>False</SpecificVersion>
<ExecutableExtension>.exe</ExecutableExtension>
<HintPath>..\TerrariaServerBins\TerrariaServer.exe</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="BackupManager.cs" />
<Compile Include="DB\RegionManager.cs" />
<Compile Include="Hooks\PlayerLoginEvent.cs" />
<Compile Include="Hooks\GeneralHooks.cs" />
<Compile Include="Hooks\PlayerHooks.cs" />
<Compile Include="PaginationTools.cs" />
<Compile Include="PluginUpdater\PluginUpdaterThread.cs" />
<Compile Include="PluginUpdater\PluginVersionCheck.cs" />
<Compile Include="PluginUpdater\VersionInfo.cs" />
<Compile Include="Rest\RestPermissions.cs" />
<Compile Include="SaveManager.cs" />
<Compile Include="DB\BanManager.cs" />
<Compile Include="DB\InventoryManager.cs" />
@ -131,7 +126,6 @@
<Compile Include="Rest\RestObject.cs" />
<Compile Include="Rest\RestVerbs.cs" />
<Compile Include="Rest\SecureRest.cs" />
<Compile Include="StatTracker.cs" />
<Compile Include="Utils.cs" />
<Compile Include="TShock.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
@ -185,11 +179,12 @@
</PreBuildEvent>
</PropertyGroup>
<PropertyGroup>
<PostBuildEvent>"$(ProjectDir)postbuild.bat"</PostBuildEvent>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
<ProjectExtensions>
<VisualStudio>
<UserProperties BuildVersion_UpdateAssemblyVersion="True" BuildVersion_UpdateFileVersion="True" BuildVersion_BuildAction="Both" BuildVersion_BuildVersioningStyle="None.None.None.MonthAndDayStamp" BuildVersion_StartDate="2011/6/17" BuildVersion_IncrementBeforeBuild="False" />
<UserProperties BuildVersion_IncrementBeforeBuild="False" BuildVersion_StartDate="2011/6/17" BuildVersion_BuildVersioningStyle="None.None.None.MonthAndDayStamp" BuildVersion_BuildAction="Both" BuildVersion_UpdateFileVersion="True" BuildVersion_UpdateAssemblyVersion="True" />
</VisualStudio>
</ProjectExtensions>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View file

@ -1,7 +1,7 @@
extensions: .cs
/*
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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Net;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.IO;
@ -24,6 +25,7 @@ using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using Terraria;
using TShockAPI.DB;
namespace TShockAPI
{
@ -217,18 +219,19 @@ namespace TShockAPI
}
/// <summary>
/// Sends message to all users with 'logs' permission.
/// Sends message to all players with 'logs' permission.
/// </summary>
/// <param name="log">Message to send</param>
/// <param name="color">Color of the message</param>
public void SendLogs(string log, Color color)
/// <param name="excludedPlayer">The player to not send the message to.</param>
public void SendLogs(string log, Color color, TSPlayer excludedPlayer = null)
{
Log.Info(log);
TSPlayer.Server.SendMessage(log, color);
foreach (TSPlayer player in TShock.Players)
{
if (player != null && player.Active && player.Group.HasPermission(Permissions.logs) && player.DisplayLogs &&
TShock.Config.DisableSpewLogs == false)
if (player != null && player != excludedPlayer && player.Active && player.Group.HasPermission(Permissions.logs) &&
player.DisplayLogs && TShock.Config.DisableSpewLogs == false)
player.SendMessage(log, color);
}
}
@ -304,7 +307,7 @@ namespace TShockAPI
tileX = startTileX + Random.Next(tileXRange*-1, tileXRange);
tileY = startTileY + Random.Next(tileYRange*-1, tileYRange);
j++;
} while (TileValid(tileX, tileY) && !TileClear(tileX, tileY));
} while (TilePlacementValid(tileX, tileY) && !TileClear(tileX, tileY));
}
/// <summary>
@ -313,7 +316,7 @@ namespace TShockAPI
/// <param name="tileX">Location X</param>
/// <param name="tileY">Location Y</param>
/// <returns>If the tile is valid</returns>
public bool TileValid(int tileX, int tileY)
public bool TilePlacementValid(int tileX, int tileY)
{
return tileX >= 0 && tileX < Main.maxTilesX && tileY >= 0 && tileY < Main.maxTilesY;
}
@ -560,6 +563,36 @@ namespace TShockAPI
Netplay.disconnect = true;
}
/// <summary>
/// Stops the server after kicking all players with a reason message, and optionally saving the world then attempts to
/// restart it.
/// </summary>
/// <param name="save">bool perform a world save before stop (default: true)</param>
/// <param name="reason">string reason (default: "Server shutting down!")</param>
public void RestartServer(bool save = true, string reason = "Server shutting down!")
{
if (TShock.Config.ServerSideInventory)
foreach (TSPlayer player in TShock.Players)
if (player != null && player.IsLoggedIn && !player.IgnoreActionsForClearingTrashCan)
TShock.InventoryDB.InsertPlayerData(player);
StopServer(true, reason);
System.Diagnostics.Process.Start(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
Environment.Exit(0);
}
/// <summary>
/// Reloads all configuration settings, groups, regions and raises the reload event.
/// </summary>
public void Reload(TSPlayer player)
{
FileTools.SetupConfig();
TShock.HandleCommandLinePostConfigLoad(Environment.GetCommandLineArgs());
TShock.Groups.LoadPermisions();
TShock.Regions.ReloadAllRegions();
Hooks.GeneralHooks.OnReloadEvent(player);
}
#if COMPAT_SIGS
[Obsolete("This method is for signature compatibility for external code only")]
public void ForceKick(TSPlayer player, string reason)
@ -641,7 +674,7 @@ namespace TShockAPI
{
string ip = player.IP;
string playerName = player.Name;
TShock.Bans.AddBan(ip, playerName, reason);
TShock.Bans.AddBan(ip, playerName, reason, false, adminUserName);
player.Disconnect(string.Format("Banned: {0}", reason));
Log.ConsoleInfo(string.Format("Banned {0} for : {1}", playerName, reason));
string verb = force ? "force " : "";
@ -654,6 +687,29 @@ namespace TShockAPI
return false;
}
public bool HasBanExpired(Ban ban, bool byName = false)
{
DateTime exp;
bool expirationExists = DateTime.TryParse(ban.Expiration, out exp);
if (!string.IsNullOrWhiteSpace(ban.Expiration) && (expirationExists) &&
(DateTime.Now >= exp))
{
if (byName)
{
TShock.Bans.RemoveBan(ban.Name, true, true, false);
}
else
{
TShock.Bans.RemoveBan(ban.IP, false, false, false);
}
return true;
}
return false;
}
/// <summary>
/// Shows a file to the user.
/// </summary>
@ -709,7 +765,7 @@ namespace TShockAPI
return TShock.Groups.groups[i];
}
}
return new Group(TShock.Config.DefaultGuestGroupName);
return Group.DefaultGroup;
}
/// <summary>
@ -841,5 +897,25 @@ namespace TShockAPI
}
return new string(returnstr);
}
/// <summary>
/// Enumerates boundary points of the given region's rectangle.
/// </summary>
/// <param name="regionArea">The region's area to enumerate through.</param>
/// <returns>The enumerated boundary points.</returns>
public IEnumerable<Point> EnumerateRegionBoundaries(Rectangle regionArea)
{
for (int x = 0; x < regionArea.Width + 1; x++)
{
yield return new Point(regionArea.Left + x, regionArea.Top);
yield return new Point(regionArea.Left + x, regionArea.Bottom);
}
for (int y = 1; y < regionArea.Height; y++)
{
yield return new Point(regionArea.Left, regionArea.Top + y);
yield return new Point(regionArea.Right, regionArea.Top + y);
}
}
}
}