hcim 1 Posted October 2, 2023 Share Posted October 2, 2023 Over the last few weeks I have experienced problems with world hopping, specifically instances of missclicking that are resulting in accounts ending up in worlds they shouldn't be, but it is likely due to the low FPS values I maintain to maximize the number of clients I run. As a result I have had to write my own world hopping handler. The implementation is nothing special but for the ~18 hours I have been running it on one of my farms of 50 accounts it has worked perfectly fine and not had the same issues that WorldHopper.hop(world_id) caused for me. Code: public class WorldHopperExtensions { private static final int WORLD_SELECTOR_BASE_WIDGET = 69; private static final int WORLD_SELECTOR_INFORMATION_CHILD_WIDGET = 19; private static final int MAIN_INTERFACE_WIDGET = 161; private static final int MAIN_INTERFACE_BUTTON_LOGOUT = 45; private static final int TOP_RIGHT_INTERFACE = 164; private static final int TOP_RIGHT_BUTTON_LOGOUT = 33; private static final int LOGOUT_WIDGET = 182; private static final int LOGOUT_WIDGET_WORLD_SWITCHER = 7; /** * Opens the world switcher menu, if necessary, then scrolls to and clicks a world widget - attempting a hop * @param world World number that you would like to hop to * @return true if the hop was successful, false otherwise */ public static boolean clickWorldWidget(int world) { Log.info("Preparing to hop to world: " + world); if (WorldHopper.getCurrentWorld() == world) return true; boolean openedWorldSwitcher = Retry.retry(3, WorldHopperExtensions::openWorldSwitcher); if (!openedWorldSwitcher) { Log.warn("Failed to open world switcher"); return false; } Waiting.wait(3600); // our scrolling may prematurely fail if we don't wait a bit (seems to be related to world list loading, increase this if proxies are particularly slow) boolean scrolled = Retry.retry(3, () -> Query.widgets().inIndexPath(WORLD_SELECTOR_BASE_WIDGET, WORLD_SELECTOR_INFORMATION_CHILD_WIDGET).textEquals(Integer.toString(world)).findFirst().map(Widget::scrollTo).orElse(false)); if (!scrolled) { Log.warn("Failed to scroll"); return false; } Waiting.wait(1200); // give the client a bit of time after scrolling before we query our widget position again, preventing missclicks boolean clickedWorld = Retry.retry(3, () -> Query.widgets().inIndexPath(WORLD_SELECTOR_BASE_WIDGET, WORLD_SELECTOR_INFORMATION_CHILD_WIDGET).widthEquals(28).textEquals(Integer.toString(world)).findFirst().map(Widget::click).orElse(false)); if (!clickedWorld) { Log.warn("Failed to click world"); return false; } boolean hopped = Waiting.waitUntil(30000, () -> GameState.getState() == GameState.State.LOGGED_IN && WorldHopper.getCurrentWorld() == world); Waiting.wait(2000); // for some bizarre reason if u try and daxpath after a world hop without delay it will have no idea where you are and try and home tele, so short delay required return hopped; } private static boolean openWorldSwitcher() { if (!isLogoutWindowOpen() && !isWorldSwitcherOpen()) { boolean clickedLogout = Retry.retry(3, () -> Query.widgets().inIndexPath(getWorldSwitchPath()).findFirst().map(Widget::click).orElse(false)); if (!clickedLogout) return false; } if (!isWorldSwitcherOpen()) { boolean clickedWorldSwitcher = Retry.retry(3, () -> Query.widgets().inIndexPath(LOGOUT_WIDGET, LOGOUT_WIDGET_WORLD_SWITCHER).findFirst().map(Widget::click).orElse(false)); if (!clickedWorldSwitcher) return false; } return isWorldSwitcherOpen(); } private static int[] getWorldSwitchPath() { return isModernLayout() ? new int[] { TOP_RIGHT_INTERFACE, TOP_RIGHT_BUTTON_LOGOUT } : new int[] { MAIN_INTERFACE_WIDGET, MAIN_INTERFACE_BUTTON_LOGOUT }; } private static boolean isModernLayout() { return GameState.getVarbit(4607) == 1; } private static boolean isLogoutWindowOpen() { return Waiting.waitUntil(1200, () -> Widgets.isVisible(LOGOUT_WIDGET, LOGOUT_WIDGET_WORLD_SWITCHER)); } private static boolean isWorldSwitcherOpen() { return Waiting.waitUntil(1200, () -> Widgets.isVisible(WORLD_SELECTOR_BASE_WIDGET)); } } Example usage: @Override public void execute(final String args) { boolean hopped = Query.worlds().isLowPing().findRandom().map(world -> WorldHopperExtensions.clickWorldWidget(world.getWorldNumber())).orElse(false); if (hopped) { Log.info("Successfully hopped :)"); } else { Log.error("I failed to hop to the world :("); } } Known issues: Sometimes the scrollTo method will only scroll to the point of showing half of the world widget, depending on where it goes to click on the widget this may result in it being unable to click the world to hop. Tribot offers little to no control on scrollTo so nothing I can fix on my end, on the plus side I've only observed it happening once after over 500 method calls. Quote Link to comment Share on other sites More sharing options...
SkrrtNick 243 Posted October 9, 2023 Share Posted October 9, 2023 Nice! I would recommend using the Waiting#waitNormal class rather than Waiting#wait, as you'd probably want to avoid static sleeps where you can 🙂 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.