Add PostNetworkSyncEvent

This commit is contained in:
Luck 2024-02-18 22:40:58 +00:00
parent 7d89c97907
commit 79273a8bcc
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
9 changed files with 222 additions and 24 deletions

View File

@ -33,7 +33,11 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.UUID;
/**
* Called when a log entry is received via the MessagingService
* Called when a log entry is received via the MessagingService.
*
* <p>Note: listening to this event is the same as listening to the {@link LogBroadcastEvent}
* and filtering for {@link LogBroadcastEvent#getOrigin() origin} =
* {@link net.luckperms.api.event.log.LogBroadcastEvent.Origin#REMOTE REMOTE}.</p>
*/
public interface LogReceiveEvent extends LuckPermsEvent {

View File

@ -0,0 +1,82 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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 net.luckperms.api.event.sync;
import net.luckperms.api.event.LuckPermsEvent;
import net.luckperms.api.event.type.Cancellable;
import net.luckperms.api.event.util.Param;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.UUID;
/**
* Called after a network synchronisation task has been completed.
*
* <p>Note: the generic {@link PostSyncEvent} will also be called for {@link SyncType#FULL full syncs}.</p>
*
* @since 5.5
*/
public interface PostNetworkSyncEvent extends LuckPermsEvent {
/**
* Gets the ID of the sync request
*
* @return the id of the sync request
*/
@Param(0)
@NonNull UUID getSyncId();
/**
* Gets the sync type.
*
* @return the sync type
*/
@Param(1)
@NonNull SyncType getType();
/**
* Gets if a sync occurred.
*
* <p>For {@link SyncType} = {@link SyncType#FULL FULL}, this method always returns true.</p>
*
* <p>For {@link SyncType} = {@link SyncType#SPECIFIC_USER SPECIFIC_USER}, this method returns true if the
* user in question was online/loaded in memory at the time, and false otherwise.</p>
*
* @return if a sync occurred
*/
@Param(2)
boolean didSyncOccur();
/**
* Gets the unique id of the specific user that has been synced, if applicable.
*
* @return the unique id of the specific user
*/
@Param(3)
@Nullable UUID getSpecificUserUniqueId();
}

View File

@ -28,7 +28,10 @@ package net.luckperms.api.event.sync;
import net.luckperms.api.event.LuckPermsEvent;
/**
* Called when an sync task has been completed
* Called after a full synchronisation task has been completed.
*
* <p>Note: this event is also called after synchronisations that were triggered over the network.
* In other words, this event will be called in addition to {@link PostNetworkSyncEvent}.</p>
*/
public interface PostSyncEvent extends LuckPermsEvent {

View File

@ -29,11 +29,15 @@ import net.luckperms.api.event.LuckPermsEvent;
import net.luckperms.api.event.type.Cancellable;
import net.luckperms.api.event.util.Param;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.UUID;
/**
* Called before a received network sync task runs
* Called after a request for synchronisation has been received via the messaging service,
* but before it has actually been completed.
*
* <p>Note: the generic {@link PreSyncEvent} will also be called for {@link SyncType#FULL full syncs}.</p>
*/
public interface PreNetworkSyncEvent extends LuckPermsEvent, Cancellable {
@ -45,4 +49,22 @@ public interface PreNetworkSyncEvent extends LuckPermsEvent, Cancellable {
@Param(0)
@NonNull UUID getSyncId();
/**
* Gets the sync type.
*
* @return the sync type
* @since 5.5
*/
@Param(1)
@NonNull SyncType getType();
/**
* Gets the unique id of the specific user that will be synced, if applicable.
*
* @return the unique id of the specific user
* @since 5.5
*/
@Param(2)
@Nullable UUID getSpecificUserUniqueId();
}

View File

@ -29,7 +29,10 @@ import net.luckperms.api.event.LuckPermsEvent;
import net.luckperms.api.event.type.Cancellable;
/**
* Called before a sync task runs
* Called just before a full synchronisation task runs.
*
* <p>Note: this event is also called before synchronisations that were triggered over the network.
* In other words, this event will be called in addition to {@link PreNetworkSyncEvent}.</p>
*/
public interface PreSyncEvent extends LuckPermsEvent, Cancellable {

View File

@ -0,0 +1,45 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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 net.luckperms.api.event.sync;
/**
* Represents the type of synchronisation task.
*
* @since 5.5
*/
public enum SyncType {
/**
* A full sync will be performed - all groups, users and tracks
*/
FULL,
/**
* Only a specific user will be synced
*/
SPECIFIC_USER
}

View File

@ -70,9 +70,11 @@ import net.luckperms.api.event.player.lookup.UsernameLookupEvent;
import net.luckperms.api.event.player.lookup.UsernameValidityCheckEvent;
import net.luckperms.api.event.source.Source;
import net.luckperms.api.event.sync.ConfigReloadEvent;
import net.luckperms.api.event.sync.PostNetworkSyncEvent;
import net.luckperms.api.event.sync.PostSyncEvent;
import net.luckperms.api.event.sync.PreNetworkSyncEvent;
import net.luckperms.api.event.sync.PreSyncEvent;
import net.luckperms.api.event.sync.SyncType;
import net.luckperms.api.event.track.TrackCreateEvent;
import net.luckperms.api.event.track.TrackDeleteEvent;
import net.luckperms.api.event.track.TrackLoadAllEvent;
@ -270,12 +272,16 @@ public final class EventDispatcher {
postAsync(ConfigReloadEvent.class);
}
public void dispatchNetworkPostSync(UUID id, SyncType type, boolean didOccur, UUID specificUserUniqueId) {
postAsync(PostNetworkSyncEvent.class, id, type, didOccur, specificUserUniqueId);
}
public void dispatchPostSync() {
postAsync(PostSyncEvent.class);
}
public boolean dispatchNetworkPreSync(boolean initialState, UUID id) {
return postCancellable(PreNetworkSyncEvent.class, initialState, id);
public boolean dispatchNetworkPreSync(boolean initialState, UUID id, SyncType type, UUID specificUserUniqueId) {
return postCancellable(PreNetworkSyncEvent.class, initialState, id, type, specificUserUniqueId);
}
public boolean dispatchPreSync(boolean initialState) {
@ -414,6 +420,7 @@ public final class EventDispatcher {
UsernameLookupEvent.class,
UsernameValidityCheckEvent.class,
ConfigReloadEvent.class,
PostNetworkSyncEvent.class,
PostSyncEvent.class,
PreNetworkSyncEvent.class,
PreSyncEvent.class,

View File

@ -39,6 +39,7 @@ import me.lucko.luckperms.common.util.ExpiringSet;
import me.lucko.luckperms.common.util.gson.GsonProvider;
import me.lucko.luckperms.common.util.gson.JObject;
import net.luckperms.api.actionlog.Action;
import net.luckperms.api.event.sync.SyncType;
import net.luckperms.api.messenger.IncomingMessageConsumer;
import net.luckperms.api.messenger.Messenger;
import net.luckperms.api.messenger.MessengerProvider;
@ -235,29 +236,35 @@ public class LuckPermsMessagingService implements InternalMessagingService, Inco
private void processIncomingMessage(Message message) {
if (message instanceof UpdateMessage) {
UpdateMessage msg = (UpdateMessage) message;
UUID msgId = msg.getId();
this.plugin.getLogger().info("[Messaging] Received update ping with id: " + msg.getId());
if (this.plugin.getEventDispatcher().dispatchNetworkPreSync(false, msg.getId())) {
if (this.plugin.getEventDispatcher().dispatchNetworkPreSync(false, msgId, SyncType.FULL, null)) {
return;
}
this.plugin.getSyncTaskBuffer().request();
this.plugin.getLogger().info("[Messaging] Received update ping with id: " + msgId);
this.plugin.getSyncTaskBuffer().request()
.thenRunAsync(() -> this.plugin.getEventDispatcher().dispatchNetworkPostSync(msgId, SyncType.FULL, true, null));
} else if (message instanceof UserUpdateMessage) {
UserUpdateMessage msg = (UserUpdateMessage) message;
UUID msgId = msg.getId();
UUID userUniqueId = msg.getUserUniqueId();
User user = this.plugin.getUserManager().getIfLoaded(msg.getUserUniqueId());
if (this.plugin.getEventDispatcher().dispatchNetworkPreSync(false, msgId, SyncType.SPECIFIC_USER, userUniqueId)) {
return;
}
User user = this.plugin.getUserManager().getIfLoaded(userUniqueId);
if (user == null) {
this.plugin.getEventDispatcher().dispatchNetworkPostSync(msgId, SyncType.SPECIFIC_USER, false, userUniqueId);
return;
}
this.plugin.getLogger().info("[Messaging] Received user update ping for '" + user.getPlainDisplayName() + "' with id: " + msg.getId());
this.plugin.getLogger().info("[Messaging] Received user update ping for '" + user.getPlainDisplayName() + "' with id: " + msgId);
this.plugin.getStorage().loadUser(user.getUniqueId(), null)
.thenRunAsync(() -> this.plugin.getEventDispatcher().dispatchNetworkPostSync(msgId, SyncType.SPECIFIC_USER, true, userUniqueId));
if (this.plugin.getEventDispatcher().dispatchNetworkPreSync(false, msg.getId())) {
return;
}
this.plugin.getStorage().loadUser(user.getUniqueId(), null);
} else if (message instanceof ActionLogMessage) {
ActionLogMessage msg = (ActionLogMessage) message;

View File

@ -26,13 +26,17 @@
package me.lucko.luckperms.standalone;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import me.lucko.luckperms.common.actionlog.LoggedAction;
import me.lucko.luckperms.common.messaging.InternalMessagingService;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.standalone.utils.TestPluginProvider;
import net.luckperms.api.actionlog.Action;
import net.luckperms.api.event.EventBus;
import net.luckperms.api.event.log.LogReceiveEvent;
import net.luckperms.api.event.sync.PreNetworkSyncEvent;
import net.luckperms.api.event.sync.SyncType;
import net.luckperms.api.model.PlayerSaveResult;
import net.luckperms.api.platform.Health;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Tag;
@ -49,7 +53,9 @@ import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@Testcontainers
@ -83,25 +89,44 @@ public class MessagingIntegrationTest {
.description("hello 123 hello 123")
.build();
// register 2 listeners on plugin B
CountDownLatch latch = new CountDownLatch(2);
UUID exampleUniqueId = UUID.fromString("c1d60c50-70b5-4722-8057-87767557e50d");
String exampleUsername = "Luck";
User user = pluginA.plugin().getStorage().loadUser(exampleUniqueId, exampleUsername).join();
EventBus eventBus = pluginB.app().getApi().getEventBus();
CountDownLatch latch1 = new CountDownLatch(1);
eventBus.subscribe(PreNetworkSyncEvent.class, e -> {
latch.countDown();
if (e.getType() == SyncType.FULL) {
latch1.countDown();
e.setCancelled(true);
}
});
CountDownLatch latch2 = new CountDownLatch(1);
eventBus.subscribe(PreNetworkSyncEvent.class, e -> {
if (e.getType() == SyncType.SPECIFIC_USER && exampleUniqueId.equals(e.getSpecificUserUniqueId())) {
latch2.countDown();
e.setCancelled(true);
}
});
CountDownLatch latch3 = new CountDownLatch(1);
eventBus.subscribe(LogReceiveEvent.class, e -> {
if (e.getEntry().equals(exampleLogEntry)) {
latch.countDown();
latch3.countDown();
}
});
// send some messages from plugin A to plugin B
messagingServiceA.pushUpdate();
messagingServiceA.pushUserUpdate(user);
messagingServiceA.pushLog(exampleLogEntry);
// wait for the messages to be sent/received
assertTrue(latch.await(30, TimeUnit.SECONDS));
assertTrue(latch1.await(10, TimeUnit.SECONDS));
assertTrue(latch2.await(10, TimeUnit.SECONDS));
assertTrue(latch3.await(10, TimeUnit.SECONDS));
}
}