diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..c1f89e94 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "TerrariaServerAPI"] + path = TerrariaServerAPI + url = https://github.com/Deathmax/TerrariaAPI-Server.git diff --git a/TShock.sln b/TShock.sln index da320f87..904e7dcd 100644 --- a/TShock.sln +++ b/TShock.sln @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\Unit EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TShockRestTestPlugin", "TShockRestTestPlugin\TShockRestTestPlugin.csproj", "{F2FEDAFB-58DE-4611-9168-A86112C346C7}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TerrariaAPI-Server", "TerrariaServerAPI\TerrariaAPI-Server.csproj", "{549A7941-D9C9-4544-BAC8-D9EEEAB4D777}" +EndProject Global GlobalSection(TestCaseManagementSettings) = postSolution CategoryFile = Terraria.vsmdi @@ -52,6 +54,18 @@ Global {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Release|Mixed Platforms.Build.0 = Release|Any CPU {F2FEDAFB-58DE-4611-9168-A86112C346C7}.Release|x86.ActiveCfg = Release|Any CPU + {549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Debug|Any CPU.Build.0 = Debug|Any CPU + {549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Debug|x86.ActiveCfg = Debug|x86 + {549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Debug|x86.Build.0 = Debug|x86 + {549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Release|Any CPU.ActiveCfg = Release|Any CPU + {549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Release|Any CPU.Build.0 = Release|Any CPU + {549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Release|x86.ActiveCfg = Release|x86 + {549A7941-D9C9-4544-BAC8-D9EEEAB4D777}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/TShockAPI/ConfigFile.cs b/TShockAPI/ConfigFile.cs index 6f66607e..f835105d 100755 --- a/TShockAPI/ConfigFile.cs +++ b/TShockAPI/ConfigFile.cs @@ -276,6 +276,8 @@ namespace TShockAPI [Description("Forces your world to be in Halloween mode regardless of the data.")] public bool ForceHalloween = false; + [Description("Allows anyone to break grass, pots, etc.")] public bool AllowCutTilesAndBreakables = false; + /// /// Reads a configuration file from a given path /// diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index a0159806..2747ee58 100755 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -716,13 +716,18 @@ namespace TShockAPI /// Y location of said chest /// public int Y { get; set; } + + /// + /// The player opening the chest + /// + public TSPlayer Player { get; set; } } /// /// ChestOpen - Called when any chest is opened /// public static HandlerList ChestOpen; - private static bool OnChestOpen(int x, int y) + private static bool OnChestOpen(int x, int y, TSPlayer player) { if (ChestOpen == null) return false; @@ -731,6 +736,7 @@ namespace TShockAPI { X = x, Y = y, + Player = player, }; ChestOpen.Invoke(null, args); return args.Handled; @@ -1608,7 +1614,7 @@ namespace TShockAPI /// /// Tiles that can be oriented (e.g., beds, chairs, bathtubs, etc). /// - private static byte[] orientableTiles = new byte[] { 15, 79, 90, 105, 128, 137, 139, 209 }; + private static byte[] orientableTiles = new byte[] { 15, 79, 90, 105, 128, 137, 139, 207, 209 }; private static bool HandleSendTileSquare(GetDataHandlerArgs args) { @@ -1738,7 +1744,14 @@ namespace TShockAPI KillTileNoItem, PlaceWire, KillWire, - PoundTile + PoundTile, + PlaceActuator, + KillActuator, + PlaceWire2, + KillWire2, + PlaceWire3, + KillWire3, + SlopeTile } public enum EditType { @@ -1751,6 +1764,14 @@ namespace TShockAPI /// Tiles that can be broken without any tools. /// private static byte[] breakableTiles = new byte[] { 4, 13, 33, 49, 50, 127, 128, 162 }; + /// + /// The maximum place styles for each tile. + /// + public static Dictionary MaxPlaceStyles = new Dictionary(); + /// + /// These projectiles create tiles on death. + /// + private static Dictionary projectileCreatesTile = new Dictionary {{42, 53}, {65, 112}, {68, 116}}; private static bool HandleTile(GetDataHandlerArgs args) { @@ -1848,6 +1869,7 @@ namespace TShockAPI } Item selectedItem = args.Player.SelectedItem; + int lastKilledProj = args.Player.LastKilledProjectile; if (action == EditAction.KillTile && !Main.tileCut[Main.tile[tileX, tileY].type] && !breakableTiles.Contains(Main.tile[tileX, tileY].type)) { // If the tile is an axe tile and they aren't selecting an axe, they're hacking. @@ -1878,17 +1900,19 @@ namespace TShockAPI return true; } } + else if (action == EditAction.PlaceTile && (projectileCreatesTile.ContainsKey(lastKilledProj) && editData == projectileCreatesTile[lastKilledProj])) + { + args.Player.LastKilledProjectile = 0; + } else if (action == EditAction.PlaceTile || action == EditAction.PlaceWall) { - if (action == EditAction.PlaceTile && TShock.Config.PreventInvalidPlaceStyle && ((editData == 4 && style > 11) || - (editData == 13 && style > 4) || (editData == 15 && style > 23) || (editData == 21 && style > 22) || - (editData == 82 && style > 5) || (editData == 91 && style > 108) || (editData == 105 && style > 49) || - (editData == 135 && style > 6) || (editData == 139 && style > 27) || (editData == 144 && style > 2) || - (editData == 149 && style > 2) || (editData == 137 && style > 4) || (editData == 79 && style > 12))) + if (action == EditAction.PlaceTile && TShock.Config.PreventInvalidPlaceStyle && + MaxPlaceStyles.ContainsKey(editData) && style > MaxPlaceStyles[editData]) { args.Player.SendTileSquare(tileX, tileY, 4); return true; } + // If they aren't selecting the item which creates the tile or wall, they're hacking. if ((editData != 127 && editData != 213) && editData != (action == EditAction.PlaceTile ? selectedItem.createTile : selectedItem.createWall)) { @@ -1922,24 +1946,48 @@ namespace TShockAPI } } } - else if (action == EditAction.PlaceWire) + else if (action == EditAction.PlaceWire || action == EditAction.PlaceWire2 || action == EditAction.PlaceWire3) { - // If they aren't selecting the wrench, they're hacking. - if (args.TPlayer.inventory[args.TPlayer.selectedItem].type != 509) + // If they aren't selecting a wrench, they're hacking. + if (selectedItem.type != 509 && selectedItem.type != 850 && selectedItem.type != 851) { args.Player.SendTileSquare(tileX, tileY, 1); return true; } } - else if (action == EditAction.KillWire) + else if (action == EditAction.KillActuator || action == EditAction.KillWire || + action == EditAction.KillWire2 || action == EditAction.KillWire3) { // If they aren't selecting the wire cutter, they're hacking. - if (args.TPlayer.inventory[args.TPlayer.selectedItem].type != 510) + if (selectedItem.type != 510) { args.Player.SendTileSquare(tileX, tileY, 1); return true; } } + else if (action == EditAction.PlaceActuator) + { + // If they aren't selecting the actuator, they're hacking. + if (selectedItem.type != 849) + { + args.Player.SendTileSquare(tileX, tileY, 1); + return true; + } + } + else if (action == EditAction.PoundTile || action == EditAction.SlopeTile) + { + // If they aren't selecting a hammer, they're hacking. + if (selectedItem.hammer == 0) + { + args.Player.SendTileSquare(tileX, tileY, 1); + return true; + } + } + + if (TShock.Config.AllowCutTilesAndBreakables && (Main.tileCut[Main.tile[tileX, tileY].type] || breakableTiles.Contains(Main.tile[tileX, tileY].type))) + { + return false; + } if (TShock.CheckIgnores(args.Player)) { @@ -2490,6 +2538,8 @@ namespace TShockAPI return true; } + args.Player.LastKilledProjectile = type; + return false; } @@ -2754,7 +2804,7 @@ namespace TShockAPI var x = args.Data.ReadInt32(); var y = args.Data.ReadInt32(); - if (OnChestOpen(x, y)) + if (OnChestOpen(x, y, args.Player)) return true; if (TShock.CheckIgnores(args.Player)) @@ -2783,6 +2833,12 @@ namespace TShockAPI args.Player.ActiveChest = id; + if (TShock.CheckTilePermission(args.Player, x, y) && TShock.Config.RegionProtectChests) + { + args.Player.SendData(PacketTypes.ChestOpen, "", -1); + return true; + } + return false; } diff --git a/TShockAPI/Properties/AssemblyInfo.cs b/TShockAPI/Properties/AssemblyInfo.cs index e2731e33..feea2a19 100644 --- a/TShockAPI/Properties/AssemblyInfo.cs +++ b/TShockAPI/Properties/AssemblyInfo.cs @@ -49,5 +49,5 @@ using System.Runtime.InteropServices; // Build Number // MMdd of the build -[assembly: AssemblyVersion("4.2.0.1019")] -[assembly: AssemblyFileVersion("4.2.0.1019")] +[assembly: AssemblyVersion("4.2.1.1110")] +[assembly: AssemblyFileVersion("4.2.1.1110")] diff --git a/TShockAPI/StatTracker.cs b/TShockAPI/StatTracker.cs new file mode 100644 index 00000000..a3381bf7 --- /dev/null +++ b/TShockAPI/StatTracker.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.Threading; +using System.IO; +using System.Web; + +namespace TShockAPI +{ + public class StatTracker + { + private bool failed; + private bool initialized; + public StatTracker() + { + + } + + public void Initialize() + { + if (!initialized) + { + initialized = true; + ThreadPool.QueueUserWorkItem(SendUpdate); + } + } + + private HttpWebResponse GetResponseNoException(HttpWebRequest req) + { + try + { + return (HttpWebResponse)req.GetResponse(); + } + catch (WebException we) + { + var resp = we.Response as HttpWebResponse; + if (resp == null) + throw; + return resp; + } + } + + private void SendUpdate(object info) + { + Thread.Sleep(1000*60*15); + var data = new JsonData + { + port = Terraria.Netplay.serverPort, + currentPlayers = TShock.Utils.ActivePlayers(), + maxPlayers = TShock.Config.MaxSlots, + systemRam = 0, + systemCPUClock = 0, + version = TShock.VersionNum.ToString(), + terrariaVersion = Terraria.Main.versionNumber2, + mono = Terraria.Main.runningMono + }; + + var serialized = Newtonsoft.Json.JsonConvert.SerializeObject(data); + var encoded = HttpUtility.UrlEncode(serialized); + var uri = String.Format("http://96.47.231.227:8000?data={0}", encoded); + var client = (HttpWebRequest)WebRequest.Create(uri); + client.Timeout = 5000; + try + { + using (var resp = GetResponseNoException(client)) + { + if (resp.StatusCode != HttpStatusCode.OK) + { + throw new IOException("Server did not respond with an OK."); + } + + failed = false; + } + } + catch (Exception e) + { + if (!failed) + { + Log.ConsoleError("StatTracker Exception: {0}", e); + failed = true; + } + } + + ThreadPool.QueueUserWorkItem(SendUpdate); + } + } + + public struct JsonData + { + public int port; + public int currentPlayers; + public int maxPlayers; + public int systemRam; + public int systemCPUClock; + public string version; + public string terrariaVersion; + public bool mono; + } +} diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index b45e4c79..4eac6a2d 100755 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -308,6 +308,11 @@ namespace TShockAPI /// Players controls are inverted if using SSC /// public bool Confused = false; + + /// + /// The last projectile type this player tried to kill. + /// + public int LastKilledProjectile = 0; /// /// Whether the player is a real, human, player on the server. diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index 686b4072..105ca044 100755 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -72,6 +72,7 @@ namespace TShockAPI public static SecureRest RestApi; public static RestManager RestManager; public static Utils Utils = Utils.Instance; + public static StatTracker StatTracker = new StatTracker(); /// /// Used for implementing REST Tokens prior to the REST system starting up. /// @@ -611,14 +612,35 @@ namespace TShockAPI { AuthToken = 0; } - - Regions.Reload(); + + Regions.Reload(); Warps.ReloadWarps(); Lighting.lightMode = 2; + ComputeMaxStyles(); FixChestStacks(); + + StatTracker.Initialize(); } + private void ComputeMaxStyles() + { + var item = new Item(); + for (int i = 0; i < Main.maxItemTypes; i++) + { + item.netDefaults(i); + if (item.placeStyle > 0) + { + if (GetDataHandlers.MaxPlaceStyles.ContainsKey(item.createTile)) + { + if (item.placeStyle > GetDataHandlers.MaxPlaceStyles[item.createTile]) + GetDataHandlers.MaxPlaceStyles[item.createTile] = item.placeStyle; + } + else + GetDataHandlers.MaxPlaceStyles.Add(item.createTile, item.placeStyle); + } + } + } private void FixChestStacks() { if (Config.IgnoreChestStacksOnLoad) diff --git a/TShockAPI/TShockAPI.csproj b/TShockAPI/TShockAPI.csproj index a2a54944..3ebc9d93 100644 --- a/TShockAPI/TShockAPI.csproj +++ b/TShockAPI/TShockAPI.csproj @@ -68,11 +68,7 @@ - - False - .exe - ..\TerrariaServerBins\TerrariaServer.exe - + @@ -126,6 +122,7 @@ + @@ -165,7 +162,12 @@ true - + + + {549A7941-D9C9-4544-BAC8-D9EEEAB4D777} + TerrariaAPI-Server + + diff --git a/TShockAPI/Utils.cs b/TShockAPI/Utils.cs index 2e3c51fc..135604f0 100644 --- a/TShockAPI/Utils.cs +++ b/TShockAPI/Utils.cs @@ -24,6 +24,7 @@ using System.Net; using System.Net.Sockets; using System.Security.Cryptography; using System.Text; +using System.Text.RegularExpressions; using Terraria; using TShockAPI.DB; @@ -717,7 +718,7 @@ namespace TShockAPI /// /// Shows a file to the user. /// - /// int player + /// TSPlayer player /// string filename reletave to savedir public void ShowFileToUser(TSPlayer player, string file) { @@ -726,31 +727,28 @@ namespace TShockAPI { while ((foo = tr.ReadLine()) != null) { + if (string.IsNullOrWhiteSpace(foo)) + { + continue; + } + foo = foo.Replace("%map%", Main.worldName); foo = foo.Replace("%players%", GetPlayers()); - //foo = SanitizeString(foo); - if (foo.Substring(0, 1) == "%" && foo.Substring(12, 1) == "%") //Look for a beginning color code. + Regex reg = new Regex("%\\s*(?\\d{1,3})\\s*,\\s*(?\\d{1,3})\\s*,\\s*(?\\d{1,3})\\s*%"); + var matches = reg.Matches(foo); + Color c = Color.White; + foreach (Match match in matches) { - string possibleColor = foo.Substring(0, 13); - foo = foo.Remove(0, 13); - float[] pC = {0, 0, 0}; - possibleColor = possibleColor.Replace("%", ""); - string[] pCc = possibleColor.Split(','); - if (pCc.Length == 3) + byte r, g, b; + if (byte.TryParse(match.Groups["r"].Value, out r) && + byte.TryParse(match.Groups["g"].Value, out g) && + byte.TryParse(match.Groups["b"].Value, out b)) { - try - { - player.SendMessage(foo, (byte) Convert.ToInt32(pCc[0]), (byte) Convert.ToInt32(pCc[1]), - (byte) Convert.ToInt32(pCc[2])); - continue; - } - catch (Exception e) - { - Log.Error(e.ToString()); - } + c = new Color(r, g, b); } + foo = foo.Remove(match.Index, match.Length); } - player.SendMessage(foo); + player.SendMessage(foo, c); } } } diff --git a/TerrariaServerAPI b/TerrariaServerAPI new file mode 160000 index 00000000..92aeb654 --- /dev/null +++ b/TerrariaServerAPI @@ -0,0 +1 @@ +Subproject commit 92aeb65461a164c5a715dd72e8fc7a6e3dfbb24b diff --git a/TerrariaServerBins/TerrariaServer.exe b/TerrariaServerBins/TerrariaServer.exe deleted file mode 100644 index f4cb8f2c..00000000 Binary files a/TerrariaServerBins/TerrariaServer.exe and /dev/null differ diff --git a/create_release.py b/create_release.py index 81493519..fa2e05e0 100644 --- a/create_release.py +++ b/create_release.py @@ -18,7 +18,8 @@ http_bin_name = "HttpServer.dll" tshock_bin_name = "TShockAPI.dll" tshock_symbols = "TShockAPI.dll.mdb" -terraria_bin = os.path.join(cur_wd, "TerrariaServerBins", terraria_bin_name) +terraria_release_bin = os.path.join(cur_wd, "TerrariaServerAPI", "bin", "Release", terraria_bin_name) +terraria_debug_bin = os.path.join(cur_wd, "TerrariaServerAPI", "bin", "Debug", terraria_bin_name) sql_dep = os.path.join(cur_wd, "SqlBins") http_bin = os.path.join(cur_wd, "HttpBins", http_bin_name) json_bin = os.path.join(cur_wd, "TShockAPI", json_bin_name) @@ -38,10 +39,12 @@ def copy_dependencies(): shutil.copy(os.path.join(sql_dep, f), release_dir) def copy_debug_files(): + shutil.copy(terraria_debug_bin, release_dir) shutil.copy(os.path.join(debug_folder, tshock_bin_name), release_dir) shutil.copy(os.path.join(debug_folder, tshock_symbols), release_dir) def copy_release_files(): + shutil.copy(terraria_release_bin, release_dir) shutil.copy(release_bin, release_dir) shutil.copy(release_bin, release_dir) @@ -62,6 +65,7 @@ def package_release(): zip.write(tshock_bin_name, os.path.join("ServerPlugins", tshock_bin_name)) zip.close() os.remove(tshock_bin_name) + os.remove(terraria_bin_name) os.chdir(cur_wd) def package_debug(): @@ -72,11 +76,11 @@ def package_debug(): zip.close() os.remove(tshock_bin_name) os.remove(tshock_symbols) + os.remove(terraria_bin_name) os.chdir(cur_wd) def delete_files(): os.chdir(release_dir) - os.remove(terraria_bin_name) for f in sql_bins_names: os.remove(f) os.remove(sqlite_dep) @@ -84,23 +88,23 @@ def delete_files(): os.remove(http_bin_name) os.chdir(cur_wd) -def update_terraria_exe(): - url = urllib2.urlopen('http://direct.tshock.co:8085/browse/TERRA-TSAPI/latestSuccessful/artifact/JOB1/Server-Bin/TerrariaServer.exe') - localFile = open('TerrariaServer.exe', 'w') - localFile.write(url.read()) - localFile.close() - shutil.copy(terraria_bin_name, terraria_bin) - os.remove(terraria_bin_name) +def update_terraria_source(): + subprocess.check_call('/usr/local/bin/git submodule init') + subprocess.check_call('/usr/local/bin/git submodule update') def build_software(): release_proc = subprocess.Popen(['/usr/local/bin/xbuild', './TShockAPI/TShockAPI.csproj', '/p:Configuration=Release']) debug_proc = subprocess.Popen(['/usr/local/bin/xbuild', './TShockAPI/TShockAPI.csproj', '/p:Configuration=Debug']) release_proc.wait() debug_proc.wait() - + if (release_proc.returncode != 0): + raise CalledProcessError(release_proc.returncode) + if (debug_proc.returncode != 0): + raise CalledProcessError(debug_proc.returncode) + if __name__ == '__main__': create_release_folder() - update_terraria_exe() + update_terraria_source() copy_dependencies() build_software() package_release()