119 lines
5.2 KiB
C#
119 lines
5.2 KiB
C#
using System;
|
|||
|
|
using System.Collections.Generic;
|
||
|
|
|
||
|
|
// МОДУЛЬ ЧАТА: общий чат мира и чат Круга. Документ: Docs/Modules/Chat.md
|
||
|
|
// Без Unity: когда появится сервер, этот класс заменится сетевой реализацией IChat,
|
||
|
|
// а панель чата (слой приложения) менять не придётся.
|
||
|
|
//
|
||
|
|
// История живёт, пока открыта игра: модуль зарегистрирован в GameServices и переживает смену сцен.
|
||
|
|
// В файл сохранения чат не пишется -- переписку будет хранить сервер.
|
||
|
|
public sealed class ChatService : IChat
|
||
|
|
{
|
||
|
|
public const int MaxLength = 200;
|
||
|
|
public const int MaxStoredPerChannel = 100;
|
||
|
|
|
||
|
|
public int MaxMessageLength => MaxLength;
|
||
|
|
|
||
|
|
private readonly Dictionary<ChatChannel, List<ChatMessage>> history = new Dictionary<ChatChannel, List<ChatMessage>>
|
||
|
|
{
|
||
|
|
{ ChatChannel.World, new List<ChatMessage>() },
|
||
|
|
{ ChatChannel.Circle, new List<ChatMessage>() }
|
||
|
|
};
|
||
|
|
|
||
|
|
public ChatService(long nowMs)
|
||
|
|
{
|
||
|
|
SeedDemoMessages(nowMs);
|
||
|
|
|
||
|
|
// Канал "Круг" -- переписка текущего Круга игрока (модуль Круга сообщает о вступлении и выходе)
|
||
|
|
EventBus.Subscribe<CircleJoinedEvent>(OnCircleJoined);
|
||
|
|
EventBus.Subscribe<CircleLeftEvent>(OnCircleLeft);
|
||
|
|
EventBus.Subscribe<GameResetEvent>(_ => history[ChatChannel.Circle].Clear()); // новая игра -- без Круга
|
||
|
|
}
|
||
|
|
|
||
|
|
public const string SystemSenderId = "system";
|
||
|
|
|
||
|
|
// Писать в канал "Круг" можно, только состоя в Круге (если модуль Круга подключён)
|
||
|
|
public bool CanWrite(ChatChannel channel)
|
||
|
|
{
|
||
|
|
if (channel != ChatChannel.Circle) return true;
|
||
|
|
return !GameServices.TryGet(out ICircle circle) || circle.IsInCircle;
|
||
|
|
}
|
||
|
|
|
||
|
|
void OnCircleJoined(CircleJoinedEvent e)
|
||
|
|
{
|
||
|
|
history[ChatChannel.Circle].Clear();
|
||
|
|
string text = e.Created
|
||
|
|
? $"Вы основали Круг «{e.CircleName}». Зовите соратников!"
|
||
|
|
: $"Вы вступили в Круг «{e.CircleName}».";
|
||
|
|
Add(new ChatMessage { channel = ChatChannel.Circle, senderId = SystemSenderId, senderName = "Круг", text = text, timeMs = NowMs });
|
||
|
|
}
|
||
|
|
|
||
|
|
void OnCircleLeft(CircleLeftEvent e)
|
||
|
|
{
|
||
|
|
history[ChatChannel.Circle].Clear();
|
||
|
|
Add(new ChatMessage
|
||
|
|
{
|
||
|
|
channel = ChatChannel.Circle,
|
||
|
|
senderId = SystemSenderId,
|
||
|
|
senderName = "Круг",
|
||
|
|
text = e.Disbanded ? $"Круг «{e.CircleName}» распущен." : $"Вы покинули Круг «{e.CircleName}».",
|
||
|
|
timeMs = NowMs
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
static long NowMs => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||
|
|
|
||
|
|
public IReadOnlyList<ChatMessage> GetMessages(ChatChannel channel)
|
||
|
|
{
|
||
|
|
return history[channel];
|
||
|
|
}
|
||
|
|
|
||
|
|
public bool Send(ChatChannel channel, string text)
|
||
|
|
{
|
||
|
|
text = text?.Trim();
|
||
|
|
if (string.IsNullOrEmpty(text)) return false;
|
||
|
|
if (!CanWrite(channel)) return false;
|
||
|
|
if (text.Length > MaxLength) text = text.Substring(0, MaxLength);
|
||
|
|
|
||
|
|
Add(new ChatMessage
|
||
|
|
{
|
||
|
|
channel = channel,
|
||
|
|
senderId = LocalPlayer.Id,
|
||
|
|
senderName = LocalPlayer.Name,
|
||
|
|
text = text,
|
||
|
|
timeMs = NowMs
|
||
|
|
});
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Входящее сообщение (сейчас -- только свои; позже сюда придут сообщения с сервера)
|
||
|
|
public void Receive(ChatMessage message)
|
||
|
|
{
|
||
|
|
Add(message);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Add(ChatMessage message)
|
||
|
|
{
|
||
|
|
List<ChatMessage> list = history[message.channel];
|
||
|
|
list.Add(message);
|
||
|
|
if (list.Count > MaxStoredPerChannel) list.RemoveAt(0);
|
||
|
|
EventBus.Publish(new ChatMessageReceivedEvent { Message = message });
|
||
|
|
}
|
||
|
|
|
||
|
|
// Несколько сообщений "от других игроков", чтобы чат не был пустым. Уберётся с приходом сервера.
|
||
|
|
void SeedDemoMessages(long nowMs)
|
||
|
|
{
|
||
|
|
const long minute = 60_000;
|
||
|
|
AddDemo(ChatChannel.World, "npc_sever", "Север", "Кто идёт на Змея Горыныча?", nowMs - 5 * minute);
|
||
|
|
AddDemo(ChatChannel.World, "npc_dreadwolf", "DreadWolf", "Набор в Круг, нужны активные игроки!", nowMs - 4 * minute);
|
||
|
|
AddDemo(ChatChannel.World, "npc_moroz", "Мороз", "На западе замечен Круг Стальные Волки.", nowMs - 3 * minute);
|
||
|
|
AddDemo(ChatChannel.World, "npc_tairon", "Тайрон", "Собираем отряд на древний храм.", nowMs - 2 * minute);
|
||
|
|
AddDemo(ChatChannel.World, "npc_veles", "Велес", "Всем удачной охоты!", nowMs - minute);
|
||
|
|
}
|
||
|
|
|
||
|
|
void AddDemo(ChatChannel channel, string id, string name, string text, long timeMs)
|
||
|
|
{
|
||
|
|
history[channel].Add(new ChatMessage { channel = channel, senderId = id, senderName = name, text = text, timeMs = timeMs });
|
||
|
|
}
|
||
|
|
}
|