Skip to content

Commit

Permalink
Merge pull request #227 from bdach/apply-chat-filters-when-editing-ro…
Browse files Browse the repository at this point in the history
…om-name

Apply chat filters to room name when editing multiplayer room settings
  • Loading branch information
peppy authored Apr 25, 2024
2 parents 8756a0a + 783f1b8 commit d18c475
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 5 deletions.
8 changes: 7 additions & 1 deletion osu.Server.Spectator.Tests/Multiplayer/MultiplayerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,13 @@ protected MultiplayerTest()
Clients.Setup(clients => clients.Group(MultiplayerHub.GetGroupId(ROOM_ID_2))).Returns(Receiver2.Object);
Clients.Setup(client => client.Caller).Returns(Caller.Object);

Hub = new TestMultiplayerHub(new MemoryDistributedCache(Options.Create(new MemoryDistributedCacheOptions())), Rooms, UserStates, DatabaseFactory.Object, hubContext.Object);
Hub = new TestMultiplayerHub(
new MemoryDistributedCache(Options.Create(new MemoryDistributedCacheOptions())),
Rooms,
UserStates,
DatabaseFactory.Object,
new ChatFilters(DatabaseFactory.Object),
hubContext.Object);
Hub.Groups = Groups.Object;
Hub.Clients = Clients.Object;

Expand Down
25 changes: 25 additions & 0 deletions osu.Server.Spectator.Tests/Multiplayer/RoomSettingsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Moq;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
using osu.Server.Spectator.Database.Models;
using Xunit;

namespace osu.Server.Spectator.Tests.Multiplayer
Expand Down Expand Up @@ -162,5 +163,29 @@ await Hub.ChangeSettings(new MultiplayerRoomSettings
Assert.Equal(QueueMode.AllPlayers, room.Settings.QueueMode);
}
}

[Fact]
public async Task RoomNameIsFiltered()
{
Database.Setup(db => db.GetAllChatFiltersAsync())
.ReturnsAsync(new[] { new chat_filter { id = 1, match = "bad", replacement = "good" } });

MultiplayerRoomSettings testSettings = new MultiplayerRoomSettings
{
Name = "bad word",
MatchType = MatchType.HeadToHead
};

await Hub.JoinRoom(ROOM_ID);
await Hub.ChangeSettings(testSettings);

using (var usage = await Hub.GetRoom(ROOM_ID))
{
var room = usage.Item;

Debug.Assert(room != null);
Assert.Equal("good word", room.Settings.Name);
}
}
}
}
8 changes: 6 additions & 2 deletions osu.Server.Spectator.Tests/Multiplayer/TestMultiplayerHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ public class TestMultiplayerHub : MultiplayerHub
{
public new MultiplayerHubContext HubContext => base.HubContext;

public TestMultiplayerHub(IDistributedCache cache, EntityStore<ServerMultiplayerRoom> rooms, EntityStore<MultiplayerClientState> users, IDatabaseFactory databaseFactory,
public TestMultiplayerHub(IDistributedCache cache,
EntityStore<ServerMultiplayerRoom> rooms,
EntityStore<MultiplayerClientState> users,
IDatabaseFactory databaseFactory,
ChatFilters chatFilters,
IHubContext<MultiplayerHub> hubContext)
: base(cache, rooms, users, databaseFactory, hubContext)
: base(cache, rooms, users, databaseFactory, chatFilters, hubContext)
{
}

Expand Down
38 changes: 38 additions & 0 deletions osu.Server.Spectator/ChatFilters.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System.Collections.Immutable;
using System.Text;
using System.Threading.Tasks;
using osu.Server.Spectator.Database;
using osu.Server.Spectator.Database.Models;

namespace osu.Server.Spectator
{
public class ChatFilters
{
private readonly IDatabaseFactory factory;
private ImmutableArray<chat_filter>? filters;

public ChatFilters(IDatabaseFactory factory)
{
this.factory = factory;
}

public async Task<string> FilterAsync(string input)
{
if (filters == null)
{
using var db = factory.GetInstance();
filters = (await db.GetAllChatFiltersAsync()).ToImmutableArray();
}

var stringBuilder = new StringBuilder(input);

foreach (var filter in filters)
stringBuilder.Replace(filter.match, filter.replacement);

return stringBuilder.ToString();
}
}
}
7 changes: 7 additions & 0 deletions osu.Server.Spectator/Database/DatabaseAccess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,13 @@ public async Task UpdateBuildUserCountAsync(osu_build build)
await connection.ExecuteAsync("UPDATE `osu_builds` SET `users` = @users WHERE `build_id` = @build_id", build);
}

public async Task<IEnumerable<chat_filter>> GetAllChatFiltersAsync()
{
var connection = await getConnectionAsync();

return await connection.QueryAsync<chat_filter>("SELECT * FROM `chat_filters`");
}

public void Dispose()
{
openConnection?.Dispose();
Expand Down
5 changes: 5 additions & 0 deletions osu.Server.Spectator/Database/IDatabaseAccess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,5 +157,10 @@ public interface IDatabaseAccess : IDisposable
/// Updates the <see cref="osu_build.users"/> count of a given <paramref name="build"/>.
/// </summary>
Task UpdateBuildUserCountAsync(osu_build build);

/// <summary>
/// Retrieves all <see cref="chat_filter"/>s from the database.
/// </summary>
Task<IEnumerable<chat_filter>> GetAllChatFiltersAsync();
}
}
17 changes: 17 additions & 0 deletions osu.Server.Spectator/Database/Models/chat_filter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;

// ReSharper disable InconsistentNaming (matches database table)

namespace osu.Server.Spectator.Database.Models
{
[Serializable]
public class chat_filter
{
public long id { get; set; }
public string match { get; set; } = string.Empty;
public string replacement { get; set; } = string.Empty;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ public static IServiceCollection AddHubEntities(this IServiceCollection serviceC
.AddSingleton<IScoreStorage, S3ScoreStorage>()
.AddSingleton<ScoreUploader>()
.AddSingleton<IScoreProcessedSubscriber, ScoreProcessedSubscriber>()
.AddSingleton<BuildUserCountUpdater>();
.AddSingleton<BuildUserCountUpdater>()
.AddSingleton<ChatFilters>();
}

/// <summary>
Expand Down
10 changes: 9 additions & 1 deletion osu.Server.Spectator/Hubs/Multiplayer/MultiplayerHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,19 @@ public class MultiplayerHub : StatefulUserHub<IMultiplayerClient, MultiplayerCli
protected readonly EntityStore<ServerMultiplayerRoom> Rooms;
protected readonly MultiplayerHubContext HubContext;
private readonly IDatabaseFactory databaseFactory;
private readonly ChatFilters chatFilters;

public MultiplayerHub(IDistributedCache cache, EntityStore<ServerMultiplayerRoom> rooms, EntityStore<MultiplayerClientState> users, IDatabaseFactory databaseFactory,
public MultiplayerHub(IDistributedCache cache,
EntityStore<ServerMultiplayerRoom> rooms,
EntityStore<MultiplayerClientState> users,
IDatabaseFactory databaseFactory,
ChatFilters chatFilters,
IHubContext<MultiplayerHub> hubContext)
: base(cache, users)
{
Rooms = rooms;
this.databaseFactory = databaseFactory;
this.chatFilters = chatFilters;
HubContext = new MultiplayerHubContext(hubContext, rooms, users);
}

Expand Down Expand Up @@ -627,6 +633,8 @@ public async Task ChangeSettings(MultiplayerRoomSettings settings)

Log(room, "Settings updating");

settings.Name = await chatFilters.FilterAsync(settings.Name);

// Server is authoritative over the playlist item ID.
// Todo: This needs to change for tournament mode.
settings.PlaylistItemId = room.Settings.PlaylistItemId;
Expand Down

0 comments on commit d18c475

Please sign in to comment.