Skip to content

Commit 30fb71e

Browse files
committed
Implement player share handling state to support silencing notifications
1 parent 4f6cc82 commit 30fb71e

File tree

8 files changed

+173
-13
lines changed

8 files changed

+173
-13
lines changed

build.gradle

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ repositories {
2121
name = "viaversion"
2222
url = uri("https://repo.viaversion.com")
2323
}
24+
25+
maven {
26+
name = "discordsrvc"
27+
url = uri("https://nexus.scarsz.me/content/groups/public/")
28+
}
2429
}
2530

2631
configure(apiDependencies) {
@@ -48,11 +53,10 @@ dependencies {
4853
// Caching
4954
shadowed("com.github.ben-manes.caffeine:caffeine:3.2.2")
5055

51-
// PlaceholderAPI
56+
// Plugin Hooks
5257
externalPlugin 'me.clip:placeholderapi:2.11.6'
53-
54-
// Luckperms for group context
5558
compileOnly 'net.luckperms:api:5.4'
59+
compileOnly 'com.discordsrv:discordsrv:1.28.0'
5660

5761
// hk2 for annotation processing only
5862
compileOnly('org.glassfish.hk2:hk2-api:3.1.1') {

src/main/java/org/mvplugins/multiverse/inventories/handleshare/AffectedProfiles.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package org.mvplugins.multiverse.inventories.handleshare;
22

33
import org.mvplugins.multiverse.inventories.profile.key.ProfileKey;
4+
import org.mvplugins.multiverse.inventories.share.Sharable;
5+
import org.mvplugins.multiverse.inventories.share.Sharables;
46
import org.mvplugins.multiverse.inventories.share.Shares;
57

68
import java.util.LinkedList;
@@ -10,6 +12,7 @@ public final class AffectedProfiles {
1012

1113
private final List<PersistingProfile> writeProfiles = new LinkedList<>();
1214
private final List<PersistingProfile> readProfiles = new LinkedList<>();
15+
private final Shares sharesToRead = Sharables.noneOf();
1316

1417
AffectedProfiles() {
1518
}
@@ -28,6 +31,7 @@ void addWriteProfile(ProfileKey profileKey, Shares shares) {
2831
*/
2932
void addReadProfile(ProfileKey profileKey, Shares shares) {
3033
readProfiles.add(new PersistingProfile(shares, profileKey));
34+
sharesToRead.addAll(shares);
3135
}
3236

3337
public List<PersistingProfile> getWriteProfiles() {
@@ -37,4 +41,8 @@ public List<PersistingProfile> getWriteProfiles() {
3741
public List<PersistingProfile> getReadProfiles() {
3842
return readProfiles;
3943
}
44+
45+
boolean isShareToRead(Sharable<?> sharable) {
46+
return sharesToRead.contains(sharable);
47+
}
4048
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package org.mvplugins.multiverse.inventories.handleshare;
2+
3+
import org.bukkit.entity.Player;
4+
import org.jetbrains.annotations.ApiStatus;
5+
import org.jvnet.hk2.annotations.Service;
6+
import org.mvplugins.multiverse.external.jakarta.inject.Inject;
7+
import org.mvplugins.multiverse.inventories.share.Sharable;
8+
9+
import java.util.HashMap;
10+
import java.util.Map;
11+
import java.util.UUID;
12+
13+
/**
14+
* Keeps track of players who are currently having their sharable handling processed.
15+
* <br />
16+
* This is used to prevent infinite loops when updating sharables that may trigger events themselves or
17+
* when suppressing notifications during the handling process.
18+
*
19+
* @since 5.3
20+
*/
21+
@ApiStatus.AvailableSince("5.3")
22+
@Service
23+
public final class PlayerShareHandlingState {
24+
25+
private final Map<UUID, AffectedProfiles> playerAffectedProfiles;
26+
27+
@Inject
28+
PlayerShareHandlingState() {
29+
this.playerAffectedProfiles = new HashMap<>();
30+
}
31+
32+
void setPlayerAffectedProfiles(Player player, AffectedProfiles status) {
33+
this.playerAffectedProfiles.put(player.getUniqueId(), status);
34+
}
35+
36+
void removePlayerAffectedProfiles(Player player) {
37+
this.playerAffectedProfiles.remove(player.getUniqueId());
38+
}
39+
40+
/**
41+
* Checks if the given player is currently having the given sharable handled.
42+
*
43+
* @param player The player to check.
44+
* @param sharable The sharable to check.
45+
* @return True if the player is having the sharable handled, false otherwise.
46+
*
47+
* @since 5.3
48+
*/
49+
@ApiStatus.AvailableSince("5.3")
50+
public boolean isHandlingSharable(Player player, Sharable<?> sharable) {
51+
AffectedProfiles status = this.playerAffectedProfiles.get(player.getUniqueId());
52+
return status != null && status.isShareToRead(sharable);
53+
}
54+
}

src/main/java/org/mvplugins/multiverse/inventories/handleshare/ShareHandler.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ sealed abstract class ShareHandler permits GameModeShareHandler, ReadOnlyShareHa
2929
protected final InventoriesConfig inventoriesConfig;
3030
protected final WorldGroupManager worldGroupManager;
3131
protected final ProfileContainerStore worldProfileContainerStore;
32+
private final PlayerShareHandlingState playerShareHandlingState;
3233

3334
ShareHandler(MultiverseInventories inventories, Player player) {
3435
this.player = player;
@@ -41,6 +42,7 @@ sealed abstract class ShareHandler permits GameModeShareHandler, ReadOnlyShareHa
4142
this.worldProfileContainerStore = inventories.getServiceLocator()
4243
.getService(ProfileContainerStoreProvider.class)
4344
.getStore(ContainerType.WORLD);
45+
this.playerShareHandlingState = inventories.getServiceLocator().getService(PlayerShareHandlingState.class);
4446
}
4547

4648
/**
@@ -87,9 +89,11 @@ private ProfileDataSnapshot getSnapshot() {
8789
}
8890

8991
private void updatePlayer() {
92+
playerShareHandlingState.setPlayerAffectedProfiles(player, affectedProfiles);
9093
for (PersistingProfile readProfile : affectedProfiles.getReadProfiles()) {
9194
ShareHandlingUpdater.updatePlayer(inventories, player, readProfile);
9295
}
96+
playerShareHandlingState.removePlayerAffectedProfiles(player);
9397
}
9498

9599
private CompletableFuture<Void> updateProfiles(ProfileDataSnapshot snapshot) {

src/main/java/org/mvplugins/multiverse/inventories/listeners/MVInvListener.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22

33
import org.jvnet.hk2.annotations.Contract;
44
import org.mvplugins.multiverse.core.dynamiclistener.DynamicListener;
5-
import org.mvplugins.multiverse.inventories.view.ReadOnlyInventoryHolder;
65

76
@Contract
8-
public sealed interface MVInvListener extends DynamicListener permits InventoryViewListener, MVEventsListener, RespawnListener, ShareHandleListener, SpawnChangeListener {
7+
public sealed interface MVInvListener extends DynamicListener permits InventoryViewListener, MVEventsListener, RespawnListener, ShareHandleListener, SilentGrantsListener, SpawnChangeListener {
98
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package org.mvplugins.multiverse.inventories.listeners;
2+
3+
import com.dumptruckman.minecraft.util.Logging;
4+
import github.scarsz.discordsrv.DiscordSRV;
5+
import github.scarsz.discordsrv.api.Subscribe;
6+
import github.scarsz.discordsrv.api.events.AchievementMessagePreProcessEvent;
7+
import org.bukkit.Bukkit;
8+
import org.bukkit.event.player.PlayerAdvancementDoneEvent;
9+
import org.bukkit.event.player.PlayerRecipeDiscoverEvent;
10+
import org.bukkit.event.server.PluginEnableEvent;
11+
import org.jvnet.hk2.annotations.Service;
12+
import org.mvplugins.multiverse.core.dynamiclistener.annotations.EventMethod;
13+
import org.mvplugins.multiverse.core.utils.ReflectHelper;
14+
import org.mvplugins.multiverse.external.jakarta.inject.Inject;
15+
import org.mvplugins.multiverse.inventories.handleshare.PlayerShareHandlingState;
16+
import org.mvplugins.multiverse.inventories.share.Sharables;
17+
18+
@Service
19+
final class SilentGrantsListener implements MVInvListener {
20+
21+
private final PlayerShareHandlingState playerShareHandlingState;
22+
23+
private final boolean hasShouldShowNotificationMethod;
24+
private final boolean hasPlayerAdvancementDoneMessageMethod;
25+
26+
@Inject
27+
SilentGrantsListener(PlayerShareHandlingState playerShareHandlingState) {
28+
this.playerShareHandlingState = playerShareHandlingState;
29+
30+
this.hasShouldShowNotificationMethod = ReflectHelper.getMethod(
31+
PlayerRecipeDiscoverEvent.class,"shouldShowNotification") != null;
32+
this.hasPlayerAdvancementDoneMessageMethod = ReflectHelper.getMethod(
33+
PlayerAdvancementDoneEvent.class,"message") != null;
34+
35+
if (Bukkit.getPluginManager().isPluginEnabled("DiscordSRV")) {
36+
Logging.fine("Registering DiscordSRV advancement grant hook.");
37+
DiscordSRV.api.subscribe(new DiscordSrvHook());
38+
}
39+
}
40+
41+
@EventMethod
42+
void onPlayerRecipeDiscover(PlayerRecipeDiscoverEvent event) {
43+
if (!this.hasShouldShowNotificationMethod) {
44+
// spigot does not have the method to suppress notifications
45+
return;
46+
}
47+
if (playerShareHandlingState.isHandlingSharable(event.getPlayer(), Sharables.RECIPES)) {
48+
Logging.finest("Suppressing recipe discover notification for player %s due to share handling.",
49+
event.getPlayer().getName());
50+
event.shouldShowNotification(false);
51+
}
52+
}
53+
54+
@EventMethod
55+
void onPlayerAdvancementDone(PlayerAdvancementDoneEvent event) {
56+
if (!this.hasPlayerAdvancementDoneMessageMethod) {
57+
// paper does not have the method to suppress notifications
58+
return;
59+
}
60+
if (playerShareHandlingState.isHandlingSharable(event.getPlayer(), Sharables.ADVANCEMENTS)) {
61+
Logging.finest("Suppressing advancement done message for player %s due to share handling.",
62+
event.getPlayer().getName());
63+
event.message(null);
64+
}
65+
}
66+
67+
@EventMethod
68+
void onPluginEnable(PluginEnableEvent event) {
69+
if (event.getPlugin().getName().equals("DiscordSRV")) {
70+
Logging.fine("Registering DiscordSRV advancement grant hook.");
71+
DiscordSRV.api.subscribe(new DiscordSrvHook());
72+
}
73+
}
74+
75+
private class DiscordSrvHook {
76+
@Subscribe
77+
public void onAchievementMessage(AchievementMessagePreProcessEvent event) {
78+
if (playerShareHandlingState.isHandlingSharable(event.getPlayer(), Sharables.ADVANCEMENTS)) {
79+
Logging.finest("Suppressing DiscordSRV advancement grant message for player %s due to share handling.",
80+
event.getPlayer().getName());
81+
event.setCancelled(true);
82+
}
83+
}
84+
}
85+
}

src/main/java/org/mvplugins/multiverse/inventories/listeners/SpawnChangeListener.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.mvplugins.multiverse.inventories.listeners;
22

33
import com.destroystokyo.paper.event.player.PlayerSetSpawnEvent;
4+
import com.dumptruckman.minecraft.util.Logging;
45
import org.bukkit.Location;
56
import org.bukkit.entity.Player;
67
import org.bukkit.event.EventPriority;
@@ -12,6 +13,7 @@
1213
import org.mvplugins.multiverse.core.dynamiclistener.annotations.SkipIfEventExist;
1314
import org.mvplugins.multiverse.external.jakarta.inject.Inject;
1415
import org.mvplugins.multiverse.inventories.MultiverseInventories;
16+
import org.mvplugins.multiverse.inventories.handleshare.PlayerShareHandlingState;
1517
import org.mvplugins.multiverse.inventories.handleshare.SingleShareWriter;
1618
import org.mvplugins.multiverse.inventories.share.Sharables;
1719
import org.mvplugins.multiverse.inventories.util.RespawnLocation;
@@ -26,23 +28,27 @@
2628
final class SpawnChangeListener implements MVInvListener {
2729

2830
private final MultiverseInventories inventories;
31+
private final PlayerShareHandlingState playerShareHandlingState;
2932

3033
@Inject
31-
public SpawnChangeListener(MultiverseInventories inventories) {
34+
public SpawnChangeListener(MultiverseInventories inventories, PlayerShareHandlingState playerShareHandlingState) {
3235
this.inventories = inventories;
36+
this.playerShareHandlingState = playerShareHandlingState;
3337
}
3438

3539
@EventClass("com.destroystokyo.paper.event.player.PlayerSetSpawnEvent")
3640
@DefaultEventPriority(EventPriority.MONITOR)
37-
EventRunnable onPlayerSetSpawn() {
41+
EventRunnable<?> onPlayerSetSpawn() {
3842
return new EventRunnable<PlayerSetSpawnEvent>() {
3943
@Override
4044
public void onEvent(PlayerSetSpawnEvent event) {
41-
if (Sharables.isIgnoringSpawnListener(event.getPlayer())) {
45+
Player player = event.getPlayer();
46+
if (playerShareHandlingState.isHandlingSharable(player, Sharables.BED_SPAWN)) {
47+
Logging.finest("Setting new spawn location silently for player %s due to share handling.",
48+
player.getName());
4249
event.setNotifyPlayer(false);
4350
return;
4451
}
45-
Player player = event.getPlayer();
4652
Location newSpawnLoc = event.getLocation();
4753
if (newSpawnLoc == null) {
4854
updatePlayerSpawn(player, null);
@@ -71,14 +77,14 @@ public void onEvent(PlayerSetSpawnEvent event) {
7177
@EventClass("org.bukkit.event.player.PlayerSpawnChangeEvent")
7278
@SkipIfEventExist("com.destroystokyo.paper.event.player.PlayerSetSpawnEvent")
7379
@DefaultEventPriority(EventPriority.MONITOR)
74-
EventRunnable onPlayerSpawnChange() {
80+
EventRunnable<?> onPlayerSpawnChange() {
7581
return new EventRunnable<PlayerSpawnChangeEvent>() {
7682
@Override
7783
public void onEvent(PlayerSpawnChangeEvent event) {
78-
if (Sharables.isIgnoringSpawnListener(event.getPlayer())) {
84+
Player player = event.getPlayer();
85+
if (playerShareHandlingState.isHandlingSharable(player, Sharables.BED_SPAWN)) {
7986
return;
8087
}
81-
Player player = event.getPlayer();
8288
Location newSpawnLoc = event.getNewSpawn();
8389
if (event.getCause() == PlayerSpawnChangeEvent.Cause.BED) {
8490
updatePlayerSpawn(player, new RespawnLocation(

src/test/java/org/mvplugins/multiverse/inventories/InjectionTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class InjectionTest : TestWithMockBukkit() {
2525

2626
@Test
2727
fun `InventoriesListener is available as a service`() {
28-
assertEquals(5, serviceLocator.getAllActiveServices(MVInvListener::class.java).size)
28+
assertEquals(6, serviceLocator.getAllActiveServices(MVInvListener::class.java).size)
2929
}
3030

3131
@Test

0 commit comments

Comments
 (0)