- 欢迎来到Minecraft插件百科!
- 对百科编辑一脸懵逼?帮助:快速入门带您快速熟悉百科编辑!
- 因近日遭受攻击,百科现已限制编辑,有意编辑请加入插件百科企鹅群:223812289
Citizens/API:修订间差异
Qsefthuopq(留言 | 贡献) (From Citizen WIKI) |
Qsefthuopq(留言 | 贡献) 无编辑摘要 |
||
第10行: | 第10行: | ||
<div class="padded" style="padding: 19px"> | <div class="padded" style="padding: 19px"> | ||
''' | '''最新正式版下载地址:''' [http://ci.citizensnpcs.co/ Jenkins] | ||
''' | '''开发版下载地址:''' [http://ci.citizensnpcs.co/job/CitizensAPI/ Jenkins] | ||
'''Documentation:''' [http://jd.citizensnpcs.co/ JavaDocs] | '''Documentation:''' [http://jd.citizensnpcs.co/ JavaDocs] | ||
''' | '''源码:''' [https://github.com/CitizensDev/CitizensAPI Github] | ||
}} | }} | ||
</div> | </div> | ||
</div> | </div> | ||
Citizens拥有广泛的API来让你制作与npc相关的插件或给npc添加新的角色特性。 确认你使用的是最新版的CitizensAPI来兼容最新版的Citizens. | |||
你可以在这里找到Javadocs:http://jd.citizensnpcs.co | |||
''' | '''如果你在使用 API的过程中遇到了问题''' 请在github 进行反馈 https://github.com/CitizensDev/Citizens2/issues 或者在Discord反馈:https://discord.gg/Q6pZGSR | ||
== | ==正确链接Java== | ||
Citizens和大多数的Java项目一样是用的Maven构造而成的。你构造关联citizens的插件也应该用Maven。 | |||
在你的 <code>pom.xml</code> (Maven 项目)文件内, 你可以使用这个 repository: | |||
<pre> | <pre> | ||
第60行: | 第60行: | ||
depend: [Citizens] into your plugin.yml. From here, a common basic entry point is the CitizensAPI class. This gives you access to the NPCRegistry for NPC lookup, as well as the TraitFactory which allows trait registration. If Citizens is not loaded, all CitizensAPI.* methods will return null. | depend: [Citizens] into your plugin.yml. From here, a common basic entry point is the CitizensAPI class. This gives you access to the NPCRegistry for NPC lookup, as well as the TraitFactory which allows trait registration. If Citizens is not loaded, all CitizensAPI.* methods will return null. | ||
== | == 创建一个 NPC == | ||
创建npc最简单的方法就是使用 NPCRegistry。这个东西管理npc的存储和创建 which manages the storage and creation of NPCs. 默认的registry由CitizensAPI.getNPCRegistry() 给予, 并且你可以创建一个新的and you can create new ones with different storage methods by calling other CitizensAPI methods. A Player NPC with name "fullwall" could then be created like this: | |||
NPC npc = CitizensAPI.getNPCRegistry().createNPC(EntityType.PLAYER, "fullwall"); | NPC npc = CitizensAPI.getNPCRegistry().createNPC(EntityType.PLAYER, "fullwall"); | ||
== | == 检测实体是否是NPC == | ||
Citizens NPCs will have the "NPC" metadata set to true. | Citizens NPCs will have the "NPC" metadata set to true. | ||
Eg. | Eg. |
2018年11月23日 (五) 05:16的版本
Citizens拥有广泛的API来让你制作与npc相关的插件或给npc添加新的角色特性。 确认你使用的是最新版的CitizensAPI来兼容最新版的Citizens.
你可以在这里找到Javadocs:http://jd.citizensnpcs.co
如果你在使用 API的过程中遇到了问题 请在github 进行反馈 https://github.com/CitizensDev/Citizens2/issues 或者在Discord反馈:https://discord.gg/Q6pZGSR
正确链接Java
Citizens和大多数的Java项目一样是用的Maven构造而成的。你构造关联citizens的插件也应该用Maven。
在你的 pom.xml
(Maven 项目)文件内, 你可以使用这个 repository:
<repository> <id>everything</id> <url>http://repo.citizensnpcs.co/</url> </repository>
and this dependency:
<dependency> <groupId>net.citizensnpcs</groupId> <artifactId>citizens</artifactId> <version>VERSION</version> <type>jar</type> <scope>provided</scope> </dependency>
And replace VERSION
with the current version of Citizens (check the filenames at http://ci.citizensnpcs.co/job/Citizens2/ for current version ID), formatted like 2.0.23-SNAPSHOT
(but with the version numbers changed to the relevant version).
Note that for most purposes, it's best to link citizens
rather than the API project, as many useful classes and utilities are not in the API. The main Citizens project includes the API and adds more options.
Hooking Into Citizens
Hooking into Citizens is as simple as creating a basic plugin and adding the line depend: [Citizens] into your plugin.yml. From here, a common basic entry point is the CitizensAPI class. This gives you access to the NPCRegistry for NPC lookup, as well as the TraitFactory which allows trait registration. If Citizens is not loaded, all CitizensAPI.* methods will return null.
创建一个 NPC
创建npc最简单的方法就是使用 NPCRegistry。这个东西管理npc的存储和创建 which manages the storage and creation of NPCs. 默认的registry由CitizensAPI.getNPCRegistry() 给予, 并且你可以创建一个新的and you can create new ones with different storage methods by calling other CitizensAPI methods. A Player NPC with name "fullwall" could then be created like this:
NPC npc = CitizensAPI.getNPCRegistry().createNPC(EntityType.PLAYER, "fullwall");
检测实体是否是NPC
Citizens NPCs will have the "NPC" metadata set to true. Eg.
boolean isCitizensNPC = entity.hasMetadata("NPC");
Creating a Trait
Traits are persistent, attachable objects that are linked to an NPC and provide specific functionality. This can be anything from a full-blown dynamic villager AI to a simple talking trait.
If using Maven, Citizens' Maven repo is available at http://repo.citizensnpcs.co
To register a trait, we use the TraitFactory class. This controls registration for your custom traits.
Code: Example registration and simple trait |
//This is your trait that will be applied to a npc using the /trait mytraitname command. Each NPC gets its own instance of this class. //the Trait class has a reference to the attached NPC class through the protected field 'npc' or getNPC(). //The Trait class also implements Listener so you can add EventHandlers directly to your trait. @TraitName("mytraitname") // convenience annotation in recent CitizensAPI versions for specifying trait name public class MyTrait extends Trait { public MyTrait() { super("mytraitname"); plugin = JavaPlugin.getPlugin(MyPlugin.class); } MyPlugin plugin = null; boolean SomeSetting = false; // see the 'Persistence API' section @Persist("mysettingname") boolean automaticallyPersistedSetting = false; // Here you should load up any values you have previously saved (optional). // This does NOT get called when applying the trait for the first time, only loading onto an existing npc at server start. // This is called AFTER onAttach so you can load defaults in onAttach and they will be overridden here. // This is called BEFORE onSpawn, npc.getBukkitEntity() will return null. public void load(DataKey key) { SomeSetting = key.getBoolean("SomeSetting", false); } // Save settings for this NPC (optional). These values will be persisted to the Citizens saves file public void save(DataKey key) { key.setBoolean("SomeSetting",SomeSetting); } // An example event handler. All traits will be registered automatically as Bukkit Listeners. @EventHandler public void click(net.citizensnpcs.api.event.NPCClickEvent event){ //Handle a click on a NPC. The event has a getNPC() method. //Be sure to check event.getNPC() == this.getNPC() so you only handle clicks on this NPC! } // Called every tick @Override public void run() { } //Run code when your trait is attached to a NPC. //This is called BEFORE onSpawn, so npc.getBukkitEntity() will return null //This would be a good place to load configurable defaults for new NPCs. @Override public void onAttach() { plugin.getServer().getLogger().info(npc.getName() + "has been assigned MyTrait!"); } // Run code when the NPC is despawned. This is called before the entity actually despawns so npc.getBukkitEntity() is still valid. @Override public void onDespawn() { } //Run code when the NPC is spawned. Note that npc.getBukkitEntity() will be null until this method is called. //This is called AFTER onAttach and AFTER Load when the server is started. @Override public void onSpawn() { } //run code when the NPC is removed. Use this to tear down any repeating tasks. @Override public void onRemove() { } } //This is your bukkit plugin class. Use it to hook your trait into Citizens and handle any commands. public class MyPlugin extends org.bukkit.plugin.java.JavaPlugin { public void onEnable() { //check if Citizens is present and enabled. if(getServer().getPluginManager().getPlugin("Citizens") == null || getServer().getPluginManager().getPlugin("Citizens").isEnabled() == false) { getLogger().log(Level.SEVERE, "Citizens 2.0 not found or not enabled"); getServer().getPluginManager().disablePlugin(this); return; } //Register your trait with Citizens. net.citizensnpcs.api.CitizensAPI.getTraitFactory().registerTrait(net.citizensnpcs.api.trait.TraitInfo.create(MyTrait.class)); } @Override public boolean onCommand(CommandSender sender, Command cmd, String cmdLabel, String[] inargs) { //handle commands for /myplugin } } |
Dos and Don'ts
white
- Check npc.isSpawned() before using npc.getBukkitEntity()
- Check npc.isSpawned() before using npc.getNavigator()
- Create a separate singleton Listener class if you expect there to be many instances of this trait running. This may help performance with frequently called events.
- Honor npc.data().get(NPC.DEFAULT_PROTECTED_METADATA) If this is true the NPC should be 'invulnerable' to normal damaging effects.
- use CitizensAPI.getNPCRegistry().isNPC() to check if an entity is a NPC. Real players and player-type NPCs will both return true for instanceof Player.
white
- Attempt to access npc.getBukkitEntity() from within traits until onSpawn() has been called or npc.isSpawned() returns true.
- Change anything in npc.getNavigator.getDefaultParams() unless you're sure you want a global change. Use the localParams() instead after setting a navigation target.
- Assume a NPC is a player-type. Mob types have some important differences.
Download an example
This is a link to a an example trait. It is similar to the code above, with some additional code for better handling commands, default configuration, and a plugin.yml
You will need to build against CitizensAPI.jar,Citizens.jar (although this is not always required), and Bukkit.jar.
NPC Events
Citizens implements its own Listeners and will call new NPC-specific versions of many common events. This saves Trait developers the trouble of finding their npcs from the normal event entities. The event object these events provide are just like their Bukkit counterparts with the addition of the getNPC() method. Citizens currently provides the following:
- EntityTargetNPCEvent
- NPCChatEvent
- NPCClickEvent
- NPCCollisionEvent
- NPCCombustByBlockEvent
- NPCCombustByEntityEvent
- NPCCombustEvent
- NPCDamageByBlockEvent
- NPCDamageByEntityEvent
- NPCDamageEvent
- NPCDeathEvent
- NPCDespawnEvent
- NPCEvent
- NPCLeftClickEvent
- NPCPushEvent
- NPCRemoveEvent
- NPCRightClickEvent
- NPCSelectEvent
- NPCSpawnEvent
- PlayerCreateNPCEvent
See the [Javadocs] for details.
Using the AI API
The AI API of Citizens can be broken down into two parts - GoalController and Navigator.
A Goal is a repeatable, abstract unit of work that can be performed by an NPC. It can be registered with a GoalController with a priority (higher is more important). The highest priority goal which can be executed will be prioritised. NPC contains getDefaultGoalController() for this purpose.
The GoalSelector allows a great deal of flexibility within goal implementations. It allows firstly the dynamic selection of sub-goals and the concurrent execution of many sub-goals, and can stop execution at any time.
In recent versions of CitizensAPI the "Behavior" class is introduced which allows a behavior tree-based AI approach that is backwards compatible with Goals and GoalControllers.
Code: Example |
public class MyGoal implements Goal {
private Object state;
private GoalSelector selector; // the current selector
public void reset() {
state = null;
// this method can be called at any time - tear down any state
}
public void run() {
if(!npcIsCool()) {
selector.finish(); // stops execution
} else if (npcIsAwesome()){
selector.select(new AwesomeGoal()); // this switches execution to AwesomeGoal and stops execution of this goal.
} else if (npcNeedsCool()) {
selector.selectAdditional(new AccumulateCoolGoal()); // AccumulateCoolGoal executes concurrently to this goal.
}
}
public boolean shouldExecute(GoalSelector selector) {
if (npcIsCool()) {
this.selector = selector;
return true;
}
return false;
}
}
|
The second concept is the Navigator. This controls the pathfinding aspects of the NPC. The Navigator can have one target at a time, and will call events to notify of completion/cancellation:
- NavigationBeginEvent
- NavigationCancelEvent
- NavigationCompleteEvent
- NavigationEvent
- NavigationReplaceEvent
The pathfinding range of the Navigator is the maximum range it will search when attempting to find a path to the target. This is usually set by the server admin. The speed modifier of the Navigator is the % modified movement speed of the NPC while moving to the target.
Using the Persistence API
Sometimes, traits can store a lot of simple variables such as primitives, Strings, Locations, and others. Saving/loading them via the trait API can be a little bit of overkill.
Citizens 2.0.4+ provides a simple Persistence API to automatically save and load these variables using DataKeys. The key to this API is the @Persist annotation. Sample code is provided below.
Code: Example |
public class MyTrait extends Trait {
// logic omitted.
@Persist boolean myVariable = false; // the default value of @Persist saves the value under the field name (in this case, 'myVariable').
@Persist("newkey") int intVariable = 11; // this saves the value under 'newkey'. The default value of the variable has been set to 11 - this will be used when loading if the key doesn't exist.
@Persist(value="newkey", required=true) String required; // if the value under 'newkey' doesn't exist, then the trait will fail to load.
}
|
More advanced use of the API can be found in the @DelegatePersistence annotation. This allows complex types such as Locations to be saved and loaded with finer grained control. These types can be given default delegates by calling PersistenceLoader#registerPersistDelegate(Persister) - Location has a built in Persister for convenience.
Code: Example |
public class MyTrait extends Trait {
// logic omitted.
@Persist
@DelegatePersistence(ExplicitComplexTypePersister.class) // explicit delegation
ComplexType myComplexType;
@Persist ComplexType implicitComplexType; // implicit delegation
static {
PersistenceLoader.registerPersistDelegate(ImplicitComplexTypePersister.class);
}
}
public class ExplicitComplexTypePersister implements Persister {
public Object create(DataKey root) {
return new ComplexType(root.getInteger("complexstructures"));
}
public void save(Object instance, DataKey root) {
ComplexType real = (ComplexType) instance; // guaranteed cast - will always succeed.
root.setInteger("complexstructures", real.getComplexStructure());
}
}
|