Skip to content

Commit

Permalink
Merge pull request #293 from Mindgamesnl/feature/voice-mod-polish
Browse files Browse the repository at this point in the history
Feature/voice mod polish
  • Loading branch information
Mindgamesnl authored Sep 18, 2022
2 parents 07a44eb + 737ab1c commit 4f7a782
Show file tree
Hide file tree
Showing 22 changed files with 142 additions and 50 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Please visit the [Documentation](https://help.openaudiomc.net/docs) for a full g

## Features
- Proximity Voice Chat (with spatial and normal audio)
- Full built-in moderation support
- Music and sound effects without resource pack
- Native Worldguard integration to asign music to regions and query for key locations
- Speaker blocks you can place throughout your world to add audio in special places
Expand Down
2 changes: 1 addition & 1 deletion module-src/parties-module/dependency-reduced-pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
<dependency>
<groupId>com.craftmend.openaudiomc</groupId>
<artifactId>OpenAudioMc</artifactId>
<version>6.7.4</version>
<version>6.7.5</version>
<scope>provided</scope>
</dependency>
<dependency>
Expand Down
2 changes: 1 addition & 1 deletion module-src/skywars-module/dependency-reduced-pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
<dependency>
<groupId>com.craftmend.openaudiomc</groupId>
<artifactId>OpenAudioMc</artifactId>
<version>6.7.4</version>
<version>6.7.5</version>
<scope>provided</scope>
</dependency>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
<dependency>
<groupId>com.craftmend.openaudiomc</groupId>
<artifactId>OpenAudioMc</artifactId>
<version>6.7.4</version>
<version>6.7.5</version>
<scope>provided</scope>
</dependency>
<dependency>
Expand Down
2 changes: 1 addition & 1 deletion module-src/vistas-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@
<dependency>
<groupId>com.craftmend.openaudiomc</groupId>
<artifactId>OpenAudioMc-vistas-client</artifactId>
<version>6.7.4</version>
<version>6.7.5</version>
<scope>compile</scope>
</dependency>

Expand Down
37 changes: 36 additions & 1 deletion plugin/notepad.md
Original file line number Diff line number Diff line change
@@ -1 +1,36 @@
- add voice command to velocity
This update adds a few much needed voice chat improvements to make it viable on larger scale servers.
Adding moderation support was a carefully considered decision and was not taken lightly as it's a double-edged sword. I worked closely with the community in discord to see what different people needed, and what the privacy concerns of others would be. You can read the entire discussion back, starting this link: https://discord.com/channels/245497740589662209/245498082723233793/1018277193035563008

We ended up with a system that allows select moderators to listen in on voice chat, but only when they renew their moderation status every two minutes (this can be configured). This prevents people from just lurking, but also reminds them that they won't be heard.

Moderation is disabled by default (to prevent abuse from admins who don't read changelogs and just update everything), and can be enabled in the config file.
Once enabled, players will see a new banner in their voice chat tab, letting them know that moderation is enabled on their current server. This isn't too useful for end servers, but acts as some additional transparency as its different than what existing users might expect from OpenAudioMc.

Full changelog:
- The [CODE]/oa voice[/CODE] sub commands now work on velocity servers
- Fixed a bug where the inspect menu wouldn't detect some usernames
- Added moderation support! You can now enable moderation in the config, allowing moderators to listen in on potentially harmful players. More info on https://help.openadiomc.net/voicechat_moderation
- Added API methods for moderation status
- Fixed some logging issues
- Added improved support for the (now publicly available) require addon (https://www.spigotmc.org/resources/require-openaudiomc.105168/)
- Fixed some SQLite related issues while embedding Storm
- Added a moderation banner to the client
- Updated german client translations (by Ceddix)
- Fixed a bug where players woulnd't be logged in the recent peer list.
- Extended worldguard regions (two regions with the same source next to each other) will now update volume (according to settings) if the player moves from one to the other.
- Implemented better logging and retries for some models
- Fixed a bug where the ip overwride value would be ignored on some hardware configurations

Looking ahead:
- I'm currently working on non-proximity or spatial audio based voice chat. This would allow normal group calls, implementation of in-game phones, etc.
- Some parts of the backend will be overhauled this winter. This won't result in any changes for end users, but will make it easier to add new features in the future as well as resolving some technical debt.
- The account system/platform is due for a major upgrade, addressing some common complaints. (better email support, cleared up addon page, improved rest api, and more)

Closing notes
It's becoming kind of a ritual now, but I truly mean it everytime I say it.
Thank YOU, and especially my Patreons who keep this project alive. I'm very grateful for your support, and I hope you'll continue to enjoy OpenAudioMc for a long time to come.

OpenAudioMc is turning 6 years old soon, and I'm very proud of what we've accomplished together.
More on that later, but for now, enjoy this update!

- Mats
4 changes: 2 additions & 2 deletions plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,9 @@
</dependency>

<dependency>
<groupId>com.github.mindgamesnl</groupId>
<groupId>com.github.Mindgamesnl</groupId>
<artifactId>storm</artifactId>
<version>ef78d7f64e</version>
<version>2438525998</version>
</dependency>

<dependency>
Expand Down
2 changes: 1 addition & 1 deletion plugin/protocol/static-resources/project_status.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"response": {
"versioning": {
"version_tag": "6.7.4",
"version_tag": "6.7.5",
"build_number": 582,
"version_importance": "&dRecommended",
"version_update_message": "6.7.4 Added support for 1.19, implements a new database type and more."
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.craftmend.openaudiomc.generic.client;

import com.craftmend.openaudiomc.generic.client.objects.ClientConnection;
import com.craftmend.openaudiomc.generic.client.store.ClientDataStore;
import com.craftmend.openaudiomc.generic.database.DatabaseService;
import com.craftmend.openaudiomc.generic.networking.interfaces.NetworkingService;
import com.craftmend.openaudiomc.generic.networking.rest.Task;
import com.craftmend.openaudiomc.generic.networking.rest.data.ErrorCode;
import com.craftmend.openaudiomc.generic.platform.interfaces.TaskService;
Expand All @@ -25,6 +27,15 @@ public class ClientDataService extends Service {

public Task<ClientDataStore> getClientData(UUID owner, boolean store, boolean createEmpty) {
Task<ClientDataStore> task = new Task<>();

// is the client online? then use it
ClientConnection onlineClient = getService(NetworkingService.class).getClient(owner);

if (onlineClient != null) {
taskService.runAsync(() -> task.finish(onlineClient.getDataCache()));
return task;
}

taskService.runAsync(() -> {
ClientDataStore cds = db.getRepository(ClientDataStore.class).getWhere("owner", owner);
if (cds == null && !createEmpty) {
Expand Down Expand Up @@ -53,5 +64,17 @@ public void save(ClientDataStore data, UUID id) {
if (data == null || id == null) return;
data.setOwner(id);
db.getRepository(ClientDataStore.class).save(data);

// update cache
if (storeCache.containsKey(id)) {
storeCache.put(id, data);
}

// is the client online? then use it
ClientConnection onlineClient = getService(NetworkingService.class).getClient(id);

if (onlineClient != null) {
onlineClient.setDataCache(data);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public class ClientConnection implements Authenticatable, Client, Serializable {
@Getter private final RtcSessionManager rtcSessionManager;
private transient final List<Runnable> connectHandlers = new ArrayList<>();
private transient final List<Runnable> disconnectHandlers = new ArrayList<>();
@Getter private ClientDataStore dataCache;
@Setter @Getter private ClientDataStore dataCache;

public ClientConnection(User playerContainer, SerializableClient fromSerialized) {
this.user = playerContainer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
public class RtcSessionManager implements Serializable {

@Getter private boolean isMicrophoneEnabled = false;
@Getter private final transient Set<UUID> subscriptions = ConcurrentHashMap.newKeySet();
@Getter private final transient Set<UUID> listeningTo = 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<>();
Expand All @@ -56,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
subscriptions.clear();
listeningTo.clear();
this.isMicrophoneEnabled = false;
makePeersDrop();
locationUpdateQueue.clear();
Expand All @@ -76,10 +76,10 @@ public boolean linkTo(ClientConnection peer) {
if (!peer.getRtcSessionManager().isReady())
return false;

if (subscriptions.contains(peer.getOwner().getUniqueId()))
if (listeningTo.contains(peer.getOwner().getUniqueId()))
return false;

if (peer.getRtcSessionManager().subscriptions.contains(clientConnection.getOwner().getUniqueId()))
if (peer.getRtcSessionManager().listeningTo.contains(clientConnection.getOwner().getUniqueId()))
return false;

boolean skipPeer = false;
Expand All @@ -89,16 +89,14 @@ public boolean linkTo(ClientConnection peer) {
skipPeer = true;
}

subscriptions.add(peer.getOwner().getUniqueId());

if (!skipPeer) {
peer.getRtcSessionManager().getSubscriptions().add(clientConnection.getOwner().getUniqueId());
peer.getRtcSessionManager().getListeningTo().add(clientConnection.getOwner().getUniqueId());
peer.sendPacket(new PacketClientSubscribeToVoice(ClientVoiceSubscribePayload.fromClient(clientConnection, Vector3.from(peer))));
AudioApi.getInstance().getEventDriver().fire(new PlayerEnterVoiceProximityEvent(clientConnection, peer, VoiceEventCause.NORMAL));
}

listeningTo.add(peer.getOwner().getUniqueId());
clientConnection.sendPacket(new PacketClientSubscribeToVoice(ClientVoiceSubscribePayload.fromClient(peer, Vector3.from(clientConnection))));

AudioApi.getInstance().getEventDriver().fire(new PlayerEnterVoiceProximityEvent(peer, clientConnection, VoiceEventCause.NORMAL));

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

if (peer.getRtcSessionManager().subscriptions.contains(clientConnection.getOwner().getUniqueId())) {
if (peer.getRtcSessionManager().listeningTo.contains(clientConnection.getOwner().getUniqueId())) {
// send unsub packet
peer.getRtcSessionManager().subscriptions.remove(clientConnection.getOwner().getUniqueId());
peer.getRtcSessionManager().listeningTo.remove(clientConnection.getOwner().getUniqueId());
peer.getRtcSessionManager().updateLocationWatcher();
peer.sendPacket(new PacketClientDropVoiceStream(new ClientVoiceDropPayload(streamKey)));

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

if (peer.getRtcSessionManager().subscriptions.contains(clientConnection.getOwner().getUniqueId())) {
if (peer.getRtcSessionManager().listeningTo.contains(clientConnection.getOwner().getUniqueId())) {
peer.getRtcSessionManager().locationUpdateQueue.add(
ClientRtcLocationUpdate
.fromClientWithLocation(clientConnection, location, Vector3.from(peer))
Expand All @@ -176,7 +174,7 @@ public void updateLocationWatcher() {
// player logged out, ignoring
return;
}
if (subscriptions.isEmpty()) {
if (listeningTo.isEmpty()) {
spigotConnection.getLocationFollowers().remove(PlayerLocationFollower.PROXIMITY_VOICE_CHAT);
} else {
spigotConnection.getLocationFollowers().add(PlayerLocationFollower.PROXIMITY_VOICE_CHAT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class SessionData {
private final transient ClientConnection client;

private int moderationTimeRemaining = 0;
private boolean resetVc = true;
private boolean resetVc = false;
private boolean isModerating = false;
private boolean isWaitingToken = false;
private boolean sessionUpdated = false;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.craftmend.openaudiomc.generic.database.internal;

import com.craftmend.openaudiomc.OpenAudioMc;
import com.craftmend.openaudiomc.generic.database.DatabaseService;
import com.craftmend.openaudiomc.generic.logging.OpenAudioLogger;
import com.craftmend.openaudiomc.generic.platform.interfaces.TaskService;
import com.craftmend.storm.Storm;
import com.craftmend.storm.api.StormModel;
import com.craftmend.storm.api.enums.Where;
Expand Down Expand Up @@ -40,7 +43,22 @@ public T castToCompatible(Object o) {

@SneakyThrows
public void save(T data) {
storm.save(data);
// try to save it first
try {
storm.save(data);
} catch (Exception e) {
// try again in a second, if it failed again, log it as an error
TaskService ts = OpenAudioMc.resolveDependency(TaskService.class);
ts.schduleSyncDelayedTask(() -> {
ts.runAsync(() -> {
try {
storm.save(data);
} catch (Exception e1) {
OpenAudioLogger.toConsole("Error: Failed to update storm model " + data.getClass().getSimpleName());
}
});
}, 20 * 5);
}
}

@SneakyThrows
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.craftmend.openaudiomc.generic.database.internal.Repository;
import com.craftmend.openaudiomc.generic.logging.OpenAudioLogger;
import com.craftmend.openaudiomc.generic.mojang.store.MojangProfile;
import com.craftmend.openaudiomc.generic.networking.interfaces.NetworkingService;
import com.craftmend.openaudiomc.generic.networking.rest.Task;
import com.craftmend.openaudiomc.generic.networking.rest.data.ErrorCode;
import com.craftmend.openaudiomc.generic.platform.interfaces.TaskService;
Expand Down Expand Up @@ -54,10 +55,10 @@ public void save(User user) {
taskService.runAsync(() -> {
MojangProfile previous = profileRepository.getWhere("uuid", user.getUniqueId());
if (previous == null) {
profileRepository.save(new MojangProfile(user.getName(), user.getUniqueId(), Instant.now()));
profileRepository.save(new MojangProfile(user.getName().toLowerCase(), user.getUniqueId(), Instant.now()));
return;
}
previous.setName(user.getName());
previous.setName(user.getName().toLowerCase());
previous.setLastSeen(Instant.now());
profileRepository.save(previous);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ public VoiceApiConnection() {
taskService.runAsync(() -> {
handleClientConnection(clientConnection);

// ignore voice if we're banned
if (clientConnection.getDataCache().getIsVoiceBlocked()) {
return;
}

// make an event, and invite the client if it isn't cancelled
ClientRequestVoiceEvent event = OpenAudioMc.getInstance().getApiEventDriver().fire(new ClientRequestVoiceEvent(clientConnection));
if (!event.isCanceled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
public class VoiceInspectGui extends Menu {

public VoiceInspectGui(Player moderator, ClientDataStore targetData, UUID targetId, String targetName) {
super(OaColor.GOLD + targetName + "'s voice profile", 9);
super(OaColor.BLACK + targetName + "'s voice profile", 9);

// three items
// name: show name, status, last seen, etc
Expand All @@ -32,14 +32,13 @@ public VoiceInspectGui(Player moderator, ClientDataStore targetData, UUID target
setItem(2, new Item(Material.NAME_TAG)
.setName(OaColor.AQUA + "About " + targetName)
.setLore(new String[]{
OaColor.BLUE + "Last seen: " + OaColor.DARK_BLUE + DurationFormatter.formatDuration(Duration.between(targetData.getLastSeen(), Instant.now())),
OaColor.BLUE + "Last VC connection: " + OaColor.DARK_BLUE + DurationFormatter.formatDuration(Duration.between(targetData.getLastSeen(), Instant.now())),

OaColor.BLUE + "Last seen: " + OaColor.AQUA + DurationFormatter.formatDuration(Duration.between(targetData.getLastSeen(), Instant.now())),
OaColor.BLUE + "Last VC connection: " + OaColor.AQUA + DurationFormatter.formatDuration(Duration.between(targetData.getLastSeen(), Instant.now())),
})
.onClick((player, item) -> {})
);

setItem(4, new Item(Material.IRON_BARS)
setItem(4, new Item(Material.TNT)
.setName(ChatColor.RED + "VoiceChat ban")
.setLore(
(targetData.getIsVoiceBlocked() ? new String[] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ public void onExecute(User sender, String[] args) {
Task<MojangProfile> mojangFetch = getService(MojangLookupService.class).getByName(args[0]);

mojangFetch.setWhenFailed(((errorCode, s) -> {
message(sender, OaColor.RED + "There's no record of that player ever joining this server");
message(sender, OaColor.RED + "There's no record of that player ever joining this server (" + s + ")");
}));

mojangFetch.setWhenFinished(mojangProfile -> {
message(sender, OaColor.GRAY + "Loading client data from " + mojangProfile.getUuid().toString() + "...");
Task<ClientDataStore> clientDataRequest = getService(ClientDataService.class)
.getClientData(mojangProfile.getUuid(), false, false);
.getClientData(mojangProfile.getUuid(), true, false);

clientDataRequest.setWhenFailed(((errorCode, s) -> {
message(sender, OaColor.RED + "Failed to load profile data...");
Expand Down
Loading

0 comments on commit 4f7a782

Please sign in to comment.