diff --git a/bukkit/pom.xml b/bukkit/pom.xml
index 7f4a22ac..6f81b753 100644
--- a/bukkit/pom.xml
+++ b/bukkit/pom.xml
@@ -108,6 +108,13 @@
${project.version}
compile
+
+
+ lilypad.client.connect
+ api
+ 0.0.1-SNAPSHOT
+ provided
+
com.zaxxer
diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java
index 448fbc62..3c4084ce 100644
--- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java
+++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java
@@ -33,6 +33,8 @@ import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.bukkit.inject.Injector;
+import me.lucko.luckperms.bukkit.messaging.BungeeMessagingService;
+import me.lucko.luckperms.bukkit.messaging.LilyPadMessagingService;
import me.lucko.luckperms.bukkit.model.ChildPermissionProvider;
import me.lucko.luckperms.bukkit.model.DefaultsProvider;
import me.lucko.luckperms.bukkit.model.LPPermissible;
@@ -61,6 +63,7 @@ import me.lucko.luckperms.common.managers.UserManager;
import me.lucko.luckperms.common.managers.impl.GenericGroupManager;
import me.lucko.luckperms.common.managers.impl.GenericTrackManager;
import me.lucko.luckperms.common.managers.impl.GenericUserManager;
+import me.lucko.luckperms.common.messaging.AbstractMessagingService;
import me.lucko.luckperms.common.messaging.RedisMessaging;
import me.lucko.luckperms.common.storage.Storage;
import me.lucko.luckperms.common.storage.StorageFactory;
@@ -114,7 +117,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
private GroupManager groupManager;
private TrackManager trackManager;
private Storage storage;
- private RedisMessaging redisMessaging = null;
+ private AbstractMessagingService messagingService = null;
private UuidCache uuidCache;
private BukkitListener listener;
private ApiProvider apiProvider;
@@ -197,17 +200,40 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
// initialise datastore
storage = StorageFactory.getInstance(this, StorageType.H2);
- // initialise redis
- if (getConfiguration().get(ConfigKeys.REDIS_ENABLED)) {
+ // initialise messaging
+ String messagingType = getConfiguration().get(ConfigKeys.MESSAGING_SERVICE).toLowerCase();
+ if (messagingType.equals("redis")) {
getLog().info("Loading redis...");
- redisMessaging = new RedisMessaging(this);
- try {
- redisMessaging.init(getConfiguration().get(ConfigKeys.REDIS_ADDRESS), getConfiguration().get(ConfigKeys.REDIS_PASSWORD));
- getLog().info("Loaded redis successfully...");
- } catch (Exception e) {
- getLog().info("Couldn't load redis...");
- e.printStackTrace();
+ if (getConfiguration().get(ConfigKeys.REDIS_ENABLED)) {
+ RedisMessaging redis = new RedisMessaging(this);
+ try {
+ redis.init(getConfiguration().get(ConfigKeys.REDIS_ADDRESS), getConfiguration().get(ConfigKeys.REDIS_PASSWORD));
+ getLog().info("Loaded redis successfully...");
+
+ messagingService = redis;
+ } catch (Exception e) {
+ getLog().warn("Couldn't load redis...");
+ e.printStackTrace();
+ }
+ } else {
+ getLog().warn("Messaging Service was set to redis, but redis is not enabled!");
}
+ } else if (messagingType.equals("bungee")) {
+ getLog().info("Loading bungee messaging service...");
+ BungeeMessagingService bungeeMessaging = new BungeeMessagingService(this);
+ bungeeMessaging.init();
+ messagingService = bungeeMessaging;
+ } else if (messagingType.equals("lilypad")) {
+ getLog().info("Loading LilyPad messaging service...");
+ if (getServer().getPluginManager().getPlugin("LilyPad-Connect") == null) {
+ getLog().warn("LilyPad-Connect plugin not present.");
+ } else {
+ LilyPadMessagingService lilyPadMessaging = new LilyPadMessagingService(this);
+ lilyPadMessaging.init();
+ messagingService = lilyPadMessaging;
+ }
+ } else if (!messagingType.equals("none")) {
+ getLog().warn("Messaging service '" + messagingType + "' not recognised.");
}
// setup the update task buffer
@@ -341,9 +367,9 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
getLog().info("Closing datastore...");
storage.shutdown();
- if (redisMessaging != null) {
- getLog().info("Closing redis...");
- redisMessaging.shutdown();
+ if (messagingService != null) {
+ getLog().info("Closing messaging service...");
+ messagingService.close();
}
getLog().info("Unregistering API...");
@@ -374,7 +400,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
groupManager = null;
trackManager = null;
storage = null;
- redisMessaging = null;
+ messagingService = null;
uuidCache = null;
listener = null;
apiProvider = null;
diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/messaging/BungeeMessagingService.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/messaging/BungeeMessagingService.java
new file mode 100644
index 00000000..10fd2f78
--- /dev/null
+++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/messaging/BungeeMessagingService.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2016 Lucko (Luck)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.lucko.luckperms.bukkit.messaging;
+
+import com.google.common.collect.Iterables;
+import com.google.common.io.ByteArrayDataInput;
+import com.google.common.io.ByteArrayDataOutput;
+import com.google.common.io.ByteStreams;
+
+import me.lucko.luckperms.bukkit.LPBukkitPlugin;
+import me.lucko.luckperms.common.messaging.AbstractMessagingService;
+
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.messaging.PluginMessageListener;
+import org.bukkit.scheduler.BukkitRunnable;
+
+import java.util.Collection;
+
+/**
+ * An implementation of {@link me.lucko.luckperms.api.MessagingService} using the plugin messaging channels.
+ */
+public class BungeeMessagingService extends AbstractMessagingService implements PluginMessageListener {
+ private final LPBukkitPlugin plugin;
+
+ public BungeeMessagingService(LPBukkitPlugin plugin) {
+ super(plugin, "Bungee");
+ this.plugin = plugin;
+ }
+
+ public void init() {
+ plugin.getServer().getMessenger().registerOutgoingPluginChannel(plugin, CHANNEL);
+ plugin.getServer().getMessenger().registerIncomingPluginChannel(plugin, CHANNEL, this);
+ }
+
+ @Override
+ public void close() {
+ plugin.getServer().getMessenger().unregisterIncomingPluginChannel(plugin, CHANNEL);
+ plugin.getServer().getMessenger().unregisterOutgoingPluginChannel(plugin, CHANNEL);
+ }
+
+ @Override
+ protected void sendMessage(String channel, String message) {
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ Collection extends Player> players = plugin.getServer().getOnlinePlayers();
+ if (players.isEmpty()) {
+ return;
+ }
+
+ Player p = Iterables.getFirst(players, null);
+
+ ByteArrayDataOutput out = ByteStreams.newDataOutput();
+ out.writeUTF(message);
+
+ byte[] data = out.toByteArray();
+
+ p.sendPluginMessage(plugin, channel, data);
+ cancel();
+ }
+ }.runTaskTimer(plugin, 1L, 100L);
+ }
+
+ @Override
+ public void onPluginMessageReceived(String s, Player player, byte[] bytes) {
+ if (!s.equals(CHANNEL)) {
+ return;
+ }
+
+ ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
+ String msg = in.readUTF();
+
+ onMessage(s, msg, null);
+ }
+}
diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/messaging/LilyPadMessagingService.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/messaging/LilyPadMessagingService.java
new file mode 100644
index 00000000..f6287923
--- /dev/null
+++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/messaging/LilyPadMessagingService.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2016 Lucko (Luck)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.lucko.luckperms.bukkit.messaging;
+
+import me.lucko.luckperms.bukkit.LPBukkitPlugin;
+import me.lucko.luckperms.common.messaging.AbstractMessagingService;
+
+import lilypad.client.connect.api.Connect;
+import lilypad.client.connect.api.event.EventListener;
+import lilypad.client.connect.api.event.MessageEvent;
+import lilypad.client.connect.api.request.RequestException;
+import lilypad.client.connect.api.request.impl.MessageRequest;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Collections;
+
+/**
+ * An implementation of {@link me.lucko.luckperms.api.MessagingService} using LilyPad.
+ */
+public class LilyPadMessagingService extends AbstractMessagingService {
+ private final LPBukkitPlugin plugin;
+ private Connect connect;
+
+ public LilyPadMessagingService(LPBukkitPlugin plugin) {
+ super(plugin, "LilyPad");
+ this.plugin = plugin;
+ }
+
+ public void init() {
+ connect = plugin.getServer().getServicesManager().getRegistration(Connect.class).getProvider();
+ connect.registerEvents(this);
+ }
+
+ @Override
+ public void close() {
+ connect.unregisterEvents(this);
+ }
+
+ @Override
+ protected void sendMessage(String channel, String message) {
+ MessageRequest request;
+
+ try {
+ request = new MessageRequest(Collections.emptyList(), channel, message);
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ return;
+ }
+
+ try {
+ connect.request(request);
+ } catch (RequestException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @EventListener
+ public void onMessage(MessageEvent event) {
+ plugin.doAsync(() -> {
+ try {
+ String channel = event.getChannel();
+ String message = event.getMessageAsString();
+
+ onMessage(channel, message, null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ });
+ }
+}
diff --git a/bukkit/src/main/resources/config.yml b/bukkit/src/main/resources/config.yml
index b109e3de..632bddee 100644
--- a/bukkit/src/main/resources/config.yml
+++ b/bukkit/src/main/resources/config.yml
@@ -213,15 +213,23 @@ data:
# e.g. if you're using sqlite or flatfile, this can be set to -1 to save resources.
sync-minutes: 3
-# Settings for Redis.
+# Settings for the messaging service
#
-# If enabled and configured, LuckPerms will use the Redis PubSub system to inform other
+# If enabled and configured, LuckPerms will use the messaging system to inform other
# connected servers of changes. Use the command "/luckperms networksync" to push changes.
-# Data is NOT stored on redis. It is only used as a messaging platform.
+# Data is NOT stored using this service. It is only used as a messaging platform.
#
# If you decide to enable this feature, you should set "sync-minutes" to -1, as there is no need for LuckPerms
# to poll the database for changes.
#
+# Available options:
+# bungee ==> uses the plugin messaging channels. Must be enabled on all connected servers to work.
+# lilypad ==> uses lilypad pub sub to push changes. You need to have the LilyPad-Connect plugin installed.
+# redis ==> uses redis pub sub to push changes. Your redis server must be configured below.
+# none ==> nothing
+messaging-service: none
+
+# Settings for Redis.
# Port 6379 is used by default; set address to "host:port" if differs
redis:
enabled: false
diff --git a/bukkit/src/main/resources/plugin.yml b/bukkit/src/main/resources/plugin.yml
index 46d35525..abfd4221 100644
--- a/bukkit/src/main/resources/plugin.yml
+++ b/bukkit/src/main/resources/plugin.yml
@@ -11,6 +11,7 @@ load: STARTUP
# It in turn fixes issues where plugins using Vault cache the provided instance when their plugin enables, or
# when they check for the presence of a service provider, before LuckPerms has enabled.
loadbefore: [Vault]
+softdepend: [LilyPad-Connect]
commands:
luckperms:
diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeePlugin.java b/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeePlugin.java
index 18bf77d3..6cf883a2 100644
--- a/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeePlugin.java
+++ b/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeePlugin.java
@@ -29,6 +29,7 @@ import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.MutableContextSet;
+import me.lucko.luckperms.bungee.messaging.BungeeMessagingService;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.api.ApiHandler;
import me.lucko.luckperms.common.api.ApiProvider;
@@ -53,6 +54,7 @@ import me.lucko.luckperms.common.managers.UserManager;
import me.lucko.luckperms.common.managers.impl.GenericGroupManager;
import me.lucko.luckperms.common.managers.impl.GenericTrackManager;
import me.lucko.luckperms.common.managers.impl.GenericUserManager;
+import me.lucko.luckperms.common.messaging.AbstractMessagingService;
import me.lucko.luckperms.common.messaging.RedisMessaging;
import me.lucko.luckperms.common.storage.Storage;
import me.lucko.luckperms.common.storage.StorageFactory;
@@ -91,7 +93,7 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
private GroupManager groupManager;
private TrackManager trackManager;
private Storage storage;
- private RedisMessaging redisMessaging = null;
+ private AbstractMessagingService messagingService = null;
private UuidCache uuidCache;
private ApiProvider apiProvider;
private Logger log;
@@ -129,17 +131,31 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
// initialise datastore
storage = StorageFactory.getInstance(this, StorageType.H2);
- // initialise redis
- if (getConfiguration().get(ConfigKeys.REDIS_ENABLED)) {
+ // initialise messaging
+ String messagingType = getConfiguration().get(ConfigKeys.MESSAGING_SERVICE).toLowerCase();
+ if (messagingType.equals("redis")) {
getLog().info("Loading redis...");
- redisMessaging = new RedisMessaging(this);
- try {
- redisMessaging.init(getConfiguration().get(ConfigKeys.REDIS_ADDRESS), getConfiguration().get(ConfigKeys.REDIS_PASSWORD));
- getLog().info("Loaded redis successfully...");
- } catch (Exception e) {
- getLog().info("Couldn't load redis...");
- e.printStackTrace();
+ if (getConfiguration().get(ConfigKeys.REDIS_ENABLED)) {
+ RedisMessaging redis = new RedisMessaging(this);
+ try {
+ redis.init(getConfiguration().get(ConfigKeys.REDIS_ADDRESS), getConfiguration().get(ConfigKeys.REDIS_PASSWORD));
+ getLog().info("Loaded redis successfully...");
+
+ messagingService = redis;
+ } catch (Exception e) {
+ getLog().warn("Couldn't load redis...");
+ e.printStackTrace();
+ }
+ } else {
+ getLog().warn("Messaging Service was set to redis, but redis is not enabled!");
}
+ } else if (messagingType.equals("bungee")) {
+ getLog().info("Loading bungee messaging service...");
+ BungeeMessagingService bungeeMessaging = new BungeeMessagingService(this);
+ bungeeMessaging.init();
+ messagingService = bungeeMessaging;
+ } else if (!messagingType.equals("none")) {
+ getLog().warn("Messaging service '" + messagingType + "' not recognised.");
}
// setup the update task buffer
@@ -215,9 +231,9 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
getLog().info("Closing datastore...");
storage.shutdown();
- if (redisMessaging != null) {
- getLog().info("Closing redis...");
- redisMessaging.shutdown();
+ if (messagingService != null) {
+ getLog().info("Closing messaging service...");
+ messagingService.close();
}
getLog().info("Unregistering API...");
diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/messaging/BungeeMessagingService.java b/bungee/src/main/java/me/lucko/luckperms/bungee/messaging/BungeeMessagingService.java
new file mode 100644
index 00000000..b85b131c
--- /dev/null
+++ b/bungee/src/main/java/me/lucko/luckperms/bungee/messaging/BungeeMessagingService.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2016 Lucko (Luck)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.lucko.luckperms.bungee.messaging;
+
+import com.google.common.io.ByteArrayDataInput;
+import com.google.common.io.ByteArrayDataOutput;
+import com.google.common.io.ByteStreams;
+
+import me.lucko.luckperms.bungee.LPBungeePlugin;
+import me.lucko.luckperms.common.messaging.AbstractMessagingService;
+
+import net.md_5.bungee.api.config.ServerInfo;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.event.PluginMessageEvent;
+import net.md_5.bungee.api.plugin.Listener;
+import net.md_5.bungee.event.EventHandler;
+
+/**
+ * An implementation of {@link me.lucko.luckperms.api.MessagingService} using the plugin messaging channels.
+ */
+public class BungeeMessagingService extends AbstractMessagingService implements Listener {
+ private final LPBungeePlugin plugin;
+
+ public BungeeMessagingService(LPBungeePlugin plugin) {
+ super(plugin, "Bungee");
+ this.plugin = plugin;
+ }
+
+ public void init() {
+ plugin.getProxy().getPluginManager().registerListener(plugin, this);
+ plugin.getProxy().registerChannel(CHANNEL);
+ }
+
+ @Override
+ public void close() {
+ plugin.getProxy().unregisterChannel(CHANNEL);
+ }
+
+ @Override
+ protected void sendMessage(String channel, String message) {
+
+ ByteArrayDataOutput out = ByteStreams.newDataOutput();
+ out.writeUTF(message);
+
+ byte[] data = out.toByteArray();
+
+ for (ServerInfo server : plugin.getProxy().getServers().values()) {
+ server.sendData(channel, data, true);
+ }
+ }
+
+ @EventHandler
+ public void onPluginMessage(PluginMessageEvent e) {
+ if (!e.getTag().equals(CHANNEL)) {
+ return;
+ }
+
+ e.setCancelled(true);
+
+ if (e.getSender() instanceof ProxiedPlayer) {
+ return;
+ }
+
+ ByteArrayDataInput in = ByteStreams.newDataInput(e.getData());
+ String msg = in.readUTF();
+
+ onMessage(e.getTag(), msg, u -> {
+ // Forward to other servers
+ plugin.doAsync(() -> sendMessage(CHANNEL, "update:" + u.toString()));
+ });
+ }
+}
diff --git a/bungee/src/main/resources/config.yml b/bungee/src/main/resources/config.yml
index dc9b5b2d..6ba7571f 100644
--- a/bungee/src/main/resources/config.yml
+++ b/bungee/src/main/resources/config.yml
@@ -155,15 +155,22 @@ data:
# e.g. if you're using sqlite or flatfile, this can be set to -1 to save resources.
sync-minutes: 3
-# Settings for Redis.
+# Settings for the messaging service
#
-# If enabled and configured, LuckPerms will use the Redis PubSub system to inform other
+# If enabled and configured, LuckPerms will use the messaging system to inform other
# connected servers of changes. Use the command "/luckpermsbungee networksync" to push changes.
-# Data is NOT stored on redis. It is only used as a messaging platform.
+# Data is NOT stored using this service. It is only used as a messaging platform.
#
# If you decide to enable this feature, you should set "sync-minutes" to -1, as there is no need for LuckPerms
# to poll the database for changes.
#
+# Available options:
+# bungee ==> uses the plugin messaging channels. Must be enabled on all connected servers to work.
+# redis ==> uses redis pub sub to push changes. Your redis server must be configured below.
+# none ==> nothing
+messaging-service: none
+
+# Settings for Redis.
# Port 6379 is used by default; set address to "host:port" if differs
redis:
enabled: false
diff --git a/common/src/main/java/me/lucko/luckperms/common/LuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/LuckPermsPlugin.java
index 85fdd5b9..b3d364e6 100644
--- a/common/src/main/java/me/lucko/luckperms/common/LuckPermsPlugin.java
+++ b/common/src/main/java/me/lucko/luckperms/common/LuckPermsPlugin.java
@@ -41,7 +41,7 @@ import me.lucko.luckperms.common.locale.LocaleManager;
import me.lucko.luckperms.common.managers.GroupManager;
import me.lucko.luckperms.common.managers.TrackManager;
import me.lucko.luckperms.common.managers.UserManager;
-import me.lucko.luckperms.common.messaging.RedisMessaging;
+import me.lucko.luckperms.common.messaging.AbstractMessagingService;
import me.lucko.luckperms.common.storage.Storage;
import me.lucko.luckperms.common.utils.BufferedRequest;
import me.lucko.luckperms.common.utils.DebugHandler;
@@ -103,7 +103,7 @@ public interface LuckPermsPlugin {
*
* @return the redis messaging service
*/
- RedisMessaging getRedisMessaging();
+ AbstractMessagingService getMessagingService();
/**
* Gets a wrapped logger instance for the platform.
diff --git a/common/src/main/java/me/lucko/luckperms/common/api/ApiProvider.java b/common/src/main/java/me/lucko/luckperms/common/api/ApiProvider.java
index 201a893a..937821f8 100644
--- a/common/src/main/java/me/lucko/luckperms/common/api/ApiProvider.java
+++ b/common/src/main/java/me/lucko/luckperms/common/api/ApiProvider.java
@@ -135,7 +135,7 @@ public class ApiProvider implements LuckPermsApi {
@Override
public Optional getMessagingService() {
- return Optional.ofNullable(plugin.getRedisMessaging());
+ return Optional.ofNullable(plugin.getMessagingService());
}
@Override
diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/NetworkSyncCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/NetworkSyncCommand.java
index eab9fb2d..d527be45 100644
--- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/NetworkSyncCommand.java
+++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/NetworkSyncCommand.java
@@ -44,8 +44,8 @@ public class NetworkSyncCommand extends SingleCommand {
plugin.getUpdateTaskBuffer().request().join();
Message.UPDATE_TASK_COMPLETE_NETWORK.send(sender);
- if (plugin.getRedisMessaging() != null) {
- plugin.getRedisMessaging().pushUpdate();
+ if (plugin.getMessagingService() != null) {
+ plugin.getMessagingService().pushUpdate();
Message.UPDATE_TASK_PUSH_SUCCESS.send(sender);
} else {
Message.UPDATE_TASK_PUSH_FAILURE.send(sender);
diff --git a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java
index 033007ca..5af6c75c 100644
--- a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java
+++ b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java
@@ -139,6 +139,7 @@ public class ConfigKeys {
.put("log", c.getString("split-storage.methods.log", "h2"))
.build();
}));
+ public static final ConfigKey MESSAGING_SERVICE = EnduringKey.wrap(StringKey.of("messaging-service", "none"));
public static final ConfigKey REDIS_ENABLED = EnduringKey.wrap(BooleanKey.of("redis.enabled", false));
public static final ConfigKey REDIS_ADDRESS = EnduringKey.wrap(StringKey.of("redis.address", null));
public static final ConfigKey REDIS_PASSWORD = EnduringKey.wrap(StringKey.of("redis.password", ""));
diff --git a/common/src/main/java/me/lucko/luckperms/common/messaging/AbstractMessagingService.java b/common/src/main/java/me/lucko/luckperms/common/messaging/AbstractMessagingService.java
new file mode 100644
index 00000000..ffed1614
--- /dev/null
+++ b/common/src/main/java/me/lucko/luckperms/common/messaging/AbstractMessagingService.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2016 Lucko (Luck)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.lucko.luckperms.common.messaging;
+
+import lombok.RequiredArgsConstructor;
+
+import me.lucko.luckperms.api.MessagingService;
+import me.lucko.luckperms.common.LuckPermsPlugin;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+import java.util.function.Consumer;
+
+/**
+ * An abstract implementation of {@link me.lucko.luckperms.api.MessagingService}.
+ */
+@RequiredArgsConstructor
+public abstract class AbstractMessagingService implements MessagingService {
+ public static final String CHANNEL = "lpuc";
+
+ private final LuckPermsPlugin plugin;
+ private final String name;
+
+ private final Set receivedMsgs = Collections.synchronizedSet(new HashSet<>());
+
+ public abstract void close();
+
+ protected abstract void sendMessage(String channel, String message);
+
+ protected void onMessage(String channel, String msg, Consumer callback) {
+ if (!channel.equals(CHANNEL)) {
+ return;
+ }
+
+ UUID uuid = parseUpdateMessage(msg);
+ if (uuid == null) {
+ return;
+ }
+
+ if (!receivedMsgs.add(uuid)) {
+ return;
+ }
+
+ plugin.getLog().info("[" + name + " Messaging] Received update ping with id: " + uuid.toString());
+ plugin.getUpdateTaskBuffer().request();
+
+ if (callback != null) {
+ callback.accept(uuid);
+ }
+ }
+
+ @Override
+ public void pushUpdate() {
+ plugin.doAsync(() -> {
+ UUID id = generateId();
+ plugin.getLog().info("[" + name + " Messaging] Sending ping with id: " + id.toString());
+
+ sendMessage(CHANNEL, "update:" + id.toString());
+ });
+ }
+
+ private UUID generateId() {
+ UUID uuid = UUID.randomUUID();
+ receivedMsgs.add(uuid);
+ return uuid;
+ }
+
+ private static UUID parseUpdateMessage(String msg) {
+ if (!msg.startsWith("update:")) {
+ return null;
+ }
+
+ String requestId = msg.substring("update:".length());
+ try {
+ return UUID.fromString(requestId);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+}
diff --git a/common/src/main/java/me/lucko/luckperms/common/messaging/RedisMessaging.java b/common/src/main/java/me/lucko/luckperms/common/messaging/RedisMessaging.java
index 29f0d073..24b4b9cd 100644
--- a/common/src/main/java/me/lucko/luckperms/common/messaging/RedisMessaging.java
+++ b/common/src/main/java/me/lucko/luckperms/common/messaging/RedisMessaging.java
@@ -24,7 +24,6 @@ package me.lucko.luckperms.common.messaging;
import lombok.RequiredArgsConstructor;
-import me.lucko.luckperms.api.MessagingService;
import me.lucko.luckperms.common.LuckPermsPlugin;
import redis.clients.jedis.Jedis;
@@ -32,22 +31,19 @@ import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisPubSub;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.UUID;
-
/**
- * Uses Redis to push/receive changes to/from other servers
+ * An implementation of {@link me.lucko.luckperms.api.MessagingService} using Redis.
*/
-@RequiredArgsConstructor
-public class RedisMessaging implements MessagingService {
- private static final String CHANNEL = "luckperms";
-
+public class RedisMessaging extends AbstractMessagingService {
private final LuckPermsPlugin plugin;
private JedisPool jedisPool;
private LPSub sub;
+ public RedisMessaging(LuckPermsPlugin plugin) {
+ super(plugin, "Redis");
+ this.plugin = plugin;
+ }
+
public void init(String address, String password) {
String[] addressSplit = address.split(":");
String host = addressSplit[0];
@@ -60,7 +56,7 @@ public class RedisMessaging implements MessagingService {
}
plugin.doAsync(() -> {
- sub = new LPSub(plugin);
+ sub = new LPSub(this);
try (Jedis jedis = jedisPool.getResource()) {
jedis.subscribe(sub, CHANNEL);
} catch (Exception e) {
@@ -69,60 +65,28 @@ public class RedisMessaging implements MessagingService {
});
}
- public void shutdown() {
+ @Override
+ public void close() {
sub.unsubscribe();
jedisPool.destroy();
}
@Override
- public void pushUpdate() {
- plugin.doAsync(() -> {
- UUID id = sub.generateId();
- plugin.getLog().info("[Redis Messaging] Sending redis ping with id: " + id.toString());
- try (Jedis jedis = jedisPool.getResource()) {
- jedis.publish(CHANNEL, "update:" + id.toString());
- } catch (Exception e) {
- e.printStackTrace();
- }
- });
+ protected void sendMessage(String channel, String message) {
+ try (Jedis jedis = jedisPool.getResource()) {
+ jedis.publish(CHANNEL, message);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
}
@RequiredArgsConstructor
private static class LPSub extends JedisPubSub {
- private final LuckPermsPlugin plugin;
- private final Set receivedMsgs = Collections.synchronizedSet(new HashSet<>());
-
- private UUID generateId() {
- UUID uuid = UUID.randomUUID();
- receivedMsgs.add(uuid);
- return uuid;
- }
+ private final RedisMessaging parent;
@Override
public void onMessage(String channel, String msg) {
- if (!channel.equals(CHANNEL)) {
- return;
- }
-
- if (!msg.startsWith("update:")) {
- return;
- }
-
- String requestId = msg.substring("update:".length());
- UUID uuid;
- try {
- uuid = UUID.fromString(requestId);
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- return;
- }
-
- if (!receivedMsgs.add(uuid)) {
- return;
- }
-
- plugin.getLog().info("[Redis Messaging] Received update ping with id: " + uuid.toString());
- plugin.getUpdateTaskBuffer().request();
+ parent.onMessage(channel, msg, null);
}
}
diff --git a/pom.xml b/pom.xml
index 069da032..440f786a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -85,5 +85,9 @@
placeholderapi
http://repo.extendedclip.com/content/repositories/placeholderapi/
+
+ lilypad-repo
+ http://ci.lilypadmc.org/plugin/repository/everything
+
diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java
index 84409abf..48881720 100644
--- a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java
+++ b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java
@@ -50,6 +50,7 @@ import me.lucko.luckperms.common.locale.NoopLocaleManager;
import me.lucko.luckperms.common.locale.SimpleLocaleManager;
import me.lucko.luckperms.common.managers.TrackManager;
import me.lucko.luckperms.common.managers.impl.GenericTrackManager;
+import me.lucko.luckperms.common.messaging.AbstractMessagingService;
import me.lucko.luckperms.common.messaging.RedisMessaging;
import me.lucko.luckperms.common.storage.Storage;
import me.lucko.luckperms.common.storage.StorageFactory;
@@ -65,6 +66,7 @@ import me.lucko.luckperms.sponge.commands.SpongeMainCommand;
import me.lucko.luckperms.sponge.contexts.WorldCalculator;
import me.lucko.luckperms.sponge.managers.SpongeGroupManager;
import me.lucko.luckperms.sponge.managers.SpongeUserManager;
+import me.lucko.luckperms.sponge.messaging.BungeeMessagingService;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.ServiceCacheHousekeepingTask;
import me.lucko.luckperms.sponge.service.base.LPSubjectCollection;
@@ -146,7 +148,7 @@ public class LPSpongePlugin implements LuckPermsPlugin {
private SpongeGroupManager groupManager;
private TrackManager trackManager;
private Storage storage;
- private RedisMessaging redisMessaging = null;
+ private AbstractMessagingService messagingService = null;
private UuidCache uuidCache;
private ApiProvider apiProvider;
private me.lucko.luckperms.api.Logger log;
@@ -185,18 +187,31 @@ public class LPSpongePlugin implements LuckPermsPlugin {
// initialise datastore
storage = StorageFactory.getInstance(this, StorageType.H2);
- // initialise redis
- if (getConfiguration().get(ConfigKeys.REDIS_ENABLED)) {
+ // initialise messaging
+ String messagingType = getConfiguration().get(ConfigKeys.MESSAGING_SERVICE).toLowerCase();
+ if (messagingType.equals("redis")) {
getLog().info("Loading redis...");
- redisMessaging = new RedisMessaging(this);
- try {
- redisMessaging.init(getConfiguration().get(ConfigKeys.REDIS_ADDRESS), getConfiguration().get(ConfigKeys.REDIS_PASSWORD));
- getLog().info("Loaded redis successfully...");
- } catch (Exception e) {
- getLog().info("Couldn't load redis...");
- e.printStackTrace();
- redisMessaging = null;
+ if (getConfiguration().get(ConfigKeys.REDIS_ENABLED)) {
+ RedisMessaging redis = new RedisMessaging(this);
+ try {
+ redis.init(getConfiguration().get(ConfigKeys.REDIS_ADDRESS), getConfiguration().get(ConfigKeys.REDIS_PASSWORD));
+ getLog().info("Loaded redis successfully...");
+
+ messagingService = redis;
+ } catch (Exception e) {
+ getLog().warn("Couldn't load redis...");
+ e.printStackTrace();
+ }
+ } else {
+ getLog().warn("Messaging Service was set to redis, but redis is not enabled!");
}
+ } else if (messagingType.equals("bungee")) {
+ getLog().info("Loading bungee messaging service...");
+ BungeeMessagingService bungeeMessaging = new BungeeMessagingService(this);
+ bungeeMessaging.init();
+ messagingService = bungeeMessaging;
+ } else if (!messagingType.equals("none")) {
+ getLog().warn("Messaging service '" + messagingType + "' not recognised.");
}
// setup the update task buffer
@@ -295,9 +310,9 @@ public class LPSpongePlugin implements LuckPermsPlugin {
getLog().info("Closing datastore...");
storage.shutdown();
- if (redisMessaging != null) {
- getLog().info("Closing redis...");
- redisMessaging.shutdown();
+ if (messagingService != null) {
+ getLog().info("Closing messaging service...");
+ messagingService.close();
}
getLog().info("Unregistering API...");
diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/messaging/BungeeMessagingService.java b/sponge/src/main/java/me/lucko/luckperms/sponge/messaging/BungeeMessagingService.java
new file mode 100644
index 00000000..55bc1826
--- /dev/null
+++ b/sponge/src/main/java/me/lucko/luckperms/sponge/messaging/BungeeMessagingService.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2016 Lucko (Luck)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.lucko.luckperms.sponge.messaging;
+
+import com.google.common.collect.Iterables;
+
+import me.lucko.luckperms.common.messaging.AbstractMessagingService;
+import me.lucko.luckperms.sponge.LPSpongePlugin;
+
+import org.spongepowered.api.Platform;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.network.ChannelBinding;
+import org.spongepowered.api.network.ChannelBuf;
+import org.spongepowered.api.network.RawDataListener;
+import org.spongepowered.api.network.RemoteConnection;
+
+import java.util.Collection;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An implementation of {@link me.lucko.luckperms.api.MessagingService} using the plugin messaging channels.
+ */
+public class BungeeMessagingService extends AbstractMessagingService implements RawDataListener {
+ private final LPSpongePlugin plugin;
+ private ChannelBinding.RawDataChannel channel = null;
+
+ public BungeeMessagingService(LPSpongePlugin plugin) {
+ super(plugin, "Bungee");
+ this.plugin = plugin;
+ }
+
+ public void init() {
+ channel = plugin.getGame().getChannelRegistrar().createRawChannel(plugin, CHANNEL);
+ channel.addListener(Platform.Type.SERVER, this);
+ }
+
+ @Override
+ public void close() {
+ if (channel != null) {
+ plugin.getGame().getChannelRegistrar().unbindChannel(channel);
+ }
+ }
+
+ @Override
+ protected void sendMessage(String channel, String message) {
+ plugin.getScheduler().createTaskBuilder().interval(10, TimeUnit.SECONDS).execute(task -> {
+ Collection players = plugin.getGame().getServer().getOnlinePlayers();
+ if (players.isEmpty()) {
+ return;
+ }
+
+ Player p = Iterables.getFirst(players, null);
+ this.channel.sendTo(p, buf -> buf.writeUTF(message));
+
+ }).submit(plugin);
+ }
+
+ @Override
+ public void handlePayload(ChannelBuf buf, RemoteConnection connection, Platform.Type type) {
+ String msg = buf.readUTF();
+ onMessage(CHANNEL, msg, null);
+ }
+}
diff --git a/sponge/src/main/resources/luckperms.conf b/sponge/src/main/resources/luckperms.conf
index 121aeac9..d22dd172 100644
--- a/sponge/src/main/resources/luckperms.conf
+++ b/sponge/src/main/resources/luckperms.conf
@@ -162,15 +162,22 @@ data {
sync-minutes=3
}
-# Settings for Redis.
+# Settings for the messaging service
#
-# If enabled and configured, LuckPerms will use the Redis PubSub system to inform other
+# If enabled and configured, LuckPerms will use the messaging system to inform other
# connected servers of changes. Use the command "/luckperms networksync" to push changes.
-# Data is NOT stored on redis. It is only used as a messaging platform.
+# Data is NOT stored using this service. It is only used as a messaging platform.
#
# If you decide to enable this feature, you should set "sync-minutes" to -1, as there is no need for LuckPerms
# to poll the database for changes.
#
+# Available options:
+# bungee ==> uses the plugin messaging channels. Must be enabled on all connected servers to work.
+# redis ==> uses redis pub sub to push changes. Your redis server must be configured below.
+# none ==> nothing
+messaging-service="none"
+
+# Settings for Redis.
# Port 6379 is used by default; set address to "host:port" if differs
redis {
enabled=false