Skip to content

Commit 2ed2f36

Browse files
committed
fix(Bouncer): Players bypass region protection and build permissions when using Quick Stack
1 parent 58f755c commit 2ed2f36

File tree

2 files changed

+112
-1
lines changed

2 files changed

+112
-1
lines changed

TShockAPI/Bouncer.cs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ internal Bouncer()
137137
GetDataHandlers.KillMe += OnKillMe;
138138
GetDataHandlers.FishOutNPC += OnFishOutNPC;
139139
GetDataHandlers.FoodPlatterTryPlacing += OnFoodPlatterTryPlacing;
140+
GetDataHandlers.ForceItemIntoNearestChest += OnForceItemIntoNearestChest;
140141

141142

142143
// The following section is based off Player.PlaceThing_Tiles_PlaceIt and Player.PlaceThing_Tiles_PlaceIt_GetLegacyTileStyle.
@@ -2505,7 +2506,7 @@ internal void OnPlaceItemFrame(object sender, GetDataHandlers.PlaceItemFrameEven
25052506
Main.item[num].playerIndexTheItemIsReservedFor = args.Player.Index;
25062507
NetMessage.SendData((int)PacketTypes.ItemDrop, args.Player.Index, -1, NetworkText.Empty, num, 1f);
25072508
NetMessage.SendData((int)PacketTypes.ItemOwner, args.Player.Index, -1, NetworkText.Empty, num);
2508-
2509+
25092510
TShock.Log.ConsoleDebug(GetString("Bouncer / OnPlaceItemFrame rejected permissions from {0}", args.Player.Name));
25102511
NetMessage.SendData((int)PacketTypes.UpdateTileEntity, -1, -1, NetworkText.Empty, args.ItemFrame.ID, 0, 1);
25112512
args.Handled = true;
@@ -2871,6 +2872,44 @@ internal void OnFoodPlatterTryPlacing(object sender, GetDataHandlers.FoodPlatter
28712872
}
28722873
}
28732874

2875+
/// <summary>
2876+
/// Called when a player is trying to put an item into chest through Quick Stack.
2877+
/// </summary>
2878+
/// <param name="sender"></param>
2879+
/// <param name="args"></param>
2880+
internal void OnForceItemIntoNearestChest(object sender, ForceItemIntoNearestChestEventArgs args)
2881+
{
2882+
var slot = args.Slot;
2883+
2884+
var id = Utils.LocateQuickStackChestIndex(args.Player, slot);
2885+
2886+
if (id == -1)
2887+
return;
2888+
2889+
if (args.Player.IsBeingDisabled())
2890+
{
2891+
TShock.Log.ConsoleDebug(GetString("Bouncer / OnForceItemIntoNearestChest rejected from disable from {0}", args.Player.Name));
2892+
args.Player.SendData(PacketTypes.PlayerSlot, "", args.Player.Index, slot, args.Player.TPlayer.inventory[slot].prefix);
2893+
args.Handled = true;
2894+
return;
2895+
}
2896+
2897+
if (!args.Player.HasBuildPermission(Main.chest[id].x, Main.chest[id].y) && TShock.Config.Settings.RegionProtectChests)
2898+
{
2899+
TShock.Log.ConsoleDebug(GetString("Bouncer / OnForceItemIntoNearestChest rejected from region protection? from {0}", args.Player.Name));
2900+
args.Player.SendData(PacketTypes.PlayerSlot, "", args.Player.Index, slot, args.Player.TPlayer.inventory[slot].prefix);
2901+
args.Handled = true;
2902+
return;
2903+
}
2904+
2905+
if (!args.Player.IsInRange(Main.chest[id].x, Main.chest[id].y))
2906+
{
2907+
TShock.Log.ConsoleDebug(GetString("Bouncer / OnForceItemIntoNearestChest rejected from range check from {0}", args.Player.Name));
2908+
args.Handled = true;
2909+
return;
2910+
}
2911+
}
2912+
28742913
internal void OnSecondUpdate()
28752914
{
28762915
Task.Run(() =>

TShockAPI/Utils.cs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,5 +1202,77 @@ internal void ComputeMaxStyles()
12021202
}
12031203
}
12041204
}
1205+
1206+
/// <summary>
1207+
/// Locates the nearest available chest suitable for quick-stacking items from the player's inventory.
1208+
/// </summary>
1209+
/// <param name="player">The player attempting to quick-stack items.</param>
1210+
/// <param name="slot">The inventory slot of the item being quick-stacked (currently unused in this method).</param>
1211+
/// <returns>
1212+
/// The index of the nearest available chest within range that isn't locked or in use, or -1 if no suitable chest is found.
1213+
/// </returns>
1214+
public static int LocateQuickStackChestIndex(TSPlayer player, short slot)
1215+
{
1216+
const float maxDistance = 600f;
1217+
Item item;
1218+
1219+
if (slot >= PlayerItemSlotID.Bank4_0 && slot < PlayerItemSlotID.Bank4_0 + NetItem.VoidSlots)
1220+
{
1221+
var index = slot - PlayerItemSlotID.Bank4_0;
1222+
item = player.TPlayer.bank4.item[index];
1223+
}
1224+
else
1225+
{
1226+
if (slot >= NetItem.InventorySlots - 1)
1227+
return -1;
1228+
item = player.TPlayer.inventory[slot];
1229+
}
1230+
1231+
for (var i = 0; i < Main.maxChests; i++)
1232+
{
1233+
var chest = Main.chest[i];
1234+
if (chest == null || Chest.IsPlayerInChest(i) || Chest.IsLocked(chest.x, chest.y))
1235+
continue;
1236+
1237+
var chestPos = new Vector2(chest.x * 16 + 16, chest.y * 16 + 16);
1238+
if ((chestPos - player.TPlayer.position).Length() >= maxDistance)
1239+
continue;
1240+
1241+
var canStack = false;
1242+
var hasSpace = false;
1243+
1244+
foreach (var chestItem in chest.item)
1245+
{
1246+
if (chestItem.IsAir)
1247+
{
1248+
hasSpace = true;
1249+
continue;
1250+
}
1251+
1252+
if (item.IsTheSameAs(chestItem))
1253+
{
1254+
canStack = true;
1255+
var space = chestItem.maxStack - chestItem.stack;
1256+
if (space > 0)
1257+
{
1258+
return i;
1259+
}
1260+
}
1261+
else
1262+
{
1263+
hasSpace = true;
1264+
}
1265+
}
1266+
1267+
if (canStack && hasSpace && item.stack > 0)
1268+
{
1269+
if (chest.item.Any(t => t.IsAir))
1270+
{
1271+
return i;
1272+
}
1273+
}
1274+
}
1275+
return -1;
1276+
}
12051277
}
12061278
}

0 commit comments

Comments
 (0)