diff --git a/api/src/main/java/me/lucko/luckperms/api/MessagingService.java b/api/src/main/java/me/lucko/luckperms/api/MessagingService.java
index 8432ec97..8dda5379 100644
--- a/api/src/main/java/me/lucko/luckperms/api/MessagingService.java
+++ b/api/src/main/java/me/lucko/luckperms/api/MessagingService.java
@@ -35,8 +35,9 @@ public interface MessagingService {
/**
* Uses the messaging service to inform other servers about changes.
*
- *
This will push the update asynchronously, and this method will return immediately. Calling this method is
- * equivalent to running "/lp networksync", except will not sync this server.
+ * This will push the update asynchronously, and this method will return
+ * immediately. Calling this method is equivalent to running "/lp networksync",
+ * except will not sync this server.
*/
void pushUpdate();
diff --git a/common/src/main/java/me/lucko/luckperms/common/actionlog/ExtendedLogEntry.java b/common/src/main/java/me/lucko/luckperms/common/actionlog/ExtendedLogEntry.java
index 4f676bde..f2976e8c 100644
--- a/common/src/main/java/me/lucko/luckperms/common/actionlog/ExtendedLogEntry.java
+++ b/common/src/main/java/me/lucko/luckperms/common/actionlog/ExtendedLogEntry.java
@@ -349,10 +349,10 @@ public class ExtendedLogEntry implements LogEntry {
}
}
- public static JsonObject serializeWithId(UUID id, LogEntry entry) {
+ public static JsonObject serializeWithId(String id, LogEntry entry) {
JsonObject data = new JsonObject();
- data.add("id", new JsonPrimitive(id.toString()));
+ data.add("id", new JsonPrimitive(id));
data.add("actor", new JsonPrimitive(entry.getActor().toString()));
data.add("actorName", new JsonPrimitive(entry.getActorName()));
data.add("type", new JsonPrimitive(entry.getType().name()));
@@ -365,10 +365,10 @@ public class ExtendedLogEntry implements LogEntry {
return data;
}
- public static Map.Entry deserialize(JsonObject object) {
+ public static Map.Entry deserialize(JsonObject object) {
ExtendedLogEntryBuilder builder = build();
- UUID id = UUID.fromString(object.get("id").getAsString());
+ String id = object.get("id").getAsString();
builder.actor(UUID.fromString(object.get("actor").getAsString()));
builder.actorName(object.get("actorName").getAsString());
diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/abstraction/SubCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/abstraction/SubCommand.java
index a5a8edff..3b9b95b7 100644
--- a/common/src/main/java/me/lucko/luckperms/common/commands/abstraction/SubCommand.java
+++ b/common/src/main/java/me/lucko/luckperms/common/commands/abstraction/SubCommand.java
@@ -190,7 +190,7 @@ public abstract class SubCommand extends Command {
if (!sender.isImport()) {
Optional messagingService = plugin.getMessagingService();
if (messagingService.isPresent() && plugin.getConfiguration().get(ConfigKeys.AUTO_PUSH_UPDATES)) {
- messagingService.get().getUpdateBuffer().request();
+ messagingService.get().pushUserUpdate(user);
}
}
}
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
index ee706558..7d4deb24 100644
--- a/common/src/main/java/me/lucko/luckperms/common/messaging/AbstractMessagingService.java
+++ b/common/src/main/java/me/lucko/luckperms/common/messaging/AbstractMessagingService.java
@@ -27,6 +27,7 @@ package me.lucko.luckperms.common.messaging;
import lombok.Getter;
+import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
@@ -34,8 +35,11 @@ import me.lucko.luckperms.api.LogEntry;
import me.lucko.luckperms.common.actionlog.ExtendedLogEntry;
import me.lucko.luckperms.common.buffers.BufferedRequest;
import me.lucko.luckperms.common.config.ConfigKeys;
+import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
+import java.nio.ByteBuffer;
+import java.util.Base64;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
@@ -49,6 +53,10 @@ import java.util.function.Consumer;
public abstract class AbstractMessagingService implements ExtendedMessagingService {
protected static final String CHANNEL = "lpuc";
+ private static final String UPDATE_HEADER = "update:";
+ private static final String USER_UPDATE_HEADER = "userupdate:";
+ private static final String LOG_HEADER = "log";
+
@Getter
private final LuckPermsPlugin plugin;
@@ -72,19 +80,21 @@ public abstract class AbstractMessagingService implements ExtendedMessagingServi
protected abstract void sendMessage(String message);
protected void onMessage(String msg, Consumer callback) {
- if (msg.startsWith("update:") && msg.length() > "update:".length()) {
- UUID uuid = parseUpdateMessage(msg);
- if (uuid == null) {
+ if (msg.startsWith(UPDATE_HEADER) && msg.length() > UPDATE_HEADER.length()) {
+ String content = msg.substring(UPDATE_HEADER.length());
+
+ UUID requestId = uuidFromString(content);
+ if (requestId == null) {
return;
}
- if (!receivedMessages.add(uuid)) {
+ if (!receivedMessages.add(requestId)) {
return;
}
- plugin.getLog().info("[" + name + " Messaging] Received update ping with id: " + uuid.toString());
+ plugin.getLog().info("[" + name + " Messaging] Received update ping with id: " + requestId.toString());
- if (plugin.getApiProvider().getEventFactory().handleNetworkPreSync(false, uuid)) {
+ if (plugin.getApiProvider().getEventFactory().handleNetworkPreSync(false, requestId)) {
return;
}
@@ -94,25 +104,62 @@ public abstract class AbstractMessagingService implements ExtendedMessagingServi
callback.accept(msg);
}
- } else if (msg.startsWith("log:") && msg.length() > "log:".length()) {
- String logData = msg.substring("log:".length());
- Map.Entry entry = null;
- try {
- entry = ExtendedLogEntry.deserialize(gson.fromJson(logData, JsonObject.class));
- } catch (Exception e) {
- plugin.getLog().warn("Error whilst deserializing log: " + logData);
- e.printStackTrace();
- }
+ } else if (msg.startsWith(USER_UPDATE_HEADER) && msg.length() > USER_UPDATE_HEADER.length()) {
+ String content = msg.substring(USER_UPDATE_HEADER.length());
+ Map.Entry entry = uuidsFromString(content);
if (entry == null) {
return;
}
- if (!receivedMessages.add(entry.getKey())) {
+ UUID requestId = entry.getKey();
+ UUID userUuid = entry.getValue();
+
+ if (!receivedMessages.add(requestId)) {
return;
}
- plugin.getApiProvider().getEventFactory().handleLogReceive(entry.getKey(), entry.getValue());
+ User user = plugin.getUserManager().getIfLoaded(userUuid);
+ if (user == null) {
+ return;
+ }
+
+ plugin.getLog().info("[" + name + " Messaging] Received user update ping for '" + user.getFriendlyName() + "' with id: " + requestId.toString());
+
+ if (plugin.getApiProvider().getEventFactory().handleNetworkPreSync(false, requestId)) {
+ return;
+ }
+
+ plugin.getStorage().loadUser(user.getUuid(), null);
+
+ if (callback != null) {
+ callback.accept(msg);
+ }
+
+ } else if (msg.startsWith(LOG_HEADER) && msg.length() > LOG_HEADER.length()) {
+ String content = msg.substring(LOG_HEADER.length());
+
+ Map.Entry entry;
+ try {
+ entry = ExtendedLogEntry.deserialize(gson.fromJson(content, JsonObject.class));
+ } catch (Exception e) {
+ return;
+ }
+
+ if (entry.getKey() == null) {
+ return;
+ }
+
+ UUID requestId = uuidFromString(entry.getKey());
+ if (requestId == null) {
+ return;
+ }
+
+ if (!receivedMessages.add(requestId)) {
+ return;
+ }
+
+ plugin.getApiProvider().getEventFactory().handleLogReceive(requestId, entry.getValue());
plugin.getLogDispatcher().dispatchFromRemote(entry.getValue());
if (callback != null) {
@@ -122,26 +169,39 @@ public abstract class AbstractMessagingService implements ExtendedMessagingServi
}
@Override
- public void pushLog(LogEntry logEntry) {
+ public void pushUpdate() {
plugin.getScheduler().doAsync(() -> {
- UUID id = generatePingId();
+ UUID requestId = generatePingId();
+ String strId = uuidToString(requestId);
- if (plugin.getApiProvider().getEventFactory().handleLogNetworkPublish(!plugin.getConfiguration().get(ConfigKeys.PUSH_LOG_ENTRIES), id, logEntry)) {
- return;
- }
-
- plugin.getLog().info("[" + name + " Messaging] Sending log with id: " + id.toString());
- sendMessage("log:" + gson.toJson(ExtendedLogEntry.serializeWithId(id, logEntry)));
+ plugin.getLog().info("[" + name + " Messaging] Sending ping with id: " + strId);
+ sendMessage("update:" + strId);
});
}
@Override
- public void pushUpdate() {
+ public void pushUserUpdate(User user) {
plugin.getScheduler().doAsync(() -> {
- UUID id = generatePingId();
- plugin.getLog().info("[" + name + " Messaging] Sending ping with id: " + id.toString());
+ UUID requestId = generatePingId();
+ String strId = uuidToString(requestId);
- sendMessage("update:" + id.toString());
+ plugin.getLog().info("[" + name + " Messaging] Sending user ping for '" + user.getFriendlyName() + "' with id: " + strId);
+ sendMessage("userupdate:" + uuidsToString(requestId, user.getUuid()));
+ });
+ }
+
+ @Override
+ public void pushLog(LogEntry logEntry) {
+ plugin.getScheduler().doAsync(() -> {
+ UUID requestId = generatePingId();
+ String strId = uuidToString(requestId);
+
+ if (plugin.getApiProvider().getEventFactory().handleLogNetworkPublish(!plugin.getConfiguration().get(ConfigKeys.PUSH_LOG_ENTRIES), requestId, logEntry)) {
+ return;
+ }
+
+ plugin.getLog().info("[" + name + " Messaging] Sending log with id: " + strId);
+ sendMessage("log:" + gson.toJson(ExtendedLogEntry.serializeWithId(strId, logEntry)));
});
}
@@ -151,10 +211,39 @@ public abstract class AbstractMessagingService implements ExtendedMessagingServi
return uuid;
}
- private static UUID parseUpdateMessage(String msg) {
- String requestId = msg.substring("update:".length());
+ private static String uuidToString(UUID uuid) {
+ ByteBuffer buf = ByteBuffer.allocate(Long.BYTES * 2);
+ buf.putLong(uuid.getMostSignificantBits());
+ buf.putLong(uuid.getLeastSignificantBits());
+ return Base64.getEncoder().encodeToString(buf.array());
+ }
+
+ private static UUID uuidFromString(String s) {
try {
- return UUID.fromString(requestId);
+ byte[] bytes = Base64.getDecoder().decode(s);
+ ByteBuffer buf = ByteBuffer.wrap(bytes);
+ return new UUID(buf.getLong(), buf.getLong());
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ private static String uuidsToString(UUID uuid1, UUID uuid2) {
+ ByteBuffer buf = ByteBuffer.allocate(Long.BYTES * 4);
+ buf.putLong(uuid1.getMostSignificantBits());
+ buf.putLong(uuid1.getLeastSignificantBits());
+ buf.putLong(uuid2.getMostSignificantBits());
+ buf.putLong(uuid2.getLeastSignificantBits());
+ return Base64.getEncoder().encodeToString(buf.array());
+ }
+
+ private static Map.Entry uuidsFromString(String s) {
+ try {
+ byte[] bytes = Base64.getDecoder().decode(s);
+ ByteBuffer buf = ByteBuffer.wrap(bytes);
+ UUID uuid1 = new UUID(buf.getLong(), buf.getLong());
+ UUID uuid2 = new UUID(buf.getLong(), buf.getLong());
+ return Maps.immutableEntry(uuid1, uuid2);
} catch (IllegalArgumentException e) {
return null;
}
@@ -162,7 +251,7 @@ public abstract class AbstractMessagingService implements ExtendedMessagingServi
private final class PushUpdateBuffer extends BufferedRequest {
public PushUpdateBuffer(LuckPermsPlugin plugin) {
- super(3000L, 200L, plugin.getScheduler().async());
+ super(2000L, 200L, plugin.getScheduler().async());
}
@Override
diff --git a/common/src/main/java/me/lucko/luckperms/common/messaging/ExtendedMessagingService.java b/common/src/main/java/me/lucko/luckperms/common/messaging/ExtendedMessagingService.java
index 425f5439..e5cfb508 100644
--- a/common/src/main/java/me/lucko/luckperms/common/messaging/ExtendedMessagingService.java
+++ b/common/src/main/java/me/lucko/luckperms/common/messaging/ExtendedMessagingService.java
@@ -28,6 +28,7 @@ package me.lucko.luckperms.common.messaging;
import me.lucko.luckperms.api.LogEntry;
import me.lucko.luckperms.api.MessagingService;
import me.lucko.luckperms.common.buffers.BufferedRequest;
+import me.lucko.luckperms.common.model.User;
public interface ExtendedMessagingService extends MessagingService {
@@ -50,6 +51,13 @@ public interface ExtendedMessagingService extends MessagingService {
*/
BufferedRequest getUpdateBuffer();
+ /**
+ * Pushes an update for a specific user.
+ *
+ * @param user the user
+ */
+ void pushUserUpdate(User user);
+
/**
* Pushes a log entry to connected servers.
*