Merge remote-tracking branch 'upstream/general-devel' into net9-upgrade
This commit is contained in:
commit
df05e94226
15 changed files with 421 additions and 1293 deletions
|
|
@ -504,6 +504,14 @@ namespace TShockAPI
|
|||
return;
|
||||
}
|
||||
|
||||
if (!float.IsFinite(pos.X) || !float.IsFinite(pos.Y))
|
||||
{
|
||||
TShock.Log.ConsoleInfo(GetString("Bouncer / OnPlayerUpdate force kicked (attempted to set position to infinity or NaN) from {0}", args.Player.Name));
|
||||
args.Player.Kick(GetString("Detected DOOM set to ON position."), true, true);
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (pos.X < 0 || pos.Y < 0 || pos.X >= Main.maxTilesX * 16 - 16 || pos.Y >= Main.maxTilesY * 16 - 16)
|
||||
{
|
||||
TShock.Log.ConsoleDebug(GetString("Bouncer / OnPlayerUpdate rejected from (position check) {0}", args.Player.Name));
|
||||
|
|
@ -1072,6 +1080,22 @@ namespace TShockAPI
|
|||
bool noDelay = args.NoDelay;
|
||||
short type = args.Type;
|
||||
|
||||
if (!float.IsFinite(pos.X) || !float.IsFinite(pos.Y))
|
||||
{
|
||||
TShock.Log.ConsoleInfo(GetString("Bouncer / OnItemDrop force kicked (attempted to set position to infinity or NaN) from {0}", args.Player.Name));
|
||||
args.Player.Kick(GetString("Detected DOOM set to ON position."), true, true);
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!float.IsFinite(vel.X) || !float.IsFinite(vel.Y))
|
||||
{
|
||||
TShock.Log.ConsoleInfo(GetString("Bouncer / OnItemDrop force kicked (attempted to set velocity to infinity or NaN) from {0}", args.Player.Name));
|
||||
args.Player.Kick(GetString("Detected DOOM set to ON position."), true, true);
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// player is attempting to crash clients
|
||||
if (type < -48 || type >= Terraria.ID.ItemID.Count)
|
||||
{
|
||||
|
|
@ -1175,6 +1199,22 @@ namespace TShockAPI
|
|||
int index = args.Index;
|
||||
float[] ai = args.Ai;
|
||||
|
||||
// Clients do send NaN values so we can't just kick them
|
||||
// See https://github.com/Pryaxis/TShock/issues/3076
|
||||
if (!float.IsFinite(pos.X) || !float.IsFinite(pos.Y))
|
||||
{
|
||||
TShock.Log.ConsoleInfo(GetString("Bouncer / OnNewProjectile rejected set position to infinity or NaN from {0}", args.Player.Name));
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!float.IsFinite(vel.X) || !float.IsFinite(vel.Y))
|
||||
{
|
||||
TShock.Log.ConsoleInfo(GetString("Bouncer / OnNewProjectile rejected set velocity to infinity or NaN from {0}", args.Player.Name));
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (index > Main.maxProjectiles)
|
||||
{
|
||||
TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile rejected from above projectile limit from {0}", args.Player.Name));
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,11 +189,15 @@ namespace TShockAPI.DB
|
|||
{
|
||||
List<string> identifiers = new List<string>
|
||||
{
|
||||
$"{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}");
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ namespace TShockAPI.DB
|
|||
|
||||
public PlayerData GetPlayerData(TSPlayer player, int acctid)
|
||||
{
|
||||
PlayerData playerData = new PlayerData(player);
|
||||
PlayerData playerData = new PlayerData(true);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ namespace TShockAPI.DB
|
|||
if (creator.EnsureTableStructure(table))
|
||||
{
|
||||
// Add default groups if they don't exist
|
||||
AddDefaultGroup("guest", "",
|
||||
AddDefaultGroup(TShock.Config.Settings.DefaultGuestGroupName, "",
|
||||
string.Join(",",
|
||||
Permissions.canbuild,
|
||||
Permissions.canregister,
|
||||
|
|
@ -68,12 +68,14 @@ namespace TShockAPI.DB
|
|||
Permissions.synclocalarea,
|
||||
Permissions.sendemoji));
|
||||
|
||||
AddDefaultGroup("default", "guest",
|
||||
AddDefaultGroup(TShock.Config.Settings.DefaultRegistrationGroupName, TShock.Config.Settings.DefaultGuestGroupName,
|
||||
string.Join(",",
|
||||
Permissions.warp,
|
||||
Permissions.canchangepassword,
|
||||
Permissions.canlogout,
|
||||
Permissions.summonboss,
|
||||
Permissions.spawnpets,
|
||||
Permissions.worldupgrades,
|
||||
Permissions.whisper,
|
||||
Permissions.wormhole,
|
||||
Permissions.canpaint,
|
||||
|
|
@ -82,7 +84,7 @@ namespace TShockAPI.DB
|
|||
Permissions.magicconch,
|
||||
Permissions.demonconch));
|
||||
|
||||
AddDefaultGroup("vip", "default",
|
||||
AddDefaultGroup("vip", TShock.Config.Settings.DefaultRegistrationGroupName,
|
||||
string.Join(",",
|
||||
Permissions.reservedslot,
|
||||
Permissions.renamenpc,
|
||||
|
|
|
|||
|
|
@ -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,7 +2249,7 @@ namespace TShockAPI
|
|||
|
||||
var args = new SyncTilePickingEventArgs
|
||||
{
|
||||
Player = player,
|
||||
Player = player,
|
||||
PlayerIndex = playerIndex,
|
||||
TileX = tileX,
|
||||
TileY = tileY,
|
||||
|
|
@ -2618,18 +2619,19 @@ namespace TShockAPI
|
|||
|
||||
private static bool HandleConnecting(GetDataHandlerArgs args)
|
||||
{
|
||||
var account = TShock.UserAccounts.GetUserAccountByName(args.Player.Name);//
|
||||
args.Player.DataWhenJoined = new PlayerData(args.Player);
|
||||
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(args.Player);
|
||||
args.Player.PlayerData = new PlayerData(false);
|
||||
args.Player.PlayerData.CopyCharacter(args.Player);
|
||||
|
||||
if (account != null && !TShock.Config.Settings.DisableUUIDLogin)
|
||||
{
|
||||
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);
|
||||
|
|
@ -2687,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;
|
||||
}
|
||||
|
|
@ -2719,52 +2722,75 @@ 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();
|
||||
|
||||
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))
|
||||
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;
|
||||
}
|
||||
|
||||
// spawn the player before teleporting
|
||||
NetMessage.SendData((int)PacketTypes.PlayerSpawn, -1, args.Player.Index, null, args.Player.Index, (int)PlayerSpawnContext.ReviveFromDeath);
|
||||
|
||||
// the player has not changed his spawnpoint yet, so we assert the server-saved spawnpoint
|
||||
// by teleporting the player instead of letting the game use the client's incorrect spawnpoint.
|
||||
TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleSpawn force ssc teleport for {0} at ({1},{2})", args.Player.Name, args.TPlayer.SpawnX, args.TPlayer.SpawnY));
|
||||
args.Player.TeleportSpawnpoint();
|
||||
|
||||
args.TPlayer.respawnTimer = respawnTimer;
|
||||
args.TPlayer.numberOfDeathsPVE = numberOfDeathsPVE;
|
||||
args.TPlayer.numberOfDeathsPVP = numberOfDeathsPVP;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (respawnTimer > 0)
|
||||
args.Player.Dead = true;
|
||||
else
|
||||
args.Player.Dead = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -2945,6 +2971,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();
|
||||
|
|
@ -3218,8 +3251,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);
|
||||
|
|
@ -3266,8 +3300,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;
|
||||
}
|
||||
|
|
@ -3435,9 +3471,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;
|
||||
}
|
||||
|
||||
|
|
@ -3465,15 +3501,23 @@ namespace TShockAPI
|
|||
if (OnNPCSpecial(args.Player, args.Data, id, type))
|
||||
return true;
|
||||
|
||||
if (type == 1 && TShock.Config.Settings.DisableDungeonGuardian)
|
||||
if (type == 1)
|
||||
{
|
||||
TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleSpecial rejected type 1 for {0}", args.Player.Name));
|
||||
args.Player.SendMessage(GetString("The Dungeon Guardian returned you to your spawn point."), Color.Purple);
|
||||
args.Player.Spawn(PlayerSpawnContext.RecallFromItem);
|
||||
return true;
|
||||
}
|
||||
if (!args.Player.HasPermission(Permissions.summonboss))
|
||||
{
|
||||
args.Player.SendErrorMessage(GetString("You do not have permission to summon the Skeletron."));
|
||||
TShock.Log.ConsoleDebug(GetString($"GetDataHandlers / HandleNpcStrike rejected Skeletron summon from {args.Player.Name}"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == 3)
|
||||
return false;
|
||||
}
|
||||
else if (type == 2)
|
||||
{
|
||||
// Plays SoundID.Item1
|
||||
return false;
|
||||
}
|
||||
else if (type == 3)
|
||||
{
|
||||
if (!args.Player.HasPermission(Permissions.usesundial))
|
||||
{
|
||||
|
|
@ -3493,6 +3537,42 @@ namespace TShockAPI
|
|||
return true;
|
||||
}
|
||||
}
|
||||
else if (type == 4)
|
||||
{
|
||||
// Big Mimic Spawn Smoke
|
||||
return false;
|
||||
}
|
||||
else if (type == 5)
|
||||
{
|
||||
// Register Kill for Torch God in Bestiary
|
||||
return false;
|
||||
}
|
||||
else if (type == 6)
|
||||
{
|
||||
if (!args.Player.HasPermission(Permissions.usemoondial))
|
||||
{
|
||||
TShock.Log.ConsoleDebug(GetString($"GetDataHandlers / HandleSpecial rejected enchanted moondial permission {args.Player.Name}"));
|
||||
args.Player.SendErrorMessage(GetString("You do not have permission to use the Enchanted Moondial."));
|
||||
return true;
|
||||
}
|
||||
else if (TShock.Config.Settings.ForceTime != "normal")
|
||||
{
|
||||
TShock.Log.ConsoleDebug(GetString($"GetDataHandlers / HandleSpecial rejected enchanted moondial permission (ForceTime) {args.Player.Name}"));
|
||||
if (!args.Player.HasPermission(Permissions.cfgreload))
|
||||
{
|
||||
args.Player.SendErrorMessage(GetString("You cannot use the Enchanted Moondial because time is stopped."));
|
||||
}
|
||||
else
|
||||
args.Player.SendErrorMessage(GetString("You must set ForceTime to normal via config to use the Enchanted Moondial."));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (!args.Player.HasPermission($"tshock.specialeffects.{type}"))
|
||||
{
|
||||
args.Player.SendErrorMessage(GetString("You do not have permission to use this effect."));
|
||||
TShock.Log.ConsoleError(GetString("Unrecognized special effect (Packet 51). Please report this to the TShock developers."));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3542,8 +3622,9 @@ namespace TShockAPI
|
|||
return false;
|
||||
}
|
||||
|
||||
private static readonly int[] invasions = { -1, -2, -3, -4, -5, -6, -7, -8, -10, -11 };
|
||||
private static readonly int[] invasions = { -1, -2, -3, -4, -5, -6, -7, -8, -10 };
|
||||
private static readonly int[] pets = { -12, -13, -14, -15 };
|
||||
private static readonly int[] upgrades = { -11, -17, -18 };
|
||||
private static bool HandleSpawnBoss(GetDataHandlerArgs args)
|
||||
{
|
||||
if (args.Player.IsBouncerThrottled())
|
||||
|
|
@ -3555,8 +3636,8 @@ namespace TShockAPI
|
|||
var plr = args.Data.ReadInt16();
|
||||
var thingType = args.Data.ReadInt16();
|
||||
|
||||
var isKnownBoss = thingType > 0 && thingType < Terraria.ID.NPCID.Count && NPCID.Sets.MPAllowedEnemies[thingType];
|
||||
if ((isKnownBoss || thingType == -16) && !args.Player.HasPermission(Permissions.summonboss))
|
||||
var isKnownBoss = (thingType > 0 && thingType < Terraria.ID.NPCID.Count && NPCID.Sets.MPAllowedEnemies[thingType]) || thingType == -16;
|
||||
if (isKnownBoss && !args.Player.HasPermission(Permissions.summonboss))
|
||||
{
|
||||
TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleSpawnBoss rejected boss {0} {1}", args.Player.Name, thingType));
|
||||
args.Player.SendErrorMessage(GetString("You do not have permission to summon bosses."));
|
||||
|
|
@ -3577,6 +3658,13 @@ namespace TShockAPI
|
|||
return true;
|
||||
}
|
||||
|
||||
if (upgrades.Contains(thingType) && !args.Player.HasPermission(Permissions.worldupgrades))
|
||||
{
|
||||
TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleSpawnBoss rejected upgrade {0} {1}", args.Player.Name, thingType));
|
||||
args.Player.SendErrorMessage(GetString("You do not have permission to use permanent boosters."));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (plr != args.Player.Index)
|
||||
return true;
|
||||
|
||||
|
|
@ -3642,10 +3730,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 (thingType < 0 || isKnownBoss)
|
||||
{
|
||||
if (TShock.Config.Settings.AnonymousBossInvasions)
|
||||
TShock.Utils.SendLogs(thing, Color.PaleVioletRed, args.Player);
|
||||
else
|
||||
TShock.Utils.Broadcast(thing, 175, 75, 255);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -78,6 +78,13 @@ namespace TShockAPI.Handlers
|
|||
Removal,
|
||||
}
|
||||
|
||||
public enum MatchResult
|
||||
{
|
||||
NotMatched,
|
||||
RejectChanges,
|
||||
BroadcastChanges,
|
||||
}
|
||||
|
||||
private readonly int Width;
|
||||
private readonly int Height;
|
||||
|
||||
|
|
@ -179,11 +186,11 @@ namespace TShockAPI.Handlers
|
|||
/// <param name="player">The player the operation originates from.</param>
|
||||
/// <param name="rect">The tile rectangle of the operation.</param>
|
||||
/// <returns><see langword="true"/>, if the rect matches this operation and the changes have been applied, otherwise <see langword="false"/>.</returns>
|
||||
public bool Matches(TSPlayer player, TileRect rect)
|
||||
public MatchResult Matches(TSPlayer player, TileRect rect)
|
||||
{
|
||||
if (rect.Width != Width || rect.Height != Height)
|
||||
{
|
||||
return false;
|
||||
return MatchResult.NotMatched;
|
||||
}
|
||||
|
||||
for (int x = 0; x < rect.Width; x++)
|
||||
|
|
@ -195,7 +202,7 @@ namespace TShockAPI.Handlers
|
|||
{
|
||||
if (tile.Type != TileType)
|
||||
{
|
||||
return false;
|
||||
return MatchResult.NotMatched;
|
||||
}
|
||||
}
|
||||
if (Type is MatchType.Placement or MatchType.StateChange)
|
||||
|
|
@ -204,7 +211,7 @@ namespace TShockAPI.Handlers
|
|||
{
|
||||
if (tile.FrameX < 0 || tile.FrameX > MaxFrameX || tile.FrameX % FrameXStep != 0)
|
||||
{
|
||||
return false;
|
||||
return MatchResult.NotMatched;
|
||||
}
|
||||
}
|
||||
if (MaxFrameY != IGNORE_FRAME)
|
||||
|
|
@ -214,7 +221,7 @@ namespace TShockAPI.Handlers
|
|||
// this is the only tile type sent in a tile rect where the frame have a different pattern (56, 74, 92 instead of 54, 72, 90)
|
||||
if (!(TileType == TileID.LunarMonolith && tile.FrameY % FrameYStep == 2))
|
||||
{
|
||||
return false;
|
||||
return MatchResult.NotMatched;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -223,7 +230,7 @@ namespace TShockAPI.Handlers
|
|||
{
|
||||
if (tile.Active)
|
||||
{
|
||||
return false;
|
||||
return MatchResult.NotMatched;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -236,7 +243,7 @@ namespace TShockAPI.Handlers
|
|||
if (!player.HasBuildPermission(x, y))
|
||||
{
|
||||
// for simplicity, let's pretend that the edit was valid, but do not execute it
|
||||
return true;
|
||||
return MatchResult.RejectChanges;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -257,18 +264,18 @@ namespace TShockAPI.Handlers
|
|||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return MatchResult.NotMatched;
|
||||
}
|
||||
|
||||
private bool MatchPlacement(TSPlayer player, TileRect rect)
|
||||
private MatchResult MatchPlacement(TSPlayer player, TileRect rect)
|
||||
{
|
||||
for (int x = rect.X; x < rect.Y + rect.Width; x++)
|
||||
for (int x = rect.X; x < rect.X + rect.Width; x++)
|
||||
{
|
||||
for (int y = rect.Y; y < rect.Y + rect.Height; y++)
|
||||
{
|
||||
if (Main.tile[x, y].active()) // the client will kill tiles that auto break before placing the object
|
||||
{
|
||||
return false;
|
||||
return MatchResult.NotMatched;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -277,7 +284,7 @@ namespace TShockAPI.Handlers
|
|||
if (TShock.TileBans.TileIsBanned((short)TileType, player))
|
||||
{
|
||||
// for simplicity, let's pretend that the edit was valid, but do not execute it
|
||||
return true;
|
||||
return MatchResult.RejectChanges;
|
||||
}
|
||||
|
||||
for (int x = 0; x < rect.Width; x++)
|
||||
|
|
@ -291,18 +298,18 @@ namespace TShockAPI.Handlers
|
|||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return MatchResult.BroadcastChanges;
|
||||
}
|
||||
|
||||
private bool MatchStateChange(TSPlayer player, TileRect rect)
|
||||
private MatchResult MatchStateChange(TSPlayer player, TileRect rect)
|
||||
{
|
||||
for (int x = rect.X; x < rect.Y + rect.Width; x++)
|
||||
for (int x = rect.X; x < rect.X + rect.Width; x++)
|
||||
{
|
||||
for (int y = rect.Y; y < rect.Y + rect.Height; y++)
|
||||
{
|
||||
if (!Main.tile[x, y].active() || Main.tile[x, y].type != TileType)
|
||||
{
|
||||
return false;
|
||||
return MatchResult.NotMatched;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -322,18 +329,18 @@ namespace TShockAPI.Handlers
|
|||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return MatchResult.BroadcastChanges;
|
||||
}
|
||||
|
||||
private bool MatchRemoval(TSPlayer player, TileRect rect)
|
||||
private MatchResult MatchRemoval(TSPlayer player, TileRect rect)
|
||||
{
|
||||
for (int x = rect.X; x < rect.Y + rect.Width; x++)
|
||||
for (int x = rect.X; x < rect.X + rect.Width; x++)
|
||||
{
|
||||
for (int y = rect.Y; y < rect.Y + rect.Height; y++)
|
||||
{
|
||||
if (!Main.tile[x, y].active() || Main.tile[x, y].type != TileType)
|
||||
{
|
||||
return false;
|
||||
return MatchResult.NotMatched;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -348,7 +355,7 @@ namespace TShockAPI.Handlers
|
|||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return MatchResult.BroadcastChanges;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -364,7 +371,7 @@ namespace TShockAPI.Handlers
|
|||
TileRectMatch.Placement(2, 3, TileID.TargetDummy, 54, 36, 18, 18),
|
||||
TileRectMatch.Placement(3, 4, TileID.TeleportationPylon, 468, 54, 18, 18),
|
||||
TileRectMatch.Placement(2, 3, TileID.DisplayDoll, 126, 36, 18, 18),
|
||||
TileRectMatch.Placement(2, 3, TileID.HatRack, 90, 54, 18, 18),
|
||||
TileRectMatch.Placement(3, 4, TileID.HatRack, 90, 54, 18, 18),
|
||||
TileRectMatch.Placement(2, 2, TileID.ItemFrame, 162, 18, 18, 18),
|
||||
TileRectMatch.Placement(3, 3, TileID.WeaponsRack2, 90, 36, 18, 18),
|
||||
TileRectMatch.Placement(1, 1, TileID.FoodPlatter, 18, 0, 18, 18),
|
||||
|
|
@ -436,7 +443,7 @@ namespace TShockAPI.Handlers
|
|||
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from throttle from {args.Player.Name}"));
|
||||
|
||||
// send correcting data
|
||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width);
|
||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -446,7 +453,7 @@ namespace TShockAPI.Handlers
|
|||
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from being disabled from {args.Player.Name}"));
|
||||
|
||||
// send correcting data
|
||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width);
|
||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -468,7 +475,7 @@ namespace TShockAPI.Handlers
|
|||
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}"));
|
||||
|
||||
// send correcting data
|
||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width);
|
||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -478,7 +485,7 @@ namespace TShockAPI.Handlers
|
|||
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from out of range from {args.Player.Name}"));
|
||||
|
||||
// send correcting data
|
||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width);
|
||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -488,19 +495,23 @@ namespace TShockAPI.Handlers
|
|||
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}"));
|
||||
|
||||
// send correcting data
|
||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width);
|
||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length);
|
||||
return;
|
||||
}
|
||||
|
||||
// check if the rect matches any valid operation
|
||||
foreach (TileRectMatch match in Matches)
|
||||
{
|
||||
if (match.Matches(args.Player, rect))
|
||||
var result = match.Matches(args.Player, rect);
|
||||
if (result != TileRectMatch.MatchResult.NotMatched)
|
||||
{
|
||||
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}"));
|
||||
|
||||
// send correcting data
|
||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width);
|
||||
if (result == TileRectMatch.MatchResult.RejectChanges)
|
||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length);
|
||||
if (result == TileRectMatch.MatchResult.BroadcastChanges)
|
||||
TSPlayer.All.SendTileRect(args.TileX, args.TileY, args.Width, args.Length);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -511,14 +522,14 @@ namespace TShockAPI.Handlers
|
|||
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect reimplemented from {args.Player.Name}"));
|
||||
|
||||
// send correcting data
|
||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width);
|
||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length);
|
||||
return;
|
||||
}
|
||||
|
||||
TShock.Log.ConsoleDebug(GetString($"Bouncer / SendTileRect rejected from matches from {args.Player.Name}"));
|
||||
|
||||
// send correcting data
|
||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Length, args.Width);
|
||||
args.Player.SendTileRect(args.TileX, args.TileY, args.Width, args.Length);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -910,7 +921,7 @@ namespace TShockAPI.Handlers
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* This is a copy of the `WorldGen.Convert` method with the following precise changes:
|
||||
* - Added a `MockTile tile` parameter
|
||||
* - Changed the `i` and `j` parameters to `k` and `l`
|
||||
|
|
@ -921,7 +932,7 @@ namespace TShockAPI.Handlers
|
|||
* - Removed the ifs checking the bounds of the tile and wall types
|
||||
* - Removed branches that would call `WorldGen.KillTile`
|
||||
* - Changed branches depending on randomness to instead set the property to both values after one another
|
||||
*
|
||||
*
|
||||
* This overall leads to a method that can be called on a MockTile and real-world coordinates and will spit out the proper conversion changes into the MockTile.
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -331,6 +331,9 @@ namespace TShockAPI
|
|||
[Description("Player can use the Enchanted Sundial item.")]
|
||||
public static readonly string usesundial = "tshock.world.time.usesundial";
|
||||
|
||||
[Description("Player can use the Enchanted Moondial item.")]
|
||||
public static readonly string usemoondial = "tshock.world.time.usemoondial";
|
||||
|
||||
[Description("User can grow plants.")]
|
||||
public static readonly string grow = "tshock.world.grow";
|
||||
|
||||
|
|
@ -343,9 +346,13 @@ namespace TShockAPI
|
|||
[Description("User can change the homes of NPCs.")]
|
||||
public static readonly string movenpc = "tshock.world.movenpc";
|
||||
|
||||
[Obsolete("Feature no longer available.")]
|
||||
[Description("User can convert hallow into corruption and vice-versa.")]
|
||||
public static readonly string converthardmode = "tshock.world.converthardmode";
|
||||
|
||||
[Description("User can use world-based permanent boosters like Advanced Combat Techniques")]
|
||||
public static readonly string worldupgrades = "tshock.world.worldupgrades";
|
||||
|
||||
[Description("User can force the server to Halloween mode.")]
|
||||
public static readonly string halloween = "tshock.world.sethalloween";
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ using TShockAPI.Net;
|
|||
using Timer = System.Timers.Timer;
|
||||
using System.Linq;
|
||||
using Terraria.GameContent.Creative;
|
||||
|
||||
namespace TShockAPI
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -62,6 +61,49 @@ namespace TShockAPI
|
|||
WriteToLogAndConsole
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An enum based on the current client's connection state to the server.
|
||||
/// </summary>
|
||||
public enum ConnectionState : int
|
||||
{
|
||||
/// <summary>
|
||||
/// The server is password protected and the connection is pending until a password is sent by the client.
|
||||
/// </summary>
|
||||
AwaitingPassword = -1,
|
||||
/// <summary>
|
||||
/// The connection has been established, and the client must verify its version.
|
||||
/// </summary>
|
||||
AwaitingVersionCheck = 0,
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
AssigningPlayerSlot = 1,
|
||||
/// <summary>
|
||||
/// The player slot has been received by the client, and the server is now waiting for the player information.
|
||||
/// </summary>
|
||||
AwaitingPlayerInfo = 2,
|
||||
/// <summary>
|
||||
/// Player information has been received, and the client is requesting world data.
|
||||
/// </summary>
|
||||
RequestingWorldData = 3,
|
||||
/// <summary>
|
||||
/// The world data is being sent to the client.
|
||||
/// </summary>
|
||||
ReceivingWorldData = 4,
|
||||
/// <summary>
|
||||
/// The world data has been received, and the client is now finalizing the load.
|
||||
/// </summary>
|
||||
FinalizingWorldLoad = 5,
|
||||
/// <summary>
|
||||
/// The client is requesting tile data.
|
||||
/// </summary>
|
||||
RequestingTileData = 6,
|
||||
/// <summary>
|
||||
/// The connection process is complete (The player has spawned), and the client has fully joined the game.
|
||||
/// </summary>
|
||||
Complete = 10
|
||||
}
|
||||
|
||||
public class TSPlayer
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -177,8 +219,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.
|
||||
|
|
@ -1287,7 +1334,7 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
|
||||
PlayerData = new PlayerData(this);
|
||||
PlayerData = new PlayerData();
|
||||
Group = TShock.Groups.GetGroupByName(TShock.Config.Settings.DefaultGuestGroupName);
|
||||
tempGroup = null;
|
||||
if (tempGroupTimer != null)
|
||||
|
|
@ -1324,6 +1371,9 @@ namespace TShockAPI
|
|||
FakePlayer = new Player { name = playerName, whoAmI = -1 };
|
||||
Group = Group.DefaultGroup;
|
||||
AwaitingResponse = new Dictionary<string, Action<object>>();
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1383,6 +1433,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>
|
||||
|
|
@ -1397,14 +1466,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>
|
||||
|
|
@ -2102,6 +2164,27 @@ namespace TShockAPI
|
|||
SendData(PacketTypes.PlayerAddBuff, number: Index, number2: type, number3: time);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The list of necessary packets to make sure gets through to the player upon connection (before they finish the handshake).
|
||||
/// </summary>
|
||||
private static readonly HashSet<PacketTypes> HandshakeNecessaryPackets = new()
|
||||
{
|
||||
PacketTypes.ContinueConnecting,
|
||||
PacketTypes.WorldInfo,
|
||||
PacketTypes.Status,
|
||||
PacketTypes.Disconnect,
|
||||
PacketTypes.TileFrameSection,
|
||||
PacketTypes.TileSendSection,
|
||||
PacketTypes.PlayerSpawnSelf
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Determines if an outgoing packet is necessary to send to a player before they have finished the connection handshake.
|
||||
/// </summary>
|
||||
/// <param name="msgType">The packet type to check against the necessary list.</param>
|
||||
/// <returns>Whether the packet is necessary for connection or not</returns>
|
||||
private bool NecessaryPacket(PacketTypes msgType) => HandshakeNecessaryPackets.Contains(msgType);
|
||||
|
||||
//Todo: Separate this into a few functions. SendTo, SendToAll, etc
|
||||
/// <summary>
|
||||
/// Sends data to the player.
|
||||
|
|
@ -2119,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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = "East";
|
||||
public static readonly string VersionCodename = "Hopefully SSC works somewhat correctly now edition";
|
||||
|
||||
/// <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";
|
||||
|
|
@ -1172,16 +1172,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)
|
||||
|
|
@ -1430,7 +1430,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));
|
||||
|
||||
|
|
@ -1451,7 +1451,6 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
tsplr.FinishedHandshake = false;
|
||||
|
||||
// Fire the OnPlayerLogout hook too, if the player was logged in and they have a TSPlayer object.
|
||||
|
|
@ -1460,8 +1459,8 @@ namespace TShockAPI
|
|||
Hooks.PlayerHooks.OnPlayerLogout(tsplr);
|
||||
}
|
||||
|
||||
// The last player will leave after this hook is executed.
|
||||
if (Utils.GetActivePlayerCount() == 1)
|
||||
// If this is the last player online, update the console title and save the world if needed
|
||||
if (Utils.GetActivePlayerCount() == 0)
|
||||
{
|
||||
if (Config.Settings.SaveWorldOnLastPlayerExit)
|
||||
SaveManager.Instance.SaveWorld();
|
||||
|
|
@ -1485,6 +1484,7 @@ namespace TShockAPI
|
|||
|
||||
if (!tsplr.FinishedHandshake)
|
||||
{
|
||||
tsplr.Kick(GetString("Your client didn't send the right connection information."), true);
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
|
@ -1668,7 +1668,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;
|
||||
|
|
|
|||
|
|
@ -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.2.2</Version>
|
||||
<Version>5.2.4</Version>
|
||||
<AssemblyTitle>TShock for Terraria</AssemblyTitle>
|
||||
<Company>Pryaxis & TShock Contributors</Company>
|
||||
<Product>TShockAPI</Product>
|
||||
<Copyright>Copyright © Pryaxis & TShock Contributors 2011-2023</Copyright>
|
||||
<Copyright>Copyright © Pryaxis & TShock Contributors 2011-2025</Copyright>
|
||||
<!-- extras for nuget -->
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<PackageLicenseExpression>GPL-3.0-or-later</PackageLicenseExpression>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue