Jump to content

Reliable World Hopping


hcim

Recommended Posts

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.

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...