Merge branch 'general-devel' into net9-upgrade

This commit is contained in:
Luke 2025-01-29 22:03:08 +10:00
commit 3e08982e73
7 changed files with 174 additions and 19 deletions

View file

@ -148,24 +148,29 @@ namespace TShockAPI
Permissions = new List<string>(); Permissions = new List<string>();
} }
public bool Run(string msg, bool silent, TSPlayer ply, List<string> parms) public bool Run(CommandArgs args)
{ {
if (!CanRun(ply)) if (!CanRun(args.Player))
return false; return false;
try try
{ {
CommandDelegate(new CommandArgs(msg, silent, ply, parms)); CommandDelegate(args);
} }
catch (Exception e) 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()); TShock.Log.Error(e.ToString());
} }
return true; return true;
} }
public bool Run(string msg, bool silent, TSPlayer ply, List<string> parms)
{
return Run(new CommandArgs(msg, silent, ply, parms));
}
public bool Run(string msg, TSPlayer ply, List<string> parms) public bool Run(string msg, TSPlayer ply, List<string> parms)
{ {
return Run(msg, false, ply, 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); TShock.Utils.SendLogs(GetString("{0} executed: {1}{2}.", player.Name, silent ? SilentSpecifier : Specifier, cmdText), Color.PaleVioletRed, player);
else else
TShock.Utils.SendLogs(GetString("{0} executed (args omitted): {1}{2}.", player.Name, silent ? SilentSpecifier : Specifier, cmdName), Color.PaleVioletRed, player); 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; return true;

View file

@ -4445,6 +4445,11 @@ namespace TShockAPI
return true; 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, // 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 // 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. // swap around the PlayerData items ourself.

View file

@ -20,6 +20,8 @@ using System;
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.Xna.Framework;
namespace TShockAPI namespace TShockAPI
{ {
/// <summary> /// <summary>
@ -52,17 +54,17 @@ namespace TShockAPI
/// <summary> /// <summary>
/// The group that this group inherits permissions from. /// The group that this group inherits permissions from.
/// </summary> /// </summary>
public Group Parent { get; set; } public virtual Group Parent { get; set; }
/// <summary> /// <summary>
/// The chat prefix for this group. /// The chat prefix for this group.
/// </summary> /// </summary>
public string Prefix { get; set; } public virtual string Prefix { get; set; }
/// <summary> /// <summary>
/// The chat suffix for this group. /// The chat suffix for this group.
/// </summary> /// </summary>
public string Suffix { get; set; } public virtual string Suffix { get; set; }
/// <summary> /// <summary>
/// The name of the parent, not particularly sure why this is here. /// The name of the parent, not particularly sure why this is here.
@ -164,6 +166,20 @@ namespace TShockAPI
/// </summary> /// </summary>
public byte B = 255; public byte B = 255;
/// <summary>
/// Simplifies work with the <see cref="R"/>, <see cref="G"/>, <see cref="B"/> properties.
/// </summary>
public virtual Color Color
{
get => new Color(R, G, B);
set
{
R = value.R;
G = value.G;
B = value.B;
}
}
/// <summary> /// <summary>
/// The default group attributed to unregistered users. /// The default group attributed to unregistered users.
/// </summary> /// </summary>
@ -242,7 +258,7 @@ namespace TShockAPI
/// Adds a permission to the list of negated permissions. /// Adds a permission to the list of negated permissions.
/// </summary> /// </summary>
/// <param name="permission">The permission to negate.</param> /// <param name="permission">The permission to negate.</param>
public void NegatePermission(string permission) public virtual void NegatePermission(string permission)
{ {
// Avoid duplicates // Avoid duplicates
if (!negatedpermissions.Contains(permission)) if (!negatedpermissions.Contains(permission))
@ -256,7 +272,7 @@ namespace TShockAPI
/// Adds a permission to the list of permissions. /// Adds a permission to the list of permissions.
/// </summary> /// </summary>
/// <param name="permission">The permission to add.</param> /// <param name="permission">The permission to add.</param>
public void AddPermission(string permission) public virtual void AddPermission(string permission)
{ {
if (permission.StartsWith("!")) if (permission.StartsWith("!"))
{ {
@ -276,7 +292,7 @@ namespace TShockAPI
/// will parse "!permission" and add it to the negated permissions. /// will parse "!permission" and add it to the negated permissions.
/// </summary> /// </summary>
/// <param name="permission">The new list of permissions to associate with the group.</param> /// <param name="permission">The new list of permissions to associate with the group.</param>
public void SetPermission(List<string> permission) public virtual void SetPermission(List<string> permission)
{ {
permissions.Clear(); permissions.Clear();
negatedpermissions.Clear(); negatedpermissions.Clear();
@ -288,7 +304,7 @@ namespace TShockAPI
/// where "!permission" will remove a negated permission. /// where "!permission" will remove a negated permission.
/// </summary> /// </summary>
/// <param name="permission"></param> /// <param name="permission"></param>
public void RemovePermission(string permission) public virtual void RemovePermission(string permission)
{ {
if (permission.StartsWith("!")) if (permission.StartsWith("!"))
{ {
@ -302,7 +318,7 @@ namespace TShockAPI
/// Assigns all fields of this instance to another. /// Assigns all fields of this instance to another.
/// </summary> /// </summary>
/// <param name="otherGroup">The other instance.</param> /// <param name="otherGroup">The other instance.</param>
public void AssignTo(Group otherGroup) public virtual void AssignTo(Group otherGroup)
{ {
otherGroup.Name = Name; otherGroup.Name = Name;
otherGroup.Parent = Parent; otherGroup.Parent = Parent;

View file

@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using TShockAPI.DB; using TShockAPI.DB;
@ -119,6 +120,49 @@ namespace TShockAPI.Hooks
public string CommandPrefix { get; set; } public string CommandPrefix { get; set; }
} }
/// <summary>
/// EventArgs used for the <see cref="PlayerHooks.PrePlayerCommand"/> event.
/// </summary>
public class PrePlayerCommandEventArgs : HandledEventArgs
{
/// <summary>
/// The command entered by the player.
/// </summary>
public Command Command { get; }
/// <summary>
/// Command arguments.
/// </summary>
public CommandArgs Arguments { get; set; }
public PrePlayerCommandEventArgs(Command command, CommandArgs args)
{
Command = command;
Arguments = args;
}
}
/// <summary>
/// EventArgs used for the <see cref="PlayerHooks.PostPlayerCommand"/> event.
/// </summary>
public class PostPlayerCommandEventArgs : HandledEventArgs
{
/// <summary>
/// The command entered by the player.
/// </summary>
public Command Command { get; }
/// <summary>
/// Command arguments.
/// </summary>
public CommandArgs Arguments { get; }
public PostPlayerCommandEventArgs(Command command, CommandArgs arguments, bool handled)
{
Command = command;
Arguments = arguments;
Handled = handled;
}
}
/// <summary> /// <summary>
/// EventArgs used for the <see cref="PlayerHooks.PlayerChat"/> event. /// EventArgs used for the <see cref="PlayerHooks.PlayerChat"/> event.
/// </summary> /// </summary>
@ -343,6 +387,26 @@ namespace TShockAPI.Hooks
/// </summary> /// </summary>
public static event PlayerCommandD PlayerCommand; public static event PlayerCommandD PlayerCommand;
/// <summary>
/// The delegate of the <see cref="PrePlayerCommand"/> event.
/// </summary>
/// <param name="e">The EventArgs for this event.</param>
public delegate void PrePlayerCommandD(PrePlayerCommandEventArgs e);
/// <summary>
/// Fired before a command is run.
/// </summary>
public static event PrePlayerCommandD PrePlayerCommand;
/// <summary>
/// The delegate of the <see cref="PostPlayerCommand"/> event.
/// </summary>
/// <param name="e">The EventArgs for this event.</param>
public delegate void PostPlayerCommandD(PostPlayerCommandEventArgs e);
/// <summary>
/// Fired after a command is run.
/// </summary>
public static event PostPlayerCommandD PostPlayerCommand;
/// <summary> /// <summary>
/// The delegate of the <see cref="PlayerChat"/> event. /// The delegate of the <see cref="PlayerChat"/> event.
/// </summary> /// </summary>
@ -449,6 +513,40 @@ namespace TShockAPI.Hooks
return playerCommandEventArgs.Handled; return playerCommandEventArgs.Handled;
} }
/// <summary>
/// Fires the <see cref="PrePlayerCommand"/> event.
/// </summary>
/// <param name="cmd">Command to be executed</param>
/// <param name="arguments">Command arguments</param>
/// <returns>True if the event has been handled.</returns>
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;
}
/// <summary>
/// Fires the <see cref="PostPlayerCommand"/> event.
/// </summary>
/// <param name="cmd">Executed command.</param>
/// <param name="arguments">Command arguments.</param>
/// <param name="handled">Is the command executed.</param>
public static void OnPostPlayerCommand(Command cmd, CommandArgs arguments, bool handled)
{
if (PostPlayerCommand == null)
return;
PostPlayerCommandEventArgs args = new PostPlayerCommandEventArgs(cmd, arguments, handled);
PostPlayerCommand(args);
}
/// <summary> /// <summary>
/// Fires the <see cref="PlayerPreLogin"/> event. /// Fires the <see cref="PlayerPreLogin"/> event.
/// </summary> /// </summary>

View file

@ -1149,11 +1149,15 @@ namespace TShockAPI
/// <param name="empty">If the server is empty; determines if we should use Utils.GetActivePlayerCount() for player count or 0.</param> /// <param name="empty">If the server is empty; determines if we should use Utils.GetActivePlayerCount() for player count or 0.</param>
internal void SetConsoleTitle(bool empty) internal void SetConsoleTitle(bool empty)
{ {
if (ShouldSkipTitle)
return;
Console.Title = GetString("{0}{1}/{2} on {3} @ {4}:{5} (TShock for Terraria v{6})", 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 + " - " : "", !string.IsNullOrWhiteSpace(TShock.Config.Settings.ServerName) ? TShock.Config.Settings.ServerName + " - " : "",
empty ? 0 : GetActivePlayerCount(), empty ? 0 : GetActivePlayerCount(),
TShock.Config.Settings.MaxSlots, Main.worldName, Netplay.ServerIP.ToString(), Netplay.ListenPort, TShock.VersionNum); 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);
/// <summary>Determines the distance between two vectors.</summary> /// <summary>Determines the distance between two vectors.</summary>
/// <param name="value1">The first vector location.</param> /// <param name="value1">The first vector location.</param>

View file

@ -27,12 +27,31 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
using System.Reflection; 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;
}
Dictionary<string, Assembly> _cache = new Dictionary<string, Assembly>(); Dictionary<string, Assembly> _cache = new Dictionary<string, Assembly>();
System.Runtime.Loader.AssemblyLoadContext.Default.Resolving += Default_Resolving; System.Runtime.Loader.AssemblyLoadContext.Default.Resolving += Default_Resolving;
await StartAsync(); return await StartAsync();
/// <summary> /// <summary>
/// Resolves a module from the ./bin folder, either with a .dll by preference or .exe /// Resolves a module from the ./bin folder, either with a .dll by preference or .exe
@ -61,15 +80,16 @@ Assembly? Default_Resolving(System.Runtime.Loader.AssemblyLoadContext arg1, Asse
/// Initiates the TSAPI server. /// Initiates the TSAPI server.
/// </summary> /// </summary>
/// <remarks>This method exists so that the resolver can attach before TSAPI needs its dependencies.</remarks> /// <remarks>This method exists so that the resolver can attach before TSAPI needs its dependencies.</remarks>
async Task StartAsync() async Task<int> StartAsync()
{ {
if (args.Length > 0 && args[0].ToLower() == "plugins") if (args.Length > 0 && args[0].ToLower() == "plugins")
{ {
var items = args.ToList(); var items = args.ToList();
items.RemoveAt(0); items.RemoveAt(0);
await TShockPluginManager.NugetCLI.Main(items); await TShockPluginManager.NugetCLI.Main(items);
return; return 0;
} }
TerrariaApi.Server.Program.Main(args); TerrariaApi.Server.Program.Main(args);
return 0;
} }

View file

@ -90,6 +90,9 @@ Use past tense when adding new entries; sign your name off when you add or chang
* Fixed the `/wind` command not being very helpful. (@punchready) * Fixed the `/wind` command not being very helpful. (@punchready)
* Fixed /help, /me, and /p commands can't work in non-English languages. (@ACaiCat) * 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) * Added a hook `AccountHooks.AccountGroupUpdate`, which is called when you change the user group. (@AgaSpace)
* * 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.
## TShock 5.2.1 ## TShock 5.2.1
* Updated `TSPlayer.GodMode`. (@AgaSpace) * Updated `TSPlayer.GodMode`. (@AgaSpace)
@ -104,9 +107,8 @@ 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) * 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 bug where when the `UseSqlLogs` config property is true, an empty log file would still get created. (@ZakFahey)
* Fixed typo in `/gbuff`. (@sgkoishi, #2955) * Fixed typo in `/gbuff`. (@sgkoishi, #2955)
* Rewrote the `.dockerignore` file into a denylist. (@timschumi) * Added `PlayerHooks.PrePlayerCommand` hook, which fired before command execution. (@AgaSpace)
* Added CI for Docker images. (@timschumi) * Added `PlayerHooks.PostPlayerCommand` hook, which fired after command execution. (@AgaSpace)
* Fixed Cursed Flares kicking players for invalid buff. (@Arthri)
## TShock 5.2 ## TShock 5.2
* An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK) * An additional option `pvpwithnoteam` is added at `PvPMode` to enable PVP with no team. (@CelestialAnarchy, #2617, @ATFGK)