From 2e70a9714a763e6a6a984e1cd726ac97e51de897 Mon Sep 17 00:00:00 2001 From: Ceikry Date: Sat, 12 Aug 2023 00:21:25 +0000 Subject: [PATCH] Fixed regression causing GE to eat noted items --- .../global/handlers/iface/ge/StockMarket.kt | 35 +++++-- Server/src/test/kotlin/ExchangeTests.kt | 92 ++++++++++++++++++- 2 files changed, 115 insertions(+), 12 deletions(-) diff --git a/Server/src/main/content/global/handlers/iface/ge/StockMarket.kt b/Server/src/main/content/global/handlers/iface/ge/StockMarket.kt index 1782881d3..944e43055 100644 --- a/Server/src/main/content/global/handlers/iface/ge/StockMarket.kt +++ b/Server/src/main/content/global/handlers/iface/ge/StockMarket.kt @@ -19,6 +19,7 @@ import core.game.ge.PriceIndex import core.game.interaction.InterfaceListener import core.tools.Log import core.tools.SystemLogger +import kotlin.math.min /** * Handles the grand exchange interface (Stock Market) @@ -216,19 +217,28 @@ class StockMarket : InterfaceListener { offer.visualize(player) } - fun confirmOffer(player: Player, offer: GrandExchangeOffer, index: Int) + enum class OfferConfirmResult { + Success, + ZeroCoins, + TooManyCoins, + NotEnoughItemsOrCoins, + ItemRemovalFailure, + OfferPlacementError + } + + fun confirmOffer(player: Player, offer: GrandExchangeOffer, index: Int) : OfferConfirmResult { if(offer.offeredValue < 1) { playAudio(player, Audio(4039, 1, 1)) sendMessage(player, "You can't make an offer for 0 coins.") - return + return OfferConfirmResult.ZeroCoins } if(offer.amount > Int.MAX_VALUE / offer.offeredValue) { playAudio(player, Audio(4039, 1, 1)) sendMessage(player, "You can't ${if(offer.sell) "sell" else "buy"} this much!") - return + return OfferConfirmResult.TooManyCoins } offer.index = index if(offer.sell) @@ -239,21 +249,25 @@ class StockMarket : InterfaceListener { playAudio(player, Audio(4039, 1, 1)) sendMessage(player, "You do not have enough of this item in your inventory to cover the") sendMessage(player, "offer.") - return + return OfferConfirmResult.NotEnoughItemsOrCoins } - var item: Item - val amountUnnoted = amountInInventory(player, offer.itemID) + val amountUnnoted = min(amountInInventory(player, offer.itemID), offer.amount) val amountLeft = offer.amount - amountUnnoted - val removedUnnoted = removeItem(player, Item(offer.itemID, amountUnnoted).also { item = it }) + val removedUnnoted = if (amountUnnoted > 0) removeItem(player, Item(offer.itemID, amountUnnoted)) else true val removeNoted = if (amountLeft > 0) removeItem(player, Item(itemDefinition(offer.itemID).noteId, amountLeft)) else true - if (!removedUnnoted || !removeNoted) return + if (!removedUnnoted || !removeNoted) return OfferConfirmResult.ItemRemovalFailure if(GrandExchange.dispatch(player, offer)) { player.removeAttribute("ge-temp") } else { - addItem(player, item.id, item.amount) + if (amountUnnoted > 0) + addItem(player, offer.itemID, offer.amount - amountLeft) + if (amountLeft > 0) + addItem(player, itemDefinition(offer.itemID).noteId, amountLeft) + sendMessage(player, "Unable to place GE offer. Please try again.") + return OfferConfirmResult.OfferPlacementError } } else @@ -263,7 +277,7 @@ class StockMarket : InterfaceListener { { playAudio(player, Audio(4039, 1, 1)) sendMessage(player, "You do not have enough coins to cover the offer.") - return + return OfferConfirmResult.NotEnoughItemsOrCoins } if(GrandExchange.dispatch(player, offer) && removeItem(player, Item(995, total))) { @@ -273,6 +287,7 @@ class StockMarket : InterfaceListener { playAudio(player, Audio(4043, 1, 1)) offer.visualize(player) toMainInterface(player) + return OfferConfirmResult.Success } fun getInventoryAmount(player: Player, itemId: Int): Int { diff --git a/Server/src/test/kotlin/ExchangeTests.kt b/Server/src/test/kotlin/ExchangeTests.kt index b2f0cdd1b..baac462fd 100644 --- a/Server/src/test/kotlin/ExchangeTests.kt +++ b/Server/src/test/kotlin/ExchangeTests.kt @@ -1,3 +1,4 @@ +import content.global.handlers.iface.ge.StockMarket import core.game.ge.OfferState import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch @@ -11,6 +12,8 @@ import core.game.ge.GEDB import core.game.ge.GrandExchange import core.game.ge.GrandExchangeOffer import core.game.ge.PriceIndex +import core.game.node.item.Item +import org.rs09.consts.Items import java.io.File @TestInstance(TestInstance.Lifecycle.PER_CLASS) class ExchangeTests { @@ -21,10 +24,10 @@ import java.io.File GEDB.init(TEST_DB_PATH) } - fun generateOffer(itemId: Int, amount: Int, price: Int, sale: Boolean, username: String = "test ${System.currentTimeMillis()}") : GrandExchangeOffer { + fun generateOffer(itemId: Int, amount: Int, price: Int, sale: Boolean, username: String = "test ${System.currentTimeMillis()}", offerState: OfferState = OfferState.REGISTERED) : GrandExchangeOffer { val offer = GrandExchangeOffer() val uid = username.hashCode() // normally this would be the account's uid but in the test we don't have an account - offer.offerState = OfferState.REGISTERED + offer.offerState = offerState offer.itemID = itemId offer.offeredValue = price offer.amount = amount @@ -37,6 +40,22 @@ import java.io.File return offer } + fun generateUnsentOffer (itemId: Int, amount: Int, price: Int, sale: Boolean, username: String, offerState: OfferState = OfferState.PENDING) : GrandExchangeOffer { + val offer = GrandExchangeOffer() + val uid = username.hashCode() // normally this would be the account's uid but in the test we don't have an account + offer.offerState = offerState + offer.itemID = itemId + offer.offeredValue = price + offer.amount = amount + offer.timeStamp = System.currentTimeMillis() + offer.index = 0 + offer.isBot = false + offer.playerUID = uid + offer.sell = sale + offer.uid = 0L + return offer + } + @AfterAll @JvmStatic fun cleanup() { File(TEST_DB_PATH).delete() } @@ -115,4 +134,73 @@ import java.io.File Assertions.assertEquals(false, b.isCancelled) } } + + @Test fun offerWithCombinedNotedAndUnnotedAmountShouldSuceed() { + TestUtils.getMockPlayer("combinedNotendAndUnnotedExcTest").use { p -> + val offer = generateUnsentOffer(4151, 1000, 1500, true, p.name, OfferState.PENDING) + val mkt = StockMarket() + p.inventory.add(Item(Items.ABYSSAL_WHIP_4151, 10)) + p.inventory.add(Item(Items.ABYSSAL_WHIP_4152, 990)) + Assertions.assertEquals(StockMarket.OfferConfirmResult.Success, mkt.confirmOffer(p, offer, 0)) + Assertions.assertEquals(0, p.inventory.getAmount(4151)) + Assertions.assertEquals(0, p.inventory.getAmount(4152)) + } + } + + @Test fun offerWithOnlyNotedAmountShouldSucceed() { + TestUtils.getMockPlayer("onlyNotedOfferSucceed").use { p -> + val offer = generateUnsentOffer(4151, 1000, 1500, true, p.name, OfferState.PENDING) + val mkt = StockMarket() + p.inventory.add(Item(Items.ABYSSAL_WHIP_4152, 1000)) + Assertions.assertEquals(StockMarket.OfferConfirmResult.Success, mkt.confirmOffer(p, offer, 0)) + Assertions.assertEquals(0, p.inventory.getAmount(4151)) + Assertions.assertEquals(0, p.inventory.getAmount(4152)) + } + } + + @Test fun offerWithMoreUnnotedItemsThanOfferAmountShouldSucceed() { + TestUtils.getMockPlayer("onlyUnnotedOfferWithExtraItemsSucceed").use { p -> + val offer = generateUnsentOffer(4151, 10, 1500, true, p.name, OfferState.PENDING) + val mkt = StockMarket() + p.inventory.add(Item(Items.ABYSSAL_WHIP_4151, 20)) + Assertions.assertEquals(StockMarket.OfferConfirmResult.Success, mkt.confirmOffer(p, offer, 0)) + Assertions.assertEquals(10, p.inventory.getAmount(4151)) + Assertions.assertEquals(0, p.inventory.getAmount(4152)) + } + } + + @Test fun offerWithOnlyUnnotedAmountShouldSucceed() { + TestUtils.getMockPlayer("onlyUnnotedOfferSucceed").use { p -> + val offer = generateUnsentOffer(4151, 10, 1500, true, p.name, OfferState.PENDING) + val mkt = StockMarket() + p.inventory.add(Item(Items.ABYSSAL_WHIP_4151, 10)) + Assertions.assertEquals(StockMarket.OfferConfirmResult.Success, mkt.confirmOffer(p, offer, 0)) + Assertions.assertEquals(0, p.inventory.getAmount(4151)) + Assertions.assertEquals(0, p.inventory.getAmount(4152)) + } + } + + @Test fun offerWithNotEnoughNotedItemsShouldFail() { + TestUtils.getMockPlayer("combinedNotedAndUnnotedFailure").use { p -> + val offer = generateUnsentOffer(4151, 1000, 1500, true, p.name, OfferState.PENDING) + val mkt = StockMarket() + p.inventory.add(Item(Items.ABYSSAL_WHIP_4151, 10)) + p.inventory.add(Item(Items.ABYSSAL_WHIP_4152, 15)) + Assertions.assertEquals(StockMarket.OfferConfirmResult.NotEnoughItemsOrCoins, mkt.confirmOffer(p, offer, 0)) + Assertions.assertEquals(10, p.inventory.getAmount(4151)) + Assertions.assertEquals(15, p.inventory.getAmount(4152)) + } + } + + @Test fun offerWithNotEnoughUnnotedItemsShouldFail() { + TestUtils.getMockPlayer("combinedNotedAndUnnotedFailure2").use { p -> + val offer = generateUnsentOffer(4151, 1000, 1500, true, p.name, OfferState.PENDING) + val mkt = StockMarket() + p.inventory.add(Item(Items.ABYSSAL_WHIP_4151, 10)) + p.inventory.add(Item(Items.ABYSSAL_WHIP_4152, 900)) + Assertions.assertEquals(StockMarket.OfferConfirmResult.NotEnoughItemsOrCoins, mkt.confirmOffer(p, offer, 0)) + Assertions.assertEquals(10, p.inventory.getAmount(4151)) + Assertions.assertEquals(900, p.inventory.getAmount(4152)) + } + } }