Skip to content

Commit

Permalink
P
Browse files Browse the repository at this point in the history
  • Loading branch information
Mindgamesnl committed Jan 27, 2024
1 parent 219e4da commit adf4055
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,17 @@ public class RtcSessionManager implements Serializable {

@Getter private boolean isMicrophoneEnabled = false;
@Getter private boolean isVoicechatDeafened = false;
@Getter private final transient Set<UUID> listeningTo = ConcurrentHashMap.newKeySet();

@Getter private final transient Set<UUID> currentGlobalPeers = ConcurrentHashMap.newKeySet();
@Getter private final transient Set<UUID> currentProximityPeers = ConcurrentHashMap.newKeySet();
@Getter private final transient Set<ClientRtcLocationUpdate> locationUpdateQueue = ConcurrentHashMap.newKeySet();
@Getter private final transient Set<RtcBlockReason> blockReasons = new HashSet<>();
@Getter private final transient Set<RtcStateFlag> stateFlags = new HashSet<>();
@Getter private final transient Set<UUID> recentPeerAdditions = new HashSet<>();
@Getter private final transient Set<UUID> recentPeerRemovals = new HashSet<>();

// these are used for messaging, not tracking. N amount players joined, etc
@Getter private final transient Set<UUID> currentProximityAdditions = new HashSet<>();
@Getter private final transient Set<UUID> currentProximityDrops = new HashSet<>();

@Setter @Getter private String streamKey;
private transient Location lastPassedLocation = null;
private final transient ClientConnection clientConnection;
Expand All @@ -51,7 +56,7 @@ public RtcSessionManager(ClientConnection clientConnection) {

this.clientConnection.onDisconnect(() -> {
// go over all other clients, check if we might have a relations ship and break up if thats the case
listeningTo.clear();
currentProximityPeers.clear();
this.isMicrophoneEnabled = false;
makePeersDrop();
locationUpdateQueue.clear();
Expand All @@ -72,19 +77,19 @@ public boolean requestLinkage(ClientConnection peer, boolean mutual, VoicePeerOp
return false;

// only force the other user to subscribe if they are not already listening to me and mutual is true
if (mutual && !peer.getRtcSessionManager().listeningTo.contains(clientConnection.getOwner().getUniqueId())) {
peer.getRtcSessionManager().getListeningTo().add(clientConnection.getOwner().getUniqueId());
if (mutual && !peer.getRtcSessionManager().currentProximityPeers.contains(clientConnection.getOwner().getUniqueId())) {
peer.getRtcSessionManager().getCurrentProximityPeers().add(clientConnection.getOwner().getUniqueId());
peer.getPeerQueue().addSubscribe(clientConnection, peer, options);
AudioApi.getInstance().getEventDriver().fire(new PlayerEnterVoiceProximityEvent(clientConnection, peer, VoiceEventCause.NORMAL));
peer.getRtcSessionManager().updateLocationWatcher();
}

// in case that I'm already listening to the other user, don't do anything
// we do this after the mutual handling, so that still continues if I'm already listening to the other user
if (listeningTo.contains(peer.getOwner().getUniqueId()))
if (currentProximityPeers.contains(peer.getOwner().getUniqueId()))
return false;

listeningTo.add(peer.getOwner().getUniqueId());
currentProximityPeers.add(peer.getOwner().getUniqueId());
clientConnection.getPeerQueue().addSubscribe(peer, clientConnection, options);
AudioApi.getInstance().getEventDriver().fire(new PlayerEnterVoiceProximityEvent(peer, clientConnection, VoiceEventCause.NORMAL));

Expand Down Expand Up @@ -122,9 +127,9 @@ public void makePeersDrop() {
if (peer.getOwner().getUniqueId() == clientConnection.getOwner().getUniqueId())
continue;

if (peer.getRtcSessionManager().listeningTo.contains(clientConnection.getOwner().getUniqueId())) {
if (peer.getRtcSessionManager().currentProximityPeers.contains(clientConnection.getOwner().getUniqueId())) {
// send unsub packet
peer.getRtcSessionManager().listeningTo.remove(clientConnection.getOwner().getUniqueId());
peer.getRtcSessionManager().currentProximityPeers.remove(clientConnection.getOwner().getUniqueId());
peer.getRtcSessionManager().updateLocationWatcher();
peer.getPeerQueue().drop(streamKey);

Expand All @@ -146,7 +151,7 @@ public void forceUpdateLocation(Location location) {
if (peer.getOwner().getUniqueId() == clientConnection.getOwner().getUniqueId())
continue;

if (peer.getRtcSessionManager().listeningTo.contains(clientConnection.getOwner().getUniqueId())) {
if (peer.getRtcSessionManager().currentProximityPeers.contains(clientConnection.getOwner().getUniqueId())) {
peer.getRtcSessionManager().locationUpdateQueue.add(
ClientRtcLocationUpdate
.fromClientWithLocation(clientConnection, location, Vector3.from(peer))
Expand All @@ -162,7 +167,7 @@ public void updateLocationWatcher() {
// player logged out, ignoring
return;
}
if (listeningTo.isEmpty()) {
if (currentProximityPeers.isEmpty()) {
spigotConnection.getLocationFollowers().remove(PlayerLocationFollower.PROXIMITY_VOICE_CHAT);
} else {
spigotConnection.getLocationFollowers().add(PlayerLocationFollower.PROXIMITY_VOICE_CHAT);
Expand Down Expand Up @@ -210,6 +215,7 @@ public void setMicrophoneEnabled(boolean state) {
}

public boolean isPeer(UUID uuid) {
return listeningTo.contains(uuid);
return currentProximityPeers.contains(uuid) || currentGlobalPeers.contains(uuid);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public String onRequest(OfflinePlayer player, @NotNull String params) {
return Integer.toString(OpenAudioMc.getService(
SpigotPlayerService.class
).getClient(player.getUniqueId())
.getClientConnection().getRtcSessionManager().getListeningTo().size());
.getClientConnection().getRtcSessionManager().getCurrentProximityPeers().size());
}

if(params.equals("voicechat_count"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import com.craftmend.openaudiomc.generic.service.Service;
import com.craftmend.openaudiomc.generic.storage.enums.StorageKey;
import com.craftmend.openaudiomc.generic.user.User;
import com.craftmend.openaudiomc.spigot.OpenAudioMcSpigot;
import com.craftmend.openaudiomc.spigot.modules.players.SpigotPlayerService;
import com.craftmend.openaudiomc.spigot.modules.voicechat.filters.FilterService;
import com.craftmend.openaudiomc.spigot.modules.voicechat.filters.PeerFilter;
Expand Down Expand Up @@ -83,8 +82,8 @@ public void onEnable() {
return;
}

event.getSpeaker().getRtcSessionManager().getRecentPeerAdditions().add(event.getListener().getOwner().getUniqueId());
event.getSpeaker().getRtcSessionManager().getRecentPeerRemovals().remove(event.getListener().getOwner().getUniqueId());
event.getSpeaker().getRtcSessionManager().getCurrentProximityAdditions().add(event.getListener().getOwner().getUniqueId());
event.getSpeaker().getRtcSessionManager().getCurrentProximityDrops().remove(event.getListener().getOwner().getUniqueId());
});

eventDriver.on(PlayerLeaveVoiceProximityEvent.class).setHandler(event -> {
Expand All @@ -98,8 +97,8 @@ public void onEnable() {
return;
}

event.getSpeaker().getRtcSessionManager().getRecentPeerRemovals().add(event.getListener().getOwner().getUniqueId());
event.getSpeaker().getRtcSessionManager().getRecentPeerAdditions().remove(event.getListener().getOwner().getUniqueId());
event.getSpeaker().getRtcSessionManager().getCurrentProximityDrops().add(event.getListener().getOwner().getUniqueId());
event.getSpeaker().getRtcSessionManager().getCurrentProximityAdditions().remove(event.getListener().getOwner().getUniqueId());
});

// do vc tick loop
Expand All @@ -115,11 +114,11 @@ public void onEnable() {
for (ClientConnection client : networkingService.getClients()) {
RtcSessionManager manager = client.getRtcSessionManager();
// handle their join messages, if any
if (!manager.getRecentPeerAdditions().isEmpty()) {
if (!manager.getCurrentProximityAdditions().isEmpty()) {
// do these
if (manager.getRecentPeerAdditions().size() == 1) {
if (manager.getCurrentProximityAdditions().size() == 1) {
// do single
ClientConnection other = clientFromId(manager.getRecentPeerAdditions().stream().findFirst().get());
ClientConnection other = clientFromId(manager.getCurrentProximityAdditions().stream().findFirst().get());
if (other != null) {
sendMessage(client.getUser(), Platform.translateColors(
StorageKey.MESSAGE_VC_USER_ADDED.getString()
Expand All @@ -128,21 +127,21 @@ public void onEnable() {
}
} else {
// do multiple
MultiNameReference mnr = new MultiNameReference(manager.getRecentPeerAdditions());
MultiNameReference mnr = new MultiNameReference(manager.getCurrentProximityAdditions());
sendMessage(client.getUser(), Platform.translateColors(
StorageKey.MESSAGE_VC_USERS_ADDED.getString()
.replace("%count", mnr.getOtherCount() + "")
.replace("%name", mnr.getFirstName()))
);
}
manager.getRecentPeerAdditions().clear();
manager.getCurrentProximityAdditions().clear();
}

if (!manager.getRecentPeerRemovals().isEmpty()) {
if (!manager.getCurrentProximityDrops().isEmpty()) {
// do these
if (manager.getRecentPeerRemovals().size() == 1) {
if (manager.getCurrentProximityDrops().size() == 1) {
// do single
ClientConnection other = clientFromId(manager.getRecentPeerRemovals().stream().findFirst().get());
ClientConnection other = clientFromId(manager.getCurrentProximityDrops().stream().findFirst().get());
if (other != null) {
sendMessage(client.getUser(), Platform.translateColors(
StorageKey.MESSAGE_VC_USER_LEFT.getString()
Expand All @@ -151,14 +150,14 @@ public void onEnable() {
}
} else {
// do multiple
MultiNameReference mnr = new MultiNameReference(manager.getRecentPeerRemovals());
MultiNameReference mnr = new MultiNameReference(manager.getCurrentProximityDrops());
sendMessage(client.getUser(), Platform.translateColors(
StorageKey.MESSAGE_VC_USERS_LEFT.getString()
.replace("%count", mnr.getOtherCount() + "")
.replace("%name", mnr.getFirstName()))
);
}
manager.getRecentPeerRemovals().clear();
manager.getCurrentProximityDrops().clear();
}
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
import com.craftmend.openaudiomc.generic.client.objects.ClientConnection;
import com.craftmend.openaudiomc.generic.client.objects.VoicePeerOptions;
import com.craftmend.openaudiomc.generic.networking.interfaces.NetworkingService;
import com.craftmend.openaudiomc.generic.networking.packets.client.voice.PacketClientDropVoiceStream;
import com.craftmend.openaudiomc.generic.networking.payloads.client.voice.ClientVoiceDropPayload;
import com.craftmend.openaudiomc.generic.utils.data.Filter;
import com.craftmend.openaudiomc.spigot.modules.voicechat.filters.PeerFilter;
import lombok.Setter;
Expand All @@ -26,6 +24,31 @@ public class PlayerProximityTicker implements Runnable {
@Setter
private Filter<ClientConnection, Player> filter;

/**
* The proximity ticker is what runs most of the business-logic for voice chat. It's responsible for
* linking players together, dropping players that are too far away and handling all the logic for
* the voice chat system.
* <br>
* It's important to keep in mind that efficiency must scale with the amount of players online,
* because it's easy to accidentally write a nested loop going over all players, which would
* be a disaster for performance. (N^2)
* <br>
* We try to calculate the max checks that could be done per player, and store these results in an array.
* This gives us cheap lookups for next iterations, and is reasonably memory effective.
* (arrays in java *do not* allocate memory for the entire footprint of the value, but only reserves a
* 64 bit pointer for the array, referencing client data we already have in memory)
* <br>
* Default behaviour is just normal proximity checks, but with a few edge cases:
* <ul>
* <li>If a player is considered a moderator, then it won't allow mutual connections with normal players</li>
* <li>If a player has N amount of "global" peers, then they should not be considered for proximity checks</li>
* </ul>
* <br>
* If you're reading this and looking to implement your own proximity checks, through the API, then please
* be aware of the design choices made here and consider following a similar pattern.
* Here be dragons.
*/

public PlayerProximityTicker(int maxDistance, PeerFilter peerFilter) {
this.filter = peerFilter;
this.filter.updateProperty("d", maxDistance);
Expand All @@ -41,10 +64,20 @@ public void addFilter(Filter<ClientConnection, Player> extraFilter) {

@Override
public void run() {
// pre tick
// pre-tick event, to take care of any pre-tick logic elsewhere
AudioApi.getInstance().getEventDriver().fire(new VoiceChatPeerTickEvent(TickEventType.BEFORE_TICK));

for (ClientConnection client : OpenAudioMc.getService(NetworkingService.class).getClients()) {
// we'll reference everything during this tick based on this initial time snapshot. This prevents
// concurrency issues later on, and means we can do relatively fast arrayCopy's when needed.
// to save time, we'll pre-filter some results.
ClientConnection[] allClients = OpenAudioMc.getService(NetworkingService.class)
.getClients()
.stream()
.filter((c) -> c.getRtcSessionManager().isReady())
.toArray(ClientConnection[]::new);

for (ClientConnection client : allClients) {

// am I valid? no? do nothing.
if (!client.getRtcSessionManager().isReady()) continue;

Expand All @@ -67,7 +100,7 @@ public void run() {
// find players that we don't have yet
applicableClients
.stream()
.filter(peer -> !client.getRtcSessionManager().getListeningTo().contains(peer.getOwner().getUniqueId()))
.filter(peer -> !client.getRtcSessionManager().getCurrentProximityPeers().contains(peer.getOwner().getUniqueId()))
.filter(peer -> !peer.getSession().isResetVc()) // they are already resetting, give it a sec
.filter(peer -> (client.isModerating() || !peer.isModerating())) // ignore moderators

Expand All @@ -85,7 +118,7 @@ public void run() {
});

// check if we have any peers that are no longer applicable
for (UUID uuid : client.getRtcSessionManager().getListeningTo()
for (UUID uuid : client.getRtcSessionManager().getCurrentProximityPeers()
.stream()
.filter(p -> p != client.getOwner().getUniqueId())
.filter(uuid -> (client.getSession().isResetVc() || applicableClients.stream().noneMatch(apc -> apc.getOwner().getUniqueId() == uuid)))
Expand All @@ -97,14 +130,14 @@ public void run() {
client.getPeerQueue().drop(peer.getRtcSessionManager().getStreamKey());
AudioApi.getInstance().getEventDriver().fire(new PlayerLeaveVoiceProximityEvent(client, peer, VoiceEventCause.NORMAL));
client.getRtcSessionManager().updateLocationWatcher();
client.getRtcSessionManager().getListeningTo().remove(peer.getOwner().getUniqueId());
client.getRtcSessionManager().getCurrentProximityPeers().remove(peer.getOwner().getUniqueId());

if (peer.isModerating()) {
continue;
}

peer.getPeerQueue().drop(client.getRtcSessionManager().getStreamKey());
peer.getRtcSessionManager().getListeningTo().remove(client.getOwner().getUniqueId());
peer.getRtcSessionManager().getCurrentProximityPeers().remove(client.getOwner().getUniqueId());
AudioApi.getInstance().getEventDriver().fire(new PlayerLeaveVoiceProximityEvent(peer, client, VoiceEventCause.NORMAL));
peer.getRtcSessionManager().updateLocationWatcher();
}
Expand Down

0 comments on commit adf4055

Please sign in to comment.