Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ repositories {
name = "viaversion"
url = uri("https://repo.viaversion.com")
}

maven {
name = "discordsrvc"
url = uri("https://nexus.scarsz.me/content/groups/public/")
}
}

configure(apiDependencies) {
Expand Down Expand Up @@ -48,11 +53,10 @@ dependencies {
// Caching
shadowed("com.github.ben-manes.caffeine:caffeine:3.2.2")

// PlaceholderAPI
// Plugin Hooks
externalPlugin 'me.clip:placeholderapi:2.11.6'

// Luckperms for group context
compileOnly 'net.luckperms:api:5.4'
compileOnly 'com.discordsrv:discordsrv:1.28.0'

// hk2 for annotation processing only
compileOnly('org.glassfish.hk2:hk2-api:3.1.1') {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.mvplugins.multiverse.inventories.handleshare;

import org.mvplugins.multiverse.inventories.profile.key.ProfileKey;
import org.mvplugins.multiverse.inventories.share.Sharable;
import org.mvplugins.multiverse.inventories.share.Sharables;
import org.mvplugins.multiverse.inventories.share.Shares;

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

private final List<PersistingProfile> writeProfiles = new LinkedList<>();
private final List<PersistingProfile> readProfiles = new LinkedList<>();
private final Shares sharesToRead = Sharables.noneOf();

AffectedProfiles() {
}
Expand All @@ -28,6 +31,7 @@ void addWriteProfile(ProfileKey profileKey, Shares shares) {
*/
void addReadProfile(ProfileKey profileKey, Shares shares) {
readProfiles.add(new PersistingProfile(shares, profileKey));
sharesToRead.addAll(shares);
}

public List<PersistingProfile> getWriteProfiles() {
Expand All @@ -37,4 +41,8 @@ public List<PersistingProfile> getWriteProfiles() {
public List<PersistingProfile> getReadProfiles() {
return readProfiles;
}

boolean isShareToRead(Sharable<?> sharable) {
return sharesToRead.contains(sharable);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.mvplugins.multiverse.inventories.handleshare;

import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.external.jakarta.inject.Inject;
import org.mvplugins.multiverse.inventories.share.Sharable;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
* Keeps track of players who are currently having their sharable handling processed.
* <br />
* This is used to prevent infinite loops when updating sharables that may trigger events themselves or
* when suppressing notifications during the handling process.
*
* @since 5.3
*/
@ApiStatus.AvailableSince("5.3")
@Service
public final class PlayerShareHandlingState {

private final Map<UUID, AffectedProfiles> playerAffectedProfiles;

@Inject
PlayerShareHandlingState() {
this.playerAffectedProfiles = new HashMap<>();
}

void setPlayerAffectedProfiles(Player player, AffectedProfiles status) {
this.playerAffectedProfiles.put(player.getUniqueId(), status);
}

void removePlayerAffectedProfiles(Player player) {
this.playerAffectedProfiles.remove(player.getUniqueId());
}

/**
* Checks if the given player is currently having the given sharable handled.
*
* @param player The player to check.
* @param sharable The sharable to check.
* @return True if the player is having the sharable handled, false otherwise.
*
* @since 5.3
*/
@ApiStatus.AvailableSince("5.3")
public boolean isHandlingSharable(Player player, Sharable<?> sharable) {
AffectedProfiles status = this.playerAffectedProfiles.get(player.getUniqueId());
return status != null && status.isShareToRead(sharable);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ sealed abstract class ShareHandler permits GameModeShareHandler, ReadOnlyShareHa
protected final InventoriesConfig inventoriesConfig;
protected final WorldGroupManager worldGroupManager;
protected final ProfileContainerStore worldProfileContainerStore;
private final PlayerShareHandlingState playerShareHandlingState;

ShareHandler(MultiverseInventories inventories, Player player) {
this.player = player;
Expand All @@ -41,6 +42,7 @@ sealed abstract class ShareHandler permits GameModeShareHandler, ReadOnlyShareHa
this.worldProfileContainerStore = inventories.getServiceLocator()
.getService(ProfileContainerStoreProvider.class)
.getStore(ContainerType.WORLD);
this.playerShareHandlingState = inventories.getServiceLocator().getService(PlayerShareHandlingState.class);
}

/**
Expand Down Expand Up @@ -87,9 +89,11 @@ private ProfileDataSnapshot getSnapshot() {
}

private void updatePlayer() {
playerShareHandlingState.setPlayerAffectedProfiles(player, affectedProfiles);
for (PersistingProfile readProfile : affectedProfiles.getReadProfiles()) {
ShareHandlingUpdater.updatePlayer(inventories, player, readProfile);
}
playerShareHandlingState.removePlayerAffectedProfiles(player);
}

private CompletableFuture<Void> updateProfiles(ProfileDataSnapshot snapshot) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

import org.jvnet.hk2.annotations.Contract;
import org.mvplugins.multiverse.core.dynamiclistener.DynamicListener;
import org.mvplugins.multiverse.inventories.view.ReadOnlyInventoryHolder;

@Contract
public sealed interface MVInvListener extends DynamicListener permits InventoryViewListener, MVEventsListener, RespawnListener, ShareHandleListener, SpawnChangeListener {
public sealed interface MVInvListener extends DynamicListener permits InventoryViewListener, MVEventsListener, RespawnListener, ShareHandleListener, SilentGrantsListener, SpawnChangeListener {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package org.mvplugins.multiverse.inventories.listeners;

import com.dumptruckman.minecraft.util.Logging;
import github.scarsz.discordsrv.DiscordSRV;
import github.scarsz.discordsrv.api.Subscribe;
import github.scarsz.discordsrv.api.events.AchievementMessagePreProcessEvent;
import org.bukkit.Bukkit;
import org.bukkit.event.player.PlayerAdvancementDoneEvent;
import org.bukkit.event.player.PlayerRecipeDiscoverEvent;
import org.bukkit.event.server.PluginEnableEvent;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.dynamiclistener.annotations.EventMethod;
import org.mvplugins.multiverse.core.utils.ReflectHelper;
import org.mvplugins.multiverse.external.jakarta.inject.Inject;
import org.mvplugins.multiverse.inventories.handleshare.PlayerShareHandlingState;
import org.mvplugins.multiverse.inventories.share.Sharables;

@Service
final class SilentGrantsListener implements MVInvListener {

private final PlayerShareHandlingState playerShareHandlingState;

private final boolean hasShouldShowNotificationMethod;
private final boolean hasPlayerAdvancementDoneMessageMethod;

@Inject
SilentGrantsListener(PlayerShareHandlingState playerShareHandlingState) {
this.playerShareHandlingState = playerShareHandlingState;

this.hasShouldShowNotificationMethod = ReflectHelper.getMethod(
PlayerRecipeDiscoverEvent.class,"shouldShowNotification") != null;
this.hasPlayerAdvancementDoneMessageMethod = ReflectHelper.getMethod(
PlayerAdvancementDoneEvent.class,"message") != null;

if (Bukkit.getPluginManager().isPluginEnabled("DiscordSRV")) {
Logging.fine("Registering DiscordSRV advancement grant hook.");
DiscordSRV.api.subscribe(new DiscordSrvHook());
}
}

@EventMethod
void onPlayerRecipeDiscover(PlayerRecipeDiscoverEvent event) {
if (!this.hasShouldShowNotificationMethod) {
// spigot does not have the method to suppress notifications
return;
}
if (playerShareHandlingState.isHandlingSharable(event.getPlayer(), Sharables.RECIPES)) {
Logging.finest("Suppressing recipe discover notification for player %s due to share handling.",
event.getPlayer().getName());
event.shouldShowNotification(false);
}
}

@EventMethod
void onPlayerAdvancementDone(PlayerAdvancementDoneEvent event) {
if (!this.hasPlayerAdvancementDoneMessageMethod) {
// paper does not have the method to suppress notifications
return;
}
if (playerShareHandlingState.isHandlingSharable(event.getPlayer(), Sharables.ADVANCEMENTS)) {
Logging.finest("Suppressing advancement done message for player %s due to share handling.",
event.getPlayer().getName());
event.message(null);
}
}

@EventMethod
void onPluginEnable(PluginEnableEvent event) {
if (event.getPlugin().getName().equals("DiscordSRV")) {
Logging.fine("Registering DiscordSRV advancement grant hook.");
DiscordSRV.api.subscribe(new DiscordSrvHook());
}
}

private class DiscordSrvHook {
@Subscribe
public void onAchievementMessage(AchievementMessagePreProcessEvent event) {
if (playerShareHandlingState.isHandlingSharable(event.getPlayer(), Sharables.ADVANCEMENTS)) {
Logging.finest("Suppressing DiscordSRV advancement grant message for player %s due to share handling.",
event.getPlayer().getName());
event.setCancelled(true);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.mvplugins.multiverse.inventories.listeners;

import com.destroystokyo.paper.event.player.PlayerSetSpawnEvent;
import com.dumptruckman.minecraft.util.Logging;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventPriority;
Expand All @@ -12,6 +13,7 @@
import org.mvplugins.multiverse.core.dynamiclistener.annotations.SkipIfEventExist;
import org.mvplugins.multiverse.external.jakarta.inject.Inject;
import org.mvplugins.multiverse.inventories.MultiverseInventories;
import org.mvplugins.multiverse.inventories.handleshare.PlayerShareHandlingState;
import org.mvplugins.multiverse.inventories.handleshare.SingleShareWriter;
import org.mvplugins.multiverse.inventories.share.Sharables;
import org.mvplugins.multiverse.inventories.util.RespawnLocation;
Expand All @@ -26,23 +28,27 @@
final class SpawnChangeListener implements MVInvListener {

private final MultiverseInventories inventories;
private final PlayerShareHandlingState playerShareHandlingState;

@Inject
public SpawnChangeListener(MultiverseInventories inventories) {
public SpawnChangeListener(MultiverseInventories inventories, PlayerShareHandlingState playerShareHandlingState) {
this.inventories = inventories;
this.playerShareHandlingState = playerShareHandlingState;
}

@EventClass("com.destroystokyo.paper.event.player.PlayerSetSpawnEvent")
@DefaultEventPriority(EventPriority.MONITOR)
EventRunnable onPlayerSetSpawn() {
EventRunnable<?> onPlayerSetSpawn() {
return new EventRunnable<PlayerSetSpawnEvent>() {
@Override
public void onEvent(PlayerSetSpawnEvent event) {
if (Sharables.isIgnoringSpawnListener(event.getPlayer())) {
Player player = event.getPlayer();
if (playerShareHandlingState.isHandlingSharable(player, Sharables.BED_SPAWN)) {
Logging.finest("Setting new spawn location silently for player %s due to share handling.",
player.getName());
event.setNotifyPlayer(false);
return;
}
Player player = event.getPlayer();
Location newSpawnLoc = event.getLocation();
if (newSpawnLoc == null) {
updatePlayerSpawn(player, null);
Expand Down Expand Up @@ -71,14 +77,14 @@ public void onEvent(PlayerSetSpawnEvent event) {
@EventClass("org.bukkit.event.player.PlayerSpawnChangeEvent")
@SkipIfEventExist("com.destroystokyo.paper.event.player.PlayerSetSpawnEvent")
@DefaultEventPriority(EventPriority.MONITOR)
EventRunnable onPlayerSpawnChange() {
EventRunnable<?> onPlayerSpawnChange() {
return new EventRunnable<PlayerSpawnChangeEvent>() {
@Override
public void onEvent(PlayerSpawnChangeEvent event) {
if (Sharables.isIgnoringSpawnListener(event.getPlayer())) {
Player player = event.getPlayer();
if (playerShareHandlingState.isHandlingSharable(player, Sharables.BED_SPAWN)) {
return;
}
Player player = event.getPlayer();
Location newSpawnLoc = event.getNewSpawn();
if (event.getCause() == PlayerSpawnChangeEvent.Cause.BED) {
updatePlayerSpawn(player, new RespawnLocation(
Expand Down
Loading
Loading