Fix Terra Blade triggered MaxProjDamage, other net changes (#2852)

* update

* update

* update

* Update changelog.md

* Update changelog.md

Fixed syntax errors.

* Update SpawnMsg.cs

Use insteadUpperCamelCase

* Update changelog.md

* Update TSPlayer.cs

* Update TSPlayer.cs

Missing letters due to input method problems.

* Update `docs/changelog.md`

Co-authored-by: James Puleo <james@jame.xyz>
This commit is contained in:
ATFGK 2022-12-06 13:45:37 +08:00 committed by GitHub
parent 825ff2ad9e
commit 95d157fd5d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 91 additions and 27 deletions

View file

@ -856,6 +856,14 @@ namespace TShockAPI
/// </summary> /// </summary>
public int RespawnTimer { get; set; } public int RespawnTimer { get; set; }
/// <summary> /// <summary>
/// Number Of Deaths PVE
/// </summary>
public int NumberOfDeathsPVE { get; set; }
/// <summary>
/// Number Of Deaths PVP
/// </summary>
public int NumberOfDeathsPVP { get; set; }
/// <summary>
/// Context of where the player is spawning from. /// Context of where the player is spawning from.
/// </summary> /// </summary>
public PlayerSpawnContext SpawnContext { get; set; } public PlayerSpawnContext SpawnContext { get; set; }
@ -864,7 +872,7 @@ namespace TShockAPI
/// PlayerSpawn - When a player spawns /// PlayerSpawn - When a player spawns
/// </summary> /// </summary>
public static HandlerList<SpawnEventArgs> PlayerSpawn = new HandlerList<SpawnEventArgs>(); public static HandlerList<SpawnEventArgs> PlayerSpawn = new HandlerList<SpawnEventArgs>();
private static bool OnPlayerSpawn(TSPlayer player, MemoryStream data, byte pid, int spawnX, int spawnY, int respawnTimer, PlayerSpawnContext spawnContext) private static bool OnPlayerSpawn(TSPlayer player, MemoryStream data, byte pid, int spawnX, int spawnY, int respawnTimer, int numberOfDeathsPVE, int numberOfDeathsPVP, PlayerSpawnContext spawnContext)
{ {
if (PlayerSpawn == null) if (PlayerSpawn == null)
return false; return false;
@ -877,6 +885,8 @@ namespace TShockAPI
SpawnX = spawnX, SpawnX = spawnX,
SpawnY = spawnY, SpawnY = spawnY,
RespawnTimer = respawnTimer, RespawnTimer = respawnTimer,
NumberOfDeathsPVE = numberOfDeathsPVE,
NumberOfDeathsPVP = numberOfDeathsPVP,
SpawnContext = spawnContext SpawnContext = spawnContext
}; };
PlayerSpawn.Invoke(null, args); PlayerSpawn.Invoke(null, args);
@ -1033,12 +1043,16 @@ namespace TShockAPI
/// 0 = Old One's Army, 1 = Granite, 2 = Marble, 3 = Hive, 4 = Gem Cave, 5 = Lihzhard Temple, 6 = Graveyard /// 0 = Old One's Army, 1 = Granite, 2 = Marble, 3 = Hive, 4 = Gem Cave, 5 = Lihzhard Temple, 6 = Graveyard
/// </summary> /// </summary>
public BitsByte Zone4 { get; set; } public BitsByte Zone4 { get; set; }
/// <summary>
/// 0 = The Aether
/// </summary>
public BitsByte Zone5 { get; set; }
} }
/// <summary> /// <summary>
/// PlayerZone - When the player sends it's zone/biome information to the server /// PlayerZone - When the player sends it's zone/biome information to the server
/// </summary> /// </summary>
public static HandlerList<PlayerZoneEventArgs> PlayerZone = new HandlerList<PlayerZoneEventArgs>(); public static HandlerList<PlayerZoneEventArgs> PlayerZone = new HandlerList<PlayerZoneEventArgs>();
private static bool OnPlayerZone(TSPlayer player, MemoryStream data, byte plr, BitsByte zone1, BitsByte zone2, BitsByte zone3, BitsByte zone4) private static bool OnPlayerZone(TSPlayer player, MemoryStream data, byte plr, BitsByte zone1, BitsByte zone2, BitsByte zone3, BitsByte zone4, BitsByte zone5)
{ {
if (PlayerZone == null) if (PlayerZone == null)
return false; return false;
@ -1051,7 +1065,8 @@ namespace TShockAPI
Zone1 = zone1, Zone1 = zone1,
Zone2 = zone2, Zone2 = zone2,
Zone3 = zone3, Zone3 = zone3,
Zone4 = zone4 Zone4 = zone4,
Zone5 = zone5
}; };
PlayerZone.Invoke(null, args); PlayerZone.Invoke(null, args);
return args.Handled; return args.Handled;
@ -1519,12 +1534,16 @@ namespace TShockAPI
/// Type /// Type
/// </summary> /// </summary>
public byte type { get; set; } public byte type { get; set; }
/// <summary>
/// Paint Coat Tile
/// </summary>
public byte coatTile { get; set; }
} }
/// <summary> /// <summary>
/// NPCStrike - Called when an NPC is attacked /// NPCStrike - Called when an NPC is attacked
/// </summary> /// </summary>
public static HandlerList<PaintTileEventArgs> PaintTile = new HandlerList<PaintTileEventArgs>(); public static HandlerList<PaintTileEventArgs> PaintTile = new HandlerList<PaintTileEventArgs>();
private static bool OnPaintTile(TSPlayer player, MemoryStream data, Int32 x, Int32 y, byte t) private static bool OnPaintTile(TSPlayer player, MemoryStream data, Int32 x, Int32 y, byte t, byte ct)
{ {
if (PaintTile == null) if (PaintTile == null)
return false; return false;
@ -1535,7 +1554,8 @@ namespace TShockAPI
Data = data, Data = data,
X = x, X = x,
Y = y, Y = y,
type = t type = t,
coatTile = ct
}; };
PaintTile.Invoke(null, args); PaintTile.Invoke(null, args);
return args.Handled; return args.Handled;
@ -1558,12 +1578,16 @@ namespace TShockAPI
/// Type /// Type
/// </summary> /// </summary>
public byte type { get; set; } public byte type { get; set; }
/// <summary>
/// Paint Coat Wall
/// </summary>
public byte coatWall { get; set; }
} }
/// <summary> /// <summary>
/// Called When a wall is painted /// Called When a wall is painted
/// </summary> /// </summary>
public static HandlerList<PaintWallEventArgs> PaintWall = new HandlerList<PaintWallEventArgs>(); public static HandlerList<PaintWallEventArgs> PaintWall = new HandlerList<PaintWallEventArgs>();
private static bool OnPaintWall(TSPlayer player, MemoryStream data, Int32 x, Int32 y, byte t) private static bool OnPaintWall(TSPlayer player, MemoryStream data, Int32 x, Int32 y, byte t, byte cw)
{ {
if (PaintWall == null) if (PaintWall == null)
return false; return false;
@ -1574,7 +1598,8 @@ namespace TShockAPI
Data = data, Data = data,
X = x, X = x,
Y = y, Y = y,
type = t type = t,
coatWall = cw
}; };
PaintWall.Invoke(null, args); PaintWall.Invoke(null, args);
return args.Handled; return args.Handled;
@ -1734,12 +1759,15 @@ namespace TShockAPI
/// <summary>Alternate variation of the object placed.</summary> /// <summary>Alternate variation of the object placed.</summary>
public byte Alternate { get; set; } public byte Alternate { get; set; }
/// <summary>Related to Rubblemaker.</summary>
public sbyte Random { get; set; }
/// <summary>The direction the object was placed.</summary> /// <summary>The direction the object was placed.</summary>
public bool Direction { get; set; } public bool Direction { get; set; }
} }
/// <summary>Fired when an object is placed in the world.</summary> /// <summary>Fired when an object is placed in the world.</summary>
public static HandlerList<PlaceObjectEventArgs> PlaceObject = new HandlerList<PlaceObjectEventArgs>(); public static HandlerList<PlaceObjectEventArgs> PlaceObject = new HandlerList<PlaceObjectEventArgs>();
private static bool OnPlaceObject(TSPlayer player, MemoryStream data, short x, short y, short type, short style, byte alternate, bool direction) private static bool OnPlaceObject(TSPlayer player, MemoryStream data, short x, short y, short type, short style, byte alternate, sbyte random, bool direction)
{ {
if (PlaceObject == null) if (PlaceObject == null)
return false; return false;
@ -1753,6 +1781,7 @@ namespace TShockAPI
Type = type, Type = type,
Style = style, Style = style,
Alternate = alternate, Alternate = alternate,
Random = random,
Direction = direction Direction = direction
}; };
@ -1980,6 +2009,10 @@ namespace TShockAPI
/// Is the damage critical? /// Is the damage critical?
/// </summary> /// </summary>
public bool Critical { get; set; } public bool Critical { get; set; }
/// <summary>
/// Cooldown Counter
/// </summary>
public sbyte CooldownCounter { get; set; }
/// <summary>The reason the player took damage and/or died.</summary> /// <summary>The reason the player took damage and/or died.</summary>
public PlayerDeathReason PlayerDeathReason { get; set; } public PlayerDeathReason PlayerDeathReason { get; set; }
} }
@ -1987,7 +2020,7 @@ namespace TShockAPI
/// PlayerDamage - Called when a player is damaged /// PlayerDamage - Called when a player is damaged
/// </summary> /// </summary>
public static HandlerList<PlayerDamageEventArgs> PlayerDamage = new HandlerList<PlayerDamageEventArgs>(); public static HandlerList<PlayerDamageEventArgs> PlayerDamage = new HandlerList<PlayerDamageEventArgs>();
private static bool OnPlayerDamage(TSPlayer player, MemoryStream data, byte id, byte dir, short dmg, bool pvp, bool crit, PlayerDeathReason playerDeathReason) private static bool OnPlayerDamage(TSPlayer player, MemoryStream data, byte id, byte dir, short dmg, bool pvp, bool crit, sbyte cooldownCounter, PlayerDeathReason playerDeathReason)
{ {
if (PlayerDamage == null) if (PlayerDamage == null)
return false; return false;
@ -2001,6 +2034,7 @@ namespace TShockAPI
Damage = dmg, Damage = dmg,
PVP = pvp, PVP = pvp,
Critical = crit, Critical = crit,
CooldownCounter = cooldownCounter,
PlayerDeathReason = playerDeathReason, PlayerDeathReason = playerDeathReason,
}; };
PlayerDamage.Invoke(null, args); PlayerDamage.Invoke(null, args);
@ -2687,9 +2721,11 @@ namespace TShockAPI
short spawnx = args.Data.ReadInt16(); short spawnx = args.Data.ReadInt16();
short spawny = args.Data.ReadInt16(); short spawny = args.Data.ReadInt16();
int respawnTimer = args.Data.ReadInt32(); int respawnTimer = args.Data.ReadInt32();
short numberOfDeathsPVE = args.Data.ReadInt16();
short numberOfDeathsPVP = args.Data.ReadInt16();
PlayerSpawnContext context = (PlayerSpawnContext)args.Data.ReadByte(); PlayerSpawnContext context = (PlayerSpawnContext)args.Data.ReadByte();
if (OnPlayerSpawn(args.Player, args.Data, player, spawnx, spawny, respawnTimer, context)) if (OnPlayerSpawn(args.Player, args.Data, player, spawnx, spawny, respawnTimer, numberOfDeathsPVE, numberOfDeathsPVP, context))
return true; return true;
if ((Main.ServerSideCharacter) && (spawnx == -1 && spawny == -1)) //this means they want to spawn to vanilla spawn if ((Main.ServerSideCharacter) && (spawnx == -1 && spawny == -1)) //this means they want to spawn to vanilla spawn
@ -2912,17 +2948,19 @@ namespace TShockAPI
Vector2 vel = args.Data.ReadVector2(); Vector2 vel = args.Data.ReadVector2();
byte owner = args.Data.ReadInt8(); byte owner = args.Data.ReadInt8();
short type = args.Data.ReadInt16(); short type = args.Data.ReadInt16();
NewProjectileData bits = new NewProjectileData((BitsByte)args.Data.ReadByte()); BitsByte bitsByte = (BitsByte)args.Data.ReadByte();
BitsByte bitsByte2 = (BitsByte)(bitsByte[2] ? args.Data.ReadByte() : 0);
float[] ai = new float[Projectile.maxAI]; float[] ai = new float[Projectile.maxAI];
for (int i = 0; i < Projectile.maxAI; ++i) for (int i = 0; i < Projectile.maxAI; ++i) ai[i] = 0f;
ai[i] = !bits.AI[i] ? 0.0f : args.Data.ReadSingle(); ai[0] = bitsByte[0] ? args.Data.ReadSingle() : 0f;
ushort bannerId = bits.HasBannerIdToRespondTo ? args.Data.ReadUInt16() : (ushort)0; ai[1] = bitsByte[1] ? args.Data.ReadSingle() : 0f;
short dmg = bits.HasDamage ? args.Data.ReadInt16() : (short)0; ushort bannerId = (ushort)(bitsByte[3] ? args.Data.ReadUInt16() : 0);
float knockback = bits.HasKnockback ? args.Data.ReadSingle() : 0.0f; short dmg = (short)(bitsByte[4] ? args.Data.ReadInt16() : 0);
short origDmg = bits.HasOriginalDamage ? args.Data.ReadInt16() : (short)0; float knockback = bitsByte[5] ? args.Data.ReadSingle() : 0f;
short projUUID = bits.HasUUUID ? args.Data.ReadInt16() : (short)-1; short origDmg = (short)(bitsByte[6] ? args.Data.ReadInt16() : 0);
if (projUUID >= 1000) short projUUID = (short)(bitsByte[7] ? args.Data.ReadInt16() : -1);
projUUID = -1; if (projUUID >= 1000) projUUID = -1;
ai[2] = (bitsByte2[0] ? args.Data.ReadSingle() : 0f);
var index = TShock.Utils.SearchProjectile(ident, owner); var index = TShock.Utils.SearchProjectile(ident, owner);
@ -3150,8 +3188,9 @@ namespace TShockAPI
BitsByte zone2 = args.Data.ReadInt8(); BitsByte zone2 = args.Data.ReadInt8();
BitsByte zone3 = args.Data.ReadInt8(); BitsByte zone3 = args.Data.ReadInt8();
BitsByte zone4 = args.Data.ReadInt8(); BitsByte zone4 = args.Data.ReadInt8();
BitsByte zone5 = args.Data.ReadInt8();
if (OnPlayerZone(args.Player, args.Data, plr, zone1, zone2, zone3, zone4)) if (OnPlayerZone(args.Player, args.Data, plr, zone1, zone2, zone3, zone4, zone5))
return true; return true;
return false; return false;
@ -3616,13 +3655,14 @@ namespace TShockAPI
var x = args.Data.ReadInt16(); var x = args.Data.ReadInt16();
var y = args.Data.ReadInt16(); var y = args.Data.ReadInt16();
var t = args.Data.ReadInt8(); var t = args.Data.ReadInt8();
var ct = args.Data.ReadInt8();//PaintCoatTile
if (x < 0 || y < 0 || x >= Main.maxTilesX || y >= Main.maxTilesY || t > Main.numTileColors) if (x < 0 || y < 0 || x >= Main.maxTilesX || y >= Main.maxTilesY || t > Main.numTileColors)
{ {
TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandlePaintTile rejected range check {0}", args.Player.Name)); TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandlePaintTile rejected range check {0}", args.Player.Name));
return true; return true;
} }
if (OnPaintTile(args.Player, args.Data, x, y, t)) if (OnPaintTile(args.Player, args.Data, x, y, t, ct))
{ {
return true; return true;
} }
@ -3663,13 +3703,14 @@ namespace TShockAPI
var x = args.Data.ReadInt16(); var x = args.Data.ReadInt16();
var y = args.Data.ReadInt16(); var y = args.Data.ReadInt16();
var t = args.Data.ReadInt8(); var t = args.Data.ReadInt8();
var cw = args.Data.ReadInt8();//PaintCoatWall
if (x < 0 || y < 0 || x >= Main.maxTilesX || y >= Main.maxTilesY || t > Main.numTileColors) if (x < 0 || y < 0 || x >= Main.maxTilesX || y >= Main.maxTilesY || t > Main.numTileColors)
{ {
TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandlePaintWall rejected range check {0}", args.Player.Name)); TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandlePaintWall rejected range check {0}", args.Player.Name));
return true; return true;
} }
if (OnPaintWall(args.Player, args.Data, x, y, t)) if (OnPaintWall(args.Player, args.Data, x, y, t, cw))
{ {
return true; return true;
} }
@ -3928,9 +3969,10 @@ namespace TShockAPI
short type = args.Data.ReadInt16(); short type = args.Data.ReadInt16();
short style = args.Data.ReadInt16(); short style = args.Data.ReadInt16();
byte alternate = args.Data.ReadInt8(); byte alternate = args.Data.ReadInt8();
sbyte random = (sbyte)args.Data.ReadInt8();
bool direction = args.Data.ReadBoolean(); bool direction = args.Data.ReadBoolean();
if (OnPlaceObject(args.Player, args.Data, x, y, type, style, alternate, direction)) if (OnPlaceObject(args.Player, args.Data, x, y, type, style, alternate, random, direction))
return true; return true;
return false; return false;
@ -4068,7 +4110,7 @@ namespace TShockAPI
private static bool HandleNpcTeleportPortal(GetDataHandlerArgs args) private static bool HandleNpcTeleportPortal(GetDataHandlerArgs args)
{ {
var npcIndex = args.Data.ReadByte(); var npcIndex = args.Data.ReadUInt16();
var portalColorIndex = args.Data.ReadInt16(); var portalColorIndex = args.Data.ReadInt16();
var newPosition = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle()); var newPosition = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle());
var velocity = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle()); var velocity = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle());
@ -4162,8 +4204,9 @@ namespace TShockAPI
var bits = (BitsByte)(args.Data.ReadByte()); var bits = (BitsByte)(args.Data.ReadByte());
var crit = bits[0]; var crit = bits[0];
var pvp = bits[1]; var pvp = bits[1];
var cooldownCounter = (sbyte)args.Data.ReadInt8();
if (OnPlayerDamage(args.Player, args.Data, id, direction, dmg, pvp, crit, playerDeathReason)) if (OnPlayerDamage(args.Player, args.Data, id, direction, dmg, pvp, crit, cooldownCounter, playerDeathReason))
return true; return true;
return false; return false;

View file

@ -33,6 +33,8 @@ namespace TShockAPI.Net
public short TileX { get; set; } public short TileX { get; set; }
public short TileY { get; set; } public short TileY { get; set; }
public int RespawnTimer { get; set; } public int RespawnTimer { get; set; }
public short NumberOfDeathsPVE { get; set; }
public short NumberOfDeathsPVP { get; set; }
public PlayerSpawnContext PlayerSpawnContext { get; set; } public PlayerSpawnContext PlayerSpawnContext { get; set; }
public override void Pack(Stream stream) public override void Pack(Stream stream)
@ -41,6 +43,8 @@ namespace TShockAPI.Net
stream.WriteInt16(TileX); stream.WriteInt16(TileX);
stream.WriteInt16(TileY); stream.WriteInt16(TileY);
stream.WriteInt32(RespawnTimer); stream.WriteInt32(RespawnTimer);
stream.WriteInt16(NumberOfDeathsPVE);
stream.WriteInt16(NumberOfDeathsPVP);
stream.WriteByte((byte) PlayerSpawnContext); stream.WriteByte((byte) PlayerSpawnContext);
} }
} }

View file

@ -1394,7 +1394,9 @@ namespace TShockAPI
/// <param name="tiley">The Y coordinate.</param> /// <param name="tiley">The Y coordinate.</param>
/// <param name="context">The PlayerSpawnContext.</param> /// <param name="context">The PlayerSpawnContext.</param>
/// <param name="respawnTimer">The respawn timer, will be Player.respawnTimer if parameter is null.</param> /// <param name="respawnTimer">The respawn timer, will be Player.respawnTimer if parameter is null.</param>
public void Spawn(int tilex, int tiley, PlayerSpawnContext context, int? respawnTimer = null) /// <param name="numberOfDeathsPVE">The number of deaths PVE, will be TPlayer.numberOfDeathsPVE if parameter is null.</param>
/// <param name="numberOfDeathsPVP">The number of deaths PVP, will be TPlayer.numberOfDeathsPVP if parameter is null.</param>
public void Spawn(int tilex, int tiley, PlayerSpawnContext context, int? respawnTimer = null, short? numberOfDeathsPVE = null, short? numberOfDeathsPVP = null)
{ {
using (var ms = new MemoryStream()) using (var ms = new MemoryStream())
{ {
@ -1404,6 +1406,8 @@ namespace TShockAPI
TileX = (short)tilex, TileX = (short)tilex,
TileY = (short)tiley, TileY = (short)tiley,
RespawnTimer = respawnTimer ?? TShock.Players[Index].RespawnTimer * 60, RespawnTimer = respawnTimer ?? TShock.Players[Index].RespawnTimer * 60,
NumberOfDeathsPVE = numberOfDeathsPVE ?? (short)TPlayer.numberOfDeathsPVE,
NumberOfDeathsPVP = numberOfDeathsPVP ?? (short)TPlayer.numberOfDeathsPVP,
PlayerSpawnContext = context, PlayerSpawnContext = context,
}; };
msg.PackFull(ms); msg.PackFull(ms);

View file

@ -78,6 +78,18 @@ Use past tense when adding new entries; sign your name off when you add or chang
* If there is no section called "Upcoming changes" below this line, please add one with `## Upcoming changes` as the first line, and then a bulleted item directly after with the first change. --> * If there is no section called "Upcoming changes" below this line, please add one with `## Upcoming changes` as the first line, and then a bulleted item directly after with the first change. -->
## Upcoming changes ## Upcoming changes
* Corrected and updated deserialization of the following packets (@ATFGK):
* `ProjectileNew`: Read the third `AI` value.
* Before this change, it was previously possible for the projectile damage limit to falsely trigger, such as when using the Terra Balde and Fire Gauntlet together.
* `PlayerSpawn`: Read the `NumberOfDeathsPVE` and `NumberOfDeathsPVP` values.
* Before this change, the `PlayerSpawnContext` was always read incorrectly, due to the values above being placed in the middle of the existing structure.
* `NpcTeleportPortal`: Read the NPC index as a `ushort` instead of a `byte`.
* `PlaceObject`: Read the `Random` value.
* Before this change, the `Direction` was always read incorrectly, due to the value above being placed in the middle of the existing structure.
* `Zones`: Read the `zone5` value.
* `PaintTile` and `PaintWall`: Read the `coatTile` and `coatWall` values.
* `PlayerHurtV2`: Read the `cooldownCounter` value.
* Updated `SpawnMsg` to include the `NumberOfDeathsPVE` and `NumberOfDeathsPVP`, and allow them to be optionally used in `TSPlayer.Spawn`. (@ATFGK)
* Added `WorldTileProvider` to the tshock config with values `default`, `constileation` or `heaptile`. This allows tile providers to be changed in environments where CLI args cannot be altered. See the documentation website for more info about these providers. (@SignatureBeef) * Added `WorldTileProvider` to the tshock config with values `default`, `constileation` or `heaptile`. This allows tile providers to be changed in environments where CLI args cannot be altered. See the documentation website for more info about these providers. (@SignatureBeef)
* Updated the Utils.FindByIdOrName to follow same logic. Now fuzzy match fallback to `StartsWith` and then `Contains`. (@sgkoishi) * Updated the Utils.FindByIdOrName to follow same logic. Now fuzzy match fallback to `StartsWith` and then `Contains`. (@sgkoishi)
* Added `ShadowCandle` and `BrainOfConfusionBuff` (BoC dodge buff) to the `PlayerAddBuffWhitelist` (@drunderscore) * Added `ShadowCandle` and `BrainOfConfusionBuff` (BoC dodge buff) to the `PlayerAddBuffWhitelist` (@drunderscore)
@ -89,6 +101,7 @@ Use past tense when adding new entries; sign your name off when you add or chang
* Increased whitelisted duration of the Mighty Wind (`WindPushed`) buff (from sandstorms). (@drunderscore) * Increased whitelisted duration of the Mighty Wind (`WindPushed`) buff (from sandstorms). (@drunderscore)
* Allowed the Hellfire (`OnFire3`) buff. (@drunderscore) * Allowed the Hellfire (`OnFire3`) buff. (@drunderscore)
## TShock 5.1.3 ## TShock 5.1.3
* Added support for Terraria 1.4.4.9 via OTAPI 3.1.20. (@SignatureBeef) * Added support for Terraria 1.4.4.9 via OTAPI 3.1.20. (@SignatureBeef)