diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs index d82f10e2..16183431 100644 --- a/TShockAPI/Commands.cs +++ b/TShockAPI/Commands.cs @@ -1607,7 +1607,7 @@ namespace TShockAPI } } - if (banUuid) + if (banUuid && player.UUID.Length > 0) { banResult = DoBan($"{Identifier.UUID}{player.UUID}", reason, expiration); } @@ -2531,7 +2531,7 @@ namespace TShockAPI foreach (TSPlayer ply in TShock.Players.Where(p => p != null && p.Active && p.TPlayer.name.ToLower().Equals(args.Parameters[0].ToLower()))) { //this will always tell the client that they have not done the quest today. - ply.SendData((PacketTypes)74, ""); + ply.SendData(PacketTypes.AnglerQuest, ""); } args.Player.SendSuccessMessage(GetString("Removed {0} players from the angler quest completion list for today.", result)); } diff --git a/TShockAPI/DB/BanManager.cs b/TShockAPI/DB/BanManager.cs index 42892100..3e93a3b7 100644 --- a/TShockAPI/DB/BanManager.cs +++ b/TShockAPI/DB/BanManager.cs @@ -189,11 +189,15 @@ namespace TShockAPI.DB { List identifiers = new List { - $"{Identifier.UUID}{player.UUID}", $"{Identifier.Name}{player.Name}", $"{Identifier.IP}{player.IP}" }; + if (player.UUID != null && player.UUID.Length > 0) + { + identifiers.Add($"{Identifier.UUID}{player.UUID}"); + } + if (player.Account != null) { identifiers.Add($"{Identifier.Account}{player.Account.Name}"); diff --git a/TShockAPI/DB/CharacterManager.cs b/TShockAPI/DB/CharacterManager.cs index 357dc746..e9e1974a 100644 --- a/TShockAPI/DB/CharacterManager.cs +++ b/TShockAPI/DB/CharacterManager.cs @@ -189,7 +189,7 @@ namespace TShockAPI.DB if (!player.IsLoggedIn) return false; - if (player.State < 10) + if (player.State < (int)ConnectionState.Complete) return false; if (player.HasPermission(Permissions.bypassssc) && !fromCommand) diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index e4c8527e..f4b04954 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -2619,7 +2619,7 @@ namespace TShockAPI private static bool HandleConnecting(GetDataHandlerArgs args) { - var account = TShock.UserAccounts.GetUserAccountByName(args.Player.Name);// + var account = TShock.UserAccounts.GetUserAccountByName(args.Player.Name); args.Player.DataWhenJoined = new PlayerData(false); args.Player.DataWhenJoined.CopyCharacter(args.Player); args.Player.PlayerData = new PlayerData(false); @@ -2629,8 +2629,9 @@ namespace TShockAPI { if (account.UUID == args.Player.UUID) { - if (args.Player.State == 1) - args.Player.State = 2; + if (args.Player.State == (int)ConnectionState.AssigningPlayerSlot) + args.Player.State = (int)ConnectionState.AwaitingPlayerInfo; + NetMessage.SendData((int)PacketTypes.WorldInfo, args.Player.Index); var group = TShock.Groups.GetGroupByName(account.Group); @@ -2688,8 +2689,9 @@ namespace TShockAPI return true; } - if (args.Player.State == 1) - args.Player.State = 2; + if (args.Player.State == (int)ConnectionState.AssigningPlayerSlot) + args.Player.State = (int)ConnectionState.AwaitingPlayerInfo; + NetMessage.SendData((int)PacketTypes.WorldInfo, args.Player.Index); return true; } @@ -2727,7 +2729,8 @@ namespace TShockAPI short numberOfDeathsPVP = args.Data.ReadInt16(); PlayerSpawnContext context = (PlayerSpawnContext)args.Data.ReadByte(); - args.Player.FinishedHandshake = true; + if (args.Player.State >= (int)ConnectionState.RequestingWorldData && !args.Player.FinishedHandshake) + args.Player.FinishedHandshake = true; //If the player has requested world data before sending spawn player, they should be at the obvious ClientRequestedWorldData state. Also only set this once to remove redundant updates. if (OnPlayerSpawn(args.Player, args.Data, player, spawnX, spawnY, respawnTimer, numberOfDeathsPVE, numberOfDeathsPVP, context)) return true; @@ -3245,8 +3248,9 @@ namespace TShockAPI args.Player.RequiresPassword = false; args.Player.PlayerData = TShock.CharacterDB.GetPlayerData(args.Player, account.ID); - if (args.Player.State == 1) - args.Player.State = 2; + if (args.Player.State == (int)ConnectionState.AssigningPlayerSlot) + args.Player.State = (int)ConnectionState.AwaitingPlayerInfo; + NetMessage.SendData((int)PacketTypes.WorldInfo, args.Player.Index); var group = TShock.Groups.GetGroupByName(account.Group); @@ -3293,8 +3297,10 @@ namespace TShockAPI if (TShock.Config.Settings.ServerPassword == password) { args.Player.RequiresPassword = false; - if (args.Player.State == 1) - args.Player.State = 2; + + if (args.Player.State == (int)ConnectionState.AssigningPlayerSlot) + args.Player.State = (int)ConnectionState.AwaitingPlayerInfo; + NetMessage.SendData((int)PacketTypes.WorldInfo, args.Player.Index); return true; } @@ -3462,9 +3468,9 @@ namespace TShockAPI if (buff == 10 && TShock.Config.Settings.DisableInvisPvP && args.TPlayer.hostile) buff = 0; - if (Netplay.Clients[args.TPlayer.whoAmI].State < 2 && (buff == 156 || buff == 47 || buff == 149)) + if (Netplay.Clients[args.TPlayer.whoAmI].State < (int)ConnectionState.AwaitingPlayerInfo && (buff == 156 || buff == 47 || buff == 149)) { - TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandlePlayerBuffList zeroed player buff due to below state 2 {0} {1}", args.Player.Name, buff)); + TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandlePlayerBuffList zeroed player buff due to below state awaiting player information {0} {1}", args.Player.Name, buff)); buff = 0; } @@ -3713,10 +3719,15 @@ namespace TShockAPI thing = GetString("{0} summoned the {1}!", args.Player.Name, npc.FullName); break; } - if (TShock.Config.Settings.AnonymousBossInvasions) - TShock.Utils.SendLogs(thing, Color.PaleVioletRed, args.Player); - else - TShock.Utils.Broadcast(thing, 175, 75, 255); + + if (NPCID.Sets.MPAllowedEnemies[thingType]) + { + if (TShock.Config.Settings.AnonymousBossInvasions) + TShock.Utils.SendLogs(thing, Color.PaleVioletRed, args.Player); + else + TShock.Utils.Broadcast(thing, 175, 75, 255); + } + return false; } diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index f6c06f85..af009fa7 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -35,7 +35,6 @@ using TShockAPI.Net; using Timer = System.Timers.Timer; using System.Linq; using Terraria.GameContent.Creative; - namespace TShockAPI { /// @@ -62,6 +61,49 @@ namespace TShockAPI WriteToLogAndConsole } + /// + /// An enum based on the current client's connection state to the server. + /// + public enum ConnectionState : int + { + /// + /// The server is password protected and the connection is pending until a password is sent by the client. + /// + AwaitingPassword = -1, + /// + /// The connection has been established, and the client must verify its version. + /// + AwaitingVersionCheck = 0, + /// + /// The server has accepted the client's password to connect and/or the server has verified the client's version string as being correct. The client is now being assigned a player slot. + /// + AssigningPlayerSlot = 1, + /// + /// The player slot has been received by the client, and the server is now waiting for the player information. + /// + AwaitingPlayerInfo = 2, + /// + /// Player information has been received, and the client is requesting world data. + /// + RequestingWorldData = 3, + /// + /// The world data is being sent to the client. + /// + ReceivingWorldData = 4, + /// + /// The world data has been received, and the client is now finalizing the load. + /// + FinalizingWorldLoad = 5, + /// + /// The client is requesting tile data. + /// + RequestingTileData = 6, + /// + /// The connection process is complete (The player has spawned), and the client has fully joined the game. + /// + Complete = 10 + } + public class TSPlayer { /// @@ -1329,6 +1371,9 @@ namespace TShockAPI FakePlayer = new Player { name = playerName, whoAmI = -1 }; Group = Group.DefaultGroup; AwaitingResponse = new Dictionary>(); + + if (playerName == "All" || playerName == "Server") + FinishedHandshake = true; //Hot fix for the all player object not getting packets like TimeSet, etc because they have no state and finished handshake will always be false. } /// @@ -2119,6 +2164,27 @@ namespace TShockAPI SendData(PacketTypes.PlayerAddBuff, number: Index, number2: type, number3: time); } + /// + /// The list of necessary packets to make sure gets through to the player upon connection (before they finish the handshake). + /// + private static readonly HashSet HandshakeNecessaryPackets = new() + { + PacketTypes.ContinueConnecting, + PacketTypes.WorldInfo, + PacketTypes.Status, + PacketTypes.Disconnect, + PacketTypes.TileFrameSection, + PacketTypes.TileSendSection, + PacketTypes.PlayerSpawnSelf + }; + + /// + /// Determines if an outgoing packet is necessary to send to a player before they have finished the connection handshake. + /// + /// The packet type to check against the necessary list. + /// Whether the packet is necessary for connection or not + private bool NecessaryPacket(PacketTypes msgType) => HandshakeNecessaryPackets.Contains(msgType); + //Todo: Separate this into a few functions. SendTo, SendToAll, etc /// /// Sends data to the player. @@ -2136,6 +2202,12 @@ namespace TShockAPI if (RealPlayer && !ConnectionAlive) return; + if (!NecessaryPacket(msgType) && !FinishedHandshake) + return; + + if (FakePlayer != null && FakePlayer.whoAmI != -1 && msgType == PacketTypes.WorldInfo && State < (int)ConnectionState.RequestingWorldData) //So.. the All player doesn't have a state, so we cannot check this, skip over them if their index is -1 (server/all) + return; + NetMessage.SendData((int)msgType, Index, -1, text == null ? null : NetworkText.FromLiteral(text), number, number2, number3, number4, number5); } diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index c5f2b968..cf47b170 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -1440,7 +1440,7 @@ namespace TShockAPI if (tsplr.ReceivedInfo) { - 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. + if (!tsplr.SilentKickInProgress && tsplr.State >= (int)ConnectionState.RequestingWorldData && 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)); @@ -1461,7 +1461,6 @@ namespace TShockAPI } } - tsplr.FinishedHandshake = false; // Fire the OnPlayerLogout hook too, if the player was logged in and they have a TSPlayer object. @@ -1495,6 +1494,7 @@ namespace TShockAPI if (!tsplr.FinishedHandshake) { + tsplr.Kick(GetString("Your client didn't send the right connection information."), true); args.Handled = true; return; } @@ -1678,7 +1678,7 @@ namespace TShockAPI return; } - if ((player.State < 10 || player.Dead) && (int)type > 12 && (int)type != 16 && (int)type != 42 && (int)type != 50 && + if ((player.State < (int)ConnectionState.Complete || player.Dead) && (int)type > 12 && (int)type != 16 && (int)type != 42 && (int)type != 50 && (int)type != 38 && (int)type != 21 && (int)type != 22 && type != PacketTypes.SyncLoadout) { e.Handled = true;