Skip to content

Commit

Permalink
fix: prevent crashing when stopping active crafting (#1837)
Browse files Browse the repository at this point in the history
* lock crafting and refactor ShouldCancelCrafting()

* lock also crafting cancel logic
  • Loading branch information
WeylonSantana authored Aug 26, 2023
1 parent 24042d5 commit ba7ff5b
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 150 deletions.
223 changes: 99 additions & 124 deletions Intersect.Server/Entities/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3629,17 +3629,7 @@ public void CloseCraftingTable()
//Craft a new item
public void CraftItem()
{
if (OpenCraftingTableId == default)
{
return;
}

if (!CraftingTableBase.TryGet(OpenCraftingTableId, out var table))
{
return;
}

if (!table.Crafts.Contains(CraftingState?.Id ?? default))
if(ShouldCancelCrafting())
{
return;
}
Expand All @@ -3649,70 +3639,52 @@ public void CraftItem()
return;
}

if (!Conditions.MeetsConditionLists(craftDescriptor.CraftingRequirements, this, null))
{
PacketSender.SendChatMsg(this, Strings.Crafting.RequirementsNotMet.ToString(), ChatMessageType.Error);
return;
}

var backupItems = new List<Item>();
foreach (var backupItem in Items)
{
backupItems.Add(backupItem.Clone());
}

var craftItem = ItemBase.Get(craftDescriptor.ItemId);
if (craftItem == null)
{
PacketSender.SendChatMsg(this, Strings.Errors.UnknownErrorTryAgain, ChatMessageType.Error, CustomColors.Alerts.Error);
Log.Error($"Unable to find item descriptor {craftItem.Id} for craft {craftDescriptor.Id}.");
return;
}

//Quickly Look through the inventory and create a catalog of what items we have, and how many
var inventoryItems = new Dictionary<Guid, int>();
foreach (var inventoryItem in Items)
lock (EntityLock)
{
if (inventoryItem == null)
var craftRoll = Randomization.Next(0, 101);
var failedCraft = craftRoll < craftDescriptor.FailureChance;
if (failedCraft)
{
continue;
}
var message = Strings.Crafting.CraftFailure;

var quantity = inventoryItem.Quantity;
if (inventoryItems.TryGetValue(inventoryItem.ItemId, out var currentQuantitySum))
{
quantity += currentQuantitySum;
}
inventoryItems[inventoryItem.ItemId] = quantity;
}
var itemLossRoll = Randomization.Next(0, 101);
var itemLost = itemLossRoll < craftDescriptor.ItemLossChance;
if (itemLost)
{
//Take the items
foreach (var ingredient in craftDescriptor.Ingredients)
{
if (TryTakeItem(ingredient.ItemId, ingredient.Quantity))
{
continue;
}

//Check the player actually has the items
foreach (var ingredient in craftDescriptor.Ingredients)
{
if (!inventoryItems.TryGetValue(ingredient.ItemId, out var currentQuantity))
{
CraftingState.Id = Guid.Empty;
return;
}
for (var i = 0; i < backupItems.Count; i++)
{
Items[i].Set(backupItems[i]);
}

if (currentQuantity < ingredient.Quantity)
{
CraftingState.Id = Guid.Empty;
return;
}
PacketSender.SendInventory(this);
CraftingState.RemainingCount--;

inventoryItems[ingredient.ItemId] = currentQuantity - ingredient.Quantity;
}
return;
}

var craftRoll = Randomization.Next(0, 101);
var failedCraft = craftRoll < craftDescriptor.FailureChance;
if (failedCraft)
{
var message = Strings.Crafting.CraftFailure;
message = Strings.Crafting.CraftFailureLostItems;
}

var itemLossRoll = Randomization.Next(0, 101);
var itemLost = itemLossRoll < craftDescriptor.ItemLossChance;
if (itemLost)
PacketSender.SendInventory(this);
PacketSender.SendChatMsg(this, message.ToString(craftItem.Name), ChatMessageType.Crafting, CustomColors.Alerts.Error);
}
else
{
//Take the items
foreach (var ingredient in craftDescriptor.Ingredients)
Expand All @@ -3722,9 +3694,9 @@ public void CraftItem()
continue;
}

for (var i = 0; i < backupItems.Count; i++)
for (var slotIndex = 0; slotIndex < backupItems.Count; slotIndex++)
{
Items[i].Set(backupItems[i]);
Items[slotIndex].Set(backupItems[slotIndex]);
}

PacketSender.SendInventory(this);
Expand All @@ -3733,110 +3705,113 @@ public void CraftItem()
return;
}

message = Strings.Crafting.CraftFailureLostItems;
}

PacketSender.SendInventory(this);
PacketSender.SendChatMsg(this, message.ToString(craftItem.Name), ChatMessageType.Crafting, CustomColors.Alerts.Error);
}
else
{
//Take the items
foreach (var ingredient in craftDescriptor.Ingredients)
{
if (TryTakeItem(ingredient.ItemId, ingredient.Quantity))
//Give them the craft
var quantity = Math.Max(craftDescriptor.Quantity, 1);
if (!craftItem.IsStackable)
{
continue;
quantity = 1;
}

for (var slotIndex = 0; slotIndex < backupItems.Count; slotIndex++)
if (TryGiveItem(craftItem.Id, quantity))
{
Items[slotIndex].Set(backupItems[slotIndex]);
}

PacketSender.SendInventory(this);
CraftingState.RemainingCount--;

return;
}

//Give them the craft
var quantity = Math.Max(craftDescriptor.Quantity, 1);
if (!craftItem.IsStackable)
{
quantity = 1;
}

if (TryGiveItem(craftItem.Id, quantity))
{
PacketSender.SendChatMsg(
this, Strings.Crafting.crafted.ToString(craftItem.Name), ChatMessageType.Crafting,
CustomColors.Alerts.Success
);
PacketSender.SendChatMsg(
this, Strings.Crafting.crafted.ToString(craftItem.Name), ChatMessageType.Crafting,
CustomColors.Alerts.Success
);

if (craftDescriptor.Event != default)
{
EnqueueStartCommonEvent(craftDescriptor.Event);
if (craftDescriptor.Event != default)
{
EnqueueStartCommonEvent(craftDescriptor.Event);
}
}
}
else
{
for (var i = 0; i < backupItems.Count; i++)
else
{
Items[i].Set(backupItems[i]);
}
for (var i = 0; i < backupItems.Count; i++)
{
Items[i].Set(backupItems[i]);
}

PacketSender.SendInventory(this);
PacketSender.SendChatMsg(
this, Strings.Crafting.nospace.ToString(craftItem.Name), ChatMessageType.Crafting,
CustomColors.Alerts.Error
);
PacketSender.SendInventory(this);
PacketSender.SendChatMsg(
this, Strings.Crafting.nospace.ToString(craftItem.Name), ChatMessageType.Crafting,
CustomColors.Alerts.Error
);
}
}
}

CraftingState.RemainingCount--;
CraftingState.RemainingCount--;
}
}

public bool ShouldCancelCrafting()
{
if (OpenCraftingTableId == default)
{
return true;
}

if (!CraftingTableBase.TryGet(OpenCraftingTableId, out var table))
{
return true;
}

if (!table.Crafts.Contains(CraftingState?.Id ?? default))
{
return true;
}

if (!CraftBase.TryGet(CraftingState?.Id ?? default, out var craftDescriptor))
{
return true;
}

//See if we have lost the items needed for our current craft, if so end the crafting session
if (!Conditions.MeetsConditionLists(craftDescriptor.CraftingRequirements, this, null))
{
PacketSender.SendChatMsg(this, Strings.Crafting.RequirementsNotMet.ToString(), ChatMessageType.Error);
return true;
}

var craftItem = ItemBase.Get(craftDescriptor.ItemId);
if (craftItem == null)
{
PacketSender.SendChatMsg(this, Strings.Errors.UnknownErrorTryAgain, ChatMessageType.Error, CustomColors.Alerts.Error);
Log.Error($"Unable to find item descriptor {craftItem.Id} for craft {craftDescriptor.Id}.");
return true;
}

//Quickly Look through the inventory and create a catalog of what items we have, and how many
var inventoryItems = new Dictionary<Guid, int>();
foreach (var item in Items)
foreach (var inventoryItem in Items)
{
if (item == default)
if (inventoryItem == null)
{
continue;
}

var quantity = item.Quantity;
if (inventoryItems.TryGetValue(item.ItemId, out var quantitySum))
var quantity = inventoryItem.Quantity;
if (inventoryItems.TryGetValue(inventoryItem.ItemId, out var currentQuantitySum))
{
quantity += quantitySum;
quantity += currentQuantitySum;
}

inventoryItems[item.ItemId] = quantity;
inventoryItems[inventoryItem.ItemId] = quantity;
}

//Check the player actually has the items
foreach (var ingredient in craftDescriptor.Ingredients)
{
if (!inventoryItems.TryGetValue(ingredient.ItemId, out int quantity))
if (!inventoryItems.TryGetValue(ingredient.ItemId, out var currentQuantity))
{
CraftingState.Id = Guid.Empty;
return true;
}

if (quantity < ingredient.Quantity)
if (currentQuantity < ingredient.Quantity)
{
CraftingState.Id = Guid.Empty;
return true;
}

inventoryItems[ingredient.ItemId] = quantity - ingredient.Quantity;
inventoryItems[ingredient.ItemId] = currentQuantity - ingredient.Quantity;
}

return false;
Expand Down
57 changes: 31 additions & 26 deletions Intersect.Server/Networking/PacketHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1885,37 +1885,42 @@ public void HandlePacket(Client client, CraftItemPacket packet)
return;
}

if (packet.CraftId == default)
lock(player.EntityLock)
{
player.CraftingState = default;
}
//if player hit stop button in crafting window
if (packet.CraftId == default)
{
player.CraftingState = default;
return;
}

if (!CraftBase.TryGet(packet.CraftId, out var craftDescriptor))
{
Log.Warn($"Player {player.Id} tried to craft {packet.CraftId} which does not exist.");
return;
}
if (!CraftBase.TryGet(packet.CraftId, out var craftDescriptor))
{
Log.Warn($"Player {player.Id} tried to craft {packet.CraftId} which does not exist.");
return;
}

if (player.OpenCraftingTableId == default)
{
Log.Warn($"Player {player.Id} tried to craft {packet.CraftId} without having opened a table yet.");
return;
}
if (player.OpenCraftingTableId == default)
{
Log.Warn($"Player {player.Id} tried to craft {packet.CraftId} without having opened a table yet.");
return;
}

if (player.CraftingState != default)
{
PacketSender.SendChatMsg(player, Strings.Crafting.AlreadyCrafting, ChatMessageType.Crafting, CustomColors.Alerts.Error);
return;
}
if (player.CraftingState != default)
{
PacketSender.SendChatMsg(player, Strings.Crafting.AlreadyCrafting, ChatMessageType.Crafting, CustomColors.Alerts.Error);
return;
}

player.CraftingState = new CraftingState
{
Id = packet.CraftId,
CraftCount = packet.Count,
RemainingCount = packet.Count,
DurationPerCraft = craftDescriptor.Time,
NextCraftCompletionTime = Timing.Global.Milliseconds + craftDescriptor.Time
};
player.CraftingState = new CraftingState
{
Id = packet.CraftId,
CraftCount = packet.Count,
RemainingCount = packet.Count,
DurationPerCraft = craftDescriptor.Time,
NextCraftCompletionTime = Timing.Global.Milliseconds + craftDescriptor.Time
};
}
}

//CloseBankPacket
Expand Down

0 comments on commit ba7ff5b

Please sign in to comment.