added a lock for trader buy method (#303)
* added a lock for trader buy method * moved the lock higher
This commit is contained in:
@@ -32,6 +32,8 @@ public class TradeHelper(
|
||||
ICloner _cloner
|
||||
)
|
||||
{
|
||||
protected static Lock buyLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Buy item from flea or trader
|
||||
/// </summary>
|
||||
@@ -48,203 +50,207 @@ public class TradeHelper(
|
||||
ItemEventRouterResponse output
|
||||
)
|
||||
{
|
||||
List<Item> offerItems = [];
|
||||
Action<int>? buyCallback;
|
||||
|
||||
if (string.Equals(buyRequestData.TransactionId, "ragfair", StringComparison.OrdinalIgnoreCase))
|
||||
lock (buyLock)
|
||||
{
|
||||
// Called when player purchases PMC offer from ragfair
|
||||
buyCallback = buyCount =>
|
||||
List<Item> offerItems = [];
|
||||
Action<int>? buyCallback;
|
||||
|
||||
if (string.Equals(buyRequestData.TransactionId, "ragfair", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var allOffers = _ragfairServer.GetOffers();
|
||||
|
||||
// We store ragfair offerId in buyRequestData.item_id
|
||||
var offerWithItem = allOffers.FirstOrDefault(x => x.Id == buyRequestData.ItemId);
|
||||
var itemPurchased = offerWithItem.Items.FirstOrDefault();
|
||||
|
||||
// Ensure purchase does not exceed trader item limit
|
||||
var assortHasBuyRestrictions = _itemHelper.HasBuyRestrictions(itemPurchased);
|
||||
if (assortHasBuyRestrictions)
|
||||
// Called when player purchases PMC offer from ragfair
|
||||
buyCallback = buyCount =>
|
||||
{
|
||||
CheckPurchaseIsWithinTraderItemLimit(
|
||||
sessionID,
|
||||
pmcData,
|
||||
buyRequestData.TransactionId,
|
||||
itemPurchased,
|
||||
buyRequestData.ItemId,
|
||||
buyCount
|
||||
);
|
||||
var allOffers = _ragfairServer.GetOffers();
|
||||
|
||||
// We store ragfair offerId in buyRequestData.item_id
|
||||
var offerWithItem = allOffers.FirstOrDefault(x => x.Id == buyRequestData.ItemId);
|
||||
var itemPurchased = offerWithItem.Items.FirstOrDefault();
|
||||
|
||||
// Ensure purchase does not exceed trader item limit
|
||||
var assortHasBuyRestrictions = _itemHelper.HasBuyRestrictions(itemPurchased);
|
||||
if (assortHasBuyRestrictions)
|
||||
{
|
||||
CheckPurchaseIsWithinTraderItemLimit(
|
||||
sessionID,
|
||||
pmcData,
|
||||
buyRequestData.TransactionId,
|
||||
itemPurchased,
|
||||
buyRequestData.ItemId,
|
||||
buyCount
|
||||
);
|
||||
|
||||
// Decrement trader item count
|
||||
var itemPurchaseDetails = new PurchaseDetails
|
||||
{
|
||||
Items =
|
||||
[
|
||||
new PurchaseItems
|
||||
{
|
||||
ItemId = buyRequestData.ItemId,
|
||||
Count = buyCount
|
||||
}
|
||||
],
|
||||
TraderId = buyRequestData.TransactionId
|
||||
};
|
||||
_traderHelper.AddTraderPurchasesToPlayerProfile(sessionID, itemPurchaseDetails, itemPurchased);
|
||||
}
|
||||
};
|
||||
|
||||
// buyCallback = BuyCallback1;
|
||||
// Get raw offer from ragfair, clone to prevent altering offer itself
|
||||
var allOffers = _ragfairServer.GetOffers();
|
||||
var offerWithItemCloned = _cloner.Clone(allOffers.FirstOrDefault(x => x.Id == buyRequestData.ItemId));
|
||||
offerItems = offerWithItemCloned.Items;
|
||||
}
|
||||
else if (buyRequestData.TransactionId == Traders.FENCE)
|
||||
{
|
||||
buyCallback = buyCount =>
|
||||
{
|
||||
// Update assort/flea item values
|
||||
var traderAssorts = _traderHelper.GetTraderAssortsByTraderId(buyRequestData.TransactionId).Items;
|
||||
var itemPurchased = traderAssorts.FirstOrDefault(assort => assort.Id == buyRequestData.ItemId);
|
||||
|
||||
// Decrement trader item count
|
||||
var itemPurchaseDetails = new PurchaseDetails
|
||||
{
|
||||
Items =
|
||||
[
|
||||
new PurchaseItems
|
||||
{
|
||||
ItemId = buyRequestData.ItemId,
|
||||
Count = buyCount
|
||||
}
|
||||
],
|
||||
TraderId = buyRequestData.TransactionId
|
||||
};
|
||||
_traderHelper.AddTraderPurchasesToPlayerProfile(sessionID, itemPurchaseDetails, itemPurchased);
|
||||
}
|
||||
};
|
||||
itemPurchased.Upd.StackObjectsCount -= buyCount;
|
||||
|
||||
// buyCallback = BuyCallback1;
|
||||
// Get raw offer from ragfair, clone to prevent altering offer itself
|
||||
var allOffers = _ragfairServer.GetOffers();
|
||||
var offerWithItemCloned = _cloner.Clone(allOffers.FirstOrDefault(x => x.Id == buyRequestData.ItemId));
|
||||
offerItems = offerWithItemCloned.Items;
|
||||
}
|
||||
else if (buyRequestData.TransactionId == Traders.FENCE)
|
||||
{
|
||||
buyCallback = buyCount =>
|
||||
{
|
||||
// Update assort/flea item values
|
||||
var traderAssorts = _traderHelper.GetTraderAssortsByTraderId(buyRequestData.TransactionId).Items;
|
||||
var itemPurchased = traderAssorts.FirstOrDefault(assort => assort.Id == buyRequestData.ItemId);
|
||||
_fenceService.AmendOrRemoveFenceOffer(buyRequestData.ItemId, buyCount);
|
||||
};
|
||||
|
||||
// Decrement trader item count
|
||||
itemPurchased.Upd.StackObjectsCount -= buyCount;
|
||||
|
||||
_fenceService.AmendOrRemoveFenceOffer(buyRequestData.ItemId, buyCount);
|
||||
};
|
||||
|
||||
var fenceItems = _fenceService.GetRawFenceAssorts().Items;
|
||||
var rootItemIndex = fenceItems.FindIndex(item => item.Id == buyRequestData.ItemId);
|
||||
if (rootItemIndex == -1)
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
var fenceItems = _fenceService.GetRawFenceAssorts().Items;
|
||||
var rootItemIndex = fenceItems.FindIndex(item => item.Id == buyRequestData.ItemId);
|
||||
if (rootItemIndex == -1)
|
||||
{
|
||||
_logger.Debug($"Tried to buy item {buyRequestData.ItemId} from fence that no longer exists");
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Tried to buy item {buyRequestData.ItemId} from fence that no longer exists");
|
||||
}
|
||||
|
||||
var message = _localisationService.GetText("ragfair-offer_no_longer_exists");
|
||||
_httpResponseUtil.AppendErrorToOutput(output, message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var message = _localisationService.GetText("ragfair-offer_no_longer_exists");
|
||||
_httpResponseUtil.AppendErrorToOutput(output, message);
|
||||
offerItems = _itemHelper.FindAndReturnChildrenAsItems(fenceItems, buyRequestData.ItemId);
|
||||
}
|
||||
else
|
||||
{
|
||||
buyCallback = buyCount =>
|
||||
{
|
||||
// Update assort/flea item values
|
||||
var traderAssorts = _traderHelper.GetTraderAssortsByTraderId(buyRequestData.TransactionId).Items;
|
||||
var itemPurchased = traderAssorts.FirstOrDefault(item => item.Id == buyRequestData.ItemId);
|
||||
|
||||
// Ensure purchase does not exceed trader item limit
|
||||
var assortHasBuyRestrictions = _itemHelper.HasBuyRestrictions(itemPurchased);
|
||||
if (assortHasBuyRestrictions)
|
||||
// Will throw error if check fails
|
||||
{
|
||||
CheckPurchaseIsWithinTraderItemLimit(
|
||||
sessionID,
|
||||
pmcData,
|
||||
buyRequestData.TransactionId,
|
||||
itemPurchased,
|
||||
buyRequestData.ItemId,
|
||||
buyCount
|
||||
);
|
||||
}
|
||||
|
||||
// Check if trader has enough stock
|
||||
if (itemPurchased.Upd.StackObjectsCount < buyCount)
|
||||
{
|
||||
throw new Exception(
|
||||
$"Unable to purchase {buyCount} items, this would exceed the remaining stock left {itemPurchased.Upd.StackObjectsCount} from the traders assort: {buyRequestData.TransactionId} this refresh"
|
||||
);
|
||||
}
|
||||
|
||||
// Decrement trader item count
|
||||
itemPurchased.Upd.StackObjectsCount -= buyCount;
|
||||
|
||||
if (assortHasBuyRestrictions)
|
||||
{
|
||||
var itemPurchaseDat = new PurchaseDetails
|
||||
{
|
||||
Items =
|
||||
[
|
||||
new PurchaseItems
|
||||
{
|
||||
ItemId = buyRequestData.ItemId,
|
||||
Count = buyCount
|
||||
}
|
||||
],
|
||||
TraderId = buyRequestData.TransactionId
|
||||
};
|
||||
|
||||
_traderHelper.AddTraderPurchasesToPlayerProfile(sessionID, itemPurchaseDat, itemPurchased);
|
||||
}
|
||||
};
|
||||
|
||||
// Get all trader assort items
|
||||
var traderItems = _traderAssortHelper.GetAssort(sessionID, buyRequestData.TransactionId).Items;
|
||||
|
||||
// Get item + children for purchase
|
||||
var relevantItems = _itemHelper.FindAndReturnChildrenAsItems(traderItems, buyRequestData.ItemId);
|
||||
if (relevantItems.Count == 0)
|
||||
{
|
||||
_logger.Error(
|
||||
$"Purchased trader: {buyRequestData.TransactionId} offer: {buyRequestData.ItemId} has no items");
|
||||
}
|
||||
|
||||
offerItems.AddRange(relevantItems);
|
||||
}
|
||||
|
||||
// Get item details from db
|
||||
var itemDbDetails = _itemHelper.GetItem(offerItems.FirstOrDefault().Template).Value;
|
||||
var itemMaxStackSize = itemDbDetails.Properties.StackMaxSize;
|
||||
var itemsToSendTotalCount = buyRequestData.Count;
|
||||
var itemsToSendRemaining = itemsToSendTotalCount;
|
||||
|
||||
// Construct array of items to send to player
|
||||
List<List<Item>> itemsToSendToPlayer = [];
|
||||
while (itemsToSendRemaining > 0)
|
||||
{
|
||||
var offerClone = _cloner.Clone(offerItems);
|
||||
// Handle stackable items that have a max stack size limit
|
||||
var itemCountToSend = Math.Min(itemMaxStackSize ?? 0, itemsToSendRemaining ?? 0);
|
||||
offerClone.FirstOrDefault().Upd.StackObjectsCount = itemCountToSend;
|
||||
|
||||
// Prevent any collisions
|
||||
_itemHelper.RemapRootItemId(offerClone);
|
||||
if (offerClone.Count > 1)
|
||||
{
|
||||
_itemHelper.ReparentItemAndChildren(offerClone.FirstOrDefault(), offerClone);
|
||||
}
|
||||
|
||||
itemsToSendToPlayer.Add(offerClone);
|
||||
|
||||
// Remove amount of items added to player stash
|
||||
itemsToSendRemaining -= itemCountToSend;
|
||||
}
|
||||
|
||||
// Construct request
|
||||
var request = new AddItemsDirectRequest
|
||||
{
|
||||
ItemsWithModsToAdd = itemsToSendToPlayer,
|
||||
FoundInRaid = foundInRaid,
|
||||
Callback = buyCallback,
|
||||
UseSortingTable = false
|
||||
};
|
||||
|
||||
// Add items + their children to stash
|
||||
_inventoryHelper.AddItemsToStash(sessionID, request, pmcData, output);
|
||||
if (output.Warnings?.Count > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
offerItems = _itemHelper.FindAndReturnChildrenAsItems(fenceItems, buyRequestData.ItemId);
|
||||
}
|
||||
else
|
||||
{
|
||||
buyCallback = buyCount =>
|
||||
/// Pay for purchase
|
||||
_paymentService.PayMoney(pmcData, buyRequestData, sessionID, output);
|
||||
if (output.Warnings?.Count > 0)
|
||||
{
|
||||
// Update assort/flea item values
|
||||
var traderAssorts = _traderHelper.GetTraderAssortsByTraderId(buyRequestData.TransactionId).Items;
|
||||
var itemPurchased = traderAssorts.FirstOrDefault(item => item.Id == buyRequestData.ItemId);
|
||||
|
||||
// Ensure purchase does not exceed trader item limit
|
||||
var assortHasBuyRestrictions = _itemHelper.HasBuyRestrictions(itemPurchased);
|
||||
if (assortHasBuyRestrictions)
|
||||
// Will throw error if check fails
|
||||
{
|
||||
CheckPurchaseIsWithinTraderItemLimit(
|
||||
sessionID,
|
||||
pmcData,
|
||||
buyRequestData.TransactionId,
|
||||
itemPurchased,
|
||||
buyRequestData.ItemId,
|
||||
buyCount
|
||||
);
|
||||
}
|
||||
|
||||
// Check if trader has enough stock
|
||||
if (itemPurchased.Upd.StackObjectsCount < buyCount)
|
||||
{
|
||||
throw new Exception(
|
||||
$"Unable to purchase {buyCount} items, this would exceed the remaining stock left {itemPurchased.Upd.StackObjectsCount} from the traders assort: {buyRequestData.TransactionId} this refresh"
|
||||
);
|
||||
}
|
||||
|
||||
// Decrement trader item count
|
||||
itemPurchased.Upd.StackObjectsCount -= buyCount;
|
||||
|
||||
if (assortHasBuyRestrictions)
|
||||
{
|
||||
var itemPurchaseDat = new PurchaseDetails
|
||||
{
|
||||
Items =
|
||||
[
|
||||
new PurchaseItems
|
||||
{
|
||||
ItemId = buyRequestData.ItemId,
|
||||
Count = buyCount
|
||||
}
|
||||
],
|
||||
TraderId = buyRequestData.TransactionId
|
||||
};
|
||||
|
||||
_traderHelper.AddTraderPurchasesToPlayerProfile(sessionID, itemPurchaseDat, itemPurchased);
|
||||
}
|
||||
};
|
||||
|
||||
// Get all trader assort items
|
||||
var traderItems = _traderAssortHelper.GetAssort(sessionID, buyRequestData.TransactionId).Items;
|
||||
|
||||
// Get item + children for purchase
|
||||
var relevantItems = _itemHelper.FindAndReturnChildrenAsItems(traderItems, buyRequestData.ItemId);
|
||||
if (relevantItems.Count == 0)
|
||||
{
|
||||
_logger.Error($"Purchased trader: {buyRequestData.TransactionId} offer: {buyRequestData.ItemId} has no items");
|
||||
var errorMessage = $"Transaction failed: {output.Warnings.FirstOrDefault().ErrorMessage}";
|
||||
_httpResponseUtil.AppendErrorToOutput(output, errorMessage, BackendErrorCodes.UnknownTradingError);
|
||||
}
|
||||
|
||||
offerItems.AddRange(relevantItems);
|
||||
}
|
||||
|
||||
// Get item details from db
|
||||
var itemDbDetails = _itemHelper.GetItem(offerItems.FirstOrDefault().Template).Value;
|
||||
var itemMaxStackSize = itemDbDetails.Properties.StackMaxSize;
|
||||
var itemsToSendTotalCount = buyRequestData.Count;
|
||||
var itemsToSendRemaining = itemsToSendTotalCount;
|
||||
|
||||
// Construct array of items to send to player
|
||||
List<List<Item>> itemsToSendToPlayer = [];
|
||||
while (itemsToSendRemaining > 0)
|
||||
{
|
||||
var offerClone = _cloner.Clone(offerItems);
|
||||
// Handle stackable items that have a max stack size limit
|
||||
var itemCountToSend = Math.Min(itemMaxStackSize ?? 0, itemsToSendRemaining ?? 0);
|
||||
offerClone.FirstOrDefault().Upd.StackObjectsCount = itemCountToSend;
|
||||
|
||||
// Prevent any collisions
|
||||
_itemHelper.RemapRootItemId(offerClone);
|
||||
if (offerClone.Count > 1)
|
||||
{
|
||||
_itemHelper.ReparentItemAndChildren(offerClone.FirstOrDefault(), offerClone);
|
||||
}
|
||||
|
||||
itemsToSendToPlayer.Add(offerClone);
|
||||
|
||||
// Remove amount of items added to player stash
|
||||
itemsToSendRemaining -= itemCountToSend;
|
||||
}
|
||||
|
||||
// Construct request
|
||||
var request = new AddItemsDirectRequest
|
||||
{
|
||||
ItemsWithModsToAdd = itemsToSendToPlayer,
|
||||
FoundInRaid = foundInRaid,
|
||||
Callback = buyCallback,
|
||||
UseSortingTable = false
|
||||
};
|
||||
|
||||
// Add items + their children to stash
|
||||
_inventoryHelper.AddItemsToStash(sessionID, request, pmcData, output);
|
||||
if (output.Warnings?.Count > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/// Pay for purchase
|
||||
_paymentService.PayMoney(pmcData, buyRequestData, sessionID, output);
|
||||
if (output.Warnings?.Count > 0)
|
||||
{
|
||||
var errorMessage = $"Transaction failed: {output.Warnings.FirstOrDefault().ErrorMessage}";
|
||||
_httpResponseUtil.AppendErrorToOutput(output, errorMessage, BackendErrorCodes.UnknownTradingError);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user