ge plugin: better handle trades updating on login

After login there are two sets of offer updates, I suspect the first is
from the server and the second is from the ge server - this also flags
ge server trade updates as on login. To do this we assume it always
happens in the first few ticks after the login burst.
This commit is contained in:
Adam
2020-12-25 14:57:56 -05:00
parent 85a953e76b
commit ffebb9cf0c
2 changed files with 112 additions and 20 deletions

View File

@@ -119,7 +119,9 @@ import org.apache.commons.text.similarity.FuzzyScore;
@Slf4j
public class GrandExchangePlugin extends Plugin
{
private static final int GE_SLOTS = 8;
@VisibleForTesting
static final int GE_SLOTS = 8;
private static final int GE_LOGIN_BURST_WINDOW = 2; // ticks
private static final int OFFER_CONTAINER_ITEM = 21;
private static final int OFFER_DEFAULT_ITEM_ID = 6512;
private static final String OSB_GE_TEXT = "<br>OSBuddy Actively traded price: ";
@@ -141,6 +143,7 @@ public class GrandExchangePlugin extends Plugin
private NavigationButton button;
@Getter(AccessLevel.PACKAGE)
@Setter(AccessLevel.PACKAGE)
private GrandExchangePanel panel;
@Getter(AccessLevel.PACKAGE)
@@ -189,7 +192,7 @@ public class GrandExchangePlugin extends Plugin
@Inject
private GrandExchangeClient grandExchangeClient;
private boolean loginBurstGeUpdates;
private int lastLoginTick;
@Inject
private OSBGrandExchangeClient osbGrandExchangeClient;
@@ -314,6 +317,8 @@ public class GrandExchangePlugin extends Plugin
osbItem = -1;
osbGrandExchangeResult = null;
lastLoginTick = -1;
}
@Override
@@ -370,12 +375,12 @@ public class GrandExchangePlugin extends Plugin
if (offer.getState() == GrandExchangeOfferState.EMPTY && client.getGameState() != GameState.LOGGED_IN)
{
// Trades are cleared by the client during LOGIN_SCREEN/HOPPING/LOGGING_IN, ignore those so we don't
// zero and re-submit the trade on login as an update
// clear the offer config.
return;
}
log.debug("GE offer updated: state: {}, slot: {}, item: {}, qty: {}, login: {}",
offer.getState(), slot, offer.getItemId(), offer.getQuantitySold(), loginBurstGeUpdates);
log.debug("GE offer updated: state: {}, slot: {}, item: {}, qty: {}, lastLoginTick: {}",
offer.getState(), slot, offer.getItemId(), offer.getQuantitySold(), lastLoginTick);
ItemComposition offerItem = itemManager.getItemComposition(offer.getItemId());
boolean shouldStack = offerItem.isStackable() || offer.getTotalQuantity() > 1;
@@ -385,11 +390,6 @@ public class GrandExchangePlugin extends Plugin
submitTrade(slot, offer);
updateConfig(slot, offer);
if (loginBurstGeUpdates && slot == GE_SLOTS - 1) // slots are sent sequentially on login; this is the last one
{
loginBurstGeUpdates = false;
}
}
@VisibleForTesting
@@ -403,6 +403,7 @@ public class GrandExchangePlugin extends Plugin
}
SavedOffer savedOffer = getOffer(slot);
boolean login = client.getTickCount() <= lastLoginTick + GE_LOGIN_BURST_WINDOW;
if (savedOffer == null && (state == GrandExchangeOfferState.BUYING || state == GrandExchangeOfferState.SELLING) && offer.getQuantitySold() == 0)
{
// new offer
@@ -413,7 +414,7 @@ public class GrandExchangePlugin extends Plugin
grandExchangeTrade.setOffer(offer.getPrice());
grandExchangeTrade.setSlot(slot);
grandExchangeTrade.setWorldType(getGeWorldType());
grandExchangeTrade.setLogin(loginBurstGeUpdates);
grandExchangeTrade.setLogin(login);
log.debug("Submitting new trade: {}", grandExchangeTrade);
grandExchangeClient.submit(grandExchangeTrade);
@@ -444,7 +445,7 @@ public class GrandExchangePlugin extends Plugin
grandExchangeTrade.setOffer(offer.getPrice());
grandExchangeTrade.setSlot(slot);
grandExchangeTrade.setWorldType(getGeWorldType());
grandExchangeTrade.setLogin(loginBurstGeUpdates);
grandExchangeTrade.setLogin(login);
log.debug("Submitting cancelled: {}", grandExchangeTrade);
grandExchangeClient.submit(grandExchangeTrade);
@@ -469,7 +470,7 @@ public class GrandExchangePlugin extends Plugin
grandExchangeTrade.setOffer(offer.getPrice());
grandExchangeTrade.setSlot(slot);
grandExchangeTrade.setWorldType(getGeWorldType());
grandExchangeTrade.setLogin(loginBurstGeUpdates);
grandExchangeTrade.setLogin(login);
log.debug("Submitting trade: {}", grandExchangeTrade);
grandExchangeClient.submit(grandExchangeTrade);
@@ -532,14 +533,19 @@ public class GrandExchangePlugin extends Plugin
@Subscribe
public void onGameStateChanged(GameStateChanged gameStateChanged)
{
if (gameStateChanged.getGameState() == GameState.LOGIN_SCREEN)
switch (gameStateChanged.getGameState())
{
panel.getOffersPanel().resetOffers();
loginBurstGeUpdates = true;
}
else if (gameStateChanged.getGameState() == GameState.LOGGED_IN)
{
grandExchangeClient.setMachineId(getMachineUuid());
case LOGIN_SCREEN:
panel.getOffersPanel().resetOffers();
break;
case LOGGING_IN:
case HOPPING:
case CONNECTION_LOST:
lastLoginTick = client.getTickCount();
break;
case LOGGED_IN:
grandExchangeClient.setMachineId(getMachineUuid());
break;
}
}

View File

@@ -29,15 +29,19 @@ import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.GrandExchangeOffer;
import net.runelite.api.GrandExchangeOfferState;
import net.runelite.api.ItemComposition;
import net.runelite.api.ItemID;
import net.runelite.api.WorldType;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GrandExchangeOfferChanged;
import net.runelite.client.Notifier;
import net.runelite.client.account.SessionManager;
@@ -52,14 +56,18 @@ import net.runelite.http.api.ge.GrandExchangeClient;
import net.runelite.http.api.ge.GrandExchangeTrade;
import net.runelite.http.api.osbuddy.OSBGrandExchangeClient;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import org.mockito.Mock;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -243,4 +251,82 @@ public class GrandExchangePluginTest
verify(configManager, never()).unsetRSProfileConfiguration(anyString(), anyString());
}
@Test
public void testLogin()
{
GrandExchangePanel panel = mock(GrandExchangePanel.class);
when(panel.getOffersPanel()).thenReturn(mock(GrandExchangeOffersPanel.class));
grandExchangePlugin.setPanel(panel);
when(itemManager.getItemComposition(anyInt())).thenReturn(mock(ItemComposition.class));
// provide config support so getOffer and setOffer work
final Map<String, Object> config = new HashMap<>();
doAnswer(a ->
{
Object[] arguments = a.getArguments();
config.put((String) arguments[1], arguments[2]);
return null;
}).when(configManager).setRSProfileConfiguration(eq("geoffer"), anyString(), anyString());
when(configManager.getRSProfileConfiguration(eq("geoffer"), anyString())).thenAnswer(a ->
{
Object[] arguments = a.getArguments();
return config.get((String) arguments[1]);
});
// set loginBurstGeUpdates
GameStateChanged gameStateChanged = new GameStateChanged();
gameStateChanged.setGameState(GameState.LOGIN_SCREEN);
grandExchangePlugin.onGameStateChanged(gameStateChanged);
// 8x buy 10 whip @ 1k ea, bought 1 sofar.
for (int i = 0; i < GrandExchangePlugin.GE_SLOTS; ++i)
{
GrandExchangeOffer grandExchangeOffer = mock(GrandExchangeOffer.class);
when(grandExchangeOffer.getQuantitySold()).thenReturn(1);
when(grandExchangeOffer.getItemId()).thenReturn(ItemID.ABYSSAL_WHIP);
when(grandExchangeOffer.getTotalQuantity()).thenReturn(10);
when(grandExchangeOffer.getPrice()).thenReturn(1000);
when(grandExchangeOffer.getSpent()).thenReturn(1000);
when(grandExchangeOffer.getState()).thenReturn(GrandExchangeOfferState.SELLING);
GrandExchangeOfferChanged grandExchangeOfferChanged = new GrandExchangeOfferChanged();
grandExchangeOfferChanged.setSlot(i);
grandExchangeOfferChanged.setOffer(grandExchangeOffer);
grandExchangePlugin.onGrandExchangeOfferChanged(grandExchangeOfferChanged);
}
// Now send update for one of the slots
GrandExchangeOffer grandExchangeOffer = mock(GrandExchangeOffer.class);
when(grandExchangeOffer.getQuantitySold()).thenReturn(2);
when(grandExchangeOffer.getItemId()).thenReturn(ItemID.ABYSSAL_WHIP);
when(grandExchangeOffer.getTotalQuantity()).thenReturn(10);
when(grandExchangeOffer.getPrice()).thenReturn(1000);
when(grandExchangeOffer.getSpent()).thenReturn(2000);
when(grandExchangeOffer.getState()).thenReturn(GrandExchangeOfferState.SELLING);
GrandExchangeOfferChanged grandExchangeOfferChanged = new GrandExchangeOfferChanged();
grandExchangeOfferChanged.setSlot(2);
grandExchangeOfferChanged.setOffer(grandExchangeOffer);
grandExchangePlugin.onGrandExchangeOfferChanged(grandExchangeOfferChanged);
// verify trade update
ArgumentCaptor<GrandExchangeTrade> captor = ArgumentCaptor.forClass(GrandExchangeTrade.class);
verify(grandExchangeClient).submit(captor.capture());
GrandExchangeTrade trade = captor.getValue();
assertFalse(trade.isBuy());
assertEquals(ItemID.ABYSSAL_WHIP, trade.getItemId());
assertEquals(2, trade.getQty());
assertEquals(1, trade.getDqty());
assertEquals(10, trade.getTotal());
assertEquals(1000, trade.getDspent());
assertEquals(2000, trade.getSpent());
assertEquals(1000, trade.getOffer());
assertEquals(2, trade.getSlot());
assertTrue(trade.isLogin());
}
}