Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import net.draycia.carbon.common.listeners.Listener;
import net.draycia.carbon.common.messages.CarbonMessages;
import net.draycia.carbon.common.messaging.MessagingManager;
import net.draycia.carbon.common.messaging.VanishSync;
import net.draycia.carbon.common.messaging.packets.PacketFactory;
import net.draycia.carbon.common.users.PlayerUtils;
import net.draycia.carbon.common.users.ProfileCache;
Expand Down Expand Up @@ -123,6 +124,14 @@ protected void init() {
TimeUnit.SECONDS
);

final VanishSync vanishSync = this.injector.getInstance(VanishSync.class);
this.periodicTasks.scheduleAtFixedRate(
vanishSync::pollAndBroadcast,
VanishSync.POLL_INTERVAL_SECONDS,
VanishSync.POLL_INTERVAL_SECONDS,
TimeUnit.SECONDS
);

this.initIntegrations();

// Load channels
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ public void whisper(
}

final String recipientUsername = recipient.username();
if (!this.network.online(recipient) || !sender.awareOf(recipient) && !sender.hasPermission("carbon.whisper.vanished")) {
if (!this.network.online(recipient) || !this.network.awareOf(sender, recipient) && !sender.hasPermission("carbon.whisper.vanished")) {
final var exception = new CarbonPlayerParser.ParseException(
recipientInputString == null ? recipientUsername : recipientInputString,
this.messages
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import net.draycia.carbon.common.messaging.packets.PacketFactory;
import net.draycia.carbon.common.messaging.packets.PartyChangePacket;
import net.draycia.carbon.common.messaging.packets.PartyInvitePacket;
import net.draycia.carbon.common.messaging.packets.PlayerInfo;
import net.draycia.carbon.common.messaging.packets.SaveCompletedPacket;
import net.draycia.carbon.common.messaging.packets.WhisperPacket;
import net.draycia.carbon.common.users.NetworkUsers;
Expand Down Expand Up @@ -289,9 +290,9 @@ private CarbonServerHandler(
protected void handleInitialization(final @NonNull InitializationPacket packet) {
super.handleInitialization(packet);
final List<? extends CarbonPlayer> players = this.server.players();
final Map<UUID, String> map = new HashMap<>();
final Map<UUID, PlayerInfo> map = new HashMap<>();
for (final CarbonPlayer player : players) {
map.put(player.uuid(), player.username());
map.put(player.uuid(), new PlayerInfo(player.username(), player.vanished()));
}
this.packetService.queuePacket(this.packetFactory.localPlayersPacket(map));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* CarbonChat
*
* Copyright (c) 2024 Josua Parks (Vicarious)
* Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.draycia.carbon.common.messaging;

import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import net.draycia.carbon.api.CarbonServer;
import net.draycia.carbon.api.users.CarbonPlayer;
import net.draycia.carbon.common.messaging.packets.PacketFactory;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.framework.qual.DefaultQualifier;

/**
* Detects local vanish state changes and broadcasts them to the rest of the network.
*
* <p>{@link CarbonPlayer#vanished()} only reflects live state for players on the querying server, so
* remote servers rely on the state synced through the player-presence packets. Join/quit seed the
* initial state; this task keeps it up to date when players are hidden or shown at runtime.</p>
*
* <p>Rather than hooking a specific vanish plugin's events (there is no common Bukkit event for vanish
* changes), this polls the same {@link CarbonPlayer#vanished()} value CarbonChat already reads, so it
* works with any supported vanish plugin (PremiumVanish, SuperVanish, VanishNoPacket, ...) and on any
* platform.</p>
*/
@DefaultQualifier(NonNull.class)
@Singleton
public final class VanishSync {

public static final long POLL_INTERVAL_SECONDS = 3;

private final CarbonServer server;
private final Provider<MessagingManager> messaging;
private final PacketFactory packetFactory;
private final Map<UUID, Boolean> lastKnown = new ConcurrentHashMap<>();

@Inject
private VanishSync(
final CarbonServer server,
final Provider<MessagingManager> messaging,
final PacketFactory packetFactory
) {
this.server = server;
this.messaging = messaging;
this.packetFactory = packetFactory;
}

public void pollAndBroadcast() {
final Set<UUID> online = new HashSet<>();

for (final CarbonPlayer player : this.server.players()) {
final UUID uuid = player.uuid();
online.add(uuid);

final boolean vanished = player.vanished();
final @Nullable Boolean previous = this.lastKnown.put(uuid, vanished);

// First observation: the join packet already seeds the state, so only correct it when the
// player turns out to be vanished (in case vanish was applied after the join packet was sent).
final boolean changed = previous == null ? vanished : previous != vanished;
if (changed) {
this.messaging.get().queuePacket(() -> this.packetFactory.addLocalPlayerPacket(uuid, player.username(), vanished));
}
}

this.lastKnown.keySet().retainAll(online);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@ public final class LocalPlayerChangePacket extends CarbonPacket {
private @MonotonicNonNull UUID playerId;
private @MonotonicNonNull String playerName;
private @MonotonicNonNull ChangeType changeType;
private boolean vanished;

@AssistedInject
public LocalPlayerChangePacket(
final @ServerId UUID serverId,
final @Assisted UUID playerId,
final @Assisted @Nullable String playerName,
final @Assisted ChangeType changeType
final @Assisted ChangeType changeType,
final @Assisted boolean vanished
) {
super(serverId);
if (changeType == ChangeType.ADD && playerName == null) {
Expand All @@ -50,6 +52,7 @@ public LocalPlayerChangePacket(
this.playerId = playerId;
this.playerName = playerName;
this.changeType = changeType;
this.vanished = vanished;
}

@AssistedInject
Expand All @@ -58,6 +61,7 @@ public LocalPlayerChangePacket(final @ServerId UUID serverId, final @Assisted UU
this.playerId = playerId;
this.playerName = null;
this.changeType = ChangeType.REMOVE;
this.vanished = false;
}

public LocalPlayerChangePacket(final UUID sender, final ByteBuf data) {
Expand All @@ -77,13 +81,18 @@ public ChangeType changeType() {
return this.changeType;
}

public boolean vanished() {
return this.vanished;
}

@Override
public void read(final ByteBuf buffer) {
this.playerId = this.readUUID(buffer);
final String type = this.readString(buffer);
this.changeType = ChangeType.valueOf(type);
if (this.changeType == ChangeType.ADD) {
this.playerName = this.readString(buffer);
this.vanished = buffer.readBoolean();
}
}

Expand All @@ -93,6 +102,7 @@ public void write(final ByteBuf buffer) {
this.writeString(this.changeType.name(), buffer);
if (this.changeType == ChangeType.ADD) {
this.writeString(this.playerName, buffer);
buffer.writeBoolean(this.vanished);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@
@DefaultQualifier(NonNull.class)
public final class LocalPlayersPacket extends CarbonPacket {

private @MonotonicNonNull Map<UUID, String> players;
private @MonotonicNonNull Map<UUID, PlayerInfo> players;

@AssistedInject
public LocalPlayersPacket(
final @ServerId UUID serverId,
final @Assisted Map<UUID, String> players
final @Assisted Map<UUID, PlayerInfo> players
) {
super(serverId);
this.players = players;
Expand All @@ -48,18 +48,29 @@ public LocalPlayersPacket(final UUID sender, final ByteBuf data) {
this.read(data);
}

public Map<UUID, String> players() {
public Map<UUID, PlayerInfo> players() {
return this.players;
}

@Override
public void read(final ByteBuf buffer) {
this.players = this.readMap(buffer, this::readUUID, this::readString);
this.players = this.readMap(buffer, this::readUUID, this::readPlayerInfo);
}

@Override
public void write(final ByteBuf buffer) {
this.writeMap(this.players, this::writeUUID, this::writeString, buffer);
this.writeMap(this.players, this::writeUUID, this::writePlayerInfo, buffer);
}

private PlayerInfo readPlayerInfo(final ByteBuf buffer) {
final String name = this.readString(buffer);
final boolean vanished = buffer.readBoolean();
return new PlayerInfo(name, vanished);
}

private void writePlayerInfo(final PlayerInfo info, final ByteBuf buffer) {
this.writeString(info.name(), buffer);
buffer.writeBoolean(info.vanished());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ public interface PacketFactory {

SaveCompletedPacket saveCompletedPacket(UUID playerId);

LocalPlayersPacket localPlayersPacket(Map<UUID, String> players);
LocalPlayersPacket localPlayersPacket(Map<UUID, PlayerInfo> players);

default LocalPlayersPacket clearLocalPlayersPacket() {
return this.localPlayersPacket(Map.of());
}

LocalPlayerChangePacket localPlayerChangePacket(UUID player, @Nullable String name, LocalPlayerChangePacket.ChangeType type);
LocalPlayerChangePacket localPlayerChangePacket(UUID player, @Nullable String name, LocalPlayerChangePacket.ChangeType type, boolean vanished);

default LocalPlayerChangePacket addLocalPlayerPacket(final UUID id, final String name) {
return this.localPlayerChangePacket(id, name, LocalPlayerChangePacket.ChangeType.ADD);
default LocalPlayerChangePacket addLocalPlayerPacket(final UUID id, final String name, final boolean vanished) {
return this.localPlayerChangePacket(id, name, LocalPlayerChangePacket.ChangeType.ADD, vanished);
}

LocalPlayerChangePacket removeLocalPlayerPacket(final UUID id);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* CarbonChat
*
* Copyright (c) 2024 Josua Parks (Vicarious)
* Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.draycia.carbon.common.messaging.packets;

import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;

/**
* A network player's shared, eventually consistent state.
*
* @param name the player's username
* @param vanished whether the player is vanished on the server they're connected to
*/
@DefaultQualifier(NonNull.class)
public record PlayerInfo(String name, boolean vanished) {
}
Loading