Merge pull request #735 from NyxStudios/general-devel

Merge dev to master
This commit is contained in:
Lucas Nicodemus 2013-12-29 11:28:44 -08:00
commit 2f41b4dd84
50 changed files with 5832 additions and 3546 deletions

View file

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

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "TerrariaServerAPI"]
path = TerrariaServerAPI
url = https://github.com/Deathmax/TerrariaAPI-Server.git

View file

@ -1,36 +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__.
### 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

@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\Unit
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TShockRestTestPlugin", "TShockRestTestPlugin\TShockRestTestPlugin.csproj", "{F2FEDAFB-58DE-4611-9168-A86112C346C7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TerrariaAPI-Server", "TerrariaServerAPI\TerrariaAPI-Server.csproj", "{549A7941-D9C9-4544-BAC8-D9EEEAB4D777}"
EndProject
Global
GlobalSection(TestCaseManagementSettings) = postSolution
CategoryFile = Terraria.vsmdi
@ -52,6 +54,18 @@ Global
{F2FEDAFB-58DE-4611-9168-A86112C346C7}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{F2FEDAFB-58DE-4611-9168-A86112C346C7}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{F2FEDAFB-58DE-4611-9168-A86112C346C7}.Release|x86.ActiveCfg = Release|Any CPU
{549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Debug|Any CPU.Build.0 = Debug|Any CPU
{549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Debug|x86.ActiveCfg = Debug|x86
{549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Debug|x86.Build.0 = Debug|x86
{549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Release|Any CPU.ActiveCfg = Release|Any CPU
{549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Release|Any CPU.Build.0 = Release|Any CPU
{549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Release|x86.ActiveCfg = Release|x86
{549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

3491
TShockAPI/Commands.cs Normal file → Executable file

File diff suppressed because it is too large Load diff

51
TShockAPI/ConfigFile.cs Normal file → Executable file
View file

@ -80,6 +80,7 @@ namespace TShockAPI
[Description("Bans a hardcore player on death.")] public bool BanOnMediumcoreDeath;
[Description("Enable/disable Terraria's built in auto save.")] public bool AutoSave = true;
[Description("Enable/disable save announcements.")] public bool AnnounceSave = true;
[Description("Number of failed login attempts before kicking the player.")] public int MaximumLoginAttempts = 3;
@ -105,6 +106,8 @@ namespace TShockAPI
[Description("Enables kicking of banned users by matching their IP Address.")] public bool EnableIPBans = true;
[Description("Enables kicking of banned users by matching their client UUID.")] public bool EnableUUIDBans = true;
[Description("Enables kicking of banned users by matching their Character Name.")] public bool EnableBanOnUsernames;
[Description("Selects the default group name to place new registrants under.")] public string
@ -135,8 +138,6 @@ namespace TShockAPI
[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";
[Description("Enable/disable the rest api.")] public bool RestApiEnabled;
[Description("This is the port which the rest api will listen on.")] public int RestApiPort = 7878;
@ -145,19 +146,15 @@ namespace TShockAPI
[Description("Displays a player's IP on join to everyone who has the log permission.")] public bool DisplayIPToAdmins;
[Description(
"Some tiles are 'fixed' by not letting TShock handle them. Disabling this may break certain aesthetic tiles.")] public
bool EnableInsecureTileFixes = true;
[Description("Kicks users using a proxy as identified with the GeoIP database.")] public bool KickProxyUsers = true;
[Description("Disables hardmode, can't never be activated. Overrides /starthardmode.")] public bool DisableHardmode;
[Description("Disables the dungeon guardian from being spawned by player packets, this will instead force a respawn.")] public bool DisableDungeonGuardian;
[Description("Enable server side inventory checks, EXPERIMENTAL")] public bool ServerSideInventory;
[Description("Enable server side characters, This stops the client from saving character data! EXPERIMENTAL!!!!!")] public bool ServerSideCharacter;
[Description("How often SSI should save, in minutes.")] public int ServerSideInventorySave = 15;
[Description("How often SSC should save, in minutes.")] public int ServerSideCharacterSave = 5;
[Description("Time, in milliseconds, to disallow discarding items after logging in when ServerSideInventory is ON.")] public int LogonDiscardThreshold=250;
@ -169,7 +166,7 @@ namespace TShockAPI
"Changes ingame chat format: {0} = Group Name, {1} = Group Prefix, {2} = Player Name, {3} = Group Suffix, {4} = Chat Message"
)] public string ChatFormat = "{1}{2}{3}: {4}";
[Description("Change the chat format when using chat above heads. This begins with a player name wrapped in brackets, as per Terraria's formatting. Same formatting as ChatFormat.")] public string ChatAboveHeadsFormat = "{4}";
[Description("Change the player name when using chat above heads. This begins with a player name wrapped in brackets, as per Terraria's formatting. Same formatting as ChatFormat(minus the text aka {4}).")] public string ChatAboveHeadsFormat = "{2}";
[Description("Force the world time to be normal, day, or night.")] public string ForceTime = "normal";
@ -206,6 +203,10 @@ namespace TShockAPI
[Description("Disable users from being able to login with account password when joining.")] public bool
DisableLoginBeforeJoin;
[Description("Disable users from being able to login with their client UUID.")] public bool DisableUUIDLogin;
[Description("Kick clients that don't send a UUID to the server.")] public bool KickEmptyUUID;
[Description("Allows users to register any username with /register.")] public bool AllowRegisterAnyUsername;
[Description("Allows users to login with any username with /login.")] public bool AllowLoginAnyUsername = true;
@ -222,7 +223,10 @@ namespace TShockAPI
[Description("Allow ice placement even when user does not have canbuild.")] public bool AllowIce = false;
[Description("Allows corrutption to spread when a world is hardmode.")] public bool AllowCorruptionCreep = true;
[Description("Allows crimson to spread when a world is hardmode.")]
public bool AllowCrimsonCreep = true;
[Description("Allows corruption to spread when a world is hardmode.")] public bool AllowCorruptionCreep = true;
[Description("Allows hallow to spread when a world is hardmode.")] public bool AllowHallowCreep = true;
@ -234,8 +238,6 @@ namespace TShockAPI
[Description("Prevent banned items from being /i or /give.")] public bool PreventBannedItemSpawn = false;
[Description("Prevent banks on SSI.")] public bool DisablePiggybanksOnSSI = false;
[Description("Prevent players from interacting with the world if dead.")] public bool PreventDeadModification =
true;
@ -263,11 +265,22 @@ namespace TShockAPI
[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 number of reserved slots past your max server slot that can be joined by reserved players")] public int ReservedSlots = 20;
[Description("The number of reserved slots past your max server slot that can be joined by reserved players")] public bool LogRest = false;
[Description("The maximum value that a character may have for health.")] public int MaxMana = 400;
[Description("The number of seconds a player must wait before being respawned.")] public int RespawnSeconds = 3;
[Description("Disables a player if this number of tiles is painted within 1 second.")] public int TilePaintThreshold = 15;
[Description("Enables max packet bufferer size.")] public bool EnableMaxBytesInBuffer = false;
[Description("Number of bytes in the packet buffer before we disconnect the player.")] public int MaxBytesInBuffer = 5242880;
[Description("Forces your world to be in Halloween mode regardless of the data.")] public bool ForceHalloween = false;
[Description("Allows anyone to break grass, pots, etc.")] public bool AllowCutTilesAndBreakables = false;
[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>
@ -351,10 +364,10 @@ namespace TShockAPI
var def = field.GetValue(defaults);
sb.AppendLine("## {0} ".SFormat(name));
sb.AppendLine("**Type:** {0} ".SFormat(type));
sb.AppendLine("**Description:** {0} ".SFormat(desc));
sb.AppendLine("**Default:** \"{0}\" ".SFormat(def));
sb.AppendLine("{0} ".SFormat(name));
sb.AppendLine("Type: {0} ".SFormat(type));
sb.AppendLine("Description: {0} ".SFormat(desc));
sb.AppendLine("Default: \"{0}\" ".SFormat(def));
sb.AppendLine();
}

View file

@ -34,6 +34,7 @@ namespace TShockAPI.DB
var table = new SqlTable("Bans",
new SqlColumn("IP", MySqlDbType.String, 16) {Primary = true},
new SqlColumn("Name", MySqlDbType.Text),
new SqlColumn("UUID", MySqlDbType.Text),
new SqlColumn("Reason", MySqlDbType.Text),
new SqlColumn("BanningUser", MySqlDbType.Text),
new SqlColumn("Date", MySqlDbType.Text),
@ -54,6 +55,11 @@ namespace TShockAPI.DB
}
}
/// <summary>
/// Gets a ban by IP.
/// </summary>
/// <param name="ip">The IP.</param>
/// <returns>The ban.</returns>
public Ban GetBanByIp(string ip)
{
try
@ -61,7 +67,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"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration"));
return new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("UUID"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration"));
}
}
catch (Exception ex)
@ -71,6 +77,9 @@ namespace TShockAPI.DB
return null;
}
/// <summary>
/// Gets a list of bans.
/// </summary>
public List<Ban> GetBans()
{
List<Ban> banlist = new List<Ban>();
@ -80,7 +89,7 @@ namespace TShockAPI.DB
{
while (reader.Read())
{
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")));
banlist.Add(new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("UUID"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration")));
}
return banlist;
}
@ -93,6 +102,12 @@ namespace TShockAPI.DB
return null;
}
/// <summary>
/// Gets a ban by name.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="casesensitive">Whether to check with case sensitivity.</param>
/// <returns>The ban.</returns>
public Ban GetBanByName(string name, bool casesensitive = true)
{
try
@ -103,7 +118,29 @@ 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"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration"));
return new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("UUID"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration"));
}
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
return null;
}
/// <summary>
/// Gets a ban by UUID.
/// </summary>
/// <param name="uuid">The UUID.</param>
/// <returns>The ban.</returns>
public Ban GetBanByUUID(string uuid)
{
try
{
using (var reader = database.QueryReader("SELECT * FROM Bans WHERE UUID=@0", uuid))
{
if (reader.Read())
return new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("UUID"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration"));
}
}
catch (Exception ex)
@ -117,14 +154,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, string banner = "", string expiration = "")
public bool AddBan(string ip, string name = "", string uuid = "", string reason = "", bool exceptions = false, string banner = "", string expiration = "")
{
try
{
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;
return database.Query("INSERT INTO Bans (IP, Name, UUID, Reason, BanningUser, Date, Expiration) VALUES (@0, @1, @2, @3, @4, @5, @6);", ip, name, uuid, reason, banner, DateTime.UtcNow.ToString("s"), expiration) != 0;
}
catch (Exception ex)
{
@ -181,6 +218,8 @@ namespace TShockAPI.DB
public string Name { get; set; }
public string UUID { get; set; }
public string Reason { get; set; }
public string BanningUser { get; set; }
@ -189,10 +228,11 @@ namespace TShockAPI.DB
public string Expiration { get; set; }
public Ban(string ip, string name, string reason, string banner, string date, string exp)
public Ban(string ip, string name, string uuid, string reason, string banner, string date, string exp)
{
IP = ip;
Name = name;
UUID = UUID;
Reason = reason;
BanningUser = banner;
Date = date;
@ -203,6 +243,7 @@ namespace TShockAPI.DB
{
IP = string.Empty;
Name = string.Empty;
UUID = string.Empty;
Reason = string.Empty;
BanningUser = "";
Date = "";

149
TShockAPI/DB/CharacterManager.cs Executable file
View file

@ -0,0 +1,149 @@
/*
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.Data;
using MySql.Data.MySqlClient;
namespace TShockAPI.DB
{
public class CharacterManager
{
public IDbConnection database;
public CharacterManager(IDbConnection db)
{
database = db;
var table = new SqlTable("tsCharacter",
new SqlColumn("Account", MySqlDbType.Int32) {Primary = true},
new SqlColumn("Health", MySqlDbType.Int32),
new SqlColumn("MaxHealth", MySqlDbType.Int32),
new SqlColumn("Mana", MySqlDbType.Int32),
new SqlColumn("MaxMana", MySqlDbType.Int32),
new SqlColumn("Inventory", MySqlDbType.Text),
new SqlColumn("spawnX", MySqlDbType.Int32),
new SqlColumn("spawnY", MySqlDbType.Int32)
);
var creator = new SqlTableCreator(db,
db.GetSqlType() == SqlType.Sqlite
? (IQueryBuilder) new SqliteQueryCreator()
: new MysqlQueryCreator());
creator.EnsureExists(table);
}
public PlayerData GetPlayerData(TSPlayer player, int acctid)
{
PlayerData playerData = new PlayerData(player);
try
{
using (var reader = database.QueryReader("SELECT * FROM tsCharacter WHERE Account=@0", acctid))
{
if (reader.Read())
{
playerData.exists = true;
playerData.health = reader.Get<int>("Health");
playerData.maxHealth = reader.Get<int>("MaxHealth");
playerData.mana = reader.Get<int>("Mana");
playerData.maxMana = reader.Get<int>("MaxMana");
playerData.inventory = NetItem.Parse(reader.Get<string>("Inventory"));
playerData.spawnX = reader.Get<int>("spawnX");
playerData.spawnY = reader.Get<int>("spawnY");
return playerData;
}
}
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
return playerData;
}
public bool SeedInitialData(User user)
{
string initialItems = "-15,1,0~-13,1,0~-16,1,45~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0~0,0,0";
try
{
database.Query("INSERT INTO tsCharacter (Account, Health, MaxHealth, Mana, MaxMana, Inventory, spawnX, spawnY) VALUES (@0, @1, @2, @3, @4, @5, @6, @7);", user.ID,
100, 100, 20, 20, initialItems, -1, -1);
return true;
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
return false;
}
public bool InsertPlayerData(TSPlayer player)
{
PlayerData playerData = player.PlayerData;
if (!player.IsLoggedIn)
return false;
if (!GetPlayerData(player, player.UserID).exists)
{
try
{
database.Query("INSERT INTO tsCharacter (Account, Health, MaxHealth, Mana, MaxMana, Inventory, spawnX, spawnY) VALUES (@0, @1, @2, @3, @4, @5, @6, @7);", player.UserID,
playerData.health, playerData.maxHealth, playerData.mana, playerData.maxMana, NetItem.ToString(playerData.inventory), player.TPlayer.SpawnX, player.TPlayer.SpawnY);
return true;
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
}
else
{
try
{
database.Query("UPDATE tsCharacter SET Health = @0, MaxHealth = @1, Mana = @2, MaxMana = @3, Inventory = @4, spawnX = @6, spawnY = @7 WHERE Account = @5;", playerData.health, playerData.maxHealth,
playerData.mana, playerData.maxMana, NetItem.ToString(playerData.inventory), player.UserID, player.TPlayer.SpawnX, player.TPlayer.SpawnY);
return true;
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
}
return false;
}
public bool RemovePlayer(int userid)
{
try
{
database.Query("DELETE FROM tsCharacter WHERE Account = @0;", userid);
return true;
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
return false;
}
}
}

View file

@ -53,18 +53,18 @@ namespace TShockAPI.DB
LoadPermisions();
// Add default groups if they don't exist
AddDefaultGroup(TShock.Config.DefaultGuestGroupName, "",
AddDefaultGroup("guest", "",
string.Join(",", Permissions.canbuild, Permissions.canregister, Permissions.canlogin, Permissions.canpartychat,
Permissions.cantalkinthird));
Permissions.cantalkinthird, Permissions.canchat));
AddDefaultGroup("default", TShock.Config.DefaultGuestGroupName,
AddDefaultGroup("default", "guest",
string.Join(",", Permissions.warp, Permissions.canchangepassword));
AddDefaultGroup("newadmin", "default",
string.Join(",", Permissions.kick, Permissions.editspawn, Permissions.reservedslot));
AddDefaultGroup("admin", "newadmin",
string.Join(",", Permissions.ban, Permissions.whitelist, Permissions.causeevents, Permissions.spawnboss,
string.Join(",", Permissions.ban, Permissions.whitelist, "tshock.world.time.*", Permissions.spawnboss,
Permissions.spawnmob, Permissions.managewarp, Permissions.time, Permissions.tp, Permissions.slap,
Permissions.kill, Permissions.logs,
Permissions.immunetokick, Permissions.tphere));
@ -181,7 +181,7 @@ namespace TShockAPI.DB
/// <param name="parentname">parent of group</param>
/// <param name="permissions">permissions</param>
/// <param name="chatcolor">chatcolor</param>
public void UpdateGroup(string name, string parentname, string permissions, string chatcolor)
public void UpdateGroup(string name, string parentname, string permissions, string chatcolor, string suffix, string prefix)
{
Group group = GetGroupByName(name);
if (group == null)
@ -210,13 +210,17 @@ namespace TShockAPI.DB
// 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)
newGroup.Prefix = prefix;
newGroup.Suffix = suffix;
string query = "UPDATE GroupList SET Parent=@0, Commands=@1, ChatColor=@2, Suffix=@3, Prefix=@4 WHERE GroupName=@5";
if (database.Query(query, parentname, newGroup.Permissions, newGroup.ChatColor, suffix, prefix, name) != 1)
throw new GroupManagerException(string.Format("Failed to update group \"{0}\".", name));
group.ChatColor = chatcolor;
group.Permissions = permissions;
group.Parent = parent;
group.Prefix = prefix;
group.Suffix = suffix;
}
#if COMPAT_SIGS

View file

@ -45,8 +45,8 @@ namespace TShockAPI.DB
table.Columns.Select(
c =>
"'{0}' {1} {2} {3} {4} {5}".SFormat(c.Name, DbTypeToString(c.Type, c.Length), c.Primary ? "PRIMARY KEY" : "",
c.AutoIncrement ? "AUTOINCREMENT" : "", c.NotNull ? "NOT NULL" : "",
c.Unique ? "UNIQUE" : ""));
c.AutoIncrement ? "AUTOINCREMENT" : "", c.NotNull ? "NOT NULL" : "",
c.Unique ? "UNIQUE" : ""));
return "CREATE TABLE {0} ({1})".SFormat(EscapeTableName(table.Name), string.Join(", ", columns));
}
@ -57,14 +57,16 @@ namespace TShockAPI.DB
private static readonly Dictionary<MySqlDbType, string> TypesAsStrings = new Dictionary<MySqlDbType, string>
{
{MySqlDbType.VarChar, "TEXT"},
{MySqlDbType.String, "TEXT"},
{MySqlDbType.Text, "TEXT"},
{MySqlDbType.TinyText, "TEXT"},
{MySqlDbType.MediumText, "TEXT"},
{MySqlDbType.LongText, "TEXT"},
{MySqlDbType.Int32, "INTEGER"},
{MySqlDbType.Blob, "BLOB"},
{ MySqlDbType.VarChar, "TEXT" },
{ MySqlDbType.String, "TEXT" },
{ MySqlDbType.Text, "TEXT" },
{ MySqlDbType.TinyText, "TEXT" },
{ MySqlDbType.MediumText, "TEXT" },
{ MySqlDbType.LongText, "TEXT" },
{ MySqlDbType.Float, "REAL" },
{ MySqlDbType.Double, "REAL" },
{ MySqlDbType.Int32, "INTEGER" },
{ MySqlDbType.Blob, "BLOB" },
};
public string DbTypeToString(MySqlDbType type, int? length)
@ -72,7 +74,7 @@ namespace TShockAPI.DB
string ret;
if (TypesAsStrings.TryGetValue(type, out ret))
return ret;
throw new NotImplementedException(Enum.GetName(typeof (MySqlDbType), type));
throw new NotImplementedException(Enum.GetName(typeof(MySqlDbType), type));
}
protected override string EscapeTableName(string table)
@ -89,12 +91,12 @@ namespace TShockAPI.DB
table.Columns.Select(
c =>
"{0} {1} {2} {3} {4}".SFormat(c.Name, DbTypeToString(c.Type, c.Length), c.Primary ? "PRIMARY KEY" : "",
c.AutoIncrement ? "AUTO_INCREMENT" : "", c.NotNull ? "NOT NULL" : ""));
c.AutoIncrement ? "AUTO_INCREMENT" : "", c.NotNull ? "NOT NULL" : ""));
var uniques = table.Columns.Where(c => c.Unique).Select(c => c.Name);
return "CREATE TABLE {0} ({1} {2})".SFormat(EscapeTableName(table.Name), string.Join(", ", columns),
uniques.Count() > 0
? ", UNIQUE({0})".SFormat(string.Join(", ", uniques))
: "");
uniques.Count() > 0
? ", UNIQUE({0})".SFormat(string.Join(", ", uniques))
: "");
}
public override string RenameTable(string from, string to)
@ -104,21 +106,23 @@ namespace TShockAPI.DB
private static readonly Dictionary<MySqlDbType, string> TypesAsStrings = new Dictionary<MySqlDbType, string>
{
{MySqlDbType.VarChar, "VARCHAR"},
{MySqlDbType.String, "CHAR"},
{MySqlDbType.Text, "TEXT"},
{MySqlDbType.TinyText, "TINYTEXT"},
{MySqlDbType.MediumText, "MEDIUMTEXT"},
{MySqlDbType.LongText, "LONGTEXT"},
{MySqlDbType.Int32, "INT"},
{ MySqlDbType.VarChar, "VARCHAR" },
{ MySqlDbType.String, "CHAR" },
{ MySqlDbType.Text, "TEXT" },
{ MySqlDbType.TinyText, "TINYTEXT" },
{ MySqlDbType.MediumText, "MEDIUMTEXT" },
{ MySqlDbType.LongText, "LONGTEXT" },
{ MySqlDbType.Float, "FLOAT" },
{ MySqlDbType.Double, "DOUBLE" },
{ MySqlDbType.Int32, "INT" },
};
public string DbTypeToString(MySqlDbType type, int? length)
{
string ret;
if (TypesAsStrings.TryGetValue(type, out ret))
return ret + (length != null ? "({0})".SFormat((int) length) : "");
throw new NotImplementedException(Enum.GetName(typeof (MySqlDbType), type));
return ret + (length != null ? "({0})".SFormat((int)length) : "");
throw new NotImplementedException(Enum.GetName(typeof(MySqlDbType), type));
}
protected override string EscapeTableName(string table)

View file

@ -1,107 +0,0 @@
/*
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.Data;
using MySql.Data.MySqlClient;
namespace TShockAPI.DB
{
public class InventoryManager
{
public IDbConnection database;
public InventoryManager(IDbConnection db)
{
database = db;
var table = new SqlTable("Inventory",
new SqlColumn("Account", MySqlDbType.Int32) {Primary = true},
new SqlColumn("MaxHealth", MySqlDbType.Int32),
new SqlColumn("MaxMana", MySqlDbType.Int32),
new SqlColumn("Inventory", MySqlDbType.Text)
);
var creator = new SqlTableCreator(db,
db.GetSqlType() == SqlType.Sqlite
? (IQueryBuilder) new SqliteQueryCreator()
: new MysqlQueryCreator());
creator.EnsureExists(table);
}
public PlayerData GetPlayerData(TSPlayer player, int acctid)
{
PlayerData playerData = new PlayerData(player);
try
{
using (var reader = database.QueryReader("SELECT * FROM Inventory WHERE Account=@0", acctid))
{
if (reader.Read())
{
playerData.exists = true;
playerData.maxHealth = reader.Get<int>("MaxHealth");
playerData.inventory = NetItem.Parse(reader.Get<string>("Inventory"));
return playerData;
}
}
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
return playerData;
}
public bool InsertPlayerData(TSPlayer player)
{
PlayerData playerData = player.PlayerData;
if (!player.IsLoggedIn)
return false;
if (!GetPlayerData(player, player.UserID).exists)
{
try
{
database.Query("INSERT INTO Inventory (Account, MaxHealth, Inventory) VALUES (@0, @1, @2);", player.UserID,
playerData.maxHealth, NetItem.ToString(playerData.inventory));
return true;
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
}
else
{
try
{
database.Query("UPDATE Inventory SET MaxHealth = @0, Inventory = @1 WHERE Account = @2;", playerData.maxHealth,
NetItem.ToString(playerData.inventory), player.UserID);
return true;
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
}
return false;
}
}
}

View file

@ -196,13 +196,32 @@ namespace TShockAPI.DB
return Name == other.Name;
}
public bool HasPermissionToUseItem(TSPlayer ply)
{
if (ply == null)
return false;
return AllowedGroups.Contains(ply.Group.Name);
// could add in the other permissions in this class instead of a giant if switch.
}
public bool HasPermissionToUseItem(TSPlayer ply)
{
if (ply == null)
return false;
if (ply.Group.HasPermission(Permissions.usebanneditem))
return true;
var cur = ply.Group;
var traversed = new List<Group>();
while (cur != null)
{
if (AllowedGroups.Contains(cur.Name))
{
return true;
}
if (traversed.Contains(cur))
{
throw new InvalidOperationException("Infinite group parenting ({0})".SFormat(cur.Name));
}
traversed.Add(cur);
cur = cur.Parent;
}
return false;
// could add in the other permissions in this class instead of a giant if switch.
}
public void SetAllowedGroups(String groups)
{

View file

@ -27,11 +27,14 @@ namespace TShockAPI.DB
{
public class RegionManager
{
/// <summary>
/// The list of regions.
/// </summary>
public List<Region> Regions = new List<Region>();
private IDbConnection database;
public RegionManager(IDbConnection db)
internal RegionManager(IDbConnection db)
{
database = db;
var table = new SqlTable("Regions",
@ -52,11 +55,12 @@ namespace TShockAPI.DB
? (IQueryBuilder) new SqliteQueryCreator()
: new MysqlQueryCreator());
creator.EnsureExists(table);
ReloadAllRegions();
}
public void ReloadAllRegions()
/// <summary>
/// Reloads all regions.
/// </summary>
public void Reload()
{
try
{
@ -419,6 +423,33 @@ namespace TShockAPI.DB
return false;
}
/// <summary>
/// Sets the position of a region.
/// </summary>
/// <param name="regionName">The region name.</param>
/// <param name="x">The X position.</param>
/// <param name="y">The Y position.</param>
/// <param name="height">The height.</param>
/// <param name="width">The width.</param>
/// <returns>Whether the operation succeeded.</returns>
public bool PositionRegion(string regionName, int x, int y, int width, int height)
{
try
{
Region region = Regions.First(r => String.Equals(regionName, r.Name, StringComparison.OrdinalIgnoreCase));
region.Area = new Rectangle(x, y, width, height);
if (database.Query("UPDATE Regions SET X1 = @0, Y1 = @1, width = @2, height = @3 WHERE RegionName = @4 AND WorldID = @5",
x, y, width, height, regionName, Main.worldID.ToString()) > 0)
return true;
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
return false;
}
/// <summary>
/// Gets all the regions names from world
/// </summary>
@ -628,8 +659,7 @@ namespace TShockAPI.DB
return true;
}
return AllowedIDs.Contains(ply.UserID) || AllowedGroups.Contains(ply.Group.Name) || Owner == ply.UserAccountName ||
ply.Group.HasPermission("manageregion");
return AllowedIDs.Contains(ply.UserID) || AllowedGroups.Contains(ply.Group.Name) || Owner == ply.UserAccountName;
}
public void setAllowedIDs(String ids)

View file

@ -17,6 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.CodeDom.Compiler;
using System.Data;
using System.Collections.Generic;
using System.Linq;
@ -37,7 +38,9 @@ namespace TShockAPI.DB
new SqlColumn("ID", MySqlDbType.Int32) {Primary = true, AutoIncrement = true},
new SqlColumn("Username", MySqlDbType.VarChar, 32) {Unique = true},
new SqlColumn("Password", MySqlDbType.VarChar, 128),
new SqlColumn("UUID", MySqlDbType.VarChar, 128),
new SqlColumn("Usergroup", MySqlDbType.Text),
new SqlColumn("Registered", MySqlDbType.Text),
new SqlColumn("LastAccessed", MySqlDbType.Text),
new SqlColumn("KnownIPs", MySqlDbType.Text)
);
@ -60,8 +63,8 @@ namespace TShockAPI.DB
int ret;
try
{
ret = database.Query("INSERT INTO Users (Username, Password, UserGroup) VALUES (@0, @1, @2);", user.Name,
TShock.Utils.HashPassword(user.Password), user.Group);
ret = database.Query("INSERT INTO Users (Username, Password, UUID, UserGroup, Registered) VALUES (@0, @1, @2, @3, @4);", user.Name,
TShock.Utils.HashPassword(user.Password), user.UUID, user.Group, DateTime.UtcNow.ToString("s"));
}
catch (Exception ex)
{
@ -114,6 +117,26 @@ namespace TShockAPI.DB
}
}
/// <summary>
/// Sets the UUID for a given username
/// </summary>
/// <param name="user">User user</param>
/// <param name="group">string uuid</param>
public void SetUserUUID(User user, string uuid)
{
try
{
if (
database.Query("UPDATE Users SET UUID = @0 WHERE Username = @1;", uuid,
user.Name) == 0)
throw new UserNotExistException(user.Name);
}
catch (Exception ex)
{
throw new UserManagerException("SetUserUUID SQL returned an error", ex);
}
}
/// <summary>
/// Sets the group for a given username
/// </summary>
@ -146,7 +169,7 @@ namespace TShockAPI.DB
{
try
{
if (database.Query("UPDATE Users SET LastAccessed = @0, KnownIps = @1 WHERE Username = @2;", DateTime.Now.ToString("G"), user.KnownIps, user.Name) == 0)
if (database.Query("UPDATE Users SET LastAccessed = @0, KnownIps = @1 WHERE Username = @2;", DateTime.UtcNow.ToString("s"), user.KnownIps, user.Name) == 0)
throw new UserNotExistException(user.Name);
}
catch (Exception ex)
@ -267,7 +290,9 @@ namespace TShockAPI.DB
user.ID = result.Get<int>("ID");
user.Group = result.Get<string>("Usergroup");
user.Password = result.Get<string>("Password");
user.UUID = result.Get<string>("UUID");
user.Name = result.Get<string>("Username");
user.Registered = result.Get<string>("Registered");
user.LastAccessed = result.Get<string>("LastAccessed");
user.KnownIps = result.Get<string>("KnownIps");
return user;
@ -279,15 +304,19 @@ namespace TShockAPI.DB
public int ID { get; set; }
public string Name { get; set; }
public string Password { get; set; }
public string UUID { get; set; }
public string Group { get; set; }
public string Registered { get; set; }
public string LastAccessed { get; set; }
public string KnownIps { get; set; }
public User(string name, string pass, string group, string last, string known)
public User(string name, string pass, string uuid, string group, string registered, string last, string known)
{
Name = name;
Password = pass;
UUID = uuid;
Group = group;
Registered = registered;
LastAccessed = last;
KnownIps = known;
}
@ -296,7 +325,9 @@ namespace TShockAPI.DB
{
Name = "";
Password = "";
UUID = "";
Group = "";
Registered = "";
LastAccessed = "";
KnownIps = "";
}

View file

@ -20,6 +20,7 @@ using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using MySql.Data.MySqlClient;
using Terraria;
@ -28,9 +29,13 @@ namespace TShockAPI.DB
public class WarpManager
{
private IDbConnection database;
/// <summary>
/// The list of warps.
/// </summary>
public List<Warp> Warps = new List<Warp>();
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
public WarpManager(IDbConnection db)
internal WarpManager(IDbConnection db)
{
database = db;
@ -48,12 +53,23 @@ namespace TShockAPI.DB
creator.EnsureExists(table);
}
public bool AddWarp(int x, int y, string name, string worldid)
/// <summary>
/// Adds a warp.
/// </summary>
/// <param name="x">The X position.</param>
/// <param name="y">The Y position.</param>
/// <param name="name">The name.</param>
/// <returns>Whether the opration succeeded.</returns>
public bool Add(int x, int y, string name)
{
try
{
database.Query("INSERT INTO Warps (X, Y, WarpName, WorldID) VALUES (@0, @1, @2, @3);", x, y, name, worldid);
return true;
if (database.Query("INSERT INTO Warps (X, Y, WarpName, WorldID) VALUES (@0, @1, @2, @3);",
x, y, name, Main.worldID.ToString()) > 0)
{
Warps.Add(new Warp(new Point(x, y), name));
return true;
}
}
catch (Exception ex)
{
@ -62,12 +78,41 @@ namespace TShockAPI.DB
return false;
}
public bool RemoveWarp(string name)
/// <summary>
/// Reloads all warps.
/// </summary>
public void ReloadWarps()
{
Warps.Clear();
using (var reader = database.QueryReader("SELECT * FROM Warps WHERE WorldID = @0",
Main.worldID.ToString()))
{
while (reader.Read())
{
Warps.Add(new Warp(
new Point(reader.Get<int>("X"), reader.Get<int>("Y")),
reader.Get<string>("WarpName"),
(reader.Get<string>("Private") ?? "0") != "0"));
}
}
}
/// <summary>
/// Removes a warp.
/// </summary>
/// <param name="warpName">The warp name.</param>
/// <returns>Whether the operation succeeded.</returns>
public bool Remove(string warpName)
{
try
{
database.Query("DELETE FROM Warps WHERE WarpName=@0 AND WorldID=@1", name, Main.worldID.ToString());
return true;
if (database.Query("DELETE FROM Warps WHERE WarpName = @0 AND WorldID = @1",
warpName, Main.worldID.ToString()) > 0)
{
Warps.RemoveAll(w => String.Equals(w.Name, warpName, StringComparison.OrdinalIgnoreCase));
return true;
}
}
catch (Exception ex)
{
@ -76,113 +121,121 @@ namespace TShockAPI.DB
return false;
}
public Warp FindWarp(string name)
/// <summary>
/// Finds the warp with the given name.
/// </summary>
/// <param name="warpName">The name.</param>
/// <returns>The warp, if it exists, or else null.</returns>
public Warp Find(string warpName)
{
return Warps.FirstOrDefault(w => String.Equals(w.Name, warpName, StringComparison.OrdinalIgnoreCase));
}
/// <summary>
/// Finds the warp with the given name.
/// </summary>
/// <param name="warpName">The name.</param>
/// <returns>The warp, if it exists, or else null.</returns>
[Obsolete]
public Warp FindWarp(string warpName)
{
return Warps.FirstOrDefault(w => String.Equals(w.Name, warpName, StringComparison.OrdinalIgnoreCase));
}
/// <summary>
/// Sets the position of a warp.
/// </summary>
/// <param name="warpName">The warp name.</param>
/// <param name="x">The X position.</param>
/// <param name="y">The Y position.</param>
/// <returns>Whether the operation suceeded.</returns>
public bool Position(string warpName, int x, int y)
{
try
{
using (
var reader = database.QueryReader("SELECT * FROM Warps WHERE WarpName=@0 AND WorldID=@1", name,
Main.worldID.ToString()))
if (database.Query("UPDATE Warps SET X = @0, Y = @1 WHERE WarpName = @2 AND WorldID = @3",
x, y, warpName, Main.worldID.ToString()) > 0)
{
if (reader.Read())
{
try
{
return new Warp(new Vector2(reader.Get<int>("X"), reader.Get<int>("Y")), reader.Get<string>("WarpName"),
reader.Get<string>("WorldID"), reader.Get<string>("Private"));
}
catch
{
return new Warp(new Vector2(reader.Get<int>("X"), reader.Get<int>("Y")), reader.Get<string>("WarpName"),
reader.Get<string>("WorldID"), "0");
}
}
Warps.Find(w => String.Equals(w.Name, warpName, StringComparison.OrdinalIgnoreCase)).Position = new Point(x, y);
return true;
}
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
return new Warp();
return false;
}
/// <summary>
/// Gets all the warps names from world
/// Sets the hidden state of a warp.
/// </summary>
/// <param name="worldid">World name to get warps from</param>
/// <returns>List of warps with only their names</returns>
public List<Warp> ListAllPublicWarps(string worldid)
/// <param name="warpName">The warp name.</param>
/// <param name="state">The state.</param>
/// <returns>Whether the operation suceeded.</returns>
public bool Hide(string warpName, bool state)
{
var warps = new List<Warp>();
try
{
using (var reader = database.QueryReader("SELECT * FROM Warps WHERE WorldID=@0", worldid))
if (database.Query("UPDATE Warps SET Private = @0 WHERE WarpName = @1 AND WorldID = @2",
state ? "1" : "0", warpName, Main.worldID.ToString()) > 0)
{
while (reader.Read())
{
try
{
if (reader.Get<String>("Private") == "0" || reader.Get<String>("Private") == null)
warps.Add(new Warp {WarpName = reader.Get<string>("WarpName")});
}
catch
{
warps.Add(new Warp {WarpName = reader.Get<string>("WarpName")});
}
}
Warps.Find(w => String.Equals(w.Name, warpName, StringComparison.OrdinalIgnoreCase)).IsPrivate = state;
return true;
}
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
return warps;
}
/// <summary>
/// Gets all the warps names from world
/// </summary>
/// <param name="worldid">World name to get warps from</param>
/// <returns>List of warps with only their names</returns>
public bool HideWarp(string warp, bool state)
{
try
{
string query = "UPDATE Warps SET Private=@0 WHERE WarpName=@1 AND WorldID=@2";
database.Query(query, state ? "1" : "0", warp, Main.worldID.ToString());
return true;
}
catch (Exception ex)
{
Log.Error(ex.ToString());
return false;
}
return false;
}
}
/// <summary>
/// Represents a warp.
/// </summary>
public class Warp
{
public Vector2 WarpPos { get; set; }
public string WarpName { get; set; }
public string WorldWarpID { get; set; }
public string Private { get; set; }
public Warp(Vector2 warppos, string name, string worldid, string hidden)
/// <summary>
/// Gets or sets the name.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets or sets the warp's privacy state.
/// </summary>
public bool IsPrivate { get; set; }
/// <summary>
/// Gets or sets the position.
/// </summary>
public Point Position { get; set; }
/// <summary>
/// Gets or sets the position.
/// </summary>
[Obsolete]
public Vector2 WarpPos
{
WarpPos = warppos;
WarpName = name;
WorldWarpID = worldid;
Private = hidden;
get { return new Vector2(Position.X, Position.Y); }
set { Position = new Point((int)value.X, (int)value.Y); }
}
public Warp(Point position, string name, bool isPrivate = false)
{
Name = name;
Position = position;
IsPrivate = isPrivate;
}
[Obsolete]
public Warp(Vector2 position, string name, bool isPrivate = false)
{
Name = name;
WarpPos = position;
IsPrivate = isPrivate;
}
public Warp()
{
WarpPos = Vector2.Zero;
WarpName = null;
WorldWarpID = string.Empty;
Private = "0";
Position = Point.Zero;
Name = "";
IsPrivate = false;
}
}
}

1841
TShockAPI/GetDataHandlers.cs Normal file → Executable file

File diff suppressed because it is too large Load diff

View file

@ -70,11 +70,11 @@ namespace TShockAPI
/// <summary>
/// The chat color of the group.
/// Returns "255255255", sets "255,255,255"
/// Returns "255,255,255", sets "255,255,255"
/// </summary>
public string ChatColor
{
get { return string.Format("{0}{1}{2}", R.ToString("X2"), G.ToString("X2"), B.ToString("X2")); }
get { return string.Format("{0},{1},{2}", R.ToString("D3"), G.ToString("D3"), B.ToString("D3")); }
set
{
if (null != value)
@ -118,7 +118,7 @@ namespace TShockAPI
/// <summary>
/// The permissions of this group and all that it inherits from.
/// </summary>
public List<string> TotalPermissions
public virtual List<string> TotalPermissions
{
get
{
@ -175,24 +175,30 @@ namespace TShockAPI
/// <param name="permission">The permission to check.</param>
/// <returns>Returns true if the user has that permission.</returns>
public virtual bool HasPermission(string permission)
{
if (String.IsNullOrEmpty(permission) || RealHasPermission(permission))
{
bool negated = false;
if (String.IsNullOrEmpty(permission) || (RealHasPermission(permission, ref negated) && !negated))
{
return true;
}
if (negated)
return false;
string[] nodes = permission.Split('.');
for (int i = nodes.Length - 1; i >= 0; i--)
{
nodes[i] = "*";
if (RealHasPermission(String.Join(".", nodes, 0, i + 1)))
if (RealHasPermission(String.Join(".", nodes, 0, i + 1), ref negated))
{
return true;
return !negated;
}
}
return false;
}
private bool RealHasPermission(string permission)
private bool RealHasPermission(string permission, ref bool negated)
{
negated = false;
if (string.IsNullOrEmpty(permission))
return true;
@ -200,9 +206,12 @@ namespace TShockAPI
var traversed = new List<Group>();
while (cur != null)
{
if (cur.negatedpermissions.Contains(permission))
return false;
if (cur.permissions.Contains(permission))
if (cur.negatedpermissions.Contains(permission))
{
negated = true;
return false;
}
if (cur.permissions.Contains(permission))
return true;
if (traversed.Contains(cur))
{
@ -226,6 +235,12 @@ namespace TShockAPI
negatedpermissions.Add(permission);
permissions.Remove(permission); // Ensure we don't have conflicting definitions for a permissions
}
for (int i = 0; i < TShock.Players.Length; i++)
{
if (TShock.Players[i] != null && TShock.Players[i].IsRaptor)
TShock.Players[i].SendRaptorPermissions();
}
}
/// <summary>
@ -245,6 +260,12 @@ namespace TShockAPI
permissions.Add(permission);
negatedpermissions.Remove(permission); // Ensure we don't have conflicting definitions for a permissions
}
for (int i = 0; i < TShock.Players.Length; i++)
{
if (TShock.Players[i] != null && TShock.Players[i].IsRaptor)
TShock.Players[i].SendRaptorPermissions();
}
}
/// <summary>
@ -272,6 +293,11 @@ namespace TShockAPI
return;
}
permissions.Remove(permission);
for (int i = 0; i < TShock.Players.Length; i++)
{
if (TShock.Players[i] != null && TShock.Players[i].IsRaptor && TShock.Players[i].Group == this)
TShock.Players[i].SendRaptorPermissions();
}
}
/// <summary>
@ -300,6 +326,10 @@ namespace TShockAPI
/// </summary>
public class SuperAdminGroup : Group
{
public override List<string> TotalPermissions
{
get { return new List<string> { "*" }; }
}
public SuperAdminGroup()
: base("superadmin")
{

View file

@ -58,7 +58,7 @@ namespace TShockAPI.Hooks
public static void OnPlayerPostLogin(TSPlayer ply)
{
if(PlayerPostLogin == null)
if (PlayerPostLogin == null)
{
return;
}

View file

@ -78,7 +78,7 @@ namespace TShockAPI
/// </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)
public static void Data(string format, params object[] args)
{
Data(String.Format(format, args));
}
@ -97,7 +97,7 @@ namespace TShockAPI
/// </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)
public static void Error(string format, params object[] args)
{
Error(String.Format(format, args));
}
@ -119,7 +119,7 @@ namespace TShockAPI
/// </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)
public static void ConsoleError(string format, params object[] args)
{
ConsoleError(String.Format(format, args));
}
@ -138,7 +138,7 @@ namespace TShockAPI
/// </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)
public static void Warn(string format, params object[] args)
{
Warn(String.Format(format, args));
}
@ -157,7 +157,7 @@ namespace TShockAPI
/// </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)
public static void Info(string format, params object[] args)
{
Info(String.Format(format, args));
}
@ -179,7 +179,7 @@ namespace TShockAPI
/// </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)
public static void ConsoleInfo(string format, params object[] args)
{
ConsoleInfo(String.Format(format, args));
}
@ -198,7 +198,7 @@ namespace TShockAPI
/// </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)
public static void Debug(string format, params object[] args)
{
Debug(String.Format(format, args));
}

View file

@ -29,10 +29,32 @@ namespace TShockAPI.Net
public byte Type { get; set; }
public short FrameX { get; set; }
public short FrameY { get; set; }
public bool Lighted { get; set; }
public byte Wall { get; set; }
public byte Liquid { get; set; }
public bool Lava { get; set; }
public byte LiquidType { get; set; }
public bool Wire { get; set; }
public bool Wire2 { get; set; }
public bool Wire3 { get; set; }
public byte HalfBrick { get; set; }
public byte Actuator { get; set; }
public bool Inactive { get; set; }
public bool IsHalf { get; set; }
public bool IsActuator { get; set; }
public byte TileColor { get; set; }
public byte WallColor { get; set; }
public bool Slope { get; set; }
public bool Slope2 { get; set; }
public bool HasColor
{
get { return TileColor > 0; }
}
public bool HasWallColor
{
get { return WallColor > 0; }
}
public bool HasWall
{
@ -57,8 +79,17 @@ namespace TShockAPI.Net
FrameY = -1;
Wall = 0;
Liquid = 0;
Lava = false;
Wire = false;
Wire2 = false;
Wire3 = false;
HalfBrick = 0;
Actuator = 0;
Inactive = false;
TileColor = 0;
WallColor = 0;
Lighted = false;
Slope = false;
Slope2 = false;
}
public NetTile(Stream stream)
@ -71,9 +102,12 @@ namespace TShockAPI.Net
{
var flags = TileFlags.None;
if (Active)
if ((Active) && (!Inactive))
flags |= TileFlags.Active;
if (Lighted)
flags |= TileFlags.Lighted;
if (HasWall)
flags |= TileFlags.Wall;
@ -82,9 +116,53 @@ namespace TShockAPI.Net
if (Wire)
flags |= TileFlags.Wire;
if (IsHalf)
flags |= TileFlags.HalfBrick;
if (IsActuator)
flags |= TileFlags.Actuator;
if (Inactive)
{
flags |= TileFlags.Inactive;
}
stream.WriteInt8((byte) flags);
var flags2 = TileFlags2.None;
if ((Wire2))
flags2 |= TileFlags2.Wire2;
if (Wire3)
flags2 |= TileFlags2.Wire3;
if (HasColor)
flags2 |= TileFlags2.Color;
if (HasWallColor)
flags2 |= TileFlags2.WallColor;
if (Slope)
flags2 |= TileFlags2.Slope;
if (Slope2)
flags2 |= TileFlags2.Slope2;
stream.WriteInt8((byte)flags2);
if (HasColor)
{
stream.WriteByte(TileColor);
}
if (HasWallColor)
{
stream.WriteByte(WallColor);
}
if (Active)
{
stream.WriteInt8(Type);
@ -101,13 +179,29 @@ namespace TShockAPI.Net
if (HasLiquid)
{
stream.WriteInt8(Liquid);
stream.WriteBoolean(Lava);
stream.WriteInt8(LiquidType);
}
}
public void Unpack(Stream stream)
{
var flags = (TileFlags) stream.ReadInt8();
var flags2 = (TileFlags2)stream.ReadInt8();
Wire2 = flags2.HasFlag(TileFlags2.Wire2);
Wire3 = flags2.HasFlag(TileFlags2.Wire3);
Slope = flags2.HasFlag(TileFlags2.Slope);
Slope2 = flags2.HasFlag(TileFlags2.Slope2);
if (flags2.HasFlag(TileFlags2.Color))
{
TileColor = stream.ReadInt8();
}
if (flags2.HasFlag(TileFlags2.WallColor))
{
WallColor = stream.ReadInt8();
}
Active = flags.HasFlag(TileFlags.Active);
if (Active)
@ -120,6 +214,11 @@ namespace TShockAPI.Net
}
}
if (flags.HasFlag(TileFlags.Lighted))
{
Lighted = true;
}
if (flags.HasFlag(TileFlags.Wall))
{
Wall = stream.ReadInt8();
@ -128,11 +227,23 @@ namespace TShockAPI.Net
if (flags.HasFlag(TileFlags.Liquid))
{
Liquid = stream.ReadInt8();
Lava = stream.ReadBoolean();
LiquidType = stream.ReadInt8();
}
if (flags.HasFlag(TileFlags.Wire))
Wire = true;
if (flags.HasFlag(TileFlags.HalfBrick))
IsHalf = true;
if (flags.HasFlag(TileFlags.Actuator))
IsActuator = true;
if (flags.HasFlag(TileFlags.Inactive))
{
Inactive = true;
Active = false;
}
}
}
@ -144,6 +255,21 @@ namespace TShockAPI.Net
Lighted = 2,
Wall = 4,
Liquid = 8,
Wire = 16
Wire = 16,
HalfBrick = 32,
Actuator = 64,
Inactive = 128
}
}
[Flags]
public enum TileFlags2 : byte
{
None = 0,
Wire2 = 1,
Wire3 = 2,
Color = 4,
WallColor = 8,
Slope = 16,
Slope2 = 32
}
}

View file

@ -25,7 +25,7 @@ namespace TShockAPI.Net
{
public override PacketTypes ID
{
get { return PacketTypes.ProjectileNew; }
get{ return PacketTypes.ProjectileNew; }
}
public short Index { get; set; }

View file

@ -24,7 +24,7 @@ using System.Text;
namespace TShockAPI.Net
{
[Flags]
public enum WorldInfoFlag : byte
public enum BossFlags : byte
{
None = 0,
OrbSmashed = 1,
@ -32,7 +32,21 @@ namespace TShockAPI.Net
DownedBoss2 = 4,
DownedBoss3 = 8,
HardMode = 16,
DownedClown = 32
DownedClown = 32,
ServerSideCharacter = 64
}
[Flags]
public enum BossFlags2 : byte
{
None = 0,
DownedMechBoss1 = 1,
DownedMechBoss2 = 2,
DownedMechBoss3 = 4,
DownedMechBossAny = 8,
CloudBg = 16,
Crimson = 32,
Pumpkin = 64
}
public class WorldInfoMsg : BaseMsg
@ -41,6 +55,7 @@ namespace TShockAPI.Net
public bool DayTime { get; set; }
public byte MoonPhase { get; set; }
public bool BloodMoon { get; set; }
public bool Eclipse { get; set; }
public int MaxTilesX { get; set; }
public int MaxTilesY { get; set; }
public int SpawnX { get; set; }
@ -48,7 +63,37 @@ namespace TShockAPI.Net
public int WorldSurface { get; set; }
public int RockLayer { get; set; }
public int WorldID { get; set; }
public WorldInfoFlag WorldFlags { get; set; }
public byte MoonType { get; set; }
public int TreeX0 { get; set; }
public int TreeX1 { get; set; }
public int TreeX2 { get; set; }
public byte TreeStyle0 { get; set; }
public byte TreeStyle1 { get; set; }
public byte TreeStyle2 { get; set; }
public byte TreeStyle3 { get; set; }
public int CaveBackX0 { get; set; }
public int CaveBackX1 { get; set; }
public int CaveBackX2 { get; set; }
public byte CaveBackStyle0 { get; set; }
public byte CaveBackStyle1 { get; set; }
public byte CaveBackStyle2 { get; set; }
public byte CaveBackStyle3 { get; set; }
public byte SetBG0 { get; set; }
public byte SetBG1 { get; set; }
public byte SetBG2 { get; set; }
public byte SetBG3 { get; set; }
public byte SetBG4 { get; set; }
public byte SetBG5 { get; set; }
public byte SetBG6 { get; set; }
public byte SetBG7 { get; set; }
public byte IceBackStyle { get; set; }
public byte JungleBackStyle { get; set; }
public byte HellBackStyle { get; set; }
public float WindSpeed { get; set; }
public byte NumberOfClouds { get; set; }
public BossFlags BossFlags { get; set; }
public BossFlags2 BossFlags2 { get; set; }
public float Rain { get; set; }
public string WorldName { get; set; }
public override PacketTypes ID
@ -62,6 +107,7 @@ namespace TShockAPI.Net
stream.WriteBoolean(DayTime);
stream.WriteInt8(MoonPhase);
stream.WriteBoolean(BloodMoon);
stream.WriteBoolean(Eclipse);
stream.WriteInt32(MaxTilesX);
stream.WriteInt32(MaxTilesY);
stream.WriteInt32(SpawnX);
@ -69,7 +115,37 @@ namespace TShockAPI.Net
stream.WriteInt32(WorldSurface);
stream.WriteInt32(RockLayer);
stream.WriteInt32(WorldID);
stream.WriteInt8((byte) WorldFlags);
stream.WriteByte(MoonType);
stream.WriteInt32(TreeX0);
stream.WriteInt32(TreeX1);
stream.WriteInt32(TreeX2);
stream.WriteByte(TreeStyle0);
stream.WriteByte(TreeStyle1);
stream.WriteByte(TreeStyle2);
stream.WriteByte(TreeStyle3);
stream.WriteInt32(CaveBackX0);
stream.WriteInt32(CaveBackX1);
stream.WriteInt32(CaveBackX2);
stream.WriteByte(CaveBackStyle0);
stream.WriteByte(CaveBackStyle1);
stream.WriteByte(CaveBackStyle2);
stream.WriteByte(CaveBackStyle3);
stream.WriteByte(SetBG0);
stream.WriteByte(SetBG1);
stream.WriteByte(SetBG2);
stream.WriteByte(SetBG3);
stream.WriteByte(SetBG4);
stream.WriteByte(SetBG5);
stream.WriteByte(SetBG6);
stream.WriteByte(SetBG7);
stream.WriteByte(IceBackStyle);
stream.WriteByte(JungleBackStyle);
stream.WriteByte(HellBackStyle);
stream.WriteSingle(WindSpeed);
stream.WriteByte(NumberOfClouds);
stream.WriteInt8((byte)BossFlags);
stream.WriteInt8((byte)BossFlags2);
stream.WriteSingle(Rain);
stream.WriteBytes(Encoding.UTF8.GetBytes(WorldName));
}
}

View file

@ -22,8 +22,8 @@ using System.ComponentModel;
using System.IO;
using System.Net.Sockets;
using System.Text;
using Hooks;
using Terraria;
using TerrariaApi.Server;
namespace TShockAPI
{
@ -34,6 +34,8 @@ namespace TShockAPI
/// </summary>
public int BytesPerUpdate { get; set; }
private readonly TShock plugin;
private PacketBuffer[] buffers = new PacketBuffer[Netplay.serverSock.Length];
private int[] Bytes = new int[52];
@ -45,8 +47,9 @@ namespace TShockAPI
Command flush;
#endif
public PacketBufferer()
public PacketBufferer(TShock p)
{
plugin = p;
BytesPerUpdate = 0xFFFF;
for (int i = 0; i < buffers.Length; i++)
buffers[i] = new PacketBuffer();
@ -58,9 +61,9 @@ namespace TShockAPI
Commands.ChatCommands.Add(flush);
#endif
NetHooks.SendBytes += ServerHooks_SendBytes;
ServerHooks.SocketReset += ServerHooks_SocketReset;
GameHooks.PostUpdate += GameHooks_Update;
ServerApi.Hooks.NetSendBytes.Register(plugin, ServerHooks_SendBytes);
ServerApi.Hooks.ServerSocketReset.Register(plugin, ServerHooks_SocketReset);
ServerApi.Hooks.GamePostUpdate.Register(plugin, GameHooks_Update);
}
~PacketBufferer()
@ -82,9 +85,9 @@ namespace TShockAPI
Commands.ChatCommands.Remove(dump);
Commands.ChatCommands.Remove(flush);
#endif
NetHooks.SendBytes -= ServerHooks_SendBytes;
ServerHooks.SocketReset -= ServerHooks_SocketReset;
GameHooks.PostUpdate -= GameHooks_Update;
ServerApi.Hooks.NetSendBytes.Deregister(plugin, ServerHooks_SendBytes);
ServerApi.Hooks.ServerSocketReset.Deregister(plugin, ServerHooks_SocketReset);
ServerApi.Hooks.GamePostUpdate.Deregister(plugin, GameHooks_Update);
}
}
@ -107,7 +110,7 @@ namespace TShockAPI
Compressed = new int[52];
}
private void GameHooks_Update()
private void GameHooks_Update(EventArgs args)
{
FlushAll();
}
@ -148,9 +151,9 @@ namespace TShockAPI
}
private void ServerHooks_SocketReset(ServerSock socket)
private void ServerHooks_SocketReset(SocketResetEventArgs args)
{
buffers[socket.whoAmI] = new PacketBuffer();
buffers[args.Socket.whoAmI] = new PacketBuffer();
}
public bool SendBytes(ServerSock socket, byte[] buffer)
@ -179,6 +182,12 @@ namespace TShockAPI
{
buffers[socket.whoAmI].AddRange(ms.ToArray());
}
if (TShock.Config.EnableMaxBytesInBuffer && buffers[socket.whoAmI].Count > TShock.Config.MaxBytesInBuffer)
{
buffers[socket.whoAmI].Clear();
socket.kill = true;
}
}
}
@ -204,6 +213,7 @@ namespace TShockAPI
switch ((uint)e.ErrorCode)
{
case 0x80004005:
case 10053:
break;
default:
Log.Warn(e.ToString());
@ -217,10 +227,10 @@ namespace TShockAPI
return false;
}
private void ServerHooks_SendBytes(ServerSock socket, byte[] buffer, int offset, int count, HandledEventArgs e)
private void ServerHooks_SendBytes(SendBytesEventArgs args)
{
e.Handled = true;
BufferBytes(socket, buffer, offset, count);
args.Handled = true;
BufferBytes(args.Socket, args.Buffer, args.Offset, args.Count);
}
#if DEBUG_NET

View file

@ -23,6 +23,9 @@ using System.Text;
namespace TShockAPI
{
/// <summary>
/// Provides tools for sending paginated output.
/// </summary>
public static class PaginationTools
{
public delegate Tuple<string, Color> LineFormatterDelegate(object lineData, int lineIndex, int pageNumber);
@ -105,7 +108,7 @@ namespace TShockAPI
this.FooterTextColor = Color.Yellow;
this.NothingToDisplayString = null;
this.LineFormatter = null;
this.LineTextColor = Color.White;
this.LineTextColor = Color.Yellow;
this.maxLinesPerPage = 4;
this.pageLimit = 0;
}
@ -122,14 +125,10 @@ namespace TShockAPI
{
if (settings.NothingToDisplayString != null)
{
if (player is TSServerPlayer)
{
if (!player.RealPlayer)
player.SendSuccessMessage(settings.NothingToDisplayString);
}
else
{
player.SendMessage(settings.NothingToDisplayString, settings.HeaderTextColor);
}
}
return;
}
@ -142,14 +141,10 @@ namespace TShockAPI
if (settings.IncludeHeader)
{
if (player is TSServerPlayer)
{
if (!player.RealPlayer)
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;
@ -196,14 +191,10 @@ namespace TShockAPI
if (lineMessage != null)
{
if (player is TSServerPlayer)
{
Console.WriteLine(lineMessage);
}
if (!player.RealPlayer)
player.SendInfoMessage(lineMessage);
else
{
player.SendMessage(lineMessage, lineColor);
}
}
}
@ -211,26 +202,18 @@ namespace TShockAPI
{
if (settings.NothingToDisplayString != null)
{
if (player is TSServerPlayer)
{
if (!player.RealPlayer)
player.SendSuccessMessage(settings.NothingToDisplayString);
}
else
{
player.SendMessage(settings.NothingToDisplayString, settings.HeaderTextColor);
}
}
}
else if (settings.IncludeFooter && pageNumber + 1 <= pageCount)
{
if (player is TSServerPlayer)
{
if (!player.RealPlayer)
player.SendInfoMessage(string.Format(settings.FooterFormat, pageNumber + 1, pageNumber, pageCount));
}
else
{
player.SendMessage(string.Format(settings.FooterFormat, pageNumber + 1, pageNumber, pageCount), settings.FooterTextColor);
}
}
}
@ -239,11 +222,11 @@ namespace TShockAPI
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)
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)
@ -254,9 +237,7 @@ namespace TShockAPI
{
try
{
termString = termFormatter(term);
if (termString == null)
if ((termString = termFormatter(term)) == null)
continue;
}
catch (Exception ex)
@ -270,41 +251,35 @@ namespace TShockAPI
termString = term.ToString();
}
bool goesOnNextLine = (lineBuilder.Length + termString.Length > maxCharsPerLine);
if (!goesOnNextLine)
if (lineBuilder.Length + termString.Length + separator.Length < maxCharsPerLine)
{
if (lineBuilder.Length > 0)
lineBuilder.Append(separator);
lineBuilder.Append(termString);
lineBuilder.Append(termString).Append(separator);
}
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);
lineBuilder.Clear().Append(termString).Append(separator);
}
}
if (lineBuilder.Length > 0)
lines.Add(lineBuilder.ToString());
if (lineBuilder.Length > 0)
{
lines.Add(lineBuilder.ToString().Substring(0, lineBuilder.Length - separator.Length));
}
return lines;
}
public static bool TryParsePageNumber(
List<string> commandParameters, int expectedParamterIndex, TSPlayer errorMessageReceiver, out int pageNumber)
public static bool TryParsePageNumber(List<string> commandParameters, int expectedParameterIndex, TSPlayer errorMessageReceiver, out int pageNumber)
{
pageNumber = 1;
if (commandParameters.Count <= expectedParamterIndex)
if (commandParameters.Count <= expectedParameterIndex)
return true;
string pageNumberRaw = commandParameters[expectedParamterIndex];
string pageNumberRaw = commandParameters[expectedParameterIndex];
if (!int.TryParse(pageNumberRaw, out pageNumber) || pageNumber < 1)
{
if (errorMessageReceiver != null)
errorMessageReceiver.SendErrorMessage(string.Format("\"{0}\" is not a valid page number.", pageNumberRaw));
errorMessageReceiver.SendErrorMessage("\"{0}\" is not a valid page number.", pageNumberRaw);
pageNumber = 1;
return false;

View file

@ -40,6 +40,9 @@ namespace TShockAPI
// tshock.admin nodes
[Description("User can set build protection status.")]
public static readonly string antibuild = "tshock.admin.antibuild";
[Description("Prevents you from being kicked.")]
public static readonly string immunetokick = "tshock.admin.nokick";
@ -74,7 +77,7 @@ namespace TShockAPI
public static readonly string seeids = "tshock.admin.seeplayerids";
[Description("User can save all the players SSI state.")]
public static readonly string savessi = "tshock.admin.savessi";
public static readonly string savessc = "tshock.admin.savessi";
[Description("User can elevate other users' groups temporarily.")]
public static readonly string settempgroup = "tshock.admin.tempgroup";
@ -107,12 +110,6 @@ namespace TShockAPI
[Description("User can reload the configurations file.")]
public static readonly string cfgreload = "tshock.cfg.reload";
[Description("User can edit the max spawns.")]
public static readonly string cfgmaxspawns = "tshock.cfg.maxspawns";
[Description("User can edit the spawnrate.")]
public static readonly string cfgspawnrate = "tshock.cfg.spawnrate";
[Description("User can download updates to plugins that are currently running.")]
public static readonly string updateplugins = "tshock.cfg.updateplugins";
@ -130,15 +127,15 @@ namespace TShockAPI
[Description("Prevents you from being disabled by projectile abuse detection.")]
public static readonly string ignoreprojectiledetection = "tshock.ignore.projectile";
[Description("Prevents you from being disabled by paint abuse detection.")]
public static readonly string ignorepaintdetection = "tshock.ignore.paint";
[Description("Prevents you from being reverted by no clip detection.")]
public static readonly string ignorenoclipdetection = "tshock.ignore.noclip";
[Description("Prevents you from being disabled by stack hack detection.")]
public static readonly string ignorestackhackdetection = "tshock.ignore.itemstack";
[Description("Prevents you from being kicked by hacked health detection.")]
public static readonly string ignorestathackdetection = "tshock.ignore.stats";
[Description("Prevents your actions from being ignored if damage is too high.")]
public static readonly string ignoredamagecap = "tshock.ignore.damage";
@ -148,19 +145,28 @@ namespace TShockAPI
[Description("Allow unrestricted SendTileSquare usage, for client side world editing.")]
public static readonly string allowclientsideworldedit = "tshock.ignore.sendtilesquare";
[Description("Allow dropping banned items without the item being eaten.")]
public static readonly string allowdroppingbanneditems = "tshock.ignore.dropbanneditem";
// tshock.item nodes
[Description("User can spawn items.")]
public static readonly string item = "tshock.item.spawn";
[Description("User can clear items.")]
public static readonly string clearitems = "tshock.item.clear";
[Description("Allows you to use banned items.")]
public static readonly string usebanneditem = "tshock.item.usebanned";
// tshock.npc nodes
[Description("User can edit the max spawns.")]
public static readonly string maxspawns = "tshock.npc.maxspawns";
[Description("User can edit the spawnrate.")]
public static readonly string spawnrate = "tshock.npc.spawnrate";
[Description("User can start an invasion.")]
public static readonly string invade = "tshock.npc.invade";
[Description("User can spawn bosses.")]
public static readonly string spawnboss = "tshock.npc.spawnboss";
@ -207,13 +213,28 @@ namespace TShockAPI
[Description("User can use /spawn.")]
public static readonly string spawn = "tshock.tp.spawn";
[Description("User can use the Rod of Discor.")]
public static readonly string rod = "tshock.tp.rod";
// tshock.world nodes
[Description("Allows you to edit the spawn.")]
public static readonly string editspawn = "tshock.world.editspawn";
[Description("Allows you to edit regions.")]
public static readonly string editregion = "tshock.world.editregion";
[Description("User can force a blood moon.")]
public static readonly string bloodmoon = "tshock.world.time.bloodmoon";
[Description("User can force a pumpkin moon.")]
public static readonly string pumpkinmoon = "tshock.world.time.pumpkinmoon";
[Description("User can force a snow moon.")]
public static readonly string snowmoon = "tshock.world.time.snowmoon";
[Description("User can set the time.")]
public static readonly string time = "tshock.world.settime";
public static readonly string time = "tshock.world.time.set";
[Description("User can grow plants.")]
public static readonly string grow = "tshock.world.grow";
@ -242,14 +263,29 @@ namespace TShockAPI
[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 drop a meteor.")]
public static readonly string dropmeteor = "tshock.world.time.dropmeteor";
[Description("User can force an eclipse.")]
public static readonly string eclipse = "tshock.world.time.eclipse";
[Description("User can force a full moon.")]
public static readonly string fullmoon = "tshock.world.time.fullmoon";
[Description("User can modify the world.")]
public static readonly string canbuild = "tshock.world.modify";
[Description("User can paint tiles.")]
public static readonly string canpaint = "tshock.world.paint";
[Description("User can turn on or off the rain.")]
public static readonly string rain = "tshock.world.rain";
// Non-grouped
[Description("User can clear items or projectiles.")]
public static readonly string clear = "tshock.clear";
[Description("User can kill others.")]
public static readonly string kill = "tshock.kill";
@ -280,6 +316,12 @@ namespace TShockAPI
[Description("User can get the server info.")]
public static readonly string serverinfo = "tshock.info";
[Description("Player recovers health as damage is taken. Can be one shotted.")]
public static readonly string godmode = "tshock.godmode";
[Description("Player can chat")]
public static readonly string canchat = "tshock.canchat";
/// <summary>
/// Lists all commands associated with a given permission
/// </summary>
@ -300,7 +342,7 @@ namespace TShockAPI
var sb = new StringBuilder();
foreach (var field in typeof(Permissions).GetFields().OrderBy(f => f.Name))
{
var name = field.Name;
var name = (string)field.GetValue(null);
var descattr =
field.GetCustomAttributes(false).FirstOrDefault(o => o is DescriptionAttribute) as DescriptionAttribute;
@ -319,9 +361,9 @@ namespace TShockAPI
c =>
c.Name + (c.Names.Count > 1 ? "({0})".SFormat(string.Join(" ", c.Names.ToArray(), 1, c.Names.Count - 1)) : ""));
sb.AppendLine("## <a id=\"{0}\">{0}</a> ".SFormat(name));
sb.AppendLine("**Description:** {0} ".SFormat(desc));
sb.AppendLine("**Commands:** {0} ".SFormat(strs.Count() > 0 ? string.Join(" ", strs) : "None"));
sb.AppendLine("{0}".SFormat(name));
sb.AppendLine("Description: {0} ".SFormat(desc));
sb.AppendLine("Commands: {0} ".SFormat(strs.Count() > 0 ? string.Join(" ", strs) : "None"));
sb.AppendLine();
}

View file

@ -1,87 +0,0 @@
/*
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

@ -1,127 +0,0 @@
/*
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

@ -1,87 +0,0 @@
/*
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

@ -49,5 +49,5 @@ using System.Runtime.InteropServices;
// Build Number
// MMdd of the build
[assembly: AssemblyVersion("4.1.0.0926")]
[assembly: AssemblyFileVersion("4.1.0.0926")]
[assembly: AssemblyVersion("4.2.2.1228")]
[assembly: AssemblyFileVersion("4.2.2.1228")]

View file

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TShockAPI
{
/// <summary>
/// Custom packet types for use with Raptor.
/// </summary>
public enum RaptorPacketTypes : byte
{
/// <summary>
/// The packet sent to the server to be acknowledged as a Raptor client.
/// </summary>
Acknowledge = 0,
/// <summary>
/// The packet sent to the client which dictates its permissions.
/// </summary>
Permissions,
/// <summary>
/// The packet sent which sets region info.
/// </summary>
Region,
/// <summary>
/// The packet sent to delete a region.
/// </summary>
RegionDelete,
/// <summary>
/// The packet sent which sets warp info.
/// </summary>
Warp,
/// <summary>
/// The packet sent to delete a warp.
/// </summary>
WarpDelete
}
}

View file

@ -1,25 +1,7 @@
/*
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/>.
*/
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.235
// Runtime Version:4.0.30319.17929
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@ -27,6 +9,9 @@ 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>
@ -74,48 +59,5 @@ namespace TShockAPI {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to #Format
///#name parent permisson1 permission2 permissionN
///#if there is no parent, put null instead
///#groups inherit permissions from their parents
///#put a ! before a permission to negate it
///#Do not remove the group default
///#Do not name a group SuperAdmin, that is hard-coded into the code, it grants total permissions
///#ALWAYS DECLARE A GROUP&apos;S PARENT BEFORE YOU DECLARE THE GROUP
///
///#currently avaliable permissions:
///#reservedslot - reserved slot for player
///#canwater - allow players to use water
///#canlav [rest of string was truncated]&quot;;.
/// </summary>
internal static string groups {
get {
return ResourceManager.GetString("groups", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to #see https://github.com/TShock/TShock/wiki/Item-List for a list of item ids
///#List each banned item ID below this, with each on a new line.
/// </summary>
internal static string itembans {
get {
return ResourceManager.GetString("itembans", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to #format
///#ip group
///#see groups.txt for a list of groups
///#127.0.0.1 superadmin.
/// </summary>
internal static string users {
get {
return ResourceManager.GetString("users", resourceCulture);
}
}
}
}

View file

@ -118,13 +118,4 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="groups" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>config\groups.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
</data>
<data name="itembans" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>config\itembans.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="users" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>config\users.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
</data>
</root>

View file

@ -196,8 +196,10 @@ namespace Rests
protected virtual object ExecuteCommand(RestCommand cmd, RestVerbs verbs, IParameterCollection parms)
{
object result = cmd.Execute(verbs, parms);
if (cmd.DoLog)
if (cmd.DoLog && TShock.Config.LogRest)
{
Log.ConsoleInfo("Anonymous requested REST endpoint: " + BuildRequestUri(cmd, verbs, parms, false));
}
return result;
}

View file

@ -50,7 +50,7 @@ namespace TShockAPI
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("/status", (a, b) => this.ServerStatus(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)));
}
@ -236,7 +236,7 @@ namespace TShockAPI
var players = new ArrayList();
foreach (TSPlayer tsPlayer in TShock.Players.Where(p => null != p))
{
var p = PlayerFilter(tsPlayer, parameters);
var p = PlayerFilter(tsPlayer, parameters, ((tokenData.UserGroupName) != "" && TShock.Utils.GetGroup(tokenData.UserGroupName).HasPermission(RestPermissions.viewips)));
if (null != p)
players.Add(p);
}
@ -258,7 +258,7 @@ 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);
rules.Add("ServerSideInventory", TShock.Config.ServerSideCharacter);
ret.Add("rules", rules);
}
@ -307,7 +307,7 @@ namespace TShockAPI
return RestMissingParam("password");
// NOTE: ip can be blank
User user = new User(username, password, group, "", "");
User user = new User(username, password, "", group, "", "", "");
try
{
TShock.Users.AddUser(user);
@ -404,7 +404,7 @@ namespace TShockAPI
try
{
TShock.Bans.AddBan(ip, name, parameters["reason"], true, tokenData.Username);
TShock.Bans.AddBan(ip, name, "", parameters["reason"], true, tokenData.Username);
}
catch (Exception e)
{
@ -622,7 +622,7 @@ namespace TShockAPI
TSPlayer player = (TSPlayer)ret;
var reason = null == parameters["reason"] ? "Banned via web" : parameters["reason"];
TShock.Bans.AddBan(player.IP, player.Name, reason);
TShock.Bans.AddBan(player.IP, player.Name, "", reason);
TShock.Utils.ForceKick(player, reason, false, true);
return RestResponse("Player " + player.Name + " was banned");
}
@ -719,7 +719,7 @@ namespace TShockAPI
var permissions = (null == parameters["permissions"]) ? group.Permissions : parameters["permissions"];
try
{
TShock.Groups.UpdateGroup(group.Name, parent, permissions, chatcolor);
TShock.Groups.UpdateGroup(group.Name, parent, permissions, chatcolor, group.Suffix, group.Prefix);
}
catch (Exception e)
{
@ -859,18 +859,22 @@ namespace TShockAPI
return group;
}
private Dictionary<string, object> PlayerFilter(TSPlayer tsPlayer, IParameterCollection parameters)
private Dictionary<string, object> PlayerFilter(TSPlayer tsPlayer, IParameterCollection parameters, bool viewips = false)
{
var player = new Dictionary<string, object>
{
{"nickname", tsPlayer.Name},
{"username", null == tsPlayer.UserAccountName ? "" : tsPlayer.UserAccountName},
{"ip", tsPlayer.IP},
{"username", tsPlayer.UserAccountName ?? ""},
{"group", tsPlayer.Group.Name},
{"active", tsPlayer.Active},
{"state", tsPlayer.State},
{"team", tsPlayer.Team},
};
if (viewips)
{
player.Add("ip", tsPlayer.IP);
}
foreach (IParameter filter in parameters)
{
if (player.ContainsKey(filter.Name) && !player[filter.Name].Equals(filter.Value))

View file

@ -83,5 +83,8 @@ namespace Rests
[Description("REST user can run raw TShock commands (the raw command permissions are also checked though).")]
public static readonly string restrawcommand = "tshock.rest.command";
[Description("REST user can view the ips of players.")]
public static readonly string viewips = "tshock.rest.viewips";
}
}

View file

@ -192,7 +192,7 @@ namespace Rests
}
object result = secureCmd.Execute(verbs, parms, tokenData);
if (cmd.DoLog)
if (cmd.DoLog && TShock.Config.LogRest)
TShock.Utils.SendLogs(string.Format(
"\"{0}\" requested REST endpoint: {1}", tokenData.Username, this.BuildRequestUri(cmd, verbs, parms, false)),
Color.PaleVioletRed);

View file

@ -20,6 +20,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
using Terraria;
using TerrariaApi.Server;
namespace TShockAPI
{
@ -45,18 +46,21 @@ namespace TShockAPI
/// <summary>
/// SaveWorld event handler which notifies users that the server may lag
/// </summary>
public void OnSaveWorld(bool resettime = false, HandledEventArgs e = null)
public void OnSaveWorld(WorldSaveEventArgs args)
{
// Protect against internal errors causing save failures
// These can be caused by an unexpected error such as a bad or out of date plugin
try
if (TShock.Config.AnnounceSave)
{
TShock.Utils.Broadcast("Saving world. Momentary lag might result from this.", Color.Red);
}
catch (Exception ex)
{
Log.Error("World saved notification failed");
Log.Error(ex.ToString());
// Protect against internal errors causing save failures
// These can be caused by an unexpected error such as a bad or out of date plugin
try
{
TShock.Utils.Broadcast("Saving world. Momentary lag might result from this.", Color.Red);
}
catch (Exception ex)
{
Log.Error("World saved notification failed");
Log.Error(ex.ToString());
}
}
}
@ -120,7 +124,7 @@ namespace TShockAPI
{
if (task.direct)
{
OnSaveWorld();
OnSaveWorld(new WorldSaveEventArgs());
WorldGen.realsaveWorld(task.resetTime);
}
else

101
TShockAPI/StatTracker.cs Normal file
View file

@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Threading;
using System.IO;
using System.Web;
namespace TShockAPI
{
public class StatTracker
{
private bool failed;
private bool initialized;
public StatTracker()
{
}
public void Initialize()
{
if (!initialized)
{
initialized = true;
ThreadPool.QueueUserWorkItem(SendUpdate);
}
}
private HttpWebResponse GetResponseNoException(HttpWebRequest req)
{
try
{
return (HttpWebResponse)req.GetResponse();
}
catch (WebException we)
{
var resp = we.Response as HttpWebResponse;
if (resp == null)
throw;
return resp;
}
}
private void SendUpdate(object info)
{
Thread.Sleep(1000*60*15);
var data = new JsonData
{
port = Terraria.Netplay.serverPort,
currentPlayers = TShock.Utils.ActivePlayers(),
maxPlayers = TShock.Config.MaxSlots,
systemRam = 0,
systemCPUClock = 0,
version = TShock.VersionNum.ToString(),
terrariaVersion = Terraria.Main.versionNumber2,
mono = Terraria.Main.runningMono
};
var serialized = Newtonsoft.Json.JsonConvert.SerializeObject(data);
var encoded = HttpUtility.UrlEncode(serialized);
var uri = String.Format("http://96.47.231.227:8000?data={0}", encoded);
var client = (HttpWebRequest)WebRequest.Create(uri);
client.Timeout = 5000;
try
{
using (var resp = GetResponseNoException(client))
{
if (resp.StatusCode != HttpStatusCode.OK)
{
throw new IOException("Server did not respond with an OK.");
}
failed = false;
}
}
catch (Exception e)
{
if (!failed)
{
Log.ConsoleError("StatTracker Exception: {0}", e);
failed = true;
}
}
ThreadPool.QueueUserWorkItem(SendUpdate);
}
}
public struct JsonData
{
public int port;
public int currentPlayers;
public int maxPlayers;
public int systemRam;
public int systemCPUClock;
public string version;
public string terrariaVersion;
public bool mono;
}
}

729
TShockAPI/TSPlayer.cs Normal file → Executable file
View file

@ -20,8 +20,10 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
using Terraria;
using TShockAPI.DB;
using TShockAPI.Net;
namespace TShockAPI
@ -38,6 +40,11 @@ namespace TShockAPI
/// </summary>
public static readonly TSPlayer All = new TSPlayer("All");
/// <summary>
/// Gets whether the player is using Raptor.
/// </summary>
public bool IsRaptor { get; internal set; }
/// <summary>
/// The amount of tiles that the player has killed in the last second.
/// </summary>
@ -49,24 +56,44 @@ namespace TShockAPI
public int TilePlaceThreshold { get; set; }
/// <summary>
/// The amount of liquid( in tiles ) that the player has placed in the last second.
/// The amount of liquid (in tiles) that the player has placed in the last second.
/// </summary>
public int TileLiquidThreshold { get; set; }
/// <summary>
/// The amount of tiles that the player has painted in the last second.
/// </summary>
public int PaintThreshold { get; set; }
/// <summary>
/// The number of projectiles created by the player in the last second.
/// </summary>
public int ProjectileThreshold { get; set; }
/// <summary>
/// A timer to keep track of whether or not the player has recently thrown an explosive
/// </summary>
public int RecentFuse = 0;
/// <summary>
/// A system to delay Remembered Position Teleports a few seconds
/// </summary>
public int RPPending = 0;
public int sX = -1;
public int sY = -1;
/// <summary>
/// A queue of tiles destroyed by the player for reverting.
/// </summary>
public Dictionary<Vector2, TileData> TilesDestroyed { get; protected set; }
public Dictionary<Vector2, Tile> TilesDestroyed { get; protected set; }
/// <summary>
/// A queue of tiles placed by the player for reverting.
/// </summary>
public Dictionary<Vector2, TileData> TilesCreated { get; protected set; }
public Dictionary<Vector2, Tile> TilesCreated { get; protected set; }
public int FirstMaxHP { get; set; }
@ -103,7 +130,7 @@ namespace TShockAPI
/// <summary>
/// The last time the player changed their team or pvp status.
/// </summary>
public DateTime LastPvpChange;
public DateTime LastPvPTeamChange;
/// <summary>
/// Temp points for use in regions and other plugins.
@ -129,11 +156,6 @@ namespace TShockAPI
/// </summary>
public DateTime LastThreat { get; set; }
/// <summary>
/// Not used, can be removed.
/// </summary>
public DateTime LastTileChangeNotify { get; set; }
public bool InitSpawn;
/// <summary>
@ -141,10 +163,8 @@ namespace TShockAPI
/// </summary>
public bool DisplayLogs = true;
public Vector2 oldSpawn = Vector2.Zero;
/// <summary>
/// The last player that the player whispered with( to or from ).
/// The last player that the player whispered with (to or from).
/// </summary>
public TSPlayer LastWhisper;
@ -195,8 +215,6 @@ namespace TShockAPI
/// </summary>
public bool mute;
public bool TpLock;
private Player FakePlayer;
public bool RequestedSection;
@ -277,6 +295,21 @@ namespace TShockAPI
/// </summary>
public bool LoginHarassed = false;
/// <summary>
/// Player cant die, unless onehit
/// </summary>
public bool GodMode = false;
/// <summary>
/// Players controls are inverted if using SSC
/// </summary>
public bool Confused = false;
/// <summary>
/// The last projectile type this player tried to kill.
/// </summary>
public int LastKilledProjectile = 0;
/// <summary>
/// Whether the player is a real, human, player on the server.
/// </summary>
@ -294,12 +327,31 @@ namespace TShockAPI
}
}
/// <summary>
/// Gets the player's selected item.
/// </summary>
public Item SelectedItem
{
get { return TPlayer.inventory[TPlayer.selectedItem]; }
}
public int State
{
get { return Netplay.serverSock[Index].state; }
set { Netplay.serverSock[Index].state = value; }
}
/// <summary>
/// Gets the player's UUID.
/// </summary>
public string UUID
{
get { return RealPlayer ? Netplay.serverSock[Index].clientUUID : ""; }
}
/// <summary>
/// Gets the player's IP.
/// </summary>
public string IP
{
get
@ -321,16 +373,16 @@ namespace TShockAPI
/// Saves the player's inventory to SSI
/// </summary>
/// <returns>bool - True/false if it saved successfully</returns>
public bool SaveServerInventory()
public bool SaveServerCharacter()
{
if (!TShock.Config.ServerSideInventory)
if (!TShock.Config.ServerSideCharacter)
{
return false;
}
try
{
PlayerData.CopyInventory(this);
TShock.InventoryDB.InsertPlayerData(this);
PlayerData.CopyCharacter(this);
TShock.CharacterDB.InsertPlayerData(this);
return true;
} catch (Exception e)
{
@ -340,6 +392,29 @@ namespace TShockAPI
}
/// <summary>
/// Sends the players server side character to client
/// </summary>
/// <returns>bool - True/false if it saved successfully</returns>
public bool SendServerCharacter()
{
if (!TShock.Config.ServerSideCharacter)
{
return false;
}
try
{
PlayerData.RestoreCharacter(this);
return true;
}
catch (Exception e)
{
Log.Error(e.Message);
return false;
}
}
/// <summary>
/// Terraria Player
/// </summary>
@ -383,6 +458,8 @@ namespace TShockAPI
get { return (int) (Y/16); }
}
public bool TpLock;
public bool InventorySlotAvailable
{
get
@ -390,7 +467,7 @@ namespace TShockAPI
bool flag = false;
if (RealPlayer)
{
for (int i = 0; i < 40; i++) //41 is trash can, 42-45 is coins, 46-49 is ammo
for (int i = 0; i < 50; i++) //51 is trash can, 52-55 is coins, 56-59 is ammo
{
if (TPlayer.inventory[i] == null || !TPlayer.inventory[i].active || TPlayer.inventory[i].name == "")
{
@ -405,8 +482,8 @@ namespace TShockAPI
public TSPlayer(int index)
{
TilesDestroyed = new Dictionary<Vector2, TileData>();
TilesCreated = new Dictionary<Vector2, TileData>();
TilesDestroyed = new Dictionary<Vector2, Tile>();
TilesCreated = new Dictionary<Vector2, Tile>();
Index = index;
Group = Group.DefaultGroup;
IceTiles = new List<Point>();
@ -415,8 +492,8 @@ namespace TShockAPI
protected TSPlayer(String playerName)
{
TilesDestroyed = new Dictionary<Vector2, TileData>();
TilesCreated = new Dictionary<Vector2, TileData>();
TilesDestroyed = new Dictionary<Vector2, Tile>();
TilesCreated = new Dictionary<Vector2, Tile>();
Index = -1;
FakePlayer = new Player {name = playerName, whoAmi = -1};
Group = Group.DefaultGroup;
@ -457,12 +534,49 @@ namespace TShockAPI
//Sending a fake world id causes the client to not be able to find a stored spawnx/y.
//This fixes the bed spawn point bug. With a fake world id it wont be able to find the bed spawn.
WorldID = !fakeid ? Main.worldID : -1,
WorldFlags = (WorldGen.shadowOrbSmashed ? WorldInfoFlag.OrbSmashed : WorldInfoFlag.None) |
(NPC.downedBoss1 ? WorldInfoFlag.DownedBoss1 : WorldInfoFlag.None) |
(NPC.downedBoss2 ? WorldInfoFlag.DownedBoss2 : WorldInfoFlag.None) |
(NPC.downedBoss3 ? WorldInfoFlag.DownedBoss3 : WorldInfoFlag.None) |
(Main.hardMode ? WorldInfoFlag.HardMode : WorldInfoFlag.None) |
(NPC.downedClown ? WorldInfoFlag.DownedClown : WorldInfoFlag.None),
MoonType = (byte)Main.moonType,
TreeX0 = Main.treeX[0],
TreeX1 = Main.treeX[1],
TreeX2 = Main.treeX[2],
TreeStyle0 = (byte)Main.treeStyle[0],
TreeStyle1 = (byte)Main.treeStyle[1],
TreeStyle2 = (byte)Main.treeStyle[2],
TreeStyle3 = (byte)Main.treeStyle[3],
CaveBackX0 = Main.caveBackX[0],
CaveBackX1 = Main.caveBackX[1],
CaveBackX2 = Main.caveBackX[2],
CaveBackStyle0 = (byte)Main.caveBackStyle[0],
CaveBackStyle1 = (byte)Main.caveBackStyle[1],
CaveBackStyle2 = (byte)Main.caveBackStyle[2],
CaveBackStyle3 = (byte)Main.caveBackStyle[3],
SetBG0 = (byte)WorldGen.treeBG,
SetBG1 = (byte)WorldGen.corruptBG,
SetBG2 = (byte)WorldGen.jungleBG,
SetBG3 = (byte)WorldGen.snowBG,
SetBG4 = (byte)WorldGen.hallowBG,
SetBG5 = (byte)WorldGen.crimsonBG,
SetBG6 = (byte)WorldGen.desertBG,
SetBG7 = (byte)WorldGen.oceanBG,
IceBackStyle = (byte)Main.iceBackStyle,
JungleBackStyle = (byte)Main.jungleBackStyle,
HellBackStyle = (byte)Main.hellBackStyle,
WindSpeed = Main.windSpeed,
NumberOfClouds = (byte)Main.numClouds,
BossFlags = (WorldGen.shadowOrbSmashed ? BossFlags.OrbSmashed : BossFlags.None) |
(NPC.downedBoss1 ? BossFlags.DownedBoss1 : BossFlags.None) |
(NPC.downedBoss2 ? BossFlags.DownedBoss2 : BossFlags.None) |
(NPC.downedBoss3 ? BossFlags.DownedBoss3 : BossFlags.None) |
(Main.hardMode ? BossFlags.HardMode : BossFlags.None) |
(NPC.downedClown ? BossFlags.DownedClown : BossFlags.None) |
(Main.ServerSideCharacter ? BossFlags.ServerSideCharacter : BossFlags.None),
BossFlags2 = (NPC.downedMechBoss1 ? BossFlags2.DownedMechBoss1 : BossFlags2.None) |
(NPC.downedMechBoss2 ? BossFlags2.DownedMechBoss2 : BossFlags2.None) |
(NPC.downedMechBoss3 ? BossFlags2.DownedMechBoss3 : BossFlags2.None) |
(NPC.downedMechBossAny ? BossFlags2.DownedMechBossAny : BossFlags2.None) |
(Main.cloudBGActive == 1f ? BossFlags2.CloudBg : BossFlags2.None) |
(WorldGen.crimson ? BossFlags2.Crimson : BossFlags2.None) |
(Main.pumpkinMoon ? BossFlags2.Pumpkin : BossFlags2.None),
Rain = Main.maxRaining,
WorldName = TShock.Config.UseServerName ? TShock.Config.ServerName : Main.worldName
};
msg.PackFull(ms);
@ -470,44 +584,47 @@ namespace TShockAPI
}
}
public bool Teleport(int tilex, int tiley)
public bool Teleport(float x, float y, byte style = 1)
{
InitSpawn = false;
SendWorldInfo(tilex, tiley, true);
//150 Should avoid all client crash errors
//The error occurs when a tile trys to update which the client hasnt load yet, Clients only update tiles withen 150 blocks
//Try 300 if it does not work (Higher number - Longer load times - Less chance of error)
//Should we properly send sections so that clients don't get tiles twice?
SendTileSquare(tilex, tiley, 150);
/* //We shouldn't need this section anymore -it can prevent otherwise acceptable teleportation under some circumstances.
if (!SendTileSquare(tilex, tiley, 150))
if (x > Main.rightWorld - 992)
{
InitSpawn = true;
SendWorldInfo(Main.spawnTileX, Main.spawnTileY, false);
return false;
x = Main.rightWorld - 992;
}
if (x < 992)
{
x = 992;
}
if (y > Main.bottomWorld - 992)
{
y = Main.bottomWorld - 992;
}
if (y < 992)
{
y = 992;
}
*/
Spawn(-1, -1);
SendWorldInfo(Main.spawnTileX, Main.spawnTileY, false);
TPlayer.position.X = (float)(tilex * 16 + 8 - TPlayer.width /2);
TPlayer.position.Y = (float)(tiley * 16 - TPlayer.height);
//We need to send the tile data again to prevent clients from thinking they *really* destroyed blocks just now.
SendTileSquare(tilex, tiley, 10);
SendTileSquare((int) (x/16), (int) (y/16), 15);
TPlayer.Teleport(new Vector2(x, y), style);
NetMessage.SendData((int)PacketTypes.Teleport, -1, -1, "", 0, TPlayer.whoAmi, x, y, style);
return true;
}
public void Spawn()
public void Heal(int health = 500)
{
Spawn(TPlayer.SpawnX, TPlayer.SpawnY);
NetMessage.SendData((int)PacketTypes.PlayerHealOther, -1, -1, "", this.TPlayer.whoAmi, health);
}
public void Spawn()
{
// TPlayer.FindSpawn();
if (this.sX > 0 && this.sY > 0)
{
Spawn(this.sX, this.sY);
}
else
{
Spawn(TPlayer.SpawnX, TPlayer.SpawnY);
}
}
public void Spawn(int tilex, int tiley)
@ -541,45 +658,52 @@ namespace TShockAPI
public virtual bool SendTileSquare(int x, int y, int size = 10)
{
try
{
int num = (size - 1)/2;
int m_x=0;
int m_y=0;
try
{
int num = (size - 1)/2;
int m_x = 0;
int m_y = 0;
if (x - num <0){
m_x=0;
}else{
m_x = x - num;
}
if (x - num < 0)
{
m_x = 0;
}
else
{
m_x = x - num;
}
if (y - num <0){
m_y=0;
}else{
m_y = y - num;
}
if (y - num < 0)
{
m_y = 0;
}
else
{
m_y = y - num;
}
if (m_x + size > Main.maxTilesX){
m_x=Main.maxTilesX - size;
}
if (m_x + size > Main.maxTilesX)
{
m_x = Main.maxTilesX - size;
}
if (m_y + size > Main.maxTilesY){
m_y=Main.maxTilesY - size;
}
if (m_y + size > Main.maxTilesY)
{
m_y = Main.maxTilesY - size;
}
SendData(PacketTypes.TileSendSquare, "", size, m_x, m_y);
return true;
}
catch (IndexOutOfRangeException)
{
// This is expected if square exceeds array.
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
return false;
SendData(PacketTypes.TileSendSquare, "", size, m_x, m_y);
return true;
}
catch (IndexOutOfRangeException)
{
// This is expected if square exceeds array.
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
return false;
}
public bool GiveItemCheck(int type, string name, int width, int height, int stack, int prefix = 0)
@ -594,7 +718,7 @@ namespace TShockAPI
public virtual void GiveItem(int type, string name, int width, int height, int stack, int prefix = 0)
{
int itemid = Item.NewItem((int) X, (int) Y, width, height, type, stack, true, prefix);
int itemid = Item.NewItem((int) X, (int) Y, width, height, type, stack, true, prefix, true);
// This is for special pickaxe/hammers/swords etc
Main.item[itemid].SetDefaults(name);
@ -604,8 +728,10 @@ namespace TShockAPI
Main.item[itemid].stack = stack;
Main.item[itemid].owner = Index;
Main.item[itemid].prefix = (byte) prefix;
NetMessage.SendData((int) PacketTypes.ItemDrop, -1, -1, "", itemid, 0f, 0f, 0f);
NetMessage.SendData((int) PacketTypes.ItemOwner, -1, -1, "", itemid, 0f, 0f, 0f);
Main.item[itemid].noGrabDelay = 1;
Main.item[itemid].velocity = Main.player[this.Index].velocity;
NetMessage.SendData((int)PacketTypes.ItemDrop, -1, -1, "", itemid, 0f, 0f, 0f);
NetMessage.SendData((int)PacketTypes.ItemOwner, -1, -1, "", itemid, 0f, 0f, 0f);
}
public virtual void SendInfoMessage(string msg)
@ -681,15 +807,37 @@ namespace TShockAPI
SendData(PacketTypes.PlayerTeam, "", Index);
}
public virtual void Disable(string reason = "")
private DateTime LastDisableNotification = DateTime.UtcNow;
public int ActiveChest = -1;
public virtual void Disable(string reason = "", bool displayConsole = true)
{
LastThreat = DateTime.UtcNow;
SetBuff(33, 330, true); //Weak
SetBuff(32, 330, true); //Slow
SetBuff(23, 330, true); //Cursed
if (!string.IsNullOrEmpty(reason))
Log.ConsoleInfo(string.Format("Player {0} has been disabled for {1}.", Name, reason));
SetBuff(47, 330, true); //Frozen
if (ActiveChest != -1)
{
SendData(PacketTypes.ChestOpen, "", -1);
}
if (!string.IsNullOrEmpty(reason))
{
if ((DateTime.UtcNow - LastDisableNotification).TotalMilliseconds > 5000)
{
if (displayConsole)
{
Log.ConsoleInfo(string.Format("Player {0} has been disabled for {1}.", Name, reason));
}
else
{
Log.Info("Player {0} has been disabled for {1}.", Name, reason);
}
LastDisableNotification = DateTime.UtcNow;
}
}
var trace = new StackTrace();
StackFrame frame = null;
frame = trace.GetFrame(1);
@ -744,6 +892,177 @@ namespace TShockAPI
return TShock.SendBytes(Netplay.serverSock[Index], data);
}
/// <summary>
/// Sends Raptor permissions to the player.
/// </summary>
public void SendRaptorPermissions()
{
if (!IsRaptor)
return;
lock (NetMessage.buffer[Index].writeBuffer)
{
int length = 0;
using (var ms = new MemoryStream(NetMessage.buffer[Index].writeBuffer, true))
{
using (var writer = new BinaryWriter(ms))
{
writer.BaseStream.Position = 4;
writer.Write((byte)PacketTypes.Placeholder);
writer.Write((byte)RaptorPacketTypes.Permissions);
writer.Write(String.Join(",", Group.TotalPermissions.ToArray()));
length = (int)writer.BaseStream.Position;
writer.BaseStream.Position = 0;
writer.Write(length - 4);
}
}
TShock.PacketBuffer.SendBytes(Netplay.serverSock[Index], NetMessage.buffer[Index].writeBuffer, 0, length);
}
}
/// <summary>
/// Sends a region to the player.
/// <param name="region">The region.</param>
/// </summary>
public void SendRaptorRegion(Region region)
{
if (!IsRaptor)
return;
lock (NetMessage.buffer[Index].writeBuffer)
{
int length = 0;
using (var ms = new MemoryStream(NetMessage.buffer[Index].writeBuffer, true))
{
using (var writer = new BinaryWriter(ms))
{
writer.BaseStream.Position = 4;
writer.Write((byte)PacketTypes.Placeholder);
writer.Write((byte)RaptorPacketTypes.Region);
writer.Write(region.Area.X);
writer.Write(region.Area.Y);
writer.Write(region.Area.Width);
writer.Write(region.Area.Height);
writer.Write(region.Name);
length = (int)writer.BaseStream.Position;
writer.BaseStream.Position = 0;
writer.Write(length - 4);
}
}
TShock.PacketBuffer.SendBytes(Netplay.serverSock[Index], NetMessage.buffer[Index].writeBuffer, 0, length);
}
}
/// <summary>
/// Sends a region deletion to the player.
/// <param name="regionName">The region name.</param>
/// </summary>
public void SendRaptorRegionDeletion(string regionName)
{
if (!IsRaptor)
return;
lock (NetMessage.buffer[Index].writeBuffer)
{
int length = 0;
using (var ms = new MemoryStream(NetMessage.buffer[Index].writeBuffer, true))
{
using (var writer = new BinaryWriter(ms))
{
writer.BaseStream.Position = 4;
writer.Write((byte)PacketTypes.Placeholder);
writer.Write((byte)RaptorPacketTypes.RegionDelete);
writer.Write(regionName);
length = (int)writer.BaseStream.Position;
writer.BaseStream.Position = 0;
writer.Write(length - 4);
}
}
TShock.PacketBuffer.SendBytes(Netplay.serverSock[Index], NetMessage.buffer[Index].writeBuffer, 0, length);
}
}
/// <summary>
/// Sends a warp to the player.
/// <param name="warp">The warp.</param>
/// </summary>
public void SendRaptorWarp(Warp warp)
{
if (!IsRaptor)
return;
lock (NetMessage.buffer[Index].writeBuffer)
{
int length = 0;
using (var ms = new MemoryStream(NetMessage.buffer[Index].writeBuffer, true))
{
using (var writer = new BinaryWriter(ms))
{
writer.BaseStream.Position = 4;
writer.Write((byte)PacketTypes.Placeholder);
writer.Write((byte)RaptorPacketTypes.Warp);
writer.Write(warp.Position.X);
writer.Write(warp.Position.Y);
writer.Write(warp.Name);
length = (int)writer.BaseStream.Position;
writer.BaseStream.Position = 0;
writer.Write(length - 4);
}
}
TShock.PacketBuffer.SendBytes(Netplay.serverSock[Index], NetMessage.buffer[Index].writeBuffer, 0, length);
}
}
/// <summary>
/// Sends a warp deletion to the player.
/// <param name="warpName">The warp name.</param>
/// </summary>
public void SendRaptorWarpDeletion(string warpName)
{
if (!IsRaptor)
return;
lock (NetMessage.buffer[Index].writeBuffer)
{
int length = 0;
using (var ms = new MemoryStream(NetMessage.buffer[Index].writeBuffer, true))
{
using (var writer = new BinaryWriter(ms))
{
writer.BaseStream.Position = 4;
writer.Write((byte)PacketTypes.Placeholder);
writer.Write((byte)RaptorPacketTypes.WarpDelete);
writer.Write(warpName);
length = (int)writer.BaseStream.Position;
writer.BaseStream.Position = 0;
writer.Write(length - 4);
}
}
TShock.PacketBuffer.SendBytes(Netplay.serverSock[Index], NetMessage.buffer[Index].writeBuffer, 0, length);
}
}
/// <summary>
/// Adds a command callback to a specified command string.
/// </summary>
@ -877,6 +1196,24 @@ namespace TShockAPI
SetTime(false, 0);
}
public void SetSnowMoon(bool snowMoon)
{
Main.snowMoon = snowMoon;
SetTime(false, 0);
}
public void SetPumpkinMoon(bool pumpkinMoon)
{
Main.pumpkinMoon = pumpkinMoon;
SetTime(false, 0);
}
public void SetEclipse(bool Eclipse)
{
Main.eclipse = Eclipse;
SetTime(true, 150);
}
public void SetTime(bool dayTime, double time)
{
Main.dayTime = dayTime;
@ -910,12 +1247,12 @@ namespace TShockAPI
NetMessage.SendData((int) PacketTypes.NpcStrike, -1, -1, "", npcid, damage, knockBack, hitDirection);
}
public void RevertTiles(Dictionary<Vector2, TileData> tiles)
public void RevertTiles(Dictionary<Vector2, Tile> tiles)
{
// Update Main.Tile first so that when tile sqaure is sent it is correct
foreach (KeyValuePair<Vector2, TileData> entry in tiles)
foreach (KeyValuePair<Vector2, Tile> entry in tiles)
{
Main.tile[(int) entry.Key.X, (int) entry.Key.Y].Data = entry.Value;
Main.tile[(int) entry.Key.X, (int) entry.Key.Y] = entry.Value;
}
// Send all players updated tile sqaures
foreach (Vector2 coords in tiles.Keys)
@ -928,9 +1265,14 @@ namespace TShockAPI
public class PlayerData
{
public NetItem[] inventory = new NetItem[NetItem.maxNetInventory];
public int health = 100;
public int maxHealth = 100;
//public int maxMana = 100;
public int mana = 20;
public int maxMana = 20;
public bool exists;
public int spawnX= -1;
public int spawnY= -1;
public PlayerData(TSPlayer player)
{
@ -950,6 +1292,7 @@ namespace TShockAPI
this.inventory[2].stack = 1;
if (player.TPlayer.inventory[2] != null && player.TPlayer.inventory[2].netID == -16)
this.inventory[2].prefix = player.TPlayer.inventory[2].prefix;
}
public void StoreSlot(int slot, int netID, int prefix, int stack)
@ -972,14 +1315,28 @@ namespace TShockAPI
}
}
public void CopyInventory(TSPlayer player)
public void CopyCharacter(TSPlayer player)
{
this.health = player.TPlayer.statLife > 0 ? player.TPlayer.statLife : 1;
this.maxHealth = player.TPlayer.statLifeMax;
this.mana = player.TPlayer.statMana;
this.maxMana = player.TPlayer.statManaMax;
if (player.sX > 0 && player.sY > 0)
{
this.spawnX = player.sX;
this.spawnY = player.sY;
}
else
{
this.spawnX = player.TPlayer.SpawnX;
this.spawnY = player.TPlayer.SpawnY;
}
Item[] inventory = player.TPlayer.inventory;
Item[] armor = player.TPlayer.armor;
Item[] dye = player.TPlayer.dye;
for (int i = 0; i < NetItem.maxNetInventory; i++)
{
if (i < 49)
if (i < NetItem.maxNetInventory - (NetItem.armorSlots + NetItem.dyeSlots))
{
if (player.TPlayer.inventory[i] != null)
{
@ -1001,11 +1358,12 @@ namespace TShockAPI
this.inventory[i].prefix = 0;
}
}
else
else if (i < NetItem.maxNetInventory - NetItem.dyeSlots)
{
if (player.TPlayer.armor[i - 48] != null)
var index = i - (NetItem.maxNetInventory - (NetItem.armorSlots + NetItem.dyeSlots));
if (player.TPlayer.armor[index] != null)
{
this.inventory[i].netID = armor[i - 48].netID;
this.inventory[i].netID = armor[index].netID;
}
else
{
@ -1014,8 +1372,31 @@ namespace TShockAPI
if (this.inventory[i].netID != 0)
{
this.inventory[i].stack = armor[i - 48].stack;
this.inventory[i].prefix = armor[i - 48].prefix;
this.inventory[i].stack = armor[index].stack;
this.inventory[i].prefix = armor[index].prefix;
}
else
{
this.inventory[i].stack = 0;
this.inventory[i].prefix = 0;
}
}
else
{
var index = i - (NetItem.maxNetInventory - NetItem.dyeSlots);
if (player.TPlayer.dye[index] != null)
{
this.inventory[i].netID = dye[index].netID;
}
else
{
this.inventory[i].netID = 0;
}
if (this.inventory[i].netID != 0)
{
this.inventory[i].stack = dye[index].stack;
this.inventory[i].prefix = dye[index].prefix;
}
else
{
@ -1025,15 +1406,137 @@ namespace TShockAPI
}
}
}
public void RestoreCharacter(TSPlayer player)
{
player.TPlayer.statLife = this.health;
player.TPlayer.statLifeMax = this.maxHealth;
player.TPlayer.statMana = this.maxMana;
player.TPlayer.statManaMax = this.maxMana;
player.TPlayer.SpawnX = this.spawnX;
player.TPlayer.SpawnY = this.spawnY;
player.sX = this.spawnX;
player.sY = this.spawnY;
for (int i = 0; i < NetItem.maxNetInventory; i++)
{
if (i < NetItem.maxNetInventory - (NetItem.armorSlots + NetItem.dyeSlots))
{
if (this.inventory[i] != null)
{
player.TPlayer.inventory[i].netDefaults(this.inventory[i].netID);
}
else
{
player.TPlayer.inventory[i].netDefaults(0);
}
if (player.TPlayer.inventory[i].netID != 0)
{
player.TPlayer.inventory[i].stack = this.inventory[i].stack;
player.TPlayer.inventory[i].prefix = (byte)this.inventory[i].prefix;
}
}
else if (i < NetItem.maxNetInventory - NetItem.dyeSlots)
{
var index = i - (NetItem.maxNetInventory - (NetItem.armorSlots + NetItem.dyeSlots));
if (this.inventory[i] != null)
{
player.TPlayer.armor[index].netDefaults(this.inventory[i].netID);
}
else
{
player.TPlayer.armor[index].netDefaults(0);
}
if (player.TPlayer.armor[index].netID != 0)
{
player.TPlayer.armor[index].stack = this.inventory[i].stack;
player.TPlayer.armor[index].prefix = (byte)this.inventory[i].prefix;
}
}
else
{
var index = i - (NetItem.maxNetInventory - NetItem.dyeSlots);
if (this.inventory[i] != null)
{
player.TPlayer.dye[index].netDefaults(this.inventory[i].netID);
}
else
{
player.TPlayer.dye[index].netDefaults(0);
}
if (player.TPlayer.dye[index].netID != 0)
{
player.TPlayer.dye[index].stack = this.inventory[i].stack;
player.TPlayer.dye[index].prefix = (byte)this.inventory[i].prefix;
}
}
}
for (int k = 0; k < 59; k++)
{
NetMessage.SendData(5, -1, -1, Main.player[player.Index].inventory[k].name, player.Index, (float)k, (float)Main.player[player.Index].inventory[k].prefix, 0f, 0);
}
NetMessage.SendData(5, -1, -1, Main.player[player.Index].armor[0].name, player.Index, 59f, (float)Main.player[player.Index].armor[0].prefix, 0f, 0);
NetMessage.SendData(5, -1, -1, Main.player[player.Index].armor[1].name, player.Index, 60f, (float)Main.player[player.Index].armor[1].prefix, 0f, 0);
NetMessage.SendData(5, -1, -1, Main.player[player.Index].armor[2].name, player.Index, 61f, (float)Main.player[player.Index].armor[2].prefix, 0f, 0);
NetMessage.SendData(5, -1, -1, Main.player[player.Index].armor[3].name, player.Index, 62f, (float)Main.player[player.Index].armor[3].prefix, 0f, 0);
NetMessage.SendData(5, -1, -1, Main.player[player.Index].armor[4].name, player.Index, 63f, (float)Main.player[player.Index].armor[4].prefix, 0f, 0);
NetMessage.SendData(5, -1, -1, Main.player[player.Index].armor[5].name, player.Index, 64f, (float)Main.player[player.Index].armor[5].prefix, 0f, 0);
NetMessage.SendData(5, -1, -1, Main.player[player.Index].armor[6].name, player.Index, 65f, (float)Main.player[player.Index].armor[6].prefix, 0f, 0);
NetMessage.SendData(5, -1, -1, Main.player[player.Index].armor[7].name, player.Index, 66f, (float)Main.player[player.Index].armor[7].prefix, 0f, 0);
NetMessage.SendData(5, -1, -1, Main.player[player.Index].armor[8].name, player.Index, 67f, (float)Main.player[player.Index].armor[8].prefix, 0f, 0);
NetMessage.SendData(5, -1, -1, Main.player[player.Index].armor[9].name, player.Index, 68f, (float)Main.player[player.Index].armor[9].prefix, 0f, 0);
NetMessage.SendData(5, -1, -1, Main.player[player.Index].armor[10].name, player.Index, 69f, (float)Main.player[player.Index].armor[10].prefix, 0f, 0);
NetMessage.SendData(5, -1, -1, Main.player[player.Index].dye[0].name, player.Index, 70f, (float)Main.player[player.Index].dye[0].prefix, 0f, 0);
NetMessage.SendData(5, -1, -1, Main.player[player.Index].dye[1].name, player.Index, 71f, (float)Main.player[player.Index].dye[1].prefix, 0f, 0);
NetMessage.SendData(5, -1, -1, Main.player[player.Index].dye[2].name, player.Index, 72f, (float)Main.player[player.Index].dye[2].prefix, 0f, 0);
NetMessage.SendData(4, -1, -1, player.Name, player.Index, 0f, 0f, 0f, 0);
NetMessage.SendData(42, -1, -1, "", player.Index, 0f, 0f, 0f, 0);
NetMessage.SendData(16, -1, -1, "", player.Index, 0f, 0f, 0f, 0);
for (int k = 0; k < 59; k++)
{
NetMessage.SendData(5, player.Index, -1, Main.player[player.Index].inventory[k].name, player.Index, (float)k, (float)Main.player[player.Index].inventory[k].prefix, 0f, 0);
}
NetMessage.SendData(5, player.Index, -1, Main.player[player.Index].armor[0].name, player.Index, 59f, (float)Main.player[player.Index].armor[0].prefix, 0f, 0);
NetMessage.SendData(5, player.Index, -1, Main.player[player.Index].armor[1].name, player.Index, 60f, (float)Main.player[player.Index].armor[1].prefix, 0f, 0);
NetMessage.SendData(5, player.Index, -1, Main.player[player.Index].armor[2].name, player.Index, 61f, (float)Main.player[player.Index].armor[2].prefix, 0f, 0);
NetMessage.SendData(5, player.Index, -1, Main.player[player.Index].armor[3].name, player.Index, 62f, (float)Main.player[player.Index].armor[3].prefix, 0f, 0);
NetMessage.SendData(5, player.Index, -1, Main.player[player.Index].armor[4].name, player.Index, 63f, (float)Main.player[player.Index].armor[4].prefix, 0f, 0);
NetMessage.SendData(5, player.Index, -1, Main.player[player.Index].armor[5].name, player.Index, 64f, (float)Main.player[player.Index].armor[5].prefix, 0f, 0);
NetMessage.SendData(5, player.Index, -1, Main.player[player.Index].armor[6].name, player.Index, 65f, (float)Main.player[player.Index].armor[6].prefix, 0f, 0);
NetMessage.SendData(5, player.Index, -1, Main.player[player.Index].armor[7].name, player.Index, 66f, (float)Main.player[player.Index].armor[7].prefix, 0f, 0);
NetMessage.SendData(5, player.Index, -1, Main.player[player.Index].armor[8].name, player.Index, 67f, (float)Main.player[player.Index].armor[8].prefix, 0f, 0);
NetMessage.SendData(5, player.Index, -1, Main.player[player.Index].armor[9].name, player.Index, 68f, (float)Main.player[player.Index].armor[9].prefix, 0f, 0);
NetMessage.SendData(5, player.Index, -1, Main.player[player.Index].armor[10].name, player.Index, 69f, (float)Main.player[player.Index].armor[10].prefix, 0f, 0);
NetMessage.SendData(5, player.Index, -1, Main.player[player.Index].dye[0].name, player.Index, 70f, (float)Main.player[player.Index].dye[0].prefix, 0f, 0);
NetMessage.SendData(5, player.Index, -1, Main.player[player.Index].dye[1].name, player.Index, 71f, (float)Main.player[player.Index].dye[1].prefix, 0f, 0);
NetMessage.SendData(5, player.Index, -1, Main.player[player.Index].dye[2].name, player.Index, 72f, (float)Main.player[player.Index].dye[2].prefix, 0f, 0);
NetMessage.SendData(4, player.Index, -1, player.Name, player.Index, 0f, 0f, 0f, 0);
NetMessage.SendData(42, player.Index, -1, "", player.Index, 0f, 0f, 0f, 0);
NetMessage.SendData(16, player.Index, -1, "", player.Index, 0f, 0f, 0f, 0);
for (int k = 0; k < 10; k++)
{
player.TPlayer.buffType[k] = 0;
}
NetMessage.SendData(50, -1, -1, "", player.Index, 0f, 0f, 0f, 0);
NetMessage.SendData(50, player.Index, -1, "", player.Index, 0f, 0f, 0f, 0);
}
}
public class NetItem
{
public static int maxNetInventory = 59;
public static readonly int maxNetInventory = 73;
public static readonly int armorSlots = 11;
public static readonly int dyeSlots = 3;
public int netID;
public int stack;
public int prefix;
public static string ToString(NetItem[] inventory)
{
string inventoryString = "";

1043
TShockAPI/TShock.cs Normal file → Executable file

File diff suppressed because it is too large Load diff

View file

@ -38,6 +38,7 @@
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>bin\Debug\TShockAPI.XML</DocumentationFile>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@ -48,6 +49,7 @@
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>bin\Release\TShockAPI.XML</DocumentationFile>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="HttpServer">
@ -66,11 +68,7 @@
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="TerrariaServer, Version=0.0.0.0, Culture=neutral, processorArchitecture=x86">
<SpecificVersion>False</SpecificVersion>
<ExecutableExtension>.exe</ExecutableExtension>
<HintPath>..\TerrariaServerBins\TerrariaServer.exe</HintPath>
</Reference>
<Reference Include="System.Web" />
</ItemGroup>
<ItemGroup>
<Compile Include="BackupManager.cs" />
@ -78,13 +76,11 @@
<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="RaptorPacketTypes.cs" />
<Compile Include="Rest\RestPermissions.cs" />
<Compile Include="SaveManager.cs" />
<Compile Include="DB\BanManager.cs" />
<Compile Include="DB\InventoryManager.cs" />
<Compile Include="DB\CharacterManager.cs" />
<Compile Include="DB\IQueryBuilder.cs" />
<Compile Include="DB\ItemManager.cs" />
<Compile Include="DB\SqlColumn.cs" />
@ -126,6 +122,7 @@
<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" />
@ -134,12 +131,8 @@
<Compile Include="DB\WarpsManager.cs" />
</ItemGroup>
<ItemGroup>
<None Include="config\groups.txt" />
<None Include="TShockAPI.licenseheader" />
</ItemGroup>
<ItemGroup>
<None Include="config\users.txt" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
@ -170,9 +163,11 @@
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<Content Include="config\itembans.txt" />
<ProjectReference Include="..\TerrariaServerAPI\TerrariaAPI-Server.csproj">
<Project>{549A7941-D9C9-4544-BAC8-D9EEEAB4D777}</Project>
<Name>TerrariaAPI-Server</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PreBuildEvent>

View file

@ -24,6 +24,7 @@ using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using Terraria;
using TShockAPI.DB;
@ -258,7 +259,7 @@ namespace TShockAPI
return found;
byte plrID;
if (byte.TryParse(plr, out plrID))
if (byte.TryParse(plr, out plrID) && plrID < Main.maxPlayers)
{
TSPlayer player = TShock.Players[plrID];
if (player != null && player.Active)
@ -307,11 +308,11 @@ namespace TShockAPI
tileX = startTileX + Random.Next(tileXRange*-1, tileXRange);
tileY = startTileY + Random.Next(tileYRange*-1, tileYRange);
j++;
} while (TilePlacementValid(tileX, tileY) && !TileClear(tileX, tileY));
} while (TilePlacementValid(tileX, tileY) && TileSolid(tileX, tileY));
}
/// <summary>
/// Determines if a tile is valid
/// Determines if a tile is valid.
/// </summary>
/// <param name="tileX">Location X</param>
/// <param name="tileY">Location Y</param>
@ -322,14 +323,16 @@ namespace TShockAPI
}
/// <summary>
/// Checks to see if the tile is clear.
/// Checks if the tile is solid.
/// </summary>
/// <param name="tileX">Location X</param>
/// <param name="tileY">Location Y</param>
/// <returns>The state of the tile</returns>
private bool TileClear(int tileX, int tileY)
/// <returns>The tile's solidity.</returns>
public bool TileSolid(int tileX, int tileY)
{
return !Main.tile[tileX, tileY].active;
return TilePlacementValid(tileX, tileY) && Main.tile[tileX, tileY] != null &&
Main.tile[tileX, tileY].active() && Main.tileSolid[Main.tile[tileX, tileY].type] &&
!Main.tile[tileX, tileY].inActive() && !Main.tile[tileX, tileY].halfBrick() && Main.tile[tileX, tileY].slope() == 0;
}
/// <summary>
@ -371,7 +374,7 @@ namespace TShockAPI
var found = new List<Item>();
Item item = new Item();
string nameLower = name.ToLower();
for (int i = -24; i < Main.maxItemTypes; i++)
for (int i = -48; i < Main.maxItemTypes; i++)
{
item.netDefaults(i);
if (item.name.ToLower() == nameLower)
@ -571,10 +574,10 @@ namespace TShockAPI
/// <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)
if (TShock.Config.ServerSideCharacter)
foreach (TSPlayer player in TShock.Players)
if (player != null && player.IsLoggedIn && !player.IgnoreActionsForClearingTrashCan)
TShock.InventoryDB.InsertPlayerData(player);
TShock.CharacterDB.InsertPlayerData(player);
StopServer(true, reason);
System.Diagnostics.Process.Start(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
@ -589,7 +592,8 @@ namespace TShockAPI
FileTools.SetupConfig();
TShock.HandleCommandLinePostConfigLoad(Environment.GetCommandLineArgs());
TShock.Groups.LoadPermisions();
TShock.Regions.ReloadAllRegions();
TShock.Regions.Reload();
TShock.Itembans.UpdateItemBans();
Hooks.GeneralHooks.OnReloadEvent(player);
}
@ -636,16 +640,16 @@ namespace TShockAPI
string playerName = player.Name;
player.SilentKickInProgress = silent;
if (player.IsLoggedIn && saveSSI)
player.SaveServerInventory();
player.SaveServerCharacter();
player.Disconnect(string.Format("Kicked: {0}", reason));
Log.ConsoleInfo(string.Format("Kicked {0} for : {1}", playerName, reason));
Log.ConsoleInfo(string.Format("Kicked {0} for : '{1}'", playerName, reason));
string verb = force ? "force " : "";
if (!silent)
{
if (string.IsNullOrWhiteSpace(adminUserName))
Broadcast(string.Format("{0} was {1}kicked for {2}", playerName, verb, reason.ToLower()), Color.Green);
Broadcast(string.Format("{0} was {1}kicked for '{2}'", playerName, verb, reason.ToLower()), Color.Green);
else
Broadcast(string.Format("{0} {1}kicked {2} for {3}", adminUserName, verb, playerName, reason.ToLower()), Color.Green);
Broadcast(string.Format("{0} {1}kicked {2} for '{3}'", adminUserName, verb, playerName, reason.ToLower()), Color.Green);
}
return true;
}
@ -673,15 +677,16 @@ namespace TShockAPI
if (force || !player.Group.HasPermission(Permissions.immunetoban))
{
string ip = player.IP;
string uuid = player.UUID;
string playerName = player.Name;
TShock.Bans.AddBan(ip, playerName, reason, false, adminUserName);
TShock.Bans.AddBan(ip, playerName, uuid, reason, false, adminUserName);
player.Disconnect(string.Format("Banned: {0}", reason));
Log.ConsoleInfo(string.Format("Banned {0} for : {1}", playerName, reason));
Log.ConsoleInfo(string.Format("Banned {0} for : '{1}'", playerName, reason));
string verb = force ? "force " : "";
if (string.IsNullOrWhiteSpace(adminUserName))
Broadcast(string.Format("{0} was {1}banned for {2}", playerName, verb, reason.ToLower()));
Broadcast(string.Format("{0} was {1}banned for '{2}'", playerName, verb, reason.ToLower()));
else
Broadcast(string.Format("{0} {1}banned {2} for {3}", adminUserName, verb, playerName, reason.ToLower()));
Broadcast(string.Format("{0} {1}banned {2} for '{3}'", adminUserName, verb, playerName, reason.ToLower()));
return true;
}
return false;
@ -693,7 +698,7 @@ namespace TShockAPI
bool expirationExists = DateTime.TryParse(ban.Expiration, out exp);
if (!string.IsNullOrWhiteSpace(ban.Expiration) && (expirationExists) &&
(DateTime.Now >= exp))
(DateTime.UtcNow >= exp))
{
if (byName)
{
@ -713,7 +718,7 @@ namespace TShockAPI
/// <summary>
/// Shows a file to the user.
/// </summary>
/// <param name="ply">int player</param>
/// <param name="ply">TSPlayer player</param>
/// <param name="file">string filename reletave to savedir</param>
public void ShowFileToUser(TSPlayer player, string file)
{
@ -722,31 +727,28 @@ namespace TShockAPI
{
while ((foo = tr.ReadLine()) != null)
{
if (string.IsNullOrWhiteSpace(foo))
{
continue;
}
foo = foo.Replace("%map%", Main.worldName);
foo = foo.Replace("%players%", GetPlayers());
//foo = SanitizeString(foo);
if (foo.Substring(0, 1) == "%" && foo.Substring(12, 1) == "%") //Look for a beginning color code.
Regex reg = new Regex("%\\s*(?<r>\\d{1,3})\\s*,\\s*(?<g>\\d{1,3})\\s*,\\s*(?<b>\\d{1,3})\\s*%");
var matches = reg.Matches(foo);
Color c = Color.White;
foreach (Match match in matches)
{
string possibleColor = foo.Substring(0, 13);
foo = foo.Remove(0, 13);
float[] pC = {0, 0, 0};
possibleColor = possibleColor.Replace("%", "");
string[] pCc = possibleColor.Split(',');
if (pCc.Length == 3)
byte r, g, b;
if (byte.TryParse(match.Groups["r"].Value, out r) &&
byte.TryParse(match.Groups["g"].Value, out g) &&
byte.TryParse(match.Groups["b"].Value, out b))
{
try
{
player.SendMessage(foo, (byte) Convert.ToInt32(pCc[0]), (byte) Convert.ToInt32(pCc[1]),
(byte) Convert.ToInt32(pCc[2]));
continue;
}
catch (Exception e)
{
Log.Error(e.ToString());
}
c = new Color(r, g, b);
}
foo = foo.Remove(match.Index, match.Length);
}
player.SendMessage(foo);
player.SendMessage(foo, c);
}
}
}
@ -787,6 +789,18 @@ namespace TShockAPI
return "";
}
/// <summary>
/// Sends the player an error message stating that more than one match was found
/// appending a csv list of the matches.
/// </summary>
/// <param name="ply">Player to send the message to</param>
/// <param name="matches">An enumerable list with the matches</param>
public void SendMultipleMatchError(TSPlayer ply, IEnumerable<object> matches)
{
ply.SendErrorMessage("More than one match found: {0}", string.Join(",", matches));
ply.SendErrorMessage("Use \"my query\" for items with spaces");
}
/// <summary>
/// Default hashing algorithm.
/// </summary>
@ -866,6 +880,50 @@ namespace TShockAPI
return true;
}
/// <summary>
/// Attempts to parse a string as a timespan (_d_m_h_s).
/// </summary>
/// <param name="time">The time string.</param>
/// <param name="seconds">The seconds.</param>
/// <returns>Whether the string was parsed successfully.</returns>
public bool TryParseTime(string str, out int seconds)
{
seconds = 0;
var sb = new StringBuilder(3);
for (int i = 0; i < str.Length; i++)
{
if (char.IsDigit(str[i]) || (str[i] == '-' || str[i] == '+'))
sb.Append(str[i]);
else
{
int num;
if (!int.TryParse(sb.ToString(), out num))
return false;
sb.Clear();
switch (str[i])
{
case 's':
seconds += num;
break;
case 'm':
seconds += num * 60;
break;
case 'h':
seconds += num * 60 * 60;
break;
case 'd':
seconds += num * 60 * 60 * 24;
break;
default:
return false;
}
}
}
return true;
}
/// <summary>
/// Searches for a projectile by identity and owner
/// </summary>

View file

@ -1,53 +0,0 @@
#Format
#name parent permisson1 permission2 permissionN
#if there is no parent, put null instead
#groups inherit permissions from their parents
#put a ! before a permission to negate it
#Do not remove the group default
#Do not name a group SuperAdmin, that is hard-coded into the code, it grants total permissions
#ALWAYS DECLARE A GROUP'S PARENT BEFORE YOU DECLARE THE GROUP
#currently avaliable permissions:
#reservedslot - reserved slot for player
#canwater - allow players to use water
#canlava - allow players to use lava
#canspike - allow players to place spikes
#warp - allow players to use warps
#kick - kick users
#ban - ban players by name or ip
#unban - unban players by name or ip
#whitelist - add ip to whitelist
#maintenance - check for updates/turn off server
#causeevents - allow player to drop star/meteor and start invasion/bloodmoon
#spawnboss - allow player to spawn bosses
#spawnmob - allow player to spawn any npcs
#tp - allow player to teleport
#tphere - allow a player to teleport players to their position
#managewarp - allow player to add/delete warp locations
#managegroup - allow player to add/delete/modify groups
#manageitem - allow a player to add/delete item bans
#manageregion - allow a user to create/edit regions
#editspawn - allow player to enable/disable build protection
#cfg - allow player to view/change tshock configuration
#time - allow player to change time
#pvpfun - enable pvpfun commands
#logs - notify player when any command is executed
#kill - allow player to kill others
#butcher - allow player to kill all alive monsters
#item - allow player to spawn items
#heal - allow player to heal
#immunetokick - player can't be kick
#immunetoban - player can't be banned
#ignorecheatdetection - allow player to cheat (health/mana cheats)
#ignoregriefdetection - allow player to grief (use explosives, water, lava even if they dont have premission to)
#usebanneditem - allows player to use banned items
#manageusers - Grab player info
#whisper - allow player to whisper to other players
#adminchat - Colors and adds a prefix to the player's chats.
#canbuild - allow player to build (does not bypass other checks)
default null canwater canlava warp canbuild
vip default reservedslot
newadmin default kick editspawn reservedslot
admin newadmin ban unban whitelist causeevents spawnboss spawnmob managewarp time tp pvpfun kill logs immunetokick tphere
trustedadmin admin maintenance cfg butcher cheat immunetoban ignorecheatdetection ignoregriefdetection usebanneditem

View file

@ -1,2 +0,0 @@
#see https://github.com/TShock/TShock/wiki/Item-List for a list of item ids
#List each banned item ID below this, with each on a new line

View file

@ -1,4 +0,0 @@
#format
#ip group
#see groups.txt for a list of groups
#127.0.0.1 superadmin

1
TerrariaServerAPI Submodule

@ -0,0 +1 @@
Subproject commit 0ddd492aa03e5c97dddbc95b2e605fa2823804ab

View file

@ -54,7 +54,7 @@ namespace UnitTests
[TestMethod]
public void AddBanTest()
{
Assert.IsTrue(Bans.AddBan("127.0.0.1", "BanTest", "Ban Testing"));
Assert.IsTrue(Bans.AddBan("127.0.0.1", "BanTest", "BanTest2", "Ban Testing"));
}
[TestMethod]
@ -62,6 +62,7 @@ namespace UnitTests
{
Assert.IsNotNull(Bans.GetBanByIp("127.0.0.1"));
Assert.IsNotNull(Bans.GetBanByName("BanTest"));
Assert.IsNotNull(Bans.GetBanByUUID("BanTest2"));
}
}
}

111
create_release.py Normal file
View file

@ -0,0 +1,111 @@
# Hey there, this is used to compile TShock on the build server.
# Don't change it. Thanks!
import os
import shutil
import subprocess
import urllib2
import zipfile
cur_wd = os.getcwd()
release_dir = os.path.join(cur_wd, "releases")
terraria_bin_name = "TerrariaServer.exe"
sql_bins_names = ["Mono.Data.Sqlite.dll", "MySql.Data.dll", "MySql.Web.dll"]
sqlite_dep = "sqlite3.dll"
json_bin_name = "Newtonsoft.Json.dll"
http_bin_name = "HttpServer.dll"
tshock_bin_name = "TShockAPI.dll"
tshock_symbols = "TShockAPI.dll.mdb"
terraria_release_bin = os.path.join(cur_wd, "TerrariaServerAPI", "bin", "Release", terraria_bin_name)
terraria_debug_bin = os.path.join(cur_wd, "TerrariaServerAPI", "bin", "Debug", terraria_bin_name)
sql_dep = os.path.join(cur_wd, "SqlBins")
http_bin = os.path.join(cur_wd, "HttpBins", http_bin_name)
json_bin = os.path.join(cur_wd, "TShockAPI", json_bin_name)
release_bin = os.path.join(cur_wd, "TShockAPI", "bin", "Release", tshock_bin_name)
debug_folder = os.path.join(cur_wd, "TShockAPI", "bin", "Debug")
def create_release_folder():
os.mkdir(release_dir)
def copy_dependencies():
shutil.copy(http_bin, release_dir)
shutil.copy(json_bin, release_dir)
shutil.copy(os.path.join(sql_dep, sqlite_dep), release_dir)
for f in sql_bins_names:
shutil.copy(os.path.join(sql_dep, f), release_dir)
def copy_debug_files():
shutil.copy(terraria_debug_bin, release_dir)
shutil.copy(os.path.join(debug_folder, tshock_bin_name), release_dir)
shutil.copy(os.path.join(debug_folder, tshock_symbols), release_dir)
def copy_release_files():
shutil.copy(terraria_release_bin, release_dir)
shutil.copy(release_bin, release_dir)
shutil.copy(release_bin, release_dir)
def create_base_zip(name):
os.chdir(release_dir)
zip = zipfile.ZipFile(name, "w")
zip.write(terraria_bin_name)
zip.write(sqlite_dep)
zip.write(http_bin_name, os.path.join("ServerPlugins", http_bin_name))
zip.write(json_bin_name, os.path.join("ServerPlugins", json_bin_name))
for f in sql_bins_names:
zip.write(f, os.path.join("ServerPlugins", f))
return zip
def package_release():
copy_release_files()
zip = create_base_zip("tshock_release.zip")
zip.write(tshock_bin_name, os.path.join("ServerPlugins", tshock_bin_name))
zip.close()
os.remove(tshock_bin_name)
os.remove(terraria_bin_name)
os.chdir(cur_wd)
def package_debug():
copy_debug_files()
zip = create_base_zip("tshock_debug.zip")
zip.write(tshock_bin_name, os.path.join("ServerPlugins", tshock_bin_name))
zip.write(tshock_symbols, os.path.join("ServerPlugins", tshock_symbols))
zip.close()
os.remove(tshock_bin_name)
os.remove(tshock_symbols)
os.remove(terraria_bin_name)
os.chdir(cur_wd)
def delete_files():
os.chdir(release_dir)
for f in sql_bins_names:
os.remove(f)
os.remove(sqlite_dep)
os.remove(json_bin_name)
os.remove(http_bin_name)
os.chdir(cur_wd)
def update_terraria_source():
subprocess.check_call(['/usr/bin/git', 'submodule', 'init'])
subprocess.check_call(['/usr/bin/git', 'submodule', 'update'])
def build_software():
release_proc = subprocess.Popen(['/usr/local/bin/xbuild', './TShockAPI/TShockAPI.csproj', '/p:Configuration=Release'])
debug_proc = subprocess.Popen(['/usr/local/bin/xbuild', './TShockAPI/TShockAPI.csproj', '/p:Configuration=Debug'])
release_proc.wait()
debug_proc.wait()
if (release_proc.returncode != 0):
raise CalledProcessError(release_proc.returncode)
if (debug_proc.returncode != 0):
raise CalledProcessError(debug_proc.returncode)
if __name__ == '__main__':
create_release_folder()
update_terraria_source()
copy_dependencies()
build_software()
package_release()
package_debug()
delete_files()

View file

@ -42,6 +42,7 @@ An example configuration file is below.
"KickOnMediumcoreDeath": false,
"BanOnMediumcoreDeath": false,
"AutoSave": true,
"AnnounceSave": true,
"MaximumLoginAttempts": 3,
"RconPassword": "",
"RconPort": 7777,
@ -141,6 +142,11 @@ The following is the official documentation for the configuration file:
**Description:** Enable/Disable Terrarias built in auto save
**Default:** "True"
## AnnounceSave
**Type:** Boolean
**Description:** Enable/Disable save announcements
**Default:** "True"
## BackupInterval
**Type:** Int32
**Description:** Backup frequency in minutes. So, a value of 60 = 60 minutes. Backups are stored in the \tshock\backups folder.