Jump to content
Frequently Asked Questions
  • Are you not able to open the client? Try following our getting started guide
  • Still not working? Try downloading and running JarFix
  • Help! My bot doesn't do anything! Enable fresh start in client settings and restart the client
  • How to purchase with PayPal/OSRS/Crypto gold? You can purchase vouchers from other users
  • How to create custom listeners


    Neffarion

    Recommended Posts

    Hello first of all thanks for this tutorial. I tweaked it slightly for my needs, in the end, I have in my main class the @override method from my NpcListener (equivalent to GrandExchangeListener in your eg.). This method actually returns a List<NPC>, I would then like to use this List in my main onLoop() method, how can I do that please help? I tried various things but not working.

    Link to comment
    Share on other sites

    34 minutes ago, jex124 said:

    Hello first of all thanks for this tutorial. I tweaked it slightly for my needs, in the end, I have in my main class the @override method from my NpcListener (equivalent to GrandExchangeListener in your eg.). This method actually returns a List<NPC>, I would then like to use this List in my main onLoop() method, how can I do that please help? I tried various things but not working.

    The onLoop is independent of the listener methods. If you want to use the npc list on the onloop then you need to have a global variable that holds the values you want, update it on that new listener method and use it on the onLoop

    Link to comment
    Share on other sites

    2 hours ago, Neffarion said:

    The onLoop is independent of the listener methods. If you want to use the npc list on the onloop then you need to have a global variable that holds the values you want, update it on that new listener method and use it on the onLoop

    In this case, where should the global variable be defined? The listener is an interface so you say update the variable there? Let us take your example as the reference, if your @override onItemBought method returned an Item obj, how would you have modified your example to be able to use it inside the onLoop? Thank you for your help. 

    Link to comment
    Share on other sites

    1 hour ago, jex124 said:

    In this case, where should the global variable be defined? The listener is an interface so you say update the variable there? Let us take your example as the reference, if your @override onItemBought method returned an Item obj, how would you have modified your example to be able to use it inside the onLoop? Thank you for your help. 

    You can just create it on the AbstractScript itself for example for simplicity

    Alright, well this is very simple but for example, if you are waiting for a specific item to be bought you can make something like

    public class Script extends AbstractScript implements GrandExchangeListener {
    
        private final int wantedItemID = 3333;
        private boolean itemBought = false;
    
        @Override
        public void onStart() {
            ListenerManager.getInstance().addListener(new GrandExchangeEvent(this));
        }
    
        @Override
        public int onLoop() {
            if(this.itemBought){
              // collect item from GE
            }
          
            return 2000;
        }
    
        @Override
        public void onItemBought(GrandExchangeItemWrapper item) {
            if(item.getID() == this.wantedItemID){
                this.itemBought = true;
            }
        }
    
        @Override
        public void onItemSold(GrandExchangeItemWrapper item) {
            
        }
    
    }

     

    Link to comment
    Share on other sites

    37 minutes ago, Neffarion said:

    You can just create it on the AbstractScript itself for example for simplicity

    Alright, well this is very simple but for example, if you are waiting for a specific item to be bought you can make something like

    
    
    
    
    
    public class Script extends AbstractScript implements GrandExchangeListener {
    
        private final int wantedItemID = 3333;
        private boolean itemBought = false;
    
        @Override
        public void onStart() {
            ListenerManager.getInstance().addListener(new GrandExchangeEvent(this));
        }
    
        @Override
        public int onLoop() {
            if(this.itemBought){
              // collect item from GE
            }
          
            return 2000;
        }
    
        @Override
        public void onItemBought(GrandExchangeItemWrapper item) {
            if(item.getID() == this.wantedItemID){
                this.itemBought = true;
            }
        }
    
        @Override
        public void onItemSold(GrandExchangeItemWrapper item) {
            
        }
    
    }

     

    Okay so in my attempts to do this, I couldn't find success.

    public class Script extends AbstractScript implements NpcListener {
    
        @Override
        public void onStart() {
            ListenerManager.getInstance().addListener(new NpcEvent(this));
        }
    
        List<NPC> npcList; //also tried = new ArrayList<>(); also tried declaring as public/static/protected
    
    
        @Override
        public int onLoop() {
    
            if(!(this.npcList == null)) { //Also tried without instance reference "this."
    
                for (NPC n : npcList) {
                    n.interactForceRight("Examine");
                    sleep(2500);
                }
            }
    
            return 2000;
        }
    
        @Override
        public void npcExamine(NpcWrapper nW) {
            this.npcList = nW.getNpcList(); //wrapper simply contains List<NPC> as provided by my fire() method.
        }
    }
    

     

    my npcList is always null, however if I implement the npc Examine logic directly in the @Override npcExamine method, it runs. 😕

    Thanks for your help.

     

    Link to comment
    Share on other sites

    2 hours ago, jex124 said:

    Okay so in my attempts to do this, I couldn't find success.

    
    
    public class Script extends AbstractScript implements NpcListener {
    
        @Override
        public void onStart() {
            ListenerManager.getInstance().addListener(new NpcEvent(this));
        }
    
        List<NPC> npcList; //also tried = new ArrayList<>(); also tried declaring as public/static/protected
    
    
        @Override
        public int onLoop() {
    
            if(!(this.npcList == null)) { //Also tried without instance reference "this."
    
                for (NPC n : npcList) {
                    n.interactForceRight("Examine");
                    sleep(2500);
                }
            }
    
            return 2000;
        }
    
        @Override
        public void npcExamine(NpcWrapper nW) {
            this.npcList = nW.getNpcList(); //wrapper simply contains List<NPC> as provided by my fire() method.
        }
    }
    

     

    my npcList is always null, however if I implement the npc Examine logic directly in the @Override npcExamine method, it runs. 😕

    Thanks for your help.

     

    thats most likely your event listener logic that is returning a null list, show us the code
    also if you want an npc listener you should probably just pass a single npc

    Link to comment
    Share on other sites

    16 hours ago, Neffarion said:

    thats most likely your event listener logic that is returning a null list, show us the code
    also if you want an npc listener you should probably just pass a single npc

    Yes there was a ConcurrentModification error as my NPC list was getting updated while I was iterating through it in my NpcEvent. I solved it through calling the iteration logic through a static method. Thank you for your help! I learned a lot.. many hours well spent :D

     

     

    ps. my NpcEvent

    public final class NpcEvent extends AbstractEvent implements EventInterface {
        private final NpcListener event;
        public NpcEvent(AbstractScript script) {
            super(script);
            this.event = (NpcListener) parentEvent;
        }
        private final String npcStringName = "Chicken";
        private List<NPC> fireNpcList;// = new ArrayList<NPC>();
    
        @Override
        public void run() {
            while (!shouldStop() && canRun()) {
                if (canVerify()) {
                    //---Scans area of npc and stores tile locations------
                    List<Tile> startingTile = new ArrayList<>();
                    for (NPC n : NPCs.all(g -> g.getName().equals(npcStringName))) {
                        startingTile.add(n.getTile());
                    }
                    //----sleeps 5.5s for any changes to npc tile locations----
                    try {
                        Thread.sleep(5500);
                    } catch (InterruptedException e) {
                        MethodProvider.logError(e.toString());
                    }
                    //-----After sleep, scans area again for npc tile locations and stores them-------
                    List<Tile> endingTile = new ArrayList<>();
                    for (NPC n : NPCs.all(g -> g.getName().equals(npcStringName))) {
                        endingTile.add(n.getTile());
                    }
                    fireNpcList = handleConnected(startingTile,endingTile,npcStringName);
                    //----------fires the response------------------
                    if (!fireNpcList.isEmpty()) {
                        fire();
                    }
                    //-----------clears lists to start loop again
                    //fireNpcList.clear();
                }
            }
        }
    
        private static ArrayList<NPC> handleConnected(List<Tile> a, List<Tile> b, String s) {
            ArrayList<NPC> npc = new ArrayList<>();
            //------Logic to remove any tile locations that hasn't changed i.e. npc has not moved-------
            List<Tile> union = new ArrayList<Tile>(a);
            union.addAll(b);
            List<Tile> intersection = new ArrayList<Tile>(a);
            intersection.retainAll(b);
            union.removeAll(intersection);
            // -----------------creates a list of npc that has moved location ----------
            for (NPC n : NPCs.all(g -> g.getName().equals(s) &&
                    g.isOnScreen())) {
                if (union.contains(n.getTile())) {
                    npc.add(n);
                }
            }
            return npc;
        }
    
        @Override
        public void fire() {
            this.event.npcFight(new NpcWrapper(fireNpcList));
        }
    
        private boolean canVerify() {
            return Client.isLoggedIn();
        }
    
        private boolean shouldStop() {
            return !Client.getInstance().getScriptManager().isRunning();
        }
    }
    
    Link to comment
    Share on other sites

    2 hours ago, jex124 said:

    Yes there was a ConcurrentModification error as my NPC list was getting updated while I was iterating through it in my NpcEvent. I solved it through calling the iteration logic through a static method. Thank you for your help! I learned a lot.. many hours well spent :D

     

     

    ps. my NpcEvent

    
    public final class NpcEvent extends AbstractEvent implements EventInterface {
        private final NpcListener event;
        public NpcEvent(AbstractScript script) {
            super(script);
            this.event = (NpcListener) parentEvent;
        }
        private final String npcStringName = "Chicken";
        private List<NPC> fireNpcList;// = new ArrayList<NPC>();
    
        @Override
        public void run() {
            while (!shouldStop() && canRun()) {
                if (canVerify()) {
                    //---Scans area of npc and stores tile locations------
                    List<Tile> startingTile = new ArrayList<>();
                    for (NPC n : NPCs.all(g -> g.getName().equals(npcStringName))) {
                        startingTile.add(n.getTile());
                    }
                    //----sleeps 5.5s for any changes to npc tile locations----
                    try {
                        Thread.sleep(5500);
                    } catch (InterruptedException e) {
                        MethodProvider.logError(e.toString());
                    }
                    //-----After sleep, scans area again for npc tile locations and stores them-------
                    List<Tile> endingTile = new ArrayList<>();
                    for (NPC n : NPCs.all(g -> g.getName().equals(npcStringName))) {
                        endingTile.add(n.getTile());
                    }
                    fireNpcList = handleConnected(startingTile,endingTile,npcStringName);
                    //----------fires the response------------------
                    if (!fireNpcList.isEmpty()) {
                        fire();
                    }
                    //-----------clears lists to start loop again
                    //fireNpcList.clear();
                }
            }
        }
    
        private static ArrayList<NPC> handleConnected(List<Tile> a, List<Tile> b, String s) {
            ArrayList<NPC> npc = new ArrayList<>();
            //------Logic to remove any tile locations that hasn't changed i.e. npc has not moved-------
            List<Tile> union = new ArrayList<Tile>(a);
            union.addAll(b);
            List<Tile> intersection = new ArrayList<Tile>(a);
            intersection.retainAll(b);
            union.removeAll(intersection);
            // -----------------creates a list of npc that has moved location ----------
            for (NPC n : NPCs.all(g -> g.getName().equals(s) &&
                    g.isOnScreen())) {
                if (union.contains(n.getTile())) {
                    npc.add(n);
                }
            }
            return npc;
        }
    
        @Override
        public void fire() {
            this.event.npcFight(new NpcWrapper(fireNpcList));
        }
    
        private boolean canVerify() {
            return Client.isLoggedIn();
        }
    
        private boolean shouldStop() {
            return !Client.getInstance().getScriptManager().isRunning();
        }
    }
    

    Okay so you have some things you have done wrong, firstly you don't need a wrapper for this at all, passing an NPC to the listener methods is enough
    Secondly, you shouldn't filter the NPC's name here, that is done within the script when your npcFight gets called afterwards (think of the listener as being independent of your script)

    Make sure you null check the npc's and npc list

    List<Tile> startingTile = new ArrayList<>();
    List<NPC> npcs = NPCs.all(g -> g != null && g.getName().equals(npcStringName));
    if(npcs != null){
      for (NPC n : npcs) {
        startingTile.add(n.getTile());
      }
    }


    Regardless, I'm not entirely sure what is the objective of your listener, but you can't compare npcs with tiles alone because they can move

    5 seconds is a long time to check in a listener, the delay is too large, I wouldn't do more than 2 seconds at most

    Also you are not sleeping the thread if canVerify fails, if you log off your listener is going to spike the cpu




     

    Link to comment
    Share on other sites

    17 minutes ago, Neffarion said:

    Okay so you have some things you have done wrong, firstly you don't need a wrapper for this at all, passing an NPC to the listener methods is enough
    Secondly, you shouldn't filter the NPC's name here, that is done within the script when your npcFight gets called afterwards (think of the listener as being independent of your script)

    Make sure you null check the npc's and npc list

    
    List<Tile> startingTile = new ArrayList<>();
    List<NPC> npcs = NPCs.all(g -> g != null && g.getName().equals(npcStringName));
    if(npcs != null){
      for (NPC n : npcs) {
        startingTile.add(n.getTile());
      }
    }


    Regardless, I'm not entirely sure what is the objective of your listener, but you can't compare npcs with tiles alone because they can move

    5 seconds is a long time to check in a listener, the delay is too large, I wouldn't do more than 2 seconds at most

    Also you are not sleeping the thread if canVerify fails, if you log off your listener is going to spike the cpu




     

    Okay so if I understand correctly,

    1. My listener should only do one job, i.e. track npc tile location changes and fire this list to my main script, I should do logic based on this in my main script.

    2. I should add the thread sleep outside of the canVerify loop? However, since I take a scan ->sleep -> scan and compare changes, I need the sleep method inbetween, or I could implement a proper wrapper (I kept an empty wrapper just for completeness) that stores the states? .

    3. From experiement, I noticed the sleep time was too much, thanks for that.

     

    So the point of all of this is, sometimes when training Ranged from a safespot, an npc might become stuck behind an obstacle eventhough it is within the bow range. In that case, my character will continously run out -> attack -> fall back to safespot. This pattern looks obvious and is inefficient. My listener tracks npc tile locations and track for changes, if npc has not moved in a timeframe, he is likely to be stuck , so I ignore him.

     

    Thanks for your help.

    Link to comment
    Share on other sites

    52 minutes ago, jex124 said:

    Okay so if I understand correctly,

    1. My listener should only do one job, i.e. track npc tile location changes and fire this list to my main script, I should do logic based on this in my main script.

    2. I should add the thread sleep outside of the canVerify loop? However, since I take a scan ->sleep -> scan and compare changes, I need the sleep method inbetween, or I could implement a proper wrapper (I kept an empty wrapper just for completeness) that stores the states? .

    3. From experiement, I noticed the sleep time was too much, thanks for that.

     

    So the point of all of this is, sometimes when training Ranged from a safespot, an npc might become stuck behind an obstacle eventhough it is within the bow range. In that case, my character will continously run out -> attack -> fall back to safespot. This pattern looks obvious and is inefficient. My listener tracks npc tile locations and track for changes, if npc has not moved in a timeframe, he is likely to be stuck , so I ignore him.

     

    Thanks for your help.

    Yes

    You should try to do it as I did in the example in the main post, that way you can do only scan -> compare -> sleep (the first time it runs it won't trigger anything but that isn't really relevant as it is runs often, and you are probably not needing it at that point so early)

    For your problem I wouldn't personally use a listener as you can just save the npc when you attack and check for movement while you wait for it to kill it
     

    Link to comment
    Share on other sites

    Archived

    This topic is now archived and is closed to further replies.

    ×
    ×
    • Create New...

    Important Information

    We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.