Skip to content

05. Creating a Custom Player

ThisTestUser edited this page Jun 7, 2022 · 5 revisions

The default player reads the instruction list and executes sequentially until there are no more instructions. In specific scenarios, you may want to create a custom player that runs differently. Custom players extend com.thistestuser.cursormacro.player.Player, and are added in the com.thistestuser.cursormacro.player.custom.CustomPlayers.register() method. That method adds a singleton instance of the custom player which should be initialized in the method. The CursorMacro instance should be passed in via the constructor as a field, which will be referred to as parent below.

You should define a volatile boolean playing in the class. The usage will be described below.

Here are the methods that need to be implemented:

setup()

This method only runs once at startup. The objective is to initialize fields and optionally set up listeners.

start(unpress)

First, set the variable playing to true, before initializing a new thread. After this, process the compiled instructions stored inside the player. The unpress option is a boolean that determines if any keys or buttons held down should be unpressed after the execution. The instructions are expected to keep track of this by calling Player.registerMouse() and Player.registerKey().

This compiled instructions should eventually be executed in a thread. The thread should first log the current time as shown in the example layout. After the instructions are executed, the thread should also call the unpress method after everything is finished running, if the unpress option is true. If your player should stop after a while, run parent.stopButton.doClick() if parent.autoStopOption.isSelected() is true. While the execution is happening, you should change the text in parent.stateLbl to give information to the user.

In addition, the thread needs to run CursorMacro.resetToIdleState() at the end of execution, if playing is false.

A suggested layout is shown below, which only depicts the code inside the thread, and does not include printing useful messages through stateLbl:

startTime = expectedTime = System.currentTimeMillis();
try {
  <execute instructions>
  if(unpress)
    unpress(parent.robot);
  //Only if the execution terminates naturally
  if(parent.autoStopOption.isSelected()) {
    parent.stopButton.doClick();
    return;
  }
} catch (Exception e) {
  //Catch errors here
} finally {
  if(!playing)
    parent.resetToIdleState();
}

Check the default implementation of the Player (DefaultPlayer) for more detailed guide on how the thread should work.

stop()

Sends a signal to stop the thread. Do not stop the thread directly; Instead, use Thread.interrupt() to skip over any thread sleeps and set running to false for the thread to finish executing early. This is to ensure that the unpress option is still respected.

isRunning()

This checks if the thread is still running. This can still be true even after the stop button is pressed. If this is true, CursorMacro will not allow any compiling or execution to take place.

shouldStop()

This indicates if the player should be terminating soon. This should be equal to !playing and is used by instructions to terminate early.

hasInstructions()

This indicates if the player has any instructions. If this is false, the "Start" button will be disabled.

compile(instrs)

This compiles with the text in the text pane, which is represented by a string instrs. The lines are separated by newline characters \n. Before this method is called, clearInstructions() has already been run, which means that there should be no instructions stored in the player. All exceptions thrown here should be casted into IllegalArgumentException to allow it to be caught. Subclasses of this exception are okay here, so throwing NumberFormatException is okay.

Note that the InstructionList class already provides a method compile(args) to parse a series of instructions recognized by the default player. This method should convert the text pane code to something that can be processed by InstructionList.

randomize(instrs, random, rndDelay, maxPercent, randomLoc, removeData)

This is used to get the code after applying a randomizer with the specified inputs, which is returned as a string. Like in compiling, this method should convert the text pane into something that can be processed by InstructionList. Note that no data should be lost between the returned output and input instrs, including whitespaces and comments. Also remember that the instructions stored should not be modified in any way at this method.

You will need insList.randomize(instrs, random, rndDelay, maxPercent, randomLoc, removeData) to process a series of instructions.

clearInstructions()

Clears all compiled instruction data in the player. This is used before compiling and when switching between different players.

getStringRepr()

This method returns a string that will be shown in the drop down panel.