-
Notifications
You must be signed in to change notification settings - Fork 4
Architecture
- Plume creates instances of all enabled module classes.
-
InitializationEvent
is fired. Services (explained below) are to be registered here. Configuration is also loaded and saved to disk. -
PostInitializationEvent
is fired. Use of requested services can happen here. -
FMLPreInitializationEvent
is fired. -
FMLServerStartingEvent
is fired. -
FMLServerStartedEvent
is fired.
Modules can be annotated with @Module
:
@Module(name = "example")
public class ExampleModule {
}
- Modules are normally automatically loaded on start.
- Modules can be enabled or disabled in the
config/plume/modules.cfg
file.
Some features (such as event bus registration) can be activated with @AutoRegister
instead of @Module
. Modules can be auto-loaded at boot time, but @AutoRegister
classes aren't loaded unless some other class requests one.
Works for classes with @Module
or @AutoRegister
.
Modules are subscribed against the FML and Forge event buses, as well as the Plume event bus.
To listen for FML state events, use @Subscribe
from WorldEdit.
@Module(name = "example")
public class ExampleModule {
@Subscribe
public void onServerStarted(FMLServerStartedEvent event) {
}
@SubscribeEvent
public void onWorldLoad(WorldEvent.Load event) {
}
}
Works for all classes that are auto-injected, including modules.
All classes (module or not) are loaded using the Guice library, which allows automatic injection of dependent classes:
public class ExampleModule {
@Inject
private OtherClass otherClass;
}
Or if constructor injection is preferred:
public class ExampleModule {
@Inject
public ExampleModule(OtherClass otherClass) {
}
}
The dependent classes themselves are injected, so they too can contain @Inject
and so on.
Note: Classes without @Module
, @AutoRegister
, or @Singleton
will be instanciated for every time an instance is requested. For example, if two classes request a shared object, both classes will get two different instances.
Works for all classes that are auto-injected, including modules.
Configuration can be written as a class:
public class ClaimConfig {
@Setting("cost")
public Cost cost = new Cost();
@ConfigSerializable
public static class Cost {
@Setting(comment = "The number of chunks that a user may own for free before claims have a cost")
public int gratisChunkCount = 25;
@Setting(comment = "The item required to buy a chunk")
public SingleItemMatcher item = Inventories.typeDamageMatcher(new ItemStack(Items.coal));
}
And then injected:
public class Claims {
@InjectConfig("claims") private Config<ClaimConfig> config;
}
The parameter for @InjectConfig
is the filename for the configuration file. If multiple classes use the same configuration and file, then every class will have the same instance of Config<?>
.
Configuration is available when InitializationEvent
is fired (after the VERY_EARLY
priority) and afterwards. Changes made to the configuration will be saved to disk in InitializationEvent
as well, in the VERY_LATE
priority.
Works for classes with @Module
or @AutoRegister
.
Commands can be registered easily with the @Command
annotation, and sub-commands are supported.
Below is an example of /debug tell <name> <age>
. The parameters to the method determine the parameters of the command.
@Command(aliases = "tell", desc = "Tell a sender someone's age")
@Group(@At("debug"))
@Require("plume.party.create")
public void create(@Sender EntityPlayer sender, String name, int age) {
sender.addChatMessage(Messages.info(name + " is " + age));
}
Java's resource bundles are used. Localization keys are stored in Plume.properties
.
Localization can be done with:
tr("claims.message", arg1, arg2, arg3)
// Run things in the main thread
@Inject private TickExecutorService tickExecutorService;
// Get the server ID (defined in the config) with environment.getServerId()
@Inject private Environment environment;
// Useful things for running commands in the background
@Inject private BackgroundExecutor executor;
// Convert names to UUIDs
@Inject private ProfileService profileService;
Plume contains a simple API for working with ListenableFutures, which comes in handy when tasks must be done in the background, and then eventually the routine has to return to the main thread:
Deferred<?> deferred = Deferreds
.when(() -> {
// This would block due to the HTTP request
UserId userId = profileService.findUserId(name);
return userId; // Passed onto the next handler
}, executor.getExecutor()) // The background executor
.done(userId -> {
sender.addChatMessage(Messages.info("Got " + userId));
}, tickExecutorService) // Run in the main thread
.fail(e -> {
if (e instanceof ProfileNotFoundException) {
sender.addChatMessage(Messages.error(tr("args.minecraftUserNotFound", ((ProfileNotFoundException) e).getName())));
} else if (e instanceof ProfileLookupException) {
sender.addChatMessage(Messages.error(tr("args.minecraftUserLookupFailed", ((ProfileLookupException) e).getName())));
} else {
sender.addChatMessage(Messages.exception(e));
}
}, tickExecutorService); // Run in the main thread
// If the task takes longer than ~500ms,
// a "please wait, processing" message gets sent
executor.notifyOnDelay(deferred, sender);