Scripting: Difference between revisions

From Citizens Wiki

No edit summary
 
(14 intermediate revisions by 2 users not shown)
Line 1: Line 1:
== Scripting Repository ==
== NOTICE ==
http://scripts.citizensnpcs.com - submit user scripts (including Denizen scripts) here.


<span style="font-family:natalya-alternate-one; font-size:300%; margin-right:-7px; margin-left:-10px;">This page refers to the legacy Javascript scripting which is no longer fully supported. The main scripting support available in modern Citizens is through [https://denizenscript.com/ Denizen].</span>
== Scripting Support in Citizens2 ==
== Scripting Support in Citizens2 ==
Citizens2 includes support for various scripting languages including Lua, Javascript and Python.
Citizens2 includes support for various scripting languages including Lua, Javascript and Python.


Javascript support is built into Java; additional languages can be added in through installing things such as JRuby (http://jruby.org/), Jython (http://www.jython.org/), Kahlua (http://www.ohloh.net/p/kahlua) and more. These use the Java Scripting API to give common scripting support to Citizens.
Javascript support is built into Java; additional languages can be added in through installing things such as JRuby (http://jruby.org/), Jython (http://www.jython.org/), Kahlua (https://github.com/krka/kahlua2) and more.


The scripting API contains a few built in objects in the global namespace which aid in some tasks. Currently, the 'events' object allows registering event listeners for Bukkit events and a reference to the Citizens plugin object is available under 'plugin'.  
The scripting API contains a few built in objects in the global namespace which aid in some tasks. Currently, the 'events' object allows registering event listeners for Bukkit events and a reference to the Citizens plugin object is available under 'plugin'.  


== Behaviours ==
== Behaviours ==
Let's see an example of how to use scripts. Citizens builds in by default the 'Behaviour' trait - this lets you specify AI tasks via scripts. Behaviours can be added to the NPC via the /npc behaviour [behaviours] command, where behaviours is a list of files to use.
Let's see an example of how to use scripts. Scripts can be added to an NPC via the /npc script --add [scripts] command, where scripts is a list of files inside the plugins/Citizens/scripts folder to use. Scripts are differentiated by their extension -- make sure it is correct!


Scripts for behaviours should be placed in the plugins/Citizens/scripts/behaviours folder. Scripts are differentiated by their extension -- make sure it is correct!
When the script file is loaded by Citizens, it will call the method 'onLoad(NPC)' - anything can be performed at this time, although the NPC will not necessarily be spawned. If the method 'run(NPC)' exists it will also be called every tick by Citizens.


When the script file is loaded by Citizens, it will call the method 'addGoals(Goals, NPC)'. Goals can be added via calling goals.addGoal(priority, goal), where goal implements the interface specified here -http://jd.citizensnpcs.com/net/citizensnpcs/api/ai/Goal.html.
    function leftClick(event) {
        event.getClicker().sendMessage("hello from Javascript scripting!");
    }


An example of a behaviour file is given below - this implements a simple thief goal.
    function onLoad(npc) {
        events.on(Java.type('net.citizensnpcs.api.event.NPCLeftClickEvent').class, leftClick);
    }


{{codebox|height=300px|Example done in Javascript|<syntaxhighlight line='true' lang="javascript">
== Scripting Gotchas and Tips ==
importPackage(Packages.net.citizensnpcs.api.ai);
=== Javascript (Nashorn) ===
importPackage(Packages.org.bukkit.entity);
* Nashorn is a much faster Javascript engine only included in Java 7 and above. It is recommended to run this over the older Rhino engine.
importPackage(Packages.org.bukkit.inventory);
* Read https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/api.html for more information on Java-Javascript connection.
importPackage(Packages.net.citizensnpcs.api.ai.event);
importPackage(java.util);
function addGoals (goals, npc) {
var thiefGoal = {
        npc : npc,
reset : function() {
            this.player = null;
        },
run : function(selector) {
            if (this.player == null || !this.player.isOnline()) {
                selector.finish();
                return;
            }
            if (this.npc.getLocation().distanceSquared(this.player.getLocation()) < 2) {
                this.player.damage(1);
                var inventory = this.player.getInventory();
                for (var i = 0; i < inventory.size(); i++) {
                    var item = inventory.getItem(i);
                    if (item == null || item.getType() == Material.AIR) {
                        continue;
                    }
                    if (item.getAmount() == 1) {
                        inventory.setItem(i, null);
                    } else {
                        item.setAmount(item.getAmount() - 1);
                        inventory.setItem(i, item);
                    }
                    break;
                }
                this.player.sendMessage(npc.getName() + " Ha! Stole an item!");
                allowed = false; // wait to get home.
                this.npc.getNavigator().setTarget(this.home);
                selector.finish();
            }
        },
shouldExecute : function(selector) {
            if (!this.npc.isSpawned() || !allowed)
                return false;
            var nearby = this.npc.getBukkitEntity().getNearbyEntities(5,5,5);
            var size = nearby.size();
            for (var i = 0; i < size; i++) {
                if (entity instanceof Player) {
                    this.player = entity;
                    this.home = this.npc.getBukkitEntity().getLocation();
                    this.npc.getNavigator().setTarget(entity, false);
                    return true;
                }
            }
    return false;
      },
      allowed : true,
      onNavigationComplete : function(event) {
          if (event.getNPC() != this.npc)
              return;
          this.allowed = true;
      },
      equals : function(other) {
          return this == other;
      }
      };
        var goal = new Goal(thiefGoal);
        events.register(goal, "onNavigationComplete", NavigationCompleteEvent);
goals.addGoal(1, goal);
}
</syntaxhighlight>
}}


== Scripting Gotchas and Tips ==
=== Javascript (Rhino) ===
=== Javascript ===
* Some code will expect the methods equals and hashcode to be implemented - a simple equals function should be used often.
* Some code will expect the methods equals and hashcode to be implemented - a simple equals function should be used often.
* for.. in loops do not work with Java collections.
* for.. in loops do not work with Java collections.
* The default Javascript-Java implementation cannot use abstract classes.
* Rhino cannot use abstract classes.
* You can implement Java interfaces by using new [interfacename](object), where object contains the methods implementing the interface.
* You can implement Java interfaces by using new [interfacename](object), where object contains the methods implementing the interface.
* Use importPackage(Packages.name.space) or importClass(Packages.name.space.YourClass) to import other classes/packages. The Packages. prefix is not required if the class is in the java.util package.
* Use importPackage(Packages.name.space) or importClass(Packages.name.space.YourClass) to import other classes/packages. The Packages. prefix is not required if the class is in the java.util package.

Latest revision as of 19:16, 3 September 2021

NOTICE

This page refers to the legacy Javascript scripting which is no longer fully supported. The main scripting support available in modern Citizens is through Denizen.



Scripting Support in Citizens2

Citizens2 includes support for various scripting languages including Lua, Javascript and Python.

Javascript support is built into Java; additional languages can be added in through installing things such as JRuby (http://jruby.org/), Jython (http://www.jython.org/), Kahlua (https://github.com/krka/kahlua2) and more.

The scripting API contains a few built in objects in the global namespace which aid in some tasks. Currently, the 'events' object allows registering event listeners for Bukkit events and a reference to the Citizens plugin object is available under 'plugin'.

Behaviours

Let's see an example of how to use scripts. Scripts can be added to an NPC via the /npc script --add [scripts] command, where scripts is a list of files inside the plugins/Citizens/scripts folder to use. Scripts are differentiated by their extension -- make sure it is correct!

When the script file is loaded by Citizens, it will call the method 'onLoad(NPC)' - anything can be performed at this time, although the NPC will not necessarily be spawned. If the method 'run(NPC)' exists it will also be called every tick by Citizens.

   function leftClick(event) {
       event.getClicker().sendMessage("hello from Javascript scripting!");
   }
   function onLoad(npc) {
       events.on(Java.type('net.citizensnpcs.api.event.NPCLeftClickEvent').class, leftClick);
   }

Scripting Gotchas and Tips

Javascript (Nashorn)

Javascript (Rhino)

  • Some code will expect the methods equals and hashcode to be implemented - a simple equals function should be used often.
  • for.. in loops do not work with Java collections.
  • Rhino cannot use abstract classes.
  • You can implement Java interfaces by using new [interfacename](object), where object contains the methods implementing the interface.
  • Use importPackage(Packages.name.space) or importClass(Packages.name.space.YourClass) to import other classes/packages. The Packages. prefix is not required if the class is in the java.util package.