baseline: OpenMU upstream b5a0961 (fresh source)
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
// <copyright file="BannableChatMessageBaseProcessor.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameLogic.PlayerActions.Chat;
|
||||
|
||||
/// <summary>
|
||||
/// A chat message processor for normal chat.
|
||||
/// </summary>
|
||||
public abstract class BannableChatMessageBaseProcessor : IChatMessageProcessor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async ValueTask ProcessMessageAsync(Player sender, (string Message, string PlayerName) content)
|
||||
{
|
||||
TimeSpan remainingChatBan = this.RemainingChatBanTimeSpan(sender);
|
||||
if (this.IsSenderBanned(remainingChatBan))
|
||||
{
|
||||
if (remainingChatBan.TotalMinutes >= 1)
|
||||
{
|
||||
await sender.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.ChatBanMinutesRemaining), (int)Math.Ceiling(remainingChatBan.TotalMinutes)).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await sender.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.ChatBanSecondsRemaining), (int)Math.Ceiling(remainingChatBan.TotalSeconds)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await this.SubclassProcessMessageAsync(sender, content).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A method to be overriden for processing a specific chat message.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <returns>A value task with the result.</returns>
|
||||
public abstract ValueTask SubclassProcessMessageAsync(Player sender, (string Message, string PlayerName) content);
|
||||
|
||||
private TimeSpan RemainingChatBanTimeSpan(Player sender)
|
||||
{
|
||||
DateTime chatBanUntil = sender.Account?.ChatBanUntil ?? default;
|
||||
DateTime currentDateTime = DateTime.UtcNow;
|
||||
return chatBanUntil - currentDateTime;
|
||||
}
|
||||
|
||||
private bool IsSenderBanned(TimeSpan remainingChatBan)
|
||||
{
|
||||
return remainingChatBan > TimeSpan.Zero;
|
||||
}
|
||||
}
|
||||
105
src/GameLogic/PlayerActions/Chat/ChatMessageAction.cs
Normal file
105
src/GameLogic/PlayerActions/Chat/ChatMessageAction.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
// <copyright file="ChatMessageAction.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameLogic.PlayerActions.Chat;
|
||||
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
|
||||
/// <summary>
|
||||
/// Action to send chat messages.
|
||||
/// </summary>
|
||||
public class ChatMessageAction
|
||||
{
|
||||
private readonly IDictionary<string, ChatMessageType> _messagePrefixes;
|
||||
private readonly IDictionary<ChatMessageType, IChatMessageProcessor> _chatProcessMessages;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ChatMessageAction"/> class.
|
||||
/// </summary>
|
||||
public ChatMessageAction()
|
||||
{
|
||||
this._messagePrefixes = new SortedDictionary<string, ChatMessageType>(new ReverseComparer())
|
||||
{
|
||||
{ "~", ChatMessageType.Party },
|
||||
{ "@", ChatMessageType.Guild },
|
||||
{ "@@", ChatMessageType.Alliance },
|
||||
{ "$", ChatMessageType.Gens },
|
||||
{ "!", ChatMessageType.GlobalNotification },
|
||||
{ "/", ChatMessageType.Command },
|
||||
};
|
||||
|
||||
this._chatProcessMessages = new Dictionary<ChatMessageType, IChatMessageProcessor>
|
||||
{
|
||||
{ ChatMessageType.Command, new ChatMessageCommandProcessor() },
|
||||
{ ChatMessageType.Whisper, new ChatMessageWhisperProcessor() },
|
||||
{ ChatMessageType.Party, new ChatMessagePartyProcessor() },
|
||||
{ ChatMessageType.Alliance, new ChatMessageAllianceProcessor() },
|
||||
{ ChatMessageType.Guild, new ChatMessageGuildProcessor() },
|
||||
{ ChatMessageType.GlobalNotification, new ChatMessageGlobalNotificationProcessor() },
|
||||
{ ChatMessageType.Normal, new ChatMessageNormalProcessor() },
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a chat message from the player to other players.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="playerName">Name of the <paramref name="sender"/>, except for <see cref="ChatMessageType.Whisper"/>, then its the receiver name.</param>
|
||||
/// <param name="message">The message which should be sent.</param>
|
||||
/// <param name="whisper">If set to <c>true</c> the message is whispered to the player with the <paramref name="playerName"/>; Otherwise, it's not a whisper.</param>
|
||||
public async ValueTask ChatMessageAsync(Player sender, string playerName, string message, bool whisper)
|
||||
{
|
||||
using var loggerScope = sender.Logger.BeginScope(this.GetType());
|
||||
ChatMessageType messageType = this.GetMessageType(message, whisper);
|
||||
|
||||
if (sender.SelectedCharacter is null)
|
||||
{
|
||||
// Is possible to receive null?
|
||||
return;
|
||||
}
|
||||
|
||||
if (messageType != ChatMessageType.Whisper && playerName != sender.SelectedCharacter?.Name)
|
||||
{
|
||||
sender.Logger.LogWarning("Maybe Hacker, Charname in chat packet != charname\t [{0}] <> [{1}]", sender.SelectedCharacter?.Name, playerName);
|
||||
}
|
||||
|
||||
if (!this._chatProcessMessages.ContainsKey(messageType))
|
||||
{
|
||||
sender.Logger.LogDebug("Not implemented chat message type: {0}", messageType);
|
||||
return;
|
||||
}
|
||||
|
||||
await this._chatProcessMessages[messageType].ProcessMessageAsync(sender, (message, playerName)).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
private ChatMessageType GetMessageType(string message, bool whisper)
|
||||
{
|
||||
if (whisper)
|
||||
{
|
||||
return ChatMessageType.Whisper;
|
||||
}
|
||||
|
||||
// byte 13: begin message
|
||||
foreach (var keyValuePair in this._messagePrefixes)
|
||||
{
|
||||
if (message.StartsWith(keyValuePair.Key, StringComparison.InvariantCulture))
|
||||
{
|
||||
return keyValuePair.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return ChatMessageType.Normal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We have to implement a reverse comparer, so that the strings which are longer, come first.
|
||||
/// </summary>
|
||||
private class ReverseComparer : IComparer<string>
|
||||
{
|
||||
public int Compare(string? x, string? y)
|
||||
{
|
||||
return string.Compare(y, x, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// <copyright file="ChatMessageAllianceProcessor.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameLogic.PlayerActions.Chat;
|
||||
|
||||
using System.ComponentModel;
|
||||
using MUnique.OpenMU.GameLogic.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// A chat message processor for alliance chat.
|
||||
/// </summary>
|
||||
public class ChatMessageAllianceProcessor : BannableChatMessageBaseProcessor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override async ValueTask SubclassProcessMessageAsync(Player sender, (string Message, string PlayerName) content)
|
||||
{
|
||||
var eventArgs = new CancelEventArgs();
|
||||
sender.GameContext.PlugInManager.GetPlugInPoint<IChatMessageReceivedPlugIn>()?.ChatMessageReceived(sender, content.Message, eventArgs);
|
||||
if (eventArgs.Cancel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(sender.GuildStatus != null && (sender.GameContext as IGameServerContext)?.EventPublisher is { } publisher))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Use DI to get the IEventPublisher
|
||||
await publisher.AllianceMessageAsync(sender.GuildStatus.GuildId, sender.SelectedCharacter!.Name, content.Message).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// <copyright file="ChatMessageCommandProcessor.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameLogic.PlayerActions.Chat;
|
||||
|
||||
using MUnique.OpenMU.GameLogic.PlugIns.ChatCommands;
|
||||
|
||||
/// <summary>
|
||||
/// A chat message processor which handles chat commands.
|
||||
/// </summary>
|
||||
public class ChatMessageCommandProcessor : IChatMessageProcessor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async ValueTask ProcessMessageAsync(Player sender, (string Message, string PlayerName) content)
|
||||
{
|
||||
var commandKey = content.Message.Split(' ').First();
|
||||
var commandHandler = sender.GameContext.PlugInManager.GetStrategy<IChatCommandPlugIn>(commandKey);
|
||||
if (commandHandler is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (sender.SelectedCharacter!.CharacterStatus < commandHandler.MinCharacterStatusRequirement)
|
||||
{
|
||||
sender.Logger.LogWarning($"{sender.Name} is trying to execute {commandKey} command without meeting the requirements");
|
||||
return;
|
||||
}
|
||||
|
||||
await commandHandler.HandleCommandAsync(sender, content.Message).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// <copyright file="ChatMessageGlobalNotificationProcessor.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameLogic.PlayerActions.Chat;
|
||||
|
||||
using System.ComponentModel;
|
||||
using MUnique.OpenMU.GameLogic.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// A chat message processor which sends a global notification.
|
||||
/// </summary>
|
||||
public class ChatMessageGlobalNotificationProcessor : IChatMessageProcessor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async ValueTask ProcessMessageAsync(Player sender, (string Message, string PlayerName) content)
|
||||
{
|
||||
var eventArgs = new CancelEventArgs();
|
||||
sender.GameContext.PlugInManager.GetPlugInPoint<IChatMessageReceivedPlugIn>()
|
||||
?.ChatMessageReceived(sender, content.Message, eventArgs);
|
||||
if (eventArgs.Cancel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (sender.SelectedCharacter!.CharacterStatus < CharacterStatus.GameMaster)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await sender.GameContext.SendGlobalNotificationAsync(content.Message).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// <copyright file="ChatMessageGuildProcessor.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameLogic.PlayerActions.Chat;
|
||||
|
||||
using System.ComponentModel;
|
||||
using MUnique.OpenMU.GameLogic.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// A chat message processor which sends the message to the guild.
|
||||
/// </summary>
|
||||
public class ChatMessageGuildProcessor : BannableChatMessageBaseProcessor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override async ValueTask SubclassProcessMessageAsync(Player sender, (string Message, string PlayerName) content)
|
||||
{
|
||||
var eventArgs = new CancelEventArgs();
|
||||
sender.GameContext.PlugInManager.GetPlugInPoint<IChatMessageReceivedPlugIn>()?.ChatMessageReceived(sender, content.Message, eventArgs);
|
||||
if (eventArgs.Cancel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(sender.GuildStatus != null && (sender.GameContext as IGameServerContext)?.EventPublisher is { } publisher))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await publisher.GuildMessageAsync(sender.GuildStatus.GuildId, sender.SelectedCharacter!.Name, content.Message).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// <copyright file="ChatMessageNormalProcessor.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameLogic.PlayerActions.Chat;
|
||||
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
|
||||
/// <summary>
|
||||
/// A chat message processor for normal chat.
|
||||
/// </summary>
|
||||
public class ChatMessageNormalProcessor : BannableChatMessageBaseProcessor
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override async ValueTask SubclassProcessMessageAsync(Player sender, (string Message, string PlayerName) content)
|
||||
{
|
||||
sender.Logger.LogDebug("Sending Chat Message to Observers, Count: {0}", sender.Observers.Count);
|
||||
await sender.ForEachWorldObserverAsync<IChatViewPlugIn>(p => p.ChatMessageAsync(content.Message, sender.SelectedCharacter!.Name, ChatMessageType.Normal), true).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// <copyright file="ChatMessagePartyProcessor.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameLogic.PlayerActions.Chat;
|
||||
|
||||
using System.ComponentModel;
|
||||
using MUnique.OpenMU.GameLogic.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// A chat message processor which sends the message to the party.
|
||||
/// </summary>
|
||||
public class ChatMessagePartyProcessor : BannableChatMessageBaseProcessor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override async ValueTask SubclassProcessMessageAsync(Player sender, (string Message, string PlayerName) content)
|
||||
{
|
||||
var eventArgs = new CancelEventArgs();
|
||||
sender.GameContext.PlugInManager.GetPlugInPoint<IChatMessageReceivedPlugIn>()?.ChatMessageReceived(sender, content.Message, eventArgs);
|
||||
if (eventArgs.Cancel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
sender.Party?.SendChatMessageAsync(content.Message, sender.SelectedCharacter!.Name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// <copyright file="ChatMessageWhisperProcessor.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameLogic.PlayerActions.Chat;
|
||||
|
||||
using System.ComponentModel;
|
||||
using MUnique.OpenMU.GameLogic.PlugIns;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
|
||||
/// <summary>
|
||||
/// A chat message processor which sends the message to the whisper receiver.
|
||||
/// </summary>
|
||||
public class ChatMessageWhisperProcessor : BannableChatMessageBaseProcessor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override async ValueTask SubclassProcessMessageAsync(Player sender, (string Message, string PlayerName) content)
|
||||
{
|
||||
var whisperReceiver = sender.GameContext.GetPlayerByCharacterName(content.PlayerName);
|
||||
if (whisperReceiver != null)
|
||||
{
|
||||
var eventArgs = new CancelEventArgs();
|
||||
sender.GameContext.PlugInManager.GetPlugInPoint<IWhisperMessageReceivedPlugIn>()?.WhisperMessageReceived(sender, whisperReceiver, content.Message, eventArgs);
|
||||
if (!eventArgs.Cancel)
|
||||
{
|
||||
await whisperReceiver.InvokeViewPlugInAsync<IChatViewPlugIn>(p => p.ChatMessageAsync(content.Message, sender.SelectedCharacter!.Name, ChatMessageType.Whisper)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/GameLogic/PlayerActions/Chat/IChatMessageProcessor.cs
Normal file
19
src/GameLogic/PlayerActions/Chat/IChatMessageProcessor.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
// <copyright file="IChatMessageProcessor.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameLogic.PlayerActions.Chat;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for a chat message processor.
|
||||
/// </summary>
|
||||
public interface IChatMessageProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends a chat message from the player to other players.
|
||||
/// </summary>
|
||||
/// <param name="sender" cref="Player">The sending Player.</param>
|
||||
/// <param name="content">The chat message's content.</param>
|
||||
/// <returns>The value task with the result.</returns>
|
||||
ValueTask ProcessMessageAsync(Player sender, (string Message, string PlayerName) content);
|
||||
}
|
||||
Reference in New Issue
Block a user