diff --git a/TShockAPI/Configuration/ServerSideConfig.cs b/TShockAPI/Configuration/ServerSideConfig.cs
index 7008af9b..8e777303 100644
--- a/TShockAPI/Configuration/ServerSideConfig.cs
+++ b/TShockAPI/Configuration/ServerSideConfig.cs
@@ -72,6 +72,10 @@ namespace TShockAPI.Configuration
///
[Description("Warns players and the console if a player has the tshock.ignore.ssc permission with data in the SSC table.")]
public bool WarnPlayersAboutBypassPermission = true;
+
+ /// If set to true, items given to players will be inserted directly into their inventory. Requires SSC. Otherwise, items given to players will spawn as dropped items.
+ [Description("If set to true, items given to players will be inserted directly into their inventory. Requires SSC. Otherwise, items given to players will spawn as dropped items.")]
+ public bool GiveItemsDirectly = false;
}
///
diff --git a/TShockAPI/Configuration/TShockConfig.cs b/TShockAPI/Configuration/TShockConfig.cs
index 21538129..e1781500 100644
--- a/TShockAPI/Configuration/TShockConfig.cs
+++ b/TShockAPI/Configuration/TShockConfig.cs
@@ -160,7 +160,7 @@ namespace TShockAPI.Configuration
/// Disables tombstone dropping during death for all players.
[Description("Disables tombstone dropping during death for all players.")]
public bool DisableTombstones = true;
-
+
///
/// Disables Skeletron Prime Bombs from spawning, useful for preventing unwanted world destruction on for the worthy seed world.
///
diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs
index 73820eaa..39b10d8f 100644
--- a/TShockAPI/TSPlayer.cs
+++ b/TShockAPI/TSPlayer.cs
@@ -1461,7 +1461,7 @@ namespace TShockAPI
}
///
- /// 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.
///
/// The x coordinate the rectangle will begin at
/// The y coordinate the rectangle will begin at
@@ -1508,6 +1508,115 @@ namespace TShockAPI
/// The item stack.
/// The item prefix.
public virtual void GiveItem(int type, int stack, int prefix = 0)
+ {
+ if (TShock.ServerSideCharacterConfig.Settings.GiveItemsDirectly)
+ GiveItemDirectly(type, stack, prefix);
+ else
+ GiveItemByDrop(type, stack, prefix);
+ }
+
+ private Item EmptySentinelItem = new Item();
+
+ private bool Depleted(Item item)
+ => item.type == 0 || item.stack == 0;
+
+ private void GiveItemDirectly(int type, int stack, int prefix)
+ {
+ if (ItemID.Sets.IsAPickup[type] || !Main.ServerSideCharacter || this.IsDisabledForSSC)
+ {
+ GiveItemByDrop(type, stack, prefix);
+ return;
+ }
+
+ var item = new Item();
+ item.netDefaults(type);
+ item.stack = stack;
+ item.prefix = (byte)prefix;
+
+ if (item.IsACoin)
+ for (int slot = -4; slot < 50; slot++)
+ if (Depleted(item = GiveItemDirectly_FillIntoOccupiedSlot(item, slot < 0 ? slot + 54 : slot)))
+ return;
+
+ if (item.FitsAmmoSlot())
+ if (Depleted(item = GiveItem_FillAmmo(item)))
+ return;
+
+ for (int slot = 0; slot < 50; slot++)
+ if (Depleted(item = GiveItemDirectly_FillIntoOccupiedSlot(item, slot)))
+ return;
+
+ if (!item.IsACoin && item.useStyle != 0)
+ for (int slot = 0; slot < 10; slot++)
+ if (Depleted(item = GiveItemDirectly_FillEmptyInventorySlot(item, slot)))
+ return;
+
+ int lastSlot = item.IsACoin ? 54 : 50;
+ for (int slot = lastSlot - 1; slot >= 0; slot--)
+ if (Depleted(item = GiveItemDirectly_FillEmptyInventorySlot(item, slot)))
+ return;
+
+ // oh no, i can't give... guess i gotta spill it on the floor
+ GiveItemByDrop(type, stack, prefix);
+ }
+
+ private void SendItemSlotPacketFor(int slot)
+ {
+ int prefix = this.TPlayer.inventory[slot].prefix;
+ NetMessage.SendData(5, this.Index, -1, null, this.Index, slot, prefix, 0f, 0, 0, 0);
+ }
+
+ private Item GiveItem_FillAmmo(Item item)
+ {
+ var inv = this.TPlayer.inventory;
+
+ for (int i = 54; i < 58; i++)
+ if (Depleted(item = GiveItemDirectly_FillIntoOccupiedSlot(item, i)))
+ return EmptySentinelItem;
+
+ if (!item.CanFillEmptyAmmoSlot())
+ return item;
+
+ for (int i = 54; i < 58; i++)
+ if (GiveItemDirectly_FillEmptyInventorySlot(item, i) == EmptySentinelItem)
+ return EmptySentinelItem;
+
+ return item;
+ }
+
+ private Item GiveItemDirectly_FillIntoOccupiedSlot(Item item, int slot)
+ {
+ var inv = this.TPlayer.inventory;
+ if (inv[slot].type <= 0 || inv[slot].stack >= inv[slot].maxStack || !item.IsTheSameAs(inv[slot]))
+ return item;
+
+ if (item.stack + inv[slot].stack <= inv[slot].maxStack)
+ {
+ inv[slot].stack += item.stack;
+ SendItemSlotPacketFor(slot);
+ return EmptySentinelItem;
+ }
+
+ var newItem = item.DeepClone();
+ newItem.stack -= inv[slot].maxStack - inv[slot].stack;
+ inv[slot].stack = inv[slot].maxStack;
+ SendItemSlotPacketFor(slot);
+
+ return newItem;
+ }
+
+ private Item GiveItemDirectly_FillEmptyInventorySlot(Item item, int slot)
+ {
+ var inv = this.TPlayer.inventory;
+ if (inv[slot].type != 0)
+ return item;
+
+ inv[slot] = item;
+ SendItemSlotPacketFor(slot);
+ return EmptySentinelItem;
+ }
+
+ private void GiveItemByDrop(int type, int stack, int prefix)
{
int itemIndex = Item.NewItem(new EntitySource_DebugCommand(), (int)X, (int)Y, TPlayer.width, TPlayer.height, type, stack, true, prefix, true);
Main.item[itemIndex].playerIndexTheItemIsReservedFor = this.Index;
diff --git a/TerrariaServerAPI b/TerrariaServerAPI
index e87215aa..c6b0fd87 160000
--- a/TerrariaServerAPI
+++ b/TerrariaServerAPI
@@ -1 +1 @@
-Subproject commit e87215aa183dfce43458341f975647cdee5d1710
+Subproject commit c6b0fd879e0d68c5125776ecde89ff3d3f730023
diff --git a/docs/changelog.md b/docs/changelog.md
index 01db9675..f05b840b 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -72,6 +72,7 @@ Use past tense when adding new entries; sign your name off when you add or chang
* Allow flask buffs to be applied on town npc due to the Flymeal. Add a permission could skip the buff detection. (@KawaiiYuyu)
* Dockerize TShock (@PotatoCider)
+* Add ability for items given to players to be inserted directly into their inventory instead of spawned as an item drop (@pontaoski)
## TShock 4.5.18
* Fixed `TSPlayer.GiveItem` not working if the player is in lava. (@PotatoCider)
diff --git a/docs/ssc-config.md b/docs/ssc-config.md
index 8d96a3ea..4e1dbf2e 100644
--- a/docs/ssc-config.md
+++ b/docs/ssc-config.md
@@ -3,6 +3,11 @@ Enable server side characters, causing client data to be saved on the server ins
* **Field type**: `Boolean`
* **Default**: `False`
+## GiveItemsDirectly
+If set to true, items given to players will be inserted directly into their inventory. Requires SSC. Otherwise, items given to players will spawn as dropped items.
+* **Field type**: `Boolean`
+* **Default**: `False`
+
## LogonDiscardThreshold
Time, in milliseconds, to disallow discarding items after logging in when ServerSideCharacters is ON.
* **Field type**: `Int32`