public Task Handle(OrderTransactionEvent evnt) { _state.HandledTransactions.TryPeek(out var handledTransaction); if (handledTransaction == evnt.EntityId) { // If we've already handled this event through the evaluator, // we don't want to mess with credits and inventory _state.HandledTransactions.Dequeue(); return(Task.CompletedTask); } var fromOrder = _state.Orders[evnt.FromSellOrder]; var toOrder = _state.Orders[evnt.ToBuyOrder]; fromOrder.QuantityFulfilled += evnt.Quantity; toOrder.QuantityFulfilled += evnt.Quantity; fromOrder.Owner.Credits += evnt.Price; toOrder.Owner.Credits -= evnt.Price; fromOrder.Owner.Inventory[evnt.ItemId].Quantity -= evnt.Quantity; if (toOrder.Owner.Inventory.TryGetValue(evnt.ItemId, out var inventory)) { inventory.Quantity += evnt.Quantity; } else { toOrder.Owner.Inventory.Add(evnt.ItemId, new WarehouseInventory { ItemId = evnt.ItemId, Quantity = evnt.Quantity, }); } return(Task.CompletedTask); }
private IEnumerable <IEvent> EvaluateBuyOrder( BrokerOrder buyOrder, BrokerOrder sellOrder, long quantityToSell, State state) { // The quantity one order can buy will be the least of either: // 1. As much as the buyer can buy (credits) // 2. As much as the seller can sell (inventory/order size) // 3. As much as the buyer wants to buy (order size) if (sellOrder.Price == 0) { yield break; } var affordableQuantity = buyOrder.Owner.Credits / sellOrder.Price; var quantityToBuy = Math.Min(affordableQuantity, quantityToSell); // Case 1 & 2 if (buyOrder.Quantity != -1) // Case 3 { quantityToBuy = Math.Min(quantityToBuy, buyOrder.Quantity - buyOrder.QuantityFulfilled); } var evnt = new OrderTransactionEvent(Guid.NewGuid(), _initiator) { FromPlayer = sellOrder.Owner.PlayerId, ToPlayer = buyOrder.Owner.PlayerId, FromSellOrder = sellOrder.OrderId, ToBuyOrder = buyOrder.OrderId, ItemId = sellOrder.ItemId, Quantity = quantityToBuy, Price = quantityToBuy * sellOrder.Price, }; buyOrder.Owner.Credits -= evnt.Price; sellOrder.Owner.Credits += evnt.Price; buyOrder.QuantityFulfilled += evnt.Quantity; sellOrder.QuantityFulfilled += evnt.Quantity; UpdatePlayerInventory(buyOrder.Owner, evnt.ItemId, evnt.Quantity); UpdatePlayerInventory(sellOrder.Owner, evnt.ItemId, -evnt.Quantity); state.HandledTransactions.Enqueue(evnt.EntityId); yield return(evnt); yield return(new OrderPartiallyFulfilledEvent(buyOrder.OrderId, _initiator) { TransactionId = evnt.EntityId, Price = evnt.Price, QuantityFulfilled = evnt.Quantity, }); yield return(new OrderPartiallyFulfilledEvent(sellOrder.OrderId, _initiator) { TransactionId = evnt.EntityId, Price = evnt.Price, QuantityFulfilled = evnt.Quantity, }); yield return(new PlayerBalanceChangedEvent(buyOrder.Owner.PlayerId, _initiator) { TransactionId = evnt.EntityId, BalanceChange = -evnt.Price, }); yield return(new PlayerBalanceChangedEvent(sellOrder.Owner.PlayerId, _initiator) { TransactionId = evnt.EntityId, BalanceChange = evnt.Price, }); yield return(new PlayerInventoryChangedEvent(buyOrder.Owner.PlayerId, _initiator) { TransactionId = evnt.EntityId, InventoryChange = new[] { new LuaItemStack { Name = evnt.ItemId, Count = evnt.Quantity, }, }, }); yield return(new PlayerInventoryChangedEvent(sellOrder.Owner.PlayerId, _initiator) { TransactionId = evnt.EntityId, InventoryChange = new[] { new LuaItemStack { Name = evnt.ItemId, Count = -evnt.Quantity, }, }, }); if (buyOrder.Quantity != -1 && buyOrder.QuantityFulfilled >= buyOrder.Quantity) { yield return(new OrderFulfilledEvent(buyOrder.OrderId, _initiator)); buyOrder.State = OrderState.Fulfilled; _logger.Information($"Buy order ${buyOrder.OrderId} completely fulfilled!"); } }