Merge branch 'general-devel' into feature/properly-seed-group-names-in-database
This commit is contained in:
commit
329fce0089
45 changed files with 3391 additions and 2148 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
@ -498,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));
|
||||
|
|
@ -1066,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)
|
||||
{
|
||||
|
|
@ -1169,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));
|
||||
|
|
@ -1878,7 +1924,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 +2127,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 +2635,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;
|
||||
|
|
@ -2659,9 +2705,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));
|
||||
|
|
@ -2851,7 +2900,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
|
||||
|
|
|
|||
|
|
@ -148,24 +148,29 @@ namespace TShockAPI
|
|||
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;
|
||||
|
||||
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<string> parms)
|
||||
{
|
||||
return Run(new CommandArgs(msg, silent, ply, parms));
|
||||
}
|
||||
|
||||
public bool Run(string msg, TSPlayer ply, List<string> 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;
|
||||
|
|
@ -1176,7 +1186,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 +1203,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 +2058,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 +3085,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 +4637,22 @@ namespace TShockAPI
|
|||
{
|
||||
if (args.Parameters.Count != 1)
|
||||
{
|
||||
args.Player.SendErrorMessage(GetString("Invalid syntax. Proper syntax: {0}wind <speed>.", Specifier));
|
||||
args.Player.SendErrorMessage(GetString("Invalid syntax. Proper syntax: {0}wind <speed in mph>.", 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
|
||||
|
|
@ -5347,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)
|
||||
|
|
@ -6462,7 +6478,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 {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!"));
|
||||
}
|
||||
|
|
@ -6740,10 +6756,6 @@ namespace TShockAPI
|
|||
|
||||
playerToGod.GodMode = !playerToGod.GodMode;
|
||||
|
||||
var godPower = CreativePowerManager.Instance.GetPower<CreativePowers.GodmodePower>();
|
||||
|
||||
godPower.SetEnabledState(playerToGod.Index, playerToGod.GodMode);
|
||||
|
||||
if (playerToGod != args.Player)
|
||||
{
|
||||
args.Player.SendSuccessMessage(playerToGod.GodMode
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
/// <summary>Disbales IP bans by default, if no arguments are passed to the ban command.</summary>
|
||||
[Description("Disbales IP bans by default, if no arguments are passed to the ban command.")]
|
||||
/// <summary>Disables IP bans by default, if no arguments are passed to the ban command.</summary>
|
||||
[Description("Disables IP bans by default, if no arguments are passed to the ban command.")]
|
||||
public bool DisableDefaultIPBan;
|
||||
|
||||
/// <summary>Enable or disable the whitelist based on IP addresses in the whitelist.txt file.</summary>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
@ -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}"));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the group for a given username
|
||||
/// </summary>
|
||||
/// <param name="author">Who changes the group</param>
|
||||
/// <param name="account">The user account</param>
|
||||
/// <param name="group">The user account group to be set</param>
|
||||
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
|
||||
{
|
||||
/// <summary>Creates a new UserAccountNotExistException object, with the user account name in the message.</summary>
|
||||
/// <param name="name">The user account name to be pasesd in the message.</param>
|
||||
/// <param name="name">The user account name to be passed in the message.</param>
|
||||
/// <returns>A new UserAccountNotExistException object with a message containing the user account name that does not exist.</returns>
|
||||
public UserAccountNotExistException(string name)
|
||||
: base(GetString($"User account {name} does not exist"))
|
||||
|
|
@ -627,6 +662,20 @@ namespace TShockAPI.DB
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>The UserGroupUpdateLockedException used when the user group update failed and the request failed as a result.</summary>.
|
||||
[Serializable]
|
||||
public class UserGroupUpdateLockedException : UserAccountManagerException
|
||||
{
|
||||
/// <summary>Creates a new UserGroupUpdateLockedException object.</summary>
|
||||
/// <param name="name">The name of the user who failed to change the group.</param>
|
||||
/// <returns>New UserGroupUpdateLockedException object with a message containing the name of the user account that failed to change the group.</returns>
|
||||
public UserGroupUpdateLockedException(string name) :
|
||||
base(GetString($"Unable to update group of user {name}."))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>A GroupNotExistsException, used when a group does not exist.</summary>
|
||||
[Serializable]
|
||||
public class GroupNotExistsException : UserAccountManagerException
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
@ -2248,6 +2249,7 @@ namespace TShockAPI
|
|||
|
||||
var args = new SyncTilePickingEventArgs
|
||||
{
|
||||
Player = player,
|
||||
PlayerIndex = playerIndex,
|
||||
TileX = tileX,
|
||||
TileY = tileY,
|
||||
|
|
@ -2618,9 +2620,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)
|
||||
|
|
@ -2718,49 +2720,71 @@ 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();
|
||||
PlayerSpawnContext context = (PlayerSpawnContext)args.Data.ReadByte();
|
||||
|
||||
if (OnPlayerSpawn(args.Player, args.Data, player, spawnx, spawny, respawnTimer, numberOfDeathsPVE, numberOfDeathsPVP, context))
|
||||
args.Player.FinishedHandshake = true;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (respawnTimer > 0)
|
||||
args.Player.Dead = true;
|
||||
else
|
||||
args.Player.Dead = 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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -2941,6 +2965,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();
|
||||
|
|
@ -4444,6 +4475,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.
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ using System;
|
|||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace TShockAPI
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -52,17 +54,17 @@ namespace TShockAPI
|
|||
/// <summary>
|
||||
/// The group that this group inherits permissions from.
|
||||
/// </summary>
|
||||
public Group Parent { get; set; }
|
||||
public virtual Group Parent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The chat prefix for this group.
|
||||
/// </summary>
|
||||
public string Prefix { get; set; }
|
||||
public virtual string Prefix { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The chat suffix for this group.
|
||||
/// </summary>
|
||||
public string Suffix { get; set; }
|
||||
public virtual string Suffix { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the parent, not particularly sure why this is here.
|
||||
|
|
@ -164,6 +166,20 @@ namespace TShockAPI
|
|||
/// </summary>
|
||||
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>
|
||||
/// The default group attributed to unregistered users.
|
||||
/// </summary>
|
||||
|
|
@ -242,7 +258,7 @@ namespace TShockAPI
|
|||
/// Adds a permission to the list of negated permissions.
|
||||
/// </summary>
|
||||
/// <param name="permission">The permission to negate.</param>
|
||||
public void NegatePermission(string permission)
|
||||
public virtual void NegatePermission(string permission)
|
||||
{
|
||||
// Avoid duplicates
|
||||
if (!negatedpermissions.Contains(permission))
|
||||
|
|
@ -256,7 +272,7 @@ namespace TShockAPI
|
|||
/// Adds a permission to the list of permissions.
|
||||
/// </summary>
|
||||
/// <param name="permission">The permission to add.</param>
|
||||
public void AddPermission(string permission)
|
||||
public virtual void AddPermission(string permission)
|
||||
{
|
||||
if (permission.StartsWith("!"))
|
||||
{
|
||||
|
|
@ -276,7 +292,7 @@ namespace TShockAPI
|
|||
/// will parse "!permission" and add it to the negated permissions.
|
||||
/// </summary>
|
||||
/// <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();
|
||||
negatedpermissions.Clear();
|
||||
|
|
@ -288,7 +304,7 @@ namespace TShockAPI
|
|||
/// where "!permission" will remove a negated permission.
|
||||
/// </summary>
|
||||
/// <param name="permission"></param>
|
||||
public void RemovePermission(string permission)
|
||||
public virtual void RemovePermission(string permission)
|
||||
{
|
||||
if (permission.StartsWith("!"))
|
||||
{
|
||||
|
|
@ -302,7 +318,7 @@ namespace TShockAPI
|
|||
/// Assigns all fields of this instance to another.
|
||||
/// </summary>
|
||||
/// <param name="otherGroup">The other instance.</param>
|
||||
public void AssignTo(Group otherGroup)
|
||||
public virtual void AssignTo(Group otherGroup)
|
||||
{
|
||||
otherGroup.Name = Name;
|
||||
otherGroup.Parent = Parent;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// The player who updated the user's group
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using TShockAPI.DB;
|
||||
|
|
@ -119,6 +120,49 @@ namespace TShockAPI.Hooks
|
|||
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>
|
||||
/// EventArgs used for the <see cref="PlayerHooks.PlayerChat"/> event.
|
||||
/// </summary>
|
||||
|
|
@ -343,6 +387,26 @@ namespace TShockAPI.Hooks
|
|||
/// </summary>
|
||||
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>
|
||||
/// The delegate of the <see cref="PlayerChat"/> event.
|
||||
/// </summary>
|
||||
|
|
@ -449,6 +513,40 @@ namespace TShockAPI.Hooks
|
|||
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>
|
||||
/// Fires the <see cref="PlayerPreLogin"/> event.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<int, string> Buffs = new Dictionary<int, string>();
|
||||
|
||||
private static readonly Dictionary<string,string> VanillaCommandsPrefixs = new Dictionary<string, string>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get vanilla command prefix in English
|
||||
/// </summary>
|
||||
/// <param name="name">vanilla command name</param>
|
||||
/// <returns>vanilla command prefix in English</returns>
|
||||
public static string GetCommandPrefixByName(string name)
|
||||
{
|
||||
string commandText;
|
||||
if (VanillaCommandsPrefixs.TryGetValue(name, out commandText))
|
||||
return commandText;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +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/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
|
@ -155,13 +155,39 @@ namespace TShockAPI
|
|||
/// <param name="netId">The net ID.</param>
|
||||
/// <param name="stack">The stack.</param>
|
||||
/// <param name="prefixId">The prefix ID.</param>
|
||||
public NetItem(int netId, int stack, byte prefixId)
|
||||
public NetItem(int netId, int stack = 1, byte prefixId = 0)
|
||||
{
|
||||
_netId = netId;
|
||||
_stack = stack;
|
||||
_prefixId = prefixId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="NetItem"/>.
|
||||
/// </summary>
|
||||
/// <param name="item">Item in the game.</param>
|
||||
public NetItem(Item item)
|
||||
{
|
||||
_netId = item.netID;
|
||||
_stack = item.stack;
|
||||
_prefixId = item.prefix;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates <see cref="Terraria.Item"/> based on data from this structure.
|
||||
/// </summary>
|
||||
/// <returns>A copy of the item.</returns>
|
||||
public Item ToItem()
|
||||
{
|
||||
Item item = new Item();
|
||||
|
||||
item.netDefaults(_netId);
|
||||
item.stack = _stack;
|
||||
item.prefix = _prefixId;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="NetItem"/> to a string.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -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}");
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
/// <summary>
|
||||
/// Sets the default values for the inventory.
|
||||
/// </summary>
|
||||
[Obsolete("The player argument is not used.")]
|
||||
public PlayerData(TSPlayer player) : this(true) { }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default values for the inventory.
|
||||
/// </summary>
|
||||
/// <param name="includingStarterInventory">Is it necessary to load items from TShock's config</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -86,12 +96,22 @@ namespace TShockAPI
|
|||
/// <param name="stack"></param>
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores an item at the specific storage slot
|
||||
/// </summary>
|
||||
/// <param name="slot"></param>
|
||||
/// <param name="item"></param>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -104,16 +124,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 +278,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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.")]
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ using TShockAPI.Hooks;
|
|||
using TShockAPI.Net;
|
||||
using Timer = System.Timers.Timer;
|
||||
using System.Linq;
|
||||
using Terraria.GameContent.Creative;
|
||||
|
||||
namespace TShockAPI
|
||||
{
|
||||
|
|
@ -176,8 +177,13 @@ namespace TShockAPI
|
|||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// A queue of tiles destroyed by the player for reverting.
|
||||
|
|
@ -350,6 +356,9 @@ namespace TShockAPI
|
|||
/// <summary>Determines if the player is disabled for not clearing their trash. A re-login is the only way to reset this.</summary>
|
||||
public bool IsDisabledPendingTrashRemoval;
|
||||
|
||||
/// <summary>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.</summary>
|
||||
public bool FinishedHandshake = false;
|
||||
|
||||
/// <summary>Checks to see if active throttling is happening on events by Bouncer. Rejects repeated events by malicious clients in a short window.</summary>
|
||||
/// <returns>If the player is currently being throttled by Bouncer, or not.</returns>
|
||||
public bool IsBouncerThrottled()
|
||||
|
|
@ -935,9 +944,15 @@ namespace TShockAPI
|
|||
public bool LoginHarassed = false;
|
||||
|
||||
/// <summary>
|
||||
/// Player cant die, unless onehit
|
||||
/// Controls the journey godmode
|
||||
/// </summary>
|
||||
public bool GodMode = false;
|
||||
public bool GodMode
|
||||
{
|
||||
get =>
|
||||
CreativePowerManager.Instance.GetPower<CreativePowers.GodmodePower>().IsEnabledForPlayer(Index);
|
||||
set =>
|
||||
CreativePowerManager.Instance.GetPower<CreativePowers.GodmodePower>().SetEnabledState(Index, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Players controls are inverted if using SSC
|
||||
|
|
@ -981,7 +996,7 @@ namespace TShockAPI
|
|||
get
|
||||
{
|
||||
return RealPlayer
|
||||
&& (Netplay.Clients[Index] != null && Netplay.Clients[Index].IsActive && !Netplay.Clients[Index].PendingTermination);
|
||||
&& (Client != null && Client.IsActive && !Client.PendingTermination);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -998,8 +1013,8 @@ namespace TShockAPI
|
|||
/// </summary>
|
||||
public int State
|
||||
{
|
||||
get { return Netplay.Clients[Index].State; }
|
||||
set { Netplay.Clients[Index].State = value; }
|
||||
get { return Client.State; }
|
||||
set { Client.State = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1007,7 +1022,7 @@ namespace TShockAPI
|
|||
/// </summary>
|
||||
public string UUID
|
||||
{
|
||||
get { return RealPlayer ? Netplay.Clients[Index].ClientUUID : ""; }
|
||||
get { return RealPlayer ? Client.ClientUUID : ""; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1019,8 +1034,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
|
||||
|
|
@ -1103,6 +1118,11 @@ namespace TShockAPI
|
|||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Player RemoteClient.
|
||||
/// </summary>
|
||||
public RemoteClient Client => Netplay.Clients[Index];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Terraria Player object associated with the player.
|
||||
/// </summary>
|
||||
|
|
@ -1135,6 +1155,11 @@ namespace TShockAPI
|
|||
get { return TPlayer.team; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets PvP player mode.
|
||||
/// </summary>
|
||||
public bool Hostile => TPlayer.hostile;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the player's X coordinate.
|
||||
/// </summary>
|
||||
|
|
@ -1267,7 +1292,7 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
|
||||
PlayerData = new PlayerData(this);
|
||||
PlayerData = new PlayerData();
|
||||
Group = TShock.Groups.GetGroupByName(TShock.Config.Settings.DefaultGuestGroupName);
|
||||
tempGroup = null;
|
||||
if (tempGroupTimer != null)
|
||||
|
|
@ -1363,6 +1388,25 @@ namespace TShockAPI
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Teleports the player to their spawnpoint.
|
||||
/// Teleports to main spawnpoint if their bed is not active.
|
||||
/// Supports SSC.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Heals the player.
|
||||
/// </summary>
|
||||
|
|
@ -1377,14 +1421,7 @@ namespace TShockAPI
|
|||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1487,6 +1524,41 @@ namespace TShockAPI
|
|||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the values of the <see cref="RemoteClient.TileSections"/> array.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">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.</param>
|
||||
/// <param name="isLoaded">Is the section loaded.</param>
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gives an item to the player. Includes banned item spawn prevention to check if the player can spawn the item.
|
||||
/// </summary>
|
||||
|
|
@ -1519,6 +1591,15 @@ namespace TShockAPI
|
|||
GiveItemByDrop(type, stack, prefix);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gives an item to the player.
|
||||
/// </summary>
|
||||
/// <param name="item">Item with data to be given to the player.</param>
|
||||
public virtual void GiveItem(NetItem item)
|
||||
{
|
||||
GiveItem(item.NetId, item.Stack, item.PrefixId);
|
||||
}
|
||||
|
||||
private Item EmptySentinelItem = new Item();
|
||||
|
||||
private bool Depleted(Item item)
|
||||
|
|
@ -1815,7 +1896,17 @@ namespace TShockAPI
|
|||
/// <param name="damage">The amount of damage the player will take.</param>
|
||||
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());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wounds the player with the given damage.
|
||||
/// </summary>
|
||||
/// <param name="damage">The amount of damage the player will take.</param>
|
||||
/// <param name="reason">The reason for causing damage to player.</param>
|
||||
public virtual void DamagePlayer(int damage, PlayerDeathReason reason)
|
||||
{
|
||||
NetMessage.SendPlayerHurt(Index, reason, damage, (new Random()).Next(-1, 1), false, false, 0, -1, -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1823,7 +1914,16 @@ namespace TShockAPI
|
|||
/// </summary>
|
||||
public virtual void KillPlayer()
|
||||
{
|
||||
NetMessage.SendPlayerDeath(Index, PlayerDeathReason.LegacyDefault(), 99999, (new Random()).Next(-1, 1), false, -1, -1);
|
||||
KillPlayer(PlayerDeathReason.LegacyDefault());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kills the player.
|
||||
/// </summary>
|
||||
/// <param name="reason">Reason for killing a player.</param>
|
||||
public virtual void KillPlayer(PlayerDeathReason reason)
|
||||
{
|
||||
NetMessage.SendPlayerDeath(Index, reason, 99999, (new Random()).Next(-1, 1), false, -1, -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1832,6 +1932,8 @@ namespace TShockAPI
|
|||
/// <param name="team">The team color index.</param>
|
||||
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);
|
||||
}
|
||||
|
|
@ -1840,6 +1942,7 @@ namespace TShockAPI
|
|||
/// Sets the player's pvp.
|
||||
/// </summary>
|
||||
/// <param name="mode">The state of the pvp mode.</param>
|
||||
/// <param name="withMsg">Whether a chat message about the change should be sent.</param>
|
||||
public virtual void SetPvP(bool mode, bool withMsg = false)
|
||||
{
|
||||
Main.player[Index].hostile = mode;
|
||||
|
|
@ -2064,7 +2167,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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ namespace TShockAPI
|
|||
/// <summary>VersionNum - The version number the TerrariaAPI will return back to the API. We just use the Assembly info.</summary>
|
||||
public static readonly Version VersionNum = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
/// <summary>VersionCodename - The version codename is displayed when the server starts. Inspired by software codenames conventions.</summary>
|
||||
public static readonly string VersionCodename = "Thank you, everyone, for your support of TShock all these years! <3";
|
||||
public static readonly string VersionCodename = "East";
|
||||
|
||||
/// <summary>SavePath - This is the path TShock saves its data in. This path is relative to the TerrariaServer.exe (not in ServerPlugins).</summary>
|
||||
public static string SavePath = "tshock";
|
||||
|
|
@ -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)
|
||||
|
|
@ -676,17 +684,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1178,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)
|
||||
|
|
@ -1372,6 +1376,8 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bans.CheckBan(player);
|
||||
Players[args.Who] = player;
|
||||
}
|
||||
|
||||
|
|
@ -1393,7 +1399,8 @@ namespace TShockAPI
|
|||
return;
|
||||
}
|
||||
|
||||
Bans.CheckBan(player);
|
||||
if (Bans.CheckBan(player))
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>OnLeave - Called when a player leaves the server.</summary>
|
||||
|
|
@ -1433,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));
|
||||
|
||||
|
|
@ -1454,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)
|
||||
{
|
||||
|
|
@ -1483,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);
|
||||
|
|
@ -1501,11 +1517,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;
|
||||
}
|
||||
|
|
@ -1699,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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
-->
|
||||
<Version>5.1.3</Version>
|
||||
<Version>5.2.2</Version>
|
||||
<AssemblyTitle>TShock for Terraria</AssemblyTitle>
|
||||
<Company>Pryaxis & TShock Contributors</Company>
|
||||
<Product>TShockAPI</Product>
|
||||
<Copyright>Copyright © Pryaxis & TShock Contributors 2011-2022</Copyright>
|
||||
<Copyright>Copyright © Pryaxis & TShock Contributors 2011-2023</Copyright>
|
||||
<!-- extras for nuget -->
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<PackageLicenseExpression>GPL-3.0-or-later</PackageLicenseExpression>
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
||||
<PackageReference Include="GetText.NET" Version="1.7.14" />
|
||||
<PackageReference Include="MySql.Data" Version="8.0.31" />
|
||||
<PackageReference Include="MySql.Data" Version="8.4.0" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="6.0.11" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ namespace TShockAPI
|
|||
/// </summary>
|
||||
public class TextLog : ILog, IDisposable
|
||||
{
|
||||
private readonly StreamWriter _logWriter;
|
||||
private readonly bool ClearFile;
|
||||
private StreamWriter _logWriter;
|
||||
|
||||
/// <summary>
|
||||
/// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
|||
/// <returns>The number of active players on the server.</returns>
|
||||
public int GetActivePlayerCount()
|
||||
{
|
||||
return Main.player.Where(p => null != p && p.active).Count();
|
||||
return TShock.Players.Count(p => null != p && p.Active && p.FinishedHandshake);
|
||||
}
|
||||
|
||||
//Random should not be generated in a method
|
||||
|
|
@ -330,7 +330,7 @@ namespace TShockAPI
|
|||
/// <returns>The item represented by the tag.</returns>
|
||||
public Item GetItemFromTag(string tag)
|
||||
{
|
||||
Regex regex = new Regex(@"\[i(tem)?(?:\/s(?<Stack>\d{1,3}))?(?:\/p(?<Prefix>\d{1,3}))?:(?<NetID>-?\d{1,4})\]");
|
||||
Regex regex = new Regex(@"\[i(tem)?(?:\/s(?<Stack>\d{1,4}))?(?:\/p(?<Prefix>\d{1,3}))?:(?<NetID>-?\d{1,4})\]");
|
||||
Match match = regex.Match(tag);
|
||||
if (!match.Success)
|
||||
return null;
|
||||
|
|
@ -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>
|
||||
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);
|
||||
|
||||
/// <summary>Determines the distance between two vectors.</summary>
|
||||
/// <param name="value1">The first vector location.</param>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue