ReSharper code reformat to match naming conventions and stuff

This commit is contained in:
Lucas Nicodemus 2011-12-30 14:38:04 -07:00
parent 1147788154
commit c6abbfe4d2
45 changed files with 11639 additions and 11342 deletions

View file

@ -24,81 +24,79 @@ using Terraria;
namespace TShockAPI namespace TShockAPI
{ {
public class BackupManager public class BackupManager
{ {
public string BackupPath { get; set; } public string BackupPath { get; set; }
public int Interval { get; set; } public int Interval { get; set; }
public int KeepFor { get; set; } public int KeepFor { get; set; }
DateTime lastbackup = DateTime.UtcNow; private DateTime lastbackup = DateTime.UtcNow;
public BackupManager(string path)
{
BackupPath = path;
}
public bool IsBackupTime public BackupManager(string path)
{ {
get BackupPath = path;
{ }
return (Interval > 0) && ((DateTime.UtcNow - lastbackup).TotalMinutes >= Interval);
}
}
public void Backup() public bool IsBackupTime
{ {
lastbackup = DateTime.UtcNow; get { return (Interval > 0) && ((DateTime.UtcNow - lastbackup).TotalMinutes >= Interval); }
ThreadPool.QueueUserWorkItem(DoBackup); }
ThreadPool.QueueUserWorkItem(DeleteOld);
}
void DoBackup(object o) public void Backup()
{ {
try lastbackup = DateTime.UtcNow;
{ ThreadPool.QueueUserWorkItem(DoBackup);
string worldname = Main.worldPathName; ThreadPool.QueueUserWorkItem(DeleteOld);
string name = Path.GetFileName(worldname); }
Main.worldPathName = Path.Combine(BackupPath, string.Format("{0}.{1:dd.MM.yy-HH.mm.ss}.bak", name, DateTime.UtcNow)); private void DoBackup(object o)
{
try
{
string worldname = Main.worldPathName;
string name = Path.GetFileName(worldname);
string worldpath = Path.GetDirectoryName(Main.worldPathName); Main.worldPathName = Path.Combine(BackupPath, string.Format("{0}.{1:dd.MM.yy-HH.mm.ss}.bak", name, DateTime.UtcNow));
if (worldpath != null && !Directory.Exists(worldpath))
Directory.CreateDirectory(worldpath);
TShock.Utils.Broadcast("Server map saving, potential lag spike"); string worldpath = Path.GetDirectoryName(Main.worldPathName);
Console.WriteLine("Backing up world..."); if (worldpath != null && !Directory.Exists(worldpath))
Directory.CreateDirectory(worldpath);
Thread SaveWorld = new Thread(TShock.Utils.SaveWorld); TShock.Utils.Broadcast("Server map saving, potential lag spike");
SaveWorld.Start(); Console.WriteLine("Backing up world...");
while (SaveWorld.ThreadState == ThreadState.Running) Thread SaveWorld = new Thread(TShock.Utils.SaveWorld);
Thread.Sleep(50); SaveWorld.Start();
Console.WriteLine("World backed up");
Console.ForegroundColor = ConsoleColor.Gray;
Log.Info(string.Format("World backed up ({0})", Main.worldPathName));
Main.worldPathName = worldname; while (SaveWorld.ThreadState == ThreadState.Running)
} Thread.Sleep(50);
catch (Exception ex) Console.WriteLine("World backed up");
{ Console.ForegroundColor = ConsoleColor.Gray;
Console.ForegroundColor = ConsoleColor.Red; Log.Info(string.Format("World backed up ({0})", Main.worldPathName));
Console.WriteLine("Backup failed");
Console.ForegroundColor = ConsoleColor.Gray;
Log.Error("Backup failed");
Log.Error(ex.ToString());
}
}
void DeleteOld(object o) Main.worldPathName = worldname;
{ }
if (KeepFor <= 0) catch (Exception ex)
return; {
foreach (var fi in new DirectoryInfo(BackupPath).GetFiles("*.bak")) Console.ForegroundColor = ConsoleColor.Red;
{ Console.WriteLine("Backup failed");
if ((DateTime.UtcNow - fi.LastWriteTimeUtc).TotalMinutes > KeepFor) Console.ForegroundColor = ConsoleColor.Gray;
{ Log.Error("Backup failed");
fi.Delete(); Log.Error(ex.ToString());
} }
} }
}
} private void DeleteOld(object o)
} {
if (KeepFor <= 0)
return;
foreach (var fi in new DirectoryInfo(BackupPath).GetFiles("*.bak"))
{
if ((DateTime.UtcNow - fi.LastWriteTimeUtc).TotalMinutes > KeepFor)
{
fi.Delete();
}
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -24,274 +24,245 @@ using Newtonsoft.Json;
namespace TShockAPI namespace TShockAPI
{ {
public class ConfigFile public class ConfigFile
{ {
[Description("The equation for calculating invasion size is 100 + (multiplier * (number of active players with greater than 200 health))")] [Description(
public int InvasionMultiplier = 1; "The equation for calculating invasion size is 100 + (multiplier * (number of active players with greater than 200 health))"
[Description("The default maximum mobs that will spawn per wave. Higher means more mobs in that wave.")] )] public int InvasionMultiplier = 1;
public int DefaultMaximumSpawns = 5;
[Description("The delay between waves. Shorter values lead to less mobs.")]
public int DefaultSpawnRate = 600;
[Description("The port the server runs on.")]
public int ServerPort = 7777;
[Description("Enable or disable the whitelist based on IP addresses in whitelist.txt")]
public bool EnableWhitelist;
[Description("Enable the ability for invaison size to never decrease. Make sure to run /invade, and note that this adds 2 million+ goblins to the spawn que for the map.")]
public bool InfiniteInvasion;
[Description("Set the server pvp mode. Vaild types are, \"normal\", \"always\", \"disabled\"")]
public string PvPMode = "normal";
[Description("Prevents tiles from being placed within SpawnProtectionRadius of the default spawn.")]
public bool SpawnProtection = true;
[Description("Radius from spawn tile for SpawnProtection.")]
public int SpawnProtectionRadius = 10;
[Description("Max slots for the server. If you want people to be kicked with \"Server is full\" set this to how many players you want max and then set Terraria max players to 2 higher.")]
public int MaxSlots = 8;
[Description("Global protection agent for any block distance based anti-grief check.")]
public bool RangeChecks = true;
[Description("Disables any building; placing of blocks")]
public bool DisableBuild;
[Description("#.#.#. = Red/Blue/Green - RGB Colors for the Admin Chat Color. Max value: 255")]
public float[] SuperAdminChatRGB = { 255, 0, 0 };
[Description("Super admin group chat prefix")]
public string SuperAdminChatPrefix = "(Admin) ";
[Description("Super admin group chat suffix")]
public string SuperAdminChatSuffix = "";
[Description("Backup frequency in minutes. So, a value of 60 = 60 minutes. Backups are stored in the \\tshock\\backups folder.")]
public int BackupInterval;
[Description("How long backups are kept in minutes. 2880 = 2 days.")]
public int BackupKeepFor = 60;
[Description("Remembers where a player left off. It works by remembering the IP, NOT the character. \neg. When you try to disconnect, and reconnect to be automatically placed at spawn, you'll be at your last location. Note: Won't save after server restarts.")] [Description("The default maximum mobs that will spawn per wave. Higher means more mobs in that wave.")] public int
public bool RememberLeavePos; DefaultMaximumSpawns = 5;
[Description("Hardcore players ONLY. This means softcore players cannot join.")]
public bool HardcoreOnly;
[Description("Mediumcore players ONLY. This means softcore players cannot join.")]
public bool MediumcoreOnly;
[Description("Kicks a Hardcore player on death.")]
public bool KickOnMediumcoreDeath;
[Description("Bans a Hardcore player on death.")]
public bool BanOnMediumcoreDeath;
[Description("Enable/Disable Terrarias built in auto save")] [Description("The delay between waves. Shorter values lead to less mobs.")] public int DefaultSpawnRate = 600;
public bool AutoSave = true; [Description("The port the server runs on.")] public int ServerPort = 7777;
[Description("Enable or disable the whitelist based on IP addresses in whitelist.txt")] public bool EnableWhitelist;
[Description("Number of failed login attempts before kicking the player.")] [Description(
public int MaximumLoginAttempts = 3; "Enable the ability for invaison size to never decrease. Make sure to run /invade, and note that this adds 2 million+ goblins to the spawn que for the map."
)] public bool InfiniteInvasion;
[Description("Not implemented")] [Description("Set the server pvp mode. Vaild types are, \"normal\", \"always\", \"disabled\"")] public string PvPMode
public string RconPassword = ""; = "normal";
[Description("Not implemented")]
public int RconPort = 7777;
[Description("Not implemented")] [Description("Prevents tiles from being placed within SpawnProtectionRadius of the default spawn.")] public bool
public string ServerName = ""; SpawnProtection = true;
[Description("Not implemented")]
public string MasterServer = "127.0.0.1";
[Description("Valid types are \"sqlite\" and \"mysql\"")] [Description("Radius from spawn tile for SpawnProtection.")] public int SpawnProtectionRadius = 10;
public string StorageType = "sqlite";
[Description("The MySQL Hostname and port to direct connections to")] [Description(
public string MySqlHost = "localhost:3306"; "Max slots for the server. If you want people to be kicked with \"Server is full\" set this to how many players you want max and then set Terraria max players to 2 higher."
[Description("Database name to connect to")] )] public int MaxSlots = 8;
public string MySqlDbName = "";
[Description("Database username to connect with")]
public string MySqlUsername = "";
[Description("Database password to connect with")]
public string MySqlPassword = "";
[Description("Bans a Mediumcore player on death.")] [Description("Global protection agent for any block distance based anti-grief check.")] public bool RangeChecks = true;
public string MediumcoreBanReason = "Death results in a ban"; [Description("Disables any building; placing of blocks")] public bool DisableBuild;
[Description("Kicks a Mediumcore player on death.")]
public string MediumcoreKickReason = "Death results in a kick";
[Description("Enables DNS resolution of incoming connections with GetGroupForIPExpensive.")] [Description("#.#.#. = Red/Blue/Green - RGB Colors for the Admin Chat Color. Max value: 255")] public float[]
public bool EnableDNSHostResolution; SuperAdminChatRGB = {255, 0, 0};
[Description("Enables kicking of banned users by matching their IP Address")] [Description("Super admin group chat prefix")] public string SuperAdminChatPrefix = "(Admin) ";
public bool EnableIPBans = true; [Description("Super admin group chat suffix")] public string SuperAdminChatSuffix = "";
[Description("Enables kicking of banned users by matching their Character Name")] [Description(
public bool EnableBanOnUsernames; "Backup frequency in minutes. So, a value of 60 = 60 minutes. Backups are stored in the \\tshock\\backups folder.")] public int BackupInterval;
[Description("Drops excessive sync packets")] [Description("How long backups are kept in minutes. 2880 = 2 days.")] public int BackupKeepFor = 60;
public bool EnableAntiLag = true;
[Description("Selects the default group name to place new registrants under")]
public string DefaultRegistrationGroupName = "default";
[Description("Selects the default group name to place non registered users under")] [Description(
public string DefaultGuestGroupName = "guest"; "Remembers where a player left off. It works by remembering the IP, NOT the character. \neg. When you try to disconnect, and reconnect to be automatically placed at spawn, you'll be at your last location. Note: Won't save after server restarts."
)] public bool RememberLeavePos;
[Description("Force-Disable printing logs to players with the log permission")] [Description("Hardcore players ONLY. This means softcore players cannot join.")] public bool HardcoreOnly;
public bool DisableSpewLogs = true; [Description("Mediumcore players ONLY. This means softcore players cannot join.")] public bool MediumcoreOnly;
[Description("Kicks a Hardcore player on death.")] public bool KickOnMediumcoreDeath;
[Description("Bans a Hardcore player on death.")] public bool BanOnMediumcoreDeath;
[Description("Valid types are \"sha512\", \"sha256\", \"md5\", append with \"-xp\" for the xp supported algorithms")] [Description("Enable/Disable Terrarias built in auto save")] public bool AutoSave = true;
public string HashAlgorithm = "sha512";
[Description("Buffers up the packets and sends them out at the end of each frame")] [Description("Number of failed login attempts before kicking the player.")] public int MaximumLoginAttempts = 3;
public bool BufferPackets = true;
[Description("String that is used when kicking people when the server is full.")] [Description("Not implemented")] public string RconPassword = "";
public string ServerFullReason = "Server is full"; [Description("Not implemented")] public int RconPort = 7777;
[Description("String that is used when kicking people when the server is full with no reserved slots.")] [Description("Not implemented")] public string ServerName = "";
public string ServerFullNoReservedReason = "Server is full. No reserved slots open."; [Description("Not implemented")] public string MasterServer = "127.0.0.1";
[Description("This will save the world if Terraria crashes from an unhandled exception.")] [Description("Valid types are \"sqlite\" and \"mysql\"")] public string StorageType = "sqlite";
public bool SaveWorldOnCrash = true;
[Description("This will announce a player's location on join")] [Description("The MySQL Hostname and port to direct connections to")] public string MySqlHost = "localhost:3306";
public bool EnableGeoIP; [Description("Database name to connect to")] public string MySqlDbName = "";
[Description("Database username to connect with")] public string MySqlUsername = "";
[Description("Database password to connect with")] public string MySqlPassword = "";
[Description("This will turn on a token requirement for the /status API endpoint.")] [Description("Bans a Mediumcore player on death.")] public string MediumcoreBanReason = "Death results in a ban";
public bool EnableTokenEndpointAuthentication; [Description("Kicks a Mediumcore player on death.")] public string MediumcoreKickReason = "Death results in a kick";
[Description("This is used when the API endpoint /status is queried.")] [Description("Enables DNS resolution of incoming connections with GetGroupForIPExpensive.")] public bool
public string ServerNickname = "TShock Server"; EnableDNSHostResolution;
[Description("Enable/Disable the rest api.")] [Description("Enables kicking of banned users by matching their IP Address")] public bool EnableIPBans = true;
public bool RestApiEnabled;
[Description("This is the port which the rest api will listen on.")] [Description("Enables kicking of banned users by matching their Character Name")] public bool EnableBanOnUsernames;
public int RestApiPort = 7878;
[Description("Disable tombstones for all players.")] [Description("Drops excessive sync packets")] public bool EnableAntiLag = true;
public bool DisableTombstones = true;
[Description("Displays a player's IP on join to everyone who has the log permission")] [Description("Selects the default group name to place new registrants under")] public string
public bool DisplayIPToAdmins; DefaultRegistrationGroupName = "default";
[Description("Some tiles are 'fixed' by not letting TShock handle them. Disabling this may break certain asthetic tiles.")]
public bool EnableInsecureTileFixes = true;
[Description("Kicks users using a proxy as identified with the GeoIP database")] [Description("Selects the default group name to place non registered users under")] public string
public bool KickProxyUsers = true; DefaultGuestGroupName = "guest";
[Description("Disables hardmode, can't never be activated. Overrides /starthardmode")] [Description("Force-Disable printing logs to players with the log permission")] public bool DisableSpewLogs = true;
public bool DisableHardmode;
[Description("Disables Dungeon Guardian from being spawned by player packets, this will instead force a respawn")] [Description("Valid types are \"sha512\", \"sha256\", \"md5\", append with \"-xp\" for the xp supported algorithms")] public string HashAlgorithm = "sha512";
public bool DisableDungeonGuardian;
[Description("Enable Server Side Inventory checks, EXPERIMENTAL")] [Description("Buffers up the packets and sends them out at the end of each frame")] public bool BufferPackets = true;
public bool ServerSideInventory;
[Description("Disables reporting of playercount to the stat system.")] [Description("String that is used when kicking people when the server is full.")] public string ServerFullReason =
public bool DisablePlayerCountReporting; "Server is full";
[Description("Disables clown bomb projectiles from spawning")] [Description("String that is used when kicking people when the server is full with no reserved slots.")] public string
public bool DisableClownBombs; ServerFullNoReservedReason = "Server is full. No reserved slots open.";
[Description("Disables snow ball projectiles from spawning")] [Description("This will save the world if Terraria crashes from an unhandled exception.")] public bool
public bool DisableSnowBalls; SaveWorldOnCrash = true;
[Description("Change ingame chat format, {0} = Group Name, {1} = Group Prefix, {2} = Player Name, {3} = Group Suffix, {4} = Chat Message")] [Description("This will announce a player's location on join")] public bool EnableGeoIP;
public string ChatFormat = "{1}{2}{3}: {4}";
[Description("Force the world time to be normal, day, or night")] [Description("This will turn on a token requirement for the /status API endpoint.")] public bool
public string ForceTime = "normal"; EnableTokenEndpointAuthentication;
[Description("Disable/Revert a player if they exceed this number of tile kills within 1 second.")] [Description("This is used when the API endpoint /status is queried.")] public string ServerNickname = "TShock Server";
public int TileKillThreshold = 60;
[Description("Disable/Revert a player if they exceed this number of tile places within 1 second.")] [Description("Enable/Disable the rest api.")] public bool RestApiEnabled;
public int TilePlaceThreshold = 20;
[Description("Disable a player if they exceed this number of liquid sets within 1 second.")] [Description("This is the port which the rest api will listen on.")] public int RestApiPort = 7878;
public int TileLiquidThreshold = 15;
[Description("Disable a player if they exceed this number of projectile new within 1 second.")] [Description("Disable tombstones for all players.")] public bool DisableTombstones = true;
public int ProjectileThreshold = 50;
[Description("Require all players to register or login before being allowed to play.")] [Description("Displays a player's IP on join to everyone who has the log permission")] public bool DisplayIPToAdmins;
public bool RequireLogin;
[Description("Disables Invisibility potions from being used in PvP (Note, they can use them on the client, but the effect isn't sent to the rest of the server)")] [Description(
public bool DisableInvisPvP; "Some tiles are 'fixed' by not letting TShock handle them. Disabling this may break certain asthetic tiles.")] public
bool EnableInsecureTileFixes = true;
[Description("The maximum distance players disabled for various reasons can move from")] [Description("Kicks users using a proxy as identified with the GeoIP database")] public bool KickProxyUsers = true;
public int MaxRangeForDisabled = 10;
[Description("Server password required to join server")] [Description("Disables hardmode, can't never be activated. Overrides /starthardmode")] public bool DisableHardmode;
public string ServerPassword = "";
[Description("Protect chests with region and build permissions")] [Description("Disables Dungeon Guardian from being spawned by player packets, this will instead force a respawn")] public bool DisableDungeonGuardian;
public bool RegionProtectChests;
[Description("Disable users from being able to login with account password when joining")] [Description("Enable Server Side Inventory checks, EXPERIMENTAL")] public bool ServerSideInventory;
public bool DisableLoginBeforeJoin;
[Description("Disables reporting of playercount to the stat system.")] public bool DisablePlayerCountReporting;
[Description("Disables clown bomb projectiles from spawning")] public bool DisableClownBombs;
[Description("Disables snow ball projectiles from spawning")] public bool DisableSnowBalls;
[Description(
"Change 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("Force the world time to be normal, day, or night")] public string ForceTime = "normal";
[Description("Disable/Revert a player if they exceed this number of tile kills within 1 second.")] public int
TileKillThreshold = 60;
[Description("Disable/Revert a player if they exceed this number of tile places within 1 second.")] public int
TilePlaceThreshold = 20;
[Description("Disable a player if they exceed this number of liquid sets within 1 second.")] public int
TileLiquidThreshold = 15;
[Description("Disable a player if they exceed this number of projectile new within 1 second.")] public int
ProjectileThreshold = 50;
[Description("Require all players to register or login before being allowed to play.")] public bool RequireLogin;
[Description(
"Disables Invisibility potions from being used in PvP (Note, they can use them on the client, but the effect isn't sent to the rest of the server)"
)] public bool DisableInvisPvP;
[Description("The maximum distance players disabled for various reasons can move from")] public int
MaxRangeForDisabled = 10;
[Description("Server password required to join server")] public string ServerPassword = "";
[Description("Protect chests with region and build permissions")] public bool RegionProtectChests;
[Description("Disable users from being able to login with account password when joining")] public bool
DisableLoginBeforeJoin;
[Description("Allows users to register any username with /register")] public bool AllowRegisterAnyUsername;
[Description("Allows users to register any username with /register")]
public bool AllowRegisterAnyUsername;
public static ConfigFile Read(string path) public static ConfigFile Read(string path)
{ {
if (!File.Exists(path)) if (!File.Exists(path))
return new ConfigFile(); return new ConfigFile();
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{ {
return Read(fs); return Read(fs);
} }
} }
public static ConfigFile Read(Stream stream) public static ConfigFile Read(Stream stream)
{ {
using (var sr = new StreamReader(stream)) using (var sr = new StreamReader(stream))
{ {
var cf = JsonConvert.DeserializeObject<ConfigFile>(sr.ReadToEnd()); var cf = JsonConvert.DeserializeObject<ConfigFile>(sr.ReadToEnd());
if (ConfigRead != null) if (ConfigRead != null)
ConfigRead(cf); ConfigRead(cf);
return cf; return cf;
} }
} }
public void Write(string path) public void Write(string path)
{ {
using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Write)) using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Write))
{ {
Write(fs); Write(fs);
} }
} }
public void Write(Stream stream) public void Write(Stream stream)
{ {
var str = JsonConvert.SerializeObject(this, Formatting.Indented); var str = JsonConvert.SerializeObject(this, Formatting.Indented);
using (var sw = new StreamWriter(stream)) using (var sw = new StreamWriter(stream))
{ {
sw.Write(str); sw.Write(str);
} }
} }
public static Action<ConfigFile> ConfigRead; public static Action<ConfigFile> ConfigRead;
static void DumpDescriptions() private static void DumpDescriptions()
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
var defaults = new ConfigFile(); var defaults = new ConfigFile();
foreach (var field in defaults.GetType().GetFields()) foreach (var field in defaults.GetType().GetFields())
{ {
if (field.IsStatic) if (field.IsStatic)
continue; continue;
var name = field.Name; var name = field.Name;
var type = field.FieldType.Name; var type = field.FieldType.Name;
var descattr = field.GetCustomAttributes(false).FirstOrDefault(o => o is DescriptionAttribute) as DescriptionAttribute; var descattr =
var desc = descattr != null && !string.IsNullOrWhiteSpace(descattr.Description) ? descattr.Description : "None"; field.GetCustomAttributes(false).FirstOrDefault(o => o is DescriptionAttribute) as DescriptionAttribute;
var desc = descattr != null && !string.IsNullOrWhiteSpace(descattr.Description) ? descattr.Description : "None";
var def = field.GetValue(defaults); var def = field.GetValue(defaults);
sb.AppendLine("## {0} ".SFormat(name)); sb.AppendLine("## {0} ".SFormat(name));
sb.AppendLine("**Type:** {0} ".SFormat(type)); sb.AppendLine("**Type:** {0} ".SFormat(type));
sb.AppendLine("**Description:** {0} ".SFormat(desc)); sb.AppendLine("**Description:** {0} ".SFormat(desc));
sb.AppendLine("**Default:** \"{0}\" ".SFormat(def)); sb.AppendLine("**Default:** \"{0}\" ".SFormat(def));
sb.AppendLine(); sb.AppendLine();
} }
File.WriteAllText("ConfigDescriptions.txt", sb.ToString()); File.WriteAllText("ConfigDescriptions.txt", sb.ToString());
} }
} }
} }

View file

@ -23,150 +23,153 @@ using MySql.Data.MySqlClient;
namespace TShockAPI.DB namespace TShockAPI.DB
{ {
public class BanManager public class BanManager
{ {
private IDbConnection database; private IDbConnection database;
public BanManager(IDbConnection db) public BanManager(IDbConnection db)
{ {
database = db; database = db;
var table = new SqlTable("Bans", var table = new SqlTable("Bans",
new SqlColumn("IP", MySqlDbType.String, 16) { Primary = true }, new SqlColumn("IP", MySqlDbType.String, 16) {Primary = true},
new SqlColumn("Name", MySqlDbType.Text), new SqlColumn("Name", MySqlDbType.Text),
new SqlColumn("Reason", MySqlDbType.Text) new SqlColumn("Reason", MySqlDbType.Text)
); );
var creator = new SqlTableCreator(db, db.GetSqlType() == SqlType.Sqlite ? (IQueryBuilder)new SqliteQueryCreator() : new MysqlQueryCreator()); var creator = new SqlTableCreator(db,
creator.EnsureExists(table); db.GetSqlType() == SqlType.Sqlite
? (IQueryBuilder) new SqliteQueryCreator()
: new MysqlQueryCreator());
creator.EnsureExists(table);
String file = Path.Combine(TShock.SavePath, "bans.txt"); String file = Path.Combine(TShock.SavePath, "bans.txt");
if (File.Exists(file)) if (File.Exists(file))
{ {
using (StreamReader sr = new StreamReader(file)) using (StreamReader sr = new StreamReader(file))
{ {
String line; String line;
while ((line = sr.ReadLine()) != null) while ((line = sr.ReadLine()) != null)
{ {
String[] info = line.Split('|'); String[] info = line.Split('|');
string query; string query;
if (TShock.Config.StorageType.ToLower() == "sqlite") if (TShock.Config.StorageType.ToLower() == "sqlite")
query = "INSERT OR IGNORE INTO Bans (IP, Name, Reason) VALUES (@0, @1, @2);"; query = "INSERT OR IGNORE INTO Bans (IP, Name, Reason) VALUES (@0, @1, @2);";
else else
query = "INSERT IGNORE INTO Bans SET IP=@0, Name=@1, Reason=@2;"; query = "INSERT IGNORE INTO Bans SET IP=@0, Name=@1, Reason=@2;";
db.Query(query, info[0].Trim(), info[1].Trim(), info[2].Trim()); db.Query(query, info[0].Trim(), info[1].Trim(), info[2].Trim());
} }
} }
String path = Path.Combine(TShock.SavePath, "old_configs"); String path = Path.Combine(TShock.SavePath, "old_configs");
String file2 = Path.Combine(path, "bans.txt"); String file2 = Path.Combine(path, "bans.txt");
if (!Directory.Exists(path)) if (!Directory.Exists(path))
Directory.CreateDirectory(path); Directory.CreateDirectory(path);
if (File.Exists(file2)) if (File.Exists(file2))
File.Delete(file2); File.Delete(file2);
File.Move(file, file2); File.Move(file, file2);
} }
} }
public Ban GetBanByIp(string ip) public Ban GetBanByIp(string ip)
{ {
try try
{ {
using (var reader = database.QueryReader("SELECT * FROM Bans WHERE IP=@0", ip)) using (var reader = database.QueryReader("SELECT * FROM Bans WHERE IP=@0", ip))
{ {
if (reader.Read()) if (reader.Read())
return new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("Reason")); return new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("Reason"));
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex.ToString()); Log.Error(ex.ToString());
} }
return null; return null;
} }
public Ban GetBanByName(string name, bool casesensitive = true) public Ban GetBanByName(string name, bool casesensitive = true)
{ {
if (!TShock.Config.EnableBanOnUsernames) if (!TShock.Config.EnableBanOnUsernames)
{ {
return null; return null;
} }
try try
{ {
var namecol = casesensitive ? "Name" : "UPPER(Name)"; var namecol = casesensitive ? "Name" : "UPPER(Name)";
if (!casesensitive) if (!casesensitive)
name = name.ToUpper(); name = name.ToUpper();
using (var reader = database.QueryReader("SELECT * FROM Bans WHERE " + namecol + "=@0", name)) using (var reader = database.QueryReader("SELECT * FROM Bans WHERE " + namecol + "=@0", name))
{ {
if (reader.Read()) if (reader.Read())
return new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("Reason")); return new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("Reason"));
}
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
return null;
}
} public bool AddBan(string ip, string name = "", string reason = "")
} {
catch (Exception ex) try
{ {
Log.Error(ex.ToString()); return database.Query("INSERT INTO Bans (IP, Name, Reason) VALUES (@0, @1, @2);", ip, name, reason) != 0;
} }
return null; catch (Exception ex)
} {
Log.Error(ex.ToString());
}
return false;
}
public bool AddBan(string ip, string name = "", string reason = "") public bool RemoveBan(string ip)
{ {
try try
{ {
return database.Query("INSERT INTO Bans (IP, Name, Reason) VALUES (@0, @1, @2);", ip, name, reason) != 0; return database.Query("DELETE FROM Bans WHERE IP=@0", ip) != 0;
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex.ToString()); Log.Error(ex.ToString());
} }
return false; return false;
} }
public bool RemoveBan(string ip) public bool ClearBans()
{ {
try try
{ {
return database.Query("DELETE FROM Bans WHERE IP=@0", ip) != 0; return database.Query("DELETE FROM Bans") != 0;
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex.ToString()); Log.Error(ex.ToString());
} }
return false; return false;
} }
public bool ClearBans() }
{
try
{
return database.Query("DELETE FROM Bans") != 0;
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
return false;
}
}
public class Ban public class Ban
{ {
public string IP { get; set; } public string IP { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string Reason { get; set; } public string Reason { get; set; }
public Ban(string ip, string name, string reason) public Ban(string ip, string name, string reason)
{ {
IP = ip; IP = ip;
Name = name; Name = name;
Reason = reason; Reason = reason;
} }
public Ban() public Ban()
{ {
IP = string.Empty; IP = string.Empty;
Name = string.Empty; Name = string.Empty;
Reason = string.Empty; Reason = string.Empty;
} }
} }
} }

View file

@ -7,254 +7,258 @@ using MySql.Data.MySqlClient;
namespace TShockAPI.DB namespace TShockAPI.DB
{ {
public class GroupManager public class GroupManager
{ {
private IDbConnection database; private IDbConnection database;
public List<Group> groups = new List<Group>(); public List<Group> groups = new List<Group>();
public GroupManager(IDbConnection db) public GroupManager(IDbConnection db)
{ {
database = db; database = db;
var table = new SqlTable("GroupList", var table = new SqlTable("GroupList",
new SqlColumn("GroupName", MySqlDbType.VarChar, 32) { Primary = true }, new SqlColumn("GroupName", MySqlDbType.VarChar, 32) {Primary = true},
new SqlColumn("Parent", MySqlDbType.VarChar, 32), new SqlColumn("Parent", MySqlDbType.VarChar, 32),
new SqlColumn("Commands", MySqlDbType.Text), new SqlColumn("Commands", MySqlDbType.Text),
new SqlColumn("ChatColor", MySqlDbType.Text), new SqlColumn("ChatColor", MySqlDbType.Text),
new SqlColumn("Prefix", MySqlDbType.Text), new SqlColumn("Prefix", MySqlDbType.Text),
new SqlColumn("Suffix", MySqlDbType.Text) new SqlColumn("Suffix", MySqlDbType.Text)
); );
var creator = new SqlTableCreator(db, db.GetSqlType() == SqlType.Sqlite ? (IQueryBuilder)new SqliteQueryCreator() : new MysqlQueryCreator()); var creator = new SqlTableCreator(db,
creator.EnsureExists(table); db.GetSqlType() == SqlType.Sqlite
? (IQueryBuilder) new SqliteQueryCreator()
: new MysqlQueryCreator());
creator.EnsureExists(table);
//Add default groups //Add default groups
AddGroup("guest", "canbuild,canregister,canlogin,canpartychat,cantalkinthird"); AddGroup("guest", "canbuild,canregister,canlogin,canpartychat,cantalkinthird");
AddGroup("default", "guest", "warp,canchangepassword"); AddGroup("default", "guest", "warp,canchangepassword");
AddGroup("newadmin", "default", "kick,editspawn,reservedslot"); AddGroup("newadmin", "default", "kick,editspawn,reservedslot");
AddGroup("admin", "newadmin", "ban,unban,whitelist,causeevents,spawnboss,spawnmob,managewarp,time,tp,pvpfun,kill,logs,immunetokick,tphere"); AddGroup("admin", "newadmin",
AddGroup("trustedadmin", "admin", "maintenance,cfg,butcher,item,heal,immunetoban,usebanneditem,manageusers"); "ban,unban,whitelist,causeevents,spawnboss,spawnmob,managewarp,time,tp,pvpfun,kill,logs,immunetokick,tphere");
AddGroup("vip", "default", "reservedslot"); AddGroup("trustedadmin", "admin", "maintenance,cfg,butcher,item,heal,immunetoban,usebanneditem,manageusers");
AddGroup("vip", "default", "reservedslot");
String file = Path.Combine(TShock.SavePath, "groups.txt"); String file = Path.Combine(TShock.SavePath, "groups.txt");
if (File.Exists(file)) if (File.Exists(file))
{ {
using (StreamReader sr = new StreamReader(file)) using (StreamReader sr = new StreamReader(file))
{ {
String line; String line;
while ((line = sr.ReadLine()) != null) while ((line = sr.ReadLine()) != null)
{ {
if (!line.Equals("") && !line.Substring(0, 1).Equals("#")) if (!line.Equals("") && !line.Substring(0, 1).Equals("#"))
{ {
String[] info = line.Split(' '); String[] info = line.Split(' ');
String comms = ""; String comms = "";
int size = info.Length; int size = info.Length;
for (int i = 1; i < size; i++) for (int i = 1; i < size; i++)
{ {
if (!comms.Equals("")) if (!comms.Equals(""))
comms = comms + ","; comms = comms + ",";
comms = comms + info[i].Trim(); comms = comms + info[i].Trim();
} }
string query = ""; string query = "";
if (TShock.Config.StorageType.ToLower() == "sqlite") if (TShock.Config.StorageType.ToLower() == "sqlite")
query = "INSERT OR IGNORE INTO GroupList (GroupName, Commands) VALUES (@0, @1);"; query = "INSERT OR IGNORE INTO GroupList (GroupName, Commands) VALUES (@0, @1);";
else if (TShock.Config.StorageType.ToLower() == "mysql") else if (TShock.Config.StorageType.ToLower() == "mysql")
query = "INSERT IGNORE INTO GroupList SET GroupName=@0, Commands=@1;"; query = "INSERT IGNORE INTO GroupList SET GroupName=@0, Commands=@1;";
db.Query(query, info[0].Trim(), comms); db.Query(query, info[0].Trim(), comms);
}
} }
} }
} String path = Path.Combine(TShock.SavePath, "old_configs");
String path = Path.Combine(TShock.SavePath, "old_configs"); String file2 = Path.Combine(path, "groups.txt");
String file2 = Path.Combine(path, "groups.txt"); if (!Directory.Exists(path))
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
Directory.CreateDirectory(path); if (File.Exists(file2))
if (File.Exists(file2)) File.Delete(file2);
File.Delete(file2); File.Move(file, file2);
File.Move(file, file2); }
} }
}
public bool GroupExists(string group) public bool GroupExists(string group)
{ {
if (group == "superadmin") if (group == "superadmin")
return true; return true;
return groups.Any(g => g.Name.Equals(group)); return groups.Any(g => g.Name.Equals(group));
} }
/// <summary> /// <summary>
/// Adds group with name and permissions if it does not exist. /// Adds group with name and permissions if it does not exist.
/// </summary> /// </summary>
/// <param name="name">name of group</param> /// <param name="name">name of group</param>
/// <param name="parentname">parent of group</param> /// <param name="parentname">parent of group</param>
/// <param name="permissions">permissions</param> /// <param name="permissions">permissions</param>
public String AddGroup(String name, string parentname, String permissions, String chatcolor) public String AddGroup(String name, string parentname, String permissions, String chatcolor)
{ {
String message = ""; String message = "";
if (GroupExists(name)) if (GroupExists(name))
return "Error: Group already exists. Use /modGroup to change permissions."; return "Error: Group already exists. Use /modGroup to change permissions.";
var group = new Group(name, null, chatcolor); var group = new Group(name, null, chatcolor);
group.permissions.Add(permissions); group.permissions.Add(permissions);
if (!string.IsNullOrWhiteSpace(parentname)) if (!string.IsNullOrWhiteSpace(parentname))
{ {
var parent = groups.FirstOrDefault(gp => gp.Name == parentname); var parent = groups.FirstOrDefault(gp => gp.Name == parentname);
if (parent == null) if (parent == null)
{ {
var error = "Invalid parent {0} for group {1}".SFormat(group.Name, parentname); var error = "Invalid parent {0} for group {1}".SFormat(group.Name, parentname);
Log.ConsoleError(error); Log.ConsoleError(error);
return error; return error;
} }
group.Parent = parent; group.Parent = parent;
} }
string query = (TShock.Config.StorageType.ToLower() == "sqlite") ? string query = (TShock.Config.StorageType.ToLower() == "sqlite")
"INSERT OR IGNORE INTO GroupList (GroupName, Parent, Commands, ChatColor) VALUES (@0, @1, @2, @3);" : ? "INSERT OR IGNORE INTO GroupList (GroupName, Parent, Commands, ChatColor) VALUES (@0, @1, @2, @3);"
"INSERT IGNORE INTO GroupList SET GroupName=@0, Parent=@1, Commands=@2, ChatColor=@3"; : "INSERT IGNORE INTO GroupList SET GroupName=@0, Parent=@1, Commands=@2, ChatColor=@3";
if (database.Query(query, name, parentname, permissions, chatcolor) == 1) if (database.Query(query, name, parentname, permissions, chatcolor) == 1)
message = "Group " + name + " has been created successfully."; message = "Group " + name + " has been created successfully.";
groups.Add(group); groups.Add(group);
return message; return message;
} }
public String AddGroup(String name, String permissions)
{
return AddGroup(name, "", permissions, "255,255,255");
}
public String AddGroup(String name, string parent, String permissions)
{
return AddGroup(name, parent, permissions, "255,255,255");
}
public String DeleteGroup(String name) public String AddGroup(String name, String permissions)
{ {
String message = ""; return AddGroup(name, "", permissions, "255,255,255");
if (!GroupExists(name)) }
return "Error: Group doesn't exists.";
if (database.Query("DELETE FROM GroupList WHERE GroupName=@0", name) == 1) public String AddGroup(String name, string parent, String permissions)
message = "Group " + name + " has been deleted successfully."; {
groups.Remove(TShock.Utils.GetGroup(name)); return AddGroup(name, parent, permissions, "255,255,255");
}
return message; public String DeleteGroup(String name)
} {
String message = "";
if (!GroupExists(name))
return "Error: Group doesn't exists.";
public String AddPermissions(String name, List<String> permissions) if (database.Query("DELETE FROM GroupList WHERE GroupName=@0", name) == 1)
{ message = "Group " + name + " has been deleted successfully.";
String message = ""; groups.Remove(TShock.Utils.GetGroup(name));
if (!GroupExists(name))
return "Error: Group doesn't exists.";
var group = TShock.Utils.GetGroup(name); return message;
//Add existing permissions (without duplicating) }
permissions.AddRange(group.permissions.Where(s => !permissions.Contains(s)));
if (database.Query("UPDATE GroupList SET Commands=@0 WHERE GroupName=@1", String.Join(",", permissions), name) != 0) public String AddPermissions(String name, List<String> permissions)
{ {
message = "Group " + name + " has been modified successfully."; String message = "";
group.SetPermission( permissions ); if (!GroupExists(name))
} return "Error: Group doesn't exists.";
return message;
}
public String DeletePermissions(String name, List<String> permissions) var group = TShock.Utils.GetGroup(name);
{ //Add existing permissions (without duplicating)
String message = ""; permissions.AddRange(group.permissions.Where(s => !permissions.Contains(s)));
if (!GroupExists(name))
return "Error: Group doesn't exists.";
var group = TShock.Utils.GetGroup(name); if (database.Query("UPDATE GroupList SET Commands=@0 WHERE GroupName=@1", String.Join(",", permissions), name) != 0)
{
message = "Group " + name + " has been modified successfully.";
group.SetPermission(permissions);
}
return message;
}
//Only get permissions that exist in the group. public String DeletePermissions(String name, List<String> permissions)
var newperms = group.permissions.Except( permissions ); {
String message = "";
if (!GroupExists(name))
return "Error: Group doesn't exists.";
if (database.Query("UPDATE GroupList SET Commands=@0 WHERE GroupName=@1", String.Join(",", newperms), name) != 0) var group = TShock.Utils.GetGroup(name);
{
message = "Group " + name + " has been modified successfully.";
group.SetPermission( newperms.ToList() );
}
return message;
}
public void LoadPermisions() //Only get permissions that exist in the group.
{ var newperms = group.permissions.Except(permissions);
//Create a temporary list so if there is an error it doesn't override the currently loaded groups with broken groups.
var tempgroups = new List<Group>();
tempgroups.Add(new SuperAdminGroup());
if (groups == null || groups.Count < 2) if (database.Query("UPDATE GroupList SET Commands=@0 WHERE GroupName=@1", String.Join(",", newperms), name) != 0)
groups = tempgroups; {
message = "Group " + name + " has been modified successfully.";
group.SetPermission(newperms.ToList());
}
return message;
}
try public void LoadPermisions()
{ {
var groupsparents = new List<Tuple<Group, string>>(); //Create a temporary list so if there is an error it doesn't override the currently loaded groups with broken groups.
using (var reader = database.QueryReader("SELECT * FROM GroupList")) var tempgroups = new List<Group>();
{ tempgroups.Add(new SuperAdminGroup());
while (reader.Read())
{
string groupname = reader.Get<String>("GroupName");
var group = new Group(groupname);
group.Prefix = reader.Get<String>("Prefix"); if (groups == null || groups.Count < 2)
group.Suffix= reader.Get<String>("Suffix"); groups = tempgroups;
//Inherit Given commands try
String[] commands = reader.Get<String>("Commands").Split(','); {
foreach (var t in commands) var groupsparents = new List<Tuple<Group, string>>();
{ using (var reader = database.QueryReader("SELECT * FROM GroupList"))
var str = t.Trim(); {
if (str.StartsWith("!")) while (reader.Read())
{ {
group.NegatePermission(str.Substring(1)); string groupname = reader.Get<String>("GroupName");
} var group = new Group(groupname);
else
{
group.AddPermission(str);
}
}
String[] chatcolour = (reader.Get<String>("ChatColor") ?? "").Split(',');
if (chatcolour.Length == 3)
{
byte.TryParse(chatcolour[0], out group.R);
byte.TryParse(chatcolour[1], out group.G);
byte.TryParse(chatcolour[2], out group.B);
}
groupsparents.Add(Tuple.Create(group, reader.Get<string>("Parent"))); group.Prefix = reader.Get<String>("Prefix");
} group.Suffix = reader.Get<String>("Suffix");
}
foreach (var t in groupsparents) //Inherit Given commands
{ String[] commands = reader.Get<String>("Commands").Split(',');
var group = t.Item1; foreach (var t in commands)
var parentname = t.Item2; {
if (!string.IsNullOrWhiteSpace(parentname)) var str = t.Trim();
{ if (str.StartsWith("!"))
var parent = groupsparents.FirstOrDefault(gp => gp.Item1.Name == parentname); {
if (parent == null) group.NegatePermission(str.Substring(1));
{ }
Log.ConsoleError("Invalid parent {0} for group {1}".SFormat(group.Name, parentname)); else
return; {
} group.AddPermission(str);
group.Parent = parent.Item1; }
} }
tempgroups.Add(group); String[] chatcolour = (reader.Get<String>("ChatColor") ?? "").Split(',');
} if (chatcolour.Length == 3)
{
byte.TryParse(chatcolour[0], out group.R);
byte.TryParse(chatcolour[1], out group.G);
byte.TryParse(chatcolour[2], out group.B);
}
groupsparents.Add(Tuple.Create(group, reader.Get<string>("Parent")));
}
}
foreach (var t in groupsparents)
{
var group = t.Item1;
var parentname = t.Item2;
if (!string.IsNullOrWhiteSpace(parentname))
{
var parent = groupsparents.FirstOrDefault(gp => gp.Item1.Name == parentname);
if (parent == null)
{
Log.ConsoleError("Invalid parent {0} for group {1}".SFormat(group.Name, parentname));
return;
}
group.Parent = parent.Item1;
}
tempgroups.Add(group);
}
groups = tempgroups; groups = tempgroups;
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex.ToString()); Log.Error(ex.ToString());
} }
} }
} }
} }

View file

@ -7,42 +7,55 @@ using TShockAPI.Extensions;
namespace TShockAPI.DB namespace TShockAPI.DB
{ {
public interface IQueryBuilder public interface IQueryBuilder
{ {
string CreateTable(SqlTable table); string CreateTable(SqlTable table);
string AlterTable(SqlTable from, SqlTable to); string AlterTable(SqlTable from, SqlTable to);
string DbTypeToString(MySqlDbType type, int? length); string DbTypeToString(MySqlDbType type, int? length);
string UpdateValue(string table, List<SqlValue> values, List<SqlValue> wheres); string UpdateValue(string table, List<SqlValue> values, List<SqlValue> wheres);
string InsertValues(string table, List<SqlValue> values); string InsertValues(string table, List<SqlValue> values);
string ReadColumn(string table, List<SqlValue> wheres); string ReadColumn(string table, List<SqlValue> wheres);
string DeleteRow(string table, List<SqlValue> wheres); string DeleteRow(string table, List<SqlValue> wheres);
} }
public class SqliteQueryCreator : IQueryBuilder public class SqliteQueryCreator : IQueryBuilder
{ {
public string CreateTable(SqlTable table) public string CreateTable(SqlTable table)
{ {
var columns = table.Columns.Select(c => "'{0}' {1} {2} {3} {4}".SFormat(c.Name, DbTypeToString(c.Type, c.Length), c.Primary ? "PRIMARY KEY" : "", c.AutoIncrement ? "AUTOINCREMENT" : "", c.NotNull ? "NOT NULL" : "", c.Unique ? "UNIQUE" : "")); var columns =
return "CREATE TABLE '{0}' ({1})".SFormat(table.Name, string.Join(", ", columns)); table.Columns.Select(
} c =>
static Random rand = new Random(); "'{0}' {1} {2} {3} {4}".SFormat(c.Name, DbTypeToString(c.Type, c.Length), c.Primary ? "PRIMARY KEY" : "",
/// <summary> c.AutoIncrement ? "AUTOINCREMENT" : "", c.NotNull ? "NOT NULL" : "",
/// Alter a table from source to destination c.Unique ? "UNIQUE" : ""));
/// </summary> return "CREATE TABLE '{0}' ({1})".SFormat(table.Name, string.Join(", ", columns));
/// <param name="from">Must have name and column names. Column types are not required</param> }
/// <param name="to">Must have column names and column types.</param>
/// <returns></returns> private static Random rand = new Random();
public string AlterTable(SqlTable from, SqlTable to)
{ /// <summary>
var rstr = rand.NextString(20); /// Alter a table from source to destination
var alter = "ALTER TABLE '{0}' RENAME TO '{1}_{0}'".SFormat(from.Name, rstr); /// </summary>
var create = CreateTable(to); /// <param name="from">Must have name and column names. Column types are not required</param>
//combine all columns in the 'from' variable excluding ones that aren't in the 'to' variable. /// <param name="to">Must have column names and column types.</param>
//exclude the ones that aren't in 'to' variable because if the column is deleted, why try to import the data? /// <returns></returns>
var insert = "INSERT INTO '{0}' ({1}) SELECT {1} FROM {2}_{0}".SFormat(from.Name, string.Join(", ", from.Columns.Where(c => to.Columns.Any(c2 => c2.Name == c.Name)).Select(c => c.Name)), rstr); public string AlterTable(SqlTable from, SqlTable to)
var drop = "DROP TABLE '{0}_{1}'".SFormat(rstr, from.Name); {
return "{0}; {1}; {2}; {3};".SFormat(alter, create, insert, drop); var rstr = rand.NextString(20);
/* var alter = "ALTER TABLE '{0}' RENAME TO '{1}_{0}'".SFormat(from.Name, rstr);
var create = CreateTable(to);
//combine all columns in the 'from' variable excluding ones that aren't in the 'to' variable.
//exclude the ones that aren't in 'to' variable because if the column is deleted, why try to import the data?
var insert = "INSERT INTO '{0}' ({1}) SELECT {1} FROM {2}_{0}".SFormat(from.Name,
string.Join(", ",
from.Columns.Where(
c =>
to.Columns.Any(
c2 => c2.Name == c.Name)).Select
(c => c.Name)), rstr);
var drop = "DROP TABLE '{0}_{1}'".SFormat(rstr, from.Name);
return "{0}; {1}; {2}; {3};".SFormat(alter, create, insert, drop);
/*
ALTER TABLE "main"."Bans" RENAME TO "oXHFcGcd04oXHFcGcd04_Bans" ALTER TABLE "main"."Bans" RENAME TO "oXHFcGcd04oXHFcGcd04_Bans"
CREATE TABLE "main"."Bans" ("IP" TEXT PRIMARY KEY ,"Name" TEXT) CREATE TABLE "main"."Bans" ("IP" TEXT PRIMARY KEY ,"Name" TEXT)
INSERT INTO "main"."Bans" SELECT "IP","Name" FROM "main"."oXHFcGcd04oXHFcGcd04_Bans" INSERT INTO "main"."Bans" SELECT "IP","Name" FROM "main"."oXHFcGcd04oXHFcGcd04_Bans"
@ -50,238 +63,264 @@ namespace TShockAPI.DB
* *
* Twitchy - Oh. I get it! * Twitchy - Oh. I get it!
*/ */
} }
public string DeleteRow(string table, List<SqlValue> wheres)
{
var sbwheres = new StringBuilder();
int count = 0;
foreach (SqlValue where in wheres)
{
sbwheres.Append(where.Name + "=" + where.Value);
if (count != wheres.Count - 1)
sbwheres.Append(" AND ");
count++;
}
if (wheres.Count > 0)
return "DELETE FROM '{0}' WHERE {1} ".SFormat(table, sbwheres.ToString());
else
return "DELETE FROM '{0}'".SFormat(table, sbwheres.ToString());
}
public string UpdateValue(string table, List<SqlValue> values, List<SqlValue> wheres)
{
var sbvalues = new StringBuilder();
var sbwheres = new StringBuilder();
int count = 0;
foreach (SqlValue value in values)
{
sbvalues.Append(value.Name + "=" + value.Value);
if (count != values.Count - 1)
sbvalues.Append(",");
count++;
}
count = 0;
foreach (SqlValue where in wheres)
{
sbwheres.Append(where.Name + "=" + where.Value);
if (count != wheres.Count - 1)
sbwheres.Append(" AND ");
count++;
}
if (wheres.Count > 0) public string DeleteRow(string table, List<SqlValue> wheres)
return "UPDATE '{0}' SET {1} WHERE {2}".SFormat(table, sbvalues.ToString(), sbwheres.ToString()); {
else var sbwheres = new StringBuilder();
return "UPDATE '{0}' SET {1}".SFormat(table, sbvalues.ToString()); int count = 0;
} foreach (SqlValue where in wheres)
public string InsertValues(string table, List<SqlValue> values) {
{ sbwheres.Append(where.Name + "=" + where.Value);
var sbnames = new StringBuilder(); if (count != wheres.Count - 1)
var sbvalues = new StringBuilder(); sbwheres.Append(" AND ");
int count = 0; count++;
}
if (wheres.Count > 0)
return "DELETE FROM '{0}' WHERE {1} ".SFormat(table, sbwheres.ToString());
else
return "DELETE FROM '{0}'".SFormat(table, sbwheres.ToString());
}
foreach (SqlValue name in values) public string UpdateValue(string table, List<SqlValue> values, List<SqlValue> wheres)
{ {
sbnames.Append(name.Name); var sbvalues = new StringBuilder();
var sbwheres = new StringBuilder();
int count = 0;
foreach (SqlValue value in values)
{
sbvalues.Append(value.Name + "=" + value.Value);
if (count != values.Count - 1)
sbvalues.Append(",");
count++;
}
count = 0;
foreach (SqlValue where in wheres)
{
sbwheres.Append(where.Name + "=" + where.Value);
if (count != wheres.Count - 1)
sbwheres.Append(" AND ");
count++;
}
if (count != values.Count - 1) if (wheres.Count > 0)
sbnames.Append(", "); return "UPDATE '{0}' SET {1} WHERE {2}".SFormat(table, sbvalues.ToString(), sbwheres.ToString());
count++; else
} return "UPDATE '{0}' SET {1}".SFormat(table, sbvalues.ToString());
count = 0; }
foreach (SqlValue value in values)
{
sbvalues.Append(value.Value.ToString());
if (count != values.Count - 1)
sbvalues.Append(", ");
count++;
}
return "INSERT INTO '{0}' ({1}) VALUES ({2})".SFormat(table, sbnames.ToString(), sbvalues.ToString());
}
public string ReadColumn(string table, List<SqlValue> wheres)
{
var sbwheres = new StringBuilder();
int count = 0;
foreach (SqlValue where in wheres) public string InsertValues(string table, List<SqlValue> values)
{ {
sbwheres.Append(where.Name + "=" + where.Value); var sbnames = new StringBuilder();
if (count != wheres.Count - 1) var sbvalues = new StringBuilder();
sbwheres.Append(" AND "); int count = 0;
count++;
}
if(wheres.Count > 0) foreach (SqlValue name in values)
return "SELECT * FROM {0} WHERE {1}".SFormat(table, sbwheres.ToString()); {
else sbnames.Append(name.Name);
return "SELECT * FROM {0}".SFormat(table);
}
static readonly Dictionary<MySqlDbType, string> TypesAsStrings = new Dictionary<MySqlDbType, string> if (count != values.Count - 1)
{ sbnames.Append(", ");
{MySqlDbType.VarChar, "TEXT"}, count++;
{MySqlDbType.String, "TEXT"}, }
{MySqlDbType.Text, "TEXT"}, count = 0;
{MySqlDbType.TinyText, "TEXT"}, foreach (SqlValue value in values)
{MySqlDbType.MediumText, "TEXT"}, {
{MySqlDbType.LongText, "TEXT"}, sbvalues.Append(value.Value.ToString());
{MySqlDbType.Int32, "INTEGER"}, if (count != values.Count - 1)
}; sbvalues.Append(", ");
public string DbTypeToString(MySqlDbType type, int? length) count++;
{ }
string ret; return "INSERT INTO '{0}' ({1}) VALUES ({2})".SFormat(table, sbnames.ToString(), sbvalues.ToString());
if (TypesAsStrings.TryGetValue(type, out ret)) }
return ret;
throw new NotImplementedException(Enum.GetName(typeof(MySqlDbType), type));
}
}
public class MysqlQueryCreator : IQueryBuilder
{
public string CreateTable(SqlTable table)
{
var columns = table.Columns.Select(c => "{0} {1} {2} {3}".SFormat(c.Name, DbTypeToString(c.Type, c.Length), c.Primary ? "PRIMARY KEY" : "", c.AutoIncrement ? "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(table.Name, string.Join(", ", columns), uniques.Count() > 0 ? ", UNIQUE({0})".SFormat(string.Join(", ", uniques)) : "");
}
static Random rand = new Random();
/// <summary>
/// Alter a table from source to destination
/// </summary>
/// <param name="from">Must have name and column names. Column types are not required</param>
/// <param name="to">Must have column names and column types.</param>
/// <returns></returns>
public string AlterTable(SqlTable from, SqlTable to)
{
var rstr = rand.NextString(20);
var alter = "RENAME TABLE {0} TO {1}_{0}".SFormat(from.Name, rstr);
var create = CreateTable(to);
//combine all columns in the 'from' variable excluding ones that aren't in the 'to' variable.
//exclude the ones that aren't in 'to' variable because if the column is deleted, why try to import the data?
var insert = "INSERT INTO {0} ({1}) SELECT {1} FROM {2}_{0}".SFormat(from.Name, string.Join(", ", from.Columns.Where(c => to.Columns.Any(c2 => c2.Name == c.Name)).Select(c => c.Name)), rstr);
var drop = "DROP TABLE {0}_{1}".SFormat(rstr, from.Name);
return "{0}; {1}; {2}; {3};".SFormat(alter, create, insert, drop);
}
public string DeleteRow(string table, List<SqlValue> wheres)
{
var sbwheres = new StringBuilder();
int count = 0;
foreach (SqlValue where in wheres)
{
sbwheres.Append(where.Name + "=" + where.Value);
if (count != wheres.Count - 1)
sbwheres.Append(" AND ");
count++;
}
if (wheres.Count > 0)
return "DELETE FROM {0} WHERE {1} ".SFormat(table, sbwheres.ToString());
else
return "DELETE FROM {0}".SFormat(table, sbwheres.ToString());
}
public string UpdateValue(string table, List<SqlValue> values, List<SqlValue> wheres)
{
var sbvalues = new StringBuilder();
var sbwheres = new StringBuilder();
int count = 0;
foreach (SqlValue value in values)
{
sbvalues.Append(value.Name + "=" + value.Value);
if (count != values.Count - 1)
sbvalues.Append("AND");
count++;
}
count = 0;
foreach (SqlValue where in wheres)
{
sbwheres.Append(where.Name + "=" + where.Value);
if (count != wheres.Count - 1)
sbwheres.Append(" AND ");
count++;
}
if (wheres.Count > 0) public string ReadColumn(string table, List<SqlValue> wheres)
return "UPDATE {0} SET {1} WHERE {2}".SFormat(table, sbvalues.ToString(), sbwheres.ToString()); {
else var sbwheres = new StringBuilder();
return "UPDATE {0} SET {1}".SFormat(table, sbvalues.ToString()); int count = 0;
}
public string InsertValues(string table, List<SqlValue> values)
{
var sbnames = new StringBuilder();
var sbvalues = new StringBuilder();
int count = 0;
foreach (SqlValue name in values) foreach (SqlValue where in wheres)
{ {
sbnames.Append(name.Name); sbwheres.Append(where.Name + "=" + where.Value);
if (count != wheres.Count - 1)
sbwheres.Append(" AND ");
count++;
}
if (count != values.Count - 1) if (wheres.Count > 0)
sbnames.Append(", "); return "SELECT * FROM {0} WHERE {1}".SFormat(table, sbwheres.ToString());
count++; else
} return "SELECT * FROM {0}".SFormat(table);
count = 0; }
foreach (SqlValue value in values)
{
sbvalues.Append(value.Value.ToString());
if (count != values.Count - 1)
sbvalues.Append(", ");
count++;
}
return "INSERT INTO {0} ({1}) VALUES ({2})".SFormat(table, sbnames.ToString(), sbvalues.ToString()); private static readonly Dictionary<MySqlDbType, string> TypesAsStrings = new Dictionary<MySqlDbType, string>
} {
public string ReadColumn(string table, List<SqlValue> wheres) {MySqlDbType.VarChar, "TEXT"},
{ {MySqlDbType.String, "TEXT"},
var sbwheres = new StringBuilder(); {MySqlDbType.Text, "TEXT"},
int count = 0; {MySqlDbType.TinyText, "TEXT"},
{MySqlDbType.MediumText, "TEXT"},
{MySqlDbType.LongText, "TEXT"},
{MySqlDbType.Int32, "INTEGER"},
};
foreach (SqlValue where in wheres) public string DbTypeToString(MySqlDbType type, int? length)
{ {
sbwheres.Append(where.Name + "=" + where.Value); string ret;
if (count != wheres.Count - 1) if (TypesAsStrings.TryGetValue(type, out ret))
sbwheres.Append(" AND "); return ret;
count++; throw new NotImplementedException(Enum.GetName(typeof (MySqlDbType), type));
} }
}
if (wheres.Count > 0) public class MysqlQueryCreator : IQueryBuilder
return "SELECT * FROM {0} WHERE {1}".SFormat(table, sbwheres.ToString()); {
else public string CreateTable(SqlTable table)
return "SELECT * FROM {0}".SFormat(table); {
} var columns =
table.Columns.Select(
c =>
"{0} {1} {2} {3}".SFormat(c.Name, DbTypeToString(c.Type, c.Length), c.Primary ? "PRIMARY KEY" : "",
c.AutoIncrement ? "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(table.Name, string.Join(", ", columns),
uniques.Count() > 0
? ", UNIQUE({0})".SFormat(string.Join(", ", uniques))
: "");
}
static readonly Dictionary<MySqlDbType, string> TypesAsStrings = new Dictionary<MySqlDbType, string> private static Random rand = new Random();
{
{MySqlDbType.VarChar, "VARCHAR"}, /// <summary>
{MySqlDbType.String, "CHAR"}, /// Alter a table from source to destination
{MySqlDbType.Text, "TEXT"}, /// </summary>
{MySqlDbType.TinyText, "TINYTEXT"}, /// <param name="from">Must have name and column names. Column types are not required</param>
{MySqlDbType.MediumText, "MEDIUMTEXT"}, /// <param name="to">Must have column names and column types.</param>
{MySqlDbType.LongText, "LONGTEXT"}, /// <returns></returns>
{MySqlDbType.Int32, "INT"}, public string AlterTable(SqlTable from, SqlTable to)
}; {
public string DbTypeToString(MySqlDbType type, int? length) var rstr = rand.NextString(20);
{ var alter = "RENAME TABLE {0} TO {1}_{0}".SFormat(from.Name, rstr);
string ret; var create = CreateTable(to);
if (TypesAsStrings.TryGetValue(type, out ret)) //combine all columns in the 'from' variable excluding ones that aren't in the 'to' variable.
return ret + (length != null ? "({0})".SFormat((int)length) : ""); //exclude the ones that aren't in 'to' variable because if the column is deleted, why try to import the data?
throw new NotImplementedException(Enum.GetName(typeof(MySqlDbType), type)); var insert = "INSERT INTO {0} ({1}) SELECT {1} FROM {2}_{0}".SFormat(from.Name,
} string.Join(", ",
} from.Columns.Where(
} c =>
to.Columns.Any(
c2 => c2.Name == c.Name)).Select(
c => c.Name)), rstr);
var drop = "DROP TABLE {0}_{1}".SFormat(rstr, from.Name);
return "{0}; {1}; {2}; {3};".SFormat(alter, create, insert, drop);
}
public string DeleteRow(string table, List<SqlValue> wheres)
{
var sbwheres = new StringBuilder();
int count = 0;
foreach (SqlValue where in wheres)
{
sbwheres.Append(where.Name + "=" + where.Value);
if (count != wheres.Count - 1)
sbwheres.Append(" AND ");
count++;
}
if (wheres.Count > 0)
return "DELETE FROM {0} WHERE {1} ".SFormat(table, sbwheres.ToString());
else
return "DELETE FROM {0}".SFormat(table, sbwheres.ToString());
}
public string UpdateValue(string table, List<SqlValue> values, List<SqlValue> wheres)
{
var sbvalues = new StringBuilder();
var sbwheres = new StringBuilder();
int count = 0;
foreach (SqlValue value in values)
{
sbvalues.Append(value.Name + "=" + value.Value);
if (count != values.Count - 1)
sbvalues.Append("AND");
count++;
}
count = 0;
foreach (SqlValue where in wheres)
{
sbwheres.Append(where.Name + "=" + where.Value);
if (count != wheres.Count - 1)
sbwheres.Append(" AND ");
count++;
}
if (wheres.Count > 0)
return "UPDATE {0} SET {1} WHERE {2}".SFormat(table, sbvalues.ToString(), sbwheres.ToString());
else
return "UPDATE {0} SET {1}".SFormat(table, sbvalues.ToString());
}
public string InsertValues(string table, List<SqlValue> values)
{
var sbnames = new StringBuilder();
var sbvalues = new StringBuilder();
int count = 0;
foreach (SqlValue name in values)
{
sbnames.Append(name.Name);
if (count != values.Count - 1)
sbnames.Append(", ");
count++;
}
count = 0;
foreach (SqlValue value in values)
{
sbvalues.Append(value.Value.ToString());
if (count != values.Count - 1)
sbvalues.Append(", ");
count++;
}
return "INSERT INTO {0} ({1}) VALUES ({2})".SFormat(table, sbnames.ToString(), sbvalues.ToString());
}
public string ReadColumn(string table, List<SqlValue> wheres)
{
var sbwheres = new StringBuilder();
int count = 0;
foreach (SqlValue where in wheres)
{
sbwheres.Append(where.Name + "=" + where.Value);
if (count != wheres.Count - 1)
sbwheres.Append(" AND ");
count++;
}
if (wheres.Count > 0)
return "SELECT * FROM {0} WHERE {1}".SFormat(table, sbwheres.ToString());
else
return "SELECT * FROM {0}".SFormat(table);
}
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"},
};
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));
}
}
}

View file

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

View file

@ -7,119 +7,122 @@ using MySql.Data.MySqlClient;
namespace TShockAPI.DB namespace TShockAPI.DB
{ {
public class ItemManager public class ItemManager
{ {
private IDbConnection database; private IDbConnection database;
public List<ItemBan> ItemBans = new List<ItemBan>(); public List<ItemBan> ItemBans = new List<ItemBan>();
public ItemManager(IDbConnection db) public ItemManager(IDbConnection db)
{ {
database = db; database = db;
var table = new SqlTable("ItemBans", var table = new SqlTable("ItemBans",
new SqlColumn("ItemName", MySqlDbType.VarChar, 50) { Primary = true }, new SqlColumn("ItemName", MySqlDbType.VarChar, 50) {Primary = true},
new SqlColumn("AllowedGroups", MySqlDbType.Text ) new SqlColumn("AllowedGroups", MySqlDbType.Text)
); );
var creator = new SqlTableCreator(db, db.GetSqlType() == SqlType.Sqlite ? (IQueryBuilder)new SqliteQueryCreator() : new MysqlQueryCreator()); var creator = new SqlTableCreator(db,
creator.EnsureExists(table); db.GetSqlType() == SqlType.Sqlite
? (IQueryBuilder) new SqliteQueryCreator()
: new MysqlQueryCreator());
creator.EnsureExists(table);
String file = Path.Combine(TShock.SavePath, "itembans.txt"); String file = Path.Combine(TShock.SavePath, "itembans.txt");
if (File.Exists(file)) if (File.Exists(file))
{ {
using (StreamReader sr = new StreamReader(file)) using (StreamReader sr = new StreamReader(file))
{ {
String line; String line;
while ((line = sr.ReadLine()) != null) while ((line = sr.ReadLine()) != null)
{ {
if (!line.Equals("") && !line.Substring(0, 1).Equals("#")) if (!line.Equals("") && !line.Substring(0, 1).Equals("#"))
{ {
string query = (TShock.Config.StorageType.ToLower() == "sqlite")
? "INSERT OR IGNORE INTO 'ItemBans' (ItemName, AllowedGroups) VALUES (@0, @1);"
: "INSERT IGNORE INTO ItemBans SET ItemName=@0,AllowedGroups=@1 ;";
string query = (TShock.Config.StorageType.ToLower() == "sqlite") ? int id = 0;
"INSERT OR IGNORE INTO 'ItemBans' (ItemName, AllowedGroups) VALUES (@0, @1);" : int.TryParse(line, out id);
"INSERT IGNORE INTO ItemBans SET ItemName=@0,AllowedGroups=@1 ;";
int id = 0; database.Query(query, TShock.Utils.GetItemById(id).name, "");
int.TryParse(line, out id); }
}
}
database.Query(query, TShock.Utils.GetItemById(id).name, ""); String path = Path.Combine(TShock.SavePath, "old_configs");
} String file2 = Path.Combine(path, "itembans.txt");
} if (!Directory.Exists(path))
} Directory.CreateDirectory(path);
if (File.Exists(file2))
File.Delete(file2);
File.Move(file, file2);
}
String path = Path.Combine(TShock.SavePath, "old_configs"); UpdateItemBans();
String file2 = Path.Combine(path, "itembans.txt"); }
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
if (File.Exists(file2))
File.Delete(file2);
File.Move(file, file2);
}
UpdateItemBans(); public void UpdateItemBans()
} {
ItemBans.Clear();
public void UpdateItemBans() using (var reader = database.QueryReader("SELECT * FROM ItemBans"))
{ {
ItemBans.Clear(); while (reader != null && reader.Read())
{
using (var reader = database.QueryReader("SELECT * FROM ItemBans"))
{
while (reader != null && reader.Read())
{
ItemBan ban = new ItemBan(reader.Get<string>("ItemName")); ItemBan ban = new ItemBan(reader.Get<string>("ItemName"));
ban.SetAllowedGroups( reader.Get<string>("AllowedGroups") ); ban.SetAllowedGroups(reader.Get<string>("AllowedGroups"));
ItemBans.Add(ban); ItemBans.Add(ban);
} }
} }
} }
public void AddNewBan(string itemname = "") public void AddNewBan(string itemname = "")
{ {
try try
{ {
database.Query("INSERT INTO ItemBans (ItemName, AllowedGroups) VALUES (@0, @1);", TShock.Utils.GetItemByName(itemname)[0].name, ""); database.Query("INSERT INTO ItemBans (ItemName, AllowedGroups) VALUES (@0, @1);",
if (!ItemIsBanned(itemname, null)) TShock.Utils.GetItemByName(itemname)[0].name, "");
ItemBans.Add( new ItemBan(itemname) ); if (!ItemIsBanned(itemname, null))
} ItemBans.Add(new ItemBan(itemname));
catch (Exception ex) }
{ catch (Exception ex)
Log.Error(ex.ToString()); {
} Log.Error(ex.ToString());
} }
}
public void RemoveBan(string itemname) public void RemoveBan(string itemname)
{ {
if (!ItemIsBanned(itemname, null)) if (!ItemIsBanned(itemname, null))
return; return;
try try
{ {
database.Query("Delete FROM 'ItemBans' WHERE ItemName=@0;", TShock.Utils.GetItemByName(itemname)[0].name); database.Query("Delete FROM 'ItemBans' WHERE ItemName=@0;", TShock.Utils.GetItemByName(itemname)[0].name);
ItemBans.Remove( new ItemBan(itemname) ); ItemBans.Remove(new ItemBan(itemname));
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex.ToString()); Log.Error(ex.ToString());
} }
} }
public bool ItemIsBanned(string name) public bool ItemIsBanned(string name)
{ {
if (ItemBans.Contains(new ItemBan(name))) if (ItemBans.Contains(new ItemBan(name)))
{ {
return true; return true;
} }
return false; return false;
} }
public bool ItemIsBanned(string name, TSPlayer ply) public bool ItemIsBanned(string name, TSPlayer ply)
{ {
if (ItemBans.Contains( new ItemBan(name) ) ) if (ItemBans.Contains(new ItemBan(name)))
{ {
ItemBan b = GetItemBanByName(name); ItemBan b = GetItemBanByName(name);
return !b.HasPermissionToUseItem(ply); return !b.HasPermissionToUseItem(ply);
} }
return false; return false;
} }
public bool AllowGroup(string item, string name) public bool AllowGroup(string item, string name)
{ {
@ -134,7 +137,7 @@ namespace TShockAPI.DB
b.SetAllowedGroups(groupsNew); b.SetAllowedGroups(groupsNew);
int q = database.Query("UPDATE ItemBans SET AllowedGroups=@0 WHERE ItemName=@1", groupsNew, int q = database.Query("UPDATE ItemBans SET AllowedGroups=@0 WHERE ItemName=@1", groupsNew,
item); item);
return q > 0; return q > 0;
} }
@ -150,7 +153,7 @@ namespace TShockAPI.DB
b.RemoveGroup(group); b.RemoveGroup(group);
string groups = string.Join(",", b.AllowedGroups); string groups = string.Join(",", b.AllowedGroups);
int q = database.Query("UPDATE ItemBans SET AllowedGroups=@0 WHERE ItemName=@1", groups, int q = database.Query("UPDATE ItemBans SET AllowedGroups=@0 WHERE ItemName=@1", groups,
item); item);
if (q > 0) if (q > 0)
return true; return true;
} }
@ -168,58 +171,58 @@ namespace TShockAPI.DB
} }
return null; return null;
} }
} }
public class ItemBan : IEquatable<ItemBan> public class ItemBan : IEquatable<ItemBan>
{ {
public string Name { get; set; } public string Name { get; set; }
public List<string> AllowedGroups { get; set; } public List<string> AllowedGroups { get; set; }
public ItemBan(string name) public ItemBan(string name)
: this() : this()
{ {
Name = name; Name = name;
AllowedGroups = new List<string>(); AllowedGroups = new List<string>();
} }
public ItemBan() public ItemBan()
{ {
Name = ""; Name = "";
AllowedGroups = new List<string>(); AllowedGroups = new List<string>();
} }
public bool Equals(ItemBan other) public bool Equals(ItemBan other)
{ {
return Name == other.Name; return Name == other.Name;
} }
public bool HasPermissionToUseItem(TSPlayer ply) public bool HasPermissionToUseItem(TSPlayer ply)
{ {
if (ply == null) if (ply == null)
return false; return false;
return AllowedGroups.Contains(ply.Group.Name); // could add in the other permissions in this class instead of a giant if switch. return AllowedGroups.Contains(ply.Group.Name);
} // could add in the other permissions in this class instead of a giant if switch.
public void SetAllowedGroups( String groups )
{
// prevent null pointer exceptions
if (!string.IsNullOrEmpty(groups))
{
List<String> groupArr = groups.Split(',').ToList();
for (int i = 0; i < groupArr.Count; i++)
{
groupArr[i] = groupArr[i].Trim();
Console.WriteLine(groupArr[i]);
}
AllowedGroups = groupArr;
}
} }
public bool RemoveGroup(string groupName) public void SetAllowedGroups(String groups)
{
// prevent null pointer exceptions
if (!string.IsNullOrEmpty(groups))
{
List<String> groupArr = groups.Split(',').ToList();
for (int i = 0; i < groupArr.Count; i++)
{
groupArr[i] = groupArr[i].Trim();
Console.WriteLine(groupArr[i]);
}
AllowedGroups = groupArr;
}
}
public bool RemoveGroup(string groupName)
{ {
return AllowedGroups.Remove(groupName); return AllowedGroups.Remove(groupName);
} }
} }
}
}

File diff suppressed because it is too large Load diff

View file

@ -23,69 +23,74 @@ using Terraria;
namespace TShockAPI.DB namespace TShockAPI.DB
{ {
public class RemeberedPosManager public class RemeberedPosManager
{ {
public IDbConnection database; public IDbConnection database;
public RemeberedPosManager(IDbConnection db) public RemeberedPosManager(IDbConnection db)
{ {
database = db; database = db;
var table = new SqlTable("RememberedPos", var table = new SqlTable("RememberedPos",
new SqlColumn("Name", MySqlDbType.VarChar, 50) { Primary = true }, new SqlColumn("Name", MySqlDbType.VarChar, 50) {Primary = true},
new SqlColumn("IP", MySqlDbType.Text), new SqlColumn("IP", MySqlDbType.Text),
new SqlColumn("X", MySqlDbType.Int32), new SqlColumn("X", MySqlDbType.Int32),
new SqlColumn("Y", MySqlDbType.Int32), new SqlColumn("Y", MySqlDbType.Int32),
new SqlColumn("WorldID", MySqlDbType.Text) new SqlColumn("WorldID", MySqlDbType.Text)
); );
var creator = new SqlTableCreator(db, db.GetSqlType() == SqlType.Sqlite ? (IQueryBuilder)new SqliteQueryCreator() : new MysqlQueryCreator()); var creator = new SqlTableCreator(db,
creator.EnsureExists(table); db.GetSqlType() == SqlType.Sqlite
} ? (IQueryBuilder) new SqliteQueryCreator()
: new MysqlQueryCreator());
creator.EnsureExists(table);
}
public Vector2 GetLeavePos(string name, string IP) public Vector2 GetLeavePos(string name, string IP)
{ {
try try
{ {
using (var reader = database.QueryReader("SELECT * FROM RememberedPos WHERE Name=@0 AND IP=@1", name, IP)) using (var reader = database.QueryReader("SELECT * FROM RememberedPos WHERE Name=@0 AND IP=@1", name, IP))
{ {
if (reader.Read()) if (reader.Read())
{ {
return new Vector2(reader.Get<int>("X"), reader.Get<int>("Y")); return new Vector2(reader.Get<int>("X"), reader.Get<int>("Y"));
} }
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex.ToString()); Log.Error(ex.ToString());
} }
return new Vector2(); return new Vector2();
} }
public void InsertLeavePos(string name, string IP, int X, int Y) public void InsertLeavePos(string name, string IP, int X, int Y)
{ {
if (GetLeavePos(name, IP) == Vector2.Zero) if (GetLeavePos(name, IP) == Vector2.Zero)
{ {
try try
{ {
database.Query("INSERT INTO RememberedPos (Name, IP, X, Y, WorldID) VALUES (@0, @1, @2, @3, @4);", name, IP, X, Y + 3, Main.worldID.ToString()); database.Query("INSERT INTO RememberedPos (Name, IP, X, Y, WorldID) VALUES (@0, @1, @2, @3, @4);", name, IP, X,
} Y + 3, Main.worldID.ToString());
catch (Exception ex) }
{ catch (Exception ex)
Log.Error(ex.ToString()); {
} Log.Error(ex.ToString());
} }
else }
{ else
try {
{ try
database.Query("UPDATE RememberedPos SET X = @0, Y = @1 WHERE Name = @2 AND IP = @3 AND WorldID = @4;", X, Y + 3, name, IP, Main.worldID.ToString()); {
} database.Query("UPDATE RememberedPos SET X = @0, Y = @1 WHERE Name = @2 AND IP = @3 AND WorldID = @4;", X, Y + 3,
catch (Exception ex) name, IP, Main.worldID.ToString());
{ }
Log.Error(ex.ToString()); catch (Exception ex)
} {
} Log.Error(ex.ToString());
} }
} }
} }
}
}

View file

@ -2,34 +2,35 @@
namespace TShockAPI.DB namespace TShockAPI.DB
{ {
public class SqlColumn public class SqlColumn
{ {
//Required //Required
public string Name { get; set; } public string Name { get; set; }
public MySqlDbType Type { get; set; } public MySqlDbType Type { get; set; }
//Optional //Optional
public bool Unique { get; set; } public bool Unique { get; set; }
public bool Primary { get; set; } public bool Primary { get; set; }
public bool AutoIncrement { get; set; } public bool AutoIncrement { get; set; }
public bool NotNull { get; set; } public bool NotNull { get; set; }
public string DefaultValue { get; set; } public string DefaultValue { get; set; }
/// <summary> /// <summary>
/// Length of the data type, null = default /// Length of the data type, null = default
/// </summary> /// </summary>
public int? Length { get; set; } public int? Length { get; set; }
public SqlColumn(string name, MySqlDbType type) public SqlColumn(string name, MySqlDbType type)
: this(name, type, null) : this(name, type, null)
{ {
} }
public SqlColumn(string name, MySqlDbType type, int? length)
{ public SqlColumn(string name, MySqlDbType type, int? length)
Name = name; {
Type = type; Name = name;
Length = length; Type = type;
} Length = length;
} }
} }
}

View file

@ -6,79 +6,86 @@ using MySql.Data.MySqlClient;
namespace TShockAPI.DB namespace TShockAPI.DB
{ {
public class SqlTable public class SqlTable
{ {
public List<SqlColumn> Columns { get; protected set; } public List<SqlColumn> Columns { get; protected set; }
public string Name { get; protected set; } public string Name { get; protected set; }
public SqlTable(string name, params SqlColumn[] columns)
: this(name, new List<SqlColumn>(columns))
{
}
public SqlTable(string name, List<SqlColumn> columns)
{
Name = name;
Columns = columns;
}
}
public class SqlTableCreator public SqlTable(string name, params SqlColumn[] columns)
{ : this(name, new List<SqlColumn>(columns))
IDbConnection database; {
IQueryBuilder creator; }
public SqlTableCreator(IDbConnection db, IQueryBuilder provider)
{
database = db;
creator = provider;
}
public void EnsureExists(SqlTable table) public SqlTable(string name, List<SqlColumn> columns)
{ {
var columns = GetColumns(table); Name = name;
if (columns.Count > 0) Columns = columns;
{ }
if (!table.Columns.All(c => columns.Contains(c.Name)) || !columns.All(c => table.Columns.Any(c2 => c2.Name == c))) }
{
var from = new SqlTable(table.Name, columns.Select(s => new SqlColumn(s, MySqlDbType.String)).ToList());
database.Query(creator.AlterTable(from, table));
}
}
else
{
database.Query(creator.CreateTable(table));
}
}
public List<string> GetColumns(SqlTable table) public class SqlTableCreator
{ {
var ret = new List<string>(); private IDbConnection database;
var name = database.GetSqlType(); private IQueryBuilder creator;
if (name == SqlType.Sqlite)
{
using (var reader = database.QueryReader("PRAGMA table_info({0})".SFormat(table.Name)))
{
while (reader.Read())
ret.Add(reader.Get<string>("name"));
}
}
else if (name == SqlType.Mysql)
{
using (var reader = database.QueryReader("SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_NAME=@0 AND TABLE_SCHEMA=@1", table.Name, database.Database))
{
while (reader.Read())
ret.Add(reader.Get<string>("COLUMN_NAME"));
}
}
else
{
throw new NotSupportedException();
}
return ret; public SqlTableCreator(IDbConnection db, IQueryBuilder provider)
} {
database = db;
creator = provider;
}
public void DeleteRow(string table, List<SqlValue> wheres) public void EnsureExists(SqlTable table)
{ {
database.Query(creator.DeleteRow(table, wheres)); var columns = GetColumns(table);
} if (columns.Count > 0)
} {
} if (!table.Columns.All(c => columns.Contains(c.Name)) || !columns.All(c => table.Columns.Any(c2 => c2.Name == c)))
{
var from = new SqlTable(table.Name, columns.Select(s => new SqlColumn(s, MySqlDbType.String)).ToList());
database.Query(creator.AlterTable(from, table));
}
}
else
{
database.Query(creator.CreateTable(table));
}
}
public List<string> GetColumns(SqlTable table)
{
var ret = new List<string>();
var name = database.GetSqlType();
if (name == SqlType.Sqlite)
{
using (var reader = database.QueryReader("PRAGMA table_info({0})".SFormat(table.Name)))
{
while (reader.Read())
ret.Add(reader.Get<string>("name"));
}
}
else if (name == SqlType.Mysql)
{
using (
var reader =
database.QueryReader(
"SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_NAME=@0 AND TABLE_SCHEMA=@1", table.Name,
database.Database))
{
while (reader.Read())
ret.Add(reader.Get<string>("COLUMN_NAME"));
}
}
else
{
throw new NotSupportedException();
}
return ret;
}
public void DeleteRow(string table, List<SqlValue> wheres)
{
database.Query(creator.DeleteRow(table, wheres));
}
}
}

View file

@ -3,50 +3,50 @@ using System.Data;
namespace TShockAPI.DB namespace TShockAPI.DB
{ {
public class SqlValue public class SqlValue
{ {
public string Name { get; set; } public string Name { get; set; }
public object Value { get; set; } public object Value { get; set; }
public SqlValue(string name, object value) public SqlValue(string name, object value)
{ {
Name = name; Name = name;
Value = value; Value = value;
} }
} }
public class SqlTableEditor public class SqlTableEditor
{ {
IDbConnection database; private IDbConnection database;
IQueryBuilder creator; private IQueryBuilder creator;
public SqlTableEditor(IDbConnection db, IQueryBuilder provider) public SqlTableEditor(IDbConnection db, IQueryBuilder provider)
{ {
database = db; database = db;
creator = provider; creator = provider;
} }
public void UpdateValues(string table, List<SqlValue> values, List<SqlValue> wheres) public void UpdateValues(string table, List<SqlValue> values, List<SqlValue> wheres)
{ {
database.Query(creator.UpdateValue(table, values, wheres)); database.Query(creator.UpdateValue(table, values, wheres));
} }
public void InsertValues(string table, List<SqlValue> values) public void InsertValues(string table, List<SqlValue> values)
{ {
database.Query(creator.InsertValues(table, values)); database.Query(creator.InsertValues(table, values));
} }
public List<object> ReadColumn(string table, string column, List<SqlValue> wheres) public List<object> ReadColumn(string table, string column, List<SqlValue> wheres)
{ {
List<object> values = new List<object>(); List<object> values = new List<object>();
using (var reader = database.QueryReader(creator.ReadColumn(table, wheres))) using (var reader = database.QueryReader(creator.ReadColumn(table, wheres)))
{ {
while (reader.Read()) while (reader.Read())
values.Add(reader.Reader.Get<object>(column)); values.Add(reader.Reader.Get<object>(column));
} }
return values; return values;
} }
} }
} }

View file

@ -1,5 +1,4 @@
 /*
/*
TShock, a server mod for Terraria TShock, a server mod for Terraria
Copyright (C) 2011 The TShock Team Copyright (C) 2011 The TShock Team
@ -24,354 +23,366 @@ using MySql.Data.MySqlClient;
namespace TShockAPI.DB namespace TShockAPI.DB
{ {
public class UserManager public class UserManager
{ {
private IDbConnection database; private IDbConnection database;
public UserManager(IDbConnection db) public UserManager(IDbConnection db)
{ {
database = db; database = db;
var table = new SqlTable("Users", var table = new SqlTable("Users",
new SqlColumn("ID", MySqlDbType.Int32) { Primary = true, AutoIncrement = true }, new SqlColumn("ID", MySqlDbType.Int32) {Primary = true, AutoIncrement = true},
new SqlColumn("Username", MySqlDbType.VarChar, 32) { Unique = true }, new SqlColumn("Username", MySqlDbType.VarChar, 32) {Unique = true},
new SqlColumn("Password", MySqlDbType.VarChar, 128), new SqlColumn("Password", MySqlDbType.VarChar, 128),
new SqlColumn("Usergroup", MySqlDbType.Text), new SqlColumn("Usergroup", MySqlDbType.Text),
new SqlColumn("IP", MySqlDbType.VarChar, 16) new SqlColumn("IP", MySqlDbType.VarChar, 16)
); );
var creator = new SqlTableCreator(db, db.GetSqlType() == SqlType.Sqlite ? (IQueryBuilder)new SqliteQueryCreator() : new MysqlQueryCreator()); var creator = new SqlTableCreator(db,
creator.EnsureExists(table); db.GetSqlType() == SqlType.Sqlite
? (IQueryBuilder) new SqliteQueryCreator()
: new MysqlQueryCreator());
creator.EnsureExists(table);
String file = Path.Combine(TShock.SavePath, "users.txt"); String file = Path.Combine(TShock.SavePath, "users.txt");
if (File.Exists(file)) if (File.Exists(file))
{ {
using (StreamReader sr = new StreamReader(file)) using (StreamReader sr = new StreamReader(file))
{ {
String line; String line;
while ((line = sr.ReadLine()) != null) while ((line = sr.ReadLine()) != null)
{ {
if (line.Equals("") || line.Substring(0, 1).Equals("#")) if (line.Equals("") || line.Substring(0, 1).Equals("#"))
continue; continue;
String[] info = line.Split(' '); String[] info = line.Split(' ');
String username = ""; String username = "";
String sha = ""; String sha = "";
String group = ""; String group = "";
String ip = ""; String ip = "";
String[] nameSha = info[0].Split(':'); String[] nameSha = info[0].Split(':');
if (nameSha.Length < 2) if (nameSha.Length < 2)
{ {
username = nameSha[0]; username = nameSha[0];
ip = nameSha[0]; ip = nameSha[0];
group = info[1]; group = info[1];
} }
else else
{ {
username = nameSha[0]; username = nameSha[0];
sha = nameSha[1]; sha = nameSha[1];
group = info[1]; group = info[1];
} }
string query = (TShock.Config.StorageType.ToLower() == "sqlite") ? string query = (TShock.Config.StorageType.ToLower() == "sqlite")
"INSERT OR IGNORE INTO Users (Username, Password, Usergroup, IP) VALUES (@0, @1, @2, @3)" : ? "INSERT OR IGNORE INTO Users (Username, Password, Usergroup, IP) VALUES (@0, @1, @2, @3)"
"INSERT IGNORE INTO Users SET Username=@0, Password=@1, Usergroup=@2, IP=@3"; : "INSERT IGNORE INTO Users SET Username=@0, Password=@1, Usergroup=@2, IP=@3";
database.Query(query, username.Trim(), sha.Trim(), group.Trim(), ip.Trim()); database.Query(query, username.Trim(), sha.Trim(), group.Trim(), ip.Trim());
} }
} }
String path = Path.Combine(TShock.SavePath, "old_configs"); String path = Path.Combine(TShock.SavePath, "old_configs");
String file2 = Path.Combine(path, "users.txt"); String file2 = Path.Combine(path, "users.txt");
if (!Directory.Exists(path)) if (!Directory.Exists(path))
Directory.CreateDirectory(path); Directory.CreateDirectory(path);
if (File.Exists(file2)) if (File.Exists(file2))
File.Delete(file2); File.Delete(file2);
File.Move(file, file2); File.Move(file, file2);
} }
}
} /// <summary>
/// Adds a given username to the database
/// </summary>
/// <param name="user">User user</param>
public void AddUser(User user)
{
try
{
if (!TShock.Groups.GroupExists(user.Group))
throw new GroupNotExistsException(user.Group);
/// <summary> if (
/// Adds a given username to the database database.Query("INSERT INTO Users (Username, Password, UserGroup, IP) VALUES (@0, @1, @2, @3);", user.Name,
/// </summary> TShock.Utils.HashPassword(user.Password), user.Group, user.Address) < 1)
/// <param name="user">User user</param> throw new UserExistsException(user.Name);
public void AddUser(User user) }
{ catch (Exception ex)
try {
{ throw new UserManagerException("AddUser SQL returned an error", ex);
if (!TShock.Groups.GroupExists(user.Group)) }
throw new GroupNotExistsException(user.Group); }
if (database.Query("INSERT INTO Users (Username, Password, UserGroup, IP) VALUES (@0, @1, @2, @3);", user.Name, TShock.Utils.HashPassword(user.Password), user.Group, user.Address) < 1) /// <summary>
throw new UserExistsException(user.Name); /// Removes a given username from the database
} /// </summary>
catch (Exception ex) /// <param name="user">User user</param>
{ public void RemoveUser(User user)
throw new UserManagerException("AddUser SQL returned an error", ex); {
} try
} {
int affected = -1;
if (!string.IsNullOrEmpty(user.Address))
{
affected = database.Query("DELETE FROM Users WHERE IP=@0", user.Address);
}
else
{
affected = database.Query("DELETE FROM Users WHERE Username=@0", user.Name);
}
/// <summary> if (affected < 1)
/// Removes a given username from the database throw new UserNotExistException(string.IsNullOrEmpty(user.Address) ? user.Name : user.Address);
/// </summary> }
/// <param name="user">User user</param> catch (Exception ex)
public void RemoveUser(User user) {
{ throw new UserManagerException("RemoveUser SQL returned an error", ex);
try }
{ }
int affected = -1;
if (!string.IsNullOrEmpty(user.Address))
{
affected = database.Query("DELETE FROM Users WHERE IP=@0", user.Address);
}
else
{
affected = database.Query("DELETE FROM Users WHERE Username=@0", user.Name);
}
if (affected < 1)
throw new UserNotExistException(string.IsNullOrEmpty(user.Address) ? user.Name : user.Address);
}
catch (Exception ex)
{
throw new UserManagerException("RemoveUser SQL returned an error", ex);
}
}
/// <summary> /// <summary>
/// Sets the Hashed Password for a given username /// Sets the Hashed Password for a given username
/// </summary> /// </summary>
/// <param name="user">User user</param> /// <param name="user">User user</param>
/// <param name="group">string password</param> /// <param name="group">string password</param>
public void SetUserPassword(User user, string password) public void SetUserPassword(User user, string password)
{ {
try try
{ {
if (database.Query("UPDATE Users SET Password = @0 WHERE Username = @1;", TShock.Utils.HashPassword(password), user.Name) == 0) if (
throw new UserNotExistException(user.Name); database.Query("UPDATE Users SET Password = @0 WHERE Username = @1;", TShock.Utils.HashPassword(password),
} user.Name) == 0)
catch (Exception ex) throw new UserNotExistException(user.Name);
{ }
throw new UserManagerException("SetUserPassword SQL returned an error", ex); catch (Exception ex)
} {
} throw new UserManagerException("SetUserPassword SQL returned an error", ex);
}
}
/// <summary> /// <summary>
/// Sets the group for a given username /// Sets the group for a given username
/// </summary> /// </summary>
/// <param name="user">User user</param> /// <param name="user">User user</param>
/// <param name="group">string group</param> /// <param name="group">string group</param>
public void SetUserGroup(User user, string group) public void SetUserGroup(User user, string group)
{ {
try try
{ {
if (!TShock.Groups.GroupExists(group)) if (!TShock.Groups.GroupExists(group))
throw new GroupNotExistsException(group); throw new GroupNotExistsException(group);
if (database.Query("UPDATE Users SET UserGroup = @0 WHERE Username = @1;", group, user.Name) == 0) if (database.Query("UPDATE Users SET UserGroup = @0 WHERE Username = @1;", group, user.Name) == 0)
throw new UserNotExistException(user.Name); throw new UserNotExistException(user.Name);
} }
catch (Exception ex) catch (Exception ex)
{ {
throw new UserManagerException("SetUserGroup SQL returned an error", ex); throw new UserManagerException("SetUserGroup SQL returned an error", ex);
} }
} }
public int GetUserID(string username) public int GetUserID(string username)
{ {
try try
{ {
using (var reader = database.QueryReader("SELECT * FROM Users WHERE Username=@0", username)) using (var reader = database.QueryReader("SELECT * FROM Users WHERE Username=@0", username))
{ {
if (reader.Read()) if (reader.Read())
{ {
return reader.Get<int>("ID"); return reader.Get<int>("ID");
} }
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.ConsoleError("FetchHashedPasswordAndGroup SQL returned an error: " + ex); Log.ConsoleError("FetchHashedPasswordAndGroup SQL returned an error: " + ex);
} }
return -1; return -1;
} }
/// <summary> /// <summary>
/// Returns a Group for a ip from the database /// Returns a Group for a ip from the database
/// </summary> /// </summary>
/// <param name="ply">string ip</param> /// <param name="ply">string ip</param>
public Group GetGroupForIP(string ip) public Group GetGroupForIP(string ip)
{ {
try try
{ {
using (var reader = database.QueryReader("SELECT * FROM Users WHERE IP=@0", ip)) using (var reader = database.QueryReader("SELECT * FROM Users WHERE IP=@0", ip))
{ {
if (reader.Read()) if (reader.Read())
{ {
string group = reader.Get<string>("UserGroup"); string group = reader.Get<string>("UserGroup");
return TShock.Utils.GetGroup(group); return TShock.Utils.GetGroup(group);
} }
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.ConsoleError("GetGroupForIP SQL returned an error: " + ex); Log.ConsoleError("GetGroupForIP SQL returned an error: " + ex);
} }
return TShock.Utils.GetGroup(TShock.Config.DefaultGuestGroupName); return TShock.Utils.GetGroup(TShock.Config.DefaultGuestGroupName);
} }
public Group GetGroupForIPExpensive(string ip) public Group GetGroupForIPExpensive(string ip)
{ {
try try
{ {
using (var reader = database.QueryReader("SELECT IP, UserGroup FROM Users")) using (var reader = database.QueryReader("SELECT IP, UserGroup FROM Users"))
{ {
while (reader.Read()) while (reader.Read())
{ {
if (TShock.Utils.GetIPv4Address(reader.Get<string>("IP")) == ip) if (TShock.Utils.GetIPv4Address(reader.Get<string>("IP")) == ip)
{ {
return TShock.Utils.GetGroup(reader.Get<string>("UserGroup")); return TShock.Utils.GetGroup(reader.Get<string>("UserGroup"));
} }
} }
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.ConsoleError("GetGroupForIP SQL returned an error: " + ex); Log.ConsoleError("GetGroupForIP SQL returned an error: " + ex);
} }
return TShock.Utils.GetGroup(TShock.Config.DefaultGuestGroupName); return TShock.Utils.GetGroup(TShock.Config.DefaultGuestGroupName);
} }
public User GetUserByName(string name) public User GetUserByName(string name)
{ {
try try
{ {
return GetUser(new User { Name = name }); return GetUser(new User {Name = name});
} }
catch (UserManagerException) catch (UserManagerException)
{ {
return null; return null;
} }
} }
public User GetUserByID(int id)
{
try
{
return GetUser(new User { ID = id });
}
catch (UserManagerException)
{
return null;
}
}
public User GetUserByIP(string ip)
{
try
{
return GetUser(new User { Address = ip });
}
catch (UserManagerException)
{
return null;
}
}
public User GetUser(User user)
{
try
{
QueryResult result;
if (string.IsNullOrEmpty(user.Address))
{
result = database.QueryReader("SELECT * FROM Users WHERE Username=@0", user.Name);
}
else
{
result = database.QueryReader("SELECT * FROM Users WHERE IP=@0", user.Address);
}
using (var reader = result) public User GetUserByID(int id)
{ {
if (reader.Read()) try
{ {
user.ID = reader.Get<int>("ID"); return GetUser(new User {ID = id});
user.Group = reader.Get<string>("Usergroup"); }
user.Password = reader.Get<string>("Password"); catch (UserManagerException)
user.Name = reader.Get<string>("Username"); {
user.Address = reader.Get<string>("IP"); return null;
return user; }
} }
}
}
catch (Exception ex)
{
throw new UserManagerException("GetUserID SQL returned an error", ex);
}
throw new UserNotExistException(string.IsNullOrEmpty(user.Address) ? user.Name : user.Address);
}
}
public class User public User GetUserByIP(string ip)
{ {
public int ID { get; set; } try
public string Name { get; set; } {
public string Password { get; set; } return GetUser(new User {Address = ip});
public string Group { get; set; } }
public string Address { get; set; } catch (UserManagerException)
{
return null;
}
}
public User(string ip, string name, string pass, string group) public User GetUser(User user)
{ {
Address = ip; try
Name = name; {
Password = pass; QueryResult result;
Group = group; if (string.IsNullOrEmpty(user.Address))
} {
public User() result = database.QueryReader("SELECT * FROM Users WHERE Username=@0", user.Name);
{ }
Address = ""; else
Name = ""; {
Password = ""; result = database.QueryReader("SELECT * FROM Users WHERE IP=@0", user.Address);
Group = ""; }
}
}
[Serializable] using (var reader = result)
public class UserManagerException : Exception {
{ if (reader.Read())
public UserManagerException(string message) {
: base(message) user.ID = reader.Get<int>("ID");
{ user.Group = reader.Get<string>("Usergroup");
user.Password = reader.Get<string>("Password");
user.Name = reader.Get<string>("Username");
user.Address = reader.Get<string>("IP");
return user;
}
}
}
catch (Exception ex)
{
throw new UserManagerException("GetUserID SQL returned an error", ex);
}
throw new UserNotExistException(string.IsNullOrEmpty(user.Address) ? user.Name : user.Address);
}
}
} public class User
public UserManagerException(string message, Exception inner) {
: base(message, inner) public int ID { get; set; }
{ public string Name { get; set; }
public string Password { get; set; }
public string Group { get; set; }
public string Address { get; set; }
} public User(string ip, string name, string pass, string group)
} {
[Serializable] Address = ip;
public class UserExistsException : UserManagerException Name = name;
{ Password = pass;
public UserExistsException(string name) Group = group;
: base("User '" + name + "' already exists") }
{
} public User()
} {
[Serializable] Address = "";
public class UserNotExistException : UserManagerException Name = "";
{ Password = "";
public UserNotExistException(string name) Group = "";
: base("User '" + name + "' does not exist") }
{ }
}
} [Serializable]
[Serializable] public class UserManagerException : Exception
public class GroupNotExistsException : UserManagerException {
{ public UserManagerException(string message)
public GroupNotExistsException(string group) : base(message)
: base("Group '" + group + "' does not exist") {
{ }
}
} public UserManagerException(string message, Exception inner)
} : base(message, inner)
{
}
}
[Serializable]
public class UserExistsException : UserManagerException
{
public UserExistsException(string name)
: base("User '" + name + "' already exists")
{
}
}
[Serializable]
public class UserNotExistException : UserManagerException
{
public UserNotExistException(string name)
: base("User '" + name + "' does not exist")
{
}
}
[Serializable]
public class GroupNotExistsException : UserManagerException
{
public GroupNotExistsException(string group)
: base("Group '" + group + "' does not exist")
{
}
}
}

View file

@ -27,245 +27,251 @@ using Terraria;
namespace TShockAPI.DB namespace TShockAPI.DB
{ {
public class WarpManager public class WarpManager
{ {
private IDbConnection database; private IDbConnection database;
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
public WarpManager(IDbConnection db) public WarpManager(IDbConnection db)
{ {
database = db; database = db;
var table = new SqlTable("Warps", var table = new SqlTable("Warps",
new SqlColumn("WarpName", MySqlDbType.VarChar, 50) { Primary = true }, new SqlColumn("WarpName", MySqlDbType.VarChar, 50) {Primary = true},
new SqlColumn("X", MySqlDbType.Int32), new SqlColumn("X", MySqlDbType.Int32),
new SqlColumn("Y", MySqlDbType.Int32), new SqlColumn("Y", MySqlDbType.Int32),
new SqlColumn("WorldID", MySqlDbType.Text), new SqlColumn("WorldID", MySqlDbType.Text),
new SqlColumn("Private", MySqlDbType.Text) new SqlColumn("Private", MySqlDbType.Text)
); );
var creator = new SqlTableCreator(db, db.GetSqlType() == SqlType.Sqlite ? (IQueryBuilder)new SqliteQueryCreator() : new MysqlQueryCreator()); var creator = new SqlTableCreator(db,
creator.EnsureExists(table); db.GetSqlType() == SqlType.Sqlite
? (IQueryBuilder) new SqliteQueryCreator()
: new MysqlQueryCreator());
creator.EnsureExists(table);
String file = Path.Combine(TShock.SavePath, "warps.xml"); String file = Path.Combine(TShock.SavePath, "warps.xml");
String name = ""; String name = "";
String world = ""; String world = "";
int x1 = 0; int x1 = 0;
int y1 = 0; int y1 = 0;
if (!File.Exists(file)) if (!File.Exists(file))
return; return;
using (var reader = XmlReader.Create(new StreamReader(file), new XmlReaderSettings { CloseInput = true })) using (var reader = XmlReader.Create(new StreamReader(file), new XmlReaderSettings {CloseInput = true}))
{ {
// Parse the file and display each of the nodes. // Parse the file and display each of the nodes.
while (reader.Read()) while (reader.Read())
{ {
switch (reader.NodeType) switch (reader.NodeType)
{ {
case XmlNodeType.Element: case XmlNodeType.Element:
switch (reader.Name) switch (reader.Name)
{ {
case "Warp": case "Warp":
name = ""; name = "";
world = ""; world = "";
x1 = 0; x1 = 0;
y1 = 0; y1 = 0;
break; break;
case "WarpName": case "WarpName":
while (reader.NodeType != XmlNodeType.Text) while (reader.NodeType != XmlNodeType.Text)
reader.Read(); reader.Read();
name = reader.Value; name = reader.Value;
break; break;
case "X": case "X":
while (reader.NodeType != XmlNodeType.Text) while (reader.NodeType != XmlNodeType.Text)
reader.Read(); reader.Read();
int.TryParse(reader.Value, out x1); int.TryParse(reader.Value, out x1);
break; break;
case "Y": case "Y":
while (reader.NodeType != XmlNodeType.Text) while (reader.NodeType != XmlNodeType.Text)
reader.Read(); reader.Read();
int.TryParse(reader.Value, out y1); int.TryParse(reader.Value, out y1);
break; break;
case "WorldName": case "WorldName":
while (reader.NodeType != XmlNodeType.Text) while (reader.NodeType != XmlNodeType.Text)
reader.Read(); reader.Read();
world = reader.Value; world = reader.Value;
break; break;
} }
break; break;
case XmlNodeType.Text: case XmlNodeType.Text:
break; break;
case XmlNodeType.XmlDeclaration: case XmlNodeType.XmlDeclaration:
case XmlNodeType.ProcessingInstruction: case XmlNodeType.ProcessingInstruction:
break; break;
case XmlNodeType.Comment: case XmlNodeType.Comment:
break; break;
case XmlNodeType.EndElement: case XmlNodeType.EndElement:
if (reader.Name.Equals("Warp")) if (reader.Name.Equals("Warp"))
{ {
string query = (TShock.Config.StorageType.ToLower() == "sqlite") string query = (TShock.Config.StorageType.ToLower() == "sqlite")
? "INSERT OR IGNORE INTO Warps VALUES (@0, @1,@2, @3);" ? "INSERT OR IGNORE INTO Warps VALUES (@0, @1,@2, @3);"
: "INSERT IGNORE INTO Warps SET X=@0, Y=@1, WarpName=@2, WorldID=@3;"; : "INSERT IGNORE INTO Warps SET X=@0, Y=@1, WarpName=@2, WorldID=@3;";
database.Query(query, x1, y1, name, world); database.Query(query, x1, y1, name, world);
} }
break; break;
} }
} }
}
} String path = Path.Combine(TShock.SavePath, "old_configs");
String file2 = Path.Combine(path, "warps.xml");
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
if (File.Exists(file2))
File.Delete(file2);
File.Move(file, file2);
}
String path = Path.Combine(TShock.SavePath, "old_configs"); public void ConvertDB()
String file2 = Path.Combine(path, "warps.xml"); {
if (!Directory.Exists(path)) try
Directory.CreateDirectory(path); {
if (File.Exists(file2)) database.Query("UPDATE Warps SET WorldID=@0", Main.worldID.ToString());
File.Delete(file2); }
File.Move(file, file2); catch (Exception ex)
} {
Log.Error(ex.ToString());
}
}
public void ConvertDB() public bool AddWarp(int x, int y, string name, string worldid)
{ {
try try
{ {
database.Query("UPDATE Warps SET WorldID=@0", Main.worldID.ToString()); database.Query("INSERT INTO Warps (X, Y, WarpName, WorldID) VALUES (@0, @1, @2, @3);", x, y, name, worldid);
} return true;
catch (Exception ex) }
{ catch (Exception ex)
Log.Error(ex.ToString()); {
} Log.Error(ex.ToString());
} }
return false;
}
public bool AddWarp(int x, int y, string name, string worldid) public bool RemoveWarp(string name)
{ {
try try
{ {
database.Query("INSERT INTO Warps (X, Y, WarpName, WorldID) VALUES (@0, @1, @2, @3);", x, y, name, worldid); database.Query("DELETE FROM Warps WHERE WarpName=@0 AND WorldID=@1", name, Main.worldID.ToString());
return true; return true;
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex.ToString()); Log.Error(ex.ToString());
} }
return false; return false;
} }
public bool RemoveWarp(string name) public Warp FindWarp(string name)
{ {
try try
{ {
database.Query("DELETE FROM Warps WHERE WarpName=@0 AND WorldID=@1", name, Main.worldID.ToString()); using (
return true; var reader = database.QueryReader("SELECT * FROM Warps WHERE WarpName=@0 AND WorldID=@1", name,
} Main.worldID.ToString()))
catch (Exception ex) {
{ if (reader.Read())
Log.Error(ex.ToString()); {
} try
return false; {
} 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");
}
}
}
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
return new Warp();
}
public Warp FindWarp(string name) /// <summary>
{ /// Gets all the warps names from world
try /// </summary>
{ /// <param name="worldid">World name to get warps from</param>
using (var reader = database.QueryReader("SELECT * FROM Warps WHERE WarpName=@0 AND WorldID=@1", name, Main.worldID.ToString())) /// <returns>List of warps with only their names</returns>
{ public List<Warp> ListAllPublicWarps(string worldid)
if (reader.Read()) {
{ var warps = new List<Warp>();
try 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")); using (var reader = database.QueryReader("SELECT * FROM Warps WHERE WorldID=@0", worldid))
} {
catch while (reader.Read())
{ {
return new Warp(new Vector2(reader.Get<int>("X"), reader.Get<int>("Y")), reader.Get<string>("WarpName"), reader.Get<string>("WorldID"), "0"); try
} {
} if (reader.Get<String>("Private") == "0" || reader.Get<String>("Private") == null)
} warps.Add(new Warp {WarpName = reader.Get<string>("WarpName")});
} }
catch (Exception ex) catch
{ {
Log.Error(ex.ToString()); warps.Add(new Warp {WarpName = reader.Get<string>("WarpName")});
} }
return new Warp(); }
} }
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
return warps;
}
/// <summary> /// <summary>
/// Gets all the warps names from world /// Gets all the warps names from world
/// </summary> /// </summary>
/// <param name="worldid">World name to get warps from</param> /// <param name="worldid">World name to get warps from</param>
/// <returns>List of warps with only their names</returns> /// <returns>List of warps with only their names</returns>
public List<Warp> ListAllPublicWarps(string worldid) public bool HideWarp(string warp, bool state)
{ {
var warps = new List<Warp>(); try
try {
{ string query = "UPDATE Warps SET Private=@0 WHERE WarpName=@1 AND WorldID=@2";
using (var reader = database.QueryReader("SELECT * FROM Warps WHERE WorldID=@0", worldid))
{
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") });
}
}
}
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
return warps;
}
/// <summary> database.Query(query, state ? "1" : "0", warp, Main.worldID.ToString());
/// 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 true; public class Warp
} {
catch (Exception ex) public Vector2 WarpPos { get; set; }
{ public string WarpName { get; set; }
Log.Error(ex.ToString()); public string WorldWarpID { get; set; }
return false; public string Private { get; set; }
}
}
}
public class Warp public Warp(Vector2 warppos, string name, string worldid, string hidden)
{ {
public Vector2 WarpPos { get; set; } WarpPos = warppos;
public string WarpName { get; set; } WarpName = name;
public string WorldWarpID { get; set; } WorldWarpID = worldid;
public string Private { get; set; } Private = hidden;
}
public Warp(Vector2 warppos, string name, string worldid, string hidden) public Warp()
{ {
WarpPos = warppos; WarpPos = Vector2.Zero;
WarpName = name; WarpName = null;
WorldWarpID = worldid; WorldWarpID = string.Empty;
Private = hidden; Private = "0";
} }
}
public Warp() }
{
WarpPos = Vector2.Zero;
WarpName = null;
WorldWarpID = string.Empty;
Private = "0";
}
}
}

View file

@ -5,183 +5,214 @@ using System.Diagnostics.CodeAnalysis;
namespace TShockAPI.DB namespace TShockAPI.DB
{ {
public static class DbExt public static class DbExt
{ {
/// <summary>
/// Executes a query on a database.
/// </summary>
/// <param name="olddb">Database to query</param>
/// <param name="query">Query string with parameters as @0, @1, etc.</param>
/// <param name="args">Parameters to be put in the query</param>
/// <returns>Rows affected by query</returns>
[SuppressMessage("Microsoft.Security", "CA2100:Review SQL queries for security vulnerabilities")]
public static int Query(this IDbConnection olddb, string query, params object[] args)
{
using (var db = olddb.CloneEx())
{
db.Open();
using (var com = db.CreateCommand())
{
com.CommandText = query;
for (int i = 0; i < args.Length; i++)
com.AddParameter("@" + i, args[i]);
/// <summary> return com.ExecuteNonQuery();
/// Executes a query on a database. }
/// </summary> }
/// <param name="olddb">Database to query</param> }
/// <param name="query">Query string with parameters as @0, @1, etc.</param>
/// <param name="args">Parameters to be put in the query</param>
/// <returns>Rows affected by query</returns>
[SuppressMessage("Microsoft.Security", "CA2100:Review SQL queries for security vulnerabilities")]
public static int Query(this IDbConnection olddb, string query, params object[] args)
{
using (var db = olddb.CloneEx())
{
db.Open();
using (var com = db.CreateCommand())
{
com.CommandText = query;
for (int i = 0; i < args.Length; i++)
com.AddParameter("@" + i, args[i]);
return com.ExecuteNonQuery(); /// <summary>
} /// Executes a query on a database.
} /// </summary>
} /// <param name="olddb">Database to query</param>
/// <summary> /// <param name="query">Query string with parameters as @0, @1, etc.</param>
/// Executes a query on a database. /// <param name="args">Parameters to be put in the query</param>
/// </summary> /// <returns>Query result as IDataReader</returns>
/// <param name="olddb">Database to query</param> [SuppressMessage("Microsoft.Security", "CA2100:Review SQL queries for security vulnerabilities")]
/// <param name="query">Query string with parameters as @0, @1, etc.</param> public static QueryResult QueryReader(this IDbConnection olddb, string query, params object[] args)
/// <param name="args">Parameters to be put in the query</param> {
/// <returns>Query result as IDataReader</returns> var db = olddb.CloneEx();
[SuppressMessage("Microsoft.Security", "CA2100:Review SQL queries for security vulnerabilities")] db.Open();
public static QueryResult QueryReader(this IDbConnection olddb, string query, params object[] args) using (var com = db.CreateCommand())
{ {
var db = olddb.CloneEx(); com.CommandText = query;
db.Open(); for (int i = 0; i < args.Length; i++)
using (var com = db.CreateCommand()) com.AddParameter("@" + i, args[i]);
{
com.CommandText = query;
for (int i = 0; i < args.Length; i++)
com.AddParameter("@" + i, args[i]);
return new QueryResult(db, com.ExecuteReader()); return new QueryResult(db, com.ExecuteReader());
} }
} }
public static QueryResult QueryReaderDict(this IDbConnection olddb, string query, Dictionary<string, object> values) public static QueryResult QueryReaderDict(this IDbConnection olddb, string query, Dictionary<string, object> values)
{ {
var db = olddb.CloneEx(); var db = olddb.CloneEx();
db.Open(); db.Open();
using (var com = db.CreateCommand()) using (var com = db.CreateCommand())
{ {
com.CommandText = query; com.CommandText = query;
foreach(var kv in values) foreach (var kv in values)
com.AddParameter("@" + kv.Key, kv.Value); com.AddParameter("@" + kv.Key, kv.Value);
return new QueryResult(db, com.ExecuteReader()); return new QueryResult(db, com.ExecuteReader());
} }
} }
public static IDbDataParameter AddParameter(this IDbCommand command, string name, object data) public static IDbDataParameter AddParameter(this IDbCommand command, string name, object data)
{ {
var parm = command.CreateParameter(); var parm = command.CreateParameter();
parm.ParameterName = name; parm.ParameterName = name;
parm.Value = data; parm.Value = data;
command.Parameters.Add(parm); command.Parameters.Add(parm);
return parm; return parm;
} }
public static IDbConnection CloneEx(this IDbConnection conn) public static IDbConnection CloneEx(this IDbConnection conn)
{ {
var clone = (IDbConnection)Activator.CreateInstance(conn.GetType()); var clone = (IDbConnection) Activator.CreateInstance(conn.GetType());
clone.ConnectionString = conn.ConnectionString; clone.ConnectionString = conn.ConnectionString;
return clone; return clone;
} }
public static SqlType GetSqlType(this IDbConnection conn) public static SqlType GetSqlType(this IDbConnection conn)
{ {
var name = conn.GetType().Name; var name = conn.GetType().Name;
if (name == "SqliteConnection") if (name == "SqliteConnection")
return SqlType.Sqlite; return SqlType.Sqlite;
if (name == "MySqlConnection") if (name == "MySqlConnection")
return SqlType.Mysql; return SqlType.Mysql;
return SqlType.Unknown; return SqlType.Unknown;
} }
static readonly Dictionary<Type, Func<IDataReader, int, object>> ReadFuncs = new Dictionary<Type, Func<IDataReader, int, object>> private static readonly Dictionary<Type, Func<IDataReader, int, object>> ReadFuncs = new Dictionary
{ <Type, Func<IDataReader, int, object>>
{typeof(bool), (s, i) => s.GetBoolean(i)}, {
{typeof(byte), (s, i) => s.GetByte(i)}, {
{typeof(Int16), (s, i) => s.GetInt16(i)}, typeof (bool),
{typeof(Int32), (s, i) => s.GetInt32(i)}, (s, i) => s.GetBoolean(i)
{typeof(Int64), (s, i) => s.GetInt64(i)}, },
{typeof(string), (s, i) => s.GetString(i)}, {
{typeof(decimal), (s, i) => s.GetDecimal(i)}, typeof (byte),
{typeof(float), (s, i) => s.GetFloat(i)}, (s, i) => s.GetByte(i)
{typeof(double), (s, i) => s.GetDouble(i)}, },
{typeof(object), (s, i) => s.GetValue(i)}, {
}; typeof (Int16),
(s, i) => s.GetInt16(i)
},
{
typeof (Int32),
(s, i) => s.GetInt32(i)
},
{
typeof (Int64),
(s, i) => s.GetInt64(i)
},
{
typeof (string),
(s, i) => s.GetString(i)
},
{
typeof (decimal),
(s, i) => s.GetDecimal(i)
},
{
typeof (float),
(s, i) => s.GetFloat(i)
},
{
typeof (double),
(s, i) => s.GetDouble(i)
},
{
typeof (object),
(s, i) => s.GetValue(i)
},
};
public static T Get<T>(this IDataReader reader, string column) public static T Get<T>(this IDataReader reader, string column)
{ {
return reader.Get<T>(reader.GetOrdinal(column)); return reader.Get<T>(reader.GetOrdinal(column));
} }
public static T Get<T>(this IDataReader reader, int column) public static T Get<T>(this IDataReader reader, int column)
{ {
if (reader.IsDBNull(column)) if (reader.IsDBNull(column))
return default(T); return default(T);
if (ReadFuncs.ContainsKey(typeof(T))) if (ReadFuncs.ContainsKey(typeof (T)))
return (T)ReadFuncs[typeof(T)](reader, column); return (T) ReadFuncs[typeof (T)](reader, column);
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
public enum SqlType public enum SqlType
{ {
Unknown, Unknown,
Sqlite, Sqlite,
Mysql Mysql
} }
public class QueryResult : IDisposable public class QueryResult : IDisposable
{ {
public IDbConnection Connection { get; protected set; } public IDbConnection Connection { get; protected set; }
public IDataReader Reader { get; protected set; } public IDataReader Reader { get; protected set; }
public QueryResult(IDbConnection conn, IDataReader reader) public QueryResult(IDbConnection conn, IDataReader reader)
{ {
Connection = conn; Connection = conn;
Reader = reader; Reader = reader;
} }
~QueryResult() ~QueryResult()
{ {
Dispose(false); Dispose(false);
} }
public void Dispose() public void Dispose()
{ {
Dispose(true); Dispose(true);
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (disposing) if (disposing)
{ {
if (Reader != null) if (Reader != null)
{ {
Reader.Dispose(); Reader.Dispose();
Reader = null; Reader = null;
} }
if (Connection != null) if (Connection != null)
{ {
Connection.Dispose(); Connection.Dispose();
Connection = null; Connection = null;
} }
} }
} }
public bool Read() public bool Read()
{ {
if (Reader == null) if (Reader == null)
return false; return false;
return Reader.Read(); return Reader.Read();
} }
public T Get<T>(string column)
{
if (Reader == null)
return default(T);
return Reader.Get<T>(Reader.GetOrdinal(column));
}
}
} public T Get<T>(string column)
{
if (Reader == null)
return default(T);
return Reader.Get<T>(Reader.GetOrdinal(column));
}
}
}

View file

@ -3,15 +3,15 @@ using System.Collections.Generic;
namespace TShockAPI namespace TShockAPI
{ {
public static class LinqExt public static class LinqExt
{ {
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action) public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{ {
if (source == null) throw new ArgumentNullException("source"); if (source == null) throw new ArgumentNullException("source");
if (action == null) throw new ArgumentNullException("action"); if (action == null) throw new ArgumentNullException("action");
foreach (T item in source) foreach (T item in source)
action(item); action(item);
} }
} }
} }

View file

@ -3,27 +3,27 @@ using System.Text;
namespace TShockAPI.Extensions namespace TShockAPI.Extensions
{ {
public static class RandomExt public static class RandomExt
{ {
public static string NextString(this Random rand, int length) public static string NextString(this Random rand, int length)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++)
{ {
switch (rand.Next(0, 3)) switch (rand.Next(0, 3))
{ {
case 0: case 0:
sb.Append((char)rand.Next('a', 'z' + 1)); sb.Append((char) rand.Next('a', 'z' + 1));
break; break;
case 1: case 1:
sb.Append((char)rand.Next('A', 'Z' + 1)); sb.Append((char) rand.Next('A', 'Z' + 1));
break; break;
case 2: case 2:
sb.Append((char)rand.Next('0', '9' + 1)); sb.Append((char) rand.Next('0', '9' + 1));
break; break;
} }
} }
return sb.ToString(); return sb.ToString();
} }
} }
} }

View file

@ -2,12 +2,12 @@
namespace TShockAPI namespace TShockAPI
{ {
public static class StringExt public static class StringExt
{ {
//Can't name it Format :( //Can't name it Format :(
public static String SFormat(this String str, params object[] args) public static String SFormat(this String str, params object[] args)
{ {
return String.Format(str, args); return String.Format(str, args);
} }
} }
} }

View file

@ -20,93 +20,111 @@ using System.IO;
namespace TShockAPI namespace TShockAPI
{ {
public class FileTools public class FileTools
{ {
internal static string RulesPath { get { return Path.Combine(TShock.SavePath, "rules.txt"); } } internal static string RulesPath
internal static string MotdPath { get { return Path.Combine(TShock.SavePath, "motd.txt"); } } {
internal static string WhitelistPath { get { return Path.Combine(TShock.SavePath, "whitelist.txt"); } } get { return Path.Combine(TShock.SavePath, "rules.txt"); }
internal static string RememberedPosPath { get { return Path.Combine(TShock.SavePath, "oldpos.xml"); } } }
internal static string ConfigPath { get { return Path.Combine(TShock.SavePath, "config.json"); } }
public static void CreateFile(string file) internal static string MotdPath
{ {
File.Create(file).Close(); get { return Path.Combine(TShock.SavePath, "motd.txt"); }
} }
public static void CreateIfNot(string file, string data = "") internal static string WhitelistPath
{ {
if (!File.Exists(file)) get { return Path.Combine(TShock.SavePath, "whitelist.txt"); }
{ }
File.WriteAllText(file, data);
}
}
/// <summary> internal static string RememberedPosPath
/// Sets up the configuration file for all variables, and creates any missing files. {
/// </summary> get { return Path.Combine(TShock.SavePath, "oldpos.xml"); }
public static void SetupConfig() }
{
if (!Directory.Exists(TShock.SavePath))
{
Directory.CreateDirectory(TShock.SavePath);
}
CreateIfNot(RulesPath, "Respect the admins!\nDon't use TNT!"); internal static string ConfigPath
CreateIfNot(MotdPath, "This server is running TShock. Type /help for a list of commands.\n%255,000,000%Current map: %map%\nCurrent players: %players%"); {
CreateIfNot(WhitelistPath); get { return Path.Combine(TShock.SavePath, "config.json"); }
}
try public static void CreateFile(string file)
{ {
if (File.Exists(ConfigPath)) File.Create(file).Close();
{ }
TShock.Config = ConfigFile.Read(ConfigPath);
// Add all the missing config properties in the json file
}
TShock.Config.Write(ConfigPath);
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Error in config file");
Console.ForegroundColor = ConsoleColor.Gray;
Log.Error("Config Exception");
Log.Error(ex.ToString());
}
public static void CreateIfNot(string file, string data = "")
{
if (!File.Exists(file))
{
File.WriteAllText(file, data);
}
}
} /// <summary>
/// Sets up the configuration file for all variables, and creates any missing files.
/// </summary>
public static void SetupConfig()
{
if (!Directory.Exists(TShock.SavePath))
{
Directory.CreateDirectory(TShock.SavePath);
}
/// <summary> CreateIfNot(RulesPath, "Respect the admins!\nDon't use TNT!");
/// Tells if a user is on the whitelist CreateIfNot(MotdPath,
/// </summary> "This server is running TShock. Type /help for a list of commands.\n%255,000,000%Current map: %map%\nCurrent players: %players%");
/// <param name="ip">string ip of the user</param> CreateIfNot(WhitelistPath);
/// <returns>true/false</returns>
public static bool OnWhitelist(string ip) try
{ {
if (!TShock.Config.EnableWhitelist) if (File.Exists(ConfigPath))
{ {
return true; TShock.Config = ConfigFile.Read(ConfigPath);
} // Add all the missing config properties in the json file
CreateIfNot(WhitelistPath, "127.0.0.1"); }
using (var tr = new StreamReader(WhitelistPath)) TShock.Config.Write(ConfigPath);
{ }
string whitelist = tr.ReadToEnd(); catch (Exception ex)
ip = TShock.Utils.GetRealIP(ip); {
bool contains = whitelist.Contains(ip); Console.ForegroundColor = ConsoleColor.Red;
if (!contains) Console.WriteLine("Error in config file");
{ Console.ForegroundColor = ConsoleColor.Gray;
foreach (var line in whitelist.Split(Environment.NewLine.ToCharArray())) Log.Error("Config Exception");
{ Log.Error(ex.ToString());
if (string.IsNullOrWhiteSpace(line)) }
continue; }
contains = TShock.Utils.GetIPv4Address(line).Equals(ip);
if (contains) /// <summary>
return true; /// Tells if a user is on the whitelist
} /// </summary>
return false; /// <param name="ip">string ip of the user</param>
} /// <returns>true/false</returns>
return true; public static bool OnWhitelist(string ip)
} {
} if (!TShock.Config.EnableWhitelist)
} {
return true;
}
CreateIfNot(WhitelistPath, "127.0.0.1");
using (var tr = new StreamReader(WhitelistPath))
{
string whitelist = tr.ReadToEnd();
ip = TShock.Utils.GetRealIP(ip);
bool contains = whitelist.Contains(ip);
if (!contains)
{
foreach (var line in whitelist.Split(Environment.NewLine.ToCharArray()))
{
if (string.IsNullOrWhiteSpace(line))
continue;
contains = TShock.Utils.GetIPv4Address(line).Equals(ip);
if (contains)
return true;
}
return false;
}
return true;
}
}
}
} }

View file

@ -27,234 +27,290 @@ using System.Net.Sockets;
namespace MaxMind namespace MaxMind
{ {
/// <summary> /// <summary>
/// Allows for looking up a country based on an IP address. See www.maxmind.com for more details. /// Allows for looking up a country based on an IP address. See www.maxmind.com for more details.
/// </summary> /// </summary>
/// <example> /// <example>
/// static void Main(string[] args) /// static void Main(string[] args)
/// { /// {
/// using(GeoIPCountry geo = new GeoIPCountry("GeoIP.dat")) /// using(GeoIPCountry geo = new GeoIPCountry("GeoIP.dat"))
/// { /// {
/// try /// try
/// { /// {
/// Console.WriteLine("Country code of IP address 67.15.94.80: " + geo.GetCountryCode("67.15.94.80")); /// Console.WriteLine("Country code of IP address 67.15.94.80: " + geo.GetCountryCode("67.15.94.80"));
/// } /// }
/// catch(Exception ex) /// catch(Exception ex)
/// { /// {
/// Console.WriteLine(ex.ToString()); /// Console.WriteLine(ex.ToString());
/// } /// }
/// } /// }
/// } /// }
/// </example> /// </example>
public sealed class GeoIPCountry : IDisposable public sealed class GeoIPCountry : IDisposable
{ {
Stream _geodata; private Stream _geodata;
bool _close; private bool _close;
// hard coded position of where country data starts in the data file. // hard coded position of where country data starts in the data file.
const long COUNTRY_BEGIN = 16776960; private const long COUNTRY_BEGIN = 16776960;
static readonly string[] CountryCodes = { private static readonly string[] CountryCodes = {
"--","AP","EU","AD","AE","AF","AG","AI","AL","AM","AN","AO","AQ","AR","AS", "--", "AP", "EU", "AD", "AE", "AF", "AG", "AI", "AL", "AM", "AN",
"AT","AU","AW","AZ","BA","BB","BD","BE","BF","BG","BH","BI","BJ","BM","BN", "AO", "AQ", "AR", "AS",
"BO","BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD","CF","CG","CH","CI", "AT", "AU", "AW", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH",
"CK","CL","CM","CN","CO","CR","CU","CV","CX","CY","CZ","DE","DJ","DK","DM", "BI", "BJ", "BM", "BN",
"DO","DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ","FK","FM","FO","FR", "BO", "BR", "BS", "BT", "BV", "BW", "BY", "BZ", "CA", "CC", "CD",
"FX","GA","GB","GD","GE","GF","GH","GI","GL","GM","GN","GP","GQ","GR","GS", "CF", "CG", "CH", "CI",
"GT","GU","GW","GY","HK","HM","HN","HR","HT","HU","ID","IE","IL","IN","IO", "CK", "CL", "CM", "CN", "CO", "CR", "CU", "CV", "CX", "CY", "CZ",
"IQ","IR","IS","IT","JM","JO","JP","KE","KG","KH","KI","KM","KN","KP","KR", "DE", "DJ", "DK", "DM",
"KW","KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT","LU","LV","LY","MA", "DO", "DZ", "EC", "EE", "EG", "EH", "ER", "ES", "ET", "FI", "FJ",
"MC","MD","MG","MH","MK","ML","MM","MN","MO","MP","MQ","MR","MS","MT","MU", "FK", "FM", "FO", "FR",
"MV","MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI","NL","NO","NP","NR", "FX", "GA", "GB", "GD", "GE", "GF", "GH", "GI", "GL", "GM", "GN",
"NU","NZ","OM","PA","PE","PF","PG","PH","PK","PL","PM","PN","PR","PS","PT", "GP", "GQ", "GR", "GS",
"PW","PY","QA","RE","RO","RU","RW","SA","SB","SC","SD","SE","SG","SH","SI", "GT", "GU", "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU", "ID",
"SJ","SK","SL","SM","SN","SO","SR","ST","SV","SY","SZ","TC","TD","TF","TG", "IE", "IL", "IN", "IO",
"TH","TJ","TK","TM","TN","TO","TL","TR","TT","TV","TW","TZ","UA","UG","UM", "IQ", "IR", "IS", "IT", "JM", "JO", "JP", "KE", "KG", "KH", "KI",
"US","UY","UZ","VA","VC","VE","VG","VI","VN","VU","WF","WS","YE","YT","RS", "KM", "KN", "KP", "KR",
"ZA","ZM","ME","ZW","A1","A2","O1","AX","GG","IM","JE","BL","MF" "KW", "KY", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", "LT",
}; "LU", "LV", "LY", "MA",
"MC", "MD", "MG", "MH", "MK", "ML", "MM", "MN", "MO", "MP", "MQ",
"MR", "MS", "MT", "MU",
"MV", "MW", "MX", "MY", "MZ", "NA", "NC", "NE", "NF", "NG", "NI",
"NL", "NO", "NP", "NR",
"NU", "NZ", "OM", "PA", "PE", "PF", "PG", "PH", "PK", "PL", "PM",
"PN", "PR", "PS", "PT",
"PW", "PY", "QA", "RE", "RO", "RU", "RW", "SA", "SB", "SC", "SD",
"SE", "SG", "SH", "SI",
"SJ", "SK", "SL", "SM", "SN", "SO", "SR", "ST", "SV", "SY", "SZ",
"TC", "TD", "TF", "TG",
"TH", "TJ", "TK", "TM", "TN", "TO", "TL", "TR", "TT", "TV", "TW",
"TZ", "UA", "UG", "UM",
"US", "UY", "UZ", "VA", "VC", "VE", "VG", "VI", "VN", "VU", "WF",
"WS", "YE", "YT", "RS",
"ZA", "ZM", "ME", "ZW", "A1", "A2", "O1", "AX", "GG", "IM", "JE",
"BL", "MF"
};
static readonly string[] CountryNames = { private static readonly string[] CountryNames = {
"N/A","Asia/Pacific Region","Europe","Andorra","United Arab Emirates","Afghanistan", "N/A", "Asia/Pacific Region", "Europe", "Andorra",
"Antigua and Barbuda","Anguilla","Albania","Armenia","Netherlands Antilles","Angola", "United Arab Emirates", "Afghanistan",
"Antarctica","Argentina","American Samoa","Austria","Australia","Aruba","Azerbaijan", "Antigua and Barbuda", "Anguilla", "Albania", "Armenia",
"Bosnia and Herzegovina","Barbados","Bangladesh","Belgium","Burkina Faso","Bulgaria", "Netherlands Antilles", "Angola",
"Bahrain","Burundi","Benin","Bermuda","Brunei Darussalam","Bolivia","Brazil","Bahamas", "Antarctica", "Argentina", "American Samoa", "Austria", "Australia",
"Bhutan","Bouvet Island","Botswana","Belarus","Belize","Canada","Cocos (Keeling) Islands", "Aruba", "Azerbaijan",
"Congo, The Democratic Republic of the","Central African Republic","Congo","Switzerland", "Bosnia and Herzegovina", "Barbados", "Bangladesh", "Belgium",
"Cote D'Ivoire","Cook Islands","Chile","Cameroon","China","Colombia","Costa Rica","Cuba", "Burkina Faso", "Bulgaria",
"Cape Verde","Christmas Island","Cyprus","Czech Republic","Germany","Djibouti","Denmark", "Bahrain", "Burundi", "Benin", "Bermuda", "Brunei Darussalam",
"Dominica","Dominican Republic","Algeria","Ecuador","Estonia","Egypt","Western Sahara", "Bolivia", "Brazil", "Bahamas",
"Eritrea","Spain","Ethiopia","Finland","Fiji","Falkland Islands (Malvinas)", "Bhutan", "Bouvet Island", "Botswana", "Belarus", "Belize", "Canada",
"Micronesia, Federated States of","Faroe Islands","France","France, Metropolitan","Gabon", "Cocos (Keeling) Islands",
"United Kingdom","Grenada","Georgia","French Guiana","Ghana","Gibraltar","Greenland", "Congo, The Democratic Republic of the", "Central African Republic",
"Gambia","Guinea","Guadeloupe","Equatorial Guinea","Greece", "Congo", "Switzerland",
"South Georgia and the South Sandwich Islands","Guatemala","Guam","Guinea-Bissau","Guyana", "Cote D'Ivoire", "Cook Islands", "Chile", "Cameroon", "China",
"Hong Kong","Heard Island and McDonald Islands","Honduras","Croatia","Haiti","Hungary", "Colombia", "Costa Rica", "Cuba",
"Indonesia","Ireland","Israel","India","British Indian Ocean Territory","Iraq", "Cape Verde", "Christmas Island", "Cyprus", "Czech Republic",
"Iran, Islamic Republic of","Iceland","Italy","Jamaica","Jordan","Japan","Kenya", "Germany", "Djibouti", "Denmark",
"Kyrgyzstan","Cambodia","Kiribati","Comoros","Saint Kitts and Nevis", "Dominica", "Dominican Republic", "Algeria", "Ecuador", "Estonia",
"Korea, Democratic People's Republic of","Korea, Republic of","Kuwait","Cayman Islands", "Egypt", "Western Sahara",
"Kazakstan","Lao People's Democratic Republic","Lebanon","Saint Lucia","Liechtenstein", "Eritrea", "Spain", "Ethiopia", "Finland", "Fiji",
"Sri Lanka","Liberia","Lesotho","Lithuania","Luxembourg","Latvia","Libyan Arab Jamahiriya", "Falkland Islands (Malvinas)",
"Morocco","Monaco","Moldova, Republic of","Madagascar","Marshall Islands","Macedonia", "Micronesia, Federated States of", "Faroe Islands", "France",
"Mali","Myanmar","Mongolia","Macau","Northern Mariana Islands","Martinique","Mauritania", "France, Metropolitan", "Gabon",
"Montserrat","Malta","Mauritius","Maldives","Malawi","Mexico","Malaysia","Mozambique", "United Kingdom", "Grenada", "Georgia", "French Guiana", "Ghana",
"Namibia","New Caledonia","Niger","Norfolk Island","Nigeria","Nicaragua","Netherlands", "Gibraltar", "Greenland",
"Norway","Nepal","Nauru","Niue","New Zealand","Oman","Panama","Peru","French Polynesia", "Gambia", "Guinea", "Guadeloupe", "Equatorial Guinea", "Greece",
"Papua New Guinea","Philippines","Pakistan","Poland","Saint Pierre and Miquelon", "South Georgia and the South Sandwich Islands", "Guatemala", "Guam",
"Pitcairn Islands","Puerto Rico","Palestinian Territory","Portugal","Palau","Paraguay", "Guinea-Bissau", "Guyana",
"Qatar","Reunion","Romania","Russian Federation","Rwanda","Saudi Arabia", "Hong Kong", "Heard Island and McDonald Islands", "Honduras",
"Solomon Islands","Seychelles","Sudan","Sweden","Singapore","Saint Helena","Slovenia", "Croatia", "Haiti", "Hungary",
"Svalbard and Jan Mayen","Slovakia","Sierra Leone","San Marino","Senegal","Somalia", "Indonesia", "Ireland", "Israel", "India",
"Suriname","Sao Tome and Principe","El Salvador","Syrian Arab Republic","Swaziland", "British Indian Ocean Territory", "Iraq",
"Turks and Caicos Islands","Chad","French Southern Territories","Togo","Thailand", "Iran, Islamic Republic of", "Iceland", "Italy", "Jamaica", "Jordan",
"Tajikistan","Tokelau","Turkmenistan","Tunisia","Tonga","Timor-Leste","Turkey", "Japan", "Kenya",
"Trinidad and Tobago","Tuvalu","Taiwan","Tanzania, United Republic of","Ukraine","Uganda", "Kyrgyzstan", "Cambodia", "Kiribati", "Comoros",
"United States Minor Outlying Islands","United States","Uruguay","Uzbekistan", "Saint Kitts and Nevis",
"Holy See (Vatican City State)","Saint Vincent and the Grenadines","Venezuela", "Korea, Democratic People's Republic of", "Korea, Republic of",
"Virgin Islands, British","Virgin Islands, U.S.","Vietnam","Vanuatu","Wallis and Futuna", "Kuwait", "Cayman Islands",
"Samoa","Yemen","Mayotte","Serbia","South Africa","Zambia","Montenegro","Zimbabwe", "Kazakstan", "Lao People's Democratic Republic", "Lebanon",
"Anonymous Proxy","Satellite Provider","Other","Aland Islands","Guernsey","Isle of Man", "Saint Lucia", "Liechtenstein",
"Jersey","Saint Barthelemy","Saint Martin" "Sri Lanka", "Liberia", "Lesotho", "Lithuania", "Luxembourg",
}; "Latvia", "Libyan Arab Jamahiriya",
"Morocco", "Monaco", "Moldova, Republic of", "Madagascar",
"Marshall Islands", "Macedonia",
"Mali", "Myanmar", "Mongolia", "Macau", "Northern Mariana Islands",
"Martinique", "Mauritania",
"Montserrat", "Malta", "Mauritius", "Maldives", "Malawi", "Mexico",
"Malaysia", "Mozambique",
"Namibia", "New Caledonia", "Niger", "Norfolk Island", "Nigeria",
"Nicaragua", "Netherlands",
"Norway", "Nepal", "Nauru", "Niue", "New Zealand", "Oman", "Panama",
"Peru", "French Polynesia",
"Papua New Guinea", "Philippines", "Pakistan", "Poland",
"Saint Pierre and Miquelon",
"Pitcairn Islands", "Puerto Rico", "Palestinian Territory",
"Portugal", "Palau", "Paraguay",
"Qatar", "Reunion", "Romania", "Russian Federation", "Rwanda",
"Saudi Arabia",
"Solomon Islands", "Seychelles", "Sudan", "Sweden", "Singapore",
"Saint Helena", "Slovenia",
"Svalbard and Jan Mayen", "Slovakia", "Sierra Leone", "San Marino",
"Senegal", "Somalia",
"Suriname", "Sao Tome and Principe", "El Salvador",
"Syrian Arab Republic", "Swaziland",
"Turks and Caicos Islands", "Chad", "French Southern Territories",
"Togo", "Thailand",
"Tajikistan", "Tokelau", "Turkmenistan", "Tunisia", "Tonga",
"Timor-Leste", "Turkey",
"Trinidad and Tobago", "Tuvalu", "Taiwan",
"Tanzania, United Republic of", "Ukraine", "Uganda",
"United States Minor Outlying Islands", "United States", "Uruguay",
"Uzbekistan",
"Holy See (Vatican City State)", "Saint Vincent and the Grenadines",
"Venezuela",
"Virgin Islands, British", "Virgin Islands, U.S.", "Vietnam",
"Vanuatu", "Wallis and Futuna",
"Samoa", "Yemen", "Mayotte", "Serbia", "South Africa", "Zambia",
"Montenegro", "Zimbabwe",
"Anonymous Proxy", "Satellite Provider", "Other", "Aland Islands",
"Guernsey", "Isle of Man",
"Jersey", "Saint Barthelemy", "Saint Martin"
};
// //
// Constructor // Constructor
// //
/// <summary> /// <summary>
/// Initialises a new instance of this class. /// Initialises a new instance of this class.
/// </summary> /// </summary>
/// <param name="datafile">An already open stream pointing to the contents of a GeoIP.dat file.</param> /// <param name="datafile">An already open stream pointing to the contents of a GeoIP.dat file.</param>
/// <remarks>The stream is not closed when this class is disposed. Be sure to clean up afterwards!</remarks> /// <remarks>The stream is not closed when this class is disposed. Be sure to clean up afterwards!</remarks>
public GeoIPCountry(Stream datafile) public GeoIPCountry(Stream datafile)
{ {
_geodata = datafile; _geodata = datafile;
_close = false; _close = false;
} }
/// <summary> /// <summary>
/// Initialises a new instance of this class, using an on-disk database. /// Initialises a new instance of this class, using an on-disk database.
/// </summary> /// </summary>
/// <param name="filename">Path to database file.</param> /// <param name="filename">Path to database file.</param>
/// <remarks>The file will be closed when this class is disposed.</remarks> /// <remarks>The file will be closed when this class is disposed.</remarks>
public GeoIPCountry(string filename) public GeoIPCountry(string filename)
{ {
FileStream fs = new FileStream(filename, FileMode.Open); FileStream fs = new FileStream(filename, FileMode.Open);
_geodata = fs; _geodata = fs;
_close = true; _close = true;
} }
/// <summary> /// <summary>
/// Retrieves a two-letter code, defined by MaxMind, which details the country the specified IP address is located. /// Retrieves a two-letter code, defined by MaxMind, which details the country the specified IP address is located.
/// </summary> /// </summary>
/// <param name="ip">IP address to query.</param> /// <param name="ip">IP address to query.</param>
/// <returns>A two-letter code string. Throws exceptions on failure.</returns> /// <returns>A two-letter code string. Throws exceptions on failure.</returns>
/// <remarks>The IP address must be IPv4.</remarks> /// <remarks>The IP address must be IPv4.</remarks>
public string GetCountryCode(IPAddress ip) public string GetCountryCode(IPAddress ip)
{ {
return CountryCodes[FindIndex(ip)]; return CountryCodes[FindIndex(ip)];
} }
/// <summary> /// <summary>
/// Retrieves a two-letter code, defined by MaxMind, which details the country the specified IP address is located. Does not throw exceptions on failure. /// Retrieves a two-letter code, defined by MaxMind, which details the country the specified IP address is located. Does not throw exceptions on failure.
/// </summary> /// </summary>
/// <param name="ip">IP address to query.</param> /// <param name="ip">IP address to query.</param>
/// <returns>Two-letter country code or null on failure.</returns> /// <returns>Two-letter country code or null on failure.</returns>
public string TryGetCountryCode(IPAddress ip) public string TryGetCountryCode(IPAddress ip)
{ {
try try
{ {
return CountryCodes[FindIndex(ip)]; return CountryCodes[FindIndex(ip)];
} }
catch (Exception) catch (Exception)
{ {
return null; return null;
} }
} }
/// <summary> /// <summary>
/// Gets the English name of a country, by a country code. /// Gets the English name of a country, by a country code.
/// </summary> /// </summary>
/// <param name="countrycode">Country code to look up, returned by GetCountryCode or TryGetCountryCode.</param> /// <param name="countrycode">Country code to look up, returned by GetCountryCode or TryGetCountryCode.</param>
/// <returns>English name of the country, or null on failure.</returns> /// <returns>English name of the country, or null on failure.</returns>
public static string GetCountryNameByCode(string countrycode) public static string GetCountryNameByCode(string countrycode)
{ {
int index = Array.IndexOf(CountryCodes, countrycode); int index = Array.IndexOf(CountryCodes, countrycode);
return index == -1 ? null : CountryNames[index]; return index == -1 ? null : CountryNames[index];
} }
int FindIndex(IPAddress ip) private int FindIndex(IPAddress ip)
{ {
return (int)FindCountryCode(0, AddressToLong(ip), 31); return (int) FindCountryCode(0, AddressToLong(ip), 31);
} }
// Converts an IPv4 address into a long, for reading from geo database // Converts an IPv4 address into a long, for reading from geo database
long AddressToLong(IPAddress ip) private long AddressToLong(IPAddress ip)
{ {
if (ip.AddressFamily != AddressFamily.InterNetwork) if (ip.AddressFamily != AddressFamily.InterNetwork)
throw new InvalidOperationException("IP address is not IPv4"); throw new InvalidOperationException("IP address is not IPv4");
long num = 0; long num = 0;
byte[] bytes = ip.GetAddressBytes(); byte[] bytes = ip.GetAddressBytes();
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
{ {
long y = bytes[i]; long y = bytes[i];
if (y < 0) if (y < 0)
y += 256; y += 256;
num += y << ((3 - i) * 8); num += y << ((3 - i)*8);
} }
return num; return num;
} }
// Traverses the GeoIP binary data looking for a country code based // Traverses the GeoIP binary data looking for a country code based
// on the IP address mask // on the IP address mask
long FindCountryCode(long offset, long ipnum, int depth) private long FindCountryCode(long offset, long ipnum, int depth)
{ {
byte[] buffer = new byte[6]; // 2 * MAX_RECORD_LENGTH byte[] buffer = new byte[6]; // 2 * MAX_RECORD_LENGTH
long[] x = new long[2]; long[] x = new long[2];
if (depth < 0) if (depth < 0)
throw new IOException("Cannot seek GeoIP database"); throw new IOException("Cannot seek GeoIP database");
_geodata.Seek(6 * offset, SeekOrigin.Begin); _geodata.Seek(6*offset, SeekOrigin.Begin);
_geodata.Read(buffer, 0, 6); _geodata.Read(buffer, 0, 6);
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
{ {
x[i] = 0; x[i] = 0;
for (int j = 0; j < 3; j++) for (int j = 0; j < 3; j++)
{ {
int y = buffer[i * 3 + j]; int y = buffer[i*3 + j];
if (y < 0) if (y < 0)
y += 256; y += 256;
x[i] += (y << (j * 8)); x[i] += (y << (j*8));
} }
} }
if ((ipnum & (1 << depth)) > 0) if ((ipnum & (1 << depth)) > 0)
{ {
if (x[1] >= COUNTRY_BEGIN) if (x[1] >= COUNTRY_BEGIN)
return x[1] - COUNTRY_BEGIN; return x[1] - COUNTRY_BEGIN;
return FindCountryCode(x[1], ipnum, depth - 1); return FindCountryCode(x[1], ipnum, depth - 1);
} }
else else
{ {
if (x[0] >= COUNTRY_BEGIN) if (x[0] >= COUNTRY_BEGIN)
return x[0] - COUNTRY_BEGIN; return x[0] - COUNTRY_BEGIN;
return FindCountryCode(x[0], ipnum, depth - 1); return FindCountryCode(x[0], ipnum, depth - 1);
} }
} }
public void Dispose()
{
if (_close && _geodata != null)
{
_geodata.Close();
_geodata = null;
}
}
}
}
public void Dispose()
{
if (_close && _geodata != null)
{
_geodata.Close();
_geodata = null;
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -20,87 +20,87 @@ using System.Collections.Generic;
namespace TShockAPI namespace TShockAPI
{ {
public class Group public class Group
{ {
public readonly List<string> permissions = new List<string>(); public readonly List<string> permissions = new List<string>();
public readonly List<string> negatedpermissions = new List<string>(); public readonly List<string> negatedpermissions = new List<string>();
public string Name { get; set; } public string Name { get; set; }
public Group Parent { get; set; } public Group Parent { get; set; }
public int Order { get; set; } public int Order { get; set; }
public string Prefix { get; set; } public string Prefix { get; set; }
public string Suffix { get; set; } public string Suffix { get; set; }
public byte R = 255; public byte R = 255;
public byte G = 255; public byte G = 255;
public byte B = 255; public byte B = 255;
public Group(string groupname, Group parentgroup = null, string chatcolor = "255,255,255") public Group(string groupname, Group parentgroup = null, string chatcolor = "255,255,255")
{ {
Name = groupname; Name = groupname;
Parent = parentgroup; Parent = parentgroup;
byte.TryParse(chatcolor.Split(',')[0], out R); byte.TryParse(chatcolor.Split(',')[0], out R);
byte.TryParse(chatcolor.Split(',')[1], out G); byte.TryParse(chatcolor.Split(',')[1], out G);
byte.TryParse(chatcolor.Split(',')[2], out B); byte.TryParse(chatcolor.Split(',')[2], out B);
} }
public virtual bool HasPermission(string permission) public virtual bool HasPermission(string permission)
{ {
var cur = this; var cur = this;
var traversed = new List<Group>(); var traversed = new List<Group>();
while (cur != null) while (cur != null)
{ {
if (string.IsNullOrEmpty(permission)) if (string.IsNullOrEmpty(permission))
return true; return true;
if (cur.negatedpermissions.Contains(permission)) if (cur.negatedpermissions.Contains(permission))
return false; return false;
if (cur.permissions.Contains(permission)) if (cur.permissions.Contains(permission))
return true; return true;
if (traversed.Contains(cur)) if (traversed.Contains(cur))
{ {
throw new Exception("Infinite group parenting ({0})".SFormat(cur.Name)); throw new Exception("Infinite group parenting ({0})".SFormat(cur.Name));
} }
traversed.Add(cur); traversed.Add(cur);
cur = cur.Parent; cur = cur.Parent;
} }
return false; return false;
} }
public void NegatePermission(string permission) public void NegatePermission(string permission)
{ {
negatedpermissions.Add(permission); negatedpermissions.Add(permission);
} }
public void AddPermission(string permission) public void AddPermission(string permission)
{ {
permissions.Add(permission); permissions.Add(permission);
} }
public void SetPermission( List<string> permission)
{
permissions.Clear();
foreach( string s in permission )
{
permissions.Add( s );
}
}
}
public class SuperAdminGroup : Group public void SetPermission(List<string> permission)
{ {
public SuperAdminGroup() permissions.Clear();
: base("superadmin") foreach (string s in permission)
{ {
R = (byte)TShock.Config.SuperAdminChatRGB[0]; permissions.Add(s);
G = (byte)TShock.Config.SuperAdminChatRGB[1]; }
B = (byte)TShock.Config.SuperAdminChatRGB[2]; }
Prefix = TShock.Config.SuperAdminChatPrefix; }
Suffix = TShock.Config.SuperAdminChatSuffix;
} public class SuperAdminGroup : Group
{
public SuperAdminGroup()
: base("superadmin")
{
R = (byte) TShock.Config.SuperAdminChatRGB[0];
G = (byte) TShock.Config.SuperAdminChatRGB[1];
B = (byte) TShock.Config.SuperAdminChatRGB[2];
Prefix = TShock.Config.SuperAdminChatPrefix;
Suffix = TShock.Config.SuperAdminChatSuffix;
}
public override bool HasPermission(string permission) public override bool HasPermission(string permission)
{ {
return true; return true;
} }
} }
} }

View file

@ -20,20 +20,21 @@ using System.IO;
namespace TShockAPI namespace TShockAPI
{ {
/// <summary> /// <summary>
/// Derived objects can be written to and read from streams. /// Derived objects can be written to and read from streams.
/// </summary> /// </summary>
public interface IPackable public interface IPackable
{ {
/// <summary> /// <summary>
/// Writes object information to the stream /// Writes object information to the stream
/// </summary> /// </summary>
/// <param name="stream">Stream to write to</param> /// <param name="stream">Stream to write to</param>
void Pack(Stream stream); void Pack(Stream stream);
/// <summary>
/// Reads object information from the stream /// <summary>
/// </summary> /// Reads object information from the stream
/// <param name="stream">Stream to read from</param> /// </summary>
void Unpack(Stream stream); /// <param name="stream">Stream to read from</param>
} void Unpack(Stream stream);
} }
}

View file

@ -22,147 +22,147 @@ using System.IO;
namespace TShockAPI namespace TShockAPI
{ {
public enum LogLevel public enum LogLevel
{ {
None = 0, None = 0,
Debug = 1, Debug = 1,
Info = 2, Info = 2,
Warning = 4, Warning = 4,
Error = 8, Error = 8,
Data = 16, Data = 16,
All = 31 All = 31
} }
public static class Log public static class Log
{ {
private static string _filename; private static string _filename;
private static LogLevel _logLevel; private static LogLevel _logLevel;
private static StreamWriter _logWriter; private static StreamWriter _logWriter;
/// <summary> /// <summary>
/// Creates the log file stream and sets the initial log level. /// Creates the log file stream and sets the initial log level.
/// </summary> /// </summary>
/// <param name="filename">The output filename. This file will be overwritten if 'clear' is set.</param> /// <param name="filename">The output filename. This file will be overwritten if 'clear' is set.</param>
/// <param name="logLevel">The <see cref="LogLevel" /> value which sets the type of messages to output.</param> /// <param name="logLevel">The <see cref="LogLevel" /> value which sets the type of messages to output.</param>
/// <param name="clear">Whether or not to clear the log file on initialization.</param> /// <param name="clear">Whether or not to clear the log file on initialization.</param>
public static void Initialize(string filename, LogLevel logLevel, bool clear) public static void Initialize(string filename, LogLevel logLevel, bool clear)
{ {
_filename = filename; _filename = filename;
_logLevel = logLevel; _logLevel = logLevel;
_logWriter = new StreamWriter(filename, !clear); _logWriter = new StreamWriter(filename, !clear);
} }
/// <summary> /// <summary>
/// Checks whether the log level contains the specified flag. /// Checks whether the log level contains the specified flag.
/// </summary> /// </summary>
/// <param name="type">The <see cref="LogLevel" /> value to check.</param> /// <param name="type">The <see cref="LogLevel" /> value to check.</param>
private static bool MayWriteType(LogLevel type) private static bool MayWriteType(LogLevel type)
{ {
return ((_logLevel & type) == type); return ((_logLevel & type) == type);
} }
/// <summary> /// <summary>
/// Writes data to the log file. /// Writes data to the log file.
/// </summary> /// </summary>
/// <param name="message">The message to be written.</param> /// <param name="message">The message to be written.</param>
public static void Data(String message) public static void Data(String message)
{ {
Write(message, LogLevel.Data); Write(message, LogLevel.Data);
} }
/// <summary> /// <summary>
/// Writes an error to the log file. /// Writes an error to the log file.
/// </summary> /// </summary>
/// <param name="message">The message to be written.</param> /// <param name="message">The message to be written.</param>
public static void Error(String message) public static void Error(String message)
{ {
Write(message, LogLevel.Error); Write(message, LogLevel.Error);
} }
/// <summary>
/// Writes an error to the log file.
/// </summary>
/// <param name="message">The message to be written.</param>
public static void ConsoleError(String message)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(message);
Console.ForegroundColor = ConsoleColor.Gray;
Write(message, LogLevel.Error);
}
/// <summary> /// <summary>
/// Writes a warning to the log file. /// Writes an error to the log file.
/// </summary> /// </summary>
/// <param name="message">The message to be written.</param> /// <param name="message">The message to be written.</param>
public static void Warn(String message) public static void ConsoleError(String message)
{ {
Write(message, LogLevel.Warning); Console.ForegroundColor = ConsoleColor.Red;
} Console.WriteLine(message);
Console.ForegroundColor = ConsoleColor.Gray;
Write(message, LogLevel.Error);
}
/// <summary> /// <summary>
/// Writes an informative string to the log file. /// Writes a warning to the log file.
/// </summary> /// </summary>
/// <param name="message">The message to be written.</param> /// <param name="message">The message to be written.</param>
public static void Info(String message) public static void Warn(String message)
{ {
Write(message, LogLevel.Info); Write(message, LogLevel.Warning);
} }
/// <summary> /// <summary>
/// Writes an informative string to the log file. Also outputs to the console. /// Writes an informative string to the log file.
/// </summary> /// </summary>
/// <param name="message">The message to be written.</param> /// <param name="message">The message to be written.</param>
public static void ConsoleInfo(String message) public static void Info(String message)
{ {
Console.ForegroundColor = ConsoleColor.Yellow; Write(message, LogLevel.Info);
Console.WriteLine(message); }
Console.ForegroundColor = ConsoleColor.Gray;
Write(message, LogLevel.Info);
}
/// <summary> /// <summary>
/// Writes a debug string to the log file. /// Writes an informative string to the log file. Also outputs to the console.
/// </summary> /// </summary>
/// <param name="message">The message to be written.</param> /// <param name="message">The message to be written.</param>
public static void Debug(String message) public static void ConsoleInfo(String message)
{ {
Write(message, LogLevel.Debug); Console.ForegroundColor = ConsoleColor.Yellow;
} Console.WriteLine(message);
Console.ForegroundColor = ConsoleColor.Gray;
Write(message, LogLevel.Info);
}
/// <summary> /// <summary>
/// Disposes objects that are being used. /// Writes a debug string to the log file.
/// </summary> /// </summary>
public static void Dispose() /// <param name="message">The message to be written.</param>
{ public static void Debug(String message)
_logWriter.Dispose(); {
} Write(message, LogLevel.Debug);
}
/// <summary> /// <summary>
/// Internal method which writes a message directly to the log file. /// Disposes objects that are being used.
/// </summary> /// </summary>
private static void Write(String message, LogLevel level) public static void Dispose()
{ {
if (!MayWriteType(level)) _logWriter.Dispose();
{ }
return;
}
string caller = "TShock"; /// <summary>
/// Internal method which writes a message directly to the log file.
/// </summary>
private static void Write(String message, LogLevel level)
{
if (!MayWriteType(level))
{
return;
}
StackFrame frame = new StackTrace().GetFrame(2); string caller = "TShock";
if (frame != null)
{
var meth = frame.GetMethod();
if (meth != null)
caller = meth.DeclaringType.Name;
}
_logWriter.WriteLine(string.Format("{0} - {1}: {2}: {3}", StackFrame frame = new StackTrace().GetFrame(2);
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture), if (frame != null)
caller, level.ToString().ToUpper(), message)); {
_logWriter.Flush(); var meth = frame.GetMethod();
} if (meth != null)
} caller = meth.DeclaringType.Name;
}
_logWriter.WriteLine(string.Format("{0} - {1}: {2}: {3}",
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture),
caller, level.ToString().ToUpper(), message));
_logWriter.Flush();
}
}
} }

View file

@ -4,32 +4,33 @@ using System.IO.Streams;
namespace TShockAPI.Net namespace TShockAPI.Net
{ {
public class BaseMsg : IPackable public class BaseMsg : IPackable
{ {
public virtual PacketTypes ID public virtual PacketTypes ID
{ {
get { throw new NotImplementedException("Msg ID not implemented"); } get { throw new NotImplementedException("Msg ID not implemented"); }
} }
public void PackFull(Stream stream)
{
long start = stream.Position;
stream.WriteInt32(1);
stream.WriteInt8((byte)ID);
Pack(stream);
long end = stream.Position;
stream.Position = start;
stream.WriteInt32((int)(end - start) - 4);
stream.Position = end;
}
public virtual void Unpack(Stream stream) public void PackFull(Stream stream)
{ {
throw new NotImplementedException(); long start = stream.Position;
} stream.WriteInt32(1);
stream.WriteInt8((byte) ID);
Pack(stream);
long end = stream.Position;
stream.Position = start;
stream.WriteInt32((int) (end - start) - 4);
stream.Position = end;
}
public virtual void Pack(Stream stream) public virtual void Unpack(Stream stream)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
}
} public virtual void Pack(Stream stream)
{
throw new NotImplementedException();
}
}
}

View file

@ -4,19 +4,18 @@ using System.Text;
namespace TShockAPI.Net namespace TShockAPI.Net
{ {
class DisconnectMsg : BaseMsg internal class DisconnectMsg : BaseMsg
{ {
public override PacketTypes ID public override PacketTypes ID
{ {
get get { return PacketTypes.Disconnect; }
{ }
return PacketTypes.Disconnect;
} public string Reason { get; set; }
}
public string Reason {get;set;} public override void Pack(Stream stream)
public override void Pack(Stream stream) {
{ stream.WriteBytes(Encoding.ASCII.GetBytes(Reason));
stream.WriteBytes(Encoding.ASCII.GetBytes(Reason)); }
} }
} }
}

View file

@ -23,116 +23,127 @@ using Terraria;
namespace TShockAPI.Net namespace TShockAPI.Net
{ {
public class NetTile : IPackable public class NetTile : IPackable
{ {
public bool Active { get; set; } public bool Active { get; set; }
public byte Type { get; set; } public byte Type { get; set; }
public short FrameX { get; set; } public short FrameX { get; set; }
public short FrameY { get; set; } public short FrameY { get; set; }
public byte Wall { get; set; } public byte Wall { get; set; }
public byte Liquid { get; set; } public byte Liquid { get; set; }
public bool Lava { get; set; } public bool Lava { get; set; }
public bool Wire { get; set; } public bool Wire { get; set; }
public bool HasWall { get { return Wall > 0; } } public bool HasWall
public bool HasLiquid { get { return Liquid > 0; } } {
public bool FrameImportant { get { return Main.tileFrameImportant[Type]; } } get { return Wall > 0; }
}
public NetTile() public bool HasLiquid
{ {
Active = false; get { return Liquid > 0; }
Type = 0; }
FrameX = -1;
FrameY = -1;
Wall = 0;
Liquid = 0;
Lava = false;
Wire = false;
}
public NetTile(Stream stream) public bool FrameImportant
: this() {
{ get { return Main.tileFrameImportant[Type]; }
Unpack(stream); }
}
public void Pack(Stream stream) public NetTile()
{ {
var flags = TileFlags.None; Active = false;
Type = 0;
FrameX = -1;
FrameY = -1;
Wall = 0;
Liquid = 0;
Lava = false;
Wire = false;
}
if (Active) public NetTile(Stream stream)
flags |= TileFlags.Active; : this()
{
Unpack(stream);
}
if (HasWall) public void Pack(Stream stream)
flags |= TileFlags.Wall; {
var flags = TileFlags.None;
if (HasLiquid) if (Active)
flags |= TileFlags.Liquid; flags |= TileFlags.Active;
if (Wire) if (HasWall)
flags |= TileFlags.Wire; flags |= TileFlags.Wall;
stream.WriteInt8((byte)flags); if (HasLiquid)
flags |= TileFlags.Liquid;
if (Active) if (Wire)
{ flags |= TileFlags.Wire;
stream.WriteInt8(Type);
if (FrameImportant)
{
stream.WriteInt16(FrameX);
stream.WriteInt16(FrameY);
}
}
if (HasWall) stream.WriteInt8((byte) flags);
stream.WriteInt8(Wall);
if (HasLiquid) if (Active)
{ {
stream.WriteInt8(Liquid); stream.WriteInt8(Type);
stream.WriteBoolean(Lava); if (FrameImportant)
} {
} stream.WriteInt16(FrameX);
stream.WriteInt16(FrameY);
}
}
public void Unpack(Stream stream) if (HasWall)
{ stream.WriteInt8(Wall);
var flags = (TileFlags)stream.ReadInt8();
Active = flags.HasFlag(TileFlags.Active); if (HasLiquid)
if (Active) {
{ stream.WriteInt8(Liquid);
Type = stream.ReadInt8(); stream.WriteBoolean(Lava);
if (FrameImportant) }
{ }
FrameX = stream.ReadInt16();
FrameY = stream.ReadInt16();
}
}
if (flags.HasFlag(TileFlags.Wall)) public void Unpack(Stream stream)
{ {
Wall = stream.ReadInt8(); var flags = (TileFlags) stream.ReadInt8();
}
if (flags.HasFlag(TileFlags.Liquid)) Active = flags.HasFlag(TileFlags.Active);
{ if (Active)
Liquid = stream.ReadInt8(); {
Lava = stream.ReadBoolean(); Type = stream.ReadInt8();
} if (FrameImportant)
{
FrameX = stream.ReadInt16();
FrameY = stream.ReadInt16();
}
}
if (flags.HasFlag(TileFlags.Wire)) if (flags.HasFlag(TileFlags.Wall))
Wire = true; {
} Wall = stream.ReadInt8();
} }
[Flags] if (flags.HasFlag(TileFlags.Liquid))
public enum TileFlags : byte {
{ Liquid = stream.ReadInt8();
None = 0, Lava = stream.ReadBoolean();
Active = 1, }
Lighted = 2,
Wall = 4, if (flags.HasFlag(TileFlags.Wire))
Liquid = 8, Wire = true;
Wire = 16 }
} }
}
[Flags]
public enum TileFlags : byte
{
None = 0,
Active = 1,
Lighted = 2,
Wall = 4,
Liquid = 8,
Wire = 16
}
}

View file

@ -3,33 +3,30 @@ using System.IO.Streams;
namespace TShockAPI.Net namespace TShockAPI.Net
{ {
public class ProjectileRemoveMsg : BaseMsg public class ProjectileRemoveMsg : BaseMsg
{ {
public override PacketTypes ID public override PacketTypes ID
{ {
get get { return PacketTypes.ProjectileNew; }
{ }
return PacketTypes.ProjectileNew;
}
}
public short Index { get; set; } public short Index { get; set; }
public byte Owner { get; set; } public byte Owner { get; set; }
public override void Pack(Stream stream) public override void Pack(Stream stream)
{ {
stream.WriteInt16(Index); stream.WriteInt16(Index);
stream.WriteSingle(-1); stream.WriteSingle(-1);
stream.WriteSingle(-1); stream.WriteSingle(-1);
stream.WriteSingle(0); stream.WriteSingle(0);
stream.WriteSingle(0); stream.WriteSingle(0);
stream.WriteSingle(0); stream.WriteSingle(0);
stream.WriteInt16(0); stream.WriteInt16(0);
stream.WriteByte(Owner); stream.WriteByte(Owner);
stream.WriteByte(0); stream.WriteByte(0);
stream.WriteSingle(0); stream.WriteSingle(0);
stream.WriteSingle(0); stream.WriteSingle(0);
stream.WriteSingle(0); stream.WriteSingle(0);
} }
} }
} }

View file

@ -3,25 +3,22 @@ using System.IO.Streams;
namespace TShockAPI.Net namespace TShockAPI.Net
{ {
public class SpawnMsg : BaseMsg public class SpawnMsg : BaseMsg
{ {
public override PacketTypes ID public override PacketTypes ID
{ {
get get { return PacketTypes.PlayerSpawn; }
{ }
return PacketTypes.PlayerSpawn;
}
}
public int TileX { get; set; } public int TileX { get; set; }
public int TileY {get;set;} public int TileY { get; set; }
public byte PlayerIndex { get; set; } public byte PlayerIndex { get; set; }
public override void Pack(Stream stream) public override void Pack(Stream stream)
{ {
stream.WriteInt8(PlayerIndex); stream.WriteInt8(PlayerIndex);
stream.WriteInt32(TileX); stream.WriteInt32(TileX);
stream.WriteInt32(TileY); stream.WriteInt32(TileY);
} }
} }
} }

View file

@ -23,54 +23,54 @@ using System.Text;
namespace TShockAPI.Net namespace TShockAPI.Net
{ {
[Flags] [Flags]
public enum WorldInfoFlag : byte public enum WorldInfoFlag : byte
{ {
None = 0, None = 0,
OrbSmashed = 1, OrbSmashed = 1,
DownedBoss1 = 2, DownedBoss1 = 2,
DownedBoss2 = 4, DownedBoss2 = 4,
DownedBoss3 = 8, DownedBoss3 = 8,
HardMode = 16, HardMode = 16,
DownedClown = 32 DownedClown = 32
} }
public class WorldInfoMsg : BaseMsg
{ public class WorldInfoMsg : BaseMsg
public int Time { get; set; } {
public bool DayTime { get; set; } public int Time { get; set; }
public byte MoonPhase { get; set; } public bool DayTime { get; set; }
public bool BloodMoon { get; set; } public byte MoonPhase { get; set; }
public int MaxTilesX { get; set; } public bool BloodMoon { get; set; }
public int MaxTilesY { get; set; } public int MaxTilesX { get; set; }
public int SpawnX { get; set; } public int MaxTilesY { get; set; }
public int SpawnY { get; set; } public int SpawnX { get; set; }
public int WorldSurface { get; set; } public int SpawnY { get; set; }
public int RockLayer { get; set; } public int WorldSurface { get; set; }
public int WorldID { get; set; } public int RockLayer { get; set; }
public WorldInfoFlag WorldFlags { get; set; } public int WorldID { get; set; }
public string WorldName { get; set; } public WorldInfoFlag WorldFlags { get; set; }
public override PacketTypes ID public string WorldName { get; set; }
{
get public override PacketTypes ID
{ {
return PacketTypes.WorldInfo; get { return PacketTypes.WorldInfo; }
} }
}
public override void Pack(Stream stream) public override void Pack(Stream stream)
{ {
stream.WriteInt32(Time); stream.WriteInt32(Time);
stream.WriteBoolean(DayTime); stream.WriteBoolean(DayTime);
stream.WriteInt8(MoonPhase); stream.WriteInt8(MoonPhase);
stream.WriteBoolean(BloodMoon); stream.WriteBoolean(BloodMoon);
stream.WriteInt32(MaxTilesX); stream.WriteInt32(MaxTilesX);
stream.WriteInt32(MaxTilesY); stream.WriteInt32(MaxTilesY);
stream.WriteInt32(SpawnX); stream.WriteInt32(SpawnX);
stream.WriteInt32(SpawnY); stream.WriteInt32(SpawnY);
stream.WriteInt32(WorldSurface); stream.WriteInt32(WorldSurface);
stream.WriteInt32(RockLayer); stream.WriteInt32(RockLayer);
stream.WriteInt32(WorldID); stream.WriteInt32(WorldID);
stream.WriteInt8((byte)WorldFlags); stream.WriteInt8((byte) WorldFlags);
stream.WriteBytes(Encoding.ASCII.GetBytes(WorldName)); stream.WriteBytes(Encoding.ASCII.GetBytes(WorldName));
} }
} }
} }

View file

@ -9,29 +9,29 @@ using Terraria;
namespace TShockAPI namespace TShockAPI
{ {
public class PacketBufferer : IDisposable public class PacketBufferer : IDisposable
{ {
/// <summary> /// <summary>
/// Maximum number of bytes to send per update per socket /// Maximum number of bytes to send per update per socket
/// </summary> /// </summary>
public int BytesPerUpdate { get; set; } public int BytesPerUpdate { get; set; }
PacketBuffer[] buffers = new PacketBuffer[Netplay.serverSock.Length]; private PacketBuffer[] buffers = new PacketBuffer[Netplay.serverSock.Length];
int[] Bytes = new int[52]; private int[] Bytes = new int[52];
int[] Packets = new int[52]; private int[] Packets = new int[52];
int[] Compressed = new int[52]; private int[] Compressed = new int[52];
#if DEBUG_NET #if DEBUG_NET
Command dump; Command dump;
Command flush; Command flush;
#endif #endif
public PacketBufferer() public PacketBufferer()
{ {
BytesPerUpdate = 0xFFFF; BytesPerUpdate = 0xFFFF;
for (int i = 0; i < buffers.Length; i++) for (int i = 0; i < buffers.Length; i++)
buffers[i] = new PacketBuffer(); buffers[i] = new PacketBuffer();
#if DEBUG_NET #if DEBUG_NET
dump = new Command("superadmin", Dump, "netdump"); dump = new Command("superadmin", Dump, "netdump");
@ -40,113 +40,115 @@ namespace TShockAPI
Commands.ChatCommands.Add(flush); Commands.ChatCommands.Add(flush);
#endif #endif
NetHooks.SendBytes += ServerHooks_SendBytes; NetHooks.SendBytes += ServerHooks_SendBytes;
ServerHooks.SocketReset += ServerHooks_SocketReset; ServerHooks.SocketReset += ServerHooks_SocketReset;
GameHooks.PostUpdate += GameHooks_Update; GameHooks.PostUpdate += GameHooks_Update;
} }
~PacketBufferer() ~PacketBufferer()
{ {
Dispose(false); Dispose(false);
} }
public void Dispose() public void Dispose()
{ {
Dispose(true); Dispose(true);
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (disposing) if (disposing)
{ {
#if DEBUG_NET #if DEBUG_NET
Commands.ChatCommands.Remove(dump); Commands.ChatCommands.Remove(dump);
Commands.ChatCommands.Remove(flush); Commands.ChatCommands.Remove(flush);
#endif #endif
NetHooks.SendBytes -= ServerHooks_SendBytes; NetHooks.SendBytes -= ServerHooks_SendBytes;
ServerHooks.SocketReset -= ServerHooks_SocketReset; ServerHooks.SocketReset -= ServerHooks_SocketReset;
GameHooks.PostUpdate -= GameHooks_Update; GameHooks.PostUpdate -= GameHooks_Update;
} }
} }
void Dump(CommandArgs args) private void Dump(CommandArgs args)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.AppendLine("{0,-25}{1,-25}{2,-25}{3}".SFormat("Name:", "Packets", "Bytes", "Compression")); sb.AppendLine("{0,-25}{1,-25}{2,-25}{3}".SFormat("Name:", "Packets", "Bytes", "Compression"));
for (int i = 1; i < Bytes.Length; i++) for (int i = 1; i < Bytes.Length; i++)
{ {
sb.AppendLine("{0,-25}{1,-25}{2,-25}{3}".SFormat(Enum.GetName(typeof(PacketTypes), i) + ":", Packets[i], Bytes[i], Compressed[i])); sb.AppendLine("{0,-25}{1,-25}{2,-25}{3}".SFormat(Enum.GetName(typeof (PacketTypes), i) + ":", Packets[i], Bytes[i],
} Compressed[i]));
File.WriteAllText(Path.Combine(TShock.SavePath, "dmp.txt"), sb.ToString()); }
} File.WriteAllText(Path.Combine(TShock.SavePath, "dmp.txt"), sb.ToString());
}
void Flush(CommandArgs args) private void Flush(CommandArgs args)
{ {
Bytes = new int[52]; Bytes = new int[52];
Packets = new int[52]; Packets = new int[52];
Compressed = new int[52]; Compressed = new int[52];
} }
void GameHooks_Update() private void GameHooks_Update()
{ {
FlushAll(); FlushAll();
} }
public void FlushAll() public void FlushAll()
{ {
for (int i = 0; i < Netplay.serverSock.Length; i++) for (int i = 0; i < Netplay.serverSock.Length; i++)
{ {
Flush(Netplay.serverSock[i]); Flush(Netplay.serverSock[i]);
} }
} }
public bool Flush(ServerSock socket) public bool Flush(ServerSock socket)
{ {
try try
{ {
if (socket == null || !socket.active) if (socket == null || !socket.active)
return false; return false;
if (buffers[socket.whoAmI].Count < 1) if (buffers[socket.whoAmI].Count < 1)
return false; return false;
byte[] buff = buffers[socket.whoAmI].GetBytes(BytesPerUpdate); byte[] buff = buffers[socket.whoAmI].GetBytes(BytesPerUpdate);
if (buff == null) if (buff == null)
return false; return false;
if (SendBytes(socket, buff)) if (SendBytes(socket, buff))
{ {
buffers[socket.whoAmI].Pop(buff.Length); buffers[socket.whoAmI].Pop(buff.Length);
return true; return true;
} }
} }
catch (Exception e) catch (Exception e)
{ {
Log.ConsoleError(e.ToString()); Log.ConsoleError(e.ToString());
} }
return false; return false;
} }
void ServerHooks_SocketReset(ServerSock socket) private void ServerHooks_SocketReset(ServerSock socket)
{ {
buffers[socket.whoAmI] = new PacketBuffer(); buffers[socket.whoAmI] = new PacketBuffer();
} }
public bool SendBytes(ServerSock socket, byte[] buffer) public bool SendBytes(ServerSock socket, byte[] buffer)
{ {
return SendBytes(socket, buffer, 0, buffer.Length); return SendBytes(socket, buffer, 0, buffer.Length);
} }
public void BufferBytes(ServerSock socket, byte[] buffer)
{
BufferBytes(socket, buffer, 0, buffer.Length);
}
public void BufferBytes(ServerSock socket, byte[] buffer, int offset, int count) public void BufferBytes(ServerSock socket, byte[] buffer)
{ {
lock (buffers[socket.whoAmI]) BufferBytes(socket, buffer, 0, buffer.Length);
{ }
public void BufferBytes(ServerSock socket, byte[] buffer, int offset, int count)
{
lock (buffers[socket.whoAmI])
{
#if DEBUG_NET #if DEBUG_NET
int size = (count - offset); int size = (count - offset);
var pt = buffer[offset + 4]; var pt = buffer[offset + 4];
@ -155,43 +157,44 @@ namespace TShockAPI
Bytes[pt] += size; Bytes[pt] += size;
Compressed[pt] += Compress(buffer, offset, count); Compressed[pt] += Compress(buffer, offset, count);
#endif #endif
using (var ms = new MemoryStream(buffer, offset, count)) using (var ms = new MemoryStream(buffer, offset, count))
{ {
buffers[socket.whoAmI].AddRange(ms.ToArray()); buffers[socket.whoAmI].AddRange(ms.ToArray());
} }
} }
} }
public bool SendBytes(ServerSock socket, byte[] buffer, int offset, int count) public bool SendBytes(ServerSock socket, byte[] buffer, int offset, int count)
{ {
try try
{ {
if (socket.tcpClient.Client != null && socket.tcpClient.Client.Poll(0, SelectMode.SelectWrite)) if (socket.tcpClient.Client != null && socket.tcpClient.Client.Poll(0, SelectMode.SelectWrite))
{ {
if (Main.runningMono) if (Main.runningMono)
socket.networkStream.Write(buffer, offset, count); socket.networkStream.Write(buffer, offset, count);
else else
socket.tcpClient.Client.Send(buffer, offset, count, SocketFlags.None); socket.tcpClient.Client.Send(buffer, offset, count, SocketFlags.None);
return true; return true;
} }
} }
catch (ObjectDisposedException) catch (ObjectDisposedException)
{ {
} }
catch (SocketException) catch (SocketException)
{ {
} }
catch (IOException) catch (IOException)
{ {
} }
return false; return false;
} }
private void ServerHooks_SendBytes(ServerSock socket, byte[] buffer, int offset, int count, HandledEventArgs e)
{
e.Handled = true;
BufferBytes(socket, buffer, offset, count);
}
void ServerHooks_SendBytes(ServerSock socket, byte[] buffer, int offset, int count, HandledEventArgs e)
{
e.Handled = true;
BufferBytes(socket, buffer, offset, count);
}
#if DEBUG_NET #if DEBUG_NET
static int Compress(byte[] buffer, int offset, int count) static int Compress(byte[] buffer, int offset, int count)
{ {
@ -205,29 +208,29 @@ namespace TShockAPI
} }
} }
#endif #endif
} }
public class PacketBuffer : List<byte> public class PacketBuffer : List<byte>
{ {
public byte[] GetBytes(int max) public byte[] GetBytes(int max)
{ {
lock (this) lock (this)
{ {
if (this.Count < 1) if (this.Count < 1)
return null; return null;
var ret = new byte[Math.Min(max, this.Count)]; var ret = new byte[Math.Min(max, this.Count)];
this.CopyTo(0, ret, 0, ret.Length); this.CopyTo(0, ret, 0, ret.Length);
return ret; return ret;
} }
} }
public void Pop(int count) public void Pop(int count)
{ {
lock (this) lock (this)
{ {
this.RemoveRange(0, count); this.RemoveRange(0, count);
} }
} }
} }
} }

View file

@ -7,238 +7,193 @@ using System.Text;
namespace TShockAPI namespace TShockAPI
{ {
public static class Permissions public static class Permissions
{ {
//Permissions with blank descriptions basically means its described by the commands it gives access to. //Permissions with blank descriptions basically means its described by the commands it gives access to.
[Description("")] [Description("")] public static readonly string causeevents;
public static readonly string causeevents;
[Description("Required to be able to build (modify tiles and liquid)")] [Description("Required to be able to build (modify tiles and liquid)")] public static readonly string canbuild;
public static readonly string canbuild;
[Description("")] [Description("")] public static readonly string kill;
public static readonly string kill;
[Description("Allows you to use banned items")] [Description("Allows you to use banned items")] public static readonly string usebanneditem;
public static readonly string usebanneditem;
[Description("Allows you to edit the spawn")] [Description("Allows you to edit the spawn")] public static readonly string editspawn;
public static readonly string editspawn;
[Description("Prevents you from being kicked")] [Description("Prevents you from being kicked")] public static readonly string immunetokick;
public static readonly string immunetokick;
[Description("Prevents you from being banned")] [Description("Prevents you from being banned")] public static readonly string immunetoban;
public static readonly string immunetoban;
[Description("Prevents you from being reverted by kill tile abuse detection")] [Description("Prevents you from being reverted by kill tile abuse detection")] public static readonly string
public static readonly string ignorekilltiledetection; ignorekilltiledetection;
[Description("Prevents you from being reverted by place tile abuse detection")] [Description("Prevents you from being reverted by place tile abuse detection")] public static readonly string
public static readonly string ignoreplacetiledetection; ignoreplacetiledetection;
[Description("Prevents you from being disabled by liquid set abuse detection")] [Description("Prevents you from being disabled by liquid set abuse detection")] public static readonly string
public static readonly string ignoreliquidsetdetection; ignoreliquidsetdetection;
[Description("Prevents you from being disabled by liquid set abuse detection")] [Description("Prevents you from being disabled by liquid set abuse detection")] public static readonly string
public static readonly string ignoreprojectiledetection; ignoreprojectiledetection;
[Description("Prevents you from being reverted by no clip detection")] [Description("Prevents you from being reverted by no clip detection")] public static readonly string
public static readonly string ignorenoclipdetection; ignorenoclipdetection;
[Description("Prevents you from being disabled by stack hack detection")] [Description("Prevents you from being disabled by stack hack detection")] public static readonly string
public static readonly string ignorestackhackdetection; ignorestackhackdetection;
[Description("Prevents you from being kicked by hacked health detection")] [Description("Prevents you from being kicked by hacked health detection")] public static readonly string
public static readonly string ignorestathackdetection; ignorestathackdetection;
[Description("Specific log messages are sent to users with this permission")] [Description("Specific log messages are sent to users with this permission")] public static readonly string logs;
public static readonly string logs;
[Description("Allows you to bypass the max slots for up to 5 slots above your max")] [Description("Allows you to bypass the max slots for up to 5 slots above your max")] public static readonly string
public static readonly string reservedslot; reservedslot;
[Description("User is notified when an update is available")] [Description("User is notified when an update is available")] public static readonly string maintenance;
public static readonly string maintenance;
[Description("User can kick others")] [Description("User can kick others")] public static readonly string kick;
public static readonly string kick;
[Description("User can ban others")] [Description("User can ban others")] public static readonly string ban;
public static readonly string ban;
[Description("User can modify the whitelist")] [Description("User can modify the whitelist")] public static readonly string whitelist;
public static readonly string whitelist;
[Description("User can spawn bosses")] [Description("User can spawn bosses")] public static readonly string spawnboss;
public static readonly string spawnboss;
[Description("User can spawn npcs")] [Description("User can spawn npcs")] public static readonly string spawnmob;
public static readonly string spawnmob;
[Description("User can teleport")] [Description("User can teleport")] public static readonly string tp;
public static readonly string tp;
[Description("User can teleport people to them")] [Description("User can teleport people to them")] public static readonly string tphere;
public static readonly string tphere;
[Description("User can use warps")] [Description("User can use warps")] public static readonly string warp;
public static readonly string warp;
[Description("User can manage warps")] [Description("User can manage warps")] public static readonly string managewarp;
public static readonly string managewarp;
[Description("User can manage item bans")] [Description("User can manage item bans")] public static readonly string manageitem;
public static readonly string manageitem;
[Description("User can manage groups")] [Description("User can manage groups")] public static readonly string managegroup;
public static readonly string managegroup;
[Description("User can edit sevrer configurations")] [Description("User can edit sevrer configurations")] public static readonly string cfg;
public static readonly string cfg;
[Description("")] [Description("")] public static readonly string time;
public static readonly string time;
[Description("")] [Description("")] public static readonly string pvpfun;
public static readonly string pvpfun;
[Description("User can edit regions")] [Description("User can edit regions")] public static readonly string manageregion;
public static readonly string manageregion;
[Description("Meant for super admins only")] [Description("Meant for super admins only")] public static readonly string rootonly;
public static readonly string rootonly;
[Description("User can whisper to others")] [Description("User can whisper to others")] public static readonly string whisper;
public static readonly string whisper;
[Description("")] [Description("")] public static readonly string annoy;
public static readonly string annoy;
[Description("User can kill all enemy npcs")] [Description("User can kill all enemy npcs")] public static readonly string butcher;
public static readonly string butcher;
[Description("User can spawn items")] [Description("User can spawn items")] public static readonly string item;
public static readonly string item;
[Description("User can clear item drops.")] [Description("User can clear item drops.")] public static readonly string clearitems;
public static readonly string clearitems;
[Description("")] [Description("")] public static readonly string heal;
public static readonly string heal;
[Description("User can buff self")] [Description("User can buff self")] public static readonly string buff;
public static readonly string buff;
[Description("User can buff other players")] [Description("User can buff other players")] public static readonly string buffplayer;
public static readonly string buffplayer;
[Description("")] [Description("")] public static readonly string grow;
public static readonly string grow;
[Description("User can change hardmode state.")] [Description("User can change hardmode state.")] public static readonly string hardmode;
public static readonly string hardmode;
[Description("User can change the homes of NPCs.")] [Description("User can change the homes of NPCs.")] public static readonly string movenpc;
public static readonly string movenpc;
[Description("Users can stop people from TPing to them")] [Description("Users can stop people from TPing to them")] public static readonly string tpallow;
public static readonly string tpallow;
[Description("Users can tp to anyone")] [Description("Users can tp to anyone")] public static readonly string tpall;
public static readonly string tpall;
[Description("Users can tp to people without showing a notice")] [Description("Users can tp to people without showing a notice")] public static readonly string tphide;
public static readonly string tphide;
[Description("User can convert hallow into corruption and vice-versa")] [Description("User can convert hallow into corruption and vice-versa")] public static readonly string converthardmode;
public static readonly string converthardmode;
[Description("User can mute and unmute users")] [Description("User can mute and unmute users")] public static readonly string mute;
public static readonly string mute;
[Description("User can register account in game")]
public static readonly string canregister;
[Description("User can login in game")]
public static readonly string canlogin;
[Description("User can change password in game")]
public static readonly string canchangepassword;
[Description("User can use party chat in game")]
public static readonly string canpartychat;
[Description("User can talk in third person")]
public static readonly string cantalkinthird;
[Description("Bypass Server Side Inventory checks")] [Description("User can register account in game")] public static readonly string canregister;
public static readonly string bypassinventorychecks;
[Description("Allow unrestricted Send Tile Square usage, for client side world editing")] [Description("User can login in game")] public static readonly string canlogin;
public static readonly string allowclientsideworldedit;
static Permissions() [Description("User can change password in game")] public static readonly string canchangepassword;
{
foreach (var field in typeof(Permissions).GetFields())
{
field.SetValue(null, field.Name);
}
}
static List<Command> GetCommands(string perm) [Description("User can use party chat in game")] public static readonly string canpartychat;
{
if (Commands.ChatCommands.Count < 1)
Commands.InitCommands();
return Commands.ChatCommands.Where(c => c.Permission == perm).ToList();
}
static void DumpDescriptions() [Description("User can talk in third person")] public static readonly string cantalkinthird;
{
var sb = new StringBuilder();
foreach (var field in typeof(Permissions).GetFields())
{
var name = field.Name;
var descattr = field.GetCustomAttributes(false).FirstOrDefault(o => o is DescriptionAttribute) as DescriptionAttribute; [Description("Bypass Server Side Inventory checks")] public static readonly string bypassinventorychecks;
var desc = descattr != null && !string.IsNullOrWhiteSpace(descattr.Description) ? descattr.Description : "None";
var commands = GetCommands(name); [Description("Allow unrestricted Send Tile Square usage, for client side world editing")] public static readonly
foreach (var c in commands) string allowclientsideworldedit;
{
for (var i = 0; i < c.Names.Count; i++)
{
c.Names[i] = "/" + c.Names[i];
}
}
var strs = commands.Select(c => c.Name + (c.Names.Count > 1 ? "({0})".SFormat(string.Join(" ", c.Names.ToArray(), 1, c.Names.Count - 1)) : ""));
sb.AppendLine("## <a name=\"{0}\">{0} ".SFormat(name));
sb.AppendLine("**Description:** {0} ".SFormat(desc));
sb.AppendLine("**Commands:** {0} ".SFormat(strs.Count() > 0 ? string.Join(" ", strs) : "None"));
sb.AppendLine();
}
File.WriteAllText("PermissionsDescriptions.txt", sb.ToString()); static Permissions()
} {
} foreach (var field in typeof (Permissions).GetFields())
{
field.SetValue(null, field.Name);
}
}
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)] private static List<Command> GetCommands(string perm)
public sealed class TodoAttribute : Attribute {
{ if (Commands.ChatCommands.Count < 1)
public string Info { get; private set; } Commands.InitCommands();
return Commands.ChatCommands.Where(c => c.Permission == perm).ToList();
}
public TodoAttribute(string info) private static void DumpDescriptions()
{ {
Info = info; var sb = new StringBuilder();
foreach (var field in typeof (Permissions).GetFields())
{
var name = field.Name;
} var descattr =
public TodoAttribute() field.GetCustomAttributes(false).FirstOrDefault(o => o is DescriptionAttribute) as DescriptionAttribute;
{ var desc = descattr != null && !string.IsNullOrWhiteSpace(descattr.Description) ? descattr.Description : "None";
}
} var commands = GetCommands(name);
foreach (var c in commands)
{
for (var i = 0; i < c.Names.Count; i++)
{
c.Names[i] = "/" + c.Names[i];
}
}
var strs =
commands.Select(
c =>
c.Name + (c.Names.Count > 1 ? "({0})".SFormat(string.Join(" ", c.Names.ToArray(), 1, c.Names.Count - 1)) : ""));
} sb.AppendLine("## <a name=\"{0}\">{0} ".SFormat(name));
sb.AppendLine("**Description:** {0} ".SFormat(desc));
sb.AppendLine("**Commands:** {0} ".SFormat(strs.Count() > 0 ? string.Join(" ", strs) : "None"));
sb.AppendLine();
}
File.WriteAllText("PermissionsDescriptions.txt", sb.ToString());
}
}
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class TodoAttribute : Attribute
{
public string Info { get; private set; }
public TodoAttribute(string info)
{
Info = info;
}
public TodoAttribute()
{
}
}
}

View file

@ -28,420 +28,429 @@ using Terraria;
namespace TShockAPI namespace TShockAPI
{ {
class RconHandler internal class RconHandler
{ {
public static string Password = ""; public static string Password = "";
private static DateTime LastRequest; private static DateTime LastRequest;
private static DateTime LastHeartbeat; private static DateTime LastHeartbeat;
public static int ListenPort; public static int ListenPort;
public static bool ContinueServer = true; public static bool ContinueServer = true;
public static string Response = ""; public static string Response = "";
private static bool Started; private static bool Started;
private static UdpClient listener; private static UdpClient listener;
private static Thread startThread; private static Thread startThread;
private static Thread heartbeat; private static Thread heartbeat;
private static Thread listen; private static Thread listen;
public static void ShutdownAllThreads() public static void ShutdownAllThreads()
{ {
if (Started) if (Started)
{ {
startThread.Abort(); startThread.Abort();
heartbeat.Abort(); heartbeat.Abort();
listen.Abort(); listen.Abort();
Started = false; Started = false;
} }
} }
public static void StartThread() public static void StartThread()
{ {
if (!Started) if (!Started)
{ {
startThread = new Thread(Start); startThread = new Thread(Start);
startThread.Start(); startThread.Start();
heartbeat = new Thread(SendHeartbeat); heartbeat = new Thread(SendHeartbeat);
heartbeat.Start(); heartbeat.Start();
} }
Started = true; Started = true;
} }
public static void Start() public static void Start()
{ {
Log.Info("Starting RconHandler."); Log.Info("Starting RconHandler.");
try try
{ {
Console.WriteLine(string.Format("RconHandler is running at UDP port {0} and password is {1}", Console.WriteLine(string.Format("RconHandler is running at UDP port {0} and password is {1}",
ListenPort, ListenPort,
Password)); Password));
listen = new Thread(Listener); listen = new Thread(Listener);
listen.Start(); listen.Start();
while (true) while (true)
{ {
if (listen.ThreadState != ThreadState.Running) if (listen.ThreadState != ThreadState.Running)
{ {
listen.Abort(); listen.Abort();
while (listen.ThreadState != ThreadState.Stopped) while (listen.ThreadState != ThreadState.Stopped)
continue; continue;
listen.Start(); listen.Start();
} }
Thread.Sleep(3000); Thread.Sleep(3000);
} }
} }
catch (Exception e) catch (Exception e)
{ {
Log.Error(e.ToString()); Log.Error(e.ToString());
} }
} }
private static void Listener() private static void Listener()
{ {
if (listener == null) if (listener == null)
try try
{ {
listener = new UdpClient(ListenPort); listener = new UdpClient(ListenPort);
} }
catch (SocketException e) catch (SocketException e)
{ {
if (e.SocketErrorCode == SocketError.AddressAlreadyInUse) if (e.SocketErrorCode == SocketError.AddressAlreadyInUse)
Log.ConsoleError("Could not bind to " + ListenPort + ". Are you sure you don't have another instance running?"); Log.ConsoleError("Could not bind to " + ListenPort + ". Are you sure you don't have another instance running?");
} }
catch (Exception e) catch (Exception e)
{ {
Log.Error(e.ToString()); Log.Error(e.ToString());
} }
while (ContinueServer) while (ContinueServer)
{ {
try try
{ {
var listenEP = new IPEndPoint(IPAddress.Any, ListenPort); var listenEP = new IPEndPoint(IPAddress.Any, ListenPort);
LastRequest = DateTime.Now; LastRequest = DateTime.Now;
byte[] bytes = listener.Receive(ref listenEP); byte[] bytes = listener.Receive(ref listenEP);
var packet = ParsePacket(bytes, listenEP); var packet = ParsePacket(bytes, listenEP);
listener.Send(packet, packet.Length, listenEP); listener.Send(packet, packet.Length, listenEP);
} }
catch (Exception e) catch (Exception e)
{ {
Log.Error(e.ToString()); Log.Error(e.ToString());
} }
} }
}
} private static string SendPacket(byte[] bytes, string hostname, int port)
{
var response = Encoding.UTF8.GetString(new byte[] {0xFF, 0xFF, 0xFF, 0xFF}) + "disconnect";
try
{
var EP = new IPEndPoint(IPAddress.Any, port);
using (var client = new UdpClient())
{
client.Connect(hostname, port);
client.Client.ReceiveTimeout = 500;
client.Send(bytes, bytes.Length);
response = Encoding.UTF8.GetString(client.Receive(ref EP));
}
}
catch (Exception e)
{
Log.Error(e.ToString());
}
return response;
}
private static string SendPacket(byte[] bytes, string hostname, int port) private static byte[] ParsePacket(byte[] bytes, IPEndPoint EP)
{ {
var response = Encoding.UTF8.GetString(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }) + "disconnect"; string response = "";
try var packetstring = Encoding.UTF8.GetString(PadPacket(bytes));
{ var redirect = false;
var EP = new IPEndPoint(IPAddress.Any, port); var print = true;
using (var client = new UdpClient()) if ((DateTime.Now - LastRequest).Milliseconds >= 100)
{ {
client.Connect(hostname, port); if (packetstring.StartsWith("rcon") || packetstring.Substring(4).StartsWith("rcon") ||
client.Client.ReceiveTimeout = 500; packetstring.Substring(5).StartsWith("rcon"))
client.Send(bytes, bytes.Length); {
response = Encoding.UTF8.GetString(client.Receive(ref EP)); if (!string.IsNullOrEmpty(Password))
} {
} var args = ParseParameters(packetstring);
catch (Exception e) if (args.Count >= 3)
{ {
Log.Error(e.ToString()); if (args[1] == Password)
} {
return response; args[1] = args[0] = "";
} string command = string.Join(" ", args.ToArray());
command = command.TrimEnd(' ').TrimEnd('\0').TrimStart(' ');
Log.ConsoleInfo("Rcon from " + EP + ":" + command);
Response = "";
response = ExecuteCommand(command);
response += "\n" + Response;
Response = "";
response = response.TrimStart('\n');
}
else
{
response = "Bad rcon password.\n";
Log.ConsoleInfo("Bad rcon password from " + EP);
}
}
else
response = "";
}
else
{
response = "No rcon password set on the server.\n";
Log.Info("No password for rcon set");
}
}
else
redirect = true;
}
if (packetstring.StartsWith("getinfo")
|| packetstring.Substring(4).StartsWith("getinfo")
|| packetstring.Substring(5).StartsWith("getinfo"))
{
var challenge = "";
if (packetstring.Split(' ').Length == 2)
challenge = packetstring.Split(' ')[1];
response = "infoResponse\n";
var infostring =
string.Format(
@"\_TShock_ver\{6}\mapname\{1}\sv_maxclients\{2}\clients\{3}\sv_privateClients\{4}\hconly\{5}\gamename\TERRARIA\protocol\100\sv_hostname\{0}\g_needPass\{7}",
TShock.Config.ServerName, Main.worldName, Main.maxNetPlayers,
TShock.Utils.ActivePlayers(), Main.maxNetPlayers - TShock.Config.MaxSlots,
TShock.Config.HardcoreOnly ? 1 : 0, TShock.VersionNum,
Netplay.password != "" ? 1 : 0);
if (challenge != "")
infostring += @"\challenge\" + challenge;
response += infostring;
print = false;
redirect = false;
}
else if (packetstring.StartsWith("getstatus")
|| packetstring.Substring(4).StartsWith("getstatus")
|| packetstring.Substring(5).StartsWith("getstatus"))
{
var challenge = "";
if (packetstring.Split(' ').Length == 2)
challenge = packetstring.Split(' ')[1];
response = "statusResponse\n";
var statusstring = string.Format(
@"\_TShock_ver\{6}\mapname\{1}\sv_maxclients\{2}\clients\{3}\sv_privateClients\{4}\hconly\{5}\gamename\TERRARIA\protocol\100\sv_hostname\{0}\g_needPass\{7}",
TShock.Config.ServerName, Main.worldName, Main.maxNetPlayers,
TShock.Utils.ActivePlayers(), Main.maxNetPlayers - TShock.Config.MaxSlots,
TShock.Config.HardcoreOnly ? 1 : 0, TShock.VersionNum,
Netplay.password != "" ? 1 : 0) + "\n";
if (challenge != "")
statusstring += @"\challenge\" + challenge;
foreach (TSPlayer player in TShock.Players)
{
if (player != null && player.Active)
statusstring += (string.Format("0 0 {0}\n", player.Name));
}
response += statusstring;
print = false;
redirect = false;
}
if (!redirect)
return (ConstructPacket(response, print));
else
return (ConstructPacket("disconnect", false));
}
private static byte[] ParsePacket(byte[] bytes, IPEndPoint EP) private static string ExecuteCommand(string text)
{ {
string response = ""; if (Main.rand == null)
var packetstring = Encoding.UTF8.GetString(PadPacket(bytes)); Main.rand = new Random();
var redirect = false; if (WorldGen.genRand == null)
var print = true; WorldGen.genRand = new Random();
if ((DateTime.Now - LastRequest).Milliseconds >= 100) if (text.StartsWith("exit"))
{ {
if (packetstring.StartsWith("rcon") || packetstring.Substring(4).StartsWith("rcon") || packetstring.Substring(5).StartsWith("rcon")) TShock.Utils.ForceKickAll("Server shutting down!");
{ WorldGen.saveWorld(false);
if (!string.IsNullOrEmpty(Password)) Netplay.disconnect = true;
{ return "Server shutting down.";
var args = ParseParameters(packetstring); }
if (args.Count >= 3) else if (text.StartsWith("playing") || text.StartsWith("/playing"))
{ {
if (args[1] == Password) int count = 0;
{ foreach (TSPlayer player in TShock.Players)
args[1] = args[0] = ""; {
string command = string.Join(" ", args.ToArray()); if (player != null && player.Active)
command = command.TrimEnd(' ').TrimEnd('\0').TrimStart(' '); {
Log.ConsoleInfo("Rcon from " + EP + ":" + command); count++;
Response = ""; TSPlayer.Server.SendMessage(string.Format("{0} ({1}) [{2}] <{3}>", player.Name, player.IP, player.Group.Name,
response = ExecuteCommand(command); player.UserAccountName));
response += "\n" + Response; }
Response = ""; }
response = response.TrimStart('\n'); TSPlayer.Server.SendMessage(string.Format("{0} players connected.", count));
} }
else else if (text.StartsWith("status"))
{ {
response = "Bad rcon password.\n"; Response += "map: " + Main.worldName + "\n";
Log.ConsoleInfo("Bad rcon password from " + EP); Response += "num score ping name lastmsg address qport rate\n";
} int count = 0;
} foreach (TSPlayer player in TShock.Players)
else {
response = ""; if (player != null && player.Active)
} {
else count++;
{ Response +=
response = "No rcon password set on the server.\n"; (string.Format("{0} 0 0 {1}({2}) {3} {4} 0 0", count, player.Name, player.Group.Name,
Log.Info("No password for rcon set"); Netplay.serverSock[player.Index].tcpClient.Client.RemoteEndPoint, "")) + "\n";
} }
} }
else }
redirect = true; else if (text.StartsWith("say "))
} {
if (packetstring.StartsWith("getinfo") Log.Info(string.Format("Server said: {0}", text.Remove(0, 4)));
|| packetstring.Substring(4).StartsWith("getinfo") return string.Format("Server said: {0}", text.Remove(0, 4));
|| packetstring.Substring(5).StartsWith("getinfo")) }
{ else if (text == "autosave")
var challenge = ""; {
if (packetstring.Split(' ').Length == 2) Main.autoSave = TShock.Config.AutoSave = !TShock.Config.AutoSave;
challenge = packetstring.Split(' ')[1]; Log.ConsoleInfo("AutoSave " + (TShock.Config.AutoSave ? "Enabled" : "Disabled"));
response = "infoResponse\n"; return "AutoSave " + (TShock.Config.AutoSave ? "Enabled" : "Disabled");
var infostring = string.Format(@"\_TShock_ver\{6}\mapname\{1}\sv_maxclients\{2}\clients\{3}\sv_privateClients\{4}\hconly\{5}\gamename\TERRARIA\protocol\100\sv_hostname\{0}\g_needPass\{7}", }
TShock.Config.ServerName, Main.worldName, Main.maxNetPlayers, else if (text.StartsWith("/"))
TShock.Utils.ActivePlayers(), Main.maxNetPlayers - TShock.Config.MaxSlots, {
TShock.Config.HardcoreOnly ? 1 : 0, TShock.VersionNum, if (!Commands.HandleCommand(TSPlayer.Server, text))
Netplay.password != "" ? 1 : 0); return "Invalid command.";
if (challenge != "") }
infostring += @"\challenge\" + challenge; else if (!Commands.HandleCommand(TSPlayer.Server, "/" + text))
response += infostring; return "Invalid command.";
print = false; return "";
redirect = false; }
}
else if (packetstring.StartsWith("getstatus")
|| packetstring.Substring(4).StartsWith("getstatus")
|| packetstring.Substring(5).StartsWith("getstatus"))
{
var challenge = "";
if (packetstring.Split(' ').Length == 2)
challenge = packetstring.Split(' ')[1];
response = "statusResponse\n";
var statusstring = string.Format(@"\_TShock_ver\{6}\mapname\{1}\sv_maxclients\{2}\clients\{3}\sv_privateClients\{4}\hconly\{5}\gamename\TERRARIA\protocol\100\sv_hostname\{0}\g_needPass\{7}",
TShock.Config.ServerName, Main.worldName, Main.maxNetPlayers,
TShock.Utils.ActivePlayers(), Main.maxNetPlayers - TShock.Config.MaxSlots,
TShock.Config.HardcoreOnly ? 1 : 0, TShock.VersionNum,
Netplay.password != "" ? 1 : 0) + "\n";
if (challenge != "")
statusstring += @"\challenge\" + challenge;
foreach (TSPlayer player in TShock.Players)
{
if (player != null && player.Active)
statusstring += (string.Format("0 0 {0}\n", player.Name));
}
response += statusstring;
print = false;
redirect = false;
}
if (!redirect)
return (ConstructPacket(response, print));
else
return (ConstructPacket("disconnect", false));
}
private static string ExecuteCommand(string text) private static byte[] ConstructPacket(string response, bool print)
{ {
if (Main.rand == null) var oob = new byte[] {0xFF, 0xFF, 0xFF, 0xFF};
Main.rand = new Random(); using (var stream = new MemoryStream())
if (WorldGen.genRand == null) {
WorldGen.genRand = new Random(); stream.WriteBytes(oob);
if (text.StartsWith("exit")) if (print)
{ stream.WriteBytes(Encoding.UTF8.GetBytes(string.Format("print\n{0}", response)));
TShock.Utils.ForceKickAll("Server shutting down!"); else
WorldGen.saveWorld(false); stream.WriteBytes(Encoding.UTF8.GetBytes(response));
Netplay.disconnect = true; var trimmedpacket = new byte[(int) stream.Length];
return "Server shutting down."; var packet = stream.GetBuffer();
} Array.Copy(packet, trimmedpacket, (int) stream.Length);
else if (text.StartsWith("playing") || text.StartsWith("/playing")) return trimmedpacket;
{ }
int count = 0; }
foreach (TSPlayer player in TShock.Players)
{
if (player != null && player.Active)
{
count++;
TSPlayer.Server.SendMessage(string.Format("{0} ({1}) [{2}] <{3}>", player.Name, player.IP, player.Group.Name, player.UserAccountName));
}
}
TSPlayer.Server.SendMessage(string.Format("{0} players connected.", count));
}
else if (text.StartsWith("status"))
{
Response += "map: " + Main.worldName + "\n";
Response += "num score ping name lastmsg address qport rate\n";
int count = 0;
foreach (TSPlayer player in TShock.Players)
{
if (player != null && player.Active)
{
count++;
Response += (string.Format("{0} 0 0 {1}({2}) {3} {4} 0 0", count, player.Name, player.Group.Name, Netplay.serverSock[player.Index].tcpClient.Client.RemoteEndPoint, "")) + "\n";
}
}
}
else if (text.StartsWith("say "))
{
Log.Info(string.Format("Server said: {0}", text.Remove(0, 4)));
return string.Format("Server said: {0}", text.Remove(0, 4));
}
else if (text == "autosave")
{
Main.autoSave = TShock.Config.AutoSave = !TShock.Config.AutoSave;
Log.ConsoleInfo("AutoSave " + (TShock.Config.AutoSave ? "Enabled" : "Disabled"));
return "AutoSave " + (TShock.Config.AutoSave ? "Enabled" : "Disabled");
}
else if (text.StartsWith("/"))
{
if (!Commands.HandleCommand(TSPlayer.Server, text))
return "Invalid command.";
}
else
if (!Commands.HandleCommand(TSPlayer.Server, "/" + text))
return "Invalid command.";
return "";
}
private static byte[] ConstructPacket(string response, bool print) private static byte[] PadPacket(byte[] packet)
{ {
var oob = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }; var returnpacket = new byte[(4 + packet.Length)];
using (var stream = new MemoryStream()) int h = 0;
{ if (packet[0] != 0xFF)
stream.WriteBytes(oob); {
if (print) for (int i = 0; i < 4; i++)
stream.WriteBytes(Encoding.UTF8.GetBytes(string.Format("print\n{0}", response))); returnpacket[i] = 0xFF;
else for (int i = 4; i < returnpacket.Length; i++)
stream.WriteBytes(Encoding.UTF8.GetBytes(response)); returnpacket[i] = packet[h++];
var trimmedpacket = new byte[(int)stream.Length]; }
var packet = stream.GetBuffer(); else
Array.Copy(packet, trimmedpacket, (int)stream.Length); returnpacket = packet;
return trimmedpacket; return returnpacket;
} }
}
private static byte[] PadPacket(byte[] packet) private static void SendHeartbeat()
{ {
var returnpacket = new byte[(4 + packet.Length)]; LastHeartbeat = DateTime.UtcNow.Subtract(new TimeSpan(0, 0, 30));
int h = 0; while (true)
if (packet[0] != 0xFF) {
{ if ((DateTime.UtcNow - LastHeartbeat).Seconds >= 30)
for (int i = 0; i < 4; i++) {
returnpacket[i] = 0xFF; var packet = ConstructPacket("heartbeat TerrariaShock", false);
for (int i = 4; i < returnpacket.Length; i++) if (listener == null)
returnpacket[i] = packet[h++]; try
} {
else listener = new UdpClient(ListenPort);
returnpacket = packet; }
return returnpacket; catch (SocketException e)
} {
if (e.SocketErrorCode == SocketError.AddressAlreadyInUse)
Log.ConsoleError("Could not bind to " + ListenPort + ". Are you sure you don't have another instance running?");
}
catch (Exception e)
{
Log.Error(e.ToString());
}
listener.Send(packet, packet.Length, TShock.Config.MasterServer, 27950);
LastHeartbeat = DateTime.UtcNow;
}
Thread.Sleep(10000);
}
}
private static void SendHeartbeat() #region ParseParams
{
LastHeartbeat = DateTime.UtcNow.Subtract(new TimeSpan(0, 0, 30));
while (true)
{
if ((DateTime.UtcNow - LastHeartbeat).Seconds >= 30)
{
var packet = ConstructPacket("heartbeat TerrariaShock", false);
if (listener == null)
try
{
listener = new UdpClient(ListenPort);
}
catch (SocketException e)
{
if (e.SocketErrorCode == SocketError.AddressAlreadyInUse)
Log.ConsoleError("Could not bind to " + ListenPort + ". Are you sure you don't have another instance running?");
}
catch (Exception e)
{
Log.Error(e.ToString());
}
listener.Send(packet, packet.Length, TShock.Config.MasterServer, 27950);
LastHeartbeat = DateTime.UtcNow;
}
Thread.Sleep(10000);
}
}
#region ParseParams private static List<String> ParseParameters(string str)
private static List<String> ParseParameters(string str) {
{ var ret = new List<string>();
var ret = new List<string>(); var sb = new StringBuilder();
var sb = new StringBuilder(); bool instr = false;
bool instr = false; for (int i = 0; i < str.Length; i++)
for (int i = 0; i < str.Length; i++) {
{ char c = str[i];
char c = str[i];
if (instr) if (instr)
{ {
if (c == '\\') if (c == '\\')
{ {
if (i + 1 >= str.Length) if (i + 1 >= str.Length)
break; break;
c = GetEscape(str[++i]); c = GetEscape(str[++i]);
} }
else if (c == '"') else if (c == '"')
{ {
ret.Add(sb.ToString()); ret.Add(sb.ToString());
sb.Clear(); sb.Clear();
instr = false; instr = false;
continue; continue;
} }
sb.Append(c); sb.Append(c);
} }
else else
{ {
if (IsWhiteSpace(c)) if (IsWhiteSpace(c))
{ {
if (sb.Length > 0) if (sb.Length > 0)
{ {
ret.Add(sb.ToString()); ret.Add(sb.ToString());
sb.Clear(); sb.Clear();
} }
} }
else if (c == '"') else if (c == '"')
{ {
if (sb.Length > 0) if (sb.Length > 0)
{ {
ret.Add(sb.ToString()); ret.Add(sb.ToString());
sb.Clear(); sb.Clear();
} }
instr = true; instr = true;
} }
else else
{ {
sb.Append(c); sb.Append(c);
} }
} }
} }
if (sb.Length > 0) if (sb.Length > 0)
ret.Add(sb.ToString()); ret.Add(sb.ToString());
return ret; return ret;
} }
private static char GetEscape(char c)
{ private static char GetEscape(char c)
switch (c) {
{ switch (c)
case '\\': {
return '\\'; case '\\':
case '"': return '\\';
return '"'; case '"':
case 't': return '"';
return '\t'; case 't':
default: return '\t';
return c; default:
} return c;
} }
private static bool IsWhiteSpace(char c) }
{
return c == ' ' || c == '\t' || c == '\n'; private static bool IsWhiteSpace(char c)
} {
#endregion return c == ' ' || c == '\t' || c == '\n';
} }
}
#endregion
}
}

View file

@ -10,132 +10,142 @@ using HttpListener = HttpServer.HttpListener;
namespace Rests namespace Rests
{ {
/// <summary> /// <summary>
/// Rest command delegate /// Rest command delegate
/// </summary> /// </summary>
/// <param name="parameters">Parameters in the url</param> /// <param name="parameters">Parameters in the url</param>
/// <param name="verbs">{x} in urltemplate</param> /// <param name="verbs">{x} in urltemplate</param>
/// <returns>Response object or null to not handle request</returns> /// <returns>Response object or null to not handle request</returns>
public delegate object RestCommandD(RestVerbs verbs, IParameterCollection parameters); public delegate object RestCommandD(RestVerbs verbs, IParameterCollection parameters);
public class Rest : IDisposable
{
readonly List<RestCommand> commands = new List<RestCommand>();
HttpListener listener;
public IPAddress Ip { get; set; }
public int Port { get; set; }
public Rest(IPAddress ip, int port) public class Rest : IDisposable
{ {
Ip = ip; private readonly List<RestCommand> commands = new List<RestCommand>();
Port = port; private HttpListener listener;
} public IPAddress Ip { get; set; }
public virtual void Start() public int Port { get; set; }
{
if (listener == null)
{
listener = HttpListener.Create(Ip, Port);
listener.RequestReceived += OnRequest;
listener.Start(int.MaxValue);
}
}
public void Start(IPAddress ip, int port)
{
Ip = ip;
Port = port;
Start();
}
public virtual void Stop()
{
listener.Stop();
}
public void Register(string path, RestCommandD callback) public Rest(IPAddress ip, int port)
{ {
AddCommand(new RestCommand(path, callback)); Ip = ip;
} Port = port;
}
public void Register(RestCommand com) public virtual void Start()
{ {
AddCommand(com); if (listener == null)
} {
listener = HttpListener.Create(Ip, Port);
listener.RequestReceived += OnRequest;
listener.Start(int.MaxValue);
}
}
protected void AddCommand(RestCommand com) public void Start(IPAddress ip, int port)
{ {
commands.Add(com); Ip = ip;
} Port = port;
Start();
}
protected virtual void OnRequest(object sender, RequestEventArgs e) public virtual void Stop()
{ {
var obj = ProcessRequest(sender, e); listener.Stop();
if (obj == null) }
throw new NullReferenceException("obj");
var str = JsonConvert.SerializeObject(obj, Formatting.Indented); public void Register(string path, RestCommandD callback)
e.Response.Connection.Type = ConnectionType.Close; {
e.Response.Body.Write(Encoding.ASCII.GetBytes(str), 0, str.Length); AddCommand(new RestCommand(path, callback));
e.Response.Status = HttpStatusCode.OK; }
return;
}
protected virtual object ProcessRequest(object sender, RequestEventArgs e) public void Register(RestCommand com)
{ {
var uri = e.Request.Uri.AbsolutePath; AddCommand(com);
uri = uri.TrimEnd('/'); }
foreach (var com in commands) protected void AddCommand(RestCommand com)
{ {
var verbs = new RestVerbs(); commands.Add(com);
if (com.HasVerbs) }
{
var match = Regex.Match(uri, com.UriVerbMatch);
if (!match.Success)
continue;
if ((match.Groups.Count - 1) != com.UriVerbs.Length)
continue;
for (int i = 0; i < com.UriVerbs.Length; i++) protected virtual void OnRequest(object sender, RequestEventArgs e)
verbs.Add(com.UriVerbs[i], match.Groups[i + 1].Value); {
} var obj = ProcessRequest(sender, e);
else if (com.UriTemplate.ToLower() != uri.ToLower()) if (obj == null)
{ throw new NullReferenceException("obj");
continue;
}
var obj = ExecuteCommand(com, verbs, e.Request.Parameters); var str = JsonConvert.SerializeObject(obj, Formatting.Indented);
if (obj != null) e.Response.Connection.Type = ConnectionType.Close;
return obj; e.Response.Body.Write(Encoding.ASCII.GetBytes(str), 0, str.Length);
e.Response.Status = HttpStatusCode.OK;
return;
}
} protected virtual object ProcessRequest(object sender, RequestEventArgs e)
return new Dictionary<string, string> { { "status", "404" }, { "error", "Specified API endpoint doesn't exist. Refer to the documentation for a list of valid endpoints." } }; {
} var uri = e.Request.Uri.AbsolutePath;
uri = uri.TrimEnd('/');
protected virtual object ExecuteCommand(RestCommand cmd, RestVerbs verbs, IParameterCollection parms) foreach (var com in commands)
{ {
return cmd.Callback(verbs, parms); var verbs = new RestVerbs();
} if (com.HasVerbs)
{
var match = Regex.Match(uri, com.UriVerbMatch);
if (!match.Success)
continue;
if ((match.Groups.Count - 1) != com.UriVerbs.Length)
continue;
#region Dispose for (int i = 0; i < com.UriVerbs.Length; i++)
public void Dispose() verbs.Add(com.UriVerbs[i], match.Groups[i + 1].Value);
{ }
Dispose(true); else if (com.UriTemplate.ToLower() != uri.ToLower())
GC.SuppressFinalize(this); {
} continue;
protected virtual void Dispose(bool disposing) }
{
if (disposing)
{
if (listener != null)
{
listener.Stop();
listener = null;
}
}
}
~Rest()
{
Dispose(false);
}
#endregion var obj = ExecuteCommand(com, verbs, e.Request.Parameters);
} if (obj != null)
} return obj;
}
return new Dictionary<string, string>
{
{"status", "404"},
{"error", "Specified API endpoint doesn't exist. Refer to the documentation for a list of valid endpoints."}
};
}
protected virtual object ExecuteCommand(RestCommand cmd, RestVerbs verbs, IParameterCollection parms)
{
return cmd.Callback(verbs, parms);
}
#region Dispose
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (listener != null)
{
listener.Stop();
listener = null;
}
}
}
~Rest()
{
Dispose(false);
}
#endregion
}
}

View file

@ -3,45 +3,45 @@ using System.Text.RegularExpressions;
namespace Rests namespace Rests
{ {
public class RestCommand public class RestCommand
{ {
public string Name { get; protected set; } public string Name { get; protected set; }
public string UriTemplate { get; protected set; } public string UriTemplate { get; protected set; }
public string UriVerbMatch { get; protected set; } public string UriVerbMatch { get; protected set; }
public string[] UriVerbs { get; protected set; } public string[] UriVerbs { get; protected set; }
public RestCommandD Callback { get; protected set; } public RestCommandD Callback { get; protected set; }
public bool RequiresToken { get; set; } public bool RequiresToken { get; set; }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="name">Used for identification</param> /// <param name="name">Used for identification</param>
/// <param name="uritemplate">Url template</param> /// <param name="uritemplate">Url template</param>
/// <param name="callback">Rest Command callback</param> /// <param name="callback">Rest Command callback</param>
public RestCommand(string name, string uritemplate, RestCommandD callback) public RestCommand(string name, string uritemplate, RestCommandD callback)
{ {
Name = name; Name = name;
UriTemplate = uritemplate; UriTemplate = uritemplate;
UriVerbMatch = string.Format("^{0}$", string.Join("([^/]*)", Regex.Split(uritemplate, "\\{[^\\{\\}]*\\}"))); UriVerbMatch = string.Format("^{0}$", string.Join("([^/]*)", Regex.Split(uritemplate, "\\{[^\\{\\}]*\\}")));
var matches = Regex.Matches(uritemplate, "\\{([^\\{\\}]*)\\}"); var matches = Regex.Matches(uritemplate, "\\{([^\\{\\}]*)\\}");
UriVerbs = (from Match match in matches select match.Groups[1].Value).ToArray(); UriVerbs = (from Match match in matches select match.Groups[1].Value).ToArray();
Callback = callback; Callback = callback;
RequiresToken = true; RequiresToken = true;
} }
/// <summary>
///
/// </summary>
/// <param name="uritemplate">Url template</param>
/// <param name="callback">Rest Command callback</param>
public RestCommand(string uritemplate, RestCommandD callback)
: this(string.Empty, uritemplate, callback)
{
} /// <summary>
///
/// </summary>
/// <param name="uritemplate">Url template</param>
/// <param name="callback">Rest Command callback</param>
public RestCommand(string uritemplate, RestCommandD callback)
: this(string.Empty, uritemplate, callback)
{
}
public bool HasVerbs public bool HasVerbs
{ {
get { return UriVerbs.Length > 0; } get { return UriVerbs.Length > 0; }
} }
} }
} }

View file

@ -8,441 +8,451 @@ using TShockAPI.DB;
namespace TShockAPI namespace TShockAPI
{ {
public class RestManager
{
private Rest Rest;
public class RestManager public RestManager(Rest rest)
{ {
private Rest Rest; Rest = rest;
public RestManager(Rest rest) }
{
Rest = rest;
}
public void RegisterRestfulCommands() public void RegisterRestfulCommands()
{ {
Rest.Register(new RestCommand("/status", Status) { RequiresToken = false }); Rest.Register(new RestCommand("/status", Status) {RequiresToken = false});
Rest.Register(new RestCommand("/tokentest", TokenTest) { RequiresToken = true }); Rest.Register(new RestCommand("/tokentest", TokenTest) {RequiresToken = true});
Rest.Register(new RestCommand("/users/read/{user}/info", UserInfo) { RequiresToken = true }); Rest.Register(new RestCommand("/users/read/{user}/info", UserInfo) {RequiresToken = true});
Rest.Register(new RestCommand("/users/destroy/{user}", UserDestroy) { RequiresToken = true }); Rest.Register(new RestCommand("/users/destroy/{user}", UserDestroy) {RequiresToken = true});
Rest.Register(new RestCommand("/users/update/{user}", UserUpdate) { RequiresToken = true }); Rest.Register(new RestCommand("/users/update/{user}", UserUpdate) {RequiresToken = true});
Rest.Register(new RestCommand("/bans/create", BanCreate) { RequiresToken = true }); Rest.Register(new RestCommand("/bans/create", BanCreate) {RequiresToken = true});
Rest.Register(new RestCommand("/bans/read/{user}/info", BanInfo) { RequiresToken = true }); Rest.Register(new RestCommand("/bans/read/{user}/info", BanInfo) {RequiresToken = true});
Rest.Register(new RestCommand("/bans/destroy/{user}", BanDestroy) { RequiresToken = true }); Rest.Register(new RestCommand("/bans/destroy/{user}", BanDestroy) {RequiresToken = true});
Rest.Register(new RestCommand("/lists/players", UserList) { RequiresToken = true }); Rest.Register(new RestCommand("/lists/players", UserList) {RequiresToken = true});
Rest.Register(new RestCommand("/world/read", WorldRead) { RequiresToken = true }); Rest.Register(new RestCommand("/world/read", WorldRead) {RequiresToken = true});
Rest.Register(new RestCommand("/world/meteor", WorldMeteor) { RequiresToken = true }); Rest.Register(new RestCommand("/world/meteor", WorldMeteor) {RequiresToken = true});
Rest.Register(new RestCommand("/world/bloodmoon/{bool}", WorldBloodmoon) { RequiresToken = true }); Rest.Register(new RestCommand("/world/bloodmoon/{bool}", WorldBloodmoon) {RequiresToken = true});
Rest.Register(new RestCommand("/players/read/{player}", PlayerRead) { RequiresToken = true }); Rest.Register(new RestCommand("/players/read/{player}", PlayerRead) {RequiresToken = true});
Rest.Register(new RestCommand("/players/{player}/kick", PlayerKick) { RequiresToken = true }); Rest.Register(new RestCommand("/players/{player}/kick", PlayerKick) {RequiresToken = true});
Rest.Register(new RestCommand("/players/{player}/ban", PlayerBan) { RequiresToken = true }); Rest.Register(new RestCommand("/players/{player}/ban", PlayerBan) {RequiresToken = true});
//RegisterExamples(); //RegisterExamples();
} }
#region RestMethods #region RestMethods
object TokenTest(RestVerbs verbs, IParameterCollection parameters) private object TokenTest(RestVerbs verbs, IParameterCollection parameters)
{ {
return new Dictionary<string, string> { { "status", "200" }, { "response", "Token is valid and was passed through correctly." } }; return new Dictionary<string, string>
} {{"status", "200"}, {"response", "Token is valid and was passed through correctly."}};
}
object Status(RestVerbs verbs, IParameterCollection parameters) private object Status(RestVerbs verbs, IParameterCollection parameters)
{ {
if (TShock.Config.EnableTokenEndpointAuthentication) if (TShock.Config.EnableTokenEndpointAuthentication)
return new RestObject("403") { Error = "Server settings require a token for this API call." }; return new RestObject("403") {Error = "Server settings require a token for this API call."};
var activeplayers = Main.player.Where(p => p != null && p.active).ToList(); var activeplayers = Main.player.Where(p => p != null && p.active).ToList();
string currentPlayers = string.Join(", ", activeplayers.Select(p => p.name)); string currentPlayers = string.Join(", ", activeplayers.Select(p => p.name));
var ret = new RestObject("200"); var ret = new RestObject("200");
ret["name"] = TShock.Config.ServerNickname; ret["name"] = TShock.Config.ServerNickname;
ret["port"] = Convert.ToString(TShock.Config.ServerPort); ret["port"] = Convert.ToString(TShock.Config.ServerPort);
ret["playercount"] = Convert.ToString(activeplayers.Count()); ret["playercount"] = Convert.ToString(activeplayers.Count());
ret["players"] = currentPlayers; ret["players"] = currentPlayers;
return ret; return ret;
} }
#endregion #endregion
#region RestUserMethods #region RestUserMethods
object UserList(RestVerbs verbs, IParameterCollection parameters) private object UserList(RestVerbs verbs, IParameterCollection parameters)
{ {
var activeplayers = Main.player.Where(p => p != null && p.active).ToList(); var activeplayers = Main.player.Where(p => p != null && p.active).ToList();
string currentPlayers = string.Join(", ", activeplayers.Select(p => p.name)); string currentPlayers = string.Join(", ", activeplayers.Select(p => p.name));
var ret = new RestObject("200"); var ret = new RestObject("200");
ret["players"] = currentPlayers; ret["players"] = currentPlayers;
return ret; return ret;
} }
object UserUpdate(RestVerbs verbs, IParameterCollection parameters) private object UserUpdate(RestVerbs verbs, IParameterCollection parameters)
{ {
var returnBlock = new Dictionary<string, string>(); var returnBlock = new Dictionary<string, string>();
var password = parameters["password"]; var password = parameters["password"];
var group = parameters["group"]; var group = parameters["group"];
if (group == null && password == null) if (group == null && password == null)
{ {
returnBlock.Add("status", "400"); returnBlock.Add("status", "400");
returnBlock.Add("error", "No parameters were passed."); returnBlock.Add("error", "No parameters were passed.");
return returnBlock; return returnBlock;
} }
var user = TShock.Users.GetUserByName(verbs["user"]); var user = TShock.Users.GetUserByName(verbs["user"]);
if (user == null) if (user == null)
{ {
returnBlock.Add("status", "400"); returnBlock.Add("status", "400");
returnBlock.Add("error", "The specefied user doesn't exist."); returnBlock.Add("error", "The specefied user doesn't exist.");
return returnBlock; return returnBlock;
} }
if (password != null) if (password != null)
{ {
TShock.Users.SetUserPassword(user, password); TShock.Users.SetUserPassword(user, password);
returnBlock.Add("password-response", "Password updated successfully."); returnBlock.Add("password-response", "Password updated successfully.");
} }
if (group != null) if (group != null)
{ {
TShock.Users.SetUserGroup(user, group); TShock.Users.SetUserGroup(user, group);
returnBlock.Add("group-response", "Group updated successfully."); returnBlock.Add("group-response", "Group updated successfully.");
} }
returnBlock.Add("status", "200"); returnBlock.Add("status", "200");
return returnBlock; return returnBlock;
} }
object UserDestroy(RestVerbs verbs, IParameterCollection parameters) private object UserDestroy(RestVerbs verbs, IParameterCollection parameters)
{ {
var user = TShock.Users.GetUserByName(verbs["user"]); var user = TShock.Users.GetUserByName(verbs["user"]);
if (user == null) if (user == null)
{ {
return new Dictionary<string, string> { { "status", "400" }, { "error", "The specified user account does not exist." } }; return new Dictionary<string, string> {{"status", "400"}, {"error", "The specified user account does not exist."}};
} }
var returnBlock = new Dictionary<string, string>(); var returnBlock = new Dictionary<string, string>();
try try
{ {
TShock.Users.RemoveUser(user); TShock.Users.RemoveUser(user);
} }
catch (Exception) catch (Exception)
{ {
returnBlock.Add("status", "400"); returnBlock.Add("status", "400");
returnBlock.Add("error", "The specified user was unable to be removed."); returnBlock.Add("error", "The specified user was unable to be removed.");
return returnBlock; return returnBlock;
} }
returnBlock.Add("status", "200"); returnBlock.Add("status", "200");
returnBlock.Add("response", "User deleted successfully."); returnBlock.Add("response", "User deleted successfully.");
return returnBlock; return returnBlock;
} }
object UserInfo(RestVerbs verbs, IParameterCollection parameters) private object UserInfo(RestVerbs verbs, IParameterCollection parameters)
{ {
var user = TShock.Users.GetUserByName(verbs["user"]); var user = TShock.Users.GetUserByName(verbs["user"]);
if (user == null) if (user == null)
{ {
return new Dictionary<string, string> { { "status", "400" }, { "error", "The specified user account does not exist." } }; return new Dictionary<string, string> {{"status", "400"}, {"error", "The specified user account does not exist."}};
} }
var returnBlock = new Dictionary<string, string>(); var returnBlock = new Dictionary<string, string>();
returnBlock.Add("status", "200"); returnBlock.Add("status", "200");
returnBlock.Add("group", user.Group); returnBlock.Add("group", user.Group);
returnBlock.Add("id", user.ID.ToString()); returnBlock.Add("id", user.ID.ToString());
return returnBlock; return returnBlock;
} }
#endregion #endregion
#region RestBanMethods #region RestBanMethods
object BanCreate(RestVerbs verbs, IParameterCollection parameters) private object BanCreate(RestVerbs verbs, IParameterCollection parameters)
{ {
var returnBlock = new Dictionary<string, string>(); var returnBlock = new Dictionary<string, string>();
var ip = parameters["ip"]; var ip = parameters["ip"];
var name = parameters["name"]; var name = parameters["name"];
var reason = parameters["reason"]; var reason = parameters["reason"];
if (ip == null && name == null) if (ip == null && name == null)
{ {
returnBlock.Add("status", "400"); returnBlock.Add("status", "400");
returnBlock.Add("error", "Required parameters were missing from this API endpoint."); returnBlock.Add("error", "Required parameters were missing from this API endpoint.");
return returnBlock; return returnBlock;
} }
if (ip == null) if (ip == null)
{ {
ip = ""; ip = "";
} }
if (name == null) if (name == null)
{ {
name = ""; name = "";
} }
if (reason == null) if (reason == null)
{ {
reason = ""; reason = "";
} }
try try
{ {
TShock.Bans.AddBan(ip, name, reason); TShock.Bans.AddBan(ip, name, reason);
} }
catch (Exception) catch (Exception)
{ {
returnBlock.Add("status", "400"); returnBlock.Add("status", "400");
returnBlock.Add("error", "The specified ban was unable to be created."); returnBlock.Add("error", "The specified ban was unable to be created.");
return returnBlock; return returnBlock;
} }
returnBlock.Add("status", "200"); returnBlock.Add("status", "200");
returnBlock.Add("response", "Ban created successfully."); returnBlock.Add("response", "Ban created successfully.");
return returnBlock; return returnBlock;
} }
object BanDestroy(RestVerbs verbs, IParameterCollection parameters) private object BanDestroy(RestVerbs verbs, IParameterCollection parameters)
{ {
var returnBlock = new Dictionary<string, string>(); var returnBlock = new Dictionary<string, string>();
var type = parameters["type"]; var type = parameters["type"];
if (type == null) if (type == null)
{ {
returnBlock.Add("Error", "Invalid Type"); returnBlock.Add("Error", "Invalid Type");
return returnBlock; return returnBlock;
} }
var ban = new Ban(); var ban = new Ban();
if (type == "ip") ban = TShock.Bans.GetBanByIp(verbs["user"]); if (type == "ip") ban = TShock.Bans.GetBanByIp(verbs["user"]);
else if (type == "name") ban = TShock.Bans.GetBanByName(verbs["user"]); else if (type == "name") ban = TShock.Bans.GetBanByName(verbs["user"]);
else else
{ {
returnBlock.Add("Error", "Invalid Type"); returnBlock.Add("Error", "Invalid Type");
return returnBlock; return returnBlock;
} }
if (ban == null) if (ban == null)
{ {
return new Dictionary<string, string> { { "status", "400" }, { "error", "The specified ban does not exist." } }; return new Dictionary<string, string> {{"status", "400"}, {"error", "The specified ban does not exist."}};
} }
try try
{ {
TShock.Bans.RemoveBan(ban.IP); TShock.Bans.RemoveBan(ban.IP);
} }
catch (Exception) catch (Exception)
{ {
returnBlock.Add("status", "400"); returnBlock.Add("status", "400");
returnBlock.Add("error", "The specified ban was unable to be removed."); returnBlock.Add("error", "The specified ban was unable to be removed.");
return returnBlock; return returnBlock;
} }
returnBlock.Add("status", "200"); returnBlock.Add("status", "200");
returnBlock.Add("response", "Ban deleted successfully."); returnBlock.Add("response", "Ban deleted successfully.");
return returnBlock; return returnBlock;
} }
object BanInfo(RestVerbs verbs, IParameterCollection parameters) private object BanInfo(RestVerbs verbs, IParameterCollection parameters)
{ {
var returnBlock = new Dictionary<string, string>(); var returnBlock = new Dictionary<string, string>();
var type = parameters["type"]; var type = parameters["type"];
if (type == null) if (type == null)
{ {
returnBlock.Add("Error", "Invalid Type"); returnBlock.Add("Error", "Invalid Type");
return returnBlock; return returnBlock;
} }
var ban = new Ban(); var ban = new Ban();
if (type == "ip") ban = TShock.Bans.GetBanByIp(verbs["user"]); if (type == "ip") ban = TShock.Bans.GetBanByIp(verbs["user"]);
else if (type == "name") ban = TShock.Bans.GetBanByName(verbs["user"]); else if (type == "name") ban = TShock.Bans.GetBanByName(verbs["user"]);
else else
{ {
returnBlock.Add("Error", "Invalid Type"); returnBlock.Add("Error", "Invalid Type");
return returnBlock; return returnBlock;
} }
if (ban == null) if (ban == null)
{ {
return new Dictionary<string, string> { { "status", "400" }, { "error", "The specified ban does not exist." } }; return new Dictionary<string, string> {{"status", "400"}, {"error", "The specified ban does not exist."}};
} }
returnBlock.Add("status", "200"); returnBlock.Add("status", "200");
returnBlock.Add("name", ban.Name); returnBlock.Add("name", ban.Name);
returnBlock.Add("ip", ban.IP); returnBlock.Add("ip", ban.IP);
returnBlock.Add("reason", ban.Reason); returnBlock.Add("reason", ban.Reason);
return returnBlock; return returnBlock;
} }
#endregion #endregion
#region RestWorldMethods #region RestWorldMethods
object WorldRead(RestVerbs verbs, IParameterCollection parameters)
{
var returnBlock = new Dictionary<string, object>();
returnBlock.Add("status", "200");
returnBlock.Add("name", Main.worldName);
returnBlock.Add("size", Main.maxTilesX + "*" + Main.maxTilesY);
returnBlock.Add("time", Main.time);
returnBlock.Add("daytime", Main.dayTime);
returnBlock.Add("bloodmoon", Main.bloodMoon);
returnBlock.Add("invasionsize", Main.invasionSize);
return returnBlock;
}
object WorldMeteor(RestVerbs verbs, IParameterCollection parameters) private object WorldRead(RestVerbs verbs, IParameterCollection parameters)
{ {
WorldGen.dropMeteor(); var returnBlock = new Dictionary<string, object>();
var returnBlock = new Dictionary<string, string>(); returnBlock.Add("status", "200");
returnBlock.Add("status", "200"); returnBlock.Add("name", Main.worldName);
returnBlock.Add("response", "Meteor has been spawned."); returnBlock.Add("size", Main.maxTilesX + "*" + Main.maxTilesY);
return returnBlock; returnBlock.Add("time", Main.time);
} returnBlock.Add("daytime", Main.dayTime);
returnBlock.Add("bloodmoon", Main.bloodMoon);
returnBlock.Add("invasionsize", Main.invasionSize);
return returnBlock;
}
object WorldBloodmoon(RestVerbs verbs, IParameterCollection parameters) private object WorldMeteor(RestVerbs verbs, IParameterCollection parameters)
{ {
var returnBlock = new Dictionary<string, string>(); WorldGen.dropMeteor();
var bloodmoonVerb = verbs["bool"]; var returnBlock = new Dictionary<string, string>();
bool bloodmoon; returnBlock.Add("status", "200");
if (bloodmoonVerb == null) returnBlock.Add("response", "Meteor has been spawned.");
{ return returnBlock;
returnBlock.Add("status", "400"); }
returnBlock.Add("error", "No parameter was passed.");
return returnBlock;
}
if (!bool.TryParse(bloodmoonVerb, out bloodmoon))
{
returnBlock.Add("status", "400");
returnBlock.Add("error", "Unable to parse parameter.");
return returnBlock;
}
Main.bloodMoon = bloodmoon;
returnBlock.Add("status", "200");
returnBlock.Add("response", "Blood Moon has been set to " + bloodmoon);
return returnBlock;
}
#endregion
#region RestPlayerMethods private object WorldBloodmoon(RestVerbs verbs, IParameterCollection parameters)
object PlayerRead(RestVerbs verbs, IParameterCollection parameters) {
{ var returnBlock = new Dictionary<string, string>();
var returnBlock = new Dictionary<string, object>(); var bloodmoonVerb = verbs["bool"];
var playerParam = parameters["player"]; bool bloodmoon;
var found = TShock.Utils.FindPlayer(playerParam); if (bloodmoonVerb == null)
if (found.Count == 0) {
{ returnBlock.Add("status", "400");
returnBlock.Add("status", "400"); returnBlock.Add("error", "No parameter was passed.");
returnBlock.Add("error", "Name " + playerParam + " was not found"); return returnBlock;
} }
else if (found.Count > 1) if (!bool.TryParse(bloodmoonVerb, out bloodmoon))
{ {
returnBlock.Add("status", "400"); returnBlock.Add("status", "400");
returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players"); returnBlock.Add("error", "Unable to parse parameter.");
} return returnBlock;
else if (found.Count == 1) }
{ Main.bloodMoon = bloodmoon;
var player = found[0]; returnBlock.Add("status", "200");
returnBlock.Add("status", "200"); returnBlock.Add("response", "Blood Moon has been set to " + bloodmoon);
returnBlock.Add("nickname", player.Name); return returnBlock;
returnBlock.Add("username", player.UserAccountName == null ? "" : player.UserAccountName); }
returnBlock.Add("ip", player.IP);
returnBlock.Add("group", player.Group.Name);
returnBlock.Add("position", player.TileX + "," + player.TileY);
var activeItems = player.TPlayer.inventory.Where(p => p.active).ToList();
returnBlock.Add("inventory", string.Join(", ", activeItems.Select(p => p.name)));
returnBlock.Add("buffs", string.Join(", ", player.TPlayer.buffType));
}
return returnBlock;
}
object PlayerKick(RestVerbs verbs, IParameterCollection parameters)
{
var returnBlock = new Dictionary<string, object>();
var playerParam = parameters["player"];
var found = TShock.Utils.FindPlayer(playerParam);
var reason = verbs["reason"];
if (found.Count == 0)
{
returnBlock.Add("status", "400");
returnBlock.Add("error", "Name " + playerParam + " was not found");
}
else if (found.Count > 1)
{
returnBlock.Add("status", "400");
returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players");
}
else if (found.Count == 1)
{
var player = found[0];
TShock.Utils.ForceKick(player, reason == null ? "Kicked via web" : reason);
returnBlock.Add("status", "200");
returnBlock.Add("response", "Player " + player.Name + " was kicked");
}
return returnBlock;
}
object PlayerBan(RestVerbs verbs, IParameterCollection parameters)
{
var returnBlock = new Dictionary<string, object>();
var playerParam = parameters["player"];
var found = TShock.Utils.FindPlayer(playerParam);
var reason = verbs["reason"];
if (found.Count == 0)
{
returnBlock.Add("status", "400");
returnBlock.Add("error", "Name " + playerParam + " was not found");
}
else if (found.Count > 1)
{
returnBlock.Add("status", "400");
returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players");
}
else if (found.Count == 1)
{
var player = found[0];
TShock.Bans.AddBan(player.IP, player.Name, reason == null ? "Banned via web" : reason);
TShock.Utils.ForceKick(player, reason == null ? "Banned via web" : reason);
returnBlock.Add("status", "200");
returnBlock.Add("response", "Player " + player.Name + " was banned");
}
return returnBlock;
}
#endregion
#region RestExampleMethods #endregion
public void RegisterExamples() #region RestPlayerMethods
{
Rest.Register(new RestCommand("/HelloWorld/name/{username}", UserTest) { RequiresToken = false });
Rest.Register(new RestCommand("/wizard/{username}", Wizard) { RequiresToken = false });
}
//The Wizard example, for demonstrating the response convention: private object PlayerRead(RestVerbs verbs, IParameterCollection parameters)
object Wizard(RestVerbs verbs, IParameterCollection parameters) {
{ var returnBlock = new Dictionary<string, object>();
var returnBack = new Dictionary<string, string>(); var playerParam = parameters["player"];
returnBack.Add("status", "200"); //Keep this in everything, 200 = ok, etc. Standard http status codes. var found = TShock.Utils.FindPlayer(playerParam);
returnBack.Add("error", "(If this failed, you would have a different status code and provide the error object.)"); //And only include this if the status isn't 200 or a failure if (found.Count == 0)
returnBack.Add("Verified Wizard", "You're a wizard, " + verbs["username"]); //Outline any api calls and possible responses in some form of documentation somewhere {
return returnBack; returnBlock.Add("status", "400");
} returnBlock.Add("error", "Name " + playerParam + " was not found");
}
else if (found.Count > 1)
{
returnBlock.Add("status", "400");
returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players");
}
else if (found.Count == 1)
{
var player = found[0];
returnBlock.Add("status", "200");
returnBlock.Add("nickname", player.Name);
returnBlock.Add("username", player.UserAccountName == null ? "" : player.UserAccountName);
returnBlock.Add("ip", player.IP);
returnBlock.Add("group", player.Group.Name);
returnBlock.Add("position", player.TileX + "," + player.TileY);
var activeItems = player.TPlayer.inventory.Where(p => p.active).ToList();
returnBlock.Add("inventory", string.Join(", ", activeItems.Select(p => p.name)));
returnBlock.Add("buffs", string.Join(", ", player.TPlayer.buffType));
}
return returnBlock;
}
//http://127.0.0.1:8080/HelloWorld/name/{username}?type=status private object PlayerKick(RestVerbs verbs, IParameterCollection parameters)
object UserTest(RestVerbs verbs, IParameterCollection parameters) {
{ var returnBlock = new Dictionary<string, object>();
var ret = new Dictionary<string, string>(); var playerParam = parameters["player"];
var type = parameters["type"]; var found = TShock.Utils.FindPlayer(playerParam);
if (type == null) var reason = verbs["reason"];
{ if (found.Count == 0)
ret.Add("Error", "Invalid Type"); {
return ret; returnBlock.Add("status", "400");
} returnBlock.Add("error", "Name " + playerParam + " was not found");
if (type == "status") }
{ else if (found.Count > 1)
ret.Add("Users", "Info here"); {
return ret; returnBlock.Add("status", "400");
} returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players");
return null; }
} else if (found.Count == 1)
#endregion {
} var player = found[0];
} TShock.Utils.ForceKick(player, reason == null ? "Kicked via web" : reason);
returnBlock.Add("status", "200");
returnBlock.Add("response", "Player " + player.Name + " was kicked");
}
return returnBlock;
}
private object PlayerBan(RestVerbs verbs, IParameterCollection parameters)
{
var returnBlock = new Dictionary<string, object>();
var playerParam = parameters["player"];
var found = TShock.Utils.FindPlayer(playerParam);
var reason = verbs["reason"];
if (found.Count == 0)
{
returnBlock.Add("status", "400");
returnBlock.Add("error", "Name " + playerParam + " was not found");
}
else if (found.Count > 1)
{
returnBlock.Add("status", "400");
returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players");
}
else if (found.Count == 1)
{
var player = found[0];
TShock.Bans.AddBan(player.IP, player.Name, reason == null ? "Banned via web" : reason);
TShock.Utils.ForceKick(player, reason == null ? "Banned via web" : reason);
returnBlock.Add("status", "200");
returnBlock.Add("response", "Player " + player.Name + " was banned");
}
return returnBlock;
}
#endregion
#region RestExampleMethods
public void RegisterExamples()
{
Rest.Register(new RestCommand("/HelloWorld/name/{username}", UserTest) {RequiresToken = false});
Rest.Register(new RestCommand("/wizard/{username}", Wizard) {RequiresToken = false});
}
//The Wizard example, for demonstrating the response convention:
private object Wizard(RestVerbs verbs, IParameterCollection parameters)
{
var returnBack = new Dictionary<string, string>();
returnBack.Add("status", "200"); //Keep this in everything, 200 = ok, etc. Standard http status codes.
returnBack.Add("error", "(If this failed, you would have a different status code and provide the error object.)");
//And only include this if the status isn't 200 or a failure
returnBack.Add("Verified Wizard", "You're a wizard, " + verbs["username"]);
//Outline any api calls and possible responses in some form of documentation somewhere
return returnBack;
}
//http://127.0.0.1:8080/HelloWorld/name/{username}?type=status
private object UserTest(RestVerbs verbs, IParameterCollection parameters)
{
var ret = new Dictionary<string, string>();
var type = parameters["type"];
if (type == null)
{
ret.Add("Error", "Invalid Type");
return ret;
}
if (type == "status")
{
ret.Add("Users", "Info here");
return ret;
}
return null;
}
#endregion
}
}

View file

@ -3,61 +3,63 @@ using System.Collections.Generic;
namespace Rests namespace Rests
{ {
[Serializable] [Serializable]
public class RestObject : Dictionary<string, object> public class RestObject : Dictionary<string, object>
{ {
public string Status public string Status
{ {
get { return this["status"] as string; } get { return this["status"] as string; }
set { this["status"] = value; } set { this["status"] = value; }
} }
public string Error
{
get { return this["error"] as string; }
set { this["error"] = value; }
}
public string Response
{
get { return this["response"] as string; }
set { this["response"] = value; }
}
public RestObject(string status) public string Error
{ {
Status = status; get { return this["error"] as string; }
} set { this["error"] = value; }
}
/// <summary> public string Response
/// Gets value safely, if it does not exist, return null. Sets/Adds value safely, if null it will remove. {
/// </summary> get { return this["response"] as string; }
/// <param name="key"></param> set { this["response"] = value; }
/// <param name="value"></param> }
/// <returns>Returns null if key does not exist.</returns>
public new object this[string key] public RestObject(string status)
{ {
get Status = status;
{ }
object ret;
if (TryGetValue(key, out ret)) /// <summary>
return ret; /// Gets value safely, if it does not exist, return null. Sets/Adds value safely, if null it will remove.
return null; /// </summary>
} /// <param name="key"></param>
set /// <param name="value"></param>
{ /// <returns>Returns null if key does not exist.</returns>
if (!ContainsKey(key)) public new object this[string key]
{ {
if (value == null) get
return; {
Add(key, value); object ret;
} if (TryGetValue(key, out ret))
else return ret;
{ return null;
if (value != null) }
base[key] = value; set
else {
Remove(key); if (!ContainsKey(key))
} {
} if (value == null)
} return;
} Add(key, value);
}
else
{
if (value != null)
base[key] = value;
else
Remove(key);
}
}
}
}
} }

View file

@ -3,40 +3,40 @@ using System.Collections.Generic;
namespace Rests namespace Rests
{ {
[Serializable] [Serializable]
public class RestVerbs : Dictionary<string, string> public class RestVerbs : Dictionary<string, string>
{ {
/// <summary> /// <summary>
/// Gets value safely, if it does not exist, return null. Sets/Adds value safely, if null it will remove. /// Gets value safely, if it does not exist, return null. Sets/Adds value safely, if null it will remove.
/// </summary> /// </summary>
/// <param name="key"></param> /// <param name="key"></param>
/// <param name="value"></param> /// <param name="value"></param>
/// <returns>Returns null if key does not exist.</returns> /// <returns>Returns null if key does not exist.</returns>
public new string this[string key] public new string this[string key]
{ {
get get
{ {
string ret; string ret;
if (TryGetValue(key, out ret)) if (TryGetValue(key, out ret))
return ret; return ret;
return null; return null;
} }
set set
{ {
if (!ContainsKey(key)) if (!ContainsKey(key))
{ {
if (value == null) if (value == null)
return; return;
Add(key, value); Add(key, value);
} }
else else
{ {
if (value != null) if (value != null)
base[key] = value; base[key] = value;
else else
Remove(key); Remove(key);
} }
} }
} }
} }
} }

View file

@ -6,84 +6,96 @@ using HttpServer;
namespace Rests namespace Rests
{ {
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="username">Username to verify</param> /// <param name="username">Username to verify</param>
/// <param name="password">Password to verify</param> /// <param name="password">Password to verify</param>
/// <returns>Returning a restobject with a null error means a successful verification.</returns> /// <returns>Returning a restobject with a null error means a successful verification.</returns>
public delegate RestObject VerifyD(string username, string password); public delegate RestObject VerifyD(string username, string password);
public class SecureRest : Rest
{
public Dictionary<string, object> Tokens { get; protected set; }
public event VerifyD Verify;
public SecureRest(IPAddress ip, int port)
: base(ip, port)
{
Tokens = new Dictionary<string, object>();
Register(new RestCommand("/token/create/{username}/{password}", NewToken) { RequiresToken = false });
Register(new RestCommand("/token/destroy/{token}", DestroyToken) { RequiresToken = true });
}
object DestroyToken(RestVerbs verbs, IParameterCollection parameters) public class SecureRest : Rest
{ {
var token = verbs["token"]; public Dictionary<string, object> Tokens { get; protected set; }
try public event VerifyD Verify;
{
Tokens.Remove(token);
}
catch (Exception)
{
return new Dictionary<string, string> { { "status", "400" }, { "error", "The specified token queued for destruction failed to be deleted." } };
}
return new Dictionary<string, string> { { "status", "200" }, { "response", "Requested token was successfully destroyed." } };
}
object NewToken(RestVerbs verbs, IParameterCollection parameters) public SecureRest(IPAddress ip, int port)
{ : base(ip, port)
var user = verbs["username"]; {
var pass = verbs["password"]; Tokens = new Dictionary<string, object>();
Register(new RestCommand("/token/create/{username}/{password}", NewToken) {RequiresToken = false});
Register(new RestCommand("/token/destroy/{token}", DestroyToken) {RequiresToken = true});
}
RestObject obj = null; private object DestroyToken(RestVerbs verbs, IParameterCollection parameters)
if (Verify != null) {
obj = Verify(user, pass); var token = verbs["token"];
try
{
Tokens.Remove(token);
}
catch (Exception)
{
return new Dictionary<string, string>
{{"status", "400"}, {"error", "The specified token queued for destruction failed to be deleted."}};
}
return new Dictionary<string, string>
{{"status", "200"}, {"response", "Requested token was successfully destroyed."}};
}
if (obj == null) private object NewToken(RestVerbs verbs, IParameterCollection parameters)
obj = new RestObject("401") { Error = "Invalid username/password combination provided. Please re-submit your query with a correct pair." }; {
var user = verbs["username"];
var pass = verbs["password"];
if (obj.Error != null) RestObject obj = null;
return obj; if (Verify != null)
obj = Verify(user, pass);
string hash; if (obj == null)
var rand = new Random(); obj = new RestObject("401")
var randbytes = new byte[32]; {Error = "Invalid username/password combination provided. Please re-submit your query with a correct pair."};
do
{
rand.NextBytes(randbytes);
hash = randbytes.Aggregate("", (s, b) => s + b.ToString("X2"));
} while (Tokens.ContainsKey(hash));
Tokens.Add(hash, user); if (obj.Error != null)
return obj;
obj["token"] = hash; string hash;
return obj; var rand = new Random();
} var randbytes = new byte[32];
do
{
rand.NextBytes(randbytes);
hash = randbytes.Aggregate("", (s, b) => s + b.ToString("X2"));
} while (Tokens.ContainsKey(hash));
Tokens.Add(hash, user);
obj["token"] = hash;
return obj;
}
protected override object ExecuteCommand(RestCommand cmd, RestVerbs verbs, IParameterCollection parms)
{
if (cmd.RequiresToken)
{
var strtoken = parms["token"];
if (strtoken == null)
return new Dictionary<string, string>
{{"status", "401"}, {"error", "Not authorized. The specified API endpoint requires a token."}};
protected override object ExecuteCommand(RestCommand cmd, RestVerbs verbs, IParameterCollection parms) object token;
{ if (!Tokens.TryGetValue(strtoken, out token))
if (cmd.RequiresToken) return new Dictionary<string, string>
{ {
var strtoken = parms["token"]; {"status", "403"},
if (strtoken == null) {
return new Dictionary<string, string> { { "status", "401" }, { "error", "Not authorized. The specified API endpoint requires a token." } }; "error",
"Not authorized. The specified API endpoint requires a token, but the provided token was not valid."
object token; }
if (!Tokens.TryGetValue(strtoken, out token)) };
return new Dictionary<string, string> { { "status", "403" }, { "error", "Not authorized. The specified API endpoint requires a token, but the provided token was not valid." } }; }
} return base.ExecuteCommand(cmd, verbs, parms);
return base.ExecuteCommand(cmd, verbs, parms); }
} }
} }
}

View file

@ -8,13 +8,13 @@ namespace TShockAPI
{ {
public class StatTracker public class StatTracker
{ {
Utils Utils = TShock.Utils; private Utils Utils = TShock.Utils;
public DateTime lastcheck = DateTime.MinValue; public DateTime lastcheck = DateTime.MinValue;
readonly int checkinFrequency = 5; private readonly int checkinFrequency = 5;
public void checkin() public void checkin()
{ {
if ((DateTime.Now - lastcheck).TotalMinutes >= checkinFrequency) if ((DateTime.Now - lastcheck).TotalMinutes >= checkinFrequency)
{ {
ThreadPool.QueueUserWorkItem(callHome); ThreadPool.QueueUserWorkItem(callHome);
lastcheck = DateTime.Now; lastcheck = DateTime.Now;
@ -51,17 +51,23 @@ namespace TShockAPI
using (var client = new WebClient()) using (var client = new WebClient())
{ {
client.Headers.Add("user-agent", client.Headers.Add("user-agent",
"TShock (" + TShock.VersionNum + ")"); "TShock (" + TShock.VersionNum + ")");
try try
{ {
string response; string response;
if (TShock.Config.DisablePlayerCountReporting) if (TShock.Config.DisablePlayerCountReporting)
{ {
response = client.DownloadString("http://tshock.co/tickto.php?do=log&fp=" + fp + "&ver=" + TShock.VersionNum + "&os=" + Environment.OSVersion + "&mono=" + Main.runningMono + "&port=" + Netplay.serverPort + "&plcount=0"); response =
client.DownloadString("http://tshock.co/tickto.php?do=log&fp=" + fp + "&ver=" + TShock.VersionNum + "&os=" +
Environment.OSVersion + "&mono=" + Main.runningMono + "&port=" + Netplay.serverPort +
"&plcount=0");
} }
else else
{ {
response = client.DownloadString("http://tshock.co/tickto.php?do=log&fp=" + fp + "&ver=" + TShock.VersionNum + "&os=" + Environment.OSVersion + "&mono=" + Main.runningMono + "&port=" + Netplay.serverPort + "&plcount=" + TShock.Utils.ActivePlayers()); response =
client.DownloadString("http://tshock.co/tickto.php?do=log&fp=" + fp + "&ver=" + TShock.VersionNum + "&os=" +
Environment.OSVersion + "&mono=" + Main.runningMono + "&port=" + Netplay.serverPort +
"&plcount=" + TShock.Utils.ActivePlayers());
} }
Log.ConsoleInfo("Stat Tracker: " + response + "\n"); Log.ConsoleInfo("Stat Tracker: " + response + "\n");
} }
@ -72,4 +78,4 @@ namespace TShockAPI
} }
} }
} }
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -24,79 +24,80 @@ using Newtonsoft.Json;
namespace TShockAPI namespace TShockAPI
{ {
class UpdateManager internal class UpdateManager
{ {
static string updateUrl = "http://shankshock.com/tshock-update.json"; private static string updateUrl = "http://shankshock.com/tshock-update.json";
public static DateTime lastcheck = DateTime.MinValue; public static DateTime lastcheck = DateTime.MinValue;
/// <summary>
/// Check once every X minutes.
/// </summary>
static readonly int CheckXMinutes = 30;
public static void UpdateProcedureCheck() /// <summary>
{ /// Check once every X minutes.
if ((DateTime.Now - lastcheck).TotalMinutes >= CheckXMinutes) /// </summary>
{ private static readonly int CheckXMinutes = 30;
ThreadPool.QueueUserWorkItem(CheckUpdate);
lastcheck = DateTime.Now;
}
}
public static void CheckUpdate(object o) public static void UpdateProcedureCheck()
{ {
var updates = ServerIsOutOfDate(); if ((DateTime.Now - lastcheck).TotalMinutes >= CheckXMinutes)
if (updates != null) {
{ ThreadPool.QueueUserWorkItem(CheckUpdate);
NotifyAdministrators(updates); lastcheck = DateTime.Now;
} }
} }
/// <summary> public static void CheckUpdate(object o)
/// Checks to see if the server is out of date. {
/// </summary> var updates = ServerIsOutOfDate();
/// <returns></returns> if (updates != null)
private static Dictionary<string, string> ServerIsOutOfDate() {
{ NotifyAdministrators(updates);
using (var client = new WebClient()) }
{ }
client.Headers.Add("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.0.3705;)");
try
{
string updatejson = client.DownloadString(updateUrl);
var update = JsonConvert.DeserializeObject<Dictionary<string, string>>(updatejson);
var version = new Version(update["version"]);
if (TShock.VersionNum.CompareTo(version) < 0)
return update;
}
catch (Exception e)
{
Log.Error(e.ToString());
}
return null;
}
}
private static void NotifyAdministrators(Dictionary<string, string> update) /// <summary>
{ /// Checks to see if the server is out of date.
var changes = update["changes"].Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); /// </summary>
NotifyAdministrator(TSPlayer.Server, changes); /// <returns></returns>
foreach (TSPlayer player in TShock.Players) private static Dictionary<string, string> ServerIsOutOfDate()
{ {
if (player != null && player.Active && player.Group.HasPermission(Permissions.maintenance)) using (var client = new WebClient())
{ {
NotifyAdministrator(player, changes); client.Headers.Add("user-agent",
} "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.0.3705;)");
} try
} {
string updatejson = client.DownloadString(updateUrl);
var update = JsonConvert.DeserializeObject<Dictionary<string, string>>(updatejson);
var version = new Version(update["version"]);
if (TShock.VersionNum.CompareTo(version) < 0)
return update;
}
catch (Exception e)
{
Log.Error(e.ToString());
}
return null;
}
}
private static void NotifyAdministrator(TSPlayer player, string[] changes) private static void NotifyAdministrators(Dictionary<string, string> update)
{ {
player.SendMessage("The server is out of date.", Color.Red); var changes = update["changes"].Split(new[] {'\n'}, StringSplitOptions.RemoveEmptyEntries);
for (int j = 0; j < changes.Length; j++) NotifyAdministrator(TSPlayer.Server, changes);
{ foreach (TSPlayer player in TShock.Players)
player.SendMessage(changes[j], Color.Red); {
} if (player != null && player.Active && player.Group.HasPermission(Permissions.maintenance))
} {
} NotifyAdministrator(player, changes);
} }
}
}
private static void NotifyAdministrator(TSPlayer player, string[] changes)
{
player.SendMessage("The server is out of date.", Color.Red);
for (int j = 0; j < changes.Length; j++)
{
player.SendMessage(changes[j], Color.Red);
}
}
}
}

File diff suppressed because it is too large Load diff