You are viewing an older version of the site. Click here to view
the latest version of this page. (This may be a dead link, if so, try the root page of the docs
here.)
== Developing an extension ==
Extensions provide a means of adding functionality to CommandHelper and the MethodScript engine.
An extension currently consists of three core parts:
# LifeCycle
# Functions
# Events
=== LifeCycles ===
The lifecycle class is the central part of the extension and does two things:
# Provide identity to the extension.
# Facilitate lifecycle operations, such as system events, utility methods, etc.
The minimal definition is as follows:
@MSExtension("ExtensionName") // Place your extension name here!
public class MyExtension extends AbstractExtension {
@Override
public Version getVersion() {
return new SimpleVersion(1,2,3); // Set your version here.
}
}
The class name given by the definition doesn't matter, but does assist with debugging in case of a stacktrace.
See the source code of AbstractExtension
for the full set of methods available to this class.
=== Functions ===
Functions will usually be the first thing a simpler extension will be used for. Functions should be "wrapped" in a parent class,
which provides general documentation for that group of functions. The full class definition for functions is as follows:
public class FunctionGroup {
public static String docs() {
return "These functions provide an example to new extension creators!";
}
@api
public static class some_function extends AbstractFunction {
<required methods>
}
<more functions>
}
=== Events ===
Events are a three part thing:
# The bindable object, which acts as a wrapper or data carrier for the event. This will be a POJO with methods to get the data needed.
# The actual extension point, which converts the bindable object into a MethodScript enabled event, with supporting methods.
# The actual call to fire the event.
The bindable event layout is simply a class that extends BindableEvent
, with one override, _GetObject()
.
If wrapping a Bukkit event, the Bukkit event should be returned here, given that the BindableEvent
instance
has been instantiated/provide with one. Otherwise, just return null.
The actual extension point is laid out as follows:
public class EventGroup {
public static String docs() {
return "These events provide an example to new extension creators!";
}
@api
public static class some_event extends AbstractGenericEvent<EventClass> {
<required methods>
}
<more events>
}
Notice that the event classes extend AbstractEvent
. As one can see, the layout is very similar to that of functions.
Events can be triggered from anywhere, with a call to EventUtils.TriggerListener(Driver.EXTENSION, <event name>, <event instance>);
.
The event name given must be the same as given in the event that has been defined, and the event instance an instance of that event.
=== Other Extension Points ===
==== Command Line Tools ====
Implement the CommandLineTool interface (or extend the AbstractCommandLineTool class) and tag the class with the @tool
annotation. All of the built in tools use this mechanism, and extensions can provide new tools just the same way.
==== Persistence Network Data Sources ====
Implement the DataSource interface (or extend the AbstractDataSource class) and tag the class with the @datasource
annotation.
=== Maven ===
Our build and dependency manager of choice is maven. While a quick and dirty setup with a bare-minimal pom is possible,
run-time speedups and compile-time checks will be missed out on if a few details aren't included.
Please include the following snippet under the <build><plugins>
section in the project's pom:
<!-- Leave this alone! Compile-time checks so that your extension works. -->
<plugin>
<groupId>org.bsc.maven</groupId>
<artifactId>maven-processor-plugin</artifactId>
<version>5.0</version>
<executions>
<execution>
<id>process</id>
<phase>process-classes</phase>
<goals>
<goal>process</goal>
</goals>
</execution>
</executions>
<configuration>
<outputDirectory>src/main/generated</outputDirectory>
<processors>
<processor>com.laytonsmith.core.extensions.ExtensionAnnotationProcessor</processor>
</processors>
</configuration>
</plugin>
<!-- Leave this alone! Run-time extension loading speedup. -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>cache-annotations</id>
<phase>process-classes</phase>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.laytonsmith.PureUtilities.ClassLoading.Annotations.CacheAnnotations</mainClass>
<arguments>
<argument>${basedir}/target/classes</argument>
<argument>${basedir}/target/classes</argument>
</arguments>
</configuration>
</plugin>
=== Obfuscation/ProGuard ===
Some extension devs have expressed a desire to obfuscate their code. Unfortunately, there's a gotcha: the caching
system we use runs before ProGuard obfuscates things, causing the original class names to be saved to the cache
instead of the obfuscated ones. The only way currently known to get around this is to tell ProGuard to not obfuscate
any of the extension points (lifecycle, function or event classes), via the -keep class
option in the
plugin's configuration
section.
However, one should be able to use the annotated extension points as wrappers for the true code, which would reside
in a different class.
=== Coding Standards ===
To prevent issues in the future, here are a few rules you should follow when creating functions and events:
Names of functions/events should be all lowercase and use underscores as spaces. The function name may NOT start with
a single underscore, and functions that start with two underscores are highly discouraged, except for meta functions
that deal with the compiler directly. Documentation should follow the same conventions, so that functions like
{{function|reflect_docs}} will work. The version that you return does not need to correspond to the MethodScript version,
it may be your extension's version number. If your function can be optimized, you are encouraged to do so.
{{LearningTrail}}
Find a bug in this page? Edit this page yourself, then submit a pull request.