Compare commits
12 Commits
2.2
...
2.3-prerel
Author | SHA1 | Date | |
---|---|---|---|
b77bdbe793 | |||
4522992c0e | |||
9d6cbee69c | |||
abf0609e2e | |||
5ac8a55c72 | |||
9bdd7d1b8a | |||
ed83c4c011 | |||
d9d548ea18 | |||
1779bf821d | |||
d50e6c8030 | |||
a58726d872 | |||
dded60a6ef |
4
.gitignore
vendored
4
.gitignore
vendored
@ -229,4 +229,6 @@ bootstrap-custom.min.css
|
||||
/DiscordWebhook/env
|
||||
/DiscordWebhook/config.dev.json
|
||||
/GameLogServer/env
|
||||
launchSettings.json
|
||||
launchSettings.json
|
||||
/VpnDetectionPrivate.js
|
||||
/Plugins/ScriptPlugins/VpnDetectionPrivate.js
|
||||
|
@ -8,7 +8,9 @@ namespace IW4MAdmin.Application.API.Master
|
||||
public class ApiServer
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public int Id { get; set; }
|
||||
public long Id { get; set; }
|
||||
[JsonProperty("ip")]
|
||||
public string IPAddress { get; set; }
|
||||
[JsonProperty("port")]
|
||||
public short Port { get; set; }
|
||||
[JsonProperty("gametype")]
|
||||
|
@ -43,8 +43,9 @@ namespace IW4MAdmin.Application.API.Master
|
||||
Hostname = s.Hostname,
|
||||
Map = s.CurrentMap.Name,
|
||||
MaxClientNum = s.MaxClients,
|
||||
Id = s.GetHashCode(),
|
||||
Port = (short)s.GetPort()
|
||||
Id = s.EndPoint,
|
||||
Port = (short)s.GetPort(),
|
||||
IPAddress = s.IP
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
|
||||
<MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
|
||||
<PackageId>RaidMax.IW4MAdmin.Application</PackageId>
|
||||
<Version>2.2</Version>
|
||||
<Version>2.2.2.2</Version>
|
||||
<Authors>RaidMax</Authors>
|
||||
<Company>Forever None</Company>
|
||||
<Product>IW4MAdmin</Product>
|
||||
@ -31,8 +31,8 @@
|
||||
<PropertyGroup>
|
||||
<ServerGarbageCollection>true</ServerGarbageCollection>
|
||||
<TieredCompilation>true</TieredCompilation>
|
||||
<AssemblyVersion>2.2.0.0</AssemblyVersion>
|
||||
<FileVersion>2.2.0.0</FileVersion>
|
||||
<AssemblyVersion>2.2.2.2</AssemblyVersion>
|
||||
<FileVersion>2.2.2.2</FileVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,82 +0,0 @@
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Objects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace IW4MAdmin.Application.Core
|
||||
{
|
||||
class ClientAuthentication : IClientAuthentication
|
||||
{
|
||||
private Queue<Player> ClientAuthenticationQueue;
|
||||
private Dictionary<long, Player> AuthenticatedClients;
|
||||
|
||||
public ClientAuthentication()
|
||||
{
|
||||
ClientAuthenticationQueue = new Queue<Player>();
|
||||
AuthenticatedClients = new Dictionary<long, Player>();
|
||||
}
|
||||
|
||||
public void AuthenticateClients(IList<Player> clients)
|
||||
{
|
||||
// we need to un-auth all the clients that have disconnected
|
||||
var clientNetworkIds = clients.Select(c => c.NetworkId);
|
||||
var clientsToRemove = AuthenticatedClients.Keys.Where(c => !clientNetworkIds.Contains(c));
|
||||
// remove them
|
||||
foreach (long Id in clientsToRemove.ToList())
|
||||
{
|
||||
AuthenticatedClients.Remove(Id);
|
||||
}
|
||||
|
||||
// loop through the polled clients to see if they've been authenticated yet
|
||||
foreach (var client in clients)
|
||||
{
|
||||
// they've not been authenticated
|
||||
if (!AuthenticatedClients.TryGetValue(client.NetworkId, out Player value))
|
||||
{
|
||||
// authenticate them
|
||||
client.State = Player.ClientState.Authenticated;
|
||||
AuthenticatedClients.Add(client.NetworkId, client);
|
||||
}
|
||||
else
|
||||
{
|
||||
// this update their ping
|
||||
// todo: this seems kinda hacky
|
||||
value.Ping = client.Ping;
|
||||
value.Score = client.Score;
|
||||
}
|
||||
}
|
||||
|
||||
// empty out the queue of clients detected through log
|
||||
while (ClientAuthenticationQueue.Count > 0)
|
||||
{
|
||||
// grab each client that's connected via log
|
||||
var clientToAuthenticate = ClientAuthenticationQueue.Dequeue();
|
||||
// if they're not already authed, auth them
|
||||
if (!AuthenticatedClients.TryGetValue(clientToAuthenticate.NetworkId, out Player value))
|
||||
{
|
||||
// authenticate them
|
||||
clientToAuthenticate.State = Player.ClientState.Authenticated;
|
||||
AuthenticatedClients.Add(clientToAuthenticate.NetworkId, clientToAuthenticate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IList<Player> GetAuthenticatedClients()
|
||||
{
|
||||
if (AuthenticatedClients.Values.Count > 18)
|
||||
{
|
||||
Program.ServerManager.GetLogger(0).WriteError($"auth client count is {AuthenticatedClients.Values.Count}, this is bad");
|
||||
return AuthenticatedClients.Values.Take(18).ToList();
|
||||
}
|
||||
|
||||
return AuthenticatedClients.Values.ToList();
|
||||
}
|
||||
|
||||
public void RequestClientAuthentication(Player client)
|
||||
{
|
||||
ClientAuthenticationQueue.Enqueue(client);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Objects;
|
||||
|
||||
namespace IW4MAdmin.Application.EventParsers
|
||||
{
|
||||
@ -20,7 +19,7 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
|
||||
if (eventType == "JoinTeam")
|
||||
{
|
||||
var origin = server.GetPlayersAsList().FirstOrDefault(c => c.NetworkId == lineSplit[1].ConvertLong());
|
||||
var origin = server.GetClientsAsList().FirstOrDefault(c => c.NetworkId == lineSplit[1].ConvertLong());
|
||||
|
||||
return new GameEvent()
|
||||
{
|
||||
@ -41,7 +40,7 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
.Replace("\x15", "")
|
||||
.Trim();
|
||||
|
||||
var origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2));
|
||||
var origin = server.GetClientsAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2));
|
||||
|
||||
if (message[0] == '!' || message[0] == '@')
|
||||
{
|
||||
@ -70,8 +69,8 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
{
|
||||
if (!server.CustomCallback)
|
||||
{
|
||||
var origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 6));
|
||||
var target = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2));
|
||||
var origin = server.GetClientsAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 6));
|
||||
var target = server.GetClientsAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2));
|
||||
|
||||
return new GameEvent()
|
||||
{
|
||||
@ -86,8 +85,8 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
|
||||
if (eventType == "ScriptKill")
|
||||
{
|
||||
var origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
|
||||
var target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong());
|
||||
var origin = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
|
||||
var target = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong());
|
||||
return new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.ScriptKill,
|
||||
@ -100,8 +99,8 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
|
||||
if (eventType == "ScriptDamage")
|
||||
{
|
||||
var origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
|
||||
var target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong());
|
||||
var origin = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
|
||||
var target = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong());
|
||||
|
||||
return new GameEvent()
|
||||
{
|
||||
@ -120,8 +119,8 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
{
|
||||
if (Regex.Match(eventType, @"^(D);((?:bot[0-9]+)|(?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$").Success)
|
||||
{
|
||||
var origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[5].ConvertLong());
|
||||
var target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
|
||||
var origin = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[5].ConvertLong());
|
||||
var target = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
|
||||
|
||||
return new GameEvent()
|
||||
{
|
||||
@ -143,41 +142,49 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
{
|
||||
return new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Join,
|
||||
Type = GameEvent.EventType.PreConnect,
|
||||
Data = logLine,
|
||||
Owner = server,
|
||||
Origin = new Player()
|
||||
Origin = new EFClient()
|
||||
{
|
||||
Name = regexMatch.Groups[4].ToString().StripColors(),
|
||||
CurrentAlias = new EFAlias()
|
||||
{
|
||||
Active = false,
|
||||
Name = regexMatch.Groups[4].ToString().StripColors(),
|
||||
},
|
||||
NetworkId = regexMatch.Groups[2].ToString().ConvertLong(),
|
||||
ClientNumber = Convert.ToInt32(regexMatch.Groups[3].ToString()),
|
||||
State = Player.ClientState.Connecting,
|
||||
State = EFClient.ClientState.Connecting,
|
||||
CurrentServer = server
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
//if (eventType == "Q")
|
||||
//{
|
||||
// var regexMatch = Regex.Match(logLine, @"^(Q;)(.{1,32});([0-9]+);(.*)$");
|
||||
// if (regexMatch.Success)
|
||||
// {
|
||||
// return new GameEvent()
|
||||
// {
|
||||
// Type = GameEvent.EventType.Quit,
|
||||
// Data = logLine,
|
||||
// Owner = server,
|
||||
// Origin = new Player()
|
||||
// {
|
||||
// Name = regexMatch.Groups[4].ToString().StripColors(),
|
||||
// NetworkId = regexMatch.Groups[2].ToString().ConvertLong(),
|
||||
// ClientNumber = Convert.ToInt32(regexMatch.Groups[3].ToString()),
|
||||
// State = Player.ClientState.Connecting
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
//}
|
||||
if (eventType == "Q")
|
||||
{
|
||||
var regexMatch = Regex.Match(logLine, @"^(Q;)(.{1,32});([0-9]+);(.*)$");
|
||||
if (regexMatch.Success)
|
||||
{
|
||||
return new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.PreDisconnect,
|
||||
Data = logLine,
|
||||
Owner = server,
|
||||
Origin = new EFClient()
|
||||
{
|
||||
CurrentAlias = new EFAlias()
|
||||
{
|
||||
Active = false,
|
||||
Name = regexMatch.Groups[4].ToString().StripColors()
|
||||
},
|
||||
NetworkId = regexMatch.Groups[2].ToString().ConvertLong(),
|
||||
ClientNumber = Convert.ToInt32(regexMatch.Groups[3].ToString()),
|
||||
State = EFClient.ClientState.Disconnecting
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (eventType.Contains("ExitLevel"))
|
||||
{
|
||||
@ -185,14 +192,8 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
{
|
||||
Type = GameEvent.EventType.MapEnd,
|
||||
Data = lineSplit[0],
|
||||
Origin = new Player()
|
||||
{
|
||||
ClientId = 1
|
||||
},
|
||||
Target = new Player()
|
||||
{
|
||||
ClientId = 1
|
||||
},
|
||||
Origin = Utilities.IW4MAdminClient(server),
|
||||
Target = Utilities.IW4MAdminClient(server),
|
||||
Owner = server
|
||||
};
|
||||
}
|
||||
@ -205,14 +206,8 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
{
|
||||
Type = GameEvent.EventType.MapChange,
|
||||
Data = lineSplit[0],
|
||||
Origin = new Player()
|
||||
{
|
||||
ClientId = 1
|
||||
},
|
||||
Target = new Player()
|
||||
{
|
||||
ClientId = 1
|
||||
},
|
||||
Origin = Utilities.IW4MAdminClient(server),
|
||||
Target = Utilities.IW4MAdminClient(server),
|
||||
Owner = server,
|
||||
Extra = dump.DictionaryFromKeyValue()
|
||||
};
|
||||
@ -221,19 +216,16 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
return new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Unknown,
|
||||
Origin = new Player()
|
||||
{
|
||||
ClientId = 1
|
||||
},
|
||||
Target = new Player()
|
||||
{
|
||||
ClientId = 1
|
||||
},
|
||||
Origin = Utilities.IW4MAdminClient(server),
|
||||
Target = Utilities.IW4MAdminClient(server),
|
||||
Owner = server
|
||||
};
|
||||
}
|
||||
|
||||
// other parsers can derive from this parser so we make it virtual
|
||||
public virtual string GetGameDir() => "userraw";
|
||||
public virtual string GetGameDir()
|
||||
{
|
||||
return "userraw";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,16 @@
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Objects;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace IW4MAdmin.Application.EventParsers
|
||||
{
|
||||
class IW5EventParser : IW4EventParser
|
||||
{
|
||||
public override string GetGameDir() => "logs";
|
||||
public override string GetGameDir()
|
||||
{
|
||||
return "logs";
|
||||
}
|
||||
|
||||
public override GameEvent GetEvent(Server server, string logLine)
|
||||
{
|
||||
@ -23,21 +22,25 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
|
||||
int clientNum = Int32.Parse(lineSplit[2]);
|
||||
|
||||
var player = new Player()
|
||||
var player = new EFClient()
|
||||
{
|
||||
NetworkId = lineSplit[1].ConvertLong(),
|
||||
ClientNumber = clientNum,
|
||||
Name = lineSplit[3]
|
||||
CurrentAlias = new EFAlias()
|
||||
{
|
||||
Active = false,
|
||||
Name = lineSplit[3]
|
||||
}
|
||||
};
|
||||
|
||||
return new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Join,
|
||||
Origin = new Player()
|
||||
Type = GameEvent.EventType.PreConnect,
|
||||
Origin = new EFClient()
|
||||
{
|
||||
ClientId = 1
|
||||
},
|
||||
Target = new Player()
|
||||
Target = new EFClient()
|
||||
{
|
||||
ClientId = 1
|
||||
},
|
||||
@ -47,7 +50,9 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
return base.GetEvent(server, logLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ namespace IW4MAdmin.Application.IO
|
||||
Server = server;
|
||||
}
|
||||
|
||||
public void PollForChanges()
|
||||
public async Task PollForChanges()
|
||||
{
|
||||
while (!Server.Manager.ShutdownRequested())
|
||||
{
|
||||
@ -45,12 +45,12 @@ namespace IW4MAdmin.Application.IO
|
||||
{
|
||||
try
|
||||
{
|
||||
UpdateLogEvents();
|
||||
await UpdateLogEvents();
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
Server.Logger.WriteWarning($"Failed to update log event for {Server.GetHashCode()}");
|
||||
Server.Logger.WriteWarning($"Failed to update log event for {Server.EndPoint}");
|
||||
Server.Logger.WriteDebug($"Exception: {e.Message}");
|
||||
Server.Logger.WriteDebug($"StackTrace: {e.StackTrace}");
|
||||
}
|
||||
@ -59,7 +59,7 @@ namespace IW4MAdmin.Application.IO
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLogEvents()
|
||||
private async Task UpdateLogEvents()
|
||||
{
|
||||
long fileSize = Reader.Length;
|
||||
|
||||
@ -74,11 +74,12 @@ namespace IW4MAdmin.Application.IO
|
||||
|
||||
PreviousFileSize = fileSize;
|
||||
|
||||
var events = Reader.ReadEventsFromLog(Server, fileDiff, 0);
|
||||
var events = await Reader.ReadEventsFromLog(Server, fileDiff, 0);
|
||||
|
||||
foreach (var ev in events)
|
||||
{
|
||||
Server.Manager.GetEventHandler().AddEvent(ev);
|
||||
await ev.WaitAsync();
|
||||
}
|
||||
|
||||
PreviousFileSize = fileSize;
|
||||
|
@ -4,6 +4,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IW4MAdmin.Application.IO
|
||||
{
|
||||
@ -22,7 +23,7 @@ namespace IW4MAdmin.Application.IO
|
||||
Parser = parser;
|
||||
}
|
||||
|
||||
public ICollection<GameEvent> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition)
|
||||
public async Task<ICollection<GameEvent>> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition)
|
||||
{
|
||||
// allocate the bytes for the new log lines
|
||||
List<string> logLines = new List<string>();
|
||||
@ -30,6 +31,7 @@ namespace IW4MAdmin.Application.IO
|
||||
// open the file as a stream
|
||||
using (var rd = new StreamReader(new FileStream(LogFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Utilities.EncodingType))
|
||||
{
|
||||
// todo: max async
|
||||
// take the old start position and go back the number of new characters
|
||||
rd.BaseStream.Seek(-fileSizeDiff, SeekOrigin.End);
|
||||
// the difference should be in the range of a int :P
|
||||
|
@ -5,6 +5,7 @@ using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using static SharedLibraryCore.Utilities;
|
||||
|
||||
namespace IW4MAdmin.Application.IO
|
||||
@ -27,20 +28,21 @@ namespace IW4MAdmin.Application.IO
|
||||
|
||||
public long Length => -1;
|
||||
|
||||
public int UpdateInterval => 1000;
|
||||
public int UpdateInterval => 350;
|
||||
|
||||
public ICollection<GameEvent> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition)
|
||||
public async Task<ICollection<GameEvent>> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition)
|
||||
{
|
||||
#if DEBUG == true
|
||||
server.Logger.WriteDebug($"Begin reading {fileSizeDiff} from http log");
|
||||
server.Logger.WriteDebug($"Begin reading from http log");
|
||||
#endif
|
||||
var events = new List<GameEvent>();
|
||||
string b64Path = server.LogPath.ToBase64UrlSafeString();
|
||||
var response = Api.Log(b64Path).Result;
|
||||
var response = await Api.Log(b64Path);
|
||||
|
||||
if (!response.Success)
|
||||
{
|
||||
server.Logger.WriteError($"Could not get log server info of {LogFile}/{b64Path} ({server.LogPath})");
|
||||
return events;
|
||||
}
|
||||
|
||||
// parse each line
|
||||
|
@ -1,24 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using IW4MAdmin.Application.EventParsers;
|
||||
using IW4MAdmin.Application.IO;
|
||||
using IW4MAdmin.Application.RconParsers;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Objects;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Exceptions;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Localization;
|
||||
|
||||
using IW4MAdmin.Application.RconParsers;
|
||||
using IW4MAdmin.Application.EventParsers;
|
||||
using IW4MAdmin.Application.IO;
|
||||
using SharedLibraryCore.Objects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IW4MAdmin
|
||||
{
|
||||
@ -26,268 +24,92 @@ namespace IW4MAdmin
|
||||
{
|
||||
private static readonly Index loc = Utilities.CurrentLocalization.LocalizationIndex;
|
||||
private GameLogEventDetection LogEvent;
|
||||
private DateTime SessionStart;
|
||||
public int Id { get; private set; }
|
||||
|
||||
public IW4MServer(IManager mgr, ServerConfiguration cfg) : base(mgr, cfg)
|
||||
{
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
override public async Task OnClientConnected(EFClient clientFromLog)
|
||||
{
|
||||
if (GameName == Game.IW4)
|
||||
{
|
||||
// todo: make this better with collisions
|
||||
int id = Math.Abs($"{IP}:{Port.ToString()}".Select(a => (int)a).Sum());
|
||||
|
||||
// hack: this is a nasty fix for get hashcode being changed
|
||||
switch (id)
|
||||
{
|
||||
case 765:
|
||||
return 886229536;
|
||||
case 760:
|
||||
return 1645744423;
|
||||
case 761:
|
||||
return 1645809959;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
int id = HashCode.Combine(IP, Port);
|
||||
return id < 0 ? Math.Abs(id) : id;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task OnPlayerJoined(Player logClient)
|
||||
{
|
||||
var existingClient = Players[logClient.ClientNumber];
|
||||
|
||||
if (existingClient == null ||
|
||||
(existingClient.NetworkId != logClient.NetworkId &&
|
||||
existingClient.State != Player.ClientState.Connected))
|
||||
{
|
||||
Logger.WriteDebug($"Log detected {logClient} joining");
|
||||
Players[logClient.ClientNumber] = logClient;
|
||||
}
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
override public async Task<bool> AddPlayer(Player polledPlayer)
|
||||
{
|
||||
if ((polledPlayer.Ping == 999 && !polledPlayer.IsBot) ||
|
||||
polledPlayer.Ping < 1 ||
|
||||
polledPlayer.ClientNumber < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// set this when they are waiting for authentication
|
||||
if (Players[polledPlayer.ClientNumber] == null &&
|
||||
polledPlayer.State == Player.ClientState.Connecting)
|
||||
{
|
||||
Players[polledPlayer.ClientNumber] = polledPlayer;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !DEBUG
|
||||
if (polledPlayer.Name.Length < 3)
|
||||
{
|
||||
Logger.WriteDebug($"Kicking {polledPlayer} because their name is too short");
|
||||
string formattedKick = String.Format(RconParser.GetCommandPrefixes().Kick, polledPlayer.ClientNumber, loc["SERVER_KICK_MINNAME"]);
|
||||
await this.ExecuteCommandAsync(formattedKick);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Players.FirstOrDefault(p => p != null && p.Name == polledPlayer.Name && p.NetworkId != polledPlayer.NetworkId) != null)
|
||||
{
|
||||
Logger.WriteDebug($"Kicking {polledPlayer} because their name is already in use");
|
||||
string formattedKick = String.Format(RconParser.GetCommandPrefixes().Kick, polledPlayer.ClientNumber, loc["SERVER_KICK_NAME_INUSE"]);
|
||||
await this.ExecuteCommandAsync(formattedKick);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (polledPlayer.Name == "Unknown Soldier" ||
|
||||
polledPlayer.Name == "UnknownSoldier" ||
|
||||
polledPlayer.Name == "CHEATER")
|
||||
{
|
||||
Logger.WriteDebug($"Kicking {polledPlayer} because their name is generic");
|
||||
string formattedKick = String.Format(RconParser.GetCommandPrefixes().Kick, polledPlayer.ClientNumber, loc["SERVER_KICK_GENERICNAME"]);
|
||||
await this.ExecuteCommandAsync(formattedKick);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (polledPlayer.Name.Where(c => Char.IsControl(c)).Count() > 0)
|
||||
{
|
||||
Logger.WriteDebug($"Kicking {polledPlayer} because their name contains control characters");
|
||||
string formattedKick = String.Format(RconParser.GetCommandPrefixes().Kick, polledPlayer.ClientNumber, loc["SERVER_KICK_CONTROLCHARS"]);
|
||||
await this.ExecuteCommandAsync(formattedKick);
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
Logger.WriteDebug($"Client slot #{polledPlayer.ClientNumber} now reserved");
|
||||
Logger.WriteDebug($"Client slot #{clientFromLog.ClientNumber} now reserved");
|
||||
|
||||
try
|
||||
{
|
||||
Player player = null;
|
||||
var client = await Manager.GetClientService().GetUnique(polledPlayer.NetworkId);
|
||||
EFClient client = await Manager.GetClientService().GetUnique(clientFromLog.NetworkId);
|
||||
|
||||
// first time client is connecting to server
|
||||
if (client == null)
|
||||
{
|
||||
Logger.WriteDebug($"Client {polledPlayer} first time connecting");
|
||||
player = (await Manager.GetClientService().Create(polledPlayer)).AsPlayer();
|
||||
Logger.WriteDebug($"Client {clientFromLog} first time connecting");
|
||||
client = await Manager.GetClientService().Create(clientFromLog);
|
||||
}
|
||||
|
||||
// client has connected in the past
|
||||
else
|
||||
{
|
||||
client.LastConnection = DateTime.UtcNow;
|
||||
client.Connections += 1;
|
||||
|
||||
var existingAlias = client.AliasLink.Children
|
||||
.FirstOrDefault(a => a.Name == polledPlayer.Name && a.IPAddress == polledPlayer.IPAddress);
|
||||
|
||||
if (existingAlias == null)
|
||||
{
|
||||
Logger.WriteDebug($"Client {polledPlayer} has connected previously under a different ip/name");
|
||||
client.CurrentAlias = new EFAlias()
|
||||
{
|
||||
IPAddress = polledPlayer.IPAddress,
|
||||
Name = polledPlayer.Name,
|
||||
};
|
||||
// we need to update their new ip and name to the virtual property
|
||||
client.Name = polledPlayer.Name;
|
||||
client.IPAddress = polledPlayer.IPAddress;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
client.CurrentAlias = existingAlias;
|
||||
client.CurrentAliasId = existingAlias.AliasId;
|
||||
client.Name = existingAlias.Name;
|
||||
client.IPAddress = existingAlias.IPAddress;
|
||||
}
|
||||
|
||||
await Manager.GetClientService().Update(client);
|
||||
player = client.AsPlayer();
|
||||
// this is only a temporary version until the IPAddress is transmitted
|
||||
client.CurrentAlias.Active = false;
|
||||
client.CurrentAlias.Name = clientFromLog.Name;
|
||||
}
|
||||
|
||||
// reserved slots stuff
|
||||
if ((MaxClients - ClientNum) < ServerConfig.ReservedSlotNumber &&
|
||||
!player.IsPrivileged())
|
||||
{
|
||||
Logger.WriteDebug($"Kicking {polledPlayer} their spot is reserved");
|
||||
string formattedKick = String.Format(RconParser.GetCommandPrefixes().Kick, polledPlayer.ClientNumber, loc["SERVER_KICK_SLOT_IS_RESERVED"]);
|
||||
await this.ExecuteCommandAsync(formattedKick);
|
||||
return false;
|
||||
}
|
||||
|
||||
Logger.WriteInfo($"Client {player} connected...");
|
||||
Logger.WriteInfo($"Client {client} connected...");
|
||||
|
||||
// Do the player specific stuff
|
||||
player.ClientNumber = polledPlayer.ClientNumber;
|
||||
player.IsBot = polledPlayer.IsBot;
|
||||
player.Score = polledPlayer.Score;
|
||||
player.CurrentServer = this;
|
||||
client.ClientNumber = clientFromLog.ClientNumber;
|
||||
client.IsBot = clientFromLog.IsBot;
|
||||
client.Score = clientFromLog.Score;
|
||||
client.Ping = clientFromLog.Ping;
|
||||
client.CurrentServer = this;
|
||||
|
||||
player.DelayedEvents = (Players[player.ClientNumber]?.DelayedEvents) ?? new Queue<GameEvent>();
|
||||
Players[player.ClientNumber] = player;
|
||||
Clients[client.ClientNumber] = client;
|
||||
|
||||
var activePenalties = await Manager.GetPenaltyService().GetActivePenaltiesAsync(player.AliasLinkId, player.IPAddress);
|
||||
var currentBan = activePenalties.FirstOrDefault(b => b.Expires > DateTime.UtcNow);
|
||||
var currentAutoFlag = activePenalties.Where(p => p.Type == Penalty.PenaltyType.Flag && p.PunisherId == 1)
|
||||
.Where(p => p.Active)
|
||||
.OrderByDescending(p => p.When)
|
||||
.FirstOrDefault();
|
||||
|
||||
// remove their auto flag status after a week
|
||||
if (player.Level == Player.Permission.Flagged &&
|
||||
currentAutoFlag != null &&
|
||||
(DateTime.Now - currentAutoFlag.When).TotalDays > 7)
|
||||
// this only happens if the preconnect event occurred from RCon polling
|
||||
if (clientFromLog.IPAddress.HasValue)
|
||||
{
|
||||
player.Level = Player.Permission.User;
|
||||
await client.OnJoin(clientFromLog.IPAddress);
|
||||
}
|
||||
|
||||
if (currentBan != null)
|
||||
client.OnConnect();
|
||||
|
||||
client.State = EFClient.ClientState.Connected;
|
||||
#if DEBUG == true
|
||||
Logger.WriteDebug($"End PreConnect for {client}");
|
||||
#endif
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Logger.WriteInfo($"Banned client {player} trying to connect...");
|
||||
var autoKickClient = Utilities.IW4MAdminClient(this);
|
||||
Origin = client,
|
||||
Owner = this,
|
||||
Type = GameEvent.EventType.Connect
|
||||
};
|
||||
|
||||
// the player is permanently banned
|
||||
if (currentBan.Type == Penalty.PenaltyType.Ban)
|
||||
{
|
||||
// don't store the kick message
|
||||
string formattedKick = String.Format(
|
||||
RconParser.GetCommandPrefixes().Kick,
|
||||
polledPlayer.ClientNumber,
|
||||
$"{loc["SERVER_BAN_PREV"]} {currentBan.Offense} ({loc["SERVER_BAN_APPEAL"]} {Website})");
|
||||
await this.ExecuteCommandAsync(formattedKick);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
string formattedKick = String.Format(
|
||||
RconParser.GetCommandPrefixes().Kick,
|
||||
polledPlayer.ClientNumber,
|
||||
$"{loc["SERVER_TB_REMAIN"]} ({(currentBan.Expires - DateTime.UtcNow).TimeSpanText()} {loc["WEBFRONT_PENALTY_TEMPLATE_REMAINING"]})");
|
||||
await this.ExecuteCommandAsync(formattedKick);
|
||||
}
|
||||
|
||||
// reban the "evading" guid
|
||||
if (player.Level != Player.Permission.Banned && currentBan.Type == Penalty.PenaltyType.Ban)
|
||||
{
|
||||
// hack: re apply the automated offense to the reban
|
||||
if (currentBan.AutomatedOffense != null)
|
||||
{
|
||||
autoKickClient.AdministeredPenalties.Add(new EFPenalty() { AutomatedOffense = currentBan.AutomatedOffense });
|
||||
}
|
||||
player.Ban($"{currentBan.Offense}", autoKickClient);
|
||||
}
|
||||
|
||||
// they didn't fully connect so empty their slot
|
||||
Players[player.ClientNumber] = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
player.State = Player.ClientState.Connected;
|
||||
return true;
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.WriteError($"{loc["SERVER_ERROR_ADDPLAYER"]} {polledPlayer.Name}::{polledPlayer.NetworkId}");
|
||||
Logger.WriteDebug(ex.Message);
|
||||
Logger.WriteDebug(ex.StackTrace);
|
||||
return false;
|
||||
Logger.WriteError($"{loc["SERVER_ERROR_ADDPLAYER"]} {clientFromLog}");
|
||||
Logger.WriteError(ex.GetExceptionInfo());
|
||||
}
|
||||
}
|
||||
|
||||
//Remove player by CLIENT NUMBER
|
||||
override public async Task RemovePlayer(int cNum)
|
||||
override public async Task OnClientDisconnected(EFClient client)
|
||||
{
|
||||
if (cNum >= 0 && Players[cNum] != null)
|
||||
Logger.WriteInfo($"Client {client} [{client.State.ToString().ToLower()}] disconnecting...");
|
||||
await client.OnDisconnect();
|
||||
Clients[client.ClientNumber] = null;
|
||||
#if DEBUG == true
|
||||
Logger.WriteDebug($"End PreDisconnect for {client}");
|
||||
#endif
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Player Leaving = Players[cNum];
|
||||
Origin = client,
|
||||
Owner = this,
|
||||
Type = GameEvent.EventType.Disconnect
|
||||
};
|
||||
|
||||
// occurs when the player disconnects via log before being authenticated by RCon
|
||||
if (Leaving.State != Player.ClientState.Connected)
|
||||
{
|
||||
Players[cNum] = null;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
Logger.WriteInfo($"Client {Leaving} [{Leaving.State.ToString().ToLower()}] disconnecting...");
|
||||
Leaving.State = Player.ClientState.Disconnecting;
|
||||
Leaving.TotalConnectionTime += Leaving.ConnectionLength;
|
||||
Leaving.LastConnection = DateTime.UtcNow;
|
||||
await Manager.GetClientService().Update(Leaving);
|
||||
Players[cNum] = null;
|
||||
}
|
||||
}
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
}
|
||||
|
||||
public override async Task ExecuteEvent(GameEvent E)
|
||||
@ -340,7 +162,7 @@ namespace IW4MAdmin
|
||||
if (E.Type == GameEvent.EventType.Command &&
|
||||
E.Extra != null &&
|
||||
(canExecuteCommand ||
|
||||
E.Origin?.Level == Player.Permission.Console))
|
||||
E.Origin?.Level == EFClient.Permission.Console))
|
||||
{
|
||||
await (((Command)E.Extra).ExecuteAsync(E));
|
||||
}
|
||||
@ -353,35 +175,46 @@ namespace IW4MAdmin
|
||||
/// <returns></returns>
|
||||
override protected async Task<bool> ProcessEvent(GameEvent E)
|
||||
{
|
||||
if (E.Type == GameEvent.EventType.Connect)
|
||||
if (E.Type == GameEvent.EventType.ChangePermission)
|
||||
{
|
||||
E.Origin.State = Player.ClientState.Authenticated;
|
||||
// add them to the server
|
||||
if (!await AddPlayer(E.Origin))
|
||||
if (!E.Target.IsPrivileged())
|
||||
{
|
||||
E.Origin.State = Player.ClientState.Connecting;
|
||||
Logger.WriteDebug("client didn't pass authentication, so we are discontinuing event");
|
||||
return false;
|
||||
// remove banned or demoted privileged user
|
||||
Manager.GetPrivilegedClients().Remove(E.Target.ClientId);
|
||||
}
|
||||
// hack: makes the event propgate with the correct info
|
||||
E.Origin = Players[E.Origin.ClientNumber];
|
||||
|
||||
ChatHistory.Add(new ChatInfo()
|
||||
else
|
||||
{
|
||||
Name = E.Origin?.Name ?? "ERROR!",
|
||||
Message = "CONNECTED",
|
||||
Time = DateTime.UtcNow
|
||||
});
|
||||
|
||||
if (E.Origin.Level > Player.Permission.Moderator)
|
||||
{
|
||||
E.Origin.Tell(string.Format(loc["SERVER_REPORT_COUNT"], E.Owner.Reports.Count));
|
||||
Manager.GetPrivilegedClients()[E.Target.ClientId] = E.Target;
|
||||
}
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.Join)
|
||||
else if (E.Type == GameEvent.EventType.PreConnect)
|
||||
{
|
||||
await OnPlayerJoined(E.Origin);
|
||||
if (Clients[E.Origin.ClientNumber] == null)
|
||||
{
|
||||
#if DEBUG == true
|
||||
Logger.WriteDebug($"Begin PreConnect for {E.Origin}");
|
||||
#endif
|
||||
await OnClientConnected(E.Origin);
|
||||
|
||||
ChatHistory.Add(new ChatInfo()
|
||||
{
|
||||
Name = E.Origin.Name,
|
||||
Message = "CONNECTED",
|
||||
Time = DateTime.UtcNow
|
||||
});
|
||||
|
||||
if (E.Origin.Level > EFClient.Permission.Moderator)
|
||||
{
|
||||
E.Origin.Tell(string.Format(loc["SERVER_REPORT_COUNT"], E.Owner.Reports.Count));
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.Flag)
|
||||
@ -446,13 +279,9 @@ namespace IW4MAdmin
|
||||
|
||||
else if (E.Type == GameEvent.EventType.Quit)
|
||||
{
|
||||
var origin = Players.FirstOrDefault(p => p != null && p.NetworkId == E.Origin.NetworkId);
|
||||
var origin = GetClientsAsList().FirstOrDefault(_client => _client.NetworkId.Equals(E.Origin));
|
||||
|
||||
if (origin != null &&
|
||||
// we only want to forward the event if they are connected.
|
||||
origin.State == Player.ClientState.Connected &&
|
||||
// make sure we don't get the disconnect event from every time the game ends
|
||||
origin.ConnectionLength < Manager.GetApplicationSettings().Configuration().RConPollRate)
|
||||
if (origin != null)
|
||||
{
|
||||
var e = new GameEvent()
|
||||
{
|
||||
@ -464,29 +293,52 @@ namespace IW4MAdmin
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
}
|
||||
|
||||
else if (origin != null &&
|
||||
origin.State != Player.ClientState.Connected)
|
||||
else
|
||||
{
|
||||
await RemovePlayer(origin.ClientNumber);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.Disconnect)
|
||||
else if (E.Type == GameEvent.EventType.PreDisconnect)
|
||||
{
|
||||
ChatHistory.Add(new ChatInfo()
|
||||
if ((DateTime.UtcNow - SessionStart).TotalSeconds < 5)
|
||||
{
|
||||
Name = E.Origin.Name,
|
||||
Message = "DISCONNECTED",
|
||||
Time = DateTime.UtcNow
|
||||
});
|
||||
|
||||
var currentState = E.Origin.State;
|
||||
await RemovePlayer(E.Origin.ClientNumber);
|
||||
|
||||
if (currentState != Player.ClientState.Connected)
|
||||
{
|
||||
throw new ServerException("Disconnecting player was not in a connected state");
|
||||
Logger.WriteInfo($"Cancelling pre disconnect for {E.Origin} as it occured too close to map end");
|
||||
E.FailReason = GameEvent.EventFailReason.Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
// predisconnect comes from minimal rcon polled players and minimal log players
|
||||
// so we need to disconnect the "full" version of the client
|
||||
var client = GetClientsAsList().FirstOrDefault(_client => _client.Equals(E.Origin));
|
||||
|
||||
if (client != null)
|
||||
{
|
||||
#if DEBUG == true
|
||||
Logger.WriteDebug($"Begin PreDisconnect for {client}");
|
||||
#endif
|
||||
ChatHistory.Add(new ChatInfo()
|
||||
{
|
||||
Name = client.Name,
|
||||
Message = "DISCONNECTED",
|
||||
Time = DateTime.UtcNow
|
||||
});
|
||||
|
||||
await OnClientDisconnected(client);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.Update)
|
||||
{
|
||||
#if DEBUG == true
|
||||
Logger.WriteDebug($"Begin Update for {E.Origin}");
|
||||
#endif
|
||||
await OnClientUpdate(E.Origin);
|
||||
}
|
||||
|
||||
if (E.Type == GameEvent.EventType.Say)
|
||||
@ -550,6 +402,7 @@ namespace IW4MAdmin
|
||||
if (E.Type == GameEvent.EventType.MapEnd)
|
||||
{
|
||||
Logger.WriteInfo("Game ending...");
|
||||
SessionStart = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
if (E.Type == GameEvent.EventType.Tell)
|
||||
@ -559,54 +412,84 @@ namespace IW4MAdmin
|
||||
|
||||
if (E.Type == GameEvent.EventType.Broadcast)
|
||||
{
|
||||
#if DEBUG == false
|
||||
// this is a little ugly but I don't want to change the abstract class
|
||||
if (E.Data != null)
|
||||
{
|
||||
await E.Owner.ExecuteCommandAsync(E.Data);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
while (ChatHistory.Count > Math.Ceiling((double)ClientNum / 2))
|
||||
ChatHistory.RemoveAt(0);
|
||||
lock (ChatHistory)
|
||||
{
|
||||
while (ChatHistory.Count > Math.Ceiling(ClientNum / 2.0))
|
||||
{
|
||||
ChatHistory.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
// the last client hasn't fully disconnected yet
|
||||
// so there will still be at least 1 client left
|
||||
if (ClientNum < 2)
|
||||
{
|
||||
ChatHistory.Clear();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Task OnClientUpdate(EFClient origin)
|
||||
{
|
||||
var client = Clients[origin.ClientNumber];
|
||||
|
||||
if (client != null)
|
||||
{
|
||||
client.Ping = origin.Ping;
|
||||
client.Score = origin.Score;
|
||||
|
||||
// update their IP if it hasn't been set yet
|
||||
if (!client.IPAddress.HasValue)
|
||||
{
|
||||
return client.OnJoin(origin.IPAddress);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// lists the connecting and disconnecting clients via RCon response
|
||||
/// array index 0 = connecting clients
|
||||
/// array index 1 = disconnecting clients
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
async Task<IList<Player>[]> PollPlayersAsync()
|
||||
async Task<IList<EFClient>[]> PollPlayersAsync()
|
||||
{
|
||||
#if DEBUG
|
||||
var now = DateTime.Now;
|
||||
#endif
|
||||
var currentClients = GetPlayersAsList();
|
||||
var polledClients = await this.GetStatusAsync();
|
||||
var currentClients = GetClientsAsList();
|
||||
var polledClients = (await this.GetStatusAsync()).AsEnumerable();
|
||||
if (this.Manager.GetApplicationSettings().Configuration().IgnoreBots)
|
||||
{
|
||||
polledClients = polledClients.Where(c => !c.IsBot);
|
||||
}
|
||||
#if DEBUG
|
||||
Logger.WriteInfo($"Polling players took {(DateTime.Now - now).TotalMilliseconds}ms");
|
||||
#endif
|
||||
Throttled = false;
|
||||
|
||||
foreach (var client in polledClients)
|
||||
{
|
||||
// todo: move out somehwere
|
||||
var existingClient = Players[client.ClientNumber] ?? client;
|
||||
existingClient.Ping = client.Ping;
|
||||
existingClient.Score = client.Score;
|
||||
}
|
||||
|
||||
var disconnectingClients = currentClients.Except(polledClients);
|
||||
var connectingClients = polledClients.Except(currentClients.Where(c => c.State == Player.ClientState.Connected));
|
||||
var connectingClients = polledClients.Except(currentClients);
|
||||
var updatedClients = polledClients.Except(connectingClients).Except(disconnectingClients);
|
||||
|
||||
return new List<Player>[] { connectingClients.ToList(), disconnectingClients.ToList() };
|
||||
return new List<EFClient>[]
|
||||
{
|
||||
connectingClients.ToList(),
|
||||
disconnectingClients.ToList(),
|
||||
updatedClients.ToList()
|
||||
};
|
||||
}
|
||||
|
||||
DateTime start = DateTime.Now;
|
||||
@ -617,19 +500,30 @@ namespace IW4MAdmin
|
||||
{
|
||||
try
|
||||
{
|
||||
#region SHUTDOWN
|
||||
if (Manager.ShutdownRequested())
|
||||
{
|
||||
// todo: fix up disconnect
|
||||
//for (int i = 0; i < Players.Count; i++)
|
||||
// await RemovePlayer(i);
|
||||
foreach (var client in GetClientsAsList())
|
||||
{
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.PreDisconnect,
|
||||
Origin = client,
|
||||
Owner = this,
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
await e.WaitAsync();
|
||||
}
|
||||
|
||||
foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
|
||||
{
|
||||
await plugin.OnUnloadAsync();
|
||||
}
|
||||
}
|
||||
|
||||
// only check every 2 minutes if the server doesn't seem to be responding
|
||||
/* if ((DateTime.Now - LastPoll).TotalMinutes < 0.5 && ConnectionErrors >= 1)
|
||||
return true;*/
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
try
|
||||
{
|
||||
@ -638,14 +532,14 @@ namespace IW4MAdmin
|
||||
|
||||
foreach (var disconnectingClient in polledClients[1])
|
||||
{
|
||||
if (disconnectingClient.State == Player.ClientState.Disconnecting)
|
||||
if (disconnectingClient.State == EFClient.ClientState.Disconnecting)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Disconnect,
|
||||
Type = GameEvent.EventType.PreDisconnect,
|
||||
Origin = disconnectingClient,
|
||||
Owner = this
|
||||
};
|
||||
@ -656,22 +550,15 @@ namespace IW4MAdmin
|
||||
waiterList.Add(e);
|
||||
}
|
||||
// wait for all the disconnect tasks to finish
|
||||
await Task.WhenAll(waiterList.Select(e => e.WaitAsync()));
|
||||
await Task.WhenAll(waiterList.Select(e => e.WaitAsync(10 * 1000)));
|
||||
|
||||
waiterList.Clear();
|
||||
// this are our new connecting clients
|
||||
foreach (var client in polledClients[0])
|
||||
{
|
||||
// this prevents duplicate events from being sent to the event api
|
||||
if (GetPlayersAsList().Count(c => c.NetworkId == client.NetworkId &&
|
||||
c.State == Player.ClientState.Connected) != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Connect,
|
||||
Type = GameEvent.EventType.PreConnect,
|
||||
Origin = client,
|
||||
Owner = this
|
||||
};
|
||||
@ -681,13 +568,31 @@ namespace IW4MAdmin
|
||||
}
|
||||
|
||||
// wait for all the connect tasks to finish
|
||||
await Task.WhenAll(waiterList.Select(e => e.WaitAsync()));
|
||||
await Task.WhenAll(waiterList.Select(e => e.WaitAsync(10 * 1000)));
|
||||
|
||||
waiterList.Clear();
|
||||
// these are the clients that have updated
|
||||
foreach (var client in polledClients[2])
|
||||
{
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Update,
|
||||
Origin = client,
|
||||
Owner = this
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
waiterList.Add(e);
|
||||
}
|
||||
|
||||
await Task.WhenAll(waiterList.Select(e => e.WaitAsync(10 * 1000)));
|
||||
|
||||
if (ConnectionErrors > 0)
|
||||
{
|
||||
Logger.WriteVerbose($"{loc["MANAGER_CONNECTION_REST"]} {IP}:{Port}");
|
||||
Throttled = false;
|
||||
}
|
||||
|
||||
ConnectionErrors = 0;
|
||||
LastPoll = DateTime.Now;
|
||||
}
|
||||
@ -695,7 +600,7 @@ namespace IW4MAdmin
|
||||
catch (NetworkException e)
|
||||
{
|
||||
ConnectionErrors++;
|
||||
if (ConnectionErrors == 1)
|
||||
if (ConnectionErrors == 3)
|
||||
{
|
||||
Logger.WriteError($"{e.Message} {IP}:{Port}, {loc["SERVER_ERROR_POLLING"]}");
|
||||
Logger.WriteDebug($"Internal Exception: {e.Data["internal_exception"]}");
|
||||
@ -707,26 +612,15 @@ namespace IW4MAdmin
|
||||
LastMessage = DateTime.Now - start;
|
||||
lastCount = DateTime.Now;
|
||||
|
||||
// todo: re-enable on tick
|
||||
/*
|
||||
if ((DateTime.Now - tickTime).TotalMilliseconds >= 1000)
|
||||
{
|
||||
foreach (var Plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
|
||||
{
|
||||
if (cts.IsCancellationRequested)
|
||||
break;
|
||||
|
||||
await Plugin.OnTickAsync(this);
|
||||
}
|
||||
tickTime = DateTime.Now;
|
||||
}*/
|
||||
|
||||
// update the player history
|
||||
if ((lastCount - playerCountStart).TotalMinutes >= SharedLibraryCore.Helpers.PlayerHistory.UpdateInterval)
|
||||
{
|
||||
while (PlayerHistory.Count > ((60 / SharedLibraryCore.Helpers.PlayerHistory.UpdateInterval) * 12)) // 12 times a hour for 12 hours
|
||||
PlayerHistory.Dequeue();
|
||||
PlayerHistory.Enqueue(new SharedLibraryCore.Helpers.PlayerHistory(ClientNum));
|
||||
while (ClientHistory.Count > ((60 / SharedLibraryCore.Helpers.PlayerHistory.UpdateInterval) * 12)) // 12 times a hour for 12 hours
|
||||
{
|
||||
ClientHistory.Dequeue();
|
||||
}
|
||||
|
||||
ClientHistory.Enqueue(new SharedLibraryCore.Helpers.PlayerHistory(ClientNum));
|
||||
playerCountStart = DateTime.Now;
|
||||
}
|
||||
|
||||
@ -776,7 +670,9 @@ namespace IW4MAdmin
|
||||
new IW3RConParser();
|
||||
|
||||
if (ServerConfig.UseIW5MParser)
|
||||
{
|
||||
RconParser = new IW5MRConParser();
|
||||
}
|
||||
|
||||
var version = await this.GetDvarAsync<string>("version");
|
||||
GameName = Utilities.GetGame(version.Value);
|
||||
@ -787,16 +683,26 @@ namespace IW4MAdmin
|
||||
RconParser = new IW4RConParser();
|
||||
}
|
||||
else if (GameName == Game.IW5)
|
||||
{
|
||||
EventParser = new IW5EventParser();
|
||||
}
|
||||
else if (GameName == Game.T5M)
|
||||
{
|
||||
EventParser = new T5MEventParser();
|
||||
}
|
||||
else if (GameName == Game.T6M)
|
||||
{
|
||||
EventParser = new T6MEventParser();
|
||||
}
|
||||
else
|
||||
{
|
||||
EventParser = new IW3EventParser(); // this uses the 'main' folder for log paths
|
||||
}
|
||||
|
||||
if (GameName == Game.UKN)
|
||||
{
|
||||
Logger.WriteWarning($"Game name not recognized: {version}");
|
||||
}
|
||||
|
||||
var infoResponse = await this.GetInfoAsync();
|
||||
// this is normally slow, but I'm only doing it because different games have different prefixes
|
||||
@ -820,6 +726,7 @@ namespace IW4MAdmin
|
||||
infoResponse["fs_game"];
|
||||
var logfile = await this.GetDvarAsync<string>("g_log");
|
||||
var logsync = await this.GetDvarAsync<int>("g_logsync");
|
||||
var ip = await this.GetDvarAsync<string>("net_ip");
|
||||
|
||||
WorkingDirectory = basepath.Value;
|
||||
|
||||
@ -841,6 +748,8 @@ namespace IW4MAdmin
|
||||
this.MaxClients = maxplayers;
|
||||
this.FSGame = game;
|
||||
this.Gametype = gametype;
|
||||
this.IP = ip.Value == "localhost" ? ServerConfig.IPAddress : ip.Value;
|
||||
|
||||
if (logsync.Value == 0 || logfile.Value == string.Empty)
|
||||
{
|
||||
// this DVAR isn't set until the a map is loaded
|
||||
@ -855,7 +764,7 @@ namespace IW4MAdmin
|
||||
CustomCallback = await ScriptLoaded();
|
||||
string mainPath = EventParser.GetGameDir();
|
||||
#if DEBUG
|
||||
// basepath.Value = @"D:\";
|
||||
// basepath.Value = @"D:\";
|
||||
#endif
|
||||
string logPath = string.Empty;
|
||||
|
||||
@ -900,7 +809,7 @@ namespace IW4MAdmin
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override async Task Warn(String Reason, Player Target, Player Origin)
|
||||
protected override async Task Warn(String Reason, EFClient Target, EFClient Origin)
|
||||
{
|
||||
// ensure player gets warned if command not performed on them in game
|
||||
if (Target.ClientNumber < 0)
|
||||
@ -942,7 +851,7 @@ namespace IW4MAdmin
|
||||
await Manager.GetPenaltyService().Create(newPenalty);
|
||||
}
|
||||
|
||||
protected override async Task Kick(String Reason, Player Target, Player Origin)
|
||||
protected override async Task Kick(String Reason, EFClient Target, EFClient Origin)
|
||||
{
|
||||
// ensure player gets kicked if command not performed on them in game
|
||||
if (Target.ClientNumber < 0)
|
||||
@ -965,7 +874,7 @@ namespace IW4MAdmin
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
await Target.CurrentServer.RemovePlayer(Target.ClientNumber);
|
||||
await Target.CurrentServer.OnClientDisconnected(Target);
|
||||
#endif
|
||||
|
||||
var newPenalty = new Penalty()
|
||||
@ -982,7 +891,7 @@ namespace IW4MAdmin
|
||||
await Manager.GetPenaltyService().Create(newPenalty);
|
||||
}
|
||||
|
||||
protected override async Task TempBan(String Reason, TimeSpan length, Player Target, Player Origin)
|
||||
protected override async Task TempBan(String Reason, TimeSpan length, EFClient Target, EFClient Origin)
|
||||
{
|
||||
// ensure player gets banned if command not performed on them in game
|
||||
if (Target.ClientNumber < 0)
|
||||
@ -1003,7 +912,7 @@ namespace IW4MAdmin
|
||||
await Target.CurrentServer.ExecuteCommandAsync(formattedKick);
|
||||
}
|
||||
#else
|
||||
await Target.CurrentServer.RemovePlayer(Target.ClientNumber);
|
||||
await Target.CurrentServer.OnClientDisconnected(Target);
|
||||
#endif
|
||||
|
||||
Penalty newPenalty = new Penalty()
|
||||
@ -1021,16 +930,16 @@ namespace IW4MAdmin
|
||||
await Manager.GetPenaltyService().Create(newPenalty);
|
||||
}
|
||||
|
||||
override protected async Task Ban(String Message, Player Target, Player Origin)
|
||||
override protected async Task Ban(String Message, EFClient Target, EFClient Origin)
|
||||
{
|
||||
// ensure player gets banned if command not performed on them in game
|
||||
if (Target.ClientNumber < 0)
|
||||
{
|
||||
Player ingameClient = null;
|
||||
EFClient ingameClient = null;
|
||||
|
||||
ingameClient = Manager.GetServers()
|
||||
.Select(s => s.GetPlayersAsList())
|
||||
.FirstOrDefault(l => l.FirstOrDefault(c => c.ClientId == Target.ClientId) != null)
|
||||
.Select(s => s.GetClientsAsList())
|
||||
.FirstOrDefault(l => l.FirstOrDefault(c => c.ClientId == Target?.ClientId) != null)
|
||||
?.First(c => c.ClientId == Target.ClientId);
|
||||
|
||||
if (ingameClient != null)
|
||||
@ -1043,40 +952,38 @@ namespace IW4MAdmin
|
||||
else
|
||||
{
|
||||
// this is set only because they're still in the server.
|
||||
Target.Level = Player.Permission.Banned;
|
||||
Target.Level = EFClient.Permission.Banned;
|
||||
|
||||
#if !DEBUG
|
||||
string formattedString = String.Format(RconParser.GetCommandPrefixes().Kick, Target.ClientNumber, $"{loc["SERVER_BAN_TEXT"]} - ^5{Message} ^7({loc["SERVER_BAN_APPEAL"]} {Website})^7");
|
||||
await Target.CurrentServer.ExecuteCommandAsync(formattedString);
|
||||
#else
|
||||
await Target.CurrentServer.RemovePlayer(Target.ClientNumber);
|
||||
await Target.CurrentServer.OnClientDisconnected(Target);
|
||||
#endif
|
||||
}
|
||||
|
||||
Penalty newPenalty = new Penalty()
|
||||
{
|
||||
Type = Penalty.PenaltyType.Ban,
|
||||
Expires = DateTime.MaxValue,
|
||||
Expires = null,
|
||||
Offender = Target,
|
||||
Offense = Message,
|
||||
Punisher = Origin,
|
||||
Active = true,
|
||||
When = DateTime.UtcNow,
|
||||
Link = Target.AliasLink,
|
||||
AutomatedOffense = Origin.AdministeredPenalties.FirstOrDefault()?.AutomatedOffense
|
||||
AutomatedOffense = Origin.AdministeredPenalties?.FirstOrDefault()?.AutomatedOffense
|
||||
};
|
||||
|
||||
await Manager.GetPenaltyService().Create(newPenalty);
|
||||
// prevent them from logging in again
|
||||
Manager.GetPrivilegedClients().Remove(Target.ClientId);
|
||||
}
|
||||
|
||||
override public async Task Unban(string reason, Player Target, Player Origin)
|
||||
override public async Task Unban(string reason, EFClient Target, EFClient Origin)
|
||||
{
|
||||
var unbanPenalty = new Penalty()
|
||||
{
|
||||
Type = Penalty.PenaltyType.Unban,
|
||||
Expires = DateTime.UtcNow,
|
||||
Expires = null,
|
||||
Offender = Target,
|
||||
Offense = reason,
|
||||
Punisher = Origin,
|
@ -39,6 +39,8 @@ namespace IW4MAdmin.Application
|
||||
|
||||
try
|
||||
{
|
||||
ServerManager = ApplicationManager.GetInstance();
|
||||
Localization.Configure.Initialize(ServerManager.GetApplicationSettings().Configuration()?.CustomLocale);
|
||||
loc = Utilities.CurrentLocalization.LocalizationIndex;
|
||||
Console.CancelKeyPress += new ConsoleCancelEventHandler(OnCancelKey);
|
||||
|
||||
@ -47,10 +49,6 @@ namespace IW4MAdmin.Application
|
||||
// todo: move out
|
||||
ConfigurationMigration.MoveConfigFolder10518(null);
|
||||
|
||||
ServerManager = ApplicationManager.GetInstance();
|
||||
Localization.Configure.Initialize(ServerManager.GetApplicationSettings().Configuration()?.CustomLocale);
|
||||
|
||||
|
||||
ServerManager.Logger.WriteInfo($"Version is {Version}");
|
||||
|
||||
var api = API.Master.Endpoint.Get();
|
||||
@ -112,7 +110,7 @@ namespace IW4MAdmin.Application
|
||||
var consoleTask = Task.Run(async () =>
|
||||
{
|
||||
String userInput;
|
||||
Player Origin = Utilities.IW4MAdminClient(ServerManager.Servers[0]);
|
||||
var Origin = Utilities.IW4MAdminClient(ServerManager.Servers[0]);
|
||||
|
||||
do
|
||||
{
|
||||
|
@ -1,24 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
|
||||
using IW4MAdmin.Application.API.Master;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Commands;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Exceptions;
|
||||
using SharedLibraryCore.Objects;
|
||||
using SharedLibraryCore.Services;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Database;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Events;
|
||||
|
||||
using IW4MAdmin.Application.API.Master;
|
||||
using IW4MAdmin.Application.Migration;
|
||||
using SharedLibraryCore.Exceptions;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IW4MAdmin.Application
|
||||
{
|
||||
@ -26,7 +23,7 @@ namespace IW4MAdmin.Application
|
||||
{
|
||||
private List<Server> _servers;
|
||||
public List<Server> Servers => _servers.OrderByDescending(s => s.ClientNum).ToList();
|
||||
public Dictionary<int, Player> PrivilegedClients { get; set; }
|
||||
public Dictionary<int, EFClient> PrivilegedClients { get; set; }
|
||||
public ILogger Logger => GetLogger(0);
|
||||
public bool Running { get; private set; }
|
||||
public bool IsInitialized { get; private set; }
|
||||
@ -49,7 +46,7 @@ namespace IW4MAdmin.Application
|
||||
ManualResetEventSlim OnQuit;
|
||||
readonly IPageList PageList;
|
||||
readonly SemaphoreSlim ProcessingEvent = new SemaphoreSlim(1, 1);
|
||||
readonly Dictionary<int, ILogger> Loggers = new Dictionary<int, ILogger>();
|
||||
readonly Dictionary<long, ILogger> Loggers = new Dictionary<long, ILogger>();
|
||||
|
||||
private ApplicationManager()
|
||||
{
|
||||
@ -60,7 +57,6 @@ namespace IW4MAdmin.Application
|
||||
ClientSvc = new ClientService();
|
||||
AliasSvc = new AliasService();
|
||||
PenaltySvc = new PenaltyService();
|
||||
PrivilegedClients = new Dictionary<int, Player>();
|
||||
ConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings");
|
||||
StartTime = DateTime.UtcNow;
|
||||
OnQuit = new ManualResetEventSlim();
|
||||
@ -85,80 +81,11 @@ namespace IW4MAdmin.Application
|
||||
|
||||
try
|
||||
{
|
||||
// if the origin client is not in an authorized state (detected by RCon) don't execute the event
|
||||
if (GameEvent.ShouldOriginEventBeDelayed(newEvent))
|
||||
{
|
||||
Logger.WriteDebug($"Delaying origin execution of event type {newEvent.Type} for {newEvent.Origin} because they are not authed");
|
||||
if (newEvent.Type == GameEvent.EventType.Command)
|
||||
{
|
||||
newEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["SERVER_DELAYED_EVENT_WAIT"]);
|
||||
}
|
||||
await newEvent.Owner.ExecuteEvent(newEvent);
|
||||
|
||||
// offload it to the player to keep
|
||||
newEvent.Origin.DelayedEvents.Enqueue(newEvent);
|
||||
}
|
||||
|
||||
// if the target client is not in an authorized state (detected by RCon) don't execute the event
|
||||
else if (GameEvent.ShouldTargetEventBeDelayed(newEvent))
|
||||
{
|
||||
Logger.WriteDebug($"Delaying target execution of event type {newEvent.Type} for {newEvent.Target} because they are not authed");
|
||||
// offload it to the player to keep
|
||||
newEvent.Target.DelayedEvents.Enqueue(newEvent);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
|
||||
await newEvent.Owner.ExecuteEvent(newEvent);
|
||||
|
||||
// save the event info to the database
|
||||
var changeHistorySvc = new ChangeHistoryService();
|
||||
await changeHistorySvc.Add(args.Event);
|
||||
|
||||
// todo: this is a hacky mess
|
||||
if (newEvent.Origin?.DelayedEvents.Count > 0 &&
|
||||
(//newEvent.Origin?.State == Player.ClientState.Connected ||
|
||||
newEvent.Type == GameEvent.EventType.Connect))
|
||||
{
|
||||
var events = newEvent.Origin.DelayedEvents;
|
||||
|
||||
// add the delayed event to the queue
|
||||
while (events.Count > 0)
|
||||
{
|
||||
var oldEvent = events.Dequeue();
|
||||
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = oldEvent.Type,
|
||||
Origin = newEvent.Origin,
|
||||
Data = oldEvent.Data,
|
||||
Extra = oldEvent.Extra,
|
||||
Owner = oldEvent.Owner,
|
||||
Message = oldEvent.Message,
|
||||
Target = oldEvent.Target,
|
||||
Remote = oldEvent.Remote
|
||||
};
|
||||
|
||||
e.Origin = newEvent.Origin;
|
||||
// check if the target was assigned
|
||||
if (e.Target != null)
|
||||
{
|
||||
// update the target incase they left or have newer info
|
||||
e.Target = newEvent.Owner.GetPlayersAsList()
|
||||
.FirstOrDefault(p => p.NetworkId == e.Target.NetworkId);
|
||||
// we have to throw out the event because they left
|
||||
if (e.Target == null)
|
||||
{
|
||||
Logger.WriteWarning($"Delayed event for {e.Origin} was ignored because the target has left");
|
||||
// hack: don't do anything with the event because the target is invalid
|
||||
e.Type = GameEvent.EventType.Unknown;
|
||||
}
|
||||
}
|
||||
Logger.WriteDebug($"Adding delayed event of type {e.Type} for {e.Origin} back for processing");
|
||||
this.GetEventHandler().AddEvent(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
// save the event info to the database
|
||||
var changeHistorySvc = new ChangeHistoryService();
|
||||
await changeHistorySvc.Add(args.Event);
|
||||
|
||||
#if DEBUG
|
||||
Logger.WriteDebug($"Processed event with id {newEvent.Id}");
|
||||
@ -192,7 +119,7 @@ namespace IW4MAdmin.Application
|
||||
Logger.WriteDebug(ex.GetExceptionInfo());
|
||||
}
|
||||
|
||||
skip:
|
||||
skip:
|
||||
|
||||
// tell anyone waiting for the output that we're done
|
||||
newEvent.OnProcessed.Set();
|
||||
@ -216,7 +143,7 @@ namespace IW4MAdmin.Application
|
||||
public async Task UpdateServerStates()
|
||||
{
|
||||
// store the server hash code and task for it
|
||||
var runningUpdateTasks = new Dictionary<int, Task>();
|
||||
var runningUpdateTasks = new Dictionary<long, Task>();
|
||||
|
||||
while (Running)
|
||||
{
|
||||
@ -236,16 +163,16 @@ namespace IW4MAdmin.Application
|
||||
}
|
||||
|
||||
// remove the update tasks as they have completd
|
||||
foreach (int serverId in serverTasksToRemove)
|
||||
foreach (long serverId in serverTasksToRemove)
|
||||
{
|
||||
runningUpdateTasks.Remove(serverId);
|
||||
}
|
||||
|
||||
// select the servers where the tasks have completed
|
||||
var serverIds = Servers.Select(s => s.GetHashCode()).Except(runningUpdateTasks.Select(r => r.Key)).ToList();
|
||||
foreach (var server in Servers.Where(s => serverIds.Contains(s.GetHashCode())))
|
||||
var serverIds = Servers.Select(s => s.EndPoint).Except(runningUpdateTasks.Select(r => r.Key)).ToList();
|
||||
foreach (var server in Servers.Where(s => serverIds.Contains(s.EndPoint)))
|
||||
{
|
||||
runningUpdateTasks.Add(server.GetHashCode(), Task.Run(async () =>
|
||||
runningUpdateTasks.Add(server.EndPoint, Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -265,11 +192,7 @@ namespace IW4MAdmin.Application
|
||||
ThreadPool.GetAvailableThreads(out int availableThreads, out int m);
|
||||
Logger.WriteDebug($"There are {workerThreads - availableThreads} active threading tasks");
|
||||
#endif
|
||||
#if DEBUG
|
||||
await Task.Delay(10000);
|
||||
#else
|
||||
await Task.Delay(ConfigHandler.Configuration().RConPollRate);
|
||||
#endif
|
||||
}
|
||||
|
||||
// trigger the event processing loop to end
|
||||
@ -286,36 +209,7 @@ namespace IW4MAdmin.Application
|
||||
await new ContextSeed(db).Seed();
|
||||
}
|
||||
|
||||
// todo: optimize this (or replace it)
|
||||
var ipList = (await ClientSvc.Find(c => c.Level > Player.Permission.Trusted))
|
||||
.Select(c => new
|
||||
{
|
||||
c.Password,
|
||||
c.PasswordSalt,
|
||||
c.ClientId,
|
||||
c.Level,
|
||||
c.Name
|
||||
});
|
||||
|
||||
foreach (var a in ipList)
|
||||
{
|
||||
try
|
||||
{
|
||||
PrivilegedClients.Add(a.ClientId, new Player()
|
||||
{
|
||||
Name = a.Name,
|
||||
ClientId = a.ClientId,
|
||||
Level = a.Level,
|
||||
PasswordSalt = a.PasswordSalt,
|
||||
Password = a.Password
|
||||
});
|
||||
}
|
||||
|
||||
catch (ArgumentException)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
PrivilegedClients = (await ClientSvc.GetPrivilegedClients()).ToDictionary(_client => _client.ClientId);
|
||||
#endregion
|
||||
|
||||
#region CONFIG
|
||||
@ -364,7 +258,9 @@ namespace IW4MAdmin.Application
|
||||
}
|
||||
|
||||
else if (config.Servers.Count == 0)
|
||||
{
|
||||
throw new ServerException("A server configuration in IW4MAdminSettings.json is invalid");
|
||||
}
|
||||
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
Utilities.EncodingType = Encoding.GetEncoding(!string.IsNullOrEmpty(config.CustomParserEncoding) ? config.CustomParserEncoding : "windows-1252");
|
||||
@ -390,7 +286,9 @@ namespace IW4MAdmin.Application
|
||||
|
||||
#region COMMANDS
|
||||
if (ClientSvc.GetOwners().Result.Count == 0)
|
||||
{
|
||||
Commands.Add(new COwner());
|
||||
}
|
||||
|
||||
Commands.Add(new CQuit());
|
||||
Commands.Add(new CKick());
|
||||
@ -431,7 +329,9 @@ namespace IW4MAdmin.Application
|
||||
Commands.Add(new CNextMap());
|
||||
|
||||
foreach (Command C in SharedLibraryCore.Plugins.PluginImporter.ActiveCommands)
|
||||
{
|
||||
Commands.Add(C);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region INIT
|
||||
@ -466,7 +366,9 @@ namespace IW4MAdmin.Application
|
||||
{
|
||||
Logger.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_UNFIXABLE"]} [{Conf.IPAddress}:{Conf.Port}]");
|
||||
if (e.GetType() == typeof(DvarException))
|
||||
{
|
||||
Logger.WriteDebug($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_DVAR"]} {(e as DvarException).Data["dvar_name"]} ({Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_DVAR_HELP"]})");
|
||||
}
|
||||
else if (e.GetType() == typeof(NetworkException))
|
||||
{
|
||||
Logger.WriteDebug(e.Message);
|
||||
@ -565,7 +467,7 @@ namespace IW4MAdmin.Application
|
||||
Running = false;
|
||||
}
|
||||
|
||||
public ILogger GetLogger(int serverId)
|
||||
public ILogger GetLogger(long serverId)
|
||||
{
|
||||
if (Loggers.ContainsKey(serverId))
|
||||
{
|
||||
@ -595,23 +497,59 @@ namespace IW4MAdmin.Application
|
||||
return MessageTokens;
|
||||
}
|
||||
|
||||
public IList<Player> GetActiveClients() => _servers.SelectMany(s => s.Players).Where(p => p != null).ToList();
|
||||
public IList<EFClient> GetActiveClients()
|
||||
{
|
||||
return _servers.SelectMany(s => s.Clients).Where(p => p != null).ToList();
|
||||
}
|
||||
|
||||
public ClientService GetClientService() => ClientSvc;
|
||||
public AliasService GetAliasService() => AliasSvc;
|
||||
public PenaltyService GetPenaltyService() => PenaltySvc;
|
||||
public IConfigurationHandler<ApplicationConfiguration> GetApplicationSettings() => ConfigHandler;
|
||||
public IDictionary<int, Player> GetPrivilegedClients() => PrivilegedClients;
|
||||
public bool ShutdownRequested() => !Running;
|
||||
public IEventHandler GetEventHandler() => Handler;
|
||||
public ClientService GetClientService()
|
||||
{
|
||||
return ClientSvc;
|
||||
}
|
||||
|
||||
public AliasService GetAliasService()
|
||||
{
|
||||
return AliasSvc;
|
||||
}
|
||||
|
||||
public PenaltyService GetPenaltyService()
|
||||
{
|
||||
return PenaltySvc;
|
||||
}
|
||||
|
||||
public IConfigurationHandler<ApplicationConfiguration> GetApplicationSettings()
|
||||
{
|
||||
return ConfigHandler;
|
||||
}
|
||||
|
||||
public IDictionary<int, EFClient> GetPrivilegedClients()
|
||||
{
|
||||
return PrivilegedClients;
|
||||
}
|
||||
|
||||
public bool ShutdownRequested()
|
||||
{
|
||||
return !Running;
|
||||
}
|
||||
|
||||
public IEventHandler GetEventHandler()
|
||||
{
|
||||
return Handler;
|
||||
}
|
||||
|
||||
public void SetHasEvent()
|
||||
{
|
||||
OnQuit.Set();
|
||||
}
|
||||
|
||||
public IList<Assembly> GetPluginAssemblies() => SharedLibraryCore.Plugins.PluginImporter.PluginAssemblies;
|
||||
public IList<Assembly> GetPluginAssemblies()
|
||||
{
|
||||
return SharedLibraryCore.Plugins.PluginImporter.PluginAssemblies;
|
||||
}
|
||||
|
||||
public IPageList GetPageList() => PageList;
|
||||
public IPageList GetPageList()
|
||||
{
|
||||
return PageList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,13 @@
|
||||
using System;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Exceptions;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.RCon;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Objects;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.RCon;
|
||||
using SharedLibraryCore.Exceptions;
|
||||
|
||||
namespace IW4MAdmin.Application.RconParsers
|
||||
{
|
||||
@ -61,7 +60,7 @@ namespace IW4MAdmin.Application.RconParsers
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<List<Player>> GetStatusAsync(Connection connection)
|
||||
public async Task<List<EFClient>> GetStatusAsync(Connection connection)
|
||||
{
|
||||
string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, "status");
|
||||
return ClientsFromStatus(response);
|
||||
@ -72,14 +71,19 @@ namespace IW4MAdmin.Application.RconParsers
|
||||
return (await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, $"set {dvarName} {dvarValue}")).Length > 0;
|
||||
}
|
||||
|
||||
public virtual CommandPrefix GetCommandPrefixes() => Prefixes;
|
||||
|
||||
private List<Player> ClientsFromStatus(string[] Status)
|
||||
public virtual CommandPrefix GetCommandPrefixes()
|
||||
{
|
||||
List<Player> StatusPlayers = new List<Player>();
|
||||
return Prefixes;
|
||||
}
|
||||
|
||||
private List<EFClient> ClientsFromStatus(string[] Status)
|
||||
{
|
||||
List<EFClient> StatusPlayers = new List<EFClient>();
|
||||
|
||||
if (Status.Length < 4)
|
||||
{
|
||||
throw new ServerException("Unexpected status response received");
|
||||
}
|
||||
|
||||
int validMatches = 0;
|
||||
foreach (String S in Status)
|
||||
@ -102,20 +106,28 @@ namespace IW4MAdmin.Application.RconParsers
|
||||
ping = int.Parse(regex.Groups[3].Value);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
long networkId = regex.Groups[4].Value.ConvertLong();
|
||||
string name = regex.Groups[5].Value.StripColors().Trim();
|
||||
int ip = regex.Groups[7].Value.Split(':')[0].ConvertToIP();
|
||||
|
||||
Player P = new Player()
|
||||
var P = new EFClient()
|
||||
{
|
||||
Name = name,
|
||||
CurrentAlias = new EFAlias()
|
||||
{
|
||||
Name = name
|
||||
},
|
||||
NetworkId = networkId,
|
||||
ClientNumber = clientNumber,
|
||||
IPAddress = ip == 0 ? int.MinValue : ip,
|
||||
Ping = ping,
|
||||
Score = score,
|
||||
IsBot = ip == 0,
|
||||
State = Player.ClientState.Connecting
|
||||
State = EFClient.ClientState.Connecting
|
||||
};
|
||||
StatusPlayers.Add(P);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Objects;
|
||||
using SharedLibraryCore.RCon;
|
||||
using SharedLibraryCore.Exceptions;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
|
||||
namespace IW4MAdmin.Application.RconParsers
|
||||
{
|
||||
@ -105,7 +106,7 @@ namespace IW4MAdmin.Application.RconParsers
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<List<Player>> GetStatusAsync(Connection connection)
|
||||
public async Task<List<EFClient>> GetStatusAsync(Connection connection)
|
||||
{
|
||||
string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, "status");
|
||||
return ClientsFromStatus(response);
|
||||
@ -118,9 +119,9 @@ namespace IW4MAdmin.Application.RconParsers
|
||||
return true;
|
||||
}
|
||||
|
||||
private List<Player> ClientsFromStatus(string[] status)
|
||||
private List<EFClient> ClientsFromStatus(string[] status)
|
||||
{
|
||||
List<Player> StatusPlayers = new List<Player>();
|
||||
List<EFClient> StatusPlayers = new List<EFClient>();
|
||||
|
||||
foreach (string statusLine in status)
|
||||
{
|
||||
@ -145,7 +146,7 @@ namespace IW4MAdmin.Application.RconParsers
|
||||
regex = Regex.Match(responseLine, @" +(\d+ +){3}");
|
||||
int score = Int32.Parse(regex.Value.Split(' ', StringSplitOptions.RemoveEmptyEntries)[0]);
|
||||
|
||||
var p = new Player()
|
||||
var p = new EFClient()
|
||||
{
|
||||
Name = name,
|
||||
NetworkId = networkId,
|
||||
@ -154,7 +155,7 @@ namespace IW4MAdmin.Application.RconParsers
|
||||
Ping = Ping,
|
||||
Score = score,
|
||||
IsBot = false,
|
||||
State = Player.ClientState.Connecting
|
||||
State = EFClient.ClientState.Connecting
|
||||
};
|
||||
|
||||
StatusPlayers.Add(p);
|
||||
|
@ -9,6 +9,7 @@ using SharedLibraryCore.Objects;
|
||||
using SharedLibraryCore.RCon;
|
||||
using SharedLibraryCore.Exceptions;
|
||||
using System.Text;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
|
||||
namespace IW4MAdmin.Application.RconParsers
|
||||
{
|
||||
@ -18,9 +19,9 @@ namespace IW4MAdmin.Application.RconParsers
|
||||
{
|
||||
Tell = "tell {0} {1}",
|
||||
Say = "say {0}",
|
||||
Kick = "clientKick {0}",
|
||||
Ban = "clientKick {0}",
|
||||
TempBan = "clientKick {0}"
|
||||
Kick = "clientkick_for_reason {0} \"{1}\"",
|
||||
Ban = "clientkick_for_reason {0} \"{1}\"",
|
||||
TempBan = "clientkick_for_reason {0} \"{1}\""
|
||||
};
|
||||
|
||||
public CommandPrefix GetCommandPrefixes() => Prefixes;
|
||||
@ -60,7 +61,7 @@ namespace IW4MAdmin.Application.RconParsers
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<List<Player>> GetStatusAsync(Connection connection)
|
||||
public async Task<List<EFClient>> GetStatusAsync(Connection connection)
|
||||
{
|
||||
string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, "status");
|
||||
return ClientsFromStatus(response);
|
||||
@ -73,9 +74,9 @@ namespace IW4MAdmin.Application.RconParsers
|
||||
return true;
|
||||
}
|
||||
|
||||
private List<Player> ClientsFromStatus(string[] status)
|
||||
private List<EFClient> ClientsFromStatus(string[] status)
|
||||
{
|
||||
List<Player> StatusPlayers = new List<Player>();
|
||||
List<EFClient> StatusPlayers = new List<EFClient>();
|
||||
|
||||
foreach (string statusLine in status)
|
||||
{
|
||||
@ -98,7 +99,7 @@ namespace IW4MAdmin.Application.RconParsers
|
||||
#endif
|
||||
int ipAddress = regex.Value.Split(':')[0].ConvertToIP();
|
||||
regex = Regex.Match(responseLine, @"[0-9]{1,2}\s+[0-9]+\s+");
|
||||
var p = new Player()
|
||||
var p = new EFClient()
|
||||
{
|
||||
Name = name,
|
||||
NetworkId = networkId,
|
||||
@ -106,7 +107,7 @@ namespace IW4MAdmin.Application.RconParsers
|
||||
IPAddress = ipAddress,
|
||||
Ping = Ping,
|
||||
Score = 0,
|
||||
State = Player.ClientState.Connecting,
|
||||
State = EFClient.ClientState.Connecting,
|
||||
IsBot = networkId == 0
|
||||
};
|
||||
|
||||
|
31
Application/SharedGUIDKick.js
Normal file
31
Application/SharedGUIDKick.js
Normal file
@ -0,0 +1,31 @@
|
||||
var plugin = {
|
||||
author: 'RaidMax',
|
||||
version: 1.1,
|
||||
name: 'Shared GUID Kicker Plugin',
|
||||
|
||||
onEventAsync: function (gameEvent, server) {
|
||||
// make sure we only check for IW4(x)
|
||||
if (server.GameName !== 2) {
|
||||
return false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
// connect or join event
|
||||
if (gameEvent.Type === 3) {
|
||||
// this GUID seems to have been packed in a IW4 torrent and results in an unreasonable amount of people using the same GUID
|
||||
if (gameEvent.Origin.NetworkId === -805366929435212061) {
|
||||
gameEvent.Origin.Kick('Your GUID is generic. Delete players/guids.dat and rejoin', _IW4MAdminClient);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onLoadAsync: function (manager) {
|
||||
},
|
||||
|
||||
onUnloadAsync: function () {
|
||||
},
|
||||
|
||||
onTickAsync: function (server) {
|
||||
}
|
||||
};
|
62
Application/VPNDetection.js
Normal file
62
Application/VPNDetection.js
Normal file
@ -0,0 +1,62 @@
|
||||
var plugin = {
|
||||
author: 'RaidMax',
|
||||
version: 1.0,
|
||||
name: 'VPN Detection Plugin',
|
||||
|
||||
manager: null,
|
||||
logger: null,
|
||||
vpnExceptionIds: [],
|
||||
|
||||
checkForVpn: function (origin) {
|
||||
var exempt = false;
|
||||
// prevent players that are exempt from being kicked
|
||||
this.vpnExceptionIds.forEach(function (id) {
|
||||
if (id === origin.ClientId) {
|
||||
exempt = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (exempt) {
|
||||
return;
|
||||
}
|
||||
|
||||
var usingVPN = false;
|
||||
|
||||
try {
|
||||
var cl = new System.Net.Http.HttpClient();
|
||||
var re = cl.GetAsync('https://api.xdefcon.com/proxy/check/?ip=' + origin.IPAddressString).Result;
|
||||
var co = re.Content;
|
||||
var parsedJSON = JSON.parse(co.ReadAsStringAsync().Result);
|
||||
co.Dispose();
|
||||
re.Dispose();
|
||||
cl.Dispose();
|
||||
usingVPN = parsedJSON.success && parsedJSON.proxy;
|
||||
} catch (e) {
|
||||
this.logger.WriteError(e.message);
|
||||
}
|
||||
|
||||
if (usingVPN) {
|
||||
this.logger.WriteInfo(origin + ' is using a VPN (' + origin.IPAddressString + ')');
|
||||
origin.Kick(_localization.LocalizationIndex["SERVER_KICK_VPNS_NOTALLOWED"], _IW4MAdminClient);
|
||||
}
|
||||
},
|
||||
|
||||
onEventAsync: function (gameEvent, server) {
|
||||
// connect event
|
||||
if (gameEvent.Type === 3) {
|
||||
this.checkForVpn(gameEvent.Origin);
|
||||
}
|
||||
},
|
||||
|
||||
onLoadAsync: function (manager) {
|
||||
this.manager = manager;
|
||||
this.logger = manager.GetLogger(0);
|
||||
},
|
||||
|
||||
onUnloadAsync: function () {
|
||||
},
|
||||
|
||||
onTickAsync: function (server) {
|
||||
}
|
||||
};
|
@ -27,6 +27,11 @@
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Prerelease' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
||||
<OutputPath>bin\Prerelease\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="DiscordWebhook.py" />
|
||||
</ItemGroup>
|
||||
@ -42,7 +47,6 @@
|
||||
</Interpreter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="config.dev.json" />
|
||||
<Content Include="config.json">
|
||||
<Publish>True</Publish>
|
||||
</Content>
|
||||
@ -50,7 +54,7 @@
|
||||
<Publish>True</Publish>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.Web.targets" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.Web.targets" />
|
||||
<!-- Uncomment the CoreCompile target to enable the Build command in
|
||||
Visual Studio and specify your pre- and post-build commands in
|
||||
the BeforeBuild and AfterBuild targets below. -->
|
||||
@ -59,7 +63,7 @@
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
<ProjectExtensions>
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
||||
<WebProjectProperties>
|
||||
|
@ -27,10 +27,16 @@
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Prerelease' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
||||
<OutputPath>bin\Prerelease\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="GameLogServer\log_reader.py">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="GameLogServer\restart_resource.py" />
|
||||
<Compile Include="GameLogServer\server.py">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
@ -42,8 +48,8 @@
|
||||
<Folder Include="GameLogServer\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="FolderProfile.pubxml" />
|
||||
<Content Include="requirements.txt" />
|
||||
<None Include="Stable.pubxml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Interpreter Include="env\">
|
||||
|
@ -5,26 +5,27 @@ import time
|
||||
class LogReader(object):
|
||||
def __init__(self):
|
||||
self.log_file_sizes = {}
|
||||
# (if the file changes more than this, ignore ) - 1 MB
|
||||
self.max_file_size_change = 1000000
|
||||
# (if the file changes more than this, ignore ) - 0.125 MB
|
||||
self.max_file_size_change = 125000
|
||||
# (if the time between checks is greater, ignore ) - 5 minutes
|
||||
self.max_file_time_change = 1000
|
||||
self.max_file_time_change = 60
|
||||
|
||||
def read_file(self, path):
|
||||
# prevent traversing directories
|
||||
if re.search('r^.+\.\.\\.+$', path):
|
||||
return False
|
||||
# must be a valid log path and log file
|
||||
if not re.search(r'^.+[\\|\/](userraw|mods)[\\|\/].+.log$', path):
|
||||
if not re.search(r'^.+[\\|\/](userraw|mods|main)[\\|\/].+.log$', path):
|
||||
return False
|
||||
# set the initialze size to the current file size
|
||||
file_size = 0
|
||||
|
||||
if path not in self.log_file_sizes:
|
||||
self.log_file_sizes[path] = {
|
||||
'length' : self.file_length(path),
|
||||
'read': time.time()
|
||||
}
|
||||
return ''
|
||||
return True
|
||||
|
||||
# grab the previous values
|
||||
last_length = self.log_file_sizes[path]['length']
|
||||
@ -50,9 +51,9 @@ class LogReader(object):
|
||||
|
||||
# if it's been too long since we read and the amount changed is too great, discard it
|
||||
# todo: do we really want old events? maybe make this an "or"
|
||||
if file_size_difference > self.max_file_size_change and time_difference > self.max_file_time_change:
|
||||
return ''
|
||||
|
||||
if file_size_difference > self.max_file_size_change or time_difference > self.max_file_time_change:
|
||||
return True
|
||||
|
||||
new_log_info = self.get_file_lines(path, file_size_difference)
|
||||
return new_log_info
|
||||
|
||||
|
@ -9,9 +9,11 @@ class LogResource(Resource):
|
||||
|
||||
if log_info is False:
|
||||
print('could not read log file ' + path)
|
||||
|
||||
|
||||
empty_read = (log_info == False) or (log_info == True)
|
||||
|
||||
return {
|
||||
'success' : log_info is not False,
|
||||
'length': -1 if log_info is False else len(log_info),
|
||||
'length': -1 if empty_read else len(log_info),
|
||||
'data': log_info
|
||||
}
|
||||
|
29
GameLogServer/GameLogServer/restart_resource.py
Normal file
29
GameLogServer/GameLogServer/restart_resource.py
Normal file
@ -0,0 +1,29 @@
|
||||
from flask_restful import Resource
|
||||
from flask import request
|
||||
import requests
|
||||
import os
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
def get_pid_of_server_windows(port):
|
||||
process = subprocess.Popen('netstat -aon', shell=True, stdout=subprocess.PIPE)
|
||||
output = process.communicate()[0]
|
||||
matches = re.search(' *(UDP) +([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}):'+ str(port) + ' +[^\w]*([0-9]+)', output.decode('utf-8'))
|
||||
if matches is not None:
|
||||
return matches.group(3)
|
||||
else:
|
||||
return 0
|
||||
|
||||
class RestartResource(Resource):
|
||||
def get(self):
|
||||
try:
|
||||
response = requests.get('http://' + request.remote_addr + ':1624/api/restartapproved')
|
||||
if response.status_code == 200:
|
||||
pid = get_pid_of_server_windows(response.json()['port'])
|
||||
subprocess.check_output("Taskkill /PID %s /F" % pid)
|
||||
else:
|
||||
return {}, 400
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return {}, 500
|
||||
return {}, 200
|
@ -1,9 +1,11 @@
|
||||
from flask import Flask
|
||||
from flask_restful import Api
|
||||
from .log_resource import LogResource
|
||||
from .restart_resource import RestartResource
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
def init():
|
||||
api = Api(app)
|
||||
api.add_resource(LogResource, '/log/<string:path>')
|
||||
api.add_resource(RestartResource, '/restart')
|
||||
|
@ -1,12 +1,26 @@
|
||||
Flask==1.0.2
|
||||
aniso8601==3.0.2
|
||||
APScheduler==3.5.3
|
||||
certifi==2018.10.15
|
||||
chardet==3.0.4
|
||||
click==6.7
|
||||
Flask==1.0.2
|
||||
Flask-JWT==0.3.2
|
||||
Flask-JWT-Extended==3.8.1
|
||||
Flask-RESTful==0.3.6
|
||||
idna==2.7
|
||||
itsdangerous==0.24
|
||||
Jinja2==2.10
|
||||
MarkupSafe==1.0
|
||||
marshmallow==3.0.0b8
|
||||
pip==9.0.3
|
||||
pytz==2018.5
|
||||
setuptools==39.0.1
|
||||
psutil==5.4.8
|
||||
pygal==2.4.0
|
||||
PyJWT==1.4.2
|
||||
pytz==2018.7
|
||||
requests==2.20.0
|
||||
setuptools==40.5.0
|
||||
six==1.11.0
|
||||
timeago==1.0.8
|
||||
tzlocal==1.5.1
|
||||
urllib3==1.24
|
||||
Werkzeug==0.14.1
|
||||
|
@ -12,4 +12,4 @@ if __name__ == '__main__':
|
||||
except ValueError:
|
||||
PORT = 5555
|
||||
init()
|
||||
app.run(HOST, PORT, debug=True)
|
||||
app.run(HOST, PORT, debug=False)
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26730.16
|
||||
|
@ -17,7 +17,7 @@
|
||||
<SuppressCollectPythonCloudServiceFiles>true</SuppressCollectPythonCloudServiceFiles>
|
||||
<Name>Master</Name>
|
||||
<RootNamespace>Master</RootNamespace>
|
||||
<InterpreterId>MSBuild|dev_env|$(MSBuildProjectFullPath)</InterpreterId>
|
||||
<InterpreterId>MSBuild|env|X:\IW4MAdmin\GameLogServer\GameLogServer.pyproj</InterpreterId>
|
||||
<IsWindowsApplication>False</IsWindowsApplication>
|
||||
<PythonRunWebServerCommand>
|
||||
</PythonRunWebServerCommand>
|
||||
@ -111,23 +111,15 @@
|
||||
<Folder Include="master\templates\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="FolderProfile.pubxml" />
|
||||
<Content Include="master\config\master.json" />
|
||||
<Content Include="master\templates\serverlist.html" />
|
||||
<None Include="Release.pubxml" />
|
||||
<Content Include="requirements.txt" />
|
||||
<Content Include="master\templates\index.html" />
|
||||
<Content Include="master\templates\layout.html" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Interpreter Include="dev_env\">
|
||||
<Id>dev_env</Id>
|
||||
<Version>3.6</Version>
|
||||
<Description>dev_env (Python 3.6 (64-bit))</Description>
|
||||
<InterpreterPath>Scripts\python.exe</InterpreterPath>
|
||||
<WindowsInterpreterPath>Scripts\pythonw.exe</WindowsInterpreterPath>
|
||||
<PathEnvironmentVariable>PYTHONPATH</PathEnvironmentVariable>
|
||||
<Architecture>X64</Architecture>
|
||||
</Interpreter>
|
||||
<InterpreterReference Include="MSBuild|env|X:\IW4MAdmin\GameLogServer\GameLogServer.pyproj" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.Web.targets" />
|
||||
<!-- Specify pre- and post-build commands in the BeforeBuild and
|
||||
|
@ -23,7 +23,8 @@ class Instance(Resource):
|
||||
def put(self, id):
|
||||
try:
|
||||
for server in request.json['servers']:
|
||||
server['ip'] = request.remote_addr
|
||||
if 'ip' not in server or server['ip'] == 'localhost':
|
||||
server['ip'] = request.remote_addr
|
||||
instance = InstanceSchema().load(request.json)
|
||||
except ValidationError as err:
|
||||
return {'message' : err.messages }, 400
|
||||
@ -34,7 +35,8 @@ class Instance(Resource):
|
||||
def post(self):
|
||||
try:
|
||||
for server in request.json['servers']:
|
||||
server['ip'] = request.remote_addr
|
||||
if 'ip' not in server or server['ip'] == 'localhost':
|
||||
server['ip'] = request.remote_addr
|
||||
instance = InstanceSchema().load(request.json)
|
||||
except ValidationError as err:
|
||||
return {'message' : err.messages }, 400
|
||||
|
@ -4,7 +4,7 @@ from master.models.servermodel import ServerModel
|
||||
class ServerSchema(Schema):
|
||||
id = fields.Int(
|
||||
required=True,
|
||||
validate=validate.Range(1, 2147483647, 'invalid id')
|
||||
validate=validate.Range(1, 25525525525565535, 'invalid id')
|
||||
)
|
||||
ip = fields.Str(
|
||||
required=True
|
||||
|
@ -1,16 +1,26 @@
|
||||
aniso8601==3.0.0
|
||||
aniso8601==3.0.2
|
||||
APScheduler==3.5.3
|
||||
certifi==2018.10.15
|
||||
chardet==3.0.4
|
||||
click==6.7
|
||||
Flask==0.12.2
|
||||
Flask==1.0.2
|
||||
Flask-JWT==0.3.2
|
||||
Flask-JWT-Extended==3.8.1
|
||||
Flask-RESTful==0.3.6
|
||||
idna==2.7
|
||||
itsdangerous==0.24
|
||||
Jinja2==2.10
|
||||
MarkupSafe==1.0
|
||||
marshmallow==3.0.0b8
|
||||
pip==9.0.1
|
||||
pip==9.0.3
|
||||
psutil==5.4.8
|
||||
pygal==2.4.0
|
||||
PyJWT==1.4.2
|
||||
pytz==2018.4
|
||||
setuptools==39.0.1
|
||||
pytz==2018.7
|
||||
requests==2.20.0
|
||||
setuptools==40.5.0
|
||||
six==1.11.0
|
||||
timeago==1.0.8
|
||||
tzlocal==1.5.1
|
||||
urllib3==1.24
|
||||
Werkzeug==0.14.1
|
||||
|
@ -1,197 +1,200 @@
|
||||
//using SharedLibraryCore;
|
||||
//using SharedLibraryCore.Objects;
|
||||
//using System;
|
||||
//using System.Collections.Generic;
|
||||
//using System.Linq;
|
||||
//using System.Text;
|
||||
//using System.Threading.Tasks;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Objects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
//namespace IW4ScriptCommands.Commands
|
||||
//{
|
||||
// class Balance : Command
|
||||
// {
|
||||
// private class TeamAssignment
|
||||
// {
|
||||
// public IW4MAdmin.Plugins.Stats.IW4Info.Team CurrentTeam { get; set; }
|
||||
// public int Num { get; set; }
|
||||
// public IW4MAdmin.Plugins.Stats.Models.EFClientStatistics Stats { get; set; }
|
||||
// }
|
||||
// public Balance() : base("balance", "balance teams", "bal", Player.Permission.Trusted, false, null)
|
||||
// {
|
||||
// }
|
||||
namespace IW4ScriptCommands.Commands
|
||||
{
|
||||
class Balance
|
||||
{
|
||||
private class TeamAssignment
|
||||
{
|
||||
public IW4MAdmin.Plugins.Stats.IW4Info.Team CurrentTeam { get; set; }
|
||||
public int Num { get; set; }
|
||||
public IW4MAdmin.Plugins.Stats.Models.EFClientStatistics Stats { get; set; }
|
||||
}
|
||||
|
||||
// public override async Task ExecuteAsync(GameEvent E)
|
||||
// {
|
||||
// string teamsString = (await E.Owner.GetDvarAsync<string>("sv_iw4madmin_teams")).Value;
|
||||
public static string GetTeamAssignments(EFClient client, bool isDisconnect, Server server, string teamsString = "")
|
||||
{
|
||||
var scriptClientTeams = teamsString.Split(';', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(c => c.Split(','))
|
||||
.Select(c => new TeamAssignment()
|
||||
{
|
||||
CurrentTeam = (IW4MAdmin.Plugins.Stats.IW4Info.Team)Enum.Parse(typeof(IW4MAdmin.Plugins.Stats.IW4Info.Team), c[1]),
|
||||
Num = server.GetClientsAsList().FirstOrDefault(p => p.ClientNumber == Int32.Parse(c[0]))?.ClientNumber ?? -1,
|
||||
Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(server.Clients.FirstOrDefault(p => p.ClientNumber == Int32.Parse(c[0])).ClientId, server.EndPoint)
|
||||
})
|
||||
.ToList();
|
||||
|
||||
// var scriptClientTeams = teamsString.Split(';', StringSplitOptions.RemoveEmptyEntries)
|
||||
// .Select(c => c.Split(','))
|
||||
// .Select(c => new TeamAssignment()
|
||||
// {
|
||||
// CurrentTeam = (IW4MAdmin.Plugins.Stats.IW4Info.Team)Enum.Parse(typeof(IW4MAdmin.Plugins.Stats.IW4Info.Team), c[1]),
|
||||
// Num = E.Owner.Players.FirstOrDefault(p => p?.NetworkId == c[0].ConvertLong())?.ClientNumber ?? -1,
|
||||
// Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(E.Owner.Players.FirstOrDefault(p => p?.NetworkId == c[0].ConvertLong()).ClientId, E.Owner.GetHashCode())
|
||||
// })
|
||||
// .ToList();
|
||||
// at least one team is full so we can't balance
|
||||
if (scriptClientTeams.Count(ct => ct.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis) >= Math.Floor(server.MaxClients / 2.0)
|
||||
|| scriptClientTeams.Count(ct => ct.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies) >= Math.Floor(server.MaxClients / 2.0))
|
||||
{
|
||||
// E.Origin?.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BALANCE_FAIL"]);
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// // at least one team is full so we can't balance
|
||||
// if (scriptClientTeams.Count(ct => ct.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis) >= Math.Floor(E.Owner.MaxClients / 2.0)
|
||||
// || scriptClientTeams.Count(ct => ct.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies) >= Math.Floor(E.Owner.MaxClients / 2.0))
|
||||
// {
|
||||
// await E.Origin?.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BALANCE_FAIL"]);
|
||||
// return;
|
||||
// }
|
||||
List<string> teamAssignments = new List<string>();
|
||||
|
||||
// List<string> teamAssignments = new List<string>();
|
||||
var _c = server.GetClientsAsList();
|
||||
if (isDisconnect && client != null)
|
||||
{
|
||||
_c = _c.Where(c => c.ClientNumber != client.ClientNumber).ToList();
|
||||
}
|
||||
|
||||
// var activeClients = E.Owner.GetPlayersAsList().Select(c => new TeamAssignment()
|
||||
// {
|
||||
// Num = c.ClientNumber,
|
||||
// Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, E.Owner.GetHashCode()),
|
||||
// CurrentTeam = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, E.Owner.GetHashCode()).Team
|
||||
// })
|
||||
// .Where(c => scriptClientTeams.FirstOrDefault(sc => sc.Num == c.Num)?.CurrentTeam != IW4MAdmin.Plugins.Stats.IW4Info.Team.Spectator)
|
||||
// .Where(c => c.CurrentTeam != scriptClientTeams.FirstOrDefault(p => p.Num == c.Num)?.CurrentTeam)
|
||||
// .OrderByDescending(c => c.Stats.Performance)
|
||||
// .ToList();
|
||||
var activeClients = _c.Select(c => new TeamAssignment()
|
||||
{
|
||||
Num = c.ClientNumber,
|
||||
Stats = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, server.EndPoint),
|
||||
CurrentTeam = IW4MAdmin.Plugins.Stats.Plugin.Manager.GetClientStats(c.ClientId, server.EndPoint).Team
|
||||
})
|
||||
.Where(c => scriptClientTeams.FirstOrDefault(sc => sc.Num == c.Num)?.CurrentTeam != IW4MAdmin.Plugins.Stats.IW4Info.Team.Spectator)
|
||||
.Where(c => c.CurrentTeam != scriptClientTeams.FirstOrDefault(p => p.Num == c.Num)?.CurrentTeam)
|
||||
.OrderByDescending(c => c.Stats.Performance)
|
||||
.ToList();
|
||||
|
||||
// var alliesTeam = scriptClientTeams
|
||||
// .Where(c => c.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies)
|
||||
// .Where(c => activeClients.Count(t => t.Num == c.Num) == 0)
|
||||
// .ToList();
|
||||
var alliesTeam = scriptClientTeams
|
||||
.Where(c => c.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies)
|
||||
.Where(c => activeClients.Count(t => t.Num == c.Num) == 0)
|
||||
.ToList();
|
||||
|
||||
// var axisTeam = scriptClientTeams
|
||||
// .Where(c => c.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis)
|
||||
// .Where(c => activeClients.Count(t => t.Num == c.Num) == 0)
|
||||
// .ToList();
|
||||
var axisTeam = scriptClientTeams
|
||||
.Where(c => c.CurrentTeam == IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis)
|
||||
.Where(c => activeClients.Count(t => t.Num == c.Num) == 0)
|
||||
.ToList();
|
||||
|
||||
// while (activeClients.Count() > 0)
|
||||
// {
|
||||
// int teamSizeDifference = alliesTeam.Count - axisTeam.Count;
|
||||
// double performanceDisparity = alliesTeam.Count > 0 ? alliesTeam.Average(t => t.Stats.Performance) : 0 -
|
||||
// axisTeam.Count > 0 ? axisTeam.Average(t => t.Stats.Performance) : 0;
|
||||
while (activeClients.Count() > 0)
|
||||
{
|
||||
int teamSizeDifference = alliesTeam.Count - axisTeam.Count;
|
||||
double performanceDisparity = alliesTeam.Count > 0 ? alliesTeam.Average(t => t.Stats.Performance) : 0 -
|
||||
axisTeam.Count > 0 ? axisTeam.Average(t => t.Stats.Performance) : 0;
|
||||
|
||||
// if (teamSizeDifference == 0)
|
||||
// {
|
||||
// if (performanceDisparity == 0)
|
||||
// {
|
||||
// alliesTeam.Add(activeClients.First());
|
||||
// activeClients.RemoveAt(0);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (performanceDisparity > 0)
|
||||
// {
|
||||
// axisTeam.Add(activeClients.First());
|
||||
// activeClients.RemoveAt(0);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// alliesTeam.Add(activeClients.First());
|
||||
// activeClients.RemoveAt(0);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else if (teamSizeDifference > 0)
|
||||
// {
|
||||
// if (performanceDisparity > 0)
|
||||
// {
|
||||
// axisTeam.Add(activeClients.First());
|
||||
// activeClients.RemoveAt(0);
|
||||
// }
|
||||
if (teamSizeDifference == 0)
|
||||
{
|
||||
if (performanceDisparity == 0)
|
||||
{
|
||||
alliesTeam.Add(activeClients.First());
|
||||
activeClients.RemoveAt(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (performanceDisparity > 0)
|
||||
{
|
||||
axisTeam.Add(activeClients.First());
|
||||
activeClients.RemoveAt(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
alliesTeam.Add(activeClients.First());
|
||||
activeClients.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (teamSizeDifference > 0)
|
||||
{
|
||||
if (performanceDisparity > 0)
|
||||
{
|
||||
axisTeam.Add(activeClients.First());
|
||||
activeClients.RemoveAt(0);
|
||||
}
|
||||
|
||||
// else
|
||||
// {
|
||||
// axisTeam.Add(activeClients.Last());
|
||||
// activeClients.RemoveAt(activeClients.Count - 1);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (performanceDisparity > 0)
|
||||
// {
|
||||
// alliesTeam.Add(activeClients.First());
|
||||
// activeClients.RemoveAt(0);
|
||||
// }
|
||||
else
|
||||
{
|
||||
axisTeam.Add(activeClients.Last());
|
||||
activeClients.RemoveAt(activeClients.Count - 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (performanceDisparity > 0)
|
||||
{
|
||||
alliesTeam.Add(activeClients.First());
|
||||
activeClients.RemoveAt(0);
|
||||
}
|
||||
|
||||
// else
|
||||
// {
|
||||
// alliesTeam.Add(activeClients.Last());
|
||||
// activeClients.RemoveAt(activeClients.Count - 1);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
else
|
||||
{
|
||||
alliesTeam.Add(activeClients.Last());
|
||||
activeClients.RemoveAt(activeClients.Count - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// alliesTeam = alliesTeam.OrderByDescending(t => t.Stats.Performance)
|
||||
// .ToList();
|
||||
alliesTeam = alliesTeam.OrderByDescending(t => t.Stats.Performance)
|
||||
.ToList();
|
||||
|
||||
// axisTeam = axisTeam.OrderByDescending(t => t.Stats.Performance)
|
||||
// .ToList();
|
||||
axisTeam = axisTeam.OrderByDescending(t => t.Stats.Performance)
|
||||
.ToList();
|
||||
|
||||
// while (Math.Abs(alliesTeam.Count - axisTeam.Count) > 1)
|
||||
// {
|
||||
// int teamSizeDifference = alliesTeam.Count - axisTeam.Count;
|
||||
// double performanceDisparity = alliesTeam.Count > 0 ? alliesTeam.Average(t => t.Stats.Performance) : 0 -
|
||||
// axisTeam.Count > 0 ? axisTeam.Average(t => t.Stats.Performance) : 0;
|
||||
while (Math.Abs(alliesTeam.Count - axisTeam.Count) > 1)
|
||||
{
|
||||
int teamSizeDifference = alliesTeam.Count - axisTeam.Count;
|
||||
double performanceDisparity = alliesTeam.Count > 0 ? alliesTeam.Average(t => t.Stats.Performance) : 0 -
|
||||
axisTeam.Count > 0 ? axisTeam.Average(t => t.Stats.Performance) : 0;
|
||||
|
||||
// if (teamSizeDifference > 0)
|
||||
// {
|
||||
// if (performanceDisparity > 0)
|
||||
// {
|
||||
// axisTeam.Add(alliesTeam.First());
|
||||
// alliesTeam.RemoveAt(0);
|
||||
// }
|
||||
if (teamSizeDifference > 0)
|
||||
{
|
||||
if (performanceDisparity > 0)
|
||||
{
|
||||
axisTeam.Add(alliesTeam.First());
|
||||
alliesTeam.RemoveAt(0);
|
||||
}
|
||||
|
||||
// else
|
||||
// {
|
||||
// axisTeam.Add(alliesTeam.Last());
|
||||
// alliesTeam.RemoveAt(axisTeam.Count - 1);
|
||||
// }
|
||||
// }
|
||||
else
|
||||
{
|
||||
axisTeam.Add(alliesTeam.Last());
|
||||
alliesTeam.RemoveAt(axisTeam.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// else
|
||||
// {
|
||||
// if (performanceDisparity > 0)
|
||||
// {
|
||||
// alliesTeam.Add(axisTeam.Last());
|
||||
// axisTeam.RemoveAt(axisTeam.Count - 1);
|
||||
// }
|
||||
else
|
||||
{
|
||||
if (performanceDisparity > 0)
|
||||
{
|
||||
alliesTeam.Add(axisTeam.Last());
|
||||
axisTeam.RemoveAt(axisTeam.Count - 1);
|
||||
}
|
||||
|
||||
// else
|
||||
// {
|
||||
// alliesTeam.Add(axisTeam.First());
|
||||
// axisTeam.RemoveAt(0);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
else
|
||||
{
|
||||
alliesTeam.Add(axisTeam.First());
|
||||
axisTeam.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// foreach (var assignment in alliesTeam)
|
||||
// {
|
||||
// teamAssignments.Add($"{assignment.Num},2");
|
||||
// assignment.Stats.Team = IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies;
|
||||
// }
|
||||
// foreach (var assignment in axisTeam)
|
||||
// {
|
||||
// teamAssignments.Add($"{assignment.Num},3");
|
||||
// assignment.Stats.Team = IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis;
|
||||
// }
|
||||
foreach (var assignment in alliesTeam)
|
||||
{
|
||||
teamAssignments.Add($"{assignment.Num},2");
|
||||
assignment.Stats.Team = IW4MAdmin.Plugins.Stats.IW4Info.Team.Allies;
|
||||
}
|
||||
|
||||
// if (alliesTeam.Count(ac => scriptClientTeams.First(sc => sc.Num == ac.Num).CurrentTeam != ac.CurrentTeam) == 0 &&
|
||||
// axisTeam.Count(ac => scriptClientTeams.First(sc => sc.Num == ac.Num).CurrentTeam != ac.CurrentTeam) == 0)
|
||||
// {
|
||||
// await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BALANCE_FAIL_BALANCED"]);
|
||||
// return;
|
||||
// }
|
||||
foreach (var assignment in axisTeam)
|
||||
{
|
||||
teamAssignments.Add($"{assignment.Num},3");
|
||||
assignment.Stats.Team = IW4MAdmin.Plugins.Stats.IW4Info.Team.Axis;
|
||||
}
|
||||
|
||||
// if (E.Origin?.Level > Player.Permission.Administrator)
|
||||
// {
|
||||
// await E.Origin.Tell($"Allies Elo: {(alliesTeam.Count > 0 ? alliesTeam.Average(t => t.Stats.Performance) : 0)}");
|
||||
// await E.Origin.Tell($"Axis Elo: {(axisTeam.Count > 0 ? axisTeam.Average(t => t.Stats.Performance) : 0)}");
|
||||
// }
|
||||
//if (alliesTeam.Count(ac => scriptClientTeams.First(sc => sc.Num == ac.Num).CurrentTeam != ac.CurrentTeam) == 0 &&
|
||||
// axisTeam.Count(ac => scriptClientTeams.First(sc => sc.Num == ac.Num).CurrentTeam != ac.CurrentTeam) == 0)
|
||||
//{
|
||||
// //E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BALANCE_FAIL_BALANCED"]);
|
||||
// return string.Empty;
|
||||
//}
|
||||
|
||||
// string args = string.Join(",", teamAssignments);
|
||||
// await E.Owner.ExecuteCommandAsync($"sv_iw4madmin_command \"balance:{args}\"");
|
||||
// await E.Origin.Tell("Balance command sent");
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//if (E.Origin?.Level > Player.Permission.Administrator)
|
||||
//{
|
||||
// E.Origin.Tell($"Allies Elo: {(alliesTeam.Count > 0 ? alliesTeam.Average(t => t.Stats.Performance) : 0)}");
|
||||
// E.Origin.Tell($"Axis Elo: {(axisTeam.Count > 0 ? axisTeam.Average(t => t.Stats.Performance) : 0)}");
|
||||
//}
|
||||
|
||||
//E.Origin.Tell("Balance command sent");
|
||||
string args = string.Join(",", teamAssignments);
|
||||
return args;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
54
Plugins/IW4ScriptCommands/GscApiController.cs
Normal file
54
Plugins/IW4ScriptCommands/GscApiController.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using IW4ScriptCommands.Commands;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Objects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WebfrontCore.Controllers.API
|
||||
{
|
||||
[Route("api/gsc/[action]")]
|
||||
public class GscApiController : ApiController
|
||||
{
|
||||
[HttpGet("{networkId}")]
|
||||
public IActionResult ClientInfo(string networkId)
|
||||
{
|
||||
var clientInfo = Manager.GetActiveClients()
|
||||
.FirstOrDefault(c => c.NetworkId == networkId.ConvertLong());
|
||||
|
||||
if (clientInfo != null)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"admin={clientInfo.IsPrivileged()}");
|
||||
sb.AppendLine($"level={(int)clientInfo.Level}");
|
||||
sb.AppendLine($"levelstring={clientInfo.Level.ToLocalizedLevelName()}");
|
||||
sb.AppendLine($"connections={clientInfo.Connections}");
|
||||
sb.AppendLine($"authenticated={clientInfo.GetAdditionalProperty<bool>("IsLoggedIn") == true}");
|
||||
|
||||
return Content(sb.ToString());
|
||||
}
|
||||
|
||||
return Content("");
|
||||
}
|
||||
|
||||
[HttpGet("{networkId}")]
|
||||
public IActionResult GetTeamAssignments(string networkId, int serverId, string teams = "", bool isDisconnect = false)
|
||||
{
|
||||
return Unauthorized();
|
||||
|
||||
var client = Manager.GetActiveClients()
|
||||
.FirstOrDefault(c => c.NetworkId == networkId.ConvertLong());
|
||||
|
||||
var server = Manager.GetServers().First(c => c.EndPoint == serverId);
|
||||
|
||||
teams = teams ?? string.Empty;
|
||||
|
||||
string assignments = Balance.GetTeamAssignments(client, isDisconnect, server, teams);
|
||||
|
||||
return Content(assignments);
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
<RuntimeFrameworkVersion>2.1.5</RuntimeFrameworkVersion>
|
||||
<ApplicationIcon />
|
||||
<StartupObject />
|
||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -17,15 +17,10 @@ namespace IW4ScriptCommands
|
||||
|
||||
public Task OnEventAsync(GameEvent E, Server S)
|
||||
{
|
||||
//if (E.Type == GameEvent.EventType.JoinTeam || E.Type == GameEvent.EventType.Disconnect)
|
||||
//{
|
||||
// E.Origin = new SharedLibraryCore.Objects.Player()
|
||||
// {
|
||||
// ClientId = 1,
|
||||
// CurrentServer = E.Owner
|
||||
// };
|
||||
// return new Commands.Balance().ExecuteAsync(E);
|
||||
//}
|
||||
if (E.Type == GameEvent.EventType.Start)
|
||||
{
|
||||
return S.SetDvarAsync("sv_iw4madmin_serverid", S.EndPoint);
|
||||
}
|
||||
|
||||
if (E.Type == GameEvent.EventType.Warn)
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Objects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -8,7 +9,7 @@ namespace IW4MAdmin.Plugins.Login.Commands
|
||||
{
|
||||
public class CLogin : Command
|
||||
{
|
||||
public CLogin() : base("login", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_DESC"], "li", Player.Permission.Trusted, false, new CommandArgument[]
|
||||
public CLogin() : base("login", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_DESC"], "li", EFClient.Permission.Trusted, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
|
@ -3,6 +3,7 @@ using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Exceptions;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Objects;
|
||||
@ -28,6 +29,7 @@ namespace IW4MAdmin.Plugins.Login
|
||||
if (E.Type == GameEvent.EventType.Connect)
|
||||
{
|
||||
AuthorizedClients.TryAdd(E.Origin.ClientId, false);
|
||||
E.Origin.SetAdditionalProperty("IsLoggedIn", false);
|
||||
}
|
||||
|
||||
if (E.Type == GameEvent.EventType.Disconnect)
|
||||
@ -37,11 +39,11 @@ namespace IW4MAdmin.Plugins.Login
|
||||
|
||||
if (E.Type == GameEvent.EventType.Command)
|
||||
{
|
||||
if (E.Origin.Level < Player.Permission.Moderator ||
|
||||
E.Origin.Level == Player.Permission.Console)
|
||||
if (E.Origin.Level < EFClient.Permission.Moderator ||
|
||||
E.Origin.Level == EFClient.Permission.Console)
|
||||
return Task.CompletedTask;
|
||||
|
||||
E.Owner.Manager.GetPrivilegedClients().TryGetValue(E.Origin.ClientId, out Player client);
|
||||
E.Owner.Manager.GetPrivilegedClients().TryGetValue(E.Origin.ClientId, out EFClient client);
|
||||
|
||||
if (((Command)E.Extra).Name == new SharedLibraryCore.Commands.CSetPassword().Name &&
|
||||
client?.Password == null)
|
||||
@ -51,7 +53,14 @@ namespace IW4MAdmin.Plugins.Login
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (!AuthorizedClients[E.Origin.ClientId])
|
||||
{
|
||||
throw new AuthorizationException(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_AUTH"]);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
E.Origin.SetAdditionalProperty("IsLoggedIn", true);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
@ -5,6 +5,7 @@ using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Objects;
|
||||
|
||||
@ -48,7 +49,7 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
|
||||
|
||||
if (containsObjectionalWord)
|
||||
{
|
||||
E.Origin.Kick(Settings.Configuration().ProfanityKickMessage, new Player()
|
||||
E.Origin.Kick(Settings.Configuration().ProfanityKickMessage, new EFClient()
|
||||
{
|
||||
ClientId = 1,
|
||||
CurrentServer = E.Owner
|
||||
@ -85,22 +86,14 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
|
||||
var clientProfanity = ProfanityCounts[E.Origin.ClientId];
|
||||
if (clientProfanity.Infringements >= Settings.Configuration().KickAfterInfringementCount)
|
||||
{
|
||||
clientProfanity.Client.Kick(Settings.Configuration().ProfanityKickMessage, new Player()
|
||||
{
|
||||
ClientId = 1,
|
||||
CurrentServer = E.Owner
|
||||
});
|
||||
clientProfanity.Client.Kick(Settings.Configuration().ProfanityKickMessage, Utilities.IW4MAdminClient(E.Owner));
|
||||
}
|
||||
|
||||
else if (clientProfanity.Infringements < Settings.Configuration().KickAfterInfringementCount)
|
||||
{
|
||||
clientProfanity.Infringements++;
|
||||
|
||||
clientProfanity.Client.Warn(Settings.Configuration().ProfanityWarningMessage, new Player()
|
||||
{
|
||||
ClientId = 1,
|
||||
CurrentServer = E.Owner
|
||||
});
|
||||
clientProfanity.Client.Warn(Settings.Configuration().ProfanityWarningMessage, Utilities.IW4MAdminClient(E.Owner));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Objects;
|
||||
|
||||
namespace IW4MAdmin.Plugins.ProfanityDeterment
|
||||
{
|
||||
class Tracking
|
||||
{
|
||||
public Player Client { get; private set; }
|
||||
public EFClient Client { get; private set; }
|
||||
public int Infringements { get; set; }
|
||||
|
||||
public Tracking(Player client)
|
||||
public Tracking(EFClient client)
|
||||
{
|
||||
Client = client;
|
||||
Infringements = 0;
|
||||
|
@ -1,7 +1,7 @@
|
||||
using SharedLibraryCore.Helpers;
|
||||
using IW4MAdmin.Plugins.Stats.Models;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Objects;
|
||||
using IW4MAdmin.Plugins.Stats.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -20,7 +20,9 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
};
|
||||
|
||||
public ChangeTracking<EFACSnapshot> Tracker { get; private set; }
|
||||
public const int QUEUE_COUNT = 10;
|
||||
|
||||
public List<EFClientKill> QueuedHits { get; set; }
|
||||
int Kills;
|
||||
int HitCount;
|
||||
Dictionary<IW4Info.HitLocation, int> HitLocationCount;
|
||||
@ -37,49 +39,55 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
Log = log;
|
||||
HitLocationCount = new Dictionary<IW4Info.HitLocation, int>();
|
||||
foreach (var loc in Enum.GetValues(typeof(IW4Info.HitLocation)))
|
||||
{
|
||||
HitLocationCount.Add((IW4Info.HitLocation)loc, 0);
|
||||
}
|
||||
|
||||
ClientStats = clientStats;
|
||||
Strain = new Strain();
|
||||
Tracker = new ChangeTracking<EFACSnapshot>();
|
||||
QueuedHits = new List<EFClientKill>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Analyze kill and see if performed by a cheater
|
||||
/// </summary>
|
||||
/// <param name="kill">kill performed by the player</param>
|
||||
/// <param name="hit">kill performed by the player</param>
|
||||
/// <returns>true if detection reached thresholds, false otherwise</returns>
|
||||
public DetectionPenaltyResult ProcessKill(EFClientKill kill, bool isDamage)
|
||||
public DetectionPenaltyResult ProcessHit(EFClientKill hit, bool isDamage)
|
||||
{
|
||||
if ((kill.DeathType != IW4Info.MeansOfDeath.MOD_PISTOL_BULLET &&
|
||||
kill.DeathType != IW4Info.MeansOfDeath.MOD_RIFLE_BULLET &&
|
||||
kill.DeathType != IW4Info.MeansOfDeath.MOD_HEAD_SHOT) ||
|
||||
kill.HitLoc == IW4Info.HitLocation.none || kill.TimeOffset - LastOffset < 0 ||
|
||||
if ((hit.DeathType != IW4Info.MeansOfDeath.MOD_PISTOL_BULLET &&
|
||||
hit.DeathType != IW4Info.MeansOfDeath.MOD_RIFLE_BULLET &&
|
||||
hit.DeathType != IW4Info.MeansOfDeath.MOD_HEAD_SHOT) ||
|
||||
hit.HitLoc == IW4Info.HitLocation.none || hit.TimeOffset - LastOffset < 0 ||
|
||||
// hack: prevents false positives
|
||||
(LastWeapon != kill.Weapon && (kill.TimeOffset - LastOffset) == 50))
|
||||
(LastWeapon != hit.Weapon && (hit.TimeOffset - LastOffset) == 50))
|
||||
{
|
||||
return new DetectionPenaltyResult()
|
||||
{
|
||||
ClientPenalty = Penalty.PenaltyType.Any,
|
||||
};
|
||||
}
|
||||
|
||||
DetectionPenaltyResult result = null;
|
||||
LastWeapon = kill.Weapon;
|
||||
LastWeapon = hit.Weapon;
|
||||
|
||||
HitLocationCount[hit.HitLoc]++;
|
||||
HitCount++;
|
||||
|
||||
HitLocationCount[kill.HitLoc]++;
|
||||
if (!isDamage)
|
||||
{
|
||||
Kills++;
|
||||
}
|
||||
|
||||
HitCount++;
|
||||
|
||||
#region VIEWANGLES
|
||||
if (kill.AnglesList.Count >= 2)
|
||||
if (hit.AnglesList.Count >= 2)
|
||||
{
|
||||
double realAgainstPredict = Vector3.ViewAngleDistance(kill.AnglesList[0], kill.AnglesList[1], kill.ViewAngles);
|
||||
double realAgainstPredict = Vector3.ViewAngleDistance(hit.AnglesList[0], hit.AnglesList[1], hit.ViewAngles);
|
||||
|
||||
// LIFETIME
|
||||
var hitLoc = ClientStats.HitLocations
|
||||
.First(hl => hl.Location == kill.HitLoc);
|
||||
.First(hl => hl.Location == hit.HitLoc);
|
||||
|
||||
float previousAverage = hitLoc.HitOffsetAverage;
|
||||
double newAverage = (previousAverage * (hitLoc.HitCount - 1) + realAgainstPredict) / hitLoc.HitCount;
|
||||
@ -88,11 +96,11 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
if (hitLoc.HitOffsetAverage > Thresholds.MaxOffset(hitLoc.HitCount) &&
|
||||
hitLoc.HitCount > 100)
|
||||
{
|
||||
Log.WriteDebug("*** Reached Max Lifetime Average for Angle Difference ***");
|
||||
Log.WriteDebug($"Lifetime Average = {newAverage}");
|
||||
Log.WriteDebug($"Bone = {hitLoc.Location}");
|
||||
Log.WriteDebug($"HitCount = {hitLoc.HitCount}");
|
||||
Log.WriteDebug($"ID = {kill.AttackerId}");
|
||||
//Log.WriteDebug("*** Reached Max Lifetime Average for Angle Difference ***");
|
||||
//Log.WriteDebug($"Lifetime Average = {newAverage}");
|
||||
//Log.WriteDebug($"Bone = {hitLoc.Location}");
|
||||
//Log.WriteDebug($"HitCount = {hitLoc.HitCount}");
|
||||
//Log.WriteDebug($"ID = {hit.AttackerId}");
|
||||
|
||||
result = new DetectionPenaltyResult()
|
||||
{
|
||||
@ -110,10 +118,10 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
if (sessAverage > Thresholds.MaxOffset(HitCount) &&
|
||||
HitCount > 30)
|
||||
{
|
||||
Log.WriteDebug("*** Reached Max Session Average for Angle Difference ***");
|
||||
Log.WriteDebug($"Session Average = {sessAverage}");
|
||||
Log.WriteDebug($"HitCount = {HitCount}");
|
||||
Log.WriteDebug($"ID = {kill.AttackerId}");
|
||||
//Log.WriteDebug("*** Reached Max Session Average for Angle Difference ***");
|
||||
//Log.WriteDebug($"Session Average = {sessAverage}");
|
||||
//Log.WriteDebug($"HitCount = {HitCount}");
|
||||
//Log.WriteDebug($"ID = {hit.AttackerId}");
|
||||
|
||||
result = new DetectionPenaltyResult()
|
||||
{
|
||||
@ -130,8 +138,11 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
#endif
|
||||
}
|
||||
|
||||
double currentStrain = Strain.GetStrain(isDamage, kill.Damage, kill.Distance / 0.0254, kill.ViewAngles, Math.Max(50, kill.TimeOffset - LastOffset));
|
||||
LastOffset = kill.TimeOffset;
|
||||
double currentStrain = Strain.GetStrain(isDamage, hit.Damage, hit.Distance / 0.0254, hit.ViewAngles, Math.Max(50, hit.TimeOffset - LastOffset));
|
||||
#if DEBUG == true
|
||||
Log.WriteDebug($"Current Strain: {currentStrain}");
|
||||
#endif
|
||||
LastOffset = hit.TimeOffset;
|
||||
|
||||
if (currentStrain > ClientStats.MaxStrain)
|
||||
{
|
||||
@ -139,8 +150,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
}
|
||||
|
||||
// flag
|
||||
if (currentStrain > Thresholds.MaxStrainFlag &&
|
||||
HitCount >= 10)
|
||||
if (currentStrain > Thresholds.MaxStrainFlag)
|
||||
{
|
||||
result = new DetectionPenaltyResult()
|
||||
{
|
||||
@ -153,7 +163,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
|
||||
// ban
|
||||
if (currentStrain > Thresholds.MaxStrainBan &&
|
||||
HitCount >= 15)
|
||||
HitCount >= 5)
|
||||
{
|
||||
result = new DetectionPenaltyResult()
|
||||
{
|
||||
@ -163,11 +173,6 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
Type = DetectionType.Strain
|
||||
};
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
Log.WriteDebug($"Current Strain: {currentStrain}");
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region SESSION_RATIOS
|
||||
@ -197,13 +202,16 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
if (currentHeadshotRatio > maxHeadshotLerpValueForBan)
|
||||
{
|
||||
Log.WriteDebug("**Maximum Headshot Ratio Reached For Ban**");
|
||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
||||
Log.WriteDebug($"ClientId: {hit.AttackerId}");
|
||||
Log.WriteDebug($"**HitCount: {HitCount}");
|
||||
Log.WriteDebug($"**Ratio {currentHeadshotRatio}");
|
||||
Log.WriteDebug($"**MaxRatio {maxHeadshotLerpValueForFlag}");
|
||||
var sb = new StringBuilder();
|
||||
foreach (var kvp in HitLocationCount)
|
||||
{
|
||||
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
||||
}
|
||||
|
||||
Log.WriteDebug(sb.ToString());
|
||||
|
||||
result = new DetectionPenaltyResult()
|
||||
@ -218,13 +226,16 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
else
|
||||
{
|
||||
Log.WriteDebug("**Maximum Headshot Ratio Reached For Flag**");
|
||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
||||
Log.WriteDebug($"ClientId: {hit.AttackerId}");
|
||||
Log.WriteDebug($"**HitCount: {HitCount}");
|
||||
Log.WriteDebug($"**Ratio {currentHeadshotRatio}");
|
||||
Log.WriteDebug($"**MaxRatio {maxHeadshotLerpValueForFlag}");
|
||||
var sb = new StringBuilder();
|
||||
foreach (var kvp in HitLocationCount)
|
||||
{
|
||||
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
||||
}
|
||||
|
||||
Log.WriteDebug(sb.ToString());
|
||||
|
||||
result = new DetectionPenaltyResult()
|
||||
@ -247,13 +258,16 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
if (currentMaxBoneRatio > maxBoneRatioLerpValueForBan)
|
||||
{
|
||||
Log.WriteDebug("**Maximum Bone Ratio Reached For Ban**");
|
||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
||||
Log.WriteDebug($"ClientId: {hit.AttackerId}");
|
||||
Log.WriteDebug($"**HitCount: {HitCount}");
|
||||
Log.WriteDebug($"**Ratio {currentMaxBoneRatio}");
|
||||
Log.WriteDebug($"**MaxRatio {maxBoneRatioLerpValueForBan}");
|
||||
var sb = new StringBuilder();
|
||||
foreach (var kvp in HitLocationCount)
|
||||
{
|
||||
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
||||
}
|
||||
|
||||
Log.WriteDebug(sb.ToString());
|
||||
|
||||
result = new DetectionPenaltyResult()
|
||||
@ -268,13 +282,16 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
else
|
||||
{
|
||||
Log.WriteDebug("**Maximum Bone Ratio Reached For Flag**");
|
||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
||||
Log.WriteDebug($"ClientId: {hit.AttackerId}");
|
||||
Log.WriteDebug($"**HitCount: {HitCount}");
|
||||
Log.WriteDebug($"**Ratio {currentMaxBoneRatio}");
|
||||
Log.WriteDebug($"**MaxRatio {maxBoneRatioLerpValueForFlag}");
|
||||
var sb = new StringBuilder();
|
||||
foreach (var kvp in HitLocationCount)
|
||||
{
|
||||
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
||||
}
|
||||
|
||||
Log.WriteDebug(sb.ToString());
|
||||
|
||||
result = new DetectionPenaltyResult()
|
||||
@ -308,15 +325,15 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
|
||||
if (currentChestAbdomenRatio > chestAbdomenLerpValueForBan && chestHits >= Thresholds.MediumSampleMinKills + 30)
|
||||
{
|
||||
Log.WriteDebug("**Maximum Chest/Abdomen Ratio Reached For Ban**");
|
||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
||||
Log.WriteDebug($"**Chest Hits: {chestHits}");
|
||||
Log.WriteDebug($"**Ratio {currentChestAbdomenRatio}");
|
||||
Log.WriteDebug($"**MaxRatio {chestAbdomenLerpValueForBan}");
|
||||
var sb = new StringBuilder();
|
||||
foreach (var kvp in HitLocationCount)
|
||||
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
||||
Log.WriteDebug(sb.ToString());
|
||||
//Log.WriteDebug("**Maximum Chest/Abdomen Ratio Reached For Ban**");
|
||||
//Log.WriteDebug($"ClientId: {hit.AttackerId}");
|
||||
//Log.WriteDebug($"**Chest Hits: {chestHits}");
|
||||
//Log.WriteDebug($"**Ratio {currentChestAbdomenRatio}");
|
||||
//Log.WriteDebug($"**MaxRatio {chestAbdomenLerpValueForBan}");
|
||||
//var sb = new StringBuilder();
|
||||
//foreach (var kvp in HitLocationCount)
|
||||
// sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
||||
//Log.WriteDebug(sb.ToString());
|
||||
|
||||
result = new DetectionPenaltyResult()
|
||||
{
|
||||
@ -329,16 +346,15 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.WriteDebug("**Maximum Chest/Abdomen Ratio Reached For Flag**");
|
||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
||||
Log.WriteDebug($"**Chest Hits: {chestHits}");
|
||||
Log.WriteDebug($"**Ratio {currentChestAbdomenRatio}");
|
||||
Log.WriteDebug($"**MaxRatio {chestAbdomenRatioLerpValueForFlag}");
|
||||
var sb = new StringBuilder();
|
||||
foreach (var kvp in HitLocationCount)
|
||||
sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
||||
Log.WriteDebug(sb.ToString());
|
||||
// Log.WriteDebug($"ThresholdReached: {AboveThresholdCount}");
|
||||
//Log.WriteDebug("**Maximum Chest/Abdomen Ratio Reached For Flag**");
|
||||
//Log.WriteDebug($"ClientId: {hit.AttackerId}");
|
||||
//Log.WriteDebug($"**Chest Hits: {chestHits}");
|
||||
//Log.WriteDebug($"**Ratio {currentChestAbdomenRatio}");
|
||||
//Log.WriteDebug($"**MaxRatio {chestAbdomenRatioLerpValueForFlag}");
|
||||
//var sb = new StringBuilder();
|
||||
//foreach (var kvp in HitLocationCount)
|
||||
// sb.Append($"HitLocation: {kvp.Key} -> {kvp.Value}\r\n");
|
||||
//Log.WriteDebug(sb.ToString());
|
||||
|
||||
result = new DetectionPenaltyResult()
|
||||
{
|
||||
@ -356,31 +372,31 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
|
||||
Tracker.OnChange(new EFACSnapshot()
|
||||
{
|
||||
When = kill.When,
|
||||
When = hit.When,
|
||||
ClientId = ClientStats.ClientId,
|
||||
SessionAngleOffset = AngleDifferenceAverage,
|
||||
CurrentSessionLength = (int)(DateTime.UtcNow - ConnectionTime).TotalSeconds,
|
||||
CurrentStrain = currentStrain,
|
||||
CurrentViewAngle = kill.ViewAngles,
|
||||
CurrentViewAngle = hit.ViewAngles,
|
||||
Hits = HitCount,
|
||||
Kills = Kills,
|
||||
Deaths = ClientStats.SessionDeaths,
|
||||
HitDestinationId = kill.DeathOrigin.Vector3Id,
|
||||
HitDestination = kill.DeathOrigin,
|
||||
HitOriginId = kill.KillOrigin.Vector3Id,
|
||||
HitOrigin = kill.KillOrigin,
|
||||
HitDestinationId = hit.DeathOrigin.Vector3Id,
|
||||
HitDestination = hit.DeathOrigin,
|
||||
HitOriginId = hit.KillOrigin.Vector3Id,
|
||||
HitOrigin = hit.KillOrigin,
|
||||
EloRating = ClientStats.EloRating,
|
||||
HitLocation = kill.HitLoc,
|
||||
HitLocation = hit.HitLoc,
|
||||
LastStrainAngle = Strain.LastAngle,
|
||||
PredictedViewAngles = kill.AnglesList,
|
||||
PredictedViewAngles = hit.AnglesList,
|
||||
// this is in "meters"
|
||||
Distance = kill.Distance,
|
||||
Distance = hit.Distance,
|
||||
SessionScore = ClientStats.SessionScore,
|
||||
HitType = kill.DeathType,
|
||||
HitType = hit.DeathType,
|
||||
SessionSPM = ClientStats.SessionSPM,
|
||||
StrainAngleBetween = Strain.LastDistance,
|
||||
TimeSinceLastEvent = (int)Strain.LastDeltaTime,
|
||||
WeaponId = kill.Weapon
|
||||
WeaponId = hit.Weapon
|
||||
});
|
||||
|
||||
return result ?? new DetectionPenaltyResult()
|
||||
@ -409,15 +425,15 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
|
||||
if (currentChestAbdomenRatio > chestAbdomenLerpValueForBan)
|
||||
{
|
||||
Log.WriteDebug("**Maximum Lifetime Chest/Abdomen Ratio Reached For Ban**");
|
||||
Log.WriteDebug($"ClientId: {stats.ClientId}");
|
||||
Log.WriteDebug($"**Total Chest Hits: {totalChestHits}");
|
||||
Log.WriteDebug($"**Ratio {currentChestAbdomenRatio}");
|
||||
Log.WriteDebug($"**MaxRatio {chestAbdomenLerpValueForBan}");
|
||||
var sb = new StringBuilder();
|
||||
foreach (var location in stats.HitLocations)
|
||||
sb.Append($"HitLocation: {location.Location} -> {location.HitCount}\r\n");
|
||||
Log.WriteDebug(sb.ToString());
|
||||
//Log.WriteDebug("**Maximum Lifetime Chest/Abdomen Ratio Reached For Ban**");
|
||||
//Log.WriteDebug($"ClientId: {stats.ClientId}");
|
||||
//Log.WriteDebug($"**Total Chest Hits: {totalChestHits}");
|
||||
//Log.WriteDebug($"**Ratio {currentChestAbdomenRatio}");
|
||||
//Log.WriteDebug($"**MaxRatio {chestAbdomenLerpValueForBan}");
|
||||
//var sb = new StringBuilder();
|
||||
//foreach (var location in stats.HitLocations)
|
||||
// sb.Append($"HitLocation: {location.Location} -> {location.HitCount}\r\n");
|
||||
//Log.WriteDebug(sb.ToString());
|
||||
|
||||
return new DetectionPenaltyResult()
|
||||
{
|
||||
@ -430,15 +446,15 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.WriteDebug("**Maximum Lifetime Chest/Abdomen Ratio Reached For Flag**");
|
||||
Log.WriteDebug($"ClientId: {stats.ClientId}");
|
||||
Log.WriteDebug($"**Total Chest Hits: {totalChestHits}");
|
||||
Log.WriteDebug($"**Ratio {currentChestAbdomenRatio}");
|
||||
Log.WriteDebug($"**MaxRatio {chestAbdomenRatioLerpValueForFlag}");
|
||||
var sb = new StringBuilder();
|
||||
foreach (var location in stats.HitLocations)
|
||||
sb.Append($"HitLocation: {location.Location} -> {location.HitCount}\r\n");
|
||||
Log.WriteDebug(sb.ToString());
|
||||
//Log.WriteDebug("**Maximum Lifetime Chest/Abdomen Ratio Reached For Flag**");
|
||||
//Log.WriteDebug($"ClientId: {stats.ClientId}");
|
||||
//Log.WriteDebug($"**Total Chest Hits: {totalChestHits}");
|
||||
//Log.WriteDebug($"**Ratio {currentChestAbdomenRatio}");
|
||||
//Log.WriteDebug($"**MaxRatio {chestAbdomenRatioLerpValueForFlag}");
|
||||
//var sb = new StringBuilder();
|
||||
//foreach (var location in stats.HitLocations)
|
||||
// sb.Append($"HitLocation: {location.Location} -> {location.HitCount}\r\n");
|
||||
//Log.WriteDebug(sb.ToString());
|
||||
|
||||
return new DetectionPenaltyResult()
|
||||
{
|
||||
|
@ -14,8 +14,6 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
public Vector3 LastAngle { get; private set; }
|
||||
public double LastDeltaTime { get; private set; }
|
||||
|
||||
public int TimesReachedMaxStrain { get; private set; }
|
||||
|
||||
public double GetStrain(bool isDamage, int damage, double killDistance, Vector3 newAngle, double deltaTime)
|
||||
{
|
||||
if (LastAngle == null)
|
||||
@ -25,14 +23,15 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
|
||||
double decayFactor = GetDecay(deltaTime);
|
||||
CurrentStrain *= decayFactor;
|
||||
|
||||
#if DEBUG
|
||||
Console.WriteLine($"Decay Factor = {decayFactor} ");
|
||||
#endif
|
||||
|
||||
double[] distance = Helpers.Extensions.AngleStuff(newAngle, LastAngle);
|
||||
LastDistance = distance[0] + distance[1];
|
||||
|
||||
#if DEBUG == true
|
||||
Console.WriteLine($"Angle Between = {LastDistance}");
|
||||
Console.WriteLine($"Distance From Target = {killDistance}");
|
||||
Console.WriteLine($"Time Offset = {deltaTime}");
|
||||
Console.WriteLine($"Decay Factor = {decayFactor} ");
|
||||
#endif
|
||||
// this happens on first kill
|
||||
if ((distance[0] == 0 && distance[1] == 0) ||
|
||||
deltaTime == 0 ||
|
||||
@ -41,14 +40,11 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
return CurrentStrain;
|
||||
}
|
||||
|
||||
double newStrain = Math.Pow(distance[0] + distance[1], 0.99) / deltaTime;
|
||||
double newStrain = Math.Pow(LastDistance, 0.99) / deltaTime;
|
||||
newStrain *= killDistance / 1000.0;
|
||||
|
||||
CurrentStrain += newStrain;
|
||||
|
||||
if (CurrentStrain > Thresholds.MaxStrainBan)
|
||||
TimesReachedMaxStrain++;
|
||||
|
||||
LastAngle = newAngle;
|
||||
return CurrentStrain;
|
||||
}
|
||||
|
@ -27,9 +27,8 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
||||
public const int HighSampleMinKills = 100;
|
||||
public const double KillTimeThreshold = 0.2;
|
||||
|
||||
public const double MaxStrainBan = 1.12;
|
||||
public const double MaxStrainBan = 0.9;
|
||||
|
||||
//=exp((MAX(-3.07+(-3.07/sqrt(J20)),-3.07-(-3.07/sqrt(J20))))+(4*(0.869)))
|
||||
public static double MaxOffset(int sampleSize) => Math.Exp(Math.Max(-3.07 + (-3.07 / Math.Sqrt(sampleSize)), -3.07 - (-3.07 / Math.Sqrt(sampleSize))) + 4 * (0.869));
|
||||
public const double MaxStrainFlag = 0.36;
|
||||
|
||||
|
@ -8,6 +8,8 @@ using SharedLibraryCore.Objects;
|
||||
using IW4MAdmin.Plugins.Stats.Models;
|
||||
using SharedLibraryCore.Database;
|
||||
using System.Collections.Generic;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
{
|
||||
@ -15,7 +17,8 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
{
|
||||
public static async Task<List<string>> GetMostPlayed(Server s)
|
||||
{
|
||||
int serverId = s.GetHashCode();
|
||||
long serverId = await StatManager.GetIdForServer(s);
|
||||
|
||||
List<string> mostPlayed = new List<string>()
|
||||
{
|
||||
$"^5--{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_MOSTPLAYED_TEXT"]}--"
|
||||
@ -34,7 +37,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
join alias in db.Aliases
|
||||
on client.CurrentAliasId equals alias.AliasId
|
||||
where stats.ServerId == serverId
|
||||
where client.Level != Player.Permission.Banned
|
||||
where client.Level != EFClient.Permission.Banned
|
||||
where client.LastConnection >= thirtyDaysAgo
|
||||
orderby stats.Kills descending
|
||||
select new
|
||||
@ -55,7 +58,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
return mostPlayed;
|
||||
}
|
||||
|
||||
public MostPlayed() : base("mostplayed", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_MOSTPLAYED_DESC"], "mp", Player.Permission.User, false) { }
|
||||
public MostPlayed() : base("mostplayed", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_MOSTPLAYED_DESC"], "mp", EFClient.Permission.User, false) { }
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
|
@ -1,32 +1,30 @@
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Objects;
|
||||
using IW4MAdmin.Plugins.Stats.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharedLibraryCore.Database;
|
||||
using IW4MAdmin.Plugins.Stats.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Database;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
{
|
||||
public class ResetStats : Command
|
||||
{
|
||||
public ResetStats() : base("resetstats", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_RESET_DESC"], "rs", Player.Permission.User, false) { }
|
||||
public ResetStats() : base("resetstats", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_RESET_DESC"], "rs", EFClient.Permission.User, false) { }
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
if (E.Origin.ClientNumber >= 0)
|
||||
{
|
||||
|
||||
int serverId = E.Owner.GetHashCode();
|
||||
long serverId = await Helpers.StatManager.GetIdForServer(E.Owner);
|
||||
|
||||
EFClientStatistics clientStats;
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
clientStats = await ctx.Set<EFClientStatistics>()
|
||||
.Where(s => s.ClientId == E.Origin.ClientId && s.ServerId == serverId)
|
||||
.Where(s => s.ClientId == E.Origin.ClientId)
|
||||
.Where(s => s.ServerId == serverId)
|
||||
.FirstAsync();
|
||||
|
||||
clientStats.Deaths = 0;
|
||||
@ -38,7 +36,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
clientStats.EloRating = 200.0;
|
||||
|
||||
// reset the cached version
|
||||
Plugin.Manager.ResetStats(E.Origin.ClientId, E.Owner.GetHashCode());
|
||||
Plugin.Manager.ResetStats(E.Origin.ClientId, serverId);
|
||||
|
||||
// fixme: this doesn't work properly when another context exists
|
||||
await ctx.SaveChangesAsync();
|
||||
|
@ -9,15 +9,16 @@ using SharedLibraryCore.Services;
|
||||
using IW4MAdmin.Plugins.Stats.Models;
|
||||
using SharedLibraryCore.Database;
|
||||
using System.Collections.Generic;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
{
|
||||
class TopStats : Command
|
||||
{
|
||||
|
||||
public static async Task<List<string>> GetTopStats(Server s)
|
||||
{
|
||||
int serverId = s.GetHashCode();
|
||||
long serverId = await StatManager.GetIdForServer(s);
|
||||
List<string> topStatsText = new List<string>()
|
||||
{
|
||||
$"^5--{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_TOP_TEXT"]}--"
|
||||
@ -34,7 +35,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
on client.CurrentAliasId equals alias.AliasId
|
||||
where stats.ServerId == serverId
|
||||
where stats.TimePlayed >= Plugin.Config.Configuration().TopPlayersMinPlayTime
|
||||
where client.Level != Player.Permission.Banned
|
||||
where client.Level != EFClient.Permission.Banned
|
||||
where client.LastConnection >= fifteenDaysAgo
|
||||
orderby stats.Performance descending
|
||||
select new
|
||||
@ -67,7 +68,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
return topStatsText;
|
||||
}
|
||||
|
||||
public TopStats() : base("topstats", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_TOP_DESC"], "ts", Player.Permission.User, false) { }
|
||||
public TopStats() : base("topstats", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_TOP_DESC"], "ts", EFClient.Permission.User, false) { }
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
|
@ -9,12 +9,14 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharedLibraryCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
{
|
||||
public class CViewStats : Command
|
||||
{
|
||||
public CViewStats() : base("stats", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_VIEW_DESC"], "xlrstats", Player.Permission.User, false, new CommandArgument[]
|
||||
public CViewStats() : base("stats", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_VIEW_DESC"], "xlrstats", EFClient.Permission.User, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -41,20 +43,26 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
}
|
||||
}
|
||||
|
||||
int serverId = E.Owner.GetHashCode();
|
||||
long serverId = await StatManager.GetIdForServer(E.Owner);
|
||||
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
if (E.Target != null)
|
||||
{
|
||||
int performanceRanking = await StatManager.GetClientOverallRanking(E.Target.ClientId);
|
||||
string performanceRankingString = performanceRanking == 0 ? loc["WEBFRONT_STATS_INDEX_UNRANKED"] : $"{loc["WEBFRONT_STATS_INDEX_RANKED"]} #{performanceRanking}";
|
||||
|
||||
pStats = (await ctx.Set<EFClientStatistics>().FirstAsync(c => c.ServerId == serverId && c.ClientId == E.Target.ClientId));
|
||||
statLine = $"^5{pStats.Kills} ^7{loc["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{loc["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{loc["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()}";
|
||||
statLine = $"^5{pStats.Kills} ^7{loc["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{loc["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{loc["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()} | {performanceRankingString}";
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
int performanceRanking = await StatManager.GetClientOverallRanking(E.Origin.ClientId);
|
||||
string performanceRankingString = performanceRanking == 0 ? loc["WEBFRONT_STATS_INDEX_UNRANKED"] : $"{loc["WEBFRONT_STATS_INDEX_RANKED"]} #{performanceRanking}";
|
||||
|
||||
pStats = (await ctx.Set<EFClientStatistics>().FirstAsync((c => c.ServerId == serverId && c.ClientId == E.Origin.ClientId)));
|
||||
statLine = $"^5{pStats.Kills} ^7{loc["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{loc["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{loc["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()}";
|
||||
statLine = $"^5{pStats.Kills} ^7{loc["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{loc["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{loc["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()} | {performanceRankingString}";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,53 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using IW4MAdmin.Plugins.Stats.Cheat;
|
||||
using IW4MAdmin.Plugins.Stats.Models;
|
||||
using IW4MAdmin.Plugins.Stats.Web.Dtos;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Database;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Objects;
|
||||
using SharedLibraryCore.Commands;
|
||||
using IW4MAdmin.Plugins.Stats.Models;
|
||||
using System.Text.RegularExpressions;
|
||||
using IW4MAdmin.Plugins.Stats.Web.Dtos;
|
||||
using SharedLibraryCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Services;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
{
|
||||
public class StatManager
|
||||
{
|
||||
private ConcurrentDictionary<int, ServerStats> Servers;
|
||||
private ConcurrentDictionary<long, ServerStats> Servers;
|
||||
private ILogger Log;
|
||||
private readonly IManager Manager;
|
||||
|
||||
|
||||
private readonly SemaphoreSlim OnProcessingPenalty;
|
||||
private readonly SemaphoreSlim OnProcessingSensitive;
|
||||
|
||||
public StatManager(IManager mgr)
|
||||
{
|
||||
Servers = new ConcurrentDictionary<int, ServerStats>();
|
||||
Servers = new ConcurrentDictionary<long, ServerStats>();
|
||||
Log = mgr.GetLogger(0);
|
||||
Manager = mgr;
|
||||
OnProcessingPenalty = new SemaphoreSlim(1, 1);
|
||||
OnProcessingSensitive = new SemaphoreSlim(1, 1);
|
||||
}
|
||||
|
||||
public EFClientStatistics GetClientStats(int clientId, int serverId) => Servers[serverId].PlayerStats[clientId];
|
||||
public EFClientStatistics GetClientStats(int clientId, long serverId)
|
||||
{
|
||||
return Servers[serverId].PlayerStats[clientId];
|
||||
}
|
||||
|
||||
public static Expression<Func<EFRating, bool>> GetRankingFunc(int? serverId = null)
|
||||
public static Expression<Func<EFRating, bool>> GetRankingFunc(long? serverId = null)
|
||||
{
|
||||
var fifteenDaysAgo = DateTime.UtcNow.AddDays(-15);
|
||||
return (r) => r.ServerId == serverId &&
|
||||
r.When > fifteenDaysAgo &&
|
||||
r.RatingHistory.Client.Level != Player.Permission.Banned &&
|
||||
r.RatingHistory.Client.Level != EFClient.Permission.Banned &&
|
||||
r.Newest &&
|
||||
r.ActivityAmount >= Plugin.Config.Configuration().TopPlayersMinPlayTime;
|
||||
}
|
||||
@ -57,7 +57,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
/// </summary>
|
||||
/// <param name="clientId">client id of the player</param>
|
||||
/// <returns></returns>
|
||||
public async Task<int> GetClientOverallRanking(int clientId)
|
||||
public static async Task<int> GetClientOverallRanking(int clientId)
|
||||
{
|
||||
using (var context = new DatabaseContext(true))
|
||||
{
|
||||
@ -191,21 +191,36 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
// insert the server if it does not exist
|
||||
try
|
||||
{
|
||||
int serverId = sv.GetHashCode();
|
||||
long serverId = GetIdForServer(sv).Result;
|
||||
EFServer server;
|
||||
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
var serverSet = ctx.Set<EFServer>();
|
||||
// get the server from the database if it exists, otherwise create and insert a new one
|
||||
server = serverSet.FirstOrDefault(c => c.ServerId == serverId);
|
||||
server = serverSet.FirstOrDefault(s => s.ServerId == serverId);
|
||||
|
||||
// the server might be using legacy server id
|
||||
if (server == null)
|
||||
{
|
||||
server = serverSet.FirstOrDefault(s => s.EndPoint == sv.ToString());
|
||||
|
||||
if (server != null)
|
||||
{
|
||||
// this provides a way to identify legacy server entries
|
||||
server.EndPoint = sv.ToString();
|
||||
ctx.Update(server);
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
// server has never been added before
|
||||
if (server == null)
|
||||
{
|
||||
server = new EFServer()
|
||||
{
|
||||
Port = sv.GetPort(),
|
||||
Active = true,
|
||||
EndPoint = sv.ToString(),
|
||||
ServerId = serverId
|
||||
};
|
||||
|
||||
@ -216,7 +231,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
}
|
||||
|
||||
// check to see if the stats have ever been initialized
|
||||
var serverStats = InitializeServerStats(sv);
|
||||
var serverStats = InitializeServerStats(server.ServerId);
|
||||
|
||||
Servers.TryAdd(serverId, new ServerStats(server, serverStats)
|
||||
{
|
||||
@ -227,6 +242,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_ERROR_ADD"]} - {e.Message}");
|
||||
Log.WriteDebug(e.GetExceptionInfo());
|
||||
}
|
||||
}
|
||||
|
||||
@ -235,14 +251,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
/// </summary>
|
||||
/// <param name="pl">Player to add/retrieve stats for</param>
|
||||
/// <returns>EFClientStatistic of specified player</returns>
|
||||
public async Task<EFClientStatistics> AddPlayer(Player pl)
|
||||
public async Task<EFClientStatistics> AddPlayer(EFClient pl)
|
||||
{
|
||||
await OnProcessingSensitive.WaitAsync();
|
||||
|
||||
try
|
||||
{
|
||||
int serverId = pl.CurrentServer.GetHashCode();
|
||||
|
||||
long serverId = await GetIdForServer(pl.CurrentServer);
|
||||
|
||||
if (!Servers.ContainsKey(serverId))
|
||||
{
|
||||
@ -299,7 +314,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
{
|
||||
Log.WriteWarning("Adding new client to stats failed");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
@ -348,7 +362,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
Log.WriteWarning("Could not add client to detection");
|
||||
}
|
||||
|
||||
Log.WriteInfo($"Adding {pl} to stats");
|
||||
pl.CurrentServer.Logger.WriteInfo($"Adding {pl} to stats");
|
||||
}
|
||||
|
||||
return clientStats;
|
||||
@ -373,21 +387,21 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
/// </summary>
|
||||
/// <param name="pl">Disconnecting client</param>
|
||||
/// <returns></returns>
|
||||
public async Task RemovePlayer(Player pl)
|
||||
public async Task RemovePlayer(EFClient pl)
|
||||
{
|
||||
Log.WriteInfo($"Removing {pl} from stats");
|
||||
pl.CurrentServer.Logger.WriteInfo($"Removing {pl} from stats");
|
||||
|
||||
int serverId = pl.CurrentServer.GetHashCode();
|
||||
long serverId = await GetIdForServer(pl.CurrentServer);
|
||||
var playerStats = Servers[serverId].PlayerStats;
|
||||
var detectionStats = Servers[serverId].PlayerDetections;
|
||||
var serverStats = Servers[serverId].ServerStatistics;
|
||||
|
||||
if (!playerStats.ContainsKey(pl.ClientId))
|
||||
{
|
||||
Log.WriteWarning($"Client disconnecting not in stats {pl}");
|
||||
pl.CurrentServer.Logger.WriteWarning($"Client disconnecting not in stats {pl}");
|
||||
// remove the client from the stats dictionary as they're leaving
|
||||
playerStats.TryRemove(pl.ClientId, out EFClientStatistics removedValue1);
|
||||
detectionStats.TryRemove(pl.ClientId, out Cheat.Detection removedValue2);
|
||||
detectionStats.TryRemove(pl.ClientId, out Detection removedValue2);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -396,7 +410,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
// remove the client from the stats dictionary as they're leaving
|
||||
playerStats.TryRemove(pl.ClientId, out EFClientStatistics removedValue3);
|
||||
detectionStats.TryRemove(pl.ClientId, out Cheat.Detection removedValue4);
|
||||
detectionStats.TryRemove(pl.ClientId, out Detection removedValue4);
|
||||
|
||||
// sync their stats before they leave
|
||||
clientStats = UpdateStats(clientStats);
|
||||
@ -408,10 +422,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
}
|
||||
|
||||
// increment the total play time
|
||||
serverStats.TotalPlayTime += (int)(DateTime.UtcNow - pl.LastConnection).TotalSeconds;
|
||||
serverStats.TotalPlayTime += pl.ConnectionLength;
|
||||
}
|
||||
|
||||
public void AddDamageEvent(string eventLine, int attackerClientId, int victimClientId, int serverId)
|
||||
public void AddDamageEvent(string eventLine, int attackerClientId, int victimClientId, long serverId)
|
||||
{
|
||||
string regex = @"^(D);(.+);([0-9]+);(allies|axis);(.+);([0-9]+);(allies|axis);(.+);(.+);([0-9]+);(.+);(.+)$";
|
||||
var match = Regex.Match(eventLine, regex, RegexOptions.IgnoreCase);
|
||||
@ -432,7 +446,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
/// Process stats for kill event
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task AddScriptHit(bool isDamage, DateTime time, Player attacker, Player victim, int serverId, string map, string hitLoc, string type,
|
||||
public async Task AddScriptHit(bool isDamage, DateTime time, EFClient attacker, EFClient victim, long serverId, string map, string hitLoc, string type,
|
||||
string damage, string weapon, string killOrigin, string deathOrigin, string viewAngles, string offset, string isKillstreakKill, string Ads,
|
||||
string fraction, string visibilityPercentage, string snapAngles)
|
||||
{
|
||||
@ -548,7 +562,22 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
if (Plugin.Config.Configuration().EnableAntiCheat)
|
||||
{
|
||||
ApplyPenalty(clientDetection.ProcessKill(hit, isDamage), clientDetection, attacker, ctx);
|
||||
if (clientDetection.QueuedHits.Count > Detection.QUEUE_COUNT)
|
||||
{
|
||||
while (clientDetection.QueuedHits.Count > 0)
|
||||
{
|
||||
clientDetection.QueuedHits = clientDetection.QueuedHits.OrderBy(_hits => _hits.TimeOffset).ToList();
|
||||
var oldestHit = clientDetection.QueuedHits.First();
|
||||
clientDetection.QueuedHits.RemoveAt(0);
|
||||
ApplyPenalty(clientDetection.ProcessHit(oldestHit, isDamage), clientDetection, attacker, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
clientDetection.QueuedHits.Add(hit);
|
||||
}
|
||||
|
||||
ApplyPenalty(clientDetection.ProcessTotalRatio(clientStats), clientDetection, attacker, ctx);
|
||||
}
|
||||
|
||||
@ -567,16 +596,16 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyPenalty(Cheat.DetectionPenaltyResult penalty, Cheat.Detection clientDetection, Player attacker, DatabaseContext ctx)
|
||||
void ApplyPenalty(DetectionPenaltyResult penalty, Detection clientDetection, EFClient attacker, DatabaseContext ctx)
|
||||
{
|
||||
switch (penalty.ClientPenalty)
|
||||
{
|
||||
case Penalty.PenaltyType.Ban:
|
||||
if (attacker.Level == Player.Permission.Banned)
|
||||
if (attacker.Level == EFClient.Permission.Banned)
|
||||
{
|
||||
break;
|
||||
}
|
||||
attacker.Ban(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_CHEAT_DETECTED"], new Player()
|
||||
attacker.Ban(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_CHEAT_DETECTED"], new EFClient()
|
||||
{
|
||||
ClientId = 1,
|
||||
AdministeredPenalties = new List<EFPenalty>()
|
||||
@ -588,7 +617,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
$"{penalty.Type}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}",
|
||||
}
|
||||
},
|
||||
Level = Player.Permission.Console,
|
||||
Level = EFClient.Permission.Console,
|
||||
CurrentServer = attacker.CurrentServer
|
||||
});
|
||||
if (clientDetection.Tracker.HasChanges)
|
||||
@ -597,7 +626,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
}
|
||||
break;
|
||||
case Penalty.PenaltyType.Flag:
|
||||
if (attacker.Level != Player.Permission.User)
|
||||
if (attacker.Level != EFClient.Permission.User)
|
||||
{
|
||||
break;
|
||||
}
|
||||
@ -606,10 +635,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
$"{penalty.Type}-{(int)penalty.Location}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}" :
|
||||
$"{penalty.Type}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}";
|
||||
|
||||
attacker.Flag(flagReason, new Player()
|
||||
attacker.Flag(flagReason, new EFClient()
|
||||
{
|
||||
ClientId = 1,
|
||||
Level = Player.Permission.Console,
|
||||
Level = EFClient.Permission.Console,
|
||||
CurrentServer = attacker.CurrentServer,
|
||||
});
|
||||
|
||||
@ -677,9 +706,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
public async Task AddStandardKill(Player attacker, Player victim)
|
||||
public async Task AddStandardKill(EFClient attacker, EFClient victim)
|
||||
{
|
||||
int serverId = attacker.CurrentServer.GetHashCode();
|
||||
long serverId = await GetIdForServer(attacker.CurrentServer);
|
||||
|
||||
EFClientStatistics attackerStats = null;
|
||||
if (!Servers[serverId].PlayerStats.ContainsKey(attacker.ClientId))
|
||||
@ -709,13 +738,18 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
// update the total stats
|
||||
Servers[serverId].ServerStatistics.TotalKills += 1;
|
||||
await Sync(attacker.CurrentServer);
|
||||
|
||||
// this happens when the round has changed
|
||||
if (attackerStats.SessionScore == 0)
|
||||
{
|
||||
attackerStats.LastScore = 0;
|
||||
}
|
||||
|
||||
if (victimStats.SessionScore == 0)
|
||||
{
|
||||
victimStats.LastScore = 0;
|
||||
}
|
||||
|
||||
attackerStats.SessionScore = attacker.Score;
|
||||
victimStats.SessionScore = victim.Score;
|
||||
@ -768,8 +802,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
{
|
||||
var clientStatsSet = ctx.Set<EFClientStatistics>();
|
||||
|
||||
clientStatsSet.Update(attackerStats);
|
||||
clientStatsSet.Update(victimStats);
|
||||
clientStatsSet.Attach(attackerStats).State = EntityState.Modified;
|
||||
clientStatsSet.Attach(victimStats).State = EntityState.Modified;
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
@ -780,7 +814,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
/// <param name="client">client to update</param>
|
||||
/// <param name="clientStats">stats of client that is being updated</param>
|
||||
/// <returns></returns>
|
||||
private async Task UpdateStatHistory(Player client, EFClientStatistics clientStats)
|
||||
private async Task UpdateStatHistory(EFClient client, EFClientStatistics clientStats)
|
||||
{
|
||||
int currentSessionTime = (int)(DateTime.UtcNow - client.LastConnection).TotalSeconds;
|
||||
|
||||
@ -979,6 +1013,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
// calulate elo
|
||||
if (Servers[attackerStats.ServerId].PlayerStats.Count > 1)
|
||||
{
|
||||
#region DEPRECATED
|
||||
/* var validAttackerLobbyRatings = Servers[attackerStats.ServerId].PlayerStats
|
||||
.Where(cs => cs.Value.ClientId != attackerStats.ClientId)
|
||||
.Where(cs =>
|
||||
@ -1002,6 +1037,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
double victimLobbyRating = validVictimLobbyRatings.Count() > 0 ?
|
||||
validVictimLobbyRatings.Average(cs => cs.Value.EloRating) :
|
||||
victimStats.EloRating;*/
|
||||
#endregion
|
||||
|
||||
double attackerEloDifference = Math.Log(Math.Max(1, victimStats.EloRating)) - Math.Log(Math.Max(1, attackerStats.EloRating));
|
||||
double winPercentage = 1.0 / (1 + Math.Pow(10, attackerEloDifference / Math.E));
|
||||
@ -1104,9 +1140,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
return clientStats;
|
||||
}
|
||||
|
||||
public EFServerStatistics InitializeServerStats(Server sv)
|
||||
public EFServerStatistics InitializeServerStats(long serverId)
|
||||
{
|
||||
int serverId = sv.GetHashCode();
|
||||
EFServerStatistics serverStats;
|
||||
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
@ -1116,7 +1151,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
if (serverStats == null)
|
||||
{
|
||||
Log.WriteDebug($"Initializing server stats for {sv}");
|
||||
Log.WriteDebug($"Initializing server stats for {serverId}");
|
||||
// server stats have never been generated before
|
||||
serverStats = new EFServerStatistics()
|
||||
{
|
||||
@ -1133,7 +1168,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
return serverStats;
|
||||
}
|
||||
|
||||
public void ResetKillstreaks(int serverId)
|
||||
public void ResetKillstreaks(long serverId)
|
||||
{
|
||||
var serverStats = Servers[serverId];
|
||||
|
||||
@ -1143,7 +1178,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetStats(int clientId, int serverId)
|
||||
public void ResetStats(int clientId, long serverId)
|
||||
{
|
||||
var stats = Servers[serverId].PlayerStats[clientId];
|
||||
stats.Kills = 0;
|
||||
@ -1154,11 +1189,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
stats.EloRating = 200;
|
||||
}
|
||||
|
||||
public async Task AddMessageAsync(int clientId, int serverId, string message)
|
||||
public async Task AddMessageAsync(int clientId, long serverId, string message)
|
||||
{
|
||||
// the web users can have no account
|
||||
if (clientId < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
@ -1176,19 +1213,64 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
public async Task Sync(Server sv)
|
||||
{
|
||||
int serverId = sv.GetHashCode();
|
||||
long serverId = await GetIdForServer(sv);
|
||||
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
var serverSet = ctx.Set<EFServer>();
|
||||
serverSet.Update(Servers[serverId].Server);
|
||||
|
||||
var serverStatsSet = ctx.Set<EFServerStatistics>();
|
||||
serverStatsSet.Update(Servers[serverId].ServerStatistics);
|
||||
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTeamBased(int serverId, bool isTeamBased)
|
||||
public void SetTeamBased(long serverId, bool isTeamBased)
|
||||
{
|
||||
Servers[serverId].IsTeamBased = isTeamBased;
|
||||
}
|
||||
|
||||
public static async Task<long> GetIdForServer(Server server)
|
||||
{
|
||||
// hack: my laziness
|
||||
if ($"{server.IP}:{server.GetPort().ToString()}" == "66.150.121.184:28965")
|
||||
{
|
||||
return 886229536;
|
||||
}
|
||||
|
||||
else if ($"{server.IP}:{server.GetPort().ToString()}" == "66.150.121.184:28960")
|
||||
{
|
||||
return 1645744423;
|
||||
}
|
||||
|
||||
else if ($"{server.IP}:{server.GetPort().ToString()}" == "66.150.121.184:28970")
|
||||
{
|
||||
return 1645809959;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
long id = HashCode.Combine(server.IP, server.GetPort());
|
||||
id = id < 0 ? Math.Abs(id) : id;
|
||||
long? serverId;
|
||||
|
||||
// todo: cache this eventually, as it shouldn't change
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
serverId = (await ctx.Set<EFServer>().FirstOrDefaultAsync(_server => _server.ServerId == server.EndPoint ||
|
||||
_server.EndPoint == server.ToString() ||
|
||||
_server.ServerId == id))?.ServerId;
|
||||
}
|
||||
|
||||
if (!serverId.HasValue)
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
return serverId.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
||||
public int AttackerId { get; set; }
|
||||
[ForeignKey("AttackerId")]
|
||||
public virtual EFClient Attacker { get; set; }
|
||||
public int ServerId { get; set; }
|
||||
public long ServerId { get; set; }
|
||||
[ForeignKey("ServerId")]
|
||||
public virtual EFServer Server { get; set; }
|
||||
public IW4Info.HitLocation HitLoc { get; set; }
|
||||
|
@ -13,7 +13,7 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
||||
{
|
||||
[Key]
|
||||
public long MessageId { get; set; }
|
||||
public int ServerId { get; set; }
|
||||
public long ServerId { get; set; }
|
||||
[ForeignKey("ServerId")]
|
||||
public virtual EFServer Server { get; set; }
|
||||
public int ClientId { get; set; }
|
||||
|
@ -15,7 +15,7 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
||||
public int ClientId { get; set; }
|
||||
[ForeignKey("ClientId")]
|
||||
public virtual EFClient Client { get; set; }
|
||||
public int ServerId { get; set; }
|
||||
public long ServerId { get; set; }
|
||||
[ForeignKey("ServerId")]
|
||||
public virtual EFServer Server { get; set; }
|
||||
[Required]
|
||||
|
@ -20,7 +20,7 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
||||
public int ClientId { get; set; }
|
||||
[ForeignKey("ClientId"), Column(Order = 0 )]
|
||||
public EFClient Client { get; set; }
|
||||
public int ServerId { get; set; }
|
||||
public long ServerId { get; set; }
|
||||
[ForeignKey("ServerId"), Column(Order = 1)]
|
||||
public EFServer Server { get; set; }
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
||||
[ForeignKey("RatingHistoryId")]
|
||||
public virtual EFClientRatingHistory RatingHistory { get; set; }
|
||||
// if null, indicates that the rating is an average rating
|
||||
public int? ServerId { get; set; }
|
||||
public long? ServerId { get; set; }
|
||||
// [ForeignKey("ServerId")] can't make this nullable if this annotation is set
|
||||
public virtual EFServer Server { get; set; }
|
||||
[Required]
|
||||
|
@ -9,8 +9,9 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.None)]
|
||||
public int ServerId { get; set; }
|
||||
public long ServerId { get; set; }
|
||||
[Required]
|
||||
public int Port { get; set; }
|
||||
public string EndPoint { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
||||
{
|
||||
[Key]
|
||||
public int StatisticId { get; set; }
|
||||
public int ServerId { get; set; }
|
||||
public long ServerId { get; set; }
|
||||
[ForeignKey("ServerId")]
|
||||
public virtual EFServer Server { get; set; }
|
||||
public long TotalKills { get; set; }
|
||||
|
@ -1,7 +1,6 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
using IW4MAdmin.Plugins.Stats.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using IW4MAdmin.Plugins.Stats.Models;
|
||||
|
||||
namespace Stats.Models
|
||||
{
|
||||
|
@ -1,20 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
|
||||
using IW4MAdmin.Plugins.Stats.Config;
|
||||
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||
using IW4MAdmin.Plugins.Stats.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Database;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Services;
|
||||
using IW4MAdmin.Plugins.Stats.Config;
|
||||
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||
using IW4MAdmin.Plugins.Stats.Models;
|
||||
using SharedLibraryCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Stats
|
||||
{
|
||||
@ -44,16 +43,19 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
break;
|
||||
case GameEvent.EventType.Disconnect:
|
||||
await Manager.RemovePlayer(E.Origin);
|
||||
await Manager.Sync(S);
|
||||
break;
|
||||
case GameEvent.EventType.Say:
|
||||
if (!string.IsNullOrEmpty(E.Data) &&
|
||||
E.Origin.ClientId > 1)
|
||||
await Manager.AddMessageAsync(E.Origin.ClientId, E.Owner.GetHashCode(), E.Data);
|
||||
{
|
||||
await Manager.AddMessageAsync(E.Origin.ClientId, await StatManager.GetIdForServer(E.Owner), E.Data);
|
||||
}
|
||||
|
||||
break;
|
||||
case GameEvent.EventType.MapChange:
|
||||
Manager.SetTeamBased(E.Owner.GetHashCode(), E.Owner.Gametype != "dm");
|
||||
Manager.ResetKillstreaks(S.GetHashCode());
|
||||
await Manager.Sync(S);
|
||||
Manager.SetTeamBased(await StatManager.GetIdForServer(E.Owner), E.Owner.Gametype != "dm");
|
||||
Manager.ResetKillstreaks(await StatManager.GetIdForServer(E.Owner));
|
||||
break;
|
||||
case GameEvent.EventType.MapEnd:
|
||||
break;
|
||||
@ -77,7 +79,7 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
string[] killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
|
||||
if (killInfo.Length >= 14)
|
||||
{
|
||||
await Manager.AddScriptHit(false, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8],
|
||||
await Manager.AddScriptHit(false, E.Time, E.Origin, E.Target, await StatManager.GetIdForServer(E.Owner), S.CurrentMap.Name, killInfo[7], killInfo[8],
|
||||
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14], killInfo[15]);
|
||||
}
|
||||
break;
|
||||
@ -90,14 +92,14 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
case GameEvent.EventType.Damage:
|
||||
if (!E.Owner.CustomCallback)
|
||||
{
|
||||
Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Target.ClientId, E.Owner.GetHashCode());
|
||||
Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Target.ClientId, await StatManager.GetIdForServer(E.Owner));
|
||||
}
|
||||
break;
|
||||
case GameEvent.EventType.ScriptDamage:
|
||||
killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
|
||||
if (killInfo.Length >= 14)
|
||||
{
|
||||
await Manager.AddScriptHit(true, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8],
|
||||
await Manager.AddScriptHit(true, E.Time, E.Origin, E.Target, await StatManager.GetIdForServer(E.Owner), S.CurrentMap.Name, killInfo[7], killInfo[8],
|
||||
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14], killInfo[15]);
|
||||
}
|
||||
break;
|
||||
@ -143,7 +145,7 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
new ProfileMeta()
|
||||
{
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_RANKING"],
|
||||
Value = "#" + await Manager.GetClientOverallRanking(clientId),
|
||||
Value = "#" + await StatManager.GetClientOverallRanking(clientId),
|
||||
},
|
||||
new ProfileMeta()
|
||||
{
|
||||
@ -327,12 +329,17 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
Manager = new StatManager(manager);
|
||||
}
|
||||
|
||||
public Task OnTickAsync(Server S) => Task.CompletedTask;
|
||||
public Task OnTickAsync(Server S)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task OnUnloadAsync()
|
||||
{
|
||||
foreach (var sv in ServerManager.GetServers())
|
||||
{
|
||||
await Manager.Sync(sv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
using IW4MAdmin.Application;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Commands;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Objects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -25,7 +26,7 @@ namespace Tests
|
||||
[Fact]
|
||||
public void SetAdditionalPropertyShouldSucceed()
|
||||
{
|
||||
var client = new Player();
|
||||
var client = new EFClient();
|
||||
int newProp = 5;
|
||||
client.SetAdditionalProperty("NewProp", newProp);
|
||||
}
|
||||
@ -33,7 +34,7 @@ namespace Tests
|
||||
[Fact]
|
||||
public void GetAdditionalPropertyShouldSucceed()
|
||||
{
|
||||
var client = new Player();
|
||||
var client = new EFClient();
|
||||
int newProp = 5;
|
||||
client.SetAdditionalProperty("NewProp", newProp);
|
||||
|
||||
@ -48,11 +49,11 @@ namespace Tests
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
var client = Manager.Servers.First().GetPlayersAsList().FirstOrDefault();
|
||||
var client = Manager.Servers.First().GetClientsAsList().FirstOrDefault();
|
||||
|
||||
Assert.False(client == null, "no client found to warn");
|
||||
|
||||
var warnEvent = client.Warn("test warn", new Player() { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
var warnEvent = client.Warn("test warn", new EFClient() { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
warnEvent.OnProcessed.Wait();
|
||||
|
||||
Assert.True((client.Warnings == 1 ||
|
||||
@ -60,19 +61,19 @@ namespace Tests
|
||||
Manager.GetPenaltyService().GetClientPenaltiesAsync(client.ClientId).Result.Count(p => p.Type == Penalty.PenaltyType.Warning) == 1,
|
||||
"warning did not get applied");
|
||||
|
||||
warnEvent = client.Warn("test warn", new Player() { ClientId = 1, Level = Player.Permission.Banned, CurrentServer = client.CurrentServer });
|
||||
warnEvent = client.Warn("test warn", new EFClient() { ClientId = 1, Level = EFClient.Permission.Banned, CurrentServer = client.CurrentServer });
|
||||
warnEvent.OnProcessed.Wait();
|
||||
|
||||
Assert.True(warnEvent.FailReason == GameEvent.EventFailReason.Permission &&
|
||||
client.Warnings == 1, "warning was applied without proper permissions");
|
||||
|
||||
// warn clear
|
||||
var warnClearEvent = client.WarnClear(new Player { ClientId = 1, Level = Player.Permission.Banned, CurrentServer = client.CurrentServer });
|
||||
var warnClearEvent = client.WarnClear(new EFClient { ClientId = 1, Level = EFClient.Permission.Banned, CurrentServer = client.CurrentServer });
|
||||
|
||||
Assert.True(warnClearEvent.FailReason == GameEvent.EventFailReason.Permission &&
|
||||
client.Warnings == 1, "warning was removed without proper permissions");
|
||||
|
||||
warnClearEvent = client.WarnClear(new Player { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
warnClearEvent = client.WarnClear(new EFClient { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
|
||||
Assert.True(!warnClearEvent.Failed && client.Warnings == 0, "warning was not cleared");
|
||||
}
|
||||
@ -85,11 +86,11 @@ namespace Tests
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
var client = Manager.Servers.First().GetPlayersAsList().FirstOrDefault();
|
||||
var client = Manager.Servers.First().GetClientsAsList().FirstOrDefault();
|
||||
Assert.False(client == null, "no client found to report");
|
||||
|
||||
// fail
|
||||
var player = new Player() { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer };
|
||||
var player = new EFClient() { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer };
|
||||
player.SetAdditionalProperty("_reportCount", 3);
|
||||
var reportEvent = client.Report("test report", player);
|
||||
reportEvent.OnProcessed.Wait(TestTimeout);
|
||||
@ -98,14 +99,14 @@ namespace Tests
|
||||
client.CurrentServer.Reports.Count(r => r.Target.NetworkId == client.NetworkId) == 0, $"too many reports were applied [{reportEvent.FailReason.ToString()}]");
|
||||
|
||||
// succeed
|
||||
reportEvent = client.Report("test report", new Player() { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
reportEvent = client.Report("test report", new EFClient() { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
reportEvent.OnProcessed.Wait(TestTimeout);
|
||||
|
||||
Assert.True(!reportEvent.Failed &&
|
||||
client.CurrentServer.Reports.Count(r => r.Target.NetworkId == client.NetworkId) == 1, $"report was not applied [{reportEvent.FailReason.ToString()}]");
|
||||
|
||||
// fail
|
||||
reportEvent = client.Report("test report", new Player() { ClientId = 1, NetworkId = 1, Level = Player.Permission.Banned, CurrentServer = client.CurrentServer });
|
||||
reportEvent = client.Report("test report", new EFClient() { ClientId = 1, NetworkId = 1, Level = EFClient.Permission.Banned, CurrentServer = client.CurrentServer });
|
||||
|
||||
Assert.True(reportEvent.FailReason == GameEvent.EventFailReason.Permission &&
|
||||
client.CurrentServer.Reports.Count(r => r.Target.NetworkId == client.NetworkId) == 1,
|
||||
@ -118,7 +119,7 @@ namespace Tests
|
||||
client.CurrentServer.Reports.Count(r => r.Target.NetworkId == client.NetworkId) == 1, $"report was applied to self");
|
||||
|
||||
// fail
|
||||
reportEvent = client.Report("test report", new Player() { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
reportEvent = client.Report("test report", new EFClient() { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
|
||||
Assert.True(reportEvent.FailReason == GameEvent.EventFailReason.Exception &&
|
||||
client.CurrentServer.Reports.Count(r => r.Target.NetworkId == client.NetworkId) == 1, $"duplicate report was applied");
|
||||
@ -132,42 +133,42 @@ namespace Tests
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
var client = Manager.Servers.First().GetPlayersAsList().FirstOrDefault();
|
||||
var client = Manager.Servers.First().GetClientsAsList().FirstOrDefault();
|
||||
Assert.False(client == null, "no client found to flag");
|
||||
|
||||
var flagEvent = client.Flag("test flag", new Player { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
var flagEvent = client.Flag("test flag", new EFClient { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
flagEvent.OnProcessed.Wait();
|
||||
|
||||
// succeed
|
||||
Assert.True(!flagEvent.Failed &&
|
||||
client.Level == Player.Permission.Flagged, $"player is not flagged [{flagEvent.FailReason.ToString()}]");
|
||||
client.Level == EFClient.Permission.Flagged, $"player is not flagged [{flagEvent.FailReason.ToString()}]");
|
||||
Assert.False(client.ReceivedPenalties.FirstOrDefault(p => p.Offense == "test flag") == null, "flag was not applied");
|
||||
|
||||
flagEvent = client.Flag("test flag", new Player { ClientId = 1, Level = Player.Permission.Banned, CurrentServer = client.CurrentServer });
|
||||
flagEvent = client.Flag("test flag", new EFClient { ClientId = 1, Level = EFClient.Permission.Banned, CurrentServer = client.CurrentServer });
|
||||
flagEvent.OnProcessed.Wait();
|
||||
|
||||
// fail
|
||||
Assert.True(client.ReceivedPenalties.Count == 1, "flag was applied without permisions");
|
||||
|
||||
flagEvent = client.Flag("test flag", new Player { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
flagEvent = client.Flag("test flag", new EFClient { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
flagEvent.OnProcessed.Wait();
|
||||
|
||||
// fail
|
||||
Assert.True(client.ReceivedPenalties.Count == 1, "duplicate flag was applied");
|
||||
|
||||
var unflagEvent = client.Unflag("test unflag", new Player { ClientId = 1, Level = Player.Permission.Banned, CurrentServer = client.CurrentServer });
|
||||
var unflagEvent = client.Unflag("test unflag", new EFClient { ClientId = 1, Level = EFClient.Permission.Banned, CurrentServer = client.CurrentServer });
|
||||
unflagEvent.OnProcessed.Wait();
|
||||
|
||||
// fail
|
||||
Assert.False(client.Level == Player.Permission.User, "user was unflagged without permissions");
|
||||
Assert.False(client.Level == EFClient.Permission.User, "user was unflagged without permissions");
|
||||
|
||||
unflagEvent = client.Unflag("test unflag", new Player { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
unflagEvent = client.Unflag("test unflag", new EFClient { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
unflagEvent.OnProcessed.Wait();
|
||||
|
||||
// succeed
|
||||
Assert.True(client.Level == Player.Permission.User, "user was not unflagged");
|
||||
Assert.True(client.Level == EFClient.Permission.User, "user was not unflagged");
|
||||
|
||||
unflagEvent = client.Unflag("test unflag", new Player { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
unflagEvent = client.Unflag("test unflag", new EFClient { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
unflagEvent.OnProcessed.Wait();
|
||||
|
||||
// succeed
|
||||
@ -182,18 +183,18 @@ namespace Tests
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
var client = Manager.Servers.First().GetPlayersAsList().FirstOrDefault();
|
||||
var client = Manager.Servers.First().GetClientsAsList().FirstOrDefault();
|
||||
Assert.False(client == null, "no client found to kick");
|
||||
|
||||
var kickEvent = client.Kick("test kick", new Player() { ClientId = 1, Level = Player.Permission.Banned, CurrentServer = client.CurrentServer });
|
||||
var kickEvent = client.Kick("test kick", new EFClient() { ClientId = 1, Level = EFClient.Permission.Banned, CurrentServer = client.CurrentServer });
|
||||
kickEvent.OnProcessed.Wait();
|
||||
|
||||
Assert.True(kickEvent.FailReason == GameEvent.EventFailReason.Permission, "client was kicked without permission");
|
||||
|
||||
kickEvent = client.Kick("test kick", new Player() { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
kickEvent = client.Kick("test kick", new EFClient() { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
kickEvent.OnProcessed.Wait();
|
||||
|
||||
Assert.True(Manager.Servers.First().GetPlayersAsList().FirstOrDefault(c => c.NetworkId == client.NetworkId) == null, "client was not kicked");
|
||||
Assert.True(Manager.Servers.First().GetClientsAsList().FirstOrDefault(c => c.NetworkId == client.NetworkId) == null, "client was not kicked");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -204,13 +205,13 @@ namespace Tests
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
var client = Manager.Servers.First().GetPlayersAsList().FirstOrDefault();
|
||||
var client = Manager.Servers.First().GetClientsAsList().FirstOrDefault();
|
||||
Assert.False(client == null, "no client found to tempban");
|
||||
|
||||
var tbCommand = new CTempBan();
|
||||
tbCommand.ExecuteAsync(new GameEvent()
|
||||
{
|
||||
Origin = new Player() { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer },
|
||||
Origin = new EFClient() { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer },
|
||||
Target = client,
|
||||
Data = "5days test tempban",
|
||||
Type = GameEvent.EventType.Command,
|
||||
@ -229,13 +230,13 @@ namespace Tests
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
var client = Manager.Servers.First().GetPlayersAsList().FirstOrDefault();
|
||||
var client = Manager.Servers.First().GetClientsAsList().FirstOrDefault();
|
||||
Assert.False(client == null, "no client found to ban");
|
||||
|
||||
var banCommand = new CBan();
|
||||
banCommand.ExecuteAsync(new GameEvent()
|
||||
{
|
||||
Origin = new Player() { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer },
|
||||
Origin = new EFClient() { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer },
|
||||
Target = client,
|
||||
Data = "test ban",
|
||||
Type = GameEvent.EventType.Command,
|
||||
@ -248,8 +249,8 @@ namespace Tests
|
||||
var unbanCommand = new CUnban();
|
||||
unbanCommand.ExecuteAsync(new GameEvent()
|
||||
{
|
||||
Origin = new Player() { ClientId = 1, Level = Player.Permission.Console, CurrentServer = client.CurrentServer },
|
||||
Target = Manager.GetClientService().Find(c => c.NetworkId == client.NetworkId).Result.First().AsPlayer(),
|
||||
Origin = new EFClient() { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer },
|
||||
Target = Manager.GetClientService().Find(c => c.NetworkId == client.NetworkId).Result.First(),
|
||||
Data = "test unban",
|
||||
Type = GameEvent.EventType.Command,
|
||||
Owner = client.CurrentServer
|
||||
|
@ -1,5 +1,6 @@
|
||||
using IW4MAdmin.Application;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Objects;
|
||||
using System;
|
||||
@ -53,7 +54,7 @@ namespace Tests
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Join,
|
||||
Origin = new Player()
|
||||
Origin = new EFClient()
|
||||
{
|
||||
Name = $"Player{i}",
|
||||
NetworkId = i,
|
||||
@ -78,7 +79,7 @@ namespace Tests
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Disconnect,
|
||||
Origin = new Player()
|
||||
Origin = new EFClient()
|
||||
{
|
||||
Name = $"Player{i}",
|
||||
NetworkId = i,
|
||||
@ -113,7 +114,7 @@ namespace Tests
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Connect,
|
||||
Origin = new Player()
|
||||
Origin = new EFClient()
|
||||
{
|
||||
Name = $"Player{i}",
|
||||
NetworkId = i,
|
||||
@ -134,7 +135,7 @@ namespace Tests
|
||||
waiters.Dequeue().OnProcessed.Wait();
|
||||
}
|
||||
|
||||
int actualClientNum = server.GetPlayersAsList().Count(p => p.State == Player.ClientState.Connected);
|
||||
int actualClientNum = server.GetClientsAsList().Count(p => p.State == EFClient.ClientState.Connected);
|
||||
Assert.True(actualClientNum == clientNum, $"client connected states don't match [{actualClientNum}:{clientNum}");
|
||||
|
||||
for (int i = clientIndexStart; i < clientNum + clientIndexStart; i++)
|
||||
@ -142,7 +143,7 @@ namespace Tests
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Disconnect,
|
||||
Origin = new Player()
|
||||
Origin = new EFClient()
|
||||
{
|
||||
Name = $"Player{i}",
|
||||
NetworkId = i,
|
||||
|
@ -1,5 +1,6 @@
|
||||
using IW4MAdmin.Application;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Objects;
|
||||
using Xunit;
|
||||
|
||||
@ -21,7 +22,7 @@ namespace Tests
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Connect,
|
||||
Origin = new Player()
|
||||
Origin = new EFClient()
|
||||
{
|
||||
Name = $"Player1",
|
||||
NetworkId = 1,
|
||||
@ -33,7 +34,7 @@ namespace Tests
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
e.OnProcessed.Wait();
|
||||
|
||||
var client = Manager.GetServers()[0].Players[0];
|
||||
var client = Manager.GetServers()[0].Clients[0];
|
||||
|
||||
e = new GameEvent()
|
||||
{
|
||||
|
@ -13,12 +13,13 @@ using Microsoft.EntityFrameworkCore;
|
||||
using System.Net;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using static SharedLibraryCore.Database.Models.EFClient;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Welcome
|
||||
{
|
||||
public class Plugin : IPlugin
|
||||
{
|
||||
String TimesConnected(Player P)
|
||||
String TimesConnected(EFClient P)
|
||||
{
|
||||
int connection = P.Connections;
|
||||
String Prefix = String.Empty;
|
||||
@ -83,15 +84,15 @@ namespace IW4MAdmin.Plugins.Welcome
|
||||
|
||||
public async Task OnEventAsync(GameEvent E, Server S)
|
||||
{
|
||||
if (E.Type == GameEvent.EventType.Connect)
|
||||
if (E.Type == GameEvent.EventType.Join)
|
||||
{
|
||||
Player newPlayer = E.Origin;
|
||||
if (newPlayer.Level >= Player.Permission.Trusted && !E.Origin.Masked)
|
||||
EFClient newPlayer = E.Origin;
|
||||
if (newPlayer.Level >= Permission.Trusted && !E.Origin.Masked)
|
||||
E.Owner.Broadcast(await ProcessAnnouncement(Config.Configuration().PrivilegedAnnouncementMessage, newPlayer));
|
||||
|
||||
newPlayer.Tell(await ProcessAnnouncement(Config.Configuration().UserWelcomeMessage, newPlayer));
|
||||
|
||||
if (newPlayer.Level == Player.Permission.Flagged)
|
||||
if (newPlayer.Level == Permission.Flagged)
|
||||
{
|
||||
string penaltyReason;
|
||||
|
||||
@ -111,7 +112,7 @@ namespace IW4MAdmin.Plugins.Welcome
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> ProcessAnnouncement(string msg, Player joining)
|
||||
private async Task<string> ProcessAnnouncement(string msg, EFClient joining)
|
||||
{
|
||||
msg = msg.Replace("{{ClientName}}", joining.Name);
|
||||
msg = msg.Replace("{{ClientLevel}}", Utilities.ConvertLevelToColor(joining.Level, joining.ClientPermission.Name));
|
||||
|
42
README.md
42
README.md
@ -5,13 +5,15 @@ _______
|
||||
### About
|
||||
**IW4MAdmin** is an administration tool for [IW4x](https://iw4xcachep26muba.onion.link/), [Pluto T6](https://forum.plutonium.pw/category/33/plutonium-t6), ~~[Pluto IW5](https://forum.plutonium.pw/category/5/plutonium-iw5)~~, and most Call of Duty<74> dedicated servers. It allows complete control of your server; from changing maps, to banning players, **IW4MAdmin** monitors and records activity on your server(s). With plugin support, extending its functionality is a breeze.
|
||||
### Download
|
||||
Latest binary builds are always available at https://raidmax.org/IW4MAdmin
|
||||
Latest binary builds are always available at:
|
||||
- [RaidMax](https://raidmax.org/IW4MAdmin)
|
||||
- [GitHub](https://github.com/RaidMax/IW4M-Admin/releases)
|
||||
|
||||
---
|
||||
### Setup
|
||||
**IW4MAdmin** requires minimal effort to get up and running.
|
||||
#### Prerequisites
|
||||
* [.NET Core 2.1 Runtime](https://www.microsoft.com/net/download) *or newer*
|
||||
* [.NET Core 2.1.5 Runtime](https://www.microsoft.com/net/download) *or newer*
|
||||
#### Installation
|
||||
1. Install .NET Core Runtime
|
||||
2. Extract `IW4MAdmin-<version>.zip`
|
||||
@ -105,6 +107,14 @@ If you wish to further customize your experience of **IW4MAdmin**, the following
|
||||
`ConnectionString`
|
||||
* Specifies the [connection string](https://www.connectionstrings.com/mysql/) to a MySQL server that is used instead of SQLite
|
||||
* Default — `null`
|
||||
|
||||
`DatabaseProvider`
|
||||
* Specifies the database provider **IW4MAdmin** should use
|
||||
* Possible values — `sqlite`, `mysql`, `postgresql`
|
||||
* Default — `sqlite`
|
||||
|
||||
`Ignore Bots`
|
||||
* Disables bots from being registered by **IW4MAdmin**
|
||||
|
||||
`RConPollRate`
|
||||
* Specifies (in milliseconds) how often to poll each server for updates
|
||||
@ -163,9 +173,9 @@ If you wish to further customize your experience of **IW4MAdmin**, the following
|
||||
___
|
||||
|
||||
### Commands
|
||||
|Name |Alias|Description |Requires Target|Syntax |Required Level|
|
||||
|--------------| -----| --------------------------------------------------------| -----------------| -------------| ----------------|
|
||||
|prune|pa|demote any privileged clients that have not connected recently (defaults to 30 days)|False|!pa \<optional inactive days\>|Owner|
|
||||
|Name |Alias|Description |Requires Target|Syntax |Required Level|
|
||||
|--------------| -----| --------------------------------------------------------| -----------------| -------------| ---------------|
|
||||
|prune|pa|demote any trusted clients that have not connected recently (defaults to 30 days)|False|!pa \<optional inactive days\>|Owner|
|
||||
|quit|q|quit IW4MAdmin|False|!q |Owner|
|
||||
|rcon|rcon|send rcon command to server|False|!rcon \<commands\>|Owner|
|
||||
|ban|b|permanently ban a client from the server|True|!b \<player\> \<reason\>|SeniorAdmin|
|
||||
@ -190,24 +200,21 @@ ___
|
||||
|unflag|uf|Remove flag for client|True|!uf \<player\>|Moderator|
|
||||
|uptime|up|get current application running time|False|!up |Moderator|
|
||||
|usage|us|get application memory usage|False|!us |Moderator|
|
||||
|balance|bal|balance teams|False|!bal |Trusted|
|
||||
|login|li|login using password|False|!li \<password\>|Trusted|
|
||||
|warn|w|warn client for infringing rules|True|!w \<player\> \<reason\>|Trusted|
|
||||
|warnclear|wc|remove all warnings for a client|True|!wc \<player\>|Trusted|
|
||||
|admins|a|list currently connected privileged clients|False|!a |User|
|
||||
|getexternalip|ip|view your external IP address|False|!ip |User|
|
||||
|help|h|list all available commands|False|!h \<optional commands\>|User|
|
||||
|mostplayed|mp|view the top 5 dedicated players on the server|False|!mp |User|
|
||||
|nextmap|nm|view next map in rotation|False|!nm |User|
|
||||
|owner|iamgod|claim ownership of the server|False|!iamgod |User|
|
||||
|ping|pi|get client's latency|False|!pi \<optional player\>|User|
|
||||
|privatemessage|pm|send message to other client|True|!pm \<player\> \<message\>|User|
|
||||
|report|rep|report a client for suspicious behavior|True|!rep \<player\> \<reason\>|User|
|
||||
|resetstats|rs|reset your stats to factory-new|False|!rs |User|
|
||||
|rules|r|list server rules|False|!r |User|
|
||||
|stats|xlrstats|view your stats|False|!xlrstats \<optional player\>|User|
|
||||
|topstats|ts|view the top 5 players in this server|False|!ts |User|
|
||||
|setgravatar|sg|set gravatar for webfront profile|False|!sg \<gravatar email\>|User|
|
||||
|whoami|who|give information about yourself|False|!who |User|
|
||||
|
||||
|
||||
_These commands include all shipped plugin commands._
|
||||
|
||||
---
|
||||
@ -302,6 +309,11 @@ ___
|
||||
#### VPN Detection [Script Plugin]
|
||||
- This plugin detects if a client is using a VPN and kicks them if they are
|
||||
- To disable this plugin, delete `Plugins\VPNDetection.js`
|
||||
- Adding ClientIds to `vpnExceptionIds` will prevent a client from being kicked.
|
||||
|
||||
#### Shared GUID Kicker [Script Plugin]
|
||||
- This plugin kicks users using a specific GUID
|
||||
- GUID `F4D2C30B712AC6E3` on IW4x was packed into a torrent version of the game.
|
||||
___
|
||||
### Webfront
|
||||
`Home`
|
||||
@ -432,9 +444,13 @@ python DiscordWebhook.py
|
||||
#### Anti-cheat
|
||||
This is an [IW4x](https://iw4xcachep26muba.onion.link/) only feature (wider game support planned), that uses analytics to detect aimbots and aim-assist tools.
|
||||
To utilize anti-cheat, enable it during setup **and** copy `_customcallbacks.gsc` from `userraw` into your `IW4x Server\userraw\scripts` folder.
|
||||
|
||||
The anti-cheat feature is a work in progress and as such will be constantly tweaked and may not be 100% accurate, however the goal is to deter as many cheaters as possible from IW4x.
|
||||
#### Database Storage
|
||||
By default, all **IW4MAdmin** information is stored in `Database.db`.
|
||||
By default, all **IW4MAdmin** information is stored in `Database.db`.
|
||||
|
||||
Should you need to reset your database, this file can simply be deleted.
|
||||
Additionally, this file should be preserved during updates to retain client information.
|
||||
Setting the `ConnectionString` property in `IW4MAdminSettings.json` will cause **IW4MAdmin** to attempt to use a MySQL connection for database storage.
|
||||
|
||||
Setting the `ConnectionString` and `DatabaseProvider` properties in `IW4MAdminSettings.json`
|
||||
will allow **IW4MAdmin** to use alternate methods for database storage.
|
@ -1,8 +1,9 @@
|
||||
dotnet publish WebfrontCore/WebfrontCore.csproj -c Prerelease -o C:\Projects\IW4M-Admin\Publish\WindowsPrerelease
|
||||
dotnet publish Application/Application.csproj -c Prerelease -o C:\Projects\IW4M-Admin\Publish\WindowsPrerelease
|
||||
dotnet publish GameLogServer/GameLogServer.pyproj -c Release -o C:\Projects\IW4M-Admin\Publish\WindowsPrerelease\GameLogServer
|
||||
dotnet publish WebfrontCore/WebfrontCore.csproj -c Prerelease -o X:\IW4MAdmin\Publish\WindowsPrerelease /p:PublishProfile=Prerelease
|
||||
dotnet publish Application/Application.csproj -c Prerelease -o X:\IW4MAdmin\Publish\WindowsPrerelease /p:PublishProfile=Prerelease
|
||||
dotnet publish GameLogServer/GameLogServer.pyproj -c Release -o X:\IW4MAdmin\Publish\WindowsPrerelease\GameLogServer
|
||||
dotnet publish GameLogServer/DiscordWebhook.pyproj -c Release -o X:\IW4MAdmin\Publish\WindowsPrerelease\DiscordWebhook
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\Tools\VsDevCmd.bat"
|
||||
msbuild GameLogServer/GameLogServer.pyproj /p:PublishProfile=FolderProfile /p:DeployOnBuild=true /p:PublishProfileRootFolder=C:\Projects\IW4M-Admin\GameLogServer\
|
||||
msbuild DiscordWebhook/DiscordWebhook.pyproj /p:PublishProfile=FolderProfile /p:DeployOnBuild=true /p:PublishProfileRootFolder=C:\Projects\IW4M-Admin\DiscordWebhook\
|
||||
cd "C:\Projects\IW4M-Admin\DEPLOY\"
|
||||
msbuild GameLogServer/GameLogServer.pyproj /p:PublishProfile=PreRelease /p:DeployOnBuild=true /p:PublishProfileRootFolder=X:\IW4MAdmin\GameLogServer\
|
||||
msbuild DiscordWebhook/DiscordWebhook.pyproj /p:PublishProfile=PreRelease /p:DeployOnBuild=true /p:PublishProfileRootFolder=X:\IW4MAdmin\DiscordWebhook\
|
||||
cd "X:\IW4MAdmin\DEPLOY\"
|
||||
PowerShell ".\upload_prerelease.ps1"
|
@ -1,8 +1,8 @@
|
||||
dotnet publish WebfrontCore/WebfrontCore.csproj -c Release -o C:\Projects\IW4M-Admin\Publish\Windows
|
||||
dotnet publish Application/Application.csproj -c Release -o C:\Projects\IW4M-Admin\Publish\Windows
|
||||
dotnet publish GameLogServer/GameLogServer.pyproj -c Release -o C:\Projects\IW4M-Admin\Publish\Windows\GameLogServer
|
||||
dotnet publish WebfrontCore/WebfrontCore.csproj -c Release -o X:\IW4MAdmin\Publish\Windows
|
||||
dotnet publish Application/Application.csproj -c Release -o X:\IW4MAdmin\Publish\Windows
|
||||
dotnet publish GameLogServer/GameLogServer.pyproj -c Release -o X:\IW4MAdmin\Publish\Windows\GameLogServer
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\Tools\VsDevCmd.bat"
|
||||
msbuild GameLogServer/GameLogServer.pyproj /p:PublishProfile=Stable /p:DeployOnBuild=true /p:PublishProfileRootFolder=C:\Projects\IW4M-Admin\GameLogServer\
|
||||
msbuild DiscordWebhook/DiscordWebhook.pyproj /p:PublishProfile=Stable /p:DeployOnBuild=true /p:PublishProfileRootFolder=C:\Projects\IW4M-Admin\DiscordWebhook\
|
||||
cd "C:\Projects\IW4M-Admin\DEPLOY\"
|
||||
msbuild GameLogServer/GameLogServer.pyproj /p:PublishProfile=Stable /p:DeployOnBuild=true /p:PublishProfileRootFolder=X:\IW4MAdmin\GameLogServer\
|
||||
msbuild DiscordWebhook/DiscordWebhook.pyproj /p:PublishProfile=Stable /p:DeployOnBuild=true /p:PublishProfileRootFolder=X:\IW4MAdmin\DiscordWebhook\
|
||||
cd "X:\IW4MAdmin\DEPLOY\"
|
||||
PowerShell ".\upload_release.ps1"
|
@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Objects;
|
||||
|
||||
namespace SharedLibraryCore
|
||||
@ -14,7 +14,7 @@ namespace SharedLibraryCore
|
||||
|
||||
public abstract class Command
|
||||
{
|
||||
public Command(String commandName, String commandDescription, String commandAlias, Player.Permission requiredPermission, bool requiresTarget, CommandArgument[] param = null)
|
||||
public Command(String commandName, String commandDescription, String commandAlias, EFClient.Permission requiredPermission, bool requiresTarget, CommandArgument[] param = null)
|
||||
{
|
||||
Name = commandName;
|
||||
Description = commandDescription;
|
||||
@ -33,7 +33,7 @@ namespace SharedLibraryCore
|
||||
public String Alias { get; private set; }
|
||||
public int RequiredArgumentCount => Arguments.Count(c => c.Required);
|
||||
public bool RequiresTarget { get; private set; }
|
||||
public Player.Permission Permission { get; private set; }
|
||||
public EFClient.Permission Permission { get; private set; }
|
||||
public CommandArgument[] Arguments { get; private set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using SharedLibraryCore.Exceptions;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Exceptions;
|
||||
using SharedLibraryCore.Objects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -60,7 +61,7 @@ namespace SharedLibraryCore.Commands
|
||||
var found = await Manager.GetClientService().Get(dbID);
|
||||
if (found != null)
|
||||
{
|
||||
E.Target = found.AsPlayer();
|
||||
E.Target = found;
|
||||
E.Target.CurrentServer = E.Owner;
|
||||
E.Data = String.Join(" ", Args.Skip(1));
|
||||
}
|
||||
@ -68,14 +69,14 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
else if (Args[0].Length < 3 && cNum > -1 && cNum < E.Owner.MaxClients) // user specifying target by client num
|
||||
{
|
||||
if (E.Owner.Players[cNum] != null)
|
||||
if (E.Owner.Clients[cNum] != null)
|
||||
{
|
||||
E.Target = E.Owner.Players[cNum];
|
||||
E.Target = E.Owner.Clients[cNum];
|
||||
E.Data = String.Join(" ", Args.Skip(1));
|
||||
}
|
||||
}
|
||||
|
||||
List<Player> matchingPlayers;
|
||||
List<EFClient> matchingPlayers;
|
||||
|
||||
if (E.Target == null && C.RequiresTarget) // Find active player including quotes (multiple words)
|
||||
{
|
||||
|
@ -12,14 +12,13 @@ using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using static SharedLibraryCore.RCon.StaticHelpers;
|
||||
|
||||
namespace SharedLibraryCore.Commands
|
||||
{
|
||||
public class CQuit : Command
|
||||
{
|
||||
public CQuit() :
|
||||
base("quit", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_QUIT_DESC"], "q", Player.Permission.Owner, false)
|
||||
base("quit", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_QUIT_DESC"], "q", EFClient.Permission.Owner, false)
|
||||
{ }
|
||||
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
@ -31,18 +30,32 @@ namespace SharedLibraryCore.Commands
|
||||
public class COwner : Command
|
||||
{
|
||||
public COwner() :
|
||||
base("owner", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_OWNER_DESC"], "iamgod", Player.Permission.User, false)
|
||||
base("owner", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_OWNER_DESC"], "iamgod", EFClient.Permission.User, false)
|
||||
{ }
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
if ((await (E.Owner.Manager.GetClientService() as ClientService).GetOwners()).Count == 0)
|
||||
{
|
||||
E.Origin.Level = Player.Permission.Owner;
|
||||
var oldPermission = E.Origin.Level;
|
||||
E.Origin.Level = EFClient.Permission.Owner;
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_OWNER_SUCCESS"]);
|
||||
// so setpassword/login works
|
||||
E.Owner.Manager.GetPrivilegedClients().Add(E.Origin.ClientId, E.Origin);
|
||||
await E.Owner.Manager.GetClientService().Update(E.Origin);
|
||||
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.ChangePermission,
|
||||
Origin = E.Origin,
|
||||
Target = E.Origin,
|
||||
Owner = E.Owner,
|
||||
Extra = new Change()
|
||||
{
|
||||
PreviousValue = oldPermission.ToString(),
|
||||
NewValue = E.Origin.Level.ToString()
|
||||
}
|
||||
};
|
||||
|
||||
E.Owner.Manager.GetEventHandler().AddEvent(e);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -54,7 +67,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CWarn : Command
|
||||
{
|
||||
public CWarn() :
|
||||
base("warn", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WARN_DESC"], "w", Player.Permission.Trusted, true, new CommandArgument[]
|
||||
base("warn", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WARN_DESC"], "w", EFClient.Permission.Trusted, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -83,7 +96,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CWarnClear : Command
|
||||
{
|
||||
public CWarnClear() :
|
||||
base("warnclear", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WARNCLEAR_DESC"], "wc", Player.Permission.Trusted, true, new CommandArgument[]
|
||||
base("warnclear", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WARNCLEAR_DESC"], "wc", EFClient.Permission.Trusted, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -107,7 +120,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CKick : Command
|
||||
{
|
||||
public CKick() :
|
||||
base("kick", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_DESC"], "k", Player.Permission.Moderator, true, new CommandArgument[]
|
||||
base("kick", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_DESC"], "k", EFClient.Permission.Moderator, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -133,7 +146,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CSay : Command
|
||||
{
|
||||
public CSay() :
|
||||
base("say", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SAY_DESC"], "s", Player.Permission.Moderator, false, new CommandArgument[]
|
||||
base("say", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SAY_DESC"], "s", EFClient.Permission.Moderator, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -153,7 +166,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CTempBan : Command
|
||||
{
|
||||
public CTempBan() :
|
||||
base("tempban", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_DESC"], "tb", Player.Permission.Administrator, true, new CommandArgument[]
|
||||
base("tempban", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_DESC"], "tb", EFClient.Permission.Administrator, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -183,9 +196,17 @@ namespace SharedLibraryCore.Commands
|
||||
string tempbanReason = match.Groups[2].ToString();
|
||||
var length = match.Groups[1].ToString().ParseTimespan();
|
||||
|
||||
var _ = !(await E.Target.TempBan(tempbanReason, length, E.Origin).WaitAsync()).Failed ?
|
||||
E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_SUCCESS"]} ^5{length.TimeSpanText()}") :
|
||||
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_FAIL"]} {E.Target.Name}");
|
||||
if (length > E.Owner.Manager.GetApplicationSettings().Configuration().MaximumTempBanTime)
|
||||
{
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_FAIL_TOOLONG"]);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
var _ = !(await E.Target.TempBan(tempbanReason, length, E.Origin).WaitAsync()).Failed ?
|
||||
E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_SUCCESS"]} ^5{length.TimeSpanText()}") :
|
||||
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_FAIL"]} {E.Target.Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -193,7 +214,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CBan : Command
|
||||
{
|
||||
public CBan() :
|
||||
base("ban", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_DESC"], "b", Player.Permission.SeniorAdmin, true, new CommandArgument[]
|
||||
base("ban", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_DESC"], "b", EFClient.Permission.SeniorAdmin, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -219,7 +240,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CUnban : Command
|
||||
{
|
||||
public CUnban() :
|
||||
base("unban", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNBAN_DESC"], "ub", Player.Permission.SeniorAdmin, true, new CommandArgument[]
|
||||
base("unban", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNBAN_DESC"], "ub", EFClient.Permission.SeniorAdmin, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -252,12 +273,12 @@ namespace SharedLibraryCore.Commands
|
||||
public class CWhoAmI : Command
|
||||
{
|
||||
public CWhoAmI() :
|
||||
base("whoami", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WHO_DESC"], "who", Player.Permission.User, false)
|
||||
base("whoami", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WHO_DESC"], "who", EFClient.Permission.User, false)
|
||||
{ }
|
||||
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
String You = String.Format("{0} [^3#{1}^7] {2} [^3@{3}^7] [{4}^7] IP: {5}", E.Origin.Name, E.Origin.ClientNumber, E.Origin.NetworkId, E.Origin.ClientId, Utilities.ConvertLevelToColor(E.Origin.Level, E.Origin.ClientPermission.Name), E.Origin.IPAddressString);
|
||||
String You = String.Format("{0} [^3#{1}^7] {2} ^7[^3@{3}^7] ^7[{4}^7] IP: {5}", E.Origin.Name, E.Origin.ClientNumber, E.Origin.NetworkId, E.Origin.ClientId, Utilities.ConvertLevelToColor(E.Origin.Level, E.Origin.ClientPermission.Name), E.Origin.IPAddressString);
|
||||
E.Origin.Tell(You);
|
||||
|
||||
return Task.CompletedTask;
|
||||
@ -267,27 +288,33 @@ namespace SharedLibraryCore.Commands
|
||||
public class CList : Command
|
||||
{
|
||||
public CList() :
|
||||
base("list", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_LIST_DESC"], "l", Player.Permission.Moderator, false)
|
||||
base("list", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_LIST_DESC"], "l", EFClient.Permission.Moderator, false)
|
||||
{ }
|
||||
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
StringBuilder playerList = new StringBuilder();
|
||||
int count = 0;
|
||||
for (int i = 0; i < E.Owner.Players.Count; i++)
|
||||
for (int i = 0; i < E.Owner.Clients.Count; i++)
|
||||
{
|
||||
var P = E.Owner.Players[i];
|
||||
var P = E.Owner.Clients[i];
|
||||
|
||||
if (P == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// todo: fix spacing
|
||||
// todo: make this better :)
|
||||
if (P.Masked)
|
||||
playerList.AppendFormat("[^3{0}^7]{3}[^3{1}^7] {2}", Utilities.ConvertLevelToColor(Player.Permission.User, P.ClientPermission.Name), P.ClientNumber, P.Name, Utilities.GetSpaces(Player.Permission.SeniorAdmin.ToString().Length - Player.Permission.User.ToString().Length));
|
||||
{
|
||||
playerList.AppendFormat("[^3{0}^7]{3}[^3{1}^7] {2}", Utilities.ConvertLevelToColor( EFClient.Permission.User, P.ClientPermission.Name), P.ClientNumber, P.Name, Utilities.GetSpaces( EFClient.Permission.SeniorAdmin.ToString().Length - EFClient.Permission.User.ToString().Length));
|
||||
}
|
||||
else
|
||||
playerList.AppendFormat("[^3{0}^7]{3}[^3{1}^7] {2}", Utilities.ConvertLevelToColor(P.Level, P.ClientPermission.Name), P.ClientNumber, P.Name, Utilities.GetSpaces(Player.Permission.SeniorAdmin.ToString().Length - P.Level.ToString().Length));
|
||||
{
|
||||
playerList.AppendFormat("[^3{0}^7]{3}[^3{1}^7] {2}", Utilities.ConvertLevelToColor(P.Level, P.ClientPermission.Name), P.ClientNumber, P.Name, Utilities.GetSpaces( EFClient.Permission.SeniorAdmin.ToString().Length - P.Level.ToString().Length));
|
||||
}
|
||||
|
||||
if (count == 2 || E.Owner.GetPlayersAsList().Count == 1)
|
||||
if (count == 2 || E.Owner.GetClientsAsList().Count == 1)
|
||||
{
|
||||
E.Origin.Tell(playerList.ToString());
|
||||
count = 0;
|
||||
@ -312,7 +339,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CHelp : Command
|
||||
{
|
||||
public CHelp() :
|
||||
base("help", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_HELP_DESC"], "h", Player.Permission.User, false, new CommandArgument[]
|
||||
base("help", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_HELP_DESC"], "h", EFClient.Permission.User, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -377,7 +404,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CFastRestart : Command
|
||||
{
|
||||
public CFastRestart() :
|
||||
base("fastrestart", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FASTRESTART_DESC"], "fr", Player.Permission.Moderator, false)
|
||||
base("fastrestart", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FASTRESTART_DESC"], "fr", EFClient.Permission.Moderator, false)
|
||||
{ }
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
@ -393,7 +420,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CMapRotate : Command
|
||||
{
|
||||
public CMapRotate() :
|
||||
base("maprotate", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAPROTATE_DESC"], "mr", Player.Permission.Administrator, false)
|
||||
base("maprotate", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAPROTATE_DESC"], "mr", EFClient.Permission.Administrator, false)
|
||||
{ }
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
@ -410,7 +437,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CSetLevel : Command
|
||||
{
|
||||
public CSetLevel() :
|
||||
base("setlevel", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_DESC"], "sl", Player.Permission.Moderator, true, new CommandArgument[]
|
||||
base("setlevel", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_DESC"], "sl", EFClient.Permission.Moderator, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -433,17 +460,17 @@ namespace SharedLibraryCore.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
Player.Permission oldPerm = E.Target.Level;
|
||||
Player.Permission newPerm = Utilities.MatchPermission(E.Data);
|
||||
EFClient.Permission oldPerm = E.Target.Level;
|
||||
EFClient.Permission newPerm = Utilities.MatchPermission(E.Data);
|
||||
|
||||
if (newPerm == Player.Permission.Owner &&
|
||||
if (newPerm == EFClient.Permission.Owner &&
|
||||
!E.Owner.Manager.GetApplicationSettings().Configuration().EnableMultipleOwners)
|
||||
{
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_OWNER"]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (E.Origin.Level < Player.Permission.Owner &&
|
||||
if (E.Origin.Level < EFClient.Permission.Owner &&
|
||||
!E.Owner.Manager.GetApplicationSettings().Configuration().EnableSteppedHierarchy)
|
||||
{
|
||||
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_STEPPEDDISABLED"]} ^5{E.Target.Name}");
|
||||
@ -452,14 +479,14 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
if (newPerm >= E.Origin.Level)
|
||||
{
|
||||
if (E.Origin.Level < Player.Permission.Owner)
|
||||
if (E.Origin.Level < EFClient.Permission.Owner)
|
||||
{
|
||||
E.Origin.Tell(string.Format(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_LEVELTOOHIGH"], E.Target.Name, (E.Origin.Level - 1).ToString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
else if (newPerm > Player.Permission.Banned)
|
||||
else if (newPerm > EFClient.Permission.Banned)
|
||||
{
|
||||
var ActiveClient = E.Owner.Manager.GetActiveClients()
|
||||
.FirstOrDefault(p => p.NetworkId == E.Target.NetworkId);
|
||||
@ -468,7 +495,11 @@ namespace SharedLibraryCore.Commands
|
||||
{
|
||||
ActiveClient.Level = newPerm;
|
||||
await E.Owner.Manager.GetClientService().Update(ActiveClient);
|
||||
ActiveClient.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SUCCESS_TARGET"]} {newPerm}");
|
||||
|
||||
if (newPerm > oldPerm)
|
||||
{
|
||||
ActiveClient.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SUCCESS_TARGET"]} {newPerm}");
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
@ -477,17 +508,6 @@ namespace SharedLibraryCore.Commands
|
||||
await E.Owner.Manager.GetClientService().Update(E.Target);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
E.Owner.Manager.GetPrivilegedClients().Add(E.Target.ClientId, E.Target);
|
||||
}
|
||||
|
||||
catch (Exception)
|
||||
{
|
||||
// this updates their privilege level to the webfront claims
|
||||
E.Owner.Manager.GetPrivilegedClients()[E.Target.ClientId] = E.Target;
|
||||
}
|
||||
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Origin = E.Origin,
|
||||
@ -503,7 +523,9 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
E.Owner.Manager.GetEventHandler().AddEvent(e);
|
||||
|
||||
E.Origin.Tell($"{E.Target.Name} {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SUCCESS"]}");
|
||||
var _ = newPerm < oldPerm ?
|
||||
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_DEMOTE_SUCCESS"]} {E.Target.Name}") :
|
||||
E.Origin.Tell($"{E.Target.Name} {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SUCCESS"]}");
|
||||
}
|
||||
|
||||
else
|
||||
@ -516,7 +538,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CUsage : Command
|
||||
{
|
||||
public CUsage() :
|
||||
base("usage", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_USAGE_DESC"], "us", Player.Permission.Moderator, false)
|
||||
base("usage", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_USAGE_DESC"], "us", EFClient.Permission.Moderator, false)
|
||||
{ }
|
||||
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
@ -529,7 +551,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CUptime : Command
|
||||
{
|
||||
public CUptime() :
|
||||
base("uptime", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UPTIME_DESC"], "up", Player.Permission.Moderator, false)
|
||||
base("uptime", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UPTIME_DESC"], "up", EFClient.Permission.Moderator, false)
|
||||
{ }
|
||||
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
@ -544,13 +566,13 @@ namespace SharedLibraryCore.Commands
|
||||
public class CListAdmins : Command
|
||||
{
|
||||
public CListAdmins() :
|
||||
base("admins", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ADMINS_DESC"], "a", Player.Permission.User, false)
|
||||
base("admins", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ADMINS_DESC"], "a", EFClient.Permission.User, false)
|
||||
{ }
|
||||
|
||||
public static string OnlineAdmins(Server S)
|
||||
{
|
||||
var onlineAdmins = S.GetPlayersAsList()
|
||||
.Where(p => p.Level > Player.Permission.Flagged)
|
||||
var onlineAdmins = S.GetClientsAsList()
|
||||
.Where(p => p.Level > EFClient.Permission.Flagged)
|
||||
.Where(p => !p.Masked)
|
||||
.Select(p => $"[^3{Utilities.ConvertLevelToColor(p.Level, p.ClientPermission.Name)}^7] {p.Name}");
|
||||
|
||||
@ -573,7 +595,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CLoadMap : Command
|
||||
{
|
||||
public CLoadMap() :
|
||||
base("map", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_DESC"], "m", Player.Permission.Administrator, false, new CommandArgument[]
|
||||
base("map", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_DESC"], "m", EFClient.Permission.Administrator, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -606,7 +628,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CFindPlayer : Command
|
||||
{
|
||||
public CFindPlayer() :
|
||||
base("find", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FIND_DESC"], "f", Player.Permission.Administrator, false, new CommandArgument[]
|
||||
base("find", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FIND_DESC"], "f", EFClient.Permission.Administrator, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -625,7 +647,7 @@ namespace SharedLibraryCore.Commands
|
||||
}
|
||||
|
||||
IList<EFClient> db_players = (await (E.Owner.Manager.GetClientService() as ClientService)
|
||||
.GetClientByName(E.Data))
|
||||
.FindClientsByIdentifier(E.Data))
|
||||
.OrderByDescending(p => p.LastConnection)
|
||||
.ToList();
|
||||
|
||||
@ -650,7 +672,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CListRules : Command
|
||||
{
|
||||
public CListRules() :
|
||||
base("rules", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RULES_DESC"], "r", Player.Permission.User, false)
|
||||
base("rules", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RULES_DESC"], "r", EFClient.Permission.User, false)
|
||||
{ }
|
||||
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
@ -668,7 +690,9 @@ namespace SharedLibraryCore.Commands
|
||||
var rules = new List<string>();
|
||||
rules.AddRange(E.Owner.Manager.GetApplicationSettings().Configuration().GlobalRules);
|
||||
if (E.Owner.ServerConfig.Rules != null)
|
||||
{
|
||||
rules.AddRange(E.Owner.ServerConfig.Rules);
|
||||
}
|
||||
|
||||
foreach (string r in rules)
|
||||
{
|
||||
@ -683,7 +707,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CPrivateMessage : Command
|
||||
{
|
||||
public CPrivateMessage() :
|
||||
base("privatemessage", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PM_DESC"], "pm", Player.Permission.User, true, new CommandArgument[]
|
||||
base("privatemessage", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PM_DESC"], "pm", EFClient.Permission.User, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -710,7 +734,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CFlag : Command
|
||||
{
|
||||
public CFlag() :
|
||||
base("flag", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_DESC"], "fp", Player.Permission.Moderator, true, new CommandArgument[]
|
||||
base("flag", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_DESC"], "fp", EFClient.Permission.Moderator, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -752,7 +776,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CUnflag : Command
|
||||
{
|
||||
public CUnflag() :
|
||||
base("unflag", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_DESC"], "uf", Player.Permission.Moderator, true, new CommandArgument[]
|
||||
base("unflag", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_DESC"], "uf", EFClient.Permission.Moderator, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -790,7 +814,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CReport : Command
|
||||
{
|
||||
public CReport() :
|
||||
base("report", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_DESC"], "rep", Player.Permission.User, true, new CommandArgument[]
|
||||
base("report", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_DESC"], "rep", EFClient.Permission.User, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -861,7 +885,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CListReports : Command
|
||||
{
|
||||
public CListReports() :
|
||||
base("reports", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORTS_DESC"], "reps", Player.Permission.Moderator, false, new CommandArgument[]
|
||||
base("reports", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORTS_DESC"], "reps", EFClient.Permission.Moderator, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -898,7 +922,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CMask : Command
|
||||
{
|
||||
public CMask() :
|
||||
base("mask", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MASK_DESC"], "hide", Player.Permission.Moderator, false)
|
||||
base("mask", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MASK_DESC"], "hide", EFClient.Permission.Moderator, false)
|
||||
{ }
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
@ -921,7 +945,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CListBanInfo : Command
|
||||
{
|
||||
public CListBanInfo() :
|
||||
base("baninfo", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BANINFO_DESC"], "bi", Player.Permission.Moderator, true, new CommandArgument[]
|
||||
base("baninfo", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BANINFO_DESC"], "bi", EFClient.Permission.Moderator, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -935,7 +959,8 @@ namespace SharedLibraryCore.Commands
|
||||
{
|
||||
var B = await E.Owner.Manager.GetPenaltyService().GetClientPenaltiesAsync(E.Target.ClientId);
|
||||
|
||||
var penalty = B.FirstOrDefault(b => b.Type > Penalty.PenaltyType.Kick && b.Expires > DateTime.UtcNow);
|
||||
var penalty = B.FirstOrDefault(b => b.Type > Penalty.PenaltyType.Kick &&
|
||||
(b.Expires == null || b.Expires > DateTime.UtcNow));
|
||||
|
||||
if (penalty == null)
|
||||
{
|
||||
@ -943,7 +968,7 @@ namespace SharedLibraryCore.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
string timeRemaining = penalty.Type == Penalty.PenaltyType.TempBan ? $"({(penalty.Expires - DateTime.UtcNow).TimeSpanText()} remaining)" : "";
|
||||
string timeRemaining = penalty.Type == Penalty.PenaltyType.TempBan ? $"({(penalty.Expires.Value - DateTime.UtcNow).TimeSpanText()} remaining)" : "";
|
||||
string success = Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BANINFO_SUCCESS"];
|
||||
|
||||
E.Origin.Tell($"^1{E.Target.Name} ^7{string.Format(success, penalty.Punisher.Name)} {penalty.Punisher.Name} {timeRemaining}");
|
||||
@ -953,7 +978,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CListAlias : Command
|
||||
{
|
||||
public CListAlias() :
|
||||
base("alias", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ALIAS_DESC"], "known", Player.Permission.Moderator, true, new CommandArgument[]
|
||||
base("alias", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ALIAS_DESC"], "known", EFClient.Permission.Moderator, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -987,7 +1012,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CExecuteRCON : Command
|
||||
{
|
||||
public CExecuteRCON() :
|
||||
base("rcon", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RCON_DESC"], "rcon", Player.Permission.Owner, false, new CommandArgument[]
|
||||
base("rcon", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RCON_DESC"], "rcon", EFClient.Permission.Owner, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -1001,16 +1026,21 @@ namespace SharedLibraryCore.Commands
|
||||
{
|
||||
var Response = await E.Owner.ExecuteCommandAsync(E.Data.Trim());
|
||||
foreach (string S in Response)
|
||||
{
|
||||
E.Origin.Tell(S.StripColors());
|
||||
}
|
||||
|
||||
if (Response.Length == 0)
|
||||
{
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RCON_SUCCESS"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CPlugins : Command
|
||||
{
|
||||
public CPlugins() :
|
||||
base("plugins", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PLUGINS_DESC"], "p", Player.Permission.Administrator, false)
|
||||
base("plugins", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PLUGINS_DESC"], "p", EFClient.Permission.Administrator, false)
|
||||
{ }
|
||||
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
@ -1027,7 +1057,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CIP : Command
|
||||
{
|
||||
public CIP() :
|
||||
base("getexternalip", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_IP_DESC"], "ip", Player.Permission.User, false)
|
||||
base("getexternalip", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_IP_DESC"], "ip", EFClient.Permission.User, false)
|
||||
{ }
|
||||
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
@ -1039,7 +1069,7 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
public class CPruneAdmins : Command
|
||||
{
|
||||
public CPruneAdmins() : base("prune", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PRUNE_DESC"], "pa", Player.Permission.Owner, false, new CommandArgument[]
|
||||
public CPruneAdmins() : base("prune", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PRUNE_DESC"], "pa", EFClient.Permission.Owner, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -1059,7 +1089,9 @@ namespace SharedLibraryCore.Commands
|
||||
{
|
||||
inactiveDays = Int32.Parse(E.Data);
|
||||
if (inactiveDays < 1)
|
||||
{
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1076,10 +1108,10 @@ namespace SharedLibraryCore.Commands
|
||||
{
|
||||
var lastActive = DateTime.UtcNow.AddDays(-inactiveDays);
|
||||
inactiveUsers = await context.Clients
|
||||
.Where(c => c.Level > Player.Permission.Flagged && c.Level <= Player.Permission.Moderator)
|
||||
.Where(c => c.Level > EFClient.Permission.Flagged && c.Level <= EFClient.Permission.Moderator)
|
||||
.Where(c => c.LastConnection < lastActive)
|
||||
.ToListAsync();
|
||||
inactiveUsers.ForEach(c => c.Level = Player.Permission.User);
|
||||
inactiveUsers.ForEach(c => c.Level = EFClient.Permission.User);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
E.Origin.Tell($"^5{inactiveUsers.Count} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PRUNE_SUCCESS"]}");
|
||||
@ -1088,7 +1120,7 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
public class CSetPassword : Command
|
||||
{
|
||||
public CSetPassword() : base("setpassword", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETPASSWORD_DESC"], "sp", Player.Permission.Moderator, false, new CommandArgument[]
|
||||
public CSetPassword() : base("setpassword", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETPASSWORD_DESC"], "sp", EFClient.Permission.Moderator, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -1122,77 +1154,89 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
public class CKillServer : Command
|
||||
{
|
||||
public CKillServer() : base("killserver", "kill the game server", "kill", Player.Permission.Administrator, false)
|
||||
public CKillServer() : base("killserver", "kill the game server", "kill", EFClient.Permission.Administrator, false)
|
||||
{
|
||||
}
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
var gameserverProcesses = System.Diagnostics.Process.GetProcessesByName("iw4x");
|
||||
|
||||
System.Diagnostics.Process currentProcess = null;
|
||||
|
||||
foreach (var p in gameserverProcesses)
|
||||
if (E.Owner.ServerConfig.ManualLogPath != null)
|
||||
{
|
||||
string cmdLine = Utilities.GetCommandLine(p.Id);
|
||||
|
||||
var regex = Regex.Match(cmdLine, @".*((?:\+set|\+) net_port) +([0-9]+).*");
|
||||
|
||||
if (regex.Success && Int32.Parse(regex.Groups[2].Value) == E.Owner.GetPort())
|
||||
using (var wc = new WebClient())
|
||||
{
|
||||
currentProcess = p;
|
||||
E.Owner.RestartRequested = true;
|
||||
var response = await wc.DownloadStringTaskAsync(new Uri($"{E.Owner.ServerConfig.ManualLogPath}/restart"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (currentProcess == null)
|
||||
{
|
||||
E.Origin.Tell("Could not find running/stalled instance of IW4x");
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// attempt to kill it natively
|
||||
try
|
||||
var gameserverProcesses = System.Diagnostics.Process.GetProcessesByName("iw4x");
|
||||
|
||||
System.Diagnostics.Process currentProcess = null;
|
||||
|
||||
foreach (var p in gameserverProcesses)
|
||||
{
|
||||
if (!E.Owner.Throttled)
|
||||
string cmdLine = Utilities.GetCommandLine(p.Id);
|
||||
|
||||
var regex = Regex.Match(cmdLine, @".*((?:\+set|\+) net_port) +([0-9]+).*");
|
||||
|
||||
if (regex.Success && Int32.Parse(regex.Groups[2].Value) == E.Owner.GetPort())
|
||||
{
|
||||
currentProcess = p;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (currentProcess == null)
|
||||
{
|
||||
E.Origin.Tell("Could not find running/stalled instance of IW4x");
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// attempt to kill it natively
|
||||
try
|
||||
{
|
||||
if (!E.Owner.Throttled)
|
||||
{
|
||||
#if !DEBUG
|
||||
await E.Owner.ExecuteCommandAsync("quit");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exceptions.NetworkException)
|
||||
{
|
||||
E.Origin.Tell("Unable to cleanly shutdown server, forcing");
|
||||
}
|
||||
|
||||
if (!currentProcess.HasExited)
|
||||
{
|
||||
try
|
||||
{
|
||||
currentProcess.Kill();
|
||||
E.Origin.Tell("Successfully killed server process");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
E.Origin.Tell("Could not kill server process");
|
||||
E.Owner.Logger.WriteDebug("Unable to kill process");
|
||||
E.Owner.Logger.WriteDebug($"Exception: {e.Message}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exceptions.NetworkException)
|
||||
{
|
||||
E.Origin.Tell("Unable to cleanly shutdown server, forcing");
|
||||
}
|
||||
|
||||
if (!currentProcess.HasExited)
|
||||
{
|
||||
try
|
||||
{
|
||||
currentProcess.Kill();
|
||||
E.Origin.Tell("Successfully killed server process");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
E.Origin.Tell("Could not kill server process");
|
||||
E.Owner.Logger.WriteDebug("Unable to kill process");
|
||||
E.Owner.Logger.WriteDebug($"Exception: {e.Message}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class CPing : Command
|
||||
{
|
||||
public CPing() : base("ping", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_DESC"], "pi", Player.Permission.User, false, new CommandArgument[]
|
||||
public CPing() : base("ping", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_DESC"], "pi", EFClient.Permission.User, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -1207,16 +1251,24 @@ namespace SharedLibraryCore.Commands
|
||||
if (E.Message.IsBroadcastCommand())
|
||||
{
|
||||
if (E.Target == null)
|
||||
{
|
||||
E.Owner.Broadcast($"{E.Origin.Name}'s {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_TARGET"]} ^5{E.Origin.Ping}^7ms");
|
||||
}
|
||||
else
|
||||
{
|
||||
E.Owner.Broadcast($"{E.Target.Name}'s {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_TARGET"]} ^5{E.Target.Ping}^7ms");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (E.Target == null)
|
||||
{
|
||||
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_SELF"]} ^5{E.Origin.Ping}^7ms");
|
||||
}
|
||||
else
|
||||
{
|
||||
E.Origin.Tell($"{E.Target.Name}'s {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_TARGET"]} ^5{E.Target.Ping}^7ms");
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
@ -1225,7 +1277,7 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
public class CSetGravatar : Command
|
||||
{
|
||||
public CSetGravatar() : base("setgravatar", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GRAVATAR_DESC"], "sg", Player.Permission.User, false, new CommandArgument[]
|
||||
public CSetGravatar() : base("setgravatar", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GRAVATAR_DESC"], "sg", EFClient.Permission.User, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -1289,7 +1341,7 @@ namespace SharedLibraryCore.Commands
|
||||
/// </summary>
|
||||
public class CNextMap : Command
|
||||
{
|
||||
public CNextMap() : base("nextmap", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_NEXTMAP_DESC"], "nm", Player.Permission.User, false) { }
|
||||
public CNextMap() : base("nextmap", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_NEXTMAP_DESC"], "nm", EFClient.Permission.User, false) { }
|
||||
public static async Task<string> GetNextMap(Server s)
|
||||
{
|
||||
string mapRotation = (await s.GetDvarAsync<string>("sv_mapRotation")).Value.ToLower();
|
||||
|
@ -21,6 +21,8 @@ namespace SharedLibraryCore.Configuration
|
||||
public string DatabaseProvider { get; set; } = "sqlite";
|
||||
public string ConnectionString { get; set; }
|
||||
public int RConPollRate { get; set; } = 5000;
|
||||
public bool IgnoreBots { get; set; }
|
||||
public TimeSpan MaximumTempBanTime { get; set; } = new TimeSpan(24 * 30, 0, 0);
|
||||
public string Id { get; set; }
|
||||
public List<ServerConfiguration> Servers { get; set; }
|
||||
public int AutoMessagePeriod { get; set; }
|
||||
|
@ -3,6 +3,7 @@ using SharedLibraryCore.Database.Models;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using static SharedLibraryCore.Database.Models.EFClient;
|
||||
|
||||
namespace SharedLibraryCore.Database
|
||||
{
|
||||
@ -47,7 +48,7 @@ namespace SharedLibraryCore.Database
|
||||
Connections = 0,
|
||||
FirstConnection = DateTime.UtcNow,
|
||||
LastConnection = DateTime.UtcNow,
|
||||
Level = Objects.Player.Permission.Console,
|
||||
Level = Permission.Console,
|
||||
Masked = true,
|
||||
NetworkId = 0,
|
||||
AliasLinkId = 1,
|
||||
|
@ -1,17 +1,13 @@
|
||||
using System;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Npgsql;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
namespace SharedLibraryCore.Database
|
||||
{
|
||||
@ -26,6 +22,7 @@ namespace SharedLibraryCore.Database
|
||||
|
||||
static string _ConnectionString;
|
||||
static string _provider;
|
||||
private static readonly string _migrationPluginDirectory = @"X:\IW4MAdmin\BUILD\Plugins\";
|
||||
|
||||
public DatabaseContext(DbContextOptions<DatabaseContext> opt) : base(opt) { }
|
||||
|
||||
@ -57,7 +54,7 @@ namespace SharedLibraryCore.Database
|
||||
$"{Path.DirectorySeparatorChar}{currentPath}" :
|
||||
currentPath;
|
||||
|
||||
var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = $"{currentPath}{Path.DirectorySeparatorChar}Database{Path.DirectorySeparatorChar}Database.db" };
|
||||
var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = Path.Join(currentPath, "Database", "Database.db") };
|
||||
var connectionString = connectionStringBuilder.ToString();
|
||||
var connection = new SqliteConnection(connectionString);
|
||||
|
||||
@ -98,6 +95,9 @@ namespace SharedLibraryCore.Database
|
||||
.WithMany(p => p.AdministeredPenalties)
|
||||
.HasForeignKey(c => c.PunisherId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
entity.Property(p => p.Expires)
|
||||
.IsRequired(false);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<EFAliasLink>(entity =>
|
||||
@ -110,6 +110,7 @@ namespace SharedLibraryCore.Database
|
||||
|
||||
modelBuilder.Entity<EFAlias>(ent =>
|
||||
{
|
||||
ent.Property(a => a.IPAddress).IsRequired(false);
|
||||
ent.HasIndex(a => a.IPAddress);
|
||||
ent.Property(a => a.Name).HasMaxLength(24);
|
||||
ent.HasIndex(a => a.Name);
|
||||
@ -123,26 +124,15 @@ namespace SharedLibraryCore.Database
|
||||
|
||||
// adapted from
|
||||
// https://aleemkhan.wordpress.com/2013/02/28/dynamically-adding-dbset-properties-in-dbcontext-for-entity-framework-code-first/
|
||||
IEnumerable<string> directoryFiles;
|
||||
|
||||
string pluginDir = $@"{Environment.CurrentDirectory}{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}Debug{Path.DirectorySeparatorChar}netcoreapp2.1{Path.DirectorySeparatorChar}Plugins";
|
||||
|
||||
if (!Directory.Exists(pluginDir))
|
||||
{
|
||||
pluginDir = Path.Join(Environment.CurrentDirectory, "Plugins");
|
||||
|
||||
if (!Directory.Exists(pluginDir))
|
||||
{
|
||||
pluginDir = Path.Join(Utilities.OperatingDirectory, "Plugins");
|
||||
}
|
||||
}
|
||||
|
||||
directoryFiles = Directory.GetFiles(pluginDir).Where(f => f.EndsWith(".dll"));
|
||||
#if DEBUG == TRUE
|
||||
foreach (string dllPath in Directory.GetFiles(@"C:\Projects\IW4M-Admin\Application\bin\Debug\netcoreapp2.1\Plugins").Where(f => f.EndsWith(".dll")))
|
||||
#if DEBUG
|
||||
string pluginDir = _migrationPluginDirectory;
|
||||
#else
|
||||
foreach (string dllPath in directoryFiles)
|
||||
string pluginDir = Path.Join(Utilities.OperatingDirectory, "Plugins");
|
||||
#endif
|
||||
IEnumerable<string> directoryFiles = Directory.GetFiles(pluginDir).Where(f => f.EndsWith(".dll"));
|
||||
|
||||
|
||||
foreach (string dllPath in directoryFiles)
|
||||
{
|
||||
Assembly library;
|
||||
try
|
||||
@ -160,7 +150,9 @@ namespace SharedLibraryCore.Database
|
||||
.Select(c => (IModelConfiguration)Activator.CreateInstance(c));
|
||||
|
||||
foreach (var configurable in configurations)
|
||||
{
|
||||
configurable.Configure(modelBuilder);
|
||||
}
|
||||
|
||||
foreach (var type in library.ExportedTypes)
|
||||
{
|
||||
|
@ -16,7 +16,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
[MaxLength(24)]
|
||||
public string Name { get; set; }
|
||||
[Required]
|
||||
public int IPAddress { get; set; }
|
||||
public int? IPAddress { get; set; }
|
||||
[Required]
|
||||
public DateTime DateAdded { get; set; }
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace SharedLibraryCore.Database.Models
|
||||
{
|
||||
public class EFClient : SharedEntity
|
||||
public partial class EFClient : SharedEntity
|
||||
{
|
||||
[Key]
|
||||
public int ClientId { get; set; }
|
||||
@ -25,7 +25,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
[ForeignKey("AliasLinkId")]
|
||||
public virtual EFAliasLink AliasLink { get; set; }
|
||||
[Required]
|
||||
public Objects.Player.Permission Level { get; set; }
|
||||
public Permission Level { get; set; }
|
||||
|
||||
[Required]
|
||||
public int CurrentAliasId { get; set; }
|
||||
@ -41,27 +41,21 @@ namespace SharedLibraryCore.Database.Models
|
||||
public virtual string Name
|
||||
{
|
||||
get { return CurrentAlias.Name; }
|
||||
set { }
|
||||
set { CurrentAlias.Name = value; }
|
||||
}
|
||||
[NotMapped]
|
||||
public virtual int IPAddress
|
||||
public virtual int? IPAddress
|
||||
{
|
||||
get { return CurrentAlias.IPAddress; }
|
||||
set { }
|
||||
set { CurrentAlias.IPAddress = value; }
|
||||
}
|
||||
|
||||
[NotMapped]
|
||||
public string IPAddressString => new System.Net.IPAddress(BitConverter.GetBytes(IPAddress)).ToString();
|
||||
public string IPAddressString => IPAddress.ConvertIPtoString();
|
||||
[NotMapped]
|
||||
public virtual IDictionary<int, long> LinkedAccounts { get; set; }
|
||||
|
||||
public virtual ICollection<EFPenalty> ReceivedPenalties { get; set; }
|
||||
public virtual ICollection<EFPenalty> AdministeredPenalties { get; set; }
|
||||
|
||||
public EFClient()
|
||||
{
|
||||
ReceivedPenalties = new List<EFPenalty>();
|
||||
AdministeredPenalties = new List<EFPenalty>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
[Required]
|
||||
public DateTime When { get; set; }
|
||||
[Required]
|
||||
public DateTime Expires { get; set; }
|
||||
public DateTime? Expires { get; set; }
|
||||
[Required]
|
||||
public string Offense { get; set; }
|
||||
public string AutomatedOffense { get; set; }
|
||||
|
@ -1,17 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static SharedLibraryCore.Objects.Player;
|
||||
|
||||
namespace SharedLibraryCore.Dtos
|
||||
namespace SharedLibraryCore.Dtos
|
||||
{
|
||||
public class ClientInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int ClientId { get; set; }
|
||||
public int LinkId { get; set; }
|
||||
public Permission Level { get; set; }
|
||||
public Database.Models.EFClient.Permission Level { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ namespace SharedLibraryCore.Dtos
|
||||
/// </summary>
|
||||
public class EntityInfo
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public long Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ namespace SharedLibraryCore.Dtos
|
||||
public List<ChatInfo> ChatHistory { get; set; }
|
||||
public List<PlayerInfo> Players { get; set; }
|
||||
public Helpers.PlayerHistory[] PlayerHistory { get; set; }
|
||||
public int ID { get; set; }
|
||||
public long ID { get; set; }
|
||||
public bool Online { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ namespace SharedLibraryCore.Events
|
||||
OwnerEntity = new EntityInfo()
|
||||
{
|
||||
Name = E.Owner.Hostname,
|
||||
Id = E.Owner.GetHashCode()
|
||||
Id = E.Owner.EndPoint
|
||||
},
|
||||
OriginEntity = E.Origin == null ? null : new EntityInfo()
|
||||
{
|
||||
|
@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharedLibraryCore.Objects;
|
||||
|
||||
namespace SharedLibraryCore
|
||||
{
|
||||
@ -50,11 +50,11 @@ namespace SharedLibraryCore
|
||||
/// </summary>
|
||||
Stop,
|
||||
/// <summary>
|
||||
/// a client was detecting as connecting via RCon
|
||||
/// a client was detecting as connecting via log
|
||||
/// </summary>
|
||||
Connect,
|
||||
/// <summary>
|
||||
/// a client was detecting joining via log
|
||||
/// a client was detecting joining by RCon
|
||||
/// </summary>
|
||||
Join,
|
||||
/// <summary>
|
||||
@ -73,6 +73,18 @@ namespace SharedLibraryCore
|
||||
/// the current map changed
|
||||
/// </summary>
|
||||
MapChange,
|
||||
/// <summary>
|
||||
/// a client was detected as starting to connect
|
||||
/// </summary>
|
||||
PreConnect,
|
||||
/// <summary>
|
||||
/// a client was detecting as starting to disconnect
|
||||
/// </summary>
|
||||
PreDisconnect,
|
||||
/// <summary>
|
||||
/// a client's information was updated
|
||||
/// </summary>
|
||||
Update,
|
||||
|
||||
// events "generated" by clients
|
||||
/// <summary>
|
||||
@ -158,7 +170,10 @@ namespace SharedLibraryCore
|
||||
}
|
||||
|
||||
static long NextEventId;
|
||||
static long GetNextEventId() => Interlocked.Increment(ref NextEventId);
|
||||
static long GetNextEventId()
|
||||
{
|
||||
return Interlocked.Increment(ref NextEventId);
|
||||
}
|
||||
|
||||
public GameEvent()
|
||||
{
|
||||
@ -170,8 +185,8 @@ namespace SharedLibraryCore
|
||||
public EventType Type;
|
||||
public string Data; // Data is usually the message sent by player
|
||||
public string Message;
|
||||
public Player Origin;
|
||||
public Player Target;
|
||||
public EFClient Origin;
|
||||
public EFClient Target;
|
||||
public Server Owner;
|
||||
public Boolean Remote = false;
|
||||
public object Extra { get; set; }
|
||||
@ -185,43 +200,13 @@ namespace SharedLibraryCore
|
||||
/// asynchronously wait for GameEvent to be processed
|
||||
/// </summary>
|
||||
/// <returns>waitable task </returns>
|
||||
public Task<GameEvent> WaitAsync(int timeOut = int.MaxValue) => Task.Run(() =>
|
||||
public Task<GameEvent> WaitAsync(int timeOut = int.MaxValue)
|
||||
{
|
||||
OnProcessed.Wait(timeOut);
|
||||
return this;
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// determine whether an event should be delayed or not
|
||||
/// applies only to the origin entity
|
||||
/// </summary>
|
||||
/// <param name="queuedEvent">event to determine status for</param>
|
||||
/// <returns>true if event should be delayed, false otherwise</returns>
|
||||
public static bool ShouldOriginEventBeDelayed(GameEvent queuedEvent)
|
||||
{
|
||||
return queuedEvent.Origin != null &&
|
||||
(queuedEvent.Origin.State != Player.ClientState.Connected &&
|
||||
// we want to allow join and quit events
|
||||
queuedEvent.Type != EventType.Connect &&
|
||||
queuedEvent.Type != EventType.Join &&
|
||||
queuedEvent.Type != EventType.Quit &&
|
||||
queuedEvent.Type != EventType.Disconnect &&
|
||||
// we don't care about unknown events
|
||||
queuedEvent.Origin.NetworkId != 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// determine whether an event should be delayed or not
|
||||
/// applies only to the target entity
|
||||
/// </summary>
|
||||
/// <param name="queuedEvent">event to determine status for</param>
|
||||
/// <returns>true if event should be delayed, false otherwise</returns>
|
||||
public static bool ShouldTargetEventBeDelayed(GameEvent queuedEvent)
|
||||
{
|
||||
return (queuedEvent.Target != null && queuedEvent.Target.ClientNumber != -1) &&
|
||||
(queuedEvent.Target.State != Player.ClientState.Connected &&
|
||||
queuedEvent.Target.NetworkId != 0 &&
|
||||
queuedEvent.Origin?.ClientId != 1);
|
||||
return Task.Run(() =>
|
||||
{
|
||||
OnProcessed.Wait(timeOut);
|
||||
return this;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +0,0 @@
|
||||
using SharedLibraryCore.Objects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharedLibraryCore.Interfaces
|
||||
{
|
||||
public interface IClientAuthentication
|
||||
{
|
||||
/// <summary>
|
||||
/// request authentication when a client join event
|
||||
/// occurs in the log, as no IP is given
|
||||
/// </summary>
|
||||
/// <param name="client">client that has joined from the log</param>
|
||||
void RequestClientAuthentication(Player client);
|
||||
/// <summary>
|
||||
/// get all clients that have been authenticated by the status poll
|
||||
/// </summary>
|
||||
/// <returns>list of all authenticated clients</returns>
|
||||
IList<Player> GetAuthenticatedClients();
|
||||
/// <summary>
|
||||
/// authenticate a list of clients from status poll
|
||||
/// </summary>
|
||||
/// <param name="clients">list of clients to authenticate</param>
|
||||
void AuthenticateClients(IList<Player> clients);
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibraryCore.Interfaces
|
||||
{
|
||||
@ -16,7 +17,7 @@ namespace SharedLibraryCore.Interfaces
|
||||
/// <param name="fileSizeDiff"></param>
|
||||
/// <param name="startPosition"></param>
|
||||
/// <returns></returns>
|
||||
ICollection<GameEvent> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition);
|
||||
Task<ICollection<GameEvent>> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition);
|
||||
/// <summary>
|
||||
/// how long the log file is
|
||||
/// </summary>
|
||||
|
@ -5,6 +5,7 @@ using SharedLibraryCore.Objects;
|
||||
using SharedLibraryCore.Services;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using System.Reflection;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
|
||||
namespace SharedLibraryCore.Interfaces
|
||||
{
|
||||
@ -13,16 +14,16 @@ namespace SharedLibraryCore.Interfaces
|
||||
Task Init();
|
||||
void Start();
|
||||
void Stop();
|
||||
ILogger GetLogger(int serverId);
|
||||
ILogger GetLogger(long serverId);
|
||||
IList<Server> GetServers();
|
||||
IList<Command> GetCommands();
|
||||
IList<Helpers.MessageToken> GetMessageTokens();
|
||||
IList<Player> GetActiveClients();
|
||||
IList<EFClient> GetActiveClients();
|
||||
IConfigurationHandler<ApplicationConfiguration> GetApplicationSettings();
|
||||
ClientService GetClientService();
|
||||
AliasService GetAliasService();
|
||||
PenaltyService GetPenaltyService();
|
||||
IDictionary<int, Player> GetPrivilegedClients();
|
||||
IDictionary<int, EFClient> GetPrivilegedClients();
|
||||
/// <summary>
|
||||
/// Get the event handlers
|
||||
/// </summary>
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Objects;
|
||||
using SharedLibraryCore.RCon;
|
||||
|
||||
@ -10,7 +11,7 @@ namespace SharedLibraryCore.Interfaces
|
||||
Task<Dvar<T>> GetDvarAsync<T>(Connection connection, string dvarName);
|
||||
Task<bool> SetDvarAsync(Connection connection, string dvarName, object dvarValue);
|
||||
Task<string[]> ExecuteCommandAsync(Connection connection, string command);
|
||||
Task<List<Player>> GetStatusAsync(Connection connection);
|
||||
Task<List<EFClient>> GetStatusAsync(Connection connection);
|
||||
CommandPrefix GetCommandPrefixes();
|
||||
}
|
||||
}
|
||||
|
690
SharedLibraryCore/Migrations/20181014171848_MakePenaltyExpirationNullable.Designer.cs
generated
Normal file
690
SharedLibraryCore/Migrations/20181014171848_MakePenaltyExpirationNullable.Designer.cs
generated
Normal file
@ -0,0 +1,690 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using SharedLibraryCore.Database;
|
||||
|
||||
namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
[DbContext(typeof(DatabaseContext))]
|
||||
[Migration("20181014171848_MakePenaltyExpirationNullable")]
|
||||
partial class MakePenaltyExpirationNullable
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.1.4-rtm-31024");
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
b.Property<int>("SnapshotId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<int>("CurrentSessionLength");
|
||||
|
||||
b.Property<double>("CurrentStrain");
|
||||
|
||||
b.Property<int>("CurrentViewAngleId");
|
||||
|
||||
b.Property<int>("Deaths");
|
||||
|
||||
b.Property<double>("Distance");
|
||||
|
||||
b.Property<double>("EloRating");
|
||||
|
||||
b.Property<int>("HitDestinationId");
|
||||
|
||||
b.Property<int>("HitLocation");
|
||||
|
||||
b.Property<int>("HitOriginId");
|
||||
|
||||
b.Property<int>("HitType");
|
||||
|
||||
b.Property<int>("Hits");
|
||||
|
||||
b.Property<int>("Kills");
|
||||
|
||||
b.Property<int>("LastStrainAngleId");
|
||||
|
||||
b.Property<double>("SessionAngleOffset");
|
||||
|
||||
b.Property<double>("SessionSPM");
|
||||
|
||||
b.Property<int>("SessionScore");
|
||||
|
||||
b.Property<double>("StrainAngleBetween");
|
||||
|
||||
b.Property<int>("TimeSinceLastEvent");
|
||||
|
||||
b.Property<int>("WeaponId");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("SnapshotId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("CurrentViewAngleId");
|
||||
|
||||
b.HasIndex("HitDestinationId");
|
||||
|
||||
b.HasIndex("HitOriginId");
|
||||
|
||||
b.HasIndex("LastStrainAngleId");
|
||||
|
||||
b.ToTable("EFACSnapshot");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
|
||||
{
|
||||
b.Property<long>("KillId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("AttackerId");
|
||||
|
||||
b.Property<int>("Damage");
|
||||
|
||||
b.Property<int?>("DeathOriginVector3Id");
|
||||
|
||||
b.Property<int>("DeathType");
|
||||
|
||||
b.Property<double>("Fraction");
|
||||
|
||||
b.Property<int>("HitLoc");
|
||||
|
||||
b.Property<bool>("IsKill");
|
||||
|
||||
b.Property<int?>("KillOriginVector3Id");
|
||||
|
||||
b.Property<int>("Map");
|
||||
|
||||
b.Property<int>("ServerId");
|
||||
|
||||
b.Property<int>("VictimId");
|
||||
|
||||
b.Property<int?>("ViewAnglesVector3Id");
|
||||
|
||||
b.Property<double>("VisibilityPercentage");
|
||||
|
||||
b.Property<int>("Weapon");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("KillId");
|
||||
|
||||
b.HasIndex("AttackerId");
|
||||
|
||||
b.HasIndex("DeathOriginVector3Id");
|
||||
|
||||
b.HasIndex("KillOriginVector3Id");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("VictimId");
|
||||
|
||||
b.HasIndex("ViewAnglesVector3Id");
|
||||
|
||||
b.ToTable("EFClientKills");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
|
||||
{
|
||||
b.Property<long>("MessageId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("Message");
|
||||
|
||||
b.Property<int>("ServerId");
|
||||
|
||||
b.Property<DateTime>("TimeSent");
|
||||
|
||||
b.HasKey("MessageId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("TimeSent");
|
||||
|
||||
b.ToTable("EFClientMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.Property<int>("RatingHistoryId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.HasKey("RatingHistoryId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("EFClientRatingHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
|
||||
{
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<int>("ServerId");
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("Deaths");
|
||||
|
||||
b.Property<double>("EloRating");
|
||||
|
||||
b.Property<int>("Kills");
|
||||
|
||||
b.Property<double>("MaxStrain");
|
||||
|
||||
b.Property<double>("RollingWeightedKDR");
|
||||
|
||||
b.Property<double>("SPM");
|
||||
|
||||
b.Property<double>("Skill");
|
||||
|
||||
b.Property<int>("TimePlayed");
|
||||
|
||||
b.Property<double>("VisionAverage");
|
||||
|
||||
b.HasKey("ClientId", "ServerId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFClientStatistics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
|
||||
{
|
||||
b.Property<int>("HitLocationCountId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnName("EFClientStatistics_ClientId");
|
||||
|
||||
b.Property<int>("HitCount");
|
||||
|
||||
b.Property<float>("HitOffsetAverage");
|
||||
|
||||
b.Property<int>("Location");
|
||||
|
||||
b.Property<float>("MaxAngleDistance");
|
||||
|
||||
b.Property<int>("ServerId")
|
||||
.HasColumnName("EFClientStatistics_ServerId");
|
||||
|
||||
b.HasKey("HitLocationCountId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("ClientId", "ServerId");
|
||||
|
||||
b.ToTable("EFHitLocationCounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
|
||||
{
|
||||
b.Property<int>("RatingId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ActivityAmount");
|
||||
|
||||
b.Property<bool>("Newest");
|
||||
|
||||
b.Property<double>("Performance");
|
||||
|
||||
b.Property<int>("Ranking");
|
||||
|
||||
b.Property<int>("RatingHistoryId");
|
||||
|
||||
b.Property<int?>("ServerId");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("RatingId");
|
||||
|
||||
b.HasIndex("Performance");
|
||||
|
||||
b.HasIndex("Ranking");
|
||||
|
||||
b.HasIndex("RatingHistoryId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("When");
|
||||
|
||||
b.ToTable("EFRating");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServer", b =>
|
||||
{
|
||||
b.Property<int>("ServerId");
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("Port");
|
||||
|
||||
b.HasKey("ServerId");
|
||||
|
||||
b.ToTable("EFServers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
|
||||
{
|
||||
b.Property<int>("StatisticId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ServerId");
|
||||
|
||||
b.Property<long>("TotalKills");
|
||||
|
||||
b.Property<long>("TotalPlayTime");
|
||||
|
||||
b.HasKey("StatisticId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFServerStatistics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
|
||||
{
|
||||
b.Property<int>("AliasId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<DateTime>("DateAdded");
|
||||
|
||||
b.Property<int>("IPAddress");
|
||||
|
||||
b.Property<int>("LinkId");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(24);
|
||||
|
||||
b.HasKey("AliasId");
|
||||
|
||||
b.HasIndex("IPAddress");
|
||||
|
||||
b.HasIndex("LinkId");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("EFAlias");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAliasLink", b =>
|
||||
{
|
||||
b.Property<int>("AliasLinkId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.HasKey("AliasLinkId");
|
||||
|
||||
b.ToTable("EFAliasLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b =>
|
||||
{
|
||||
b.Property<int>("ChangeHistoryId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("CurrentValue");
|
||||
|
||||
b.Property<int>("OriginEntityId");
|
||||
|
||||
b.Property<string>("PreviousValue");
|
||||
|
||||
b.Property<int>("TargetEntityId");
|
||||
|
||||
b.Property<DateTime>("TimeChanged");
|
||||
|
||||
b.Property<int>("TypeOfChange");
|
||||
|
||||
b.HasKey("ChangeHistoryId");
|
||||
|
||||
b.ToTable("EFChangeHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
|
||||
{
|
||||
b.Property<int>("ClientId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("AliasLinkId");
|
||||
|
||||
b.Property<int>("Connections");
|
||||
|
||||
b.Property<int>("CurrentAliasId");
|
||||
|
||||
b.Property<DateTime>("FirstConnection");
|
||||
|
||||
b.Property<DateTime>("LastConnection");
|
||||
|
||||
b.Property<int>("Level");
|
||||
|
||||
b.Property<bool>("Masked");
|
||||
|
||||
b.Property<long>("NetworkId");
|
||||
|
||||
b.Property<string>("Password");
|
||||
|
||||
b.Property<string>("PasswordSalt");
|
||||
|
||||
b.Property<int>("TotalConnectionTime");
|
||||
|
||||
b.HasKey("ClientId");
|
||||
|
||||
b.HasIndex("AliasLinkId");
|
||||
|
||||
b.HasIndex("CurrentAliasId");
|
||||
|
||||
b.HasIndex("NetworkId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("EFClients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
|
||||
{
|
||||
b.Property<int>("MetaId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<DateTime>("Created");
|
||||
|
||||
b.Property<string>("Extra");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<DateTime>("Updated");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("MetaId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("EFMeta");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
|
||||
{
|
||||
b.Property<int>("PenaltyId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<string>("AutomatedOffense");
|
||||
|
||||
b.Property<DateTime?>("Expires");
|
||||
|
||||
b.Property<int>("LinkId");
|
||||
|
||||
b.Property<int>("OffenderId");
|
||||
|
||||
b.Property<string>("Offense")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<int>("PunisherId");
|
||||
|
||||
b.Property<int>("Type");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("PenaltyId");
|
||||
|
||||
b.HasIndex("LinkId");
|
||||
|
||||
b.HasIndex("OffenderId");
|
||||
|
||||
b.HasIndex("PunisherId");
|
||||
|
||||
b.ToTable("EFPenalties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
|
||||
{
|
||||
b.Property<int>("Vector3Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int?>("EFACSnapshotSnapshotId");
|
||||
|
||||
b.Property<float>("X");
|
||||
|
||||
b.Property<float>("Y");
|
||||
|
||||
b.Property<float>("Z");
|
||||
|
||||
b.HasKey("Vector3Id");
|
||||
|
||||
b.HasIndex("EFACSnapshotSnapshotId");
|
||||
|
||||
b.ToTable("Vector3");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "CurrentViewAngle")
|
||||
.WithMany()
|
||||
.HasForeignKey("CurrentViewAngleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitDestination")
|
||||
.WithMany()
|
||||
.HasForeignKey("HitDestinationId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("HitOriginId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "LastStrainAngle")
|
||||
.WithMany()
|
||||
.HasForeignKey("LastStrainAngleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Attacker")
|
||||
.WithMany()
|
||||
.HasForeignKey("AttackerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "DeathOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("DeathOriginVector3Id");
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "KillOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("KillOriginVector3Id");
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Victim")
|
||||
.WithMany()
|
||||
.HasForeignKey("VictimId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "ViewAngles")
|
||||
.WithMany()
|
||||
.HasForeignKey("ViewAnglesVector3Id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics")
|
||||
.WithMany("HitLocations")
|
||||
.HasForeignKey("ClientId", "ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", "RatingHistory")
|
||||
.WithMany("Ratings")
|
||||
.HasForeignKey("RatingHistoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
|
||||
.WithMany("Children")
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "AliasLink")
|
||||
.WithMany()
|
||||
.HasForeignKey("AliasLinkId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAlias", "CurrentAlias")
|
||||
.WithMany()
|
||||
.HasForeignKey("CurrentAliasId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany("Meta")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
|
||||
.WithMany("ReceivedPenalties")
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Offender")
|
||||
.WithMany("ReceivedPenalties")
|
||||
.HasForeignKey("OffenderId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Punisher")
|
||||
.WithMany("AdministeredPenalties")
|
||||
.HasForeignKey("PunisherId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot")
|
||||
.WithMany("PredictedViewAngles")
|
||||
.HasForeignKey("EFACSnapshotSnapshotId");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
public partial class MakePenaltyExpirationNullable : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder.ActiveProvider == "Microsoft.EntityFrameworkCore.Sqlite")
|
||||
{
|
||||
migrationBuilder.Sql(@"PRAGMA foreign_keys = 0;
|
||||
|
||||
CREATE TABLE sqlitestudio_temp_table AS SELECT *
|
||||
FROM EFPenalties;
|
||||
|
||||
DROP TABLE EFPenalties;
|
||||
|
||||
CREATE TABLE EFPenalties (
|
||||
PenaltyId INTEGER NOT NULL
|
||||
CONSTRAINT PK_EFPenalties PRIMARY KEY AUTOINCREMENT,
|
||||
Active INTEGER NOT NULL,
|
||||
Expires TEXT,
|
||||
LinkId INTEGER NOT NULL,
|
||||
OffenderId INTEGER NOT NULL,
|
||||
Offense TEXT NOT NULL,
|
||||
PunisherId INTEGER NOT NULL,
|
||||
Type INTEGER NOT NULL,
|
||||
[When] TEXT NOT NULL,
|
||||
AutomatedOffense TEXT,
|
||||
CONSTRAINT FK_EFPenalties_EFAliasLinks_LinkId FOREIGN KEY (
|
||||
LinkId
|
||||
)
|
||||
REFERENCES EFAliasLinks (AliasLinkId) ON DELETE CASCADE,
|
||||
CONSTRAINT FK_EFPenalties_EFClients_OffenderId FOREIGN KEY (
|
||||
OffenderId
|
||||
)
|
||||
REFERENCES EFClients (ClientId) ON DELETE RESTRICT,
|
||||
CONSTRAINT FK_EFPenalties_EFClients_PunisherId FOREIGN KEY (
|
||||
PunisherId
|
||||
)
|
||||
REFERENCES EFClients (ClientId) ON DELETE RESTRICT
|
||||
);
|
||||
|
||||
INSERT INTO EFPenalties (
|
||||
PenaltyId,
|
||||
Active,
|
||||
Expires,
|
||||
LinkId,
|
||||
OffenderId,
|
||||
Offense,
|
||||
PunisherId,
|
||||
Type,
|
||||
[When],
|
||||
AutomatedOffense
|
||||
)
|
||||
SELECT PenaltyId,
|
||||
Active,
|
||||
Expires,
|
||||
LinkId,
|
||||
OffenderId,
|
||||
Offense,
|
||||
PunisherId,
|
||||
Type,
|
||||
""When"",
|
||||
AutomatedOffense
|
||||
FROM sqlitestudio_temp_table;
|
||||
|
||||
DROP TABLE sqlitestudio_temp_table;
|
||||
|
||||
CREATE INDEX IX_EFPenalties_LinkId ON EFPenalties(
|
||||
""LinkId""
|
||||
);
|
||||
|
||||
CREATE INDEX IX_EFPenalties_OffenderId ON EFPenalties(
|
||||
""OffenderId""
|
||||
);
|
||||
|
||||
CREATE INDEX IX_EFPenalties_PunisherId ON EFPenalties(
|
||||
""PunisherId""
|
||||
);
|
||||
|
||||
PRAGMA foreign_keys = 1; ");
|
||||
}
|
||||
else
|
||||
{
|
||||
migrationBuilder.AlterColumn<DateTime>(
|
||||
name: "Expires",
|
||||
table: "EFPenalties",
|
||||
nullable: true,
|
||||
oldClrType: typeof(DateTime));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<DateTime>(
|
||||
name: "Expires",
|
||||
table: "EFPenalties",
|
||||
nullable: false,
|
||||
oldClrType: typeof(DateTime),
|
||||
oldNullable: true);
|
||||
}
|
||||
}
|
||||
}
|
690
SharedLibraryCore/Migrations/20181125193243_MakeClientIPNullable.Designer.cs
generated
Normal file
690
SharedLibraryCore/Migrations/20181125193243_MakeClientIPNullable.Designer.cs
generated
Normal file
@ -0,0 +1,690 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using SharedLibraryCore.Database;
|
||||
|
||||
namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
[DbContext(typeof(DatabaseContext))]
|
||||
[Migration("20181125193243_MakeClientIPNullable")]
|
||||
partial class MakeClientIPNullable
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.1.4-rtm-31024");
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
b.Property<int>("SnapshotId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<int>("CurrentSessionLength");
|
||||
|
||||
b.Property<double>("CurrentStrain");
|
||||
|
||||
b.Property<int>("CurrentViewAngleId");
|
||||
|
||||
b.Property<int>("Deaths");
|
||||
|
||||
b.Property<double>("Distance");
|
||||
|
||||
b.Property<double>("EloRating");
|
||||
|
||||
b.Property<int>("HitDestinationId");
|
||||
|
||||
b.Property<int>("HitLocation");
|
||||
|
||||
b.Property<int>("HitOriginId");
|
||||
|
||||
b.Property<int>("HitType");
|
||||
|
||||
b.Property<int>("Hits");
|
||||
|
||||
b.Property<int>("Kills");
|
||||
|
||||
b.Property<int>("LastStrainAngleId");
|
||||
|
||||
b.Property<double>("SessionAngleOffset");
|
||||
|
||||
b.Property<double>("SessionSPM");
|
||||
|
||||
b.Property<int>("SessionScore");
|
||||
|
||||
b.Property<double>("StrainAngleBetween");
|
||||
|
||||
b.Property<int>("TimeSinceLastEvent");
|
||||
|
||||
b.Property<int>("WeaponId");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("SnapshotId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("CurrentViewAngleId");
|
||||
|
||||
b.HasIndex("HitDestinationId");
|
||||
|
||||
b.HasIndex("HitOriginId");
|
||||
|
||||
b.HasIndex("LastStrainAngleId");
|
||||
|
||||
b.ToTable("EFACSnapshot");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
|
||||
{
|
||||
b.Property<long>("KillId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("AttackerId");
|
||||
|
||||
b.Property<int>("Damage");
|
||||
|
||||
b.Property<int?>("DeathOriginVector3Id");
|
||||
|
||||
b.Property<int>("DeathType");
|
||||
|
||||
b.Property<double>("Fraction");
|
||||
|
||||
b.Property<int>("HitLoc");
|
||||
|
||||
b.Property<bool>("IsKill");
|
||||
|
||||
b.Property<int?>("KillOriginVector3Id");
|
||||
|
||||
b.Property<int>("Map");
|
||||
|
||||
b.Property<int>("ServerId");
|
||||
|
||||
b.Property<int>("VictimId");
|
||||
|
||||
b.Property<int?>("ViewAnglesVector3Id");
|
||||
|
||||
b.Property<double>("VisibilityPercentage");
|
||||
|
||||
b.Property<int>("Weapon");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("KillId");
|
||||
|
||||
b.HasIndex("AttackerId");
|
||||
|
||||
b.HasIndex("DeathOriginVector3Id");
|
||||
|
||||
b.HasIndex("KillOriginVector3Id");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("VictimId");
|
||||
|
||||
b.HasIndex("ViewAnglesVector3Id");
|
||||
|
||||
b.ToTable("EFClientKills");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
|
||||
{
|
||||
b.Property<long>("MessageId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("Message");
|
||||
|
||||
b.Property<int>("ServerId");
|
||||
|
||||
b.Property<DateTime>("TimeSent");
|
||||
|
||||
b.HasKey("MessageId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("TimeSent");
|
||||
|
||||
b.ToTable("EFClientMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.Property<int>("RatingHistoryId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.HasKey("RatingHistoryId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("EFClientRatingHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
|
||||
{
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<int>("ServerId");
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("Deaths");
|
||||
|
||||
b.Property<double>("EloRating");
|
||||
|
||||
b.Property<int>("Kills");
|
||||
|
||||
b.Property<double>("MaxStrain");
|
||||
|
||||
b.Property<double>("RollingWeightedKDR");
|
||||
|
||||
b.Property<double>("SPM");
|
||||
|
||||
b.Property<double>("Skill");
|
||||
|
||||
b.Property<int>("TimePlayed");
|
||||
|
||||
b.Property<double>("VisionAverage");
|
||||
|
||||
b.HasKey("ClientId", "ServerId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFClientStatistics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
|
||||
{
|
||||
b.Property<int>("HitLocationCountId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnName("EFClientStatistics_ClientId");
|
||||
|
||||
b.Property<int>("HitCount");
|
||||
|
||||
b.Property<float>("HitOffsetAverage");
|
||||
|
||||
b.Property<int>("Location");
|
||||
|
||||
b.Property<float>("MaxAngleDistance");
|
||||
|
||||
b.Property<int>("ServerId")
|
||||
.HasColumnName("EFClientStatistics_ServerId");
|
||||
|
||||
b.HasKey("HitLocationCountId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("ClientId", "ServerId");
|
||||
|
||||
b.ToTable("EFHitLocationCounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
|
||||
{
|
||||
b.Property<int>("RatingId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ActivityAmount");
|
||||
|
||||
b.Property<bool>("Newest");
|
||||
|
||||
b.Property<double>("Performance");
|
||||
|
||||
b.Property<int>("Ranking");
|
||||
|
||||
b.Property<int>("RatingHistoryId");
|
||||
|
||||
b.Property<int?>("ServerId");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("RatingId");
|
||||
|
||||
b.HasIndex("Performance");
|
||||
|
||||
b.HasIndex("Ranking");
|
||||
|
||||
b.HasIndex("RatingHistoryId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("When");
|
||||
|
||||
b.ToTable("EFRating");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServer", b =>
|
||||
{
|
||||
b.Property<int>("ServerId");
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("Port");
|
||||
|
||||
b.HasKey("ServerId");
|
||||
|
||||
b.ToTable("EFServers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
|
||||
{
|
||||
b.Property<int>("StatisticId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ServerId");
|
||||
|
||||
b.Property<long>("TotalKills");
|
||||
|
||||
b.Property<long>("TotalPlayTime");
|
||||
|
||||
b.HasKey("StatisticId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFServerStatistics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
|
||||
{
|
||||
b.Property<int>("AliasId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<DateTime>("DateAdded");
|
||||
|
||||
b.Property<int?>("IPAddress");
|
||||
|
||||
b.Property<int>("LinkId");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(24);
|
||||
|
||||
b.HasKey("AliasId");
|
||||
|
||||
b.HasIndex("IPAddress");
|
||||
|
||||
b.HasIndex("LinkId");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("EFAlias");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAliasLink", b =>
|
||||
{
|
||||
b.Property<int>("AliasLinkId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.HasKey("AliasLinkId");
|
||||
|
||||
b.ToTable("EFAliasLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b =>
|
||||
{
|
||||
b.Property<int>("ChangeHistoryId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("CurrentValue");
|
||||
|
||||
b.Property<int>("OriginEntityId");
|
||||
|
||||
b.Property<string>("PreviousValue");
|
||||
|
||||
b.Property<int>("TargetEntityId");
|
||||
|
||||
b.Property<DateTime>("TimeChanged");
|
||||
|
||||
b.Property<int>("TypeOfChange");
|
||||
|
||||
b.HasKey("ChangeHistoryId");
|
||||
|
||||
b.ToTable("EFChangeHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
|
||||
{
|
||||
b.Property<int>("ClientId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("AliasLinkId");
|
||||
|
||||
b.Property<int>("Connections");
|
||||
|
||||
b.Property<int>("CurrentAliasId");
|
||||
|
||||
b.Property<DateTime>("FirstConnection");
|
||||
|
||||
b.Property<DateTime>("LastConnection");
|
||||
|
||||
b.Property<int>("Level");
|
||||
|
||||
b.Property<bool>("Masked");
|
||||
|
||||
b.Property<long>("NetworkId");
|
||||
|
||||
b.Property<string>("Password");
|
||||
|
||||
b.Property<string>("PasswordSalt");
|
||||
|
||||
b.Property<int>("TotalConnectionTime");
|
||||
|
||||
b.HasKey("ClientId");
|
||||
|
||||
b.HasIndex("AliasLinkId");
|
||||
|
||||
b.HasIndex("CurrentAliasId");
|
||||
|
||||
b.HasIndex("NetworkId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("EFClients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
|
||||
{
|
||||
b.Property<int>("MetaId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<DateTime>("Created");
|
||||
|
||||
b.Property<string>("Extra");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<DateTime>("Updated");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("MetaId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("EFMeta");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
|
||||
{
|
||||
b.Property<int>("PenaltyId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<string>("AutomatedOffense");
|
||||
|
||||
b.Property<DateTime?>("Expires");
|
||||
|
||||
b.Property<int>("LinkId");
|
||||
|
||||
b.Property<int>("OffenderId");
|
||||
|
||||
b.Property<string>("Offense")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<int>("PunisherId");
|
||||
|
||||
b.Property<int>("Type");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("PenaltyId");
|
||||
|
||||
b.HasIndex("LinkId");
|
||||
|
||||
b.HasIndex("OffenderId");
|
||||
|
||||
b.HasIndex("PunisherId");
|
||||
|
||||
b.ToTable("EFPenalties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
|
||||
{
|
||||
b.Property<int>("Vector3Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int?>("EFACSnapshotSnapshotId");
|
||||
|
||||
b.Property<float>("X");
|
||||
|
||||
b.Property<float>("Y");
|
||||
|
||||
b.Property<float>("Z");
|
||||
|
||||
b.HasKey("Vector3Id");
|
||||
|
||||
b.HasIndex("EFACSnapshotSnapshotId");
|
||||
|
||||
b.ToTable("Vector3");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "CurrentViewAngle")
|
||||
.WithMany()
|
||||
.HasForeignKey("CurrentViewAngleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitDestination")
|
||||
.WithMany()
|
||||
.HasForeignKey("HitDestinationId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("HitOriginId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "LastStrainAngle")
|
||||
.WithMany()
|
||||
.HasForeignKey("LastStrainAngleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Attacker")
|
||||
.WithMany()
|
||||
.HasForeignKey("AttackerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "DeathOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("DeathOriginVector3Id");
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "KillOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("KillOriginVector3Id");
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Victim")
|
||||
.WithMany()
|
||||
.HasForeignKey("VictimId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "ViewAngles")
|
||||
.WithMany()
|
||||
.HasForeignKey("ViewAnglesVector3Id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics")
|
||||
.WithMany("HitLocations")
|
||||
.HasForeignKey("ClientId", "ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", "RatingHistory")
|
||||
.WithMany("Ratings")
|
||||
.HasForeignKey("RatingHistoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
|
||||
.WithMany("Children")
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "AliasLink")
|
||||
.WithMany()
|
||||
.HasForeignKey("AliasLinkId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAlias", "CurrentAlias")
|
||||
.WithMany()
|
||||
.HasForeignKey("CurrentAliasId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany("Meta")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
|
||||
.WithMany("ReceivedPenalties")
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Offender")
|
||||
.WithMany("ReceivedPenalties")
|
||||
.HasForeignKey("OffenderId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Punisher")
|
||||
.WithMany("AdministeredPenalties")
|
||||
.HasForeignKey("PunisherId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot")
|
||||
.WithMany("PredictedViewAngles")
|
||||
.HasForeignKey("EFACSnapshotSnapshotId");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
public partial class MakeClientIPNullable : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder.ActiveProvider == "Microsoft.EntityFrameworkCore.Sqlite")
|
||||
{
|
||||
migrationBuilder.Sql(@"PRAGMA foreign_keys = 0;
|
||||
|
||||
CREATE TABLE sqlitestudio_temp_table AS SELECT *
|
||||
FROM EFAlias;
|
||||
|
||||
DROP TABLE EFAlias;
|
||||
|
||||
CREATE TABLE EFAlias (
|
||||
AliasId INTEGER NOT NULL
|
||||
CONSTRAINT PK_EFAlias PRIMARY KEY AUTOINCREMENT,
|
||||
Active INTEGER NOT NULL,
|
||||
DateAdded TEXT NOT NULL,
|
||||
IPAddress INTEGER,
|
||||
LinkId INTEGER NOT NULL,
|
||||
Name TEXT NOT NULL,
|
||||
CONSTRAINT FK_EFAlias_EFAliasLinks_LinkId FOREIGN KEY (
|
||||
LinkId
|
||||
)
|
||||
REFERENCES EFAliasLinks (AliasLinkId) ON DELETE RESTRICT
|
||||
);
|
||||
|
||||
INSERT INTO EFAlias (
|
||||
AliasId,
|
||||
Active,
|
||||
DateAdded,
|
||||
IPAddress,
|
||||
LinkId,
|
||||
Name
|
||||
)
|
||||
SELECT AliasId,
|
||||
Active,
|
||||
DateAdded,
|
||||
IPAddress,
|
||||
LinkId,
|
||||
Name
|
||||
FROM sqlitestudio_temp_table;
|
||||
|
||||
DROP TABLE sqlitestudio_temp_table;
|
||||
|
||||
CREATE INDEX IX_EFAlias_LinkId ON EFAlias (
|
||||
""LinkId""
|
||||
);
|
||||
|
||||
CREATE INDEX IX_EFAlias_IPAddress ON EFAlias(
|
||||
""IPAddress""
|
||||
);
|
||||
|
||||
CREATE INDEX IX_EFAlias_Name ON EFAlias(
|
||||
""Name""
|
||||
);
|
||||
|
||||
PRAGMA foreign_keys = 1;
|
||||
", suppressTransaction:true);
|
||||
}
|
||||
else
|
||||
{
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "IPAddress",
|
||||
table: "EFAlias",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "IPAddress",
|
||||
table: "EFAlias",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldNullable: true);
|
||||
}
|
||||
}
|
||||
}
|
692
SharedLibraryCore/Migrations/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs
generated
Normal file
692
SharedLibraryCore/Migrations/20181127144417_AddEndpointToEFServerUpdateServerIdType.Designer.cs
generated
Normal file
@ -0,0 +1,692 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using SharedLibraryCore.Database;
|
||||
|
||||
namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
[DbContext(typeof(DatabaseContext))]
|
||||
[Migration("20181127144417_AddEndpointToEFServerUpdateServerIdType")]
|
||||
partial class AddEndpointToEFServerUpdateServerIdType
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.1.4-rtm-31024");
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
b.Property<int>("SnapshotId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<int>("CurrentSessionLength");
|
||||
|
||||
b.Property<double>("CurrentStrain");
|
||||
|
||||
b.Property<int>("CurrentViewAngleId");
|
||||
|
||||
b.Property<int>("Deaths");
|
||||
|
||||
b.Property<double>("Distance");
|
||||
|
||||
b.Property<double>("EloRating");
|
||||
|
||||
b.Property<int>("HitDestinationId");
|
||||
|
||||
b.Property<int>("HitLocation");
|
||||
|
||||
b.Property<int>("HitOriginId");
|
||||
|
||||
b.Property<int>("HitType");
|
||||
|
||||
b.Property<int>("Hits");
|
||||
|
||||
b.Property<int>("Kills");
|
||||
|
||||
b.Property<int>("LastStrainAngleId");
|
||||
|
||||
b.Property<double>("SessionAngleOffset");
|
||||
|
||||
b.Property<double>("SessionSPM");
|
||||
|
||||
b.Property<int>("SessionScore");
|
||||
|
||||
b.Property<double>("StrainAngleBetween");
|
||||
|
||||
b.Property<int>("TimeSinceLastEvent");
|
||||
|
||||
b.Property<int>("WeaponId");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("SnapshotId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("CurrentViewAngleId");
|
||||
|
||||
b.HasIndex("HitDestinationId");
|
||||
|
||||
b.HasIndex("HitOriginId");
|
||||
|
||||
b.HasIndex("LastStrainAngleId");
|
||||
|
||||
b.ToTable("EFACSnapshot");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
|
||||
{
|
||||
b.Property<long>("KillId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("AttackerId");
|
||||
|
||||
b.Property<int>("Damage");
|
||||
|
||||
b.Property<int?>("DeathOriginVector3Id");
|
||||
|
||||
b.Property<int>("DeathType");
|
||||
|
||||
b.Property<double>("Fraction");
|
||||
|
||||
b.Property<int>("HitLoc");
|
||||
|
||||
b.Property<bool>("IsKill");
|
||||
|
||||
b.Property<int?>("KillOriginVector3Id");
|
||||
|
||||
b.Property<int>("Map");
|
||||
|
||||
b.Property<long>("ServerId");
|
||||
|
||||
b.Property<int>("VictimId");
|
||||
|
||||
b.Property<int?>("ViewAnglesVector3Id");
|
||||
|
||||
b.Property<double>("VisibilityPercentage");
|
||||
|
||||
b.Property<int>("Weapon");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("KillId");
|
||||
|
||||
b.HasIndex("AttackerId");
|
||||
|
||||
b.HasIndex("DeathOriginVector3Id");
|
||||
|
||||
b.HasIndex("KillOriginVector3Id");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("VictimId");
|
||||
|
||||
b.HasIndex("ViewAnglesVector3Id");
|
||||
|
||||
b.ToTable("EFClientKills");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
|
||||
{
|
||||
b.Property<long>("MessageId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("Message");
|
||||
|
||||
b.Property<long>("ServerId");
|
||||
|
||||
b.Property<DateTime>("TimeSent");
|
||||
|
||||
b.HasKey("MessageId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("TimeSent");
|
||||
|
||||
b.ToTable("EFClientMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.Property<int>("RatingHistoryId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.HasKey("RatingHistoryId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("EFClientRatingHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
|
||||
{
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<long>("ServerId");
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("Deaths");
|
||||
|
||||
b.Property<double>("EloRating");
|
||||
|
||||
b.Property<int>("Kills");
|
||||
|
||||
b.Property<double>("MaxStrain");
|
||||
|
||||
b.Property<double>("RollingWeightedKDR");
|
||||
|
||||
b.Property<double>("SPM");
|
||||
|
||||
b.Property<double>("Skill");
|
||||
|
||||
b.Property<int>("TimePlayed");
|
||||
|
||||
b.Property<double>("VisionAverage");
|
||||
|
||||
b.HasKey("ClientId", "ServerId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFClientStatistics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
|
||||
{
|
||||
b.Property<int>("HitLocationCountId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnName("EFClientStatistics_ClientId");
|
||||
|
||||
b.Property<int>("HitCount");
|
||||
|
||||
b.Property<float>("HitOffsetAverage");
|
||||
|
||||
b.Property<int>("Location");
|
||||
|
||||
b.Property<float>("MaxAngleDistance");
|
||||
|
||||
b.Property<long>("ServerId")
|
||||
.HasColumnName("EFClientStatistics_ServerId");
|
||||
|
||||
b.HasKey("HitLocationCountId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("ClientId", "ServerId");
|
||||
|
||||
b.ToTable("EFHitLocationCounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
|
||||
{
|
||||
b.Property<int>("RatingId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ActivityAmount");
|
||||
|
||||
b.Property<bool>("Newest");
|
||||
|
||||
b.Property<double>("Performance");
|
||||
|
||||
b.Property<int>("Ranking");
|
||||
|
||||
b.Property<int>("RatingHistoryId");
|
||||
|
||||
b.Property<long?>("ServerId");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("RatingId");
|
||||
|
||||
b.HasIndex("Performance");
|
||||
|
||||
b.HasIndex("Ranking");
|
||||
|
||||
b.HasIndex("RatingHistoryId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("When");
|
||||
|
||||
b.ToTable("EFRating");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServer", b =>
|
||||
{
|
||||
b.Property<long>("ServerId");
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<string>("EndPoint");
|
||||
|
||||
b.Property<int>("Port");
|
||||
|
||||
b.HasKey("ServerId");
|
||||
|
||||
b.ToTable("EFServers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
|
||||
{
|
||||
b.Property<int>("StatisticId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<long>("ServerId");
|
||||
|
||||
b.Property<long>("TotalKills");
|
||||
|
||||
b.Property<long>("TotalPlayTime");
|
||||
|
||||
b.HasKey("StatisticId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFServerStatistics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
|
||||
{
|
||||
b.Property<int>("AliasId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<DateTime>("DateAdded");
|
||||
|
||||
b.Property<int?>("IPAddress");
|
||||
|
||||
b.Property<int>("LinkId");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(24);
|
||||
|
||||
b.HasKey("AliasId");
|
||||
|
||||
b.HasIndex("IPAddress");
|
||||
|
||||
b.HasIndex("LinkId");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("EFAlias");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAliasLink", b =>
|
||||
{
|
||||
b.Property<int>("AliasLinkId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.HasKey("AliasLinkId");
|
||||
|
||||
b.ToTable("EFAliasLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b =>
|
||||
{
|
||||
b.Property<int>("ChangeHistoryId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("CurrentValue");
|
||||
|
||||
b.Property<int>("OriginEntityId");
|
||||
|
||||
b.Property<string>("PreviousValue");
|
||||
|
||||
b.Property<int>("TargetEntityId");
|
||||
|
||||
b.Property<DateTime>("TimeChanged");
|
||||
|
||||
b.Property<int>("TypeOfChange");
|
||||
|
||||
b.HasKey("ChangeHistoryId");
|
||||
|
||||
b.ToTable("EFChangeHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
|
||||
{
|
||||
b.Property<int>("ClientId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("AliasLinkId");
|
||||
|
||||
b.Property<int>("Connections");
|
||||
|
||||
b.Property<int>("CurrentAliasId");
|
||||
|
||||
b.Property<DateTime>("FirstConnection");
|
||||
|
||||
b.Property<DateTime>("LastConnection");
|
||||
|
||||
b.Property<int>("Level");
|
||||
|
||||
b.Property<bool>("Masked");
|
||||
|
||||
b.Property<long>("NetworkId");
|
||||
|
||||
b.Property<string>("Password");
|
||||
|
||||
b.Property<string>("PasswordSalt");
|
||||
|
||||
b.Property<int>("TotalConnectionTime");
|
||||
|
||||
b.HasKey("ClientId");
|
||||
|
||||
b.HasIndex("AliasLinkId");
|
||||
|
||||
b.HasIndex("CurrentAliasId");
|
||||
|
||||
b.HasIndex("NetworkId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("EFClients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
|
||||
{
|
||||
b.Property<int>("MetaId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<DateTime>("Created");
|
||||
|
||||
b.Property<string>("Extra");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<DateTime>("Updated");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("MetaId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("EFMeta");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
|
||||
{
|
||||
b.Property<int>("PenaltyId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<string>("AutomatedOffense");
|
||||
|
||||
b.Property<DateTime?>("Expires");
|
||||
|
||||
b.Property<int>("LinkId");
|
||||
|
||||
b.Property<int>("OffenderId");
|
||||
|
||||
b.Property<string>("Offense")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<int>("PunisherId");
|
||||
|
||||
b.Property<int>("Type");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("PenaltyId");
|
||||
|
||||
b.HasIndex("LinkId");
|
||||
|
||||
b.HasIndex("OffenderId");
|
||||
|
||||
b.HasIndex("PunisherId");
|
||||
|
||||
b.ToTable("EFPenalties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
|
||||
{
|
||||
b.Property<int>("Vector3Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int?>("EFACSnapshotSnapshotId");
|
||||
|
||||
b.Property<float>("X");
|
||||
|
||||
b.Property<float>("Y");
|
||||
|
||||
b.Property<float>("Z");
|
||||
|
||||
b.HasKey("Vector3Id");
|
||||
|
||||
b.HasIndex("EFACSnapshotSnapshotId");
|
||||
|
||||
b.ToTable("Vector3");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "CurrentViewAngle")
|
||||
.WithMany()
|
||||
.HasForeignKey("CurrentViewAngleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitDestination")
|
||||
.WithMany()
|
||||
.HasForeignKey("HitDestinationId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("HitOriginId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "LastStrainAngle")
|
||||
.WithMany()
|
||||
.HasForeignKey("LastStrainAngleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Attacker")
|
||||
.WithMany()
|
||||
.HasForeignKey("AttackerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "DeathOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("DeathOriginVector3Id");
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "KillOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("KillOriginVector3Id");
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Victim")
|
||||
.WithMany()
|
||||
.HasForeignKey("VictimId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "ViewAngles")
|
||||
.WithMany()
|
||||
.HasForeignKey("ViewAnglesVector3Id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics")
|
||||
.WithMany("HitLocations")
|
||||
.HasForeignKey("ClientId", "ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", "RatingHistory")
|
||||
.WithMany("Ratings")
|
||||
.HasForeignKey("RatingHistoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
|
||||
.WithMany("Children")
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "AliasLink")
|
||||
.WithMany()
|
||||
.HasForeignKey("AliasLinkId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAlias", "CurrentAlias")
|
||||
.WithMany()
|
||||
.HasForeignKey("CurrentAliasId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany("Meta")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
|
||||
.WithMany("ReceivedPenalties")
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Offender")
|
||||
.WithMany("ReceivedPenalties")
|
||||
.HasForeignKey("OffenderId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Punisher")
|
||||
.WithMany("AdministeredPenalties")
|
||||
.HasForeignKey("PunisherId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot")
|
||||
.WithMany("PredictedViewAngles")
|
||||
.HasForeignKey("EFACSnapshotSnapshotId");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
public partial class AddEndpointToEFServerUpdateServerIdType : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "EndPoint",
|
||||
table: "EFServers",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "EndPoint",
|
||||
table: "EFServers");
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using SharedLibraryCore.Database;
|
||||
|
||||
namespace SharedLibraryCore.Migrations
|
||||
@ -15,9 +14,7 @@ namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
|
||||
.HasAnnotation("ProductVersion", "2.1.3-rtm-32065")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
.HasAnnotation("ProductVersion", "2.1.4-rtm-31024");
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
@ -108,7 +105,7 @@ namespace SharedLibraryCore.Migrations
|
||||
|
||||
b.Property<int>("Map");
|
||||
|
||||
b.Property<int>("ServerId");
|
||||
b.Property<long>("ServerId");
|
||||
|
||||
b.Property<int>("VictimId");
|
||||
|
||||
@ -148,7 +145,7 @@ namespace SharedLibraryCore.Migrations
|
||||
|
||||
b.Property<string>("Message");
|
||||
|
||||
b.Property<int>("ServerId");
|
||||
b.Property<long>("ServerId");
|
||||
|
||||
b.Property<DateTime>("TimeSent");
|
||||
|
||||
@ -183,7 +180,7 @@ namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<int>("ServerId");
|
||||
b.Property<long>("ServerId");
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
@ -230,7 +227,7 @@ namespace SharedLibraryCore.Migrations
|
||||
|
||||
b.Property<float>("MaxAngleDistance");
|
||||
|
||||
b.Property<int>("ServerId")
|
||||
b.Property<long>("ServerId")
|
||||
.HasColumnName("EFClientStatistics_ServerId");
|
||||
|
||||
b.HasKey("HitLocationCountId");
|
||||
@ -259,7 +256,7 @@ namespace SharedLibraryCore.Migrations
|
||||
|
||||
b.Property<int>("RatingHistoryId");
|
||||
|
||||
b.Property<int?>("ServerId");
|
||||
b.Property<long?>("ServerId");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
@ -280,10 +277,12 @@ namespace SharedLibraryCore.Migrations
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServer", b =>
|
||||
{
|
||||
b.Property<int>("ServerId");
|
||||
b.Property<long>("ServerId");
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<string>("EndPoint");
|
||||
|
||||
b.Property<int>("Port");
|
||||
|
||||
b.HasKey("ServerId");
|
||||
@ -298,7 +297,7 @@ namespace SharedLibraryCore.Migrations
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ServerId");
|
||||
b.Property<long>("ServerId");
|
||||
|
||||
b.Property<long>("TotalKills");
|
||||
|
||||
@ -320,7 +319,7 @@ namespace SharedLibraryCore.Migrations
|
||||
|
||||
b.Property<DateTime>("DateAdded");
|
||||
|
||||
b.Property<int>("IPAddress");
|
||||
b.Property<int?>("IPAddress");
|
||||
|
||||
b.Property<int>("LinkId");
|
||||
|
||||
@ -456,7 +455,7 @@ namespace SharedLibraryCore.Migrations
|
||||
|
||||
b.Property<string>("AutomatedOffense");
|
||||
|
||||
b.Property<DateTime>("Expires");
|
||||
b.Property<DateTime?>("Expires");
|
||||
|
||||
b.Property<int>("LinkId");
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
using System;
|
||||
using SharedLibraryCore.Objects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibraryCore.Objects
|
||||
namespace SharedLibraryCore.Database.Models
|
||||
{
|
||||
public class Player : Database.Models.EFClient
|
||||
public partial class EFClient
|
||||
{
|
||||
public enum ClientState
|
||||
{
|
||||
@ -17,11 +17,6 @@ namespace SharedLibraryCore.Objects
|
||||
/// </summary>
|
||||
Connecting,
|
||||
/// <summary>
|
||||
/// represents when the client has been parsed by RCon,
|
||||
/// but has not been validated against the database
|
||||
/// </summary>
|
||||
Authenticated,
|
||||
/// <summary>
|
||||
/// represents when the client has been authenticated by RCon
|
||||
/// and validated by the database
|
||||
/// </summary>
|
||||
@ -76,18 +71,21 @@ namespace SharedLibraryCore.Objects
|
||||
Console = 8
|
||||
}
|
||||
|
||||
public Player()
|
||||
public EFClient()
|
||||
{
|
||||
ConnectionTime = DateTime.UtcNow;
|
||||
ClientNumber = -1;
|
||||
DelayedEvents = new Queue<GameEvent>();
|
||||
_additionalProperties = new Dictionary<string, object>
|
||||
{
|
||||
{ "_reportCount", 0 }
|
||||
};
|
||||
CurrentAlias = new EFAlias();
|
||||
}
|
||||
|
||||
public override string ToString() => $"{Name}::{NetworkId}";
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Name}::{NetworkId}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// send a message directly to the connected client
|
||||
@ -113,7 +111,7 @@ namespace SharedLibraryCore.Objects
|
||||
/// </summary>
|
||||
/// <param name="warnReason">reason for warn</param>
|
||||
/// <param name="sender">client performing the warn</param>
|
||||
public GameEvent Warn(String warnReason, Player sender)
|
||||
public GameEvent Warn(String warnReason, EFClient sender)
|
||||
{
|
||||
var e = new GameEvent()
|
||||
{
|
||||
@ -146,7 +144,7 @@ namespace SharedLibraryCore.Objects
|
||||
/// </summary>
|
||||
/// <param name="sender">client performing the warn clear</param>
|
||||
/// <returns></returns>
|
||||
public GameEvent WarnClear(Player sender)
|
||||
public GameEvent WarnClear(EFClient sender)
|
||||
{
|
||||
var e = new GameEvent()
|
||||
{
|
||||
@ -175,7 +173,7 @@ namespace SharedLibraryCore.Objects
|
||||
/// <param name="reportReason">reason for the report</param>
|
||||
/// <param name="sender">client performing the report</param>
|
||||
/// <returns></returns>
|
||||
public GameEvent Report(string reportReason, Player sender)
|
||||
public GameEvent Report(string reportReason, EFClient sender)
|
||||
{
|
||||
var e = new GameEvent()
|
||||
{
|
||||
@ -221,7 +219,7 @@ namespace SharedLibraryCore.Objects
|
||||
/// <param name="flagReason">reason for flagging</param>
|
||||
/// <param name="sender">client performing the flag</param>
|
||||
/// <returns>game event for the flag</returns>
|
||||
public GameEvent Flag(string flagReason, Player sender)
|
||||
public GameEvent Flag(string flagReason, EFClient sender)
|
||||
{
|
||||
var e = new GameEvent()
|
||||
{
|
||||
@ -238,14 +236,14 @@ namespace SharedLibraryCore.Objects
|
||||
e.FailReason = GameEvent.EventFailReason.Permission;
|
||||
}
|
||||
|
||||
else if (this.Level == Player.Permission.Flagged)
|
||||
else if (this.Level == Permission.Flagged)
|
||||
{
|
||||
e.FailReason = GameEvent.EventFailReason.Invalid;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
this.Level = Player.Permission.Flagged;
|
||||
this.Level = Permission.Flagged;
|
||||
}
|
||||
|
||||
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
@ -258,7 +256,7 @@ namespace SharedLibraryCore.Objects
|
||||
/// <param name="unflagReason">reason to unflag a player for</param>
|
||||
/// <param name="sender">client performing the unflag</param>
|
||||
/// <returns>game event for the un flug</returns>
|
||||
public GameEvent Unflag(string unflagReason, Player sender)
|
||||
public GameEvent Unflag(string unflagReason, EFClient sender)
|
||||
{
|
||||
var e = new GameEvent()
|
||||
{
|
||||
@ -275,7 +273,7 @@ namespace SharedLibraryCore.Objects
|
||||
e.FailReason = GameEvent.EventFailReason.Permission;
|
||||
}
|
||||
|
||||
else if (this.Level != Player.Permission.Flagged)
|
||||
else if (this.Level != EFClient.Permission.Flagged)
|
||||
{
|
||||
e.FailReason = GameEvent.EventFailReason.Invalid;
|
||||
}
|
||||
@ -294,7 +292,7 @@ namespace SharedLibraryCore.Objects
|
||||
/// </summary>
|
||||
/// <param name="kickReason">reason to kick for</param>
|
||||
/// <param name="sender">client performing the kick</param>
|
||||
public GameEvent Kick(String kickReason, Player sender)
|
||||
public GameEvent Kick(String kickReason, EFClient sender)
|
||||
{
|
||||
var e = new GameEvent()
|
||||
{
|
||||
@ -322,7 +320,7 @@ namespace SharedLibraryCore.Objects
|
||||
/// <param name="tempbanReason">reason for the temp ban</param>
|
||||
/// <param name="banLength">how long the temp ban lasts</param>
|
||||
/// <param name="sender">client performing the tempban</param>
|
||||
public GameEvent TempBan(String tempbanReason, TimeSpan banLength, Player sender)
|
||||
public GameEvent TempBan(String tempbanReason, TimeSpan banLength, EFClient sender)
|
||||
{
|
||||
var e = new GameEvent()
|
||||
{
|
||||
@ -350,7 +348,7 @@ namespace SharedLibraryCore.Objects
|
||||
/// </summary>
|
||||
/// <param name="banReason">reason for the ban</param>
|
||||
/// <param name="sender">client performing the ban</param>
|
||||
public GameEvent Ban(String banReason, Player sender)
|
||||
public GameEvent Ban(String banReason, EFClient sender)
|
||||
{
|
||||
var e = new GameEvent()
|
||||
{
|
||||
@ -378,7 +376,7 @@ namespace SharedLibraryCore.Objects
|
||||
/// <param name="unbanReason">reason for the unban</param>
|
||||
/// <param name="sender">client performing the unban</param>
|
||||
/// <returns></returns>
|
||||
public GameEvent Unban(String unbanReason, Player sender)
|
||||
public GameEvent Unban(String unbanReason, EFClient sender)
|
||||
{
|
||||
var e = new GameEvent()
|
||||
{
|
||||
@ -400,9 +398,135 @@ namespace SharedLibraryCore.Objects
|
||||
return e;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles any client related logic on connection
|
||||
/// </summary>
|
||||
public void OnConnect()
|
||||
{
|
||||
var loc = Utilities.CurrentLocalization.LocalizationIndex;
|
||||
|
||||
if (Name.Length < 3)
|
||||
{
|
||||
CurrentServer.Logger.WriteDebug($"Kicking {this} because their name is too short");
|
||||
Kick(loc["SERVER_KICK_MINNAME"], Utilities.IW4MAdminClient(CurrentServer));
|
||||
return;
|
||||
}
|
||||
|
||||
if (Name == "Unknown Soldier" ||
|
||||
Name == "UnknownSoldier" ||
|
||||
Name == "CHEATER")
|
||||
{
|
||||
CurrentServer.Logger.WriteDebug($"Kicking {this} because their name is generic");
|
||||
Kick(loc["SERVER_KICK_GENERICNAME"], Utilities.IW4MAdminClient(CurrentServer));
|
||||
return;
|
||||
}
|
||||
|
||||
if (Name.Where(c => char.IsControl(c)).Count() > 0)
|
||||
{
|
||||
CurrentServer.Logger.WriteDebug($"Kicking {this} because their name contains control characters");
|
||||
Kick(loc["SERVER_KICK_CONTROLCHARS"], Utilities.IW4MAdminClient(CurrentServer));
|
||||
return;
|
||||
}
|
||||
|
||||
// reserved slots stuff
|
||||
if (CurrentServer.MaxClients - (CurrentServer.GetClientsAsList().Count(_client => !_client.IsPrivileged())) < CurrentServer.ServerConfig.ReservedSlotNumber &&
|
||||
!this.IsPrivileged())
|
||||
{
|
||||
CurrentServer.Logger.WriteDebug($"Kicking {this} their spot is reserved");
|
||||
Kick(loc["SERVER_KICK_SLOT_IS_RESERVED"], Utilities.IW4MAdminClient(CurrentServer));
|
||||
return;
|
||||
}
|
||||
|
||||
LastConnection = DateTime.UtcNow;
|
||||
Connections += 1;
|
||||
}
|
||||
|
||||
public async Task OnDisconnect()
|
||||
{
|
||||
State = ClientState.Disconnecting;
|
||||
TotalConnectionTime += ConnectionLength;
|
||||
LastConnection = DateTime.UtcNow;
|
||||
await CurrentServer.Manager.GetClientService().Update(this);
|
||||
}
|
||||
|
||||
public async Task OnJoin(int? ipAddress)
|
||||
{
|
||||
IPAddress = ipAddress;
|
||||
|
||||
await CurrentServer.Manager.GetClientService().UpdateAlias(this);
|
||||
|
||||
var loc = Utilities.CurrentLocalization.LocalizationIndex;
|
||||
var activePenalties = await CurrentServer.Manager.GetPenaltyService().GetActivePenaltiesAsync(AliasLinkId, ipAddress);
|
||||
var currentBan = activePenalties.FirstOrDefault(p => p.Type == Penalty.PenaltyType.Ban || p.Type == Penalty.PenaltyType.TempBan);
|
||||
|
||||
var currentAutoFlag = activePenalties.Where(p => p.Type == Penalty.PenaltyType.Flag && p.PunisherId == 1)
|
||||
.Where(p => p.Active)
|
||||
.OrderByDescending(p => p.When)
|
||||
.FirstOrDefault();
|
||||
|
||||
// remove their auto flag status after a week
|
||||
if (Level == Permission.Flagged &&
|
||||
currentAutoFlag != null &&
|
||||
(DateTime.UtcNow - currentAutoFlag.When).TotalDays > 7)
|
||||
{
|
||||
Level = Permission.User;
|
||||
}
|
||||
|
||||
if (currentBan != null)
|
||||
{
|
||||
CurrentServer.Logger.WriteInfo($"Banned client {this} trying to join...");
|
||||
var autoKickClient = Utilities.IW4MAdminClient(CurrentServer);
|
||||
|
||||
// reban the "evading" guid
|
||||
if (Level != Permission.Banned &&
|
||||
currentBan.Type == Penalty.PenaltyType.Ban)
|
||||
{
|
||||
CurrentServer.Logger.WriteInfo($"Banned client {this} connected using a new GUID");
|
||||
// hack: re apply the automated offense to the reban
|
||||
if (currentBan.AutomatedOffense != null)
|
||||
{
|
||||
autoKickClient.AdministeredPenalties?.Add(new EFPenalty()
|
||||
{
|
||||
AutomatedOffense = currentBan.AutomatedOffense
|
||||
});
|
||||
}
|
||||
Ban($"{currentBan.Offense}", autoKickClient);
|
||||
}
|
||||
|
||||
// the player is permanently banned
|
||||
else if (currentBan.Type == Penalty.PenaltyType.Ban)
|
||||
{
|
||||
Kick($"{loc["SERVER_BAN_PREV"]} {currentBan.Offense} ({loc["SERVER_BAN_APPEAL"]} {CurrentServer.Website})", autoKickClient);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
Kick($"{loc["SERVER_TB_REMAIN"]} ({(currentBan.Expires.Value - DateTime.UtcNow).TimeSpanText()} {loc["WEBFRONT_PENALTY_TEMPLATE_REMAINING"]})", autoKickClient);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Join,
|
||||
Origin = this,
|
||||
Target = this,
|
||||
Owner = CurrentServer
|
||||
};
|
||||
|
||||
CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
[NotMapped]
|
||||
Dictionary<string, object> _additionalProperties;
|
||||
public T GetAdditionalProperty<T>(string name) => (T)_additionalProperties[name];
|
||||
|
||||
public T GetAdditionalProperty<T>(string name)
|
||||
{
|
||||
return _additionalProperties.ContainsKey(name) ? (T)_additionalProperties[name] : default(T);
|
||||
}
|
||||
|
||||
public void SetAdditionalProperty(string name, object value)
|
||||
{
|
||||
if (_additionalProperties.ContainsKey(name))
|
||||
@ -431,22 +555,10 @@ namespace SharedLibraryCore.Objects
|
||||
public int Score { get; set; }
|
||||
[NotMapped]
|
||||
public bool IsBot { get; set; }
|
||||
private int _ipaddress;
|
||||
public override int IPAddress
|
||||
{
|
||||
get { return _ipaddress; }
|
||||
set { _ipaddress = value; }
|
||||
}
|
||||
private string _name;
|
||||
public override string Name
|
||||
{
|
||||
get { return _name; }
|
||||
set { _name = value; }
|
||||
}
|
||||
|
||||
[NotMapped]
|
||||
public ClientState State { get; set; }
|
||||
[NotMapped]
|
||||
public Queue<GameEvent> DelayedEvents { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
// this is kinda dirty, but I need localizable level names
|
||||
public ClientPermission ClientPermission => new ClientPermission()
|
||||
@ -458,9 +570,12 @@ namespace SharedLibraryCore.Objects
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return ((Player)obj).NetworkId == this.NetworkId;
|
||||
return ((EFClient)obj).NetworkId == this.NetworkId;
|
||||
}
|
||||
|
||||
public override int GetHashCode() => (int)NetworkId;
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (int)NetworkId;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using static SharedLibraryCore.Objects.Player;
|
||||
using static SharedLibraryCore.Database.Models.EFClient;
|
||||
|
||||
namespace SharedLibraryCore.Objects
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@ -8,8 +9,8 @@ namespace SharedLibraryCore.Objects
|
||||
{
|
||||
public class Report
|
||||
{
|
||||
public Player Target { get; set; }
|
||||
public Player Origin { get; set; }
|
||||
public String Reason { get; set; }
|
||||
public EFClient Target { get; set; }
|
||||
public EFClient Origin { get; set; }
|
||||
public string Reason { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -29,7 +28,8 @@ namespace SharedLibraryCore.RCon
|
||||
static readonly ConcurrentDictionary<EndPoint, ConnectionState> ActiveQueries = new ConcurrentDictionary<EndPoint, ConnectionState>();
|
||||
public IPEndPoint Endpoint { get; private set; }
|
||||
public string RConPassword { get; private set; }
|
||||
ILogger Log;
|
||||
|
||||
private readonly ILogger Log;
|
||||
|
||||
public Connection(string ipAddress, int port, string password, ILogger log)
|
||||
{
|
||||
@ -88,42 +88,50 @@ namespace SharedLibraryCore.RCon
|
||||
|
||||
byte[] response = null;
|
||||
|
||||
retrySend:
|
||||
connectionState.SendEventArgs.UserToken = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
|
||||
retrySend:
|
||||
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
|
||||
{
|
||||
DontFragment = true,
|
||||
Ttl = 42,
|
||||
//DontFragment = true,
|
||||
Ttl = 100,
|
||||
ExclusiveAddressUse = true,
|
||||
};
|
||||
|
||||
connectionState.OnSentData.Reset();
|
||||
connectionState.OnReceivedData.Reset();
|
||||
connectionState.ConnectionAttempts++;
|
||||
})
|
||||
{
|
||||
connectionState.SendEventArgs.UserToken = socket;
|
||||
connectionState.OnSentData.Reset();
|
||||
connectionState.OnReceivedData.Reset();
|
||||
connectionState.ConnectionAttempts++;
|
||||
#if DEBUG == true
|
||||
Log.WriteDebug($"Sending {payload.Length} bytes to [{this.Endpoint}] ({connectionState.ConnectionAttempts}/{StaticHelpers.AllowedConnectionFails})");
|
||||
Log.WriteDebug($"Sending {payload.Length} bytes to [{this.Endpoint}] ({connectionState.ConnectionAttempts}/{StaticHelpers.AllowedConnectionFails})");
|
||||
#endif
|
||||
try
|
||||
{
|
||||
response = await SendPayloadAsync(payload, waitForResponse);
|
||||
connectionState.OnComplete.Release(1);
|
||||
connectionState.ConnectionAttempts = 0;
|
||||
}
|
||||
|
||||
catch/* (Exception ex)*/
|
||||
{
|
||||
if (connectionState.ConnectionAttempts < StaticHelpers.AllowedConnectionFails)
|
||||
try
|
||||
{
|
||||
// Log.WriteWarning($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}] ({connectionState.ConnectionAttempts}/{StaticHelpers.AllowedConnectionFails})");
|
||||
await Task.Delay(StaticHelpers.FloodProtectionInterval);
|
||||
goto retrySend;
|
||||
response = await SendPayloadAsync(payload, waitForResponse);
|
||||
|
||||
if (response.Length == 0 && waitForResponse)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
connectionState.OnComplete.Release(1);
|
||||
connectionState.ConnectionAttempts = 0;
|
||||
}
|
||||
|
||||
connectionState.OnComplete.Release(1);
|
||||
//Log.WriteDebug(ex.GetExceptionInfo());
|
||||
throw new NetworkException($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}]");
|
||||
catch
|
||||
{
|
||||
if (connectionState.ConnectionAttempts < StaticHelpers.AllowedConnectionFails)
|
||||
{
|
||||
// Log.WriteWarning($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}] ({connectionState.ConnectionAttempts}/{StaticHelpers.AllowedConnectionFails})");
|
||||
await Task.Delay(StaticHelpers.FloodProtectionInterval);
|
||||
goto retrySend;
|
||||
}
|
||||
|
||||
connectionState.OnComplete.Release(1);
|
||||
//Log.WriteDebug(ex.GetExceptionInfo());
|
||||
throw new NetworkException($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}]");
|
||||
}
|
||||
}
|
||||
|
||||
string responseString = Utilities.EncodingType.GetString(response, 0, response.Length).TrimEnd('\0') + '\n';
|
||||
string responseString = Utilities.EncodingType.GetString(response, 0, response.Length) + '\n';
|
||||
|
||||
if (responseString.Contains("Invalid password"))
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@ -71,7 +72,7 @@ namespace SharedLibraryCore
|
||||
cfg.AllowClr(new[]
|
||||
{
|
||||
typeof(System.Net.Http.HttpClient).Assembly,
|
||||
typeof(Objects.Player).Assembly,
|
||||
typeof(EFClient).Assembly,
|
||||
})
|
||||
.CatchClrExceptions());
|
||||
|
||||
|
@ -9,6 +9,7 @@ using SharedLibraryCore.Objects;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
|
||||
namespace SharedLibraryCore
|
||||
{
|
||||
@ -32,14 +33,14 @@ namespace SharedLibraryCore
|
||||
IP = config.IPAddress;
|
||||
Port = config.Port;
|
||||
Manager = mgr;
|
||||
Logger = Manager.GetLogger(this.GetHashCode());
|
||||
Logger = Manager.GetLogger(this.EndPoint);
|
||||
Logger.WriteInfo(this.ToString());
|
||||
ServerConfig = config;
|
||||
RemoteConnection = new RCon.Connection(IP, Port, Password, Logger);
|
||||
|
||||
Players = new List<Player>(new Player[18]);
|
||||
Clients = new List<EFClient>(new EFClient[18]);
|
||||
Reports = new List<Report>();
|
||||
PlayerHistory = new Queue<PlayerHistory>();
|
||||
ClientHistory = new Queue<PlayerHistory>();
|
||||
ChatHistory = new List<ChatInfo>();
|
||||
NextMessage = 0;
|
||||
CustomSayEnabled = Manager.GetApplicationSettings().Configuration().EnableCustomSayName;
|
||||
@ -48,6 +49,8 @@ namespace SharedLibraryCore
|
||||
InitializeAutoMessages();
|
||||
}
|
||||
|
||||
public long EndPoint => Convert.ToInt64($"{IP.Replace(".", "")}{Port}");
|
||||
|
||||
//Returns current server IP set by `net_ip` -- *STRING*
|
||||
public String GetIP()
|
||||
{
|
||||
@ -61,32 +64,32 @@ namespace SharedLibraryCore
|
||||
}
|
||||
|
||||
//Returns list of all current players
|
||||
public List<Player> GetPlayersAsList()
|
||||
public List<EFClient> GetClientsAsList()
|
||||
{
|
||||
return Players.FindAll(x => x != null);
|
||||
return Clients.FindAll(x => x != null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a player to the server's player list
|
||||
/// </summary>
|
||||
/// <param name="P">Player pulled from memory reading</param>
|
||||
/// <param name="P">EFClient pulled from memory reading</param>
|
||||
/// <returns>True if player added sucessfully, false otherwise</returns>
|
||||
abstract public Task<bool> AddPlayer(Player P);
|
||||
abstract public Task OnClientConnected(EFClient P);
|
||||
|
||||
/// <summary>
|
||||
/// Remove player by client number
|
||||
/// </summary>
|
||||
/// <param name="cNum">Client ID of player to be removed</param>
|
||||
/// <returns>true if removal succeded, false otherwise</returns>
|
||||
abstract public Task RemovePlayer(int cNum);
|
||||
abstract public Task OnClientDisconnected(EFClient client);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get a player by name
|
||||
/// </summary>
|
||||
/// <param name="pName">Player name to search for</param>
|
||||
/// <param name="pName">EFClient name to search for</param>
|
||||
/// <returns>Matching player if found</returns>
|
||||
public List<Player> GetClientByName(String pName)
|
||||
public List<EFClient> GetClientByName(String pName)
|
||||
{
|
||||
string[] QuoteSplit = pName.Split('"');
|
||||
bool literal = false;
|
||||
@ -96,9 +99,9 @@ namespace SharedLibraryCore
|
||||
literal = true;
|
||||
}
|
||||
if (literal)
|
||||
return Players.Where(p => p != null && p.Name.ToLower().Equals(pName.ToLower())).ToList();
|
||||
return Clients.Where(p => p != null && p.Name.ToLower().Equals(pName.ToLower())).ToList();
|
||||
|
||||
return Players.Where(p => p != null && p.Name.ToLower().Contains(pName.ToLower())).ToList();
|
||||
return Clients.Where(p => p != null && p.Name.ToLower().Contains(pName.ToLower())).ToList();
|
||||
}
|
||||
|
||||
virtual public Task<bool> ProcessUpdatesAsync(CancellationToken cts) => (Task<bool>)Task.CompletedTask;
|
||||
@ -115,21 +118,18 @@ namespace SharedLibraryCore
|
||||
/// Send a message to all players
|
||||
/// </summary>
|
||||
/// <param name="message">Message to be sent to all players</param>
|
||||
public GameEvent Broadcast(string message, Player sender = null)
|
||||
public GameEvent Broadcast(string message, EFClient sender = null)
|
||||
{
|
||||
#if DEBUG == false
|
||||
string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Say, $"{(CustomSayEnabled ? $"{CustomSayName}: " : "")}{message}");
|
||||
#else
|
||||
|
||||
#if DEBUG == true
|
||||
Logger.WriteVerbose(message.StripColors());
|
||||
#endif
|
||||
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Broadcast,
|
||||
#if DEBUG == true
|
||||
Data = message,
|
||||
#else
|
||||
Data = formattedMessage,
|
||||
#endif
|
||||
Owner = this,
|
||||
Origin = sender,
|
||||
};
|
||||
@ -142,19 +142,19 @@ namespace SharedLibraryCore
|
||||
/// Send a message to a particular players
|
||||
/// </summary>
|
||||
/// <param name="Message">Message to send</param>
|
||||
/// <param name="Target">Player to send message to</param>
|
||||
protected async Task Tell(String Message, Player Target)
|
||||
/// <param name="Target">EFClient to send message to</param>
|
||||
protected async Task Tell(String Message, EFClient Target)
|
||||
{
|
||||
#if !DEBUG
|
||||
string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Tell, Target.ClientNumber, $"{(CustomSayEnabled ? $"{CustomSayName}: " : "")}{Message}");
|
||||
if (Target.ClientNumber > -1 && Message.Length > 0 && Target.Level != Player.Permission.Console)
|
||||
if (Target.ClientNumber > -1 && Message.Length > 0 && Target.Level != EFClient.Permission.Console)
|
||||
await this.ExecuteCommandAsync(formattedMessage);
|
||||
#else
|
||||
Logger.WriteVerbose($"{Target.ClientNumber}->{Message.StripColors()}");
|
||||
await Task.CompletedTask;
|
||||
#endif
|
||||
|
||||
if (Target.Level == Player.Permission.Console)
|
||||
if (Target.Level == EFClient.Permission.Console)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Cyan;
|
||||
Console.WriteLine(Message.StripColors());
|
||||
@ -182,7 +182,7 @@ namespace SharedLibraryCore
|
||||
/// <param name="message">Message to send out</param>
|
||||
public void ToAdmins(String message)
|
||||
{
|
||||
foreach (var client in GetPlayersAsList().Where(c => c.Level > Player.Permission.Flagged))
|
||||
foreach (var client in GetClientsAsList().Where(c => c.Level > EFClient.Permission.Flagged))
|
||||
{
|
||||
client.Tell(message);
|
||||
}
|
||||
@ -192,15 +192,15 @@ namespace SharedLibraryCore
|
||||
/// Kick a player from the server
|
||||
/// </summary>
|
||||
/// <param name="Reason">Reason for kicking</param>
|
||||
/// <param name="Target">Player to kick</param>
|
||||
abstract protected Task Kick(String Reason, Player Target, Player Origin);
|
||||
/// <param name="Target">EFClient to kick</param>
|
||||
abstract protected Task Kick(String Reason, EFClient Target, EFClient Origin);
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily ban a player ( default 1 hour ) from the server
|
||||
/// </summary>
|
||||
/// <param name="Reason">Reason for banning the player</param>
|
||||
/// <param name="Target">The player to ban</param>
|
||||
abstract protected Task TempBan(String Reason, TimeSpan length, Player Target, Player Origin);
|
||||
abstract protected Task TempBan(String Reason, TimeSpan length, EFClient Target, EFClient Origin);
|
||||
|
||||
/// <summary>
|
||||
/// Perm ban a player from the server
|
||||
@ -208,9 +208,9 @@ namespace SharedLibraryCore
|
||||
/// <param name="Reason">The reason for the ban</param>
|
||||
/// <param name="Target">The person to ban</param>
|
||||
/// <param name="Origin">The person who banned the target</param>
|
||||
abstract protected Task Ban(String Reason, Player Target, Player Origin);
|
||||
abstract protected Task Ban(String Reason, EFClient Target, EFClient Origin);
|
||||
|
||||
abstract protected Task Warn(String Reason, Player Target, Player Origin);
|
||||
abstract protected Task Warn(String Reason, EFClient Target, EFClient Origin);
|
||||
|
||||
/// <summary>
|
||||
/// Unban a player by npID / GUID
|
||||
@ -218,7 +218,7 @@ namespace SharedLibraryCore
|
||||
/// <param name="npID">npID of the player</param>
|
||||
/// <param name="Target">I don't remember what this is for</param>
|
||||
/// <returns></returns>
|
||||
abstract public Task Unban(string reason, Player Target, Player Origin);
|
||||
abstract public Task Unban(string reason, EFClient Target, EFClient Origin);
|
||||
|
||||
/// <summary>
|
||||
/// Change the current searver map
|
||||
@ -287,7 +287,7 @@ namespace SharedLibraryCore
|
||||
public List<Map> Maps { get; protected set; }
|
||||
public List<Report> Reports { get; set; }
|
||||
public List<ChatInfo> ChatHistory { get; protected set; }
|
||||
public Queue<PlayerHistory> PlayerHistory { get; private set; }
|
||||
public Queue<PlayerHistory> ClientHistory { get; private set; }
|
||||
public Game GameName { get; protected set; }
|
||||
|
||||
// Info
|
||||
@ -299,11 +299,11 @@ namespace SharedLibraryCore
|
||||
{
|
||||
get
|
||||
{
|
||||
return Players.Where(p => p != null).Count();
|
||||
return Clients.Where(p => p != null).Count();
|
||||
}
|
||||
}
|
||||
public int MaxClients { get; protected set; }
|
||||
public List<Player> Players { get; protected set; }
|
||||
public List<EFClient> Clients { get; protected set; }
|
||||
public string Password { get; private set; }
|
||||
public bool Throttled { get; protected set; }
|
||||
public bool CustomCallback { get; protected set; }
|
||||
@ -312,9 +312,10 @@ namespace SharedLibraryCore
|
||||
public IRConParser RconParser { get; protected set; }
|
||||
public IEventParser EventParser { get; set; }
|
||||
public string LogPath { get; protected set; }
|
||||
public bool RestartRequested { get; set; }
|
||||
|
||||
// Internal
|
||||
protected string IP;
|
||||
public string IP { get; protected set; }
|
||||
protected int Port;
|
||||
protected string FSGame;
|
||||
protected int NextMessage;
|
||||
|
@ -1,15 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore.Database;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using System.Linq.Expressions;
|
||||
using SharedLibraryCore.Objects;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using static SharedLibraryCore.Database.Models.EFClient;
|
||||
|
||||
namespace SharedLibraryCore.Services
|
||||
{
|
||||
@ -20,55 +17,28 @@ namespace SharedLibraryCore.Services
|
||||
{
|
||||
using (var context = new DatabaseContext())
|
||||
{
|
||||
bool hasExistingAlias = false;
|
||||
// get all aliases by IP
|
||||
var aliases = await context.Aliases
|
||||
.Include(a => a.Link)
|
||||
.Where(a => a.IPAddress == entity.IPAddress)
|
||||
.ToListAsync();
|
||||
|
||||
// see if they have a matching IP + Name but new NetworkId
|
||||
var existingAlias = aliases.FirstOrDefault(a => a.Name == entity.Name);
|
||||
// if existing alias matches link them
|
||||
EFAliasLink aliasLink = existingAlias?.Link;
|
||||
// if no exact matches find the first IP that matches
|
||||
aliasLink = aliasLink ?? aliases.FirstOrDefault()?.Link;
|
||||
// if no exact or IP matches, create new link
|
||||
aliasLink = aliasLink ?? new EFAliasLink()
|
||||
{
|
||||
Active = true,
|
||||
};
|
||||
|
||||
// this has to be set here because we can't evalute it properly later
|
||||
hasExistingAlias = existingAlias != null;
|
||||
|
||||
// if no existing alias create new alias
|
||||
existingAlias = existingAlias ?? new EFAlias()
|
||||
{
|
||||
Active = true,
|
||||
DateAdded = DateTime.UtcNow,
|
||||
IPAddress = entity.IPAddress,
|
||||
Link = aliasLink,
|
||||
Name = entity.Name,
|
||||
};
|
||||
|
||||
var client = new EFClient()
|
||||
{
|
||||
Active = true,
|
||||
// set the level to the level of the existing client if they have the same IP + Name but new NetworkId
|
||||
// fixme: issues?
|
||||
Level = hasExistingAlias ?
|
||||
(await context.Clients.Where(c => c.AliasLinkId == existingAlias.LinkId)
|
||||
.OrderByDescending(c => c.Level)
|
||||
.FirstOrDefaultAsync())?.Level ?? Player.Permission.User :
|
||||
Player.Permission.User,
|
||||
Level = Permission.User,
|
||||
FirstConnection = DateTime.UtcNow,
|
||||
Connections = 1,
|
||||
LastConnection = DateTime.UtcNow,
|
||||
Masked = false,
|
||||
NetworkId = entity.NetworkId,
|
||||
AliasLink = aliasLink,
|
||||
CurrentAlias = existingAlias,
|
||||
AliasLink = new EFAliasLink()
|
||||
{
|
||||
Active = false
|
||||
},
|
||||
};
|
||||
|
||||
client.CurrentAlias = new Alias()
|
||||
{
|
||||
Name = entity.Name,
|
||||
Link = client.AliasLink,
|
||||
DateAdded = DateTime.UtcNow,
|
||||
// the first time a client is created, we may not have their ip,
|
||||
// so we create a temporary alias
|
||||
Active = false
|
||||
};
|
||||
|
||||
context.Clients.Add(client);
|
||||
@ -78,6 +48,131 @@ namespace SharedLibraryCore.Services
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateAlias(EFClient entity)
|
||||
{
|
||||
// todo: move this out
|
||||
if (entity.IsBot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (var context = new DatabaseContext())
|
||||
{
|
||||
context.Attach(entity);
|
||||
|
||||
string name = entity.Name;
|
||||
int? ip = entity.IPAddress;
|
||||
|
||||
bool hasExistingAlias = false;
|
||||
|
||||
// get all aliases by IP
|
||||
var iqAliases = context.Aliases
|
||||
.Include(a => a.Link)
|
||||
.Where(a => a.Link.Active)
|
||||
.Where(a => (a.IPAddress != null && a.IPAddress == ip) ||
|
||||
a.LinkId == entity.AliasLinkId);
|
||||
|
||||
#if DEBUG == true
|
||||
var aliasSql = iqAliases.ToSql();
|
||||
#endif
|
||||
var aliases = await iqAliases.ToListAsync();
|
||||
|
||||
// see if they have a matching IP + Name but new NetworkId
|
||||
var existingAlias = aliases.FirstOrDefault(a => a.Name == name);
|
||||
// if existing alias matches link them
|
||||
EFAliasLink aliasLink = existingAlias?.Link;
|
||||
// if no exact matches find the first IP that matches
|
||||
aliasLink = aliasLink ?? aliases.FirstOrDefault()?.Link;
|
||||
// if no exact or IP matches, create new link
|
||||
aliasLink = aliasLink ?? new EFAliasLink();
|
||||
|
||||
// this has to be set here because we can't evalute it properly later
|
||||
hasExistingAlias = existingAlias != null;
|
||||
|
||||
if (hasExistingAlias && !entity.AliasLink.Active)
|
||||
{
|
||||
entity.CurrentServer.Logger.WriteDebug($"Removing temporary alias for ${entity}");
|
||||
|
||||
// we want to delete the temporary alias
|
||||
context.Entry(entity.CurrentAlias).State = EntityState.Deleted;
|
||||
entity.CurrentAlias = null;
|
||||
|
||||
// we want to delete the temporary alias link
|
||||
context.Entry(entity.AliasLink).State = EntityState.Deleted;
|
||||
entity.AliasLink = null;
|
||||
|
||||
// they have an existing alias so assign it
|
||||
entity.CurrentAlias = existingAlias;
|
||||
entity.AliasLink = aliasLink;
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// update the temporary alias to permanent one
|
||||
else if (!entity.AliasLink.Active)
|
||||
{
|
||||
entity.CurrentServer.Logger.WriteDebug($"Linking permanent alias for ${entity}");
|
||||
|
||||
// we want to track the current alias and link
|
||||
var alias = context.Update(entity.CurrentAlias).Entity;
|
||||
var _aliasLink = context.Update(entity.AliasLink).Entity;
|
||||
|
||||
alias.Active = true;
|
||||
alias.IPAddress = ip;
|
||||
alias.Name = name;
|
||||
_aliasLink.Active = true;
|
||||
|
||||
existingAlias = alias;
|
||||
aliasLink = _aliasLink;
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// if no existing alias create new alias
|
||||
existingAlias = existingAlias ?? new EFAlias()
|
||||
{
|
||||
DateAdded = DateTime.UtcNow,
|
||||
IPAddress = ip,
|
||||
Link = aliasLink,
|
||||
Name = name,
|
||||
};
|
||||
|
||||
if (!hasExistingAlias)
|
||||
{
|
||||
entity.CurrentServer.Logger.WriteDebug($"Connecting player does not have an existing alias {entity}");
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
var linkIds = aliases.Select(a => a.LinkId);
|
||||
|
||||
if (linkIds.Count() > 0)
|
||||
{
|
||||
var highestLevel = await context.Clients
|
||||
.Where(c => linkIds.Contains(c.AliasLinkId))
|
||||
.MaxAsync(c => c.Level);
|
||||
|
||||
if (entity.Level != highestLevel)
|
||||
{
|
||||
context.Update(entity);
|
||||
entity.Level = highestLevel;
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (entity.CurrentAlias != existingAlias ||
|
||||
entity.AliasLink != aliasLink)
|
||||
{
|
||||
entity.CurrentAlias = existingAlias;
|
||||
entity.AliasLink = aliasLink;
|
||||
|
||||
context.Update(entity);
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<EFClient> Delete(EFClient entity)
|
||||
{
|
||||
using (var context = new DatabaseContext())
|
||||
@ -131,13 +226,17 @@ namespace SharedLibraryCore.Services
|
||||
var foundClient = await iqClient.FirstOrDefaultAsync();
|
||||
|
||||
if (foundClient == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foundClient.Client.LinkedAccounts = new Dictionary<int, long>();
|
||||
// todo: find out the best way to do this
|
||||
// I'm doing this here because I don't know the best way to have multiple awaits in the query
|
||||
foreach (var linked in foundClient.LinkedAccounts)
|
||||
{
|
||||
foundClient.Client.LinkedAccounts.Add(linked.ClientId, linked.NetworkId);
|
||||
}
|
||||
|
||||
return foundClient.Client;
|
||||
}
|
||||
@ -167,7 +266,7 @@ namespace SharedLibraryCore.Services
|
||||
var client = context.Clients
|
||||
.Include(c => c.AliasLink)
|
||||
.Include(c => c.CurrentAlias)
|
||||
.Single(e => e.ClientId == entity.ClientId);
|
||||
.First(e => e.ClientId == entity.ClientId);
|
||||
|
||||
// if their level has been changed
|
||||
if (entity.Level != client.Level)
|
||||
@ -190,7 +289,7 @@ namespace SharedLibraryCore.Services
|
||||
{
|
||||
client.CurrentAlias = new EFAlias()
|
||||
{
|
||||
Active = true,
|
||||
Active = entity.CurrentAlias.IPAddress.HasValue ? true : false,
|
||||
DateAdded = DateTime.UtcNow,
|
||||
IPAddress = entity.CurrentAlias.IPAddress,
|
||||
Name = entity.CurrentAlias.Name,
|
||||
@ -201,6 +300,8 @@ namespace SharedLibraryCore.Services
|
||||
else
|
||||
{
|
||||
client.CurrentAliasId = entity.CurrentAliasId;
|
||||
client.IPAddress = entity.IPAddress;
|
||||
client.Name = entity.Name;
|
||||
}
|
||||
|
||||
// set remaining non-navigation properties that may have been updated
|
||||
@ -218,7 +319,10 @@ namespace SharedLibraryCore.Services
|
||||
|
||||
// this is set so future updates don't trigger a new alias add
|
||||
if (entity.CurrentAlias.AliasId == 0)
|
||||
{
|
||||
entity.CurrentAlias.AliasId = client.CurrentAlias.AliasId;
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
}
|
||||
@ -227,24 +331,28 @@ namespace SharedLibraryCore.Services
|
||||
public async Task<IList<EFClient>> GetOwners()
|
||||
{
|
||||
using (var context = new DatabaseContext())
|
||||
{
|
||||
return await context.Clients
|
||||
.Where(c => c.Level == Player.Permission.Owner)
|
||||
.Where(c => c.Level == Permission.Owner)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IList<ClientInfo>> GetPrivilegedClients()
|
||||
public async Task<List<EFClient>> GetPrivilegedClients()
|
||||
{
|
||||
using (var context = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
var iqClients = from client in context.Clients
|
||||
where client.Level >= Player.Permission.Trusted
|
||||
where client.Level >= Permission.Trusted
|
||||
where client.Active
|
||||
select new ClientInfo()
|
||||
select new EFClient()
|
||||
{
|
||||
AliasLinkId = client.AliasLinkId,
|
||||
CurrentAlias = client.CurrentAlias,
|
||||
ClientId = client.ClientId,
|
||||
Name = client.CurrentAlias.Name,
|
||||
LinkId = client.AliasLinkId,
|
||||
Level = client.Level
|
||||
Level = client.Level,
|
||||
Password = client.Password,
|
||||
PasswordSalt = client.PasswordSalt
|
||||
};
|
||||
|
||||
#if DEBUG == true
|
||||
@ -255,27 +363,30 @@ namespace SharedLibraryCore.Services
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IList<EFClient>> GetClientByName(string name)
|
||||
public async Task<IList<EFClient>> FindClientsByIdentifier(string identifier)
|
||||
{
|
||||
if (name.Length < 3)
|
||||
if (identifier.Length < 3)
|
||||
{
|
||||
return new List<EFClient>();
|
||||
}
|
||||
|
||||
name = name.ToLower();
|
||||
identifier = identifier.ToLower();
|
||||
|
||||
using (var context = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
int asIP = name.ConvertToIP();
|
||||
// hack: so IW4MAdmin and bots don't show up in search results
|
||||
asIP = asIP == 0 ? int.MaxValue : asIP;
|
||||
long networkId = identifier.ConvertLong();
|
||||
int ipAddress = identifier.ConvertToIP();
|
||||
|
||||
var iqLinkIds = (from alias in context.Aliases
|
||||
where asIP != int.MaxValue ? alias.IPAddress == asIP : alias.Name.ToLower().Contains(name)
|
||||
select alias.LinkId);
|
||||
where alias.IPAddress == ipAddress ||
|
||||
alias.Name.ToLower().Contains(identifier)
|
||||
select alias.LinkId).Distinct();
|
||||
|
||||
var linkIds = iqLinkIds.ToList();
|
||||
|
||||
var iqClients = context.Clients
|
||||
.Where(c => linkIds.Contains(c.AliasLinkId))
|
||||
.Where(c => linkIds.Contains(c.AliasLinkId) ||
|
||||
networkId == c.NetworkId)
|
||||
.Include(c => c.CurrentAlias)
|
||||
.Include(c => c.AliasLink.Children);
|
||||
|
||||
@ -290,20 +401,16 @@ namespace SharedLibraryCore.Services
|
||||
public async Task<int> GetTotalClientsAsync()
|
||||
{
|
||||
using (var context = new DatabaseContext(true))
|
||||
{
|
||||
return await context.Clients
|
||||
.CountAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public Task<EFClient> CreateProxy()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<int> GetTotalPlayTime()
|
||||
{
|
||||
using (var context = new DatabaseContext(true))
|
||||
return await context.Clients.SumAsync(c => c.TotalConnectionTime);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore.Database;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore.Objects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using static SharedLibraryCore.Database.Models.EFClient;
|
||||
|
||||
namespace SharedLibraryCore.Services
|
||||
{
|
||||
@ -30,15 +31,12 @@ namespace SharedLibraryCore.Services
|
||||
AutomatedOffense = newEntity.AutomatedOffense
|
||||
};
|
||||
|
||||
if (addedEntity.Expires == DateTime.MaxValue)
|
||||
addedEntity.Expires = DateTime.Parse(System.Data.SqlTypes.SqlDateTime.MaxValue.ToString());
|
||||
|
||||
// make bans propogate to all aliases
|
||||
if (addedEntity.Type == Objects.Penalty.PenaltyType.Ban)
|
||||
{
|
||||
await context.Clients
|
||||
.Where(c => c.AliasLinkId == addedEntity.LinkId)
|
||||
.ForEachAsync(c => c.Level = Objects.Player.Permission.Banned);
|
||||
.ForEachAsync(c => c.Level = Permission.Banned);
|
||||
}
|
||||
|
||||
// make flags propogate to all aliases
|
||||
@ -46,7 +44,7 @@ namespace SharedLibraryCore.Services
|
||||
{
|
||||
await context.Clients
|
||||
.Where(c => c.AliasLinkId == addedEntity.LinkId)
|
||||
.ForEachAsync(c => c.Level = Objects.Player.Permission.Flagged);
|
||||
.ForEachAsync(c => c.Level = Permission.Flagged);
|
||||
}
|
||||
|
||||
context.Penalties.Add(addedEntity);
|
||||
@ -89,6 +87,7 @@ namespace SharedLibraryCore.Services
|
||||
public async Task<IList<EFPenalty>> GetRecentPenalties(int count, int offset, Penalty.PenaltyType showOnly = Penalty.PenaltyType.Any)
|
||||
{
|
||||
using (var context = new DatabaseContext(true))
|
||||
{
|
||||
return await context.Penalties
|
||||
.Include(p => p.Offender.CurrentAlias)
|
||||
.Include(p => p.Punisher.CurrentAlias)
|
||||
@ -98,17 +97,20 @@ namespace SharedLibraryCore.Services
|
||||
.Skip(offset)
|
||||
.Take(count)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IList<EFPenalty>> GetClientPenaltiesAsync(int clientId)
|
||||
{
|
||||
using (var context = new DatabaseContext(true))
|
||||
{
|
||||
return await context.Penalties
|
||||
.Where(p => p.OffenderId == clientId)
|
||||
.Where(p => p.Active)
|
||||
.Include(p => p.Offender.CurrentAlias)
|
||||
.Include(p => p.Punisher.CurrentAlias)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -148,7 +150,7 @@ namespace SharedLibraryCore.Services
|
||||
PunisherId = penalty.PunisherId,
|
||||
Offense = penalty.Offense,
|
||||
Type = penalty.Type.ToString(),
|
||||
TimeRemaining = now > penalty.Expires ? "" : penalty.Expires.ToString(),
|
||||
TimeRemaining = penalty.Expires.HasValue ? (now > penalty.Expires ? "" : penalty.Expires.ToString()) : DateTime.MaxValue.ToString(),
|
||||
AutomatedOffense = penalty.AutomatedOffense
|
||||
},
|
||||
When = penalty.When,
|
||||
@ -160,12 +162,15 @@ namespace SharedLibraryCore.Services
|
||||
{
|
||||
// todo: why does this have to be done?
|
||||
if (((PenaltyInfo)p.Value).Type.Length < 2)
|
||||
{
|
||||
((PenaltyInfo)p.Value).Type = ((Penalty.PenaltyType)Convert.ToInt32(((PenaltyInfo)p.Value).Type)).ToString();
|
||||
}
|
||||
|
||||
var pi = ((PenaltyInfo)p.Value);
|
||||
if (pi.TimeRemaining.Length > 0)
|
||||
if (pi.TimeRemaining?.Length > 0)
|
||||
{
|
||||
pi.TimeRemaining = (DateTime.Parse(((PenaltyInfo)p.Value).TimeRemaining) - now).TimeSpanText();
|
||||
|
||||
}
|
||||
});
|
||||
return list;
|
||||
}
|
||||
@ -207,7 +212,9 @@ namespace SharedLibraryCore.Services
|
||||
{
|
||||
// todo: why does this have to be done?
|
||||
if (((PenaltyInfo)p.Value).Type.Length < 2)
|
||||
{
|
||||
((PenaltyInfo)p.Value).Type = ((Penalty.PenaltyType)Convert.ToInt32(((PenaltyInfo)p.Value).Type)).ToString();
|
||||
}
|
||||
});
|
||||
|
||||
return list;
|
||||
@ -215,26 +222,37 @@ namespace SharedLibraryCore.Services
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<EFPenalty>> GetActivePenaltiesAsync(int linkId, int ip = 0)
|
||||
public async Task<List<EFPenalty>> GetActivePenaltiesAsync(int linkId, int? ip = null)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
Expression<Func<EFPenalty, bool>> filter = (p) => new Penalty.PenaltyType[]
|
||||
{
|
||||
Penalty.PenaltyType.TempBan,
|
||||
Penalty.PenaltyType.Ban, Penalty.PenaltyType.Flag
|
||||
}.Contains(p.Type) &&
|
||||
p.Active &&
|
||||
(p.Expires == null || p.Expires > now);
|
||||
|
||||
using (var context = new DatabaseContext(true))
|
||||
{
|
||||
var iqPenalties = context.Penalties
|
||||
.Where(p => p.LinkId == linkId ||
|
||||
p.Link.Children.Any(a => a.IPAddress == ip))
|
||||
.Where(p => p.Active)
|
||||
.Where(p => p.Expires > now);
|
||||
|
||||
|
||||
var iqLinkPenalties = context.Penalties
|
||||
.Where(p => p.LinkId == linkId)
|
||||
.Where(filter);
|
||||
|
||||
var iqIPPenalties = context.Aliases
|
||||
.Where(a => a.IPAddress == ip)
|
||||
.SelectMany(a => a.Link.ReceivedPenalties)
|
||||
.Where(filter);
|
||||
|
||||
#if DEBUG == true
|
||||
var penaltiesSql = iqPenalties.ToSql();
|
||||
var penaltiesSql = iqLinkPenalties.ToSql();
|
||||
var ipPenaltiesSql = iqIPPenalties.ToSql();
|
||||
#endif
|
||||
|
||||
var activePenalties = await iqPenalties.ToListAsync();
|
||||
var activePenalties = (await iqLinkPenalties.ToListAsync()).Union(await iqIPPenalties.ToListAsync());
|
||||
// this is a bit more performant in memory (ordering)
|
||||
return activePenalties.OrderByDescending(p =>p.When).ToList();
|
||||
return activePenalties.OrderByDescending(p => p.When).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,7 +264,7 @@ namespace SharedLibraryCore.Services
|
||||
var penalties = await context.Penalties
|
||||
.Include(p => p.Link.Children)
|
||||
.Where(p => p.LinkId == aliasLinkId)
|
||||
.Where(p => p.Expires > now)
|
||||
.Where(p => p.Expires > now || p.Expires == null)
|
||||
.ToListAsync();
|
||||
|
||||
penalties.ForEach(async p =>
|
||||
@ -259,7 +277,7 @@ namespace SharedLibraryCore.Services
|
||||
{
|
||||
await internalContext.Clients
|
||||
.Where(c => c.AliasLinkId == p.LinkId)
|
||||
.ForEachAsync(c => c.Level = Player.Permission.User);
|
||||
.ForEachAsync(c => c.Level = EFClient.Permission.User);
|
||||
await internalContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,12 @@
|
||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Migrations\20181126232438_AddEndpointToEFServer.cs" />
|
||||
<Compile Remove="Migrations\20181126233300_AddEndpointToEFServer.cs" />
|
||||
<Compile Remove="Migrations\20181127143920_AddEndpointToEFServerUpdateServerIdType.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Jint" Version="2.11.58" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.4" />
|
||||
|
@ -1,20 +1,18 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using SharedLibraryCore.Objects;
|
||||
using static SharedLibraryCore.Server;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Globalization;
|
||||
using System.Diagnostics;
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Query;
|
||||
using Microsoft.EntityFrameworkCore.Query;
|
||||
using Microsoft.EntityFrameworkCore.Query.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using static SharedLibraryCore.Server;
|
||||
|
||||
namespace SharedLibraryCore
|
||||
{
|
||||
@ -27,13 +25,21 @@ namespace SharedLibraryCore
|
||||
#endif
|
||||
public static Encoding EncodingType;
|
||||
public static Localization.Layout CurrentLocalization = new Localization.Layout(new Dictionary<string, string>());
|
||||
public static Player IW4MAdminClient(Server server = null) => new Player()
|
||||
public static EFClient IW4MAdminClient(Server server = null)
|
||||
{
|
||||
ClientId = 1,
|
||||
State = Player.ClientState.Connected,
|
||||
Level = Player.Permission.Console,
|
||||
CurrentServer = server
|
||||
};
|
||||
return new EFClient()
|
||||
{
|
||||
ClientId = 1,
|
||||
State = EFClient.ClientState.Connected,
|
||||
Level = EFClient.Permission.Console,
|
||||
CurrentServer = server,
|
||||
CurrentAlias = new EFAlias()
|
||||
{
|
||||
Name = "IW4MAdmin"
|
||||
},
|
||||
AdministeredPenalties = new List<EFPenalty>()
|
||||
};
|
||||
}
|
||||
|
||||
public static string HttpRequest(string location, string header, string headerValue)
|
||||
{
|
||||
@ -62,7 +68,9 @@ namespace SharedLibraryCore
|
||||
public static String RemoveWords(this string str, int num)
|
||||
{
|
||||
if (str == null || str.Length == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
String newStr = String.Empty;
|
||||
String[] tmp = str.Split(' ');
|
||||
@ -70,7 +78,9 @@ namespace SharedLibraryCore
|
||||
for (int i = 0; i < tmp.Length; i++)
|
||||
{
|
||||
if (i >= num)
|
||||
{
|
||||
newStr += tmp[i] + ' ';
|
||||
}
|
||||
}
|
||||
|
||||
return newStr;
|
||||
@ -98,16 +108,20 @@ namespace SharedLibraryCore
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static Player.Permission MatchPermission(String str)
|
||||
public static EFClient.Permission MatchPermission(String str)
|
||||
{
|
||||
String lookingFor = str.ToLower();
|
||||
|
||||
for (Player.Permission Perm = Player.Permission.User; Perm < Player.Permission.Console; Perm++)
|
||||
for (EFClient.Permission Perm = EFClient.Permission.User; Perm < EFClient.Permission.Console; Perm++)
|
||||
{
|
||||
if (lookingFor.Contains(Perm.ToString().ToLower())
|
||||
|| lookingFor.Contains(CurrentLocalization.LocalizationIndex[$"GLOBAL_PERMISSION_{Perm.ToString().ToUpper()}"].ToLower()))
|
||||
{
|
||||
return Perm;
|
||||
}
|
||||
}
|
||||
|
||||
return Player.Permission.Banned;
|
||||
return EFClient.Permission.Banned;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -118,7 +132,10 @@ namespace SharedLibraryCore
|
||||
public static String StripColors(this string str)
|
||||
{
|
||||
if (str == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
str = Regex.Replace(str, @"(\^+((?![a-z]|[A-Z]).){0,1})+", "");
|
||||
string str2 = Regex.Match(str, @"(^\/+.*$)|(^.*\/+$)")
|
||||
.Value
|
||||
@ -131,25 +148,25 @@ namespace SharedLibraryCore
|
||||
/// </summary>
|
||||
/// <param name="level">Specified player level</param>
|
||||
/// <returns></returns>
|
||||
public static String ConvertLevelToColor(Player.Permission level, string localizedLevel)
|
||||
public static String ConvertLevelToColor(EFClient.Permission level, string localizedLevel)
|
||||
{
|
||||
char colorCode = '6';
|
||||
// todo: maybe make this game independant?
|
||||
switch (level)
|
||||
{
|
||||
case Player.Permission.Banned:
|
||||
case EFClient.Permission.Banned:
|
||||
colorCode = '1';
|
||||
break;
|
||||
case Player.Permission.Flagged:
|
||||
case EFClient.Permission.Flagged:
|
||||
colorCode = '9';
|
||||
break;
|
||||
case Player.Permission.Owner:
|
||||
case EFClient.Permission.Owner:
|
||||
colorCode = '5';
|
||||
break;
|
||||
case Player.Permission.User:
|
||||
case EFClient.Permission.User:
|
||||
colorCode = '2';
|
||||
break;
|
||||
case Player.Permission.Trusted:
|
||||
case EFClient.Permission.Trusted:
|
||||
colorCode = '3';
|
||||
break;
|
||||
default:
|
||||
@ -159,7 +176,10 @@ namespace SharedLibraryCore
|
||||
return $"^{colorCode}{localizedLevel ?? level.ToString()}";
|
||||
}
|
||||
|
||||
public static string ToLocalizedLevelName(this Player.Permission perm) => CurrentLocalization.LocalizationIndex[$"GLOBAL_PERMISSION_{perm.ToString().ToUpper()}"];
|
||||
public static string ToLocalizedLevelName(this EFClient.Permission perm)
|
||||
{
|
||||
return CurrentLocalization.LocalizationIndex[$"GLOBAL_PERMISSION_{perm.ToString().ToUpper()}"];
|
||||
}
|
||||
|
||||
public static String ProcessMessageToken(this Server server, IList<Helpers.MessageToken> tokens, String str)
|
||||
{
|
||||
@ -172,7 +192,9 @@ namespace SharedLibraryCore
|
||||
var found = tokens.FirstOrDefault(t => t.Name.ToLower() == Identifier.ToLower());
|
||||
|
||||
if (found != null)
|
||||
{
|
||||
str = str.Replace(Match, found.Process(server));
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
@ -243,24 +265,30 @@ namespace SharedLibraryCore
|
||||
{
|
||||
str = str.Substring(0, Math.Min(str.Length, 16));
|
||||
if (Int64.TryParse(str, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out long id))
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
var bot = Regex.Match(str, @"bot[0-9]+").Value;
|
||||
if (!string.IsNullOrEmpty(bot))
|
||||
{
|
||||
// should set their GUID to the negation of their 1 based index (-1 - -18)
|
||||
return -(Convert.ToInt64(bot.Substring(3)) + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return long.MinValue;
|
||||
}
|
||||
|
||||
public static int ConvertToIP(this string str)
|
||||
{
|
||||
System.Net.IPAddress.TryParse(str, out System.Net.IPAddress ip);
|
||||
|
||||
return ip == null ? 0 : BitConverter.ToInt32(ip.GetAddressBytes(), 0);
|
||||
return ip == null ? int.MaxValue : BitConverter.ToInt32(ip.GetAddressBytes(), 0);
|
||||
}
|
||||
|
||||
public static string ConvertIPtoString(this int ip)
|
||||
public static string ConvertIPtoString(this int? ip)
|
||||
{
|
||||
return new System.Net.IPAddress(BitConverter.GetBytes(ip)).ToString();
|
||||
return !ip.HasValue ? "" : new System.Net.IPAddress(BitConverter.GetBytes(ip.Value)).ToString();
|
||||
}
|
||||
|
||||
public static String GetTimePassed(DateTime start)
|
||||
@ -280,19 +308,28 @@ namespace SharedLibraryCore
|
||||
if (Elapsed.TotalMinutes < 120)
|
||||
{
|
||||
if (Elapsed.TotalMinutes < 1.5)
|
||||
{
|
||||
return $"1 {CurrentLocalization.LocalizationIndex["GLOBAL_TIME_MINUTES"]}{ago}";
|
||||
}
|
||||
|
||||
return Math.Round(Elapsed.TotalMinutes, 0) + $" {CurrentLocalization.LocalizationIndex["GLOBAL_TIME_MINUTES"]}{ago}";
|
||||
}
|
||||
if (Elapsed.TotalHours <= 24)
|
||||
{
|
||||
if (Elapsed.TotalHours < 1.5)
|
||||
{
|
||||
return $"1 {CurrentLocalization.LocalizationIndex["GLOBAL_TIME_HOURS"]}{ago}";
|
||||
}
|
||||
|
||||
return Math.Round(Elapsed.TotalHours, 0) + $" { CurrentLocalization.LocalizationIndex["GLOBAL_TIME_HOURS"]}{ago}";
|
||||
}
|
||||
if (Elapsed.TotalDays <= 90)
|
||||
{
|
||||
if (Elapsed.TotalDays < 1.5)
|
||||
{
|
||||
return $"1 {CurrentLocalization.LocalizationIndex["GLOBAL_TIME_DAYS"]}{ago}";
|
||||
}
|
||||
|
||||
return Math.Round(Elapsed.TotalDays, 0) + $" {CurrentLocalization.LocalizationIndex["GLOBAL_TIME_DAYS"]}{ago}";
|
||||
}
|
||||
if (Elapsed.TotalDays <= 365)
|
||||
@ -308,19 +345,39 @@ namespace SharedLibraryCore
|
||||
public static Game GetGame(string gameName)
|
||||
{
|
||||
if (gameName.Contains("IW4"))
|
||||
{
|
||||
return Game.IW4;
|
||||
}
|
||||
|
||||
if (gameName.Contains("CoD4"))
|
||||
{
|
||||
return Game.IW3;
|
||||
}
|
||||
|
||||
if (gameName.Contains("COD_WaW"))
|
||||
{
|
||||
return Game.T4;
|
||||
}
|
||||
|
||||
if (gameName.Contains("COD_T5_S"))
|
||||
{
|
||||
return Game.T5;
|
||||
}
|
||||
|
||||
if (gameName.Contains("T5M"))
|
||||
{
|
||||
return Game.T5M;
|
||||
}
|
||||
|
||||
if (gameName.Contains("IW5"))
|
||||
{
|
||||
return Game.IW5;
|
||||
}
|
||||
|
||||
if (gameName.Contains("COD_T6_S"))
|
||||
{
|
||||
return Game.T6M;
|
||||
}
|
||||
|
||||
return Game.UKN;
|
||||
}
|
||||
@ -335,7 +392,9 @@ namespace SharedLibraryCore
|
||||
var expressionMatch = Regex.Match(input, @"([0-9]+)(\w+)");
|
||||
|
||||
if (!expressionMatch.Success) // fallback to default tempban length of 1 hour
|
||||
{
|
||||
return new TimeSpan(1, 0, 0);
|
||||
}
|
||||
|
||||
char lengthDenote = expressionMatch.Groups[2].ToString()[0];
|
||||
int length = Int32.Parse(expressionMatch.Groups[1].ToString());
|
||||
@ -375,52 +434,42 @@ namespace SharedLibraryCore
|
||||
var loc = CurrentLocalization.LocalizationIndex;
|
||||
|
||||
if (span.TotalMinutes < 60)
|
||||
{
|
||||
return $"{span.Minutes} {loc["GLOBAL_TIME_MINUTES"]}";
|
||||
}
|
||||
else if (span.Hours >= 1 && span.TotalHours < 24)
|
||||
{
|
||||
return $"{span.Hours} {loc["GLOBAL_TIME_HOURS"]}";
|
||||
}
|
||||
else if (span.TotalDays >= 1 && span.TotalDays < 7)
|
||||
{
|
||||
return $"{span.Days} {loc["GLOBAL_TIME_DAYS"]}";
|
||||
}
|
||||
else if (span.TotalDays >= 7 && span.TotalDays < 90)
|
||||
{
|
||||
return $"{Math.Round(span.Days / 7.0, 0)} {loc["GLOBAL_TIME_WEEKS"]}";
|
||||
}
|
||||
else if (span.TotalDays >= 90 && span.TotalDays < 365)
|
||||
{
|
||||
return $"{Math.Round(span.Days / 30.0, 0)} {loc["GLOBAL_TIME_MONTHS"]}";
|
||||
}
|
||||
else if (span.TotalDays >= 365 && span.TotalDays < 36500)
|
||||
{
|
||||
return $"{Math.Round(span.Days / 365.0, 0)} {loc["GLOBAL_TIME_YEARS"]}";
|
||||
}
|
||||
else if (span.TotalDays >= 36500)
|
||||
{
|
||||
return loc["GLOBAL_TIME_FOREVER"];
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
public static Player AsPlayer(this Database.Models.EFClient client)
|
||||
public static bool IsPrivileged(this EFClient p)
|
||||
{
|
||||
return client == null ? null : new Player()
|
||||
{
|
||||
Active = client.Active,
|
||||
AliasLink = client.AliasLink,
|
||||
AliasLinkId = client.AliasLinkId,
|
||||
ClientId = client.ClientId,
|
||||
ClientNumber = -1,
|
||||
FirstConnection = client.FirstConnection,
|
||||
Connections = client.Connections,
|
||||
NetworkId = client.NetworkId,
|
||||
TotalConnectionTime = client.TotalConnectionTime,
|
||||
Masked = client.Masked,
|
||||
Name = client.CurrentAlias.Name,
|
||||
IPAddress = client.CurrentAlias.IPAddress,
|
||||
Level = client.Level,
|
||||
LastConnection = client.LastConnection == DateTime.MinValue ? DateTime.UtcNow : client.LastConnection,
|
||||
CurrentAlias = client.CurrentAlias,
|
||||
CurrentAliasId = client.CurrentAlias.AliasId,
|
||||
// todo: make sure this is up to date
|
||||
IsBot = client.IPAddress == int.MinValue,
|
||||
Password = client.Password,
|
||||
PasswordSalt = client.PasswordSalt
|
||||
};
|
||||
return p.Level > EFClient.Permission.User;
|
||||
}
|
||||
|
||||
public static bool IsPrivileged(this Player p) => p.Level > Player.Permission.User;
|
||||
|
||||
public static bool PromptBool(string question)
|
||||
{
|
||||
Console.Write($"{question}? [y/n]: ");
|
||||
@ -472,7 +521,9 @@ namespace SharedLibraryCore
|
||||
int.TryParse(lineSplit[cIDPos].Trim(), out pID);
|
||||
|
||||
if (pID == -1) // special case similar to mod_suicide
|
||||
{
|
||||
int.TryParse(lineSplit[2], out pID);
|
||||
}
|
||||
|
||||
return pID;
|
||||
}
|
||||
@ -487,7 +538,9 @@ namespace SharedLibraryCore
|
||||
{
|
||||
dict = new Dictionary<string, string>();
|
||||
for (int i = 0; i < values.Length; i += 2)
|
||||
{
|
||||
dict.Add(values[i], values[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
return dict;
|
||||
@ -516,15 +569,30 @@ namespace SharedLibraryCore
|
||||
return cmdLine.Length > 1 ? cmdLine[1] : cmdLine[0];
|
||||
}
|
||||
|
||||
public static string ToBase64UrlSafeString(this string src) => Convert.ToBase64String(src.Select(c => Convert.ToByte(c)).ToArray()).Replace('+', '-').Replace('/', '_');
|
||||
public static string ToBase64UrlSafeString(this string src)
|
||||
{
|
||||
return Convert.ToBase64String(src.Select(c => Convert.ToByte(c)).ToArray()).Replace('+', '-').Replace('/', '_');
|
||||
}
|
||||
|
||||
public static Task<Dvar<T>> GetDvarAsync<T>(this Server server, string dvarName) => server.RconParser.GetDvarAsync<T>(server.RemoteConnection, dvarName);
|
||||
public static Task<Dvar<T>> GetDvarAsync<T>(this Server server, string dvarName)
|
||||
{
|
||||
return server.RconParser.GetDvarAsync<T>(server.RemoteConnection, dvarName);
|
||||
}
|
||||
|
||||
public static Task SetDvarAsync(this Server server, string dvarName, object dvarValue) => server.RconParser.SetDvarAsync(server.RemoteConnection, dvarName, dvarValue);
|
||||
public static Task SetDvarAsync(this Server server, string dvarName, object dvarValue)
|
||||
{
|
||||
return server.RconParser.SetDvarAsync(server.RemoteConnection, dvarName, dvarValue);
|
||||
}
|
||||
|
||||
public static async Task<string[]> ExecuteCommandAsync(this Server server, string commandName) => await server.RconParser.ExecuteCommandAsync(server.RemoteConnection, commandName);
|
||||
public static async Task<string[]> ExecuteCommandAsync(this Server server, string commandName)
|
||||
{
|
||||
return await server.RconParser.ExecuteCommandAsync(server.RemoteConnection, commandName);
|
||||
}
|
||||
|
||||
public static Task<List<Player>> GetStatusAsync(this Server server) => server.RconParser.GetStatusAsync(server.RemoteConnection);
|
||||
public static Task<List<EFClient>> GetStatusAsync(this Server server)
|
||||
{
|
||||
return server.RconParser.GetStatusAsync(server.RemoteConnection);
|
||||
}
|
||||
|
||||
public static async Task<Dictionary<string, string>> GetInfoAsync(this Server server)
|
||||
{
|
||||
@ -533,7 +601,10 @@ namespace SharedLibraryCore
|
||||
{
|
||||
response = await server.RemoteConnection.SendQueryAsync(RCon.StaticHelpers.QueryType.GET_INFO);
|
||||
if (response.Length == 2)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
await Task.Delay(RCon.StaticHelpers.FloodProtectionInterval);
|
||||
}
|
||||
return response.FirstOrDefault(r => r[0] == '\\')?.DictionaryFromKeyValue();
|
||||
@ -546,7 +617,10 @@ namespace SharedLibraryCore
|
||||
return double.Parse(version) / 1000.0;
|
||||
}
|
||||
|
||||
public static string GetVersionAsString() => Assembly.GetCallingAssembly().GetName().Version.ToString();
|
||||
public static string GetVersionAsString()
|
||||
{
|
||||
return Assembly.GetCallingAssembly().GetName().Version.ToString();
|
||||
}
|
||||
|
||||
#if DEBUG == true
|
||||
|
||||
|
@ -1,16 +1,16 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Events;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WebfrontCore.Controllers.API
|
||||
{
|
||||
public class ApiController : BaseController
|
||||
{
|
||||
public IActionResult Index() => Ok($"IW4MAdmin API");
|
||||
public IActionResult Index()
|
||||
{
|
||||
return Ok($"IW4MAdmin API");
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult Event(bool shouldConsume = true)
|
||||
@ -25,15 +25,15 @@ namespace WebfrontCore.Controllers.API
|
||||
var serverInfo = Manager.GetServers()
|
||||
.Select(server => new
|
||||
{
|
||||
Id = server.GetHashCode(),
|
||||
Id = server.EndPoint,
|
||||
Name = server.Hostname,
|
||||
MaxPlayers = server.MaxClients,
|
||||
CurrentPlayers = server.GetPlayersAsList().Count,
|
||||
CurrentPlayers = server.GetClientsAsList().Count,
|
||||
Map = server.CurrentMap,
|
||||
GameMode = server.Gametype,
|
||||
Port = server.GetPort(),
|
||||
Game = server.GameName.ToString(),
|
||||
Players = server.GetPlayersAsList()
|
||||
Players = server.GetClientsAsList()
|
||||
.Select(player => new
|
||||
{
|
||||
player.Name,
|
||||
@ -53,5 +53,23 @@ namespace WebfrontCore.Controllers.API
|
||||
|
||||
return Json(serverInfo);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult RestartApproved()
|
||||
{
|
||||
var serverToRestart = Manager.GetServers().FirstOrDefault(_server => _server.RestartRequested);
|
||||
|
||||
if (serverToRestart != null)
|
||||
{
|
||||
serverToRestart.RestartRequested = false;
|
||||
}
|
||||
|
||||
return serverToRestart != null ?
|
||||
(IActionResult)Json(new
|
||||
{
|
||||
port = serverToRestart.GetPort()
|
||||
}) :
|
||||
Unauthorized();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SharedLibraryCore;
|
||||
using WebfrontCore.ViewModels;
|
||||
using static SharedLibraryCore.Objects.Player;
|
||||
using static SharedLibraryCore.Database.Models.EFClient;
|
||||
|
||||
namespace WebfrontCore.Controllers
|
||||
{
|
||||
@ -79,7 +79,7 @@ namespace WebfrontCore.Controllers
|
||||
|
||||
return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new
|
||||
{
|
||||
serverId = server.GetHashCode(),
|
||||
serverId = server.EndPoint,
|
||||
command
|
||||
}));
|
||||
}
|
||||
@ -110,7 +110,7 @@ namespace WebfrontCore.Controllers
|
||||
|
||||
return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new
|
||||
{
|
||||
serverId = server.GetHashCode(),
|
||||
serverId = server.EndPoint,
|
||||
command = $"!unban @{targetId} {Reason}"
|
||||
}));
|
||||
}
|
||||
@ -162,7 +162,7 @@ namespace WebfrontCore.Controllers
|
||||
Values = Enum.GetValues(typeof(Permission)).OfType<Permission>()
|
||||
.Where(p => p <= Client.Level)
|
||||
.Where(p => p != Permission.Banned)
|
||||
.Where(p => p != Permission.Flagged)
|
||||
.Where(p => p != Permission.Flagged)
|
||||
.ToDictionary(p => p.ToString(), p=> p.ToLocalizedLevelName())
|
||||
},
|
||||
},
|
||||
@ -178,7 +178,7 @@ namespace WebfrontCore.Controllers
|
||||
|
||||
return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new
|
||||
{
|
||||
serverId = server.GetHashCode(),
|
||||
serverId = server.EndPoint,
|
||||
command = $"!setlevel @{targetId} {level}"
|
||||
}));
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user