using Grpc.Core; using MareSynchronosShared.Protos; using Microsoft.Extensions.Logging; using System.Collections.Concurrent; using System.Linq; using System.Runtime.ConstrainedExecution; using System.Threading.Tasks; namespace MareSynchronosServices.Identity; internal class IdentityService : IdentificationService.IdentificationServiceBase { private readonly ILogger _logger; private readonly IdentityHandler _handler; private readonly ConcurrentDictionary> identChanges = new(); public IdentityService(ILogger logger, IdentityHandler handler) { _logger = logger; _handler = handler; } public override async Task GetIdentForUid(UidMessage request, ServerCallContext context) { var result = await _handler.GetIdentForuid(request.Uid); return new CharacterIdentMessage() { Ident = result.CharacterIdent, ServerId = result.ServerId }; } public override Task GetOnlineUserCount(ServerMessage request, ServerCallContext context) { return Task.FromResult(new OnlineUserCountResponse() { Count = _handler.GetOnlineUsers(request.ServerId) }); } public override Task ClearIdentsForServer(ServerMessage request, ServerCallContext context) { var idents = _handler.GetIdentsForServer(request.ServerId); foreach (var entry in idents) { EnqueueIdentOffline(new UidWithIdent() { Ident = new CharacterIdentMessage() { Ident = entry.Value.CharacterIdent, ServerId = entry.Value.ServerId }, Uid = new UidMessage() { Uid = entry.Key } }); } _handler.ClearIdentsForServer(request.ServerId); return Task.FromResult(new Empty()); } public override Task RecreateServerIdents(ServerIdentMessage request, ServerCallContext context) { foreach (var identMsg in request.Idents) { _handler.SetIdent(identMsg.UidWithIdent.Uid.Uid, identMsg.UidWithIdent.Ident.ServerId, identMsg.UidWithIdent.Ident.Ident); EnqueueIdentOnline(identMsg.UidWithIdent); } return Task.FromResult(new Empty()); } public override async Task SendStreamIdentStatusChange(IAsyncStreamReader requestStream, ServerCallContext context) { await requestStream.MoveNext(); var server = requestStream.Current.Server; if (server == null) throw new System.Exception("First message needs to be server message"); _logger.LogInformation("Registered Server " + server.ServerId + " input stream"); identChanges[server.ServerId] = new ConcurrentQueue(); while (await requestStream.MoveNext().ConfigureAwait(false)) { var cur = requestStream.Current.IdentChange; if (cur == null) throw new System.Exception("Expected client ident change"); EnqueueIdentChange(cur); if (cur.IsOnline) { _handler.SetIdent(cur.UidWithIdent.Uid.Uid, cur.UidWithIdent.Ident.ServerId, cur.UidWithIdent.Ident.Ident); } else { _handler.RemoveIdent(cur.UidWithIdent.Uid.Uid, cur.UidWithIdent.Ident.ServerId); } } _logger.LogInformation("Server input stream from " + server + " finished"); return new Empty(); } public override async Task ReceiveStreamIdentStatusChange(ServerMessage request, IServerStreamWriter responseStream, ServerCallContext context) { var server = request.ServerId; _logger.LogInformation("Registered Server " + server + " output stream"); try { while (true) { if (identChanges.ContainsKey(server) && identChanges[server].TryDequeue(out var cur)) { _logger.LogInformation("Sending " + cur.UidWithIdent.Uid.Uid + " to " + server); await responseStream.WriteAsync(cur).ConfigureAwait(false); } else { await Task.Delay(10).ConfigureAwait(false); } } } catch { _logger.LogInformation("Server output stream to " + server + " finished or faulty"); } } public override Task GetAllIdents(ServerMessage request, ServerCallContext context) { var response = new UidWithIdentMessage(); foreach (var item in _handler.GetIdentsForAllExcept(request.ServerId)) { response.UidWithIdent.Add(new UidWithIdent() { Uid = new UidMessage() { Uid = item.Key }, Ident = new CharacterIdentMessage() { Ident = item.Value.CharacterIdent, ServerId = item.Value.ServerId } }); } return Task.FromResult(response); } private void EnqueueIdentChange(IdentChange identchange) { _logger.LogInformation("Enqueued " + identchange.UidWithIdent.Uid.Uid + ":" + identchange.IsOnline + " from " + identchange.UidWithIdent.Ident.ServerId); foreach (var dict in identChanges.Where(k => k.Key != identchange.UidWithIdent.Ident.ServerId)) { dict.Value.Enqueue(identchange); } } private void EnqueueIdentOnline(UidWithIdent ident) { EnqueueIdentChange(new IdentChange() { IsOnline = true, UidWithIdent = ident }); } private void EnqueueIdentOffline(UidWithIdent ident) { EnqueueIdentChange(new IdentChange() { IsOnline = false, UidWithIdent = ident }); } }