Traum
Trade With Caution-
Posts
18 -
Joined
-
Last visited
-
Feedback
0%
About Traum
Profile Information
-
Gender
Male
Recent Profile Visitors
966 profile views
Traum's Achievements
Newbie (1/10)
11
Reputation
-
Synchronized blocks are a bit old-fashioned and verbose IMO, I would recommend using the java.util.concurrent package where possible. The Reentrant Lock object basically encapsulates the behavior you are describing in a simple object you can pass around more easily. import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockExample { public static void main(String[] args) { new Script().onStart(); } private static class Script { private Gui gui; public void onStart() { Lock lock = new ReentrantLock(); gui = new Gui(lock); new Thread(gui).start(); System.out.println("Script pre-lock"); lock.lock(); System.out.println("Script post-lock"); } } private static class Gui implements Runnable { private final Lock lock; public Gui(Lock lock) { this.lock = lock; } @Override public void run() { lock.lock(); /* * Simulate user behavior. */ for (int i = 1; i < 4; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Gui has had the lock for " + i + " seconds"); } onFinalAction(); } public void onFinalAction() { lock.unlock(); } } } Script pre-lock Gui has had the lock for 1 seconds Gui has had the lock for 2 seconds Gui has had the lock for 3 seconds Script post-lock
-
Hi, not very elegant, but should do the trick: public class Answer { private MethodProvider mp; public Answer() { // Step 1: find the closest marrentil slot from your mouse point. // Step 2: find the closest vial slot from the closest marrentill slot. int closestMarrentillSlot = getClosestSlotFromPoint(mp, mp.getMouse().getPosition(), (v) -> v != null && v.getName().equals("Marrentill")); Rectangle closestMarrentillSlotRectangle = InventorySlotDestination.getSlot(closestMarrentillSlot); Point closestMarrentillSlotPoint = new Point((int)closestMarrentillSlotRectangle.getCenterX(), (int)closestMarrentillSlotRectangle.getCenterY()); int closestVialSlot = getClosestSlotFromPoint(mp, closestMarrentillSlotPoint, (v) -> v != null && v.getName().equals("Vial of water")); } public static int getClosestSlotFromPoint(MethodProvider mp, Point point, Filter<Item> filter) { int slot = 0; // The closest slot. int distance = Integer.MAX_VALUE; // The smallest distance. for (int i = 0; i < 28; i++) { Item item = mp.getInventory().getItemInSlot(i); if(item == null || !filter.match(item)) continue; Rectangle rectangle = InventorySlotDestination.getSlot(i); if(rectangle == null) continue; int delta = (int)new Point((int)rectangle.getCenterX(), (int)rectangle.getCenterY()).distance(point); if(delta < distance) { slot = i; distance = delta; } } return slot; } }
-
Adapts an OSBot Filter to work as a Java Util Predicate and vice versa. Useful if you don't wan't to rewrite your Predicate and / or Filter libraries but still want to enjoy the benefits provided by both worlds. If someone has a cleaner method, please do let me know! public class FilterPredicate<V> implements Filter<V>, Predicate<V> { protected boolean _test(V value) { return false; } @Override public boolean match(V value) { return _test(value); } @Override public boolean test(V value) { return _test(value); } // Virtual constructor for OSBot Filter. public FilterPredicate<V> fromFilter(Filter<V> filter) { return new FilterPredicate<V>() { @Override protected boolean _test(V value) { return filter.match(value); } }; } // Virtual constructor for Java Util Predicate. public FilterPredicate<V> fromPredicate(Predicate<V> predicate) { return new FilterPredicate<V>() { @Override protected boolean _test(V value) { return predicate.test(value); } }; } } Examples: * EXAMPLE A: Predicate<V> predicate = ... Filter<V> filter = new FilterPredicate<V>().fromPredicate(predicate); * EXAMPLE B: Filter<V> filter = ... Predicate<V> predicate = new FilterPredicate<V>().fromFilter(filter); * EXAMPLE C: PredicateFilter<V> god = new PredicateFilter<V>().fromPredicate(lambda); god.predicate_specific_method(); god.filter_specific_method();
-
I think you did misinterpret it, but that's probably my fault: The list of presets is not part of the provided script-gui implementation, it's simply there to demonstrate an extra benefit of having a unifying data structure . Also, my GUI example is very minimal and not very thorough, I just quickly wrote it up to be able to demonstrate data passing (the actual subject of the tutorial) .
-
Oh yes absolutely, I just wanted to point out the verbosity when not making the data composite. Quick tip: when using enums as key values in a map, consider choosing an EnumMap over a HashMap!
-
Decorating entities, an alternative to static libraries I often see static libraries such as the following: public class CombatLibrary { private CombatLibrary() { } public static boolean attack(NPC target) { return target.interact("Attack"); } } Script call: CombatLibrary.attack(getNpcs().closest("Goblin")); It's fine, not very OO, but it works (for most things). However, let me introduce you to an arguably cleaner design by using the decorator pattern. First of, you'll need a utility class to wrap your NPCs , all decorator classes will extend this. We pass an existing NPC instance to the constructor. Our constructor calls NPC's constructor (NPCDecorator extends NPC, NPC is NPCDecorator's superclass) and passes it the NPC instance value's accessor value. public class NPCDecorator extends NPC { public NPCDecorator(NPC npc) { super(npc.accessor); } } Now let's make our first concrete decorator. We simply extend NPC decorator. We added a simple method calling a method of the NPC API with a specific parameter value. public class AttackableNPC extends NPCDecorator { public AttackableNPC(NPC npc) { super(npc); } public boolean attack() { return interact("Attack"); } } Script call: new AttackableNPC(getNpcs().closest("Goblin")).attack(); The provided implementation is very simple and does not provide more functionality than the static library method (as is). But it could be so much more, this design allows for OO (inheritance, polymorphism,etc..) among other things. I might write up a more complex implementation to demonstrate it's full power in the future!
-
[Tutorial] How to pass data from you GUI to your script Feel free to make suggestions / ask questions. 1. Create a DATA class - This class is going to hold all the data and variables modifiable by the end user. Example variables: String monsterName; String eatingThreshold; boolean useAntipoison; Position monsterCoordinates; Roles: - The variables need to be usable by our script (a different class), in order to enable access we can provide getter methods (safe) or just change the visibility of the variables (lazy). - The variables need to be modifiable by our GUI (again, a different class), in order to enable modification we can provide setter methods (safe) or just change the visibility of the variables (lazy). Example code: // This is a simple example data class for a fighter script. public class FighterData { // I'm not using getters / setters here to reduce verbosity, you should though. public String monsterName; public int eatThreshold; public boolean logWhenDied; public FighterData(String monsterName, int eatThreshold, boolean logWhenDied) { this.monsterName = monsterName; this.eatThreshold = eatThreshold; this.logWhenDied = logWhenDied; } } 2. Add an instance of your DATA class to your script @ScriptManifest(author = "Me", info = "", logo = "", name = "Fighter Script", version = 0) public class MyScript extends Script { private FighterData data = new FighterData("Goblin", 20, true); @Override public int onLoop() throws InterruptedException { if (myPlayer().getHealth() < data.eatThreshold) { // Eat. } return 200; } } So far all we have done is take the global variables from your script class (traditionally speaking) and moved them to their own separate class. QUESTION: Can't I just make my global script variables public (or provide getters and setter to them) and pass my script instance to the GUI? ANSWER: You could. Here are some arguments for using a separate class: - It will make your life much easier when you decide to implement importation and exportation of the data. - You can store multiple sets of data for later use easily and cleanly (for data-based task scheduling for example). - By encapsulating the data you can easily and without too much code, provide preset values for that data. Compare: public static final FighterData GOBLIN_SUICIDER = new FighterData("Goblin", -1, false); public static final FighterData DRUID_TANK = new FighterData("Druid", 20, true); public static final FighterData SPIDER_BOSS = new FighterData("Spider", 5, true); public static final FighterData ROCK_CRAB_SENSEI = new FighterData("Rock Crab", 6, true); public static final FighterData UNICORN_PRO = new FighterData("Unicorn", 9, true); private FighterData profile = GOBLIN_SUICIDER; vs: private String monsterName; private int eatThreshold; private boolean logWhenDied; public void build(String preset) { // We are just typing our data class's constructor code over and over again... switch (preset) { case "GOBLIN_SUICIDER": monsterName = "Goblin" eatThreshold = -1; logWhenDied = false; break; case "DRUID_TANK": monsterName = "Druid" eatThreshold = 20; logWhenDied = true; break; case "SPIDER_BOSS": monsterName = "Spider" eatThreshold = 5; logWhenDied = true; break; } case "ROCK_CRAB_SENSEI": monsterName = "Rock Crab" eatThreshold = 6; logWhenDied = true; break; } case "UNICORN_PRO": monsterName = "Unicorn" eatThreshold = 9; logWhenDied = true; break; } } build("GOBLIN_SUICIDER"); - Etcetera 3. Time for the GUI / VIEW code Current implementation: DATA OBJECT -> SCRIPT Desired implementation: GUI -> DATA OBJECT -> SCRIPT (The GUI and the SCRIPT share the DATA OBJECT). QUESTION: How do we share the data object between the SCRIPT class and the GUI class? ANSWER: Just pass a reference to the data object instance to the GUI (via a constructor for example). SCRIPT: package tutorial; import org.osbot.rs07.api.model.NPC; import org.osbot.rs07.script.Script; import org.osbot.rs07.script.ScriptManifest; @ScriptManifest(author = "Me", info = "", logo = "", name = "Fighter Script", version = 0) public class MyScripty extends Script { private FighterData data = new FighterData("Goblin", 20, true); private FighterGUI gui = new FighterGUI(data); @Override public void onStart() throws InterruptedException { super.onStart(); gui.setVisible(true); //... } private NPC target = null; private boolean died = false; @Override public int onLoop() throws InterruptedException { if (myPlayer().getHealth() < data.eatThreshold) { // Eat. } if (died && data.logWhenDied) { // Log out. } else { target = getNpcs().closest(data.monsterName); // Attack. } return 200; } } GUI: import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.SwingUtilities; public class FighterGUI extends JFrame { private static final long serialVersionUID = -1969829387256339229 L; private FighterData data; public FighterGUI(FighterData model) { super(); this.data = model; SwingUtilities.invokeLater(new Runnable() { public void run() { setTitle("Fighter Script"); setDefaultCloseOperation(DISPOSE_ON_CLOSE); setResizable(false); populate(); pack(); setLocationRelativeTo(null); } }); } private void populate() { JPanel cp = new JPanel(); setContentPane(cp); JTextField name = new JTextField("monster name"); cp.add(name); JButton nameok = new JButton("ok"); nameok.addActionListener((al) -> data.monsterName = name.getText()); cp.add(nameok); JTextField eat = new JTextField("eat threshold"); cp.add(eat); JButton eatok = new JButton("ok"); nameok.addActionListener((al) -> data.eatThreshold = Integer.parseInt(eat.getText())); // PLEASE DO NOT FORGET TO ADD INTEGER PARSING ERROR HANDLING HERE! cp.add(eatok); JCheckBox log = new JCheckBox("log after death"); log.addActionListener((al) -> data.logWhenDied = log.isSelected()); cp.add(log); } }