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 # 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) * 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. * 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
* Improved the `/grow` command to reduce code duplication, use `TileID` constants for less ambiguous types. (@drunderscore) * 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 ## TShock 4.5.12
* Fixed the ability to spawn Zenith projectile with non-original items. (@AgaSpace) * 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) * 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/ $ cd ./TerrariaServerAPI/TShock.Modifications.Bootstrapper/bin/$BUILD_MODE/
$ mono TShock.Modifications.Bootstrapper.exe -in=OTAPI.dll \ $ 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 -o=Output/OTAPI.dll
1. Verify that non-zero modifications ran successfully. Then, build the Terraria Server API executable. 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.NPCAddBuff += OnNPCAddBuff;
GetDataHandlers.NPCHome += OnUpdateNPCHome; GetDataHandlers.NPCHome += OnUpdateNPCHome;
GetDataHandlers.HealOtherPlayer += OnHealOtherPlayer; GetDataHandlers.HealOtherPlayer += OnHealOtherPlayer;
GetDataHandlers.ReleaseNPC += OnReleaseNPC;
GetDataHandlers.PlaceObject += OnPlaceObject; GetDataHandlers.PlaceObject += OnPlaceObject;
GetDataHandlers.PlaceTileEntity += OnPlaceTileEntity; GetDataHandlers.PlaceTileEntity += OnPlaceTileEntity;
GetDataHandlers.PlaceItemFrame += OnPlaceItemFrame; GetDataHandlers.PlaceItemFrame += OnPlaceItemFrame;
@ -405,7 +406,7 @@ namespace TShockAPI
((action == EditAction.PlaceWall || action == EditAction.ReplaceWall) && editData >= Main.maxWallTypes)) ((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); 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; args.Handled = true;
return; return;
} }
@ -420,7 +421,7 @@ namespace TShockAPI
if (args.Player.Dead && TShock.Config.Settings.PreventDeadModification) if (args.Player.Dead && TShock.Config.Settings.PreventDeadModification)
{ {
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from (pdm) {0} {1} {2}", args.Player.Name, action, editData); 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; args.Handled = true;
return; return;
} }
@ -434,7 +435,7 @@ namespace TShockAPI
if (TShock.TileBans.TileIsBanned(editData, args.Player)) if (TShock.TileBans.TileIsBanned(editData, args.Player))
{ {
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from (tb) {0} {1} {2}", args.Player.Name, action, editData); 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.Player.SendErrorMessage("You do not have permission to place this tile.");
args.Handled = true; args.Handled = true;
return; return;
@ -457,7 +458,7 @@ namespace TShockAPI
{ {
TShock.Log.ConsoleError("Bouncer / OnTileEdit rejected from (placestyle) {0} {1} {2} placeStyle: {3} expectedStyle: {4}", TShock.Log.ConsoleError("Bouncer / OnTileEdit rejected from (placestyle) {0} {1} {2} placeStyle: {3} expectedStyle: {4}",
args.Player.Name, action, editData, requestedPlaceStyle, actualItemPlaceStyle); args.Player.Name, action, editData, requestedPlaceStyle, actualItemPlaceStyle);
args.Player.SendTileSquare(tileX, tileY, 1); args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true; args.Handled = true;
return; return;
} }
@ -468,7 +469,7 @@ namespace TShockAPI
{ {
TShock.Log.ConsoleError("Bouncer / OnTileEdit rejected from (placestyle) {0} {1} {2} placeStyle: {3} expectedStyle: {4}", TShock.Log.ConsoleError("Bouncer / OnTileEdit rejected from (placestyle) {0} {1} {2} placeStyle: {3} expectedStyle: {4}",
args.Player.Name, action, editData, requestedPlaceStyle, correctedPlaceStyle); args.Player.Name, action, editData, requestedPlaceStyle, correctedPlaceStyle);
args.Player.SendTileSquare(tileX, tileY, 1); args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true; args.Handled = true;
return; 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)) 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); 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; args.Handled = true;
return; 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)) 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); 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; args.Handled = true;
return; 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) && !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); 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; args.Handled = true;
return; return;
} }
@ -513,7 +514,7 @@ namespace TShockAPI
if (selectedItem.hammer == 0 && !ItemID.Sets.Explosives[selectedItem.netID] && args.Player.RecentFuse == 0 && selectedItem.createWall == 0) 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); 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; args.Handled = true;
return; 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))) !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); 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; args.Handled = true;
return; return;
} }
@ -543,7 +544,7 @@ namespace TShockAPI
requestedPlaceStyle > GetMaxPlaceStyle(editData)) requestedPlaceStyle > GetMaxPlaceStyle(editData))
{ {
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from (ms1) {0} {1} {2}", args.Player.Name, action, 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; args.Handled = true;
return; return;
} }
@ -552,7 +553,7 @@ namespace TShockAPI
if (selectedItem.netID == ItemID.IceRod && editData != TileID.MagicalIceBlock) 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); 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; args.Handled = true;
} }
/// If they aren't selecting the item which creates the tile, they're hacking. /// 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) 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); 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; args.Handled = true;
return; return;
} }
@ -571,7 +572,7 @@ namespace TShockAPI
if ((action == EditAction.PlaceWall || action == EditAction.ReplaceWall) && editData != selectedItem.createWall) 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); 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; args.Handled = true;
return; return;
} }
@ -581,7 +582,7 @@ namespace TShockAPI
{ {
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from (chestcap) {0} {1} {2}", args.Player.Name, action, editData); 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.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; args.Handled = true;
return; return;
} }
@ -599,7 +600,7 @@ namespace TShockAPI
&& selectedItem.type != ItemID.WireKite) && selectedItem.type != ItemID.WireKite)
{ {
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from place wire from {0} {1} {2}", args.Player.Name, action, editData); 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; args.Handled = true;
return; return;
} }
@ -613,7 +614,7 @@ namespace TShockAPI
&& selectedItem.type != ItemID.MulticolorWrench) && selectedItem.type != ItemID.MulticolorWrench)
{ {
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from wire cutter from {0} {1} {2}", args.Player.Name, action, editData); 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; args.Handled = true;
return; return;
} }
@ -624,7 +625,7 @@ namespace TShockAPI
if (selectedItem.type != ItemID.Actuator && !args.Player.TPlayer.autoActuator) 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); 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; args.Handled = true;
return; return;
} }
@ -634,7 +635,7 @@ namespace TShockAPI
if (action == EditAction.KillWall || action == EditAction.ReplaceWall) 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); 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; args.Handled = true;
return; return;
} }
@ -645,7 +646,7 @@ namespace TShockAPI
if (args.Player.IsBeingDisabled()) if (args.Player.IsBeingDisabled())
{ {
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from disable from {0} {1} {2}", args.Player.Name, action, editData); 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; args.Handled = true;
return; 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); 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; args.Handled = true;
return; return;
} }
@ -698,7 +699,7 @@ namespace TShockAPI
else else
{ {
args.Player.Disable("Reached TileKill threshold.", DisableFlags.WriteToLogAndConsole); 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); 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 else
{ {
args.Player.Disable("Reached TilePlace threshold.", DisableFlags.WriteToLogAndConsole); 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); 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()) if (args.Player.IsBouncerThrottled())
{ {
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from throttled from {0} {1} {2}", args.Player.Name, action, editData); 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; args.Handled = true;
return; 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("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."); 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; args.Handled = true;
return; return;
} }
@ -1317,7 +1318,7 @@ namespace TShockAPI
if (args.Player.IsBeingDisabled()) if (args.Player.IsBeingDisabled())
{ {
TShock.Log.ConsoleDebug("Bouncer / OnPlaceChest rejected from disabled from {0}", args.Player.Name); 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; args.Handled = true;
return; return;
} }
@ -1325,7 +1326,7 @@ namespace TShockAPI
if (args.Player.SelectedItem.placeStyle != style) if (args.Player.SelectedItem.placeStyle != style)
{ {
TShock.Log.ConsoleError(string.Format("Bouncer / OnPlaceChest / rejected from invalid place style from {0}", args.Player.Name)); 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; args.Handled = true;
return; return;
} }
@ -1337,7 +1338,7 @@ namespace TShockAPI
&& (!TShock.Utils.HasWorldReachedMaxChests() && Main.tile[tileX, tileY].type != TileID.Dirt)) //Chest && (!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); 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; args.Handled = true;
return; return;
} }
@ -1349,7 +1350,7 @@ namespace TShockAPI
{ {
TShock.Log.ConsoleDebug("Bouncer / OnPlaceChest rejected from weird placement check from {0}", args.Player.Name); 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. //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; args.Handled = true;
return; return;
} }
@ -1358,7 +1359,7 @@ namespace TShockAPI
if (!args.Player.HasBuildPermission(tileX, tileY)) if (!args.Player.HasBuildPermission(tileX, tileY))
{ {
TShock.Log.ConsoleDebug("Bouncer / OnPlaceChest rejected from invalid permission from {0}", args.Player.Name); 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; args.Handled = true;
return; return;
} }
@ -1366,7 +1367,7 @@ namespace TShockAPI
if (!args.Player.IsInRange(tileX, tileY)) if (!args.Player.IsInRange(tileX, tileY))
{ {
TShock.Log.ConsoleDebug("Bouncer / OnPlaceChest rejected from range check from {0}", args.Player.Name); 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; args.Handled = true;
return; return;
} }
@ -1451,7 +1452,7 @@ namespace TShockAPI
if (args.Player.IsBeingDisabled()) if (args.Player.IsBeingDisabled())
{ {
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected disabled from {0}", args.Player.Name); 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; args.Handled = true;
return; return;
} }
@ -1465,7 +1466,7 @@ namespace TShockAPI
else else
{ {
args.Player.Disable("Reached TileLiquid threshold.", DisableFlags.WriteToLogAndConsole); 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); 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); 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.SendErrorMessage("You do not have permission to perform this action.");
args.Player.Disable("Spreading lava without holding a lava bucket", DisableFlags.WriteToLogAndConsole); 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; args.Handled = true;
return; return;
} }
@ -1548,7 +1549,7 @@ namespace TShockAPI
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected lava bucket from {0}", args.Player.Name); 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.SendErrorMessage("You do not have permission to perform this action.");
args.Player.Disable("Using banned lava bucket without permissions", DisableFlags.WriteToLogAndConsole); 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; args.Handled = true;
return; return;
} }
@ -1558,7 +1559,7 @@ namespace TShockAPI
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected bucket check 2 from {0}", args.Player.Name); 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.SendErrorMessage("You do not have permission to perform this action.");
args.Player.Disable("Spreading water without holding a water bucket", DisableFlags.WriteToLogAndConsole); 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; args.Handled = true;
return; return;
} }
@ -1568,7 +1569,7 @@ namespace TShockAPI
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected bucket check 3 from {0}", args.Player.Name); 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.SendErrorMessage("You do not have permission to perform this action.");
args.Player.Disable("Using banned water bucket without permissions", DisableFlags.WriteToLogAndConsole); 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; args.Handled = true;
return; return;
} }
@ -1578,7 +1579,7 @@ namespace TShockAPI
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected bucket check 4 from {0}", args.Player.Name); 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.SendErrorMessage("You do not have permission to perform this action.");
args.Player.Disable("Spreading honey without holding a honey bucket", DisableFlags.WriteToLogAndConsole); 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; args.Handled = true;
return; return;
} }
@ -1588,7 +1589,7 @@ namespace TShockAPI
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected bucket check 5 from {0}", args.Player.Name); 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.SendErrorMessage("You do not have permission to perform this action.");
args.Player.Disable("Using banned honey bucket without permissions", DisableFlags.WriteToLogAndConsole); 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; args.Handled = true;
return; return;
} }
@ -1597,7 +1598,7 @@ namespace TShockAPI
if (!args.Player.HasBuildPermission(tileX, tileY)) if (!args.Player.HasBuildPermission(tileX, tileY))
{ {
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected build permission from {0}", args.Player.Name); 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; args.Handled = true;
return; return;
} }
@ -1605,7 +1606,7 @@ namespace TShockAPI
if (!wasThereABombNearby && !args.Player.IsInRange(tileX, tileY, 16)) if (!wasThereABombNearby && !args.Player.IsInRange(tileX, tileY, 16))
{ {
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected range checks from {0}", args.Player.Name); 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; args.Handled = true;
return; return;
} }
@ -1613,7 +1614,7 @@ namespace TShockAPI
if (args.Player.IsBouncerThrottled()) if (args.Player.IsBouncerThrottled())
{ {
TShock.Log.ConsoleDebug("Bouncer / OnLiquidSet rejected throttle from {0}", args.Player.Name); 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; args.Handled = true;
return; return;
} }
@ -1829,6 +1830,52 @@ namespace TShockAPI
return; 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> /// <summary>Bouncer's PlaceObject hook reverts malicious tile placement.</summary>
/// <param name="sender">The object that triggered the event.</param> /// <param name="sender">The object that triggered the event.</param>
/// <param name="args">The packet arguments that the event has.</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)) if (type == TileID.FakeContainers && (style == 52 || style == 53))
{ {
TShock.Log.ConsoleDebug("Bouncer / OnPlaceObject rejected fake containers from {0}", args.Player.Name); 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; args.Handled = true;
return; return;
} }
@ -1888,7 +1935,7 @@ namespace TShockAPI
if (TShock.TileBans.TileIsBanned(type, args.Player)) if (TShock.TileBans.TileIsBanned(type, args.Player))
{ {
TShock.Log.ConsoleDebug("Bouncer / OnPlaceObject rejected banned tiles from {0}", args.Player.Name); 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.Player.SendErrorMessage("You do not have permission to place this tile.");
args.Handled = true; args.Handled = true;
return; return;
@ -1897,7 +1944,7 @@ namespace TShockAPI
if (args.Player.Dead && TShock.Config.Settings.PreventDeadModification) 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); 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; args.Handled = true;
return; return;
} }
@ -1905,7 +1952,7 @@ namespace TShockAPI
if (args.Player.IsBeingDisabled()) if (args.Player.IsBeingDisabled())
{ {
TShock.Log.ConsoleDebug("Bouncer / OnPlaceObject rejected disabled from {0}", args.Player.Name); 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; args.Handled = true;
return; return;
} }
@ -1916,7 +1963,7 @@ namespace TShockAPI
if (type != args.Player.TPlayer.inventory[args.Player.TPlayer.selectedItem].createTile) 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); 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; args.Handled = true;
return; return;
} }
@ -1940,7 +1987,7 @@ namespace TShockAPI
&& !args.Player.HasBuildPermission(i, j)) && !args.Player.HasBuildPermission(i, j))
{ {
TShock.Log.ConsoleDebug("Bouncer / OnPlaceObject rejected mad loop from {0}", args.Player.Name); 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; args.Handled = true;
return; return;
} }
@ -1956,7 +2003,7 @@ namespace TShockAPI
&& !args.Player.IsInRange(x, y)) && !args.Player.IsInRange(x, y))
{ {
TShock.Log.ConsoleDebug("Bouncer / OnPlaceObject rejected range checks from {0}", args.Player.Name); 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; args.Handled = true;
return; 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); 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.Disable("Reached TilePlace threshold.", DisableFlags.WriteToLogAndConsole);
args.Player.SendTileSquare(x, y, 4); args.Player.SendTileSquareCentered(x, y, 4);
args.Handled = true; args.Handled = true;
return; return;
} }
@ -2363,7 +2410,7 @@ namespace TShockAPI
if ((args.Player.SelectedItem.type != args.ItemID && args.Player.ItemInHand.type != args.ItemID)) 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); 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; args.Handled = true;
return; return;
} }
@ -2373,7 +2420,7 @@ namespace TShockAPI
Item item = new Item(); Item item = new Item();
item.netDefaults(args.ItemID); item.netDefaults(args.ItemID);
args.Player.GiveItemCheck(args.ItemID, item.Name, args.Stack, args.Prefix); 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; args.Handled = true;
return; return;
} }
@ -2384,7 +2431,7 @@ namespace TShockAPI
Item item = new Item(); Item item = new Item();
item.netDefaults(args.ItemID); item.netDefaults(args.ItemID);
args.Player.GiveItemCheck(args.ItemID, item.Name, args.Stack, args.Prefix); 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; args.Handled = true;
return; 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. 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); 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; args.Handled = true;
return; return;
} }

View file

@ -2060,7 +2060,8 @@ namespace TShockAPI
"eclipse", "eclipse",
"invasion", "invasion",
"sandstorm", "sandstorm",
"rain" "rain",
"lanternsnight"
}; };
static readonly List<string> _validInvasions = new List<string>() static readonly List<string> _validInvasions = new List<string>()
{ {
@ -2159,6 +2160,16 @@ namespace TShockAPI
Rain(args); Rain(args);
return; return;
case "lanternsnight":
case "lanterns":
if (!args.Player.HasPermission(Permissions.managelanternsnightevent))
{
FailedPermissionCheck();
return;
}
LanternsNight(args);
return;
default: default:
args.Player.SendErrorMessage("Invalid event type! Valid event types: {0}", String.Join(", ", _validEvents)); args.Player.SendErrorMessage("Invalid event type! Valid event types: {0}", String.Join(", ", _validEvents));
return; 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) private static void ClearAnglerQuests(CommandArgs args)
{ {
if (args.Parameters.Count > 0) if (args.Parameters.Count > 0)
@ -4874,7 +4899,7 @@ namespace TShockAPI
try try
{ {
args.Player.SendTileSquare(boundaryPoint.X, boundaryPoint.Y, 1); args.Player.SendTileSquareCentered(boundaryPoint.X, boundaryPoint.Y, 1);
} }
finally finally
{ {
@ -4888,7 +4913,7 @@ namespace TShockAPI
{ {
foreach (Point boundaryPoint in Utils.Instance.EnumerateRegionBoundaries(regionArea)) foreach (Point boundaryPoint in Utils.Instance.EnumerateRegionBoundaries(regionArea))
if ((boundaryPoint.X + boundaryPoint.Y & 1) == 0) 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); Debug.Assert(boundaryHideTimer != null);
boundaryHideTimer.Dispose(); boundaryHideTimer.Dispose();
@ -5645,7 +5670,7 @@ namespace TShockAPI
private static void SyncLocalArea(CommandArgs args) 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!"); args.Player.SendWarningMessage("Sync'd!");
return; return;
} }
@ -6524,7 +6549,7 @@ namespace TShockAPI
} }
if (args.Parameters.Count == 1) 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 + "."); args.Player.SendSuccessMessage("Tried to grow a " + name + ".");
} }
} }

View file

@ -136,6 +136,7 @@ namespace TShockAPI
{ PacketTypes.Teleport, HandleTeleport }, { PacketTypes.Teleport, HandleTeleport },
{ PacketTypes.PlayerHealOther, HandleHealOther }, { PacketTypes.PlayerHealOther, HandleHealOther },
{ PacketTypes.CatchNPC, HandleCatchNpc }, { PacketTypes.CatchNPC, HandleCatchNpc },
{ PacketTypes.ReleaseNPC, HandleReleaseNpc },
{ PacketTypes.TeleportationPotion, HandleTeleportationPotion }, { PacketTypes.TeleportationPotion, HandleTeleportationPotion },
{ PacketTypes.CompleteAnglerQuest, HandleCompleteAnglerQuest }, { PacketTypes.CompleteAnglerQuest, HandleCompleteAnglerQuest },
{ PacketTypes.NumberOfAnglerQuestsCompleted, HandleNumberOfAnglerQuestsCompleted }, { PacketTypes.NumberOfAnglerQuestsCompleted, HandleNumberOfAnglerQuestsCompleted },
@ -1676,6 +1677,56 @@ namespace TShockAPI
return args.Handled; 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> /// <summary>The arguments to the PlaceObject hook.</summary>
public class PlaceObjectEventArgs : GetDataHandledEventArgs public class PlaceObjectEventArgs : GetDataHandledEventArgs
{ {
@ -3549,6 +3600,11 @@ namespace TShockAPI
return true; 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. // Not selecting paintbrush or paint scraper or the spectre versions? Hacking.
if (args.Player.SelectedItem.type != ItemID.PaintRoller && if (args.Player.SelectedItem.type != ItemID.PaintRoller &&
args.Player.SelectedItem.type != ItemID.PaintScraper && args.Player.SelectedItem.type != ItemID.PaintScraper &&
@ -3556,8 +3612,8 @@ namespace TShockAPI
args.Player.SelectedItem.type != ItemID.SpectrePaintRoller && args.Player.SelectedItem.type != ItemID.SpectrePaintRoller &&
args.Player.SelectedItem.type != ItemID.SpectrePaintScraper && args.Player.SelectedItem.type != ItemID.SpectrePaintScraper &&
args.Player.SelectedItem.type != ItemID.SpectrePaintbrush && args.Player.SelectedItem.type != ItemID.SpectrePaintbrush &&
!args.Player.Accessories.Any(i => i != null && i.stack > 0 && !args.Player.Accessories.Any(hasPaintSprayerAbilities) &&
(i.type == ItemID.PaintSprayer || i.type == ItemID.ArchitectGizmoPack))) !args.Player.Inventory.Any(hasPaintSprayerAbilities))
{ {
TShock.Log.ConsoleDebug("GetDataHandlers / HandlePaintTile rejected select consistency {0}", args.Player.Name); TShock.Log.ConsoleDebug("GetDataHandlers / HandlePaintTile rejected select consistency {0}", args.Player.Name);
args.Player.SendData(PacketTypes.PaintTile, "", x, y, Main.tile[x, y].color()); 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); NetMessage.SendData((int)PacketTypes.NpcUpdate, -1, -1, NetworkText.Empty, npcID);
return true; return true;
} }
if(args.Player.IsBeingDisabled())
{
TShock.Log.ConsoleDebug("GetDataHandlers / HandleCatchNpc rejected catch npc {0}", args.Player.Name);
return true;
}
return false; 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) private static bool HandleTeleportationPotion(GetDataHandlerArgs args)
{ {
var type = args.Data.ReadByte(); var type = args.Data.ReadByte();
@ -3818,7 +3895,7 @@ namespace TShockAPI
if (TShock.TileBans.TileIsBanned((short)TileID.LogicSensor, args.Player)) 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."); args.Player.SendErrorMessage("You do not have permission to place Logic Sensors.");
return true; return true;
} }

View file

@ -199,7 +199,7 @@ namespace TShockAPI
{ {
if (args.Player.TPlayer.autoActuator && DataModel.ItemIsBanned("Actuator", args.Player)) 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.Player.SendErrorMessage("You do not have permission to place actuators.");
args.Handled = true; args.Handled = true;
return; return;
@ -207,7 +207,7 @@ namespace TShockAPI
if (DataModel.ItemIsBanned(EnglishLanguage.GetItemNameById(args.Player.SelectedItem.netID), args.Player)) 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; args.Handled = true;
return; return;
} }

View file

@ -307,6 +307,9 @@ namespace TShockAPI
[Description("User can use the 'rain' subcommand of the 'worldevent' command")] [Description("User can use the 'rain' subcommand of the 'worldevent' command")]
public static readonly string managerainevent = "tshock.world.events.rain"; 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.")] [Description("User can change expert state.")]
public static readonly string toggleexpert = "tshock.world.toggleexpert"; 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 // 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) // so that the update manager works correctly (via the Github releases api and mimic)
[assembly: AssemblyVersion("4.5.12")] [assembly: AssemblyVersion("4.5.13")]
[assembly: AssemblyFileVersion("4.5.12")] [assembly: AssemblyFileVersion("4.5.13")]

View file

@ -171,7 +171,7 @@ namespace TShockAPI
} }
// Revert all tile changes and handle the event // 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; e.Handled = true;
} }
@ -190,7 +190,7 @@ namespace TShockAPI
player.AwaitingTempPoint = 0; player.AwaitingTempPoint = 0;
// Revert all tile changes and handle the event // 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; 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> /// <summary>
/// Gets the player's accessories. /// Gets the player's accessories.
/// </summary> /// </summary>
@ -1215,7 +1227,7 @@ namespace TShockAPI
y = 992; 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); TPlayer.Teleport(new Vector2(x, y), style);
NetMessage.SendData((int)PacketTypes.Teleport, -1, -1, NetworkText.Empty, 0, TPlayer.whoAmI, x, y, style); NetMessage.SendData((int)PacketTypes.Teleport, -1, -1, NetworkText.Empty, 0, TPlayer.whoAmI, x, y, style);
return true; return true;
@ -1297,11 +1309,27 @@ namespace TShockAPI
/// <param name="y">The y coordinate to send.</param> /// <param name="y">The y coordinate to send.</param>
/// <param name="size">The size square set of tiles 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> /// <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) public virtual bool SendTileSquare(int x, int y, int size = 10)
{ {
return SendTileRect((short)x, (short)y, (byte)size, (byte)size); 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> /// <summary>
/// Sends a rectangle of tiles at a location with the given length and width. /// Sends a rectangle of tiles at a location with the given length and width.
/// </summary> /// </summary>
@ -1336,7 +1364,7 @@ namespace TShockAPI
public bool GiveItemCheck(int type, string name, int stack, int prefix = 0) public bool GiveItemCheck(int type, string name, int stack, int prefix = 0)
{ {
if ((TShock.ItemBans.DataModel.ItemIsBanned(name) && TShock.Config.Settings.PreventBannedItemSpawn) && 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; return false;
GiveItem(type, stack, prefix); GiveItem(type, stack, prefix);

View file

@ -190,7 +190,7 @@ namespace TShockAPI
// Send all players updated tile squares // Send all players updated tile squares
foreach (Vector2 coords in tiles.Keys) 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> /// <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; 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> /// <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> /// <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"; 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