diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index deb811a2..3d47d3c6 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -5,6 +5,10 @@ on: [push, pull_request] jobs: build: runs-on: ubuntu-latest + permissions: + attestations: write + id-token: write + packages: write steps: - name: Checkout uses: actions/checkout@v4 @@ -14,12 +18,43 @@ 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 }} + 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') }} + flavor: | + latest=${{ startsWith(github.ref, 'refs/tags/v') }} - name: Build image - uses: docker/build-push-action@v5 + id: build + uses: docker/build-push-action@v6 with: context: . platforms: linux/amd64,linux/arm64,linux/arm/v7,windows/amd64 - push: false + 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 }} + subject-digest: ${{ steps.build.outputs.digest }} + push-to-registry: true diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 58c14b1c..0f5f6e3c 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 }; @@ -434,6 +434,12 @@ namespace TShockAPI CanBeAddedWithoutHostile = true, CanOnlyBeAppliedToSender = true }; + PlayerAddBuffWhitelist[BuffID.ParryDamageBuff] = new BuffLimit + { + MaxTicks = 60 * 5, + CanBeAddedWithoutHostile = true, + CanOnlyBeAppliedToSender = true + }; #endregion Whitelist } @@ -1878,7 +1884,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 +2087,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 +2595,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 d39b84e0..e6bc1b59 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.")); @@ -2044,6 +2048,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); } @@ -3070,12 +3075,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.")); @@ -4622,21 +4627,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/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/TShockAPI/DB/UserManager.cs b/TShockAPI/DB/UserManager.cs index 0354989e..6fc50d98 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 @@ -619,7 +654,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")) @@ -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($"Unable to update group of user {name}.")) + { + } + } + + /// A GroupNotExistsException, used when a group does not exist. [Serializable] public class GroupNotExistsException : UserAccountManagerException diff --git a/TShockAPI/Hooks/AccountHooks.cs b/TShockAPI/Hooks/AccountHooks.cs index 9c08b26d..ae9fff24 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,31 @@ namespace TShockAPI.Hooks } } + public 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 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 +88,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 AccountGroupUpdateEventArgs(account.Name, group); + AccountGroupUpdate?.Invoke(args); + group = args.Group; + + return args.Handled; + } } } 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; } } diff --git a/TShockAPI/Localization/EnglishLanguage.cs b/TShockAPI/Localization/EnglishLanguage.cs index 1ec4ecea..334c4780 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 VanillaCommandsPrefixs = 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 (VanillaCommandsPrefixs.ContainsKey(command.Value._name)) + continue; + VanillaCommandsPrefixs.Add(command.Value._name,command.Key.Value); + } + ChatManager.Commands._localizedCommands.Clear(); } finally { @@ -136,5 +149,18 @@ namespace TShockAPI.Localization return null; } + + /// + /// Get vanilla command prefix in English + /// + /// vanilla command name + /// vanilla command prefix in English + public static string GetCommandPrefixByName(string name) + { + string commandText; + if (VanillaCommandsPrefixs.TryGetValue(name, out commandText)) + return commandText; + return null; + } } } diff --git a/TShockAPI/NetItem.cs b/TShockAPI/NetItem.cs index 278fda1c..0f0dc6e3 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; @@ -155,13 +155,39 @@ 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; _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. + /// + /// A copy of the item. + public Item ToItem() + { + Item item = new Item(); + + item.netDefaults(_netId); + item.stack = _stack; + item.prefix = _prefixId; + + return item; + } + /// /// Converts the to a string. /// diff --git a/TShockAPI/Permissions.cs b/TShockAPI/Permissions.cs index d7ec7166..99ac5d7d 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))})" + : $"/{c.Name}"); sb.AppendLine($"## {name}"); sb.AppendLine($"{desc}"); 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); diff --git a/TShockAPI/Rest/RestManager.cs b/TShockAPI/Rest/RestManager.cs index c41e7767..5dd2ff20 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")}, @@ -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) @@ -944,8 +945,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/TShock.cs b/TShockAPI/TShock.cs index 2be5cfe7..fae533ea 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -1495,11 +1495,11 @@ namespace TShockAPI { if (!String.IsNullOrEmpty(text)) { - text = item.Key.Value + ' ' + text; + text = EnglishLanguage.GetCommandPrefixByName(item.Value._name) + ' ' + text; } else { - text = item.Key.Value; + text = EnglishLanguage.GetCommandPrefixByName(item.Value._name); } break; } diff --git a/TShockAPI/Utils.cs b/TShockAPI/Utils.cs index 4ee41c01..23b1e224 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 diff --git a/TShockPluginManager/Nuget.cs b/TShockPluginManager/Nuget.cs index 4e22209b..8ba73338 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); + } } } } diff --git a/docs/changelog.md b/docs/changelog.md index b1974546..6da4547c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -78,6 +78,20 @@ 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 `/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) +* Fix typo in config for IP bans. (@redchess64) +* 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 `stack` and `prefix` are now optional parameters. +* Fixed unable to transfer long response body for REST API. (@sgkoishi, #2925) +* Fixed the `/wind` command not being very helpful. (@punchready) +* Fixed /help, /me, and /p commands can't work in non-English languages. (@ACaiCat) +* Added a hook `AccountHooks.AccountGroupUpdate`, which is called when you change the user group. (@AgaSpace) + +## 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) @@ -126,7 +140,6 @@ 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) -* 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) 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 . ``` 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 @@
diff --git a/renovate.json b/renovate.json deleted file mode 100644 index 159ec1e2..00000000 --- a/renovate.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:base" - ], - "git-submodules": { - "enabled": true - }, - "packageRules": [ - { - "matchPackageNames": ["OTAPI.Upcoming", "ModFramework", "TerrariaServerAPI"], - "ignoreUnstable": "false", - "bumpVersion": "prerelease", - "groupName": "OTAPI things" - } - ] -}