Plume is a collection of "core" services for running a server and a port of the "Rebar" plugin that was developed for SKCraft in early MC Beta. It continues Rebar's goals of supporting multiple servers, being performant, asynchronous, and fault-tolerant.
However, Plume is still in development.
- Module loading
- Configuration
- Services
- External modules
- User database
- Multiple group inheritance
- Whitelist support
- /invite
- Management commands (not planned; web UI is preferred)
- Authorization and permissions
- Server-specific permissions
- World-specific permissions
- Temporary groups
- Arbitrary context value support
- Host key support
- Bans
- Ban and pardon commands
- Ban lookup (planned but web UI is preferred)
- Broadcast to other servers to notify of new bans
- User-oriented chunk claiming
- Protection
- Management commands
- Per-chunk claim costs
- Query tools
- Client-side UI
- Friend lists (parties)
- Management commands
- Client side GUI
- Block logger
- Logging
- Block break
- Block place
- Bucket empty
- Bucket fill
- Entity damage
- Entity spawn
- Explosion
- Item drop
- Item pickup
- Open container
- Player chat
- Player command
- Player death
- Search
- Rollback
- Replay
- Preview
- Pruning
- Watson support
- Wand
- Logging
- Command blocking
- Server console-only commands
- Locations
- Warps
- Homes
- Cross-server chat
- Chat channels
- Chat name highlighting
- Colored player names
- Item blacklist
- Mod mode
- Message of the day
- Server restart countdown
- World border (planned to be abandoned in MC 1.8+)
- Profilers
- Tick profiler
- Java profiler
- Teleport commands
- /to
- /bring
- /return
- Spawn commands
- /spawn
- /setspawn
- Status commands
- /heal
- /feed
- /god
- Speed commands
- /flight
- /walk
- Enderpearl teleporting
- BeanShell
JDK 8 required. IntelliJ IDEA recommended.
Plume uses a module system where individual modules can be enabled or disabled, but the default modules — and the meat of Plume — require MySQL. In addition, only a whitelist mode is available at the moment, so please follow the instructions below to set yourself up.
- Install MySQL Community Server locally.
- Users not familiar with MySQL can install HeidiSQL.
- Create a
plume_dev
user withplume_dev
as the password. You can either give the user full access to the database, or preferrably grant read/write/manage access toplume\_*
and read access to the tablemysql.proc
. This can be done in HeidiSQL in "User manager" under "Tools", - Create the databases
plume_data
andplume_log
. In HeidiSQL, right click the server on the left, go to "Create new" and choose "Database". Use "utf8mb4_general_ci" as the collation. - Import the tables from
schema/
into their respective databases. In HeidiSQL, select "plume_data", go to "Load SQL file..", select "plume_data.sql", and then click the blue play button in the toolbar to execute the query. Do the same for "plume_log". - Run
./gradlew clean setupDecompWorkspace build
- In IntelliJ IDEA, open up the
build.gradle
file to create a new project for Plume. - Make sure to install the Lombok plugin (see IDEA's settings to install plugins, then click Browse Repositories) and then after IDEA restarts, enable "annotation processing" in settings (use the search box).
- On the Gradle panel (it may be on the right), browse to Plume - > Tasks -> Other and double click "genIntellijRuns". When it completes, confirm to reload the workspace.
- Run -> Edit Configurations, choose "Minecraft Server", and add
-Dfml.coreMods.load=com.skcraft.plume.asm.PlumePlugin
to VM options, followed bynogui
to program arguments.
Plume currently only runs on the server. When you first run the server, you will have to modify run/eula.txt
to accept the EULA. Connect to the server with a regular client (NOT the client provided within your IDE).
In addition, add an entry to the "group" table (in MySQL) with *
for permissions AND autoJoin
set to 1
. Once the server is started, use /invite <your_name>
to whitelist yourself.
- 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 all classes that are auto-injected, including modules.
Services can be registered in Plume's InitializationEvent
(use @Subscribe
):
services.register(BanManager.class, bans);
In this case, BanManager
is an interface and bans
refers to an implementation.
Another class that needs this service can request it:
@InjectService
private Service<BanManager> bans;
All requested services are currently required. Some time between InitializationEvent
and PostInitializationEvent
, Plume will check to make sure that all services requested exist. If not, loading will abort.
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);
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.