diff --git a/common/src/main/java/net/draycia/carbon/common/messages/CarbonMessages.java b/common/src/main/java/net/draycia/carbon/common/messages/CarbonMessages.java index 22ce8d751..25cf94396 100644 --- a/common/src/main/java/net/draycia/carbon/common/messages/CarbonMessages.java +++ b/common/src/main/java/net/draycia/carbon/common/messages/CarbonMessages.java @@ -496,6 +496,12 @@ void errorCommandCommandExecution( @Message("command.party.disband.description") Component partyDisbandDesc(); + @Message("party.player_joined") + void playerJoinedParty(Audience audience, Component partyName, Component displayName); + + @Message("party.player_left") + void playerLeftParty(Audience audience, Component partyName, Component displayName); + @Message("deletemessage.prefix") Component deleteMessagePrefix(); diff --git a/common/src/main/java/net/draycia/carbon/common/users/CachingUserManager.java b/common/src/main/java/net/draycia/carbon/common/users/CachingUserManager.java index 2097534fb..19f685c49 100644 --- a/common/src/main/java/net/draycia/carbon/common/users/CachingUserManager.java +++ b/common/src/main/java/net/draycia/carbon/common/users/CachingUserManager.java @@ -35,6 +35,8 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; import java.util.stream.Collectors; +import net.draycia.carbon.api.CarbonServer; +import net.draycia.carbon.api.users.CarbonPlayer; import net.draycia.carbon.api.users.Party; import net.draycia.carbon.common.messaging.MessagingManager; import net.draycia.carbon.common.messaging.packets.PacketFactory; @@ -58,6 +60,7 @@ public abstract class CachingUserManager implements UserManagerInternal messagingManager; private final PacketFactory packetFactory; + private final CarbonServer server; private final ReentrantLock cacheLock; private final Map> cache; private final AsyncCache partyCache; @@ -67,7 +70,8 @@ protected CachingUserManager( final ProfileResolver profileResolver, final Injector injector, final Provider messagingManager, - final PacketFactory packetFactory + final PacketFactory packetFactory, + final CarbonServer server ) { this.logger = logger; this.executor = Executors.newSingleThreadExecutor(ConcurrentUtil.carbonThreadFactory(logger, this.getClass().getSimpleName())); @@ -78,6 +82,7 @@ protected CachingUserManager( this.injector = injector; this.messagingManager = messagingManager; this.packetFactory = packetFactory; + this.server = server; this.cacheLock = new ReentrantLock(); this.cache = new HashMap<>(); } @@ -253,21 +258,33 @@ public final void disbandParty(final UUID id) { @Override public void partyChangeMessageReceived(final PartyChangePacket pkt) { - final @Nullable CompletableFuture future = this.partyCache.getIfPresent(pkt.partyId()); - if (future != null) { - future.thenAccept(party -> { - final PartyImpl impl = (PartyImpl) party; - pkt.changes().forEach((id, type) -> { - switch (type) { - case ADD -> impl.addMemberRaw(id); - case REMOVE -> impl.removeMemberRaw(id); - } - }); - }).whenComplete(($, thr) -> { - if (thr != null) { - this.logger.warn("Exception handling party change packet {}", pkt, thr); + @Nullable CompletableFuture<@Nullable Party> future = this.partyCache.getIfPresent(pkt.partyId()); + if (future == null) { + // we want to notify any online members even if the party isn't loaded locally yet + for (final CarbonPlayer player : this.server.players()) { + if (pkt.partyId().equals(((WrappedCarbonPlayer) player).partyId())) { + future = this.party(pkt.partyId()); } - }); + } } + if (future == null) { + return; + } + future.thenAccept(party -> { + if (party == null) { + return; + } + final PartyImpl impl = (PartyImpl) party; + pkt.changes().forEach((id, type) -> { + switch (type) { + case ADD -> impl.addMemberRaw(id); + case REMOVE -> impl.removeMemberRaw(id); + } + }); + }).whenComplete(($, thr) -> { + if (thr != null) { + this.logger.warn("Exception handling party change packet {}", pkt, thr); + } + }); } } diff --git a/common/src/main/java/net/draycia/carbon/common/users/PartyImpl.java b/common/src/main/java/net/draycia/carbon/common/users/PartyImpl.java index 39f8ad241..efb23e39a 100644 --- a/common/src/main/java/net/draycia/carbon/common/users/PartyImpl.java +++ b/common/src/main/java/net/draycia/carbon/common/users/PartyImpl.java @@ -19,6 +19,7 @@ */ package net.draycia.carbon.common.users; +import com.google.common.base.Suppliers; import com.google.inject.Inject; import java.util.Map; import java.util.Objects; @@ -27,11 +28,14 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; +import java.util.function.Supplier; import net.draycia.carbon.api.CarbonServer; import net.draycia.carbon.api.event.CarbonEventHandler; import net.draycia.carbon.api.event.events.PartyJoinEvent; import net.draycia.carbon.api.event.events.PartyLeaveEvent; +import net.draycia.carbon.api.users.CarbonPlayer; import net.draycia.carbon.api.users.Party; +import net.draycia.carbon.common.messages.CarbonMessages; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import org.apache.logging.log4j.Logger; @@ -52,6 +56,7 @@ public final class PartyImpl implements Party { private transient @MonotonicNonNull @Inject CarbonServer server; private transient @MonotonicNonNull @Inject Logger logger; private transient @MonotonicNonNull @Inject CarbonEventHandler events; + private transient @MonotonicNonNull @Inject CarbonMessages messages; private transient volatile boolean disbanded = false; private PartyImpl( @@ -173,6 +178,8 @@ public Party party() { return PartyImpl.this; } }); + + this.notifyJoin(id); } public void removeMemberRaw(final UUID id) { @@ -190,6 +197,8 @@ public Party party() { return PartyImpl.this; } }); + + this.notifyLeave(id); } public Map pollChanges() { @@ -212,6 +221,42 @@ public UUID id() { return this.id; } + private void notifyJoin(final UUID joined) { + this.notifyMembersChanged(joined, (p, party, member) -> { + this.messages.playerJoinedParty(member, party.name(), p.displayName()); + }); + } + + private void notifyLeave(final UUID left) { + this.notifyMembersChanged(left, (p, party, member) -> { + this.messages.playerLeftParty(member, party.name(), p.displayName()); + }); + } + + private void notifyMembersChanged(final UUID changed, final ChangeNotifier notify) { + final Supplier> changedPlayer = Suppliers.memoize(() -> this.userManager.user(changed)); + for (final CarbonPlayer player : this.server.players()) { + if (player.uuid().equals(changed)) { + continue; + } + final WrappedCarbonPlayer wrapped = (WrappedCarbonPlayer) player; + if (this.id().equals(wrapped.partyId())) { + changedPlayer.get().thenAccept(p -> { + notify.notify(p, this, wrapped); + }).whenComplete(($, thr) -> { + this.logger.warn("Exception notifying members of party change", thr); + }); + } + } + } + + @FunctionalInterface + private interface ChangeNotifier { + + void notify(CarbonPlayer changed, Party party, CarbonPlayer member); + + } + @Override public String toString() { return "PartyImpl[" + diff --git a/common/src/main/java/net/draycia/carbon/common/users/db/DatabaseUserManager.java b/common/src/main/java/net/draycia/carbon/common/users/db/DatabaseUserManager.java index d636565a1..d3be52f6f 100644 --- a/common/src/main/java/net/draycia/carbon/common/users/db/DatabaseUserManager.java +++ b/common/src/main/java/net/draycia/carbon/common/users/db/DatabaseUserManager.java @@ -31,6 +31,7 @@ import java.util.UUID; import java.util.function.Consumer; import net.draycia.carbon.api.CarbonChat; +import net.draycia.carbon.api.CarbonServer; import net.draycia.carbon.api.channels.ChannelRegistry; import net.draycia.carbon.api.channels.ChatChannel; import net.draycia.carbon.common.config.ConfigManager; @@ -83,14 +84,16 @@ private DatabaseUserManager( final Injector injector, final Provider messagingManager, final PacketFactory packetFactory, - final ChannelRegistry channelRegistry + final ChannelRegistry channelRegistry, + final CarbonServer server ) { super( logger, profileResolver, injector, messagingManager, - packetFactory + packetFactory, + server ); this.jdbi = jdbi; this.dataSource = dataSource; @@ -274,6 +277,7 @@ public static final class Factory { private final Injector injector; private final Provider messagingManager; private final PacketFactory packetFactory; + private final CarbonServer server; @Inject private Factory( @@ -283,7 +287,8 @@ private Factory( final ProfileResolver profileResolver, final Injector injector, final Provider messagingManager, - final PacketFactory packetFactory + final PacketFactory packetFactory, + final CarbonServer server ) { this.channelRegistry = channelRegistry; this.configManager = configManager; @@ -292,6 +297,7 @@ private Factory( this.injector = injector; this.messagingManager = messagingManager; this.packetFactory = packetFactory; + this.server = server; } public DatabaseUserManager create(final String migrationsLocation, final Consumer configureJdbi) { @@ -352,7 +358,8 @@ public DatabaseUserManager create(final String migrationsLocation, final Consume this.injector, this.messagingManager, this.packetFactory, - this.channelRegistry + this.channelRegistry, + this.server ); } diff --git a/common/src/main/java/net/draycia/carbon/common/users/json/JSONUserManager.java b/common/src/main/java/net/draycia/carbon/common/users/json/JSONUserManager.java index 0d912537e..a90c5efbb 100644 --- a/common/src/main/java/net/draycia/carbon/common/users/json/JSONUserManager.java +++ b/common/src/main/java/net/draycia/carbon/common/users/json/JSONUserManager.java @@ -30,6 +30,7 @@ import java.nio.file.Path; import java.util.Map; import java.util.UUID; +import net.draycia.carbon.api.CarbonServer; import net.draycia.carbon.api.channels.ChannelRegistry; import net.draycia.carbon.api.channels.ChatChannel; import net.draycia.carbon.common.DataDirectory; @@ -69,14 +70,16 @@ public JSONUserManager( final UUIDSerializerGson uuidSerializer, final Provider messagingManager, final PacketFactory packetFactory, - final CarbonChannelRegistry channelRegistry + final CarbonChannelRegistry channelRegistry, + final CarbonServer server ) throws IOException { super( logger, profileResolver, injector, messagingManager, - packetFactory + packetFactory, + server ); this.userDirectory = dataDirectory.resolve("users"); this.partyDirectory = dataDirectory.resolve("party"); diff --git a/common/src/main/resources/locale/messages-en_US.properties b/common/src/main/resources/locale/messages-en_US.properties index 9d746f2ce..83bc8e315 100644 --- a/common/src/main/resources/locale/messages-en_US.properties +++ b/common/src/main/resources/locale/messages-en_US.properties @@ -84,6 +84,8 @@ command.party.accept.description=Accept party invites. command.party.leave.description=Leave your current party. command.party.already_in_party= is already in your party. command.party.disband.description=Disband your current party. +party.player_joined= joined your party. +party.player_left= left your party. config.reload.failed=Config failed to reload config.reload.success=Config reloaded successfully error.command.argument_parsing=Invalid command argument: