From 59f9440d174504b69ec81f3368c1b501f0148fb4 Mon Sep 17 00:00:00 2001 From: SGKoishi Date: Mon, 12 Dec 2022 17:15:34 -0800 Subject: [PATCH 001/135] Use same TSPlayer.Active check --- TShockAPI/Bouncer.cs | 6 +++--- TShockAPI/Commands.cs | 8 ++++---- TShockAPI/Rest/RestManager.cs | 6 +++--- TShockAPI/Utils.cs | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index affa1609..e86501b1 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -1878,7 +1878,7 @@ namespace TShockAPI return; } - if (TShock.Players[id] == null) + if (TShock.Players[id] == null || !TShock.Players[id].Active) { TShock.Log.ConsoleDebug(GetString( "Bouncer / OnPlayerBuff rejected {0} ({1}) applying buff {2} to {3} for {4} ticks: target is null", args.Player.Name, @@ -2081,7 +2081,7 @@ namespace TShockAPI short amount = args.Amount; byte plr = args.TargetPlayerIndex; - if (amount <= 0 || Main.player[plr] == null || !Main.player[plr].active) + if (amount <= 0 || TShock.Players[plr] == null || !TShock.Players[plr].Active) { TShock.Log.ConsoleDebug(GetString("Bouncer / OnHealOtherPlayer rejected null checks")); args.Handled = true; @@ -2589,7 +2589,7 @@ namespace TShockAPI byte direction = args.Direction; PlayerDeathReason reason = args.PlayerDeathReason; - if (id >= Main.maxPlayers || TShock.Players[id] == null) + if (id >= Main.maxPlayers || TShock.Players[id] == null || !TShock.Players[id].Active) { TShock.Log.ConsoleDebug(GetString("Bouncer / OnPlayerDamage rejected null check")); args.Handled = true; diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs index 2214f252..74fa4f42 100644 --- a/TShockAPI/Commands.cs +++ b/TShockAPI/Commands.cs @@ -3070,12 +3070,12 @@ namespace TShockAPI args.Player.SendErrorMessage(GetString("You do not have permission to teleport all other players.")); return; } - for (int i = 0; i < Main.maxPlayers; i++) + foreach (var player in TShock.Players) { - if (Main.player[i].active && (Main.player[i] != args.TPlayer)) + if (player != null && player.Active && player.Index != args.Player.Index) { - if (TShock.Players[i].Teleport(args.TPlayer.position.X, args.TPlayer.position.Y)) - TShock.Players[i].SendSuccessMessage(GetString("You were teleported to {0}.", args.Player.Name)); + if (player.Teleport(args.TPlayer.position.X, args.TPlayer.position.Y)) + player.SendSuccessMessage(GetString("You were teleported to {0}.", args.Player.Name)); } } args.Player.SendSuccessMessage(GetString("Teleported everyone to yourself.")); diff --git a/TShockAPI/Rest/RestManager.cs b/TShockAPI/Rest/RestManager.cs index c41e7767..b6efd6c5 100644 --- a/TShockAPI/Rest/RestManager.cs +++ b/TShockAPI/Rest/RestManager.cs @@ -402,7 +402,7 @@ namespace TShockAPI {"serverversion", Main.versionNumber}, {"tshockversion", TShock.VersionNum}, {"port", TShock.Config.Settings.ServerPort}, - {"playercount", Main.player.Where(p => null != p && p.active).Count()}, + {"playercount", TShock.Utils.GetActivePlayerCount()}, {"maxplayers", TShock.Config.Settings.MaxSlots}, {"world", (TShock.Config.Settings.UseServerName ? TShock.Config.Settings.ServerName : Main.worldName)}, {"uptime", (DateTime.Now - System.Diagnostics.Process.GetCurrentProcess().StartTime).ToString(@"d'.'hh':'mm':'ss")}, @@ -944,8 +944,8 @@ namespace TShockAPI [Token] private object PlayerList(RestRequestArgs args) { - var activeplayers = Main.player.Where(p => null != p && p.active).ToList(); - return new RestObject() { { "players", string.Join(", ", activeplayers.Select(p => p.name)) } }; + var activeplayers = TShock.Players.Where(p => null != p && p.Active).Select(p => p.Name); + return new RestObject() { { "players", string.Join(", ", activeplayers) } }; } [Description("Fetches detailed user information on all connected users, and can be filtered by specifying a key value pair filter users where the key is a field and the value is a users field value.")] diff --git a/TShockAPI/Utils.cs b/TShockAPI/Utils.cs index efe17130..a7291e73 100644 --- a/TShockAPI/Utils.cs +++ b/TShockAPI/Utils.cs @@ -172,7 +172,7 @@ namespace TShockAPI foreach (TSPlayer player in TShock.Players) { if (player != null && player != excludedPlayer && player.Active && player.HasPermission(Permissions.logs) && - player.DisplayLogs && TShock.Config.Settings.DisableSpewLogs == false) + player.DisplayLogs && !TShock.Config.Settings.DisableSpewLogs) player.SendMessage(log, color); } } @@ -183,7 +183,7 @@ namespace TShockAPI /// The number of active players on the server. public int GetActivePlayerCount() { - return Main.player.Where(p => null != p && p.active).Count(); + return TShock.Players.Count(p => null != p && p.Active); } //Random should not be generated in a method From 8b57f321250a03a71853e09589c1e0ce8977c02e Mon Sep 17 00:00:00 2001 From: James Puleo Date: Tue, 20 Dec 2022 21:36:31 -0500 Subject: [PATCH 002/135] Detect invalid TShock installations There are two common mistakes made by those installing/updating TShock: - Extracting TShock into the Terraria client directory - Extracting TShock 5 or newer into a legacy (TShock 4 or older) install By checking for the existence of a file named `TerrariaServer.exe`, we can potentially detect these invalid installations, and prompt the user with a more useful diagnostic, rather than (likely) crashing moments later. --- TShockLauncher/Program.cs | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/TShockLauncher/Program.cs b/TShockLauncher/Program.cs index 4a139370..0163cc2d 100644 --- a/TShockLauncher/Program.cs +++ b/TShockLauncher/Program.cs @@ -29,12 +29,30 @@ along with this program. If not, see . using System.Reflection; using TShockPluginManager; +// On occasion, users have been seen extracting TShock into their client installation directory -- this is of course incorrect, and is known +// to cause issues. Let's attempt to catch this before anything happens (specifically, before Terraria assemblies are resolved) and prevent +// TShock from launching. +if (File.Exists("TerrariaServer.exe")) +{ + Console.ForegroundColor = ConsoleColor.Red; + Console.Error.WriteLine("A \"TerrariaServer.exe\" file has been found in the current working directory."); + Console.Error.WriteLine( + "This indicates either installation into a Terraria client directory, or installation into a legacy (TShock 4 or older) TShock directory."); + Console.Error.WriteLine( + "TShock is never to be installed inside a Terraria client directory. You should instead extract your TShock installation into it's own directory."); + Console.Error.WriteLine( + "If you are updating a legacy TShock installation, please follow the following documentation to update: https://ikebukuro.tshock.co/#/?id=upgrading-from-tshock-4"); + Console.Error.WriteLine("The launcher will now exit."); + Console.ResetColor(); + return 1; +} + if (args.Length > 0 && args[0].ToLower() == "plugins") { var items = args.ToList(); items.RemoveAt(0); await NugetCLI.Main(items); - return; + return 0; } @@ -42,7 +60,7 @@ Dictionary _cache = new Dictionary(); System.Runtime.Loader.AssemblyLoadContext.Default.Resolving += Default_Resolving; -Start(); +return Start(); /// /// Resolves a module from the ./bin folder, either with a .dll by preference or .exe @@ -70,7 +88,8 @@ Assembly? Default_Resolving(System.Runtime.Loader.AssemblyLoadContext arg1, Asse /// Initiates the TSAPI server. /// /// This method exists so that the resolver can attach before TSAPI needs its dependencies. -void Start() +int Start() { TerrariaApi.Server.Program.Main(args); + return 0; } From 3776baaf129eb03f16d61a3f16072a0e348e4ef7 Mon Sep 17 00:00:00 2001 From: James Puleo Date: Tue, 20 Dec 2022 21:42:50 -0500 Subject: [PATCH 003/135] Update `docs/changelog.md` --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index f43f9651..9295ece7 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -105,6 +105,8 @@ Use past tense when adding new entries; sign your name off when you add or chang * Initialized achievements and the `AchievementManager` on the server. This ensures that players cannot cause exceptions to be thrown, chat messages are always logged, and allows achievement names to be localized in the console. Also added a test case for this. (@drunderscore) * Allowed multiple test cases to be in TShock's test suite. (@drunderscore) * Fixed unable to use Purification/Evil Powder in jungle. (@sgkoishi) +* Detected invalid installations, by checking for a file named `TerrariaServer.exe`. (@drunderscore) + * This made the two most common installation mistakes (extracting into the Terraria client directory, and extracting TShock 5 or newer into a TShock 4 or older install) prompt the user with a more useful diagnostic, rather than (likely) crashing moments later. ## TShock 5.1.3 * Added support for Terraria 1.4.4.9 via OTAPI 3.1.20. (@SignatureBeef) From 2f7a8072019faa9fe7e66cf301dd6df8e74ceccc Mon Sep 17 00:00:00 2001 From: hufang360 Date: Mon, 9 Jan 2023 10:56:17 +0800 Subject: [PATCH 004/135] Update Utils.cs Item stack can be 9999. --- TShockAPI/Utils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TShockAPI/Utils.cs b/TShockAPI/Utils.cs index efe17130..4ee41c01 100644 --- a/TShockAPI/Utils.cs +++ b/TShockAPI/Utils.cs @@ -330,7 +330,7 @@ namespace TShockAPI /// The item represented by the tag. public Item GetItemFromTag(string tag) { - Regex regex = new Regex(@"\[i(tem)?(?:\/s(?\d{1,3}))?(?:\/p(?\d{1,3}))?:(?-?\d{1,4})\]"); + Regex regex = new Regex(@"\[i(tem)?(?:\/s(?\d{1,4}))?(?:\/p(?\d{1,3}))?:(?-?\d{1,4})\]"); Match match = regex.Match(tag); if (!match.Success) return null; From fc4c50928553e277ca9542c8f8a13a31116e83b6 Mon Sep 17 00:00:00 2001 From: Stargazing Koishi Date: Wed, 11 Jan 2023 02:23:01 -0800 Subject: [PATCH 005/135] Add a check to InsertPlayerData to prevent data overwrite --- TShockAPI/DB/CharacterManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TShockAPI/DB/CharacterManager.cs b/TShockAPI/DB/CharacterManager.cs index 575ac3dc..5a5e13a6 100644 --- a/TShockAPI/DB/CharacterManager.cs +++ b/TShockAPI/DB/CharacterManager.cs @@ -189,6 +189,9 @@ namespace TShockAPI.DB if (!player.IsLoggedIn) return false; + if (player.State < 10) + return false; + if (player.HasPermission(Permissions.bypassssc) && !fromCommand) { TShock.Log.ConsoleInfo(GetParticularString("{0} is a player name", $"Skipping SSC save (due to tshock.ignore.ssc) for {player.Account.Name}")); From f7ea4719683718188a2f7ec0e0b7c09793df01f8 Mon Sep 17 00:00:00 2001 From: James Puleo Date: Sun, 29 Jan 2023 20:47:19 -0500 Subject: [PATCH 006/135] Set the `GetDataHandledEventArgs.Player` property for pick sync handler --- TShockAPI/GetDataHandlers.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index 9ce1a5a2..1ef2f4f8 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -2248,6 +2248,7 @@ namespace TShockAPI var args = new SyncTilePickingEventArgs { + Player = player, PlayerIndex = playerIndex, TileX = tileX, TileY = tileY, From 3f4dd7b4b50aae381ea0a720d6481d0f1e7ade57 Mon Sep 17 00:00:00 2001 From: James Puleo Date: Sun, 29 Jan 2023 20:52:37 -0500 Subject: [PATCH 007/135] Update `docs/changelog.md` --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index f43f9651..be96f88c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -105,6 +105,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Initialized achievements and the `AchievementManager` on the server. This ensures that players cannot cause exceptions to be thrown, chat messages are always logged, and allows achievement names to be localized in the console. Also added a test case for this. (@drunderscore) * Allowed multiple test cases to be in TShock's test suite. (@drunderscore) * Fixed unable to use Purification/Evil Powder in jungle. (@sgkoishi) +* Set the `GetDataHandledEventArgs.Player` property for the `SyncTilePicking` data handler. (@drunderscore) ## TShock 5.1.3 * Added support for Terraria 1.4.4.9 via OTAPI 3.1.20. (@SignatureBeef) From 510d696f16ae30cc461eb72d26afbef4c9a05146 Mon Sep 17 00:00:00 2001 From: Cardinal System Date: Sun, 5 Feb 2023 14:07:24 -0800 Subject: [PATCH 008/135] New Crowdin updates (#2886) * New translations template.pot (Portuguese, Brazilian) [skip actions] * New translations template.pot (Portuguese, Brazilian) [skip actions] * New translations template.pot (Chinese Simplified) [skip actions] * New translations template.pot (Chinese Simplified) [skip actions] * New translations template.pot (Chinese Simplified) [skip actions] * New translations template.pot (Chinese Simplified) [skip actions] * New translations template.pot (Spanish) [skip actions] * New translations template.pot (Chinese Simplified) [skip actions] * New translations template.pot (Spanish) [skip actions] * New translations template.pot (Spanish) [skip actions] * New translations template.pot (Spanish) [skip actions] * New translations template.pot (Spanish) [skip actions] * New translations template.pot (Spanish) [skip actions] * New translations template.pot (Spanish) [skip actions] * New translations template.pot (Spanish) [skip actions] * New translations template.pot (Spanish) [skip actions] * New translations template.pot (Spanish) [skip actions] * New translations template.pot (Turkish) [skip actions] * New translations template.pot (Spanish) [skip actions] * New translations template.pot (Spanish) [skip actions] * New translations template.pot (Spanish) [skip actions] * New translations template.pot (Spanish) [skip actions] * New translations template.pot (Spanish) [skip actions] * New translations template.pot (Spanish) [skip actions] * New translations template.pot (Spanish) [skip actions] --- i18n/es_ES/TShockAPI.po | 1847 ++++++++++++++++++++------------------- i18n/pt_BR/TShockAPI.po | 64 +- i18n/tr_TR/TShockAPI.po | 16 +- i18n/zh_CN/TShockAPI.po | 70 +- 4 files changed, 1000 insertions(+), 997 deletions(-) diff --git a/i18n/es_ES/TShockAPI.po b/i18n/es_ES/TShockAPI.po index 5898f6f1..3fc0368f 100644 --- a/i18n/es_ES/TShockAPI.po +++ b/i18n/es_ES/TShockAPI.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: tshock\n" "POT-Creation-Date: 2022-12-06 05:43:49+0000\n" -"PO-Revision-Date: 2022-12-06 05:52\n" +"PO-Revision-Date: 2023-01-18 17:58\n" "Last-Translator: \n" "Language-Team: Spanish\n" "MIME-Version: 1.0\n" @@ -38,19 +38,19 @@ msgstr "#{0} - Estás baneado: {1}" #: ../../TShockAPI/Commands.cs:6499 msgid " 'basic', 'sakura', 'willow', 'boreal', 'mahogany', 'ebonwood', 'shadewood', 'pearlwood'." -msgstr " 'básico', 'cerezo', 'sauce', 'boreal', 'caoba', 'madera de ébano', 'madera oscura', 'madera de perla'." +msgstr " 'basic', 'sakura', 'willow', 'boreal', 'mahogany', 'ebonwood', 'shadewood', 'pearlwood'." #: ../../TShockAPI/Commands.cs:6505 msgid " 'cactus', 'herb', 'mushroom'." -msgstr " 'cactus', 'hierba', 'hongo'." +msgstr " 'cactus', 'herb', 'mushroom'." #: ../../TShockAPI/Commands.cs:6501 msgid " 'palm', 'corruptpalm', 'crimsonpalm', 'hallowpalm'." -msgstr " 'palma', 'palma corrupta, 'palma carmesí', 'palma santa'." +msgstr " 'palm', 'corruptpalm', 'crimsonpalm', 'hallowpalm'." #: ../../TShockAPI/Commands.cs:6503 msgid " 'topaz', 'amethyst', 'sapphire', 'emerald', 'ruby', 'diamond', 'amber'." -msgstr " 'topacio', 'amatista', 'zafiro', 'esmeralda', 'rubí', 'diamante', 'ámbar'." +msgstr " 'topaz', 'amethyst', 'sapphire', 'emerald', 'ruby', 'diamond', 'amber'." #: ../../TShockAPI/Commands.cs:1446 #, csharp-format @@ -60,17 +60,17 @@ msgstr " {0}{1} \"{2}\" (Encuentre la IP asociada con la cuenta del objetivo d #: ../../TShockAPI/Commands.cs:1444 #, csharp-format msgid " {0}{1} \"{2}{3}\" {4} {5} (Permanently bans this account name)" -msgstr " {0}{1} \"{2}{3}\" {4} {5} (Prohíbe permanentemente este nombre de cuenta)" +msgstr " {0}{1} \"{2}{3}\" {4} {5} (Banea permanentemente este nombre de cuenta)" #: ../../TShockAPI/Commands.cs:1449 #, csharp-format msgid " {0}{1} {2} (Find the player index for the target)" -msgstr " {0}{1} {2} (Buscar el índice del jugador para el objetivo)" +msgstr " {0}{1} {2} (Buscar el índice de jugador para el objetivo)" #: ../../TShockAPI/Commands.cs:1450 #, csharp-format msgid " {0}{1} {2}{3} {4} {5} (Permanently bans the online player by Account, UUID, and IP)" -msgstr " {0}{1} {2}{3} {4} {5} (Banea permanentemente al jugador conectado por cuenta, UUID o IP)" +msgstr " {0}{1} {2}{3} {4} {5} (Banea permanentemente al jugador conectado por cuenta, UUID, e IP)" #: ../../TShockAPI/Commands.cs:1447 #, csharp-format @@ -99,7 +99,7 @@ msgstr " A menos que se pase {0} al comando, se asume que {1} es un jugador o #: ../../TShockAPI/Commands.cs:1262 #, csharp-format msgid " -> Logged-in as: {0}; in group {1}." -msgstr "-> Sesión iniciada como: {0}; en grupo {1}." +msgstr " -> Sesión iniciada como: {0}; en grupo {1}." #: ../../TShockAPI/Commands.cs:1398 #, csharp-format @@ -131,7 +131,7 @@ msgstr "- Banea a un jugador desconectado por su dirección IP" #: ../../TShockAPI/Commands.cs:1448 msgid "- Ban an online player by index (Useful for hard to type names)" -msgstr "- Banea a un jugador conectado por índice (útil para nombres difíciles de escribir)" +msgstr "- Banea a un jugador conectado por índice (Útil para nombres difíciles de escribir)" #: ../../TShockAPI/Commands.cs:6498 msgid "- Default trees :" @@ -143,7 +143,7 @@ msgstr "- Árboles de gemas :" #: ../../TShockAPI/Commands.cs:1406 msgid "- Lists active bans. Color trends towards green as the ban approaches expiration" -msgstr "- Enumera las prohibiciones activas. El color tiende al verde a medida que la prohibición se aproxima a su vencimiento" +msgstr "- Enumera los baneos activos. El color tiende al verde a medida que el ban se aproxima a su vencimiento" #: ../../TShockAPI/Commands.cs:6504 msgid "- Misc :" @@ -155,11 +155,11 @@ msgstr "- Palmeras :" #: ../../TShockAPI/TShock.cs:963 msgid "!!! > Set DisableLoginBeforeJoin to true in the config file and /reload if this is a problem." -msgstr "!!! > En el archivo config marca DisableLoginBeforeJoin como true y usa /reload si esto es un problema." +msgstr "!!! > En el archivo de configuración marca DisableLoginBeforeJoin como true y usa /reload si esto es un problema." #: ../../TShockAPI/TShock.cs:957 msgid "!!! > Set DisableUUIDLogin to true in the config file and /reload if this is a problem." -msgstr "!!! > En el archivo config marca DisableUUIDLogin como true y usa /reload si esto es un problema." +msgstr "!!! > En el archivo de configuración marca DisableUUIDLogin como true y usa /reload si esto es un problema." #: ../../TShockAPI/TShock.cs:962 msgid "!!! Login before join is enabled. Existing accounts can login & the server password will be bypassed." @@ -181,7 +181,7 @@ msgstr "¡\"{0}\" no es un ID de buff válido!" #: ../../TShockAPI/Commands.cs:5906 #, csharp-format msgid "\"{0}\" is not a valid clear option." -msgstr "\"{0}\" no es una opción clara válida." +msgstr "\"{0}\" no es una opción de eliminación válida." #: ../../TShockAPI/Commands.cs:6026 #, csharp-format @@ -196,16 +196,16 @@ msgstr "\"{0}\" no es un número de página válido." #: ../../TShockAPI/Commands.cs:5826 #, csharp-format msgid "\"{0}\" is not a valid radius." -msgstr "\"{0}\" no es un valor válido." +msgstr "\"{0}\" no es un radio válido." #: ../../TShockAPI/Rest/SecureRest.cs:213 #, csharp-format msgid "\"{0}\" requested REST endpoint: {1}" -msgstr "\"{0}\" solicitó el parámetro REST: {1}" +msgstr "\"{0}\" solicitó el punto final REST: {1}" #: ../../TShockAPI/Commands.cs:2020 msgid "(Server Broadcast) " -msgstr "(Mensaje del Servidor) " +msgstr "(Anuncio de Servidor) " #: ../../TShockAPI/Configuration/TShockConfig.cs:496 msgid "(Super Admin) " @@ -229,17 +229,17 @@ msgstr "{0} ({1}) ha cambiado la contraseña de la cuenta {2}." #: ../../TShockAPI/Commands.cs:986 #, csharp-format msgid "{0} ({1}) failed to change the password for account {2}." -msgstr "{0} ({1}) falló al cambiar la contraseña para la cuenta {2}." +msgstr "{0} ({1}) no pudo cambiar la contraseña de la cuenta {2}." #: ../../TShockAPI/TShock.cs:1659 #, csharp-format msgid "{0} ({1}) from '{2}' group from '{3}' joined. ({4}/{5})" -msgstr "{0} ({1}) de '{2}' grupo de '{3}' unidos. ({4}/{5})" +msgstr "{0} ({1}) del grupo '{2}' desde '{3}' se ha conectado. ({4}/{5})" #: ../../TShockAPI/TShock.cs:1667 #, csharp-format msgid "{0} ({1}) from '{2}' group joined. ({3}/{4})" -msgstr "{0} ({1}) de '{2}' se unió al grupo ahora son: ({3}/{4})" +msgstr "{0} ({1}) del grupo '{2}' se ha conectado. ({3}/{4})" #: ../../TShockAPI/Commands.cs:775 #, csharp-format @@ -249,7 +249,7 @@ msgstr "{0} ({1}) tuvo {2} o más intentos de inicio de sesión no válidos y fu #: ../../TShockAPI/TShock.cs:1663 #, csharp-format msgid "{0} ({1}) has joined." -msgstr "{0} ({1}) se ha unido." +msgstr "{0} ({1}) se ha conectado." #: ../../TShockAPI/Commands.cs:5354 #, csharp-format @@ -295,7 +295,7 @@ msgstr "{0} {1} {2}" #: ../../TShockAPI/Commands.cs:1464 #, csharp-format msgid "{0} {1} on {2} ({3} ago)" -msgstr "{0} {1} en {2} (hace {3})" +msgstr "{0} {1} el {2} (hace {3})" #: ../../TShockAPI/Commands.cs:6368 #, csharp-format @@ -350,17 +350,17 @@ msgstr "{0} añadió la cuenta {1} al grupo {2}." #: ../../TShockAPI/GetDataHandlers.cs:3546 #, csharp-format msgid "{0} applied advanced combat techniques volume 2!" -msgstr "{0} aplicó el volumen 2 de técnicas avanzadas de combate!" +msgstr "¡{0} aplicó el volumen 2 de Técnicas Avanzadas de Combate!" #: ../../TShockAPI/GetDataHandlers.cs:3564 #, csharp-format msgid "{0} applied advanced combat techniques!" -msgstr "¡{0} aplicó técnicas avanzadas de combate!" +msgstr "¡{0} aplicó Técnicas Avanzadas de Combate!" #: ../../TShockAPI/GetDataHandlers.cs:3543 #, csharp-format msgid "{0} applied traveling merchant's satchel!" -msgstr "¡{0} ha aplicado satchel de mercader ambulante!" +msgstr "¡{0} ha aplicado el sachet del mercader ambulante!" #: ../../TShockAPI/Commands.cs:1064 #, csharp-format @@ -371,7 +371,7 @@ msgstr "{0} intentó registrar la cuenta {1} pero ya está en uso." #: ../../TShockAPI/GetDataHandlers.cs:3212 #, csharp-format msgid "{0} authenticated successfully as user {1}." -msgstr "{0} se ha autenticado correctamente como usuario {1}." +msgstr "{0} se ha autenticado correctamente como el usuario {1}." #: ../../TShockAPI/Commands.cs:905 #, csharp-format @@ -387,8 +387,8 @@ msgstr "{0} baneó a {1} por '{2}'." #, csharp-format msgid "{0} butchered {1} NPC." msgid_plural "{0} butchered {1} NPCs." -msgstr[0] "{0} mató {1} NPC." -msgstr[1] "{0} mató {1} NPC." +msgstr[0] "{0} mató {1} PNJ." +msgstr[1] "{0} mató a {1} PNJ." #: ../../TShockAPI/Commands.cs:2462 #, csharp-format @@ -408,12 +408,12 @@ msgstr "{0} cambió la cuenta {1} al grupo {2}." #: ../../TShockAPI/Commands.cs:4466 #, csharp-format msgid "{0} changed the maximum spawns to {1}." -msgstr "{0} cambió la cantidad máxima de enemigos entrantes a {1}." +msgstr "{0} cambió la cantidad máxima de enemigos generados a {1}." #: ../../TShockAPI/Commands.cs:4447 #, csharp-format msgid "{0} changed the maximum spawns to 5." -msgstr "{0} cambió la cantidad máxima de enemigos entrantes a 5." +msgstr "{0} cambió la cantidad máxima de enemigos generados a 5." #: ../../TShockAPI/Commands.cs:1154 #, csharp-format @@ -446,8 +446,8 @@ msgstr[1] "{0} eliminó {1} objetos en un radio de {2}." #, csharp-format msgid "{0} deleted {1} NPC within a radius of {2}." msgid_plural "{0} deleted {1} NPCs within a radius of {2}." -msgstr[0] "{0} eliminó {1} NPC en un radio de {2}." -msgstr[1] "{0} eliminó {1} NPCs en un radio de {2}." +msgstr[0] "{0} eliminó {1} PNJ en un radio de {2}." +msgstr[1] "{0} eliminó a {1} PNJ en un radio de {2}." #: ../../TShockAPI/Commands.cs:5902 #, csharp-format @@ -516,7 +516,7 @@ msgstr[1] "{0} te ha dado {1} {2}s." #: ../../TShockAPI/Commands.cs:4247 #, csharp-format msgid "{0} has been allowed to place tile {1}." -msgstr "A {0} se le ha permitido colocar la casilla {1}." +msgstr "A {0} se le ha permitido colocar el bloque {1}." #: ../../TShockAPI/Commands.cs:3881 #, csharp-format @@ -573,32 +573,32 @@ msgstr "{0} ha finalizado la invasión del Ejército del Antiguo." #: ../../TShockAPI/TShock.cs:1670 #, csharp-format msgid "{0} has joined." -msgstr "{0} se ha unido." +msgstr "{0} se ha conectado." #: ../../TShockAPI/TShock.cs:1674 #, csharp-format msgid "{0} has joined. IP: {1}" -msgstr "{0} se ha unido. IP: {1}" +msgstr "{0} se ha conectado. IP: {1}" #: ../../TShockAPI/Commands.cs:5655 #, csharp-format msgid "{0} has launched {1} into space." -msgstr "{0} ha lanzado a {1} al espacio." +msgstr "{0} ha lanzado a {1} a la estratosfera." #: ../../TShockAPI/Commands.cs:5653 #, csharp-format msgid "{0} has launched herself into space." -msgstr "{0} se lanzó ella misma al espacio." +msgstr "{0} se lanzó ella misma a la estratosfera." #: ../../TShockAPI/Commands.cs:5651 #, csharp-format msgid "{0} has launched himself into space." -msgstr "{0} se lanzó el mismo al espacio." +msgstr "{0} se lanzó el mismo a la estratosfera." #: ../../TShockAPI/TShock.cs:1397 #, csharp-format msgid "{0} has left." -msgstr "{0} ha salido." +msgstr "{0} se desconectó." #: ../../TShockAPI/Commands.cs:5500 #, csharp-format @@ -645,7 +645,7 @@ msgstr "{0} ha invocado un Muro Carnoso." #: ../../TShockAPI/GetDataHandlers.cs:2620 #, csharp-format msgid "{0} has SSC data in the database, but has the tshock.ignore.ssc permission. This means their SSC data is being ignored." -msgstr "{0} tiene datos SSC en la base de datos, pero posee el permiso tshock.ignore.ssc. Esto significa que sus datos SSC están siendo ignorados." +msgstr "{0} tiene datos SSC en la base de datos, mas tiene el permiso tshock.ignore.ssc; sus datos SSC están siendo ignorados." #: ../../TShockAPI/Commands.cs:2342 #, csharp-format @@ -670,7 +670,7 @@ msgstr "{0} inició la invasión de la legión de escarcha." #: ../../TShockAPI/Commands.cs:5488 #, csharp-format msgid "{0} has unmuted {1}." -msgstr "{0} ha dessilenciado a {1}." +msgstr "{0} ha desmuteado a {1}." #: ../../TShockAPI/Commands.cs:6356 #, csharp-format @@ -690,22 +690,22 @@ msgstr "{0} se curó a sí mismo por {1} HP." #: ../../TShockAPI/Commands.cs:4250 #, csharp-format msgid "{0} is already allowed to place tile {1}." -msgstr "{0} ya tiene permiso de colocar el bloque {1}." +msgstr "{0} ya tenía permiso de colocar el bloque {1}." #: ../../TShockAPI/Commands.cs:3885 #, csharp-format msgid "{0} is already allowed to use {1}." -msgstr "{0} ya tenía permiso de usar {1}" +msgstr "{0} ya tenía permiso de usar {1}." #: ../../TShockAPI/Commands.cs:4074 #, csharp-format msgid "{0} is already allowed to use projectile {1}." -msgstr "{0} ya tiene permiso de usar el proyectil {1}." +msgstr "{0} ya tenía permiso de usar el proyectil {1}." #: ../../TShockAPI/Commands.cs:5940 #, csharp-format msgid "{0} is already dead!" -msgstr "¡{0} ya está muerto!" +msgstr "¡{0} ya había muerto!" #: ../../TShockAPI/Commands.cs:3956 #, csharp-format @@ -715,12 +715,12 @@ msgstr "{0} ya tenía prohibido usar {1}." #: ../../TShockAPI/Commands.cs:4309 #, csharp-format msgid "{0} is already prevented from placing tile {1}." -msgstr "A {0} ya se le ha prohibido colocar el bloque {1}." +msgstr "A {0} ya se le había prohibido colocar el bloque {1}." #: ../../TShockAPI/Commands.cs:4133 #, csharp-format msgid "{0} is already prevented from using projectile {1}." -msgstr "A {0} ya se le ha prohibido usar el proyectil {1}." +msgstr "A {0} ya se le había prohibido usar el proyectil {1}." #: ../../TShockAPI/ItemBans.cs:234 #, csharp-format @@ -747,7 +747,7 @@ msgstr "{0} no está baneado(a)." #: ../../TShockAPI/Commands.cs:5990 #, csharp-format msgid "{0} is not dead!" -msgstr "¡{0} no esta muerto!" +msgstr "¡{0} no ha muerto!" #: ../../TShockAPI/Commands.cs:6750 #, csharp-format @@ -757,7 +757,7 @@ msgstr "{0} está ahora en modo dios." #: ../../TShockAPI/Commands.cs:5586 #, csharp-format msgid "{0} is offline and cannot receive your reply." -msgstr "{0} está desconectado y no puede recibir tu respuesta." +msgstr "{0} está desconectado(a) y no puede recibir tu respuesta." #: ../../TShockAPI/Commands.cs:5949 #: ../../TShockAPI/Rest/RestManager.cs:1068 @@ -904,7 +904,7 @@ msgstr "{0} eliminó exitosamente la cuenta: {1}." #: ../../TShockAPI/GetDataHandlers.cs:3567 #, csharp-format msgid "{0} summoned a Blood Moon!" -msgstr "¡{0} invocó una Luna de Sangre!" +msgstr "¡{0} invocó una Luna Sangrienta!" #: ../../TShockAPI/GetDataHandlers.cs:3579 #, csharp-format @@ -950,7 +950,7 @@ msgstr "¡{0} ha invocado a {1}" #: ../../TShockAPI/GetDataHandlers.cs:2980 #, csharp-format msgid "{0} summoned the Empress of Light!" -msgstr "¡{0} ha invocado a la Emperatriz de la luz!" +msgstr "¡{0} ha invocado a la Emperatriz de la Luz!" #: ../../TShockAPI/GetDataHandlers.cs:3585 #, csharp-format @@ -960,7 +960,7 @@ msgstr "¡{0} ha invocado los Piratas!" #: ../../TShockAPI/GetDataHandlers.cs:3588 #, csharp-format msgid "{0} summoned the Snow Legion!" -msgstr "¡{0} ha invocado a la Legión Helada!" +msgstr "¡{0} ha invocado a la Legión de Escarcha!" #: ../../TShockAPI/Commands.cs:3004 #: ../../TShockAPI/Commands.cs:3044 @@ -1002,7 +1002,7 @@ msgstr "{0} te transportó hacia {1}." #: ../../TShockAPI/TSPlayer.cs:1953 #, csharp-format msgid "{0} was banned for '{1}'." -msgstr "{0} fue prohibido por '{1}'." +msgstr "{0} fue baneado por '{1}'." #: ../../TShockAPI/TSPlayer.cs:1924 #, csharp-format @@ -1043,7 +1043,7 @@ msgstr "{0}{1} no define ningún alias." #: ../../TShockAPI/Commands.cs:5284 #, csharp-format msgid "{0}{1} help: " -msgstr "Ayuda de {0}{1} : " +msgstr "Ayuda de {0}{1}: " #: ../../TShockAPI/Utils.cs:1152 #, csharp-format @@ -1099,18 +1099,18 @@ msgstr "* **Comandos**: `{0}`" #: ../../TShockAPI/Configuration/TShockConfig.cs:645 #, csharp-format msgid "* **Default**: `{0}`" -msgstr "" +msgstr "* **Predeterminado**: `{0}`" #: ../../TShockAPI/Configuration/ServerSideConfig.cs:122 #: ../../TShockAPI/Configuration/TShockConfig.cs:644 #, csharp-format msgid "* **Field type**: `{0}`" -msgstr "" +msgstr "* **Tipo de campo**: `{0}`" #: ../../TShockAPI/Rest/RestManager.cs:1220 #, csharp-format msgid "* **Permissions**: `{0}`" -msgstr "" +msgstr "* **Permisos**: `{0}`" #: ../../TShockAPI/Commands.cs:5430 #, csharp-format @@ -1120,15 +1120,15 @@ msgstr "*{0} {1}" #: ../../TShockAPI/Rest/RestManager.cs:1253 #, csharp-format msgid "**Example Usage**: `{0}?{1}`" -msgstr "" +msgstr "**Ejemplo de Uso**: `{0}?{1}`" #: ../../TShockAPI/Rest/RestManager.cs:1243 msgid "**Nouns**:" -msgstr "" +msgstr "**Sustantivos**:" #: ../../TShockAPI/Rest/RestManager.cs:1230 msgid "**Verbs**:" -msgstr "" +msgstr "**Verbos**:" #: ../../TShockAPI/Commands.cs:1613 #, csharp-format @@ -1149,12 +1149,12 @@ msgstr " {1}" #: ../../TShockPluginManager/NugetCLI.cs:187 #, csharp-format msgid "{0} from {1} [{2}]" -msgstr "" +msgstr "{0} de {1} [{2}]" #: ../../TShockPluginManager/NugetCLI.cs:178 #, csharp-format msgid "{0} from {1} [{2}]" -msgstr "" +msgstr "{0} de {1} [{2}]" #: ../../TShockAPI/Commands.cs:5551 #: ../../TShockAPI/Commands.cs:5582 @@ -1166,15 +1166,15 @@ msgstr " {1}" #, csharp-format msgid "=== Dependency ===" msgid_plural "=== Dependencies ===" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "=== Dependencia ===" +msgstr[1] "=== Dependencias ===" #: ../../TShockPluginManager/NugetCLI.cs:133 #, csharp-format msgid "=== Requested Plugin ===" msgid_plural "=== Requested Plugins ===" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "=== Plugin Solicitado ===" +msgstr[1] "=== Plugins Solicitados ===" #: ../../TShockAPI/Commands.cs:2818 msgid "a Deerclops" @@ -1202,11 +1202,11 @@ msgstr "Un meteoro ha sido desencadenado." #: ../../TShockAPI/Commands.cs:2803 msgid "a Nebula Pillar" -msgstr "un Pilar de Nebulosa" +msgstr "una Columna de Nebulosa" #: ../../TShockAPI/TShock.cs:974 msgid "A password for this server was set in config.json and is being used." -msgstr "Una contraseña para este servidor fue establecida en config.json y está siendo usada." +msgstr "Se estableció una contraseña para este servidor en config.json y está en uso." #: ../../TShockAPI/Commands.cs:1316 msgid "A player name must be provided to kick a player. Please provide one." @@ -1219,15 +1219,15 @@ msgstr "Un plugin en el servidor detuvo tu inicio de sesión." #: ../../TShockAPI/Rest/SecureRest.cs:120 #, csharp-format msgid "A REST login from {0} was blocked as it currently has {1} rate-limit tokens and is at the RESTMaximumRequestsPerInterval threshold." -msgstr "Se bloqueó un inicio de sesión REST de {0} dado que ya tiene {1} tókenes restringidos por frecuencia, alcanzando el límite de RESTMaximumRequestsPerInterval." +msgstr "Se bloqueó un inicio de sesión REST de {0} dado que ya tiene {1} tókens restringidos por frecuencia, alcanzando el umbral de RESTMaximumRequestsPerInterval." #: ../../TShockAPI/Commands.cs:2798 msgid "a Solar Pillar" -msgstr "un Pilar Solar" +msgstr "una Columna Solar" #: ../../TShockAPI/Commands.cs:2813 msgid "a Stardust Pillar" -msgstr "un Pilar de Polvo Estelar" +msgstr "una Columna de Polvo Estelar" #: ../../TShockAPI/Commands.cs:864 msgid "A user account by that name does not exist." @@ -1235,7 +1235,7 @@ msgstr "Una cuenta de usuario con ese nombre no existe." #: ../../TShockAPI/Commands.cs:2808 msgid "a Vortex Pillar" -msgstr "un Pilar de Vórtice" +msgstr "una Columna de Vórtice" #: ../../TShockAPI/Commands.cs:1159 #, csharp-format @@ -1289,7 +1289,7 @@ msgstr "{0} añadido a la lista blanca." #: ../../TShockAPI/Bouncer.cs:2041 #, csharp-format msgid "Added buff to {0} NPC abnormally." -msgstr "Añadido buff al NPC {0} anormalmente." +msgstr "Añadido buff al PNJ {0} de forma anormal." #: ../../TShockAPI/Commands.cs:4855 #, csharp-format @@ -1317,7 +1317,7 @@ msgstr "Alias de {0}{1}: {0}{2}" #: ../../TShockAPI/Commands.cs:6013 msgid "All alive NPCs (excluding town NPCs) on the server will be killed if you do not input a name or ID." -msgstr "Todos los PNJs vivos (excluyendo los PNJs de la ciudad) en el servidor serán asesinados si no introduces un nombre o ID." +msgstr "Se matará a todos los PNJ vivos (excepto los ciudadanos) en el servidor si no introduces un nombre o ID." #: ../../TShockAPI/Commands.cs:2626 msgid "all bosses" @@ -1325,7 +1325,7 @@ msgstr "todos los jefes" #: ../../TShockPluginManager/NugetCLI.cs:169 msgid "All done! :)" -msgstr "" +msgstr "¡Todo listo! :)" #: ../../TShockAPI/Commands.cs:2104 msgid "All REST tokens have been destroyed." @@ -1358,15 +1358,15 @@ msgstr "allowg - Permite a un grupo acceder a dicha región." #: ../../TShockAPI/Commands.cs:6670 msgid "Amber Gemtree" -msgstr "Gema de Ámbar" +msgstr "Arbolgema de Ámbar" #: ../../TShockAPI/Commands.cs:6650 msgid "Amethyst Gemtree" -msgstr "Gema de Amatista" +msgstr "Arbolgema de Amatista" #: ../../TShockAPI/TShock.cs:996 msgid "An account has been detected in the user database, but setup-code.txt is still present." -msgstr "Se ha detectado una cuenta en la base de datos de usuarios, pero setup-code.txt sigue presente." +msgstr "Se ha detectado una cuenta de usuario en la base de datos, pero setup-code.txt sigue presente." #: ../../TShockAPI/DB/GroupManager.cs:474 #, csharp-format @@ -1380,7 +1380,7 @@ msgstr "Ha ocurrido una excepción durante la transacción de la base de datos: #: ../../TShockAPI/TShock.cs:1489 msgid "An exception occurred executing a command." -msgstr "" +msgstr "Se ha producido una excepción al ejecutar un comando." #: ../../TShockAPI/DB/BanManager.cs:644 msgid "An identifier for a character name." @@ -1388,7 +1388,7 @@ msgstr "Un identificador para un nombre de personaje." #: ../../TShockAPI/DB/BanManager.cs:648 msgid "An identifier for a TShock User Account name." -msgstr "Un identificador para un nombre de usuario de TShock." +msgstr "Un identificador para un nombre de Cuenta de Usuario de TShock." #: ../../TShockAPI/DB/BanManager.cs:640 msgid "An identifier for a UUID." @@ -1405,16 +1405,16 @@ msgstr "Se pidió comprobar si hay actualizaciones. De haber una se te notificar #: ../../TShockAPI/Commands.cs:5599 msgid "Annoy Syntax" -msgstr "Sintaxis molesta" +msgstr "Sintaxis de Molestar" #: ../../TShockAPI/Commands.cs:5616 #, csharp-format msgid "Annoying {0} for {1} seconds." -msgstr "Molesta a {0} durante {1} segundos." +msgstr "Molestando a {0} por {1} segundos." #: ../../TShockAPI/Commands.cs:338 msgid "Annoys a player for an amount of time." -msgstr "Molesta a un jugador durante un tiempo." +msgstr "Molesta a un jugador durante un período de tiempo." #: ../../TShockAPI/Rest/Rest.cs:452 #, csharp-format @@ -1423,11 +1423,11 @@ msgstr "Anónimo solicitó un punto final REST: {0}" #: ../../TShockAPI/Commands.cs:5224 msgid "Anti-build is now off." -msgstr "El Anti-construcción ya no está activo." +msgstr "Anti-construcción ha sido desactivado." #: ../../TShockAPI/Commands.cs:5224 msgid "Anti-build is now on." -msgstr "El Anti-construcción está activo." +msgstr "Anti-construcción ha sido activado." #: ../../TShockAPI/Commands.cs:3204 msgid "Arguments: add [warp name], del [warp name], list [page]." @@ -1442,17 +1442,17 @@ msgstr "Parámetros: send [jugador] [nombre de warp], hide [nombre de warp] [Act #: ../../TShockAPI/GetDataHandlers.cs:3211 #, csharp-format msgid "Authenticated as {0} successfully." -msgstr "Autenticado como {0} con éxito." +msgstr "Autenticado como {0} correctamente." #: ../../TShockAPI/TShock.cs:440 #: ../../TShockAPI/TShock.cs:1590 msgid "AutoSave Disabled" -msgstr "Autoguardado desactivado" +msgstr "AutoGuardado Desactivado" #: ../../TShockAPI/TShock.cs:438 #: ../../TShockAPI/TShock.cs:1588 msgid "AutoSave Enabled" -msgstr "Autoguardado activado." +msgstr "AutoGuardado Activado" #: ../../TShockAPI/Rest/RestManager.cs:811 msgid "AutoSave has been disabled" @@ -1468,19 +1468,19 @@ msgstr "El autoguardado esta actualmente desactivado" #: ../../TShockAPI/Rest/RestManager.cs:796 msgid "Autosave is currently enabled" -msgstr "El autoguardado está actualmente activo" +msgstr "El autoguardado está actualmente activado" #: ../../TShockAPI/Commands.cs:1368 msgid "Available Ban commands:" -msgstr "Comandos de prohibición disponibles:" +msgstr "Comandos de Baneo disponibles:" #: ../../TShockAPI/Commands.cs:1432 msgid "Available identifiers ({{0}}/{{1}}):" -msgstr "Identificadores disponibles ({{0}}/{1}}):" +msgstr "Identificadores disponibles ({{0}}/{{1}}):" #: ../../TShockAPI/Commands.cs:5208 msgid "Available Region Sub-Commands ({{0}}/{{1}}):" -msgstr "Subcomandos de región disponibles ({{0}}/{{1}}):" +msgstr "Sub-Comandos de Región Disponibles ({{0}}/{{1}}):" #: ../../TShockAPI/Commands.cs:2109 msgid "Available REST Sub-Commands:" @@ -1501,11 +1501,11 @@ msgstr "Hilo de Respaldo" #: ../../TShockAPI/TShock.cs:444 msgid "Backups Disabled" -msgstr "Respaldos desactivados" +msgstr "Respaldos Desactivados" #: ../../TShockAPI/TShock.cs:442 msgid "Backups Enabled" -msgstr "Respaldos activados" +msgstr "Respaldos Activados" #: ../../TShockAPI/Commands.cs:837 msgid "Bad login attempt." @@ -1520,12 +1520,12 @@ msgstr "ban {0}" #: ../../TShockAPI/Commands.cs:1372 #, csharp-format msgid "ban {0} " -msgstr "ban {0} " +msgstr "ban {0} " #: ../../TShockAPI/Commands.cs:1369 #, csharp-format msgid "ban {0} [Flags]" -msgstr "ban {0} [Flags]" +msgstr "ban {0} [Parámetros]" #: ../../TShockAPI/Commands.cs:1633 #, csharp-format @@ -1539,7 +1539,7 @@ msgstr "El ban {0} acaba de marcarse como expirado." #: ../../TShockAPI/Commands.cs:1383 msgid "Ban Add Syntax" -msgstr "Ban Add Syntax" +msgstr "Sintaxis de Ban Add" #: ../../TShockAPI/Commands.cs:1490 #, csharp-format @@ -1553,15 +1553,15 @@ msgstr "Ban añadido. Número de ticket: {0}" #: ../../TShockAPI/Commands.cs:1396 msgid "Ban Del Syntax" -msgstr "Sintaxis Ban Del" +msgstr "Sintaxis de Ban Del" #: ../../TShockAPI/Commands.cs:1412 msgid "Ban Details Syntax" -msgstr "Detalles de la prohibición Sintaxis" +msgstr "Sintaxis de Ban Details" #: ../../TShockAPI/Commands.cs:1404 msgid "Ban List Syntax" -msgstr "Sintaxis de la lista de prohibiciones" +msgstr "Sintaxis de Ban List" #: ../../TShockAPI/Rest/RestManager.cs:695 msgid "Ban removed." @@ -1569,12 +1569,12 @@ msgstr "Ban revocado." #: ../../TShockAPI/Commands.cs:1442 msgid "Ban Usage Examples" -msgstr "Ejemplos de uso de prohibición" +msgstr "Ejemplos del Uso de Ban" #: ../../TShockAPI/Commands.cs:3841 #, csharp-format msgid "Banned {0}." -msgstr "Prohibido {0}." +msgstr "Se ha baneado a {0}." #: ../../TShockAPI/Commands.cs:4037 #, csharp-format @@ -1589,7 +1589,7 @@ msgstr "Se ha prohibido el bloque {0}." #: ../../TShockAPI/TSPlayer.cs:1950 #, csharp-format msgid "Banned: {0}" -msgstr "Prohibido: {0}" +msgstr "Baneado: {0}" #: ../../TShockAPI/Commands.cs:1517 msgid "Banned." @@ -1597,11 +1597,11 @@ msgstr "Baneado." #: ../../TShockAPI/Commands.cs:1670 msgid "Bans ({{0}}/{{1}}):" -msgstr "Prohibidos ({{0}}/{{1}}):" +msgstr "Baneos ({{0}}/{{1}}):" #: ../../TShockAPI/Commands.cs:6592 msgid "Basic Tree" -msgstr "Árbol básico" +msgstr "Árbol Básico" #: ../../TShockAPI/Commands.cs:2755 msgid "Betsy" @@ -1615,21 +1615,21 @@ msgstr "La Luna Sangrienta fue marcada como {0}" #: ../../TShockAPI/Rest/RestManager.cs:904 #, csharp-format msgid "Bloodmoon state: {0}" -msgstr "Estado de la luna de sangre: {0}" +msgstr "Estado de la Luna Sangrienta: {0}" #: ../../TShockAPI/Commands.cs:6596 msgid "Boreal Tree" -msgstr "Árbol boreal" +msgstr "Árbol Boreal" #: ../../TShockAPI/GetDataHandlers.cs:3264 #, csharp-format msgid "Bouncer / HandleNpcTalk rejected from bouncer out of bounds from {0}" -msgstr "" +msgstr "Bouncer / HandleNpcTalk rechazó desde el bouncer fuera de area de {0}" #: ../../TShockAPI/GetDataHandlers.cs:3257 #, csharp-format msgid "Bouncer / HandleNpcTalk rejected from bouncer throttle from {0}" -msgstr "" +msgstr "Bouncer / HandleNpcTalk rechazó desde el bouncer limitado de {0}" #: ../../TShockAPI/Bouncer.cs:1465 #, csharp-format @@ -1689,17 +1689,17 @@ msgstr "Bouncer / OnFishOutNPC rechazo comprobaciones de rango de {0}" #: ../../TShockAPI/Bouncer.cs:2747 #, csharp-format msgid "Bouncer / OnFishOutNPC rejected summon boss permissions from {0}" -msgstr "Bouncer / OnFishOutNPC rechazo permisos de invocación de jefe de {0}" +msgstr "Bouncer / OnFishOutNPC rechazó permisos de invocación de jefe de {0}" #: ../../TShockAPI/Bouncer.cs:2781 #, csharp-format msgid "Bouncer / OnFoodPlatterTryPlacing rejected disabled from {0}" -msgstr "Bouncer / OnFoodPlatterTryPlacing rechazo desactivación de {0}" +msgstr "Bouncer / OnFoodPlatterTryPlacing rechazó desactivación de {0}" #: ../../TShockAPI/Bouncer.cs:2774 #, csharp-format msgid "Bouncer / OnFoodPlatterTryPlacing rejected item not placed by hand from {0}" -msgstr "Bouncer / OnFoodPlatterTryPlacing rechazo objeto no puesto manualmente de {0}" +msgstr "Bouncer / OnFoodPlatterTryPlacing rechazó objeto no puesto manualmente de {0}" #: ../../TShockAPI/Bouncer.cs:2792 #, csharp-format @@ -1714,7 +1714,7 @@ msgstr "Bouncer / OnFoodPlatterTryPlacing rechazó las comprobaciones de rango d #: ../../TShockAPI/Bouncer.cs:2767 #, csharp-format msgid "Bouncer / OnFoodPlatterTryPlacing rejected tile placement valid from {0}" -msgstr "Bouncer / OnFoodPlatterTryPlacing rechazó la colocación de baldosas válida desde {0}" +msgstr "Bouncer / OnFoodPlatterTryPlacing rechazó la colocación de bloques válidos desde {0}" #: ../../TShockAPI/Bouncer.cs:2508 #, csharp-format @@ -1724,12 +1724,12 @@ msgstr "Bouncer / OnGemLockToggle colocación inválida/deadmod de {0}" #: ../../TShockAPI/Bouncer.cs:2501 #, csharp-format msgid "Bouncer / OnGemLockToggle rejected boundaries check from {0}" -msgstr "Bouncer / OnGemLockToggle rechazó la comprobación de límites de {0}" +msgstr "Bouncer / OnGemLockToggle rechazada la comprobación de umbral de {0}" #: ../../TShockAPI/Bouncer.cs:2515 #, csharp-format msgid "Bouncer / OnGemLockToggle rejected disabled from {0}" -msgstr "Bouncer / OnGemLockToggle rechazado deshabilitado desde {0}" +msgstr "Bouncer / OnGemLockToggle rechazó el estado deshabilitado desde {0}" #: ../../TShockAPI/Bouncer.cs:2524 #, csharp-format @@ -1753,211 +1753,211 @@ msgstr "Bouncer / OnHealOtherPlayer 0.2 comprobar desde {0}" #: ../../TShockAPI/Bouncer.cs:2120 #, csharp-format msgid "Bouncer / OnHealOtherPlayer rejected disabled/throttled from {0}" -msgstr "" +msgstr "Bouncer / OnHealOtherPlayer rechazó el estado deshabilitado/limitado desde {0}" #: ../../TShockAPI/Bouncer.cs:2112 #, csharp-format msgid "Bouncer / OnHealOtherPlayer rejected heal other threshold from {0} {1}/{2}" -msgstr "" +msgstr "Bouncer / OnHealOtherPlayer rechazó el umbral de sanar a otro desde {0} {1}/{2}" #: ../../TShockAPI/Bouncer.cs:2086 msgid "Bouncer / OnHealOtherPlayer rejected null checks" -msgstr "" +msgstr "Bouncer / OnHealOtherPlayer rechazó comprobaciones nulas" #: ../../TShockAPI/Bouncer.cs:1074 #, csharp-format msgid "Bouncer / OnItemDrop rejected from attempt crash from {0}" -msgstr "" +msgstr "Bouncer / OnItemDrop rechazó desde un intento de crasheo desde {0}" #: ../../TShockAPI/Bouncer.cs:1150 #, csharp-format msgid "Bouncer / OnItemDrop rejected from disabled from {0}" -msgstr "" +msgstr "Bouncer / OnItemDrop rechazó desde estado deshabilitado de {0}" #: ../../TShockAPI/Bouncer.cs:1130 #, csharp-format msgid "Bouncer / OnItemDrop rejected from drop item ban check / max stack check / min stack check from {0}" -msgstr "" +msgstr "Bouncer / OnItemDrop rechazó la comprobación de objeto prohibido arrojado / comprobación de stack máximo / comprobación de stack mínimo desde {0}" #: ../../TShockAPI/Bouncer.cs:1099 #, csharp-format msgid "Bouncer / OnItemDrop rejected from dupe range check from {0}" -msgstr "" +msgstr "Bouncer / OnItemDrop rechazó desde la comprobación de rango de duplicación de {0}" #: ../../TShockAPI/Bouncer.cs:1120 #, csharp-format msgid "Bouncer / OnItemDrop rejected from item drop/pickup check from {0}" -msgstr "" +msgstr "Bouncer / OnItemDrop rechazó desde la comprobación de objeto arrojado/recogido de {0}" #: ../../TShockAPI/Bouncer.cs:1084 #, csharp-format msgid "Bouncer / OnItemDrop rejected from prefix check from {0}" -msgstr "" +msgstr "Bouncer / OnItemDrop rechazado desde la comprobación de prefijo de {0}" #: ../../TShockAPI/Bouncer.cs:1110 #, csharp-format msgid "Bouncer / OnItemDrop rejected from range check from {0}" -msgstr "" +msgstr "Bouncer / OnItemDrop rechazado desde la comprobación de rango de {0}" #: ../../TShockAPI/Bouncer.cs:1141 #, csharp-format msgid "Bouncer / OnItemDrop rejected from sneaky from {0}" -msgstr "" +msgstr "Bouncer / OnItemDrop rechazado desde un sospechoso de {0}" #: ../../TShockAPI/Bouncer.cs:2703 #, csharp-format msgid "Bouncer / OnKillMe rejected bad length death text from {0}" -msgstr "" +msgstr "Bouncer / OnKillMe rechazó el texto de muerte de longitud incorrecta de {0}" #: ../../TShockAPI/Bouncer.cs:2710 #, csharp-format msgid "Bouncer / OnKillMe rejected custom death message from {0}" -msgstr "" +msgstr "Bouncer / OnKillMe rechazó el mensaje personalizado de muerte de {0}" #: ../../TShockAPI/Bouncer.cs:2684 #, csharp-format msgid "Bouncer / OnKillMe rejected high damage from {0} {1}" -msgstr "" +msgstr "Bouncer / OnKillMe rechazó el daño elevado de {0} {1}" #: ../../TShockAPI/Bouncer.cs:2693 #, csharp-format msgid "Bouncer / OnKillMe rejected index check from {0}" -msgstr "" +msgstr "Bouncer / OnKillMe rechazó la comprobación de índice de {0}" #: ../../TShockAPI/Bouncer.cs:1832 #, csharp-format msgid "Bouncer / OnLiquidSet rejected build permission from {0}" -msgstr "" +msgstr "Bouncer / OnLiquidSet rechazó el permiso de construcción de {0}" #: ../../TShockAPI/Bouncer.cs:1676 #, csharp-format msgid "Bouncer / OnLiquidSet rejected disabled from {0}" -msgstr "" +msgstr "Bouncer / OnLiquidSet rechazo por deshabilitamiento de {0}" #: ../../TShockAPI/Bouncer.cs:1694 #, csharp-format msgid "Bouncer / OnLiquidSet rejected from liquid threshold from {0} {1}/{2}" -msgstr "" +msgstr "Bouncer / OnLiquidSet rechazado desde el umbral de líquido de {0} {1}/{2}" #: ../../TShockAPI/Bouncer.cs:1669 #, csharp-format msgid "Bouncer / OnLiquidSet rejected invalid check from {0}" -msgstr "" +msgstr "Bouncer / OnLiquidSet rechazó comprobación inválida desde {0}" #: ../../TShockAPI/Bouncer.cs:1731 #, csharp-format msgid "Bouncer / OnLiquidSet rejected liquid type {0} from {1} holding {2}" -msgstr "" +msgstr "Bouncer / OnLiquidSet rechazó el tipo de líquido {0} de {1} sosteniendo {2}" #: ../../TShockAPI/Bouncer.cs:1840 #, csharp-format msgid "Bouncer / OnLiquidSet rejected range checks from {0}" -msgstr "" +msgstr "Bouncer / OnLiquidSet rechazó las comprobaciones de rango de {0}" #: ../../TShockAPI/Bouncer.cs:1848 #, csharp-format msgid "Bouncer / OnLiquidSet rejected throttle from {0}" -msgstr "" +msgstr "Bouncer / OnLiquidSet rechazó limitación desde {0}" #: ../../TShockAPI/Bouncer.cs:2573 #, csharp-format msgid "Bouncer / OnMassWireOperation rejected build perms from {0}" -msgstr "" +msgstr "Bouncer / OnMassWireOperation rechazó los permisos de construcción desde {0}" #: ../../TShockAPI/Bouncer.cs:2566 #, csharp-format msgid "Bouncer / OnMassWireOperation rejected disabled from {0}" -msgstr "" +msgstr "Bouncer / OnMassWireOperation rechazó por deshabilitación desde {0}" #: ../../TShockAPI/Bouncer.cs:2559 #, csharp-format msgid "Bouncer / OnMassWireOperation rejected valid placement from {0}" -msgstr "" +msgstr "Bouncer / OnMassWireOperation rechazó la colocación válida desde {0}" #: ../../TShockAPI/Bouncer.cs:1274 #, csharp-format msgid "Bouncer / OnNewProjectile please report to tshock about this! normally this is a reject from {0} {1}" -msgstr "" +msgstr "Bouncer / OnNewProjectile ¡Favor reportar esto a TShock! Normalmente esto es un rechazo desde {0} {1}" #: ../../TShockAPI/Bouncer.cs:1230 #, csharp-format msgid "Bouncer / OnNewProjectile please report to tshock about this! normally this is a reject from {0} {1} (golf)" -msgstr "" +msgstr "Bouncer / OnNewProjectile ¡Favor reportar esto a TShock! Normalmente esto es un rechazo desde {0} {1} (golf)" #: ../../TShockAPI/Bouncer.cs:1174 #, csharp-format msgid "Bouncer / OnNewProjectile rejected from above projectile limit from {0}" -msgstr "" +msgstr "Bouncer / OnNewProjectile rechazado por estar sobre el límite de proyectil desde {0}" #: ../../TShockAPI/Bouncer.cs:1315 #, csharp-format msgid "Bouncer / OnNewProjectile rejected from bouncer modified AI from {0}." -msgstr "" +msgstr "Bouncer / OnNewProjectile rechazado desde el bouncer por IA modificada de {0}." #: ../../TShockAPI/Bouncer.cs:1331 #, csharp-format msgid "Bouncer / OnNewProjectile rejected from bouncer modified Zenith projectile from {0}." -msgstr "" +msgstr "Bouncer / OnNewProjectile rechazado desde el bouncer por proyectil Cenit modificado de {0}." #: ../../TShockAPI/Bouncer.cs:1302 #, csharp-format msgid "Bouncer / OnNewProjectile rejected from bouncer throttle from {0}" -msgstr "" +msgstr "Bouncer / OnNewProjectile rechazado desde el bouncer por limitación de {0}" #: ../../TShockAPI/Bouncer.cs:1201 #, csharp-format msgid "Bouncer / OnNewProjectile rejected from disabled from {0}" -msgstr "" +msgstr "Bouncer / OnNewProjectile rechazó desde estado deshabilitado de {0}" #: ../../TShockAPI/Bouncer.cs:1238 #, csharp-format msgid "Bouncer / OnNewProjectile rejected from hostile projectile from {0}" -msgstr "" +msgstr "Bouncer / OnNewProjectile rechazado por uso de proyectil hostil de {0}" #: ../../TShockAPI/Bouncer.cs:1183 #, csharp-format msgid "Bouncer / OnNewProjectile rejected from permission check from {0} {1}" -msgstr "" +msgstr "Bouncer / OnNewProjectile rechazado por comprobación de permisos de {0} {1}" #: ../../TShockAPI/Bouncer.cs:1294 #, csharp-format msgid "Bouncer / OnNewProjectile rejected from projectile create threshold from {0} {1}/{2}" -msgstr "" +msgstr "Bouncer / OnNewProjectile rechazado por umbral de creación de proyectil desde {0} {1}/{2}" #: ../../TShockAPI/Bouncer.cs:1193 #, csharp-format msgid "Bouncer / OnNewProjectile rejected from projectile damage limit from {0} {1}/{2}" -msgstr "" +msgstr "Bouncer / OnNewProjectile rechazado por límite de daño de proyectil desde {0} {1}/{2}" #: ../../TShockAPI/Bouncer.cs:1249 #, csharp-format msgid "Bouncer / OnNewProjectile rejected from tombstones from {0}" -msgstr "" +msgstr "Bouncer / OnNewProjectile rechazado por lápidas de {0}" #: ../../TShockAPI/Bouncer.cs:1269 #, csharp-format msgid "Bouncer / OnNewProjectile rejected from weird check from {0} {1}" -msgstr "" +msgstr "Bouncer / OnNewProjectile rechazado por comprobación extraña de {0} {1}" #: ../../TShockAPI/Bouncer.cs:2040 #, csharp-format msgid "Bouncer / OnNPCAddBuff rejected abnormal buff ({0}, last for {4}) added to {1} ({2}) from {3}." -msgstr "" +msgstr "Bouncer / OnNPCAddBuff rechazó un buff anormal ({0}, de duración {4}) aplicado a {1} ({2}) por {3}." #: ../../TShockAPI/Bouncer.cs:1994 #, csharp-format msgid "Bouncer / OnNPCAddBuff rejected disabled from {0}" -msgstr "" +msgstr "Bouncer / OnNPCAddBuff rechazó el estado deshabilitado desde {0}" #: ../../TShockAPI/Bouncer.cs:1987 #, csharp-format msgid "Bouncer / OnNPCAddBuff rejected null npc from {0}" -msgstr "" +msgstr "Bouncer / OnNPCAddBuff rechazó PNJ nulo de {0}" #: ../../TShockAPI/Bouncer.cs:1978 #, csharp-format msgid "Bouncer / OnNPCAddBuff rejected out of bounds NPC update from {0}" -msgstr "" +msgstr "Bouncer / OnNPCAddBuff rechazó actualización de PNJ fuera de los bordes desde {0}" #: ../../TShockAPI/Bouncer.cs:1416 #, csharp-format @@ -2535,41 +2535,41 @@ msgstr "" #: ../../TShockAPI/Utils.cs:136 #, csharp-format msgid "Broadcast: {0}" -msgstr "" +msgstr "Anuncio: {0}" #: ../../TShockAPI/Utils.cs:159 #, csharp-format msgid "Broadcast: {0}: {1}" -msgstr "" +msgstr "Anuncio: {0}: {1}" #: ../../TShockAPI/Commands.cs:272 msgid "Broadcasts a message to everyone on the server." -msgstr "" +msgstr "Transmite un mensaje a todo el servidor." #: ../../TShockAPI/Commands.cs:6367 msgid "Buff Syntax and Example" -msgstr "" +msgstr "Sintaxis y Ejemplo de Buff" #: ../../TShockAPI/Commands.cs:6010 msgid "Butcher Syntax and Example" -msgstr "" +msgstr "Sintaxis y Ejemplo de Butcher" #: ../../TShockAPI/GetDataHandlers.cs:2619 msgid "Bypass SSC is enabled for your account. SSC data will not be loaded or saved." -msgstr "" +msgstr "Tu cuenta tiene activado el bypass de SSC. Los datos SSC no serán cargados ni guardados." #: ../../TShockAPI/Commands.cs:6676 msgid "Cactus" -msgstr "" +msgstr "Cactus" #: ../../TShockAPI/DB/IQueryBuilder.cs:319 msgid "Can't set to true SqlColumn.DefaultCurrentTimestamp when the MySqlDbType is not DateTime" -msgstr "" +msgstr "SqlColumn.DefaultCurrentTimestamp no puede marcarse como true (verdadero) cuando el MySqlDbType no es DateTime" #: ../../TShockAPI/Modules/ModuleManager.cs:56 #, csharp-format msgid "Cannot load module {0} as it does not derive from {1}" -msgstr "" +msgstr "No se puede cargar el módulo {0} ya que no se deriva de {1}" #: ../../TShockAPI/Bouncer.cs:1270 msgid "Certain projectiles have been ignored for cheat detection." @@ -2578,20 +2578,20 @@ msgstr "Algunos proyectiles fueron ignorados por detección de trampas." #: ../../TShockAPI/Commands.cs:4462 #, csharp-format msgid "Changed the maximum spawns to {0}." -msgstr "" +msgstr "Se cambió la generación máxima de enemigos a {0}." #: ../../TShockAPI/Commands.cs:4443 msgid "Changed the maximum spawns to 5." -msgstr "" +msgstr "Se cambió la generación máxima de enemigos a 5." #: ../../TShockAPI/Commands.cs:4501 #, csharp-format msgid "Changed the spawn rate to {0}." -msgstr "" +msgstr "Se cambió la tasa de aparición de enemigos a {0}." #: ../../TShockAPI/Commands.cs:4483 msgid "Changed the spawn rate to 600." -msgstr "" +msgstr "Se cambió la tasa de aparición de enemigos a 600." #: ../../TShockAPI/Commands.cs:368 msgid "Changes the server password." @@ -2603,7 +2603,7 @@ msgstr "Cambia la velocidad del viento." #: ../../TShockAPI/Commands.cs:467 msgid "Changes the world mode." -msgstr "" +msgstr "Cambia el tipo de mundo." #: ../../TShockAPI/Commands.cs:252 msgid "Changes your account's password." @@ -2612,12 +2612,12 @@ msgstr "Cambia la contraseña de tu cuenta." #: ../../TShockAPI/Commands.cs:3646 #, csharp-format msgid "Chat color for \"{0}\" is \"{1}\"." -msgstr "" +msgstr "El color de chat para \"{0}\" es \"{1}\"." #: ../../TShockAPI/Commands.cs:3632 #, csharp-format msgid "Chat color for group \"{0}\" set to \"{1}\"." -msgstr "" +msgstr "Color de chat para grupo \"{0}\" establecido como \"{1}\"." #: ../../TShockAPI/Commands.cs:352 msgid "Checks for TShock updates." @@ -2625,15 +2625,15 @@ msgstr "Busca actualizaciones de TShock." #: ../../TShockAPI/Commands.cs:5186 msgid "clear - Clears the temporary region points." -msgstr "" +msgstr "clear - Elimina los puntos de región temporales." #: ../../TShockAPI/Commands.cs:5814 msgid "Clear Syntax" -msgstr "" +msgstr "Sintaxis de Clear" #: ../../TShockAPI/Commands.cs:2531 msgid "Cleared all users from the angler quest completion list for today." -msgstr "Se han eliminado a todos los usuarios de la lista de misiones completas del pescador." +msgstr "Eliminados todos los usuarios de la lista de misiones completadas hoy del pescador." #: ../../TShockAPI/Commands.cs:537 msgid "Clears item drops or projectiles." @@ -2646,61 +2646,61 @@ msgstr "color - Cambia el color de chat de un grupo." #: ../../TShockAPI/Commands.cs:5329 #, csharp-format msgid "Command aliases: {0}, {1}, {2}" -msgstr "" +msgstr "Alias del comando: {0}, {1}, {2}" #: ../../TShockAPI/Commands.cs:162 msgid "Command failed, check logs for more details." -msgstr "" +msgstr "Comando fallido, compruebe los registros para más detalles." #: ../../TShockAPI/Commands.cs:5260 msgid "Commands ({{0}}/{{1}}):" -msgstr "" +msgstr "Comandos ({{0}}/{{1}}):" #: ../../TShockAPI/Commands.cs:3203 msgid "Commands: add, del, hide, list, send, [warpname]." -msgstr "" +msgstr "Comandos: add, del, hide, list, send, [nombre de warp]." #: ../../TShockAPI/TShock.cs:765 #, csharp-format msgid "Config path has been set to {0}" -msgstr "" +msgstr "La ruta de configuración se ha establecido en {0}" #: ../../TShockAPI/Commands.cs:4395 msgid "Configuration, permissions, and regions reload complete. Some changes may require a server restart." -msgstr "" +msgstr "Recarga de configuración, permisos y regiones completada. Algunos cambios pueden requerir un reinicio del servidor." #: ../../TShockPluginManager/NugetCLI.cs:100 msgid "Connect to the internet to figure out what to download?" -msgstr "" +msgstr "¿Conectarse a Internet para averiguar qué descargar?" #: ../../TShockAPI/TShock.cs:1329 msgid "Connecting via a proxy is not allowed." -msgstr "" +msgstr "No se permite la conexión a través de un proxy." #: ../../TShockAPI/Commands.cs:1778 #, csharp-format msgid "Correct usage: {0}overridessc|{0}ossc " -msgstr "" +msgstr "Uso correcto: {0}overridessc|{0}ossc " #: ../../TShockAPI/Commands.cs:6641 msgid "Corruption Palm" -msgstr "" +msgstr "Palmera Corrupta" #: ../../TShockAPI/TShock.cs:296 #, csharp-format msgid "Could not apply the given log path / log format, defaults will be used. Exception details:\n" "{0}" -msgstr "" +msgstr "No se pudo usar la ruta / formato de logs dado; se usarán los valores predeterminados. Detalles de la excepción: {0}" #: ../../TShockAPI/DB/ResearchDatastore.cs:54 #: ../../TShockAPI/DB/BanManager.cs:82 msgid "Could not find a database library (probably Sqlite3.dll)" -msgstr "" +msgstr "No se encontró una biblioteca de base de datos (probablemente Sqlite3.dll)" #: ../../TShockAPI/Commands.cs:3268 #, csharp-format msgid "Could not find a warp named {0} to remove." -msgstr "" +msgstr "No se pudo encontrar un warp llamado {0} para eliminarlo." #: ../../TShockAPI/Commands.cs:5526 #: ../../TShockAPI/Commands.cs:5610 @@ -2709,17 +2709,17 @@ msgstr "" #: ../../TShockAPI/Commands.cs:5972 #, csharp-format msgid "Could not find any player named \"{0}\"" -msgstr "" +msgstr "No se encontró ningún jugador llamado \"{0}\"" #: ../../TShockAPI/Commands.cs:5928 #, csharp-format msgid "Could not find any player named \"{0}\"." -msgstr "" +msgstr "No se encontró ningún jugador llamado \"{0}\"." #: ../../TShockAPI/Commands.cs:5471 #, csharp-format msgid "Could not find any players named \"{0}\"" -msgstr "" +msgstr "Ninguno de los jugadores se llama \"{0}\"" #: ../../TShockAPI/Commands.cs:1934 #, csharp-format @@ -2733,7 +2733,7 @@ msgstr "No se pudo encontrar al jugador {0}." #: ../../TShockAPI/Commands.cs:5044 msgid "Could not find specified region" -msgstr "" +msgstr "No se pudo encontrar la región especificada" #: ../../TShockAPI/Commands.cs:3291 msgid "Could not find specified warp." @@ -2753,47 +2753,47 @@ msgstr "No se pudo encontrar la región {0}." #: ../../TShockAPI/Commands.cs:1581 msgid "Could not find the target specified. Check that you have the correct spelling." -msgstr "No se pudo encontrar el objetivo especificado. Compruebe que la escritura sea la correcta." +msgstr "No se pudo encontrar el objetivo especificado. Revise que esté bien escrito." #: ../../TShockAPI/Commands.cs:6200 #, csharp-format msgid "Could not rename {0}!" -msgstr "" +msgstr "¡No se pudo renombrar a {0}!" #: ../../TShockAPI/TShock.cs:1448 msgid "Crash attempt via long chat packet." -msgstr "" +msgstr "Intento de crasheo a través de un paquete de chat largo." #: ../../TShockAPI/Commands.cs:595 msgid "Creates a reference tables for Terraria data types and the TShock permission system in the server folder." -msgstr "" +msgstr "Crea tablas de referencia para datos de tipo Terraria y el sistema de permisos TShock en la carpeta del servidor." #: ../../TShockAPI/Commands.cs:5414 msgid "Creates: with the password as part of the owner group." -msgstr "" +msgstr "Crea: con la contraseña como parte del grupo owner." #: ../../TShockAPI/Handlers/NetModules/CreativePowerHandler.cs:53 msgid "CreativePowerHandler received permission check request for unknown creative power" -msgstr "" +msgstr "CreativePowerHandler recibió una petición de comprobación de permiso para un poder creativo desconocido" #: ../../TShockAPI/Handlers/NetModules/CreativeUnlocksHandler.cs:65 #, csharp-format msgid "CreativeUnlocksHandler received non-vanilla unlock request. Random field value: {0} but should be 0 from {1}" -msgstr "" +msgstr "CreativeUnlocksHandler recibió una petición de apertura no-vainilla. Valor de campo al azar: {0} pero debería ser 0 de {1}" #: ../../TShockAPI/Commands.cs:6636 msgid "Crimson Palm" -msgstr "" +msgstr "Palmera Carmesí" #: ../../TShockAPI/Commands.cs:4434 #, csharp-format msgid "Current maximum spawns: {0}." -msgstr "" +msgstr "Número máximo actual de enemigos generados: {0}." #: ../../TShockAPI/Commands.cs:4474 #, csharp-format msgid "Current spawn rate: {0}." -msgstr "" +msgstr "Tasa actual de generación de enemigos: {0}." #: ../../TShockAPI/Bouncer.cs:2686 #, csharp-format @@ -2802,129 +2802,129 @@ msgstr "Intento de Exploit de Muerte: Daño {0}" #: ../../TShockAPI/Bouncer.cs:2704 msgid "Death reason outside of normal bounds." -msgstr "" +msgstr "Razón de muerte fuera de los límites normales." #: ../../TShockAPI/Configuration/TShockConfig.cs:321 #: ../../TShockAPI/Configuration/TShockConfig.cs:357 msgid "Death results in a ban" -msgstr "" +msgstr "Morir resulta en un baneo" #: ../../TShockAPI/Configuration/TShockConfig.cs:349 msgid "Death results in a kick" -msgstr "" +msgstr "Morir resulta en una expulsión" #: ../../TShockAPI/Commands.cs:5187 msgid "define - Defines the region with the given name." -msgstr "" +msgstr "define - Establece la región con el nombre otorgado." #: ../../TShockAPI/Commands.cs:3442 msgid "del - Deletes a group." -msgstr "" +msgstr "del - Elimina un grupo." #: ../../TShockAPI/Commands.cs:3973 msgid "del - Deletes an item ban." -msgstr "" +msgstr "del - Elimina una prohibición de objeto." #: ../../TShockAPI/Commands.cs:4151 msgid "del - Deletes an projectile ban." -msgstr "" +msgstr "del - Elimina una prohibición de proyectil." #: ../../TShockAPI/Commands.cs:4327 msgid "del - Deletes a tile ban." -msgstr "" +msgstr "del - Elimina una prohibición de bloque." #: ../../TShockAPI/Commands.cs:5188 msgid "delete - Deletes the given region." -msgstr "" +msgstr "delete - Elimina la región especificada." #: ../../TShockAPI/Commands.cs:4746 #, csharp-format msgid "Deleted region \"{0}\"." -msgstr "" +msgstr "Región \"{0}\" eliminada." #: ../../TShockAPI/Commands.cs:3443 msgid "delperm - Removes permissions from a group." -msgstr "" +msgstr "delperm - Quita permisos a un grupo." #: ../../TShockAPI/Commands.cs:6627 msgid "Desert Palm" -msgstr "" +msgstr "Palmera Desértica" #: ../../TShockAPI/Commands.cs:2111 msgid "destroytokens - Destroys all current REST tokens." -msgstr "" +msgstr "destroytokens - Destruye todos los tókens REST actuales." #: ../../TShockAPI/Bouncer.cs:480 #: ../../TShockAPI/Bouncer.cs:488 #: ../../TShockAPI/Bouncer.cs:496 msgid "Detected DOOM set to ON position." -msgstr "" +msgstr "Detectado DOOM establecido en la posición ON." #: ../../TShockAPI/Commands.cs:6666 msgid "Diamond Gemtree" -msgstr "" +msgstr "Arbolgema de Diamante" #: ../../TShockAPI/Commands.cs:1881 msgid "Disabled halloween mode." -msgstr "" +msgstr "Modo halloween desactivado." #: ../../TShockAPI/Commands.cs:1900 msgid "Disabled xmas mode." -msgstr "" +msgstr "Modo navideño desactivado." #: ../../TShockAPI/Bouncer.cs:543 #, csharp-format msgid "Disabled. You need to {0}login to load your saved data." -msgstr "Deshabilitado. Necesitas {0}login para cargar tus datos guardados." +msgstr "Deshabilitado. Debes iniciar sesión con {0}login para cargar tus datos guardados." #: ../../TShockAPI/Bouncer.cs:539 msgid "Disabled. You went too far with banned armor." -msgstr "" +msgstr "Deshabilitado. Has ido demasiado lejos usando armadura prohibida." #: ../../TShockAPI/Bouncer.cs:535 msgid "Disabled. You went too far with hacked item stacks." -msgstr "" +msgstr "Deshabilitado. Has ido demasiado lejos con pilas de objetos hackeados." #: ../../TShockAPI/Commands.cs:3974 msgid "disallow - Disallows a group from using an item." -msgstr "" +msgstr "disallow - Le prohíbe a un grupo usar el objeto." #: ../../TShockAPI/Commands.cs:4152 msgid "disallow - Disallows a group from using a projectile." -msgstr "" +msgstr "disallow - Le prohíbe a un grupo usar el proyectil." #: ../../TShockAPI/Commands.cs:4328 msgid "disallow - Disallows a group from place a tile." -msgstr "" +msgstr "disallow - Le prohíbe a un grupo colocar el bloque." #: ../../TShockPluginManager/NugetCLI.cs:140 #: ../../TShockPluginManager/NugetCLI.cs:161 msgid "Download and install the given packages?" -msgstr "" +msgstr "¿Descargar e instalar los paquetes en cuestión?" #: ../../TShockAPI/Commands.cs:2648 msgid "Duke Fishron" -msgstr "" +msgstr "Duque Fishron" #: ../../TShockAPI/Commands.cs:6617 msgid "Ebonwood Tree" -msgstr "" +msgstr "Árbol de Ébano" #: ../../TShockAPI/Commands.cs:6658 msgid "Emerald Gemtree" -msgstr "" +msgstr "Arbolgema de Esmeralda" #: ../../TShockAPI/Commands.cs:1879 msgid "Enabled halloween mode." -msgstr "" +msgstr "Modo halloween activado." #: ../../TShockAPI/Commands.cs:1898 msgid "Enabled xmas mode." -msgstr "" +msgstr "Modo navideño activado." #: ../../TShockAPI/Commands.cs:488 msgid "Enables starting and stopping various world events." -msgstr "" +msgstr "Permite iniciar y detener varios eventos del mundo." #: ../../TShockAPI/DB/GroupManager.cs:665 #, csharp-format @@ -2937,22 +2937,22 @@ msgstr "Error: los dos nombres son iguales." #: ../../TShockAPI/Commands.cs:2777 msgid "Everscream" -msgstr "" +msgstr "Gritoeterno" #: ../../TShockAPI/Commands.cs:1407 #, csharp-format msgid "Example usage: {0}" -msgstr "" +msgstr "Ejemplo de uso: {0}" #: ../../TShockAPI/Commands.cs:5463 #, csharp-format msgid "Example usage: {0} \"{1}\" \"{2}\"" -msgstr "" +msgstr "Ejemplo de uso: {0} \"{1}\" \"{2}\"" #: ../../TShockAPI/Commands.cs:6369 #, csharp-format msgid "Example usage: {0} \"{1}\" {2}" -msgstr "" +msgstr "Ejemplo de uso: {0} \"{1}\" {2}" #: ../../TShockAPI/Commands.cs:1399 #: ../../TShockAPI/Commands.cs:1415 @@ -2962,7 +2962,7 @@ msgstr "" #: ../../TShockAPI/Commands.cs:6012 #, csharp-format msgid "Example usage: {0} {1}" -msgstr "" +msgstr "Ejemplo de uso: {0} {1}" #: ../../TShockAPI/Commands.cs:5520 #: ../../TShockAPI/Commands.cs:5685 @@ -2970,91 +2970,91 @@ msgstr "" #: ../../TShockAPI/Commands.cs:6309 #, csharp-format msgid "Example usage: {0} {1} {2}" -msgstr "" +msgstr "Ejemplo de uso: {0} {1} {2}" #: ../../TShockAPI/Commands.cs:6419 #, csharp-format msgid "Example usage: {0} {1} {2} {3}" -msgstr "" +msgstr "Ejemplo de uso: {0} {1} {2} {3}" #: ../../TShockAPI/Commands.cs:1391 #, csharp-format msgid "Example usage: {0} {1} {2} {3} {4}" -msgstr "" +msgstr "Ejemplo de uso: {0} {1} {2} {3} {4}" #: ../../TShockAPI/Commands.cs:5601 #, csharp-format msgid "Example usage: {0} <{1}> <{2}>" -msgstr "" +msgstr "Ejemplo de uso: {0} <{1}> <{2}>" #: ../../TShockAPI/Commands.cs:2002 msgid "Example: /sudo /ban add particles 2d Hacking." -msgstr "" +msgstr "Ejemplo: /sudo /ban add particles 2d Hackeando." #: ../../TShockAPI/Commands.cs:3206 #, csharp-format msgid "Examples: {0}warp add foobar, {0}warp hide foobar true, {0}warp foobar." -msgstr "" +msgstr "Ejemplos: {0}warp add foobar, {0}warp hide foobar true, {0}warp foobar." #: ../../TShockAPI/Commands.cs:328 msgid "Executes a command as the super admin." -msgstr "" +msgstr "Ejecuta un comando como súper admin." #: ../../TShockAPI/GetDataHandlers.cs:4371 msgid "Exploit attempt detected!" -msgstr "" +msgstr "¡Intento de exploit detectado!" #: ../../TShockAPI/Commands.cs:1494 #, csharp-format msgid "Failed to add ban for identifier: {0}." -msgstr "No se ha podido agregar el ban para el identificador {0}." +msgstr "No se pudo agregar el ban para el identificador {0}." #: ../../TShockAPI/Rest/RestManager.cs:671 #, csharp-format msgid "Failed to add ban. {0}" -msgstr "Error al agregar el ban. {0}" +msgstr "No se pudo agregar el ban. {0}" #: ../../TShockAPI/DB/GroupManager.cs:324 #, csharp-format msgid "Failed to add group {0}." -msgstr "No se ha podido agregar el grupo {0}." +msgstr "No se pudo agregar el grupo {0}." #: ../../TShockAPI/DB/GroupManager.cs:512 #: ../../TShockAPI/DB/GroupManager.cs:513 #, csharp-format msgid "Failed to delete group {0}." -msgstr "No se ha podido eliminar el grupo {0}." +msgstr "No se pudo eliminar el grupo {0}." #: ../../TShockAPI/Commands.cs:2524 msgid "Failed to find any users by that name on the list." -msgstr "No se ha podido encontrar ningún usuario con este nombre en la lista." +msgstr "No se pudo encontrar ningún usuario con ese nombre en la lista." #: ../../TShockAPI/Commands.cs:1638 #: ../../TShockAPI/Rest/RestManager.cs:698 msgid "Failed to remove ban." -msgstr "Error al eliminar el ban." +msgstr "No se pudo eliminar el baneo." #: ../../TShockAPI/DB/GroupManager.cs:480 #, csharp-format msgid "Failed to rename group {0}." -msgstr "Error al renombrar el grupo {0}." +msgstr "No se pudo renombrar el grupo {0}." #: ../../TShockAPI/Commands.cs:5145 msgid "Failed to rename the region." -msgstr "Error al renombrar la región." +msgstr "No se pudo renombrar la región." #: ../../TShockAPI/Bouncer.cs:2685 msgid "Failed to shade polygon normals." -msgstr "" +msgstr "Error al sombrear las normales del polígono." #: ../../TShockAPI/DB/GroupManager.cs:369 #, csharp-format msgid "Failed to update group \"{0}\"." -msgstr "Error al actualizar el grupo \"{0}\"." +msgstr "No se pudo actualizar el grupo \"{0}\"." #: ../../TShockAPI/Commands.cs:1864 msgid "Failed to upload your character data to the server. Are you logged-in to an account?" -msgstr "Error al subir los datos de tu personaje al servidor. ¿Has iniciado sesión en una cuenta?" +msgstr "No se pudo subir tus datos de personaje al servidor. ¿Iniciaste sesión en una cuenta?" #: ../../TShockAPI/Rest/Rest.cs:245 msgid "Fatal Startup Exception" @@ -3067,20 +3067,20 @@ msgstr "Excepción fatal en la inicialización de TShock: no se pudo conectar a #: ../../TShockAPI/DB/UserManager.cs:218 #, csharp-format msgid "FetchHashedPasswordAndGroup SQL returned an error: {0}" -msgstr "" +msgstr "SQL FetchHashedPasswordAndGroup devolvió un error: {0}" #: ../../TShockAPI/Commands.cs:5683 msgid "Firework Syntax" -msgstr "" +msgstr "Sintaxis de Firework" #: ../../TShockAPI/Commands.cs:1944 msgid "For example, 1d and 10h-30m+2m are both valid time strings, but 2 is not." -msgstr "" +msgstr "Por ejemplo, 1d y 10h-30m+2m son ambas cadenas de tiempo válidas, pero 2 no lo es." #: ../../TShockAPI/Commands.cs:1374 #, csharp-format msgid "For more info, use {0} {1} or {2} {3}" -msgstr "" +msgstr "Para más información, usa {0} {1} o {2} {3}" #: ../../TShockAPI/Commands.cs:514 msgid "Forces all liquids to update immediately." @@ -3090,15 +3090,15 @@ msgstr "Hace que todos los líquidos se actualicen inmediatamente." #, csharp-format msgid "Gave {0} {1} {2}." msgid_plural "Gave {0} {1} {2}s." -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Dio {0} {1} {2}." +msgstr[1] "Dio {0} {1} {2}s." #: ../../TShockAPI/Commands.cs:6140 #, csharp-format msgid "Gave {0} {1}." msgid_plural "Gave {0} {1}s." -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Dio {0} {1}." +msgstr[1] "Dio {0} {1}s." #: ../../TShockAPI/GetDataHandlers.cs:3796 #, csharp-format @@ -3473,11 +3473,11 @@ msgstr "" #: ../../TShockAPI/DB/UserManager.cs:291 #, csharp-format msgid "GetUser SQL returned an error {0}" -msgstr "SQL GetUser devolvió un error ({0}" +msgstr "SQL GetUser devolvió un error {0}" #: ../../TShockAPI/Commands.cs:6417 msgid "Give Buff Syntax and Example" -msgstr "" +msgstr "Sintaxis y Ejemplo de Give Buff" #: ../../TShockAPI/Commands.cs:541 msgid "Gives another player a buff or debuff for an amount of time. Putting -1 for time will set it to 415 days." @@ -3497,12 +3497,12 @@ msgstr "Te da un objeto." #: ../../TShockAPI/Commands.cs:6690 msgid "Glowing Mushroom Tree" -msgstr "" +msgstr "Árbol de Champiñón Brillante" #: ../../TShockAPI/Handlers/LandGolfBallInCupHandler.cs:123 #, csharp-format msgid "GolfPacketHandler: Player did not have create a golf club projectile the last 5 seconds! - From {0}" -msgstr "" +msgstr "GolfPacketHandler: ¡El jugador no había creado un proyectil de palo de golf en los últimos 5 segundos! - De {0}" #: ../../TShockAPI/Commands.cs:3506 #, csharp-format @@ -3604,7 +3604,7 @@ msgstr "El grupo {0} se agregó correctamente." #: ../../TShockAPI/Commands.cs:3454 msgid "Group Sub-Commands ({{0}}/{{1}}):" -msgstr "" +msgstr "Sub-Comandos de Group ({{0}}/{{1}}):" #: ../../TShockAPI/Commands.cs:3745 msgid "Groups ({{0}}/{{1}}):" @@ -3616,12 +3616,12 @@ msgstr "Crece plantas en tu ubicación." #: ../../TShockAPI/Commands.cs:6631 msgid "Hallow Palm" -msgstr "" +msgstr "Palmera Sagrada" #: ../../TShockAPI/GetDataHandlers.cs:4372 #, csharp-format msgid "HandleSyncCavernMonsterType: Player is trying to modify NPC cavernMonsterType; this is a crafted packet! - From {0}" -msgstr "" +msgstr "HandleSyncCavernMonsterType: El jugador está intentando modificar el cavernMonsterType de PNJ; ¡este es un paquete creado artificialmente! - De {0}" #: ../../TShockAPI/Commands.cs:2593 msgid "Hardmode is disabled in the server configuration file." @@ -3637,16 +3637,16 @@ msgstr "Se ha activado el modo difícil." #: ../../TShockAPI/Commands.cs:6307 msgid "Heal Syntax and Example" -msgstr "" +msgstr "Sintaxis y Ejemplo de Heal" #: ../../TShockAPI/Bouncer.cs:2097 msgid "HealOtherPlayer cheat attempt!" -msgstr "" +msgstr "¡Intento de trampa con HealOtherPlayer!" #: ../../TShockAPI/Bouncer.cs:2106 #, csharp-format msgid "HealOtherPlayer threshold exceeded {0}." -msgstr "" +msgstr "Umbral de HealOtherPlayer excedió {0}." #: ../../TShockAPI/Commands.cs:549 msgid "Heals a player in HP and MP." @@ -3654,7 +3654,7 @@ msgstr "Restaura los HP y MP del jugador." #: ../../TShockAPI/Commands.cs:6684 msgid "Herb" -msgstr "" +msgstr "Hierba" #: ../../TShockAPI/Commands.cs:4658 msgid "Hit a block to get the name of the region." @@ -3669,7 +3669,7 @@ msgstr "Golpea un bloque para definir el punto {0}." #: ../../TShockAPI/ItemBans.cs:163 #, csharp-format msgid "holding banned item: {0}" -msgstr "Tienes un objeto baneado: {0}" +msgstr "sosteniendo un objeto prohibido: {0}" #: ../../TShockAPI/Commands.cs:1235 #, csharp-format @@ -3678,100 +3678,100 @@ msgstr "ID: {0}" #: ../../TShockAPI/Commands.cs:6310 msgid "If no amount is specified, it will default to healing the target player by their max HP." -msgstr "Si no se especifica una cantidad el jugador se curará hasta su vida máxima." +msgstr "Si no se especifica una cantidad el jugador curará toda su vida." #: ../../TShockAPI/Bouncer.cs:1392 msgid "If this player wasn't hacking, please report the damage threshold they were disabled for to TShock so we can improve this!" -msgstr "" +msgstr "Si este jugador no estaba haciendo trampa, ¡favor reportar a TShock el umbral de daño por el que fue deshabilitado para mejorar el sistema!" #: ../../TShockAPI/Bouncer.cs:2113 msgid "If this player wasn't hacking, please report the HealOtherPlayer threshold they were disabled for to TShock so we can improve this!" -msgstr "" +msgstr "Si este jugador no estaba haciendo trampa, ¡favor reportar a TShock el umbral de HealOtherPlayer por el que fue deshabilitado para mejorar el sistema!" #: ../../TShockAPI/Bouncer.cs:1295 msgid "If this player wasn't hacking, please report the projectile create threshold they were disabled for to TShock so we can improve this!" -msgstr "" +msgstr "Si este jugador no estaba haciendo trampa, ¡favor reportar a TShock el umbral de proyectiles creados por el que fue deshabilitado para mejorar el sistema!" #: ../../TShockAPI/Bouncer.cs:927 msgid "If this player wasn't hacking, please report the tile kill threshold they were disabled for to TShock so we can improve this!" -msgstr "" +msgstr "Si este jugador no estaba haciendo trampa, ¡favor reportar a TShock el umbral de destrucción de bloques por el que fue deshabilitado para mejorar el sistema!" #: ../../TShockAPI/Bouncer.cs:1695 msgid "If this player wasn't hacking, please report the tile liquid threshold they were disabled for to TShock so we can improve this!" -msgstr "" +msgstr "Si este jugador no estaba haciendo trampa, ¡favor reportar a TShock el umbral de bloques líquidos por el que fue deshabilitado para mejorar el sistema!" #: ../../TShockAPI/Bouncer.cs:945 msgid "If this player wasn't hacking, please report the tile place threshold they were disabled for to TShock so we can improve this!" -msgstr "" +msgstr "Si este jugador no estaba haciendo trampa, ¡favor reportar a TShock el umbral de colocación de bloques por el que fue deshabilitado para mejorar el sistema!" #: ../../TShockAPI/GetDataHandlers.cs:3026 msgid "If this was not skeletron prime related, please report to TShock what happened." -msgstr "" +msgstr "Si esto no estaba relacionado con Esqueletrón Prime, por favor reporta a TShock lo ocurrido." #: ../../TShockAPI/Commands.cs:5377 msgid "If you are locked out of all admin accounts, ask for help on https://tshock.co/" -msgstr "" +msgstr "Si perdiste el acceso a todas las cuentas de administrador, pide ayuda en https://tshock.co/" #: ../../TShockAPI/Commands.cs:5817 #, csharp-format msgid "If you do not specify a radius, it will use a default radius of {0} around your character." -msgstr "" +msgstr "Si no especificas un radio, usará uno predeterminado de {0} alrededor de tu personaje." #: ../../TShockAPI/Commands.cs:6370 #, csharp-format msgid "If you don't specify the duration, it will default to {0} seconds." -msgstr "" +msgstr "Si no especificas una duración, se usara la predeterminada de {0} segundos." #: ../../TShockAPI/Commands.cs:857 msgid "If you forgot your password, contact the administrator for help." -msgstr "" +msgstr "Si olvidaste tu contraseña, pide ayuda al administrador." #: ../../TShockAPI/Commands.cs:6371 #, csharp-format msgid "If you put {0} as the duration, it will use the max possible time of 415 days." -msgstr "" +msgstr "Si estableces {0} como la duración, usará el máximo tiempo posible que es 415 días." #: ../../TShockAPI/Commands.cs:5416 #, csharp-format msgid "If you understand, please {0}login now, and then type {0}setup." -msgstr "" +msgstr "Si has entendido, por favor usa {0}login , y a continuación teclea {0}setup." #: ../../TShockPluginManager/NugetCLI.cs:142 msgid "If you want to know which plugins need which dependencies, press E." -msgstr "" +msgstr "Si deseas saber qué plugins necesitan qué dependencias, pulsa E." #: ../../TShockPluginManager/NugetCLI.cs:162 msgid "If you'd like to see which plugins need which dependencies again, press E." -msgstr "" +msgstr "Si deseas ver nuevamente qué plugins necesitan qué dependencias, pulsa E." #: ../../TShockAPI/Bouncer.cs:986 msgid "If you're seeing this message and you know what that player did, please report it to TShock for further investigation." -msgstr "" +msgstr "Si ves este mensaje y sabes lo que hizo ese jugador, favor reportar a TShock para investigar más a fondo." #: ../../TShockAPI/Bouncer.cs:1341 msgid "Ignoring shrapnel per config.." -msgstr "" +msgstr "Ignorando esquirlas de acuerdo a la configuración.." #: ../../TShockAPI/GetDataHandlers.cs:2465 msgid "Illegal name: prefixes tsi: and tsn: are forbidden." -msgstr "" +msgstr "Nombre ilegal: los prefijos tsi: y tsn: están prohibidos." #: ../../TShockAPI/Handlers/IllegalPerSe/EmojiPlayerMismatch.cs:19 #, csharp-format msgid "IllegalPerSe: Emoji packet rejected for ID spoofing. Expected {0}, received {1} from {2}." -msgstr "" +msgstr "IllegalPerSe: Paquete de emoji rechazado por falsificación de ID. Esperado {0}, recibido {1} de {2}." #: ../../TShockAPI/Commands.cs:3189 msgid "Incoming teleports are now allowed." -msgstr "" +msgstr "Ahora se permiten teletransportes entrantes." #: ../../TShockAPI/Commands.cs:3191 msgid "Incoming teleports are now disabled." -msgstr "" +msgstr "Ahora se bloquean teletransportes entrantes." #: ../../TShockAPI/Commands.cs:5403 msgid "Incorrect setup code. This incident has been logged." -msgstr "El código de configuración es incorrecto. Este incidente se ha reportado." +msgstr "Código de configuración incorrecto. Incidente archivado en el registro." #: ../../TShockAPI/DB/TileManager.cs:223 #: ../../TShockAPI/DB/ProjectileManager.cs:223 @@ -3781,12 +3781,12 @@ msgstr "Grupo infinito de jerarquía ({0})" #: ../../TShockAPI/Commands.cs:5197 msgid "info [-d] - Displays several information about the given region." -msgstr "" +msgstr "info [-d] - Muestra bastante información sobre la región dada." #: ../../TShockAPI/Commands.cs:4986 #, csharp-format msgid "Information About Region \"{0}\" ({{0}}/{{1}}):" -msgstr "Información sobre la Región \"{0}\" ({{0}}/{{1}}):" +msgstr "Información Sobre la Región \"{0}\" ({{0}}/{{1}}):" #: ../../TShockAPI/Commands.cs:1232 msgid "Information about the currently running world" @@ -3798,7 +3798,7 @@ msgstr "Se ha producido un error al insertar el ban en la base de datos." #: ../../TShockPluginManager/NugetCLI.cs:39 msgid "Install the plugins as specified in the plugins.json" -msgstr "" +msgstr "Instala los plugins como se especifica en plugins.json" #: ../../TShockAPI/Rest/Rest.cs:426 msgid "Internal server error." @@ -3807,26 +3807,26 @@ msgstr "Error interno del servidor." #: ../../TShockAPI/Commands.cs:1505 #, csharp-format msgid "Invalid Ban Add syntax. Refer to {0} for details on how to use the {1} command" -msgstr "" +msgstr "Sintaxis de Ban Add inválida. Revisa {0} para ver detalles de uso para el comando {1}" #: ../../TShockAPI/Commands.cs:1621 #, csharp-format msgid "Invalid Ban Del syntax. Refer to {0} for details on how to use the {1} command" -msgstr "" +msgstr "Sintaxis de Ban Del inválida. Revisa {0} para ver detalles de uso para el comando {1}" #: ../../TShockAPI/Commands.cs:1680 #, csharp-format msgid "Invalid Ban Details syntax. Refer to {0} for details on how to use the {1} command" -msgstr "" +msgstr "Sintaxis de Ban Details inválida. Revisa {0} para ver detalles de uso para el comando {1}" #: ../../TShockAPI/Commands.cs:1658 #, csharp-format msgid "Invalid Ban List syntax. Refer to {0} for details on how to use the {1} command" -msgstr "" +msgstr "Sintaxis de Ban List inválida. Revisa {0} para ver detalles de uso para el comando {1}" #: ../../TShockAPI/DB/UserManager.cs:500 msgid "Invalid BCrypt work factor in config file! Creating new hash using default work factor." -msgstr "" +msgstr "¡Factor de trabajo BCrypt inválido en el archivo de configuración! Creando nuevo hash usando el factor de trabajo predeterminado." #: ../../TShockAPI/Commands.cs:2608 msgid "Invalid boss amount." @@ -3834,11 +3834,11 @@ msgstr "La cantidad de jefes no es válida." #: ../../TShockAPI/Commands.cs:2821 msgid "Invalid boss type!" -msgstr "El tipo de jefe no es válido." +msgstr "¡El tipo de jefe no es válido!" #: ../../TShockAPI/Commands.cs:6470 msgid "Invalid buff ID!" -msgstr "El ID del buff no es válido." +msgstr "¡El ID de buff no es válido!" #: ../../TShockAPI/Commands.cs:680 #, csharp-format @@ -3847,27 +3847,27 @@ msgstr "Se ha introducido un comando inválido. Escriba {0}help para obtener una #: ../../TShockAPI/Commands.cs:5275 msgid "Invalid command." -msgstr "" +msgstr "Comando inválido." #: ../../TShockAPI/Commands.cs:3131 msgid "Invalid destination NPC." -msgstr "El NPC de destino no es válido." +msgstr "El PNJ destino no es válido." #: ../../TShockAPI/Commands.cs:2941 #: ../../TShockAPI/Commands.cs:2972 #: ../../TShockAPI/Commands.cs:3011 #: ../../TShockAPI/Commands.cs:3084 msgid "Invalid destination player." -msgstr "El jugador de destino no es válido." +msgstr "El jugador destino no es válido." #: ../../TShockAPI/Commands.cs:2240 #, csharp-format msgid "Invalid event type. Valid event types: {0}." -msgstr "" +msgstr "Tipo de evento inválido. Tipos de eventos válidos: {0}." #: ../../TShockAPI/Commands.cs:2382 msgid "Invalid frost moon event wave." -msgstr "" +msgstr "Ronda inválida para el evento de luna helada." #: ../../TShockAPI/Commands.cs:3765 #: ../../TShockAPI/Commands.cs:3868 @@ -3882,44 +3882,44 @@ msgstr "Grupo inválido." #: ../../TShockAPI/Commands.cs:2401 #, csharp-format msgid "Invalid invasion type. Valid invasion types: {0}." -msgstr "" +msgstr "Tipo de invasión inválido. Tipos válidos de invasión: {0}." #: ../../TShockAPI/Commands.cs:6083 #: ../../TShockAPI/Commands.cs:6232 #: ../../TShockAPI/Commands.cs:6295 msgid "Invalid item type!" -msgstr "" +msgstr "¡Tipo de objeto inválido!" #: ../../TShockAPI/Commands.cs:3807 #: ../../TShockAPI/Commands.cs:3858 #: ../../TShockAPI/Commands.cs:3903 #: ../../TShockAPI/Commands.cs:3929 msgid "Invalid item." -msgstr "" +msgstr "Objeto inválido." #: ../../TShockAPI/Commands.cs:4455 #, csharp-format msgid "Invalid maximum spawns. Acceptable range is {0} to {1}." -msgstr "" +msgstr "Máximo de enemigos generados inválido. El rango aceptable es de {0} a {1}." #: ../../TShockAPI/Commands.cs:2860 #: ../../TShockAPI/Commands.cs:6166 msgid "Invalid mob type!" -msgstr "" +msgstr "¡Tipo de enemigo inválido!" #: ../../TShockAPI/Commands.cs:2844 #: ../../TShockAPI/Commands.cs:2900 msgid "Invalid mob type." -msgstr "" +msgstr "Tipo de enemigo inválido." #: ../../TShockAPI/Commands.cs:2569 #, csharp-format msgid "Invalid mode world mode. Valid modes: {0}" -msgstr "" +msgstr "Modo de mundo inválido. Modos válidos: {0}" #: ../../TShockAPI/Commands.cs:1421 msgid "Invalid page number. Page number must be numeric." -msgstr "" +msgstr "Número de página inválido. La cifra debe ser numérica." #: ../../TShockAPI/DB/GroupManager.cs:309 #, csharp-format @@ -3933,12 +3933,12 @@ msgstr "Grupo padre {0} inválido para el grupo {1}." #: ../../TShockAPI/Commands.cs:928 msgid "Invalid password." -msgstr "Contraseña invalida." +msgstr "Contraseña inválida." #: ../../TShockAPI/Commands.cs:6263 #: ../../TShockAPI/Commands.cs:6718 msgid "Invalid player!" -msgstr "" +msgstr "¡Jugador inválido!" #: ../../TShockAPI/Commands.cs:1255 msgid "Invalid player." @@ -3946,7 +3946,7 @@ msgstr "Jugador inválido." #: ../../TShockAPI/Commands.cs:4040 msgid "Invalid projectile ID!" -msgstr "ID de proyectil inválido." +msgstr "¡ID de proyectil inválido!" #: ../../TShockAPI/Commands.cs:4077 #: ../../TShockAPI/Commands.cs:4098 @@ -3956,12 +3956,12 @@ msgstr "ID de proyectil inválido." #: ../../TShockAPI/Commands.cs:2364 msgid "Invalid pumpkin moon event wave." -msgstr "" +msgstr "Ronda inválida para el evento de luna calabaza." #: ../../TShockAPI/Commands.cs:5127 #, csharp-format msgid "Invalid region \"{0}\"." -msgstr "" +msgstr "Región \"{0}\" inválida." #: ../../TShockAPI/Rest/Rest.cs:247 #, csharp-format @@ -3969,386 +3969,389 @@ msgid "Invalid REST configuration: \n" "You may already have a REST service bound to port {0}. \n" "Please adjust your configuration and restart the server. \n" "Press any key to exit." -msgstr "" +msgstr "Configuración REST inválida: \n" +"Puede que tengas un servicio REST enlazado al puerto {0}. \n" +"Por favor ajusta tu configuración y reinicia el servidor. \n" +"Presiona cualquier tecla para salir." #: ../../TShockAPI/GetDataHandlers.cs:3231 msgid "Invalid server password." -msgstr "" +msgstr "Contraseña de servidor inválida." #: ../../TShockAPI/Commands.cs:3782 #, csharp-format msgid "Invalid subcommand! Type {0}group help for more information on valid commands." -msgstr "" +msgstr "¡Subcomando inválido! Teclea {0}group help para más información sobre comandos válidos." #: ../../TShockAPI/Commands.cs:4009 #, csharp-format msgid "Invalid subcommand. Type {0}itemban help for more information on valid subcommands." -msgstr "" +msgstr "¡Subcomando inválido! Teclea {0}itemban help para más información sobre subcomandos válidos." #: ../../TShockAPI/Commands.cs:4187 #, csharp-format msgid "Invalid subcommand. Type {0}projban help for more information on valid subcommands." -msgstr "" +msgstr "¡Subcomando inválido! Teclea {0}projban help para más información sobre subcomandos válidos." #: ../../TShockAPI/Commands.cs:4363 #, csharp-format msgid "Invalid subcommand. Type {0}tileban help for more information on valid subcommands." -msgstr "" +msgstr "¡Subcomando inválido! Teclea {0}tileban help para más información sobre subcomandos válidos." #: ../../TShockAPI/Commands.cs:3641 msgid "Invalid syntax for color, expected \"rrr,ggg,bbb\"." -msgstr "" +msgstr "Sintaxis incorrecta para color, se esperaba \"rrr,vvv,aaa\" (rojo, verde, azul)." #: ../../TShockAPI/Commands.cs:1915 msgid "Invalid syntax." -msgstr "Sintáxis inválida." +msgstr "Sintaxis inválida." #: ../../TShockAPI/Commands.cs:2332 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}worldevent invasion [invasion type] [invasion wave]." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}worldevent invasion [tipo de invasión] [ronda de invasión]." #: ../../TShockAPI/Commands.cs:1270 #: ../../TShockAPI/Commands.cs:1304 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}accountinfo ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}accountinfo ." #: ../../TShockAPI/Commands.cs:5754 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}aliases " -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}aliases " #: ../../TShockAPI/Commands.cs:3369 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}group add [permissions]." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}group add [permisos]." #: ../../TShockAPI/Commands.cs:3398 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}group addperm ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}group addperm ." #: ../../TShockAPI/Commands.cs:3606 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}group color [new color(000,000,000)]." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}group color [color nuevo(000,000,000)]." #: ../../TShockAPI/Commands.cs:3679 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}group del ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}group del ." #: ../../TShockAPI/Commands.cs:3703 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}group delperm ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}group delperm ." #: ../../TShockAPI/Commands.cs:3756 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}group listperm [page]." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}group listperm [página]." #: ../../TShockAPI/Commands.cs:3466 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}group parent [new parent group name]." -msgstr "Sintaxis no válida. Sintaxis correcta: {0}grupo parent [nuevo nombre del grupo padre]." +msgstr "Sintaxis inválida. Sintaxis correcta: {0}group parent [nuevo nombre del grupo padre]." #: ../../TShockAPI/Commands.cs:3561 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}group prefix [new prefix]." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}group prefix [prefijo nuevo]." #: ../../TShockAPI/Commands.cs:3656 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}group rename ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}group rename ." #: ../../TShockAPI/Commands.cs:3516 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}group suffix [new suffix]." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}group suffix [sufijo nuevo]." #: ../../TShockAPI/Commands.cs:5241 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}help " -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}help " #: ../../TShockAPI/Commands.cs:6058 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}item [item amount] [prefix id/name]" -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}item [cantidad del objeto] [id/nombre del prefijo]" #: ../../TShockAPI/Commands.cs:3800 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}itemban add ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}itemban add ." #: ../../TShockAPI/Commands.cs:3851 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}itemban allow ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}itemban allow ." #: ../../TShockAPI/Commands.cs:3896 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}itemban del ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}itemban del ." #: ../../TShockAPI/Commands.cs:3922 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}itemban disallow ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}itemban disallow ." #: ../../TShockAPI/Commands.cs:1311 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}kick [reason]." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}kick [motivo]." #: ../../TShockAPI/Commands.cs:5424 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}me " -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}me " #: ../../TShockAPI/Commands.cs:5437 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}p " -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}p " #: ../../TShockAPI/Commands.cs:4030 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}projban add " -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}projban add " #: ../../TShockAPI/Commands.cs:4049 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}projban allow ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}projban allow ." #: ../../TShockAPI/Commands.cs:4086 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}projban del ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}projban del ." #: ../../TShockAPI/Commands.cs:4107 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}projban disallow ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}projban disallow ." #: ../../TShockAPI/Commands.cs:4796 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}region allow ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}region allow ." #: ../../TShockAPI/Commands.cs:4866 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}region allowg ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}region allowg ." #: ../../TShockAPI/Commands.cs:4710 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}region define ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}region define ." #: ../../TShockAPI/Commands.cs:4752 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}region delete ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}region delete ." #: ../../TShockAPI/Commands.cs:4925 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}region info [-d] [page]." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}region info [-d] [página]." #: ../../TShockAPI/Commands.cs:4733 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}region protect ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}region protect ." #: ../../TShockAPI/Commands.cs:4831 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}region remove ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}region remove ." #: ../../TShockAPI/Commands.cs:4901 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}region removeg ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}region removeg ." #: ../../TShockAPI/Commands.cs:5109 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}region rename " -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}region rename " #: ../../TShockAPI/Commands.cs:5099 #: ../../TShockAPI/Commands.cs:5102 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}region resize " -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}region resize " #: ../../TShockAPI/Commands.cs:5159 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}region tp ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}region tp ." #: ../../TShockAPI/Commands.cs:5047 #: ../../TShockAPI/Commands.cs:5050 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}region z <#>" -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}region z <#>" #: ../../TShockAPI/Commands.cs:1037 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}register ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}register ." #: ../../TShockAPI/Commands.cs:6157 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}renameNPC " -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}renameNPC " #: ../../TShockAPI/Commands.cs:4402 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}serverpassword \"\"." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}serverpassword \"\"." #: ../../TShockAPI/Commands.cs:4583 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}slap [damage]." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}slap [daño]." #: ../../TShockAPI/Commands.cs:2601 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}spawnboss [amount]." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}spawnboss [cantidad]." #: ../../TShockAPI/Commands.cs:2839 #: ../../TShockAPI/Commands.cs:2851 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}spawnmob [amount]." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}spawnmob [cantidad]." #: ../../TShockAPI/Commands.cs:4206 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}tileban add ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}tileban add ." #: ../../TShockAPI/Commands.cs:4225 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}tileban allow ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}tileban allow ." #: ../../TShockAPI/Commands.cs:4262 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}tileban del ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}tileban del ." #: ../../TShockAPI/Commands.cs:4283 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}tileban disallow ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}tileban disallow ." #: ../../TShockAPI/Commands.cs:2931 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}tp [player 2]." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}tp [jugador 2]." #: ../../TShockAPI/Commands.cs:2933 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}tp ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}tp ." #: ../../TShockAPI/Commands.cs:3058 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}tphere ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}tphere ." #: ../../TShockAPI/Commands.cs:3056 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}tphere ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}tphere ." #: ../../TShockAPI/Commands.cs:3103 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}tpnpc ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}tpnpc ." #: ../../TShockAPI/Commands.cs:3167 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}tppos ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}tppos ." #: ../../TShockAPI/Commands.cs:1249 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}userinfo ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}userinfo ." #: ../../TShockAPI/Commands.cs:3202 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}warp [command] [arguments]." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}warp [comando] [argumentos]." #: ../../TShockAPI/Commands.cs:3211 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}warp [name] or {0}warp list ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}warp [nombre] o {0}warp list ." #: ../../TShockAPI/Commands.cs:3254 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}warp add [name]." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}warp add [nombre]." #: ../../TShockAPI/Commands.cs:3271 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}warp del [name]." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}warp del [nombre]." #: ../../TShockAPI/Commands.cs:3294 #: ../../TShockAPI/Commands.cs:3297 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}warp hide [name] ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}warp hide [nombre] ." #: ../../TShockAPI/Commands.cs:3305 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}warp send [player] [warpname]." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}warp send [jugador] [nombreDeWarp]." #: ../../TShockAPI/Commands.cs:4625 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}wind ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}wind ." #: ../../TShockAPI/Commands.cs:2146 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}worldevent ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}worldevent ." #: ../../TShockAPI/Commands.cs:2548 #, csharp-format msgid "Invalid syntax. Proper syntax: {0}worldmode ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: {0}worldmode ." #: ../../TShockAPI/Commands.cs:4736 #, csharp-format msgid "Invalid syntax. Proper syntax: /region protect ." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: /region protect ." #: ../../TShockAPI/Commands.cs:4676 msgid "Invalid syntax. Proper syntax: /region set <1/2>." -msgstr "" +msgstr "Sintaxis inválida. Sintaxis correcta: /region set <1/2>." #: ../../TShockAPI/Commands.cs:3151 #: ../../TShockAPI/Commands.cs:3312 #: ../../TShockAPI/Commands.cs:4589 #: ../../TShockAPI/Commands.cs:4597 msgid "Invalid target player." -msgstr "" +msgstr "Jugador destino inválido." #: ../../TShockAPI/Commands.cs:1627 #: ../../TShockAPI/Commands.cs:1686 #, csharp-format msgid "Invalid Ticket Number. Refer to {0} for details on how to use the {1} command" -msgstr "" +msgstr "Número de Ticket inválido. Consulte {0} para más detalles sobre cómo utilizar el comando {1}" #: ../../TShockAPI/Commands.cs:4216 #: ../../TShockAPI/Commands.cs:4253 @@ -4359,27 +4362,27 @@ msgstr "ID de bloque inválido." #: ../../TShockAPI/Commands.cs:1943 msgid "Invalid time string! Proper format: _d_h_m_s, with at least one time specifier." -msgstr "" +msgstr "¡Cadena de tiempo inválida! Formato correcto: _d_h_m_s, con al menos un especificador de hora." #: ../../TShockAPI/Commands.cs:4548 #: ../../TShockAPI/Commands.cs:4557 msgid "Invalid time string. Proper format: hh:mm, in 24-hour time." -msgstr "" +msgstr "Cadena de tiempo inválida. Formato correcto: hh:mm, en formato de 24 horas." #: ../../TShockAPI/Rest/RestManager.cs:1332 #, csharp-format msgid "Invalid Type: '{0}'" -msgstr "" +msgstr "Tipo Inválido: '{0}'" #: ../../TShockAPI/Commands.cs:1079 #: ../../TShockAPI/Commands.cs:1212 #, csharp-format msgid "Invalid user syntax. Try {0}user help." -msgstr "" +msgstr "Sintaxis de user inválida. Prueba {0}user help." #: ../../TShockAPI/Commands.cs:3242 msgid "Invalid warp name. The names 'list', 'hide', 'del' and 'add' are reserved for commands." -msgstr "" +msgstr "Nombre de warp inválido. Los nombres 'list', 'hide', 'del' y 'add' están reservados para comandos." #: ../../TShockAPI/Commands.cs:4632 msgid "Invalid wind speed." @@ -4388,7 +4391,7 @@ msgstr "Velocidad de viento inválida." #: ../../TShockAPI/Commands.cs:2559 #, csharp-format msgid "Invalid world mode. Valid world modes: {0}" -msgstr "" +msgstr "Modo de mundo inválido. Modos válidos: {0}" #: ../../TShockAPI/Commands.cs:1260 #, csharp-format @@ -4397,16 +4400,16 @@ msgstr "Dirección IP: {0}." #: ../../TShockAPI/Commands.cs:3981 msgid "Item Ban Sub-Commands ({{0}}/{{1}}):" -msgstr "" +msgstr "Sub-Comandos de Item Ban ({{0}}/{{1}}):" #: ../../TShockAPI/Commands.cs:3999 msgid "Item bans ({{0}}/{{1}}):" -msgstr "Items baneados ({{0}}/{{1}}):" +msgstr "Objetos prohibidos ({{0}}/{{1}}):" #: ../../TShockAPI/TSPlayer.cs:1920 #, csharp-format msgid "Kicked {0} for : '{1}'" -msgstr "Has expulsado a {0} por: '{1}'" +msgstr "Se expulsó a {0} por : '{1}'" #: ../../TShockAPI/Rest/RestManager.cs:1049 msgid "Kicked via web" @@ -4419,98 +4422,98 @@ msgstr "Expulsado: {0}" #: ../../TShockAPI/Commands.cs:5917 msgid "Kill syntax and example" -msgstr "" +msgstr "Sintaxis y ejemplo de Kill" #: ../../TShockAPI/Commands.cs:553 msgid "Kills another player." -msgstr "" +msgstr "Mata a otro jugador." #: ../../TShockAPI/Commands.cs:393 msgid "Kills hostile NPCs or NPCs of a certain type." -msgstr "" +msgstr "Mata PNJ hostiles o a un tipo específico de PNJ." #: ../../TShockAPI/Handlers/LandGolfBallInCupHandler.cs:114 #, csharp-format msgid "LandGolfBallInCupHandler: Invalid golf ball projectile ID {0}! - From {1}" -msgstr "" +msgstr "LandGolfBallInCupHandler: ¡ID de proyectil de pelota de golf inválida! - De {1}" #: ../../TShockAPI/Handlers/LandGolfBallInCupHandler.cs:130 #, csharp-format msgid "LandGolfBallInCupHandler: Item selected is not a golf club! - From {0}" -msgstr "" +msgstr "LandGolfBallInCupHandler: ¡El objeto seleccionado no es un palo de golf! - De {0}" #: ../../TShockAPI/Handlers/LandGolfBallInCupHandler.cs:92 #, csharp-format msgid "LandGolfBallInCupHandler: Packet rejected for ID spoofing. Expected {0}, received {1} from {2}." -msgstr "" +msgstr "LandGolfBallInCupHandler: Paquete rechazado por falsificación de ID. Se esperaba {0}, se recibió {1} de {2}." #: ../../TShockAPI/Handlers/LandGolfBallInCupHandler.cs:107 #, csharp-format msgid "LandGolfBallInCupHandler: Tile at packet position X:{0} Y:{1} is not a golf hole! - From {2}" -msgstr "" +msgstr "LandGolfBallInCupHandler: ¡La casilla en la posición del paquete X:{0} Y:{1} no tiene un hoyo de golf! - De {2}" #: ../../TShockAPI/Handlers/LandGolfBallInCupHandler.cs:100 #, csharp-format msgid "LandGolfBallInCupHandler: X and Y position is out of world bounds! - From {0}" -msgstr "" +msgstr "LandGolfBallInCupHandler: ¡Las posiciones X e Y están fuera de los bordes del mundo! - De {0}" #: ../../TShockAPI/Commands.cs:2493 msgid "Lanterns are now down." -msgstr "" +msgstr "Los faroles ahora están deshabilitados." #: ../../TShockAPI/Commands.cs:2489 msgid "Lanterns are now up." -msgstr "" +msgstr "Los faroles ahora están habilitados." #: ../../TShockAPI/Commands.cs:4423 msgid "Liquids are already settling." -msgstr "" +msgstr "Los líquidos ya se están estabilizando." #: ../../TShockAPI/Commands.cs:5191 msgid "list - Lists all regions." -msgstr "" +msgstr "list - Da una lista de todas las regiones." #: ../../TShockAPI/Commands.cs:3975 msgid "list [page] - Lists all item bans." -msgstr "" +msgstr "list [página] - Da una lista de todos los ítems prohibidos." #: ../../TShockAPI/Commands.cs:4153 msgid "list [page] - Lists all projectile bans." -msgstr "" +msgstr "list [página] - Da una lista de todos los proyectiles prohibidos." #: ../../TShockAPI/Commands.cs:4329 msgid "list [page] - Lists all tile bans." -msgstr "" +msgstr "list [página] - Da una lista de todos los bloques prohibidos." #: ../../TShockAPI/Commands.cs:3444 msgid "list [page] - Lists groups." -msgstr "" +msgstr "list [página] - Muestra los grupos." #: ../../TShockAPI/Commands.cs:5327 msgid "List Online Players Syntax" -msgstr "" +msgstr "Sintaxis de List Online Players" #: ../../TShockAPI/TShock.cs:828 #, csharp-format msgid "Listening on IP {0}." -msgstr "" +msgstr "Escuchando IP {0}." #: ../../TShockAPI/TShock.cs:809 #, csharp-format msgid "Listening on port {0}." -msgstr "" +msgstr "Escuchando puerto {0}." #: ../../TShockAPI/Commands.cs:3445 msgid "listperm [page] - Lists a group's permissions." -msgstr "" +msgstr "listperm [página] - Muestra los permisos de un grupo." #: ../../TShockAPI/Commands.cs:613 msgid "Lists commands or gives help on them." -msgstr "" +msgstr "Muestra comandos o ayuda sobre los mismos." #: ../../TShockAPI/Commands.cs:2110 msgid "listusers - Lists all REST users and their current active tokens." -msgstr "" +msgstr "listusers - Muestra todos los usuarios REST y sus tókens activos." #: ../../TShockAPI/TShock.cs:798 #, csharp-format @@ -4524,20 +4527,20 @@ msgstr "La ubicación de {0} es ({1}, {2})." #: ../../TShockAPI/Commands.cs:1750 msgid "Log display disabled." -msgstr "" +msgstr "Registro en pantalla desactivado." #: ../../TShockAPI/Commands.cs:1746 msgid "Log display enabled." -msgstr "" +msgstr "Registro en pantalla activado." #: ../../TShockAPI/TShock.cs:785 #, csharp-format msgid "Log path has been set to {0}" -msgstr "" +msgstr "La ruta del registro se ha establecido en {0}" #: ../../TShockAPI/Commands.cs:874 msgid "Login attempt failed - see the message above." -msgstr "" +msgstr "Fallo en el intento de inicio de sesión - vea el mensaje anterior." #: ../../TShockAPI/TShock.cs:980 msgid "Login before join enabled. Users may be prompted for an account specific password instead of a server password on connect." @@ -4549,20 +4552,20 @@ msgstr "Inicio de sesión con UUID habilitado. Los usuarios iniciarán sesión a #: ../../TShockAPI/Commands.cs:240 msgid "Logs you into an account." -msgstr "Inicia sesión en una cuenta." +msgstr "Inicias sesión en una cuenta." #: ../../TShockAPI/Commands.cs:246 msgid "Logs you out of your current account." -msgstr "Cierras sesión de tu cuenta actual." +msgstr "Cierras la sesión de tu cuenta actual." #: ../../TShockAPI/Commands.cs:1227 #, csharp-format msgid "Machine name: {0}" -msgstr "" +msgstr "Nombre de la máquina: {0}" #: ../../TShockPluginManager/NugetCLI.cs:141 msgid "Make sure that you trust the plugins you're installing." -msgstr "" +msgstr "Asegúrate de estar instalando plugins en los que confías." #: ../../TShockAPI/Bouncer.cs:2471 msgid "Malicious portal attempt." @@ -4570,67 +4573,67 @@ msgstr "Intento malicioso de uso de portal." #: ../../TShockPluginManager/NugetCLI.cs:35 msgid "Manage plugins and their requirements" -msgstr "" +msgstr "Administra plugins y sus requerimientos" #: ../../TShockAPI/Commands.cs:280 msgid "Manages groups." -msgstr "" +msgstr "Gestiona grupos." #: ../../TShockAPI/Commands.cs:284 msgid "Manages item bans." -msgstr "" +msgstr "Gestiona los objetos prohibidos." #: ../../TShockAPI/Commands.cs:268 msgid "Manages player bans." -msgstr "" +msgstr "Gestiona los bans de jugadores." #: ../../TShockAPI/Commands.cs:288 msgid "Manages projectile bans." -msgstr "" +msgstr "Gestiona los proyectiles prohibidos." #: ../../TShockAPI/Commands.cs:296 msgid "Manages regions." -msgstr "" +msgstr "Gestiona regiones." #: ../../TShockAPI/Commands.cs:570 msgid "Manages the REST API." -msgstr "" +msgstr "Gestiona el API REST." #: ../../TShockAPI/Commands.cs:376 msgid "Manages the server whitelist." -msgstr "" +msgstr "Gestiona la lista blanca del servidor." #: ../../TShockAPI/Commands.cs:292 msgid "Manages tile bans." -msgstr "" +msgstr "Gestiona los bloques prohibidos." #: ../../TShockAPI/Commands.cs:232 msgid "Manages user accounts." -msgstr "" +msgstr "Gestiona las cuentas de usuario." #: ../../TShockAPI/Bouncer.cs:1775 msgid "Manipulating unknown liquid type" -msgstr "" +msgstr "Manipulando un tipo de líquido desconocido" #: ../../TShockAPI/Commands.cs:4721 #, csharp-format msgid "Marked region {0} as protected." -msgstr "" +msgstr "Región {0} marcada como protegida." #: ../../TShockAPI/Commands.cs:4728 #, csharp-format msgid "Marked region {0} as unprotected." -msgstr "" +msgstr "Región {0} marcada como desprotegida." #: ../../TShockAPI/Commands.cs:1222 #, csharp-format msgid "Memory usage: {0}" -msgstr "" +msgstr "Uso de memoria: {0}" #: ../../TShockAPI/SqlLog.cs:39 #, csharp-format msgid "Message: {0}: {1}: {2}" -msgstr "" +msgstr "Mensaje: {0}: {1}: {2}" #: ../../TShockAPI/Rest/RestManager.cs:874 msgid "Meteor has been spawned" @@ -4642,7 +4645,7 @@ msgstr "Mal comportamiento." #: ../../TShockAPI/Commands.cs:6214 msgid "Missing item name/id." -msgstr "Falta el nombre del objeto o el ID." +msgstr "Falta el nombre/ID del objeto." #: ../../TShockAPI/Rest/RestManager.cs:1274 #, csharp-format @@ -4661,19 +4664,19 @@ msgstr "Falta el nombre del jugador." #: ../../TShockAPI/Commands.cs:1237 #, csharp-format msgid "Mode: {0}" -msgstr "" +msgstr "Modo: {0}" #: ../../TShockAPI/TSPlayer.cs:1966 msgid "More than one match found -- unable to decide which is correct: " -msgstr "Más de una coincidencia encontrada -- no se puede decidir cuál es la correcta: " +msgstr "Hay más de una coincidencia -- no se puede decidir la correcta: " #: ../../TShockAPI/Commands.cs:2767 msgid "Mourning Wood" -msgstr "" +msgstr "Árbol de Luto" #: ../../TShockAPI/Net/BaseMsg.cs:29 msgid "Msg ID not implemented" -msgstr "" +msgstr "ID de Msg no implementado" #: ../../TShockAPI/DB/UserManager.cs:294 #, csharp-format @@ -4682,25 +4685,25 @@ msgstr "Se encontraron múltiples cuentas para {0} '{1}'" #: ../../TShockAPI/Commands.cs:5461 msgid "Mute Syntax" -msgstr "" +msgstr "Sintaxis de Mute" #: ../../TShockAPI/Commands.cs:1293 msgid "N/A" -msgstr "" +msgstr "N/D" #: ../../TShockAPI/Commands.cs:5189 msgid "name [-u][-z][-p] - Shows the name of the region at the given point." -msgstr "" +msgstr "name [-u][-z][-p] - Muestra el nombre de la región en el punto determinado." #: ../../TShockAPI/Commands.cs:1233 #, csharp-format msgid "Name: {0}" -msgstr "" +msgstr "Nombre: {0}" #: ../../TShockAPI/Handlers/NetModules/CreativeUnlocksHandler.cs:55 #, csharp-format msgid "NetModuleHandler received attempt to unlock sacrifice while not in journey mode from {0}" -msgstr "" +msgstr "NetModuleHandler recibida petición de {0} para desbloquear sacrificio sin estar en modo viaje" #: ../../TShockAPI/Commands.cs:1474 msgid "Never." @@ -4713,28 +4716,28 @@ msgstr "¡El nuevo nombre es demasiado largo!" #: ../../TShockAPI/TShock.cs:864 #, csharp-format msgid "New worlds will be generated with the {0} world evil type!" -msgstr "" +msgstr "¡Los mundos nuevos serán generados con el bioma maligno {0}!" #: ../../TShockAPI/Permissions.cs:547 msgid "No associated commands." -msgstr "" +msgstr "No hay comandos asociados." #: ../../TShockAPI/Commands.cs:1694 msgid "No bans found matching the provided ticket number." -msgstr "" +msgstr "No se encontraron baneos que coincidan con el número de ticket." #: ../../TShockAPI/Commands.cs:5784 #, csharp-format msgid "No command or command alias matching \"{0}\" found." -msgstr "" +msgstr "No se encontró ningún comando o alias que coincida con \"{0}\"." #: ../../TShockAPI/Permissions.cs:530 msgid "No description available." -msgstr "" +msgstr "No hay descripción disponible." #: ../../TShockAPI/Commands.cs:145 msgid "No help available." -msgstr "" +msgstr "No hay ayuda disponible." #: ../../TShockAPI/Rest/RestManager.cs:721 msgid "No matching bans found." @@ -4748,20 +4751,20 @@ msgstr "No se encontró ningún jugador que coincida con '{0}'." #: ../../TShockAPI/Commands.cs:1786 #, csharp-format msgid "No players matched \"{0}\"." -msgstr "" +msgstr "Ningún jugador coincide con \"{0}\"." #: ../../TShockAPI/Commands.cs:6123 #, csharp-format msgid "No prefix matched \"{0}\"." -msgstr "" +msgstr "Ningún prefijo coincide con \"{0}\"." #: ../../TShockAPI/Commands.cs:5492 msgid "No reason specified." -msgstr "" +msgstr "No se ha especificado ninguna razón." #: ../../TShockAPI/Rest/RestManager.cs:1224 msgid "No special permissions are required for this route." -msgstr "" +msgstr "No se requieren permisos especiales para esta ruta." #: ../../TShockAPI/Commands.cs:3474 #: ../../TShockAPI/Commands.cs:3483 @@ -4770,39 +4773,39 @@ msgstr "" #: ../../TShockAPI/Commands.cs:3614 #, csharp-format msgid "No such group \"{0}\"." -msgstr "" +msgstr "No existe el grupo \"{0}\"." #: ../../TShockAPI/DB/IQueryBuilder.cs:345 msgid "No values supplied" -msgstr "" +msgstr "No se proporcionó ningún valor" #: ../../TShockAPI/Rest/SecureRest.cs:194 msgid "Not authorized. The provided token became invalid due to group changes, please create a new token." -msgstr "" +msgstr "No autorizado. El tóken proporcionado fue invalidado debido a cambios en los grupos, por favor cree un nuevo tóken." #: ../../TShockAPI/Rest/SecureRest.cs:186 msgid "Not authorized. The specified API endpoint requires a token, but the provided token was not valid." -msgstr "" +msgstr "No autorizado. El punto final API especificado requiere un tóken, pero el que fue proporcionado no era válido." #: ../../TShockAPI/Rest/RestCommand.cs:95 #: ../../TShockAPI/Rest/RestCommand.cs:101 #: ../../TShockAPI/Rest/SecureRest.cs:180 msgid "Not authorized. The specified API endpoint requires a token." -msgstr "" +msgstr "No autorizado. El punto final API especificado requiere un tóken." #: ../../TShockAPI/Rest/SecureRest.cs:200 #, csharp-format msgid "Not authorized. User \"{0}\" has no access to use the specified API endpoint." -msgstr "" +msgstr "No autorizado. El usuario \"{0}\" no tiene acceso para utilizar el punto final API especificado." #: ../../TShockAPI/Commands.cs:991 #, csharp-format msgid "Not logged in or Invalid syntax. Proper syntax: {0}password ." -msgstr "" +msgstr "No conectado o sintaxis inválida. Sintaxis correcta: {0}password ." #: ../../TShockAPI/DB/UserManager.cs:468 msgid "Not upgrading work factor because bcrypt hash in an invalid format." -msgstr "" +msgstr "No se está actualizando el factor de trabajo porque el hash de bcrypt está en un formato inválido." #: ../../TShockAPI/Bouncer.cs:1383 #: ../../TShockAPI/Bouncer.cs:1387 @@ -4812,34 +4815,34 @@ msgstr "Daño a NPC excedió {0}." #: ../../TShockPluginManager/NugetCLI.cs:103 msgid "One moment..." -msgstr "" +msgstr "Un momento..." #: ../../TShockAPI/DB/RegionManager.cs:102 #, csharp-format msgid "One of your UserIDs is not a usable integer: {0}" -msgstr "" +msgstr "Uno de tus UserIDs no es un entero utilizable: {0}" #: ../../TShockAPI/Commands.cs:5344 #, csharp-format msgid "Online Players ({0}/{1})" -msgstr "" +msgstr "Jugadores en Línea ({0}/{1})" #: ../../TShockAPI/Commands.cs:1225 #, csharp-format msgid "Operating system: {0}" -msgstr "" +msgstr "Sistema operativo: {0}" #: ../../TShockAPI/Commands.cs:308 msgid "Overrides serverside characters for a player, temporarily." -msgstr "" +msgstr "Ignora temporalmente los personajes exclusivos de servidor para un jugador." #: ../../TShockAPI/PaginationTools.cs:105 msgid "Page {{0}} of {{1}}" -msgstr "" +msgstr "Página {{0}} de {{1}}" #: ../../TShockAPI/Commands.cs:3446 msgid "parent - Changes a group's parent group." -msgstr "parent - Cambia el grupo padre de un grupo." +msgstr "parent - Cambia el grupo padre de un grupo." #: ../../TShockAPI/Commands.cs:3504 #, csharp-format @@ -4849,17 +4852,17 @@ msgstr "El padre de \"{0}\" es \"{1}\"." #: ../../TShockAPI/Commands.cs:3492 #, csharp-format msgid "Parent of group \"{0}\" set to \"{1}\"." -msgstr "Padre del grupo \"{0}\" establecido a \"{1}\"." +msgstr "Padre del grupo \"{0}\" asignado a \"{1}\"." #: ../../TShockAPI/DB/GroupManager.cs:356 #, csharp-format msgid "Parenting group {0} to {1} would cause loops in the parent chain." -msgstr "Hacer superior al grupo {0} a {1} causaría bucles en la cadena de parentesco." +msgstr "Hacer al grupo {0} padre de {1} causaría bucles en la cadena de parentesco." #: ../../TShockAPI/Commands.cs:1163 #, csharp-format msgid "Password change attempt for {0} failed for an unknown reason. Check the server console for more details." -msgstr "" +msgstr "El intento de cambio de contraseña de {0} falló por una razón desconocida. Comprueba la consola del servidor para más detalles." #: ../../TShockAPI/Commands.cs:1155 #, csharp-format @@ -4893,37 +4896,37 @@ msgstr "Ruta: {0}" #: ../../TShockAPI/Commands.cs:6622 msgid "Pearlwood Tree" -msgstr "" +msgstr "Árbol de Madera Perlada" #: ../../TShockAPI/Commands.cs:3774 #, csharp-format msgid "Permissions for {0} ({{0}}/{{1}}):" -msgstr "" +msgstr "Permisos para {0} ({{0}}/{{1}}):" #: ../../TShockAPI/Commands.cs:2680 msgid "Plantera" -msgstr "" +msgstr "Plantera" #: ../../TShockAPI/Commands.cs:1803 #, csharp-format msgid "Player \"{0}\" has to perform a /login attempt first." -msgstr "" +msgstr "El jugador \"{0}\" debe realizar antes un intento de /login." #: ../../TShockAPI/Commands.cs:1808 #, csharp-format msgid "Player \"{0}\" has to reconnect first, because they need to delete their trash." -msgstr "" +msgstr "El jugador \"{0}\" debe reconectarse antes, pues necesita eliminar su papelera." #: ../../TShockAPI/Commands.cs:1798 #, csharp-format msgid "Player \"{0}\" is already logged in." -msgstr "" +msgstr "El jugador \"{0}\" ya está conectado." #: ../../TShockAPI/TSPlayer.cs:1883 #: ../../TShockAPI/TSPlayer.cs:1887 #, csharp-format msgid "Player {0} has been disabled for {1}." -msgstr "" +msgstr "El jugador {0} ha sido deshabilitado por {1}." #: ../../TShockAPI/Rest/RestManager.cs:1394 #, csharp-format @@ -4939,19 +4942,19 @@ msgstr "Al jugador {0} se le revocó su silencio" #, csharp-format msgid "Player {0} matches {1} player" msgid_plural "Player {0} matches {1} players" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "El jugador {0} coincide con {1} jugador" +msgstr[1] "El jugador {0} coincide con {1} jugadores" #: ../../TShockAPI/Commands.cs:4792 #: ../../TShockAPI/Commands.cs:4827 #, csharp-format msgid "Player {0} not found." -msgstr "" +msgstr "No se encontró al jugador {0}." #: ../../TShockAPI/Bouncer.cs:1140 #, csharp-format msgid "Player {0} tried to sneak {1} onto the server!" -msgstr "" +msgstr "¡El jugador {0} intentó ingresar {1} de contrabando al servidor!" #: ../../TShockAPI/Rest/RestManager.cs:1069 #, csharp-format @@ -4971,7 +4974,7 @@ msgstr "El daño al jugador excedió {0}." #: ../../TShockAPI/Commands.cs:6289 msgid "Player does not have free slots!" -msgstr "" +msgstr "¡El jugador no tiene espacios vacíos!" #: ../../TShockAPI/Bouncer.cs:1182 #, csharp-format @@ -4980,38 +4983,38 @@ msgstr "El jugador no tiene permiso para crear el proyectil {0}." #: ../../TShockAPI/Commands.cs:1324 msgid "Player not found. Unable to kick the player." -msgstr "" +msgstr "Jugador no encontrado. Imposible expulsar al jugador." #: ../../TShockAPI/TShock.cs:1696 #, csharp-format msgid "Please {0}register or {0}login to play!" -msgstr "" +msgstr "¡Por favor usa {0}register o {0}login para jugar!" #: ../../TShockAPI/Commands.cs:951 msgid "Please close NPC windows before logging out." -msgstr "" +msgstr "Por favor cierre las ventanas de PNJ antes de cerrar sesión." #: ../../TShockAPI/Commands.cs:5761 msgid "Please enter a proper command name or alias." -msgstr "" +msgstr "Por favor ingresa un nombre o alias de comando válido." #: ../../TShockAPI/Commands.cs:1063 msgid "Please try a different username." -msgstr "" +msgstr "Por favor prueba con un nombre de usuario distinto." #: ../../TShockAPI/Commands.cs:5415 #, csharp-format msgid "Please use {0}login after this process." -msgstr "" +msgstr "Por favor usa {0}login después de esto." #: ../../TShockAPI/Commands.cs:5412 msgid "Please use the following to create a permanent account for you." -msgstr "" +msgstr "Por favor utiliza lo siguiente para crearte una cuenta permanente." #: ../../TShockAPI/TShock.cs:895 #, csharp-format msgid "Port overridden by startup argument. Set to {0}" -msgstr "" +msgstr "Puerto sobreescrito por argumento de inicio. Establecido en {0}" #: ../../TShockAPI/DB/ResearchDatastore.cs:53 #: ../../TShockAPI/DB/BanManager.cs:81 @@ -5020,45 +5023,45 @@ msgstr "Posible problema con su base de datos - ¿Está el archivo Sqlite3.dll p #: ../../TShockAPI/Commands.cs:3447 msgid "prefix - Changes a group's prefix." -msgstr "" +msgstr "prefix - Cambia el prefijo de un grupo." #: ../../TShockAPI/Commands.cs:3594 #, csharp-format msgid "Prefix of \"{0}\" is \"{1}\"." -msgstr "" +msgstr "El prefijo de \"{0}\" es \"{1}\"." #: ../../TShockAPI/Commands.cs:3582 #, csharp-format msgid "Prefix of group \"{0}\" set to \"{1}\"." -msgstr "" +msgstr "Prefijo del grupo \"{0}\" establecido como \"{1}\"." #: ../../TShockAPI/Commands.cs:304 msgid "Prevents a player from talking." -msgstr "" +msgstr "Evita que un jugador hable." #: ../../TShockAPI/Commands.cs:1226 #, csharp-format msgid "Proc count: {0}" -msgstr "" +msgstr "Cantidad de Proc: {0}" #: ../../TShockAPI/Commands.cs:4065 #: ../../TShockAPI/Commands.cs:4123 #, csharp-format msgid "Projectile {0} is not banned." -msgstr "" +msgstr "El proyectil {0} no está prohibido." #: ../../TShockAPI/Commands.cs:4159 msgid "Projectile Ban Sub-Commands ({{0}}/{{1}}):" -msgstr "" +msgstr "Sub-Comandos de Projectile Ban ({{0}}/{{1}}):" #: ../../TShockAPI/Commands.cs:4177 msgid "Projectile bans ({{0}}/{{1}}):" -msgstr "" +msgstr "Prohibiciones de Proyectil ({{0}}/{{1}}):" #: ../../TShockAPI/Bouncer.cs:1286 #, csharp-format msgid "Projectile create threshold exceeded {0}." -msgstr "" +msgstr "El umbral de creación de proyectiles excedió {0}." #: ../../TShockAPI/Bouncer.cs:1192 #, csharp-format @@ -5067,7 +5070,7 @@ msgstr "El daño del proyectil es mayor que {0}." #: ../../TShockAPI/Commands.cs:5198 msgid "protect - Sets whether the tiles inside the region are protected or not." -msgstr "" +msgstr "protect - Establece si las casillas en el interior de la región están o no protegidas." #: ../../TShockAPI/RegionHandler.cs:159 msgid "Protected regions at this point: " @@ -5076,101 +5079,101 @@ msgstr "Regiones protegidas en este punto: " #: ../../TShockAPI/Commands.cs:4950 #, csharp-format msgid "Protected: {0}." -msgstr "" +msgstr "Protegido: {0}." #: ../../TShockAPI/Commands.cs:1373 #, csharp-format msgid "Quick usage: {0} {1} \"Griefing\"" -msgstr "" +msgstr "Uso abreviado: {0} {1} \"Molestar\"" #: ../../TShockAPI/TSPlayer.cs:758 #, csharp-format msgid "Rangecheck failed for {0} ({1}, {2}) (rg: {3}/{5}, {4}/{5})" -msgstr "" +msgstr "Rangecheck falló para {0} ({1}, {2}) (rg: {3}/{5}, {4}/{5})" #: ../../TShockAPI/TShock.cs:1196 msgid "Reached HealOtherPlayer threshold" -msgstr "" +msgstr "Se alcanzó el umbral de HealOtherPlayer" #: ../../TShockAPI/Bouncer.cs:2110 msgid "Reached HealOtherPlayer threshold." -msgstr "" +msgstr "Se alcanzó el umbral de HealOtherPlayer." #: ../../TShockAPI/TShock.cs:1187 msgid "Reached paint threshold" -msgstr "" +msgstr "Se alcanzó el umbral de pintura" #: ../../TShockAPI/Bouncer.cs:1290 msgid "Reached projectile create threshold." -msgstr "" +msgstr "Se alcanzó el umbral de creación de proyectiles." #: ../../TShockAPI/TShock.cs:1178 msgid "Reached projectile threshold" -msgstr "" +msgstr "Se alcanzó el umbral de proyectiles" #: ../../TShockAPI/Bouncer.cs:922 #: ../../TShockAPI/TShock.cs:1108 msgid "Reached TileKill threshold." -msgstr "" +msgstr "Se alcanzó el umbral de TileKill." #: ../../TShockAPI/TShock.cs:1169 msgid "Reached TileLiquid threshold" -msgstr "" +msgstr "Se alcanzó el umbral de TileLiquid" #: ../../TShockAPI/Bouncer.cs:1686 #, csharp-format msgid "Reached TileLiquid threshold {0}." -msgstr "" +msgstr "Se alcanzó el umbral de líquidos {0}." #: ../../TShockAPI/Bouncer.cs:1690 msgid "Reached TileLiquid threshold." -msgstr "" +msgstr "Se alcanzó el umbral de líquidos." #: ../../TShockAPI/TShock.cs:1125 msgid "Reached TilePlace threshold" -msgstr "" +msgstr "Se alcanzó el umbral de bloques puestos" #: ../../TShockAPI/Bouncer.cs:940 #: ../../TShockAPI/Bouncer.cs:2376 msgid "Reached TilePlace threshold." -msgstr "" +msgstr "Se alcanzó el umbral de bloques puestos." #: ../../TShockPluginManager/NugetCLI.cs:128 msgid "Read the message below to find out more." -msgstr "" +msgstr "Lea el siguiente mensaje para obtener más información." #: ../../TShockAPI/Commands.cs:1495 #, csharp-format msgid "Reason: {0}." -msgstr "" +msgstr "Razón: {0}." #: ../../TShockAPI/Extensions/DbExt.cs:254 #, csharp-format msgid "Received type '{0}', however column '{1}' expects type '{2}'" -msgstr "" +msgstr "Recibido tipo '{0}', sin embargo la columna '{1}' esperaba tipo '{2}'" #: ../../TShockAPI/Commands.cs:5135 #, csharp-format msgid "Region \"{0}\" already exists." -msgstr "" +msgstr "La región \"{0}\" ya existe." #: ../../TShockAPI/Commands.cs:5167 #, csharp-format msgid "Region \"{0}\" does not exist." -msgstr "" +msgstr "La región \"{0}\" no existe." #: ../../TShockAPI/Commands.cs:4701 #, csharp-format msgid "Region {0} already exists." -msgstr "" +msgstr "La región {0} ya existe." #: ../../TShockAPI/Commands.cs:4980 msgid "Region is not shared with any groups." -msgstr "" +msgstr "La región no es compartida con ningún grupo." #: ../../TShockAPI/Commands.cs:4969 msgid "Region is not shared with any users." -msgstr "" +msgstr "La región no es compartida con ningún usuario." #: ../../TShockAPI/Commands.cs:4949 #, csharp-format @@ -5192,7 +5195,7 @@ msgstr "¡La región se ha redimensionado correctamente!" #: ../../TShockAPI/Commands.cs:5042 #, csharp-format msgid "Region's z is now {0}" -msgstr "" +msgstr "El índice z de la región ahora es {0}" #: ../../TShockAPI/Commands.cs:4915 msgid "Regions ({{0}}/{{1}}):" @@ -5209,11 +5212,11 @@ msgstr "Te registra una cuenta." #: ../../TShockAPI/Commands.cs:1070 #, csharp-format msgid "RegisterUser returned an error: {0}." -msgstr "RegisterUser devolvió un error {0}." +msgstr "RegisterUser devolvió un error: {0}." #: ../../TShockAPI/Bouncer.cs:2161 msgid "Released critter was not from its item." -msgstr "La criatura liberada no era de su objeto." +msgstr "La criatura liberada no correspondía a su objeto." #: ../../TShockAPI/Commands.cs:364 msgid "Reloads the server configuration file." @@ -5221,17 +5224,17 @@ msgstr "Recarga el archivo de configuración del servidor." #: ../../TShockAPI/Commands.cs:5194 msgid "remove - Removes a user from a region." -msgstr "" +msgstr "remove - Retira a un usuario de una región." #: ../../TShockAPI/Commands.cs:2521 #, csharp-format msgid "Removed {0} players from the angler quest completion list for today." -msgstr "" +msgstr "Eliminados {0} jugadores del listado de misiones completadas hoy del pescador." #: ../../TShockAPI/Commands.cs:4890 #, csharp-format msgid "Removed group {0} from {1}" -msgstr "" +msgstr "El grupo {0} fue removido de {1}" #: ../../TShockAPI/Commands.cs:3494 #, csharp-format @@ -5241,111 +5244,111 @@ msgstr "Se eliminó el padre del grupo \"{0}\"." #: ../../TShockAPI/Commands.cs:3584 #, csharp-format msgid "Removed prefix of group \"{0}\"." -msgstr "" +msgstr "Se quitó el prefijo del grupo \"{0}\"." #: ../../TShockAPI/Commands.cs:3539 #, csharp-format msgid "Removed suffix of group \"{0}\"." -msgstr "" +msgstr "Se quitó el sufijo del grupo \"{0}\"." #: ../../TShockAPI/Commands.cs:4820 #, csharp-format msgid "Removed user {0} from {1}." -msgstr "" +msgstr "Se quitó el usuario {0} de {1}." #: ../../TShockAPI/Commands.cs:5196 msgid "removeg - Removes a user group from a region." -msgstr "" +msgstr "removeg - Quita a un grupo de usuarios de la región." #: ../../TShockAPI/Commands.cs:300 msgid "Removes a player from the server." -msgstr "" +msgstr "Elimina a un jugador del servidor." #: ../../TShockAPI/DB/UserManager.cs:112 msgid "RemoveUser SQL returned an error" -msgstr "" +msgstr "SQL RemoveUser devolvió un error" #: ../../TShockAPI/Commands.cs:3441 msgid "rename - Changes a group's name." -msgstr "" +msgstr "rename - Cambia el nombre de un grupo." #: ../../TShockAPI/Commands.cs:5190 msgid "rename - Renames the given region." -msgstr "" +msgstr "rename - Renombra la región dada." #: ../../TShockAPI/Commands.cs:397 msgid "Renames an NPC." -msgstr "" +msgstr "Renombra a un PNJ." #: ../../TShockAPI/Commands.cs:566 msgid "Replies to a PM sent to you." -msgstr "" +msgstr "Contestas al mensaje privado que te fue enviado." #: ../../TShockAPI/Rest/SecureRest.cs:93 msgid "Requested token was successfully destroyed." -msgstr "" +msgstr "El tóken solicitado fue destruido exitosamente." #: ../../TShockAPI/Handlers/RequestTileEntityInteractionHandler.cs:35 #, csharp-format msgid "RequestTileEntityInteractionHandler: Rejected packet due to lack of building permissions! - From {0} | Position X:{1} Y:{2}, TileEntity type: {3}, Tile type: {4}" -msgstr "" +msgstr "RequestTileEntityInteractionHandler: ¡Paquete rechazado por falta de permisos de construcción! - De {0} | Posición X:{1} Y:{2}, Tipo de TileEntity: {3}, Tipo de bloque: {4}" #: ../../TShockAPI/Commands.cs:419 msgid "Resets the list of users who have completed an angler quest that day." -msgstr "" +msgstr "Reinicia la lista de usuarios que han completado durante el día una misión del pescador." #: ../../TShockAPI/SaveManager.cs:163 #, csharp-format msgid "resetTime {0}, direct {1}" -msgstr "" +msgstr "resetTime {0}, direct {1}" #: ../../TShockAPI/Commands.cs:5192 msgid "resize - Resizes a region." -msgstr "" +msgstr "resize - Redimensiona una región." #: ../../TShockAPI/Commands.cs:603 msgid "Respawn yourself or another player." -msgstr "" +msgstr "Te hace reaparecer a ti u otro jugador." #: ../../TShockAPI/UpdateManager.cs:87 msgid "Retrying in 5 minutes." -msgstr "" +msgstr "Reintentando en 5 minutos." #: ../../TShockAPI/Commands.cs:456 msgid "Returns the user's or specified user's current position." -msgstr "" +msgstr "Muestra la posición actual ya sea del usuario o de otro jugador." #: ../../TShockAPI/Commands.cs:6600 msgid "Rich Mahogany" -msgstr "" +msgstr "Madera de Caoba Rico" #: ../../TShockAPI/Commands.cs:5627 msgid "Rocket Syntax" -msgstr "" +msgstr "Sintaxis de Rocket" #: ../../TShockAPI/Commands.cs:342 msgid "Rockets a player upwards. Requires SSC." -msgstr "" +msgstr "Propulsa a un jugador hacia arriba. Requiere SSC." #: ../../TShockAPI/Commands.cs:6662 msgid "Ruby Gemtree" -msgstr "" +msgstr "Arbolgema de Rubí" #: ../../TShockAPI/Commands.cs:6604 msgid "Sakura Tree" -msgstr "" +msgstr "Árbol Sakura" #: ../../TShockAPI/Commands.cs:2783 msgid "Santa-NK1" -msgstr "" +msgstr "Papá-NK1" #: ../../TShockAPI/Commands.cs:6654 msgid "Sapphire Gemtree" -msgstr "" +msgstr "Arbolgema de Zafiro" #: ../../TShockAPI/Commands.cs:312 msgid "Saves all serverside characters." -msgstr "" +msgstr "Guarda todos los personajes exclusivos-de-servidor." #: ../../TShockAPI/Commands.cs:500 msgid "Saves the world file." @@ -5353,7 +5356,7 @@ msgstr "Guarda el archivo del mundo." #: ../../TShockAPI/SaveManager.cs:58 msgid "Saving world..." -msgstr "" +msgstr "Guardando mundo..." #: ../../TShockAPI/Commands.cs:1236 #, csharp-format @@ -5370,11 +5373,11 @@ msgstr "Envía un mensaje privado a un jugador." #: ../../TShockAPI/Commands.cs:599 msgid "Sends all tiles from the server to the player to resync the client with the actual world state." -msgstr "Envía todos los bloques desde el servidor al jugador para sincronizar el cliente con el estado actual del mundo." +msgstr "Envía todas las casillas desde el servidor al jugador para sincronizar el cliente con el estado actual del mundo." #: ../../TShockAPI/Commands.cs:557 msgid "Sends an action message to everyone." -msgstr "" +msgstr "Envía a todos un mensaje de acción." #: ../../TShockAPI/Commands.cs:431 msgid "Sends you to the world's spawn point." @@ -5390,15 +5393,15 @@ msgstr "¡Se ha interrumpido la consola del servidor!" #: ../../TShockAPI/Configuration/TShockConfig.cs:337 msgid "Server is full" -msgstr "El servidor está lleno." +msgstr "Servidor lleno" #: ../../TShockAPI/Configuration/TShockConfig.cs:341 msgid "Server is full. No reserved slots open." -msgstr "El servidor está lleno. No quedan espacios reservados." +msgstr "Servidor lleno y sin cupos reservados." #: ../../TShockAPI/TShock.cs:1300 msgid "Server is shutting down..." -msgstr "" +msgstr "El servidor se está cerrando..." #: ../../TShockAPI/BackupManager.cs:73 msgid "Server map saving..." @@ -5407,104 +5410,104 @@ msgstr "Guardando mapa del servidor..." #: ../../TShockAPI/Commands.cs:4407 #, csharp-format msgid "Server password has been changed to: {0}." -msgstr "" +msgstr "La contraseña de servidor se cambió a: {0}." #: ../../TShockAPI/Commands.cs:2040 #: ../../TShockAPI/Commands.cs:2046 msgid "Server shutting down: " -msgstr "" +msgstr "El servidor se está cerrando: " #: ../../TShockAPI/Commands.cs:2040 msgid "Server shutting down!" -msgstr "" +msgstr "¡El servidor se está cerrando!" #: ../../TShockAPI/Commands.cs:2046 msgid "Server shutting down." -msgstr "" +msgstr "El servidor se está cerrando." #: ../../TShockAPI/Commands.cs:959 msgid "Server side characters are enabled. You need to be logged-in to play." -msgstr "" +msgstr "Los personajes exclusivos-de-servidor están activados. Debes estar autenticado para jugar." #: ../../TShockAPI/TShock.cs:1691 #, csharp-format msgid "Server side characters is enabled! Please {0}register or {0}login to play!" -msgstr "" +msgstr "¡Los personajes exclusivos-de-servidor están activados! ¡Por favor usa {0}register o {0}login para jugar!" #: ../../TShockAPI/Commands.cs:1813 #, csharp-format msgid "Server-side character data from \"{0}\" has been replaced by their current local data." -msgstr "" +msgstr "Los datos exclusivos-de-servidor de \"{0}\" fueron reemplazados por sus datos locales actuales." #: ../../TShockAPI/Commands.cs:1773 msgid "Server-side characters is disabled." -msgstr "" +msgstr "Personajes exclusivos-de-servidor desactivados." #: ../../TShockAPI/Commands.cs:5185 msgid "set <1/2> - Sets the temporary region points." -msgstr "" +msgstr "set <1/2> - Establece los puntos temporales de región." #: ../../TShockAPI/Commands.cs:4697 #, csharp-format msgid "Set region {0}." -msgstr "" +msgstr "Se creó la región {0}." #: ../../TShockAPI/RegionHandler.cs:187 #, csharp-format msgid "Set temp point {0}." -msgstr "" +msgstr "Se estableció el punto temporal {0}." #: ../../TShockAPI/Commands.cs:510 msgid "Sets the dungeon's position to your location." -msgstr "" +msgstr "Establece la posición de la mazmorra en tu ubicación." #: ../../TShockAPI/Commands.cs:401 msgid "Sets the maximum number of NPCs." -msgstr "" +msgstr "Establece el número máximo de PNJ." #: ../../TShockAPI/Commands.cs:415 msgid "Sets the spawn rate of NPCs." -msgstr "" +msgstr "Establece la tasa de aparición de PNJ." #: ../../TShockAPI/Commands.cs:518 msgid "Sets the world time." -msgstr "" +msgstr "Establece la hora del mundo." #: ../../TShockAPI/Commands.cs:505 msgid "Sets the world's spawn point to your location." -msgstr "" +msgstr "Establece la posición del punto de reaparición del mundo en tu ubicación." #: ../../TShockAPI/Commands.cs:4427 msgid "Settling liquids." -msgstr "" +msgstr "Estabilizando líquidos." #: ../../TShockAPI/DB/UserManager.cs:182 msgid "SetUserGroup SQL returned an error" -msgstr "" +msgstr "SQL SetUserGroup devolvió un error" #: ../../TShockAPI/DB/UserManager.cs:134 msgid "SetUserPassword SQL returned an error" -msgstr "" +msgstr "SQL SetUserPassword devolvió un error" #: ../../TShockAPI/DB/UserManager.cs:154 msgid "SetUserUUID SQL returned an error" -msgstr "" +msgstr "SQL SetUserUUID devolvió un error" #: ../../TShockAPI/Commands.cs:6612 msgid "Shadewood Tree" -msgstr "" +msgstr "Árbol Sombrío" #: ../../TShockAPI/Commands.cs:5386 msgid "Share your server, talk with admins, and chill on GitHub & Discord. -- https://tshock.co/" -msgstr "" +msgstr "Comparte tu servidor, habla con otros admins, y relájate en GitHub & Discord. https://tshock.co/" #: ../../TShockAPI/Commands.cs:4975 msgid "Shared with groups: " -msgstr "Compartida con los grupos: " +msgstr "Compartiendo con los grupos: " #: ../../TShockAPI/Commands.cs:4964 msgid "Shared with: " -msgstr "Compartida con: " +msgstr "Compartiendo con: " #: ../../TShockAPI/Commands.cs:609 msgid "Shows a command's aliases." @@ -5561,16 +5564,16 @@ msgstr "Tamaño: {0}x{1}" #: ../../TShockAPI/Commands.cs:2699 msgid "Skeletron" -msgstr "" +msgstr "Esqueletrón" #: ../../TShockAPI/Commands.cs:2687 msgid "Skeletron Prime" -msgstr "" +msgstr "Esqueletrón Mayor" #: ../../TShockAPI/TSPlayer.cs:1069 #, csharp-format msgid "Skipping SSC save (due to tshock.ignore.ssc) for {0}" -msgstr "" +msgstr "Omitiendo guardado SSC (debido a tshock.ignore.ssc) para {0}" #: ../../TShockAPI/Commands.cs:574 msgid "Slaps a player, dealing damage." @@ -5578,470 +5581,470 @@ msgstr "Abofetea a un jugador, infligiéndole daño." #: ../../TShockAPI/Commands.cs:2446 msgid "Slime rain cannot be activated during normal rain. Stop the normal rainstorm and try again." -msgstr "" +msgstr "No se puede activar la lluvia de slime durante una lluvia normal. Detén el aguacero actual y vuelve a intentarlo." #: ../../TShockAPI/Commands.cs:1062 #, csharp-format msgid "Sorry, {0} was already taken by another person." -msgstr "" +msgstr "Lamentablemente {0} ya fue reservado por otra persona." #: ../../TShockAPI/Commands.cs:996 #: ../../TShockAPI/Commands.cs:1069 #, csharp-format msgid "Sorry, an error occurred: {0}." -msgstr "" +msgstr "Lo sentimos, ha ocurrido un error: {0}." #: ../../TShockAPI/Commands.cs:4378 msgid "Spawn has now been set at your location." -msgstr "" +msgstr "Punto de aparición establecido en tu posición." #: ../../TShockAPI/Commands.cs:5230 msgid "Spawn is now open." -msgstr "El spawn ahora está abierto." +msgstr "El punto de aparición ahora está abierto." #: ../../TShockAPI/Commands.cs:5230 msgid "Spawn is now protected." -msgstr "El spawn ahora está protegido." +msgstr "El punto de aparición ahora está protegido." #: ../../TShockAPI/Commands.cs:2874 #, csharp-format msgid "Spawned {0} {1} time." msgid_plural "Spawned {0} {1} times." -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Se invocó {0} {1} vez." +msgstr[1] "Se invocó {0} {1} veces." #: ../../TShockAPI/Commands.cs:2891 msgid "Spawned a Wall of Flesh." -msgstr "" +msgstr "Se invocó un Muro Carnoso." #: ../../TShockAPI/Commands.cs:406 msgid "Spawns a number of bosses around you." -msgstr "" +msgstr "Invoca una cantidad de jefes a tu alrededor." #: ../../TShockAPI/Commands.cs:411 msgid "Spawns a number of mobs around you." -msgstr "" +msgstr "Invoca una cantidad de enemigos a tu alrededor." #: ../../TShockAPI/Commands.cs:346 msgid "Spawns fireworks at a player." -msgstr "" +msgstr "Genera fuegos artificiales en un jugador." #: ../../TShockAPI/Rest/Rest.cs:433 msgid "Specified API endpoint doesn't exist. Refer to the documentation for a list of valid endpoints." -msgstr "" +msgstr "El punto final API especificado no existe. Consulte la documentación para ver una lista de puntos finales válidos." #: ../../TShockAPI/SqlLog.cs:346 #, csharp-format msgid "SQL log failed at: {0}. {1}" -msgstr "" +msgstr "El registro SQL falló en: {0}. {1}" #: ../../TShockAPI/SqlLog.cs:317 #, csharp-format msgid "SQL Log insert query failed: {0}" -msgstr "" +msgstr "Consulta de inserción de registro SQL fallida: {0}" #: ../../TShockAPI/Commands.cs:5667 msgid "SSC must be enabled to use this command." -msgstr "" +msgstr "Para usar este comando SSC debe estar activado." #: ../../TShockAPI/TSPlayer.cs:434 #, csharp-format msgid "Stack cheat detected. Remove armor {0} ({1}) and then rejoin." -msgstr "" +msgstr "Trampa de stack detectada. Quítate la armadura {0} ({1}) y luego vuelve a entrar." #: ../../TShockAPI/TSPlayer.cs:577 #, csharp-format msgid "Stack cheat detected. Remove Defender's Forge item {0} ({1}) and then rejoin." -msgstr "" +msgstr "Trampa de stack detectada. Quita el objeto {0} ({1}) de la Forja del Defensor y luego vuelve a entrar." #: ../../TShockAPI/TSPlayer.cs:454 #, csharp-format msgid "Stack cheat detected. Remove dye {0} ({1}) and then rejoin." -msgstr "" +msgstr "Trampa de stack detectada. Quítate el tinte {0} ({1}) y luego vuelve a entrar." #: ../../TShockAPI/TSPlayer.cs:414 #: ../../TShockAPI/TSPlayer.cs:474 #, csharp-format msgid "Stack cheat detected. Remove item {0} ({1}) and then rejoin." -msgstr "" +msgstr "Trampa de stack detectada. Quítate el objeto {0} ({1}) y luego vuelve a entrar." #: ../../TShockAPI/TSPlayer.cs:494 #, csharp-format msgid "Stack cheat detected. Remove item dye {0} ({1}) and then rejoin." -msgstr "" +msgstr "Trampa de stack detectada. Quítate el objeto de tinte {0} ({1}) y luego vuelve a entrar." #: ../../TShockAPI/TSPlayer.cs:618 #: ../../TShockAPI/TSPlayer.cs:638 #, csharp-format msgid "Stack cheat detected. Remove Loadout 1 item {0} ({1}) and then rejoin." -msgstr "" +msgstr "Trampa de stack detectada. Quítate el objeto {0} ({1}) del Equipamiento 1 y luego vuelve a entrar." #: ../../TShockAPI/TSPlayer.cs:658 #: ../../TShockAPI/TSPlayer.cs:678 #, csharp-format msgid "Stack cheat detected. Remove Loadout 2 item {0} ({1}) and then rejoin." -msgstr "" +msgstr "Trampa de stack detectada. Quítate el objeto {0} ({1}) del Equipamiento 2 y luego vuelve a entrar." #: ../../TShockAPI/TSPlayer.cs:698 #: ../../TShockAPI/TSPlayer.cs:718 #, csharp-format msgid "Stack cheat detected. Remove Loadout 3 item {0} ({1}) and then rejoin." -msgstr "" +msgstr "Trampa de stack detectada. Quítate el objeto {0} ({1}) del Equipamiento 3 y luego vuelve a entrar." #: ../../TShockAPI/TSPlayer.cs:515 #, csharp-format msgid "Stack cheat detected. Remove piggy-bank item {0} ({1}) and then rejoin." -msgstr "" +msgstr "Trampa de stack detectada. Quita el objeto {0} ({1}) de la Hucha y luego vuelve a entrar." #: ../../TShockAPI/TSPlayer.cs:536 #, csharp-format msgid "Stack cheat detected. Remove safe item {0} ({1}) and then rejoin." -msgstr "" +msgstr "Trampa de stack detectada. Quita el objeto {0} ({1}) de la Caja Fuerte y luego vuelve a entrar." #: ../../TShockAPI/TSPlayer.cs:556 #, csharp-format msgid "Stack cheat detected. Remove trash item {0} ({1}) and then rejoin." -msgstr "" +msgstr "Trampa de stack detectada. Quita el objeto {0} ({1}) de tu papelera y luego vuelve a entrar." #: ../../TShockAPI/TSPlayer.cs:598 #, csharp-format msgid "Stack cheat detected. Remove Void Vault item {0} ({1}) and then rejoin." -msgstr "" +msgstr "Trampa de stack detectada. Quita el objeto {0} ({1}) de tu Cámara del Vacío y luego vuelve a entrar." #: ../../TShockAPI/Commands.cs:2279 msgid "Started a blood moon event." -msgstr "" +msgstr "Iniciada una luna sangrienta." #: ../../TShockAPI/Commands.cs:2264 msgid "Started a full moon event." -msgstr "" +msgstr "Iniciada una luna llena." #: ../../TShockAPI/Commands.cs:2306 msgid "Started an eclipse." -msgstr "" +msgstr "Iniciado un eclipse." #: ../../TShockAPI/TShock.cs:927 msgid "Startup parameter overrode maximum player slot configuration value." -msgstr "" +msgstr "El parámetro de inicio impuso su propio valor máximo de jugadores sobre los de la configuración." #: ../../TShockAPI/TShock.cs:909 msgid "Startup parameter overrode REST enable." -msgstr "" +msgstr "El parámetro de inicio anuló la activación de REST." #: ../../TShockAPI/TShock.cs:918 msgid "Startup parameter overrode REST port." -msgstr "" +msgstr "El parámetro de inicio anuló el puerto REST." #: ../../TShockAPI/TShock.cs:901 msgid "Startup parameter overrode REST token." -msgstr "" +msgstr "El parámetro de inicio anuló el tóken REST." #: ../../TShockAPI/Commands.cs:2310 msgid "Stopped an eclipse." -msgstr "" +msgstr "Se detuvo un eclipse." #: ../../TShockAPI/Commands.cs:2283 msgid "Stopped the current blood moon event." -msgstr "" +msgstr "Se detuvo la luna sangrienta en curso." #: ../../TShockAPI/Rest/SecureRest.cs:167 msgid "Successful login" -msgstr "" +msgstr "Inicio de sesión exitoso" #: ../../TShockAPI/Commands.cs:3448 msgid "suffix - Changes a group's suffix." -msgstr "" +msgstr "suffix - Cambia el sufijo de un grupo." #: ../../TShockAPI/Commands.cs:3549 #, csharp-format msgid "Suffix of \"{0}\" is \"{1}\"." -msgstr "" +msgstr "El sufijo de \"{0}\" es \"{1}\"." #: ../../TShockAPI/Commands.cs:3537 #, csharp-format msgid "Suffix of group \"{0}\" set to \"{1}\"." -msgstr "" +msgstr "Sufijo del grupo \"{0}\" establecido como \"{1}\"." #: ../../TShockAPI/Commands.cs:5798 msgid "Sync'd!" -msgstr "" +msgstr "¡Sincronizado!" #: ../../TShockAPI/Handlers/SyncTilePickingHandler.cs:23 #, csharp-format msgid "SyncTilePickingHandler: X and Y position is out of world bounds! - From {0}" -msgstr "" +msgstr "SyncTilePickingHandler: ¡Las posiciones X e Y están fuera de los límites del mundo! - De {0}" #: ../../TShockAPI/Commands.cs:3029 #, csharp-format msgid "Teleported {0} to {1}." -msgstr "" +msgstr "Se teletransportó a {0} hacia {1}." #: ../../TShockAPI/Commands.cs:3094 #, csharp-format msgid "Teleported {0} to yourself." -msgstr "" +msgstr "Se teletransportó a {0} hacia tu posición." #: ../../TShockAPI/Commands.cs:3008 #, csharp-format msgid "Teleported everyone to {0}." -msgstr "" +msgstr "Se teletransportó a todos hacia {0}." #: ../../TShockAPI/Commands.cs:3081 msgid "Teleported everyone to yourself." -msgstr "" +msgstr "Se teletransportó a todos a tu posición." #: ../../TShockAPI/Commands.cs:3183 #, csharp-format msgid "Teleported to {0}, {1}." -msgstr "" +msgstr "Teletransportado a {0}, {1}." #: ../../TShockAPI/Commands.cs:2954 #, csharp-format msgid "Teleported to {0}." -msgstr "" +msgstr "Teletransportado a {0}." #: ../../TShockAPI/Commands.cs:3137 #, csharp-format msgid "Teleported to the '{0}'." -msgstr "" +msgstr "Teletransportado hacia '{0}'." #: ../../TShockAPI/Commands.cs:2923 msgid "Teleported to the map's spawn point." -msgstr "" +msgstr "Teletransportado al punto de aparición del mapa." #: ../../TShockAPI/Commands.cs:2917 msgid "Teleported to your spawn point (home)." -msgstr "" +msgstr "Teletransportado a tu punto de aparición (casa)." #: ../../TShockAPI/Commands.cs:436 msgid "Teleports a player to another player." -msgstr "" +msgstr "Teletransporta a un jugador a la posición de otro." #: ../../TShockAPI/Commands.cs:441 msgid "Teleports a player to yourself." -msgstr "" +msgstr "Teletransporta a un jugador hacia tu posición." #: ../../TShockAPI/Commands.cs:582 msgid "Teleports you to a warp point or manages warps." -msgstr "" +msgstr "Te teletransporta a un punto de warp o permite administrarlos." #: ../../TShockAPI/Commands.cs:446 msgid "Teleports you to an npc." -msgstr "" +msgstr "Te teletransporta a un PNJ." #: ../../TShockAPI/Commands.cs:451 msgid "Teleports you to tile coordinates." -msgstr "" +msgstr "Te teletransporta a coordenadas de casilla." #: ../../TShockAPI/Commands.cs:324 msgid "Temporarily elevates you to Super Admin." -msgstr "" +msgstr "Te asciende temporalmente a Super Admin." #: ../../TShockAPI/Commands.cs:320 msgid "Temporarily sets another player's group." -msgstr "" +msgstr "Establece de forma temporal el grupo de otro usuario." #: ../../TShockAPI/Commands.cs:4759 msgid "Temporary region set points have been removed." -msgstr "" +msgstr "Los puntos temporales para demarcar región fueron quitados." #: ../../TShockAPI/Commands.cs:5411 msgid "Temporary system access has been given to you, so you can run one command." -msgstr "" +msgstr "Se te ha dado acceso temporal de sistema, así que puedes ejecutar un comando." #: ../../TShockAPI/Commands.cs:5387 msgid "Thank you for using TShock for Terraria!" -msgstr "" +msgstr "¡Gracias por usar TShock para Terraria!" #: ../../TShockAPI/Commands.cs:1190 msgid "That group does not exist." -msgstr "" +msgstr "No existe ese grupo." #: ../../TShockAPI/DB/BanManager.cs:258 msgid "The ban is invalid because a current ban for this identifier already exists." -msgstr "" +msgstr "El baneo es inválido porque ya existe un ban actual para este identificador." #: ../../TShockAPI/DB/BanManager.cs:295 msgid "The ban was not valid for an unknown reason." -msgstr "" +msgstr "El ban no es válido por razón desconocida." #: ../../TShockAPI/Commands.cs:2634 msgid "the Brain of Cthulhu" -msgstr "" +msgstr "el Cerebro de Cthulhu" #: ../../TShockAPI/Commands.cs:4522 #, csharp-format msgid "The current time is {0}:{1:D2}." -msgstr "" +msgstr "La hora actual es {0}:{1:D2}." #: ../../TShockAPI/DB/GroupManager.cs:218 msgid "The default usergroup could not be found." -msgstr "" +msgstr "No se pudo encontrar el grupo predeterminado." #: ../../TShockAPI/DB/GroupManager.cs:217 msgid "The default usergroup could not be found. This may indicate a typo in the configuration file, or that the group was renamed or deleted." -msgstr "" +msgstr "No se pudo encontrar el grupo de usuarios predeterminado. Esto puede indicar un error tipográfico en el archivo de configuración, o que el grupo fue renombrado o eliminado." #: ../../TShockAPI/Commands.cs:3174 msgid "The destination coordinates provided don't look like valid numbers." -msgstr "" +msgstr "Las coordenadas de destino proporcionadas no parecen ser números válidos." #: ../../TShockAPI/Commands.cs:3334 #: ../../TShockAPI/Commands.cs:3349 #, csharp-format msgid "The destination warp, {0}, was not found." -msgstr "" +msgstr "No se encontró el warp de destino, {0}." #: ../../TShockAPI/Commands.cs:2641 msgid "the Destroyer" -msgstr "" +msgstr "el Destructor" #: ../../TShockAPI/GetDataHandlers.cs:3427 msgid "The Dungeon Guardian returned you to your spawn point." -msgstr "" +msgstr "El Guardián de la Mazmorra te devolvió a tu punto de aparición." #: ../../TShockAPI/Commands.cs:4386 msgid "The dungeon's position has now been set at your location." -msgstr "" +msgstr "La posición de la mazmorra se ha establecido en tu ubicación." #: ../../TShockAPI/Commands.cs:2655 msgid "the Eater of Worlds" -msgstr "" +msgstr "el Devoramundos" #: ../../TShockAPI/Commands.cs:2736 msgid "the Empress of Light" -msgstr "" +msgstr "la Emperatriz de la Luz" #: ../../TShockAPI/Commands.cs:2663 msgid "the Eye of Cthulhu" -msgstr "" +msgstr "el Ojo de Cthulhu" #: ../../TShockAPI/Commands.cs:2762 msgid "the Flying Dutchman" -msgstr "" +msgstr "el Holandés Volador" #: ../../TShockAPI/Commands.cs:2668 msgid "the Golem" -msgstr "" +msgstr "el Gólem" #: ../../TShockAPI/DB/GroupManager.cs:595 #, csharp-format msgid "The group {0} appeared more than once. Keeping current group settings." -msgstr "" +msgstr "El grupo {0} apareció más de una vez. Manteniendo la configuración actual de los grupos." #: ../../TShockAPI/DB/GroupManager.cs:212 msgid "The guest group could not be found." -msgstr "" +msgstr "No se pudo encontrar el grupo huésped." #: ../../TShockAPI/DB/GroupManager.cs:211 msgid "The guest group could not be found. This may indicate a typo in the configuration file, or that the group was renamed or deleted." -msgstr "" +msgstr "No se pudo encontrar el grupo huésped. Esto puede indicar un error tipográfico en el archivo de configuración, o que el grupo fue renombrado o eliminado." #: ../../TShockAPI/Commands.cs:2788 msgid "the Ice Queen" -msgstr "" +msgstr "la Reina del Hielo" #: ../../TShockAPI/Commands.cs:5376 msgid "The initial setup system is disabled. This incident has been logged." -msgstr "" +msgstr "El sistema de configuración inicial está deshabilitado. Este incidente ha sido registrado." #: ../../TShockAPI/Commands.cs:6097 #, csharp-format msgid "The item type {0} is invalid." -msgstr "" +msgstr "El tipo de objeto {0} no es válido." #: ../../TShockAPI/Commands.cs:2675 msgid "the King Slime" -msgstr "" +msgstr "el Rey Slime" #: ../../TShockAPI/Commands.cs:2750 msgid "the Lunatic Cultist" -msgstr "" +msgstr "el Sectario Lunático" #: ../../TShockAPI/PaginationTools.cs:185 msgid "The method referenced by LineFormatter has thrown an exception. See inner exception for details." -msgstr "" +msgstr "El método al que hace referencia LineFormatter arrojó una excepción. Revise la excepción interna para más detalles." #: ../../TShockAPI/PaginationTools.cs:247 msgid "The method represented by termFormatter has thrown an exception. See inner exception for details." -msgstr "" +msgstr "El método al que representa termFormatter arrojó una excepción. Revise la excepción interna para más detalles." #: ../../TShockAPI/Commands.cs:2729 msgid "the Moon Lord" -msgstr "" +msgstr "el Señor de la Luna" #: ../../TShockAPI/Commands.cs:3410 msgid "The permissions have been added to all of the groups in the system." -msgstr "" +msgstr "Los permisos se agregaron a todos los grupos en el sistema." #: ../../TShockAPI/Commands.cs:3715 msgid "The permissions have been removed from all of the groups in the system." -msgstr "" +msgstr "Los permisos se quitaron de todos los grupos en el sistema." #: ../../TShockAPI/Commands.cs:1860 msgid "The player's character data was successfully uploaded from their initial connection." -msgstr "" +msgstr "Los datos de personaje del jugador fueron subidos exitosamente desde su conexión inicial." #: ../../TShockAPI/Commands.cs:2772 msgid "the Pumpking" -msgstr "" +msgstr "el Rey Calabaza" #: ../../TShockAPI/Commands.cs:2693 msgid "the Queen Bee" -msgstr "" +msgstr "la Abeja Reina" #: ../../TShockAPI/Commands.cs:2742 msgid "the Queen Slime" -msgstr "" +msgstr "la Reina Slime" #: ../../TShockAPI/Rest/RestManager.cs:158 msgid "The REST authentication token." -msgstr "" +msgstr "El tóken de autenticación REST." #: ../../TShockAPI/UpdateManager.cs:150 msgid "The server is out of date. Latest version: " -msgstr "" +msgstr "Servidor desactualizado. Última versión: " #: ../../TShockAPI/Commands.cs:4495 msgid "The spawn rate you provided is out-of-range or not a number." -msgstr "" +msgstr "La tasa de generación provista esta fuera del rango o no es un número." #: ../../TShockAPI/Rest/SecureRest.cs:90 msgid "The specified token queued for destruction failed to be deleted." -msgstr "" +msgstr "El tóken especificado marcado para ser destruido no pudo ser eliminado." #: ../../TShockAPI/Commands.cs:1869 msgid "The target player has not logged in yet." -msgstr "" +msgstr "El jugador objetivo aún no ha iniciado sesión." #: ../../TShockAPI/Commands.cs:1849 msgid "The targeted user cannot have their data uploaded, because they are not a player." -msgstr "" +msgstr "El usuario objetivo no puede subir sus datos, pues no es un jugador." #: ../../TShockAPI/Commands.cs:2707 msgid "the Twins" -msgstr "" +msgstr "los Gemelos" #: ../../TShockAPI/Commands.cs:1136 #, csharp-format msgid "The user {0} does not exist! Therefore, the account was not deleted." -msgstr "" +msgstr "¡El usuario {0} no existe! Por ende, no se eliminó la cuenta." #: ../../TShockAPI/PaginationTools.cs:95 msgid "The value has to be greater than or equal to zero." -msgstr "" +msgstr "El valor debe ser mayor o igual a cero." #: ../../TShockAPI/PaginationTools.cs:81 msgid "The value has to be greater than zero." -msgstr "" +msgstr "El valor debe ser mayor que cero." #: ../../TShockPluginManager/NugetCLI.cs:127 msgid "The versions of plugins you requested aren't compatible with eachother." -msgstr "" +msgstr "Las versiones de plugins que has solicitado no son compatibles entre ellas." #: ../../TShockAPI/Commands.cs:2722 msgid "the Wall of Flesh" -msgstr "" +msgstr "el Muro Carnoso" #: ../../TShockAPI/Bouncer.cs:805 msgid "The world's chest limit has been reached - unable to place more." @@ -6049,168 +6052,168 @@ msgstr "El límite de cofres del mundo fue alcanzado - imposible colocar más." #: ../../TShockAPI/Commands.cs:1672 msgid "There are currently no active bans." -msgstr "" +msgstr "No hay bans activos en este momento." #: ../../TShockAPI/Commands.cs:2093 msgid "There are currently no active REST users." -msgstr "" +msgstr "No hay usuarios REST activos en este momento." #: ../../TShockAPI/Commands.cs:1434 msgid "There are currently no available identifiers." -msgstr "" +msgstr "No hay identificadores disponibles en este momento." #: ../../TShockAPI/Commands.cs:4001 msgid "There are currently no banned items." -msgstr "" +msgstr "No hay objetos prohibidos en este momento." #: ../../TShockAPI/Commands.cs:4179 msgid "There are currently no banned projectiles." -msgstr "" +msgstr "No hay proyectiles prohibidos en este momento." #: ../../TShockAPI/Commands.cs:4355 msgid "There are currently no banned tiles." -msgstr "" +msgstr "No hay bloques prohibidos en este momento." #: ../../TShockAPI/Commands.cs:3776 #, csharp-format msgid "There are currently no permissions for {0}." -msgstr "" +msgstr "Ahora mismo {0} no posee permisos." #: ../../TShockAPI/Commands.cs:5341 msgid "There are currently no players online." -msgstr "" +msgstr "Actualmente no hay jugadores en línea." #: ../../TShockAPI/Commands.cs:4917 msgid "There are currently no regions defined." -msgstr "" +msgstr "Actualmente no hay regiones definidas." #: ../../TShockAPI/Commands.cs:3230 msgid "There are currently no warps defined." -msgstr "" +msgstr "Actualmente no hay warps definidos." #: ../../TShockAPI/RegionHandler.cs:155 msgid "There are no regions at this point, or they are not protected." -msgstr "" +msgstr "Este punto o no tiene regiones, o no están protegidas." #: ../../TShockAPI/RegionHandler.cs:154 msgid "There are no regions at this point." -msgstr "" +msgstr "No hay regiones en este punto." #: ../../TShockAPI/Commands.cs:2713 msgid "There is already a Wall of Flesh." -msgstr "" +msgstr "Ya hay un Muro Carnoso." #: ../../TShockAPI/Commands.cs:936 msgid "There was an error processing your login or authentication related request." -msgstr "" +msgstr "Hubo un error al procesar tu petición de inicio de sesión o relacionada con autenticación." #: ../../TShockPluginManager/NugetCLI.cs:121 msgid "There was an issue figuring out what to download." -msgstr "" +msgstr "Hubo un problema al determinar qué descargar." #: ../../TShockPluginManager/NugetCLI.cs:90 msgid "There was an issue reading the packages.json." -msgstr "" +msgstr "Hubo un problema leyendo el archivo packages.json." #: ../../TShockPluginManager/NugetCLI.cs:99 #, csharp-format msgid "This is the plugin you requested to install." msgid_plural "These are the plugins you requested to install" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Este es el plugin que solicitaste instalar." +msgstr[1] "Estos son los plugins que solicitaste instalar." #: ../../TShockAPI/TShock.cs:1013 #: ../../TShockAPI/TShock.cs:1023 #, csharp-format msgid "This token will display until disabled by verification. ({0}setup)" -msgstr "" +msgstr "Este tóken se mostrará hasta que se deshabilite por verificación. ({0}setup)" #: ../../TShockAPI/Commands.cs:4241 #: ../../TShockAPI/Commands.cs:4299 #, csharp-format msgid "Tile {0} is not banned." -msgstr "" +msgstr "El bloque {0} no está prohibido." #: ../../TShockAPI/Commands.cs:4335 msgid "Tile Ban Sub-Commands ({{0}}/{{1}}):" -msgstr "" +msgstr "Sub-Comandos de Tile Ban ({{0}}/{{1}}):" #: ../../TShockAPI/Commands.cs:4353 msgid "Tile bans ({{0}}/{{1}}):" -msgstr "" +msgstr "Objetos prohibidos ({{0}}/{{1}}):" #: ../../TShockAPI/Bouncer.cs:918 #, csharp-format msgid "Tile kill threshold exceeded {0}." -msgstr "" +msgstr "El umbral de destrucción de bloques ha excedido {0}." #: ../../TShockAPI/Bouncer.cs:936 #, csharp-format msgid "Tile place threshold exceeded {0}." -msgstr "" +msgstr "El umbral de colocación de bloques ha excedido {0}." #: ../../TShockAPI/Commands.cs:6420 #, csharp-format msgid "To buff a player without them knowing, use {0} instead of {1}" -msgstr "" +msgstr "Para potenciar a un jugador sin que lo sepan, usa {0} en lugar de {1}" #: ../../TShockAPI/Commands.cs:6015 #: ../../TShockAPI/Commands.cs:6311 #, csharp-format msgid "To execute this command silently, use {0} instead of {1}" -msgstr "" +msgstr "Para ejecutar este comando silenciosamente, usa {0} en lugar de {1}" #: ../../TShockAPI/Commands.cs:6014 #, csharp-format msgid "To get rid of NPCs without making them drop items, use the {0} command instead." -msgstr "" +msgstr "Para deshacerte de los PNJ sin hacerlos soltar objetos, usa el comando {0} en su lugar." #: ../../TShockAPI/Commands.cs:5464 #, csharp-format msgid "To mute a player without broadcasting to chat, use the command with {0} instead of {1}" -msgstr "" +msgstr "Para silenciar a un jugador sin mostrarlo en el chat, usa el comando con {0} en lugar de {1}" #: ../../TShockAPI/TShock.cs:1012 #: ../../TShockAPI/TShock.cs:1022 #, csharp-format msgid "To setup the server, join the game and type {0}setup {1}" -msgstr "" +msgstr "Para configurar el servidor, únete al juego y escribe {0}setup {1}" #: ../../TShockAPI/Commands.cs:471 msgid "Toggles build protection." -msgstr "" +msgstr "Activa o desactiva la protección de construcción." #: ../../TShockAPI/Commands.cs:484 msgid "Toggles christmas mode (present spawning, santa, etc)." -msgstr "" +msgstr "Activa o desactiva el modo navideño (generación de regalos, santa, etc)." #: ../../TShockAPI/Commands.cs:545 msgid "Toggles godmode on a player." -msgstr "" +msgstr "Activa o desactiva el modo dios en un jugador." #: ../../TShockAPI/Commands.cs:480 msgid "Toggles halloween mode (goodie bags, pumpkins, etc)." -msgstr "" +msgstr "Activa o desactiva el modo halloween (bolsitas sorpresa, calabazas, etc)." #: ../../TShockAPI/Commands.cs:496 msgid "Toggles spawn protection." -msgstr "" +msgstr "Activa o desactiva la protección del área de aparición." #: ../../TShockAPI/Commands.cs:492 msgid "Toggles the world's hardmode status." -msgstr "" +msgstr "Activa o desactiva el modo difícil del mundo." #: ../../TShockAPI/Commands.cs:591 msgid "Toggles to either ignore or recieve whispers from other players." -msgstr "" +msgstr "Cambia entre ignorar o recibir susurros de otros jugadores." #: ../../TShockAPI/Commands.cs:461 msgid "Toggles whether other people can teleport you." -msgstr "" +msgstr "Alterna entre si otras personas pueden o no teletransportarse a tu posición." #: ../../TShockAPI/Commands.cs:276 msgid "Toggles whether you receive server logs." -msgstr "" +msgstr "Alterna si recibes o no los registros del servidor." #: ../../TShockAPI/Commands.cs:777 msgid "Too many invalid login attempts." @@ -6218,7 +6221,7 @@ msgstr "Demasiados intentos de inicio de sesión inválidos." #: ../../TShockAPI/Commands.cs:6646 msgid "Topaz Gemtree" -msgstr "" +msgstr "Arbolgema de Topacio" #: ../../TShockAPI/Commands.cs:1224 #, csharp-format @@ -6227,289 +6230,289 @@ msgstr "Tiempo total de procesador: {0}" #: ../../TShockAPI/Commands.cs:5202 msgid "tp - Teleports you to the given region's center." -msgstr "" +msgstr "tp - Te teletransporta al centro de la región determinada." #: ../../TShockAPI/Commands.cs:6511 msgid "Trees types & misc available to use. ({{0}}/{{1}}):" -msgstr "" +msgstr "Tipos de árboles & misc disponibles para su uso. ({{0}}/{{1}}):" #: ../../TShockAPI/Commands.cs:6700 #, csharp-format msgid "Tried to grow a {0}." -msgstr "" +msgstr "Se intentó hacer crecer {0}." #: ../../TShockAPI/TShock.cs:399 #, csharp-format msgid "TShock {0} ({1}) now running." -msgstr "" +msgstr "TShock {0} ({1}) ahora en ejecución." #: ../../TShockAPI/Commands.cs:1367 msgid "TShock Ban Help" -msgstr "" +msgstr "Ayuda de TShock Ban" #: ../../TShockAPI/TShock.cs:449 msgid "TShock comes with no warranty & is free software." -msgstr "" +msgstr "TShock es gratuito y viene sin garantía." #: ../../TShockAPI/TShock.cs:461 msgid "TShock encountered a problem from which it cannot recover. The following message may help diagnose the problem." -msgstr "" +msgstr "TShock encontró un problema del que no puede recuperarse. El siguiente mensaje puede ayudar a diagnosticar el problema." #: ../../TShockAPI/TShock.cs:1021 msgid "TShock Notice: setup-code.txt is still present, and the code located in that file will be used." -msgstr "" +msgstr "Aviso de TShock: setup-code.txt todavía está presente, y el código ubicado en ese archivo será utilizado." #: ../../TShockAPI/TShock.cs:353 msgid "TShock was improperly shut down. Please use the exit command in the future to prevent this." -msgstr "" +msgstr "TShock se cerró incorrectamente. Utilice el comando exit en el futuro para evitar esto." #: ../../TShockAPI/TShock.cs:997 msgid "TShock will now disable the initial setup system and remove setup-code.txt as it is no longer needed." -msgstr "" +msgstr "TShock desactivará ahora el sistema de configuración inicial y eliminará setup-code.txt pues ya no es necesario." #: ../../TShockAPI/Commands.cs:5299 #, csharp-format msgid "TShock: {0} {1}." -msgstr "" +msgstr "TShock: {0} {1}." #: ../../TShockAPI/Commands.cs:1433 #, csharp-format msgid "Type {0}ban help identifiers {{0}} for more." -msgstr "" +msgstr "Escribe {0}ban help identifiers {{0}} para ver más." #: ../../TShockAPI/Commands.cs:1671 #, csharp-format msgid "Type {0}ban list {{0}} for more." -msgstr "" +msgstr "Escribe {0}ban list {{0}} para ver más." #: ../../TShockAPI/Commands.cs:3455 #, csharp-format msgid "Type {0}group help {{0}} for more sub-commands." -msgstr "" +msgstr "Escribe {0}group help {{0}} para más sub-comandos." #: ../../TShockAPI/Commands.cs:3746 #, csharp-format msgid "Type {0}group list {{0}} for more." -msgstr "" +msgstr "Escribe {0}group list {{0}} para ver más." #: ../../TShockAPI/Commands.cs:3775 #, csharp-format msgid "Type {0}group listperm {1} {{0}} for more." -msgstr "" +msgstr "Escribe {0}group listperm {1} {{0}} para ver más." #: ../../TShockAPI/Commands.cs:6512 #, csharp-format msgid "Type {0}grow help {{0}} for more sub-commands." -msgstr "" +msgstr "Escribe {0}grow help {{0}} para más sub-comandos." #: ../../TShockAPI/Commands.cs:5261 #, csharp-format msgid "Type {0}help {{0}} for more." -msgstr "" +msgstr "Escribe {0}help {{0}} para ver más." #: ../../TShockAPI/Commands.cs:3982 #, csharp-format msgid "Type {0}itemban help {{0}} for more sub-commands." -msgstr "" +msgstr "Escribe {0}itemban help {{0}} para más sub-comandos." #: ../../TShockAPI/Commands.cs:4000 #, csharp-format msgid "Type {0}itemban list {{0}} for more." -msgstr "" +msgstr "Escribe {0}itemban list {{0}} para ver más." #: ../../TShockAPI/Commands.cs:1053 #, csharp-format msgid "Type {0}login \"{1}\" {2} to log-in to your account." -msgstr "" +msgstr "Escribe {0}login \"{1}\" {2} para iniciar sesión en tu cuenta." #: ../../TShockAPI/Commands.cs:1055 #, csharp-format msgid "Type {0}login {1} to log-in to your account." -msgstr "" +msgstr "Escribe {0}login {1} para iniciar sesión en tu cuenta." #: ../../TShockAPI/Commands.cs:1050 #, csharp-format msgid "Type {0}login to log-in to your account using your UUID." -msgstr "" +msgstr "Escribe {0}login para iniciar sesión usando tu UUID." #: ../../TShockAPI/Commands.cs:4160 #, csharp-format msgid "Type {0}projban help {{0}} for more sub-commands." -msgstr "" +msgstr "Escribe {0}projban help {{0}} para más sub-comandos." #: ../../TShockAPI/Commands.cs:4178 #, csharp-format msgid "Type {0}projban list {{0}} for more." -msgstr "" +msgstr "Escribe {0}projban list {{0}} para ver más." #: ../../TShockAPI/Commands.cs:5209 #, csharp-format msgid "Type {0}region {{0}} for more sub-commands." -msgstr "" +msgstr "Escribe {0}region {{0}} para más sub-comandos." #: ../../TShockAPI/Commands.cs:4987 #, csharp-format msgid "Type {0}region info {1} {{0}} for more information." -msgstr "" +msgstr "Escribe {0}region info {1} {{0}} para más información." #: ../../TShockAPI/Commands.cs:4916 #, csharp-format msgid "Type {0}region list {{0}} for more." -msgstr "" +msgstr "Escribe {0}region list {{0}} para ver más." #: ../../TShockAPI/Commands.cs:2095 #, csharp-format msgid "Type {0}rest listusers {{0}} for more." -msgstr "" +msgstr "Escribe {0}rest listusers {{0}} para ver más." #: ../../TShockAPI/Commands.cs:4336 #, csharp-format msgid "Type {0}tileban help {{0}} for more sub-commands." -msgstr "" +msgstr "Escribe {0}tileban help {{0}} para más sub-comandos." #: ../../TShockAPI/Commands.cs:4354 #, csharp-format msgid "Type {0}tileban list {{0}} for more." -msgstr "" +msgstr "Escribe {0}tileban list {{0}} para ver más." #: ../../TShockAPI/Commands.cs:3229 #, csharp-format msgid "Type {0}warp list {{0}} for more." -msgstr "" +msgstr "Escribe {0}warp list {{0}} para ver más." #: ../../TShockAPI/Commands.cs:5367 #, csharp-format msgid "Type {0}who {1} for more." -msgstr "" +msgstr "Escribe {0}who {1} para ver más." #: ../../TShockAPI/PaginationTools.cs:108 msgid "Type / {{0}} for more." -msgstr "" +msgstr "Escribe / {{0}} para ver más." #: ../../TShockAPI/Commands.cs:6444 #, csharp-format msgid "Unable to find any buff named \"{0}\"" -msgstr "" +msgstr "No se encontró ningún buff de nombre \"{0}\"" #: ../../TShockAPI/Commands.cs:6385 #, csharp-format msgid "Unable to find any buffs named \"{0}\"" -msgstr "" +msgstr "No se encontraron buffs de nombre \"{0}\"" #: ../../TShockAPI/Commands.cs:6429 #, csharp-format msgid "Unable to find any player named \"{0}\"" -msgstr "" +msgstr "No se encontró ningún jugador de nombre \"{0}\"" #: ../../TShockAPI/Commands.cs:6323 #, csharp-format msgid "Unable to find any players named \"{0}\"" -msgstr "" +msgstr "No se encontraron jugadores de nombre \"{0}\"" #: ../../TShockAPI/Commands.cs:5670 #, csharp-format msgid "Unable to launch {0} because he is not logged in." -msgstr "" +msgstr "No se puede hacer despegar a {0} porque no está conectado." #: ../../TShockAPI/Commands.cs:5672 #, csharp-format msgid "Unable to launch {0} because she is not logged in." -msgstr "" +msgstr "No se puede hacer despegar a {0} porque no está conectada." #: ../../TShockAPI/TShock.cs:1484 msgid "Unable to parse command '{0}' from player {1}." -msgstr "" +msgstr "No se puede analizar el comando '{0}' del jugador {1}." #: ../../TShockAPI/TShock.cs:1483 msgid "Unable to parse command. Please contact an administrator for assistance." -msgstr "" +msgstr "No se puede analizar el comando. Por favor contacte a un administrador para obtener ayuda." #: ../../TShockAPI/Commands.cs:2885 msgid "Unable to spawn a Wall of Flesh based on its current state or your current location." -msgstr "" +msgstr "No se puede generar un Muro Carnoso basado en su estado o tu ubicación actual." #: ../../TShockAPI/DB/UserManager.cs:450 #, csharp-format msgid "Unable to verify the password hash for user {0} ({1})" -msgstr "" +msgstr "No se puede verificar el hash de contraseña para el usuario {0} ({1})" #: ../../TShockAPI/Commands.cs:3912 #, csharp-format msgid "Unbanned {0}." -msgstr "" +msgstr "Se revocó la prohibición de {0}." #: ../../TShockAPI/Commands.cs:4094 #, csharp-format msgid "Unbanned projectile {0}." -msgstr "" +msgstr "Se revocó la prohibición del proyectil {0}." #: ../../TShockAPI/Commands.cs:4270 #, csharp-format msgid "Unbanned tile {0}." -msgstr "" +msgstr "Se revocó la prohibición del bloque {0}." #: ../../TShockAPI/Commands.cs:1455 #, csharp-format msgid "Unknown ban command. Try {0} {1}, {2}, {3}, {4}, {5}, or {6}." -msgstr "" +msgstr "Comando de ban desconocido. Prueba {0} {1}, {2}, {3}, {4}, {5}, o {6}." #: ../../TShockAPI/Commands.cs:6694 msgid "Unknown plant!" -msgstr "" +msgstr "¡Planta desconocida!" #: ../../TShockAPI/Bouncer.cs:246 msgid "Unrecognized player direction" -msgstr "" +msgstr "Dirección del jugador no reconocida" #: ../../TShockAPI/TShock.cs:462 msgid "Until the problem is resolved, TShock will not be able to start (and will crash on startup)." -msgstr "" +msgstr "Hasta que no se resuelva el problema, TShock no podrá iniciarse (y crasheará al cargar)." #: ../../TShockAPI/UpdateManager.cs:120 #, csharp-format msgid "Update server did not respond with an OK. Server message: [error {0}] {1}" -msgstr "" +msgstr "El servidor de actualización no respondió con un OK. Mensaje del servidor: [error {0}] {1}" #: ../../TShockAPI/DB/UserManager.cs:197 msgid "UpdateLogin SQL returned an error" -msgstr "" +msgstr "SQL UpdateLogin devolvió un error" #: ../../TShockAPI/UpdateManager.cs:83 #: ../../TShockAPI/UpdateManager.cs:86 #, csharp-format msgid "UpdateManager warning: {0}" -msgstr "" +msgstr "Advertencia de UpdateManager: {0}" #: ../../TShockAPI/Commands.cs:316 msgid "Upload the account information when you joined the server as your Server Side Character data." -msgstr "" +msgstr "Sube la información de la cuenta cuando te uniste al servidor como tus datos de Personaje Exclusivo-de-Servidor." #: ../../TShockAPI/Commands.cs:1916 #, csharp-format msgid "Usage: {0}tempgroup [time]" -msgstr "" +msgstr "Uso: {0}tempgroup [tiempo]" #: ../../TShockAPI/Commands.cs:2001 msgid "Usage: /sudo [command]." -msgstr "" +msgstr "Uso: /sudo [comando]." #: ../../TShockAPI/Commands.cs:1844 #: ../../TShockAPI/Commands.cs:1850 msgid "Usage: /uploadssc [playername]." -msgstr "" +msgstr "Uso: /uploadssc [nombreJugador]." #: ../../TShockAPI/Commands.cs:2441 #, csharp-format msgid "Use \"{0}worldevent rain slime\" to start slime rain!" -msgstr "Usa \"{0}worldevent rain slime\" para iniciar una lluvia de slimes." +msgstr "¡Usa \"{0}worldevent rain slime\" para iniciar una lluvia de slimes!" #: ../../TShockAPI/TSPlayer.cs:1971 msgid "Use \"my query\" for items with spaces." -msgstr "Usa comillas Ej. \"mi nombre\" para objetos con espacios." +msgstr "Usa comillas (Ej. \"tal objeto\") para objetos con espacios." #: ../../TShockAPI/TSPlayer.cs:1972 msgid "Use tsi:[number] or tsn:[username] to distinguish between user IDs and usernames." -msgstr "Usa tsi:[number] o tsn:[username] para distinguir entre IDs de usuario y nombres de usuario." +msgstr "Usa tsi:[number] o tsn:[username] para distinguir entre IDs y nombres de usuario." #: ../../TShockAPI/Commands.cs:227 msgid "Used to authenticate as superadmin when first setting up TShock." @@ -6518,7 +6521,7 @@ msgstr "Se usa para autenticarse como superadmin la primera vez que configuras T #: ../../TShockAPI/Rest/RestManager.cs:1341 #, csharp-format msgid "User {0} '{1}' doesn't exist" -msgstr "El usuario {0} '{1}' no existe." +msgstr "El usuario {0} '{1}' no existe" #: ../../TShockAPI/Commands.cs:1114 #, csharp-format @@ -6533,7 +6536,7 @@ msgstr "El usuario {0} no pudo ser añadido, comprueba la consola para más deta #: ../../TShockAPI/Commands.cs:1198 #, csharp-format msgid "User {0} could not be added. Check console for details." -msgstr "El usuario {0} no pudo ser añadido, comprueba la consola para más detalles." +msgstr "El usuario {0} no pudo ser añadido. Comprueba la consola para más detalles." #: ../../TShockAPI/Commands.cs:1194 #: ../../TShockAPI/Commands.cs:1302 @@ -6544,7 +6547,7 @@ msgstr "El usuario {0} no existe." #: ../../TShockAPI/DB/UserManager.cs:612 #, csharp-format msgid "User account {0} already exists" -msgstr "La cuenta de usuario {0} ya existe." +msgstr "La cuenta de usuario {0} ya existe" #: ../../TShockAPI/DB/UserManager.cs:625 #, csharp-format @@ -6553,109 +6556,109 @@ msgstr "La cuenta de usuario {0} no existe" #: ../../TShockAPI/Commands.cs:1204 msgid "User management command help:" -msgstr "" +msgstr "Ayuda del comando de gestión de usuario:" #: ../../TShockAPI/Rest/SecureRest.cs:138 #: ../../TShockAPI/Rest/SecureRest.cs:144 #: ../../TShockAPI/Rest/SecureRest.cs:152 msgid "Username or password may be incorrect or this account may not have sufficient privileges." -msgstr "" +msgstr "El nombre de usuario o la contraseña pueden ser incorrectos o esta cuenta puede no tener suficientes privilegios." #: ../../TShockAPI/TShock.cs:390 #: ../../TShockAPI/TShock.cs:394 #, csharp-format msgid "Using {0} for tile implementation" -msgstr "" +msgstr "Usando {0} para la implementación de casillas" #: ../../TShockAPI/Bouncer.cs:1794 #, csharp-format msgid "Using {0} on non-honey" -msgstr "" +msgstr "Usando {0} en algo que no es miel" #: ../../TShockAPI/Bouncer.cs:1803 #, csharp-format msgid "Using {0} on non-lava" -msgstr "" +msgstr "Usando {0} en algo que no es lava" #: ../../TShockAPI/Bouncer.cs:1810 #, csharp-format msgid "Using {0} on non-shimmer" -msgstr "" +msgstr "Usando {0} en algo que no es fulgor" #: ../../TShockAPI/Bouncer.cs:1785 #, csharp-format msgid "Using {0} on non-water" -msgstr "" +msgstr "Usando {0} en algo que no es agua" #: ../../TShockAPI/Bouncer.cs:1817 #, csharp-format msgid "Using {0} on non-water or shimmer" -msgstr "" +msgstr "Usando {0} en algo que no es agua o fulgor" #: ../../TShockAPI/Bouncer.cs:1825 #, csharp-format msgid "Using {0} to manipulate unknown liquid {1}" -msgstr "" +msgstr "Usando {0} para manipular el líquido desconocido {1}" #: ../../TShockAPI/Bouncer.cs:1740 #, csharp-format msgid "Using banned {0} to manipulate liquid" -msgstr "" +msgstr "Usando el {0} prohibido para manipular líquido" #: ../../TShockAPI/Bouncer.cs:1763 msgid "Using banned honey bucket without permissions" -msgstr "" +msgstr "Usando el cubo de miel prohibido sin permisos" #: ../../TShockAPI/Bouncer.cs:1756 msgid "Using banned lava bucket without permissions" -msgstr "" +msgstr "Usando cubo de lava prohibido sin permisos" #: ../../TShockAPI/Bouncer.cs:1770 msgid "Using banned shimmering water bucket without permissions" -msgstr "" +msgstr "Usando cubo de fulgor prohibido sin permisos" #: ../../TShockAPI/Bouncer.cs:1749 msgid "Using banned water bucket without permissions" -msgstr "" +msgstr "Usando cubo de agua prohibido sin permisos" #: ../../TShockAPI/Commands.cs:924 msgid "UUID does not match this character." -msgstr "" +msgstr "El UUID no coincide con este personaje." #: ../../TShockAPI/Commands.cs:2147 #, csharp-format msgid "Valid event types: {0}." -msgstr "" +msgstr "Tipos de eventos válidos: {0}." #: ../../TShockAPI/Commands.cs:2148 #, csharp-format msgid "Valid invasion types if spawning an invasion: {0}." -msgstr "" +msgstr "Tipos de invasión válidos si se genera una invasión: {0}." #: ../../TShockAPI/Commands.cs:2333 #, csharp-format msgid "Valid invasion types: {0}." -msgstr "" +msgstr "Tipos de invasión válidos: {0}." #: ../../TShockAPI/Commands.cs:2549 #, csharp-format msgid "Valid world modes: {0}" -msgstr "" +msgstr "Modos de mundo válidos: {0}" #: ../../TShockAPI/Commands.cs:3250 #, csharp-format msgid "Warp {0} already exists." -msgstr "" +msgstr "El warp {0} ya existe." #: ../../TShockAPI/Commands.cs:3286 #, csharp-format msgid "Warp {0} is now private." -msgstr "" +msgstr "El warp {0} es ahora privado." #: ../../TShockAPI/Commands.cs:3288 #, csharp-format msgid "Warp {0} is now public." -msgstr "" +msgstr "El warp {0} es ahora público." #: ../../TShockAPI/Commands.cs:3246 #, csharp-format @@ -6674,23 +6677,23 @@ msgstr "Te transportas a {0}." #: ../../TShockAPI/Commands.cs:3228 msgid "Warps ({{0}}/{{1}}):" -msgstr "" +msgstr "Warps ({{0}}/{{1}}):" #: ../../TShockAPI/TShock.cs:448 msgid "Welcome to TShock for Terraria!" -msgstr "" +msgstr "¡Bienvenido a TShock para Terraria!" #: ../../TShockAPI/Commands.cs:5518 msgid "Whisper Syntax" -msgstr "" +msgstr "Sintaxis de Whisper" #: ../../TShockAPI/Commands.cs:6608 msgid "Willow Tree" -msgstr "" +msgstr "Sauce" #: ../../TShockPluginManager/NugetCLI.cs:85 msgid "Without a list of plugins to install, no plugins can be installed." -msgstr "" +msgstr "No se pueden instalar plugins sin una lista de estos para su instalación." #: ../../TShockAPI/BackupManager.cs:80 #, csharp-format @@ -6709,30 +6712,30 @@ msgstr "Tipo de mundo cambiado a {0}." #: ../../TShockAPI/TShock.cs:818 #, csharp-format msgid "World name will be overridden by: {0}" -msgstr "" +msgstr "El nombre del mundo será reemplazado por: {0}" #: ../../TShockAPI/TShock.cs:775 #, csharp-format msgid "World path has been set to {0}" -msgstr "" +msgstr "La ruta del mundo se ha establecido en {0}" #: ../../TShockAPI/SaveManager.cs:137 #, csharp-format msgid "World saved at ({0})" -msgstr "" +msgstr "Mundo guardado en ({0})" #: ../../TShockAPI/SaveManager.cs:135 msgid "World saved." -msgstr "" +msgstr "Mundo guardado." #: ../../TShockAPI/Commands.cs:4948 #, csharp-format msgid "X: {0}; Y: {1}; W: {2}; H: {3}, Z: {4}" -msgstr "" +msgstr "X: {0}; Y: {1}; W: {2}; H: {3}, Z: {4}" #: ../../TShockAPI/Commands.cs:5938 msgid "You are already dead!" -msgstr "" +msgstr "¡Ya estás muerto!" #: ../../TShockAPI/Commands.cs:783 msgid "You are already logged in, and cannot login again." @@ -6740,31 +6743,31 @@ msgstr "Tu sesión ya ha sido iniciada, por lo que no puedes volver a iniciarla. #: ../../TShockAPI/Commands.cs:2913 msgid "You are dead. Dead players can't go home." -msgstr "Estas muerto(a). Los jugadores muertos no pueden ir a casa." +msgstr "Estás muerto(a). Los jugadores muertos no pueden ir a casa." #: ../../TShockAPI/TShock.cs:1501 msgid "You are muted!" -msgstr "" +msgstr "¡Estás silenciado!" #: ../../TShockAPI/Commands.cs:5428 #: ../../TShockAPI/Commands.cs:5443 #: ../../TShockAPI/Commands.cs:5534 #: ../../TShockAPI/Commands.cs:5571 msgid "You are muted." -msgstr "" +msgstr "Estás silenciado." #: ../../TShockAPI/Commands.cs:6758 #, csharp-format msgid "You are no longer in god mode." -msgstr "" +msgstr "Ya no estás en modo dios." #: ../../TShockAPI/Commands.cs:5988 msgid "You are not dead!" -msgstr "" +msgstr "¡No estás muerto!" #: ../../TShockAPI/Commands.cs:5454 msgid "You are not in a party!" -msgstr "" +msgstr "¡No estás en un grupo!" #: ../../TShockAPI/Commands.cs:945 msgid "You are not logged-in. Therefore, you cannot logout." @@ -6772,33 +6775,33 @@ msgstr "No has iniciado sesión. Por ende, no puedes cerrarla." #: ../../TShockAPI/Configuration/TShockConfig.cs:333 msgid "You are not on the whitelist." -msgstr "" +msgstr "No estás en la lista blanca." #: ../../TShockAPI/Commands.cs:5618 msgid "You are now being annoyed." -msgstr "" +msgstr "Ahora estás siendo molestado." #: ../../TShockAPI/Commands.cs:6757 #, csharp-format msgid "You are now in god mode." -msgstr "" +msgstr "Ahora estas en modo dios." #: ../../TShockAPI/Commands.cs:6406 #: ../../TShockAPI/Commands.cs:6463 #, csharp-format msgid "You buffed yourself with {0} ({1}) for {2} seconds." -msgstr "" +msgstr "Te potencias con {0} ({1}) por {2} segundos." #: ../../TShockAPI/Commands.cs:6049 #, csharp-format msgid "You butchered {0} NPC." msgid_plural "You butchered {0} NPCs." -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Mataste {0} PNJ." +msgstr[1] "Mataste {0} PNJ." #: ../../TShockAPI/TShock.cs:450 msgid "You can modify & distribute it under the terms of the GNU GPLv3." -msgstr "" +msgstr "Puedes modificar y distribuirlo bajo los términos de la GNU GPLv3." #: ../../TShockAPI/Commands.cs:694 #, csharp-format @@ -6808,23 +6811,23 @@ msgstr "Puedes utilizar '{0}sudo {0}{1}' para anular esta comprobación." #: ../../TShockAPI/Commands.cs:5602 #, csharp-format msgid "You can use {0} instead of {1} to annoy a player silently." -msgstr "" +msgstr "Puedes usar {0} en lugar de {1} para molestar a un jugador silenciosamente." #: ../../TShockAPI/Commands.cs:5818 #: ../../TShockAPI/Commands.cs:5920 #, csharp-format msgid "You can use {0} instead of {1} to execute this command silently." -msgstr "" +msgstr "Puede usar {0} en lugar de {1} para ejecutar este comando de forma silenciosa." #: ../../TShockAPI/Commands.cs:5686 #, csharp-format msgid "You can use {0} instead of {1} to launch a firework silently." -msgstr "" +msgstr "Puedes usar {0} en lugar de {1} para lanzar un fuego artificial silenciosamente." #: ../../TShockAPI/Commands.cs:5630 #, csharp-format msgid "You can use {0} instead of {1} to rocket a player silently." -msgstr "" +msgstr "Puedes usar {0} en lugar de {1} para hacer despegar a un jugador silenciosamente." #: ../../TShockAPI/Commands.cs:5564 #, csharp-format @@ -6838,11 +6841,11 @@ msgstr "Puedes usar {0}{1} para susurrar a otros jugadores." #: ../../TShockAPI/Commands.cs:6733 msgid "You can't god mode a non player!" -msgstr "No puedes poner en modo dios a alguien que no es un jugador." +msgstr "¡No puedes poner en modo dios nada que no sea un jugador!" #: ../../TShockAPI/Commands.cs:6333 msgid "You can't heal a dead player!" -msgstr "No puedes curar a un jugador muerto." +msgstr "¡No puedes curar a un jugador muerto!" #: ../../TShockAPI/Commands.cs:1337 msgid "You can't kick another admin." @@ -6851,7 +6854,7 @@ msgstr "No puedes expulsar a otro admin." #: ../../TShockAPI/DB/GroupManager.cs:501 #: ../../TShockAPI/DB/GroupManager.cs:502 msgid "You can't remove the default guest group." -msgstr "No puedes quitar el grupo huésped inicial." +msgstr "No puedes quitar el grupo huésped predeterminado." #: ../../TShockAPI/Commands.cs:5957 msgid "You can't respawn the server console!" @@ -6859,7 +6862,7 @@ msgstr "¡No puedes reaparecer a la consola del servidor!" #: ../../TShockAPI/Commands.cs:814 msgid "You cannot login whilst crowd controlled." -msgstr "" +msgstr "No puedes iniciar sesión mientras se te está conteniendo." #: ../../TShockAPI/Commands.cs:800 msgid "You cannot login whilst dead." @@ -6872,11 +6875,11 @@ msgstr "No puedes iniciar sesión mientras usas un objeto." #: ../../TShockAPI/Commands.cs:6144 #: ../../TShockAPI/Commands.cs:6283 msgid "You cannot spawn banned items." -msgstr "No puedes crear objetos baneados." +msgstr "No puedes generar objetos prohibidos." #: ../../TShockAPI/GetDataHandlers.cs:3445 msgid "You cannot use the Enchanted Sundial because time is stopped." -msgstr "No puedes usar el Reloj de sol encantado porque el tiempo está parado." +msgstr "No puedes usar el Reloj de Sol Encantado porque el tiempo está detenido." #: ../../TShockAPI/Commands.cs:5541 msgid "You cannot whisper to yourself." @@ -6893,8 +6896,8 @@ msgstr[1] "Has eliminado {0} objetos en un radio de {1}." #, csharp-format msgid "You deleted {0} NPC within a radius of {1}." msgid_plural "You deleted {0} NPCs within a radius of {1}." -msgstr[0] "Has eliminado {0} NPC en un radio de {1}." -msgstr[1] "Has eliminado {0} NPCs en un radio de {1}." +msgstr[0] "Has eliminado {0} PNJ en un radio de {1}." +msgstr[1] "Has eliminado {0} PNJ en un radio de {1}." #: ../../TShockAPI/Commands.cs:5900 #, csharp-format @@ -6905,7 +6908,7 @@ msgstr[1] "Has eliminado {0} proyectiles en un radio de {1}." #: ../../TShockAPI/Commands.cs:6316 msgid "You didn't put a player name." -msgstr "No has puesto el nombre del jugador." +msgstr "No has puesto un nombre de jugador." #: ../../TShockAPI/GetDataHandlers.cs:4211 msgid "You died! Normally, you'd be banned." @@ -6942,15 +6945,15 @@ msgstr "No tienes permiso de detener la expansión de biomas del servidor." #: ../../TShockAPI/Handlers/NetModules/CreativePowerHandler.cs:109 msgid "You do not have permission to freeze the rain strength of the server." -msgstr "No tienes permiso de inmovilizar la fuerza de la lluvia del servidor." +msgstr "No tienes permiso de congelar la fuerza de la lluvia del servidor." #: ../../TShockAPI/Handlers/NetModules/CreativePowerHandler.cs:103 msgid "You do not have permission to freeze the time of the server." -msgstr "No tienes permiso de inmovilizar el tiempo del servidor." +msgstr "No tienes permiso de detener el tiempo del servidor." #: ../../TShockAPI/Handlers/NetModules/CreativePowerHandler.cs:110 msgid "You do not have permission to freeze the wind strength of the server." -msgstr "No tienes permiso de inmovilizar la fuerza del viento del servidor." +msgstr "No tienes permiso de congelar la fuerza del viento del servidor." #: ../../TShockAPI/Commands.cs:6711 msgid "You do not have permission to god mode another player." @@ -6962,24 +6965,24 @@ msgstr "No tienes permiso para hacer crecer este tipo de árbol." #: ../../TShockAPI/GetDataHandlers.cs:2960 msgid "You do not have permission to hurt Town NPCs." -msgstr "No tienes permiso para dañar a NPCs de ciudad." +msgstr "No tienes permiso para dañar a PNJ ciudadanos." #: ../../TShockAPI/Handlers/RequestTileEntityInteractionHandler.cs:22 msgid "You do not have permission to modify a Hat Rack in a protected area!" -msgstr "No tienes permiso para modificar el Perchero para sombreros en un área protegida." +msgstr "¡No tienes permiso para modificar el Perchero para Sombreros en un área protegida!" #: ../../TShockAPI/Handlers/DisplayDollItemSyncHandler.cs:21 #: ../../TShockAPI/Handlers/RequestTileEntityInteractionHandler.cs:28 msgid "You do not have permission to modify a Mannequin in a protected area!" -msgstr "No tienes permiso para modificar un Maniquí en un área protegida." +msgstr "¡No tienes permiso para modificar un Maniquí en un área protegida!" #: ../../TShockAPI/Handlers/RequestTileEntityInteractionHandler.cs:34 msgid "You do not have permission to modify a TileEntity in a protected area!" -msgstr "No tienes permiso para modificar una TileEntity en un área protegida." +msgstr "¡No tienes permiso para modificar una TileEntity en un área protegida!" #: ../../TShockAPI/Handlers/NetModules/CreativePowerHandler.cs:114 msgid "You do not have permission to modify the NPC spawn rate of the server." -msgstr "No tienes permiso para modificar la tasa de aparición de NPCs del servidor." +msgstr "No tienes permiso para modificar la tasa de generación de PNJ del servidor." #: ../../TShockAPI/Handlers/NetModules/CreativePowerHandler.cs:107 msgid "You do not have permission to modify the rain strength of the server." @@ -7003,7 +7006,7 @@ msgstr "No tienes permiso para cambiar la fuerza del viento del servidor." #: ../../TShockAPI/Handlers/NetModules/CreativePowerHandler.cs:112 msgid "You do not have permission to modify the world difficulty of the server." -msgstr "No tienes permiso para cambiar la dificultad del servidor." +msgstr "No tienes permiso para cambiar la dificultad de mundo del servidor." #: ../../TShockAPI/Commands.cs:5479 #, csharp-format @@ -7020,7 +7023,7 @@ msgstr "No tienes permiso para colocar actuadores." #: ../../TShockAPI/GetDataHandlers.cs:3967 msgid "You do not have permission to place Logic Sensors." -msgstr "No tienes permiso para colocar sensores lógicos." +msgstr "No tienes permiso para colocar Sensores Lógicos." #: ../../TShockAPI/Bouncer.cs:655 #: ../../TShockAPI/Bouncer.cs:2263 @@ -7029,7 +7032,7 @@ msgstr "No tienes permiso para colocar este bloque." #: ../../TShockAPI/GetDataHandlers.cs:3493 msgid "You do not have permission to relocate Town NPCs." -msgstr "No tienes permiso para reubicar a los NPCs de ciudad." +msgstr "No tienes permiso para reubicar a los PNJ ciudadanos." #: ../../TShockAPI/Commands.cs:5965 msgid "You do not have permission to respawn another player." @@ -7058,7 +7061,7 @@ msgstr "No tienes permiso para iniciar invasiones." #: ../../TShockAPI/Commands.cs:2156 #, csharp-format msgid "You do not have permission to start the {0} event." -msgstr "No tienes permiso para iniciar el evento {0}" +msgstr "No tienes permiso para iniciar el evento {0}." #: ../../TShockAPI/GetDataHandlers.cs:4145 msgid "You do not have permission to start the Old One's Army." @@ -7070,11 +7073,11 @@ msgstr "No tienes permiso para invocar jefes." #: ../../TShockAPI/GetDataHandlers.cs:2970 msgid "You do not have permission to summon the Empress of Light." -msgstr "No tienes permiso para invocar a la Emperatriz de la luz." +msgstr "No tienes permiso para invocar a la Emperatriz de la Luz." #: ../../TShockAPI/GetDataHandlers.cs:2987 msgid "You do not have permission to summon the Lunatic Cultist!" -msgstr "¡No tienes permiso para invocar al Cultista Lunático!" +msgstr "¡No tienes permiso para invocar al Sectario Lunático!" #: ../../TShockAPI/Commands.cs:3070 msgid "You do not have permission to teleport all other players." @@ -7103,7 +7106,7 @@ msgstr "No tienes permiso de teletransportarte usando pilones." #: ../../TShockAPI/GetDataHandlers.cs:3769 msgid "You do not have permission to teleport using Wormhole Potions." -msgstr "No tienes permiso para teletransportarte usando Pociones de agujero de gusano." +msgstr "No tienes permiso para teletransportarte usando Pociones de Agujero de Gusano." #: ../../TShockAPI/Commands.cs:5154 msgid "You do not have permission to teleport." @@ -7115,11 +7118,11 @@ msgstr "No tienes permiso de activar el modo dios." #: ../../TShockAPI/Commands.cs:1839 msgid "You do not have permission to upload another player's character join-state server-side-character data." -msgstr "" +msgstr "No tienes permiso para subir datos de join-state de personaje exclusivo-de-servidor para otro jugador." #: ../../TShockAPI/GetDataHandlers.cs:3437 msgid "You do not have permission to use the Enchanted Sundial." -msgstr "No tienes permiso para usar el Reloj de sol encantado." +msgstr "No tienes permiso para usar el Reloj de Sol Encantado." #: ../../TShockAPI/Commands.cs:652 #, csharp-format @@ -7128,11 +7131,11 @@ msgstr "Has introducido un espacio después de {0} en lugar de un comando. Escri #: ../../TShockAPI/Commands.cs:985 msgid "You failed to change your password." -msgstr "Tu intento de cambiar tu contraseña ha fallado." +msgstr "No pudiste cambiar tu contraseña." #: ../../TShockAPI/GetDataHandlers.cs:2458 msgid "You have been Bounced." -msgstr "" +msgstr "Se te ha negado la entrada." #: ../../TShockAPI/Rest/RestManager.cs:1393 msgid "You have been remotely muted" @@ -7168,11 +7171,11 @@ msgstr "Has muerto en modo extremo y todos tus objetos se han perdido para siemp #: ../../TShockAPI/Commands.cs:5662 #, csharp-format msgid "You have launched {0} into space." -msgstr "Has lanzado a {0} al espacio." +msgstr "Has lanzado a {0} a la estratosfera." #: ../../TShockAPI/Commands.cs:5660 msgid "You have launched yourself into space." -msgstr "Te has lanzado a ti mismo al espacio." +msgstr "Te has lanzado a ti mismo a la estratosfera." #: ../../TShockAPI/Commands.cs:5498 #, csharp-format @@ -7195,7 +7198,7 @@ msgstr "Cambiaste tu contraseña exitosamente." #: ../../TShockAPI/Commands.cs:5486 #, csharp-format msgid "You have unmuted {0}." -msgstr "Has dejado de silenciar a {0}." +msgstr "Has desmuteado a {0}." #: ../../TShockAPI/Commands.cs:5590 msgid "You haven't previously received any whispers." @@ -7204,12 +7207,12 @@ msgstr "No has recibido ningún susurro recientemente." #: ../../TShockAPI/Commands.cs:6347 #, csharp-format msgid "You healed {0} for {1} HP." -msgstr "Has curado {1} puntos de vida a {0}" +msgstr "Curas {1} puntos de vida a {0}." #: ../../TShockAPI/Commands.cs:6345 #, csharp-format msgid "You healed yourself for {0} HP." -msgstr "Te has curado {0} puntos de vida." +msgstr "Te curas por {0} puntos de vida." #: ../../TShockAPI/Commands.cs:5947 #, csharp-format @@ -7223,11 +7226,11 @@ msgstr "¡Te has matado a ti mismo!" #: ../../TShockAPI/Commands.cs:5744 #, csharp-format msgid "You launched fireworks on {0}." -msgstr "Has lanzado cohetes a {0}." +msgstr "Has lanzado fuegos artificiales sobre {0}." #: ../../TShockAPI/Commands.cs:5742 msgid "You launched fireworks on yourself." -msgstr "Has lanzado cohetes a ti mismo." +msgstr "Has lanzado fuegos artificiales sobre ti mismo." #: ../../TShockAPI/TShock.cs:608 msgid "You logged in from another location." @@ -7243,7 +7246,7 @@ msgstr "Ahora podrás recibir susurros de otros jugadores." #: ../../TShockAPI/GetDataHandlers.cs:2621 msgid "You may wish to consider removing the tshock.ignore.ssc permission or negating it for this player." -msgstr "Puedes considerar eliminar el permiso tshock.ignore.ssc o negarlo para este jugador." +msgstr "Deberías considerar el quitar o denegar el permiso tshock.ignore.ssc para este jugador." #: ../../TShockAPI/DB/RegionManager.cs:788 msgid "You must be logged in to take advantage of protected regions." @@ -7255,7 +7258,7 @@ msgstr "¡Debes proporcionar el código de configuración!" #: ../../TShockAPI/GetDataHandlers.cs:3448 msgid "You must set ForceTime to normal via config to use the Enchanted Sundial." -msgstr "Debe configurar el ForceTime a normal a través de la configuración para usar el Reloj de sol encantado" +msgstr "Debes cambiar a normal la opción ForceTime en el archivo de configuración para usar el Reloj de Sol Encantado." #: ../../TShockAPI/Commands.cs:2718 msgid "You must spawn the Wall of Flesh in hell." @@ -7267,15 +7270,15 @@ msgstr "Debes usar este comando dentro del juego." #: ../../TShockAPI/GetDataHandlers.cs:2523 msgid "You need to join with a hardcore player." -msgstr "Necesitas unirte con un jugador en extremo." +msgstr "Necesitas unirte con un jugador en modo extremo." #: ../../TShockAPI/GetDataHandlers.cs:2517 msgid "You need to join with a mediumcore player or higher." -msgstr "Necesitas unirte con un jugador en Núcleo suave o superior." +msgstr "Necesitas unirte con un jugador en modo medio o superior." #: ../../TShockAPI/GetDataHandlers.cs:2511 msgid "You need to join with a softcore player." -msgstr "Necesitas unirte con un jugador en clásico ." +msgstr "Necesitas unirte con un jugador en modo clásico." #: ../../TShockAPI/Bouncer.cs:551 msgid "You need to rejoin to ensure your trash can is cleared!" @@ -7307,7 +7310,7 @@ msgstr "Serás teletransportado a tu última ubicación conocida..." #: ../../TShockAPI/Commands.cs:5563 msgid "You will no longer receive whispers from other players." -msgstr "Ahora podrás recibir susurros de otros jugadores." +msgstr "Ya no recibirás más susurros de otros jugadores." #: ../../TShockAPI/Commands.cs:6485 msgid "You're not allowed to change tiles here!" @@ -7315,7 +7318,7 @@ msgstr "¡No tienes permiso para cambiar bloques aquí!" #: ../../TShockPluginManager/NugetCLI.cs:84 msgid "You're trying to sync, but you don't have a packages.json file." -msgstr "" +msgstr "Estás intentando sincronizar, pero no tienes un archivo packages.json." #: ../../TShockAPI/Commands.cs:1987 msgid "Your account has been elevated to superadmin for 10 minutes." @@ -7337,11 +7340,11 @@ msgstr "Tu cliente envió un nombre de personaje en blanco." #: ../../TShockAPI/TShock.cs:1351 msgid "Your client sent a blank UUID. Configure it to send one or use a different client." -msgstr "Tu juego envió un UUID en blanco. Configúralo para que envíe uno o usa un juego distinto." +msgstr "Tu cliente envió un UUID en blanco. Configúralo para que envíe uno o usa un cliente distinto." #: ../../TShockAPI/DB/RegionManager.cs:107 msgid "Your database contains invalid UserIDs (they should be integers)." -msgstr "Tu base de datos contiene IDs de usuario inválidas (deberian ser números enteros)" +msgstr "Tu base de datos contiene UserIDs inválidas (deberían ser números enteros)." #: ../../TShockAPI/Commands.cs:1966 #, csharp-format @@ -7355,11 +7358,11 @@ msgstr "Tu grupo fue cambiado temporalmente a {0}" #: ../../TShockAPI/Commands.cs:6149 msgid "Your inventory seems full." -msgstr "Tu inventario está lleno." +msgstr "Tu inventario parece estar lleno." #: ../../TShockAPI/Commands.cs:1859 msgid "Your local character data, from your initial connection, has been uploaded to the server." -msgstr "Los datos de tu personaje local, desde tu conexión inicial, se han subido al servidor." +msgstr "Los datos locales de tu personaje, desde tu conexión inicial, se han subido al servidor." #: ../../TShockAPI/Commands.cs:5385 #, csharp-format @@ -7398,10 +7401,10 @@ msgstr "z <#> - Establece el orden Z de la región." #: ../../TShockAPI/GetDataHandlers.cs:3235 msgctxt "Likely non-vanilla client send zero-length password" msgid "You have been Bounced for invalid password." -msgstr "" +msgstr "Se te ha negado la entrada por contraseña inválida." #: ../../TShockAPI/TSServerPlayer.cs:34 msgctxt "The account name of server console." msgid "ServerConsole" -msgstr "Consola del Servidor" +msgstr "ServerConsole" diff --git a/i18n/pt_BR/TShockAPI.po b/i18n/pt_BR/TShockAPI.po index 542a526f..5b4899b3 100644 --- a/i18n/pt_BR/TShockAPI.po +++ b/i18n/pt_BR/TShockAPI.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: tshock\n" "POT-Creation-Date: 2022-12-06 05:43:49+0000\n" -"PO-Revision-Date: 2022-12-06 05:52\n" +"PO-Revision-Date: 2022-12-30 01:02\n" "Last-Translator: \n" "Language-Team: Portuguese, Brazilian\n" "MIME-Version: 1.0\n" @@ -1149,7 +1149,7 @@ msgstr "De {0} a {1}" #: ../../TShockPluginManager/NugetCLI.cs:187 #, csharp-format msgid "{0} from {1} [{2}]" -msgstr "" +msgstr "{0} de {1} [{2}]" #: ../../TShockPluginManager/NugetCLI.cs:178 #, csharp-format @@ -1166,15 +1166,15 @@ msgstr " {1}" #, csharp-format msgid "=== Dependency ===" msgid_plural "=== Dependencies ===" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "=== Dependência ===" +msgstr[1] "=== Dependências ===" #: ../../TShockPluginManager/NugetCLI.cs:133 #, csharp-format msgid "=== Requested Plugin ===" msgid_plural "=== Requested Plugins ===" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "=== Plugin exigido ===" +msgstr[1] "=== Plugins exigidos ===" #: ../../TShockAPI/Commands.cs:2818 msgid "a Deerclops" @@ -1325,7 +1325,7 @@ msgstr "todos os chefes" #: ../../TShockPluginManager/NugetCLI.cs:169 msgid "All done! :)" -msgstr "" +msgstr "Concluído!" #: ../../TShockAPI/Commands.cs:2104 msgid "All REST tokens have been destroyed." @@ -1847,7 +1847,7 @@ msgstr "Bouncer / OnLiquidSet rejeitou a verificação inválida de {0}" #: ../../TShockAPI/Bouncer.cs:1731 #, csharp-format msgid "Bouncer / OnLiquidSet rejected liquid type {0} from {1} holding {2}" -msgstr "" +msgstr "Segurador / OnLiquidSet rejeitou o tipo líquido {0} de {1} segurando {2}" #: ../../TShockAPI/Bouncer.cs:1840 #, csharp-format @@ -2671,7 +2671,7 @@ msgstr "Configuração, permissões e regiões recarregadas completamente. Algum #: ../../TShockPluginManager/NugetCLI.cs:100 msgid "Connect to the internet to figure out what to download?" -msgstr "" +msgstr "Conectar-se à internet para descobrir o que baixar?" #: ../../TShockAPI/TShock.cs:1329 msgid "Connecting via a proxy is not allowed." @@ -2901,7 +2901,7 @@ msgstr "disallow - Desautoriza um grupo de usar o bloco." #: ../../TShockPluginManager/NugetCLI.cs:140 #: ../../TShockPluginManager/NugetCLI.cs:161 msgid "Download and install the given packages?" -msgstr "" +msgstr "Baixar e instalar os pacotes indicados?" #: ../../TShockAPI/Commands.cs:2648 msgid "Duke Fishron" @@ -3739,11 +3739,11 @@ msgstr "Se você entendeu, por favor digite {0}login #: ../../TShockPluginManager/NugetCLI.cs:142 msgid "If you want to know which plugins need which dependencies, press E." -msgstr "" +msgstr "Se você quiser saber quais plugins precisam de quais dependências, pressione E." #: ../../TShockPluginManager/NugetCLI.cs:162 msgid "If you'd like to see which plugins need which dependencies again, press E." -msgstr "" +msgstr "Se você quiser ver quais plugins precisam de dependências novamente, pressione E." #: ../../TShockAPI/Bouncer.cs:986 msgid "If you're seeing this message and you know what that player did, please report it to TShock for further investigation." @@ -3799,7 +3799,7 @@ msgstr "Falha ao inserir o banimento no banco de dados." #: ../../TShockPluginManager/NugetCLI.cs:39 msgid "Install the plugins as specified in the plugins.json" -msgstr "" +msgstr "Instalar os plugins conforme especificado no plugins.json" #: ../../TShockAPI/Rest/Rest.cs:426 msgid "Internal server error." @@ -4566,7 +4566,7 @@ msgstr "Nome da máquina: {0}" #: ../../TShockPluginManager/NugetCLI.cs:141 msgid "Make sure that you trust the plugins you're installing." -msgstr "" +msgstr "Certifique-se de confiar nos plugins que você está instalando." #: ../../TShockAPI/Bouncer.cs:2471 msgid "Malicious portal attempt." @@ -4574,7 +4574,7 @@ msgstr "Tentativa de portal malicioso." #: ../../TShockPluginManager/NugetCLI.cs:35 msgid "Manage plugins and their requirements" -msgstr "" +msgstr "Gerenciar plugins e suas exigências" #: ../../TShockAPI/Commands.cs:280 msgid "Manages groups." @@ -4614,7 +4614,7 @@ msgstr "Gerencia contas de usuários." #: ../../TShockAPI/Bouncer.cs:1775 msgid "Manipulating unknown liquid type" -msgstr "" +msgstr "Manipulando tipo de líquido desconhecido" #: ../../TShockAPI/Commands.cs:4721 #, csharp-format @@ -4816,7 +4816,7 @@ msgstr "Dano do NPC excedeu {0}." #: ../../TShockPluginManager/NugetCLI.cs:103 msgid "One moment..." -msgstr "" +msgstr "Aguarde..." #: ../../TShockAPI/DB/RegionManager.cs:102 #, csharp-format @@ -5141,7 +5141,7 @@ msgstr "Limite de TilePlace atingido." #: ../../TShockPluginManager/NugetCLI.cs:128 msgid "Read the message below to find out more." -msgstr "" +msgstr "Leia a mensagem abaixo para saber mais." #: ../../TShockAPI/Commands.cs:1495 #, csharp-format @@ -6041,7 +6041,7 @@ msgstr "O valor deve ser maior que zero." #: ../../TShockPluginManager/NugetCLI.cs:127 msgid "The versions of plugins you requested aren't compatible with eachother." -msgstr "" +msgstr "As versões de plugins que você solicitou não são compatíveis uns com os outros." #: ../../TShockAPI/Commands.cs:2722 msgid "the Wall of Flesh" @@ -6110,17 +6110,17 @@ msgstr "Ocorreu um erro ao processar seu login ou solicitação relacionada à a #: ../../TShockPluginManager/NugetCLI.cs:121 msgid "There was an issue figuring out what to download." -msgstr "" +msgstr "Houve um problema ao descobrir o que baixar." #: ../../TShockPluginManager/NugetCLI.cs:90 msgid "There was an issue reading the packages.json." -msgstr "" +msgstr "Houve um problema ao ler packages.json." #: ../../TShockPluginManager/NugetCLI.cs:99 #, csharp-format msgid "This is the plugin you requested to install." msgid_plural "These are the plugins you requested to install" -msgstr[0] "" +msgstr[0] "Este é o plugin que você solicitou para instalar." msgstr[1] "" #: ../../TShockAPI/TShock.cs:1013 @@ -6574,37 +6574,37 @@ msgstr "Usando {0} para implementação de bloco" #: ../../TShockAPI/Bouncer.cs:1794 #, csharp-format msgid "Using {0} on non-honey" -msgstr "" +msgstr "Usando {0} em não-mel" #: ../../TShockAPI/Bouncer.cs:1803 #, csharp-format msgid "Using {0} on non-lava" -msgstr "" +msgstr "Usando {0} em não-lava" #: ../../TShockAPI/Bouncer.cs:1810 #, csharp-format msgid "Using {0} on non-shimmer" -msgstr "" +msgstr "Usando {0} em não-cintilante" #: ../../TShockAPI/Bouncer.cs:1785 #, csharp-format msgid "Using {0} on non-water" -msgstr "" +msgstr "Usando {0} em não-água" #: ../../TShockAPI/Bouncer.cs:1817 #, csharp-format msgid "Using {0} on non-water or shimmer" -msgstr "" +msgstr "Usando {0} em não-água ou cintilante" #: ../../TShockAPI/Bouncer.cs:1825 #, csharp-format msgid "Using {0} to manipulate unknown liquid {1}" -msgstr "" +msgstr "Usando {0} para manipular líquido desconhecido {1}" #: ../../TShockAPI/Bouncer.cs:1740 #, csharp-format msgid "Using banned {0} to manipulate liquid" -msgstr "" +msgstr "Uso banido {0} para manipular líquido" #: ../../TShockAPI/Bouncer.cs:1763 msgid "Using banned honey bucket without permissions" @@ -6616,7 +6616,7 @@ msgstr "Usando balde de lava banido sem permissões" #: ../../TShockAPI/Bouncer.cs:1770 msgid "Using banned shimmering water bucket without permissions" -msgstr "" +msgstr "Usando balde de água cintilante banido sem permissões" #: ../../TShockAPI/Bouncer.cs:1749 msgid "Using banned water bucket without permissions" @@ -6694,7 +6694,7 @@ msgstr "Árvore de salgueiro" #: ../../TShockPluginManager/NugetCLI.cs:85 msgid "Without a list of plugins to install, no plugins can be installed." -msgstr "" +msgstr "Sem uma lista de plugins para instalar, nenhum plugin pode ser instalado." #: ../../TShockAPI/BackupManager.cs:80 #, csharp-format @@ -7319,7 +7319,7 @@ msgstr "Não é permitido alterar blocos aqui!" #: ../../TShockPluginManager/NugetCLI.cs:84 msgid "You're trying to sync, but you don't have a packages.json file." -msgstr "" +msgstr "Você está tentando sincronizar, mas não tem um arquivo packages.json." #: ../../TShockAPI/Commands.cs:1987 msgid "Your account has been elevated to superadmin for 10 minutes." diff --git a/i18n/tr_TR/TShockAPI.po b/i18n/tr_TR/TShockAPI.po index ba9c61ce..42c02c61 100644 --- a/i18n/tr_TR/TShockAPI.po +++ b/i18n/tr_TR/TShockAPI.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: tshock\n" "POT-Creation-Date: 2022-12-06 05:43:49+0000\n" -"PO-Revision-Date: 2022-12-06 05:52\n" +"PO-Revision-Date: 2023-01-11 13:30\n" "Last-Translator: \n" "Language-Team: Turkish\n" "MIME-Version: 1.0\n" @@ -1028,12 +1028,12 @@ msgstr "{0} adlı oyuncunun son bilinen IP adresi {1}." #: ../../TShockAPI/Commands.cs:1286 #, csharp-format msgid "{0}'s last login occurred {1} {2} UTC{3}." -msgstr "" +msgstr "{0} adlı oyuncunun son girişi {1} {2} UTC {3}." #: ../../TShockAPI/Commands.cs:1298 #, csharp-format msgid "{0}'s register date is {1} {2} UTC{3}." -msgstr "" +msgstr "{0} adlı oyuncunun kayıt tarihi {1} {2} UTC{3}." #: ../../TShockAPI/Commands.cs:5778 #, csharp-format @@ -1093,19 +1093,19 @@ msgstr "" #: ../../TShockAPI/Permissions.cs:547 #, csharp-format msgid "* **Commands**: `{0}`" -msgstr "" +msgstr "* **Komutlar**: `{0}`" #: ../../TShockAPI/Configuration/ServerSideConfig.cs:123 #: ../../TShockAPI/Configuration/TShockConfig.cs:645 #, csharp-format msgid "* **Default**: `{0}`" -msgstr "" +msgstr "* **Varsayılan**: `{0}`" #: ../../TShockAPI/Configuration/ServerSideConfig.cs:122 #: ../../TShockAPI/Configuration/TShockConfig.cs:644 #, csharp-format msgid "* **Field type**: `{0}`" -msgstr "" +msgstr "* **Alan türü**: `{0}`" #: ../../TShockAPI/Rest/RestManager.cs:1220 #, csharp-format @@ -1167,14 +1167,14 @@ msgstr "<{0} hedefine> {1}" msgid "=== Dependency ===" msgid_plural "=== Dependencies ===" msgstr[0] "" -msgstr[1] "" +msgstr[1] "=== Bağımlılıklar ===" #: ../../TShockPluginManager/NugetCLI.cs:133 #, csharp-format msgid "=== Requested Plugin ===" msgid_plural "=== Requested Plugins ===" msgstr[0] "" -msgstr[1] "" +msgstr[1] "=== İstenen Eklentiler ===" #: ../../TShockAPI/Commands.cs:2818 msgid "a Deerclops" diff --git a/i18n/zh_CN/TShockAPI.po b/i18n/zh_CN/TShockAPI.po index b9b1b0a6..f60d7727 100644 --- a/i18n/zh_CN/TShockAPI.po +++ b/i18n/zh_CN/TShockAPI.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: tshock\n" "POT-Creation-Date: 2022-12-06 05:43:49+0000\n" -"PO-Revision-Date: 2022-12-06 17:49\n" +"PO-Revision-Date: 2023-01-04 07:35\n" "Last-Translator: \n" "Language-Team: Chinese Simplified\n" "MIME-Version: 1.0\n" @@ -99,7 +99,7 @@ msgstr " {1}会被作为玩家名称或序号进行识别,除非添加了参 #: ../../TShockAPI/Commands.cs:1262 #, csharp-format msgid " -> Logged-in as: {0}; in group {1}." -msgstr " -> 登录为用户 {0} , 在组 {1} 中。" +msgstr " ->登录为用户 {0} , 在组 {1} 中。" #: ../../TShockAPI/Commands.cs:1398 #, csharp-format @@ -127,7 +127,7 @@ msgstr "- 根据用户名封禁一个不在线的玩家" #: ../../TShockAPI/Commands.cs:1445 msgid "- Ban an offline player by IP address" -msgstr "- 封禁离线玩家的IP地址" +msgstr "- 通过IP地址封禁离线的玩家" #: ../../TShockAPI/Commands.cs:1448 msgid "- Ban an online player by index (Useful for hard to type names)" @@ -345,7 +345,7 @@ msgstr "{0} <{1}|{2}|{3}> [{4}]" #: ../../TShockAPI/Commands.cs:1106 #, csharp-format msgid "{0} added account {1} to group {2}." -msgstr "{0} 将账号 {1} 添加至组 {2}!" +msgstr "{0} 将账号 {1} 添加至组 {2}!" #: ../../TShockAPI/GetDataHandlers.cs:3546 #, csharp-format @@ -365,7 +365,7 @@ msgstr "{0}使用了游商背包([i:5343])。" #: ../../TShockAPI/Commands.cs:1064 #, csharp-format msgid "{0} attempted to register for the account {1} but it was already taken." -msgstr "{0}尝试注册账号“{1}” ,但它已被注册。" +msgstr "{0}试图注册账号“{1}” ,但它已被占用。" #: ../../TShockAPI/GetDataHandlers.cs:2637 #: ../../TShockAPI/GetDataHandlers.cs:3212 @@ -381,7 +381,7 @@ msgstr "{0}已成功登录账号{1}。" #: ../../TShockAPI/TSPlayer.cs:1955 #, csharp-format msgid "{0} banned {1} for '{2}'." -msgstr "{0} 以'{2}'为理由封禁了{1}" +msgstr "{0} 以'{2}'为理由封禁了'{1}'" #: ../../TShockAPI/Commands.cs:6051 #, csharp-format @@ -553,7 +553,7 @@ msgstr "{0}已将你所属的用户组更改为{1}。" #: ../../TShockAPI/Commands.cs:3026 #, csharp-format msgid "{0} has disabled incoming teleports." -msgstr "{0} 禁止玩家对其使用传送" +msgstr "{0} 禁止玩家对其使用传送。" #: ../../TShockAPI/Commands.cs:2412 #, csharp-format @@ -608,22 +608,22 @@ msgstr "{0} 已将你复活。" #: ../../TShockAPI/GetDataHandlers.cs:3555 #, csharp-format msgid "{0} has sent a request to the bunny delivery service!" -msgstr "{0}联系了兔兔运送服务站。" +msgstr "{0}从递送站申领了新的兔兔!" #: ../../TShockAPI/GetDataHandlers.cs:3561 #, csharp-format msgid "{0} has sent a request to the cat delivery service!" -msgstr "{0}联系了猫咪运送服务站。" +msgstr "{0}从递送站申领了新的猫猫!" #: ../../TShockAPI/GetDataHandlers.cs:3558 #, csharp-format msgid "{0} has sent a request to the dog delivery service!" -msgstr "{0}联系了狗狗运送服务站。" +msgstr "{0}从递送站申领了新的狗狗!" #: ../../TShockAPI/GetDataHandlers.cs:3552 #, csharp-format msgid "{0} has sent a request to the slime delivery service!" -msgstr "{0}联系了史莱姆运送服务站。" +msgstr "{0}已向史莱姆递送服务发送请求!" #: ../../TShockAPI/Commands.cs:2878 #, csharp-format @@ -684,7 +684,7 @@ msgstr "{0}为他自己恢复了{1}血量。" #: ../../TShockAPI/Commands.cs:4250 #, csharp-format msgid "{0} is already allowed to place tile {1}." -msgstr "{0} 已被允许放置方块 {1}。" +msgstr "{0} 已被允许放置图格 {1}。" #: ../../TShockAPI/Commands.cs:3885 #, csharp-format @@ -719,12 +719,12 @@ msgstr "{0} 已被禁止使用射弹 {1}。" #: ../../TShockAPI/ItemBans.cs:234 #, csharp-format msgid "{0} is banned! Remove it!" -msgstr "{0} 已被封禁!请删除它!" +msgstr "{0} 已被禁用!请移除它!" #: ../../TShockAPI/Commands.cs:6751 #, csharp-format msgid "{0} is no longer in god mode." -msgstr "已关闭{0}的无敌模式。" +msgstr "已关闭{0}的上帝模式。" #: ../../TShockAPI/Commands.cs:5546 #: ../../TShockAPI/Commands.cs:5577 @@ -1253,7 +1253,7 @@ msgstr "成功移除账号。" #: ../../TShockAPI/Commands.cs:2094 msgid "Active REST Users ({{0}}/{{1}}):" -msgstr "活跃REST用户({{0}}/{{1}}):" +msgstr "有效的临时REST用户({{0}}/{{1}}):" #: ../../TShockAPI/Commands.cs:3971 msgid "add - Adds an item ban." @@ -1307,7 +1307,7 @@ msgstr "{0}{1}的别名:{0}{2}" #: ../../TShockAPI/Commands.cs:6013 msgid "All alive NPCs (excluding town NPCs) on the server will be killed if you do not input a name or ID." -msgstr "输入名称或序号可以消灭特定NPC,否则将默认消灭所有非城镇NPC。" +msgstr "输入NPC名称或ID可以杀死特定NPC,否则将默认杀死所有非城镇NPC。" #: ../../TShockAPI/Commands.cs:2626 msgid "all bosses" @@ -1315,7 +1315,7 @@ msgstr "所有boss" #: ../../TShockPluginManager/NugetCLI.cs:169 msgid "All done! :)" -msgstr "下载完毕!" +msgstr "下载完毕!: )" #: ../../TShockAPI/Commands.cs:2104 msgid "All REST tokens have been destroyed." @@ -1328,15 +1328,15 @@ msgstr "已分配内存: {0}" #: ../../TShockAPI/Commands.cs:3972 msgid "allow - Allows a group to use an item." -msgstr "allow <物品> <组名> - 允许指定组使用被禁用的物品" +msgstr "allow <物品> <用户组> - 给予用户组使用特定物品的豁免权。" #: ../../TShockAPI/Commands.cs:4150 msgid "allow - Allows a group to use a projectile." -msgstr "allow <射弹> <用户组> - 给与用户组使用特定射弹的豁免权。" +msgstr "allow <射弹> <用户组> - 给予用户组使用特定射弹的豁免权。" #: ../../TShockAPI/Commands.cs:4326 msgid "allow - Allows a group to place a tile." -msgstr "allow <物块> <用户组> - 给与用户组使用特定物块的豁免权。" +msgstr "allow <物块> <用户组> - 给予用户组使用特定物块的豁免权。" #: ../../TShockAPI/Commands.cs:5193 msgid "allow - Allows a user to a region." @@ -1374,20 +1374,20 @@ msgstr "执行指令时发生了异常。" #: ../../TShockAPI/DB/BanManager.cs:644 msgid "An identifier for a character name." -msgstr "角色名字的标识。" +msgstr "可以匹配游戏内的角色名。" #: ../../TShockAPI/DB/BanManager.cs:648 msgid "An identifier for a TShock User Account name." -msgstr "一个TShock用户名的标识符。" +msgstr "可以匹配TShock用户名。" #: ../../TShockAPI/DB/BanManager.cs:640 msgid "An identifier for a UUID." -msgstr "UUID的标识。" +msgstr "可以匹配UUID。" #: ../../TShockAPI/DB/BanManager.cs:636 #, csharp-format msgid "An identifier for an IP Address in octet format. e.g., '{0}'." -msgstr "用句点分隔的十进制格式的IP地址,例如{0}。" +msgstr "可以匹配IP地址,格式为用句点分隔的十进制,例如{0}。" #: ../../TShockAPI/Commands.cs:2052 msgid "An update check has been queued. If an update is available, you will be notified shortly." @@ -1446,7 +1446,7 @@ msgstr "自动存档已开启。" #: ../../TShockAPI/Rest/RestManager.cs:811 msgid "AutoSave has been disabled" -msgstr "自动存档已关闭" +msgstr "自动保存已关闭" #: ../../TShockAPI/Rest/RestManager.cs:807 msgid "AutoSave has been enabled" @@ -1466,7 +1466,7 @@ msgstr "可用Ban指令:" #: ../../TShockAPI/Commands.cs:1432 msgid "Available identifiers ({{0}}/{{1}}):" -msgstr "可用的标识符 ({{0}}/{{1}}):" +msgstr "可选的匹配代码 ({{0}}/{{1}}):" #: ../../TShockAPI/Commands.cs:5208 msgid "Available Region Sub-Commands ({{0}}/{{1}}):" @@ -1474,7 +1474,7 @@ msgstr "可用的区域子命令 ({{0}}/{{1}}):" #: ../../TShockAPI/Commands.cs:2109 msgid "Available REST Sub-Commands:" -msgstr "Rest命令使用帮助:" +msgstr "REST命令使用帮助:" #: ../../TShockAPI/BackupManager.cs:75 msgid "Backing up world..." @@ -1684,7 +1684,7 @@ msgstr "Bouncer / OnFishOutNPC(钓出NPC防护)因没有召唤Boss的权限 #: ../../TShockAPI/Bouncer.cs:2781 #, csharp-format msgid "Bouncer / OnFoodPlatterTryPlacing rejected disabled from {0}" -msgstr "Bouncer / OnFoodPlatterTryPlacing(放置食物防护)因玩家被拘束阻止了{0}" +msgstr "Bouncer / OnFoodPlatterTryPlacing(放置食物防护)因玩家被限制行动而阻止了{0}" #: ../../TShockAPI/Bouncer.cs:2774 #, csharp-format @@ -1997,7 +1997,7 @@ msgstr "Bouncer / OnPlaceChest(宝箱放置防护)因超出范围而阻止 #: ../../TShockAPI/Bouncer.cs:1562 #, csharp-format msgid "Bouncer / OnPlaceChest rejected from weird check from {0}" -msgstr "Bouncer / OnPlaceChest 拒绝了来自 {0} 的怪物的检查" +msgstr "Bouncer / OnPlaceChest 拒绝了来自 {0} 的奇怪的检查" #: ../../TShockAPI/Bouncer.cs:1573 #, csharp-format @@ -2998,7 +2998,7 @@ msgstr "检测到恶意攻击" #: ../../TShockAPI/Commands.cs:1494 #, csharp-format msgid "Failed to add ban for identifier: {0}." -msgstr "无法给{0}添加封禁标识符" +msgstr "无法添加封禁(匹配代码{0})。" #: ../../TShockAPI/Rest/RestManager.cs:671 #, csharp-format @@ -3515,7 +3515,7 @@ msgstr "用户组{1}为{0}的父组,但也同时是它的子组。已删除此 #: ../../TShockAPI/DB/GroupManager.cs:578 msgid "Group \"superadmin\" is defined in the database even though it's a reserved group name." -msgstr "组 ”superadmin”(超级管理员)已存在于数据库中,即使这是一个保留的组名。" +msgstr "数据库中存在superadmin组,但它已被预留,不应存在于数据库中。" #: ../../TShockAPI/DB/GroupManager.cs:708 #, csharp-format @@ -3573,7 +3573,7 @@ msgstr "用户组{0}的父组为{1},但该用户组不存在。已删除此父 #: ../../TShockAPI/DB/GroupManager.cs:637 #, csharp-format msgid "Group {0} is referencing itself as parent group; parent reference was removed." -msgstr "组{0}试图将自己作为父组。父组的引用已被删除。" +msgstr "用户组{0}的父组为它自己。已删除此父子关系。" #: ../../TShockAPI/Commands.cs:4862 #: ../../TShockAPI/Commands.cs:4897 @@ -5849,7 +5849,7 @@ msgstr "此用户组不存在。" #: ../../TShockAPI/DB/BanManager.cs:258 msgid "The ban is invalid because a current ban for this identifier already exists." -msgstr "禁令无效,因为当前禁止此标识符的禁令已经存在。" +msgstr "目前已经封禁了符合这个匹配代码的玩家,无法再次封禁。" #: ../../TShockAPI/DB/BanManager.cs:295 msgid "The ban was not valid for an unknown reason." @@ -5986,7 +5986,7 @@ msgstr "史莱姆皇后" #: ../../TShockAPI/Rest/RestManager.cs:158 msgid "The REST authentication token." -msgstr "REST 身份验证令牌。" +msgstr "REST 授权令牌" #: ../../TShockAPI/UpdateManager.cs:150 msgid "The server is out of date. Latest version: " @@ -6047,7 +6047,7 @@ msgstr "当前没有任何有效的临时REST用户。" #: ../../TShockAPI/Commands.cs:1434 msgid "There are currently no available identifiers." -msgstr "目前没有可用的匹配代码。" +msgstr "目前没有可选的匹配代码。" #: ../../TShockAPI/Commands.cs:4001 msgid "There are currently no banned items." From 9a0269fb83fff1a66d87c53587111f23193968e7 Mon Sep 17 00:00:00 2001 From: punchready <22683812+punchready@users.noreply.github.com> Date: Tue, 28 Feb 2023 22:05:25 +0100 Subject: [PATCH 009/135] Update definitions for Flower Boots to allow Ash Plants --- TShockAPI/Handlers/SendTileRectHandler.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/TShockAPI/Handlers/SendTileRectHandler.cs b/TShockAPI/Handlers/SendTileRectHandler.cs index 3ff85fea..b66a72fa 100644 --- a/TShockAPI/Handlers/SendTileRectHandler.cs +++ b/TShockAPI/Handlers/SendTileRectHandler.cs @@ -38,6 +38,10 @@ namespace TShockAPI.Handlers { TileID.JungleGrass } }, + { TileID.AshPlants, new HashSet() + { + TileID.AshGrass + } }, }; /// @@ -67,6 +71,10 @@ namespace TShockAPI.Handlers { 9, 10, 11, 12, 13, 14, 15, 16, } }, + { TileID.AshPlants, new HashSet() + { + 6, 7, 8, 9, 10, + } }, }; /// From defbe5c34fed7173877296cf04fcfb0cf6df312b Mon Sep 17 00:00:00 2001 From: punchready <22683812+punchready@users.noreply.github.com> Date: Tue, 28 Feb 2023 22:09:36 +0100 Subject: [PATCH 010/135] Update changelog --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index be96f88c..061988a2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -106,6 +106,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Allowed multiple test cases to be in TShock's test suite. (@drunderscore) * Fixed unable to use Purification/Evil Powder in jungle. (@sgkoishi) * Set the `GetDataHandledEventArgs.Player` property for the `SyncTilePicking` data handler. (@drunderscore) +* Allowed Flower Boots to place Ash Flowers on Ash Grass blocks. (@punchready) ## TShock 5.1.3 * Added support for Terraria 1.4.4.9 via OTAPI 3.1.20. (@SignatureBeef) From 10aca8573dc2c3dd8ca7d3866bf6e5f0e676ff9a Mon Sep 17 00:00:00 2001 From: Stargazing Koishi Date: Tue, 7 Mar 2023 18:39:38 -0800 Subject: [PATCH 011/135] Remove Connection: Close for REST api fix #2923 --- TShockAPI/Rest/Rest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/TShockAPI/Rest/Rest.cs b/TShockAPI/Rest/Rest.cs index 58cc23d8..a6e681e2 100644 --- a/TShockAPI/Rest/Rest.cs +++ b/TShockAPI/Rest/Rest.cs @@ -351,7 +351,6 @@ namespace Rests { str = string.Format("{0}({1});", jsonp, str); } - e.Response.Connection.Type = ConnectionType.Close; e.Response.ContentType = new ContentTypeHeader("application/json; charset=utf-8"); e.Response.Add(serverHeader); var bytes = Encoding.UTF8.GetBytes(str); From 69569a5be5664430a6ddeb1c377e8ebe96a9dae1 Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Fri, 10 Mar 2023 10:37:15 +0900 Subject: [PATCH 012/135] Added obvious warning about spamming github to code of conduct --- .github/CODE_OF_CONDUCT.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index 990b9ed1..a6b7aa40 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -1,3 +1,5 @@ > By participating in the TShock for Terraria community, all members will adhere to maintaining decorum with respect to all humans, in and out of the community. Members will not engage in discussion that inappropriately disparages or marginalizes any group of people or any individual. Members will not attempt to further or advance an agenda to the point of being overbearing or close minded (such as through spreading FUD). Members will not abuse services provided to them and will follow the guidance of community leaders on a situational basis about what abuse consists of. Members will adhere to United States and international law. If members notice a violation of this code of conduct, they will not engage but will instead contact the leadership team on either the forums or Discord. -> Do not attempt to circumvent or bypass the code of conduct by using clever logic or reasoning (e.g., insulting Facepunch members, because they weren't directly mentioned here). \ No newline at end of file +> Do not attempt to circumvent or bypass the code of conduct by using clever logic or reasoning (e.g., insulting Facepunch members, because they weren't directly mentioned here). + +> Do not abuse GitHub services by generating useless emails to all people who watch the repo. This includes unnecessary PR approvals and off-topic discussion, issue, and PR comments that serve no purpose. Due to the high visibility nature of actions that create emails, you may be blocked temporarily as your first warning. From b833e4852694152188a5c0fa7359d1909f5e4dc2 Mon Sep 17 00:00:00 2001 From: Jonathan Rascher Date: Mon, 2 Jan 2023 15:38:29 -0600 Subject: [PATCH 013/135] Remove unnecessary range check for quick stack --- TShockAPI/TShock.cs | 8 ++------ docs/changelog.md | 1 + 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index 7a5a38af..65380a52 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -676,17 +676,13 @@ namespace TShockAPI if (args.Chest != null) { + // After checking for protected regions, no further range checking is necessarily because the client packet only specifies the + // inventory slot to quick stack. The vanilla Terraria server itself determines what chests are close enough to the player. if (Config.Settings.RegionProtectChests && !Regions.CanBuild((int)args.WorldPosition.X, (int)args.WorldPosition.Y, tsplr)) { args.Handled = true; return; } - - if (!tsplr.IsInRange(args.Chest.x, args.Chest.y)) - { - args.Handled = true; - return; - } } } diff --git a/docs/changelog.md b/docs/changelog.md index be96f88c..7e39da75 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -106,6 +106,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Allowed multiple test cases to be in TShock's test suite. (@drunderscore) * Fixed unable to use Purification/Evil Powder in jungle. (@sgkoishi) * Set the `GetDataHandledEventArgs.Player` property for the `SyncTilePicking` data handler. (@drunderscore) +* Removed unnecessary range check that artifically shortened quick stack reach. (@boddyn, #2885, @bcat) ## TShock 5.1.3 * Added support for Terraria 1.4.4.9 via OTAPI 3.1.20. (@SignatureBeef) From 1948ad3d2cf960619bd9954bec87072d6aa207b1 Mon Sep 17 00:00:00 2001 From: Stargazing Koishi Date: Thu, 9 Mar 2023 21:01:09 -0800 Subject: [PATCH 014/135] Update changelog.md --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index be96f88c..8af0bada 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -106,6 +106,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Allowed multiple test cases to be in TShock's test suite. (@drunderscore) * Fixed unable to use Purification/Evil Powder in jungle. (@sgkoishi) * Set the `GetDataHandledEventArgs.Player` property for the `SyncTilePicking` data handler. (@drunderscore) +* Fixed unable to transfer long response body for REST API. (@sgkoishi, #2925) ## TShock 5.1.3 * Added support for Terraria 1.4.4.9 via OTAPI 3.1.20. (@SignatureBeef) From 7b7de04a7f76c42dc72774ec7256e3367bbe4b36 Mon Sep 17 00:00:00 2001 From: James Puleo Date: Mon, 13 Mar 2023 01:43:12 -0400 Subject: [PATCH 015/135] Relax custom death message restrictions to allow Inferno potions in PvP For some reason, Inferno potions do PvP damage without specifying the source player in the `PlayerDeathReason` -- so special-case the only identifying information being the other index value of `16`. --- TShockAPI/Bouncer.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index affa1609..6ff7fd1a 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -2659,9 +2659,12 @@ namespace TShockAPI * If the player was not specified, that is, the player index is -1, then it is definitely a custom cause, as you can only deal damage with a projectile or another player. * This is how everything else works. If an NPC is specified, its value is not -1, which is a custom cause. * + * An exception to this is damage dealt by the Inferno potion to other players -- it is only identified by the other index value of 16, + * even lacking a source player index. + * * Checking whether this damage came from the player is necessary, because the damage from the player can come even when it is hit by a NPC */ - if (TShock.Config.Settings.DisableCustomDeathMessages && id != args.Player.Index && + if (TShock.Config.Settings.DisableCustomDeathMessages && id != args.Player.Index && reason._sourceOtherIndex != 16 && (reason._sourcePlayerIndex == -1 || reason._sourceNPCIndex != -1 || reason._sourceOtherIndex != -1 || reason._sourceCustomReason != null)) { TShock.Log.ConsoleDebug(GetString("Bouncer / OnPlayerDamage rejected custom death message from {0}", args.Player.Name)); From 359fe8caf841e02bd5de54abf7baa57e67e7edcf Mon Sep 17 00:00:00 2001 From: James Puleo Date: Mon, 13 Mar 2023 02:11:03 -0400 Subject: [PATCH 016/135] Update `docs/changelog.md` --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index be96f88c..2f0f20c9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -106,6 +106,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Allowed multiple test cases to be in TShock's test suite. (@drunderscore) * Fixed unable to use Purification/Evil Powder in jungle. (@sgkoishi) * Set the `GetDataHandledEventArgs.Player` property for the `SyncTilePicking` data handler. (@drunderscore) +* Relaxed custom death message restrictions to allow Inferno potions in PvP. (@drunderscore) ## TShock 5.1.3 * Added support for Terraria 1.4.4.9 via OTAPI 3.1.20. (@SignatureBeef) From 5acc3afcb6f92bc65c7dfb9539b9fb83b4c18b6b Mon Sep 17 00:00:00 2001 From: punchready Date: Fri, 24 Mar 2023 21:39:24 +0100 Subject: [PATCH 017/135] Update STR handling to reject for-sure invalid framing values --- TShockAPI/Handlers/SendTileRectHandler.cs | 33 +++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/TShockAPI/Handlers/SendTileRectHandler.cs b/TShockAPI/Handlers/SendTileRectHandler.cs index 3ff85fea..cb016307 100644 --- a/TShockAPI/Handlers/SendTileRectHandler.cs +++ b/TShockAPI/Handlers/SendTileRectHandler.cs @@ -173,6 +173,7 @@ namespace TShockAPI.Handlers } NetTile newTile = tiles[x, y]; + TileObjectData data; // If the new tile has an associated TileObjectData object, we take the tile and the surrounding tiles that make up the tile object @@ -210,10 +211,26 @@ namespace TShockAPI.Handlers case TileID.ShimmerMonolith: { // Allowed changes + + // Based on empirical tests, these should be some conservative upper bounds for framing values + if (newTile.FrameX != -1 || newTile.FrameY != -1) + { + if (newTile.FrameX is < 0 or > 1000) + { + processed[x, y] = true; + continue; + } + if (newTile.FrameY is < 0 or > 5000) + { + processed[x, y] = true; + continue; + } + } } break; default: { + processed[x, y] = true; continue; } } @@ -233,10 +250,26 @@ namespace TShockAPI.Handlers case TileID.TargetDummy: { // Allowed placements + + // Based on empirical tests, these should be some conservative upper bounds for framing values + if (newTile.FrameX != -1 || newTile.FrameY != -1) + { + if (newTile.FrameX is < 0 or > 1000) + { + processed[x, y] = true; + continue; + } + if (newTile.FrameY is < 0 or > 500) + { + processed[x, y] = true; + continue; + } + } } break; default: { + processed[x, y] = true; continue; } } From 3f55a86589943c881626c60e270dcac56d14f6ac Mon Sep 17 00:00:00 2001 From: punchready Date: Fri, 24 Mar 2023 21:40:31 +0100 Subject: [PATCH 018/135] Update changelog --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index be96f88c..053afcff 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -106,6 +106,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Allowed multiple test cases to be in TShock's test suite. (@drunderscore) * Fixed unable to use Purification/Evil Powder in jungle. (@sgkoishi) * Set the `GetDataHandledEventArgs.Player` property for the `SyncTilePicking` data handler. (@drunderscore) +* Improved the exploit protection in tile rect handling. (@punchready) ## TShock 5.1.3 * Added support for Terraria 1.4.4.9 via OTAPI 3.1.20. (@SignatureBeef) From b714ab18a548ff38fea565fbdb35a9ef24eb80e7 Mon Sep 17 00:00:00 2001 From: punchready Date: Tue, 4 Apr 2023 05:08:17 +0200 Subject: [PATCH 019/135] Completely rewrite STR handling --- TShockAPI/Bouncer.cs | 4 +- .../Handlers/SendTileRectHandlerRefactor.cs | 841 ++++++++++++++++++ 2 files changed, 843 insertions(+), 2 deletions(-) create mode 100644 TShockAPI/Handlers/SendTileRectHandlerRefactor.cs diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 6ff7fd1a..c7c72f96 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -35,7 +35,7 @@ namespace TShockAPI /// Bouncer is the TShock anti-hack and anti-cheat system. internal sealed class Bouncer { - internal Handlers.SendTileRectHandler STSHandler { get; private set; } + internal Handlers.SendTileRectHandlerRefactor STSHandler { get; private set; } internal Handlers.NetModules.NetModulePacketHandler NetModuleHandler { get; private set; } internal Handlers.EmojiHandler EmojiHandler { get; private set; } internal Handlers.IllegalPerSe.EmojiPlayerMismatch EmojiPlayerMismatch { get; private set; } @@ -83,7 +83,7 @@ namespace TShockAPI /// A new Bouncer. internal Bouncer() { - STSHandler = new Handlers.SendTileRectHandler(); + STSHandler = new Handlers.SendTileRectHandlerRefactor(); GetDataHandlers.SendTileRect += STSHandler.OnReceive; NetModuleHandler = new Handlers.NetModules.NetModulePacketHandler(); diff --git a/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs b/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs new file mode 100644 index 00000000..86048193 --- /dev/null +++ b/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs @@ -0,0 +1,841 @@ +using System.Collections.Generic; +using System.IO; + +using Terraria; +using Terraria.ID; + +using TShockAPI.Net; + +namespace TShockAPI.Handlers +{ + /// + /// Provides processors for handling tile rect packets. + /// This required many hours of reverse engineering work, and is kindly provided to TShock for free by @punchready. + /// + public sealed class SendTileRectHandlerRefactor : IPacketHandler + { + /// + /// Represents a tile rectangle sent through the packet. + /// + private sealed class TileRect + { + private readonly NetTile[,] _tiles; + public readonly int X; + public readonly int Y; + public readonly int Width; + public readonly int Height; + + /// + /// Accesses the tiles contained in this rect. + /// + /// The X coordinate within the rect. + /// The Y coordinate within the rect. + /// The tile at the given position within the rect. + public NetTile this[int x, int y] => _tiles[x, y]; + + /// + /// Constructs a new tile rect based on the given information. + /// + public TileRect(NetTile[,] tiles, int x, int y, int width, int height) + { + _tiles = tiles; + X = x; + Y = y; + Width = width; + Height = height; + } + + /// + /// Reads a tile rect from the given stream. + /// + /// The resulting tile rect. + public static TileRect Read(MemoryStream stream, int tileX, int tileY, int width, int height) + { + NetTile[,] tiles = new NetTile[width, height]; + for (int x = 0; x < width; x++) + { + for (int y = 0; y < height; y++) + { + tiles[x, y] = new NetTile(); + tiles[x, y].Unpack(stream); // explicit > implicit + } + } + return new TileRect(tiles, tileX, tileY, width, height); + } + } + + /// + /// Represents a common tile rect operation (Placement, State Change, Removal). + /// + private readonly struct TileRectMatch + { + public const short IGNORE_FRAME = -1; + + private enum MatchType + { + Placement, + StateChange, + Removal, + } + + private readonly int Width; + private readonly int Height; + + private readonly ushort TileType; + private readonly short MaxFrameX; + private readonly short MaxFrameY; + + private readonly MatchType Type; + + private TileRectMatch(MatchType type, int width, int height, ushort tileType, short maxFrameX, short maxFrameY) + { + Type = type; + Width = width; + Height = height; + TileType = tileType; + MaxFrameX = maxFrameX; + MaxFrameY = maxFrameY; + } + + /// + /// Creates a new placement operation. + /// + /// The width of the placement. + /// The height of the placement. + /// The tile type of the placement. + /// The maximum allowed frameX of the placement, or if this operation does not change frameX. + /// The maximum allowed frameY of the placement, or if this operation does not change frameY. + /// The resulting operation match. + public static TileRectMatch Placement(int width, int height, ushort tileType, short maxFrameX, short maxFrameY) + { + return new TileRectMatch(MatchType.Placement, width, height, tileType, maxFrameX, maxFrameY); + } + + /// + /// Creates a new state change operation. + /// + /// The width of the state change. + /// The height of the state change. + /// The target tile type of the state change. + /// The maximum allowed frameX of the state change, or if this operation does not change frameX. + /// The maximum allowed frameY of the state change, or if this operation does not change frameY. + /// The resulting operation match. + public static TileRectMatch StateChange(int width, int height, ushort tileType, short maxFrameX, short maxFrameY) + { + return new TileRectMatch(MatchType.StateChange, width, height, tileType, maxFrameX, maxFrameY); + } + + /// + /// Creates a new removal operation. + /// + /// The width of the removal. + /// The height of the removal. + /// The target tile type of the removal. + /// The resulting operation match. + public static TileRectMatch Removal(int width, int height, ushort tileType) + { + return new TileRectMatch(MatchType.Removal, width, height, tileType, 0, 0); + } + + /// + /// Determines whether the given tile rectangle matches this operation, and if so, applies it to the world. + /// + /// The player the operation originates from. + /// The tile rectangle of the operation. + /// , if the rect matches this operation and the changes have been applied, otherwise . + public bool Matches(TSPlayer player, TileRect rect) + { + if (rect.Width != Width || rect.Height != Height) + { + return false; + } + + for (int x = 0; x < rect.Width; x++) + { + for (int y = 0; y < rect.Height; y++) + { + NetTile tile = rect[x, y]; + if (Type is MatchType.Placement or MatchType.StateChange) + { + if (tile.Type != TileType) + { + return false; + } + } + if (Type is MatchType.Placement or MatchType.StateChange) + { + if (MaxFrameX != IGNORE_FRAME) + { + if (tile.FrameX < 0 || tile.FrameX > MaxFrameX) + { + return false; + } + } + if (MaxFrameY != IGNORE_FRAME) + { + if (tile.FrameY < 0 || tile.FrameY > MaxFrameY) + { + return false; + } + } + } + if (Type == MatchType.Removal) + { + if (tile.Active) + { + return false; + } + } + } + } + + for (int x = rect.X; x < rect.X + rect.Width; x++) + { + for (int y = rect.Y; y < rect.Y + rect.Height; y++) + { + if (!player.HasBuildPermission(x, y)) + { + // for simplicity, let's pretend that the edit was valid, but do not execute it + return true; + } + } + } + + switch (Type) + { + case MatchType.Placement: + { + return MatchPlacement(player, rect); + } + case MatchType.StateChange: + { + return MatchStateChange(player, rect); + } + case MatchType.Removal: + { + return MatchRemoval(player, rect); + } + } + + return false; + } + + private bool MatchPlacement(TSPlayer player, TileRect rect) + { + for (int x = rect.X; x < rect.Y + rect.Width; x++) + { + for (int y = rect.Y; y < rect.Y + rect.Height; y++) + { + if (Main.tile[x, y].active() && !(Main.tile[x, y].type != TileID.RollingCactus && (Main.tileCut[Main.tile[x, y].type] || TileID.Sets.BreakableWhenPlacing[Main.tile[x, y].type]))) + { + return false; + } + } + } + + // let's hope tile types never go out of short range (they use ushort in terraria's code) + if (TShock.TileBans.TileIsBanned((short)TileType, player)) + { + // for simplicity, let's pretend that the edit was valid, but do not execute it + return true; + } + + for (int x = 0; x < rect.Width; x++) + { + for (int y = 0; y < rect.Height; y++) + { + Main.tile[x + rect.X, y + rect.Y].active(active: true); + Main.tile[x + rect.X, y + rect.Y].type = rect[x, y].Type; + if (MaxFrameX != IGNORE_FRAME) + { + Main.tile[x + rect.X, y + rect.Y].frameX = rect[x, y].FrameX; + } + if (MaxFrameY != IGNORE_FRAME) + { + Main.tile[x + rect.X, y + rect.Y].frameY = rect[x, y].FrameY; + } + } + } + + return true; + } + + private bool MatchStateChange(TSPlayer player, TileRect rect) + { + for (int x = rect.X; x < rect.Y + rect.Width; x++) + { + for (int y = rect.Y; y < rect.Y + rect.Height; y++) + { + if (!Main.tile[x, y].active() || Main.tile[x, y].type != TileType) + { + return false; + } + } + } + + for (int x = 0; x < rect.Width; x++) + { + for (int y = 0; y < rect.Height; y++) + { + if (MaxFrameX != IGNORE_FRAME) + { + Main.tile[x + rect.X, y + rect.Y].frameX = rect[x, y].FrameX; + } + if (MaxFrameY != IGNORE_FRAME) + { + Main.tile[x + rect.X, y + rect.Y].frameY = rect[x, y].FrameY; + } + } + } + + return true; + } + + private bool MatchRemoval(TSPlayer player, TileRect rect) + { + for (int x = rect.X; x < rect.Y + rect.Width; x++) + { + for (int y = rect.Y; y < rect.Y + rect.Height; y++) + { + if (!Main.tile[x, y].active() || Main.tile[x, y].type != TileType) + { + return false; + } + } + } + + for (int x = 0; x < rect.Width; x++) + { + for (int y = 0; y < rect.Height; y++) + { + Main.tile[x + rect.X, y + rect.Y].active(active: false); + Main.tile[x + rect.X, y + rect.Y].frameX = -1; + Main.tile[x + rect.X, y + rect.Y].frameY = -1; + } + } + + return true; + } + } + + /// + /// Contains the complete list of valid tile rect operations the game currently performs. + /// + private static readonly TileRectMatch[] Matches = new TileRectMatch[] + { + TileRectMatch.Placement(2, 3, TileID.TargetDummy, 54, 36), + TileRectMatch.Placement(3, 4, TileID.TeleportationPylon, 468, 54), + TileRectMatch.Placement(2, 3, TileID.DisplayDoll, 126, 36), + TileRectMatch.Placement(2, 3, TileID.HatRack, 90, 54), + TileRectMatch.Placement(2, 2, TileID.ItemFrame, 162, 18), + TileRectMatch.Placement(3, 3, TileID.WeaponsRack2, 90, 36), + TileRectMatch.Placement(1, 1, TileID.FoodPlatter, 18, 0), + TileRectMatch.Placement(1, 1, TileID.LogicSensor, 108, 0), + + TileRectMatch.StateChange(3, 2, TileID.Campfire, TileRectMatch.IGNORE_FRAME, 54), + TileRectMatch.StateChange(4, 3, TileID.Cannon, TileRectMatch.IGNORE_FRAME, 468), + TileRectMatch.StateChange(2, 2, TileID.ArrowSign, TileRectMatch.IGNORE_FRAME, 270), + TileRectMatch.StateChange(2, 2, TileID.PaintedArrowSign, TileRectMatch.IGNORE_FRAME, 270), + TileRectMatch.StateChange(2, 2, TileID.MusicBoxes, 54, TileRectMatch.IGNORE_FRAME), + TileRectMatch.StateChange(2, 3, TileID.LunarMonolith, TileRectMatch.IGNORE_FRAME, 92), + TileRectMatch.StateChange(2, 3, TileID.BloodMoonMonolith, TileRectMatch.IGNORE_FRAME, 90), + TileRectMatch.StateChange(2, 3, TileID.VoidMonolith, TileRectMatch.IGNORE_FRAME, 90), + TileRectMatch.StateChange(2, 3, TileID.EchoMonolith, TileRectMatch.IGNORE_FRAME, 90), + TileRectMatch.StateChange(2, 3, TileID.ShimmerMonolith, TileRectMatch.IGNORE_FRAME, 144), + TileRectMatch.StateChange(2, 4, TileID.WaterFountain, TileRectMatch.IGNORE_FRAME, 126), + TileRectMatch.StateChange(1, 1, TileID.Candles, 18, TileRectMatch.IGNORE_FRAME), + TileRectMatch.StateChange(1, 1, TileID.PeaceCandle, 18, TileRectMatch.IGNORE_FRAME), + TileRectMatch.StateChange(1, 1, TileID.WaterCandle, 18, TileRectMatch.IGNORE_FRAME), + TileRectMatch.StateChange(1, 1, TileID.PlatinumCandle, 18, TileRectMatch.IGNORE_FRAME), + TileRectMatch.StateChange(1, 1, TileID.ShadowCandle, 18, TileRectMatch.IGNORE_FRAME), + TileRectMatch.StateChange(1, 1, TileID.Traps, 90, 90), + TileRectMatch.StateChange(1, 1, TileID.WirePipe, 36, TileRectMatch.IGNORE_FRAME), + TileRectMatch.StateChange(1, 1, TileID.ProjectilePressurePad, 66, TileRectMatch.IGNORE_FRAME), + TileRectMatch.StateChange(1, 1, TileID.Plants, 792, TileRectMatch.IGNORE_FRAME), + TileRectMatch.StateChange(1, 1, TileID.MinecartTrack, 36, TileRectMatch.IGNORE_FRAME), + + TileRectMatch.Removal(1, 2, TileID.Firework), + TileRectMatch.Removal(1, 1, TileID.LandMine), + }; + + + /// + /// Handles a packet receive event. + /// + public void OnReceive(object sender, GetDataHandlers.SendTileRectEventArgs args) + { + // this permission bypasses all checks for direct access to the world + if (args.Player.HasPermission(Permissions.allowclientsideworldedit)) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect accepted clientside world edit from {args.Player.Name}")); + + // use vanilla handling + args.Handled = false; + return; + } + + // this handler handles the entire logic of this packet + args.Handled = true; + + // player throttled? + if (args.Player.IsBouncerThrottled()) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from throttle from {args.Player.Name}")); + + // send correcting data + args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + return; + } + + // player disabled? + if (args.Player.IsBeingDisabled()) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from being disabled from {args.Player.Name}")); + + // send correcting data + args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + return; + } + + // as of 1.4 this is the biggest size the client will send in any case, determined by full code analysis + // see default matches above and special cases below + if (args.Width > 4 || args.Length > 4) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from non-vanilla tilemod from {args.Player.Name}")); + + // definitely invalid; do not send any correcting data + return; + } + + // read the tile rectangle + TileRect rect = TileRect.Read(args.Data, args.TileX, args.TileY, args.Width, args.Length); + + // check if the positioning is valid + if (!IsRectPositionValid(args.Player, rect)) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from out of bounds / build permission from {args.Player.Name}")); + + // send nothing due to out of bounds + return; + } + + // a very special case, due to the clentaminator having a larger range than TSPlayer.IsInRange() allows + if (MatchesConversionSpread(args.Player, rect)) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}")); + + // send correcting data + args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + return; + } + + // check if the distance is valid + if (!IsRectDistanceValid(args.Player, rect)) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from out of range from {args.Player.Name}")); + + // send correcting data + args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + return; + } + + // a very special case, due to the flower seed check otherwise hijacking this + if (MatchesFlowerBoots(args.Player, rect)) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}")); + + // send correcting data + args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + return; + } + + // check if the rect matches any valid operation + foreach (TileRectMatch match in Matches) + { + if (match.Matches(args.Player, rect)) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}")); + + // send correcting data + args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + return; + } + } + + // a few special cases + if ( + MatchesConversionSpread(args.Player, rect) || + MatchesGrassMow(args.Player, rect) || + MatchesChristmasTree(args.Player, rect) + ) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}")); + + // send correcting data + args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + return; + } + + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from matches from {args.Player.Name}")); + + // send correcting data + args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + return; + } + + /// + /// Checks whether the tile rect is at a valid position for the given player. + /// + /// The player the operation originates from. + /// The tile rectangle of the operation. + /// , if the rect at a valid position, otherwise . + private static bool IsRectPositionValid(TSPlayer player, TileRect rect) + { + for (int x = 0; x < rect.Width; x++) + { + for (int y = 0; y < rect.Height; y++) + { + int realX = rect.X + x; + int realY = rect.Y + y; + + if (realX < 0 || realX >= Main.maxTilesX || realY < 0 || realY >= Main.maxTilesY) + { + return false; + } + } + } + + return true; + } + + /// + /// Checks whether the tile rect is at a valid distance to the given player. + /// + /// The player the operation originates from. + /// The tile rectangle of the operation. + /// , if the rect at a valid distance, otherwise . + private static bool IsRectDistanceValid(TSPlayer player, TileRect rect) + { + for (int x = 0; x < rect.Width; x++) + { + for (int y = 0; y < rect.Height; y++) + { + int realX = rect.X + x; + int realY = rect.Y + y; + + if (!player.IsInRange(realX, realY)) + { + return false; + } + } + } + + return true; + } + + + /// + /// Checks whether the tile rect is a valid conversion spread (Clentaminator, Powders, etc.) + /// + /// The player the operation originates from. + /// The tile rectangle of the operation. + /// , if the rect matches a conversion spread operation, otherwise . + private static bool MatchesConversionSpread(TSPlayer player, TileRect rect) + { + if (rect.Width != 1 || rect.Height != 1) + { + return false; + } + + ITile oldTile = Main.tile[rect.X, rect.Y]; + NetTile newTile = rect[0, 0]; + + bool matchedTileOrWall = false; + + if (oldTile.active()) + { + if ( + ( + (TileID.Sets.Conversion.Stone[oldTile.type] || Main.tileMoss[oldTile.type]) && + (TileID.Sets.Conversion.Stone[newTile.Type] || Main.tileMoss[newTile.Type]) + ) || + ( + (oldTile.type == TileID.Dirt || oldTile.type == TileID.Mud) && + (newTile.Type == TileID.Dirt || newTile.Type == TileID.Mud) + ) || + TileID.Sets.Conversion.Grass[oldTile.type] && TileID.Sets.Conversion.Grass[newTile.Type] || + TileID.Sets.Conversion.Ice[oldTile.type] && TileID.Sets.Conversion.Ice[newTile.Type] || + TileID.Sets.Conversion.Sand[oldTile.type] && TileID.Sets.Conversion.Sand[newTile.Type] || + TileID.Sets.Conversion.Sandstone[oldTile.type] && TileID.Sets.Conversion.Sandstone[newTile.Type] || + TileID.Sets.Conversion.HardenedSand[oldTile.type] && TileID.Sets.Conversion.HardenedSand[newTile.Type] || + TileID.Sets.Conversion.Thorn[oldTile.type] && TileID.Sets.Conversion.Thorn[newTile.Type] || + TileID.Sets.Conversion.Moss[oldTile.type] && TileID.Sets.Conversion.Moss[newTile.Type] || + TileID.Sets.Conversion.MossBrick[oldTile.type] && TileID.Sets.Conversion.MossBrick[newTile.Type] + ) + { + if (TShock.TileBans.TileIsBanned((short)newTile.Type, player)) + { + // for simplicity, let's pretend that the edit was valid, but do not execute it + matchedTileOrWall = true; + } + else if (!player.HasBuildPermission(rect.X, rect.Y)) + { + // for simplicity, let's pretend that the edit was valid, but do not execute it + matchedTileOrWall = true; + } + else + { + Main.tile[rect.X, rect.Y].type = newTile.Type; + Main.tile[rect.X, rect.Y].frameX = newTile.FrameX; + Main.tile[rect.X, rect.Y].frameY = newTile.FrameY; + + matchedTileOrWall = true; + } + } + } + + if (oldTile.wall != 0) + { + if ( + WallID.Sets.Conversion.Stone[oldTile.wall] && WallID.Sets.Conversion.Stone[newTile.Wall] || + WallID.Sets.Conversion.Grass[oldTile.wall] && WallID.Sets.Conversion.Grass[newTile.Wall] || + WallID.Sets.Conversion.Sandstone[oldTile.wall] && WallID.Sets.Conversion.Sandstone[newTile.Wall] || + WallID.Sets.Conversion.HardenedSand[oldTile.wall] && WallID.Sets.Conversion.HardenedSand[newTile.Wall] || + WallID.Sets.Conversion.PureSand[oldTile.wall] && WallID.Sets.Conversion.PureSand[newTile.Wall] || + WallID.Sets.Conversion.NewWall1[oldTile.wall] && WallID.Sets.Conversion.NewWall1[newTile.Wall] || + WallID.Sets.Conversion.NewWall2[oldTile.wall] && WallID.Sets.Conversion.NewWall2[newTile.Wall] || + WallID.Sets.Conversion.NewWall3[oldTile.wall] && WallID.Sets.Conversion.NewWall3[newTile.Wall] || + WallID.Sets.Conversion.NewWall4[oldTile.wall] && WallID.Sets.Conversion.NewWall4[newTile.Wall] + ) + { + // wallbans when? + + if (!player.HasBuildPermission(rect.X, rect.Y)) + { + // for simplicity, let's pretend that the edit was valid, but do not execute it + matchedTileOrWall = true; + } + else + { + Main.tile[rect.X, rect.Y].wall = newTile.Wall; + + matchedTileOrWall = true; + } + } + } + + return matchedTileOrWall; + } + + + private static readonly Dictionary> PlantToGrassMap = new Dictionary> + { + { TileID.Plants, new HashSet() + { + TileID.Grass, TileID.GolfGrass + } }, + { TileID.HallowedPlants, new HashSet() + { + TileID.HallowedGrass, TileID.GolfGrassHallowed + } }, + { TileID.HallowedPlants2, new HashSet() + { + TileID.HallowedGrass, TileID.GolfGrassHallowed + } }, + { TileID.JunglePlants2, new HashSet() + { + TileID.JungleGrass + } }, + { TileID.AshPlants, new HashSet() + { + TileID.AshGrass + } }, + }; + + private static readonly Dictionary> GrassToStyleMap = new Dictionary>() + { + { TileID.Plants, new HashSet() + { + 6, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 24, 27, 30, 33, 36, 39, 42, + 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 44, + } }, + { TileID.HallowedPlants, new HashSet() + { + 4, 6, + } }, + { TileID.HallowedPlants2, new HashSet() + { + 2, 3, 4, 6, 7, + } }, + { TileID.JunglePlants2, new HashSet() + { + 9, 10, 11, 12, 13, 14, 15, 16, + } }, + { TileID.AshPlants, new HashSet() + { + 6, 7, 8, 9, 10, + } }, + }; + + /// + /// Checks whether the tile rect is a valid Flower Boots placement. + /// + /// The player the operation originates from. + /// The tile rectangle of the operation. + /// , if the rect matches a Flower Boots placement, otherwise . + private static bool MatchesFlowerBoots(TSPlayer player, TileRect rect) + { + if (rect.Width != 1 || rect.Height != 1) + { + return false; + } + + if (!player.TPlayer.flowerBoots) + { + return false; + } + + ITile oldTile = Main.tile[rect.X, rect.Y]; + NetTile newTile = rect[0, 0]; + + if ( + PlantToGrassMap.TryGetValue(newTile.Type, out HashSet grassTiles) && + !oldTile.active() && grassTiles.Contains(Main.tile[rect.X, rect.Y + 1].type) && + GrassToStyleMap[newTile.Type].Contains((ushort)(newTile.FrameX / 18)) + ) + { + if (TShock.TileBans.TileIsBanned((short)newTile.Type, player)) + { + // for simplicity, let's pretend that the edit was valid, but do not execute it + return true; + } + + if (!player.HasBuildPermission(rect.X, rect.Y)) + { + // for simplicity, let's pretend that the edit was valid, but do not execute it + return true; + } + + Main.tile[rect.X, rect.Y].active(active: true); + Main.tile[rect.X, rect.Y].type = newTile.Type; + Main.tile[rect.X, rect.Y].frameX = newTile.FrameX; + Main.tile[rect.X, rect.Y].frameY = 0; + + return true; + } + + return false; + } + + + private static readonly Dictionary GrassToMowedMap = new Dictionary + { + { TileID.Grass, TileID.GolfGrass }, + { TileID.HallowedGrass, TileID.GolfGrassHallowed }, + }; + + /// + /// Checks whether the tile rect is a valid grass mow. + /// + /// The player the operation originates from. + /// The tile rectangle of the operation. + /// , if the rect matches a grass mowing operation, otherwise . + private static bool MatchesGrassMow(TSPlayer player, TileRect rect) + { + if (rect.Width != 1 || rect.Height != 1) + { + return false; + } + + ITile oldTile = Main.tile[rect.X, rect.Y]; + NetTile newTile = rect[0, 0]; + + if (GrassToMowedMap.TryGetValue(oldTile.type, out ushort mowed) && newTile.Type == mowed) + { + if (TShock.TileBans.TileIsBanned((short)newTile.Type, player)) + { + // for simplicity, let's pretend that the edit was valid, but do not execute it + return true; + } + + if (!player.HasBuildPermission(rect.X, rect.Y)) + { + // for simplicity, let's pretend that the edit was valid, but do not execute it + return true; + } + + Main.tile[rect.X, rect.Y].type = newTile.Type; + if (!newTile.FrameImportant) + { + Main.tile[rect.X, rect.Y].frameX = -1; + Main.tile[rect.X, rect.Y].frameY = -1; + } + + // prevent a common crash when the game checks all vines in an unlimited horizontal length + if (TileID.Sets.IsVine[Main.tile[rect.X, rect.Y + 1].type]) + { + WorldGen.KillTile(rect.X, rect.Y + 1); + } + + return true; + } + + return false; + } + + + /// + /// Checks whether the tile rect is a valid christmas tree modification. + /// This also required significant reverse engineering effort. + /// + /// The player the operation originates from. + /// The tile rectangle of the operation. + /// , if the rect matches a christmas tree operation, otherwise . + private static bool MatchesChristmasTree(TSPlayer player, TileRect rect) + { + if (rect.Width != 1 || rect.Height != 1) + { + return false; + } + + ITile oldTile = Main.tile[rect.X, rect.Y]; + NetTile newTile = rect[0, 0]; + + if (oldTile.type == TileID.ChristmasTree && newTile.Type == TileID.ChristmasTree) + { + if (newTile.FrameX != 10) + { + return false; + } + + int obj_0 = (newTile.FrameY & 0b0000000000000111); + int obj_1 = (newTile.FrameY & 0b0000000000111000) >> 3; + int obj_2 = (newTile.FrameY & 0b0000001111000000) >> 6; + int obj_3 = (newTile.FrameY & 0b0011110000000000) >> 10; + int obj_x = (newTile.FrameY & 0b1100000000000000) >> 14; + + if (obj_x != 0) + { + return false; + } + + if (obj_0 is < 0 or > 4 || obj_1 is < 0 or > 6 || obj_2 is < 0 or > 11 || obj_3 is < 0 or > 11) + { + return false; + } + + if (!player.HasBuildPermission(rect.X, rect.Y)) + { + // for simplicity, let's pretend that the edit was valid, but do not execute it + return true; + } + + Main.tile[rect.X, rect.Y].frameY = newTile.FrameY; + + return true; + } + + return false; + } + } +} From 2fdf096ce8104a1a77bbc0a7394fddb1634f75ff Mon Sep 17 00:00:00 2001 From: punchready Date: Tue, 4 Apr 2023 05:10:37 +0200 Subject: [PATCH 020/135] Update changelog --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index c4a95d14..48c965d5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -109,7 +109,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Relaxed custom death message restrictions to allow Inferno potions in PvP. (@drunderscore) * Allowed Flower Boots to place Ash Flowers on Ash Grass blocks. (@punchready) * Removed unnecessary range check that artifically shortened quick stack reach. (@boddyn, #2885, @bcat) -* Improved the exploit protection in tile rect handling. (@punchready) +* Re-wrote tile rect handling from scratch, fixing a certain exploitable flaw in the old code and significantly reducing the potential exploit surface, potentially even down to zero. (@punchready) ## TShock 5.1.3 * Added support for Terraria 1.4.4.9 via OTAPI 3.1.20. (@SignatureBeef) From 3302b4653ea986bcd6a8d7f813430fb1e6514031 Mon Sep 17 00:00:00 2001 From: punchready Date: Tue, 4 Apr 2023 05:51:53 +0200 Subject: [PATCH 021/135] Update STR checks to be even more strict --- .../Handlers/SendTileRectHandlerRefactor.cs | 125 ++++++++++++------ 1 file changed, 85 insertions(+), 40 deletions(-) diff --git a/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs b/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs index 86048193..9b8b0096 100644 --- a/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs +++ b/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs @@ -69,7 +69,7 @@ namespace TShockAPI.Handlers /// private readonly struct TileRectMatch { - public const short IGNORE_FRAME = -1; + private const short IGNORE_FRAME = -1; private enum MatchType { @@ -84,10 +84,12 @@ namespace TShockAPI.Handlers private readonly ushort TileType; private readonly short MaxFrameX; private readonly short MaxFrameY; + private readonly short FrameXStep; + private readonly short FrameYStep; private readonly MatchType Type; - private TileRectMatch(MatchType type, int width, int height, ushort tileType, short maxFrameX, short maxFrameY) + private TileRectMatch(MatchType type, int width, int height, ushort tileType, short maxFrameX, short maxFrameY, short frameXStep, short frameYStep) { Type = type; Width = width; @@ -95,6 +97,8 @@ namespace TShockAPI.Handlers TileType = tileType; MaxFrameX = maxFrameX; MaxFrameY = maxFrameY; + FrameXStep = frameXStep; + FrameYStep = frameYStep; } /// @@ -105,10 +109,12 @@ namespace TShockAPI.Handlers /// The tile type of the placement. /// The maximum allowed frameX of the placement, or if this operation does not change frameX. /// The maximum allowed frameY of the placement, or if this operation does not change frameY. + /// The step size in which frameX changes for this placement, or 1 if any value is allowed. + /// The step size in which frameX changes for this placement, or 1 if any value is allowed. /// The resulting operation match. - public static TileRectMatch Placement(int width, int height, ushort tileType, short maxFrameX, short maxFrameY) + public static TileRectMatch Placement(int width, int height, ushort tileType, short maxFrameX, short maxFrameY, short frameXStep, short frameYStep) { - return new TileRectMatch(MatchType.Placement, width, height, tileType, maxFrameX, maxFrameY); + return new TileRectMatch(MatchType.Placement, width, height, tileType, maxFrameX, maxFrameY, frameXStep, frameYStep); } /// @@ -117,12 +123,42 @@ namespace TShockAPI.Handlers /// The width of the state change. /// The height of the state change. /// The target tile type of the state change. - /// The maximum allowed frameX of the state change, or if this operation does not change frameX. - /// The maximum allowed frameY of the state change, or if this operation does not change frameY. + /// The maximum allowed frameX of the state change. + /// The maximum allowed frameY of the state change. + /// The step size in which frameX changes for this placement, or 1 if any value is allowed. + /// The step size in which frameY changes for this placement, or 1 if any value is allowed. /// The resulting operation match. - public static TileRectMatch StateChange(int width, int height, ushort tileType, short maxFrameX, short maxFrameY) + public static TileRectMatch StateChange(int width, int height, ushort tileType, short maxFrameX, short maxFrameY, short frameXStep, short frameYStep) { - return new TileRectMatch(MatchType.StateChange, width, height, tileType, maxFrameX, maxFrameY); + return new TileRectMatch(MatchType.StateChange, width, height, tileType, maxFrameX, maxFrameY, frameXStep, frameYStep); + } + + /// + /// Creates a new state change operation which only changes frameX. + /// + /// The width of the state change. + /// The height of the state change. + /// The target tile type of the state change. + /// The maximum allowed frameX of the state change. + /// The step size in which frameX changes for this placement, or 1 if any value is allowed. + /// The resulting operation match. + public static TileRectMatch StateChangeX(int width, int height, ushort tileType, short maxFrame, short frameStep) + { + return new TileRectMatch(MatchType.StateChange, width, height, tileType, maxFrame, IGNORE_FRAME, frameStep, 0); + } + + /// + /// Creates a new state change operation which only changes frameY. + /// + /// The width of the state change. + /// The height of the state change. + /// The target tile type of the state change. + /// The maximum allowed frameY of the state change. + /// The step size in which frameY changes for this placement, or 1 if any value is allowed. + /// The resulting operation match. + public static TileRectMatch StateChangeY(int width, int height, ushort tileType, short maxFrame, short frameStep) + { + return new TileRectMatch(MatchType.StateChange, width, height, tileType, IGNORE_FRAME, maxFrame, 0, frameStep); } /// @@ -134,7 +170,7 @@ namespace TShockAPI.Handlers /// The resulting operation match. public static TileRectMatch Removal(int width, int height, ushort tileType) { - return new TileRectMatch(MatchType.Removal, width, height, tileType, 0, 0); + return new TileRectMatch(MatchType.Removal, width, height, tileType, 0, 0, 0, 0); } /// @@ -166,14 +202,14 @@ namespace TShockAPI.Handlers { if (MaxFrameX != IGNORE_FRAME) { - if (tile.FrameX < 0 || tile.FrameX > MaxFrameX) + if (tile.FrameX < 0 || tile.FrameX > MaxFrameX || tile.FrameX % FrameXStep != 0) { return false; } } if (MaxFrameY != IGNORE_FRAME) { - if (tile.FrameY < 0 || tile.FrameY > MaxFrameY) + if (tile.FrameY < 0 || tile.FrameY > MaxFrameY || tile.FrameY % FrameYStep != 0) { return false; } @@ -321,38 +357,47 @@ namespace TShockAPI.Handlers /// /// Contains the complete list of valid tile rect operations the game currently performs. /// + // The matches restrict the tile rects to only place one kind of tile, and only with the given maximum values and step sizes for frameX and frameY. This performs pretty much perfect checks on the data, allowing only valid placements. + // For TileID.MinecartTrack, the data is taken from `Minecart._trackSwitchOptions`, allowing any framing value in this array (currently 0-36). + // For TileID.Plants, it is taken from `ItemID.Sets.flowerPacketInfo[n].stylesOnPurity`, allowing every style multiplied by 18. + // The other operations are based on code analysis and manual observation. private static readonly TileRectMatch[] Matches = new TileRectMatch[] { - TileRectMatch.Placement(2, 3, TileID.TargetDummy, 54, 36), - TileRectMatch.Placement(3, 4, TileID.TeleportationPylon, 468, 54), - TileRectMatch.Placement(2, 3, TileID.DisplayDoll, 126, 36), - TileRectMatch.Placement(2, 3, TileID.HatRack, 90, 54), - TileRectMatch.Placement(2, 2, TileID.ItemFrame, 162, 18), - TileRectMatch.Placement(3, 3, TileID.WeaponsRack2, 90, 36), - TileRectMatch.Placement(1, 1, TileID.FoodPlatter, 18, 0), - TileRectMatch.Placement(1, 1, TileID.LogicSensor, 108, 0), + TileRectMatch.Placement(2, 3, TileID.TargetDummy, 54, 36, 18, 18), + TileRectMatch.Placement(3, 4, TileID.TeleportationPylon, 468, 54, 18, 18), + TileRectMatch.Placement(2, 3, TileID.DisplayDoll, 126, 36, 18, 18), + TileRectMatch.Placement(2, 3, TileID.HatRack, 90, 54, 18, 18), + TileRectMatch.Placement(2, 2, TileID.ItemFrame, 162, 18, 18, 18), + TileRectMatch.Placement(3, 3, TileID.WeaponsRack2, 90, 36, 18, 18), + TileRectMatch.Placement(1, 1, TileID.FoodPlatter, 18, 0, 18, 18), + TileRectMatch.Placement(1, 1, TileID.LogicSensor, 108, 0, 18, 18), - TileRectMatch.StateChange(3, 2, TileID.Campfire, TileRectMatch.IGNORE_FRAME, 54), - TileRectMatch.StateChange(4, 3, TileID.Cannon, TileRectMatch.IGNORE_FRAME, 468), - TileRectMatch.StateChange(2, 2, TileID.ArrowSign, TileRectMatch.IGNORE_FRAME, 270), - TileRectMatch.StateChange(2, 2, TileID.PaintedArrowSign, TileRectMatch.IGNORE_FRAME, 270), - TileRectMatch.StateChange(2, 2, TileID.MusicBoxes, 54, TileRectMatch.IGNORE_FRAME), - TileRectMatch.StateChange(2, 3, TileID.LunarMonolith, TileRectMatch.IGNORE_FRAME, 92), - TileRectMatch.StateChange(2, 3, TileID.BloodMoonMonolith, TileRectMatch.IGNORE_FRAME, 90), - TileRectMatch.StateChange(2, 3, TileID.VoidMonolith, TileRectMatch.IGNORE_FRAME, 90), - TileRectMatch.StateChange(2, 3, TileID.EchoMonolith, TileRectMatch.IGNORE_FRAME, 90), - TileRectMatch.StateChange(2, 3, TileID.ShimmerMonolith, TileRectMatch.IGNORE_FRAME, 144), - TileRectMatch.StateChange(2, 4, TileID.WaterFountain, TileRectMatch.IGNORE_FRAME, 126), - TileRectMatch.StateChange(1, 1, TileID.Candles, 18, TileRectMatch.IGNORE_FRAME), - TileRectMatch.StateChange(1, 1, TileID.PeaceCandle, 18, TileRectMatch.IGNORE_FRAME), - TileRectMatch.StateChange(1, 1, TileID.WaterCandle, 18, TileRectMatch.IGNORE_FRAME), - TileRectMatch.StateChange(1, 1, TileID.PlatinumCandle, 18, TileRectMatch.IGNORE_FRAME), - TileRectMatch.StateChange(1, 1, TileID.ShadowCandle, 18, TileRectMatch.IGNORE_FRAME), - TileRectMatch.StateChange(1, 1, TileID.Traps, 90, 90), - TileRectMatch.StateChange(1, 1, TileID.WirePipe, 36, TileRectMatch.IGNORE_FRAME), - TileRectMatch.StateChange(1, 1, TileID.ProjectilePressurePad, 66, TileRectMatch.IGNORE_FRAME), - TileRectMatch.StateChange(1, 1, TileID.Plants, 792, TileRectMatch.IGNORE_FRAME), - TileRectMatch.StateChange(1, 1, TileID.MinecartTrack, 36, TileRectMatch.IGNORE_FRAME), + TileRectMatch.StateChangeY(3, 2, TileID.Campfire, 54, 18), + TileRectMatch.StateChangeY(4, 3, TileID.Cannon, 468, 18), + TileRectMatch.StateChangeY(2, 2, TileID.ArrowSign, 270, 18), + TileRectMatch.StateChangeY(2, 2, TileID.PaintedArrowSign, 270, 18), + + TileRectMatch.StateChangeX(2, 2, TileID.MusicBoxes, 54, 18), + + TileRectMatch.StateChangeY(2, 3, TileID.LunarMonolith, 92, 18), + TileRectMatch.StateChangeY(2, 3, TileID.BloodMoonMonolith, 90, 18), + TileRectMatch.StateChangeY(2, 3, TileID.VoidMonolith, 90, 18), + TileRectMatch.StateChangeY(2, 3, TileID.EchoMonolith, 90, 18), + TileRectMatch.StateChangeY(2, 3, TileID.ShimmerMonolith, 144, 18), + TileRectMatch.StateChangeY(2, 4, TileID.WaterFountain, 126, 18), + + TileRectMatch.StateChangeX(1, 1, TileID.Candles, 18, 18), + TileRectMatch.StateChangeX(1, 1, TileID.PeaceCandle, 18, 18), + TileRectMatch.StateChangeX(1, 1, TileID.WaterCandle, 18, 18), + TileRectMatch.StateChangeX(1, 1, TileID.PlatinumCandle, 18, 18), + TileRectMatch.StateChangeX(1, 1, TileID.ShadowCandle, 18, 18), + + TileRectMatch.StateChange(1, 1, TileID.Traps, 90, 90, 18, 18), + + TileRectMatch.StateChangeX(1, 1, TileID.WirePipe, 36, 18), + TileRectMatch.StateChangeX(1, 1, TileID.ProjectilePressurePad, 66, 22), + TileRectMatch.StateChangeX(1, 1, TileID.Plants, 792, 18), + TileRectMatch.StateChangeX(1, 1, TileID.MinecartTrack, 36, 1), TileRectMatch.Removal(1, 2, TileID.Firework), TileRectMatch.Removal(1, 1, TileID.LandMine), From 26482da23f70c8b22fa00b5ccdc853bc4e0e0f68 Mon Sep 17 00:00:00 2001 From: punchready Date: Tue, 4 Apr 2023 06:02:55 +0200 Subject: [PATCH 022/135] Remove frame ignoring from tile rect placement operations --- TShockAPI/Handlers/SendTileRectHandlerRefactor.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs b/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs index 9b8b0096..a3c87100 100644 --- a/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs +++ b/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs @@ -107,8 +107,8 @@ namespace TShockAPI.Handlers /// The width of the placement. /// The height of the placement. /// The tile type of the placement. - /// The maximum allowed frameX of the placement, or if this operation does not change frameX. - /// The maximum allowed frameY of the placement, or if this operation does not change frameY. + /// The maximum allowed frameX of the placement. + /// The maximum allowed frameY of the placement. /// The step size in which frameX changes for this placement, or 1 if any value is allowed. /// The step size in which frameX changes for this placement, or 1 if any value is allowed. /// The resulting operation match. @@ -282,14 +282,8 @@ namespace TShockAPI.Handlers { Main.tile[x + rect.X, y + rect.Y].active(active: true); Main.tile[x + rect.X, y + rect.Y].type = rect[x, y].Type; - if (MaxFrameX != IGNORE_FRAME) - { - Main.tile[x + rect.X, y + rect.Y].frameX = rect[x, y].FrameX; - } - if (MaxFrameY != IGNORE_FRAME) - { - Main.tile[x + rect.X, y + rect.Y].frameY = rect[x, y].FrameY; - } + Main.tile[x + rect.X, y + rect.Y].frameX = rect[x, y].FrameX; + Main.tile[x + rect.X, y + rect.Y].frameY = rect[x, y].FrameY; } } From d0409db5fb0774a9787627e92d14037e7b696712 Mon Sep 17 00:00:00 2001 From: punchready Date: Tue, 4 Apr 2023 06:16:44 +0200 Subject: [PATCH 023/135] Never send back too large tile rects in handling --- .../Handlers/SendTileRectHandlerRefactor.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs b/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs index a3c87100..1472423f 100644 --- a/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs +++ b/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs @@ -416,6 +416,16 @@ namespace TShockAPI.Handlers // this handler handles the entire logic of this packet args.Handled = true; + // as of 1.4 this is the biggest size the client will send in any case, determined by full code analysis + // see default matches above and special cases below + if (args.Width > 4 || args.Length > 4) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from size from {args.Player.Name}")); + + // definitely invalid; do not send any correcting data + return; + } + // player throttled? if (args.Player.IsBouncerThrottled()) { @@ -436,16 +446,6 @@ namespace TShockAPI.Handlers return; } - // as of 1.4 this is the biggest size the client will send in any case, determined by full code analysis - // see default matches above and special cases below - if (args.Width > 4 || args.Length > 4) - { - TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from non-vanilla tilemod from {args.Player.Name}")); - - // definitely invalid; do not send any correcting data - return; - } - // read the tile rectangle TileRect rect = TileRect.Read(args.Data, args.TileX, args.TileY, args.Width, args.Length); From c309990f9488af07939111e5bc153cdcd571e25b Mon Sep 17 00:00:00 2001 From: punchready Date: Wed, 5 Apr 2023 06:43:47 +0200 Subject: [PATCH 024/135] Fix LunarMonolith toggling --- TShockAPI/Handlers/SendTileRectHandlerRefactor.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs b/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs index 1472423f..b3a2d1e7 100644 --- a/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs +++ b/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs @@ -211,10 +211,14 @@ namespace TShockAPI.Handlers { if (tile.FrameY < 0 || tile.FrameY > MaxFrameY || tile.FrameY % FrameYStep != 0) { + // this is the only tile type sent in a tile rect where the frame have a different pattern (56, 74, 92 instead of 54, 72, 90) + if (!(TileType == TileID.LunarMonolith && tile.FrameY % FrameYStep == 2)) + { return false; } } } + } if (Type == MatchType.Removal) { if (tile.Active) From b57eb91230c9cff20c2249236355953a99339ba7 Mon Sep 17 00:00:00 2001 From: punchready Date: Wed, 5 Apr 2023 06:44:21 +0200 Subject: [PATCH 025/135] Rewrite conversion spread handling to be much more accurate --- .../Handlers/SendTileRectHandlerRefactor.cs | 576 ++++++++++++++++-- 1 file changed, 511 insertions(+), 65 deletions(-) diff --git a/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs b/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs index b3a2d1e7..34f8a495 100644 --- a/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs +++ b/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs @@ -214,11 +214,11 @@ namespace TShockAPI.Handlers // this is the only tile type sent in a tile rect where the frame have a different pattern (56, 74, 92 instead of 54, 72, 90) if (!(TileType == TileID.LunarMonolith && tile.FrameY % FrameYStep == 2)) { - return false; + return false; + } } } } - } if (Type == MatchType.Removal) { if (tile.Active) @@ -578,7 +578,7 @@ namespace TShockAPI.Handlers /// - /// Checks whether the tile rect is a valid conversion spread (Clentaminator, Powders, etc.) + /// Checks whether the tile rect is a valid conversion spread (Clentaminator, Powders, etc.). /// /// The player the operation originates from. /// The tile rectangle of the operation. @@ -593,81 +593,48 @@ namespace TShockAPI.Handlers ITile oldTile = Main.tile[rect.X, rect.Y]; NetTile newTile = rect[0, 0]; - bool matchedTileOrWall = false; + WorldGenMock.SimulateConversionChange(rect.X, rect.Y, out HashSet validTiles, out HashSet validWalls); - if (oldTile.active()) + if (newTile.Type != oldTile.type && validTiles.Contains(newTile.Type)) { - if ( - ( - (TileID.Sets.Conversion.Stone[oldTile.type] || Main.tileMoss[oldTile.type]) && - (TileID.Sets.Conversion.Stone[newTile.Type] || Main.tileMoss[newTile.Type]) - ) || - ( - (oldTile.type == TileID.Dirt || oldTile.type == TileID.Mud) && - (newTile.Type == TileID.Dirt || newTile.Type == TileID.Mud) - ) || - TileID.Sets.Conversion.Grass[oldTile.type] && TileID.Sets.Conversion.Grass[newTile.Type] || - TileID.Sets.Conversion.Ice[oldTile.type] && TileID.Sets.Conversion.Ice[newTile.Type] || - TileID.Sets.Conversion.Sand[oldTile.type] && TileID.Sets.Conversion.Sand[newTile.Type] || - TileID.Sets.Conversion.Sandstone[oldTile.type] && TileID.Sets.Conversion.Sandstone[newTile.Type] || - TileID.Sets.Conversion.HardenedSand[oldTile.type] && TileID.Sets.Conversion.HardenedSand[newTile.Type] || - TileID.Sets.Conversion.Thorn[oldTile.type] && TileID.Sets.Conversion.Thorn[newTile.Type] || - TileID.Sets.Conversion.Moss[oldTile.type] && TileID.Sets.Conversion.Moss[newTile.Type] || - TileID.Sets.Conversion.MossBrick[oldTile.type] && TileID.Sets.Conversion.MossBrick[newTile.Type] - ) + if (TShock.TileBans.TileIsBanned((short)newTile.Type, player)) { - if (TShock.TileBans.TileIsBanned((short)newTile.Type, player)) - { - // for simplicity, let's pretend that the edit was valid, but do not execute it - matchedTileOrWall = true; - } - else if (!player.HasBuildPermission(rect.X, rect.Y)) - { - // for simplicity, let's pretend that the edit was valid, but do not execute it - matchedTileOrWall = true; - } - else - { - Main.tile[rect.X, rect.Y].type = newTile.Type; - Main.tile[rect.X, rect.Y].frameX = newTile.FrameX; - Main.tile[rect.X, rect.Y].frameY = newTile.FrameY; + // for simplicity, let's pretend that the edit was valid, but do not execute it + return true; + } + else if (!player.HasBuildPermission(rect.X, rect.Y)) + { + // for simplicity, let's pretend that the edit was valid, but do not execute it + return true; + } + else + { + Main.tile[rect.X, rect.Y].type = newTile.Type; + Main.tile[rect.X, rect.Y].frameX = newTile.FrameX; + Main.tile[rect.X, rect.Y].frameY = newTile.FrameY; - matchedTileOrWall = true; - } + return true; } } - if (oldTile.wall != 0) + if (newTile.Wall != oldTile.wall && validWalls.Contains(newTile.Wall)) { - if ( - WallID.Sets.Conversion.Stone[oldTile.wall] && WallID.Sets.Conversion.Stone[newTile.Wall] || - WallID.Sets.Conversion.Grass[oldTile.wall] && WallID.Sets.Conversion.Grass[newTile.Wall] || - WallID.Sets.Conversion.Sandstone[oldTile.wall] && WallID.Sets.Conversion.Sandstone[newTile.Wall] || - WallID.Sets.Conversion.HardenedSand[oldTile.wall] && WallID.Sets.Conversion.HardenedSand[newTile.Wall] || - WallID.Sets.Conversion.PureSand[oldTile.wall] && WallID.Sets.Conversion.PureSand[newTile.Wall] || - WallID.Sets.Conversion.NewWall1[oldTile.wall] && WallID.Sets.Conversion.NewWall1[newTile.Wall] || - WallID.Sets.Conversion.NewWall2[oldTile.wall] && WallID.Sets.Conversion.NewWall2[newTile.Wall] || - WallID.Sets.Conversion.NewWall3[oldTile.wall] && WallID.Sets.Conversion.NewWall3[newTile.Wall] || - WallID.Sets.Conversion.NewWall4[oldTile.wall] && WallID.Sets.Conversion.NewWall4[newTile.Wall] - ) + // wallbans when? + + if (!player.HasBuildPermission(rect.X, rect.Y)) { - // wallbans when? + // for simplicity, let's pretend that the edit was valid, but do not execute it + return true; + } + else + { + Main.tile[rect.X, rect.Y].wall = newTile.Wall; - if (!player.HasBuildPermission(rect.X, rect.Y)) - { - // for simplicity, let's pretend that the edit was valid, but do not execute it - matchedTileOrWall = true; - } - else - { - Main.tile[rect.X, rect.Y].wall = newTile.Wall; - - matchedTileOrWall = true; - } + return true; } } - return matchedTileOrWall; + return false; } @@ -881,4 +848,483 @@ namespace TShockAPI.Handlers return false; } } + + /// + /// This helper class allows simulating a `WorldGen.Convert` call and retrieving all valid changes for a given tile. + /// + internal static class WorldGenMock + { + /// + /// This is a mock tile which collects all possible changes the `WorldGen.Convert` code could make in its property setters. + /// + private sealed class MockTile + { + private readonly HashSet _setTypes; + private readonly HashSet _setWalls; + + private ushort _type; + private ushort _wall; + + public MockTile(ushort type, ushort wall, HashSet setTypes, HashSet setWalls) + { + _setTypes = setTypes; + _setWalls = setWalls; + _type = type; + _wall = wall; + } + +#pragma warning disable IDE1006 + + public ushort type + { + get => _type; + set + { + _setTypes.Add(value); + _type = value; + } + } + + public ushort wall + { + get => _wall; + set + { + _setWalls.Add(value); + _wall = value; + } + } + +#pragma warning restore IDE1006 + } + + /// + /// Simulates what would happen if `WorldGen.Convert` was called on the given coordinates and returns two sets with the possible tile type and wall types that the conversion could change the tile to. + /// + public static void SimulateConversionChange(int x, int y, out HashSet validTiles, out HashSet validWalls) + { + validTiles = new HashSet(); + validWalls = new HashSet(); + + // all the conversion types used in the code, most apparent in Projectile ai 31 + foreach (int conversionType in new int[] { 0, 1, 2, 3, 4, 5, 6, 7 }) + { + MockTile mock = new(Main.tile[x, y].type, Main.tile[x, y].wall, validTiles, validWalls); + Convert(mock, x, y, conversionType); + } + } + + /* + * This is a copy of the `WorldGen.Convert` method with the following precise changes: + * - Added a `MockTile tile` parameter + * - Changed the `i` and `j` parameters to `k` and `l` + * - Removed the size parameter + * - Removed the area loop and `Tile tile = Main.tile[k, l]` access in favor of using the tile parameter + * - Removed all calls to `WorldGen.SquareWallFrame`, `NetMessage.SendTileSquare`, `WorldGen.TryKillingTreesAboveIfTheyWouldBecomeInvalid` + * - Changed all `continue` statements to `break` statements + * - Removed the ifs checking the bounds of the tile and wall types + * - Removed branches that would call `WorldGen.KillTile` + * - Changed branches depending on randomness to instead set the property to both values after one another + * + * This overall leads to a method that can be called on a MockTile and real-world coordinates and will spit out the proper conversion changes into the MockTile. + */ + + private static void Convert(MockTile tile, int k, int l, int conversionType) + { + int type = tile.type; + int wall = tile.wall; + switch (conversionType) + { + case 4: + if (WallID.Sets.Conversion.Grass[wall] && wall != 81) + { + tile.wall = 81; + } + else if (WallID.Sets.Conversion.Stone[wall] && wall != 83) + { + tile.wall = 83; + } + else if (WallID.Sets.Conversion.HardenedSand[wall] && wall != 218) + { + tile.wall = 218; + } + else if (WallID.Sets.Conversion.Sandstone[wall] && wall != 221) + { + tile.wall = 221; + } + else if (WallID.Sets.Conversion.NewWall1[wall] && wall != 192) + { + tile.wall = 192; + } + else if (WallID.Sets.Conversion.NewWall2[wall] && wall != 193) + { + tile.wall = 193; + } + else if (WallID.Sets.Conversion.NewWall3[wall] && wall != 194) + { + tile.wall = 194; + } + else if (WallID.Sets.Conversion.NewWall4[wall] && wall != 195) + { + tile.wall = 195; + } + if ((Main.tileMoss[type] || TileID.Sets.Conversion.Stone[type]) && type != 203) + { + tile.type = 203; + } + else if (TileID.Sets.Conversion.JungleGrass[type] && type != 662) + { + tile.type = 662; + } + else if (TileID.Sets.Conversion.Grass[type] && type != 199) + { + tile.type = 199; + } + else if (TileID.Sets.Conversion.Ice[type] && type != 200) + { + tile.type = 200; + } + else if (TileID.Sets.Conversion.Sand[type] && type != 234) + { + tile.type = 234; + } + else if (TileID.Sets.Conversion.HardenedSand[type] && type != 399) + { + tile.type = 399; + } + else if (TileID.Sets.Conversion.Sandstone[type] && type != 401) + { + tile.type = 401; + } + else if (TileID.Sets.Conversion.Thorn[type] && type != 352) + { + tile.type = 352; + } + break; + case 2: + if (WallID.Sets.Conversion.Grass[wall] && wall != 70) + { + tile.wall = 70; + } + else if (WallID.Sets.Conversion.Stone[wall] && wall != 28) + { + tile.wall = 28; + } + else if (WallID.Sets.Conversion.HardenedSand[wall] && wall != 219) + { + tile.wall = 219; + } + else if (WallID.Sets.Conversion.Sandstone[wall] && wall != 222) + { + tile.wall = 222; + } + else if (WallID.Sets.Conversion.NewWall1[wall] && wall != 200) + { + tile.wall = 200; + } + else if (WallID.Sets.Conversion.NewWall2[wall] && wall != 201) + { + tile.wall = 201; + } + else if (WallID.Sets.Conversion.NewWall3[wall] && wall != 202) + { + tile.wall = 202; + } + else if (WallID.Sets.Conversion.NewWall4[wall] && wall != 203) + { + tile.wall = 203; + } + if ((Main.tileMoss[type] || TileID.Sets.Conversion.Stone[type]) && type != 117) + { + tile.type = 117; + } + else if (TileID.Sets.Conversion.GolfGrass[type] && type != 492) + { + tile.type = 492; + } + else if (TileID.Sets.Conversion.Grass[type] && type != 109 && type != 492) + { + tile.type = 109; + } + else if (TileID.Sets.Conversion.Ice[type] && type != 164) + { + tile.type = 164; + } + else if (TileID.Sets.Conversion.Sand[type] && type != 116) + { + tile.type = 116; + } + else if (TileID.Sets.Conversion.HardenedSand[type] && type != 402) + { + tile.type = 402; + } + else if (TileID.Sets.Conversion.Sandstone[type] && type != 403) + { + tile.type = 403; + } + if (type == 59 && (Main.tile[k - 1, l].type == 109 || Main.tile[k + 1, l].type == 109 || Main.tile[k, l - 1].type == 109 || Main.tile[k, l + 1].type == 109)) + { + tile.type = 0; + } + break; + case 1: + if (WallID.Sets.Conversion.Grass[wall] && wall != 69) + { + tile.wall = 69; + } + else if (TileID.Sets.Conversion.JungleGrass[type] && type != 661) + { + tile.type = 661; + } + else if (WallID.Sets.Conversion.Stone[wall] && wall != 3) + { + tile.wall = 3; + } + else if (WallID.Sets.Conversion.HardenedSand[wall] && wall != 217) + { + tile.wall = 217; + } + else if (WallID.Sets.Conversion.Sandstone[wall] && wall != 220) + { + tile.wall = 220; + } + else if (WallID.Sets.Conversion.NewWall1[wall] && wall != 188) + { + tile.wall = 188; + } + else if (WallID.Sets.Conversion.NewWall2[wall] && wall != 189) + { + tile.wall = 189; + } + else if (WallID.Sets.Conversion.NewWall3[wall] && wall != 190) + { + tile.wall = 190; + } + else if (WallID.Sets.Conversion.NewWall4[wall] && wall != 191) + { + tile.wall = 191; + } + if ((Main.tileMoss[type] || TileID.Sets.Conversion.Stone[type]) && type != 25) + { + tile.type = 25; + } + else if (TileID.Sets.Conversion.Grass[type] && type != 23) + { + tile.type = 23; + } + else if (TileID.Sets.Conversion.Ice[type] && type != 163) + { + tile.type = 163; + } + else if (TileID.Sets.Conversion.Sand[type] && type != 112) + { + tile.type = 112; + } + else if (TileID.Sets.Conversion.HardenedSand[type] && type != 398) + { + tile.type = 398; + } + else if (TileID.Sets.Conversion.Sandstone[type] && type != 400) + { + tile.type = 400; + } + else if (TileID.Sets.Conversion.Thorn[type] && type != 32) + { + tile.type = 32; + } + break; + case 3: + if (WallID.Sets.CanBeConvertedToGlowingMushroom[wall]) + { + tile.wall = 80; + } + if (tile.type == 60) + { + tile.type = 70; + } + break; + case 5: + if ((WallID.Sets.Conversion.Stone[wall] || WallID.Sets.Conversion.NewWall1[wall] || WallID.Sets.Conversion.NewWall2[wall] || WallID.Sets.Conversion.NewWall3[wall] || WallID.Sets.Conversion.NewWall4[wall] || WallID.Sets.Conversion.Ice[wall] || WallID.Sets.Conversion.Sandstone[wall]) && wall != 187) + { + tile.wall = 187; + } + else if ((WallID.Sets.Conversion.HardenedSand[wall] || WallID.Sets.Conversion.Dirt[wall] || WallID.Sets.Conversion.Snow[wall]) && wall != 216) + { + tile.wall = 216; + } + if ((TileID.Sets.Conversion.Grass[type] || TileID.Sets.Conversion.Sand[type] || TileID.Sets.Conversion.Snow[type] || TileID.Sets.Conversion.Dirt[type]) && type != 53) + { + int num = 53; + if (WorldGen.BlockBelowMakesSandConvertIntoHardenedSand(k, l)) + { + num = 397; + } + tile.type = (ushort)num; + } + else if (TileID.Sets.Conversion.HardenedSand[type] && type != 397) + { + tile.type = 397; + } + else if ((Main.tileMoss[type] || TileID.Sets.Conversion.Stone[type] || TileID.Sets.Conversion.Ice[type] || TileID.Sets.Conversion.Sandstone[type]) && type != 396) + { + tile.type = 396; + } + break; + case 6: + if ((WallID.Sets.Conversion.Stone[wall] || WallID.Sets.Conversion.NewWall1[wall] || WallID.Sets.Conversion.NewWall2[wall] || WallID.Sets.Conversion.NewWall3[wall] || WallID.Sets.Conversion.NewWall4[wall] || WallID.Sets.Conversion.Ice[wall] || WallID.Sets.Conversion.Sandstone[wall]) && wall != 71) + { + tile.wall = 71; + } + else if ((WallID.Sets.Conversion.HardenedSand[wall] || WallID.Sets.Conversion.Dirt[wall] || WallID.Sets.Conversion.Snow[wall]) && wall != 40) + { + tile.wall = 40; + } + if ((TileID.Sets.Conversion.Grass[type] || TileID.Sets.Conversion.Sand[type] || TileID.Sets.Conversion.HardenedSand[type] || TileID.Sets.Conversion.Snow[type] || TileID.Sets.Conversion.Dirt[type]) && type != 147) + { + tile.type = 147; + } + else if ((Main.tileMoss[type] || TileID.Sets.Conversion.Stone[type] || TileID.Sets.Conversion.Ice[type] || TileID.Sets.Conversion.Sandstone[type]) && type != 161) + { + tile.type = 161; + } + break; + case 7: + if ((WallID.Sets.Conversion.Stone[wall] || WallID.Sets.Conversion.Ice[wall] || WallID.Sets.Conversion.Sandstone[wall]) && wall != 1) + { + tile.wall = 1; + } + else if ((WallID.Sets.Conversion.HardenedSand[wall] || WallID.Sets.Conversion.Snow[wall] || WallID.Sets.Conversion.Dirt[wall]) && wall != 2) + { + tile.wall = 2; + } + else if (WallID.Sets.Conversion.NewWall1[wall] && wall != 196) + { + tile.wall = 196; + } + else if (WallID.Sets.Conversion.NewWall2[wall] && wall != 197) + { + tile.wall = 197; + } + else if (WallID.Sets.Conversion.NewWall3[wall] && wall != 198) + { + tile.wall = 198; + } + else if (WallID.Sets.Conversion.NewWall4[wall] && wall != 199) + { + tile.wall = 199; + } + if ((TileID.Sets.Conversion.Stone[type] || TileID.Sets.Conversion.Ice[type] || TileID.Sets.Conversion.Sandstone[type]) && type != 1) + { + tile.type = 1; + } + else if (TileID.Sets.Conversion.GolfGrass[type] && type != 477) + { + tile.type = 477; + } + else if (TileID.Sets.Conversion.Grass[type] && type != 2 && type != 477) + { + tile.type = 2; + } + else if ((TileID.Sets.Conversion.Sand[type] || TileID.Sets.Conversion.HardenedSand[type] || TileID.Sets.Conversion.Snow[type] || TileID.Sets.Conversion.Dirt[type]) && type != 0) + { + int num2 = 0; + if (WorldGen.TileIsExposedToAir(k, l)) + { + num2 = 2; + } + tile.type = (ushort)num2; + } + break; + } + if (tile.wall == 69 || tile.wall == 70 || tile.wall == 81) + { + if (l < Main.worldSurface) + { + tile.wall = 65; + tile.wall = 63; + } + else + { + tile.wall = 64; + } + } + else if (WallID.Sets.Conversion.Stone[wall] && wall != 1 && wall != 262 && wall != 274 && wall != 61 && wall != 185) + { + tile.wall = 1; + } + else if (WallID.Sets.Conversion.Stone[wall] && wall == 262) + { + tile.wall = 61; + } + else if (WallID.Sets.Conversion.Stone[wall] && wall == 274) + { + tile.wall = 185; + } + if (WallID.Sets.Conversion.NewWall1[wall] && wall != 212) + { + tile.wall = 212; + } + else if (WallID.Sets.Conversion.NewWall2[wall] && wall != 213) + { + tile.wall = 213; + } + else if (WallID.Sets.Conversion.NewWall3[wall] && wall != 214) + { + tile.wall = 214; + } + else if (WallID.Sets.Conversion.NewWall4[wall] && wall != 215) + { + tile.wall = 215; + } + else if (tile.wall == 80) + { + tile.wall = 15; + tile.wall = 64; + } + else if (WallID.Sets.Conversion.HardenedSand[wall] && wall != 216) + { + tile.wall = 216; + } + else if (WallID.Sets.Conversion.Sandstone[wall] && wall != 187) + { + tile.wall = 187; + } + if (tile.type == 492) + { + tile.type = 477; + } + else if (TileID.Sets.Conversion.JungleGrass[type] && type != 60) + { + tile.type = 60; + } + else if (TileID.Sets.Conversion.Grass[type] && type != 2 && type != 477) + { + tile.type = 2; + } + else if (TileID.Sets.Conversion.Stone[type] && type != 1) + { + tile.type = 1; + } + else if (TileID.Sets.Conversion.Sand[type] && type != 53) + { + tile.type = 53; + } + else if (TileID.Sets.Conversion.HardenedSand[type] && type != 397) + { + tile.type = 397; + } + else if (TileID.Sets.Conversion.Sandstone[type] && type != 396) + { + tile.type = 396; + } + else if (TileID.Sets.Conversion.Ice[type] && type != 161) + { + tile.type = 161; + } + else if (TileID.Sets.Conversion.MushroomGrass[type]) + { + tile.type = 60; + } + } + } } From 823d942b4728b34dcb380572600452f8ddfde888 Mon Sep 17 00:00:00 2001 From: SGKoishi Date: Fri, 28 Apr 2023 15:55:14 -0700 Subject: [PATCH 026/135] Update changelog --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index c4a95d14..88a8c518 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -110,6 +110,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Allowed Flower Boots to place Ash Flowers on Ash Grass blocks. (@punchready) * Removed unnecessary range check that artifically shortened quick stack reach. (@boddyn, #2885, @bcat) * Improved the exploit protection in tile rect handling. (@punchready) +* Changed the use of `Player.active` to `TSPlayer.Active` for consistency. (@sgkoishi, #2939) ## TShock 5.1.3 * Added support for Terraria 1.4.4.9 via OTAPI 3.1.20. (@SignatureBeef) From 0dd15277e471a00e2a714bf1710698ecfc514fee Mon Sep 17 00:00:00 2001 From: punchready Date: Tue, 9 May 2023 12:41:54 +0200 Subject: [PATCH 027/135] Remove old STR handler --- TShockAPI/Bouncer.cs | 4 +- TShockAPI/Handlers/SendTileRectHandler.cs | 1858 +++++++++++------ .../Handlers/SendTileRectHandlerRefactor.cs | 1330 ------------ 3 files changed, 1226 insertions(+), 1966 deletions(-) delete mode 100644 TShockAPI/Handlers/SendTileRectHandlerRefactor.cs diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index c7c72f96..6ff7fd1a 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -35,7 +35,7 @@ namespace TShockAPI /// Bouncer is the TShock anti-hack and anti-cheat system. internal sealed class Bouncer { - internal Handlers.SendTileRectHandlerRefactor STSHandler { get; private set; } + internal Handlers.SendTileRectHandler STSHandler { get; private set; } internal Handlers.NetModules.NetModulePacketHandler NetModuleHandler { get; private set; } internal Handlers.EmojiHandler EmojiHandler { get; private set; } internal Handlers.IllegalPerSe.EmojiPlayerMismatch EmojiPlayerMismatch { get; private set; } @@ -83,7 +83,7 @@ namespace TShockAPI /// A new Bouncer. internal Bouncer() { - STSHandler = new Handlers.SendTileRectHandlerRefactor(); + STSHandler = new Handlers.SendTileRectHandler(); GetDataHandlers.SendTileRect += STSHandler.OnReceive; NetModuleHandler = new Handlers.NetModules.NetModulePacketHandler(); diff --git a/TShockAPI/Handlers/SendTileRectHandler.cs b/TShockAPI/Handlers/SendTileRectHandler.cs index 80d5a47c..64d9d8a7 100644 --- a/TShockAPI/Handlers/SendTileRectHandler.cs +++ b/TShockAPI/Handlers/SendTileRectHandler.cs @@ -1,26 +1,644 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Generic; +using System.IO; using Terraria; -using Terraria.DataStructures; -using Terraria.GameContent.Tile_Entities; using Terraria.ID; -using Terraria.ObjectData; using TShockAPI.Net; namespace TShockAPI.Handlers { /// - /// Provides processors for handling Tile Rect packets + /// Provides processors for handling tile rect packets. + /// This required many hours of reverse engineering work, and is kindly provided to TShock for free by @punchready. /// - public class SendTileRectHandler : IPacketHandler + public sealed class SendTileRectHandler : IPacketHandler { /// - /// Maps plant tile types to their valid grass ground tiles when using flower boots + /// Represents a tile rectangle sent through the packet. /// - private static readonly Dictionary> FlowerBootPlantToGrassMap = new Dictionary> + private sealed class TileRect + { + private readonly NetTile[,] _tiles; + public readonly int X; + public readonly int Y; + public readonly int Width; + public readonly int Height; + + /// + /// Accesses the tiles contained in this rect. + /// + /// The X coordinate within the rect. + /// The Y coordinate within the rect. + /// The tile at the given position within the rect. + public NetTile this[int x, int y] => _tiles[x, y]; + + /// + /// Constructs a new tile rect based on the given information. + /// + public TileRect(NetTile[,] tiles, int x, int y, int width, int height) + { + _tiles = tiles; + X = x; + Y = y; + Width = width; + Height = height; + } + + /// + /// Reads a tile rect from the given stream. + /// + /// The resulting tile rect. + public static TileRect Read(MemoryStream stream, int tileX, int tileY, int width, int height) + { + NetTile[,] tiles = new NetTile[width, height]; + for (int x = 0; x < width; x++) + { + for (int y = 0; y < height; y++) + { + tiles[x, y] = new NetTile(); + tiles[x, y].Unpack(stream); // explicit > implicit + } + } + return new TileRect(tiles, tileX, tileY, width, height); + } + } + + /// + /// Represents a common tile rect operation (Placement, State Change, Removal). + /// + private readonly struct TileRectMatch + { + private const short IGNORE_FRAME = -1; + + private enum MatchType + { + Placement, + StateChange, + Removal, + } + + private readonly int Width; + private readonly int Height; + + private readonly ushort TileType; + private readonly short MaxFrameX; + private readonly short MaxFrameY; + private readonly short FrameXStep; + private readonly short FrameYStep; + + private readonly MatchType Type; + + private TileRectMatch(MatchType type, int width, int height, ushort tileType, short maxFrameX, short maxFrameY, short frameXStep, short frameYStep) + { + Type = type; + Width = width; + Height = height; + TileType = tileType; + MaxFrameX = maxFrameX; + MaxFrameY = maxFrameY; + FrameXStep = frameXStep; + FrameYStep = frameYStep; + } + + /// + /// Creates a new placement operation. + /// + /// The width of the placement. + /// The height of the placement. + /// The tile type of the placement. + /// The maximum allowed frameX of the placement. + /// The maximum allowed frameY of the placement. + /// The step size in which frameX changes for this placement, or 1 if any value is allowed. + /// The step size in which frameX changes for this placement, or 1 if any value is allowed. + /// The resulting operation match. + public static TileRectMatch Placement(int width, int height, ushort tileType, short maxFrameX, short maxFrameY, short frameXStep, short frameYStep) + { + return new TileRectMatch(MatchType.Placement, width, height, tileType, maxFrameX, maxFrameY, frameXStep, frameYStep); + } + + /// + /// Creates a new state change operation. + /// + /// The width of the state change. + /// The height of the state change. + /// The target tile type of the state change. + /// The maximum allowed frameX of the state change. + /// The maximum allowed frameY of the state change. + /// The step size in which frameX changes for this placement, or 1 if any value is allowed. + /// The step size in which frameY changes for this placement, or 1 if any value is allowed. + /// The resulting operation match. + public static TileRectMatch StateChange(int width, int height, ushort tileType, short maxFrameX, short maxFrameY, short frameXStep, short frameYStep) + { + return new TileRectMatch(MatchType.StateChange, width, height, tileType, maxFrameX, maxFrameY, frameXStep, frameYStep); + } + + /// + /// Creates a new state change operation which only changes frameX. + /// + /// The width of the state change. + /// The height of the state change. + /// The target tile type of the state change. + /// The maximum allowed frameX of the state change. + /// The step size in which frameX changes for this placement, or 1 if any value is allowed. + /// The resulting operation match. + public static TileRectMatch StateChangeX(int width, int height, ushort tileType, short maxFrame, short frameStep) + { + return new TileRectMatch(MatchType.StateChange, width, height, tileType, maxFrame, IGNORE_FRAME, frameStep, 0); + } + + /// + /// Creates a new state change operation which only changes frameY. + /// + /// The width of the state change. + /// The height of the state change. + /// The target tile type of the state change. + /// The maximum allowed frameY of the state change. + /// The step size in which frameY changes for this placement, or 1 if any value is allowed. + /// The resulting operation match. + public static TileRectMatch StateChangeY(int width, int height, ushort tileType, short maxFrame, short frameStep) + { + return new TileRectMatch(MatchType.StateChange, width, height, tileType, IGNORE_FRAME, maxFrame, 0, frameStep); + } + + /// + /// Creates a new removal operation. + /// + /// The width of the removal. + /// The height of the removal. + /// The target tile type of the removal. + /// The resulting operation match. + public static TileRectMatch Removal(int width, int height, ushort tileType) + { + return new TileRectMatch(MatchType.Removal, width, height, tileType, 0, 0, 0, 0); + } + + /// + /// Determines whether the given tile rectangle matches this operation, and if so, applies it to the world. + /// + /// The player the operation originates from. + /// The tile rectangle of the operation. + /// , if the rect matches this operation and the changes have been applied, otherwise . + public bool Matches(TSPlayer player, TileRect rect) + { + if (rect.Width != Width || rect.Height != Height) + { + return false; + } + + for (int x = 0; x < rect.Width; x++) + { + for (int y = 0; y < rect.Height; y++) + { + NetTile tile = rect[x, y]; + if (Type is MatchType.Placement or MatchType.StateChange) + { + if (tile.Type != TileType) + { + return false; + } + } + if (Type is MatchType.Placement or MatchType.StateChange) + { + if (MaxFrameX != IGNORE_FRAME) + { + if (tile.FrameX < 0 || tile.FrameX > MaxFrameX || tile.FrameX % FrameXStep != 0) + { + return false; + } + } + if (MaxFrameY != IGNORE_FRAME) + { + if (tile.FrameY < 0 || tile.FrameY > MaxFrameY || tile.FrameY % FrameYStep != 0) + { + // this is the only tile type sent in a tile rect where the frame have a different pattern (56, 74, 92 instead of 54, 72, 90) + if (!(TileType == TileID.LunarMonolith && tile.FrameY % FrameYStep == 2)) + { + return false; + } + } + } + } + if (Type == MatchType.Removal) + { + if (tile.Active) + { + return false; + } + } + } + } + + for (int x = rect.X; x < rect.X + rect.Width; x++) + { + for (int y = rect.Y; y < rect.Y + rect.Height; y++) + { + if (!player.HasBuildPermission(x, y)) + { + // for simplicity, let's pretend that the edit was valid, but do not execute it + return true; + } + } + } + + switch (Type) + { + case MatchType.Placement: + { + return MatchPlacement(player, rect); + } + case MatchType.StateChange: + { + return MatchStateChange(player, rect); + } + case MatchType.Removal: + { + return MatchRemoval(player, rect); + } + } + + return false; + } + + private bool MatchPlacement(TSPlayer player, TileRect rect) + { + for (int x = rect.X; x < rect.Y + rect.Width; x++) + { + for (int y = rect.Y; y < rect.Y + rect.Height; y++) + { + if (Main.tile[x, y].active() && !(Main.tile[x, y].type != TileID.RollingCactus && (Main.tileCut[Main.tile[x, y].type] || TileID.Sets.BreakableWhenPlacing[Main.tile[x, y].type]))) + { + return false; + } + } + } + + // let's hope tile types never go out of short range (they use ushort in terraria's code) + if (TShock.TileBans.TileIsBanned((short)TileType, player)) + { + // for simplicity, let's pretend that the edit was valid, but do not execute it + return true; + } + + for (int x = 0; x < rect.Width; x++) + { + for (int y = 0; y < rect.Height; y++) + { + Main.tile[x + rect.X, y + rect.Y].active(active: true); + Main.tile[x + rect.X, y + rect.Y].type = rect[x, y].Type; + Main.tile[x + rect.X, y + rect.Y].frameX = rect[x, y].FrameX; + Main.tile[x + rect.X, y + rect.Y].frameY = rect[x, y].FrameY; + } + } + + return true; + } + + private bool MatchStateChange(TSPlayer player, TileRect rect) + { + for (int x = rect.X; x < rect.Y + rect.Width; x++) + { + for (int y = rect.Y; y < rect.Y + rect.Height; y++) + { + if (!Main.tile[x, y].active() || Main.tile[x, y].type != TileType) + { + return false; + } + } + } + + for (int x = 0; x < rect.Width; x++) + { + for (int y = 0; y < rect.Height; y++) + { + if (MaxFrameX != IGNORE_FRAME) + { + Main.tile[x + rect.X, y + rect.Y].frameX = rect[x, y].FrameX; + } + if (MaxFrameY != IGNORE_FRAME) + { + Main.tile[x + rect.X, y + rect.Y].frameY = rect[x, y].FrameY; + } + } + } + + return true; + } + + private bool MatchRemoval(TSPlayer player, TileRect rect) + { + for (int x = rect.X; x < rect.Y + rect.Width; x++) + { + for (int y = rect.Y; y < rect.Y + rect.Height; y++) + { + if (!Main.tile[x, y].active() || Main.tile[x, y].type != TileType) + { + return false; + } + } + } + + for (int x = 0; x < rect.Width; x++) + { + for (int y = 0; y < rect.Height; y++) + { + Main.tile[x + rect.X, y + rect.Y].active(active: false); + Main.tile[x + rect.X, y + rect.Y].frameX = -1; + Main.tile[x + rect.X, y + rect.Y].frameY = -1; + } + } + + return true; + } + } + + /// + /// Contains the complete list of valid tile rect operations the game currently performs. + /// + // The matches restrict the tile rects to only place one kind of tile, and only with the given maximum values and step sizes for frameX and frameY. This performs pretty much perfect checks on the data, allowing only valid placements. + // For TileID.MinecartTrack, the data is taken from `Minecart._trackSwitchOptions`, allowing any framing value in this array (currently 0-36). + // For TileID.Plants, it is taken from `ItemID.Sets.flowerPacketInfo[n].stylesOnPurity`, allowing every style multiplied by 18. + // The other operations are based on code analysis and manual observation. + private static readonly TileRectMatch[] Matches = new TileRectMatch[] + { + TileRectMatch.Placement(2, 3, TileID.TargetDummy, 54, 36, 18, 18), + TileRectMatch.Placement(3, 4, TileID.TeleportationPylon, 468, 54, 18, 18), + TileRectMatch.Placement(2, 3, TileID.DisplayDoll, 126, 36, 18, 18), + TileRectMatch.Placement(2, 3, TileID.HatRack, 90, 54, 18, 18), + TileRectMatch.Placement(2, 2, TileID.ItemFrame, 162, 18, 18, 18), + TileRectMatch.Placement(3, 3, TileID.WeaponsRack2, 90, 36, 18, 18), + TileRectMatch.Placement(1, 1, TileID.FoodPlatter, 18, 0, 18, 18), + TileRectMatch.Placement(1, 1, TileID.LogicSensor, 108, 0, 18, 18), + + TileRectMatch.StateChangeY(3, 2, TileID.Campfire, 54, 18), + TileRectMatch.StateChangeY(4, 3, TileID.Cannon, 468, 18), + TileRectMatch.StateChangeY(2, 2, TileID.ArrowSign, 270, 18), + TileRectMatch.StateChangeY(2, 2, TileID.PaintedArrowSign, 270, 18), + + TileRectMatch.StateChangeX(2, 2, TileID.MusicBoxes, 54, 18), + + TileRectMatch.StateChangeY(2, 3, TileID.LunarMonolith, 92, 18), + TileRectMatch.StateChangeY(2, 3, TileID.BloodMoonMonolith, 90, 18), + TileRectMatch.StateChangeY(2, 3, TileID.VoidMonolith, 90, 18), + TileRectMatch.StateChangeY(2, 3, TileID.EchoMonolith, 90, 18), + TileRectMatch.StateChangeY(2, 3, TileID.ShimmerMonolith, 144, 18), + TileRectMatch.StateChangeY(2, 4, TileID.WaterFountain, 126, 18), + + TileRectMatch.StateChangeX(1, 1, TileID.Candles, 18, 18), + TileRectMatch.StateChangeX(1, 1, TileID.PeaceCandle, 18, 18), + TileRectMatch.StateChangeX(1, 1, TileID.WaterCandle, 18, 18), + TileRectMatch.StateChangeX(1, 1, TileID.PlatinumCandle, 18, 18), + TileRectMatch.StateChangeX(1, 1, TileID.ShadowCandle, 18, 18), + + TileRectMatch.StateChange(1, 1, TileID.Traps, 90, 90, 18, 18), + + TileRectMatch.StateChangeX(1, 1, TileID.WirePipe, 36, 18), + TileRectMatch.StateChangeX(1, 1, TileID.ProjectilePressurePad, 66, 22), + TileRectMatch.StateChangeX(1, 1, TileID.Plants, 792, 18), + TileRectMatch.StateChangeX(1, 1, TileID.MinecartTrack, 36, 1), + + TileRectMatch.Removal(1, 2, TileID.Firework), + TileRectMatch.Removal(1, 1, TileID.LandMine), + }; + + + /// + /// Handles a packet receive event. + /// + public void OnReceive(object sender, GetDataHandlers.SendTileRectEventArgs args) + { + // this permission bypasses all checks for direct access to the world + if (args.Player.HasPermission(Permissions.allowclientsideworldedit)) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect accepted clientside world edit from {args.Player.Name}")); + + // use vanilla handling + args.Handled = false; + return; + } + + // this handler handles the entire logic of this packet + args.Handled = true; + + // as of 1.4 this is the biggest size the client will send in any case, determined by full code analysis + // see default matches above and special cases below + if (args.Width > 4 || args.Length > 4) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from size from {args.Player.Name}")); + + // definitely invalid; do not send any correcting data + return; + } + + // player throttled? + if (args.Player.IsBouncerThrottled()) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from throttle from {args.Player.Name}")); + + // send correcting data + args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + return; + } + + // player disabled? + if (args.Player.IsBeingDisabled()) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from being disabled from {args.Player.Name}")); + + // send correcting data + args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + return; + } + + // read the tile rectangle + TileRect rect = TileRect.Read(args.Data, args.TileX, args.TileY, args.Width, args.Length); + + // check if the positioning is valid + if (!IsRectPositionValid(args.Player, rect)) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from out of bounds / build permission from {args.Player.Name}")); + + // send nothing due to out of bounds + return; + } + + // a very special case, due to the clentaminator having a larger range than TSPlayer.IsInRange() allows + if (MatchesConversionSpread(args.Player, rect)) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}")); + + // send correcting data + args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + return; + } + + // check if the distance is valid + if (!IsRectDistanceValid(args.Player, rect)) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from out of range from {args.Player.Name}")); + + // send correcting data + args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + return; + } + + // a very special case, due to the flower seed check otherwise hijacking this + if (MatchesFlowerBoots(args.Player, rect)) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}")); + + // send correcting data + args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + return; + } + + // check if the rect matches any valid operation + foreach (TileRectMatch match in Matches) + { + if (match.Matches(args.Player, rect)) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}")); + + // send correcting data + args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + return; + } + } + + // a few special cases + if ( + MatchesConversionSpread(args.Player, rect) || + MatchesGrassMow(args.Player, rect) || + MatchesChristmasTree(args.Player, rect) + ) + { + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}")); + + // send correcting data + args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + return; + } + + TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from matches from {args.Player.Name}")); + + // send correcting data + args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + return; + } + + /// + /// Checks whether the tile rect is at a valid position for the given player. + /// + /// The player the operation originates from. + /// The tile rectangle of the operation. + /// , if the rect at a valid position, otherwise . + private static bool IsRectPositionValid(TSPlayer player, TileRect rect) + { + for (int x = 0; x < rect.Width; x++) + { + for (int y = 0; y < rect.Height; y++) + { + int realX = rect.X + x; + int realY = rect.Y + y; + + if (realX < 0 || realX >= Main.maxTilesX || realY < 0 || realY >= Main.maxTilesY) + { + return false; + } + } + } + + return true; + } + + /// + /// Checks whether the tile rect is at a valid distance to the given player. + /// + /// The player the operation originates from. + /// The tile rectangle of the operation. + /// , if the rect at a valid distance, otherwise . + private static bool IsRectDistanceValid(TSPlayer player, TileRect rect) + { + for (int x = 0; x < rect.Width; x++) + { + for (int y = 0; y < rect.Height; y++) + { + int realX = rect.X + x; + int realY = rect.Y + y; + + if (!player.IsInRange(realX, realY)) + { + return false; + } + } + } + + return true; + } + + + /// + /// Checks whether the tile rect is a valid conversion spread (Clentaminator, Powders, etc.). + /// + /// The player the operation originates from. + /// The tile rectangle of the operation. + /// , if the rect matches a conversion spread operation, otherwise . + private static bool MatchesConversionSpread(TSPlayer player, TileRect rect) + { + if (rect.Width != 1 || rect.Height != 1) + { + return false; + } + + ITile oldTile = Main.tile[rect.X, rect.Y]; + NetTile newTile = rect[0, 0]; + + WorldGenMock.SimulateConversionChange(rect.X, rect.Y, out HashSet validTiles, out HashSet validWalls); + + if (newTile.Type != oldTile.type && validTiles.Contains(newTile.Type)) + { + if (TShock.TileBans.TileIsBanned((short)newTile.Type, player)) + { + // for simplicity, let's pretend that the edit was valid, but do not execute it + return true; + } + else if (!player.HasBuildPermission(rect.X, rect.Y)) + { + // for simplicity, let's pretend that the edit was valid, but do not execute it + return true; + } + else + { + Main.tile[rect.X, rect.Y].type = newTile.Type; + Main.tile[rect.X, rect.Y].frameX = newTile.FrameX; + Main.tile[rect.X, rect.Y].frameY = newTile.FrameY; + + return true; + } + } + + if (newTile.Wall != oldTile.wall && validWalls.Contains(newTile.Wall)) + { + // wallbans when? + + if (!player.HasBuildPermission(rect.X, rect.Y)) + { + // for simplicity, let's pretend that the edit was valid, but do not execute it + return true; + } + else + { + Main.tile[rect.X, rect.Y].wall = newTile.Wall; + + return true; + } + } + + return false; + } + + + private static readonly Dictionary> PlantToGrassMap = new Dictionary> { { TileID.Plants, new HashSet() { @@ -44,27 +662,19 @@ namespace TShockAPI.Handlers } }, }; - /// - /// Maps plant tile types to a list of valid styles, which are used to determine the FrameX value of the plant tile - /// See `Player.DoBootsEffect_PlaceFlowersOnTile` - /// - private static readonly Dictionary> FlowerBootPlantToStyleMap = new Dictionary>() + private static readonly Dictionary> GrassToStyleMap = new Dictionary>() { { TileID.Plants, new HashSet() { - // The upper line is from a `NextFromList` call - // The lower line is from an additional switch which will add the listed options by adding a random value to a select set of styles 6, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 24, 27, 30, 33, 36, 39, 42, 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 44, } }, { TileID.HallowedPlants, new HashSet() { - // 5 is intentionally missing here because it is being skipped by vanilla 4, 6, } }, { TileID.HallowedPlants2, new HashSet() { - // 5 is intentionally missing here because it is being skipped by vanilla 2, 3, 4, 6, 7, } }, { TileID.JunglePlants2, new HashSet() @@ -78,662 +688,642 @@ namespace TShockAPI.Handlers }; /// - /// Item IDs that can spawn flowers while you walk + /// Checks whether the tile rect is a valid Flower Boots placement. /// - public static List FlowerBootItems = new List + /// The player the operation originates from. + /// The tile rectangle of the operation. + /// , if the rect matches a Flower Boots placement, otherwise . + private static bool MatchesFlowerBoots(TSPlayer player, TileRect rect) { - ItemID.FlowerBoots, - ItemID.FairyBoots - }; - - /// - /// Maps TileIDs to Tile Entity IDs. - /// Note: is empty at the time of writing, but entities are dynamically assigned their ID at initialize time - /// which is why we can use the _myEntityId field on each entity type - /// - public static Dictionary TileEntityIdToTileIdMap = new Dictionary - { - { TileID.TargetDummy, TETrainingDummy._myEntityID }, - { TileID.ItemFrame, TEItemFrame._myEntityID }, - { TileID.LogicSensor, TELogicSensor._myEntityID }, - { TileID.DisplayDoll, TEDisplayDoll._myEntityID }, - { TileID.WeaponsRack2, TEWeaponsRack._myEntityID }, - { TileID.HatRack, TEHatRack._myEntityID }, - { TileID.FoodPlatter, TEFoodPlatter._myEntityID }, - { TileID.TeleportationPylon, TETeleportationPylon._myEntityID } - }; - - /// - /// Invoked when a SendTileRect packet is received - /// - /// - /// - public void OnReceive(object sender, GetDataHandlers.SendTileRectEventArgs args) - { - // By default, we'll handle everything - args.Handled = true; - - if (ShouldSkipProcessing(args)) + if (rect.Width != 1 || rect.Height != 1) { - return; + return false; } - bool[,] processed = new bool[args.Width, args.Length]; - NetTile[,] tiles = ReadNetTilesFromStream(args.Data, args.Width, args.Length); - - Debug.VisualiseTileSetDiff(args.TileX, args.TileY, args.Width, args.Length, tiles); - - IterateTileRect(tiles, processed, args); - - // Uncommenting this function will send the same tile rect 10 blocks above you for visualisation. This will modify your world and overwrite existing blocks. - // Use in test worlds only. - //Debug.DisplayTileSetInGame(args.TileX, (short)(args.TileY - 10), args.Width, args.Length, tiles, args.Player); - - // If we are handling this event then we have updated the server's Main.tile state the way we want it. - // At this point we should send our state back to the client so they remain in sync with the server - if (args.Handled == true) + if (!player.TPlayer.flowerBoots) { - TSPlayer.All.SendTileRect(args.TileX, args.TileY, args.Width, args.Length); - TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from carbonara from {args.Player.Name}")); - } - } - - /// - /// Iterates over each tile in the tile rectangle and performs processing on individual tiles or multi-tile Tile Objects - /// - /// - /// - /// - internal void IterateTileRect(NetTile[,] tiles, bool[,] processed, GetDataHandlers.SendTileRectEventArgs args) - { - int tileX = args.TileX; - int tileY = args.TileY; - byte width = args.Width; - byte length = args.Length; - - for (int x = 0; x < width; x++) - { - for (int y = 0; y < length; y++) - { - // Do not process already processed tiles - if (processed[x, y]) - { - continue; - } - - int realX = tileX + x; - int realY = tileY + y; - - // Do not process tiles outside of the world boundaries - if ((realX < 0 || realX >= Main.maxTilesX) - || (realY < 0 || realY > Main.maxTilesY)) - { - processed[x, y] = true; - continue; - } - - // Do not process tiles that the player cannot update - if (!args.Player.HasBuildPermission(realX, realY) || - !args.Player.IsInRange(realX, realY)) - { - processed[x, y] = true; - continue; - } - - NetTile newTile = tiles[x, y]; - - TileObjectData data; - - // If the new tile has an associated TileObjectData object, we take the tile and the surrounding tiles that make up the tile object - // and process them as a tile object - if (newTile.Type < TileObjectData._data.Count && TileObjectData._data[newTile.Type] != null) - { - // Verify that the changes are actually valid conceptually - // Many tiles that are never placed or modified using this packet are valid TileObjectData entries, which is the main attack vector for most exploits using this packet - if (Main.tile[realX, realY].type == newTile.Type) - { - switch (newTile.Type) - { - // Some individual cases might still allow crashing exploits, as the actual framing is not being checked here - // Doing so requires hard-coding the individual valid framing values and is a lot of effort - case TileID.ProjectilePressurePad: - case TileID.WirePipe: - case TileID.Traps: - case TileID.Candles: - case TileID.PeaceCandle: - case TileID.WaterCandle: - case TileID.PlatinumCandle: - case TileID.Firework: - case TileID.WaterFountain: - case TileID.BloodMoonMonolith: - case TileID.VoidMonolith: - case TileID.LunarMonolith: - case TileID.MusicBoxes: - case TileID.ArrowSign: - case TileID.PaintedArrowSign: - case TileID.Cannon: - case TileID.Campfire: - case TileID.Plants: - case TileID.MinecartTrack: - case TileID.ChristmasTree: - case TileID.ShimmerMonolith: - { - // Allowed changes - - // Based on empirical tests, these should be some conservative upper bounds for framing values - if (newTile.FrameX != -1 || newTile.FrameY != -1) - { - if (newTile.FrameX is < 0 or > 1000) - { - processed[x, y] = true; - continue; - } - if (newTile.FrameY is < 0 or > 5000) - { - processed[x, y] = true; - continue; - } - } - } - break; - default: - { - processed[x, y] = true; - continue; - } - } - } - else - { - // Together with Flower Boots and Land Mine destruction, these are the only cases where a tile type is allowed to be modified - switch (newTile.Type) - { - case TileID.LogicSensor: - case TileID.FoodPlatter: - case TileID.WeaponsRack2: - case TileID.ItemFrame: - case TileID.HatRack: - case TileID.DisplayDoll: - case TileID.TeleportationPylon: - case TileID.TargetDummy: - { - // Allowed placements - - // Based on empirical tests, these should be some conservative upper bounds for framing values - if (newTile.FrameX != -1 || newTile.FrameY != -1) - { - if (newTile.FrameX is < 0 or > 1000) - { - processed[x, y] = true; - continue; - } - if (newTile.FrameY is < 0 or > 500) - { - processed[x, y] = true; - continue; - } - } - } - break; - default: - { - processed[x, y] = true; - continue; - } - } - } - - data = TileObjectData._data[newTile.Type]; - NetTile[,] newTiles; - int objWidth = data.Width; - int objHeight = data.Height; - - // Ensure the tile object fits inside the rect before processing it - if (!DoesTileObjectFitInTileRect(x, y, objWidth, objHeight, width, length, processed)) - { - continue; - } - - newTiles = new NetTile[objWidth, objHeight]; - - for (int i = 0; i < objWidth; i++) - { - for (int j = 0; j < objHeight; j++) - { - newTiles[i, j] = tiles[x + i, y + j]; - processed[x + i, y + j] = true; - } - } - ProcessTileObject(newTile.Type, realX, realY, objWidth, objHeight, newTiles, args); - continue; - } - - // If the new tile does not have an associated tile object, process it as an individual tile - ProcessSingleTile(realX, realY, newTile, width, length, args); - processed[x, y] = true; - } - } - } - - /// - /// Processes a tile object consisting of multiple tiles from the tile rect packet - /// - /// The tile type the object is comprised of - /// 2D array of NetTile containing the new tiles properties - /// X position at the top left of the object - /// Y position at the top left of the object - /// Width of the tile object - /// Height of the tile object - /// SendTileRectEventArgs containing event information - internal void ProcessTileObject(int tileType, int realX, int realY, int width, int height, NetTile[,] newTiles, GetDataHandlers.SendTileRectEventArgs args) - { - // As long as the player has permission to build, we should allow a tile object to be placed - // More in depth checks should take place in handlers for the Place Object (79), Update Tile Entity (86), and Place Tile Entity (87) packets - if (!args.Player.HasBuildPermissionForTileObject(realX, realY, width, height)) - { - TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from no permission for tile object from {args.Player.Name}")); - return; + return false; } - if (TShock.TileBans.TileIsBanned((short)tileType)) - { - TShock.Log.ConsoleDebug(GetString("Bouncer / SendTileRect rejected for banned tile")); - return; - } + ITile oldTile = Main.tile[rect.X, rect.Y]; + NetTile newTile = rect[0, 0]; - // Update all tiles in the tile object. These will be sent back to the player later - UpdateMultipleServerTileStates(realX, realY, width, height, newTiles); - - // Tile entities have special placements that we should let the game deal with - if (TileEntityIdToTileIdMap.ContainsKey(tileType)) - { - TileEntity.PlaceEntityNet(realX, realY, TileEntityIdToTileIdMap[tileType]); - } - } - - /// - /// Processes a single tile from the tile rect packet - /// - /// X position at the top left of the object - /// Y position at the top left of the object - /// The NetTile containing new tile properties - /// The width of the rectangle being processed - /// The length of the rectangle being processed - /// SendTileRectEventArgs containing event information - internal void ProcessSingleTile(int realX, int realY, NetTile newTile, byte rectWidth, byte rectLength, GetDataHandlers.SendTileRectEventArgs args) - { - // Some boots allow growing flowers on grass. This process sends a 1x1 tile rect to grow the flowers - // The rect size must be 1 and the player must have an accessory that allows growing flowers in order for this rect to be valid - if (rectWidth == 1 && rectLength == 1 && WorldGen.InWorld(realX, realY + 1) && args.Player.Accessories.Any(a => a != null && FlowerBootItems.Contains(a.type))) - { - ProcessFlowerBoots(realX, realY, newTile); - return; - } - - ITile tile = Main.tile[realX, realY]; - - // Triggering a single land mine tile - if (rectWidth == 1 && rectLength == 1 && tile.type == TileID.LandMine && !newTile.Active) - { - UpdateServerTileState(tile, newTile, TileDataType.Tile); - } - - // Hammering a single junction box - if (rectWidth == 1 && rectLength == 1 && tile.type == TileID.WirePipe) - { - UpdateServerTileState(tile, newTile, TileDataType.Tile); - } - - // Mowing a single grass tile: Grass -> GolfGrass OR HallowedGrass -> GolfGrassHallowed - if (rectWidth == 1 && rectLength == 1 && - ( - tile.type == TileID.Grass && newTile.Type == TileID.GolfGrass || - tile.type == TileID.HallowedGrass && newTile.Type == TileID.GolfGrassHallowed - )) - { - UpdateServerTileState(tile, newTile, TileDataType.Tile); - if (WorldGen.InWorld(realX, realY + 1) && TileID.Sets.IsVine[Main.tile[realX, realY + 1].type]) // vanilla does in theory break the vines on its own, but we can't trust that - { - WorldGen.KillTile(realX, realY + 1); - } - } - - // Conversion: only sends a 1x1 rect; has to happen AFTER grass mowing as it would otherwise also let mowing through, but without fixing vines - if (rectWidth == 1 && rectLength == 1) - { - ProcessConversionSpreads(tile, newTile); - } - - // All other single tile updates should not be processed. - } - - /// - /// Applies changes to a tile if a tile rect for flower-growing boots is valid - /// - /// The tile x position of the tile rect packet - this is where the flowers are intending to grow - /// The tile y position of the tile rect packet - this is where the flowers are intending to grow - /// The NetTile containing information about the flowers that are being grown - internal void ProcessFlowerBoots(int realX, int realY, NetTile newTile) - { - ITile tile = Main.tile[realX, realY]; - // Ensure that: - // - the placed plant is valid for the grass below - // - the target tile is empty - // - and the placed plant has valid framing (style * 18 = FrameX) if ( - FlowerBootPlantToGrassMap.TryGetValue(newTile.Type, out HashSet grassTiles) && - !tile.active() && - grassTiles.Contains(Main.tile[realX, realY + 1].type) && - FlowerBootPlantToStyleMap[newTile.Type].Contains((ushort)(newTile.FrameX / 18)) + PlantToGrassMap.TryGetValue(newTile.Type, out HashSet grassTiles) && + !oldTile.active() && grassTiles.Contains(Main.tile[rect.X, rect.Y + 1].type) && + GrassToStyleMap[newTile.Type].Contains((ushort)(newTile.FrameX / 18)) ) { - UpdateServerTileState(tile, newTile, TileDataType.Tile); - } - } - - // Moss and MossBrick are not used in conversion - private static List _convertibleTiles = typeof(TileID.Sets.Conversion) - .GetFields() - .ExceptBy(new[] { nameof(TileID.Sets.Conversion.Moss), nameof(TileID.Sets.Conversion.MossBrick) }, f => f.Name) - .Select(f => (bool[])f.GetValue(null)) - .ToList(); - // PureSand is only used in WorldGen.SpreadDesertWalls, which is server side - private static List _convertibleWalls = typeof(WallID.Sets.Conversion) - .GetFields() - .ExceptBy(new[] { nameof(WallID.Sets.Conversion.PureSand) }, f => f.Name) - .Select(f => (bool[])f.GetValue(null)) - .ToList(); - - /// - /// Updates a single tile on the server if it is a valid conversion from one tile or wall type to another (eg stone -> corrupt stone) - /// - /// The tile to update - /// The NetTile containing new tile properties - internal void ProcessConversionSpreads(ITile tile, NetTile newTile) - { - var allowTile = false; - if (Main.tileMoss[tile.type] && TileID.Sets.Conversion.Stone[newTile.Type]) - { - allowTile = true; - } - else if ((Main.tileMoss[tile.type] || TileID.Sets.Conversion.Stone[tile.type] || TileID.Sets.Conversion.Ice[tile.type] || TileID.Sets.Conversion.Sandstone[tile.type]) && - (newTile.Type == TileID.Sandstone || newTile.Type == TileID.IceBlock)) - { - // ProjectileID.SandSpray and ProjectileID.SnowSpray - allowTile = true; - } - else - { - foreach (var tileType in _convertibleTiles) + if (TShock.TileBans.TileIsBanned((short)newTile.Type, player)) { - if (tileType[tile.type] && tileType[newTile.Type]) - { - allowTile = true; - break; - } + // for simplicity, let's pretend that the edit was valid, but do not execute it + return true; } - } - if (allowTile) - { - TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect processing a tile conversion update - [{tile.type}] -> [{newTile.Type}]")); - UpdateServerTileState(tile, newTile, TileDataType.Tile); - } - - foreach (var wallType in _convertibleWalls) - { - if (wallType[tile.wall] && wallType[newTile.Wall]) + if (!player.HasBuildPermission(rect.X, rect.Y)) { - TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect processing a wall conversion update - [{tile.wall}] -> [{newTile.Wall}]")); - UpdateServerTileState(tile, newTile, TileDataType.Wall); - break; + // for simplicity, let's pretend that the edit was valid, but do not execute it + return true; } - } - } - /// - /// Updates a single tile's world state with a set of changes from the networked tile state - /// - /// The tile to update - /// The NetTile containing the change - /// The type of data to merge into world state - public static void UpdateServerTileState(ITile tile, NetTile newTile, TileDataType updateType) - { - //This logic (updateType & TDT.Tile) != 0 is the way Terraria does it (see: Tile.cs/Clear(TileDataType)) - //& is not a typo - we're performing a binary AND test to see if a given flag is set. + Main.tile[rect.X, rect.Y].active(active: true); + Main.tile[rect.X, rect.Y].type = newTile.Type; + Main.tile[rect.X, rect.Y].frameX = newTile.FrameX; + Main.tile[rect.X, rect.Y].frameY = 0; - if ((updateType & TileDataType.Tile) != 0) - { - tile.active(newTile.Active); - tile.type = newTile.Type; - - if (newTile.FrameImportant) - { - tile.frameX = newTile.FrameX; - tile.frameY = newTile.FrameY; - } - else if (tile.type != newTile.Type || !tile.active()) - { - //This is vanilla logic - if the tile changed types (or wasn't active) the frame values might not be valid - so we reset them to -1. - tile.frameX = -1; - tile.frameY = -1; - } - } - - if ((updateType & TileDataType.Wall) != 0) - { - tile.wall = newTile.Wall; - } - - if ((updateType & TileDataType.TilePaint) != 0) - { - tile.color(newTile.TileColor); - tile.fullbrightBlock(newTile.FullbrightBlock); - tile.invisibleBlock(newTile.InvisibleBlock); - } - - if ((updateType & TileDataType.WallPaint) != 0) - { - tile.wallColor(newTile.WallColor); - tile.fullbrightWall(newTile.FullbrightWall); - tile.invisibleWall(newTile.InvisibleWall); - } - - if ((updateType & TileDataType.Liquid) != 0) - { - tile.liquid = newTile.Liquid; - tile.liquidType(newTile.LiquidType); - } - - if ((updateType & TileDataType.Slope) != 0) - { - tile.halfBrick(newTile.IsHalf); - tile.slope(newTile.Slope); - } - - if ((updateType & TileDataType.Wiring) != 0) - { - tile.wire(newTile.Wire); - tile.wire2(newTile.Wire2); - tile.wire3(newTile.Wire3); - tile.wire4(newTile.Wire4); - } - - if ((updateType & TileDataType.Actuator) != 0) - { - tile.actuator(newTile.IsActuator); - tile.inActive(newTile.Inactive); - } - } - - /// - /// Performs on multiple tiles - /// - /// - /// - /// - /// - /// - public static void UpdateMultipleServerTileStates(int x, int y, int width, int height, NetTile[,] newTiles) - { - for (int i = 0; i < width; i++) - { - for (int j = 0; j < height; j++) - { - UpdateServerTileState(Main.tile[x + i, y + j], newTiles[i, j], TileDataType.Tile); - } - } - } - - /// - /// Reads a set of NetTiles from a memory stream - /// - /// - /// - /// - /// - static NetTile[,] ReadNetTilesFromStream(System.IO.MemoryStream stream, byte width, byte length) - { - NetTile[,] tiles = new NetTile[width, length]; - for (int x = 0; x < width; x++) - { - for (int y = 0; y < length; y++) - { - tiles[x, y] = new NetTile(stream); - } - } - - return tiles; - } - - /// - /// Determines whether or not the tile rect should be immediately accepted or rejected - /// - /// - /// - static bool ShouldSkipProcessing(GetDataHandlers.SendTileRectEventArgs args) - { - if (args.Player.HasPermission(Permissions.allowclientsideworldedit)) - { - TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect accepted clientside world edit from {args.Player.Name}")); - args.Handled = false; - return true; - } - - if (args.Width > 4 || args.Length > 4) // as of 1.4.3.6 this is the biggest size the client will send in any case - { - TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from non-vanilla tilemod from {args.Player.Name}")); - return true; - } - - if (args.Player.IsBouncerThrottled()) - { - TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from throttle from {args.Player.Name}")); - args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); - return true; - } - - if (args.Player.IsBeingDisabled()) - { - TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from being disabled from {args.Player.Name}")); - args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); return true; } return false; } - /// - /// Checks if a tile object fits inside the dimensions of a tile rectangle - /// - /// - /// - /// - /// - /// - /// - /// - /// - static bool DoesTileObjectFitInTileRect(int x, int y, int width, int height, short rectWidth, short rectLength, bool[,] processed) - { - if (x + width > rectWidth || y + height > rectLength) - { - // This is ugly, but we want to mark all these tiles as processed so that we're not hitting this check multiple times for one dodgy tile object - for (int i = x; i < rectWidth; i++) - { - for (int j = y; j < rectLength; j++) - { - processed[i, j] = true; - } - } - TShock.Log.ConsoleDebug(GetString("Bouncer / SendTileRectHandler - rejected tile object because object dimensions fall outside the tile rect (excessive size)")); + private static readonly Dictionary GrassToMowedMap = new Dictionary + { + { TileID.Grass, TileID.GolfGrass }, + { TileID.HallowedGrass, TileID.GolfGrassHallowed }, + }; + + /// + /// Checks whether the tile rect is a valid grass mow. + /// + /// The player the operation originates from. + /// The tile rectangle of the operation. + /// , if the rect matches a grass mowing operation, otherwise . + private static bool MatchesGrassMow(TSPlayer player, TileRect rect) + { + if (rect.Width != 1 || rect.Height != 1) + { return false; } - return true; + ITile oldTile = Main.tile[rect.X, rect.Y]; + NetTile newTile = rect[0, 0]; + + if (GrassToMowedMap.TryGetValue(oldTile.type, out ushort mowed) && newTile.Type == mowed) + { + if (TShock.TileBans.TileIsBanned((short)newTile.Type, player)) + { + // for simplicity, let's pretend that the edit was valid, but do not execute it + return true; + } + + if (!player.HasBuildPermission(rect.X, rect.Y)) + { + // for simplicity, let's pretend that the edit was valid, but do not execute it + return true; + } + + Main.tile[rect.X, rect.Y].type = newTile.Type; + if (!newTile.FrameImportant) + { + Main.tile[rect.X, rect.Y].frameX = -1; + Main.tile[rect.X, rect.Y].frameY = -1; + } + + // prevent a common crash when the game checks all vines in an unlimited horizontal length + if (TileID.Sets.IsVine[Main.tile[rect.X, rect.Y + 1].type]) + { + WorldGen.KillTile(rect.X, rect.Y + 1); + } + + return true; + } + + return false; } - class Debug + + /// + /// Checks whether the tile rect is a valid christmas tree modification. + /// This also required significant reverse engineering effort. + /// + /// The player the operation originates from. + /// The tile rectangle of the operation. + /// , if the rect matches a christmas tree operation, otherwise . + private static bool MatchesChristmasTree(TSPlayer player, TileRect rect) { - /// - /// Displays the difference in IDs between existing tiles and a set of NetTiles to the console - /// - /// X position at the top left of the rect - /// Y position at the top left of the rect - /// Width of the NetTile set - /// Height of the NetTile set - /// New tiles to be visualised - public static void VisualiseTileSetDiff(int tileX, int tileY, int width, int height, NetTile[,] newTiles) + if (rect.Width != 1 || rect.Height != 1) { - if (TShock.Config.Settings.DebugLogs) + return false; + } + + ITile oldTile = Main.tile[rect.X, rect.Y]; + NetTile newTile = rect[0, 0]; + + if (oldTile.type == TileID.ChristmasTree && newTile.Type == TileID.ChristmasTree) + { + if (newTile.FrameX != 10) { - char pad = '0'; - for (int y = 0; y < height; y++) - { - int realY = y + tileY; - for (int x = 0; x < width; x++) - { - int realX = x + tileX; - ushort type = Main.tile[realX, realY].type; - string type2 = type.ToString(); - Console.Write((type2.ToString()).PadLeft(3, pad) + (Main.tile[realX, realY].active() ? "a" : "-") + " "); - } - Console.Write(" -> "); - for (int x = 0; x < width; x++) - { - int realX = x + tileX; - ushort type = newTiles[x, y].Type; - string type2 = type.ToString(); - Console.Write((type2.ToString()).PadLeft(3, pad) + (newTiles[x, y].Active ? "a" : "-") + " "); - } - Console.Write("\n"); - } + return false; + } + + int obj_0 = (newTile.FrameY & 0b0000000000000111); + int obj_1 = (newTile.FrameY & 0b0000000000111000) >> 3; + int obj_2 = (newTile.FrameY & 0b0000001111000000) >> 6; + int obj_3 = (newTile.FrameY & 0b0011110000000000) >> 10; + int obj_x = (newTile.FrameY & 0b1100000000000000) >> 14; + + if (obj_x != 0) + { + return false; + } + + if (obj_0 is < 0 or > 4 || obj_1 is < 0 or > 6 || obj_2 is < 0 or > 11 || obj_3 is < 0 or > 11) + { + return false; + } + + if (!player.HasBuildPermission(rect.X, rect.Y)) + { + // for simplicity, let's pretend that the edit was valid, but do not execute it + return true; + } + + Main.tile[rect.X, rect.Y].frameY = newTile.FrameY; + + return true; + } + + return false; + } + } + + /// + /// This helper class allows simulating a `WorldGen.Convert` call and retrieving all valid changes for a given tile. + /// + internal static class WorldGenMock + { + /// + /// This is a mock tile which collects all possible changes the `WorldGen.Convert` code could make in its property setters. + /// + private sealed class MockTile + { + private readonly HashSet _setTypes; + private readonly HashSet _setWalls; + + private ushort _type; + private ushort _wall; + + public MockTile(ushort type, ushort wall, HashSet setTypes, HashSet setWalls) + { + _setTypes = setTypes; + _setWalls = setWalls; + _type = type; + _wall = wall; + } + +#pragma warning disable IDE1006 + + public ushort type + { + get => _type; + set + { + _setTypes.Add(value); + _type = value; } } - /// - /// Sends a tile rect at the given (tileX, tileY) coordinate, using the given set of NetTiles information to update the tile rect - /// - /// X position at the top left of the rect - /// Y position at the top left of the rect - /// Width of the NetTile set - /// Height of the NetTile set - /// New tiles to place in the rect - /// Player to send the debug display to - public static void DisplayTileSetInGame(short tileX, short tileY, byte width, byte height, NetTile[,] newTiles, TSPlayer player) + public ushort wall { - for (int x = 0; x < width; x++) + get => _wall; + set { - for (int y = 0; y < height; y++) - { - UpdateServerTileState(Main.tile[tileX + x, tileY + y], newTiles[x, y], TileDataType.All); - } - //Add a line of dirt blocks at the bottom for safety - UpdateServerTileState(Main.tile[tileX + x, tileY + height], new NetTile { Active = true, Type = 0 }, TileDataType.All); + _setWalls.Add(value); + _wall = value; } + } - player.SendTileRect(tileX, tileY, width, height); +#pragma warning restore IDE1006 + } + + /// + /// Simulates what would happen if `WorldGen.Convert` was called on the given coordinates and returns two sets with the possible tile type and wall types that the conversion could change the tile to. + /// + public static void SimulateConversionChange(int x, int y, out HashSet validTiles, out HashSet validWalls) + { + validTiles = new HashSet(); + validWalls = new HashSet(); + + // all the conversion types used in the code, most apparent in Projectile ai 31 + foreach (int conversionType in new int[] { 0, 1, 2, 3, 4, 5, 6, 7 }) + { + MockTile mock = new(Main.tile[x, y].type, Main.tile[x, y].wall, validTiles, validWalls); + Convert(mock, x, y, conversionType); + } + } + + /* + * This is a copy of the `WorldGen.Convert` method with the following precise changes: + * - Added a `MockTile tile` parameter + * - Changed the `i` and `j` parameters to `k` and `l` + * - Removed the size parameter + * - Removed the area loop and `Tile tile = Main.tile[k, l]` access in favor of using the tile parameter + * - Removed all calls to `WorldGen.SquareWallFrame`, `NetMessage.SendTileSquare`, `WorldGen.TryKillingTreesAboveIfTheyWouldBecomeInvalid` + * - Changed all `continue` statements to `break` statements + * - Removed the ifs checking the bounds of the tile and wall types + * - Removed branches that would call `WorldGen.KillTile` + * - Changed branches depending on randomness to instead set the property to both values after one another + * + * This overall leads to a method that can be called on a MockTile and real-world coordinates and will spit out the proper conversion changes into the MockTile. + */ + + private static void Convert(MockTile tile, int k, int l, int conversionType) + { + int type = tile.type; + int wall = tile.wall; + switch (conversionType) + { + case 4: + if (WallID.Sets.Conversion.Grass[wall] && wall != 81) + { + tile.wall = 81; + } + else if (WallID.Sets.Conversion.Stone[wall] && wall != 83) + { + tile.wall = 83; + } + else if (WallID.Sets.Conversion.HardenedSand[wall] && wall != 218) + { + tile.wall = 218; + } + else if (WallID.Sets.Conversion.Sandstone[wall] && wall != 221) + { + tile.wall = 221; + } + else if (WallID.Sets.Conversion.NewWall1[wall] && wall != 192) + { + tile.wall = 192; + } + else if (WallID.Sets.Conversion.NewWall2[wall] && wall != 193) + { + tile.wall = 193; + } + else if (WallID.Sets.Conversion.NewWall3[wall] && wall != 194) + { + tile.wall = 194; + } + else if (WallID.Sets.Conversion.NewWall4[wall] && wall != 195) + { + tile.wall = 195; + } + if ((Main.tileMoss[type] || TileID.Sets.Conversion.Stone[type]) && type != 203) + { + tile.type = 203; + } + else if (TileID.Sets.Conversion.JungleGrass[type] && type != 662) + { + tile.type = 662; + } + else if (TileID.Sets.Conversion.Grass[type] && type != 199) + { + tile.type = 199; + } + else if (TileID.Sets.Conversion.Ice[type] && type != 200) + { + tile.type = 200; + } + else if (TileID.Sets.Conversion.Sand[type] && type != 234) + { + tile.type = 234; + } + else if (TileID.Sets.Conversion.HardenedSand[type] && type != 399) + { + tile.type = 399; + } + else if (TileID.Sets.Conversion.Sandstone[type] && type != 401) + { + tile.type = 401; + } + else if (TileID.Sets.Conversion.Thorn[type] && type != 352) + { + tile.type = 352; + } + break; + case 2: + if (WallID.Sets.Conversion.Grass[wall] && wall != 70) + { + tile.wall = 70; + } + else if (WallID.Sets.Conversion.Stone[wall] && wall != 28) + { + tile.wall = 28; + } + else if (WallID.Sets.Conversion.HardenedSand[wall] && wall != 219) + { + tile.wall = 219; + } + else if (WallID.Sets.Conversion.Sandstone[wall] && wall != 222) + { + tile.wall = 222; + } + else if (WallID.Sets.Conversion.NewWall1[wall] && wall != 200) + { + tile.wall = 200; + } + else if (WallID.Sets.Conversion.NewWall2[wall] && wall != 201) + { + tile.wall = 201; + } + else if (WallID.Sets.Conversion.NewWall3[wall] && wall != 202) + { + tile.wall = 202; + } + else if (WallID.Sets.Conversion.NewWall4[wall] && wall != 203) + { + tile.wall = 203; + } + if ((Main.tileMoss[type] || TileID.Sets.Conversion.Stone[type]) && type != 117) + { + tile.type = 117; + } + else if (TileID.Sets.Conversion.GolfGrass[type] && type != 492) + { + tile.type = 492; + } + else if (TileID.Sets.Conversion.Grass[type] && type != 109 && type != 492) + { + tile.type = 109; + } + else if (TileID.Sets.Conversion.Ice[type] && type != 164) + { + tile.type = 164; + } + else if (TileID.Sets.Conversion.Sand[type] && type != 116) + { + tile.type = 116; + } + else if (TileID.Sets.Conversion.HardenedSand[type] && type != 402) + { + tile.type = 402; + } + else if (TileID.Sets.Conversion.Sandstone[type] && type != 403) + { + tile.type = 403; + } + if (type == 59 && (Main.tile[k - 1, l].type == 109 || Main.tile[k + 1, l].type == 109 || Main.tile[k, l - 1].type == 109 || Main.tile[k, l + 1].type == 109)) + { + tile.type = 0; + } + break; + case 1: + if (WallID.Sets.Conversion.Grass[wall] && wall != 69) + { + tile.wall = 69; + } + else if (TileID.Sets.Conversion.JungleGrass[type] && type != 661) + { + tile.type = 661; + } + else if (WallID.Sets.Conversion.Stone[wall] && wall != 3) + { + tile.wall = 3; + } + else if (WallID.Sets.Conversion.HardenedSand[wall] && wall != 217) + { + tile.wall = 217; + } + else if (WallID.Sets.Conversion.Sandstone[wall] && wall != 220) + { + tile.wall = 220; + } + else if (WallID.Sets.Conversion.NewWall1[wall] && wall != 188) + { + tile.wall = 188; + } + else if (WallID.Sets.Conversion.NewWall2[wall] && wall != 189) + { + tile.wall = 189; + } + else if (WallID.Sets.Conversion.NewWall3[wall] && wall != 190) + { + tile.wall = 190; + } + else if (WallID.Sets.Conversion.NewWall4[wall] && wall != 191) + { + tile.wall = 191; + } + if ((Main.tileMoss[type] || TileID.Sets.Conversion.Stone[type]) && type != 25) + { + tile.type = 25; + } + else if (TileID.Sets.Conversion.Grass[type] && type != 23) + { + tile.type = 23; + } + else if (TileID.Sets.Conversion.Ice[type] && type != 163) + { + tile.type = 163; + } + else if (TileID.Sets.Conversion.Sand[type] && type != 112) + { + tile.type = 112; + } + else if (TileID.Sets.Conversion.HardenedSand[type] && type != 398) + { + tile.type = 398; + } + else if (TileID.Sets.Conversion.Sandstone[type] && type != 400) + { + tile.type = 400; + } + else if (TileID.Sets.Conversion.Thorn[type] && type != 32) + { + tile.type = 32; + } + break; + case 3: + if (WallID.Sets.CanBeConvertedToGlowingMushroom[wall]) + { + tile.wall = 80; + } + if (tile.type == 60) + { + tile.type = 70; + } + break; + case 5: + if ((WallID.Sets.Conversion.Stone[wall] || WallID.Sets.Conversion.NewWall1[wall] || WallID.Sets.Conversion.NewWall2[wall] || WallID.Sets.Conversion.NewWall3[wall] || WallID.Sets.Conversion.NewWall4[wall] || WallID.Sets.Conversion.Ice[wall] || WallID.Sets.Conversion.Sandstone[wall]) && wall != 187) + { + tile.wall = 187; + } + else if ((WallID.Sets.Conversion.HardenedSand[wall] || WallID.Sets.Conversion.Dirt[wall] || WallID.Sets.Conversion.Snow[wall]) && wall != 216) + { + tile.wall = 216; + } + if ((TileID.Sets.Conversion.Grass[type] || TileID.Sets.Conversion.Sand[type] || TileID.Sets.Conversion.Snow[type] || TileID.Sets.Conversion.Dirt[type]) && type != 53) + { + int num = 53; + if (WorldGen.BlockBelowMakesSandConvertIntoHardenedSand(k, l)) + { + num = 397; + } + tile.type = (ushort)num; + } + else if (TileID.Sets.Conversion.HardenedSand[type] && type != 397) + { + tile.type = 397; + } + else if ((Main.tileMoss[type] || TileID.Sets.Conversion.Stone[type] || TileID.Sets.Conversion.Ice[type] || TileID.Sets.Conversion.Sandstone[type]) && type != 396) + { + tile.type = 396; + } + break; + case 6: + if ((WallID.Sets.Conversion.Stone[wall] || WallID.Sets.Conversion.NewWall1[wall] || WallID.Sets.Conversion.NewWall2[wall] || WallID.Sets.Conversion.NewWall3[wall] || WallID.Sets.Conversion.NewWall4[wall] || WallID.Sets.Conversion.Ice[wall] || WallID.Sets.Conversion.Sandstone[wall]) && wall != 71) + { + tile.wall = 71; + } + else if ((WallID.Sets.Conversion.HardenedSand[wall] || WallID.Sets.Conversion.Dirt[wall] || WallID.Sets.Conversion.Snow[wall]) && wall != 40) + { + tile.wall = 40; + } + if ((TileID.Sets.Conversion.Grass[type] || TileID.Sets.Conversion.Sand[type] || TileID.Sets.Conversion.HardenedSand[type] || TileID.Sets.Conversion.Snow[type] || TileID.Sets.Conversion.Dirt[type]) && type != 147) + { + tile.type = 147; + } + else if ((Main.tileMoss[type] || TileID.Sets.Conversion.Stone[type] || TileID.Sets.Conversion.Ice[type] || TileID.Sets.Conversion.Sandstone[type]) && type != 161) + { + tile.type = 161; + } + break; + case 7: + if ((WallID.Sets.Conversion.Stone[wall] || WallID.Sets.Conversion.Ice[wall] || WallID.Sets.Conversion.Sandstone[wall]) && wall != 1) + { + tile.wall = 1; + } + else if ((WallID.Sets.Conversion.HardenedSand[wall] || WallID.Sets.Conversion.Snow[wall] || WallID.Sets.Conversion.Dirt[wall]) && wall != 2) + { + tile.wall = 2; + } + else if (WallID.Sets.Conversion.NewWall1[wall] && wall != 196) + { + tile.wall = 196; + } + else if (WallID.Sets.Conversion.NewWall2[wall] && wall != 197) + { + tile.wall = 197; + } + else if (WallID.Sets.Conversion.NewWall3[wall] && wall != 198) + { + tile.wall = 198; + } + else if (WallID.Sets.Conversion.NewWall4[wall] && wall != 199) + { + tile.wall = 199; + } + if ((TileID.Sets.Conversion.Stone[type] || TileID.Sets.Conversion.Ice[type] || TileID.Sets.Conversion.Sandstone[type]) && type != 1) + { + tile.type = 1; + } + else if (TileID.Sets.Conversion.GolfGrass[type] && type != 477) + { + tile.type = 477; + } + else if (TileID.Sets.Conversion.Grass[type] && type != 2 && type != 477) + { + tile.type = 2; + } + else if ((TileID.Sets.Conversion.Sand[type] || TileID.Sets.Conversion.HardenedSand[type] || TileID.Sets.Conversion.Snow[type] || TileID.Sets.Conversion.Dirt[type]) && type != 0) + { + int num2 = 0; + if (WorldGen.TileIsExposedToAir(k, l)) + { + num2 = 2; + } + tile.type = (ushort)num2; + } + break; + } + if (tile.wall == 69 || tile.wall == 70 || tile.wall == 81) + { + if (l < Main.worldSurface) + { + tile.wall = 65; + tile.wall = 63; + } + else + { + tile.wall = 64; + } + } + else if (WallID.Sets.Conversion.Stone[wall] && wall != 1 && wall != 262 && wall != 274 && wall != 61 && wall != 185) + { + tile.wall = 1; + } + else if (WallID.Sets.Conversion.Stone[wall] && wall == 262) + { + tile.wall = 61; + } + else if (WallID.Sets.Conversion.Stone[wall] && wall == 274) + { + tile.wall = 185; + } + if (WallID.Sets.Conversion.NewWall1[wall] && wall != 212) + { + tile.wall = 212; + } + else if (WallID.Sets.Conversion.NewWall2[wall] && wall != 213) + { + tile.wall = 213; + } + else if (WallID.Sets.Conversion.NewWall3[wall] && wall != 214) + { + tile.wall = 214; + } + else if (WallID.Sets.Conversion.NewWall4[wall] && wall != 215) + { + tile.wall = 215; + } + else if (tile.wall == 80) + { + tile.wall = 15; + tile.wall = 64; + } + else if (WallID.Sets.Conversion.HardenedSand[wall] && wall != 216) + { + tile.wall = 216; + } + else if (WallID.Sets.Conversion.Sandstone[wall] && wall != 187) + { + tile.wall = 187; + } + if (tile.type == 492) + { + tile.type = 477; + } + else if (TileID.Sets.Conversion.JungleGrass[type] && type != 60) + { + tile.type = 60; + } + else if (TileID.Sets.Conversion.Grass[type] && type != 2 && type != 477) + { + tile.type = 2; + } + else if (TileID.Sets.Conversion.Stone[type] && type != 1) + { + tile.type = 1; + } + else if (TileID.Sets.Conversion.Sand[type] && type != 53) + { + tile.type = 53; + } + else if (TileID.Sets.Conversion.HardenedSand[type] && type != 397) + { + tile.type = 397; + } + else if (TileID.Sets.Conversion.Sandstone[type] && type != 396) + { + tile.type = 396; + } + else if (TileID.Sets.Conversion.Ice[type] && type != 161) + { + tile.type = 161; + } + else if (TileID.Sets.Conversion.MushroomGrass[type]) + { + tile.type = 60; } } } diff --git a/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs b/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs deleted file mode 100644 index 34f8a495..00000000 --- a/TShockAPI/Handlers/SendTileRectHandlerRefactor.cs +++ /dev/null @@ -1,1330 +0,0 @@ -using System.Collections.Generic; -using System.IO; - -using Terraria; -using Terraria.ID; - -using TShockAPI.Net; - -namespace TShockAPI.Handlers -{ - /// - /// Provides processors for handling tile rect packets. - /// This required many hours of reverse engineering work, and is kindly provided to TShock for free by @punchready. - /// - public sealed class SendTileRectHandlerRefactor : IPacketHandler - { - /// - /// Represents a tile rectangle sent through the packet. - /// - private sealed class TileRect - { - private readonly NetTile[,] _tiles; - public readonly int X; - public readonly int Y; - public readonly int Width; - public readonly int Height; - - /// - /// Accesses the tiles contained in this rect. - /// - /// The X coordinate within the rect. - /// The Y coordinate within the rect. - /// The tile at the given position within the rect. - public NetTile this[int x, int y] => _tiles[x, y]; - - /// - /// Constructs a new tile rect based on the given information. - /// - public TileRect(NetTile[,] tiles, int x, int y, int width, int height) - { - _tiles = tiles; - X = x; - Y = y; - Width = width; - Height = height; - } - - /// - /// Reads a tile rect from the given stream. - /// - /// The resulting tile rect. - public static TileRect Read(MemoryStream stream, int tileX, int tileY, int width, int height) - { - NetTile[,] tiles = new NetTile[width, height]; - for (int x = 0; x < width; x++) - { - for (int y = 0; y < height; y++) - { - tiles[x, y] = new NetTile(); - tiles[x, y].Unpack(stream); // explicit > implicit - } - } - return new TileRect(tiles, tileX, tileY, width, height); - } - } - - /// - /// Represents a common tile rect operation (Placement, State Change, Removal). - /// - private readonly struct TileRectMatch - { - private const short IGNORE_FRAME = -1; - - private enum MatchType - { - Placement, - StateChange, - Removal, - } - - private readonly int Width; - private readonly int Height; - - private readonly ushort TileType; - private readonly short MaxFrameX; - private readonly short MaxFrameY; - private readonly short FrameXStep; - private readonly short FrameYStep; - - private readonly MatchType Type; - - private TileRectMatch(MatchType type, int width, int height, ushort tileType, short maxFrameX, short maxFrameY, short frameXStep, short frameYStep) - { - Type = type; - Width = width; - Height = height; - TileType = tileType; - MaxFrameX = maxFrameX; - MaxFrameY = maxFrameY; - FrameXStep = frameXStep; - FrameYStep = frameYStep; - } - - /// - /// Creates a new placement operation. - /// - /// The width of the placement. - /// The height of the placement. - /// The tile type of the placement. - /// The maximum allowed frameX of the placement. - /// The maximum allowed frameY of the placement. - /// The step size in which frameX changes for this placement, or 1 if any value is allowed. - /// The step size in which frameX changes for this placement, or 1 if any value is allowed. - /// The resulting operation match. - public static TileRectMatch Placement(int width, int height, ushort tileType, short maxFrameX, short maxFrameY, short frameXStep, short frameYStep) - { - return new TileRectMatch(MatchType.Placement, width, height, tileType, maxFrameX, maxFrameY, frameXStep, frameYStep); - } - - /// - /// Creates a new state change operation. - /// - /// The width of the state change. - /// The height of the state change. - /// The target tile type of the state change. - /// The maximum allowed frameX of the state change. - /// The maximum allowed frameY of the state change. - /// The step size in which frameX changes for this placement, or 1 if any value is allowed. - /// The step size in which frameY changes for this placement, or 1 if any value is allowed. - /// The resulting operation match. - public static TileRectMatch StateChange(int width, int height, ushort tileType, short maxFrameX, short maxFrameY, short frameXStep, short frameYStep) - { - return new TileRectMatch(MatchType.StateChange, width, height, tileType, maxFrameX, maxFrameY, frameXStep, frameYStep); - } - - /// - /// Creates a new state change operation which only changes frameX. - /// - /// The width of the state change. - /// The height of the state change. - /// The target tile type of the state change. - /// The maximum allowed frameX of the state change. - /// The step size in which frameX changes for this placement, or 1 if any value is allowed. - /// The resulting operation match. - public static TileRectMatch StateChangeX(int width, int height, ushort tileType, short maxFrame, short frameStep) - { - return new TileRectMatch(MatchType.StateChange, width, height, tileType, maxFrame, IGNORE_FRAME, frameStep, 0); - } - - /// - /// Creates a new state change operation which only changes frameY. - /// - /// The width of the state change. - /// The height of the state change. - /// The target tile type of the state change. - /// The maximum allowed frameY of the state change. - /// The step size in which frameY changes for this placement, or 1 if any value is allowed. - /// The resulting operation match. - public static TileRectMatch StateChangeY(int width, int height, ushort tileType, short maxFrame, short frameStep) - { - return new TileRectMatch(MatchType.StateChange, width, height, tileType, IGNORE_FRAME, maxFrame, 0, frameStep); - } - - /// - /// Creates a new removal operation. - /// - /// The width of the removal. - /// The height of the removal. - /// The target tile type of the removal. - /// The resulting operation match. - public static TileRectMatch Removal(int width, int height, ushort tileType) - { - return new TileRectMatch(MatchType.Removal, width, height, tileType, 0, 0, 0, 0); - } - - /// - /// Determines whether the given tile rectangle matches this operation, and if so, applies it to the world. - /// - /// The player the operation originates from. - /// The tile rectangle of the operation. - /// , if the rect matches this operation and the changes have been applied, otherwise . - public bool Matches(TSPlayer player, TileRect rect) - { - if (rect.Width != Width || rect.Height != Height) - { - return false; - } - - for (int x = 0; x < rect.Width; x++) - { - for (int y = 0; y < rect.Height; y++) - { - NetTile tile = rect[x, y]; - if (Type is MatchType.Placement or MatchType.StateChange) - { - if (tile.Type != TileType) - { - return false; - } - } - if (Type is MatchType.Placement or MatchType.StateChange) - { - if (MaxFrameX != IGNORE_FRAME) - { - if (tile.FrameX < 0 || tile.FrameX > MaxFrameX || tile.FrameX % FrameXStep != 0) - { - return false; - } - } - if (MaxFrameY != IGNORE_FRAME) - { - if (tile.FrameY < 0 || tile.FrameY > MaxFrameY || tile.FrameY % FrameYStep != 0) - { - // this is the only tile type sent in a tile rect where the frame have a different pattern (56, 74, 92 instead of 54, 72, 90) - if (!(TileType == TileID.LunarMonolith && tile.FrameY % FrameYStep == 2)) - { - return false; - } - } - } - } - if (Type == MatchType.Removal) - { - if (tile.Active) - { - return false; - } - } - } - } - - for (int x = rect.X; x < rect.X + rect.Width; x++) - { - for (int y = rect.Y; y < rect.Y + rect.Height; y++) - { - if (!player.HasBuildPermission(x, y)) - { - // for simplicity, let's pretend that the edit was valid, but do not execute it - return true; - } - } - } - - switch (Type) - { - case MatchType.Placement: - { - return MatchPlacement(player, rect); - } - case MatchType.StateChange: - { - return MatchStateChange(player, rect); - } - case MatchType.Removal: - { - return MatchRemoval(player, rect); - } - } - - return false; - } - - private bool MatchPlacement(TSPlayer player, TileRect rect) - { - for (int x = rect.X; x < rect.Y + rect.Width; x++) - { - for (int y = rect.Y; y < rect.Y + rect.Height; y++) - { - if (Main.tile[x, y].active() && !(Main.tile[x, y].type != TileID.RollingCactus && (Main.tileCut[Main.tile[x, y].type] || TileID.Sets.BreakableWhenPlacing[Main.tile[x, y].type]))) - { - return false; - } - } - } - - // let's hope tile types never go out of short range (they use ushort in terraria's code) - if (TShock.TileBans.TileIsBanned((short)TileType, player)) - { - // for simplicity, let's pretend that the edit was valid, but do not execute it - return true; - } - - for (int x = 0; x < rect.Width; x++) - { - for (int y = 0; y < rect.Height; y++) - { - Main.tile[x + rect.X, y + rect.Y].active(active: true); - Main.tile[x + rect.X, y + rect.Y].type = rect[x, y].Type; - Main.tile[x + rect.X, y + rect.Y].frameX = rect[x, y].FrameX; - Main.tile[x + rect.X, y + rect.Y].frameY = rect[x, y].FrameY; - } - } - - return true; - } - - private bool MatchStateChange(TSPlayer player, TileRect rect) - { - for (int x = rect.X; x < rect.Y + rect.Width; x++) - { - for (int y = rect.Y; y < rect.Y + rect.Height; y++) - { - if (!Main.tile[x, y].active() || Main.tile[x, y].type != TileType) - { - return false; - } - } - } - - for (int x = 0; x < rect.Width; x++) - { - for (int y = 0; y < rect.Height; y++) - { - if (MaxFrameX != IGNORE_FRAME) - { - Main.tile[x + rect.X, y + rect.Y].frameX = rect[x, y].FrameX; - } - if (MaxFrameY != IGNORE_FRAME) - { - Main.tile[x + rect.X, y + rect.Y].frameY = rect[x, y].FrameY; - } - } - } - - return true; - } - - private bool MatchRemoval(TSPlayer player, TileRect rect) - { - for (int x = rect.X; x < rect.Y + rect.Width; x++) - { - for (int y = rect.Y; y < rect.Y + rect.Height; y++) - { - if (!Main.tile[x, y].active() || Main.tile[x, y].type != TileType) - { - return false; - } - } - } - - for (int x = 0; x < rect.Width; x++) - { - for (int y = 0; y < rect.Height; y++) - { - Main.tile[x + rect.X, y + rect.Y].active(active: false); - Main.tile[x + rect.X, y + rect.Y].frameX = -1; - Main.tile[x + rect.X, y + rect.Y].frameY = -1; - } - } - - return true; - } - } - - /// - /// Contains the complete list of valid tile rect operations the game currently performs. - /// - // The matches restrict the tile rects to only place one kind of tile, and only with the given maximum values and step sizes for frameX and frameY. This performs pretty much perfect checks on the data, allowing only valid placements. - // For TileID.MinecartTrack, the data is taken from `Minecart._trackSwitchOptions`, allowing any framing value in this array (currently 0-36). - // For TileID.Plants, it is taken from `ItemID.Sets.flowerPacketInfo[n].stylesOnPurity`, allowing every style multiplied by 18. - // The other operations are based on code analysis and manual observation. - private static readonly TileRectMatch[] Matches = new TileRectMatch[] - { - TileRectMatch.Placement(2, 3, TileID.TargetDummy, 54, 36, 18, 18), - TileRectMatch.Placement(3, 4, TileID.TeleportationPylon, 468, 54, 18, 18), - TileRectMatch.Placement(2, 3, TileID.DisplayDoll, 126, 36, 18, 18), - TileRectMatch.Placement(2, 3, TileID.HatRack, 90, 54, 18, 18), - TileRectMatch.Placement(2, 2, TileID.ItemFrame, 162, 18, 18, 18), - TileRectMatch.Placement(3, 3, TileID.WeaponsRack2, 90, 36, 18, 18), - TileRectMatch.Placement(1, 1, TileID.FoodPlatter, 18, 0, 18, 18), - TileRectMatch.Placement(1, 1, TileID.LogicSensor, 108, 0, 18, 18), - - TileRectMatch.StateChangeY(3, 2, TileID.Campfire, 54, 18), - TileRectMatch.StateChangeY(4, 3, TileID.Cannon, 468, 18), - TileRectMatch.StateChangeY(2, 2, TileID.ArrowSign, 270, 18), - TileRectMatch.StateChangeY(2, 2, TileID.PaintedArrowSign, 270, 18), - - TileRectMatch.StateChangeX(2, 2, TileID.MusicBoxes, 54, 18), - - TileRectMatch.StateChangeY(2, 3, TileID.LunarMonolith, 92, 18), - TileRectMatch.StateChangeY(2, 3, TileID.BloodMoonMonolith, 90, 18), - TileRectMatch.StateChangeY(2, 3, TileID.VoidMonolith, 90, 18), - TileRectMatch.StateChangeY(2, 3, TileID.EchoMonolith, 90, 18), - TileRectMatch.StateChangeY(2, 3, TileID.ShimmerMonolith, 144, 18), - TileRectMatch.StateChangeY(2, 4, TileID.WaterFountain, 126, 18), - - TileRectMatch.StateChangeX(1, 1, TileID.Candles, 18, 18), - TileRectMatch.StateChangeX(1, 1, TileID.PeaceCandle, 18, 18), - TileRectMatch.StateChangeX(1, 1, TileID.WaterCandle, 18, 18), - TileRectMatch.StateChangeX(1, 1, TileID.PlatinumCandle, 18, 18), - TileRectMatch.StateChangeX(1, 1, TileID.ShadowCandle, 18, 18), - - TileRectMatch.StateChange(1, 1, TileID.Traps, 90, 90, 18, 18), - - TileRectMatch.StateChangeX(1, 1, TileID.WirePipe, 36, 18), - TileRectMatch.StateChangeX(1, 1, TileID.ProjectilePressurePad, 66, 22), - TileRectMatch.StateChangeX(1, 1, TileID.Plants, 792, 18), - TileRectMatch.StateChangeX(1, 1, TileID.MinecartTrack, 36, 1), - - TileRectMatch.Removal(1, 2, TileID.Firework), - TileRectMatch.Removal(1, 1, TileID.LandMine), - }; - - - /// - /// Handles a packet receive event. - /// - public void OnReceive(object sender, GetDataHandlers.SendTileRectEventArgs args) - { - // this permission bypasses all checks for direct access to the world - if (args.Player.HasPermission(Permissions.allowclientsideworldedit)) - { - TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect accepted clientside world edit from {args.Player.Name}")); - - // use vanilla handling - args.Handled = false; - return; - } - - // this handler handles the entire logic of this packet - args.Handled = true; - - // as of 1.4 this is the biggest size the client will send in any case, determined by full code analysis - // see default matches above and special cases below - if (args.Width > 4 || args.Length > 4) - { - TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from size from {args.Player.Name}")); - - // definitely invalid; do not send any correcting data - return; - } - - // player throttled? - if (args.Player.IsBouncerThrottled()) - { - TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from throttle from {args.Player.Name}")); - - // send correcting data - args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); - return; - } - - // player disabled? - if (args.Player.IsBeingDisabled()) - { - TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from being disabled from {args.Player.Name}")); - - // send correcting data - args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); - return; - } - - // read the tile rectangle - TileRect rect = TileRect.Read(args.Data, args.TileX, args.TileY, args.Width, args.Length); - - // check if the positioning is valid - if (!IsRectPositionValid(args.Player, rect)) - { - TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from out of bounds / build permission from {args.Player.Name}")); - - // send nothing due to out of bounds - return; - } - - // a very special case, due to the clentaminator having a larger range than TSPlayer.IsInRange() allows - if (MatchesConversionSpread(args.Player, rect)) - { - TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}")); - - // send correcting data - args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); - return; - } - - // check if the distance is valid - if (!IsRectDistanceValid(args.Player, rect)) - { - TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from out of range from {args.Player.Name}")); - - // send correcting data - args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); - return; - } - - // a very special case, due to the flower seed check otherwise hijacking this - if (MatchesFlowerBoots(args.Player, rect)) - { - TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}")); - - // send correcting data - args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); - return; - } - - // check if the rect matches any valid operation - foreach (TileRectMatch match in Matches) - { - if (match.Matches(args.Player, rect)) - { - TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}")); - - // send correcting data - args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); - return; - } - } - - // a few special cases - if ( - MatchesConversionSpread(args.Player, rect) || - MatchesGrassMow(args.Player, rect) || - MatchesChristmasTree(args.Player, rect) - ) - { - TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}")); - - // send correcting data - args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); - return; - } - - TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from matches from {args.Player.Name}")); - - // send correcting data - args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); - return; - } - - /// - /// Checks whether the tile rect is at a valid position for the given player. - /// - /// The player the operation originates from. - /// The tile rectangle of the operation. - /// , if the rect at a valid position, otherwise . - private static bool IsRectPositionValid(TSPlayer player, TileRect rect) - { - for (int x = 0; x < rect.Width; x++) - { - for (int y = 0; y < rect.Height; y++) - { - int realX = rect.X + x; - int realY = rect.Y + y; - - if (realX < 0 || realX >= Main.maxTilesX || realY < 0 || realY >= Main.maxTilesY) - { - return false; - } - } - } - - return true; - } - - /// - /// Checks whether the tile rect is at a valid distance to the given player. - /// - /// The player the operation originates from. - /// The tile rectangle of the operation. - /// , if the rect at a valid distance, otherwise . - private static bool IsRectDistanceValid(TSPlayer player, TileRect rect) - { - for (int x = 0; x < rect.Width; x++) - { - for (int y = 0; y < rect.Height; y++) - { - int realX = rect.X + x; - int realY = rect.Y + y; - - if (!player.IsInRange(realX, realY)) - { - return false; - } - } - } - - return true; - } - - - /// - /// Checks whether the tile rect is a valid conversion spread (Clentaminator, Powders, etc.). - /// - /// The player the operation originates from. - /// The tile rectangle of the operation. - /// , if the rect matches a conversion spread operation, otherwise . - private static bool MatchesConversionSpread(TSPlayer player, TileRect rect) - { - if (rect.Width != 1 || rect.Height != 1) - { - return false; - } - - ITile oldTile = Main.tile[rect.X, rect.Y]; - NetTile newTile = rect[0, 0]; - - WorldGenMock.SimulateConversionChange(rect.X, rect.Y, out HashSet validTiles, out HashSet validWalls); - - if (newTile.Type != oldTile.type && validTiles.Contains(newTile.Type)) - { - if (TShock.TileBans.TileIsBanned((short)newTile.Type, player)) - { - // for simplicity, let's pretend that the edit was valid, but do not execute it - return true; - } - else if (!player.HasBuildPermission(rect.X, rect.Y)) - { - // for simplicity, let's pretend that the edit was valid, but do not execute it - return true; - } - else - { - Main.tile[rect.X, rect.Y].type = newTile.Type; - Main.tile[rect.X, rect.Y].frameX = newTile.FrameX; - Main.tile[rect.X, rect.Y].frameY = newTile.FrameY; - - return true; - } - } - - if (newTile.Wall != oldTile.wall && validWalls.Contains(newTile.Wall)) - { - // wallbans when? - - if (!player.HasBuildPermission(rect.X, rect.Y)) - { - // for simplicity, let's pretend that the edit was valid, but do not execute it - return true; - } - else - { - Main.tile[rect.X, rect.Y].wall = newTile.Wall; - - return true; - } - } - - return false; - } - - - private static readonly Dictionary> PlantToGrassMap = new Dictionary> - { - { TileID.Plants, new HashSet() - { - TileID.Grass, TileID.GolfGrass - } }, - { TileID.HallowedPlants, new HashSet() - { - TileID.HallowedGrass, TileID.GolfGrassHallowed - } }, - { TileID.HallowedPlants2, new HashSet() - { - TileID.HallowedGrass, TileID.GolfGrassHallowed - } }, - { TileID.JunglePlants2, new HashSet() - { - TileID.JungleGrass - } }, - { TileID.AshPlants, new HashSet() - { - TileID.AshGrass - } }, - }; - - private static readonly Dictionary> GrassToStyleMap = new Dictionary>() - { - { TileID.Plants, new HashSet() - { - 6, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 24, 27, 30, 33, 36, 39, 42, - 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 44, - } }, - { TileID.HallowedPlants, new HashSet() - { - 4, 6, - } }, - { TileID.HallowedPlants2, new HashSet() - { - 2, 3, 4, 6, 7, - } }, - { TileID.JunglePlants2, new HashSet() - { - 9, 10, 11, 12, 13, 14, 15, 16, - } }, - { TileID.AshPlants, new HashSet() - { - 6, 7, 8, 9, 10, - } }, - }; - - /// - /// Checks whether the tile rect is a valid Flower Boots placement. - /// - /// The player the operation originates from. - /// The tile rectangle of the operation. - /// , if the rect matches a Flower Boots placement, otherwise . - private static bool MatchesFlowerBoots(TSPlayer player, TileRect rect) - { - if (rect.Width != 1 || rect.Height != 1) - { - return false; - } - - if (!player.TPlayer.flowerBoots) - { - return false; - } - - ITile oldTile = Main.tile[rect.X, rect.Y]; - NetTile newTile = rect[0, 0]; - - if ( - PlantToGrassMap.TryGetValue(newTile.Type, out HashSet grassTiles) && - !oldTile.active() && grassTiles.Contains(Main.tile[rect.X, rect.Y + 1].type) && - GrassToStyleMap[newTile.Type].Contains((ushort)(newTile.FrameX / 18)) - ) - { - if (TShock.TileBans.TileIsBanned((short)newTile.Type, player)) - { - // for simplicity, let's pretend that the edit was valid, but do not execute it - return true; - } - - if (!player.HasBuildPermission(rect.X, rect.Y)) - { - // for simplicity, let's pretend that the edit was valid, but do not execute it - return true; - } - - Main.tile[rect.X, rect.Y].active(active: true); - Main.tile[rect.X, rect.Y].type = newTile.Type; - Main.tile[rect.X, rect.Y].frameX = newTile.FrameX; - Main.tile[rect.X, rect.Y].frameY = 0; - - return true; - } - - return false; - } - - - private static readonly Dictionary GrassToMowedMap = new Dictionary - { - { TileID.Grass, TileID.GolfGrass }, - { TileID.HallowedGrass, TileID.GolfGrassHallowed }, - }; - - /// - /// Checks whether the tile rect is a valid grass mow. - /// - /// The player the operation originates from. - /// The tile rectangle of the operation. - /// , if the rect matches a grass mowing operation, otherwise . - private static bool MatchesGrassMow(TSPlayer player, TileRect rect) - { - if (rect.Width != 1 || rect.Height != 1) - { - return false; - } - - ITile oldTile = Main.tile[rect.X, rect.Y]; - NetTile newTile = rect[0, 0]; - - if (GrassToMowedMap.TryGetValue(oldTile.type, out ushort mowed) && newTile.Type == mowed) - { - if (TShock.TileBans.TileIsBanned((short)newTile.Type, player)) - { - // for simplicity, let's pretend that the edit was valid, but do not execute it - return true; - } - - if (!player.HasBuildPermission(rect.X, rect.Y)) - { - // for simplicity, let's pretend that the edit was valid, but do not execute it - return true; - } - - Main.tile[rect.X, rect.Y].type = newTile.Type; - if (!newTile.FrameImportant) - { - Main.tile[rect.X, rect.Y].frameX = -1; - Main.tile[rect.X, rect.Y].frameY = -1; - } - - // prevent a common crash when the game checks all vines in an unlimited horizontal length - if (TileID.Sets.IsVine[Main.tile[rect.X, rect.Y + 1].type]) - { - WorldGen.KillTile(rect.X, rect.Y + 1); - } - - return true; - } - - return false; - } - - - /// - /// Checks whether the tile rect is a valid christmas tree modification. - /// This also required significant reverse engineering effort. - /// - /// The player the operation originates from. - /// The tile rectangle of the operation. - /// , if the rect matches a christmas tree operation, otherwise . - private static bool MatchesChristmasTree(TSPlayer player, TileRect rect) - { - if (rect.Width != 1 || rect.Height != 1) - { - return false; - } - - ITile oldTile = Main.tile[rect.X, rect.Y]; - NetTile newTile = rect[0, 0]; - - if (oldTile.type == TileID.ChristmasTree && newTile.Type == TileID.ChristmasTree) - { - if (newTile.FrameX != 10) - { - return false; - } - - int obj_0 = (newTile.FrameY & 0b0000000000000111); - int obj_1 = (newTile.FrameY & 0b0000000000111000) >> 3; - int obj_2 = (newTile.FrameY & 0b0000001111000000) >> 6; - int obj_3 = (newTile.FrameY & 0b0011110000000000) >> 10; - int obj_x = (newTile.FrameY & 0b1100000000000000) >> 14; - - if (obj_x != 0) - { - return false; - } - - if (obj_0 is < 0 or > 4 || obj_1 is < 0 or > 6 || obj_2 is < 0 or > 11 || obj_3 is < 0 or > 11) - { - return false; - } - - if (!player.HasBuildPermission(rect.X, rect.Y)) - { - // for simplicity, let's pretend that the edit was valid, but do not execute it - return true; - } - - Main.tile[rect.X, rect.Y].frameY = newTile.FrameY; - - return true; - } - - return false; - } - } - - /// - /// This helper class allows simulating a `WorldGen.Convert` call and retrieving all valid changes for a given tile. - /// - internal static class WorldGenMock - { - /// - /// This is a mock tile which collects all possible changes the `WorldGen.Convert` code could make in its property setters. - /// - private sealed class MockTile - { - private readonly HashSet _setTypes; - private readonly HashSet _setWalls; - - private ushort _type; - private ushort _wall; - - public MockTile(ushort type, ushort wall, HashSet setTypes, HashSet setWalls) - { - _setTypes = setTypes; - _setWalls = setWalls; - _type = type; - _wall = wall; - } - -#pragma warning disable IDE1006 - - public ushort type - { - get => _type; - set - { - _setTypes.Add(value); - _type = value; - } - } - - public ushort wall - { - get => _wall; - set - { - _setWalls.Add(value); - _wall = value; - } - } - -#pragma warning restore IDE1006 - } - - /// - /// Simulates what would happen if `WorldGen.Convert` was called on the given coordinates and returns two sets with the possible tile type and wall types that the conversion could change the tile to. - /// - public static void SimulateConversionChange(int x, int y, out HashSet validTiles, out HashSet validWalls) - { - validTiles = new HashSet(); - validWalls = new HashSet(); - - // all the conversion types used in the code, most apparent in Projectile ai 31 - foreach (int conversionType in new int[] { 0, 1, 2, 3, 4, 5, 6, 7 }) - { - MockTile mock = new(Main.tile[x, y].type, Main.tile[x, y].wall, validTiles, validWalls); - Convert(mock, x, y, conversionType); - } - } - - /* - * This is a copy of the `WorldGen.Convert` method with the following precise changes: - * - Added a `MockTile tile` parameter - * - Changed the `i` and `j` parameters to `k` and `l` - * - Removed the size parameter - * - Removed the area loop and `Tile tile = Main.tile[k, l]` access in favor of using the tile parameter - * - Removed all calls to `WorldGen.SquareWallFrame`, `NetMessage.SendTileSquare`, `WorldGen.TryKillingTreesAboveIfTheyWouldBecomeInvalid` - * - Changed all `continue` statements to `break` statements - * - Removed the ifs checking the bounds of the tile and wall types - * - Removed branches that would call `WorldGen.KillTile` - * - Changed branches depending on randomness to instead set the property to both values after one another - * - * This overall leads to a method that can be called on a MockTile and real-world coordinates and will spit out the proper conversion changes into the MockTile. - */ - - private static void Convert(MockTile tile, int k, int l, int conversionType) - { - int type = tile.type; - int wall = tile.wall; - switch (conversionType) - { - case 4: - if (WallID.Sets.Conversion.Grass[wall] && wall != 81) - { - tile.wall = 81; - } - else if (WallID.Sets.Conversion.Stone[wall] && wall != 83) - { - tile.wall = 83; - } - else if (WallID.Sets.Conversion.HardenedSand[wall] && wall != 218) - { - tile.wall = 218; - } - else if (WallID.Sets.Conversion.Sandstone[wall] && wall != 221) - { - tile.wall = 221; - } - else if (WallID.Sets.Conversion.NewWall1[wall] && wall != 192) - { - tile.wall = 192; - } - else if (WallID.Sets.Conversion.NewWall2[wall] && wall != 193) - { - tile.wall = 193; - } - else if (WallID.Sets.Conversion.NewWall3[wall] && wall != 194) - { - tile.wall = 194; - } - else if (WallID.Sets.Conversion.NewWall4[wall] && wall != 195) - { - tile.wall = 195; - } - if ((Main.tileMoss[type] || TileID.Sets.Conversion.Stone[type]) && type != 203) - { - tile.type = 203; - } - else if (TileID.Sets.Conversion.JungleGrass[type] && type != 662) - { - tile.type = 662; - } - else if (TileID.Sets.Conversion.Grass[type] && type != 199) - { - tile.type = 199; - } - else if (TileID.Sets.Conversion.Ice[type] && type != 200) - { - tile.type = 200; - } - else if (TileID.Sets.Conversion.Sand[type] && type != 234) - { - tile.type = 234; - } - else if (TileID.Sets.Conversion.HardenedSand[type] && type != 399) - { - tile.type = 399; - } - else if (TileID.Sets.Conversion.Sandstone[type] && type != 401) - { - tile.type = 401; - } - else if (TileID.Sets.Conversion.Thorn[type] && type != 352) - { - tile.type = 352; - } - break; - case 2: - if (WallID.Sets.Conversion.Grass[wall] && wall != 70) - { - tile.wall = 70; - } - else if (WallID.Sets.Conversion.Stone[wall] && wall != 28) - { - tile.wall = 28; - } - else if (WallID.Sets.Conversion.HardenedSand[wall] && wall != 219) - { - tile.wall = 219; - } - else if (WallID.Sets.Conversion.Sandstone[wall] && wall != 222) - { - tile.wall = 222; - } - else if (WallID.Sets.Conversion.NewWall1[wall] && wall != 200) - { - tile.wall = 200; - } - else if (WallID.Sets.Conversion.NewWall2[wall] && wall != 201) - { - tile.wall = 201; - } - else if (WallID.Sets.Conversion.NewWall3[wall] && wall != 202) - { - tile.wall = 202; - } - else if (WallID.Sets.Conversion.NewWall4[wall] && wall != 203) - { - tile.wall = 203; - } - if ((Main.tileMoss[type] || TileID.Sets.Conversion.Stone[type]) && type != 117) - { - tile.type = 117; - } - else if (TileID.Sets.Conversion.GolfGrass[type] && type != 492) - { - tile.type = 492; - } - else if (TileID.Sets.Conversion.Grass[type] && type != 109 && type != 492) - { - tile.type = 109; - } - else if (TileID.Sets.Conversion.Ice[type] && type != 164) - { - tile.type = 164; - } - else if (TileID.Sets.Conversion.Sand[type] && type != 116) - { - tile.type = 116; - } - else if (TileID.Sets.Conversion.HardenedSand[type] && type != 402) - { - tile.type = 402; - } - else if (TileID.Sets.Conversion.Sandstone[type] && type != 403) - { - tile.type = 403; - } - if (type == 59 && (Main.tile[k - 1, l].type == 109 || Main.tile[k + 1, l].type == 109 || Main.tile[k, l - 1].type == 109 || Main.tile[k, l + 1].type == 109)) - { - tile.type = 0; - } - break; - case 1: - if (WallID.Sets.Conversion.Grass[wall] && wall != 69) - { - tile.wall = 69; - } - else if (TileID.Sets.Conversion.JungleGrass[type] && type != 661) - { - tile.type = 661; - } - else if (WallID.Sets.Conversion.Stone[wall] && wall != 3) - { - tile.wall = 3; - } - else if (WallID.Sets.Conversion.HardenedSand[wall] && wall != 217) - { - tile.wall = 217; - } - else if (WallID.Sets.Conversion.Sandstone[wall] && wall != 220) - { - tile.wall = 220; - } - else if (WallID.Sets.Conversion.NewWall1[wall] && wall != 188) - { - tile.wall = 188; - } - else if (WallID.Sets.Conversion.NewWall2[wall] && wall != 189) - { - tile.wall = 189; - } - else if (WallID.Sets.Conversion.NewWall3[wall] && wall != 190) - { - tile.wall = 190; - } - else if (WallID.Sets.Conversion.NewWall4[wall] && wall != 191) - { - tile.wall = 191; - } - if ((Main.tileMoss[type] || TileID.Sets.Conversion.Stone[type]) && type != 25) - { - tile.type = 25; - } - else if (TileID.Sets.Conversion.Grass[type] && type != 23) - { - tile.type = 23; - } - else if (TileID.Sets.Conversion.Ice[type] && type != 163) - { - tile.type = 163; - } - else if (TileID.Sets.Conversion.Sand[type] && type != 112) - { - tile.type = 112; - } - else if (TileID.Sets.Conversion.HardenedSand[type] && type != 398) - { - tile.type = 398; - } - else if (TileID.Sets.Conversion.Sandstone[type] && type != 400) - { - tile.type = 400; - } - else if (TileID.Sets.Conversion.Thorn[type] && type != 32) - { - tile.type = 32; - } - break; - case 3: - if (WallID.Sets.CanBeConvertedToGlowingMushroom[wall]) - { - tile.wall = 80; - } - if (tile.type == 60) - { - tile.type = 70; - } - break; - case 5: - if ((WallID.Sets.Conversion.Stone[wall] || WallID.Sets.Conversion.NewWall1[wall] || WallID.Sets.Conversion.NewWall2[wall] || WallID.Sets.Conversion.NewWall3[wall] || WallID.Sets.Conversion.NewWall4[wall] || WallID.Sets.Conversion.Ice[wall] || WallID.Sets.Conversion.Sandstone[wall]) && wall != 187) - { - tile.wall = 187; - } - else if ((WallID.Sets.Conversion.HardenedSand[wall] || WallID.Sets.Conversion.Dirt[wall] || WallID.Sets.Conversion.Snow[wall]) && wall != 216) - { - tile.wall = 216; - } - if ((TileID.Sets.Conversion.Grass[type] || TileID.Sets.Conversion.Sand[type] || TileID.Sets.Conversion.Snow[type] || TileID.Sets.Conversion.Dirt[type]) && type != 53) - { - int num = 53; - if (WorldGen.BlockBelowMakesSandConvertIntoHardenedSand(k, l)) - { - num = 397; - } - tile.type = (ushort)num; - } - else if (TileID.Sets.Conversion.HardenedSand[type] && type != 397) - { - tile.type = 397; - } - else if ((Main.tileMoss[type] || TileID.Sets.Conversion.Stone[type] || TileID.Sets.Conversion.Ice[type] || TileID.Sets.Conversion.Sandstone[type]) && type != 396) - { - tile.type = 396; - } - break; - case 6: - if ((WallID.Sets.Conversion.Stone[wall] || WallID.Sets.Conversion.NewWall1[wall] || WallID.Sets.Conversion.NewWall2[wall] || WallID.Sets.Conversion.NewWall3[wall] || WallID.Sets.Conversion.NewWall4[wall] || WallID.Sets.Conversion.Ice[wall] || WallID.Sets.Conversion.Sandstone[wall]) && wall != 71) - { - tile.wall = 71; - } - else if ((WallID.Sets.Conversion.HardenedSand[wall] || WallID.Sets.Conversion.Dirt[wall] || WallID.Sets.Conversion.Snow[wall]) && wall != 40) - { - tile.wall = 40; - } - if ((TileID.Sets.Conversion.Grass[type] || TileID.Sets.Conversion.Sand[type] || TileID.Sets.Conversion.HardenedSand[type] || TileID.Sets.Conversion.Snow[type] || TileID.Sets.Conversion.Dirt[type]) && type != 147) - { - tile.type = 147; - } - else if ((Main.tileMoss[type] || TileID.Sets.Conversion.Stone[type] || TileID.Sets.Conversion.Ice[type] || TileID.Sets.Conversion.Sandstone[type]) && type != 161) - { - tile.type = 161; - } - break; - case 7: - if ((WallID.Sets.Conversion.Stone[wall] || WallID.Sets.Conversion.Ice[wall] || WallID.Sets.Conversion.Sandstone[wall]) && wall != 1) - { - tile.wall = 1; - } - else if ((WallID.Sets.Conversion.HardenedSand[wall] || WallID.Sets.Conversion.Snow[wall] || WallID.Sets.Conversion.Dirt[wall]) && wall != 2) - { - tile.wall = 2; - } - else if (WallID.Sets.Conversion.NewWall1[wall] && wall != 196) - { - tile.wall = 196; - } - else if (WallID.Sets.Conversion.NewWall2[wall] && wall != 197) - { - tile.wall = 197; - } - else if (WallID.Sets.Conversion.NewWall3[wall] && wall != 198) - { - tile.wall = 198; - } - else if (WallID.Sets.Conversion.NewWall4[wall] && wall != 199) - { - tile.wall = 199; - } - if ((TileID.Sets.Conversion.Stone[type] || TileID.Sets.Conversion.Ice[type] || TileID.Sets.Conversion.Sandstone[type]) && type != 1) - { - tile.type = 1; - } - else if (TileID.Sets.Conversion.GolfGrass[type] && type != 477) - { - tile.type = 477; - } - else if (TileID.Sets.Conversion.Grass[type] && type != 2 && type != 477) - { - tile.type = 2; - } - else if ((TileID.Sets.Conversion.Sand[type] || TileID.Sets.Conversion.HardenedSand[type] || TileID.Sets.Conversion.Snow[type] || TileID.Sets.Conversion.Dirt[type]) && type != 0) - { - int num2 = 0; - if (WorldGen.TileIsExposedToAir(k, l)) - { - num2 = 2; - } - tile.type = (ushort)num2; - } - break; - } - if (tile.wall == 69 || tile.wall == 70 || tile.wall == 81) - { - if (l < Main.worldSurface) - { - tile.wall = 65; - tile.wall = 63; - } - else - { - tile.wall = 64; - } - } - else if (WallID.Sets.Conversion.Stone[wall] && wall != 1 && wall != 262 && wall != 274 && wall != 61 && wall != 185) - { - tile.wall = 1; - } - else if (WallID.Sets.Conversion.Stone[wall] && wall == 262) - { - tile.wall = 61; - } - else if (WallID.Sets.Conversion.Stone[wall] && wall == 274) - { - tile.wall = 185; - } - if (WallID.Sets.Conversion.NewWall1[wall] && wall != 212) - { - tile.wall = 212; - } - else if (WallID.Sets.Conversion.NewWall2[wall] && wall != 213) - { - tile.wall = 213; - } - else if (WallID.Sets.Conversion.NewWall3[wall] && wall != 214) - { - tile.wall = 214; - } - else if (WallID.Sets.Conversion.NewWall4[wall] && wall != 215) - { - tile.wall = 215; - } - else if (tile.wall == 80) - { - tile.wall = 15; - tile.wall = 64; - } - else if (WallID.Sets.Conversion.HardenedSand[wall] && wall != 216) - { - tile.wall = 216; - } - else if (WallID.Sets.Conversion.Sandstone[wall] && wall != 187) - { - tile.wall = 187; - } - if (tile.type == 492) - { - tile.type = 477; - } - else if (TileID.Sets.Conversion.JungleGrass[type] && type != 60) - { - tile.type = 60; - } - else if (TileID.Sets.Conversion.Grass[type] && type != 2 && type != 477) - { - tile.type = 2; - } - else if (TileID.Sets.Conversion.Stone[type] && type != 1) - { - tile.type = 1; - } - else if (TileID.Sets.Conversion.Sand[type] && type != 53) - { - tile.type = 53; - } - else if (TileID.Sets.Conversion.HardenedSand[type] && type != 397) - { - tile.type = 397; - } - else if (TileID.Sets.Conversion.Sandstone[type] && type != 396) - { - tile.type = 396; - } - else if (TileID.Sets.Conversion.Ice[type] && type != 161) - { - tile.type = 161; - } - else if (TileID.Sets.Conversion.MushroomGrass[type]) - { - tile.type = 60; - } - } - } -} From c67d5cf1523fa33ecfbd7f4a6ad78462e35310f7 Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Wed, 10 May 2023 01:49:28 -0400 Subject: [PATCH 028/135] Version tick: 5.2 --- TShockAPI/TShock.cs | 2 +- TShockAPI/TShockAPI.csproj | 4 ++-- TerrariaServerAPI | 2 +- docs/changelog.md | 5 ++++- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index 65380a52..054e31df 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -63,7 +63,7 @@ namespace TShockAPI /// VersionNum - The version number the TerrariaAPI will return back to the API. We just use the Assembly info. public static readonly Version VersionNum = Assembly.GetExecutingAssembly().GetName().Version; /// VersionCodename - The version codename is displayed when the server starts. Inspired by software codenames conventions. - public static readonly string VersionCodename = "Thank you, everyone, for your support of TShock all these years! <3"; + public static readonly string VersionCodename = "Intensity"; /// SavePath - This is the path TShock saves its data in. This path is relative to the TerrariaServer.exe (not in ServerPlugins). public static string SavePath = "tshock"; diff --git a/TShockAPI/TShockAPI.csproj b/TShockAPI/TShockAPI.csproj index 7bc42215..a3731709 100644 --- a/TShockAPI/TShockAPI.csproj +++ b/TShockAPI/TShockAPI.csproj @@ -18,11 +18,11 @@ Also, be sure to release on github with the exact assembly version tag as below so that the update manager works correctly (via the Github releases api and mimic) --> - 5.1.3 + 5.2.0 TShock for Terraria Pryaxis & TShock Contributors TShockAPI - Copyright © Pryaxis & TShock Contributors 2011-2022 + Copyright © Pryaxis & TShock Contributors 2011-2023 True GPL-3.0-or-later diff --git a/TerrariaServerAPI b/TerrariaServerAPI index e0e2df24..8a3fffd7 160000 --- a/TerrariaServerAPI +++ b/TerrariaServerAPI @@ -1 +1 @@ -Subproject commit e0e2df24dbc618683cc75595c59bf7f88acada08 +Subproject commit 8a3fffd71db401736ea80619122c70c449c10ff3 diff --git a/docs/changelog.md b/docs/changelog.md index c4a95d14..3f64f355 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -78,7 +78,10 @@ Use past tense when adding new entries; sign your name off when you add or chang * If there is no section called "Upcoming changes" below this line, please add one with `## Upcoming changes` as the first line, and then a bulleted item directly after with the first change. --> ## Upcoming changes -* An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team.(@CelestialAnarchy, #2617, @ATFGK) +Your changes could be here! + +## TShock 5.2 +* An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) * Corrected and updated deserialization of the following packets (@ATFGK): * `ProjectileNew`: Read the third `AI` value. * Before this change, it was previously possible for the projectile damage limit to falsely trigger, such as when using the Terra Balde and Fire Gauntlet together. From 3d11d84d7339ffe7f165cf2f15316a1d6660dd48 Mon Sep 17 00:00:00 2001 From: SGKoishi Date: Sat, 13 May 2023 03:56:07 -0700 Subject: [PATCH 029/135] Fix dump-reference-data mutate command name --- TShockAPI/Permissions.cs | 15 +++------------ docs/changelog.md | 2 +- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/TShockAPI/Permissions.cs b/TShockAPI/Permissions.cs index d7ec7166..debc8ea5 100644 --- a/TShockAPI/Permissions.cs +++ b/TShockAPI/Permissions.cs @@ -529,18 +529,9 @@ namespace TShockAPI field.GetCustomAttributes(false).FirstOrDefault(o => o is DescriptionAttribute) as DescriptionAttribute; var desc = descattr != null && !string.IsNullOrWhiteSpace(descattr.Description) ? descattr.Description : GetString("No description available."); - 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)) : "")); + var strs = GetCommands(name).Select(c => c.Names.Count > 1 + ? $"{c.Name} ({string.Join(" ", c.Names.Skip(1).Select(n => $"/{n}"))})" + : c.Name); sb.AppendLine($"## {name}"); sb.AppendLine($"{desc}"); diff --git a/docs/changelog.md b/docs/changelog.md index 3f64f355..d7b1d19f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -78,7 +78,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * If there is no section called "Upcoming changes" below this line, please add one with `## Upcoming changes` as the first line, and then a bulleted item directly after with the first change. --> ## Upcoming changes -Your changes could be here! +* Fixed `/dump-reference-data` mutate the command names. (#2943, @sgkoishi) ## TShock 5.2 * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) From 51ecef73a16dd1df10bf093ef529be57c5bd1b64 Mon Sep 17 00:00:00 2001 From: Stargazing Koishi Date: Sat, 13 May 2023 04:09:15 -0700 Subject: [PATCH 030/135] Optimize Linq Co-authored-by: Arthri <41360489+Arthri@users.noreply.github.com> --- TShockAPI/Permissions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TShockAPI/Permissions.cs b/TShockAPI/Permissions.cs index debc8ea5..99ac5d7d 100644 --- a/TShockAPI/Permissions.cs +++ b/TShockAPI/Permissions.cs @@ -530,8 +530,8 @@ namespace TShockAPI var desc = descattr != null && !string.IsNullOrWhiteSpace(descattr.Description) ? descattr.Description : GetString("No description available."); var strs = GetCommands(name).Select(c => c.Names.Count > 1 - ? $"{c.Name} ({string.Join(" ", c.Names.Skip(1).Select(n => $"/{n}"))})" - : c.Name); + ? $"/{c.Name} (/{string.Join(" /", c.Names.Skip(1))})" + : $"/{c.Name}"); sb.AppendLine($"## {name}"); sb.AppendLine($"{desc}"); From 6d4682ed7e37722054b23c885cd6c85987605ccc Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 09:11:18 +0700 Subject: [PATCH 031/135] Updated the `GodMode` field. * Changed the GodMode field to a property that is controlled by the journey mode. - Removed the call to godmode in `Commands.ToggleGodMode`. --- TShockAPI/Commands.cs | 4 ---- TShockAPI/TSPlayer.cs | 11 +++++++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs index 2214f252..3d1767ea 100644 --- a/TShockAPI/Commands.cs +++ b/TShockAPI/Commands.cs @@ -6740,10 +6740,6 @@ namespace TShockAPI playerToGod.GodMode = !playerToGod.GodMode; - var godPower = CreativePowerManager.Instance.GetPower(); - - godPower.SetEnabledState(playerToGod.Index, playerToGod.GodMode); - if (playerToGod != args.Player) { args.Player.SendSuccessMessage(playerToGod.GodMode diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index ef5c3c22..f6ece337 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -34,6 +34,7 @@ using TShockAPI.Hooks; using TShockAPI.Net; using Timer = System.Timers.Timer; using System.Linq; +using Terraria.GameContent.Creative; namespace TShockAPI { @@ -935,9 +936,15 @@ namespace TShockAPI public bool LoginHarassed = false; /// - /// Player cant die, unless onehit + /// Controls the journey godmode /// - public bool GodMode = false; + public bool GodMode + { + get => + CreativePowerManager.Instance.GetPower().IsEnabledForPlayer(Index); + set => + CreativePowerManager.Instance.GetPower().SetEnabledState(Index, value); + } /// /// Players controls are inverted if using SSC From c047b0e87d5309b503c9d0beb91797a81dafe38f Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 09:14:43 +0700 Subject: [PATCH 032/135] Added the `Client` property. The `Client` property gets data from the array `Terraria.Netplay.Clients`. --- TShockAPI/TSPlayer.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index f6ece337..10b2e760 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -988,7 +988,7 @@ namespace TShockAPI get { return RealPlayer - && (Netplay.Clients[Index] != null && Netplay.Clients[Index].IsActive && !Netplay.Clients[Index].PendingTermination); + && (Client != null && Client.IsActive && !Client.PendingTermination); } } @@ -1005,8 +1005,8 @@ namespace TShockAPI /// public int State { - get { return Netplay.Clients[Index].State; } - set { Netplay.Clients[Index].State = value; } + get { return Client.State; } + set { Client.State = value; } } /// @@ -1014,7 +1014,7 @@ namespace TShockAPI /// public string UUID { - get { return RealPlayer ? Netplay.Clients[Index].ClientUUID : ""; } + get { return RealPlayer ? Client.ClientUUID : ""; } } /// @@ -1026,8 +1026,8 @@ namespace TShockAPI { if (string.IsNullOrEmpty(CacheIP)) return - CacheIP = RealPlayer ? (Netplay.Clients[Index].Socket.IsConnected() - ? TShock.Utils.GetRealIP(Netplay.Clients[Index].Socket.GetRemoteAddress().ToString()) + CacheIP = RealPlayer ? (Client.Socket.IsConnected() + ? TShock.Utils.GetRealIP(Client.Socket.GetRemoteAddress().ToString()) : "") : "127.0.0.1"; else @@ -1110,6 +1110,11 @@ namespace TShockAPI } + /// + /// Player RemoteClient. + /// + public RemoteClient Client => Netplay.Clients[Index]; + /// /// Gets the Terraria Player object associated with the player. /// @@ -2071,7 +2076,7 @@ namespace TShockAPI if (!RealPlayer || !ConnectionAlive) return; - Netplay.Clients[Index].Socket.AsyncSend(data, 0, data.Length, Netplay.Clients[Index].ServerWriteCallBack); + Client.Socket.AsyncSend(data, 0, data.Length, Client.ServerWriteCallBack); } /// From fa9e4419bd6ca6830a1b7684dfff168e549b0420 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 09:16:14 +0700 Subject: [PATCH 033/135] Added documentation to the `withMsg` parameter in the `SetPvP` method. --- TShockAPI/TSPlayer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index 10b2e760..f4cd0a3d 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -1852,6 +1852,7 @@ namespace TShockAPI /// Sets the player's pvp. /// /// The state of the pvp mode. + /// Whether a chat message about the change should be sent. public virtual void SetPvP(bool mode, bool withMsg = false) { Main.player[Index].hostile = mode; From 3647bbaf54d16ceead0a0ab96172d113f1d20d93 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 09:20:20 +0700 Subject: [PATCH 034/135] Added a reason for the `KillPlayer` and `DamagePlayer` methods. Added overloads that have a cause in the parameters (`PlayerDeathReason`) --- TShockAPI/TSPlayer.cs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index f4cd0a3d..6bfe0c44 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -1827,7 +1827,17 @@ namespace TShockAPI /// The amount of damage the player will take. public virtual void DamagePlayer(int damage) { - NetMessage.SendPlayerHurt(Index, PlayerDeathReason.LegacyDefault(), damage, (new Random()).Next(-1, 1), false, false, 0, -1, -1); + DamagePlayer(damage, PlayerDeathReason.LegacyDefault()); + } + + /// + /// Wounds the player with the given damage. + /// + /// The amount of damage the player will take. + /// The reason for causing damage to player. + public virtual void DamagePlayer(int damage, PlayerDeathReason reason) + { + NetMessage.SendPlayerHurt(Index, reason, damage, (new Random()).Next(-1, 1), false, false, 0, -1, -1); } /// @@ -1835,7 +1845,16 @@ namespace TShockAPI /// public virtual void KillPlayer() { - NetMessage.SendPlayerDeath(Index, PlayerDeathReason.LegacyDefault(), 99999, (new Random()).Next(-1, 1), false, -1, -1); + KillPlayer(PlayerDeathReason.LegacyDefault()); + } + + /// + /// Kills the player. + /// + /// Reason for killing a player. + public virtual void KillPlayer(PlayerDeathReason reason) + { + NetMessage.SendPlayerDeath(Index, reason, 99999, (new Random()).Next(-1, 1), false, -1, -1); } /// From d9352d690240ad34d142fae30b2e37c26e2e9b86 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 09:23:42 +0700 Subject: [PATCH 035/135] Added an exception that occurs when the developer changes team. --- TShockAPI/TSPlayer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index 6bfe0c44..284d9a31 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -1863,6 +1863,8 @@ namespace TShockAPI /// The team color index. public virtual void SetTeam(int team) { + if (team < 0 || team >= Main.teamColor.Length) + throw new ArgumentException("The player's team is not in the range of available."); Main.player[Index].team = team; NetMessage.SendData((int)PacketTypes.PlayerTeam, -1, -1, NetworkText.Empty, Index); } From 3d585d4d699d74c0f3f4cd569e00f7fae9f5324a Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 09:38:35 +0700 Subject: [PATCH 036/135] Added the `UpdateSection` method. I described its action in the comments. --- TShockAPI/TSPlayer.cs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index 284d9a31..79572412 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -1499,6 +1499,41 @@ namespace TShockAPI return false; } + /// + /// Changes the values of the array. + /// + /// The area of the sections you want to set a value to. + /// The minimum size should be set to 200x150. If null, then the entire map is specified. + /// Is the section loaded. + // The server does not send the player the whole world, it sends it in sections. To do this, it sets up visible and invisible sections. + // If the player was not in any section(Client.TileSections[x, y] == false) then the server will send the missing section of the world. + // This method allows you to simulate what the player has or has not seen these sections. + // For example, we can put some number of earths blocks in some vast area, for example, for the whole world, but the player will not see the changes, because some section is already loaded for him. At this point this method can come into effect! With it we will be able to select some zone and make it both visible and invisible to the player. + // The server will assume that the zone is not loaded on the player, and will resend the data, but with earth blocks. + public void UpdateSection(Rectangle? rectangle = null, bool isLoaded = false) + { + if (rectangle.HasValue) + { + for (int i = Netplay.GetSectionX(rectangle.Value.X); i < Netplay.GetSectionX(rectangle.Value.X + rectangle.Value.Width) && i < Main.maxSectionsX; i++) + { + for (int j = Netplay.GetSectionY(rectangle.Value.Y); j < Netplay.GetSectionY(rectangle.Value.Y + rectangle.Value.Height) && j < Main.maxSectionsY; j++) + { + Client.TileSections[i, j] = isLoaded; + } + } + } + else + { + for (int i = 0; i < Main.maxSectionsX; i++) + { + for (int j = 0; j < Main.maxSectionsY; j++) + { + Client.TileSections[i, j] = isLoaded; + } + } + } + } + /// /// Gives an item to the player. Includes banned item spawn prevention to check if the player can spawn the item. /// From 2f7514c0efc1c9411e7346bdb00852935c4a0533 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 09:43:18 +0700 Subject: [PATCH 037/135] Added an overload for `TSPlayer.GiveItem` Added `TShockAPI.NetItem` structure to the parameters. --- TShockAPI/TSPlayer.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index 79572412..8aadacdf 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -1566,6 +1566,15 @@ namespace TShockAPI GiveItemByDrop(type, stack, prefix); } + /// + /// Gives an item to the player. + /// + /// Item with data to be given to the player. + public virtual void GiveItem(NetItem item) + { + GiveItem(item.NetId, item.Stack, item.PrefixId); + } + private Item EmptySentinelItem = new Item(); private bool Depleted(Item item) From 4d92f11cc805a52906abf974c6caf043035f5b4c Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 09:56:47 +0700 Subject: [PATCH 038/135] Added the `TSPlayer.Hostile` property. It gets `TPlayer.hostile`. I also wanted to add the ability to change `TPlayer.hostile`, but noticed a property `TSPlayer.Team`. You can only use the `TSPlayer.SetTeam` method to change it. So it's exactly the same here: You can use the method `TSPlayer.SetPvP` (By the way, it should be renamed to `TSPlayer.SetHostile`) --- TShockAPI/TSPlayer.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index 8aadacdf..c9194c36 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -1147,6 +1147,11 @@ namespace TShockAPI get { return TPlayer.team; } } + /// + /// Gets PvP player mode. + /// + public bool Hostile => TPlayer.hostile; + /// /// Gets the player's X coordinate. /// From b30a8cb6b2c4f1d5fff8b12e1ce58c01e70c57b9 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 10:03:55 +0700 Subject: [PATCH 039/135] Update changelog.md --- docs/changelog.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 3f64f355..f02867f8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -78,7 +78,16 @@ Use past tense when adding new entries; sign your name off when you add or chang * If there is no section called "Upcoming changes" below this line, please add one with `## Upcoming changes` as the first line, and then a bulleted item directly after with the first change. --> ## Upcoming changes -Your changes could be here! +* Updated `TSPlayer.GodMode`. (@AgaSpace) + * Previously the field was used as some kind of dataset changed by /godmode command, but now it is a property that receives/changes data in journey mode. +* Added the `TSPlayer.Client` property. It allows the developer to get the `RemoteClient` player, without an additional call to `Terraria.Netplay.Clients`. (@AgaSpace) +* Updated the documentation for the `TSPlayer.SetPvP` method. The `sendMsg` parameter, which is responsible for sending a pvp mode change message, was not documented earlier. (@AgaSpace) +* Added methods `TSPlayer.KillPlayer` and `TSPlayer.DamagePlayer` for which you can specify the cause (`PlayerDeathReason`) in parameters. +* Added an error when trying to change a `TSPlayer` team to, say, 9, when there are only 6. (@AgaSpace) +* Added an error when trying to call the `TSPlayer.SetTeam` method with an argument (team) greater than 5 or less than 0. (@AgaSpace) +* Added a method `TSPlayer.UpdateSection` with arguments `rectangle` and `isLoaded`, which will load some area from the server to the player. (@AgaSpace) +* Added a method `TSPlayer.GiveItem`, which has `TShockAPI.NetItem` structure in its arguments. (@AgaSpace) +* Added a property `TSPlayer.Hostile`, which gets pvp player mode. ## TShock 5.2 * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) From c3f5994451b02a0b93230b41519f34fa97ac9a9a Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 10:08:02 +0700 Subject: [PATCH 040/135] Added optional arguments `stack` and `prefixId` to the `NetItem` constructor --- TShockAPI/NetItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TShockAPI/NetItem.cs b/TShockAPI/NetItem.cs index 278fda1c..03596a1f 100644 --- a/TShockAPI/NetItem.cs +++ b/TShockAPI/NetItem.cs @@ -155,7 +155,7 @@ namespace TShockAPI /// The net ID. /// The stack. /// The prefix ID. - public NetItem(int netId, int stack, byte prefixId) + public NetItem(int netId, int stack = 1, byte prefixId = 0) { _netId = netId; _stack = stack; From 62b8e5067ce3d97055ff7ed8063d9994909816c4 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 10:13:25 +0700 Subject: [PATCH 041/135] Added the `NetItem.Build' method. The method will create a Terraria.Item instance based on the data from the structure. --- TShockAPI/NetItem.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/TShockAPI/NetItem.cs b/TShockAPI/NetItem.cs index 03596a1f..095d3f1c 100644 --- a/TShockAPI/NetItem.cs +++ b/TShockAPI/NetItem.cs @@ -162,6 +162,24 @@ namespace TShockAPI _prefixId = prefixId; } + /// + /// Creates based on data from this structure. + /// + /// A copy of the item. + /// If the item ID is 0. + public Item Build() + { + if (_netId == 0) + throw new Exception("It is impossible to create an item whose ID is 0."); + Item item = new Item(); + + item.netDefaults(_netId); + item.stack = _stack; + item.prefix = _prefixId; + + return item; + } + /// /// Converts the to a string. /// From 86be1738ccc2f6d8930d4fe3dea411acc310dd49 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 10:13:59 +0700 Subject: [PATCH 042/135] Added a constructor with arguments taking `Terraria.Item`. --- TShockAPI/NetItem.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/TShockAPI/NetItem.cs b/TShockAPI/NetItem.cs index 095d3f1c..86fe82a7 100644 --- a/TShockAPI/NetItem.cs +++ b/TShockAPI/NetItem.cs @@ -162,6 +162,17 @@ namespace TShockAPI _prefixId = prefixId; } + /// + /// Creates a new . + /// + /// Item in the game. + public NetItem(Item item) + { + _netId = item.netID; + _stack = item.stack; + _prefixId = item.prefix; + } + /// /// Creates based on data from this structure. /// From 7ab05707869366b884cce0680efee356cfc6ea7e Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 10:22:39 +0700 Subject: [PATCH 043/135] Update changelog.md --- docs/changelog.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 3f64f355..54e6ff66 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -78,7 +78,10 @@ Use past tense when adding new entries; sign your name off when you add or chang * If there is no section called "Upcoming changes" below this line, please add one with `## Upcoming changes` as the first line, and then a bulleted item directly after with the first change. --> ## Upcoming changes -Your changes could be here! +* Updated `TShockAPI.NetItem` (@AgaSpace): + * Added constructor overload with parameter `Terraria.Item`. + * Added the `Build` method to get a copy of `Terraria.Item`. + * In the constructor I added optional parameters `stack` and `prefix`. ## TShock 5.2 * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) From 2e8823434c298e3052674add48d54b83fb658cae Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 10:30:52 +0700 Subject: [PATCH 044/135] Updated the PlayerData constructors. Added a new constructor with a parameter that is responsible for installing TShock items into inventory. The `TSPlayer` parameter was not used, so I labeled the constructor obsolete. --- TShockAPI/PlayerData.cs | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/TShockAPI/PlayerData.cs b/TShockAPI/PlayerData.cs index 2a8dd41f..aae27672 100644 --- a/TShockAPI/PlayerData.cs +++ b/TShockAPI/PlayerData.cs @@ -23,6 +23,7 @@ using Terraria.Localization; using Terraria.GameContent.NetModules; using Terraria.Net; using Terraria.ID; +using System; namespace TShockAPI { @@ -63,18 +64,27 @@ namespace TShockAPI public int unlockedSuperCart; public int enabledSuperCart; - public PlayerData(TSPlayer player) + /// + /// Sets the default values for the inventory. + /// + [Obsolete("The player argument is not used.")] + public PlayerData(TSPlayer player) : this(true) { } + + /// + /// Sets the default values for the inventory. + /// + /// Is it necessary to load items from TShock's config + public PlayerData(bool includingStarterInventory = true) { for (int i = 0; i < NetItem.MaxInventory; i++) - { this.inventory[i] = new NetItem(); - } - for (int i = 0; i < TShock.ServerSideCharacterConfig.Settings.StartingInventory.Count; i++) - { - var item = TShock.ServerSideCharacterConfig.Settings.StartingInventory[i]; - StoreSlot(i, item.NetId, item.PrefixId, item.Stack); - } + if (includingStarterInventory) + for (int i = 0; i < TShock.ServerSideCharacterConfig.Settings.StartingInventory.Count; i++) + { + var item = TShock.ServerSideCharacterConfig.Settings.StartingInventory[i]; + StoreSlot(i, item.NetId, item.PrefixId, item.Stack); + } } /// From 763519150a5a4ab4fdca7435cdf0dfa8c3d3e246 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 10:33:54 +0700 Subject: [PATCH 045/135] Updated the `PlayerData.StoreSlot` method Removed the ability to call a method when a slot less than 0 is specified. Added an overload that takes `NetItem` in parameters. --- TShockAPI/PlayerData.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/TShockAPI/PlayerData.cs b/TShockAPI/PlayerData.cs index aae27672..b2866eb1 100644 --- a/TShockAPI/PlayerData.cs +++ b/TShockAPI/PlayerData.cs @@ -96,12 +96,22 @@ namespace TShockAPI /// public void StoreSlot(int slot, int netID, byte prefix, int stack) { - if (slot > (this.inventory.Length - 1)) //if the slot is out of range then dont save + StoreSlot(slot, new NetItem(netID, stack, prefix)); + } + + /// + /// Stores an item at the specific storage slot + /// + /// + /// + public void StoreSlot(int slot, NetItem item) + { + if (slot > (this.inventory.Length - 1) || slot < 0) //if the slot is out of range then dont save { return; } - this.inventory[slot] = new NetItem(netID, stack, prefix); + this.inventory[slot] = item; } /// From b184133a7f8594e84bf8dca16d0c1525715477fd Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 10:43:01 +0700 Subject: [PATCH 046/135] Updated the call to the obsolete constructor `PlayerData` --- TShockAPI/DB/CharacterManager.cs | 2 +- TShockAPI/GetDataHandlers.cs | 4 ++-- TShockAPI/TSPlayer.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/TShockAPI/DB/CharacterManager.cs b/TShockAPI/DB/CharacterManager.cs index 5a5e13a6..357dc746 100644 --- a/TShockAPI/DB/CharacterManager.cs +++ b/TShockAPI/DB/CharacterManager.cs @@ -79,7 +79,7 @@ namespace TShockAPI.DB public PlayerData GetPlayerData(TSPlayer player, int acctid) { - PlayerData playerData = new PlayerData(player); + PlayerData playerData = new PlayerData(false); try { diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index 1ef2f4f8..f82b7a8e 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -2619,9 +2619,9 @@ namespace TShockAPI private static bool HandleConnecting(GetDataHandlerArgs args) { var account = TShock.UserAccounts.GetUserAccountByName(args.Player.Name);// - args.Player.DataWhenJoined = new PlayerData(args.Player); + args.Player.DataWhenJoined = new PlayerData(false); args.Player.DataWhenJoined.CopyCharacter(args.Player); - args.Player.PlayerData = new PlayerData(args.Player); + args.Player.PlayerData = new PlayerData(false); args.Player.PlayerData.CopyCharacter(args.Player); if (account != null && !TShock.Config.Settings.DisableUUIDLogin) diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index ef5c3c22..afbd3176 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -1267,7 +1267,7 @@ namespace TShockAPI } } - PlayerData = new PlayerData(this); + PlayerData = new PlayerData(); Group = TShock.Groups.GetGroupByName(TShock.Config.Settings.DefaultGuestGroupName); tempGroup = null; if (tempGroupTimer != null) From 8ccb3c6210077c8ddaf90dcc799ac096a0362b39 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 10:43:12 +0700 Subject: [PATCH 047/135] Update changelog.md --- docs/changelog.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 3f64f355..93485b3f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -78,7 +78,9 @@ Use past tense when adding new entries; sign your name off when you add or chang * If there is no section called "Upcoming changes" below this line, please add one with `## Upcoming changes` as the first line, and then a bulleted item directly after with the first change. --> ## Upcoming changes -Your changes could be here! +* Added a constructor for `TShockAPI.PlayerData` that accepts the `includingStarterInventory` parameter, which is responsible for loading the TShock inventory. +* Declared the constructor `TShockAPI.PlayerData` accepting the argument `TShockAPI.TSPlayer` obsolete. +* Updated the `PlayerData.StoreSlot` method: Added an overload that takes `TShockAPI.NetItem`. ## TShock 5.2 * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) From 22d8575e01bad823c7e6fac60e24413d77bde990 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 12:04:17 +0700 Subject: [PATCH 048/135] Corrected the `UserAccountNotExistException` documentation. --- TShockAPI/DB/UserManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TShockAPI/DB/UserManager.cs b/TShockAPI/DB/UserManager.cs index 0354989e..7b8fe4a2 100644 --- a/TShockAPI/DB/UserManager.cs +++ b/TShockAPI/DB/UserManager.cs @@ -619,7 +619,7 @@ namespace TShockAPI.DB public class UserAccountNotExistException : UserAccountManagerException { /// Creates a new UserAccountNotExistException object, with the user account name in the message. - /// The user account name to be pasesd in the message. + /// The user account name to be passed in the message. /// A new UserAccountNotExistException object with a message containing the user account name that does not exist. public UserAccountNotExistException(string name) : base(GetString($"User account {name} does not exist")) From 8a0920b6eabadb40bc43cb65a4eb565366ff0f75 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 12:08:11 +0700 Subject: [PATCH 049/135] Added a hook `AccountHooks.AccountGroupUpdate`. --- TShockAPI/Hooks/AccountHooks.cs | 52 +++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/TShockAPI/Hooks/AccountHooks.cs b/TShockAPI/Hooks/AccountHooks.cs index 9c08b26d..e224b467 100644 --- a/TShockAPI/Hooks/AccountHooks.cs +++ b/TShockAPI/Hooks/AccountHooks.cs @@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +using System.ComponentModel; using TShockAPI.DB; namespace TShockAPI.Hooks { @@ -39,6 +40,37 @@ namespace TShockAPI.Hooks } } + public abstract class AccountGroupUpdateEventArgs : HandledEventArgs + { + public string AccountName { get; private set; } + public Group Group { get; set; } + + public AccountGroupUpdateEventArgs(string accountName, Group group) + { + this.AccountName = accountName; + this.Group = group; + } + } + + public class AccountGroupUpdateByPluginEventArgs : AccountGroupUpdateEventArgs + { + public AccountGroupUpdateByPluginEventArgs(string accountName, Group group) : base(accountName, group) + { + } + } + public class AccountGroupUpdateByPlayerEventArgs : AccountGroupUpdateEventArgs + { + /// + /// The player who updated the user's group + /// + public TSPlayer Player { get; private set; } + + public AccountGroupUpdateByPlayerEventArgs(TSPlayer player, string accountName, Group group) : base(accountName, group) + { + this.Player = player; + } + } + public class AccountHooks { public delegate void AccountCreateD(AccountCreateEventArgs e); @@ -62,5 +94,25 @@ namespace TShockAPI.Hooks AccountDelete(new AccountDeleteEventArgs(u)); } + + public delegate void AccountGroupUpdateD(AccountGroupUpdateEventArgs e); + public static event AccountGroupUpdateD AccountGroupUpdate; + + public static bool OnAccountGroupUpdate(UserAccount account, TSPlayer author, ref Group group) + { + AccountGroupUpdateEventArgs args = new AccountGroupUpdateByPlayerEventArgs(author, account.Name, group); + AccountGroupUpdate?.Invoke(args); + group = args.Group; + + return args.Handled; + } + public static bool OnAccountGroupUpdate(UserAccount account, ref Group group) + { + AccountGroupUpdateEventArgs args = new AccountGroupUpdateByPluginEventArgs(account.Name, group); + AccountGroupUpdate?.Invoke(args); + group = args.Group; + + return args.Handled; + } } } From 1e037748c1d511fdc3449ff6261a55a049b4e96d Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 12:13:56 +0700 Subject: [PATCH 050/135] Updated the `UserManager.SetUserGroup`. Added an exception `UserGroupUpdateLockedException`, which appears when a hook locks a group change. Added an overload for `UserManager.SetUserGroup`, with the `TSPlayer` parameter (author) --- TShockAPI/DB/UserManager.cs | 51 ++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/TShockAPI/DB/UserManager.cs b/TShockAPI/DB/UserManager.cs index 7b8fe4a2..6411b289 100644 --- a/TShockAPI/DB/UserManager.cs +++ b/TShockAPI/DB/UserManager.cs @@ -25,6 +25,7 @@ using MySql.Data.MySqlClient; using System.Text.RegularExpressions; using BCrypt.Net; using System.Security.Cryptography; +using TShockAPI.Hooks; namespace TShockAPI.DB { @@ -166,7 +167,41 @@ namespace TShockAPI.DB if (null == grp) throw new GroupNotExistsException(group); - if (_database.Query("UPDATE Users SET UserGroup = @0 WHERE Username = @1;", group, account.Name) == 0) + if (AccountHooks.OnAccountGroupUpdate(account, ref grp)) + throw new UserGroupUpdateLockedException(account.Name); + + if (_database.Query("UPDATE Users SET UserGroup = @0 WHERE Username = @1;", grp.Name, account.Name) == 0) + throw new UserAccountNotExistException(account.Name); + + try + { + // Update player group reference for any logged in player + foreach (var player in TShock.Players.Where(p => p != null && p.Account != null && p.Account.Name == account.Name)) + { + player.Group = grp; + } + } + catch (Exception ex) + { + throw new UserAccountManagerException(GetString("SetUserGroup SQL returned an error"), ex); + } + } + /// + /// Sets the group for a given username + /// + /// Who changes the group + /// The user account + /// The user account group to be set + public void SetUserGroup(TSPlayer author, UserAccount account, string group) + { + Group grp = TShock.Groups.GetGroupByName(group); + if (null == grp) + throw new GroupNotExistsException(group); + + if (AccountHooks.OnAccountGroupUpdate(account, author, ref grp)) + throw new UserGroupUpdateLockedException(account.Name); + + if (_database.Query("UPDATE Users SET UserGroup = @0 WHERE Username = @1;", grp.Name, account.Name) == 0) throw new UserAccountNotExistException(account.Name); try @@ -627,6 +662,20 @@ namespace TShockAPI.DB } } + /// The UserGroupUpdateLockedException used when the user group update failed and the request failed as a result.. + [Serializable] + public class UserGroupUpdateLockedException : UserAccountManagerException + { + /// Creates a new UserGroupUpdateLockedException object. + /// The name of the user who failed to change the group. + /// New UserGroupUpdateLockedException object with a message containing the name of the user account that failed to change the group. + public UserGroupUpdateLockedException(string name) : + base(GetString($"The user {name} group could not be updated")) + { + } + } + + /// A GroupNotExistsException, used when a group does not exist. [Serializable] public class GroupNotExistsException : UserAccountManagerException From 230d9b094591b196b40aff2905e90d8bf88ec9f8 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 12:17:47 +0700 Subject: [PATCH 051/135] Updated the `SetUserGroup` in the commands. --- TShockAPI/Commands.cs | 6 +++++- TShockAPI/Rest/RestManager.cs | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs index 2214f252..164bda89 100644 --- a/TShockAPI/Commands.cs +++ b/TShockAPI/Commands.cs @@ -1176,7 +1176,7 @@ namespace TShockAPI try { - TShock.UserAccounts.SetUserGroup(account, args.Parameters[2]); + TShock.UserAccounts.SetUserGroup(args.Player, account, args.Parameters[2]); TShock.Log.ConsoleInfo(GetString("{0} changed account {1} to group {2}.", args.Player.Name, account.Name, args.Parameters[2])); args.Player.SendSuccessMessage(GetString("Account {0} has been changed to group {1}.", account.Name, args.Parameters[2])); @@ -1193,6 +1193,10 @@ namespace TShockAPI { args.Player.SendErrorMessage(GetString($"User {account.Name} does not exist.")); } + catch (UserGroupUpdateLockedException) + { + args.Player.SendErrorMessage(GetString("Hook blocked the attempt to change the user group.")); + } catch (UserAccountManagerException e) { args.Player.SendErrorMessage(GetString($"User {account.Name} could not be added. Check console for details.")); diff --git a/TShockAPI/Rest/RestManager.cs b/TShockAPI/Rest/RestManager.cs index c41e7767..0a96b004 100644 --- a/TShockAPI/Rest/RestManager.cs +++ b/TShockAPI/Rest/RestManager.cs @@ -555,7 +555,8 @@ namespace TShockAPI { try { - TShock.UserAccounts.SetUserGroup(account, group); + TShock.UserAccounts.SetUserGroup(new TSRestPlayer(args.TokenData.Username, TShock.Groups.GetGroupByName(args.TokenData.UserGroupName)), + account, group); response.Add("group-response", "Group updated successfully"); } catch (Exception e) From 4e85c5ddac099fad4ecfb4ae472854d526729d2a Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sun, 14 May 2023 12:19:05 +0700 Subject: [PATCH 052/135] Update changelog.md --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 3f64f355..ebfcd895 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -78,7 +78,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * If there is no section called "Upcoming changes" below this line, please add one with `## Upcoming changes` as the first line, and then a bulleted item directly after with the first change. --> ## Upcoming changes -Your changes could be here! +* Added a hook `AccountHooks.AccountGroupUpdate`, which is called when you change the user group. (@AgaSpace) ## TShock 5.2 * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) From cc753cf1dac747db6dc613d9c0b7faed9045fdda Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Tue, 6 Jun 2023 16:00:18 +0700 Subject: [PATCH 053/135] Removed unnecessary abstraction. --- TShockAPI/Hooks/AccountHooks.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/TShockAPI/Hooks/AccountHooks.cs b/TShockAPI/Hooks/AccountHooks.cs index e224b467..ae9fff24 100644 --- a/TShockAPI/Hooks/AccountHooks.cs +++ b/TShockAPI/Hooks/AccountHooks.cs @@ -40,7 +40,7 @@ namespace TShockAPI.Hooks } } - public abstract class AccountGroupUpdateEventArgs : HandledEventArgs + public class AccountGroupUpdateEventArgs : HandledEventArgs { public string AccountName { get; private set; } public Group Group { get; set; } @@ -52,12 +52,6 @@ namespace TShockAPI.Hooks } } - public class AccountGroupUpdateByPluginEventArgs : AccountGroupUpdateEventArgs - { - public AccountGroupUpdateByPluginEventArgs(string accountName, Group group) : base(accountName, group) - { - } - } public class AccountGroupUpdateByPlayerEventArgs : AccountGroupUpdateEventArgs { /// @@ -108,7 +102,7 @@ namespace TShockAPI.Hooks } public static bool OnAccountGroupUpdate(UserAccount account, ref Group group) { - AccountGroupUpdateEventArgs args = new AccountGroupUpdateByPluginEventArgs(account.Name, group); + AccountGroupUpdateEventArgs args = new AccountGroupUpdateEventArgs(account.Name, group); AccountGroupUpdate?.Invoke(args); group = args.Group; From f18243242fcf04a15ede101db6ed827544b0e165 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Tue, 6 Jun 2023 16:12:05 +0700 Subject: [PATCH 054/135] Update Group.cs --- TShockAPI/Group.cs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/TShockAPI/Group.cs b/TShockAPI/Group.cs index ff2ba2e9..0858dfd4 100644 --- a/TShockAPI/Group.cs +++ b/TShockAPI/Group.cs @@ -52,17 +52,17 @@ namespace TShockAPI /// /// The group that this group inherits permissions from. /// - public Group Parent { get; set; } + public virtual Group Parent { get; set; } /// /// The chat prefix for this group. /// - public string Prefix { get; set; } + public virtual string Prefix { get; set; } /// /// The chat suffix for this group. /// - public string Suffix { get; set; } + public virtual string Suffix { get; set; } /// /// The name of the parent, not particularly sure why this is here. @@ -154,15 +154,15 @@ namespace TShockAPI /// /// The group's chat color red byte. /// - public byte R = 255; + public virtual byte R { get; set; } = 255; /// /// The group's chat color green byte. /// - public byte G = 255; + public virtual byte G { get; set; } = 255; /// /// The group's chat color blue byte. /// - public byte B = 255; + public virtual byte B { get; set; } = 255; /// /// The default group attributed to unregistered users. @@ -242,7 +242,7 @@ namespace TShockAPI /// Adds a permission to the list of negated permissions. /// /// The permission to negate. - public void NegatePermission(string permission) + public virtual void NegatePermission(string permission) { // Avoid duplicates if (!negatedpermissions.Contains(permission)) @@ -256,7 +256,7 @@ namespace TShockAPI /// Adds a permission to the list of permissions. /// /// The permission to add. - public void AddPermission(string permission) + public virtual void AddPermission(string permission) { if (permission.StartsWith("!")) { @@ -276,7 +276,7 @@ namespace TShockAPI /// will parse "!permission" and add it to the negated permissions. /// /// The new list of permissions to associate with the group. - public void SetPermission(List permission) + public virtual void SetPermission(List permission) { permissions.Clear(); negatedpermissions.Clear(); @@ -288,7 +288,7 @@ namespace TShockAPI /// where "!permission" will remove a negated permission. /// /// - public void RemovePermission(string permission) + public virtual void RemovePermission(string permission) { if (permission.StartsWith("!")) { @@ -302,7 +302,7 @@ namespace TShockAPI /// Assigns all fields of this instance to another. /// /// The other instance. - public void AssignTo(Group otherGroup) + public virtual void AssignTo(Group otherGroup) { otherGroup.Name = Name; otherGroup.Parent = Parent; From 121afa963cbff2910f4418851371700dba124651 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Tue, 6 Jun 2023 16:18:48 +0700 Subject: [PATCH 055/135] Added a `Group.Color` --- TShockAPI/Group.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/TShockAPI/Group.cs b/TShockAPI/Group.cs index 0858dfd4..f0d64b2d 100644 --- a/TShockAPI/Group.cs +++ b/TShockAPI/Group.cs @@ -20,6 +20,8 @@ using System; using System.Linq; using System.Collections.Generic; +using Microsoft.Xna.Framework; + namespace TShockAPI { /// @@ -164,6 +166,17 @@ namespace TShockAPI /// public virtual byte B { get; set; } = 255; + public virtual Color Color + { + get => new Color(R, G, B); + set + { + R = value.R; + G = value.G; + B = value.B; + } + } + /// /// The default group attributed to unregistered users. /// From 73e2440043f172355d537a1ef1fe5a8159caa939 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Tue, 6 Jun 2023 16:21:52 +0700 Subject: [PATCH 056/135] Added XML documentation. --- TShockAPI/Group.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TShockAPI/Group.cs b/TShockAPI/Group.cs index f0d64b2d..0867a8a0 100644 --- a/TShockAPI/Group.cs +++ b/TShockAPI/Group.cs @@ -166,6 +166,9 @@ namespace TShockAPI /// public virtual byte B { get; set; } = 255; + /// + /// Simplifies work with the , , properties. + /// public virtual Color Color { get => new Color(R, G, B); From 07bf66f072aef44c43559ac424ecb3990c4d4beb Mon Sep 17 00:00:00 2001 From: punchready <22683812+punchready@users.noreply.github.com> Date: Tue, 6 Jun 2023 13:25:03 +0200 Subject: [PATCH 057/135] Fix MatchPlacement allowing auto breakable tiles --- TShockAPI/Handlers/SendTileRectHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TShockAPI/Handlers/SendTileRectHandler.cs b/TShockAPI/Handlers/SendTileRectHandler.cs index 64d9d8a7..3aa4b14c 100644 --- a/TShockAPI/Handlers/SendTileRectHandler.cs +++ b/TShockAPI/Handlers/SendTileRectHandler.cs @@ -266,7 +266,7 @@ namespace TShockAPI.Handlers { for (int y = rect.Y; y < rect.Y + rect.Height; y++) { - if (Main.tile[x, y].active() && !(Main.tile[x, y].type != TileID.RollingCactus && (Main.tileCut[Main.tile[x, y].type] || TileID.Sets.BreakableWhenPlacing[Main.tile[x, y].type]))) + if (Main.tile[x, y].active()) // the client will kill tiles that auto break before placing the object { return false; } From 914d7432646080ab7eae8d0ccebd4c375f7c7439 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Tue, 6 Jun 2023 19:15:58 +0700 Subject: [PATCH 058/135] Changed the method from `NetItem.Build` to `NetItem.ToItem`. --- TShockAPI/NetItem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TShockAPI/NetItem.cs b/TShockAPI/NetItem.cs index 86fe82a7..0a3e0c99 100644 --- a/TShockAPI/NetItem.cs +++ b/TShockAPI/NetItem.cs @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ - using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -178,7 +178,7 @@ namespace TShockAPI /// /// A copy of the item. /// If the item ID is 0. - public Item Build() + public Item ToItem() { if (_netId == 0) throw new Exception("It is impossible to create an item whose ID is 0."); From 149ca8a70cdc7b2994ed1584c25230ff7378b0ba Mon Sep 17 00:00:00 2001 From: punchready <22683812+punchready@users.noreply.github.com> Date: Wed, 7 Jun 2023 03:05:10 +0200 Subject: [PATCH 059/135] Remove duplicate conversion spread matching --- TShockAPI/Handlers/SendTileRectHandler.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/TShockAPI/Handlers/SendTileRectHandler.cs b/TShockAPI/Handlers/SendTileRectHandler.cs index 3aa4b14c..d85185b8 100644 --- a/TShockAPI/Handlers/SendTileRectHandler.cs +++ b/TShockAPI/Handlers/SendTileRectHandler.cs @@ -506,11 +506,7 @@ namespace TShockAPI.Handlers } // a few special cases - if ( - MatchesConversionSpread(args.Player, rect) || - MatchesGrassMow(args.Player, rect) || - MatchesChristmasTree(args.Player, rect) - ) + if (MatchesGrassMow(args.Player, rect) || MatchesChristmasTree(args.Player, rect)) { TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}")); From a917beaca0ae3f63774fbb212bce51cd5d0f41e5 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Wed, 7 Jun 2023 23:44:04 +0700 Subject: [PATCH 060/135] Update changelog.md --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 3f64f355..ec7f000f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -78,7 +78,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * If there is no section called "Upcoming changes" below this line, please add one with `## Upcoming changes` as the first line, and then a bulleted item directly after with the first change. --> ## Upcoming changes -Your changes could be here! +* Added more modification options to `Group`. (@AgaSpace) ## TShock 5.2 * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) From dc817911346ac7c82180553d53a45d3325c5b5ab Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Thu, 8 Jun 2023 12:41:11 +0700 Subject: [PATCH 061/135] Removed RGB properties --- TShockAPI/Group.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TShockAPI/Group.cs b/TShockAPI/Group.cs index 0867a8a0..45086eef 100644 --- a/TShockAPI/Group.cs +++ b/TShockAPI/Group.cs @@ -156,15 +156,15 @@ namespace TShockAPI /// /// The group's chat color red byte. /// - public virtual byte R { get; set; } = 255; + public byte R = 255; /// /// The group's chat color green byte. /// - public virtual byte G { get; set; } = 255; + public byte G = 255; /// /// The group's chat color blue byte. /// - public virtual byte B { get; set; } = 255; + public byte B = 255; /// /// Simplifies work with the , , properties. From 3a90029d5199e724baeaf04a1aea010b9be3e103 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Thu, 8 Jun 2023 16:33:32 +0700 Subject: [PATCH 062/135] Allowed to receive an item if it is 0. --- TShockAPI/NetItem.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/TShockAPI/NetItem.cs b/TShockAPI/NetItem.cs index 0a3e0c99..13f3f49d 100644 --- a/TShockAPI/NetItem.cs +++ b/TShockAPI/NetItem.cs @@ -180,8 +180,6 @@ namespace TShockAPI /// If the item ID is 0. public Item ToItem() { - if (_netId == 0) - throw new Exception("It is impossible to create an item whose ID is 0."); Item item = new Item(); item.netDefaults(_netId); From b3f314985070afc450da441524698c1640d6dccc Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Thu, 8 Jun 2023 16:34:06 +0700 Subject: [PATCH 063/135] Update changelog.md --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 54e6ff66..e02dcbdc 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -80,7 +80,7 @@ Use past tense when adding new entries; sign your name off when you add or chang ## Upcoming changes * Updated `TShockAPI.NetItem` (@AgaSpace): * Added constructor overload with parameter `Terraria.Item`. - * Added the `Build` method to get a copy of `Terraria.Item`. + * Added the `ToItem` method to get a copy of `Terraria.Item`. * In the constructor I added optional parameters `stack` and `prefix`. ## TShock 5.2 From 4eb2aa49de40e5c64b4dc09acacdf554634737c6 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Thu, 8 Jun 2023 18:11:26 +0700 Subject: [PATCH 064/135] Removed unnecessary documentation --- TShockAPI/NetItem.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/TShockAPI/NetItem.cs b/TShockAPI/NetItem.cs index 13f3f49d..0f0dc6e3 100644 --- a/TShockAPI/NetItem.cs +++ b/TShockAPI/NetItem.cs @@ -177,7 +177,6 @@ namespace TShockAPI /// Creates based on data from this structure. /// /// A copy of the item. - /// If the item ID is 0. public Item ToItem() { Item item = new Item(); From 5498f84942c5487b651214db20a331c7f7b87dc6 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Thu, 8 Jun 2023 18:12:25 +0700 Subject: [PATCH 065/135] Update changelog.md --- docs/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index e02dcbdc..d1f486b2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -81,7 +81,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Updated `TShockAPI.NetItem` (@AgaSpace): * Added constructor overload with parameter `Terraria.Item`. * Added the `ToItem` method to get a copy of `Terraria.Item`. - * In the constructor I added optional parameters `stack` and `prefix`. + * In the constructor `stack` and `prefix` are now optional parameters. ## TShock 5.2 * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) From b5b72e9f6c4a6d439ddc158fb6d01fb8391a0257 Mon Sep 17 00:00:00 2001 From: James Puleo Date: Fri, 9 Jun 2023 03:00:45 -0400 Subject: [PATCH 066/135] Ensure `TSPlayer.PlayerData` is non-null whilst syncing loadouts During connection, we receive a `SyncLoadout` packet before the `ContinueConnecting2` packet, meaning we have not yet created a `PlayerData` for this player. --- TShockAPI/GetDataHandlers.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index 1ef2f4f8..b736a512 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -4445,6 +4445,11 @@ namespace TShockAPI return true; } + // Don't modify the player data if it isn't there. + // This is the case whilst the player is connecting, as we receive the SyncLoadout packet before the ContinueConnecting2 packet. + if (args.Player.PlayerData == null) + return false; + // The client does not sync slot changes when changing loadouts, it only tells the server the loadout index changed, // and the server will replicate the changes the client did. This means that PlayerData.StoreSlot is never called, so we need to // swap around the PlayerData items ourself. From fbc1d9dc8d792f3b9511736276ca40e8d7b35184 Mon Sep 17 00:00:00 2001 From: James Puleo Date: Fri, 9 Jun 2023 03:05:43 -0400 Subject: [PATCH 067/135] Update `docs/changelog.md` --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index f02867f8..d329a6df 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -88,6 +88,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Added a method `TSPlayer.UpdateSection` with arguments `rectangle` and `isLoaded`, which will load some area from the server to the player. (@AgaSpace) * Added a method `TSPlayer.GiveItem`, which has `TShockAPI.NetItem` structure in its arguments. (@AgaSpace) * Added a property `TSPlayer.Hostile`, which gets pvp player mode. +* Ensured `TSPlayer.PlayerData` is non-null whilst syncing loadouts. (@drunderscore) ## TShock 5.2 * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) From 72ecc472187dde708ea2a2d1176e3b9872cd27f7 Mon Sep 17 00:00:00 2001 From: James Puleo Date: Fri, 9 Jun 2023 05:52:19 -0400 Subject: [PATCH 068/135] Update `docs/changelog.md` (#2953) --- docs/changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index f02867f8..89e4d39c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -82,12 +82,12 @@ Use past tense when adding new entries; sign your name off when you add or chang * Previously the field was used as some kind of dataset changed by /godmode command, but now it is a property that receives/changes data in journey mode. * Added the `TSPlayer.Client` property. It allows the developer to get the `RemoteClient` player, without an additional call to `Terraria.Netplay.Clients`. (@AgaSpace) * Updated the documentation for the `TSPlayer.SetPvP` method. The `sendMsg` parameter, which is responsible for sending a pvp mode change message, was not documented earlier. (@AgaSpace) -* Added methods `TSPlayer.KillPlayer` and `TSPlayer.DamagePlayer` for which you can specify the cause (`PlayerDeathReason`) in parameters. +* Added methods `TSPlayer.KillPlayer` and `TSPlayer.DamagePlayer` for which you can specify the cause (`PlayerDeathReason`) in parameters. (@AgaSpace) * Added an error when trying to change a `TSPlayer` team to, say, 9, when there are only 6. (@AgaSpace) * Added an error when trying to call the `TSPlayer.SetTeam` method with an argument (team) greater than 5 or less than 0. (@AgaSpace) * Added a method `TSPlayer.UpdateSection` with arguments `rectangle` and `isLoaded`, which will load some area from the server to the player. (@AgaSpace) * Added a method `TSPlayer.GiveItem`, which has `TShockAPI.NetItem` structure in its arguments. (@AgaSpace) -* Added a property `TSPlayer.Hostile`, which gets pvp player mode. +* Added a property `TSPlayer.Hostile`, which gets pvp player mode. (@AgaSpace) ## TShock 5.2 * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) From a6666ff21abf1d40133a98eb5dfe2e5e0c6b8c58 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Sat, 10 Jun 2023 13:59:38 +0700 Subject: [PATCH 069/135] Updated the message --- TShockAPI/DB/UserManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TShockAPI/DB/UserManager.cs b/TShockAPI/DB/UserManager.cs index 6411b289..6fc50d98 100644 --- a/TShockAPI/DB/UserManager.cs +++ b/TShockAPI/DB/UserManager.cs @@ -670,7 +670,7 @@ namespace TShockAPI.DB /// The name of the user who failed to change the group. /// New UserGroupUpdateLockedException object with a message containing the name of the user account that failed to change the group. public UserGroupUpdateLockedException(string name) : - base(GetString($"The user {name} group could not be updated")) + base(GetString($"Unable to update group of user {name}.")) { } } From 5bcde689d4262ae18ca639e4a9895d078ba31cc4 Mon Sep 17 00:00:00 2001 From: SGKoishi Date: Sun, 11 Jun 2023 22:11:20 -0700 Subject: [PATCH 070/135] Fix a typo in gbuff --- TShockAPI/Commands.cs | 2 +- docs/changelog.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs index 3d1767ea..a4669b04 100644 --- a/TShockAPI/Commands.cs +++ b/TShockAPI/Commands.cs @@ -6462,7 +6462,7 @@ namespace TShockAPI if (target == user) user.SendSuccessMessage(GetString($"You buffed yourself with {TShock.Utils.GetBuffName(id)} ({TShock.Utils.GetBuffDescription(id)}) for {time} seconds.")); else - target.SendSuccessMessage(GetString($"You have buffed {user.Name} with {TShock.Utils.GetBuffName(id)} ({TShock.Utils.GetBuffDescription(id)}) for {time} seconds!")); + user.SendSuccessMessage(GetString($"You have buffed {user.Name} with {TShock.Utils.GetBuffName(id)} ({TShock.Utils.GetBuffDescription(id)}) for {time} seconds!")); if (!args.Silent && target != user) target.SendSuccessMessage(GetString($"{user.Name} has buffed you with {TShock.Utils.GetBuffName(id)} ({TShock.Utils.GetBuffDescription(id)}) for {time} seconds!")); } diff --git a/docs/changelog.md b/docs/changelog.md index 89e4d39c..d4ab3cfe 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -88,6 +88,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Added a method `TSPlayer.UpdateSection` with arguments `rectangle` and `isLoaded`, which will load some area from the server to the player. (@AgaSpace) * Added a method `TSPlayer.GiveItem`, which has `TShockAPI.NetItem` structure in its arguments. (@AgaSpace) * Added a property `TSPlayer.Hostile`, which gets pvp player mode. (@AgaSpace) +* Fixed typo in `/gbuff`. (@sgkoishi, #2955) ## TShock 5.2 * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) From f66ad72e2caeec3de8c6a64cc007afdc83d1637a Mon Sep 17 00:00:00 2001 From: Stargazing Koishi Date: Sun, 11 Jun 2023 22:20:48 -0700 Subject: [PATCH 071/135] Update TShockAPI/Commands.cs Co-authored-by: punchready <22683812+punchready@users.noreply.github.com> --- TShockAPI/Commands.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs index a4669b04..d39b84e0 100644 --- a/TShockAPI/Commands.cs +++ b/TShockAPI/Commands.cs @@ -6462,7 +6462,7 @@ namespace TShockAPI if (target == user) user.SendSuccessMessage(GetString($"You buffed yourself with {TShock.Utils.GetBuffName(id)} ({TShock.Utils.GetBuffDescription(id)}) for {time} seconds.")); else - user.SendSuccessMessage(GetString($"You have buffed {user.Name} with {TShock.Utils.GetBuffName(id)} ({TShock.Utils.GetBuffDescription(id)}) for {time} seconds!")); + user.SendSuccessMessage(GetString($"You have buffed {target.Name} with {TShock.Utils.GetBuffName(id)} ({TShock.Utils.GetBuffDescription(id)}) for {time} seconds!")); if (!args.Silent && target != user) target.SendSuccessMessage(GetString($"{user.Name} has buffed you with {TShock.Utils.GetBuffName(id)} ({TShock.Utils.GetBuffDescription(id)}) for {time} seconds!")); } From ffed36c6fd9f1003f7d9f7094867d89420212e4d Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Thu, 15 Jun 2023 12:00:13 +0700 Subject: [PATCH 072/135] Added hooks --- TShockAPI/Hooks/PlayerHooks.cs | 101 +++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/TShockAPI/Hooks/PlayerHooks.cs b/TShockAPI/Hooks/PlayerHooks.cs index 7a3e2067..e6b68581 100644 --- a/TShockAPI/Hooks/PlayerHooks.cs +++ b/TShockAPI/Hooks/PlayerHooks.cs @@ -119,6 +119,53 @@ namespace TShockAPI.Hooks public string CommandPrefix { get; set; } } + /// + /// EventArgs used for the event. + /// + public class PrePlayerCommandEventArgs : HandledEventArgs + { + /// + /// The command entered by the player. + /// + public Command Command { get; } + /// + /// Command arguments. + /// + public CommandArgs Arguments { get; set; } + + public PrePlayerCommandEventArgs(Command command, CommandArgs args) + { + Command = command; + Arguments = args; + } + } + + /// + /// EventArgs used for the event. + /// + public class PostPlayerCommandEventArgs + { + /// + /// The command entered by the player. + /// + public Command Command { get; } + /// + /// Command arguments. + /// + public CommandArgs Arguments { get; } + /// + /// Is the command executed. + /// + public bool Handled { get; } + + public PostPlayerCommandEventArgs(Command command, CommandArgs arguments, bool handled) + { + Command = command; + Arguments = arguments; + Handled = handled; + } + } + /// /// EventArgs used for the event. /// @@ -343,6 +390,26 @@ namespace TShockAPI.Hooks /// public static event PlayerCommandD PlayerCommand; + /// + /// The delegate of the event. + /// + /// The EventArgs for this event. + public delegate void PrePlayerCommandD(PrePlayerCommandEventArgs e); + /// + /// Fired before the command is run. + /// + public static event PrePlayerCommandD PrePlayerCommand; + + /// + /// The delegate of the event. + /// + /// The EventArgs for this event. + public delegate void PostPlayerCommandD(PostPlayerCommandEventArgs e); + /// + /// Fired after the command is run. + /// + public static event PostPlayerCommandD PostPlayerCommand; + /// /// The delegate of the event. /// @@ -449,6 +516,40 @@ namespace TShockAPI.Hooks return playerCommandEventArgs.Handled; } + /// + /// Fires the event. + /// + /// Command to be executed + /// Command arguments + /// True if the event has been handled. + public static bool OnPrePlayerCommand(Command cmd, ref CommandArgs arguments) + { + if (PrePlayerCommand == null) + return false; + + PrePlayerCommandEventArgs args = new PrePlayerCommandEventArgs(cmd, arguments); + + PrePlayerCommand(args); + + arguments = args.Arguments; + return args.Handled; + } + + /// + /// Fires the event. + /// + /// Executed command. + /// Command arguments. + /// Is the command executed. + public static void OnPostPlayerCommand(Command cmd, CommandArgs arguments, bool handled) + { + if (PostPlayerCommand == null) + return; + + PostPlayerCommandEventArgs args = new PostPlayerCommandEventArgs(cmd, arguments, handled); + PostPlayerCommand(args); + } + /// /// Fires the event. /// From 380f823e6186667e5379f867ec09b66b66b0fa60 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Thu, 15 Jun 2023 12:00:49 +0700 Subject: [PATCH 073/135] Added a `Run` overload to `Command`. The overload executes the CommandArgs you want. --- TShockAPI/Commands.cs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs index 2214f252..f10be370 100644 --- a/TShockAPI/Commands.cs +++ b/TShockAPI/Commands.cs @@ -148,24 +148,29 @@ namespace TShockAPI Permissions = new List(); } - public bool Run(string msg, bool silent, TSPlayer ply, List parms) + public bool Run(CommandArgs args) { - if (!CanRun(ply)) + if (!CanRun(args.Player)) return false; try { - CommandDelegate(new CommandArgs(msg, silent, ply, parms)); + CommandDelegate(args); } catch (Exception e) { - ply.SendErrorMessage(GetString("Command failed, check logs for more details.")); + args.Player.SendErrorMessage(GetString("Command failed, check logs for more details.")); TShock.Log.Error(e.ToString()); } return true; } + public bool Run(string msg, bool silent, TSPlayer ply, List parms) + { + return Run(new CommandArgs(msg, silent, ply, parms)); + } + public bool Run(string msg, TSPlayer ply, List parms) { return Run(msg, false, ply, parms); @@ -704,7 +709,12 @@ namespace TShockAPI TShock.Utils.SendLogs(GetString("{0} executed: {1}{2}.", player.Name, silent ? SilentSpecifier : Specifier, cmdText), Color.PaleVioletRed, player); else TShock.Utils.SendLogs(GetString("{0} executed (args omitted): {1}{2}.", player.Name, silent ? SilentSpecifier : Specifier, cmdName), Color.PaleVioletRed, player); - cmd.Run(cmdText, silent, player, args); + + CommandArgs arguments = new CommandArgs(cmdText, silent, player, args); + bool handled = PlayerHooks.OnPrePlayerCommand(cmd, ref arguments); + if (!handled) + cmd.Run(arguments); + PlayerHooks.OnPostPlayerCommand(cmd, arguments, handled); } } return true; From 7a3b2e051f6d7fe7dd012d0457f83050238360d0 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Thu, 15 Jun 2023 12:11:44 +0700 Subject: [PATCH 074/135] Update changelog.md --- docs/changelog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 3f64f355..f5a0003c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -78,7 +78,8 @@ Use past tense when adding new entries; sign your name off when you add or chang * If there is no section called "Upcoming changes" below this line, please add one with `## Upcoming changes` as the first line, and then a bulleted item directly after with the first change. --> ## Upcoming changes -Your changes could be here! +* Added `PlayerHooks.PrePlayerCommand` hook, which fired before command execution. +* Added `PlayerHooks.PostPlayerCommand` hook, which fired after command execution. ## TShock 5.2 * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) From a656a1cc198014e06514c62b4f11456b29db0773 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Thu, 15 Jun 2023 12:17:11 +0700 Subject: [PATCH 075/135] Added authorship --- docs/changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index f5a0003c..52868390 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -78,8 +78,8 @@ Use past tense when adding new entries; sign your name off when you add or chang * If there is no section called "Upcoming changes" below this line, please add one with `## Upcoming changes` as the first line, and then a bulleted item directly after with the first change. --> ## Upcoming changes -* Added `PlayerHooks.PrePlayerCommand` hook, which fired before command execution. -* Added `PlayerHooks.PostPlayerCommand` hook, which fired after command execution. +* Added `PlayerHooks.PrePlayerCommand` hook, which fired before command execution. (@AgaSpace) +* Added `PlayerHooks.PostPlayerCommand` hook, which fired after command execution. (@AgaSpace) ## TShock 5.2 * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) From 1ef96f9537131aa5df55900ecbc39b69bc210935 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Thu, 15 Jun 2023 13:01:52 +0700 Subject: [PATCH 076/135] Marked the `PlayerCommand` hook as obsolete. --- TShockAPI/Hooks/PlayerHooks.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TShockAPI/Hooks/PlayerHooks.cs b/TShockAPI/Hooks/PlayerHooks.cs index e6b68581..15cac88d 100644 --- a/TShockAPI/Hooks/PlayerHooks.cs +++ b/TShockAPI/Hooks/PlayerHooks.cs @@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +using System; using System.Collections.Generic; using System.ComponentModel; using TShockAPI.DB; @@ -388,6 +389,7 @@ namespace TShockAPI.Hooks /// /// Fired by players when using a command. /// + [Obsolete("There is an alternative to PlayerHooks.PrePlayerCommand")] public static event PlayerCommandD PlayerCommand; /// From 21422f25fd3b4c3a0476ea1e83ee0de8d8895cf4 Mon Sep 17 00:00:00 2001 From: Zoom L1 <46046453+AgaSpace@users.noreply.github.com> Date: Thu, 15 Jun 2023 15:51:01 +0700 Subject: [PATCH 077/135] Update TShockAPI/Hooks/PlayerHooks.cs Co-authored-by: Arthri <41360489+Arthri@users.noreply.github.com> --- TShockAPI/Hooks/PlayerHooks.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TShockAPI/Hooks/PlayerHooks.cs b/TShockAPI/Hooks/PlayerHooks.cs index 15cac88d..8135a3f9 100644 --- a/TShockAPI/Hooks/PlayerHooks.cs +++ b/TShockAPI/Hooks/PlayerHooks.cs @@ -408,7 +408,7 @@ namespace TShockAPI.Hooks /// The EventArgs for this event. public delegate void PostPlayerCommandD(PostPlayerCommandEventArgs e); /// - /// Fired after the command is run. + /// Fired after a command is run. /// public static event PostPlayerCommandD PostPlayerCommand; From b40f0e632e1dfb8966dca14a9ee707318a1ed12f Mon Sep 17 00:00:00 2001 From: Zoom L1 <46046453+AgaSpace@users.noreply.github.com> Date: Thu, 15 Jun 2023 16:01:43 +0700 Subject: [PATCH 078/135] Update TShockAPI/Hooks/PlayerHooks.cs Co-authored-by: Arthri <41360489+Arthri@users.noreply.github.com> --- TShockAPI/Hooks/PlayerHooks.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TShockAPI/Hooks/PlayerHooks.cs b/TShockAPI/Hooks/PlayerHooks.cs index 8135a3f9..dd35d662 100644 --- a/TShockAPI/Hooks/PlayerHooks.cs +++ b/TShockAPI/Hooks/PlayerHooks.cs @@ -398,7 +398,7 @@ namespace TShockAPI.Hooks /// The EventArgs for this event. public delegate void PrePlayerCommandD(PrePlayerCommandEventArgs e); /// - /// Fired before the command is run. + /// Fired before a command is run. /// public static event PrePlayerCommandD PrePlayerCommand; From db40d5034833e0a0833d50de3e87d2da99b08af7 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Thu, 15 Jun 2023 16:03:43 +0700 Subject: [PATCH 079/135] Updated the message --- TShockAPI/Hooks/PlayerHooks.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TShockAPI/Hooks/PlayerHooks.cs b/TShockAPI/Hooks/PlayerHooks.cs index dd35d662..183205fe 100644 --- a/TShockAPI/Hooks/PlayerHooks.cs +++ b/TShockAPI/Hooks/PlayerHooks.cs @@ -389,7 +389,7 @@ namespace TShockAPI.Hooks /// /// Fired by players when using a command. /// - [Obsolete("There is an alternative to PlayerHooks.PrePlayerCommand")] + [Obsolete("Use PlayerHooks.PrePlayerCommand.")] public static event PlayerCommandD PlayerCommand; /// From 723719350b07daf84ec14aa12d57e1d6691697b4 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Thu, 15 Jun 2023 17:37:22 +0700 Subject: [PATCH 080/135] Made `PostPlayerCommandEventArgs` inherited from `HandledEventArgs`. --- TShockAPI/Hooks/PlayerHooks.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/TShockAPI/Hooks/PlayerHooks.cs b/TShockAPI/Hooks/PlayerHooks.cs index 183205fe..b5048274 100644 --- a/TShockAPI/Hooks/PlayerHooks.cs +++ b/TShockAPI/Hooks/PlayerHooks.cs @@ -144,7 +144,7 @@ namespace TShockAPI.Hooks /// /// EventArgs used for the event. /// - public class PostPlayerCommandEventArgs + public class PostPlayerCommandEventArgs : HandledEventArgs { /// /// The command entered by the player. @@ -154,10 +154,6 @@ namespace TShockAPI.Hooks /// Command arguments. /// public CommandArgs Arguments { get; } - /// - /// Is the command executed. - /// - public bool Handled { get; } public PostPlayerCommandEventArgs(Command command, CommandArgs arguments, bool handled) { From 87fabd3d260b8bc77a47e939fdc7c756dc967b59 Mon Sep 17 00:00:00 2001 From: AkjaHAsLk1IALk0MasH <46046453+AgaSpace@users.noreply.github.com> Date: Thu, 15 Jun 2023 18:44:30 +0700 Subject: [PATCH 081/135] Removed the "obsolete" tag --- TShockAPI/Hooks/PlayerHooks.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/TShockAPI/Hooks/PlayerHooks.cs b/TShockAPI/Hooks/PlayerHooks.cs index b5048274..43756464 100644 --- a/TShockAPI/Hooks/PlayerHooks.cs +++ b/TShockAPI/Hooks/PlayerHooks.cs @@ -385,7 +385,6 @@ namespace TShockAPI.Hooks /// /// Fired by players when using a command. /// - [Obsolete("Use PlayerHooks.PrePlayerCommand.")] public static event PlayerCommandD PlayerCommand; /// From b1a054472fcabd14182c9dd1ec7fa3871ab9eb39 Mon Sep 17 00:00:00 2001 From: Cardinal System Date: Tue, 15 Aug 2023 09:53:09 +0000 Subject: [PATCH 082/135] =?UTF-8?q?Update=20translation=20template=20?= =?UTF-8?q?=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- i18n/template.pot | 600 +++++++++++++++++++++++----------------------- 1 file changed, 296 insertions(+), 304 deletions(-) diff --git a/i18n/template.pot b/i18n/template.pot index a4fe57ab..9da0b2e0 100644 --- a/i18n/template.pot +++ b/i18n/template.pot @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: TShock\n" -"POT-Creation-Date: 2022-12-06 05:43:49+0000\n" -"PO-Revision-Date: 2022-12-06 05:43:50+0000\n" +"POT-Creation-Date: 2023-08-15 09:53:07+0000\n" +"PO-Revision-Date: 2023-08-15 09:53:09+0000\n" "Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -10,8 +10,8 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "X-Generator: GetText.NET Extractor\n" -#: ../../TShockAPI/DB/CharacterManager.cs:194 -#: ../../TShockAPI/DB/CharacterManager.cs:264 +#: ../../TShockAPI/DB/CharacterManager.cs:197 +#: ../../TShockAPI/DB/CharacterManager.cs:267 #, csharp-format msgctxt "{0} is a player name" msgid "Skipping SSC save (due to tshock.ignore.ssc) for {0}" @@ -161,31 +161,31 @@ msgstr "" msgid "- Palm trees :" msgstr "" -#: ../../TShockAPI/TShock.cs:963 +#: ../../TShockAPI/TShock.cs:999 msgid "" "!!! > Set DisableLoginBeforeJoin to true in the config file and /reload if " "this is a problem." msgstr "" -#: ../../TShockAPI/TShock.cs:957 +#: ../../TShockAPI/TShock.cs:993 msgid "" "!!! > Set DisableUUIDLogin to true in the config file and /reload if this is " "a problem." msgstr "" -#: ../../TShockAPI/TShock.cs:962 +#: ../../TShockAPI/TShock.cs:998 msgid "" "!!! Login before join is enabled. Existing accounts can login & the server " "password will be bypassed." msgstr "" -#: ../../TShockAPI/TShock.cs:951 +#: ../../TShockAPI/TShock.cs:987 msgid "" "!!! The server password in config.json was overridden by the interactive " "prompt and will be ignored." msgstr "" -#: ../../TShockAPI/TShock.cs:956 +#: ../../TShockAPI/TShock.cs:992 msgid "" "!!! UUID login is enabled. If a user's UUID matches an account, the server " "password will be bypassed." @@ -249,12 +249,12 @@ msgstr "" msgid "{0} ({1}) failed to change the password for account {2}." msgstr "" -#: ../../TShockAPI/TShock.cs:1659 +#: ../../TShockAPI/TShock.cs:1695 #, csharp-format msgid "{0} ({1}) from '{2}' group from '{3}' joined. ({4}/{5})" msgstr "" -#: ../../TShockAPI/TShock.cs:1667 +#: ../../TShockAPI/TShock.cs:1703 #, csharp-format msgid "{0} ({1}) from '{2}' group joined. ({3}/{4})" msgstr "" @@ -266,7 +266,7 @@ msgid "" "automatically." msgstr "" -#: ../../TShockAPI/TShock.cs:1663 +#: ../../TShockAPI/TShock.cs:1699 #, csharp-format msgid "{0} ({1}) has joined." msgstr "" @@ -367,17 +367,17 @@ msgstr "" msgid "{0} added account {1} to group {2}." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3546 +#: ../../TShockAPI/GetDataHandlers.cs:3587 #, csharp-format msgid "{0} applied advanced combat techniques volume 2!" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3564 +#: ../../TShockAPI/GetDataHandlers.cs:3605 #, csharp-format msgid "{0} applied advanced combat techniques!" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3543 +#: ../../TShockAPI/GetDataHandlers.cs:3584 #, csharp-format msgid "{0} applied traveling merchant's satchel!" msgstr "" @@ -387,8 +387,8 @@ msgstr "" msgid "{0} attempted to register for the account {1} but it was already taken." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2637 -#: ../../TShockAPI/GetDataHandlers.cs:3212 +#: ../../TShockAPI/GetDataHandlers.cs:2672 +#: ../../TShockAPI/GetDataHandlers.cs:3252 #, csharp-format msgid "{0} authenticated successfully as user {1}." msgstr "" @@ -398,7 +398,7 @@ msgstr "" msgid "{0} authenticated successfully as user: {1}." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:1955 +#: ../../TShockAPI/TSPlayer.cs:2042 #, csharp-format msgid "{0} banned {1} for '{2}'." msgstr "" @@ -486,7 +486,7 @@ msgstr "" msgid "{0} disabled xmas mode." msgstr "" -#: ../../TShockAPI/TShock.cs:1398 +#: ../../TShockAPI/TShock.cs:1434 #, csharp-format msgid "{0} disconnected." msgstr "" @@ -590,12 +590,12 @@ msgstr "" msgid "{0} has ended the Old One's Army event." msgstr "" -#: ../../TShockAPI/TShock.cs:1670 +#: ../../TShockAPI/TShock.cs:1706 #, csharp-format msgid "{0} has joined." msgstr "" -#: ../../TShockAPI/TShock.cs:1674 +#: ../../TShockAPI/TShock.cs:1710 #, csharp-format msgid "{0} has joined. IP: {1}" msgstr "" @@ -615,7 +615,7 @@ msgstr "" msgid "{0} has launched himself into space." msgstr "" -#: ../../TShockAPI/TShock.cs:1397 +#: ../../TShockAPI/TShock.cs:1433 #, csharp-format msgid "{0} has left." msgstr "" @@ -630,22 +630,22 @@ msgstr "" msgid "{0} has respawned you." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3555 +#: ../../TShockAPI/GetDataHandlers.cs:3596 #, csharp-format msgid "{0} has sent a request to the bunny delivery service!" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3561 +#: ../../TShockAPI/GetDataHandlers.cs:3602 #, csharp-format msgid "{0} has sent a request to the cat delivery service!" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3558 +#: ../../TShockAPI/GetDataHandlers.cs:3599 #, csharp-format msgid "{0} has sent a request to the dog delivery service!" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3552 +#: ../../TShockAPI/GetDataHandlers.cs:3593 #, csharp-format msgid "{0} has sent a request to the slime delivery service!" msgstr "" @@ -662,7 +662,7 @@ msgstr[1] "" msgid "{0} has spawned a Wall of Flesh." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2620 +#: ../../TShockAPI/GetDataHandlers.cs:2655 #, csharp-format msgid "" "{0} has SSC data in the database, but has the tshock.ignore.ssc permission. " @@ -749,7 +749,7 @@ msgstr "" msgid "{0} is banned! Remove it!" msgstr "" -#: ../../TShockAPI/Commands.cs:6751 +#: ../../TShockAPI/Commands.cs:6747 #, csharp-format msgid "{0} is no longer in god mode." msgstr "" @@ -771,7 +771,7 @@ msgstr "" msgid "{0} is not dead!" msgstr "" -#: ../../TShockAPI/Commands.cs:6750 +#: ../../TShockAPI/Commands.cs:6746 #, csharp-format msgid "{0} is now in god mode." msgstr "" @@ -787,7 +787,7 @@ msgstr "" msgid "{0} just killed you!" msgstr "" -#: ../../TShockAPI/TSPlayer.cs:1926 +#: ../../TShockAPI/TSPlayer.cs:2013 #, csharp-format msgid "{0} kicked {1} for '{2}'" msgstr "" @@ -887,8 +887,8 @@ msgstr "" msgid "{0} started the frost moon at wave {1}!" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4150 -#: ../../TShockAPI/GetDataHandlers.cs:4152 +#: ../../TShockAPI/GetDataHandlers.cs:4194 +#: ../../TShockAPI/GetDataHandlers.cs:4196 #, csharp-format msgid "{0} started the Old One's Army event!" msgstr "" @@ -923,63 +923,63 @@ msgstr "" msgid "{0} successfully deleted account: {1}." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3567 +#: ../../TShockAPI/GetDataHandlers.cs:3608 #, csharp-format msgid "{0} summoned a Blood Moon!" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3579 +#: ../../TShockAPI/GetDataHandlers.cs:3620 #, csharp-format msgid "{0} summoned a frost moon!" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3591 +#: ../../TShockAPI/GetDataHandlers.cs:3632 #, csharp-format msgid "{0} summoned a Goblin Invasion!" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3573 +#: ../../TShockAPI/GetDataHandlers.cs:3614 #, csharp-format msgid "{0} summoned a Martian invasion!" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3549 +#: ../../TShockAPI/GetDataHandlers.cs:3590 #, csharp-format msgid "{0} summoned a Mechdusa!" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3570 +#: ../../TShockAPI/GetDataHandlers.cs:3611 #, csharp-format msgid "{0} summoned a Moon Lord!" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3582 +#: ../../TShockAPI/GetDataHandlers.cs:3623 #, csharp-format msgid "{0} summoned a pumpkin moon!" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3576 +#: ../../TShockAPI/GetDataHandlers.cs:3617 #, csharp-format msgid "{0} summoned an eclipse!" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3598 +#: ../../TShockAPI/GetDataHandlers.cs:3639 #, csharp-format msgid "{0} summoned the {1}!" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2977 -#: ../../TShockAPI/GetDataHandlers.cs:2980 +#: ../../TShockAPI/GetDataHandlers.cs:3016 +#: ../../TShockAPI/GetDataHandlers.cs:3019 #, csharp-format msgid "{0} summoned the Empress of Light!" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3585 +#: ../../TShockAPI/GetDataHandlers.cs:3626 #, csharp-format msgid "{0} summoned the Pirates!" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3588 +#: ../../TShockAPI/GetDataHandlers.cs:3629 #, csharp-format msgid "{0} summoned the Snow Legion!" msgstr "" @@ -1021,12 +1021,12 @@ msgstr "" msgid "{0} warped you to {1}." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:1953 +#: ../../TShockAPI/TSPlayer.cs:2040 #, csharp-format msgid "{0} was banned for '{1}'." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:1924 +#: ../../TShockAPI/TSPlayer.cs:2011 #, csharp-format msgid "{0} was kicked for '{1}'" msgstr "" @@ -1216,7 +1216,7 @@ msgid "" "re-create the allowed field." msgstr "" -#: ../../TShockAPI/TShock.cs:986 +#: ../../TShockAPI/TShock.cs:1022 msgid "" "A malicious server can easily steal a user's UUID. You may consider turning " "this option off if you run a public server." @@ -1234,7 +1234,7 @@ msgstr "" msgid "a Nebula Pillar" msgstr "" -#: ../../TShockAPI/TShock.cs:974 +#: ../../TShockAPI/TShock.cs:1010 msgid "A password for this server was set in config.json and is being used." msgstr "" @@ -1242,7 +1242,7 @@ msgstr "" msgid "A player name must be provided to kick a player. Please provide one." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2451 +#: ../../TShockAPI/GetDataHandlers.cs:2486 msgid "A plugin on this server stopped your login." msgstr "" @@ -1398,7 +1398,7 @@ msgstr "" msgid "Amethyst Gemtree" msgstr "" -#: ../../TShockAPI/TShock.cs:996 +#: ../../TShockAPI/TShock.cs:1032 msgid "" "An account has been detected in the user database, but setup-code.txt is " "still present." @@ -1414,7 +1414,7 @@ msgstr "" msgid "An exception has occurred during database transaction: {0}" msgstr "" -#: ../../TShockAPI/TShock.cs:1489 +#: ../../TShockAPI/TShock.cs:1525 msgid "An exception occurred executing a command." msgstr "" @@ -1477,19 +1477,19 @@ msgid "" msgstr "" #: ../../TShockAPI/Commands.cs:903 -#: ../../TShockAPI/GetDataHandlers.cs:2636 -#: ../../TShockAPI/GetDataHandlers.cs:3211 +#: ../../TShockAPI/GetDataHandlers.cs:2671 +#: ../../TShockAPI/GetDataHandlers.cs:3251 #, csharp-format msgid "Authenticated as {0} successfully." msgstr "" -#: ../../TShockAPI/TShock.cs:440 -#: ../../TShockAPI/TShock.cs:1590 +#: ../../TShockAPI/TShock.cs:471 +#: ../../TShockAPI/TShock.cs:1626 msgid "AutoSave Disabled" msgstr "" -#: ../../TShockAPI/TShock.cs:438 -#: ../../TShockAPI/TShock.cs:1588 +#: ../../TShockAPI/TShock.cs:469 +#: ../../TShockAPI/TShock.cs:1624 msgid "AutoSave Enabled" msgstr "" @@ -1538,11 +1538,11 @@ msgstr "" msgid "Backup Thread" msgstr "" -#: ../../TShockAPI/TShock.cs:444 +#: ../../TShockAPI/TShock.cs:475 msgid "Backups Disabled" msgstr "" -#: ../../TShockAPI/TShock.cs:442 +#: ../../TShockAPI/TShock.cs:473 msgid "Backups Enabled" msgstr "" @@ -1625,7 +1625,7 @@ msgstr "" msgid "Banned tile {0}." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:1950 +#: ../../TShockAPI/TSPlayer.cs:2037 #, csharp-format msgid "Banned: {0}" msgstr "" @@ -1660,12 +1660,12 @@ msgstr "" msgid "Boreal Tree" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3264 +#: ../../TShockAPI/GetDataHandlers.cs:3304 #, csharp-format msgid "Bouncer / HandleNpcTalk rejected from bouncer out of bounds from {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3257 +#: ../../TShockAPI/GetDataHandlers.cs:3297 #, csharp-format msgid "Bouncer / HandleNpcTalk rejected from bouncer throttle from {0}" msgstr "" @@ -1705,57 +1705,57 @@ msgstr "" msgid "Bouncer / OnChestOpen rejected from region check from {0}" msgstr "" -#: ../../TShockAPI/Bouncer.cs:2735 +#: ../../TShockAPI/Bouncer.cs:2738 #, csharp-format msgid "" "Bouncer / OnFishOutNPC rejected for not finding active bobber projectile! - " "From {0}" msgstr "" -#: ../../TShockAPI/Bouncer.cs:2729 +#: ../../TShockAPI/Bouncer.cs:2732 #, csharp-format msgid "Bouncer / OnFishOutNPC rejected for not using a fishing rod! - From {0}" msgstr "" -#: ../../TShockAPI/Bouncer.cs:2741 +#: ../../TShockAPI/Bouncer.cs:2744 #, csharp-format msgid "" "Bouncer / OnFishOutNPC rejected for the NPC not being on the fishable NPCs " "list! - From {0}" msgstr "" -#: ../../TShockAPI/Bouncer.cs:2753 +#: ../../TShockAPI/Bouncer.cs:2756 #, csharp-format msgid "Bouncer / OnFishOutNPC rejected range checks from {0}" msgstr "" -#: ../../TShockAPI/Bouncer.cs:2747 +#: ../../TShockAPI/Bouncer.cs:2750 #, csharp-format msgid "Bouncer / OnFishOutNPC rejected summon boss permissions from {0}" msgstr "" -#: ../../TShockAPI/Bouncer.cs:2781 +#: ../../TShockAPI/Bouncer.cs:2784 #, csharp-format msgid "Bouncer / OnFoodPlatterTryPlacing rejected disabled from {0}" msgstr "" -#: ../../TShockAPI/Bouncer.cs:2774 +#: ../../TShockAPI/Bouncer.cs:2777 #, csharp-format msgid "" "Bouncer / OnFoodPlatterTryPlacing rejected item not placed by hand from {0}" msgstr "" -#: ../../TShockAPI/Bouncer.cs:2792 +#: ../../TShockAPI/Bouncer.cs:2795 #, csharp-format msgid "Bouncer / OnFoodPlatterTryPlacing rejected permissions from {0}" msgstr "" -#: ../../TShockAPI/Bouncer.cs:2803 +#: ../../TShockAPI/Bouncer.cs:2806 #, csharp-format msgid "Bouncer / OnFoodPlatterTryPlacing rejected range checks from {0}" msgstr "" -#: ../../TShockAPI/Bouncer.cs:2767 +#: ../../TShockAPI/Bouncer.cs:2770 #, csharp-format msgid "" "Bouncer / OnFoodPlatterTryPlacing rejected tile placement valid from {0}" @@ -1852,22 +1852,22 @@ msgstr "" msgid "Bouncer / OnItemDrop rejected from sneaky from {0}" msgstr "" -#: ../../TShockAPI/Bouncer.cs:2703 +#: ../../TShockAPI/Bouncer.cs:2706 #, csharp-format msgid "Bouncer / OnKillMe rejected bad length death text from {0}" msgstr "" -#: ../../TShockAPI/Bouncer.cs:2710 +#: ../../TShockAPI/Bouncer.cs:2713 #, csharp-format msgid "Bouncer / OnKillMe rejected custom death message from {0}" msgstr "" -#: ../../TShockAPI/Bouncer.cs:2684 +#: ../../TShockAPI/Bouncer.cs:2687 #, csharp-format msgid "Bouncer / OnKillMe rejected high damage from {0} {1}" msgstr "" -#: ../../TShockAPI/Bouncer.cs:2693 +#: ../../TShockAPI/Bouncer.cs:2696 #, csharp-format msgid "Bouncer / OnKillMe rejected index check from {0}" msgstr "" @@ -2280,7 +2280,7 @@ msgid "" "ticks: target is null" msgstr "" -#: ../../TShockAPI/Bouncer.cs:2667 +#: ../../TShockAPI/Bouncer.cs:2670 #, csharp-format msgid "Bouncer / OnPlayerDamage rejected custom death message from {0}" msgstr "" @@ -2596,59 +2596,51 @@ msgstr "" msgid "Bouncer / OnUpdateNPCHome rejected range checks from {0}" msgstr "" -#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:558 +#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:413 #, csharp-format msgid "Bouncer / SendTileRect accepted clientside world edit from {0}" msgstr "" -#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:414 +#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:468 +#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:488 +#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:500 +#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:511 #, csharp-format -msgid "" -"Bouncer / SendTileRect processing a tile conversion update - [{0}] -> [{1}]" +msgid "Bouncer / SendTileRect reimplemented from {0}" msgstr "" -#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:429 -#, csharp-format -msgid "" -"Bouncer / SendTileRect processing a wall conversion update - [{0}] -> [{1}]" -msgstr "" - -#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:129 -#, csharp-format -msgid "Bouncer / SendTileRect reimplemented from carbonara from {0}" -msgstr "" - -#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:299 -msgid "Bouncer / SendTileRect rejected for banned tile" -msgstr "" - -#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:578 +#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:446 #, csharp-format msgid "Bouncer / SendTileRect rejected from being disabled from {0}" msgstr "" -#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:293 +#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:518 +#, csharp-format +msgid "Bouncer / SendTileRect rejected from matches from {0}" +msgstr "" + +#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:459 #, csharp-format msgid "" -"Bouncer / SendTileRect rejected from no permission for tile object from {0}" +"Bouncer / SendTileRect rejected from out of bounds / build permission from " +"{0}" msgstr "" -#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:565 +#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:478 #, csharp-format -msgid "Bouncer / SendTileRect rejected from non-vanilla tilemod from {0}" +msgid "Bouncer / SendTileRect rejected from out of range from {0}" msgstr "" -#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:571 +#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:427 +#, csharp-format +msgid "Bouncer / SendTileRect rejected from size from {0}" +msgstr "" + +#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:436 #, csharp-format msgid "Bouncer / SendTileRect rejected from throttle from {0}" msgstr "" -#: ../../TShockAPI/Handlers/SendTileRectHandler.cs:610 -msgid "" -"Bouncer / SendTileRectHandler - rejected tile object because object " -"dimensions fall outside the tile rect (excessive size)" -msgstr "" - #: ../../TShockAPI/Utils.cs:136 #, csharp-format msgid "Broadcast: {0}" @@ -2671,7 +2663,7 @@ msgstr "" msgid "Butcher Syntax and Example" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2619 +#: ../../TShockAPI/GetDataHandlers.cs:2654 msgid "" "Bypass SSC is enabled for your account. SSC data will not be loaded or saved." msgstr "" @@ -2780,7 +2772,7 @@ msgstr "" msgid "Commands: add, del, hide, list, send, [warpname]." msgstr "" -#: ../../TShockAPI/TShock.cs:765 +#: ../../TShockAPI/TShock.cs:801 #, csharp-format msgid "Config path has been set to {0}" msgstr "" @@ -2795,7 +2787,7 @@ msgstr "" msgid "Connect to the internet to figure out what to download?" msgstr "" -#: ../../TShockAPI/TShock.cs:1329 +#: ../../TShockAPI/TShock.cs:1365 msgid "Connecting via a proxy is not allowed." msgstr "" @@ -2808,7 +2800,7 @@ msgstr "" msgid "Corruption Palm" msgstr "" -#: ../../TShockAPI/TShock.cs:296 +#: ../../TShockAPI/TShock.cs:300 #, csharp-format msgid "" "Could not apply the given log path / log format, defaults will be used. " @@ -2886,7 +2878,7 @@ msgstr "" msgid "Could not rename {0}!" msgstr "" -#: ../../TShockAPI/TShock.cs:1448 +#: ../../TShockAPI/TShock.cs:1484 msgid "Crash attempt via long chat packet." msgstr "" @@ -2928,12 +2920,12 @@ msgstr "" msgid "Current spawn rate: {0}." msgstr "" -#: ../../TShockAPI/Bouncer.cs:2686 +#: ../../TShockAPI/Bouncer.cs:2689 #, csharp-format msgid "Death Exploit Attempt: Damage {0}" msgstr "" -#: ../../TShockAPI/Bouncer.cs:2704 +#: ../../TShockAPI/Bouncer.cs:2707 msgid "Death reason outside of normal bounds." msgstr "" @@ -3134,7 +3126,7 @@ msgstr "" msgid "Executes a command as the super admin." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4371 +#: ../../TShockAPI/GetDataHandlers.cs:4416 msgid "Exploit attempt detected!" msgstr "" @@ -3177,7 +3169,7 @@ msgstr "" msgid "Failed to rename the region." msgstr "" -#: ../../TShockAPI/Bouncer.cs:2685 +#: ../../TShockAPI/Bouncer.cs:2688 msgid "Failed to shade polygon normals." msgstr "" @@ -3239,402 +3231,402 @@ msgid_plural "Gave {0} {1}s." msgstr[0] "" msgstr[1] "" -#: ../../TShockAPI/GetDataHandlers.cs:3796 +#: ../../TShockAPI/GetDataHandlers.cs:3839 #, csharp-format msgid "GetDataHandlers / HandleCatchNpc catch zero {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3804 +#: ../../TShockAPI/GetDataHandlers.cs:3847 #, csharp-format msgid "GetDataHandlers / HandleCatchNpc rejected catch npc {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3119 +#: ../../TShockAPI/GetDataHandlers.cs:3158 #, csharp-format msgid "" "GetDataHandlers / HandleChestActive rejected build permission and region " "check {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3094 +#: ../../TShockAPI/GetDataHandlers.cs:3133 #, csharp-format msgid "GetDataHandlers / HandleChestItem rejected max stacks {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2844 +#: ../../TShockAPI/GetDataHandlers.cs:2881 #, csharp-format msgid "GetDataHandlers / HandleDoorUse rejected door gap check {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2829 +#: ../../TShockAPI/GetDataHandlers.cs:2866 #, csharp-format msgid "GetDataHandlers / HandleDoorUse rejected out of range door {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2835 +#: ../../TShockAPI/GetDataHandlers.cs:2872 #, csharp-format msgid "GetDataHandlers / HandleDoorUse rejected type 0 5 check {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2669 +#: ../../TShockAPI/GetDataHandlers.cs:2704 msgid "GetDataHandlers / HandleGetSection rejected reserve slot" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4042 +#: ../../TShockAPI/GetDataHandlers.cs:4086 #, csharp-format msgid "GetDataHandlers / HandleKillPortal rejected owner mismatch check {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2989 +#: ../../TShockAPI/GetDataHandlers.cs:3028 #, csharp-format msgid "GetDataHandlers / HandleNpcStrike rejected Cultist summon from {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2972 +#: ../../TShockAPI/GetDataHandlers.cs:3011 #, csharp-format msgid "GetDataHandlers / HandleNpcStrike rejected EoL summon from {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2962 +#: ../../TShockAPI/GetDataHandlers.cs:3001 #, csharp-format msgid "GetDataHandlers / HandleNpcStrike rejected npc strike {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3250 +#: ../../TShockAPI/GetDataHandlers.cs:3290 #, csharp-format msgid "GetDataHandlers / HandleNpcTalk rejected npc talk {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4086 +#: ../../TShockAPI/GetDataHandlers.cs:4130 #, csharp-format msgid "" "GetDataHandlers / HandleNpcTeleportPortal rejected not thinking with portals " "{0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4079 +#: ../../TShockAPI/GetDataHandlers.cs:4123 #, csharp-format msgid "GetDataHandlers / HandleNpcTeleportPortal rejected null check {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3920 +#: ../../TShockAPI/GetDataHandlers.cs:3963 #, csharp-format msgid "" "GetDataHandlers / HandleNumberOfAnglerQuestsCompleted surprise packet! " "Someone tell the TShock team! {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4144 +#: ../../TShockAPI/GetDataHandlers.cs:4188 #, csharp-format msgid "GetDataHandlers / HandleOldOnesArmy rejected permissions {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4138 +#: ../../TShockAPI/GetDataHandlers.cs:4182 #, csharp-format msgid "GetDataHandlers / HandleOldOnesArmy rejected throttled {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3622 +#: ../../TShockAPI/GetDataHandlers.cs:3664 #, csharp-format msgid "GetDataHandlers / HandlePaintTile rejected range check {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3640 +#: ../../TShockAPI/GetDataHandlers.cs:3682 #, csharp-format msgid "GetDataHandlers / HandlePaintTile rejected select consistency {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3649 +#: ../../TShockAPI/GetDataHandlers.cs:3691 #, csharp-format msgid "" "GetDataHandlers / HandlePaintTile rejected throttle/permission/range check " "{0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3669 +#: ../../TShockAPI/GetDataHandlers.cs:3712 #, csharp-format msgid "GetDataHandlers / HandlePaintWall rejected range check {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3687 +#: ../../TShockAPI/GetDataHandlers.cs:3730 #, csharp-format msgid "GetDataHandlers / HandlePaintWall rejected selector consistency {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3696 +#: ../../TShockAPI/GetDataHandlers.cs:3739 #, csharp-format msgid "" "GetDataHandlers / HandlePaintWall rejected throttle/permission/range {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3411 +#: ../../TShockAPI/GetDataHandlers.cs:3452 #, csharp-format msgid "GetDataHandlers / HandlePlayerBuffList handled event and sent data {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3396 +#: ../../TShockAPI/GetDataHandlers.cs:3437 #, csharp-format msgid "" "GetDataHandlers / HandlePlayerBuffList zeroed player buff due to below state " "2 {0} {1}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2777 +#: ../../TShockAPI/GetDataHandlers.cs:2814 #, csharp-format msgid "GetDataHandlers / HandlePlayerHp rejected over max hp {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2522 +#: ../../TShockAPI/GetDataHandlers.cs:2557 msgid "GetDataHandlers / HandlePlayerInfo rejected hardcore required" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2516 +#: ../../TShockAPI/GetDataHandlers.cs:2551 msgid "GetDataHandlers / HandlePlayerInfo rejected mediumcore required" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2457 +#: ../../TShockAPI/GetDataHandlers.cs:2492 msgid "GetDataHandlers / HandlePlayerInfo rejected name length 0" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2450 +#: ../../TShockAPI/GetDataHandlers.cs:2485 #, csharp-format msgid "GetDataHandlers / HandlePlayerInfo rejected plugin phase {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2510 +#: ../../TShockAPI/GetDataHandlers.cs:2545 msgid "GetDataHandlers / HandlePlayerInfo rejected softcore required" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4210 -#: ../../TShockAPI/GetDataHandlers.cs:4216 +#: ../../TShockAPI/GetDataHandlers.cs:4255 +#: ../../TShockAPI/GetDataHandlers.cs:4261 #, csharp-format msgid "GetDataHandlers / HandlePlayerKillMeV2 kicked with difficulty {0} {1}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4225 +#: ../../TShockAPI/GetDataHandlers.cs:4270 #, csharp-format msgid "GetDataHandlers / HandlePlayerKillMeV2 ssc delete {0} {1}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3289 +#: ../../TShockAPI/GetDataHandlers.cs:3329 #, csharp-format msgid "GetDataHandlers / HandlePlayerMana rejected max mana {0} {1}/{2}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2554 +#: ../../TShockAPI/GetDataHandlers.cs:2589 msgid "GetDataHandlers / HandlePlayerSlot rejected ignore ssc packets" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3316 +#: ../../TShockAPI/GetDataHandlers.cs:3357 #, csharp-format msgid "GetDataHandlers / HandlePlayerTeam rejected team fastswitch {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2757 +#: ../../TShockAPI/GetDataHandlers.cs:2794 #, csharp-format msgid "GetDataHandlers / HandlePlayerUpdate home position delta {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3144 +#: ../../TShockAPI/GetDataHandlers.cs:3183 msgid "GetDataHandlers / HandlePlayerZone rejected null check" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3025 +#: ../../TShockAPI/GetDataHandlers.cs:3064 #, csharp-format msgid "" "GetDataHandlers / HandleProjectileKill permitted skeletron prime exemption " "{0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3029 +#: ../../TShockAPI/GetDataHandlers.cs:3068 #, csharp-format msgid "GetDataHandlers / HandleProjectileKill rejected banned projectile {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3014 +#: ../../TShockAPI/GetDataHandlers.cs:3053 #, csharp-format msgid "GetDataHandlers / HandleProjectileKill rejected tombstone {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3354 +#: ../../TShockAPI/GetDataHandlers.cs:3395 #, csharp-format msgid "GetDataHandlers / HandleSign rejected sign on build permission {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3361 +#: ../../TShockAPI/GetDataHandlers.cs:3402 #, csharp-format msgid "GetDataHandlers / HandleSign rejected sign range check {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3334 +#: ../../TShockAPI/GetDataHandlers.cs:3375 #, csharp-format msgid "GetDataHandlers / HandleSignRead rejected out of bounds {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2700 +#: ../../TShockAPI/GetDataHandlers.cs:2737 #, csharp-format msgid "GetDataHandlers / HandleSpawn force teleport 'vanilla spawn' {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2711 +#: ../../TShockAPI/GetDataHandlers.cs:2748 #, csharp-format msgid "GetDataHandlers / HandleSpawn force teleport phase 1 {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2720 +#: ../../TShockAPI/GetDataHandlers.cs:2757 #, csharp-format msgid "GetDataHandlers / HandleSpawn force teleport phase 2 {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2682 +#: ../../TShockAPI/GetDataHandlers.cs:2717 #, csharp-format msgid "GetDataHandlers / HandleSpawn rejected dead player spawn request {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3517 +#: ../../TShockAPI/GetDataHandlers.cs:3558 #, csharp-format msgid "GetDataHandlers / HandleSpawnBoss rejected boss {0} {1}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3507 +#: ../../TShockAPI/GetDataHandlers.cs:3548 #, csharp-format msgid "GetDataHandlers / HandleSpawnBoss rejected bouner throttled {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3524 +#: ../../TShockAPI/GetDataHandlers.cs:3565 #, csharp-format msgid "GetDataHandlers / HandleSpawnBoss rejected invasion {0} {1}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3531 +#: ../../TShockAPI/GetDataHandlers.cs:3572 #, csharp-format msgid "GetDataHandlers / HandleSpawnBoss rejected pet {0} {1}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3442 +#: ../../TShockAPI/GetDataHandlers.cs:3483 #, csharp-format msgid "" "GetDataHandlers / HandleSpecial rejected enchanted sundial permission " "(ForceTime) {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3436 +#: ../../TShockAPI/GetDataHandlers.cs:3477 #, csharp-format msgid "" "GetDataHandlers / HandleSpecial rejected enchanted sundial permission {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3426 +#: ../../TShockAPI/GetDataHandlers.cs:3467 #, csharp-format msgid "GetDataHandlers / HandleSpecial rejected type 1 for {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4005 +#: ../../TShockAPI/GetDataHandlers.cs:4049 #, csharp-format msgid "" "GetDataHandlers / HandleSyncExtraValue rejected expert/master mode check {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3999 +#: ../../TShockAPI/GetDataHandlers.cs:4043 #, csharp-format msgid "GetDataHandlers / HandleSyncExtraValue rejected extents check {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4011 +#: ../../TShockAPI/GetDataHandlers.cs:4055 #, csharp-format msgid "" "GetDataHandlers / HandleSyncExtraValue rejected npc id out of bounds check - " "NPC ID: {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4018 +#: ../../TShockAPI/GetDataHandlers.cs:4062 #, csharp-format msgid "" "GetDataHandlers / HandleSyncExtraValue rejected npc is null - NPC ID: {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4025 +#: ../../TShockAPI/GetDataHandlers.cs:4069 #, csharp-format msgid "" "GetDataHandlers / HandleSyncExtraValue rejected range check {0},{1} vs " "{2},{3} which is {4}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4397 +#: ../../TShockAPI/GetDataHandlers.cs:4442 #, csharp-format msgid "GetDataHandlers / HandleSyncLoadout rejected loadout index sync {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4388 +#: ../../TShockAPI/GetDataHandlers.cs:4433 #, csharp-format msgid "" "GetDataHandlers / HandleSyncLoadout rejected loadout index sync out of bounds " "{0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3753 +#: ../../TShockAPI/GetDataHandlers.cs:3796 #, csharp-format msgid "GetDataHandlers / HandleTeleport rejected npc teleport {0} {1}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3762 +#: ../../TShockAPI/GetDataHandlers.cs:3805 #, csharp-format msgid "GetDataHandlers / HandleTeleport rejected p2p extents {0} {1}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3768 +#: ../../TShockAPI/GetDataHandlers.cs:3811 #, csharp-format msgid "" "GetDataHandlers / HandleTeleport rejected p2p wormhole permission {0} {1}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3744 +#: ../../TShockAPI/GetDataHandlers.cs:3787 #, csharp-format msgid "GetDataHandlers / HandleTeleport rejected rod type {0} {1}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3842 -#: ../../TShockAPI/GetDataHandlers.cs:3858 -#: ../../TShockAPI/GetDataHandlers.cs:3881 +#: ../../TShockAPI/GetDataHandlers.cs:3885 #: ../../TShockAPI/GetDataHandlers.cs:3901 +#: ../../TShockAPI/GetDataHandlers.cs:3924 +#: ../../TShockAPI/GetDataHandlers.cs:3944 #, csharp-format msgid "" "GetDataHandlers / HandleTeleportationPotion rejected not holding the correct " "item {0} {1}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3832 +#: ../../TShockAPI/GetDataHandlers.cs:3875 #, csharp-format msgid "" "GetDataHandlers / HandleTeleportationPotion rejected permissions {0} {1}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4126 +#: ../../TShockAPI/GetDataHandlers.cs:4170 #, csharp-format msgid "GetDataHandlers / HandleToggleParty rejected no party {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3059 +#: ../../TShockAPI/GetDataHandlers.cs:3098 #, csharp-format msgid "GetDataHandlers / HandleTogglePvp rejected fastswitch {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3052 +#: ../../TShockAPI/GetDataHandlers.cs:3091 #, csharp-format msgid "GetDataHandlers / HandleTogglePvp rejected index mismatch {0}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2735 +#: ../../TShockAPI/GetDataHandlers.cs:2772 msgid "GetDataHandlers / OnPlayerUpdate rejected from null player." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2464 +#: ../../TShockAPI/GetDataHandlers.cs:2499 msgid "" "GetDataHandlers / rejecting player for name prefix starting with tsi: or " "tsn:." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3492 +#: ../../TShockAPI/GetDataHandlers.cs:3533 #, csharp-format msgid "GetDataHandlers / UpdateNPCHome rejected no permission {0}" msgstr "" @@ -3801,7 +3793,7 @@ msgstr "" msgid "Hallow Palm" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4372 +#: ../../TShockAPI/GetDataHandlers.cs:4417 #, csharp-format msgid "" "HandleSyncCavernMonsterType: Player is trying to modify NPC " @@ -3903,7 +3895,7 @@ msgid "" "were disabled for to TShock so we can improve this!" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3026 +#: ../../TShockAPI/GetDataHandlers.cs:3065 msgid "" "If this was not skeletron prime related, please report to TShock what " "happened." @@ -3964,7 +3956,7 @@ msgstr "" msgid "Ignoring shrapnel per config.." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2465 +#: ../../TShockAPI/GetDataHandlers.cs:2500 msgid "Illegal name: prefixes tsi: and tsn: are forbidden." msgstr "" @@ -4197,7 +4189,7 @@ msgid "" "Press any key to exit." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3231 +#: ../../TShockAPI/GetDataHandlers.cs:3271 msgid "Invalid server password." msgstr "" @@ -4660,7 +4652,7 @@ msgstr "" msgid "Item bans ({{0}}/{{1}}):" msgstr "" -#: ../../TShockAPI/TSPlayer.cs:1920 +#: ../../TShockAPI/TSPlayer.cs:2007 #, csharp-format msgid "Kicked {0} for : '{1}'" msgstr "" @@ -4669,7 +4661,7 @@ msgstr "" msgid "Kicked via web" msgstr "" -#: ../../TShockAPI/TSPlayer.cs:1919 +#: ../../TShockAPI/TSPlayer.cs:2006 #, csharp-format msgid "Kicked: {0}" msgstr "" @@ -4753,12 +4745,12 @@ msgstr "" msgid "List Online Players Syntax" msgstr "" -#: ../../TShockAPI/TShock.cs:828 +#: ../../TShockAPI/TShock.cs:864 #, csharp-format msgid "Listening on IP {0}." msgstr "" -#: ../../TShockAPI/TShock.cs:809 +#: ../../TShockAPI/TShock.cs:845 #, csharp-format msgid "Listening on port {0}." msgstr "" @@ -4775,7 +4767,7 @@ msgstr "" msgid "listusers - Lists all REST users and their current active tokens." msgstr "" -#: ../../TShockAPI/TShock.cs:798 +#: ../../TShockAPI/TShock.cs:834 #, csharp-format msgid "Loading dedicated config file: {0}" msgstr "" @@ -4793,7 +4785,7 @@ msgstr "" msgid "Log display enabled." msgstr "" -#: ../../TShockAPI/TShock.cs:785 +#: ../../TShockAPI/TShock.cs:821 #, csharp-format msgid "Log path has been set to {0}" msgstr "" @@ -4802,13 +4794,13 @@ msgstr "" msgid "Login attempt failed - see the message above." msgstr "" -#: ../../TShockAPI/TShock.cs:980 +#: ../../TShockAPI/TShock.cs:1016 msgid "" "Login before join enabled. Users may be prompted for an account specific " "password instead of a server password on connect." msgstr "" -#: ../../TShockAPI/TShock.cs:985 +#: ../../TShockAPI/TShock.cs:1021 msgid "Login using UUID enabled. Users automatically login via UUID." msgstr "" @@ -4928,7 +4920,7 @@ msgstr "" msgid "Mode: {0}" msgstr "" -#: ../../TShockAPI/TSPlayer.cs:1966 +#: ../../TShockAPI/TSPlayer.cs:2053 msgid "More than one match found -- unable to decide which is correct: " msgstr "" @@ -4977,7 +4969,7 @@ msgstr "" msgid "New name is too large!" msgstr "" -#: ../../TShockAPI/TShock.cs:864 +#: ../../TShockAPI/TShock.cs:900 #, csharp-format msgid "New worlds will be generated with the {0} world evil type!" msgstr "" @@ -5055,9 +5047,9 @@ msgid "" "token was not valid." msgstr "" +#: ../../TShockAPI/Rest/SecureRest.cs:180 #: ../../TShockAPI/Rest/RestCommand.cs:95 #: ../../TShockAPI/Rest/RestCommand.cs:101 -#: ../../TShockAPI/Rest/SecureRest.cs:180 msgid "Not authorized. The specified API endpoint requires a token." msgstr "" @@ -5197,8 +5189,8 @@ msgstr "" msgid "Player \"{0}\" is already logged in." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:1883 -#: ../../TShockAPI/TSPlayer.cs:1887 +#: ../../TShockAPI/TSPlayer.cs:1970 +#: ../../TShockAPI/TSPlayer.cs:1974 #, csharp-format msgid "Player {0} has been disabled for {1}." msgstr "" @@ -5260,7 +5252,7 @@ msgstr "" msgid "Player not found. Unable to kick the player." msgstr "" -#: ../../TShockAPI/TShock.cs:1696 +#: ../../TShockAPI/TShock.cs:1732 #, csharp-format msgid "Please {0}register or {0}login to play!" msgstr "" @@ -5286,7 +5278,7 @@ msgstr "" msgid "Please use the following to create a permanent account for you." msgstr "" -#: ../../TShockAPI/TShock.cs:895 +#: ../../TShockAPI/TShock.cs:931 #, csharp-format msgid "Port overridden by startup argument. Set to {0}" msgstr "" @@ -5363,12 +5355,12 @@ msgstr "" msgid "Quick usage: {0} {1} \"Griefing\"" msgstr "" -#: ../../TShockAPI/TSPlayer.cs:758 +#: ../../TShockAPI/TSPlayer.cs:759 #, csharp-format msgid "Rangecheck failed for {0} ({1}, {2}) (rg: {3}/{5}, {4}/{5})" msgstr "" -#: ../../TShockAPI/TShock.cs:1196 +#: ../../TShockAPI/TShock.cs:1232 msgid "Reached HealOtherPlayer threshold" msgstr "" @@ -5376,7 +5368,7 @@ msgstr "" msgid "Reached HealOtherPlayer threshold." msgstr "" -#: ../../TShockAPI/TShock.cs:1187 +#: ../../TShockAPI/TShock.cs:1223 msgid "Reached paint threshold" msgstr "" @@ -5384,16 +5376,16 @@ msgstr "" msgid "Reached projectile create threshold." msgstr "" -#: ../../TShockAPI/TShock.cs:1178 +#: ../../TShockAPI/TShock.cs:1214 msgid "Reached projectile threshold" msgstr "" +#: ../../TShockAPI/TShock.cs:1144 #: ../../TShockAPI/Bouncer.cs:922 -#: ../../TShockAPI/TShock.cs:1108 msgid "Reached TileKill threshold." msgstr "" -#: ../../TShockAPI/TShock.cs:1169 +#: ../../TShockAPI/TShock.cs:1205 msgid "Reached TileLiquid threshold" msgstr "" @@ -5406,7 +5398,7 @@ msgstr "" msgid "Reached TileLiquid threshold." msgstr "" -#: ../../TShockAPI/TShock.cs:1125 +#: ../../TShockAPI/TShock.cs:1161 msgid "Reached TilePlace threshold" msgstr "" @@ -5670,7 +5662,7 @@ msgstr "" msgid "Sends you to your spawn point." msgstr "" -#: ../../TShockAPI/TShock.cs:738 +#: ../../TShockAPI/TShock.cs:774 msgid "Server console interrupted!" msgstr "" @@ -5682,7 +5674,7 @@ msgstr "" msgid "Server is full. No reserved slots open." msgstr "" -#: ../../TShockAPI/TShock.cs:1300 +#: ../../TShockAPI/TShock.cs:1336 msgid "Server is shutting down..." msgstr "" @@ -5712,7 +5704,7 @@ msgstr "" msgid "Server side characters are enabled. You need to be logged-in to play." msgstr "" -#: ../../TShockAPI/TShock.cs:1691 +#: ../../TShockAPI/TShock.cs:1727 #, csharp-format msgid "" "Server side characters is enabled! Please {0}register or {0}login to play!" @@ -5841,7 +5833,7 @@ msgstr "" msgid "Shuts down the server without saving." msgstr "" -#: ../../TShockAPI/TShock.cs:735 +#: ../../TShockAPI/TShock.cs:771 msgid "Shutting down safely. To force shutdown, send SIGINT (CTRL + C) again." msgstr "" @@ -5858,7 +5850,7 @@ msgstr "" msgid "Skeletron Prime" msgstr "" -#: ../../TShockAPI/TSPlayer.cs:1069 +#: ../../TShockAPI/TSPlayer.cs:1076 #, csharp-format msgid "Skipping SSC save (due to tshock.ignore.ssc) for {0}" msgstr "" @@ -5939,67 +5931,67 @@ msgstr "" msgid "SSC must be enabled to use this command." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:434 +#: ../../TShockAPI/TSPlayer.cs:435 #, csharp-format msgid "Stack cheat detected. Remove armor {0} ({1}) and then rejoin." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:577 +#: ../../TShockAPI/TSPlayer.cs:578 #, csharp-format msgid "" "Stack cheat detected. Remove Defender's Forge item {0} ({1}) and then rejoin." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:454 +#: ../../TShockAPI/TSPlayer.cs:455 #, csharp-format msgid "Stack cheat detected. Remove dye {0} ({1}) and then rejoin." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:414 -#: ../../TShockAPI/TSPlayer.cs:474 +#: ../../TShockAPI/TSPlayer.cs:415 +#: ../../TShockAPI/TSPlayer.cs:475 #, csharp-format msgid "Stack cheat detected. Remove item {0} ({1}) and then rejoin." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:494 +#: ../../TShockAPI/TSPlayer.cs:495 #, csharp-format msgid "Stack cheat detected. Remove item dye {0} ({1}) and then rejoin." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:618 -#: ../../TShockAPI/TSPlayer.cs:638 +#: ../../TShockAPI/TSPlayer.cs:619 +#: ../../TShockAPI/TSPlayer.cs:639 #, csharp-format msgid "Stack cheat detected. Remove Loadout 1 item {0} ({1}) and then rejoin." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:658 -#: ../../TShockAPI/TSPlayer.cs:678 +#: ../../TShockAPI/TSPlayer.cs:659 +#: ../../TShockAPI/TSPlayer.cs:679 #, csharp-format msgid "Stack cheat detected. Remove Loadout 2 item {0} ({1}) and then rejoin." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:698 -#: ../../TShockAPI/TSPlayer.cs:718 +#: ../../TShockAPI/TSPlayer.cs:699 +#: ../../TShockAPI/TSPlayer.cs:719 #, csharp-format msgid "Stack cheat detected. Remove Loadout 3 item {0} ({1}) and then rejoin." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:515 +#: ../../TShockAPI/TSPlayer.cs:516 #, csharp-format msgid "Stack cheat detected. Remove piggy-bank item {0} ({1}) and then rejoin." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:536 +#: ../../TShockAPI/TSPlayer.cs:537 #, csharp-format msgid "Stack cheat detected. Remove safe item {0} ({1}) and then rejoin." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:556 +#: ../../TShockAPI/TSPlayer.cs:557 #, csharp-format msgid "Stack cheat detected. Remove trash item {0} ({1}) and then rejoin." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:598 +#: ../../TShockAPI/TSPlayer.cs:599 #, csharp-format msgid "Stack cheat detected. Remove Void Vault item {0} ({1}) and then rejoin." msgstr "" @@ -6016,19 +6008,19 @@ msgstr "" msgid "Started an eclipse." msgstr "" -#: ../../TShockAPI/TShock.cs:927 +#: ../../TShockAPI/TShock.cs:963 msgid "Startup parameter overrode maximum player slot configuration value." msgstr "" -#: ../../TShockAPI/TShock.cs:909 +#: ../../TShockAPI/TShock.cs:945 msgid "Startup parameter overrode REST enable." msgstr "" -#: ../../TShockAPI/TShock.cs:918 +#: ../../TShockAPI/TShock.cs:954 msgid "Startup parameter overrode REST port." msgstr "" -#: ../../TShockAPI/TShock.cs:901 +#: ../../TShockAPI/TShock.cs:937 msgid "Startup parameter overrode REST token." msgstr "" @@ -6197,7 +6189,7 @@ msgstr "" msgid "the Destroyer" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3427 +#: ../../TShockAPI/GetDataHandlers.cs:3468 msgid "The Dungeon Guardian returned you to your spawn point." msgstr "" @@ -6431,8 +6423,8 @@ msgid_plural "These are the plugins you requested to install" msgstr[0] "" msgstr[1] "" -#: ../../TShockAPI/TShock.cs:1013 -#: ../../TShockAPI/TShock.cs:1023 +#: ../../TShockAPI/TShock.cs:1049 +#: ../../TShockAPI/TShock.cs:1059 #, csharp-format msgid "This token will display until disabled by verification. ({0}setup)" msgstr "" @@ -6486,8 +6478,8 @@ msgid "" "instead of {1}" msgstr "" -#: ../../TShockAPI/TShock.cs:1012 -#: ../../TShockAPI/TShock.cs:1022 +#: ../../TShockAPI/TShock.cs:1048 +#: ../../TShockAPI/TShock.cs:1058 #, csharp-format msgid "To setup the server, join the game and type {0}setup {1}" msgstr "" @@ -6554,7 +6546,7 @@ msgstr "" msgid "Tried to grow a {0}." msgstr "" -#: ../../TShockAPI/TShock.cs:399 +#: ../../TShockAPI/TShock.cs:403 #, csharp-format msgid "TShock {0} ({1}) now running." msgstr "" @@ -6563,29 +6555,29 @@ msgstr "" msgid "TShock Ban Help" msgstr "" -#: ../../TShockAPI/TShock.cs:449 +#: ../../TShockAPI/TShock.cs:480 msgid "TShock comes with no warranty & is free software." msgstr "" -#: ../../TShockAPI/TShock.cs:461 +#: ../../TShockAPI/TShock.cs:492 msgid "" "TShock encountered a problem from which it cannot recover. The following " "message may help diagnose the problem." msgstr "" -#: ../../TShockAPI/TShock.cs:1021 +#: ../../TShockAPI/TShock.cs:1057 msgid "" "TShock Notice: setup-code.txt is still present, and the code located in that " "file will be used." msgstr "" -#: ../../TShockAPI/TShock.cs:353 +#: ../../TShockAPI/TShock.cs:357 msgid "" "TShock was improperly shut down. Please use the exit command in the future to " "prevent this." msgstr "" -#: ../../TShockAPI/TShock.cs:997 +#: ../../TShockAPI/TShock.cs:1033 msgid "" "TShock will now disable the initial setup system and remove setup-code.txt as " "it is no longer needed." @@ -6740,11 +6732,11 @@ msgstr "" msgid "Unable to launch {0} because she is not logged in." msgstr "" -#: ../../TShockAPI/TShock.cs:1484 +#: ../../TShockAPI/TShock.cs:1520 msgid "Unable to parse command '{0}' from player {1}." msgstr "" -#: ../../TShockAPI/TShock.cs:1483 +#: ../../TShockAPI/TShock.cs:1519 msgid "" "Unable to parse command. Please contact an administrator for assistance." msgstr "" @@ -6788,7 +6780,7 @@ msgstr "" msgid "Unrecognized player direction" msgstr "" -#: ../../TShockAPI/TShock.cs:462 +#: ../../TShockAPI/TShock.cs:493 msgid "" "Until the problem is resolved, TShock will not be able to start (and will " "crash on startup)." @@ -6835,11 +6827,11 @@ msgstr "" msgid "Use \"{0}worldevent rain slime\" to start slime rain!" msgstr "" -#: ../../TShockAPI/TSPlayer.cs:1971 +#: ../../TShockAPI/TSPlayer.cs:2058 msgid "Use \"my query\" for items with spaces." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:1972 +#: ../../TShockAPI/TSPlayer.cs:2059 msgid "" "Use tsi:[number] or tsn:[username] to distinguish between user IDs and " "usernames." @@ -6897,8 +6889,8 @@ msgid "" "privileges." msgstr "" -#: ../../TShockAPI/TShock.cs:390 #: ../../TShockAPI/TShock.cs:394 +#: ../../TShockAPI/TShock.cs:398 #, csharp-format msgid "Using {0} for tile implementation" msgstr "" @@ -7012,7 +7004,7 @@ msgstr "" msgid "Warps ({{0}}/{{1}}):" msgstr "" -#: ../../TShockAPI/TShock.cs:448 +#: ../../TShockAPI/TShock.cs:479 msgid "Welcome to TShock for Terraria!" msgstr "" @@ -7042,12 +7034,12 @@ msgstr "" msgid "World mode set to {0}." msgstr "" -#: ../../TShockAPI/TShock.cs:818 +#: ../../TShockAPI/TShock.cs:854 #, csharp-format msgid "World name will be overridden by: {0}" msgstr "" -#: ../../TShockAPI/TShock.cs:775 +#: ../../TShockAPI/TShock.cs:811 #, csharp-format msgid "World path has been set to {0}" msgstr "" @@ -7078,7 +7070,7 @@ msgstr "" msgid "You are dead. Dead players can't go home." msgstr "" -#: ../../TShockAPI/TShock.cs:1501 +#: ../../TShockAPI/TShock.cs:1537 msgid "You are muted!" msgstr "" @@ -7089,7 +7081,7 @@ msgstr "" msgid "You are muted." msgstr "" -#: ../../TShockAPI/Commands.cs:6758 +#: ../../TShockAPI/Commands.cs:6754 #, csharp-format msgid "You are no longer in god mode." msgstr "" @@ -7114,7 +7106,7 @@ msgstr "" msgid "You are now being annoyed." msgstr "" -#: ../../TShockAPI/Commands.cs:6757 +#: ../../TShockAPI/Commands.cs:6753 #, csharp-format msgid "You are now in god mode." msgstr "" @@ -7132,7 +7124,7 @@ msgid_plural "You butchered {0} NPCs." msgstr[0] "" msgstr[1] "" -#: ../../TShockAPI/TShock.cs:450 +#: ../../TShockAPI/TShock.cs:481 msgid "You can modify & distribute it under the terms of the GNU GPLv3." msgstr "" @@ -7210,7 +7202,7 @@ msgstr "" msgid "You cannot spawn banned items." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3445 +#: ../../TShockAPI/GetDataHandlers.cs:3486 msgid "You cannot use the Enchanted Sundial because time is stopped." msgstr "" @@ -7243,7 +7235,7 @@ msgstr[1] "" msgid "You didn't put a player name." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4211 +#: ../../TShockAPI/GetDataHandlers.cs:4256 msgid "You died! Normally, you'd be banned." msgstr "" @@ -7252,15 +7244,15 @@ msgstr "" msgid "You do not have access to this command." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:828 +#: ../../TShockAPI/TSPlayer.cs:829 msgid "You do not have permission to build in the spawn point." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:831 +#: ../../TShockAPI/TSPlayer.cs:832 msgid "You do not have permission to build in this region." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:825 +#: ../../TShockAPI/TSPlayer.cs:826 msgid "You do not have permission to build on this server." msgstr "" @@ -7296,7 +7288,7 @@ msgstr "" msgid "You do not have permission to grow this tree type" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2960 +#: ../../TShockAPI/GetDataHandlers.cs:2999 msgid "You do not have permission to hurt Town NPCs." msgstr "" @@ -7357,7 +7349,7 @@ msgstr "" msgid "You do not have permission to place actuators." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3967 +#: ../../TShockAPI/GetDataHandlers.cs:4011 msgid "You do not have permission to place Logic Sensors." msgstr "" @@ -7366,7 +7358,7 @@ msgstr "" msgid "You do not have permission to place this tile." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3493 +#: ../../TShockAPI/GetDataHandlers.cs:3534 msgid "You do not have permission to relocate Town NPCs." msgstr "" @@ -7382,15 +7374,15 @@ msgstr "" msgid "You do not have permission to send emotes!" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3532 +#: ../../TShockAPI/GetDataHandlers.cs:3573 msgid "You do not have permission to spawn pets." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4127 +#: ../../TShockAPI/GetDataHandlers.cs:4171 msgid "You do not have permission to start a party." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3525 +#: ../../TShockAPI/GetDataHandlers.cs:3566 msgid "You do not have permission to start invasions." msgstr "" @@ -7399,19 +7391,19 @@ msgstr "" msgid "You do not have permission to start the {0} event." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4145 +#: ../../TShockAPI/GetDataHandlers.cs:4189 msgid "You do not have permission to start the Old One's Army." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3518 +#: ../../TShockAPI/GetDataHandlers.cs:3559 msgid "You do not have permission to summon bosses." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2970 +#: ../../TShockAPI/GetDataHandlers.cs:3009 msgid "You do not have permission to summon the Empress of Light." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2987 +#: ../../TShockAPI/GetDataHandlers.cs:3026 msgid "You do not have permission to summon the Lunatic Cultist!" msgstr "" @@ -7427,12 +7419,12 @@ msgstr "" msgid "You do not have permission to teleport other players." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3833 +#: ../../TShockAPI/GetDataHandlers.cs:3876 #, csharp-format msgid "You do not have permission to teleport using {0}." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3745 +#: ../../TShockAPI/GetDataHandlers.cs:3788 msgid "You do not have permission to teleport using items." msgstr "" @@ -7440,7 +7432,7 @@ msgstr "" msgid "You do not have permission to teleport using pylons." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3769 +#: ../../TShockAPI/GetDataHandlers.cs:3812 msgid "You do not have permission to teleport using Wormhole Potions." msgstr "" @@ -7458,7 +7450,7 @@ msgid "" "server-side-character data." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3437 +#: ../../TShockAPI/GetDataHandlers.cs:3478 msgid "You do not have permission to use the Enchanted Sundial." msgstr "" @@ -7473,7 +7465,7 @@ msgstr "" msgid "You failed to change your password." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2458 +#: ../../TShockAPI/GetDataHandlers.cs:2493 msgid "You have been Bounced." msgstr "" @@ -7504,7 +7496,7 @@ msgstr "" msgid "You have changed {0}'s group to {1} for {2}" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:4226 +#: ../../TShockAPI/GetDataHandlers.cs:4271 msgid "" "You have fallen in hardcore mode, and your items have been lost forever." msgstr "" @@ -7573,11 +7565,11 @@ msgstr "" msgid "You launched fireworks on yourself." msgstr "" -#: ../../TShockAPI/TShock.cs:608 +#: ../../TShockAPI/TShock.cs:648 msgid "You logged in from another location." msgstr "" -#: ../../TShockAPI/TShock.cs:599 +#: ../../TShockAPI/TShock.cs:639 msgid "You logged in from the same IP." msgstr "" @@ -7585,7 +7577,7 @@ msgstr "" msgid "You may now receive whispers from other players." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2621 +#: ../../TShockAPI/GetDataHandlers.cs:2656 msgid "" "You may wish to consider removing the tshock.ignore.ssc permission or " "negating it for this player." @@ -7599,7 +7591,7 @@ msgstr "" msgid "You must provide a setup code!" msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3448 +#: ../../TShockAPI/GetDataHandlers.cs:3489 msgid "" "You must set ForceTime to normal via config to use the Enchanted Sundial." msgstr "" @@ -7612,15 +7604,15 @@ msgstr "" msgid "You must use this command in-game." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2523 +#: ../../TShockAPI/GetDataHandlers.cs:2558 msgid "You need to join with a hardcore player." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2517 +#: ../../TShockAPI/GetDataHandlers.cs:2552 msgid "You need to join with a mediumcore player or higher." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:2511 +#: ../../TShockAPI/GetDataHandlers.cs:2546 msgid "You need to join with a softcore player." msgstr "" @@ -7648,7 +7640,7 @@ msgstr "" msgid "You were teleported to {0}." msgstr "" -#: ../../TShockAPI/TShock.cs:1706 +#: ../../TShockAPI/TShock.cs:1742 msgid "You will be teleported to your last known location..." msgstr "" @@ -7684,7 +7676,7 @@ msgstr "" msgid "Your client sent a blank character name." msgstr "" -#: ../../TShockAPI/TShock.cs:1351 +#: ../../TShockAPI/TShock.cs:1387 msgid "" "Your client sent a blank UUID. Configure it to send one or use a different " "client." @@ -7721,7 +7713,7 @@ msgid "" "off." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3217 +#: ../../TShockAPI/GetDataHandlers.cs:3257 msgid "Your password did not match this character's password." msgstr "" @@ -7742,7 +7734,7 @@ msgstr "" msgid "Your server-side character data has been saved." msgstr "" -#: ../../TShockAPI/TSPlayer.cs:1325 +#: ../../TShockAPI/TSPlayer.cs:1342 msgid "Your temporary group access has expired." msgstr "" @@ -7750,7 +7742,7 @@ msgstr "" msgid "z <#> - Sets the z-order of the region." msgstr "" -#: ../../TShockAPI/GetDataHandlers.cs:3235 +#: ../../TShockAPI/GetDataHandlers.cs:3275 msgctxt "Likely non-vanilla client send zero-length password" msgid "You have been Bounced for invalid password." msgstr "" From 60599ef5b58ef8f548ec36742a091f11ddda9d8b Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Fri, 22 Sep 2023 22:30:18 +0200 Subject: [PATCH 083/135] Rewrite the `.dockerignore` file into a denylist This should help with not forgetting to add any new directories (such as TShockInstaller and TShockPluginManager, which have been missing until now). --- .dockerignore | 33 +++++++++++++++++++-------------- docs/changelog.md | 1 + 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/.dockerignore b/.dockerignore index b20ac5b6..234117c0 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,15 +1,20 @@ -# ignore every file -* +# Ignore all Git metadata and build output (in all directories). +**/.git* +**/bin/ +**/obj/ -# except for the ones required for building -!i18n/ -!prebuilts/ -!TerrariaServerAPI/ -!TShockAPI/ -!TShockLauncher/ -!TShockLauncher.Tests/ -!TShock.sln - -# but exclude build artifacts -*/bin/ -*/obj/ +# Ignore other specific files that aren't needed for the build itself. +/.all-contributorsrc +/.dockerignore +/.editorconfig +/.vscode +/appveyor.yml +/COPYING +/crowdin.yml +/Dockerfile +/docs +/README.md +/README_cn.md +/renovate.json +/scripts +/SECURITY.md diff --git a/docs/changelog.md b/docs/changelog.md index 6a159e8a..035df8ad 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -89,6 +89,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Added a method `TSPlayer.GiveItem`, which has `TShockAPI.NetItem` structure in its arguments. (@AgaSpace) * Added a property `TSPlayer.Hostile`, which gets pvp player mode. (@AgaSpace) * Fixed typo in `/gbuff`. (@sgkoishi, #2955) +* Rewrote the `.dockerignore` file into a denylist. (@timschumi) ## TShock 5.2 * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) From 93749be8bf57520d764264dbef007252883fe9a6 Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Fri, 29 Sep 2023 17:36:58 +0200 Subject: [PATCH 084/135] Clear default Dockerfile assignments for `*PLATFORM` These kept Docker buildx from automatically building for multiple platforms. --- Dockerfile | 10 ++++++---- docs/docker.md | 8 ++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index 186bee70..87024fb9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,8 @@ -ARG TARGETPLATFORM=linux/amd64 -ARG BUILDPLATFORM=${TARGETPLATFORM} +# TARGETPLATFORM and BUILDPLATFORM are automatically filled in by Docker buildx. +# They should not be set in the global scope manually. FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/dotnet/sdk:6.0 AS builder -ARG TARGETPLATFORM - # Copy build context WORKDIR /TShock COPY . ./ @@ -12,6 +10,10 @@ COPY . ./ # Build and package release based on target architecture RUN dotnet build -v m WORKDIR /TShock/TShockLauncher + +# Make TARGETPLATFORM available to the container. +ARG TARGETPLATFORM + RUN \ case "${TARGETPLATFORM}" in \ "linux/amd64") export ARCH="linux-x64" \ diff --git a/docs/docker.md b/docs/docker.md index 0b89681b..ca018510 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -14,15 +14,15 @@ Open ports can also be passed through using `-p :`. For Example: ```bash -# Building the image -docker build -t tshock:linux-amd64 --build-arg TARGETPLATFORM=linux/amd64 . +# Building the image using buildx and loading it into docker +docker buildx build -t tshock:latest --load . # Running the image docker run -p 7777:7777 -p 7878:7878 \ -v /home/cider/tshock/:/tshock \ -v /home/cider/.local/share/Terraria/Worlds:/worlds \ -v /home/cider/tshock/plugins:/plugins \ - --rm -it tshock:linux-amd64 \ + --rm -it tshock:latest \ -world /worlds/backflip.wld -motd "OMFG DOCKER" ``` @@ -33,7 +33,7 @@ Using `docker buildx`, you could build [multi-platform images](https://docs.dock For Example: ```bash # Building the image using buildx and loading it into docker -sudo docker buildx build -t tshock:linux-arm64 --platform linux/arm64 --load . +docker buildx build -t tshock:linux-arm64 --platform linux/arm64 --load . # Running the image docker run -p 7777:7777 -p 7878:7878 \ From 3ffe8e1274597151dee42a42f8df64a2f7b9a076 Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Fri, 29 Sep 2023 17:13:00 +0200 Subject: [PATCH 085/135] Add CI for Docker images --- .github/workflows/ci-docker.yml | 25 +++++++++++++++++++++++++ docs/changelog.md | 1 + 2 files changed, 26 insertions(+) create mode 100644 .github/workflows/ci-docker.yml diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml new file mode 100644 index 00000000..deb811a2 --- /dev/null +++ b/.github/workflows/ci-docker.yml @@ -0,0 +1,25 @@ +name: CI (Docker image) + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: 'recursive' + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up buildx + uses: docker/setup-buildx-action@v3 + - name: Build image + uses: docker/build-push-action@v5 + with: + context: . + platforms: linux/amd64,linux/arm64,linux/arm/v7,windows/amd64 + push: false + pull: true + cache-from: type=gha, scope=${{ github.workflow }} + cache-to: type=gha, scope=${{ github.workflow }} diff --git a/docs/changelog.md b/docs/changelog.md index 035df8ad..3cb52f80 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -90,6 +90,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Added a property `TSPlayer.Hostile`, which gets pvp player mode. (@AgaSpace) * Fixed typo in `/gbuff`. (@sgkoishi, #2955) * Rewrote the `.dockerignore` file into a denylist. (@timschumi) +* Added CI for Docker images. (@timschumi) ## TShock 5.2 * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) From b6041a738e61f9c995e2b3bbcd3c101f658f3ff5 Mon Sep 17 00:00:00 2001 From: SGKoishi Date: Thu, 21 Dec 2023 20:39:43 -0800 Subject: [PATCH 086/135] Whitelist the Striking Moment (`ParryDamageBuff`) buff --- TShockAPI/Bouncer.cs | 6 ++++++ docs/changelog.md | 1 + 2 files changed, 7 insertions(+) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 6ff7fd1a..4a41a42c 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -434,6 +434,12 @@ namespace TShockAPI CanBeAddedWithoutHostile = true, CanOnlyBeAppliedToSender = true }; + PlayerAddBuffWhitelist[BuffID.WindPushed] = new BuffLimit + { + MaxTicks = 2, + CanBeAddedWithoutHostile = true, + CanOnlyBeAppliedToSender = true + }; #endregion Whitelist } diff --git a/docs/changelog.md b/docs/changelog.md index 035df8ad..235b5ec9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -90,6 +90,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Added a property `TSPlayer.Hostile`, which gets pvp player mode. (@AgaSpace) * Fixed typo in `/gbuff`. (@sgkoishi, #2955) * Rewrote the `.dockerignore` file into a denylist. (@timschumi) +* Added `ParryDamageBuff` (Striking Moment with Brand of the Inferno and shield) to the `PlayerAddBuffWhitelist` (@sgkoishi, #3005) ## TShock 5.2 * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) From 3c2a3f3e53e2eb25ba3d6eeb2ebf066136dcdaa8 Mon Sep 17 00:00:00 2001 From: SGKoishi Date: Thu, 21 Dec 2023 20:43:41 -0800 Subject: [PATCH 087/135] Add unstaged changes --- TShockAPI/Bouncer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 4a41a42c..594d76ff 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -434,9 +434,9 @@ namespace TShockAPI CanBeAddedWithoutHostile = true, CanOnlyBeAppliedToSender = true }; - PlayerAddBuffWhitelist[BuffID.WindPushed] = new BuffLimit + PlayerAddBuffWhitelist[BuffID.ParryDamageBuff] = new BuffLimit { - MaxTicks = 2, + MaxTicks = 300, CanBeAddedWithoutHostile = true, CanOnlyBeAppliedToSender = true }; From cf726368c51647f6189729bead08ef658644951d Mon Sep 17 00:00:00 2001 From: SGKoishi Date: Fri, 22 Dec 2023 13:19:58 -0800 Subject: [PATCH 088/135] Use seconds as the time unit --- TShockAPI/Bouncer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 594d76ff..22d6461a 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -424,7 +424,7 @@ namespace TShockAPI }; PlayerAddBuffWhitelist[BuffID.BrainOfConfusionBuff] = new BuffLimit { - MaxTicks = 240, + MaxTicks = 60 * 4, CanBeAddedWithoutHostile = true, CanOnlyBeAppliedToSender = true }; @@ -436,7 +436,7 @@ namespace TShockAPI }; PlayerAddBuffWhitelist[BuffID.ParryDamageBuff] = new BuffLimit { - MaxTicks = 300, + MaxTicks = 60 * 5, CanBeAddedWithoutHostile = true, CanOnlyBeAppliedToSender = true }; From dbca317639625fe3e317d05f521fb04b6cb37d96 Mon Sep 17 00:00:00 2001 From: SGKoishi Date: Wed, 27 Dec 2023 13:01:55 -0800 Subject: [PATCH 089/135] Update CursedInferno time limit --- TShockAPI/Bouncer.cs | 2 +- docs/changelog.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 22d6461a..aed7ac04 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -2860,7 +2860,7 @@ namespace TShockAPI { BuffID.Poisoned, 3600 }, // BuffID: 20 { BuffID.OnFire, 1200 }, // BuffID: 24 { BuffID.Confused, short.MaxValue }, // BuffID: 31 Brain of Confusion Internal Item ID: 3223 - { BuffID.CursedInferno, 420 }, // BuffID: 39 + { BuffID.CursedInferno, 600 }, // BuffID: 39 { BuffID.Frostburn, 900 }, // BuffID: 44 { BuffID.Ichor, 1200 }, // BuffID: 69 { BuffID.Venom, 1800 }, // BuffID: 70 diff --git a/docs/changelog.md b/docs/changelog.md index 235b5ec9..2a4fbfcd 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -90,7 +90,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Added a property `TSPlayer.Hostile`, which gets pvp player mode. (@AgaSpace) * Fixed typo in `/gbuff`. (@sgkoishi, #2955) * Rewrote the `.dockerignore` file into a denylist. (@timschumi) -* Added `ParryDamageBuff` (Striking Moment with Brand of the Inferno and shield) to the `PlayerAddBuffWhitelist` (@sgkoishi, #3005) +* Added `ParryDamageBuff` (Striking Moment with Brand of the Inferno and shield) for player, updated `CursedInferno` buff for NPC (@sgkoishi, #3005) ## TShock 5.2 * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) From aa526223a6a941933cf7a46fb2177ef58d5eb5cc Mon Sep 17 00:00:00 2001 From: Johannes Grimm Date: Tue, 30 Jan 2024 17:55:39 +0100 Subject: [PATCH 090/135] fix: off no save command Added seting SaveOnSeverExit befor shutting down the server Fixes #2938 --- TShockAPI/Commands.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs index d39b84e0..496669bb 100644 --- a/TShockAPI/Commands.cs +++ b/TShockAPI/Commands.cs @@ -2044,6 +2044,7 @@ namespace TShockAPI private static void OffNoSave(CommandArgs args) { string reason = ((args.Parameters.Count > 0) ? GetString("Server shutting down: ") + String.Join(" ", args.Parameters) : GetString("Server shutting down.")); + Netplay.SaveOnServerExit = false; TShock.Utils.StopServer(false, reason); } From a19ac72e0db7f37728f1ebaa1c4aaa4713642357 Mon Sep 17 00:00:00 2001 From: ZakFahey Date: Sun, 18 Feb 2024 11:36:26 -0800 Subject: [PATCH 091/135] Lazy initialize TextLog._logWriter There is an instance of this in SqlLog that's used as a backup, but since the StreamWriter is created in the constructor, the file is created too. This means that you'll get an empty text log created, every time a server starts up, even if you have text logs disabled. This is especially problematic when you have a multi-server setup with file write conflicts. --- TShockAPI/TextLog.cs | 14 +++++++++++--- docs/changelog.md | 1 + 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/TShockAPI/TextLog.cs b/TShockAPI/TextLog.cs index 070c6c88..ae3bb29e 100644 --- a/TShockAPI/TextLog.cs +++ b/TShockAPI/TextLog.cs @@ -29,7 +29,8 @@ namespace TShockAPI /// public class TextLog : ILog, IDisposable { - private readonly StreamWriter _logWriter; + private readonly bool ClearFile; + private StreamWriter _logWriter; /// /// File name of the Text log @@ -44,7 +45,7 @@ namespace TShockAPI public TextLog(string filename, bool clear) { FileName = filename; - _logWriter = new StreamWriter(filename, !clear); + ClearFile = clear; } public bool MayWriteType(TraceLevel type) @@ -247,6 +248,10 @@ namespace TShockAPI { if (!MayWriteType(level)) return; + if (_logWriter is null) + { + _logWriter = new StreamWriter(FileName, !ClearFile); + } var caller = "TShock"; @@ -278,7 +283,10 @@ namespace TShockAPI public void Dispose() { - _logWriter.Dispose(); + if (_logWriter != null) + { + _logWriter.Dispose(); + } } } } diff --git a/docs/changelog.md b/docs/changelog.md index 035df8ad..ebb335cc 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -88,6 +88,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Added a method `TSPlayer.UpdateSection` with arguments `rectangle` and `isLoaded`, which will load some area from the server to the player. (@AgaSpace) * Added a method `TSPlayer.GiveItem`, which has `TShockAPI.NetItem` structure in its arguments. (@AgaSpace) * Added a property `TSPlayer.Hostile`, which gets pvp player mode. (@AgaSpace) +* Fixed bug where when the `UseSqlLogs` config property is true, an empty log file would still get created. (@ZakFahey) * Fixed typo in `/gbuff`. (@sgkoishi, #2955) * Rewrote the `.dockerignore` file into a denylist. (@timschumi) From 5c4db00f1e997bce7122f9da3937560d77ddfc08 Mon Sep 17 00:00:00 2001 From: Arthri <41360489+a@users.noreply.github.com> Date: Wed, 24 Apr 2024 05:31:49 +0000 Subject: [PATCH 092/135] Fix Cursed Flare --- TShockAPI/Bouncer.cs | 2 +- docs/changelog.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 6ff7fd1a..58c14b1c 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -2854,7 +2854,7 @@ namespace TShockAPI { BuffID.Poisoned, 3600 }, // BuffID: 20 { BuffID.OnFire, 1200 }, // BuffID: 24 { BuffID.Confused, short.MaxValue }, // BuffID: 31 Brain of Confusion Internal Item ID: 3223 - { BuffID.CursedInferno, 420 }, // BuffID: 39 + { BuffID.CursedInferno, 600 }, // BuffID: 39 { BuffID.Frostburn, 900 }, // BuffID: 44 { BuffID.Ichor, 1200 }, // BuffID: 69 { BuffID.Venom, 1800 }, // BuffID: 70 diff --git a/docs/changelog.md b/docs/changelog.md index 27e037d6..b1974546 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -92,6 +92,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Fixed typo in `/gbuff`. (@sgkoishi, #2955) * Rewrote the `.dockerignore` file into a denylist. (@timschumi) * Added CI for Docker images. (@timschumi) +* Fixed Cursed Flares kicking players for invalid buff. (@Arthri) ## TShock 5.2 * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) From 8df09cb4a8a8707524390265744e61e3dbe690c6 Mon Sep 17 00:00:00 2001 From: Cai <13110818005@qq.com> Date: Wed, 31 Jul 2024 19:11:50 +0800 Subject: [PATCH 093/135] fix: /help, /me, and /p commands can't work in non-English languages --- TShockAPI/Localization/EnglishLanguage.cs | 26 +++++++++++++++++++++++ TShockAPI/TShock.cs | 4 ++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/TShockAPI/Localization/EnglishLanguage.cs b/TShockAPI/Localization/EnglishLanguage.cs index 1ec4ecea..97507141 100644 --- a/TShockAPI/Localization/EnglishLanguage.cs +++ b/TShockAPI/Localization/EnglishLanguage.cs @@ -20,7 +20,9 @@ using System; using System.Collections.Generic; using System.Linq; using Terraria; +using Terraria.Initializers; using Terraria.Localization; +using Terraria.UI.Chat; namespace TShockAPI.Localization { @@ -37,6 +39,8 @@ namespace TShockAPI.Localization private static readonly Dictionary Buffs = new Dictionary(); + private static readonly Dictionary VanillaCommands = new Dictionary(); + internal static void Initialize() { var culture = Language.ActiveCulture; @@ -71,6 +75,15 @@ namespace TShockAPI.Localization var i = (int)field.GetValue(null); Prefixs.Add(i, Lang.prefix[i].Value); } + + ChatInitializer.Load(); + foreach (var command in ChatManager.Commands._localizedCommands) + { + if (VanillaCommands.ContainsKey(command.Value._name)) + continue; + VanillaCommands.Add(command.Value._name,command.Key.Value); + } + ChatManager.Commands._localizedCommands.Clear(); } finally { @@ -136,5 +149,18 @@ namespace TShockAPI.Localization return null; } + + /// + /// Get vanilla command text in English + /// + /// vanilla command name + /// vanilla command text English + public static string GetCommandTextByName(string name) + { + string commandText; + if (VanillaCommands.TryGetValue(name, out commandText)) + return commandText; + return null; + } } } diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index 054e31df..48fab88d 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -1497,11 +1497,11 @@ namespace TShockAPI { if (!String.IsNullOrEmpty(text)) { - text = item.Key.Value + ' ' + text; + text = EnglishLanguage.GetCommandTextByName(item.Value._name) + ' ' + text; } else { - text = item.Key.Value; + text = EnglishLanguage.GetCommandTextByName(item.Value._name); } break; } From b90182f1e5a8cc47057fbb809bd2ee3311c13714 Mon Sep 17 00:00:00 2001 From: Cai <13110818005@qq.com> Date: Wed, 31 Jul 2024 19:16:23 +0800 Subject: [PATCH 094/135] update changelog --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index b1974546..d2e1444e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -93,6 +93,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Rewrote the `.dockerignore` file into a denylist. (@timschumi) * Added CI for Docker images. (@timschumi) * Fixed Cursed Flares kicking players for invalid buff. (@Arthri) +* Fixed /help, /me, and /p commands can't work in non-English languages (@ACaiCat) ## TShock 5.2 * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) From a8624a816344dd39a63a701e98fe57c086613a34 Mon Sep 17 00:00:00 2001 From: Cai <13110818005@qq.com> Date: Wed, 31 Jul 2024 19:16:23 +0800 Subject: [PATCH 095/135] update changelog --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index b1974546..d2e1444e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -93,6 +93,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Rewrote the `.dockerignore` file into a denylist. (@timschumi) * Added CI for Docker images. (@timschumi) * Fixed Cursed Flares kicking players for invalid buff. (@Arthri) +* Fixed /help, /me, and /p commands can't work in non-English languages (@ACaiCat) ## TShock 5.2 * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) From 7419205548fb9779f83bc924222c1df9741ddff5 Mon Sep 17 00:00:00 2001 From: Cai <13110818005@qq.com> Date: Wed, 31 Jul 2024 19:24:53 +0800 Subject: [PATCH 096/135] rename --- TShockAPI/Localization/EnglishLanguage.cs | 14 +++++++------- TShockAPI/TShock.cs | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/TShockAPI/Localization/EnglishLanguage.cs b/TShockAPI/Localization/EnglishLanguage.cs index 97507141..334c4780 100644 --- a/TShockAPI/Localization/EnglishLanguage.cs +++ b/TShockAPI/Localization/EnglishLanguage.cs @@ -39,7 +39,7 @@ namespace TShockAPI.Localization private static readonly Dictionary Buffs = new Dictionary(); - private static readonly Dictionary VanillaCommands = new Dictionary(); + private static readonly Dictionary VanillaCommandsPrefixs = new Dictionary(); internal static void Initialize() { @@ -79,9 +79,9 @@ namespace TShockAPI.Localization ChatInitializer.Load(); foreach (var command in ChatManager.Commands._localizedCommands) { - if (VanillaCommands.ContainsKey(command.Value._name)) + if (VanillaCommandsPrefixs.ContainsKey(command.Value._name)) continue; - VanillaCommands.Add(command.Value._name,command.Key.Value); + VanillaCommandsPrefixs.Add(command.Value._name,command.Key.Value); } ChatManager.Commands._localizedCommands.Clear(); } @@ -151,14 +151,14 @@ namespace TShockAPI.Localization } /// - /// Get vanilla command text in English + /// Get vanilla command prefix in English /// /// vanilla command name - /// vanilla command text English - public static string GetCommandTextByName(string name) + /// vanilla command prefix in English + public static string GetCommandPrefixByName(string name) { string commandText; - if (VanillaCommands.TryGetValue(name, out commandText)) + if (VanillaCommandsPrefixs.TryGetValue(name, out commandText)) return commandText; return null; } diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index 48fab88d..9c1015bc 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -1497,11 +1497,11 @@ namespace TShockAPI { if (!String.IsNullOrEmpty(text)) { - text = EnglishLanguage.GetCommandTextByName(item.Value._name) + ' ' + text; + text = EnglishLanguage.GetCommandPrefixByName(item.Value._name) + ' ' + text; } else { - text = EnglishLanguage.GetCommandTextByName(item.Value._name); + text = EnglishLanguage.GetCommandPrefixByName(item.Value._name); } break; } From 5075997264b48e27960e3446a948ecb0ea0f5a03 Mon Sep 17 00:00:00 2001 From: quicm <2648373+quicm@users.noreply.github.com> Date: Tue, 17 Dec 2024 01:13:01 +1030 Subject: [PATCH 097/135] Add bandaid fix to RemoteClient.Reset for SecAd GHSA-hvm9-wc8j-mgrc --- TShockAPI/TShock.cs | 8 ++++++++ TShockAPI/TShockAPI.csproj | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index 054e31df..bcd72aa3 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -428,6 +428,8 @@ namespace TShockAPI Hooks.AccountHooks.AccountDelete += OnAccountDelete; Hooks.AccountHooks.AccountCreate += OnAccountCreate; + On.Terraria.RemoteClient.Reset += RemoteClient_Reset; + GetDataHandlers.InitGetDataHandler(); Commands.InitCommands(); @@ -496,6 +498,12 @@ namespace TShockAPI } } + private static void RemoteClient_Reset(On.Terraria.RemoteClient.orig_Reset orig, RemoteClient client) + { + client.ClientUUID = null; + orig(client); + } + private static void OnAchievementInitializerLoad(ILContext il) { // Modify AchievementInitializer.Load to remove the Main.netMode == 2 check (occupies the first 4 IL instructions) diff --git a/TShockAPI/TShockAPI.csproj b/TShockAPI/TShockAPI.csproj index a3731709..9da8cd08 100644 --- a/TShockAPI/TShockAPI.csproj +++ b/TShockAPI/TShockAPI.csproj @@ -18,7 +18,7 @@ Also, be sure to release on github with the exact assembly version tag as below so that the update manager works correctly (via the Github releases api and mimic) --> - 5.2.0 + 5.2.1 TShock for Terraria Pryaxis & TShock Contributors TShockAPI From 3da4bf4b45ad77f96afb261aaf92c82056c7bf4c Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Fri, 3 Jan 2025 00:05:25 +0900 Subject: [PATCH 098/135] Remove inactive people from sponsors --- .github/FUNDING.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 8275735a..75b69d48 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1,2 @@ # These are supported funding model platforms -github: [SignatureBeef, hakusaro, Stealownz, QuiCM] +github: [SignatureBeef, QuiCM] From 99701f9a8415382c8a48ecc324d988a8a579d3f0 Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Sat, 4 Jan 2025 05:31:04 +0900 Subject: [PATCH 099/135] Update logo url in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b5559b37..0369df53 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- TShock for Terraria
+ TShock for Terraria
AppVeyor Build Status From 1b31665a888769be8747637c5be51e81ff8cbf26 Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Sat, 4 Jan 2025 05:31:27 +0900 Subject: [PATCH 100/135] Update logo in readme --- README_cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_cn.md b/README_cn.md index e038817e..65c5eba6 100644 --- a/README_cn.md +++ b/README_cn.md @@ -1,5 +1,5 @@

- TShock for Terraria
+ TShock for Terraria
AppVeyor Build Status From af969898b1e54f684e48cd30bc019393725c4a00 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 21:17:29 +0000 Subject: [PATCH 101/135] Update dependency NuGet.Packaging to 6.3.4 [SECURITY] --- TShockPluginManager/TShockPluginManager.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TShockPluginManager/TShockPluginManager.csproj b/TShockPluginManager/TShockPluginManager.csproj index 33c503fd..774f9819 100644 --- a/TShockPluginManager/TShockPluginManager.csproj +++ b/TShockPluginManager/TShockPluginManager.csproj @@ -7,7 +7,7 @@ - + From 68bb381ece3fca103ddb6f5c2197046de70be498 Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Sat, 4 Jan 2025 13:24:14 +0100 Subject: [PATCH 102/135] docker: Bump docker/build-push-action to v6 --- .github/workflows/ci-docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index deb811a2..41454ae4 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -15,7 +15,7 @@ jobs: - name: Set up buildx uses: docker/setup-buildx-action@v3 - name: Build image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . platforms: linux/amd64,linux/arm64,linux/arm/v7,windows/amd64 From a82881b7d3a01f002c2cc15151f6c50ed5cdbc92 Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Sat, 4 Jan 2025 13:33:23 +0100 Subject: [PATCH 103/135] docker: Tag and push images to GHCR automatically Co-authored-by: BrailleBennett Co-authored-by: TheSuperGamer20578 --- .github/workflows/ci-docker.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 41454ae4..691c8e81 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -5,6 +5,8 @@ on: [push, pull_request] jobs: build: runs-on: ubuntu-latest + permissions: + packages: write steps: - name: Checkout uses: actions/checkout@v4 @@ -14,12 +16,25 @@ jobs: uses: docker/setup-qemu-action@v3 - name: Set up buildx uses: docker/setup-buildx-action@v3 + - name: Login to ghcr.io + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Generate version information + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }} - name: Build image uses: docker/build-push-action@v6 with: context: . platforms: linux/amd64,linux/arm64,linux/arm/v7,windows/amd64 - push: false + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} pull: true cache-from: type=gha, scope=${{ github.workflow }} cache-to: type=gha, scope=${{ github.workflow }} From cc3d2af15f100d6b3a23a4cb252a40b67fe6e006 Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Sat, 4 Jan 2025 13:45:19 +0100 Subject: [PATCH 104/135] docker: Generate build provenance attestations Co-authored-by: BrailleBennett Co-authored-by: TheSuperGamer20578 --- .github/workflows/ci-docker.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 691c8e81..82e152a9 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -6,6 +6,8 @@ jobs: build: runs-on: ubuntu-latest permissions: + attestations: write + id-token: write packages: write steps: - name: Checkout @@ -28,6 +30,7 @@ jobs: with: images: ghcr.io/${{ github.repository }} - name: Build image + id: build uses: docker/build-push-action@v6 with: context: . @@ -38,3 +41,9 @@ jobs: pull: true cache-from: type=gha, scope=${{ github.workflow }} cache-to: type=gha, scope=${{ github.workflow }} + - name: Generate build provenance attestation + uses: actions/attest-build-provenance@v2 + with: + subject-name: ghcr.io/${{ github.repository }} + subject-digest: ${{ steps.build.outputs.digest }} + push-to-registry: true From ca3dfcdf17947b09b525fe593e2ac8bf17602a4c Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Sat, 4 Jan 2025 14:04:57 +0100 Subject: [PATCH 105/135] docker: Push semver tags if a version tag is pushed --- .github/workflows/ci-docker.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 82e152a9..cb09fc93 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -29,6 +29,14 @@ jobs: uses: docker/metadata-action@v5 with: images: ghcr.io/${{ github.repository }} + tags: | + type=schedule + type=ref,event=branch + type=ref,event=tag,enable=${{ !startsWith(github.ref, 'refs/tags/v') }} + type=ref,event=pr + type=semver,pattern={{version}},enable=${{ startsWith(github.ref, 'refs/tags/v') }} + type=semver,pattern={{major}}.{{minor}},enable=${{ startsWith(github.ref, 'refs/tags/v') }} + type=semver,pattern={{major}},enable=${{ startsWith(github.ref, 'refs/tags/v') }} - name: Build image id: build uses: docker/build-push-action@v6 From 660ec5bc601f075860f77d4330cf2c37fa2180c2 Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Sat, 4 Jan 2025 14:14:38 +0100 Subject: [PATCH 106/135] docker: Restrict tagging 'latest' to version tags --- .github/workflows/ci-docker.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index cb09fc93..6965c773 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -37,6 +37,8 @@ jobs: type=semver,pattern={{version}},enable=${{ startsWith(github.ref, 'refs/tags/v') }} type=semver,pattern={{major}}.{{minor}},enable=${{ startsWith(github.ref, 'refs/tags/v') }} type=semver,pattern={{major}},enable=${{ startsWith(github.ref, 'refs/tags/v') }} + flavor: | + latest=${{ startsWith(github.ref, 'refs/tags/v') }} - name: Build image id: build uses: docker/build-push-action@v6 From cbdae5b15eedd5237199f0539ffc6d6b7d92e185 Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Sat, 4 Jan 2025 14:29:40 +0100 Subject: [PATCH 107/135] Add a changelog entry for publishing to GHCR --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index b1974546..b5303c5f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -93,6 +93,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Rewrote the `.dockerignore` file into a denylist. (@timschumi) * Added CI for Docker images. (@timschumi) * Fixed Cursed Flares kicking players for invalid buff. (@Arthri) +* Added automatic publishing of Docker images to GHCR. (@timschumi) ## TShock 5.2 * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) From d54711651b9f2732f8200db238c710561e126e2d Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Sat, 4 Jan 2025 14:36:58 +0100 Subject: [PATCH 108/135] docker: Update documentation for officially provided Docker images --- docs/docker.md | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/docs/docker.md b/docs/docker.md index ca018510..afc4bdfc 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -14,32 +14,27 @@ Open ports can also be passed through using `-p :`. For Example: ```bash -# Building the image using buildx and loading it into docker -docker buildx build -t tshock:latest --load . - -# Running the image docker run -p 7777:7777 -p 7878:7878 \ -v /home/cider/tshock/:/tshock \ -v /home/cider/.local/share/Terraria/Worlds:/worlds \ -v /home/cider/tshock/plugins:/plugins \ - --rm -it tshock:latest \ + --rm -it ghcr.io/pryaxis/tshock:latest \ -world /worlds/backflip.wld -motd "OMFG DOCKER" ``` -## Building for Other Platforms +## Building custom images -Using `docker buildx`, you could build [multi-platform images](https://docs.docker.com/build/building/multi-platform/) for TShock. +Occasionally, it may be necessary to adjust TShock with customizations that are not included in the upstream project. +Therefore, these changes are also not available in the officially provided Docker images. + +To build and load a Docker image from your local checkout, use the following `buildx` command: -For Example: ```bash -# Building the image using buildx and loading it into docker -docker buildx build -t tshock:linux-arm64 --platform linux/arm64 --load . - -# Running the image -docker run -p 7777:7777 -p 7878:7878 \ - -v /home/cider/tshock/:/tshock \ - -v /home/cider/.local/share/Terraria/Worlds:/worlds \ - -v /home/cider/tshock/plugins:/plugins \ - --rm -it tshock:linux-arm64 \ - -world /worlds/backflip.wld -motd "ARM64 ftw" +docker buildx build -t tshock:latest --load . +``` + +It is also possible to build [multi-platform images](https://docs.docker.com/build/building/multi-platform/) for TShock (e.g. an image targeting `arm64`, on a host that is not `arm64`): + +```bash +docker buildx build -t tshock:linux-arm64 --platform linux/arm64 --load . ``` From 02ce8fdf0deac5c52a98070d9970fbd1b5654490 Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Sun, 5 Jan 2025 01:30:28 +0100 Subject: [PATCH 109/135] docker: Don't push to the registry for Pull Requests This requires more thought on how to handle secrets, and whether to just omit it permanently. --- .github/workflows/ci-docker.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 6965c773..3d47d3c6 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -45,13 +45,14 @@ jobs: with: context: . platforms: linux/amd64,linux/arm64,linux/arm/v7,windows/amd64 - push: true + push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} pull: true cache-from: type=gha, scope=${{ github.workflow }} cache-to: type=gha, scope=${{ github.workflow }} - name: Generate build provenance attestation + if: ${{ github.event_name != 'pull_request' }} uses: actions/attest-build-provenance@v2 with: subject-name: ghcr.io/${{ github.repository }} From d53517104ced20ce5f7b17b8351b200d20769463 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 5 Jan 2025 10:45:58 +0000 Subject: [PATCH 110/135] Update dependency NuGet.Protocol to 6.3.3 [SECURITY] --- TShockPluginManager/TShockPluginManager.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TShockPluginManager/TShockPluginManager.csproj b/TShockPluginManager/TShockPluginManager.csproj index 774f9819..1d53f320 100644 --- a/TShockPluginManager/TShockPluginManager.csproj +++ b/TShockPluginManager/TShockPluginManager.csproj @@ -8,7 +8,7 @@ - + From e21322b86beb0850d4302aa9c622d5c825ea3dec Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 5 Jan 2025 10:58:00 +0000 Subject: [PATCH 111/135] Update dependency SharpZipLib to 1.4.2 --- TShockInstaller/TShockInstaller.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TShockInstaller/TShockInstaller.csproj b/TShockInstaller/TShockInstaller.csproj index 9b73d66a..ac3bfff4 100644 --- a/TShockInstaller/TShockInstaller.csproj +++ b/TShockInstaller/TShockInstaller.csproj @@ -12,6 +12,6 @@ - + From 78dd32a8374c9d8a0bc5b5a80b37c38708fb21f1 Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Sat, 25 Jan 2025 21:28:24 +0900 Subject: [PATCH 112/135] Fix changelog versioning --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index b1974546..54b23a8a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -78,6 +78,9 @@ Use past tense when adding new entries; sign your name off when you add or chang * If there is no section called "Upcoming changes" below this line, please add one with `## Upcoming changes` as the first line, and then a bulleted item directly after with the first change. --> ## Upcoming changes +* You know the drill + +## TShock 5.2.1 * Updated `TSPlayer.GodMode`. (@AgaSpace) * Previously the field was used as some kind of dataset changed by /godmode command, but now it is a property that receives/changes data in journey mode. * Added the `TSPlayer.Client` property. It allows the developer to get the `RemoteClient` player, without an additional call to `Terraria.Netplay.Clients`. (@AgaSpace) From b4968adb7d964f851633f840d407654e6e90b9aa Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Sat, 25 Jan 2025 22:27:48 +0900 Subject: [PATCH 113/135] Fix typo in config file Closes https://github.com/Pryaxis/TShock/pull/3052 --- TShockAPI/Configuration/TShockConfig.cs | 4 ++-- docs/changelog.md | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/TShockAPI/Configuration/TShockConfig.cs b/TShockAPI/Configuration/TShockConfig.cs index 79c374b2..c91466fe 100644 --- a/TShockAPI/Configuration/TShockConfig.cs +++ b/TShockAPI/Configuration/TShockConfig.cs @@ -320,8 +320,8 @@ namespace TShockAPI.Configuration [Description("The reason given if banning a mediumcore player on death.")] public string MediumcoreBanReason = GetString("Death results in a ban"); - ///

Disbales IP bans by default, if no arguments are passed to the ban command. - [Description("Disbales IP bans by default, if no arguments are passed to the ban command.")] + /// Disables IP bans by default, if no arguments are passed to the ban command. + [Description("Disables IP bans by default, if no arguments are passed to the ban command.")] public bool DisableDefaultIPBan; /// Enable or disable the whitelist based on IP addresses in the whitelist.txt file. diff --git a/docs/changelog.md b/docs/changelog.md index 683cddcb..00aec595 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -79,6 +79,8 @@ Use past tense when adding new entries; sign your name off when you add or chang ## Upcoming changes * Fixed `/dump-reference-data` mutate the command names. (#2943, @sgkoishi) +* You know the drill +* Fix typo in config for IP bans. (@redchess64) ## TShock 5.2.1 * Updated `TSPlayer.GodMode`. (@AgaSpace) From 9aba57dab6d6886f61f76a4a0597116f5efe1cb7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 25 Jan 2025 13:31:02 +0000 Subject: [PATCH 114/135] Update dependency MySql.Data to 8.4.0 --- TShockAPI/TShockAPI.csproj | 2 +- TShockLauncher/TShockLauncher.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TShockAPI/TShockAPI.csproj b/TShockAPI/TShockAPI.csproj index 9da8cd08..34df3c74 100644 --- a/TShockAPI/TShockAPI.csproj +++ b/TShockAPI/TShockAPI.csproj @@ -34,7 +34,7 @@ - + diff --git a/TShockLauncher/TShockLauncher.csproj b/TShockLauncher/TShockLauncher.csproj index e3c4ac32..fbe428bb 100644 --- a/TShockLauncher/TShockLauncher.csproj +++ b/TShockLauncher/TShockLauncher.csproj @@ -30,7 +30,7 @@ - + From 8246b95739b0a3e8e0e20491be7b1c7a6ae13275 Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Sat, 25 Jan 2025 22:50:15 +0900 Subject: [PATCH 115/135] Disable stupid semantic commits --- renovate.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/renovate.json b/renovate.json index 159ec1e2..4292e678 100644 --- a/renovate.json +++ b/renovate.json @@ -1,7 +1,8 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ - "config:base" + "config:base", + ":semanticCommitsDisabled" ], "git-submodules": { "enabled": true From 7e9b97be29849720f815eecc6daa1fa523d872a6 Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Sat, 25 Jan 2025 22:51:42 +0900 Subject: [PATCH 116/135] Disable renovate This is really irritating --- renovate.json | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 renovate.json diff --git a/renovate.json b/renovate.json deleted file mode 100644 index 4292e678..00000000 --- a/renovate.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:base", - ":semanticCommitsDisabled" - ], - "git-submodules": { - "enabled": true - }, - "packageRules": [ - { - "matchPackageNames": ["OTAPI.Upcoming", "ModFramework", "TerrariaServerAPI"], - "ignoreUnstable": "false", - "bumpVersion": "prerelease", - "groupName": "OTAPI things" - } - ] -} From 61a81bb4ff8d0a07f9870cc776045fe3a741448b Mon Sep 17 00:00:00 2001 From: SGKoishi Date: Sun, 26 Jan 2025 01:00:17 +0900 Subject: [PATCH 117/135] Fix merge conflict --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 75e8d251..841bba3b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -130,6 +130,10 @@ Use past tense when adding new entries; sign your name off when you add or chang * Allowed multiple test cases to be in TShock's test suite. (@drunderscore) * Fixed unable to use Purification/Evil Powder in jungle. (@sgkoishi) * Set the `GetDataHandledEventArgs.Player` property for the `SyncTilePicking` data handler. (@drunderscore) +* Relaxed custom death message restrictions to allow Inferno potions in PvP. (@drunderscore) +* Allowed Flower Boots to place Ash Flowers on Ash Grass blocks. (@punchready) +* Removed unnecessary range check that artifically shortened quick stack reach. (@boddyn, #2885, @bcat) +* Re-wrote tile rect handling from scratch, fixing a certain exploitable flaw in the old code and significantly reducing the potential exploit surface, potentially even down to zero. (@punchready) ## TShock 5.1.3 * Added support for Terraria 1.4.4.9 via OTAPI 3.1.20. (@SignatureBeef) From b984ff8b65433e54478459421dba9247df2c62bb Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Sun, 26 Jan 2025 08:45:11 +0900 Subject: [PATCH 118/135] Apply patch from https://github.com/Pryaxis/TShock/pull/3018 This applies https://github.com/Pryaxis/TShock/pull/3018 as a patch per @punchready because they deleted the head repository so that we can't merge it and none of the git objects have valid refs anymore on the command line and we can't merge it on the gui. --- TShockAPI/Commands.cs | 11 ++++++----- docs/changelog.md | 1 + 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs index e7491632..1006b50f 100644 --- a/TShockAPI/Commands.cs +++ b/TShockAPI/Commands.cs @@ -4622,21 +4622,22 @@ namespace TShockAPI { if (args.Parameters.Count != 1) { - args.Player.SendErrorMessage(GetString("Invalid syntax. Proper syntax: {0}wind .", Specifier)); + args.Player.SendErrorMessage(GetString("Invalid syntax. Proper syntax: {0}wind .", Specifier)); return; } - int speed; - if (!int.TryParse(args.Parameters[0], out speed) || speed * 100 < 0) + float mph; + if (!float.TryParse(args.Parameters[0], out mph) || mph is < -40f or > 40f) { - args.Player.SendErrorMessage(GetString("Invalid wind speed.")); + args.Player.SendErrorMessage(GetString("Invalid wind speed (must be between -40 and 40).")); return; } + float speed = mph / 50f; // -40 to 40 mph -> -0.8 to 0.8 Main.windSpeedCurrent = speed; Main.windSpeedTarget = speed; TSPlayer.All.SendData(PacketTypes.WorldInfo); - TSPlayer.All.SendInfoMessage(GetString("{0} changed the wind speed to {1}.", args.Player.Name, speed)); + TSPlayer.All.SendInfoMessage(GetString("{0} changed the wind speed to {1}mph.", args.Player.Name, mph)); } #endregion Time/PvpFun Commands diff --git a/docs/changelog.md b/docs/changelog.md index 4866e01c..41fa2853 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -83,6 +83,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Changed the use of `Player.active` to `TSPlayer.Active` for consistency. (@sgkoishi, #2939) * Fix typo in config for IP bans. (@redchess64) * Fixed unable to transfer long response body for REST API. (@sgkoishi, #2925) +* Fixed the `/wind` command not being very helpful. (@punchready) ## TShock 5.2.1 * Updated `TSPlayer.GodMode`. (@AgaSpace) From fdee582dc74266b9e5231414c78ee34a13baf7e8 Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Sun, 26 Jan 2025 09:00:41 +0900 Subject: [PATCH 119/135] Redirect ikebukuro to wiki This redirects the hosted docs on github pages to the wiki, which is easier for normal people to edit. --- docs/index.html | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/docs/index.html b/docs/index.html index 2ae1df82..0cfccf11 100644 --- a/docs/index.html +++ b/docs/index.html @@ -11,12 +11,7 @@
From 52ed275be2393a496edf26430b1f7d05a8fe72b5 Mon Sep 17 00:00:00 2001 From: SGKoishi Date: Sun, 26 Jan 2025 13:00:06 +0900 Subject: [PATCH 120/135] Fix #2895 --- TShockPluginManager/Nuget.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/TShockPluginManager/Nuget.cs b/TShockPluginManager/Nuget.cs index 9fdefb6b..1d00fe1c 100644 --- a/TShockPluginManager/Nuget.cs +++ b/TShockPluginManager/Nuget.cs @@ -30,7 +30,6 @@ using NuGet.Versioning; namespace TShockPluginManager { - public class Nugetter { // this object can figure out the right framework folders to use from a set of packages @@ -82,15 +81,13 @@ namespace TShockPluginManager // make sure the source repository can actually tell us about dependencies var dependencyInfoResource = await sourceRepository.GetResourceAsync(); // get the try and dependencies - // (the above function returns a nullable value, but doesn't properly indicate it as such) - #pragma warning disable CS8602 - var dependencyInfo = await dependencyInfoResource?.ResolvePackage( + if (dependencyInfoResource is null) continue; + var dependencyInfo = await dependencyInfoResource.ResolvePackage( package, framework, cacheContext, logger, CancellationToken.None); - #pragma warning restore CS8602 // oop, we don't have the ability to get dependency info from this repository, or // it wasn't found. let's try the next source repository! - if (dependencyInfo == null) continue; + if (dependencyInfo is null) continue; availablePackages.Add(dependencyInfo); foreach (var dependency in dependencyInfo.Dependencies) @@ -302,8 +299,11 @@ namespace TShockPluginManager var relativeFolder = Path.GetDirectoryName(packageRelativeFilePath); var targetFolder = Path.Join(isPlugin ? "./ServerPlugins" : "./bin", relativeFolder); - Directory.CreateDirectory(targetFolder); - File.Copy(filePath, Path.Join(targetFolder, Path.GetFileName(filePath)), true); + if (File.Exists(filePath)) + { + Directory.CreateDirectory(targetFolder); + File.Copy(filePath, Path.Join(targetFolder, Path.GetFileName(filePath)), true); + } } } } From 74a07150f82eefb4a3359bb7f3b0911f91d8f5ae Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Sun, 26 Jan 2025 13:37:38 +0900 Subject: [PATCH 121/135] Update submodule --- TerrariaServerAPI | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TerrariaServerAPI b/TerrariaServerAPI index 8a3fffd7..d4bb7e3a 160000 --- a/TerrariaServerAPI +++ b/TerrariaServerAPI @@ -1 +1 @@ -Subproject commit 8a3fffd71db401736ea80619122c70c449c10ff3 +Subproject commit d4bb7e3a21e875cfeb23bcf5cf847c85d9470ccf From c4a141308e58d4c7cc9ac07fac8143434597b13d Mon Sep 17 00:00:00 2001 From: SGKoishi Date: Sun, 26 Jan 2025 15:11:15 +0900 Subject: [PATCH 122/135] Automatic language detection --- TShockAPI/I18n.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/TShockAPI/I18n.cs b/TShockAPI/I18n.cs index e8f5eedb..ac2985d0 100644 --- a/TShockAPI/I18n.cs +++ b/TShockAPI/I18n.cs @@ -60,6 +60,12 @@ namespace TShockAPI } } + if (LanguageManager.Instance.ActiveCulture == GameCulture.DefaultCulture) + { + var bf = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static; + // LanguageManager.SetLanguage will change this so we need to reset it back to null + typeof(CultureInfo).GetField("s_currentThreadUICulture", bf)?.SetValue(null, null); + } return CultureInfo.CurrentUICulture; } } From 605be8f813dcc01cd24e3c422d545dea720f5b0c Mon Sep 17 00:00:00 2001 From: SGKoishi Date: Mon, 27 Jan 2025 08:11:36 +0900 Subject: [PATCH 123/135] Detect xterm compatibility to avoid console spam --- TShockAPI/Utils.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TShockAPI/Utils.cs b/TShockAPI/Utils.cs index 23b1e224..3b9c0286 100644 --- a/TShockAPI/Utils.cs +++ b/TShockAPI/Utils.cs @@ -1149,11 +1149,15 @@ namespace TShockAPI /// If the server is empty; determines if we should use Utils.GetActivePlayerCount() for player count or 0. internal void SetConsoleTitle(bool empty) { + if (ShouldSkipTitle) + return; Console.Title = GetString("{0}{1}/{2} on {3} @ {4}:{5} (TShock for Terraria v{6})", !string.IsNullOrWhiteSpace(TShock.Config.Settings.ServerName) ? TShock.Config.Settings.ServerName + " - " : "", empty ? 0 : GetActivePlayerCount(), TShock.Config.Settings.MaxSlots, Main.worldName, Netplay.ServerIP.ToString(), Netplay.ListenPort, TShock.VersionNum); } + // Some terminals doesn't supports XTerm escape sequences for setting the title + private static bool ShouldSkipTitle = !System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows) && !(Environment.GetEnvironmentVariable("TERM")?.Contains("xterm") ?? false); /// Determines the distance between two vectors. /// The first vector location. From 49b1003eca32fa8028d39402f48bb1932f0f5a66 Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Mon, 27 Jan 2025 21:07:36 +0900 Subject: [PATCH 124/135] Update github actions upload artifact to v4 This is part of #3050, but thinking about this logically, the deprecation of v3 happens in just 3 days, so unless we want to have .NET 9 testing done in the next 3 days (unlikely?), it's not a great idea to let this break. --- .github/workflows/ci-otapi3.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-otapi3.yml b/.github/workflows/ci-otapi3.yml index f5599ffc..e72d8c22 100644 --- a/.github/workflows/ci-otapi3.yml +++ b/.github/workflows/ci-otapi3.yml @@ -63,14 +63,14 @@ jobs: tar -cvf ../../../../../../TShock-Beta-${{ matrix.arch }}-Release.tar * - name: Upload artifact (non-Windows) - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ matrix.arch != 'win-x64' }} with: name: TShock-Beta-${{ matrix.arch }}-Release path: TShock-Beta-${{ matrix.arch }}-Release.tar - name: Upload artifact (Windows) - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ matrix.arch == 'win-x64' }} with: name: TShock-Beta-${{ matrix.arch }}-Release From d35934b3a2c0e89af2f19f75e622049e5ac9f23b Mon Sep 17 00:00:00 2001 From: ohayo Date: Thu, 30 Jan 2025 22:46:13 +1000 Subject: [PATCH 125/135] Check for bans on connection as well as join, etc Should prevent people from being able to bypass this. --- TShockAPI/TShock.cs | 2 ++ docs/changelog.md | 1 + 2 files changed, 3 insertions(+) diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index 365c531d..6955efdd 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -1376,6 +1376,8 @@ namespace TShockAPI } } } + + Bans.CheckBan(player); Players[args.Who] = player; } diff --git a/docs/changelog.md b/docs/changelog.md index 490a6b0a..910696ce 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -78,6 +78,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * If there is no section called "Upcoming changes" below this line, please add one with `## Upcoming changes` as the first line, and then a bulleted item directly after with the first change. --> ## Upcoming changes +* Fixed a security issue with how bans are handled on join. (@ohayo) * Fixed `/dump-reference-data` mutate the command names. (#2943, @sgkoishi) * Added `ParryDamageBuff` (Striking Moment with Brand of the Inferno and shield) for player, updated `CursedInferno` buff for NPC (@sgkoishi, #3005) * Changed the use of `Player.active` to `TSPlayer.Active` for consistency. (@sgkoishi, #2939) From 53789b40e4bd1211fa564d05f135226af95912b9 Mon Sep 17 00:00:00 2001 From: ohayo Date: Fri, 31 Jan 2025 09:17:26 +1000 Subject: [PATCH 126/135] Prevent further things from clients who dont handshake - The player will only finish the handshake once they spawn their player, a normal client would always do this eventually. - They cannot chat, even if they request world data but just not spawn their player. - Other clients will not be notified of their join/leave in both cases (dont request WD or do but dont spawn) - And most importantly, they do not show on the in game player list but still show on the server console /playing cmd. --- TShockAPI/Commands.cs | 2 +- TShockAPI/GetDataHandlers.cs | 3 +++ TShockAPI/TSPlayer.cs | 3 +++ TShockAPI/TShock.cs | 18 ++++++++++++++---- TShockAPI/Utils.cs | 2 +- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs index f7d3247a..d82f10e2 100644 --- a/TShockAPI/Commands.cs +++ b/TShockAPI/Commands.cs @@ -5363,7 +5363,7 @@ namespace TShockAPI foreach (TSPlayer ply in TShock.Players) { - if (ply != null && ply.Active) + if (ply != null && ply.Active && ply.FinishedHandshake) { if (displayIdsRequested) if (ply.Account != null) diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index b736a512..8f70fd2c 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -2726,6 +2726,8 @@ namespace TShockAPI short numberOfDeathsPVP = args.Data.ReadInt16(); PlayerSpawnContext context = (PlayerSpawnContext)args.Data.ReadByte(); + args.Player.FinishedHandshake = true; + if (OnPlayerSpawn(args.Player, args.Data, player, spawnx, spawny, respawnTimer, numberOfDeathsPVE, numberOfDeathsPVP, context)) return true; @@ -2762,6 +2764,7 @@ namespace TShockAPI args.Player.Dead = true; else args.Player.Dead = false; + return false; } diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index c9194c36..cb66649a 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -351,6 +351,9 @@ namespace TShockAPI /// Determines if the player is disabled for not clearing their trash. A re-login is the only way to reset this. public bool IsDisabledPendingTrashRemoval; + /// Determines if the player has finished the handshake (Sent all necessary packets for connection, such as Request World Data, Spawn Player, etc). A normal client would do all of this no problem. + public bool FinishedHandshake = false; + /// Checks to see if active throttling is happening on events by Bouncer. Rejects repeated events by malicious clients in a short window. /// If the player is currently being throttled by Bouncer, or not. public bool IsBouncerThrottled() diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index 6955efdd..ae9738cc 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -1399,7 +1399,8 @@ namespace TShockAPI return; } - Bans.CheckBan(player); + if (Bans.CheckBan(player)) + return; } /// OnLeave - Called when a player leaves the server. @@ -1439,7 +1440,7 @@ namespace TShockAPI if (tsplr.ReceivedInfo) { - if (!tsplr.SilentKickInProgress && tsplr.State >= 3) + if (!tsplr.SilentKickInProgress && tsplr.State >= 3 && tsplr.FinishedHandshake) //The player has left, do not broadcast any clients exploiting the behaviour of not spawning their player. Utils.Broadcast(GetString("{0} has left.", tsplr.Name), Color.Yellow); Log.Info(GetString("{0} disconnected.", tsplr.Name)); @@ -1460,6 +1461,9 @@ namespace TShockAPI } } + + tsplr.FinishedHandshake = false; + // Fire the OnPlayerLogout hook too, if the player was logged in and they have a TSPlayer object. if (tsplr.IsLoggedIn) { @@ -1489,6 +1493,12 @@ namespace TShockAPI return; } + if (!tsplr.FinishedHandshake) + { + args.Handled = true; + return; + } + if (args.Text.Length > 500) { tsplr.Kick(GetString("Crash attempt via long chat packet."), true); @@ -1705,14 +1715,14 @@ namespace TShockAPI Log.Info(GetString("{0} ({1}) from '{2}' group from '{3}' joined. ({4}/{5})", player.Name, player.IP, player.Group.Name, player.Country, TShock.Utils.GetActivePlayerCount(), TShock.Config.Settings.MaxSlots)); - if (!player.SilentJoinInProgress) + if (!player.SilentJoinInProgress && player.FinishedHandshake) Utils.Broadcast(GetString("{0} ({1}) has joined.", player.Name, player.Country), Color.Yellow); } else { Log.Info(GetString("{0} ({1}) from '{2}' group joined. ({3}/{4})", player.Name, player.IP, player.Group.Name, TShock.Utils.GetActivePlayerCount(), TShock.Config.Settings.MaxSlots)); - if (!player.SilentJoinInProgress) + if (!player.SilentJoinInProgress && player.FinishedHandshake) Utils.Broadcast(GetString("{0} has joined.", player.Name), Color.Yellow); } diff --git a/TShockAPI/Utils.cs b/TShockAPI/Utils.cs index 3b9c0286..613e7a8a 100644 --- a/TShockAPI/Utils.cs +++ b/TShockAPI/Utils.cs @@ -183,7 +183,7 @@ namespace TShockAPI /// The number of active players on the server. public int GetActivePlayerCount() { - return TShock.Players.Count(p => null != p && p.Active); + return TShock.Players.Count(p => null != p && p.Active && p.FinishedHandshake); } //Random should not be generated in a method From ed398430bc51b835a97fa7dec3b9f86375ae6587 Mon Sep 17 00:00:00 2001 From: ohayo Date: Fri, 31 Jan 2025 10:04:50 +1000 Subject: [PATCH 127/135] Update changelog.md --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index 910696ce..afcfc727 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -78,6 +78,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * If there is no section called "Upcoming changes" below this line, please add one with `## Upcoming changes` as the first line, and then a bulleted item directly after with the first change. --> ## Upcoming changes +* Added a variable for handshake (True upon spawn player), clients no longer notify others of their presence and cant chat if this is never set to true. (@ohayo) * Fixed a security issue with how bans are handled on join. (@ohayo) * Fixed `/dump-reference-data` mutate the command names. (#2943, @sgkoishi) * Added `ParryDamageBuff` (Striking Moment with Brand of the Inferno and shield) for player, updated `CursedInferno` buff for NPC (@sgkoishi, #3005) From e4e28cb1b561dbf9a30eafd925c8fe93052e0aad Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Sat, 1 Feb 2025 22:32:36 +0900 Subject: [PATCH 128/135] Version tick: 5.2.2 --- TShockAPI/TShock.cs | 2 +- TShockAPI/TShockAPI.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index ae9738cc..e984da2f 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -63,7 +63,7 @@ namespace TShockAPI /// VersionNum - The version number the TerrariaAPI will return back to the API. We just use the Assembly info. public static readonly Version VersionNum = Assembly.GetExecutingAssembly().GetName().Version; /// VersionCodename - The version codename is displayed when the server starts. Inspired by software codenames conventions. - public static readonly string VersionCodename = "Intensity"; + public static readonly string VersionCodename = "East"; /// SavePath - This is the path TShock saves its data in. This path is relative to the TerrariaServer.exe (not in ServerPlugins). public static string SavePath = "tshock"; diff --git a/TShockAPI/TShockAPI.csproj b/TShockAPI/TShockAPI.csproj index 34df3c74..88f61606 100644 --- a/TShockAPI/TShockAPI.csproj +++ b/TShockAPI/TShockAPI.csproj @@ -18,7 +18,7 @@ Also, be sure to release on github with the exact assembly version tag as below so that the update manager works correctly (via the Github releases api and mimic) --> - 5.2.1 + 5.2.2 TShock for Terraria Pryaxis & TShock Contributors TShockAPI From 20532135580568478ba7dbc98150604751454e99 Mon Sep 17 00:00:00 2001 From: Arthri <41360489+a@users.noreply.github.com> Date: Sat, 8 Feb 2025 07:13:03 +0000 Subject: [PATCH 129/135] Bounce infinite or NaN velocity / position --- TShockAPI/Bouncer.cs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 0f5f6e3c..bfcd1836 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -504,6 +504,14 @@ namespace TShockAPI return; } + if (!float.IsFinite(pos.X) || !float.IsFinite(pos.Y)) + { + TShock.Log.ConsoleInfo(GetString("Bouncer / OnPlayerUpdate force kicked (attempted to set position to infinity or NaN) from {0}", args.Player.Name)); + args.Player.Kick(GetString("Detected DOOM set to ON position."), true, true); + args.Handled = true; + return; + } + if (pos.X < 0 || pos.Y < 0 || pos.X >= Main.maxTilesX * 16 - 16 || pos.Y >= Main.maxTilesY * 16 - 16) { TShock.Log.ConsoleDebug(GetString("Bouncer / OnPlayerUpdate rejected from (position check) {0}", args.Player.Name)); @@ -1072,6 +1080,22 @@ namespace TShockAPI bool noDelay = args.NoDelay; short type = args.Type; + if (!float.IsFinite(pos.X) || !float.IsFinite(pos.Y)) + { + TShock.Log.ConsoleInfo(GetString("Bouncer / OnItemDrop force kicked (attempted to set position to infinity or NaN) from {0}", args.Player.Name)); + args.Player.Kick(GetString("Detected DOOM set to ON position."), true, true); + args.Handled = true; + return; + } + + if (!float.IsFinite(vel.X) || !float.IsFinite(vel.Y)) + { + TShock.Log.ConsoleInfo(GetString("Bouncer / OnItemDrop force kicked (attempted to set velocity to infinity or NaN) from {0}", args.Player.Name)); + args.Player.Kick(GetString("Detected DOOM set to ON position."), true, true); + args.Handled = true; + return; + } + // player is attempting to crash clients if (type < -48 || type >= Terraria.ID.ItemID.Count) { @@ -1175,6 +1199,22 @@ namespace TShockAPI int index = args.Index; float[] ai = args.Ai; + if (!float.IsFinite(pos.X) || !float.IsFinite(pos.Y)) + { + TShock.Log.ConsoleInfo(GetString("Bouncer / OnNewProjectile force kicked (attempted to set position to infinity or NaN) from {0}", args.Player.Name)); + args.Player.Kick(GetString("Detected DOOM set to ON position."), true, true); + args.Handled = true; + return; + } + + if (!float.IsFinite(vel.X) || !float.IsFinite(vel.Y)) + { + TShock.Log.ConsoleInfo(GetString("Bouncer / OnNewProjectile force kicked (attempted to set velocity to infinity or NaN) from {0}", args.Player.Name)); + args.Player.Kick(GetString("Detected DOOM set to ON position."), true, true); + args.Handled = true; + return; + } + if (index > Main.maxProjectiles) { TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile rejected from above projectile limit from {0}", args.Player.Name)); From 64d819bebb086f92e55bcd6d1ae26d64d6eb7af0 Mon Sep 17 00:00:00 2001 From: Arthri <41360489+a@users.noreply.github.com> Date: Sat, 8 Feb 2025 07:18:06 +0000 Subject: [PATCH 130/135] Changelog entry --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index afcfc727..505419e2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -95,6 +95,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * * Ensured `TSPlayer.PlayerData` is non-null whilst syncing loadouts. (@drunderscore) * * Detected invalid installations, by checking for a file named `TerrariaServer.exe`. (@drunderscore) * This made the two most common installation mistakes (extracting into the Terraria client directory, and extracting TShock 5 or newer into a TShock 4 or older install) prompt the user with a more useful diagnostic, rather than (likely) crashing moments later. +* Changed Bouncer to block updates which set the following fields to infinity or NaN: player position, projectile position, projectile velocity, item position, and item velocity. (@Arthri) ## TShock 5.2.1 * Updated `TSPlayer.GodMode`. (@AgaSpace) From 836cc33c8dd338fe558b8e6c5f1b410d0573d7ad Mon Sep 17 00:00:00 2001 From: Joseph Goh Date: Mon, 27 Jan 2025 09:58:40 +0800 Subject: [PATCH 131/135] rewrite: bed spawning for SSC --- TShockAPI/GetDataHandlers.cs | 81 ++++++++++++++++++++++-------------- TShockAPI/PlayerData.cs | 14 +------ TShockAPI/TSPlayer.cs | 37 +++++++++++----- TShockAPI/TShock.cs | 16 +++---- docs/changelog.md | 3 ++ 5 files changed, 90 insertions(+), 61 deletions(-) diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index 8f70fd2c..07010c94 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -2248,7 +2248,7 @@ namespace TShockAPI var args = new SyncTilePickingEventArgs { - Player = player, + Player = player, PlayerIndex = playerIndex, TileX = tileX, TileY = tileY, @@ -2719,8 +2719,8 @@ namespace TShockAPI } byte player = args.Data.ReadInt8(); - short spawnx = args.Data.ReadInt16(); - short spawny = args.Data.ReadInt16(); + short spawnX = args.Data.ReadInt16(); + short spawnY = args.Data.ReadInt16(); int respawnTimer = args.Data.ReadInt32(); short numberOfDeathsPVE = args.Data.ReadInt16(); short numberOfDeathsPVP = args.Data.ReadInt16(); @@ -2728,43 +2728,62 @@ namespace TShockAPI args.Player.FinishedHandshake = true; - if (OnPlayerSpawn(args.Player, args.Data, player, spawnx, spawny, respawnTimer, numberOfDeathsPVE, numberOfDeathsPVP, context)) + if (OnPlayerSpawn(args.Player, args.Data, player, spawnX, spawnY, respawnTimer, numberOfDeathsPVE, numberOfDeathsPVP, context)) return true; + + args.Player.Dead = respawnTimer > 0; - if ((Main.ServerSideCharacter) && (spawnx == -1 && spawny == -1)) //this means they want to spawn to vanilla spawn + if (Main.ServerSideCharacter) { - args.Player.sX = Main.spawnTileX; - args.Player.sY = Main.spawnTileY; - args.Player.Teleport(args.Player.sX * 16, (args.Player.sY * 16) - 48); - TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleSpawn force teleport 'vanilla spawn' {0}", args.Player.Name)); - } + // As long as the player has not changed his spawnpoint since initial connection, + // we should not use the client's spawnpoint value. This is because the spawnpoint + // value is not saved on the client when SSC is enabled. Hence, we have to assert + // the server-saved spawnpoint value until we can detect that the player has changed + // his spawn. Once we detect the spawnpoint changed, the client's spawnpoint value + // becomes the correct one to use. + // + // Note that spawnpoint changes (right-clicking beds) are not broadcasted to the + // server. Hence, the only way to detect spawnpoint changes is from the + // PlayerSpawn packet. - else if ((Main.ServerSideCharacter) && (args.Player.sX > 0) && (args.Player.sY > 0) && (args.TPlayer.SpawnX > 0) && ((args.TPlayer.SpawnX != args.Player.sX) && (args.TPlayer.SpawnY != args.Player.sY))) - { - args.Player.sX = args.TPlayer.SpawnX; - args.Player.sY = args.TPlayer.SpawnY; - - if (((Main.tile[args.Player.sX, args.Player.sY - 1].active() && Main.tile[args.Player.sX, args.Player.sY - 1].type == TileID.Beds)) && (WorldGen.StartRoomCheck(args.Player.sX, args.Player.sY - 1))) + // handle initial connection + if (args.Player.State == 3) { - args.Player.Teleport(args.Player.sX * 16, (args.Player.sY * 16) - 48); - TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleSpawn force teleport phase 1 {0}", args.Player.Name)); - } - } + // server saved spawnpoint value + args.Player.initialSpawn = true; + args.Player.initialServerSpawnX = args.TPlayer.SpawnX; + args.Player.initialServerSpawnY = args.TPlayer.SpawnY; - else if ((Main.ServerSideCharacter) && (args.Player.sX > 0) && (args.Player.sY > 0)) - { - if (((Main.tile[args.Player.sX, args.Player.sY - 1].active() && Main.tile[args.Player.sX, args.Player.sY - 1].type == TileID.Beds)) && (WorldGen.StartRoomCheck(args.Player.sX, args.Player.sY - 1))) + // initial client spawn point, do not use this to spawn the player + // we only use it to detect if the spawnpoint has changed during this session + args.Player.initialClientSpawnX = spawnX; + args.Player.initialClientSpawnY = spawnY; + + // we first let the game handle completing the connection (state 3 => 10), + // then we will spawn the player at the saved spawnpoint in the next second, + // by reasserting the correct spawnpoint value + return false; + } + + // once we detect the client has changed his spawnpoint in the current session, + // the client spawnpoint value will be correct for the rest of the session + if (args.Player.spawnSynced || args.Player.initialClientSpawnX != spawnX || args.Player.initialClientSpawnY != spawnY) { - args.Player.Teleport(args.Player.sX * 16, (args.Player.sY * 16) - 48); - TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleSpawn force teleport phase 2 {0}", args.Player.Name)); + // Player has changed his spawnpoint, client and server TPlayer.Spawn{X,Y} is now synced + args.Player.spawnSynced = true; + return false; } + + // the player has not changed his spawnpoint yet, so we assert the server-saved spawnpoint + // by teleporting the player instead of letting the game use the client's incorrect spawnpoint. + TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleSpawn force ssc teleport for {0} at ({1},{2})", args.Player.Name, args.TPlayer.SpawnX, args.TPlayer.SpawnY)); + args.Player.TeleportSpawnpoint(); + + args.TPlayer.respawnTimer = respawnTimer; + args.TPlayer.numberOfDeathsPVE = numberOfDeathsPVE; + args.TPlayer.numberOfDeathsPVP = numberOfDeathsPVP; + return true; } - - if (respawnTimer > 0) - args.Player.Dead = true; - else - args.Player.Dead = false; - return false; } diff --git a/TShockAPI/PlayerData.cs b/TShockAPI/PlayerData.cs index 2a8dd41f..bab9f7a6 100644 --- a/TShockAPI/PlayerData.cs +++ b/TShockAPI/PlayerData.cs @@ -104,16 +104,8 @@ namespace TShockAPI this.maxHealth = player.TPlayer.statLifeMax; this.mana = player.TPlayer.statMana; this.maxMana = player.TPlayer.statManaMax; - if (player.sX > 0 && player.sY > 0) - { - this.spawnX = player.sX; - this.spawnY = player.sY; - } - else - { - this.spawnX = player.TPlayer.SpawnX; - this.spawnY = player.TPlayer.SpawnY; - } + this.spawnX = player.TPlayer.SpawnX; + this.spawnY = player.TPlayer.SpawnY; extraSlot = player.TPlayer.extraAccessory ? 1 : 0; this.skinVariant = player.TPlayer.skinVariant; this.hair = player.TPlayer.hair; @@ -266,8 +258,6 @@ namespace TShockAPI player.TPlayer.statManaMax = this.maxMana; player.TPlayer.SpawnX = this.spawnX; player.TPlayer.SpawnY = this.spawnY; - player.sX = this.spawnX; - player.sY = this.spawnY; player.TPlayer.hairDye = this.hairDye; player.TPlayer.anglerQuestsFinished = this.questsCompleted; player.TPlayer.UsingBiomeTorches = this.usingBiomeTorches == 1; diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index cb66649a..4fd65275 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -177,8 +177,13 @@ namespace TShockAPI ///
public int RPPending = 0; - public int sX = -1; - public int sY = -1; + + public bool initialSpawn = false; + public int initialServerSpawnX = -2; + public int initialServerSpawnY = -2; + public bool spawnSynced = false; + public int initialClientSpawnX = -2; + public int initialClientSpawnY = -2; /// /// A queue of tiles destroyed by the player for reverting. @@ -1383,6 +1388,25 @@ namespace TShockAPI return true; } + /// + /// Teleports the player to their spawnpoint. + /// Teleports to main spawnpoint if their bed is not active. + /// Supports SSC. + /// + public bool TeleportSpawnpoint() + { + // NOTE: it is vanilla behaviour to not permanently override the spawnpoint if the bed spawn is broken/invalid + int x = TPlayer.SpawnX; + int y = TPlayer.SpawnY; + if ((x == -1 && y == -1) || + !Main.tile[x, y - 1].active() || Main.tile[x, y - 1].type != TileID.Beds || !WorldGen.StartRoomCheck(x, y - 1)) + { + x = Main.spawnTileX; + y = Main.spawnTileY; + } + return Teleport(x * 16, y * 16 - 48); + } + /// /// Heals the player. /// @@ -1397,14 +1421,7 @@ namespace TShockAPI /// public void Spawn(PlayerSpawnContext context, int? respawnTimer = null) { - if (this.sX > 0 && this.sY > 0) - { - Spawn(this.sX, this.sY, context, respawnTimer); - } - else - { - Spawn(TPlayer.SpawnX, TPlayer.SpawnY, context, respawnTimer); - } + Spawn(TPlayer.SpawnX, TPlayer.SpawnY, context, respawnTimer); } /// diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index e984da2f..c5f2b968 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -1182,16 +1182,16 @@ namespace TShockAPI if (player.RecentFuse > 0) player.RecentFuse--; - if ((Main.ServerSideCharacter) && (player.TPlayer.SpawnX > 0) && (player.sX != player.TPlayer.SpawnX)) + if (Main.ServerSideCharacter && player.initialSpawn) { - player.sX = player.TPlayer.SpawnX; - player.sY = player.TPlayer.SpawnY; - } + player.initialSpawn = false; - if ((Main.ServerSideCharacter) && (player.sX > 0) && (player.sY > 0) && (player.TPlayer.SpawnX < 0)) - { - player.TPlayer.SpawnX = player.sX; - player.TPlayer.SpawnY = player.sY; + // reassert the correct spawnpoint value after the game's Spawn handler changed it + player.TPlayer.SpawnX = player.initialServerSpawnX; + player.TPlayer.SpawnY = player.initialServerSpawnY; + + player.TeleportSpawnpoint(); + TShock.Log.ConsoleDebug(GetString("OnSecondUpdate / initial ssc spawn for {0} at ({1}, {2})", player.Name, player.TPlayer.SpawnX, player.TPlayer.SpawnY)); } if (player.RPPending > 0) diff --git a/docs/changelog.md b/docs/changelog.md index afcfc727..3c9051c0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -95,6 +95,9 @@ Use past tense when adding new entries; sign your name off when you add or chang * * Ensured `TSPlayer.PlayerData` is non-null whilst syncing loadouts. (@drunderscore) * * Detected invalid installations, by checking for a file named `TerrariaServer.exe`. (@drunderscore) * This made the two most common installation mistakes (extracting into the Terraria client directory, and extracting TShock 5 or newer into a TShock 4 or older install) prompt the user with a more useful diagnostic, rather than (likely) crashing moments later. +* Rewrote bed spawning for SSC. (@PotatoCider) + * Removed `TSPlayer.s{X,Y}` in favour of using desyncing client and server spawnpoint values (`Terraria.Player.Spawn{X,Y}`) until the player has changed their spawnpoint per session. + * Partially fixed the bed spawning bug when SSC is enabled. Players would need to spawn at their beds at least once to tell TShock that the player's spawnpoint has changed. ## TShock 5.2.1 * Updated `TSPlayer.GodMode`. (@AgaSpace) From 1e23785a04dfcedb139f00ed86d0666640844361 Mon Sep 17 00:00:00 2001 From: LaoSparrow Date: Fri, 28 Feb 2025 23:01:40 +0800 Subject: [PATCH 132/135] fix(Bouncer/SendTileRectHandler): MatchRemoval incorrect check range --- TShockAPI/Handlers/SendTileRectHandler.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TShockAPI/Handlers/SendTileRectHandler.cs b/TShockAPI/Handlers/SendTileRectHandler.cs index d85185b8..e5e3f360 100644 --- a/TShockAPI/Handlers/SendTileRectHandler.cs +++ b/TShockAPI/Handlers/SendTileRectHandler.cs @@ -327,7 +327,7 @@ namespace TShockAPI.Handlers private bool MatchRemoval(TSPlayer player, TileRect rect) { - for (int x = rect.X; x < rect.Y + rect.Width; x++) + for (int x = rect.X; x < rect.X + rect.Width; x++) { for (int y = rect.Y; y < rect.Y + rect.Height; y++) { @@ -910,7 +910,7 @@ namespace TShockAPI.Handlers } } - /* + /* * This is a copy of the `WorldGen.Convert` method with the following precise changes: * - Added a `MockTile tile` parameter * - Changed the `i` and `j` parameters to `k` and `l` @@ -921,7 +921,7 @@ namespace TShockAPI.Handlers * - Removed the ifs checking the bounds of the tile and wall types * - Removed branches that would call `WorldGen.KillTile` * - Changed branches depending on randomness to instead set the property to both values after one another - * + * * This overall leads to a method that can be called on a MockTile and real-world coordinates and will spit out the proper conversion changes into the MockTile. */ From 28aa3aea482badcd54344d910c7f8811a8e1a99e Mon Sep 17 00:00:00 2001 From: LaoSparrow Date: Sat, 1 Mar 2025 01:42:18 +0800 Subject: [PATCH 133/135] fix(Bouncer/SendTileRectHandler): tile rect changes not synced between clients && unable to place HatRack --- TShockAPI/Handlers/SendTileRectHandler.cs | 67 +++++++++++++---------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/TShockAPI/Handlers/SendTileRectHandler.cs b/TShockAPI/Handlers/SendTileRectHandler.cs index e5e3f360..d66c3ef6 100644 --- a/TShockAPI/Handlers/SendTileRectHandler.cs +++ b/TShockAPI/Handlers/SendTileRectHandler.cs @@ -78,6 +78,13 @@ namespace TShockAPI.Handlers Removal, } + public enum MatchResult + { + NotMatched, + RejectChanges, + BroadcastChanges, + } + private readonly int Width; private readonly int Height; @@ -179,11 +186,11 @@ namespace TShockAPI.Handlers /// The player the operation originates from. /// The tile rectangle of the operation. /// , if the rect matches this operation and the changes have been applied, otherwise . - public bool Matches(TSPlayer player, TileRect rect) + public MatchResult Matches(TSPlayer player, TileRect rect) { if (rect.Width != Width || rect.Height != Height) { - return false; + return MatchResult.NotMatched; } for (int x = 0; x < rect.Width; x++) @@ -195,7 +202,7 @@ namespace TShockAPI.Handlers { if (tile.Type != TileType) { - return false; + return MatchResult.NotMatched; } } if (Type is MatchType.Placement or MatchType.StateChange) @@ -204,7 +211,7 @@ namespace TShockAPI.Handlers { if (tile.FrameX < 0 || tile.FrameX > MaxFrameX || tile.FrameX % FrameXStep != 0) { - return false; + return MatchResult.NotMatched; } } if (MaxFrameY != IGNORE_FRAME) @@ -214,7 +221,7 @@ namespace TShockAPI.Handlers // this is the only tile type sent in a tile rect where the frame have a different pattern (56, 74, 92 instead of 54, 72, 90) if (!(TileType == TileID.LunarMonolith && tile.FrameY % FrameYStep == 2)) { - return false; + return MatchResult.NotMatched; } } } @@ -223,7 +230,7 @@ namespace TShockAPI.Handlers { if (tile.Active) { - return false; + return MatchResult.NotMatched; } } } @@ -236,7 +243,7 @@ namespace TShockAPI.Handlers if (!player.HasBuildPermission(x, y)) { // for simplicity, let's pretend that the edit was valid, but do not execute it - return true; + return MatchResult.RejectChanges; } } } @@ -257,10 +264,10 @@ namespace TShockAPI.Handlers } } - return false; + return MatchResult.NotMatched; } - private bool MatchPlacement(TSPlayer player, TileRect rect) + private MatchResult MatchPlacement(TSPlayer player, TileRect rect) { for (int x = rect.X; x < rect.Y + rect.Width; x++) { @@ -268,7 +275,7 @@ namespace TShockAPI.Handlers { if (Main.tile[x, y].active()) // the client will kill tiles that auto break before placing the object { - return false; + return MatchResult.NotMatched; } } } @@ -277,7 +284,7 @@ namespace TShockAPI.Handlers if (TShock.TileBans.TileIsBanned((short)TileType, player)) { // for simplicity, let's pretend that the edit was valid, but do not execute it - return true; + return MatchResult.RejectChanges; } for (int x = 0; x < rect.Width; x++) @@ -291,10 +298,10 @@ namespace TShockAPI.Handlers } } - return true; + return MatchResult.BroadcastChanges; } - private bool MatchStateChange(TSPlayer player, TileRect rect) + private MatchResult MatchStateChange(TSPlayer player, TileRect rect) { for (int x = rect.X; x < rect.Y + rect.Width; x++) { @@ -302,7 +309,7 @@ namespace TShockAPI.Handlers { if (!Main.tile[x, y].active() || Main.tile[x, y].type != TileType) { - return false; + return MatchResult.NotMatched; } } } @@ -322,10 +329,10 @@ namespace TShockAPI.Handlers } } - return true; + return MatchResult.BroadcastChanges; } - private bool MatchRemoval(TSPlayer player, TileRect rect) + private MatchResult MatchRemoval(TSPlayer player, TileRect rect) { for (int x = rect.X; x < rect.X + rect.Width; x++) { @@ -333,7 +340,7 @@ namespace TShockAPI.Handlers { if (!Main.tile[x, y].active() || Main.tile[x, y].type != TileType) { - return false; + return MatchResult.NotMatched; } } } @@ -348,7 +355,7 @@ namespace TShockAPI.Handlers } } - return true; + return MatchResult.BroadcastChanges; } } @@ -364,7 +371,7 @@ namespace TShockAPI.Handlers TileRectMatch.Placement(2, 3, TileID.TargetDummy, 54, 36, 18, 18), TileRectMatch.Placement(3, 4, TileID.TeleportationPylon, 468, 54, 18, 18), TileRectMatch.Placement(2, 3, TileID.DisplayDoll, 126, 36, 18, 18), - TileRectMatch.Placement(2, 3, TileID.HatRack, 90, 54, 18, 18), + TileRectMatch.Placement(3, 4, TileID.HatRack, 90, 54, 18, 18), TileRectMatch.Placement(2, 2, TileID.ItemFrame, 162, 18, 18, 18), TileRectMatch.Placement(3, 3, TileID.WeaponsRack2, 90, 36, 18, 18), TileRectMatch.Placement(1, 1, TileID.FoodPlatter, 18, 0, 18, 18), @@ -436,7 +443,7 @@ namespace TShockAPI.Handlers TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from throttle from {args.Player.Name}")); // send correcting data - args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length); return; } @@ -446,7 +453,7 @@ namespace TShockAPI.Handlers TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from being disabled from {args.Player.Name}")); // send correcting data - args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length); return; } @@ -468,7 +475,7 @@ namespace TShockAPI.Handlers TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}")); // send correcting data - args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length); return; } @@ -478,7 +485,7 @@ namespace TShockAPI.Handlers TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from out of range from {args.Player.Name}")); // send correcting data - args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length); return; } @@ -488,19 +495,23 @@ namespace TShockAPI.Handlers TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}")); // send correcting data - args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length); return; } // check if the rect matches any valid operation foreach (TileRectMatch match in Matches) { - if (match.Matches(args.Player, rect)) + var result = match.Matches(args.Player, rect); + if (result != TileRectMatch.MatchResult.NotMatched) { TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}")); // send correcting data - args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + if (result == TileRectMatch.MatchResult.RejectChanges) + args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length); + if (result == TileRectMatch.MatchResult.BroadcastChanges) + TSPlayer.All.SendTileRect(args.TileX, args.TileY, args.Width, args.Length); return; } } @@ -511,14 +522,14 @@ namespace TShockAPI.Handlers TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}")); // send correcting data - args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length); return; } TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from matches from {args.Player.Name}")); // send correcting data - args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width); + args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length); return; } From 61e574ce7230544ce42dc3f730fda99d4843ad75 Mon Sep 17 00:00:00 2001 From: LaoSparrow Date: Sat, 1 Mar 2025 02:02:24 +0800 Subject: [PATCH 134/135] docs(changelog): update entries --- docs/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index afcfc727..147d5559 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -95,6 +95,10 @@ Use past tense when adding new entries; sign your name off when you add or chang * * Ensured `TSPlayer.PlayerData` is non-null whilst syncing loadouts. (@drunderscore) * * Detected invalid installations, by checking for a file named `TerrariaServer.exe`. (@drunderscore) * This made the two most common installation mistakes (extracting into the Terraria client directory, and extracting TShock 5 or newer into a TShock 4 or older install) prompt the user with a more useful diagnostic, rather than (likely) crashing moments later. +* Updated `TShockAPI.Handlers.SendTileRectHandler` (@LaoSparrow): + * Fixed incorrect validating range in `TileRectMatch.MatchRemoval`. + * Fixed tile rect changes (e.g. turning on and off campfires) are not synced between clients. + * Fixed unable to place Hat Rack without permission `tshock.ignore.sendtilesquare`. ## TShock 5.2.1 * Updated `TSPlayer.GodMode`. (@AgaSpace) From 740c5c9250c262e2602292ac9e5b2d396ec0f842 Mon Sep 17 00:00:00 2001 From: LaoSparrow Date: Sat, 1 Mar 2025 03:41:30 +0800 Subject: [PATCH 135/135] fix(GetDataHandlers): handle and ignore `NpcItemStrike(msgid 24)` --- TShockAPI/GetDataHandlers.cs | 8 ++++++++ docs/changelog.md | 1 + 2 files changed, 9 insertions(+) diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index 8f70fd2c..22cfadb3 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -95,6 +95,7 @@ namespace TShockAPI { PacketTypes.TileSendSquare, HandleSendTileRect }, { PacketTypes.ItemDrop, HandleItemDrop }, { PacketTypes.ItemOwner, HandleItemOwner }, + { PacketTypes.NpcItemStrike, HandleNpcItemStrike }, { PacketTypes.ProjectileNew, HandleProjectileNew }, { PacketTypes.NpcStrike, HandleNpcStrike }, { PacketTypes.ProjectileDestroy, HandleProjectileKill }, @@ -2945,6 +2946,13 @@ namespace TShockAPI return false; } + private static bool HandleNpcItemStrike(GetDataHandlerArgs args) + { + // Never sent by vanilla client, ignore this + TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleNpcItemStrike surprise packet! Someone tell the TShock team! {0}", args.Player.Name)); + return true; + } + private static bool HandleProjectileNew(GetDataHandlerArgs args) { short ident = args.Data.ReadInt16(); diff --git a/docs/changelog.md b/docs/changelog.md index 147d5559..cf4e43f2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -99,6 +99,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Fixed incorrect validating range in `TileRectMatch.MatchRemoval`. * Fixed tile rect changes (e.g. turning on and off campfires) are not synced between clients. * Fixed unable to place Hat Rack without permission `tshock.ignore.sendtilesquare`. +* Updated `GetDataHandlers` to ignore `NpcItemStrike(msgid 24)`, which should never be sent by a vanilla client. (@LaoSparrow) ## TShock 5.2.1 * Updated `TSPlayer.GodMode`. (@AgaSpace)