diff --git a/.github/workflows/ci-otapi3-nuget.yml b/.github/workflows/ci-otapi3-nuget.yml
index 7834ccf01..c4f0649ed 100644
--- a/.github/workflows/ci-otapi3-nuget.yml
+++ b/.github/workflows/ci-otapi3-nuget.yml
@@ -1,8 +1,7 @@
name: Deploy NuGet(OTAPI3)
on:
- push:
- branches: [ nuget-release ]
+ workflow_dispatch:
jobs:
build:
@@ -21,10 +20,10 @@ jobs:
- name: Restore dependencies
run: dotnet restore
- name: Build
- run: dotnet build TShock.sln --configuration Release --no-restore
- - name: Test
- run: dotnet test --no-build --verbosity normal --configuration Release
-
+ run: |
+ dotnet build -c Release
+ dotnet pack -c Release -o nupkgs
# Publish to nuget
- name: Push TShockAPI
- run: dotnet nuget push TShockAPI/bin/Release/*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json
+ run: |
+ dotnet nuget push nupkgs/UnrealMultiple.*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json
diff --git a/.github/workflows/ci-otapi3.yml b/.github/workflows/ci-otapi3.yml
index f7e500370..54d22d834 100644
--- a/.github/workflows/ci-otapi3.yml
+++ b/.github/workflows/ci-otapi3.yml
@@ -39,12 +39,12 @@ jobs:
- name: Produce installer
run: |
cd TShockInstaller
- dotnet publish -r ${{ matrix.arch }} -f net9.0 -c Release -p:PublishSingleFile=true --self-contained true
+ dotnet publish -r ${{ matrix.arch }} -f net9.0 -c Release -p:PublishSingleFile=true --self-contained
- name: Produce build
run: |
cd TShockLauncher
- dotnet publish -r ${{ matrix.arch }} -f net9.0 -c Release -p:PublishSingleFile=true --self-contained false
+ dotnet publish -r ${{ matrix.arch }} -f net9.0 -c Release -p:PublishSingleFile=true --no-self-contained
- name: Chmod scripts
if: ${{ matrix.arch != 'win-x64' }}
diff --git a/.gitignore b/.gitignore
index 5ac4b6aad..fe15dca75 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,7 +5,6 @@ releases/
###################
*.com
*.class
-*.dll
*.exe
*.o
*.so
@@ -69,3 +68,5 @@ packages/*
# Private key files #
scripts/ssh_private_key
+.idea
+nupkgs
diff --git a/.gitmodules b/.gitmodules
index f3c700b63..4cf49db0f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,5 +1,5 @@
[submodule "TerrariaServerAPI"]
path = TerrariaServerAPI
- url = https://github.com/NyxStudios/TerrariaAPI-Server.git
+ url = https://github.com/WindFrost-CSFT/TSAPI.git
ignore = dirty
branch = general-devel
diff --git a/TShock.sln b/TShock.sln
index 36256729c..980783786 100644
--- a/TShock.sln
+++ b/TShock.sln
@@ -24,8 +24,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TerrariaServerAPI", "Terrar
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TShockLauncher", "TShockLauncher\TShockLauncher.csproj", "{2A312452-A43F-43E3-8AEB-E22F9A35C210}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TShockLauncher.Tests", "TShockLauncher.Tests\TShockLauncher.Tests.csproj", "{90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TShockInstaller", "TShockInstaller\TShockInstaller.csproj", "{17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TShockPluginManager", "TShockPluginManager\TShockPluginManager.csproj", "{9FFABC7D-B042-4B58-98F5-7FA787B9A757}"
@@ -90,22 +88,6 @@ Global
{2A312452-A43F-43E3-8AEB-E22F9A35C210}.Release|x64.Build.0 = Release|Any CPU
{2A312452-A43F-43E3-8AEB-E22F9A35C210}.Release|x86.ActiveCfg = Release|Any CPU
{2A312452-A43F-43E3-8AEB-E22F9A35C210}.Release|x86.Build.0 = Release|Any CPU
- {90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}.Debug|x64.ActiveCfg = Debug|Any CPU
- {90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}.Debug|x64.Build.0 = Debug|Any CPU
- {90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}.Debug|x86.ActiveCfg = Debug|Any CPU
- {90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}.Debug|x86.Build.0 = Debug|Any CPU
- {90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}.Release|Any CPU.Build.0 = Release|Any CPU
- {90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}.Release|x64.ActiveCfg = Release|Any CPU
- {90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}.Release|x64.Build.0 = Release|Any CPU
- {90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}.Release|x86.ActiveCfg = Release|Any CPU
- {90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}.Release|x86.Build.0 = Release|Any CPU
{17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs
index c5e241561..e5d487ab5 100644
--- a/TShockAPI/Bouncer.cs
+++ b/TShockAPI/Bouncer.cs
@@ -15,6 +15,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
+
using System;
using System.Collections.Generic;
using System.Linq;
@@ -28,6 +29,7 @@ You should have received a copy of the GNU General Public License
using Terraria.Localization;
using TShockAPI.Models.PlayerUpdate;
using System.Threading.Tasks;
+using NuGet.Protocol.Plugins;
using OTAPI;
using Terraria.GameContent.Tile_Entities;
@@ -165,11 +167,12 @@ internal Bouncer()
var usingBiomeTorches = player.UsingBiomeTorches;
player.UsingBiomeTorches = true;
// BiomeTorchPlaceStyle returns the place style of the player's current biome's biome torch
- var biomeTorchPlaceStyle = player.BiomeTorchPlaceStyle(actualItemPlaceStyle);
+ var type = 114514; // Not used
+ player.BiomeTorchPlaceStyle(ref type ,ref actualItemPlaceStyle);
// Reset UsingBiomeTorches value
player.UsingBiomeTorches = usingBiomeTorches;
- return biomeTorchPlaceStyle;
+ return actualItemPlaceStyle;
}
else
{
@@ -255,6 +258,19 @@ internal Bouncer()
}
});
+ PlaceStyleCorrectors.Add(TileID.Grass,
+ (player, requestedPlaceStyle, actualItemPlaceStyle) =>
+ {
+ if (player.selectedItem is (ItemID.AcornAxe or ItemID.StaffofRegrowth) &&
+ actualItemPlaceStyle is <= 6 and >= 0)
+ {
+ return actualItemPlaceStyle;
+ }
+
+ return requestedPlaceStyle;
+
+ });
+
#region PlayerAddBuff Whitelist
PlayerAddBuffWhitelist = new BuffLimit[Terraria.ID.BuffID.Count];
@@ -456,7 +472,7 @@ internal void OnGetSection(object sender, GetDataHandlers.GetSectionEventArgs ar
}
args.Player.RequestedSection = true;
- if (String.IsNullOrEmpty(args.Player.Name))
+ if (string.IsNullOrEmpty(args.Player.Name))
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnGetSection rejected empty player name."));
args.Player.Kick(GetString("Your client sent a blank character name."), true, true);
@@ -482,7 +498,7 @@ internal void OnPlayerUpdate(object sender, GetDataHandlers.PlayerUpdateEventArg
var pos = args.Position;
var vel = args.Velocity;
- if (Single.IsInfinity(vel.X) || Single.IsInfinity(vel.Y))
+ if (float.IsInfinity(vel.X) || float.IsInfinity(vel.Y))
{
TShock.Log.ConsoleInfo(GetString("Bouncer / OnPlayerUpdate force kicked (attempted to set velocity to infinity) from {0}", args.Player.Name));
args.Player.Kick(GetString("Detected DOOM set to ON position."), true, true);
@@ -490,7 +506,7 @@ internal void OnPlayerUpdate(object sender, GetDataHandlers.PlayerUpdateEventArg
return;
}
- if (Single.IsNaN(vel.X) || Single.IsNaN(vel.Y))
+ if (float.IsNaN(vel.X) || float.IsNaN(vel.Y))
{
TShock.Log.ConsoleInfo(GetString("Bouncer / OnPlayerUpdate force kicked (attempted to set velocity to NaN) from {0}", args.Player.Name));
args.Player.Kick(GetString("Detected DOOM set to ON position."), true, true);
@@ -609,6 +625,7 @@ internal void OnTileEdit(object sender, GetDataHandlers.TileEditEventArgs args)
EditType type = args.editDetail;
ITile tile = Main.tile[tileX, tileY];
+
// 'placeStyle' is a term used in Terraria land to determine which frame of a sprite is displayed when the sprite is placed. The placeStyle
// determines the frameX and frameY offsets
byte requestedPlaceStyle = args.Style;
@@ -720,7 +737,7 @@ internal void OnTileEdit(object sender, GetDataHandlers.TileEditEventArgs args)
if (action == EditAction.KillTile && !Main.tileCut[tile.type] && !breakableTiles.Contains(tile.type) && args.Player.RecentFuse == 0)
{
// If the tile is an axe tile and they aren't selecting an axe, they're hacking.
- if (Main.tileAxe[tile.type] && ((args.Player.TPlayer.mount.Type != MountID.Drill && selectedItem.axe == 0) && !ItemID.Sets.Explosives[selectedItem.netID]))
+ if (Main.tileAxe[tile.type] && ((args.Player.TPlayer.mount.Type != MountID.Drill && selectedItem.axe == 0) && !ItemID.Sets.Explosives[selectedItem.type]))
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnTileEdit rejected from (axe) {0} {1} {2}", args.Player.Name, action, editData));
args.Player.SendTileSquareCentered(tileX, tileY, 4);
@@ -728,7 +745,7 @@ internal void OnTileEdit(object sender, GetDataHandlers.TileEditEventArgs args)
return;
}
// If the tile is a hammer tile and they aren't selecting a hammer, they're hacking.
- else if (Main.tileHammer[tile.type] && ((args.Player.TPlayer.mount.Type != MountID.Drill && selectedItem.hammer == 0) && !ItemID.Sets.Explosives[selectedItem.netID]))
+ else if (Main.tileHammer[tile.type] && ((args.Player.TPlayer.mount.Type != MountID.Drill && selectedItem.hammer == 0) && !ItemID.Sets.Explosives[selectedItem.type]))
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnTileEdit rejected from (hammer) {0} {1} {2}", args.Player.Name, action, editData));
args.Player.SendTileSquareCentered(tileX, tileY, 4);
@@ -740,13 +757,26 @@ internal void OnTileEdit(object sender, GetDataHandlers.TileEditEventArgs args)
// also add an exception for snake coils, they can be removed when the player places a new one or after x amount of time
// If the tile is part of the breakable when placing set, it might be getting broken by a placement.
else if (tile.type != TileID.ItemFrame && tile.type != TileID.MysticSnakeRope
- && !ItemID.Sets.Explosives[selectedItem.netID]
+ && !ItemID.Sets.Explosives[selectedItem.type]
&& !TileID.Sets.BreakableWhenPlacing[tile.type]
&& !Main.tileAxe[tile.type] && !Main.tileHammer[tile.type] && tile.wall == 0
&& selectedItem.pick == 0 && selectedItem.type != ItemID.GravediggerShovel
&& args.Player.TPlayer.mount.Type != MountID.Drill
&& args.Player.TPlayer.mount.Type != MountID.DiggingMoleMinecart)
{
+ if (args.Player.TPlayer.ownedProjectileCounts[ProjectileID.PalworldDigtoise] > 0)
+ {
+ var digtoiseProjectile = Main.projectile
+ .FirstOrDefault(p =>
+ p != null && p.active && p.type == 1098 && p.owner == args.Player.Index);
+
+ if (digtoiseProjectile?.ai[0] is 1f or 2f or 3f
+ && digtoiseProjectile.ai[1] > 40f)
+ {
+ return;
+ }
+ }
+
TShock.Log.ConsoleDebug(GetString("Bouncer / OnTileEdit rejected from (pick) {0} {1} {2}", args.Player.Name, action,
editData));
args.Player.SendTileSquareCentered(tileX, tileY, 4);
@@ -757,7 +787,7 @@ internal void OnTileEdit(object sender, GetDataHandlers.TileEditEventArgs args)
else if (action == EditAction.KillWall)
{
// If they aren't selecting a hammer, they could be hacking.
- if (selectedItem.hammer == 0 && !ItemID.Sets.Explosives[selectedItem.netID] && args.Player.RecentFuse == 0 && selectedItem.createWall == 0)
+ if (selectedItem.hammer == 0 && !ItemID.Sets.Explosives[selectedItem.type] && args.Player.RecentFuse == 0 && selectedItem.createWall == 0)
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnTileEdit rejected from (hammer2) {0} {1} {2}", args.Player.Name, action, editData));
args.Player.SendTileSquareCentered(tileX, tileY, 1);
@@ -765,7 +795,7 @@ internal void OnTileEdit(object sender, GetDataHandlers.TileEditEventArgs args)
return;
}
}
- else if (action == EditAction.PlaceTile && (projectileCreatesTile.ContainsKey(lastKilledProj) && editData == projectileCreatesTile[lastKilledProj]))
+ else if (action == EditAction.PlaceTile && projectileCreatesTile.TryGetValue(lastKilledProj, out int value) && editData == value)
{
args.Player.LastKilledProjectile = 0;
}
@@ -774,11 +804,11 @@ internal void OnTileEdit(object sender, GetDataHandlers.TileEditEventArgs args)
// Handle placement if the user is placing rope that comes from a ropecoil,
// but have not created the ropecoil projectile recently or the projectile was not at the correct coordinate, or the tile that the projectile places does not match the rope it is suposed to place
// projectile should be the same X coordinate as all tile places (Note by @Olink)
- if (ropeCoilPlacements.ContainsKey(selectedItem.netID) &&
+ if (ropeCoilPlacements.ContainsKey(selectedItem.type) &&
!args.Player.RecentlyCreatedProjectiles.Any(p => GetDataHandlers.projectileCreatesTile.ContainsKey(p.Type) && GetDataHandlers.projectileCreatesTile[p.Type] == editData &&
!p.Killed && Math.Abs((int)(Main.projectile[p.Index].position.X / 16f) - tileX) <= Math.Abs(Main.projectile[p.Index].velocity.X)))
{
- TShock.Log.ConsoleDebug(GetString("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(GetString("Bouncer / OnTileEdit rejected from (inconceivable rope coil) {0} {1} {2} selectedItem:{3} itemCreateTile:{4}", args.Player.Name, action, editData, selectedItem.type, selectedItem.createTile));
args.Player.SendTileSquareCentered(tileX, tileY, 1);
args.Handled = true;
return;
@@ -795,29 +825,66 @@ internal void OnTileEdit(object sender, GetDataHandlers.TileEditEventArgs args)
return;
}
- /// Handle placement action if the player is using an Ice Rod but not placing the iceblock.
- if (selectedItem.netID == ItemID.IceRod && editData != TileID.MagicalIceBlock)
+ // Handle placement action for Regrowth tools to ensure they only replant herbs on valid containers.
+ if (selectedItem.type is ItemID.AcornAxe or ItemID.StaffofRegrowth)
+ {
+ if ((int)editData is not (TileID.Grass or TileID.HallowedGrass or TileID.CorruptGrass
+ or TileID.CrimsonGrass or TileID.JungleGrass or TileID.MushroomGrass
+ or TileID.CorruptJungleGrass or TileID.CrimsonJungleGrass or TileID.AshGrass)
+ && !TileID.Sets.Conversion.Moss[editData])
+ {
+ if (editData != TileID.ImmatureHerbs)
+ {
+ TShock.Log.ConsoleDebug(GetString(
+ "Bouncer / OnTileEdit rejected {0} from placing non-herb tile {1} using {2}",
+ args.Player.Name, editData, selectedItem.Name));
+ args.Player.SendTileSquareCentered(tileX, tileY, 4);
+ args.Handled = true;
+ }
+
+ var containerTile = Main.tile[tileX, tileY + 1];
+ if (!containerTile.active() ||
+ containerTile.type is not (TileID.ClayPot or TileID.RockGolemHead or TileID.PlanterBox))
+ {
+ TShock.Log.ConsoleDebug(GetString(
+ "Bouncer / OnTileEdit rejected {0} from planting herb on invalid tile {1} using {2}",
+ args.Player.Name, containerTile.type, selectedItem.Name));
+ args.Player.SendTileSquareCentered(tileX, tileY, 4);
+ args.Handled = true;
+ }
+ }
+ }
+
+ if (selectedItem.type == ItemID.AcornSlingshot && editData != TileID.Saplings)
+ {
+ TShock.Log.ConsoleDebug(GetString("Bouncer / OnTileEdit rejected from using acorn slingshot but not placing saplings {0} {1} {2}", args.Player.Name, action, editData));
+ args.Player.SendTileSquareCentered(tileX, tileY, 4);
+ args.Handled = true;
+ }
+
+ // Handle placement action if the player is using an Ice Rod but not placing the iceblock.
+ if (selectedItem.type == ItemID.IceRod && editData != TileID.MagicalIceBlock)
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnTileEdit rejected from using ice rod but not placing ice block {0} {1} {2}", args.Player.Name, action, editData));
args.Player.SendTileSquareCentered(tileX, tileY, 4);
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.
if ((action == EditAction.PlaceTile || action == EditAction.ReplaceTile) && editData != selectedItem.createTile)
{
- /// These would get caught up in the below check because Terraria does not set their createTile field.
- if (selectedItem.netID != ItemID.IceRod && selectedItem.netID != ItemID.DirtBomb && selectedItem.netID != ItemID.StickyBomb && (args.Player.TPlayer.mount.Type != MountID.DiggingMoleMinecart || editData != TileID.MinecartTrack))
+ // These would get caught up in the below check because Terraria does not set their createTile field.
+ if (selectedItem.type != ItemID.IceRod && selectedItem.type != ItemID.DirtBomb && selectedItem.type != ItemID.StickyBomb && (args.Player.TPlayer.mount.Type != MountID.DiggingMoleMinecart || editData != TileID.MinecartTrack) && selectedItem.type != ItemID.AcornAxe && selectedItem.type != ItemID.StaffofRegrowth && selectedItem.type != ItemID.AcornSlingshot)
{
- TShock.Log.ConsoleDebug(GetString("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(GetString("Bouncer / OnTileEdit rejected from tile placement not matching selected item createTile {0} {1} {2} selectedItemID:{3} createTile:{4}", args.Player.Name, action, editData, selectedItem.type, selectedItem.createTile));
args.Player.SendTileSquareCentered(tileX, tileY, 4);
args.Handled = true;
return;
}
}
- /// If they aren't selecting the item which creates the wall, they're hacking.
+ // If they aren't selecting the item which creates the wall, they're hacking.
if ((action == EditAction.PlaceWall || action == EditAction.ReplaceWall) && editData != selectedItem.createWall)
{
- TShock.Log.ConsoleDebug(GetString("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(GetString("Bouncer / OnTileEdit rejected from wall placement not matching selected item createWall {0} {1} {2} selectedItemID:{3} createWall:{4}", args.Player.Name, action, editData, selectedItem.type, selectedItem.createWall));
args.Player.SendTileSquareCentered(tileX, tileY, 4);
args.Handled = true;
return;
@@ -917,7 +984,7 @@ internal void OnTileEdit(object sender, GetDataHandlers.TileEditEventArgs args)
return;
}
- if (action == EditAction.KillTile || action == EditAction.KillWall && ItemID.Sets.Explosives[selectedItem.netID] && args.Player.RecentFuse == 0)
+ if (action == EditAction.KillTile || action == EditAction.KillWall && ItemID.Sets.Explosives[selectedItem.type] && args.Player.RecentFuse == 0)
{
args.Handled = false;
return;
@@ -1003,6 +1070,8 @@ internal void OnTileEdit(object sender, GetDataHandlers.TileEditEventArgs args)
}
}
args.Handled = false;
+
+
return;
}
catch
@@ -1124,42 +1193,43 @@ internal void OnItemDrop(object sender, GetDataHandlers.ItemDropEventArgs args)
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnItemDrop rejected from prefix check from {0}", args.Player.Name));
- args.Player.SendData(PacketTypes.ItemDrop, "", id);
+ args.Player.SendData(PacketTypes.SyncItemDespawn, "", id);
args.Handled = true;
return;
}
//Item removed, let client do this to prevent item duplication
// client side (but only if it passed the range check) (i.e., return false)
- if (type == 0)
- {
- if (!args.Player.IsInRange((int)(Main.item[id].position.X / 16f), (int)(Main.item[id].position.Y / 16f)))
- {
- // Causes item duplications. Will be re added if necessary
- //args.Player.SendData(PacketTypes.ItemDrop, "", id);
- TShock.Log.ConsoleDebug(GetString("Bouncer / OnItemDrop rejected from dupe range check from {0}", args.Player.Name));
- args.Handled = true;
- return;
- }
-
- args.Handled = false;
- return;
- }
-
- if (!args.Player.IsInRange((int)(pos.X / 16f), (int)(pos.Y / 16f)))
+ // if (type == 0)
+ // {
+ // if (!args.Player.IsInRange((int)(Main.item[id].position.X / 16f), (int)(Main.item[id].position.Y / 16f)))
+ // {
+ // // Causes item duplications. Will be re added if necessary
+ // //args.Player.SendData(PacketTypes.ItemDrop, "", id);
+ // TShock.Log.ConsoleDebug(GetString("Bouncer / OnItemDrop rejected from dupe range check from {0}", args.Player.Name));
+ // args.Handled = true;
+ // return;
+ // }
+ //
+ // args.Handled = false;
+ // return;
+ // }
+
+ if (!args.Player.IsInRange((int)(pos.X / 16f), (int)(pos.Y / 16f), 128))
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnItemDrop rejected from range check from {0}", args.Player.Name));
- args.Player.SendData(PacketTypes.ItemDrop, "", id);
+ args.Player.SendData(PacketTypes.SyncItemDespawn, "", id);
args.Handled = true;
return;
}
// stop the client from changing the item type of a drop but
// only if the client isn't picking up the item
- if (Main.item[id].active && Main.item[id].netID != type)
+ if (Main.item[id].active && Main.item[id].type != type &&
+ !(Main.item[id].type == ItemID.EmptyBucket && type == ItemID.WaterBucket)) // Empty bucket turns into Water Bucket on rainy days
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnItemDrop rejected from item drop/pickup check from {0}", args.Player.Name));
- args.Player.SendData(PacketTypes.ItemDrop, "", id);
+ args.Player.SendData(PacketTypes.SyncItemDespawn, "", id);
args.Handled = true;
return;
}
@@ -1169,7 +1239,7 @@ internal void OnItemDrop(object sender, GetDataHandlers.ItemDropEventArgs args)
if ((stacks > item.maxStack || stacks <= 0) || (TShock.ItemBans.DataModel.ItemIsBanned(EnglishLanguage.GetItemNameById(item.type), args.Player) && !args.Player.HasPermission(Permissions.allowdroppingbanneditems)))
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnItemDrop rejected from drop item ban check / max stack check / min stack check from {0}", args.Player.Name));
- args.Player.SendData(PacketTypes.ItemDrop, "", id);
+ args.Player.SendData(PacketTypes.SyncItemDespawn, "", id);
args.Handled = true;
return;
}
@@ -1180,7 +1250,7 @@ internal void OnItemDrop(object sender, GetDataHandlers.ItemDropEventArgs args)
//Player is probably trying to sneak items onto the server in their hands!!!
TShock.Log.ConsoleInfo(GetString("Player {0} tried to sneak {1} onto the server!", args.Player.Name, item.Name));
TShock.Log.ConsoleDebug(GetString("Bouncer / OnItemDrop rejected from sneaky from {0}", args.Player.Name));
- args.Player.SendData(PacketTypes.ItemDrop, "", id);
+ args.Player.SendData(PacketTypes.SyncItemDespawn, "", id);
args.Handled = true;
return;
@@ -1189,7 +1259,7 @@ internal void OnItemDrop(object sender, GetDataHandlers.ItemDropEventArgs args)
if (args.Player.IsBeingDisabled())
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnItemDrop rejected from disabled from {0}", args.Player.Name));
- args.Player.SendData(PacketTypes.ItemDrop, "", id);
+ args.Player.SendData(PacketTypes.SyncItemDespawn, "", id);
args.Handled = true;
return;
}
@@ -1261,7 +1331,7 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA
return;
}
- /// If the projectile is a directional projectile, check if the player is holding their respected item to validate the projectile creation.
+ // If the projectile is a directional projectile, check if the player is holding their respected item to validate the projectile creation.
if (directionalProjectiles.ContainsKey(type))
{
if (directionalProjectiles[type] == args.Player.TPlayer.HeldItem.type)
@@ -1271,19 +1341,20 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA
}
}
- /// If the created projectile is a golf club, check if the player is holding one of the golf club items to validate the projectile creation.
+ // If the created projectile is a golf club, check if the player is holding one of the golf club items to validate the projectile creation.
if (type == ProjectileID.GolfClubHelper && Handlers.LandGolfBallInCupHandler.GolfClubItemIDs.Contains(args.Player.TPlayer.HeldItem.type))
{
args.Handled = false;
return;
}
- /// If the created projectile is a golf ball and the player is not holding a golf club item and neither a golf ball item and neither they have had a golf club projectile created recently.
+ // If the created projectile is a golf ball and the player is not holding a golf club item and neither a golf ball item and neither they have had a golf club projectile created recently.
if (Handlers.LandGolfBallInCupHandler.GolfBallProjectileIDs.Contains(type) &&
!Handlers.LandGolfBallInCupHandler.GolfClubItemIDs.Contains(args.Player.TPlayer.HeldItem.type) &&
!Handlers.LandGolfBallInCupHandler.GolfBallItemIDs.Contains(args.Player.TPlayer.HeldItem.type) &&
!args.Player.RecentlyCreatedProjectiles.Any(p => p.Type == ProjectileID.GolfClubHelper))
{
+ return;
TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile please report to tshock about this! normally this is a reject from {0} {1} (golf)", args.Player.Name, type));
}
@@ -1352,11 +1423,13 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA
|| (type >= ProjectileID.MartianTurretBolt && type <= ProjectileID.RayGunnerLaser)
|| type == ProjectileID.CultistBossLightningOrb)
{
+ return;
TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile rejected from weird check from {0} {1}", args.Player.Name, type));
TShock.Log.Debug(GetString("Certain projectiles have been ignored for cheat detection."));
}
else
{
+ return;
TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile please report to tshock about this! normally this is a reject from {0} {1}", args.Player.Name, type));
// args.Player.Disable(String.Format("Does not have projectile permission to update projectile. ({0})", type), DisableFlags.WriteToLogAndConsole);
// args.Player.RemoveProjectile(ident, owner);
@@ -1695,13 +1768,13 @@ internal void OnPlayerZone(object sender, GetDataHandlers.PlayerZoneEventArgs ar
foreach (var npc in Main.npc)
{
- if (npc.netID == NPCID.LunarTowerSolar)
+ if (npc.type == NPCID.LunarTowerSolar)
hasSolarTower = true;
- else if (npc.netID == NPCID.LunarTowerVortex)
+ else if (npc.type == NPCID.LunarTowerVortex)
hasVortexTower = true;
- else if (npc.netID == NPCID.LunarTowerNebula)
+ else if (npc.type == NPCID.LunarTowerNebula)
hasNebulaTower = true;
- else if (npc.netID == NPCID.LunarTowerStardust)
+ else if (npc.type == NPCID.LunarTowerStardust)
hasStardustTower = true;
}
@@ -2082,52 +2155,57 @@ internal void OnNPCAddBuff(object sender, GetDataHandlers.NPCAddBuffEventArgs ar
return;
}
- if (!args.Player.HasPermission(Permissions.ignorenpcbuffdetection))
+ // "TorchSlime" will add a super long burn buff
+ if (type == BuffID.OnFire && Main.getGoodWorld && npc.aiStyle == 1 && (short)npc.ai[1] == ItemID.Torch)
{
- bool detectedNPCBuffTimeCheat = false;
+ return;
+ }
- if (NPCAddBuffTimeMax.ContainsKey(type))
- {
- if (time > NPCAddBuffTimeMax[type])
- {
- detectedNPCBuffTimeCheat = true;
- }
+ bool detectedNPCBuffTimeCheat = false;
- if (npc.townNPC)
- {
- if (type != BuffID.Poisoned
- && type != BuffID.OnFire
- && type != BuffID.Confused
- && type != BuffID.CursedInferno
- && type != BuffID.Ichor
- && type != BuffID.Venom
- && type != BuffID.Midas
- && type != BuffID.Wet
- && type != BuffID.Lovestruck
- && type != BuffID.Stinky
- && type != BuffID.Slimed
- && type != BuffID.DryadsWard
- && type != BuffID.GelBalloonBuff
- && type != BuffID.OnFire3
- && type != BuffID.Frostburn2
- && type != BuffID.Shimmer)
- {
- detectedNPCBuffTimeCheat = true;
- }
- }
- }
- else
+ if (NPCAddBuffTimeMax.ContainsKey(type))
+ {
+ if (time > NPCAddBuffTimeMax[type])
{
detectedNPCBuffTimeCheat = true;
}
- if (detectedNPCBuffTimeCheat)
- {
- TShock.Log.ConsoleDebug(GetString("Bouncer / OnNPCAddBuff rejected abnormal buff ({0}, last for {4}) added to {1} ({2}) from {3}.", type, npc.TypeName, npc.netID, args.Player.Name, time));
- args.Player.Kick(GetString($"Added buff to {npc.TypeName} NPC abnormally."), true);
- args.Handled = true;
+ if (npc.townNPC)
+ {
+ if (type != BuffID.Poisoned
+ && type != BuffID.OnFire
+ && type != BuffID.Confused
+ && type != BuffID.CursedInferno
+ && type != BuffID.Ichor
+ && type != BuffID.Venom
+ && type != BuffID.Midas
+ && type != BuffID.Wet
+ && type != BuffID.Lovestruck
+ && type != BuffID.Stinky
+ && type != BuffID.Slimed
+ && type != BuffID.DryadsWard
+ && type != BuffID.GelBalloonBuff
+ && type != BuffID.OnFire3
+ && type != BuffID.Frostburn2
+ && type != BuffID.Shimmer)
+ {
+ detectedNPCBuffTimeCheat = true;
+ }
}
}
+ else
+ {
+ detectedNPCBuffTimeCheat = true;
+ }
+
+ if (detectedNPCBuffTimeCheat)
+ {
+ TShock.Log.ConsoleDebug(GetString(
+ "Bouncer / OnNPCAddBuff rejected abnormal buff ({0}, last for {4}) added to {1} ({2}) from {3}.",
+ type, npc.TypeName, npc.type, args.Player.Name, time));
+ args.Player.Kick($"Added buff {type} (last for {time}) to {npc.TypeName} NPC abnormally.", true);
+ args.Handled = true;
+ }
}
/// The Bouncer handler for when an NPC is rehomed.
@@ -2389,6 +2467,16 @@ internal void OnPlaceObject(object sender, GetDataHandlers.PlaceObjectEventArgs
return;
}
}
+ else if (type == TileID.GardenGnome)
+ {
+ if (style is > 4 or < 0)
+ {
+ TShock.Log.ConsoleDebug(GetString("Bouncer / OnPlaceObject rejected {0} due to invalid garden gnome style {1}", args.Player.Name, style));
+ args.Player.SendTileSquareCentered(x, y, 4);
+ args.Handled = true;
+ return;
+ }
+ }
else
{
// This is necessary to check in order to prevent special tiles such as
@@ -2404,8 +2492,11 @@ internal void OnPlaceObject(object sender, GetDataHandlers.PlaceObjectEventArgs
if (args.Player.SelectedItem.placeStyle != style)
{
- var validTorch = args.Player.SelectedItem.createTile == TileID.Torches && args.Player.TPlayer.BiomeTorchPlaceStyle(args.Player.SelectedItem.placeStyle) == style;
- var validCampfire = args.Player.SelectedItem.createTile == TileID.Campfire && args.Player.TPlayer.BiomeCampfirePlaceStyle(args.Player.SelectedItem.placeStyle) == style;
+ var uselessType = 114514;
+ args.Player.TPlayer.BiomeTorchPlaceStyle(ref uselessType, ref args.Player.SelectedItem.placeStyle);
+
+ var validTorch = args.Player.SelectedItem.createTile == TileID.Torches && args.Player.SelectedItem.placeStyle == style;
+ var validCampfire = args.Player.SelectedItem.createTile == TileID.Campfire && args.Player.SelectedItem.placeStyle == style;
if (!args.Player.TPlayer.unlockedBiomeTorches || (!validTorch && !validCampfire))
{
TShock.Log.ConsoleError(GetString("Bouncer / OnPlaceObject rejected object placement with invalid style {1} (expected {2}) from {0}", args.Player.Name, style, args.Player.SelectedItem.placeStyle));
@@ -2815,7 +2906,7 @@ internal void OnKillMe(object sender, GetDataHandlers.KillMeEventArgs args)
///
internal void OnFishOutNPC(object sender, GetDataHandlers.FishOutNPCEventArgs args)
{
- /// Getting recent projectiles of the player and selecting the first that is a bobber.
+ // Getting recent projectiles of the player and selecting the first that is a bobber.
var projectile = args.Player.RecentlyCreatedProjectiles.FirstOrDefault(p => Main.projectile[p.Index].bobber);
if (!FishingRodItemIDs.Contains(args.Player.SelectedItem.type))
@@ -2824,7 +2915,7 @@ internal void OnFishOutNPC(object sender, GetDataHandlers.FishOutNPCEventArgs ar
args.Handled = true;
return;
}
- if (projectile.Type == 0 || projectile.Killed) /// The bobber projectile is never killed when the NPC spawns. Type can only be 0 if no recent projectile is found that is named Bobber.
+ if (projectile.Type == 0 || projectile.Killed) // The bobber projectile is never killed when the NPC spawns. Type can only be 0 if no recent projectile is found that is named Bobber.
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnFishOutNPC rejected for not finding active bobber projectile! - From {0}", args.Player.Name));
args.Handled = true;
@@ -2973,42 +3064,39 @@ internal static int GetMaxPlaceStyle(int tileID)
// Moved to Projectile.StatusNPC(int i).
private static Dictionary NPCAddBuffTimeMax = new Dictionary()
{
- { BuffID.Poisoned, 3600 }, // BuffID: 20
- { BuffID.OnFire, 1200 }, // BuffID: 24
- { BuffID.Confused, short.MaxValue }, // BuffID: 31 Brain of Confusion Internal Item ID: 3223
- { BuffID.CursedInferno, 600 }, // BuffID: 39
- { BuffID.Frostburn, 900 }, // BuffID: 44
- { BuffID.Ichor, 1200 }, // BuffID: 69
- { BuffID.Venom, 1800 }, // BuffID: 70
- { BuffID.Midas, 120 }, // BuffID: 72
- { BuffID.Wet, 1500 }, // BuffID: 103
- { BuffID.Lovestruck, 1800 }, // BuffID: 119
- { BuffID.Stinky, 1800 }, // BuffID: 120
- { BuffID.Slimed, 1500 }, // BuffID: 137
- { BuffID.SoulDrain, 30 }, // BuffID: 151
- { BuffID.ShadowFlame, 660 }, // BuffID: 153
- { BuffID.DryadsWard, 120 }, // BuffID: 165
- { BuffID.BoneJavelin, 900 }, // BuffID: 169
- { BuffID.StardustMinionBleed, 900 }, // BuffID: 183
- { BuffID.DryadsWardDebuff, 120 }, // BuffID: 186
- { BuffID.Daybreak, 300 }, // BuffID: 189 Solar Eruption Item ID: 3473, Daybreak Item ID: 3543
- { BuffID.BetsysCurse, 600 }, // BuffID: 203
- { BuffID.Oiled, 540 }, // BuffID: 204
- { BuffID.BlandWhipEnemyDebuff, 240 }, // BuffID: 307
- { BuffID.SwordWhipNPCDebuff, 240 }, // BuffID: 309
- { BuffID.ScytheWhipEnemyDebuff, 240 }, // BuffID: 310
- { BuffID.FlameWhipEnemyDebuff, 240 }, // BuffID: 313
- { BuffID.ThornWhipNPCDebuff, 240 }, // BuffID: 315
- { BuffID.RainbowWhipNPCDebuff, 240 }, // BuffID: 316
- { BuffID.MaceWhipNPCDebuff, 240 }, // BuffID: 319
- { BuffID.GelBalloonBuff, 1800 }, // BuffID: 320
- { BuffID.OnFire3, 1200 }, // BuffID: 323
- { BuffID.Frostburn2, 1200 }, // BuffID: 324
- { BuffID.BoneWhipNPCDebuff, 240 }, // BuffID: 326
- { BuffID.TentacleSpike, 540 }, // BuffID: 337
- { BuffID.CoolWhipNPCDebuff, 240 }, // BuffID: 340
- { BuffID.BloodButcherer, 540 }, // BuffID: 344
- { BuffID.Shimmer, 100 }, // BuffID: 353
+ { BuffID.Shimmer, 100 },
+ { BuffID.Venom, 1800 },
+ { BuffID.CursedInferno, 600 },
+ { BuffID.OnFire, 19392 }, // It's supposed to be 600, but it's actually 19392. And I can't find 19132 anywhere.
+ { BuffID.Ichor, 1140 },
+ { BuffID.Confused, 16684 },
+ { BuffID.Poisoned, 3600 },
+ { BuffID.Midas, 120 },
+ { BuffID.Bleeding, 720 },
+ { BuffID.Frostburn2, 1200 },
+ { BuffID.OnFire3, 1200 },
+ { BuffID.Stinky, 1800 },
+ { BuffID.Slimed, 180 },
+ { BuffID.Hemorrhage, 720 },
+ { BuffID.BrokenArmor, 1200 },
+ { BuffID.BoneJavelin, 900 },
+ { BuffID.Daybreak, 300 },
+ { BuffID.TentacleSpike, 540 },
+ { BuffID.BloodButcherer, 540 },
+ { BuffID.BetsysCurse, 600 },
+ { BuffID.StardustMinionBleed, 900 },
+ { BuffID.ShadowFlame, 600 },
+ { BuffID.Frostburn, 239 },
+ { BuffID.Oiled, 510 },
+ { BuffID.SoulDrain, 30 },
+ { BuffID.EelWhipNPCDebuff, 240 },
+ { BuffID.ScytheWhipEnemyDebuff, 240 },
+ { BuffID.Wet, 1500 },
+ { BuffID.DryadsWard, 120 },
+ { BuffID.DryadsWardDebuff, 120 },
+ { BuffID.Tipsy, 3659 },
+ { BuffID.Lovestruck, 1800 },
+ { BuffID.GelBalloonBuff, 1800 },
};
///
@@ -3057,7 +3145,7 @@ internal static int GetMaxPlaceStyle(int tileID)
///
private static Dictionary directionalProjectiles = new Dictionary()
{
- ///Spears
+ //Spears
{ ProjectileID.DarkLance, ItemID.DarkLance},
{ ProjectileID.Trident, ItemID.Trident},
{ ProjectileID.Spear, ItemID.Spear},
@@ -3077,7 +3165,7 @@ internal static int GetMaxPlaceStyle(int tileID)
{ ProjectileID.MonkStaffT2, ItemID.MonkStaffT2},
{ ProjectileID.ThunderSpear, ItemID.ThunderSpear},
{ ProjectileID.GladiusStab, ItemID.Gladius},
- /// ShortSwords
+ // ShortSwords
{ ProjectileID.RulerStab, ItemID.Ruler },
{ ProjectileID.CopperShortswordStab, ItemID.CopperShortsword },
{ ProjectileID.TinShortswordStab, ItemID.TinShortsword },
diff --git a/TShockAPI/CLI/CommandLineParser.cs b/TShockAPI/CLI/CommandLineParser.cs
index d62917fb6..6a80ff067 100644
--- a/TShockAPI/CLI/CommandLineParser.cs
+++ b/TShockAPI/CLI/CommandLineParser.cs
@@ -147,12 +147,11 @@ public CommandLineParser After(Action callback)
///
public T Get(FlagSet flags)
{
- if (!_results.ContainsKey(flags))
+ if (!_results.TryGetValue(flags, out object result))
{
return default(T);
}
- object result = _results[flags];
Type t = typeof(T);
if (t == typeof(string))
diff --git a/TShockAPI/CLI/FlagSet.cs b/TShockAPI/CLI/FlagSet.cs
index ed559f3df..1a1c187f2 100644
--- a/TShockAPI/CLI/FlagSet.cs
+++ b/TShockAPI/CLI/FlagSet.cs
@@ -43,10 +43,7 @@ public class FlagSet : IEquatable
/// Flags represented by this FlagSet
public FlagSet(params string[] flags)
{
- if (flags == null)
- {
- throw new ArgumentNullException(nameof(flags));
- }
+ ArgumentNullException.ThrowIfNull(flags);
_flags = flags.Select(f => f.ToLowerInvariant());
}
diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs
index 98cc639d1..8e1a594ec 100644
--- a/TShockAPI/Commands.cs
+++ b/TShockAPI/Commands.cs
@@ -35,7 +35,9 @@ You should have received a copy of the GNU General Public License
using Microsoft.Xna.Framework;
using TShockAPI.Localization;
using System.Text.RegularExpressions;
+using Terraria.Chat.Commands;
using Terraria.DataStructures;
+using Terraria.GameContent;
using Terraria.GameContent.Creative;
namespace TShockAPI
@@ -113,8 +115,7 @@ public CommandDelegate CommandDelegate
get { return commandDelegate; }
set
{
- if (value == null)
- throw new ArgumentNullException();
+ ArgumentNullException.ThrowIfNull(value);
commandDelegate = value;
}
@@ -134,10 +135,9 @@ public Command(string permissions, CommandDelegate cmd, params string[] names)
public Command(CommandDelegate cmd, params string[] names)
{
- if (cmd == null)
- throw new ArgumentNullException("cmd");
+ ArgumentNullException.ThrowIfNull(cmd);
if (names == null || names.Length < 1)
- throw new ArgumentException("names");
+ throw new ArgumentException("is null or empty", nameof(names));
AllowServer = true;
CommandDelegate = cmd;
@@ -647,6 +647,10 @@ public static void InitCommands()
{
HelpText = GetString("Shows the number of PVP deaths for all online players."),
});
+ add(new Command(BossDamage, "bossdamage")
+ {
+ HelpText = GetString("Shows recent boss kill contribution."),
+ });
TShockCommands = new ReadOnlyCollection(tshockCommands);
}
@@ -691,11 +695,10 @@ public static bool HandleCommand(TSPlayer player, string text)
if (Hooks.PlayerHooks.OnPlayerCommand(player, cmdName, cmdText, args, ref cmds, cmdPrefix))
return true;
- if (cmds.Count() == 0)
+ if (!cmds.Any())
{
- if (player.AwaitingResponse.ContainsKey(cmdName))
+ if (player.AwaitingResponse.TryGetValue(cmdName, out Action