Skip to content

Commit

Permalink
rewrite: bed spawning for SSC
Browse files Browse the repository at this point in the history
  • Loading branch information
PotatoCider committed Jan 27, 2025
1 parent 5e4f17b commit 1a957d8
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 58 deletions.
73 changes: 45 additions & 28 deletions TShockAPI/GetDataHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2248,7 +2248,7 @@ private static bool OnSyncTilePicking(TSPlayer player, MemoryStream data, byte p

var args = new SyncTilePickingEventArgs
{
Player = player,
Player = player,
PlayerIndex = playerIndex,
TileX = tileX,
TileY = tileY,
Expand Down Expand Up @@ -2719,49 +2719,66 @@ private static bool HandleSpawn(GetDataHandlerArgs args)
}

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))
if (OnPlayerSpawn(args.Player, args.Data, player, spawnX, spawnY, respawnTimer, numberOfDeathsPVE, numberOfDeathsPVP, context))
return true;

if ((Main.ServerSideCharacter) && (spawnx == -1 && spawny == -1)) //this means they want to spawn to vanilla spawn
{
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));
}
TShock.Log.ConsoleInfo("GetDataHandlers / HandleSpawn spawnXY ({0}, {1})", spawnX, spawnY);

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)))
if (Main.ServerSideCharacter)
{
args.Player.sX = args.TPlayer.SpawnX;
args.Player.sY = args.TPlayer.SpawnY;
// As long as the player has not changed his spawnpoint since initial connection,
// we should not let the client handle spawning the player. This is because the
// spawnpoint value is not saved in the client, and the game does not allow the
// server to edit it directly. Hence, we have to assert the correct spawnpoint value
// until we can detect that the player has changed his spawn (when the player attempts
// to respawn at a changed location). We can then safely sync the spawnpoint values
// on both the client and the server.

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)))
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));
// We convert (-1, -1) spawnpoints to main spawn to be compatible with
// Player.Teleport

// server saved spawnpoint value
args.Player.initialSpawn = true;
args.Player.initialServerSpawnX = args.TPlayer.SpawnX;
args.Player.initialServerSpawnY = args.TPlayer.SpawnY;


// initial client spawn point, do not use this to spawn the player
// we only use it to detect if the spawnpoint has changed
args.Player.initialClientSpawnX = spawnX;
args.Player.initialClientSpawnY = spawnY;

// we let the game handle completing the connection (state 3 => 10), we will spawn the player at the
// saved spawnpoint in the next second, and reassert the correct spawnpoint value
return false;
}
}

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)))
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 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;
// here, we assert the correct spawnpoint by teleporting the player instead of letting the client handle it.
args.TPlayer.respawnTimer = respawnTimer;
args.TPlayer.numberOfDeathsPVE = numberOfDeathsPVE;
args.TPlayer.numberOfDeathsPVP = numberOfDeathsPVP;
args.Player.Dead = respawnTimer > 0;

args.Player.TeleportSpawnpoint();
TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleSpawn ssc teleport for {0} at ({1},{2})", args.Player.Name, args.TPlayer.SpawnX, args.TPlayer.SpawnY));
return true;
}
return false;
}

Expand Down
14 changes: 2 additions & 12 deletions TShockAPI/PlayerData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,8 @@ public void CopyCharacter(TSPlayer player)
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;
Expand Down Expand Up @@ -266,8 +258,6 @@ public void RestoreCharacter(TSPlayer player)
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;
Expand Down
33 changes: 23 additions & 10 deletions TShockAPI/TSPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,13 @@ public static List<TSPlayer> FindByNameOrID(string search)
/// </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.
Expand Down Expand Up @@ -1380,6 +1385,21 @@ public bool Teleport(float x, float y, byte style = 1)
return true;
}

/// <summary>
/// Teleports the player to their spawnpoint. Supports SSC.
/// </summary>
public bool TeleportSpawnpoint()
{
int x = TPlayer.SpawnX;
int y = TPlayer.SpawnY;
if (x == -1 && y == -1)
{
x = Main.spawnTileX;
y = Main.spawnTileY;
}
return Teleport(x * 16, y * 16 - 48);
}

/// <summary>
/// Heals the player.
/// </summary>
Expand All @@ -1394,14 +1414,7 @@ public void Heal(int health = 600)
/// </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>
Expand Down
16 changes: 8 additions & 8 deletions TShockAPI/TShock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1182,16 +1182,16 @@ private void OnSecondUpdate()
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)
Expand Down
3 changes: 3 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ Use past tense when adding new entries; sign your name off when you add or chang
* Fixed /help, /me, and /p commands can't work in non-English languages. (@ACaiCat)
* Added a hook `AccountHooks.AccountGroupUpdate`, which is called when you change the user group. (@AgaSpace)
* * Ensured `TSPlayer.PlayerData` is non-null whilst syncing loadouts. (@drunderscore)
* Rewrote bed spawning for SSC. (@PotatoCider)
* Removed `TSPlayer.s{X,Y}` in favour of using desyncing client and server spawnpoint values (`Terraria.Player.Spawn{X,Y}`) until the player has changed their spawnpoint per session.
* Partially fixed the bed spawning bug when SSC is enabled. Players would need to spawn at their beds at least once to tell TShock that the player's spawnpoint has changed.

## TShock 5.2.1
* Updated `TSPlayer.GodMode`. (@AgaSpace)
Expand Down

0 comments on commit 1a957d8

Please sign in to comment.