Merge branch 'general-devel' into feature/grow-code-dupe

This commit is contained in:
Chris 2022-02-22 18:51:01 +10:30 committed by GitHub
commit ed12236c52
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 320 additions and 74 deletions

2
.github/FUNDING.yml vendored
View file

@ -1,2 +1,2 @@
# These are supported funding model platforms
github: [DeathCradle, hakusaro, Stealownz, QuiCM]
github: [SignatureBeef, hakusaro, Stealownz, QuiCM]

View file

@ -12,9 +12,19 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin
* Do not forget to sign every line you change with your name. (@hakusaro)
* 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
* Improved the `/grow` command to reduce code duplication, use `TileID` constants for less ambiguous types. (@drunderscore)
## TShock 4.5.13
* Added hook `GetDataHandlers.OnReleaseNpc` to handling ReleaseNPC packet and a bouncer to stops unregistered and logged out players on SSC servers from releasing critters NPC. The bouncer has additional filter to stops players who tried to release different critter using crafted packet, e.g. using bunny item to release golden bunny. (@tru321)
* Added filter in `GetDataHandlers.HandleCatchNpc` that stops unregistered and logged out players on SSC servers to catch critters. (@tru321)
* Fixed rejection check inside of `HandlePaintTile` to account for the Paint Sprayer (or Architect Gizmo Pack) being inside your inventory, rather than on an accessory slot. (@drunderscore)
* Added the lanterns night event to the `/worldevent` command. (@0x3fcf1bbd)
* Marked `TSPlayer.SendTileSquare` as deprecated, and created `TSPlayer.SendTileSquareCentered` that sends a tile square centered around the passed coordinates. (@0x3fcf1bbd)
* Added coordinates clamping to `TSPlayer.SendTileRect` so as to avoid OOBs. (@0x3fcf1bbd)
* Removed extraneous space causing build commands in README to fail. (@EtherTyper)
## TShock 4.5.12
* Fixed the ability to spawn Zenith projectile with non-original items. (@AgaSpace)
* Added hook `GetDataHandlers.OnNpcTalk` for NpcTalk and a handler for it that stops unregistered and logged out players from interacting with NPCs, preventing them from smuggling or duplicating items via NPC item slots. (@tru321)

View file

@ -207,7 +207,7 @@ You need to re-run the patcher any time `OTAPI` updates. You need to rebuild `Te
$ cd ./TerrariaServerAPI/TShock.Modifications.Bootstrapper/bin/$BUILD_MODE/
$ mono TShock.Modifications.Bootstrapper.exe -in=OTAPI.dll \
-mod=../../../TShock.Modifications.**/bin/$BUILD_MODE/TShock.Modifications.*.dll \
-mod=../../../TShock.Modifications.**/bin/$BUILD_MODE/TShock.Modifications.*.dll \
-o=Output/OTAPI.dll
1. Verify that non-zero modifications ran successfully. Then, build the Terraria Server API executable.

View file

@ -105,6 +105,7 @@ namespace TShockAPI
GetDataHandlers.NPCAddBuff += OnNPCAddBuff;
GetDataHandlers.NPCHome += OnUpdateNPCHome;
GetDataHandlers.HealOtherPlayer += OnHealOtherPlayer;
GetDataHandlers.ReleaseNPC += OnReleaseNPC;
GetDataHandlers.PlaceObject += OnPlaceObject;
GetDataHandlers.PlaceTileEntity += OnPlaceTileEntity;
GetDataHandlers.PlaceItemFrame += OnPlaceItemFrame;
@ -405,7 +406,7 @@ namespace TShockAPI
((action == EditAction.PlaceWall || action == EditAction.ReplaceWall) && editData >= Main.maxWallTypes))
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from editData out of bounds {0} {1} {2}", args.Player.Name, action, editData);
args.Player.SendTileSquare(tileX, tileY, 4);
args.Player.SendTileSquareCentered(tileX, tileY, 4);
args.Handled = true;
return;
}
@ -420,7 +421,7 @@ namespace TShockAPI
if (args.Player.Dead && TShock.Config.Settings.PreventDeadModification)
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from (pdm) {0} {1} {2}", args.Player.Name, action, editData);
args.Player.SendTileSquare(tileX, tileY, 4);
args.Player.SendTileSquareCentered(tileX, tileY, 4);
args.Handled = true;
return;
}
@ -434,7 +435,7 @@ namespace TShockAPI
if (TShock.TileBans.TileIsBanned(editData, args.Player))
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from (tb) {0} {1} {2}", args.Player.Name, action, editData);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Player.SendErrorMessage("You do not have permission to place this tile.");
args.Handled = true;
return;
@ -457,7 +458,7 @@ namespace TShockAPI
{
TShock.Log.ConsoleError("Bouncer / OnTileEdit rejected from (placestyle) {0} {1} {2} placeStyle: {3} expectedStyle: {4}",
args.Player.Name, action, editData, requestedPlaceStyle, actualItemPlaceStyle);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true;
return;
}
@ -468,7 +469,7 @@ namespace TShockAPI
{
TShock.Log.ConsoleError("Bouncer / OnTileEdit rejected from (placestyle) {0} {1} {2} placeStyle: {3} expectedStyle: {4}",
args.Player.Name, action, editData, requestedPlaceStyle, correctedPlaceStyle);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true;
return;
}
@ -483,7 +484,7 @@ namespace TShockAPI
if (Main.tileAxe[tile.type] && ((args.Player.TPlayer.mount.Type != 8 && selectedItem.axe == 0) && !ItemID.Sets.Explosives[selectedItem.netID] && args.Player.RecentFuse == 0))
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from (axe) {0} {1} {2}", args.Player.Name, action, editData);
args.Player.SendTileSquare(tileX, tileY, 4);
args.Player.SendTileSquareCentered(tileX, tileY, 4);
args.Handled = true;
return;
}
@ -491,7 +492,7 @@ namespace TShockAPI
else if (Main.tileHammer[tile.type] && ((args.Player.TPlayer.mount.Type != 8 && selectedItem.hammer == 0) && !ItemID.Sets.Explosives[selectedItem.netID] && args.Player.RecentFuse == 0))
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from (hammer) {0} {1} {2}", args.Player.Name, action, editData);
args.Player.SendTileSquare(tileX, tileY, 4);
args.Player.SendTileSquareCentered(tileX, tileY, 4);
args.Handled = true;
return;
}
@ -502,7 +503,7 @@ namespace TShockAPI
&& !Main.tileAxe[tile.type] && !Main.tileHammer[tile.type] && tile.wall == 0 && args.Player.TPlayer.mount.Type != 8 && selectedItem.pick == 0 && selectedItem.type != ItemID.GravediggerShovel && !ItemID.Sets.Explosives[selectedItem.netID] && args.Player.RecentFuse == 0)
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from (pick) {0} {1} {2}", args.Player.Name, action, editData);
args.Player.SendTileSquare(tileX, tileY, 4);
args.Player.SendTileSquareCentered(tileX, tileY, 4);
args.Handled = true;
return;
}
@ -513,7 +514,7 @@ namespace TShockAPI
if (selectedItem.hammer == 0 && !ItemID.Sets.Explosives[selectedItem.netID] && args.Player.RecentFuse == 0 && selectedItem.createWall == 0)
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from (hammer2) {0} {1} {2}", args.Player.Name, action, editData);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true;
return;
}
@ -532,7 +533,7 @@ namespace TShockAPI
!p.Killed && Math.Abs((int)(Main.projectile[p.Index].position.X / 16f) - tileX) <= Math.Abs(Main.projectile[p.Index].velocity.X)))
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from (inconceivable rope coil) {0} {1} {2} selectedItem:{3} itemCreateTile:{4}", args.Player.Name, action, editData, selectedItem.netID, selectedItem.createTile);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true;
return;
}
@ -543,7 +544,7 @@ namespace TShockAPI
requestedPlaceStyle > GetMaxPlaceStyle(editData))
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from (ms1) {0} {1} {2}", args.Player.Name, action, editData);
args.Player.SendTileSquare(tileX, tileY, 4);
args.Player.SendTileSquareCentered(tileX, tileY, 4);
args.Handled = true;
return;
}
@ -552,7 +553,7 @@ namespace TShockAPI
if (selectedItem.netID == ItemID.IceRod && editData != TileID.MagicalIceBlock)
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from using ice rod but not placing ice block {0} {1} {2}", args.Player.Name, action, editData);
args.Player.SendTileSquare(tileX, tileY, 4);
args.Player.SendTileSquareCentered(tileX, tileY, 4);
args.Handled = true;
}
/// If they aren't selecting the item which creates the tile, they're hacking.
@ -562,7 +563,7 @@ namespace TShockAPI
if (selectedItem.netID != ItemID.IceRod && selectedItem.netID != ItemID.DirtBomb && selectedItem.netID != ItemID.StickyBomb)
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from tile placement not matching selected item createTile {0} {1} {2} selectedItemID:{3} createTile:{4}", args.Player.Name, action, editData, selectedItem.netID, selectedItem.createTile);
args.Player.SendTileSquare(tileX, tileY, 4);
args.Player.SendTileSquareCentered(tileX, tileY, 4);
args.Handled = true;
return;
}
@ -571,7 +572,7 @@ namespace TShockAPI
if ((action == EditAction.PlaceWall || action == EditAction.ReplaceWall) && editData != selectedItem.createWall)
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from wall placement not matching selected item createWall {0} {1} {2} selectedItemID:{3} createWall:{4}", args.Player.Name, action, editData, selectedItem.netID, selectedItem.createWall);
args.Player.SendTileSquare(tileX, tileY, 4);
args.Player.SendTileSquareCentered(tileX, tileY, 4);
args.Handled = true;
return;
}
@ -581,7 +582,7 @@ namespace TShockAPI
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from (chestcap) {0} {1} {2}", args.Player.Name, action, editData);
args.Player.SendErrorMessage("The world's chest limit has been reached - unable to place more.");
args.Player.SendTileSquare(tileX, tileY, 3);
args.Player.SendTileSquareCentered(tileX, tileY, 3);
args.Handled = true;
return;
}
@ -599,7 +600,7 @@ namespace TShockAPI
&& selectedItem.type != ItemID.WireKite)
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from place wire from {0} {1} {2}", args.Player.Name, action, editData);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true;
return;
}
@ -613,7 +614,7 @@ namespace TShockAPI
&& selectedItem.type != ItemID.MulticolorWrench)
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from wire cutter from {0} {1} {2}", args.Player.Name, action, editData);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true;
return;
}
@ -624,7 +625,7 @@ namespace TShockAPI
if (selectedItem.type != ItemID.Actuator && !args.Player.TPlayer.autoActuator)
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from actuator/presserator from {0} {1} {2}", args.Player.Name, action, editData);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true;
return;
}
@ -634,7 +635,7 @@ namespace TShockAPI
if (action == EditAction.KillWall || action == EditAction.ReplaceWall)
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from sts allow cut from {0} {1} {2}", args.Player.Name, action, editData);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true;
return;
}
@ -645,7 +646,7 @@ namespace TShockAPI
if (args.Player.IsBeingDisabled())
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from disable from {0} {1} {2}", args.Player.Name, action, editData);
args.Player.SendTileSquare(tileX, tileY, 4);
args.Player.SendTileSquareCentered(tileX, tileY, 4);
args.Handled = true;
return;
}
@ -684,7 +685,7 @@ namespace TShockAPI
}
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from explosives/fuses from {0} {1} {2}", args.Player.Name, action, editData);
args.Player.SendTileSquare(tileX, tileY, 4);
args.Player.SendTileSquareCentered(tileX, tileY, 4);
args.Handled = true;
return;
}
@ -698,7 +699,7 @@ namespace TShockAPI
else
{
args.Player.Disable("Reached TileKill threshold.", DisableFlags.WriteToLogAndConsole);
args.Player.SendTileSquare(tileX, tileY, 4);
args.Player.SendTileSquareCentered(tileX, tileY, 4);
}
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from tile kill threshold from {0}, (value: {1})", args.Player.Name, args.Player.TileKillThreshold);
@ -716,7 +717,7 @@ namespace TShockAPI
else
{
args.Player.Disable("Reached TilePlace threshold.", DisableFlags.WriteToLogAndConsole);
args.Player.SendTileSquare(tileX, tileY, 4);
args.Player.SendTileSquareCentered(tileX, tileY, 4);
}
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from tile place threshold from {0}, (value: {1})", args.Player.Name, args.Player.TilePlaceThreshold);
@ -728,7 +729,7 @@ namespace TShockAPI
if (args.Player.IsBouncerThrottled())
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from throttled from {0} {1} {2}", args.Player.Name, action, editData);
args.Player.SendTileSquare(tileX, tileY, 4);
args.Player.SendTileSquareCentered(tileX, tileY, 4);
args.Handled = true;
return;
}
@ -762,7 +763,7 @@ namespace TShockAPI
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from weird confusing flow control from {0}", args.Player.Name);
TShock.Log.ConsoleDebug("If you're seeing this message and you know what that player did, please report it to TShock for further investigation.");
args.Player.SendTileSquare(tileX, tileY, 4);
args.Player.SendTileSquareCentered(tileX, tileY, 4);
args.Handled = true;
return;
}
@ -1317,7 +1318,7 @@ namespace TShockAPI
if (args.Player.IsBeingDisabled())
{
TShock.Log.ConsoleDebug("Bouncer / OnPlaceChest rejected from disabled from {0}", args.Player.Name);
args.Player.SendTileSquare(tileX, tileY, 3);
args.Player.SendTileSquareCentered(tileX, tileY, 3);
args.Handled = true;
return;
}
@ -1325,7 +1326,7 @@ namespace TShockAPI
if (args.Player.SelectedItem.placeStyle != style)
{
TShock.Log.ConsoleError(string.Format("Bouncer / OnPlaceChest / rejected from invalid place style from {0}", args.Player.Name));
args.Player.SendTileSquare(tileX, tileY, 3);
args.Player.SendTileSquareCentered(tileX, tileY, 3);
args.Handled = true;
return;
}
@ -1337,7 +1338,7 @@ namespace TShockAPI
&& (!TShock.Utils.HasWorldReachedMaxChests() && Main.tile[tileX, tileY].type != TileID.Dirt)) //Chest
{
TShock.Log.ConsoleDebug("Bouncer / OnPlaceChest rejected from weird check from {0}", args.Player.Name);
args.Player.SendTileSquare(tileX, tileY, 3);
args.Player.SendTileSquareCentered(tileX, tileY, 3);
args.Handled = true;
return;
}
@ -1349,7 +1350,7 @@ namespace TShockAPI
{
TShock.Log.ConsoleDebug("Bouncer / OnPlaceChest rejected from weird placement check from {0}", args.Player.Name);
//Prevent a dresser from being placed on a teleporter, as this can cause client and server crashes.
args.Player.SendTileSquare(tileX, tileY, 3);
args.Player.SendTileSquareCentered(tileX, tileY, 3);
args.Handled = true;
return;
}
@ -1358,7 +1359,7 @@ namespace TShockAPI
if (!args.Player.HasBuildPermission(tileX, tileY))
{
TShock.Log.ConsoleDebug("Bouncer / OnPlaceChest rejected from invalid permission from {0}", args.Player.Name);
args.Player.SendTileSquare(tileX, tileY, 3);
args.Player.SendTileSquareCentered(tileX, tileY, 3);
args.Handled = true;
return;
}
@ -1366,7 +1367,7 @@ namespace TShockAPI
if (!args.Player.IsInRange(tileX, tileY))
{
TShock.Log.ConsoleDebug("Bouncer / OnPlaceChest rejected from range check from {0}", args.Player.Name);
args.Player.SendTileSquare(tileX, tileY, 3);
args.Player.SendTileSquareCentered(tileX, tileY, 3);
args.Handled = true;
return;
}
@ -1451,7 +1452,7 @@ namespace TShockAPI
if (args.Player.IsBeingDisabled())
{
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected disabled from {0}", args.Player.Name);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true;
return;
}
@ -1465,7 +1466,7 @@ namespace TShockAPI
else
{
args.Player.Disable("Reached TileLiquid threshold.", DisableFlags.WriteToLogAndConsole);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Player.SendTileSquareCentered(tileX, tileY, 1);
}
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected from liquid threshold from {0} {1}/{2}", args.Player.Name, args.Player.TileLiquidThreshold, TShock.Config.Settings.TileLiquidThreshold);
@ -1538,7 +1539,7 @@ namespace TShockAPI
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected bucket check 1 from {0}", args.Player.Name);
args.Player.SendErrorMessage("You do not have permission to perform this action.");
args.Player.Disable("Spreading lava without holding a lava bucket", DisableFlags.WriteToLogAndConsole);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true;
return;
}
@ -1548,7 +1549,7 @@ namespace TShockAPI
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected lava bucket from {0}", args.Player.Name);
args.Player.SendErrorMessage("You do not have permission to perform this action.");
args.Player.Disable("Using banned lava bucket without permissions", DisableFlags.WriteToLogAndConsole);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true;
return;
}
@ -1558,7 +1559,7 @@ namespace TShockAPI
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected bucket check 2 from {0}", args.Player.Name);
args.Player.SendErrorMessage("You do not have permission to perform this action.");
args.Player.Disable("Spreading water without holding a water bucket", DisableFlags.WriteToLogAndConsole);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true;
return;
}
@ -1568,7 +1569,7 @@ namespace TShockAPI
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected bucket check 3 from {0}", args.Player.Name);
args.Player.SendErrorMessage("You do not have permission to perform this action.");
args.Player.Disable("Using banned water bucket without permissions", DisableFlags.WriteToLogAndConsole);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true;
return;
}
@ -1578,7 +1579,7 @@ namespace TShockAPI
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected bucket check 4 from {0}", args.Player.Name);
args.Player.SendErrorMessage("You do not have permission to perform this action.");
args.Player.Disable("Spreading honey without holding a honey bucket", DisableFlags.WriteToLogAndConsole);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true;
return;
}
@ -1588,7 +1589,7 @@ namespace TShockAPI
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected bucket check 5 from {0}", args.Player.Name);
args.Player.SendErrorMessage("You do not have permission to perform this action.");
args.Player.Disable("Using banned honey bucket without permissions", DisableFlags.WriteToLogAndConsole);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true;
return;
}
@ -1597,7 +1598,7 @@ namespace TShockAPI
if (!args.Player.HasBuildPermission(tileX, tileY))
{
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected build permission from {0}", args.Player.Name);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true;
return;
}
@ -1605,7 +1606,7 @@ namespace TShockAPI
if (!wasThereABombNearby && !args.Player.IsInRange(tileX, tileY, 16))
{
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected range checks from {0}", args.Player.Name);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true;
return;
}
@ -1613,7 +1614,7 @@ namespace TShockAPI
if (args.Player.IsBouncerThrottled())
{
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected throttle from {0}", args.Player.Name);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true;
return;
}
@ -1829,6 +1830,52 @@ namespace TShockAPI
return;
}
/// <summary>
/// A bouncer for checking NPC released by player
/// </summary>
/// <param name="sender">The object that triggered the event.</param>
/// <param name="args">The packet arguments that the event has.</param>
internal void OnReleaseNPC(object sender, GetDataHandlers.ReleaseNpcEventArgs args)
{
int x = args.X;
int y = args.Y;
short type = args.Type;
byte style = args.Style;
// if npc released outside allowed tile
if (x >= Main.maxTilesX * 16 - 16 || x < 0 || y >= Main.maxTilesY * 16 - 16 || y < 0)
{
TShock.Log.ConsoleDebug("Bouncer / OnReleaseNPC rejected out of bounds from {0}", args.Player.Name);
args.Handled = true;
return;
}
// if player disabled
if (args.Player.IsBeingDisabled())
{
TShock.Log.ConsoleDebug("Bouncer / OnReleaseNPC rejected npc release from {0}", args.Player.Name);
args.Handled = true;
return;
}
// if released npc not from its item (from crafted packet)
// e.g. using bunny item to release golden bunny
if (args.Player.TPlayer.lastVisualizedSelectedItem.makeNPC != type && args.Player.TPlayer.lastVisualizedSelectedItem.placeStyle != style)
{
TShock.Log.ConsoleDebug("Bouncer / OnReleaseNPC released different critter from {0}", args.Player.Name);
args.Player.Kick("Released critter was not from its item.", true);
args.Handled = true;
return;
}
if (args.Player.IsBouncerThrottled())
{
TShock.Log.ConsoleDebug("Bouncer / OnReleaseNPC rejected throttle from {0}", args.Player.Name);
args.Handled = true;
return;
}
}
/// <summary>Bouncer's PlaceObject hook reverts malicious tile placement.</summary>
/// <param name="sender">The object that triggered the event.</param>
/// <param name="args">The packet arguments that the event has.</param>
@ -1879,7 +1926,7 @@ namespace TShockAPI
if (type == TileID.FakeContainers && (style == 52 || style == 53))
{
TShock.Log.ConsoleDebug("Bouncer / OnPlaceObject rejected fake containers from {0}", args.Player.Name);
args.Player.SendTileSquare(x, y, 4);
args.Player.SendTileSquareCentered(x, y, 4);
args.Handled = true;
return;
}
@ -1888,7 +1935,7 @@ namespace TShockAPI
if (TShock.TileBans.TileIsBanned(type, args.Player))
{
TShock.Log.ConsoleDebug("Bouncer / OnPlaceObject rejected banned tiles from {0}", args.Player.Name);
args.Player.SendTileSquare(x, y, 1);
args.Player.SendTileSquareCentered(x, y, 1);
args.Player.SendErrorMessage("You do not have permission to place this tile.");
args.Handled = true;
return;
@ -1897,7 +1944,7 @@ namespace TShockAPI
if (args.Player.Dead && TShock.Config.Settings.PreventDeadModification)
{
TShock.Log.ConsoleDebug("Bouncer / OnPlaceObject rejected dead people don't do things from {0}", args.Player.Name);
args.Player.SendTileSquare(x, y, 4);
args.Player.SendTileSquareCentered(x, y, 4);
args.Handled = true;
return;
}
@ -1905,7 +1952,7 @@ namespace TShockAPI
if (args.Player.IsBeingDisabled())
{
TShock.Log.ConsoleDebug("Bouncer / OnPlaceObject rejected disabled from {0}", args.Player.Name);
args.Player.SendTileSquare(x, y, 4);
args.Player.SendTileSquareCentered(x, y, 4);
args.Handled = true;
return;
}
@ -1916,7 +1963,7 @@ namespace TShockAPI
if (type != args.Player.TPlayer.inventory[args.Player.TPlayer.selectedItem].createTile)
{
TShock.Log.ConsoleDebug("Bouncer / OnPlaceObject rejected awkward tile creation/selection from {0}", args.Player.Name);
args.Player.SendTileSquare(x, y, 4);
args.Player.SendTileSquareCentered(x, y, 4);
args.Handled = true;
return;
}
@ -1940,7 +1987,7 @@ namespace TShockAPI
&& !args.Player.HasBuildPermission(i, j))
{
TShock.Log.ConsoleDebug("Bouncer / OnPlaceObject rejected mad loop from {0}", args.Player.Name);
args.Player.SendTileSquare(i, j, 4);
args.Player.SendTileSquareCentered(i, j, 4);
args.Handled = true;
return;
}
@ -1956,7 +2003,7 @@ namespace TShockAPI
&& !args.Player.IsInRange(x, y))
{
TShock.Log.ConsoleDebug("Bouncer / OnPlaceObject rejected range checks from {0}", args.Player.Name);
args.Player.SendTileSquare(x, y, 4);
args.Player.SendTileSquareCentered(x, y, 4);
args.Handled = true;
return;
}
@ -1965,7 +2012,7 @@ namespace TShockAPI
{
TShock.Log.ConsoleDebug("Bouncer / OnPlaceObject rejected tile place threshold from {0} {1}/{2}", args.Player.Name, args.Player.TilePlaceThreshold, TShock.Config.Settings.TilePlaceThreshold);
args.Player.Disable("Reached TilePlace threshold.", DisableFlags.WriteToLogAndConsole);
args.Player.SendTileSquare(x, y, 4);
args.Player.SendTileSquareCentered(x, y, 4);
args.Handled = true;
return;
}
@ -2363,7 +2410,7 @@ namespace TShockAPI
if ((args.Player.SelectedItem.type != args.ItemID && args.Player.ItemInHand.type != args.ItemID))
{
TShock.Log.ConsoleDebug("Bouncer / OnFoodPlatterTryPlacing rejected item not placed by hand from {0}", args.Player.Name);
args.Player.SendTileSquare(args.TileX, args.TileY, 1);
args.Player.SendTileSquareCentered(args.TileX, args.TileY, 1);
args.Handled = true;
return;
}
@ -2373,7 +2420,7 @@ namespace TShockAPI
Item item = new Item();
item.netDefaults(args.ItemID);
args.Player.GiveItemCheck(args.ItemID, item.Name, args.Stack, args.Prefix);
args.Player.SendTileSquare(args.TileX, args.TileY, 1);
args.Player.SendTileSquareCentered(args.TileX, args.TileY, 1);
args.Handled = true;
return;
}
@ -2384,7 +2431,7 @@ namespace TShockAPI
Item item = new Item();
item.netDefaults(args.ItemID);
args.Player.GiveItemCheck(args.ItemID, item.Name, args.Stack, args.Prefix);
args.Player.SendTileSquare(args.TileX, args.TileY, 1);
args.Player.SendTileSquareCentered(args.TileX, args.TileY, 1);
args.Handled = true;
return;
}
@ -2392,7 +2439,7 @@ namespace TShockAPI
if (!args.Player.IsInRange(args.TileX, args.TileY, range: 13)) // To my knowledge, max legit tile reach with accessories.
{
TShock.Log.ConsoleDebug("Bouncer / OnFoodPlatterTryPlacing rejected range checks from {0}", args.Player.Name);
args.Player.SendTileSquare(args.TileX, args.TileY, 1);
args.Player.SendTileSquareCentered(args.TileX, args.TileY, 1);
args.Handled = true;
return;
}

View file

@ -2060,7 +2060,8 @@ namespace TShockAPI
"eclipse",
"invasion",
"sandstorm",
"rain"
"rain",
"lanternsnight"
};
static readonly List<string> _validInvasions = new List<string>()
{
@ -2159,6 +2160,16 @@ namespace TShockAPI
Rain(args);
return;
case "lanternsnight":
case "lanterns":
if (!args.Player.HasPermission(Permissions.managelanternsnightevent))
{
FailedPermissionCheck();
return;
}
LanternsNight(args);
return;
default:
args.Player.SendErrorMessage("Invalid event type! Valid event types: {0}", String.Join(", ", _validEvents));
return;
@ -2374,6 +2385,20 @@ namespace TShockAPI
}
}
private static void LanternsNight(CommandArgs args)
{
LanternNight.ToggleManualLanterns();
string msg = $" st{(LanternNight.LanternsUp ? "art" : "opp")}ed a lantern night.";
if (args.Silent)
{
args.Player.SendInfoMessage("You" + msg);
}
else
{
TSPlayer.All.SendInfoMessage(args.Player.Name + msg);
}
}
private static void ClearAnglerQuests(CommandArgs args)
{
if (args.Parameters.Count > 0)
@ -4874,7 +4899,7 @@ namespace TShockAPI
try
{
args.Player.SendTileSquare(boundaryPoint.X, boundaryPoint.Y, 1);
args.Player.SendTileSquareCentered(boundaryPoint.X, boundaryPoint.Y, 1);
}
finally
{
@ -4888,7 +4913,7 @@ namespace TShockAPI
{
foreach (Point boundaryPoint in Utils.Instance.EnumerateRegionBoundaries(regionArea))
if ((boundaryPoint.X + boundaryPoint.Y & 1) == 0)
args.Player.SendTileSquare(boundaryPoint.X, boundaryPoint.Y, 1);
args.Player.SendTileSquareCentered(boundaryPoint.X, boundaryPoint.Y, 1);
Debug.Assert(boundaryHideTimer != null);
boundaryHideTimer.Dispose();
@ -5645,7 +5670,7 @@ namespace TShockAPI
private static void SyncLocalArea(CommandArgs args)
{
args.Player.SendTileSquare((int) args.Player.TileX, (int) args.Player.TileY, 32);
args.Player.SendTileSquareCentered(args.Player.TileX, args.Player.TileY, 32);
args.Player.SendWarningMessage("Sync'd!");
return;
}
@ -6524,7 +6549,7 @@ namespace TShockAPI
}
if (args.Parameters.Count == 1)
{
args.Player.SendTileSquare(x - 2, y - 20, 25);
args.Player.SendTileSquareCentered(x - 2, y - 20, 25);
args.Player.SendSuccessMessage("Tried to grow a " + name + ".");
}
}

View file

@ -136,6 +136,7 @@ namespace TShockAPI
{ PacketTypes.Teleport, HandleTeleport },
{ PacketTypes.PlayerHealOther, HandleHealOther },
{ PacketTypes.CatchNPC, HandleCatchNpc },
{ PacketTypes.ReleaseNPC, HandleReleaseNpc },
{ PacketTypes.TeleportationPotion, HandleTeleportationPotion },
{ PacketTypes.CompleteAnglerQuest, HandleCompleteAnglerQuest },
{ PacketTypes.NumberOfAnglerQuestsCompleted, HandleNumberOfAnglerQuestsCompleted },
@ -1676,6 +1677,56 @@ namespace TShockAPI
return args.Handled;
}
/// <summary>
/// The ReleaseNPC event arguments
/// </summary>
public class ReleaseNpcEventArgs : GetDataHandledEventArgs
{
/// <summary>
/// The X value of where NPC released
/// </summary>
public int X { get; set; }
/// <summary>
/// The Y value of where NPC released
/// </summary>
public int Y { get; set; }
/// <summary>
/// The NPC Type that player release
/// </summary>
public short Type { get; set; }
/// <summary>
/// The NPC release style
/// </summary>
public byte Style { get; set; }
}
/// <summary>
/// Called when player release a NPC, for checking critter released from item.
/// </summary>
public static HandlerList<ReleaseNpcEventArgs> ReleaseNPC = new HandlerList<ReleaseNpcEventArgs>();
private static bool OnReleaseNpc(TSPlayer player, MemoryStream data, int _x, int _y, short _type, byte _style)
{
if (ReleaseNPC == null)
{
return false;
}
var args = new ReleaseNpcEventArgs
{
Player = player,
Data = data,
X = _x,
Y = _y,
Type = _type,
Style = _style
};
ReleaseNPC.Invoke(null, args);
return args.Handled;
}
/// <summary>The arguments to the PlaceObject hook.</summary>
public class PlaceObjectEventArgs : GetDataHandledEventArgs
{
@ -3549,6 +3600,11 @@ namespace TShockAPI
return true;
}
bool hasPaintSprayerAbilities(Item item) =>
item != null
&& item.stack > 0
&& (item.type == ItemID.PaintSprayer || item.type == ItemID.ArchitectGizmoPack);
// Not selecting paintbrush or paint scraper or the spectre versions? Hacking.
if (args.Player.SelectedItem.type != ItemID.PaintRoller &&
args.Player.SelectedItem.type != ItemID.PaintScraper &&
@ -3556,8 +3612,8 @@ namespace TShockAPI
args.Player.SelectedItem.type != ItemID.SpectrePaintRoller &&
args.Player.SelectedItem.type != ItemID.SpectrePaintScraper &&
args.Player.SelectedItem.type != ItemID.SpectrePaintbrush &&
!args.Player.Accessories.Any(i => i != null && i.stack > 0 &&
(i.type == ItemID.PaintSprayer || i.type == ItemID.ArchitectGizmoPack)))
!args.Player.Accessories.Any(hasPaintSprayerAbilities) &&
!args.Player.Inventory.Any(hasPaintSprayerAbilities))
{
TShock.Log.ConsoleDebug("GetDataHandlers / HandlePaintTile rejected select consistency {0}", args.Player.Name);
args.Player.SendData(PacketTypes.PaintTile, "", x, y, Main.tile[x, y].color());
@ -3720,10 +3776,31 @@ namespace TShockAPI
NetMessage.SendData((int)PacketTypes.NpcUpdate, -1, -1, NetworkText.Empty, npcID);
return true;
}
if(args.Player.IsBeingDisabled())
{
TShock.Log.ConsoleDebug("GetDataHandlers / HandleCatchNpc rejected catch npc {0}", args.Player.Name);
return true;
}
return false;
}
private static bool HandleReleaseNpc(GetDataHandlerArgs args)
{
var x = args.Data.ReadInt32();
var y = args.Data.ReadInt32();
var type = args.Data.ReadInt16();
var style = args.Data.ReadInt8();
if (OnReleaseNpc(args.Player, args.Data, x, y, type, style))
{
return true;
}
return false;
}
private static bool HandleTeleportationPotion(GetDataHandlerArgs args)
{
var type = args.Data.ReadByte();
@ -3818,7 +3895,7 @@ namespace TShockAPI
if (TShock.TileBans.TileIsBanned((short)TileID.LogicSensor, args.Player))
{
args.Player.SendTileSquare(x, y, 1);
args.Player.SendTileSquareCentered(x, y, 1);
args.Player.SendErrorMessage("You do not have permission to place Logic Sensors.");
return true;
}

View file

@ -199,7 +199,7 @@ namespace TShockAPI
{
if (args.Player.TPlayer.autoActuator && DataModel.ItemIsBanned("Actuator", args.Player))
{
args.Player.SendTileSquare(args.X, args.Y, 1);
args.Player.SendTileSquareCentered(args.X, args.Y, 1);
args.Player.SendErrorMessage("You do not have permission to place actuators.");
args.Handled = true;
return;
@ -207,7 +207,7 @@ namespace TShockAPI
if (DataModel.ItemIsBanned(EnglishLanguage.GetItemNameById(args.Player.SelectedItem.netID), args.Player))
{
args.Player.SendTileSquare(args.X, args.Y, 4);
args.Player.SendTileSquareCentered(args.X, args.Y, 4);
args.Handled = true;
return;
}

View file

@ -307,6 +307,9 @@ namespace TShockAPI
[Description("User can use the 'rain' subcommand of the 'worldevent' command")]
public static readonly string managerainevent = "tshock.world.events.rain";
[Description("User can use the 'lanternsnight' subcommand of the 'worldevent' command")]
public static readonly string managelanternsnightevent = "tshock.world.events.lanternsnight";
[Description("User can change expert state.")]
public static readonly string toggleexpert = "tshock.world.toggleexpert";

View file

@ -53,5 +53,5 @@ using System.Runtime.InteropServices;
// 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)
[assembly: AssemblyVersion("4.5.12")]
[assembly: AssemblyFileVersion("4.5.12")]
[assembly: AssemblyVersion("4.5.13")]
[assembly: AssemblyFileVersion("4.5.13")]

View file

@ -171,7 +171,7 @@ namespace TShockAPI
}
// Revert all tile changes and handle the event
player.SendTileSquare(e.X, e.Y, 4);
player.SendTileSquareCentered(e.X, e.Y, 4);
e.Handled = true;
}
@ -190,7 +190,7 @@ namespace TShockAPI
player.AwaitingTempPoint = 0;
// Revert all tile changes and handle the event
player.SendTileSquare(e.X, e.Y, 4);
player.SendTileSquareCentered(e.X, e.Y, 4);
e.Handled = true;
}

View file

@ -898,6 +898,18 @@ namespace TShockAPI
}
}
/// <summary>
/// Gets the player's inventory (first 5 rows)
/// </summary>
public IEnumerable<Item> Inventory
{
get
{
for (int i = 0; i < 50; i++)
yield return TPlayer.inventory[i];
}
}
/// <summary>
/// Gets the player's accessories.
/// </summary>
@ -1215,7 +1227,7 @@ namespace TShockAPI
y = 992;
}
SendTileSquare((int)(x / 16), (int)(y / 16), 15);
SendTileSquareCentered((int)(x / 16), (int)(y / 16), 15);
TPlayer.Teleport(new Vector2(x, y), style);
NetMessage.SendData((int)PacketTypes.Teleport, -1, -1, NetworkText.Empty, 0, TPlayer.whoAmI, x, y, style);
return true;
@ -1297,11 +1309,27 @@ namespace TShockAPI
/// <param name="y">The y coordinate to send.</param>
/// <param name="size">The size square set of tiles to send.</param>
/// <returns>true if the tile square was sent successfully, else false</returns>
[Obsolete("This method may not send tiles the way you would expect it to. The (x,y) coordinates are the top left corner of the tile square, switch to " + nameof(SendTileSquareCentered) + " if you wish for the coordindates to be the center of the square.")]
public virtual bool SendTileSquare(int x, int y, int size = 10)
{
return SendTileRect((short)x, (short)y, (byte)size, (byte)size);
}
/// <summary>
/// Sends a tile square at a center location with a given size.
/// Typically used to revert changes by Bouncer through sending the
/// "old" version of modified data back to a client.
/// Prevents desync issues.
/// </summary>
/// <param name="x">The x coordinates of the center of the square.</param>
/// <param name="y">The y coordinates of the center of the square.</param>
/// <param name="size">The size square set of tiles to send.</param>
/// <returns>true if the tile square was sent successfully, else false</returns>
public virtual bool SendTileSquareCentered(int x, int y, byte size = 10)
{
return SendTileRect((short)(x - (size / 2)), (short)(y - (size / 2)), size, size);
}
/// <summary>
/// Sends a rectangle of tiles at a location with the given length and width.
/// </summary>
@ -1336,7 +1364,7 @@ namespace TShockAPI
public bool GiveItemCheck(int type, string name, int stack, int prefix = 0)
{
if ((TShock.ItemBans.DataModel.ItemIsBanned(name) && TShock.Config.Settings.PreventBannedItemSpawn) &&
(TShock.ItemBans.DataModel.ItemIsBanned(name, this) || !TShock.Config.Settings.AllowAllowedGroupsToSpawnBannedItems))
(TShock.ItemBans.DataModel.ItemIsBanned(name, this) || !TShock.Config.Settings.AllowAllowedGroupsToSpawnBannedItems))
return false;
GiveItem(type, stack, prefix);

View file

@ -190,7 +190,7 @@ namespace TShockAPI
// Send all players updated tile squares
foreach (Vector2 coords in tiles.Keys)
{
All.SendTileSquare((int)coords.X, (int)coords.Y, 3);
All.SendTileSquareCentered((int)coords.X, (int)coords.Y, 3);
}
}

View file

@ -58,7 +58,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 = "Herrscher of Logic";
public static readonly string VersionCodename = "Let us know if you're using this on raspberry pi or we might drop support for it";
/// <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";

56
appveyor.yml Normal file
View file

@ -0,0 +1,56 @@
version: '{build}'
max_jobs: 16
image: Visual Studio 2019
build_script:
- ps: >-
git submodule update --init --recursive
cd ./TerrariaServerAPI/
nuget restore TShock.4.OTAPI.sln
msbuild TShock.4.OTAPI.sln /p:Configuration=Debug
cd ./TShock.Modifications.Bootstrapper/bin/Debug/
./TShock.Modifications.Bootstrapper.exe
cd ../../../
msbuild ./TerrariaServerAPI/TerrariaServerAPI.csproj /p:Configuration=Debug
msbuild TShock.4.OTAPI.sln /p:Configuration=Release
cd ./TShock.Modifications.Bootstrapper/bin/Release/
./TShock.Modifications.Bootstrapper.exe
cd ../../../
msbuild ./TerrariaServerAPI/TerrariaServerAPI.csproj /p:Configuration=Release
cd ../
nuget restore TShock.sln
msbuild ./TShockAPI/TShockAPI.csproj /p:Configuration=Release
msbuild ./TShockAPI/TShockAPI.csproj /p:Configuration=Debug
artifacts:
- path: ./TShockAPI/bin/Debug/
name: TShockAVDebug
- path: ./TShockAPI/bin/Release/
name: TShockAVRelease