1
0
mirror of https://github.com/RaidMax/IW4M-Admin.git synced 2025-06-10 07:13:58 -05:00

implement new eventing system

This commit is contained in:
RaidMax
2023-04-05 09:54:57 -05:00
parent 53a6ef2ec3
commit ebe69a94ad
39 changed files with 1410 additions and 526 deletions

View File

@ -9,7 +9,7 @@ using SharedLibraryCore.Helpers;
using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Net;
@ -31,6 +31,9 @@ using IW4MAdmin.Application.Plugin.Script;
using IW4MAdmin.Plugins.Stats.Helpers;
using Microsoft.EntityFrameworkCore;
using SharedLibraryCore.Alerts;
using SharedLibraryCore.Events.Management;
using SharedLibraryCore.Events.Server;
using SharedLibraryCore.Interfaces.Events;
using static Data.Models.Client.EFClient;
namespace IW4MAdmin
@ -44,11 +47,12 @@ namespace IW4MAdmin
private const int REPORT_FLAG_COUNT = 4;
private long lastGameTime = 0;
public int Id { get; private set; }
private readonly IServiceProvider _serviceProvider;
private readonly IClientNoticeMessageFormatter _messageFormatter;
private readonly ILookupCache<EFServer> _serverCache;
private readonly CommandConfiguration _commandConfiguration;
private EFServer _cachedDatabaseServer;
private readonly StatManager _statManager;
public IW4MServer(
ServerConfiguration serverConfiguration,
@ -72,6 +76,18 @@ namespace IW4MAdmin
_messageFormatter = messageFormatter;
_serverCache = serverCache;
_commandConfiguration = commandConfiguration;
_statManager = serviceProvider.GetRequiredService<StatManager>();
IGameServerEventSubscriptions.MonitoringStarted += async (gameEvent, token) =>
{
if (gameEvent.Server.Id != Id)
{
return;
}
await EnsureServerAdded();
await _statManager.EnsureServerAdded(gameEvent.Server, token);
};
}
public override async Task<EFClient> OnClientConnected(EFClient clientFromLog)
@ -108,7 +124,7 @@ namespace IW4MAdmin
Clients[client.ClientNumber] = client;
ServerLogger.LogDebug("End PreConnect for {client}", client.ToString());
var e = new GameEvent()
var e = new GameEvent
{
Origin = client,
Owner = this,
@ -116,6 +132,11 @@ namespace IW4MAdmin
};
Manager.AddEvent(e);
Manager.QueueEvent(new ClientStateInitializeEvent
{
Client = client,
Source = this,
});
return client;
}
@ -210,10 +231,17 @@ namespace IW4MAdmin
ServerLogger.LogInformation("Executing command {Command} for {Client}", cmd.Name,
E.Origin.ToString());
await cmd.ExecuteAsync(E);
Manager.QueueEvent(new ClientExecuteCommandEvent
{
Command = cmd,
Client = E.Origin,
Source = this,
CommandText = E.Data
});
}
var pluginTasks = Manager.Plugins
.Select(async plugin => await CreatePluginTask(plugin, E));
var pluginTasks = Manager.Plugins.Where(plugin => !plugin.IsParser)
.Select(plugin => CreatePluginTask(plugin, E));
await Task.WhenAll(pluginTasks);
}
@ -250,7 +278,7 @@ namespace IW4MAdmin
try
{
await plugin.OnEventAsync(gameEvent, this).WithWaitCancellation(tokenSource.Token);
await plugin.OnEventAsync(gameEvent, this);
}
catch (OperationCanceledException)
{
@ -277,29 +305,7 @@ namespace IW4MAdmin
{
ServerLogger.LogDebug("processing event of type {type}", E.Type);
if (E.Type == GameEvent.EventType.Start)
{
var existingServer = (await _serverCache
.FirstAsync(server => server.Id == EndPoint));
var serverId = await GetIdForServer(E.Owner);
if (existingServer == null)
{
var server = new EFServer()
{
Port = Port,
EndPoint = ToString(),
ServerId = serverId,
GameName = (Reference.Game?)GameName,
HostName = Hostname
};
await _serverCache.AddAsync(server);
}
}
else if (E.Type == GameEvent.EventType.ConnectionLost)
if (E.Type == GameEvent.EventType.ConnectionLost)
{
var exception = E.Extra as Exception;
ServerLogger.LogError(exception,
@ -350,9 +356,18 @@ namespace IW4MAdmin
else if (E.Type == GameEvent.EventType.ChangePermission)
{
var newPermission = (Permission) E.Extra;
var oldPermission = E.Target.Level;
ServerLogger.LogInformation("{origin} is setting {target} to permission level {newPermission}",
E.Origin.ToString(), E.Target.ToString(), newPermission);
await Manager.GetClientService().UpdateLevel(newPermission, E.Target, E.Origin);
Manager.QueueEvent(new ClientPermissionChangeEvent
{
Client = E.Origin,
Source = this,
OldPermission = oldPermission,
NewPermission = newPermission
});
}
else if (E.Type == GameEvent.EventType.Connect)
@ -500,6 +515,12 @@ namespace IW4MAdmin
await Manager.GetPenaltyService().Create(newPenalty);
E.Target.SetLevel(Permission.Flagged, E.Origin);
Manager.QueueEvent(new ClientPenaltyEvent
{
Client = E.Target,
Penalty = newPenalty
});
}
else if (E.Type == GameEvent.EventType.Unflag)
@ -519,6 +540,12 @@ namespace IW4MAdmin
await Manager.GetPenaltyService().RemoveActivePenalties(E.Target.AliasLinkId, E.Target.NetworkId,
E.Target.GameName, E.Target.CurrentAlias?.IPAddress);
await Manager.GetPenaltyService().Create(unflagPenalty);
Manager.QueueEvent(new ClientPenaltyRevokeEvent
{
Client = E.Target,
Penalty = unflagPenalty
});
}
else if (E.Type == GameEvent.EventType.Report)
@ -554,6 +581,13 @@ namespace IW4MAdmin
Utilities.CurrentLocalization.LocalizationIndex["SERVER_AUTO_FLAG_REPORT"]
.FormatExt(reportNum), Utilities.IW4MAdminClient(E.Owner));
}
Manager.QueueEvent(new ClientPenaltyEvent
{
Client = E.Target,
Penalty = newReport,
Source = this
});
}
else if (E.Type == GameEvent.EventType.TempBan)
@ -728,6 +762,11 @@ namespace IW4MAdmin
{
MaxClients = int.Parse(dict["com_maxclients"]);
}
else if (dict.ContainsKey("com_maxplayers"))
{
MaxClients = int.Parse(dict["com_maxplayers"]);
}
if (dict.ContainsKey("mapname"))
{
@ -772,34 +811,6 @@ namespace IW4MAdmin
{
E.Origin.UpdateTeam(E.Extra as string);
}
else if (E.Type == GameEvent.EventType.MetaUpdated)
{
if (E.Extra is "PersistentClientGuid")
{
var parts = E.Data.Split(",");
if (parts.Length == 2 && int.TryParse(parts[0], out var high) &&
int.TryParse(parts[1], out var low))
{
var guid = long.Parse(high.ToString("X") + low.ToString("X"), NumberStyles.HexNumber);
var penalties = await Manager.GetPenaltyService()
.GetActivePenaltiesByIdentifier(null, guid, (Reference.Game)GameName);
var banPenalty =
penalties.FirstOrDefault(penalty => penalty.Type == EFPenalty.PenaltyType.Ban);
if (banPenalty is not null && E.Origin.Level != Permission.Banned)
{
ServerLogger.LogInformation(
"Banning {Client} as they have have provided a persistent clientId of {PersistentClientId}, which is banned",
E.Origin.ToString(), guid);
E.Origin.Ban(loc["SERVER_BAN_EVADE"].FormatExt(guid),
Utilities.IW4MAdminClient(this), true);
}
}
}
}
lock (ChatHistory)
{
@ -820,6 +831,53 @@ namespace IW4MAdmin
}
}
public async Task EnsureServerAdded()
{
var gameServer = await _serverCache
.FirstAsync(server => server.EndPoint == base.Id);
if (gameServer == null)
{
gameServer = new EFServer
{
Port = ListenPort,
EndPoint = base.Id,
ServerId = BuildLegacyDatabaseId(),
GameName = (Reference.Game?)GameName,
HostName = ServerName
};
await _serverCache.AddAsync(gameServer);
}
await using var context = _serviceProvider.GetRequiredService<IDatabaseContextFactory>()
.CreateContext(enableTracking: false);
context.Servers.Attach(gameServer);
// we want to set the gamename up if it's never been set, or it changed
if (!gameServer.GameName.HasValue || gameServer.GameName.Value != GameCode)
{
gameServer.GameName = GameCode;
context.Entry(gameServer).Property(property => property.GameName).IsModified = true;
}
if (gameServer.HostName == null || gameServer.HostName != ServerName)
{
gameServer.HostName = ServerName;
context.Entry(gameServer).Property(property => property.HostName).IsModified = true;
}
if (gameServer.IsPasswordProtected != !string.IsNullOrEmpty(GamePassword))
{
gameServer.IsPasswordProtected = !string.IsNullOrEmpty(GamePassword);
context.Entry(gameServer).Property(property => property.IsPasswordProtected).IsModified = true;
}
await context.SaveChangesAsync();
_cachedDatabaseServer = gameServer;
}
private async Task OnClientUpdate(EFClient origin)
{
var client = GetClientsAsList().FirstOrDefault(c => c.NetworkId == origin.NetworkId);
@ -909,22 +967,15 @@ namespace IW4MAdmin
public override async Task<long> GetIdForServer(Server server = null)
{
server ??= this;
if ($"{server.IP}:{server.Port.ToString()}" == "66.150.121.184:28965")
{
return 886229536;
}
// todo: this is not stable and will need to be migrated again...
long id = HashCode.Combine(server.IP, server.Port);
id = id < 0 ? Math.Abs(id) : id;
return (await _serverCache.FirstAsync(cachedServer =>
cachedServer.EndPoint == server.Id || cachedServer.ServerId == server.EndPoint)).ServerId;
}
var serverId = (await _serverCache
.FirstAsync(_server => _server.ServerId == server.EndPoint ||
_server.EndPoint == server.ToString() ||
_server.ServerId == id))?.ServerId;
return !serverId.HasValue ? id : serverId.Value;
private long BuildLegacyDatabaseId()
{
long id = HashCode.Combine(ListenAddress, ListenPort);
return id < 0 ? Math.Abs(id) : id;
}
private void UpdateMap(string mapname)
@ -983,7 +1034,7 @@ namespace IW4MAdmin
{
await client.OnDisconnect();
var e = new GameEvent()
var e = new GameEvent
{
Type = GameEvent.EventType.Disconnect,
Owner = this,
@ -994,6 +1045,14 @@ namespace IW4MAdmin
await e.WaitAsync(Utilities.DefaultCommandTimeout, new CancellationTokenRegistration().Token);
}
using var tokenSource = new CancellationTokenSource();
tokenSource.CancelAfter(Utilities.DefaultCommandTimeout);
Manager.QueueEvent(new MonitorStopEvent
{
Server = this
});
}
private DateTime _lastMessageSent = DateTime.Now;
@ -1075,6 +1134,16 @@ namespace IW4MAdmin
Manager.AddEvent(gameEvent);
}
if (polledClients[2].Any())
{
Manager.QueueEvent(new ClientDataUpdateEvent
{
Clients = new ReadOnlyCollection<EFClient>(polledClients[2]),
Server = this,
Source = this,
});
}
if (Throttled)
{
var gameEvent = new GameEvent
@ -1086,6 +1155,12 @@ namespace IW4MAdmin
};
Manager.AddEvent(gameEvent);
Manager.QueueEvent(new ConnectionRestoreEvent
{
Server = this,
Source = this
});
}
LastPoll = DateTime.Now;
@ -1109,6 +1184,12 @@ namespace IW4MAdmin
};
Manager.AddEvent(gameEvent);
Manager.QueueEvent(new ConnectionInterruptEvent
{
Server = this,
Source = this
});
return true;
}
finally
@ -1469,6 +1550,12 @@ namespace IW4MAdmin
.FormatExt(activeClient.Warnings, activeClient.Name, reason);
activeClient.CurrentServer.Broadcast(message);
}
Manager.QueueEvent(new ClientPenaltyEvent
{
Client = targetClient,
Penalty = newPenalty
});
}
public override async Task Kick(string reason, EFClient targetClient, EFClient originClient, EFPenalty previousPenalty)
@ -1507,6 +1594,12 @@ namespace IW4MAdmin
ServerLogger.LogDebug("Executing tempban kick command for {ActiveClient}", activeClient.ToString());
await activeClient.CurrentServer.ExecuteCommandAsync(formattedKick);
}
Manager.QueueEvent(new ClientPenaltyEvent
{
Client = targetClient,
Penalty = newPenalty
});
}
public override async Task TempBan(string reason, TimeSpan length, EFClient targetClient, EFClient originClient)
@ -1540,6 +1633,12 @@ namespace IW4MAdmin
ServerLogger.LogDebug("Executing tempban kick command for {ActiveClient}", activeClient.ToString());
await activeClient.CurrentServer.ExecuteCommandAsync(formattedKick);
}
Manager.QueueEvent(new ClientPenaltyEvent
{
Client = targetClient,
Penalty = newPenalty
});
}
public override async Task Ban(string reason, EFClient targetClient, EFClient originClient, bool isEvade = false)
@ -1575,6 +1674,12 @@ namespace IW4MAdmin
_messageFormatter.BuildFormattedMessage(RconParser.Configuration, newPenalty));
await activeClient.CurrentServer.ExecuteCommandAsync(formattedString);
}
Manager.QueueEvent(new ClientPenaltyEvent
{
Client = targetClient,
Penalty = newPenalty
});
}
public override async Task Unban(string reason, EFClient targetClient, EFClient originClient)
@ -1596,6 +1701,12 @@ namespace IW4MAdmin
await Manager.GetPenaltyService().RemoveActivePenalties(targetClient.AliasLink.AliasLinkId,
targetClient.NetworkId, targetClient.GameName, targetClient.CurrentAlias?.IPAddress);
await Manager.GetPenaltyService().Create(unbanPenalty);
Manager.QueueEvent(new ClientPenaltyRevokeEvent
{
Client = targetClient,
Penalty = unbanPenalty
});
}
public override void InitializeTokens()
@ -1605,5 +1716,7 @@ namespace IW4MAdmin
Manager.GetMessageTokens().Add(new MessageToken("NEXTMAP", (Server s) => SharedLibraryCore.Commands.NextMapCommand.GetNextMap(s, _translationLookup)));
Manager.GetMessageTokens().Add(new MessageToken("ADMINS", (Server s) => Task.FromResult(ListAdminsCommand.OnlineAdmins(s, _translationLookup))));
}
public override long LegacyDatabaseId => _cachedDatabaseServer.ServerId;
}
}