the meats
This commit is contained in:
parent
6fa466fdf8
commit
1adf3ceb3c
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,6 +1,10 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
Detection.cs
|
||||
DetectionPenaltyResult.cs
|
||||
Thresholds.cs
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
|
@ -1,90 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{D076ABC9-DDD6-4E30-9584-E45273950902}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Database</RootNamespace>
|
||||
<AssemblyName>Database</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release-Nightly|AnyCPU'">
|
||||
<OutputPath>bin\Release-Nightly\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release-Stable|AnyCPU'">
|
||||
<OutputPath>bin\Release-Stable\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="EntityFramework.SqlServerCompact, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\EntityFramework.SqlServerCompact.6.2.0\lib\net45\EntityFramework.SqlServerCompact.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.SqlServer.Compact.4.0.8876.1\lib\net40\System.Data.SqlServerCe.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="IW4MAdminDatabaseContext.cs" />
|
||||
<Compile Include="IW4MAdminDatabase.cs" />
|
||||
<Compile Include="Models\Alias.cs" />
|
||||
<Compile Include="Models\Client.cs" />
|
||||
<Compile Include="Models\Penalty.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SharedLibrary\SharedLibrary.csproj">
|
||||
<Project>{d51eeceb-438a-47da-870f-7d7b41bc24d6}</Project>
|
||||
<Name>SharedLibrary</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>
|
||||
if not exist "$(TargetDir)x86" md "$(TargetDir)x86"
|
||||
xcopy /s /y "$(SolutionDir)packages\Microsoft.SqlServer.Compact.4.0.8876.1\NativeBinaries\x86\*.*" "$(TargetDir)x86"
|
||||
if not exist "$(TargetDir)amd64" md "$(TargetDir)amd64"
|
||||
xcopy /s /y "$(SolutionDir)packages\Microsoft.SqlServer.Compact.4.0.8876.1\NativeBinaries\amd64\*.*" "$(TargetDir)amd64"</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -1,38 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Database.Models;
|
||||
|
||||
namespace Database
|
||||
{
|
||||
public class IW4MAdminDatabase : SharedLibrary.Interfaces.IDatabase
|
||||
{
|
||||
private IW4MAdminDatabaseContext _context;
|
||||
|
||||
public IW4MAdminDatabase()
|
||||
{
|
||||
_context = new IW4MAdminDatabaseContext();
|
||||
}
|
||||
|
||||
public SharedLibrary.Interfaces.IDatabaseContext GetContext() => _context;
|
||||
|
||||
public async Task<Client> AddClient(Client newClient)
|
||||
{
|
||||
var client = _context.Clients.Add(newClient);
|
||||
await _context.SaveChangesAsync();
|
||||
return client;
|
||||
}
|
||||
|
||||
public Client GetClient(int clientID) => _context.Clients.SingleOrDefault(c => c.ClientId == clientID);
|
||||
public Client GetClient(string networkID) => _context.Clients.SingleOrDefault(c => c.NetworkId == networkID);
|
||||
public IList<Client> GetOwners() => _context.Clients.Where(c => c.Level == SharedLibrary.Player.Permission.Owner).ToList();
|
||||
public IList<SharedLibrary.Player> GetPlayers(IList<string> networkIDs) => _context.Clients.Where(c => networkIDs.Contains(c.NetworkId)).Select(c => c.ToPlayer()).ToList();
|
||||
public IList<Penalty> GetPenalties (int clientID) => _context.Penalties.Where(p => p.OffenderId == clientID).ToList();
|
||||
public IList<SharedLibrary.Player> GetAdmins() => _context.Clients.Where(c => c.Level > SharedLibrary.Player.Permission.Flagged).Select(c => c.ToPlayer()).ToList();
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Entity;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Database.Models;
|
||||
using System.Data.SqlServerCe;
|
||||
|
||||
namespace Database
|
||||
{
|
||||
public class IW4MAdminDatabaseContext : DbContext, SharedLibrary.Interfaces.IDatabaseContext
|
||||
{
|
||||
public DbSet<Client> Clients { get; set; }
|
||||
public DbSet<Alias> Aliases { get; set; }
|
||||
public DbSet<Penalty> Penalties { get; set; }
|
||||
|
||||
public IW4MAdminDatabaseContext() : base("DefaultConnection") { }
|
||||
protected override void OnModelCreating(DbModelBuilder modelBuilder)
|
||||
{
|
||||
var instance = System.Data.Entity.SqlServerCompact.SqlCeProviderServices.Instance;
|
||||
// todo custom load DBSets from plugins
|
||||
// https://aleemkhan.wordpress.com/2013/02/28/dynamically-adding-dbset-properties-in-dbcontext-for-entity-framework-code-first/
|
||||
base.OnModelCreating(modelBuilder);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Database.Models
|
||||
{
|
||||
public class Alias
|
||||
{
|
||||
[Key]
|
||||
public int AliasId { get; set; }
|
||||
public int ClientId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string IP { get; set; }
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Database.Models
|
||||
{
|
||||
public class Client
|
||||
{
|
||||
[Key]
|
||||
public int ClientId { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
[Required]
|
||||
public string NetworkId { get; set; }
|
||||
[Required]
|
||||
public SharedLibrary.Player.Permission Level { get; set; }
|
||||
public int Connections { get; set; }
|
||||
[Required]
|
||||
public string IPAddress { get; set; }
|
||||
[Required]
|
||||
public DateTime LastConnection { get; set; }
|
||||
public bool Masked { get; set; }
|
||||
|
||||
public SharedLibrary.Player ToPlayer()
|
||||
{
|
||||
return new SharedLibrary.Player()
|
||||
{
|
||||
Name = Name,
|
||||
Connections = Connections,
|
||||
DatabaseID = ClientId,
|
||||
NetworkID = NetworkId,
|
||||
Level = Level,
|
||||
IP = IPAddress,
|
||||
LastConnection = LastConnection,
|
||||
Masked = Masked
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Database.Models
|
||||
{
|
||||
public class Penalty
|
||||
{
|
||||
[Key]
|
||||
public int PenaltyId { get; set; }
|
||||
public int OffenderId { get; set; }
|
||||
public Client Offender { get; set; }
|
||||
public int PunisherId { get; set; }
|
||||
public Client Punisher { get; set; }
|
||||
public DateTime When { get; set; }
|
||||
public DateTime Expires { get; set; }
|
||||
public SharedLibrary.Penalty.Type Type { get; set; }
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Database")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Database")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("d076abc9-ddd6-4e30-9584-e45273950902")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="EntityFramework" version="6.2.0" targetFramework="net45" />
|
||||
<package id="EntityFramework.SqlServerCompact" version="6.2.0" targetFramework="net45" />
|
||||
<package id="Microsoft.SqlServer.Compact" version="4.0.8876.1" targetFramework="net45" />
|
||||
</packages>
|
@ -1,7 +1,7 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27130.2036
|
||||
VisualStudioVersion = 15.0.27004.2006
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StatsPlugin", "Plugins\SimpleStats\StatsPlugin.csproj", "{4785AB75-66F3-4391-985D-63A5A049A0FA}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
@ -320,16 +320,16 @@ Global
|
||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Nightly|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Nightly|x64.ActiveCfg = Release|Any CPU
|
||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Nightly|x64.Build.0 = Release|Any CPU
|
||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Nightly|x86.ActiveCfg = Release|Any CPU
|
||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Nightly|x86.Build.0 = Release|Any CPU
|
||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Nightly|x86.ActiveCfg = Release|x86
|
||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Nightly|x86.Build.0 = Release|x86
|
||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Stable|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Stable|Any CPU.Build.0 = Release|Any CPU
|
||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Stable|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Stable|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Stable|x64.ActiveCfg = Release|Any CPU
|
||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Stable|x64.Build.0 = Release|Any CPU
|
||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Stable|x86.ActiveCfg = Release|Any CPU
|
||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Stable|x86.Build.0 = Release|Any CPU
|
||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Stable|x86.ActiveCfg = Release|x86
|
||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Stable|x86.Build.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -1,4 +1,5 @@
|
||||
using SharedLibrary.Interfaces;
|
||||
using SharedLibrary.Objects;
|
||||
using StatsPlugin.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -30,62 +31,276 @@ namespace StatsPlugin.Cheat
|
||||
/// </summary>
|
||||
/// <param name="kill">kill performed by the player</param>
|
||||
/// <returns>true if detection reached thresholds, false otherwise</returns>
|
||||
public bool ProcessKill(EFClientKill kill)
|
||||
public DetectionPenaltyResult ProcessKill(EFClientKill kill)
|
||||
{
|
||||
if (kill.DeathType != IW4Info.MeansOfDeath.MOD_PISTOL_BULLET && kill.DeathType != IW4Info.MeansOfDeath.MOD_RIFLE_BULLET)
|
||||
return false;
|
||||
|
||||
bool thresholdReached = false;
|
||||
if ((kill.DeathType != IW4Info.MeansOfDeath.MOD_PISTOL_BULLET &&
|
||||
kill.DeathType != IW4Info.MeansOfDeath.MOD_RIFLE_BULLET) ||
|
||||
kill.HitLoc == IW4Info.HitLocation.none)
|
||||
return new DetectionPenaltyResult()
|
||||
{
|
||||
ClientPenalty = Penalty.PenaltyType.Any,
|
||||
RatioAmount = 0
|
||||
};
|
||||
|
||||
HitLocationCount[kill.HitLoc]++;
|
||||
Kills++;
|
||||
AverageKillTime = (AverageKillTime + (DateTime.UtcNow - LastKill).TotalSeconds) / Kills;
|
||||
|
||||
if (Kills > Thresholds.LowSampleMinKills)
|
||||
#region SESSION_RATIOS
|
||||
if (Kills >= Thresholds.LowSampleMinKills)
|
||||
{
|
||||
double marginOfError = Thresholds.GetMarginOfError(Kills);
|
||||
// determine what the max headshot percentage can be for current number of kills
|
||||
double lerpAmount = Math.Min(1.0, (Kills - Thresholds.LowSampleMinKills) / (double)(Thresholds.HighSampleMinKills - Thresholds.LowSampleMinKills));
|
||||
double maxHeadshotLerpValue = Thresholds.Lerp( Thresholds.HeadshotRatioThresholdLowSample, Thresholds.HeadshotRatioThresholdHighSample, lerpAmount);
|
||||
double maxHeadshotLerpValueForFlag = Thresholds.Lerp(Thresholds.HeadshotRatioThresholdLowSample(2.0), Thresholds.HeadshotRatioThresholdHighSample(2.0), lerpAmount) + marginOfError;
|
||||
double maxHeadshotLerpValueForBan = Thresholds.Lerp(Thresholds.HeadshotRatioThresholdLowSample(3.0), Thresholds.HeadshotRatioThresholdHighSample(3.0), lerpAmount) + marginOfError;
|
||||
// determine what the max bone percentage can be for current number of kills
|
||||
double maxBoneRatioLerpValue = Thresholds.Lerp(Thresholds.BoneRatioThresholdLowSample, Thresholds.BoneRatioThresholdHighSample, lerpAmount);
|
||||
double maxBoneRatioLerpValueForFlag = Thresholds.Lerp(Thresholds.BoneRatioThresholdLowSample(2.25), Thresholds.BoneRatioThresholdHighSample(2.25), lerpAmount) + marginOfError;
|
||||
double maxBoneRatioLerpValueForBan = Thresholds.Lerp(Thresholds.BoneRatioThresholdLowSample(3.25), Thresholds.BoneRatioThresholdHighSample(3.25), lerpAmount) + marginOfError;
|
||||
|
||||
// calculate headshot ratio
|
||||
double headshotRatio = ((HitLocationCount[IW4Info.HitLocation.head] + HitLocationCount[IW4Info.HitLocation.helmet]) / (double)Kills) - marginOfError;
|
||||
double currentHeadshotRatio = ((HitLocationCount[IW4Info.HitLocation.head] + HitLocationCount[IW4Info.HitLocation.helmet]) / (double)Kills);
|
||||
// calculate maximum bone
|
||||
double maximumBoneRatio = (HitLocationCount.Values.Select(v => v / (double)Kills).Max()) - marginOfError;
|
||||
double currentMaxBoneRatio = (HitLocationCount.Values.Select(v => v / (double)Kills).Max());
|
||||
|
||||
if (headshotRatio > maxHeadshotLerpValue)
|
||||
var bone = HitLocationCount.FirstOrDefault(b => b.Value == HitLocationCount.Values.Max()).Key;
|
||||
#region HEADSHOT_RATIO
|
||||
// flag on headshot
|
||||
if (currentHeadshotRatio > maxHeadshotLerpValueForFlag)
|
||||
{
|
||||
AboveThresholdCount++;
|
||||
Log.WriteDebug("**Maximum Headshot Ratio Reached**");
|
||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
||||
Log.WriteDebug($"**Kills: {Kills}");
|
||||
Log.WriteDebug($"**Ratio {headshotRatio}");
|
||||
Log.WriteDebug($"**MaxRatio {maxHeadshotLerpValue}");
|
||||
var sb = new StringBuilder();
|
||||
foreach (var kvp in HitLocationCount)
|
||||
sb.Append($"HitLocation: {kvp.Key} Count: {kvp.Value}");
|
||||
Log.WriteDebug(sb.ToString());
|
||||
Log.WriteDebug($"ThresholdReached: {AboveThresholdCount}");
|
||||
thresholdReached = true;
|
||||
// ban on headshot
|
||||
if (currentHeadshotRatio > maxHeadshotLerpValueForFlag)
|
||||
{
|
||||
AboveThresholdCount++;
|
||||
Log.WriteDebug("**Maximum Headshot Ratio Reached For Ban**");
|
||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
||||
Log.WriteDebug($"**Kills: {Kills}");
|
||||
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());
|
||||
Log.WriteDebug($"ThresholdReached: {AboveThresholdCount}");
|
||||
|
||||
return new DetectionPenaltyResult()
|
||||
{
|
||||
ClientPenalty = Penalty.PenaltyType.Ban,
|
||||
RatioAmount = currentHeadshotRatio,
|
||||
Bone = IW4Info.HitLocation.head,
|
||||
KillCount = Kills
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
AboveThresholdCount++;
|
||||
Log.WriteDebug("**Maximum Headshot Ratio Reached For Flag**");
|
||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
||||
Log.WriteDebug($"**Kills: {Kills}");
|
||||
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());
|
||||
Log.WriteDebug($"ThresholdReached: {AboveThresholdCount}");
|
||||
|
||||
return new DetectionPenaltyResult()
|
||||
{
|
||||
ClientPenalty = Penalty.PenaltyType.Flag,
|
||||
RatioAmount = currentHeadshotRatio,
|
||||
Bone = IW4Info.HitLocation.head,
|
||||
KillCount = Kills
|
||||
};
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
else if (maximumBoneRatio > maxBoneRatioLerpValue)
|
||||
#region BONE_RATIO
|
||||
// flag on bone ratio
|
||||
else if (currentMaxBoneRatio > maxBoneRatioLerpValueForFlag)
|
||||
{
|
||||
Log.WriteDebug("**Maximum Bone Ratio Reached**");
|
||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
||||
Log.WriteDebug($"**Kills: {Kills}");
|
||||
Log.WriteDebug($"**Ratio {maximumBoneRatio}");
|
||||
Log.WriteDebug($"**MaxRatio {maxBoneRatioLerpValue}");
|
||||
var sb = new StringBuilder();
|
||||
foreach (var kvp in HitLocationCount)
|
||||
sb.Append($"HitLocation: {kvp.Key} Count: {kvp.Value}");
|
||||
Log.WriteDebug(sb.ToString());
|
||||
thresholdReached = true;
|
||||
// ban on bone ratio
|
||||
if (currentMaxBoneRatio > maxBoneRatioLerpValueForBan)
|
||||
{
|
||||
Log.WriteDebug("**Maximum Bone Ratio Reached For Ban**");
|
||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
||||
Log.WriteDebug($"**Kills: {Kills}");
|
||||
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());
|
||||
|
||||
return new DetectionPenaltyResult()
|
||||
{
|
||||
ClientPenalty = Penalty.PenaltyType.Ban,
|
||||
RatioAmount = currentMaxBoneRatio,
|
||||
Bone = bone,
|
||||
KillCount = Kills
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.WriteDebug("**Maximum Bone Ratio Reached For Flag**");
|
||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
||||
Log.WriteDebug($"**Kills: {Kills}");
|
||||
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());
|
||||
|
||||
return new DetectionPenaltyResult()
|
||||
{
|
||||
ClientPenalty = Penalty.PenaltyType.Flag,
|
||||
RatioAmount = currentMaxBoneRatio,
|
||||
Bone = bone,
|
||||
KillCount = Kills
|
||||
};
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region CHEST_ABDOMEN_RATIO_SESSION
|
||||
int chestKills = HitLocationCount[IW4Info.HitLocation.torso_upper];
|
||||
|
||||
if (chestKills >= Thresholds.MediumSampleMinKills)
|
||||
{
|
||||
double marginOfError = Thresholds.GetMarginOfError(chestKills);
|
||||
double lerpAmount = Math.Min(1.0, (chestKills - Thresholds.LowSampleMinKills) / (double)(Thresholds.HighSampleMinKills - Thresholds.LowSampleMinKills));
|
||||
// determine max acceptable ratio of chest to abdomen kills
|
||||
double chestAbdomenRatioLerpValueForFlag = Thresholds.Lerp(Thresholds.ChestAbdomenRatioThresholdLowSample(2.25), Thresholds.ChestAbdomenRatioThresholdHighSample(2.25), lerpAmount) + marginOfError;
|
||||
double chestAbdomenLerpValueForBan = Thresholds.Lerp(Thresholds.ChestAbdomenRatioThresholdLowSample(3.25), Thresholds.ChestAbdomenRatioThresholdHighSample(3.25), lerpAmount) + marginOfError;
|
||||
|
||||
double currentChestAbdomenRatio = HitLocationCount[IW4Info.HitLocation.torso_upper] / (double)HitLocationCount[IW4Info.HitLocation.torso_lower];
|
||||
|
||||
if (currentChestAbdomenRatio > chestAbdomenRatioLerpValueForFlag)
|
||||
{
|
||||
|
||||
if (currentChestAbdomenRatio > chestAbdomenLerpValueForBan && chestKills >= Thresholds.MediumSampleMinKills + 30)
|
||||
{
|
||||
Log.WriteDebug("**Maximum Chest/Abdomen Ratio Reached For Ban**");
|
||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
||||
Log.WriteDebug($"**Chest Kills: {chestKills}");
|
||||
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($"ThresholdReached: {AboveThresholdCount}");
|
||||
|
||||
return new DetectionPenaltyResult()
|
||||
{
|
||||
ClientPenalty = Penalty.PenaltyType.Ban,
|
||||
RatioAmount = currentChestAbdomenRatio,
|
||||
Bone = IW4Info.HitLocation.torso_upper,
|
||||
KillCount = chestKills
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.WriteDebug("**Maximum Chest/Abdomen Ratio Reached For Flag**");
|
||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
||||
Log.WriteDebug($"**Chest Kills: {chestKills}");
|
||||
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}");
|
||||
|
||||
return new DetectionPenaltyResult()
|
||||
{
|
||||
ClientPenalty = Penalty.PenaltyType.Flag,
|
||||
RatioAmount = currentChestAbdomenRatio,
|
||||
Bone = IW4Info.HitLocation.torso_upper,
|
||||
KillCount = chestKills
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
return new DetectionPenaltyResult()
|
||||
{
|
||||
ClientPenalty = Penalty.PenaltyType.Any,
|
||||
RatioAmount = 0
|
||||
};
|
||||
}
|
||||
|
||||
public DetectionPenaltyResult ProcessTotalRatio(EFClientStatistics stats)
|
||||
{
|
||||
int totalChestKills = stats.HitLocations.Single(c => c.Location == IW4Info.HitLocation.left_arm_upper).HitCount;
|
||||
|
||||
if (totalChestKills >= 250)
|
||||
{
|
||||
double marginOfError = Thresholds.GetMarginOfError(totalChestKills);
|
||||
double lerpAmount = Math.Min(1.0, (totalChestKills - Thresholds.LowSampleMinKills) / (double)(Thresholds.HighSampleMinKills - Thresholds.LowSampleMinKills));
|
||||
// determine max acceptable ratio of chest to abdomen kills
|
||||
double chestAbdomenRatioLerpValueForFlag = Thresholds.Lerp(Thresholds.ChestAbdomenRatioThresholdLowSample(2.25), Thresholds.ChestAbdomenRatioThresholdHighSample(2.25), lerpAmount) + marginOfError;
|
||||
double chestAbdomenLerpValueForBan = Thresholds.Lerp(Thresholds.ChestAbdomenRatioThresholdLowSample(3.0), Thresholds.ChestAbdomenRatioThresholdHighSample(3.0), lerpAmount) + marginOfError;
|
||||
|
||||
double currentChestAbdomenRatio = HitLocationCount[IW4Info.HitLocation.torso_upper] / (double)HitLocationCount[IW4Info.HitLocation.torso_lower];
|
||||
|
||||
if (currentChestAbdomenRatio > chestAbdomenRatioLerpValueForFlag)
|
||||
{
|
||||
|
||||
if (currentChestAbdomenRatio > chestAbdomenLerpValueForBan)
|
||||
{
|
||||
Log.WriteDebug("**Maximum Lifetime Chest/Abdomen Ratio Reached For Ban**");
|
||||
Log.WriteDebug($"ClientId: {stats.ClientId}");
|
||||
Log.WriteDebug($"**Total Chest Kills: {totalChestKills}");
|
||||
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($"ThresholdReached: {AboveThresholdCount}");
|
||||
|
||||
return new DetectionPenaltyResult()
|
||||
{
|
||||
ClientPenalty = Penalty.PenaltyType.Ban,
|
||||
RatioAmount = currentChestAbdomenRatio,
|
||||
Bone = IW4Info.HitLocation.torso_upper,
|
||||
KillCount = totalChestKills
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.WriteDebug("**Maximum Lifetime Chest/Abdomen Ratio Reached For Flag**");
|
||||
Log.WriteDebug($"ClientId: {stats.ClientId}");
|
||||
Log.WriteDebug($"**Total Chest Kills: {totalChestKills}");
|
||||
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}");
|
||||
|
||||
return new DetectionPenaltyResult()
|
||||
{
|
||||
ClientPenalty = Penalty.PenaltyType.Flag,
|
||||
RatioAmount = currentChestAbdomenRatio,
|
||||
Bone = IW4Info.HitLocation.torso_upper,
|
||||
KillCount = totalChestKills
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return thresholdReached;
|
||||
return new DetectionPenaltyResult()
|
||||
{
|
||||
Bone = IW4Info.HitLocation.none,
|
||||
ClientPenalty = Penalty.PenaltyType.Any
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,26 +8,30 @@ namespace StatsPlugin.Cheat
|
||||
{
|
||||
class Thresholds
|
||||
{
|
||||
private const double Deviations = 3.33;
|
||||
|
||||
public const double HeadshotRatioThresholdLowSample = HeadshotRatioStandardDeviationLowSample * Deviations + HeadshotRatioMean;
|
||||
public const double HeadshotRatioThresholdHighSample = HeadshotRatioStandardDeviationHighSample * Deviations + HeadshotRatioMean;
|
||||
public static double HeadshotRatioThresholdLowSample(double deviations) => HeadshotRatioStandardDeviationLowSample * deviations + HeadshotRatioMean;
|
||||
public static double HeadshotRatioThresholdHighSample(double deviations) => HeadshotRatioStandardDeviationHighSample * deviations + HeadshotRatioMean;
|
||||
public const double HeadshotRatioStandardDeviationLowSample = 0.1769994181;
|
||||
public const double HeadshotRatioStandardDeviationHighSample = 0.03924263235;
|
||||
//public const double HeadshotRatioMean = 0.09587712258;
|
||||
public const double HeadshotRatioMean = 0.222;
|
||||
|
||||
public const double BoneRatioThresholdLowSample = BoneRatioStandardDeviationLowSample * Deviations + BoneRatioMean;
|
||||
public const double BoneRatioThresholdHighSample = BoneRatioStandardDeviationHighSample * Deviations + BoneRatioMean;
|
||||
public static double BoneRatioThresholdLowSample(double deviations) => BoneRatioStandardDeviationLowSample * deviations + BoneRatioMean;
|
||||
public static double BoneRatioThresholdHighSample(double deviations) => BoneRatioStandardDeviationHighSample * deviations + BoneRatioMean;
|
||||
public const double BoneRatioStandardDeviationLowSample = 0.1324612879;
|
||||
public const double BoneRatioStandardDeviationHighSample = 0.0515753935;
|
||||
public const double BoneRatioMean = 0.3982907516;
|
||||
public const double BoneRatioMean = 0.4593110238;
|
||||
|
||||
public static double ChestAbdomenRatioThresholdLowSample(double deviations) => ChestAbdomenStandardDeviationLowSample * deviations + ChestAbdomenRatioMean;
|
||||
public static double ChestAbdomenRatioThresholdHighSample(double deviations) => ChestAbdomenStandardDeviationHighSample * deviations + ChestAbdomenRatioMean;
|
||||
public const double ChestAbdomenStandardDeviationLowSample = 0.2859234644;
|
||||
public const double ChestAbdomenStandardDeviationHighSample = 0.2195212861;
|
||||
public const double ChestAbdomenRatioMean = 0.3925617500;
|
||||
|
||||
public const int LowSampleMinKills = 15;
|
||||
public const int MediumSampleMinKills = 30;
|
||||
public const int HighSampleMinKills = 100;
|
||||
public const double KillTimeThreshold = 0.2;
|
||||
|
||||
public static double GetMarginOfError(int numKills) => 1.645 /(2 * Math.Sqrt(numKills));
|
||||
public static double GetMarginOfError(int numKills) => 0.98 / Math.Sqrt(numKills);
|
||||
|
||||
public static double Lerp(double v1, double v2, double amount)
|
||||
{
|
||||
|
@ -27,6 +27,9 @@ namespace StatsPlugin.Commands
|
||||
stats.SPM = 0;
|
||||
stats.Skill = 0;
|
||||
|
||||
// reset the cached version
|
||||
Plugin.Manager.ResetStats(E.Origin.ClientId, E.Owner.GetHashCode());
|
||||
|
||||
// fixme: this doesn't work properly when another context exists
|
||||
await svc.SaveChangesAsync();
|
||||
await E.Origin.Tell("Your stats have been reset");
|
||||
|
@ -2,6 +2,7 @@
|
||||
using StatsPlugin.Cheat;
|
||||
using StatsPlugin.Models;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@ -10,15 +11,15 @@ using System.Threading.Tasks;
|
||||
namespace StatsPlugin.Helpers
|
||||
{
|
||||
class ServerStats {
|
||||
public Dictionary<int, EFClientStatistics> PlayerStats { get; set; }
|
||||
public Dictionary<int, Detection> PlayerDetections { get; set; }
|
||||
public ConcurrentDictionary<int, EFClientStatistics> PlayerStats { get; set; }
|
||||
public ConcurrentDictionary<int, Detection> PlayerDetections { get; set; }
|
||||
public EFServerStatistics ServerStatistics { get; private set; }
|
||||
public EFServer Server { get; private set; }
|
||||
|
||||
public ServerStats(EFServer sv, EFServerStatistics st)
|
||||
{
|
||||
PlayerStats = new Dictionary<int, EFClientStatistics>();
|
||||
PlayerDetections = new Dictionary<int, Detection>();
|
||||
PlayerStats = new ConcurrentDictionary<int, EFClientStatistics>();
|
||||
PlayerDetections = new ConcurrentDictionary<int, Detection>();
|
||||
ServerStatistics = st;
|
||||
Server = sv;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ using SharedLibrary.Interfaces;
|
||||
using SharedLibrary.Objects;
|
||||
using SharedLibrary.Services;
|
||||
using StatsPlugin.Models;
|
||||
using SharedLibrary.Commands;
|
||||
|
||||
namespace StatsPlugin.Helpers
|
||||
{
|
||||
@ -75,7 +76,7 @@ namespace StatsPlugin.Helpers
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.WriteWarning($"Could not add server to ServerStats - {e.Message}");
|
||||
Log.WriteError($"Could not add server to ServerStats - {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,9 +85,17 @@ namespace StatsPlugin.Helpers
|
||||
/// </summary>
|
||||
/// <param name="pl">Player to add/retrieve stats for</param>
|
||||
/// <returns>EFClientStatistic of specified player</returns>
|
||||
public EFClientStatistics AddPlayer(Player pl)
|
||||
public async Task<EFClientStatistics> AddPlayer(Player pl)
|
||||
{
|
||||
Log.WriteInfo($"Adding {pl} to stats");
|
||||
int serverId = pl.CurrentServer.GetHashCode();
|
||||
|
||||
if (!Servers.ContainsKey(serverId))
|
||||
{
|
||||
Log.WriteError($"[Stats::AddPlayer] Server with id {serverId} could not be found");
|
||||
return null;
|
||||
}
|
||||
|
||||
var playerStats = Servers[serverId].PlayerStats;
|
||||
var statsSvc = ContextThreads[serverId];
|
||||
|
||||
@ -105,33 +114,50 @@ namespace StatsPlugin.Helpers
|
||||
ServerId = serverId,
|
||||
Skill = 0.0,
|
||||
SPM = 0.0,
|
||||
HitLocations = Enum.GetValues(typeof(IW4Info.HitLocation)).OfType<IW4Info.HitLocation>().Select(hl => new EFHitLocationCount()
|
||||
{
|
||||
Active = true,
|
||||
HitCount = 0,
|
||||
Location = hl
|
||||
})
|
||||
.ToList()
|
||||
};
|
||||
|
||||
clientStats = statsSvc.ClientStatSvc.Insert(clientStats);
|
||||
await statsSvc.ClientStatSvc.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// migration for previous existing stats
|
||||
else if (clientStats.HitLocations.Count == 0)
|
||||
{
|
||||
clientStats.HitLocations = Enum.GetValues(typeof(IW4Info.HitLocation)).OfType<IW4Info.HitLocation>().Select(hl => new EFHitLocationCount()
|
||||
{
|
||||
Active = true,
|
||||
HitCount = 0,
|
||||
Location = hl
|
||||
})
|
||||
.ToList();
|
||||
await statsSvc.ClientStatSvc.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// set these on connecting
|
||||
clientStats.LastActive = DateTime.UtcNow;
|
||||
clientStats.LastStatCalculation = DateTime.UtcNow;
|
||||
clientStats.SessionScore = pl.Score;
|
||||
|
||||
lock (playerStats)
|
||||
if (playerStats.ContainsKey(pl.ClientId))
|
||||
{
|
||||
if (playerStats.ContainsKey(pl.ClientNumber))
|
||||
{
|
||||
Log.WriteWarning($"Duplicate clientnumber in stats {pl.ClientId} vs {playerStats[pl.ClientNumber].ClientId}");
|
||||
playerStats.Remove(pl.ClientNumber);
|
||||
}
|
||||
playerStats.Add(pl.ClientNumber, clientStats);
|
||||
Log.WriteWarning($"Duplicate ClientId in stats {pl.ClientId} vs {playerStats[pl.ClientId].ClientId}");
|
||||
playerStats.TryRemove(pl.ClientId, out EFClientStatistics removedValue);
|
||||
}
|
||||
playerStats.TryAdd(pl.ClientId, clientStats);
|
||||
|
||||
var detectionStats = Servers[serverId].PlayerDetections;
|
||||
lock (detectionStats)
|
||||
{
|
||||
if (detectionStats.ContainsKey(pl.ClientNumber))
|
||||
detectionStats.Remove(pl.ClientNumber);
|
||||
|
||||
detectionStats.Add(pl.ClientNumber, new Cheat.Detection(Log));
|
||||
}
|
||||
if (detectionStats.ContainsKey(pl.ClientId))
|
||||
detectionStats.TryRemove(pl.ClientId, out Cheat.Detection removedValue);
|
||||
|
||||
detectionStats.TryAdd(pl.ClientId, new Cheat.Detection(Log));
|
||||
|
||||
return clientStats;
|
||||
}
|
||||
@ -143,19 +169,27 @@ namespace StatsPlugin.Helpers
|
||||
/// <returns></returns>
|
||||
public async Task RemovePlayer(Player pl)
|
||||
{
|
||||
Log.WriteInfo($"Removing {pl} from stats");
|
||||
|
||||
int serverId = pl.CurrentServer.GetHashCode();
|
||||
var playerStats = Servers[serverId].PlayerStats;
|
||||
var detectionStats = Servers[serverId].PlayerDetections;
|
||||
var serverStats = Servers[serverId].ServerStatistics;
|
||||
var statsSvc = ContextThreads[serverId];
|
||||
|
||||
if (!playerStats.ContainsKey(pl.ClientId))
|
||||
{
|
||||
Log.WriteWarning($"Client disconnecting not in stats {pl}");
|
||||
return;
|
||||
}
|
||||
|
||||
// get individual client's stats
|
||||
var clientStats = playerStats[pl.ClientNumber];
|
||||
var clientStats = playerStats[pl.ClientId];
|
||||
// sync their score
|
||||
clientStats.SessionScore = pl.Score;
|
||||
// remove the client from the stats dictionary as they're leaving
|
||||
lock (playerStats)
|
||||
playerStats.Remove(pl.ClientNumber);
|
||||
lock (detectionStats)
|
||||
detectionStats.Remove(pl.ClientNumber);
|
||||
playerStats.TryRemove(pl.ClientId, out EFClientStatistics removedValue);
|
||||
detectionStats.TryRemove(pl.ClientId, out Cheat.Detection removedValue2);
|
||||
|
||||
// sync their stats before they leave
|
||||
UpdateStats(clientStats);
|
||||
@ -174,16 +208,7 @@ namespace StatsPlugin.Helpers
|
||||
public async Task AddScriptKill(Player attacker, Player victim, int serverId, string map, string hitLoc, string type,
|
||||
string damage, string weapon, string killOrigin, string deathOrigin)
|
||||
{
|
||||
await AddStandardKill(attacker, victim);
|
||||
|
||||
if (victim == null)
|
||||
{
|
||||
Log.WriteError($"[AddScriptKill] Victim is null");
|
||||
return;
|
||||
}
|
||||
|
||||
var statsSvc = ContextThreads[serverId];
|
||||
var playerDetection = Servers[serverId].PlayerDetections[attacker.ClientNumber];
|
||||
|
||||
var kill = new EFClientKill()
|
||||
{
|
||||
@ -200,30 +225,88 @@ namespace StatsPlugin.Helpers
|
||||
Weapon = ParseEnum<IW4Info.WeaponName>.Get(weapon, typeof(IW4Info.WeaponName))
|
||||
};
|
||||
|
||||
playerDetection.ProcessKill(kill);
|
||||
if (kill.DeathType == IW4Info.MeansOfDeath.MOD_SUICIDE &&
|
||||
kill.Damage == 10000)
|
||||
{
|
||||
// suicide by switching teams so let's not count it against them
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
await AddStandardKill(attacker, victim);
|
||||
|
||||
statsSvc.KillStatsSvc.Insert(kill);
|
||||
await statsSvc.KillStatsSvc.SaveChangesAsync();
|
||||
var playerDetection = Servers[serverId].PlayerDetections[attacker.ClientId];
|
||||
var playerStats = Servers[serverId].PlayerStats[attacker.ClientId];
|
||||
|
||||
// increment their hit count
|
||||
if (kill.DeathType == IW4Info.MeansOfDeath.MOD_PISTOL_BULLET ||
|
||||
kill.DeathType == IW4Info.MeansOfDeath.MOD_RIFLE_BULLET)
|
||||
{
|
||||
playerStats.HitLocations.Single(hl => hl.Location == kill.HitLoc).HitCount += 1;
|
||||
await statsSvc.ClientStatSvc.SaveChangesAsync();
|
||||
}
|
||||
|
||||
//statsSvc.KillStatsSvc.Insert(kill);
|
||||
//await statsSvc.KillStatsSvc.SaveChangesAsync();
|
||||
|
||||
async Task executePenalty(Cheat.DetectionPenaltyResult penalty)
|
||||
{
|
||||
switch (penalty.ClientPenalty)
|
||||
{
|
||||
case Penalty.PenaltyType.Ban:
|
||||
await attacker.Ban("You appear to be cheating", new Player() { ClientId = 1 });
|
||||
break;
|
||||
case Penalty.PenaltyType.Flag:
|
||||
if (attacker.Level != Player.Permission.User)
|
||||
break;
|
||||
var flagCmd = new CFlag();
|
||||
await flagCmd.ExecuteAsync(new Event(Event.GType.Flag, $"{(int)penalty.Bone}-{Math.Round(penalty.RatioAmount, 2).ToString()}@{penalty.KillCount}", new Player()
|
||||
{
|
||||
ClientId = 1,
|
||||
Level = Player.Permission.Console,
|
||||
ClientNumber = -1,
|
||||
CurrentServer = attacker.CurrentServer
|
||||
}, attacker, attacker.CurrentServer));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await executePenalty(playerDetection.ProcessKill(kill));
|
||||
await executePenalty(playerDetection.ProcessTotalRatio(playerStats));
|
||||
}
|
||||
|
||||
public async Task AddStandardKill(Player attacker, Player victim)
|
||||
{
|
||||
int serverId = attacker.CurrentServer.GetHashCode();
|
||||
var attackerStats = Servers[serverId].PlayerStats[attacker.ClientNumber];
|
||||
|
||||
if (victim == null)
|
||||
EFClientStatistics attackerStats = null;
|
||||
try
|
||||
{
|
||||
Log.WriteError($"[AddStandardKill] Victim is null");
|
||||
attackerStats = Servers[serverId].PlayerStats[attacker.ClientId];
|
||||
}
|
||||
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
Log.WriteError($"[Stats::AddStandardKill] kill attacker ClientId is invalid {attacker.ClientId}-{attacker}");
|
||||
return;
|
||||
}
|
||||
|
||||
var victimStats = Servers[serverId].PlayerStats[victim.ClientNumber];
|
||||
EFClientStatistics victimStats = null;
|
||||
try
|
||||
{
|
||||
victimStats = Servers[serverId].PlayerStats[victim.ClientId];
|
||||
}
|
||||
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
Log.WriteError($"[Stats::AddStandardKill] kill victim ClientId is invalid {victim.ClientId}-{victim}");
|
||||
return;
|
||||
}
|
||||
|
||||
// update the total stats
|
||||
Servers[serverId].ServerStatistics.TotalKills += 1;
|
||||
|
||||
attackerStats.SessionScore = attacker.Score;
|
||||
victimStats.SessionScore = victim.Score;
|
||||
|
||||
// calculate for the clients
|
||||
CalculateKill(attackerStats, victimStats);
|
||||
|
||||
@ -292,9 +375,7 @@ namespace StatsPlugin.Helpers
|
||||
return clientStats;
|
||||
|
||||
// calculate the players Score Per Minute for the current session
|
||||
int currentScore = Manager.GetActiveClients()
|
||||
.First(c => c.ClientId == clientStats.ClientId)
|
||||
.Score;
|
||||
int currentScore = clientStats.SessionScore;
|
||||
double killSPM = currentScore / (timeSinceLastCalc * 60.0);
|
||||
|
||||
// calculate how much the KDR should weigh
|
||||
@ -350,6 +431,25 @@ namespace StatsPlugin.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetKillstreaks(int serverId)
|
||||
{
|
||||
var serverStats = Servers[serverId];
|
||||
foreach (var stat in serverStats.PlayerStats.Values)
|
||||
{
|
||||
stat.KillStreak = 0;
|
||||
stat.DeathStreak = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetStats(int clientId, int serverId)
|
||||
{
|
||||
var stats = Servers[serverId].PlayerStats[clientId];
|
||||
stats.Kills = 0;
|
||||
stats.Deaths = 0;
|
||||
stats.SPM = 0;
|
||||
stats.Skill = 0;
|
||||
}
|
||||
|
||||
public async Task AddMessageAsync(int clientId, int serverId, string message)
|
||||
{
|
||||
// the web users can have no account
|
||||
|
@ -10,7 +10,6 @@ namespace StatsPlugin.Helpers
|
||||
{
|
||||
public class ThreadSafeStatsService
|
||||
{
|
||||
|
||||
public GenericRepository<EFClientStatistics> ClientStatSvc { get; private set; }
|
||||
public GenericRepository<EFServer> ServerSvc { get; private set; }
|
||||
public GenericRepository<EFClientKill> KillStatsSvc { get; private set; }
|
||||
|
@ -1358,7 +1358,9 @@ namespace StatsPlugin
|
||||
m40a3_mp,
|
||||
peacekeeper_mp,
|
||||
dragunov_mp,
|
||||
cobra_player_minigun_mp
|
||||
cobra_player_minigun_mp,
|
||||
destructible_car,
|
||||
sentry_minigun_mp
|
||||
}
|
||||
|
||||
public enum MapName
|
||||
|
@ -24,7 +24,9 @@ namespace StatsPlugin.Models
|
||||
public int Kills { get; set; }
|
||||
[Required]
|
||||
public int Deaths { get; set; }
|
||||
[Required]
|
||||
|
||||
public virtual ICollection<EFHitLocationCount> HitLocations { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public double KDR
|
||||
{
|
||||
@ -51,5 +53,7 @@ namespace StatsPlugin.Models
|
||||
public int LastScore { get; set; }
|
||||
[NotMapped]
|
||||
public DateTime LastActive { get; set; }
|
||||
[NotMapped]
|
||||
public int SessionScore { get; set; }
|
||||
}
|
||||
}
|
||||
|
21
Plugins/SimpleStats/Models/EFHitLocationCount.cs
Normal file
21
Plugins/SimpleStats/Models/EFHitLocationCount.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using SharedLibrary.Database.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StatsPlugin.Models
|
||||
{
|
||||
public class EFHitLocationCount : SharedEntity
|
||||
{
|
||||
[Key]
|
||||
public int HitLocationCountId { get; set; }
|
||||
[Required]
|
||||
public IW4Info.HitLocation Location { get; set; }
|
||||
[Required]
|
||||
public int HitCount { get; set; }
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@ namespace StatsPlugin
|
||||
|
||||
public string Author => "RaidMax";
|
||||
|
||||
private StatManager Manager;
|
||||
public static StatManager Manager { get; private set; }
|
||||
private IManager ServerManager;
|
||||
|
||||
public async Task OnEventAsync(Event E, Server S)
|
||||
@ -35,19 +35,20 @@ namespace StatsPlugin
|
||||
case Event.GType.Stop:
|
||||
break;
|
||||
case Event.GType.Connect:
|
||||
Manager.AddPlayer(E.Origin);
|
||||
await Manager.AddPlayer(E.Origin);
|
||||
break;
|
||||
case Event.GType.Disconnect:
|
||||
await Manager.RemovePlayer(E.Origin);
|
||||
break;
|
||||
case Event.GType.Say:
|
||||
if (E.Data != string.Empty && E.Data.Trim().Length > 0 && E.Data.Trim()[0] != '!')
|
||||
if (E.Data != string.Empty && E.Data.Trim().Length > 0 && E.Message.Trim()[0] != '!' && E.Origin.ClientId > 1)
|
||||
await Manager.AddMessageAsync(E.Origin.ClientId, E.Owner.GetHashCode(), E.Data);
|
||||
break;
|
||||
case Event.GType.MapChange:
|
||||
Manager.ResetKillstreaks(S.GetHashCode());
|
||||
await Manager.Sync(S);
|
||||
break;
|
||||
case Event.GType.MapEnd:
|
||||
await Manager.Sync(S);
|
||||
break;
|
||||
case Event.GType.Broadcast:
|
||||
break;
|
||||
@ -92,6 +93,25 @@ namespace StatsPlugin
|
||||
double kdr = Math.Round(kills / (double)deaths, 2);
|
||||
double skill = Math.Round(clientStats.Sum(c => c.Skill) / clientStats.Count, 2);
|
||||
|
||||
double chestRatio = 0;
|
||||
double abdomenRatio = 0;
|
||||
double chestAbdomenRatio = 0;
|
||||
|
||||
if (clientStats.FirstOrDefault()?.HitLocations.Count > 0)
|
||||
{
|
||||
chestRatio = Math.Round(clientStats.Where(c => c.HitLocations.Count > 0).Sum(c =>
|
||||
c.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.torso_upper).HitCount) /
|
||||
(double)clientStats.Where(c => c.HitLocations.Count > 0)
|
||||
.Sum(c => c.HitLocations.Where(hl => hl.Location != IW4Info.HitLocation.none).Sum(f => f.HitCount)), 2);
|
||||
|
||||
abdomenRatio = Math.Round(clientStats.Where(c => c.HitLocations.Count > 0).Sum(c =>
|
||||
c.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.torso_lower).HitCount) /
|
||||
(double)clientStats.Where(c => c.HitLocations.Count > 0).Sum(c => c.HitLocations.Where(hl => hl.Location != IW4Info.HitLocation.none).Sum(f => f.HitCount)), 2);
|
||||
|
||||
chestAbdomenRatio = Math.Round(clientStats.Where(c => c.HitLocations.Count > 0).Sum(cs => cs.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.torso_upper).HitCount) /
|
||||
(double)clientStats.Where(c => c.HitLocations.Count > 0).Sum(cs => cs.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.torso_lower).HitCount), 2);
|
||||
}
|
||||
|
||||
return new List<ProfileMeta>()
|
||||
{
|
||||
new ProfileMeta()
|
||||
@ -113,6 +133,24 @@ namespace StatsPlugin
|
||||
{
|
||||
Key = "Skill",
|
||||
Value = skill
|
||||
},
|
||||
new ProfileMeta()
|
||||
{
|
||||
Key = "Chest Ratio",
|
||||
Value = chestRatio,
|
||||
Sensitive = true
|
||||
},
|
||||
new ProfileMeta()
|
||||
{
|
||||
Key = "Abdomen Ratio",
|
||||
Value = abdomenRatio,
|
||||
Sensitive = true
|
||||
},
|
||||
new ProfileMeta()
|
||||
{
|
||||
Key = "Chest To Abdomen Ratio",
|
||||
Value = chestAbdomenRatio,
|
||||
Sensitive = true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -119,6 +119,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Cheat\Detection.cs" />
|
||||
<Compile Include="Cheat\DetectionPenaltyResult.cs" />
|
||||
<Compile Include="Cheat\Thresholds.cs" />
|
||||
<Compile Include="Commands\ResetStats.cs" />
|
||||
<Compile Include="Commands\TopStats.cs" />
|
||||
@ -131,6 +132,7 @@
|
||||
<Compile Include="MinimapConfig.cs" />
|
||||
<Compile Include="Models\EFClientKill.cs" />
|
||||
<Compile Include="Models\EFClientMessage.cs" />
|
||||
<Compile Include="Models\EFHitLocationCount.cs" />
|
||||
<Compile Include="Models\EFServer.cs" />
|
||||
<Compile Include="Models\EFClientStatistics.cs" />
|
||||
<Compile Include="Models\EFServerStatistics.cs" />
|
||||
@ -146,7 +148,6 @@
|
||||
<ProjectReference Include="..\..\SharedLibrary\SharedLibrary.csproj">
|
||||
<Project>{d51eeceb-438a-47da-870f-7d7b41bc24d6}</Project>
|
||||
<Name>SharedLibrary</Name>
|
||||
<Private>False</Private>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
|
@ -126,7 +126,7 @@ namespace Welcome_Plugin
|
||||
msg = msg.Replace("{{ClientLevel}}", Utilities.ConvertLevelToColor(joining.Level));
|
||||
try
|
||||
{
|
||||
CountryLookupProj.CountryLookup CLT = new CountryLookupProj.CountryLookup("Plugins/GeoIP.dat");
|
||||
CountryLookupProj.CountryLookup CLT = new CountryLookupProj.CountryLookup($"{Utilities.OperatingDirectory}Plugins{System.IO.Path.DirectorySeparatorChar}GeoIP.dat");
|
||||
msg = msg.Replace("{{ClientLocation}}", CLT.LookupCountryName(joining.IPAddressString));
|
||||
}
|
||||
|
||||
|
@ -1,2 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration />
|
||||
<configuration>
|
||||
<configSections>
|
||||
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
|
||||
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
|
||||
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
|
||||
</configSections>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/>
|
||||
</startup>
|
||||
<runtime>
|
||||
<assemblyBinding>
|
||||
<!-- <probing privatePath="lib"/>-->
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
<entityFramework>
|
||||
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlCeConnectionFactory, EntityFramework">
|
||||
<parameters>
|
||||
<parameter value="System.Data.SqlServerCe.4.0" />
|
||||
</parameters>
|
||||
</defaultConnectionFactory>
|
||||
<providers>
|
||||
<provider invariantName="System.Data.SqlServerCe.4.0" type="System.Data.Entity.SqlServerCompact.SqlCeProviderServices, EntityFramework.SqlServerCompact" />
|
||||
</providers>
|
||||
</entityFramework>
|
||||
<system.data>
|
||||
<DbProviderFactories>
|
||||
<remove invariant="System.Data.SqlServerCe.4.0" />
|
||||
<add name="Microsoft SQL Server Compact Data Provider 4.0" invariant="System.Data.SqlServerCe.4.0" description=".NET Framework Data Provider for Microsoft SQL Server Compact" type="System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" />
|
||||
</DbProviderFactories>
|
||||
</system.data>
|
||||
<connectionStrings>
|
||||
<add name="DefaultConnection"
|
||||
providerName="System.Data.SqlServerCe.4.0"
|
||||
connectionString="Data Source=|DataDirectory|\Database.sdf"/>
|
||||
</connectionStrings>
|
||||
</configuration>
|
||||
|
@ -11,6 +11,7 @@ using SharedLibrary.Database;
|
||||
using System.Data.Entity;
|
||||
using SharedLibrary.Database.Models;
|
||||
using SharedLibrary.Services;
|
||||
using SharedLibrary.Exceptions;
|
||||
|
||||
namespace SharedLibrary.Commands
|
||||
{
|
||||
@ -170,6 +171,9 @@ namespace SharedLibrary.Commands
|
||||
{
|
||||
String Message = Utilities.RemoveWords(E.Data, 1).Trim();
|
||||
var length = E.Data.Split(' ')[0].ToLower().ParseTimespan();
|
||||
if (length.TotalHours >= 1 && length.TotalHours < 2)
|
||||
Message = E.Data;
|
||||
|
||||
|
||||
if (E.Origin.Level > E.Target.Level)
|
||||
{
|
||||
@ -428,8 +432,8 @@ namespace SharedLibrary.Commands
|
||||
|
||||
if (newPerm > Player.Permission.Banned)
|
||||
{
|
||||
var ActiveClient = E.Owner.Manager.GetActiveClients().FirstOrDefault(p => p.NetworkId == E.Target.NetworkId);
|
||||
|
||||
var ActiveClient = E.Owner.Manager.GetActiveClients()
|
||||
.FirstOrDefault(p => p.NetworkId == E.Target.NetworkId);
|
||||
|
||||
if (ActiveClient != null)
|
||||
{
|
||||
@ -444,7 +448,6 @@ namespace SharedLibrary.Commands
|
||||
}
|
||||
|
||||
await E.Origin.Tell($"{E.Target.Name} was successfully promoted!");
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
@ -485,17 +488,22 @@ namespace SharedLibrary.Commands
|
||||
|
||||
public override async Task ExecuteAsync(Event E)
|
||||
{
|
||||
int numOnline = 0;
|
||||
for (int i = 0; i < E.Owner.Players.Count; i++)
|
||||
{
|
||||
var P = E.Owner.Players[i];
|
||||
if (P != null && P.Level > Player.Permission.Flagged && !P.Masked)
|
||||
{
|
||||
numOnline++;
|
||||
if (E.Message[0] == '@')
|
||||
await E.Owner.Broadcast(String.Format("[^3{0}^7] {1}", Utilities.ConvertLevelToColor(P.Level), P.Name));
|
||||
else
|
||||
await E.Origin.Tell(String.Format("[^3{0}^7] {1}", Utilities.ConvertLevelToColor(P.Level), P.Name));
|
||||
}
|
||||
}
|
||||
|
||||
if (numOnline == 0)
|
||||
await E.Origin.Tell("No visible administrators online");
|
||||
}
|
||||
}
|
||||
|
||||
@ -519,14 +527,14 @@ namespace SharedLibrary.Commands
|
||||
{
|
||||
if (m.Name.ToLower() == newMap || m.Alias.ToLower() == newMap)
|
||||
{
|
||||
await E.Owner.Broadcast("Changing to map ^2" + m.Alias);
|
||||
await E.Owner.Broadcast($"Changing to map ^5{m.Alias}");
|
||||
Task.Delay(5000).Wait();
|
||||
await E.Owner.LoadMap(m.Name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await E.Owner.Broadcast("Attempting to change to unknown map ^1" + newMap);
|
||||
await E.Owner.Broadcast($"Attempting to change to unknown map ^5{newMap}");
|
||||
Task.Delay(5000).Wait();
|
||||
await E.Owner.LoadMap(newMap);
|
||||
}
|
||||
@ -927,7 +935,7 @@ namespace SharedLibrary.Commands
|
||||
}
|
||||
})
|
||||
{ }
|
||||
|
||||
|
||||
public override async Task ExecuteAsync(Event E)
|
||||
{
|
||||
int inactiveDays = 30;
|
||||
@ -961,7 +969,89 @@ namespace SharedLibrary.Commands
|
||||
inactiveUsers.ForEach(c => c.Level = Player.Permission.User);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
await E.Origin.Tell($"Pruned {inactiveUsers.Count} inactive privileged users");
|
||||
await E.Origin.Tell($"Pruned {inactiveUsers.Count} inactive privileged users");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class CRestartServer : Command
|
||||
{
|
||||
public CRestartServer() : base("restartserver", "restart the server", "restart", Player.Permission.Administrator, false)
|
||||
{
|
||||
}
|
||||
|
||||
public override async Task ExecuteAsync(Event E)
|
||||
{
|
||||
var gameserverProcesses = System.Diagnostics.Process.GetProcessesByName("iw4x");
|
||||
var currentProcess = gameserverProcesses.FirstOrDefault(g => g.GetCommandLine().Contains($"+set net_port {E.Owner.GetPort()}"));
|
||||
|
||||
if (currentProcess == null)
|
||||
{
|
||||
await E.Origin.Tell("Could not find running/stalled instance of IW4x");
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
var commandLine = currentProcess.GetCommandLine();
|
||||
// attempt to kill it natively
|
||||
try
|
||||
{
|
||||
if (!E.Owner.Throttled)
|
||||
{
|
||||
// await E.Owner.ExecuteCommandAsync("quit");
|
||||
}
|
||||
}
|
||||
|
||||
catch (NetworkException)
|
||||
{
|
||||
await E.Origin.Tell("Unable to cleanly shutdown server, forcing");
|
||||
}
|
||||
|
||||
if (!currentProcess.HasExited)
|
||||
{
|
||||
try
|
||||
{
|
||||
currentProcess.Kill();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await E.Origin.Tell("Could not kill IW4x process");
|
||||
E.Owner.Logger.WriteDebug("Unable to kill process");
|
||||
E.Owner.Logger.WriteDebug($"Exception: {e.Message}");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
System.Diagnostics.Process process = new System.Diagnostics.Process();
|
||||
process.StartInfo.UseShellExecute = false;
|
||||
#if !DEBUG
|
||||
process.StartInfo.WorkingDirectory = E.Owner.WorkingDirectory;
|
||||
#else
|
||||
process.StartInfo.WorkingDirectory = @"C:\Users\User\Desktop\MW2";
|
||||
#endif
|
||||
process.StartInfo.FileName = $"{process.StartInfo.WorkingDirectory}\\iw4x.exe";
|
||||
process.StartInfo.Arguments = commandLine.Substring(6);
|
||||
process.StartInfo.UserName = E.Owner.Config.RestartUsername;
|
||||
|
||||
var pw = new System.Security.SecureString();
|
||||
foreach (char c in E.Owner.Config.RestartPassword)
|
||||
pw.AppendChar(c);
|
||||
|
||||
process.StartInfo.Password = pw;
|
||||
process.Start();
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
await E.Origin.Tell("Could not start the IW4x process");
|
||||
E.Owner.Logger.WriteDebug("Unable to start process");
|
||||
E.Owner.Logger.WriteDebug($"Exception: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ using System.Data.Entity.ModelConfiguration.Conventions;
|
||||
using System.Reflection;
|
||||
using System.Data.Entity.Infrastructure;
|
||||
using System.Data.Entity.SqlServerCompact;
|
||||
using System.IO;
|
||||
|
||||
namespace SharedLibrary.Database
|
||||
{
|
||||
@ -23,7 +24,8 @@ namespace SharedLibrary.Database
|
||||
|
||||
public DatabaseContext() : base("DefaultConnection")
|
||||
{
|
||||
System.Data.Entity.Database.SetInitializer(new Initializer());
|
||||
System.Data.Entity.Database.SetInitializer(new MigrateDatabaseToLatestVersion<DatabaseContext, Migrations.Configuration>());
|
||||
//Database.CreateIfNotExists();
|
||||
Configuration.LazyLoadingEnabled = true;
|
||||
}
|
||||
|
||||
@ -50,7 +52,12 @@ namespace SharedLibrary.Database
|
||||
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
|
||||
|
||||
// https://aleemkhan.wordpress.com/2013/02/28/dynamically-adding-dbset-properties-in-dbcontext-for-entity-framework-code-first/
|
||||
//string dir = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + Path.DirectorySeparatorChar;
|
||||
#if !DEBUG
|
||||
foreach (string dllPath in System.IO.Directory.GetFiles($"{Utilities.OperatingDirectory}Plugins"))
|
||||
#else
|
||||
foreach (string dllPath in System.IO.Directory.GetFiles(@"C:\Users\User\Desktop\stuff\IW4M-Admin\IW4M-Admin\WebfrontCore\bin\x86\Debug\Plugins"))
|
||||
#endif
|
||||
{
|
||||
Assembly library;
|
||||
try
|
||||
@ -64,7 +71,7 @@ namespace SharedLibrary.Database
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach(var type in library.ExportedTypes)
|
||||
foreach (var type in library.ExportedTypes)
|
||||
{
|
||||
if (type.IsClass && type.IsSubclassOf(typeof(SharedEntity)))
|
||||
{
|
||||
|
@ -1,42 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Entity;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibrary.Database
|
||||
{
|
||||
public class Initializer : DropCreateDatabaseIfModelChanges<DatabaseContext>
|
||||
{
|
||||
protected override void Seed(DatabaseContext context)
|
||||
{
|
||||
var aliasLink = new Models.EFAliasLink();
|
||||
|
||||
var currentAlias = new Models.EFAlias()
|
||||
{
|
||||
Active = true,
|
||||
DateAdded = DateTime.UtcNow,
|
||||
IPAddress = 0,
|
||||
Name = "IW4MAdmin",
|
||||
Link = aliasLink
|
||||
};
|
||||
|
||||
context.Clients.Add(new Models.EFClient()
|
||||
{
|
||||
Active = false,
|
||||
Connections = 0,
|
||||
FirstConnection = DateTime.UtcNow,
|
||||
LastConnection = DateTime.UtcNow,
|
||||
Level = Objects.Player.Permission.Console,
|
||||
Masked = true,
|
||||
NetworkId = 0,
|
||||
AliasLink = aliasLink,
|
||||
CurrentAlias = currentAlias
|
||||
});
|
||||
|
||||
base.Seed(context);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
26
SharedLibrary/Database/Repair.cs
Normal file
26
SharedLibrary/Database/Repair.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using SharedLibrary.Interfaces;
|
||||
using System;
|
||||
using System.Data.SqlServerCe;
|
||||
|
||||
namespace SharedLibrary.Database
|
||||
{
|
||||
public class Repair
|
||||
{
|
||||
public static void Run(ILogger log)
|
||||
{
|
||||
SqlCeEngine engine = new SqlCeEngine(@"Data Source=|DataDirectory|\Database.sdf");
|
||||
if (false == engine.Verify())
|
||||
{
|
||||
log.WriteWarning("Database is corrupted.");
|
||||
try
|
||||
{
|
||||
engine.Repair(null, RepairOption.DeleteCorruptedRows);
|
||||
}
|
||||
catch (SqlCeException ex)
|
||||
{
|
||||
log.WriteError(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibrary.Dtos
|
||||
{
|
||||
public class PenaltyInfo
|
||||
public class PenaltyInfo : SharedInfo
|
||||
{
|
||||
public string OffenderName { get; set; }
|
||||
public int OffenderId { get; set; }
|
||||
|
@ -7,9 +7,10 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibrary.Dtos
|
||||
{
|
||||
public class ProfileMeta
|
||||
public class ProfileMeta : SharedInfo
|
||||
{
|
||||
public DateTime When { get; set; }
|
||||
public bool Sensitive { get; set; }
|
||||
public string WhenString => Utilities.GetTimePassed(When, false);
|
||||
public string Key { get; set; }
|
||||
public dynamic Value { get; set; }
|
||||
|
8
SharedLibrary/Dtos/SharedInfo.cs
Normal file
8
SharedLibrary/Dtos/SharedInfo.cs
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
namespace SharedLibrary.Dtos
|
||||
{
|
||||
public class SharedInfo
|
||||
{
|
||||
public bool Sensitive { get; set; }
|
||||
}
|
||||
}
|
@ -2,12 +2,13 @@
|
||||
using SharedLibrary.Objects;
|
||||
using SharedLibrary.Database.Models;
|
||||
using SharedLibrary.Services;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibrary.Interfaces
|
||||
{
|
||||
public interface IManager
|
||||
{
|
||||
void Init();
|
||||
Task Init();
|
||||
void Start();
|
||||
void Stop();
|
||||
ILogger GetLogger();
|
||||
@ -15,7 +16,7 @@ namespace SharedLibrary.Interfaces
|
||||
IList<Command> GetCommands();
|
||||
IList<Helpers.MessageToken> GetMessageTokens();
|
||||
IList<Player> GetActiveClients();
|
||||
ClientService GetClientService();
|
||||
ClientService GetClientService();
|
||||
AliasService GetAliasService();
|
||||
PenaltyService GetPenaltyService();
|
||||
}
|
||||
|
29
SharedLibrary/Migrations/201803030146021_Intial.Designer.cs
generated
Normal file
29
SharedLibrary/Migrations/201803030146021_Intial.Designer.cs
generated
Normal file
@ -0,0 +1,29 @@
|
||||
// <auto-generated />
|
||||
namespace SharedLibrary.Migrations
|
||||
{
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Data.Entity.Migrations;
|
||||
using System.Data.Entity.Migrations.Infrastructure;
|
||||
using System.Resources;
|
||||
|
||||
[GeneratedCode("EntityFramework.Migrations", "6.2.0-61023")]
|
||||
public sealed partial class Intial : IMigrationMetadata
|
||||
{
|
||||
private readonly ResourceManager Resources = new ResourceManager(typeof(Intial));
|
||||
|
||||
string IMigrationMetadata.Id
|
||||
{
|
||||
get { return "201803030146021_Intial"; }
|
||||
}
|
||||
|
||||
string IMigrationMetadata.Source
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
string IMigrationMetadata.Target
|
||||
{
|
||||
get { return Resources.GetString("Target"); }
|
||||
}
|
||||
}
|
||||
}
|
16
SharedLibrary/Migrations/201803030146021_Intial.cs
Normal file
16
SharedLibrary/Migrations/201803030146021_Intial.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace SharedLibrary.Migrations
|
||||
{
|
||||
using System;
|
||||
using System.Data.Entity.Migrations;
|
||||
|
||||
public partial class Intial : DbMigration
|
||||
{
|
||||
public override void Up()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
126
SharedLibrary/Migrations/201803030146021_Intial.resx
Normal file
126
SharedLibrary/Migrations/201803030146021_Intial.resx
Normal file
File diff suppressed because one or more lines are too long
52
SharedLibrary/Migrations/Configuration.cs
Normal file
52
SharedLibrary/Migrations/Configuration.cs
Normal file
@ -0,0 +1,52 @@
|
||||
namespace SharedLibrary.Migrations
|
||||
{
|
||||
using System;
|
||||
using System.Data.Entity;
|
||||
using System.Data.Entity.Migrations;
|
||||
using System.Linq;
|
||||
|
||||
internal sealed class Configuration : DbMigrationsConfiguration<SharedLibrary.Database.DatabaseContext>
|
||||
{
|
||||
public Configuration()
|
||||
{
|
||||
AutomaticMigrationsEnabled = true;
|
||||
AutomaticMigrationDataLossAllowed = true;
|
||||
}
|
||||
|
||||
protected override void Seed(SharedLibrary.Database.DatabaseContext context)
|
||||
{
|
||||
context.AliasLinks.AddOrUpdate(new SharedLibrary.Database.Models.EFAliasLink()
|
||||
{
|
||||
AliasLinkId = 1
|
||||
});
|
||||
|
||||
var currentAlias = new SharedLibrary.Database.Models.EFAlias()
|
||||
{
|
||||
AliasId = 1,
|
||||
Active = true,
|
||||
DateAdded = DateTime.UtcNow,
|
||||
IPAddress = 0,
|
||||
Name = "IW4MAdmin",
|
||||
LinkId = 1
|
||||
};
|
||||
|
||||
context.Aliases.AddOrUpdate(currentAlias);
|
||||
|
||||
context.Clients.AddOrUpdate(new SharedLibrary.Database.Models.EFClient()
|
||||
{
|
||||
ClientId = 1,
|
||||
Active = false,
|
||||
Connections = 0,
|
||||
FirstConnection = DateTime.UtcNow,
|
||||
LastConnection = DateTime.UtcNow,
|
||||
Level = Objects.Player.Permission.Console,
|
||||
Masked = true,
|
||||
NetworkId = 0,
|
||||
AliasLinkId = 1,
|
||||
CurrentAliasId = 1,
|
||||
});
|
||||
|
||||
base.Seed(context);
|
||||
}
|
||||
}
|
||||
}
|
@ -66,7 +66,7 @@ namespace SharedLibrary.Network
|
||||
do
|
||||
{
|
||||
ReceiveBuffer = ServerOOBConnection.Receive(ref Endpoint);
|
||||
QueryResponseString.Append(Encoding.ASCII.GetString(ReceiveBuffer).TrimEnd('\0'));
|
||||
QueryResponseString.Append(Encoding.UTF7.GetString(ReceiveBuffer).TrimEnd('\0'));
|
||||
} while (ServerOOBConnection.Available > 0 && ServerOOBConnection.Client.Connected);
|
||||
|
||||
if (QueryResponseString.ToString().Contains("Invalid password"))
|
||||
|
@ -385,6 +385,7 @@ namespace SharedLibrary
|
||||
public string Password { get; private set; }
|
||||
public bool Throttled { get; protected set; }
|
||||
public bool CustomCallback { get; protected set; }
|
||||
public string WorkingDirectory { get; protected set; }
|
||||
|
||||
// Internal
|
||||
protected string IP;
|
||||
|
@ -10,6 +10,8 @@ namespace SharedLibrary
|
||||
public string FtpPrefix;
|
||||
public bool AllowMultipleOwners;
|
||||
public bool AllowTrustedRank;
|
||||
public string RestartUsername;
|
||||
public string RestartPassword;
|
||||
|
||||
public override string Filename()
|
||||
{
|
||||
|
@ -168,6 +168,11 @@ namespace SharedLibrary.Services
|
||||
};
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
client.CurrentAliasId = entity.CurrentAliasId;
|
||||
}
|
||||
|
||||
// set remaining non-navigation properties that may have been updated
|
||||
client.Level = entity.Level;
|
||||
client.LastConnection = entity.LastConnection;
|
||||
|
@ -15,6 +15,21 @@
|
||||
<TargetFrameworkProfile />
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
@ -118,22 +133,10 @@
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="EntityFramework.SqlServerCompact, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\EntityFramework.SqlServerCompact.6.2.0\lib\net45\EntityFramework.SqlServerCompact.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.SqlServer.Compact.4.0.8876.1\lib\net40\System.Data.SqlServerCe.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Management" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
@ -144,19 +147,20 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Database\Importer.cs" />
|
||||
<Compile Include="Database\Initializer.cs" />
|
||||
<Compile Include="Database\DatabaseContext.cs" />
|
||||
<Compile Include="Database\Models\EFAlias.cs" />
|
||||
<Compile Include="Database\Models\EFAliasLink.cs" />
|
||||
<Compile Include="Database\Models\EFClient.cs" />
|
||||
<Compile Include="Database\Models\EFPenalty.cs" />
|
||||
<Compile Include="Database\Models\SharedEntity.cs" />
|
||||
<Compile Include="Database\Repair.cs" />
|
||||
<Compile Include="Dtos\CommandResponseInfo.cs" />
|
||||
<Compile Include="Dtos\PlayerInfo.cs" />
|
||||
<Compile Include="Dtos\ClientInfo.cs" />
|
||||
<Compile Include="Dtos\ProfileMeta.cs" />
|
||||
<Compile Include="Dtos\PenaltyInfo.cs" />
|
||||
<Compile Include="Dtos\ServerInfo.cs" />
|
||||
<Compile Include="Dtos\SharedInfo.cs" />
|
||||
<Compile Include="Exceptions\DatabaseException.cs" />
|
||||
<Compile Include="Helpers\AsyncStatus.cs" />
|
||||
<Compile Include="Commands\NativeCommands.cs" />
|
||||
@ -173,6 +177,11 @@
|
||||
<Compile Include="Interfaces\IManager.cs" />
|
||||
<Compile Include="Interfaces\ISerializable.cs" />
|
||||
<Compile Include="Helpers\MessageToken.cs" />
|
||||
<Compile Include="Migrations\201803030146021_Intial.cs" />
|
||||
<Compile Include="Migrations\201803030146021_Intial.Designer.cs">
|
||||
<DependentUpon>201803030146021_Intial.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Migrations\Configuration.cs" />
|
||||
<Compile Include="Objects\Alias.cs" />
|
||||
<Compile Include="Objects\Penalty.cs" />
|
||||
<Compile Include="Command.cs" />
|
||||
@ -197,13 +206,37 @@
|
||||
<Compile Include="Utilities.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EntityFramework">
|
||||
<Version>6.2.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="EntityFramework.SqlServerCompact">
|
||||
<Version>6.2.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json">
|
||||
<Version>11.0.1</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.5.2">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Microsoft .NET Framework 4.5.2 %28x86 and x64%29</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Migrations\201803030146021_Intial.resx">
|
||||
<DependentUpon>201803030146021_Intial.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)BUILD\lib"
|
||||
@ -218,9 +251,7 @@ if not exist "$(TargetDir)amd64" md "$(TargetDir)amd64"
|
||||
xcopy /Y /I /E "$(TargetDir)*" "$(SolutionDir)BUILD\Lib"</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>if exist "$(SolutionDir)BUILD\Plugins" rmdir /Q /S "$(SolutionDir)BUILD\Plugins"
|
||||
mkdir "$(SolutionDir)BUILD\Plugins"
|
||||
|
||||
<PreBuildEvent>
|
||||
if not exist "$(SolutionDir)BUILD" mkdir "$(SolutionDir)BUILD"
|
||||
if not exist "$(SolutionDir)BUILD\Lib" mkdir "$(SolutionDir)BUILD\Lib"
|
||||
if not exist "$(SolutionDir)BUILD\userraw\scripts" mkdir "$(SolutionDir)BUILD\userraw\scripts"</PreBuildEvent>
|
||||
|
@ -4,11 +4,13 @@ using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Management;
|
||||
|
||||
using SharedLibrary.Objects;
|
||||
using static SharedLibrary.Server;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace SharedLibrary
|
||||
{
|
||||
@ -61,14 +63,14 @@ namespace SharedLibrary
|
||||
int cID = -1;
|
||||
int Ping = -1;
|
||||
Int32.TryParse(playerInfo[2], out Ping);
|
||||
String cName = Utilities.StripColors(responseLine.Substring(46, 18)).Trim();
|
||||
String cName = Encoding.UTF8.GetString(Encoding.Convert(Encoding.UTF7, Encoding.UTF8, Encoding.UTF7.GetBytes(StripColors(responseLine.Substring(46, 18)).Trim())));
|
||||
long npID = Regex.Match(responseLine, @"([a-z]|[0-9]){16}", RegexOptions.IgnoreCase).Value.ConvertLong();
|
||||
int.TryParse(playerInfo[0], out cID);
|
||||
var regex = Regex.Match(responseLine, @"\d+\.\d+\.\d+.\d+\:\d{1,5}");
|
||||
int cIP = regex.Value.Split(':')[0].ConvertToIP();
|
||||
regex = Regex.Match(responseLine, @"[0-9]{1,2}\s+[0-9]+\s+");
|
||||
int score = Int32.Parse(regex.Value.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)[1]);
|
||||
Player P = new Player() { Name = cName, NetworkId = npID, ClientNumber = cID, IPAddress = cIP, Ping = Ping, Score = score};
|
||||
Player P = new Player() { Name = cName, NetworkId = npID, ClientNumber = cID, IPAddress = cIP, Ping = Ping, Score = score };
|
||||
StatusPlayers.Add(P);
|
||||
}
|
||||
}
|
||||
@ -96,8 +98,11 @@ namespace SharedLibrary
|
||||
{
|
||||
if (str == null)
|
||||
return "";
|
||||
return Regex.Replace(str, @"(\^+((?![a-z]|[A-Z]).){0,1})+", "")
|
||||
str = Regex.Replace(str, @"(\^+((?![a-z]|[A-Z]).){0,1})+", "");
|
||||
string str2 = Regex.Match(str, @"(^\/+.*$)|(^.*\/+$)")
|
||||
.Value
|
||||
.Replace("/", " /");
|
||||
return str2.Length > 0 ? str2 : str;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -334,11 +339,6 @@ namespace SharedLibrary
|
||||
return "1 hour";
|
||||
}
|
||||
|
||||
public static string EscapeMarkdown(this string markdownString)
|
||||
{
|
||||
return markdownString.Replace("<", "\\<").Replace(">", "\\>").Replace("|", "\\|");
|
||||
}
|
||||
|
||||
public static Player AsPlayer(this Database.Models.EFClient client)
|
||||
{
|
||||
return client == null ? null : new Player()
|
||||
@ -361,5 +361,35 @@ namespace SharedLibrary
|
||||
CurrentAliasId = client.CurrentAlias.AliasId
|
||||
};
|
||||
}
|
||||
|
||||
/*https://stackoverflow.com/questions/2633628/can-i-get-command-line-arguments-of-other-processes-from-net-c*/
|
||||
// Define an extension method for type System.Process that returns the command
|
||||
// line via WMI.
|
||||
public static string GetCommandLine(this Process process)
|
||||
{
|
||||
string cmdLine = null;
|
||||
using (var searcher = new ManagementObjectSearcher(
|
||||
$"SELECT CommandLine FROM Win32_Process WHERE ProcessId = {process.Id}"))
|
||||
{
|
||||
// By definition, the query returns at most 1 match, because the process
|
||||
// is looked up by ID (which is unique by definition).
|
||||
var matchEnum = searcher.Get().GetEnumerator();
|
||||
if (matchEnum.MoveNext()) // Move to the 1st item.
|
||||
{
|
||||
cmdLine = matchEnum.Current["CommandLine"]?.ToString();
|
||||
}
|
||||
}
|
||||
if (cmdLine == null)
|
||||
{
|
||||
// Not having found a command line implies 1 of 2 exceptions, which the
|
||||
// WMI query masked:
|
||||
// An "Access denied" exception due to lack of privileges.
|
||||
// A "Cannot process request because the process (<pid>) has exited."
|
||||
// exception due to the process having terminated.
|
||||
// We provoke the same exception again simply by accessing process.MainModule.
|
||||
var dummy = process.MainModule; // Provoke exception.
|
||||
}
|
||||
return cmdLine;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,191 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Kayak.Http;
|
||||
using Kayak;
|
||||
|
||||
|
||||
namespace IW4MAdmin
|
||||
{
|
||||
class Scheduler : ISchedulerDelegate
|
||||
{
|
||||
public void OnException(IScheduler scheduler, Exception e)
|
||||
{
|
||||
// it looks like there's a library error in
|
||||
// Kayak.Http.HttpServerTransactionDelegate.OnError
|
||||
if ((uint)e.HResult == 0x80004003 || (uint)e.InnerException?.HResult == 0x80004003)
|
||||
return;
|
||||
|
||||
ApplicationManager.GetInstance().Logger.WriteWarning("Web service has encountered an error - " + e.Message);
|
||||
ApplicationManager.GetInstance().Logger.WriteDebug($"Stack Trace: {e.StackTrace}");
|
||||
|
||||
if (e.InnerException != null)
|
||||
{
|
||||
ApplicationManager.GetInstance().Logger.WriteDebug($"Inner Exception: {e.InnerException.Message}");
|
||||
ApplicationManager.GetInstance().Logger.WriteDebug($"Inner Stack Trace: {e.InnerException.StackTrace}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void OnStop(IScheduler scheduler)
|
||||
{
|
||||
ApplicationManager.GetInstance().Logger.WriteInfo("Web service has been stopped...");
|
||||
}
|
||||
}
|
||||
|
||||
class Request : IHttpRequestDelegate
|
||||
{
|
||||
public void OnRequest(HttpRequestHead request, IDataProducer requestBody, IHttpResponseDelegate response)
|
||||
{
|
||||
#if DEBUG
|
||||
var logger = ApplicationManager.GetInstance().GetLogger();
|
||||
logger.WriteDebug($"HTTP request {request.Path}");
|
||||
logger.WriteDebug($"QueryString: {request.QueryString}");
|
||||
logger.WriteDebug($"IP: {request.IPAddress}");
|
||||
#endif
|
||||
|
||||
NameValueCollection querySet = new NameValueCollection();
|
||||
|
||||
if (request.QueryString != null)
|
||||
querySet = System.Web.HttpUtility.ParseQueryString(request.QueryString);
|
||||
|
||||
querySet.Set("IP", request.IPAddress);
|
||||
|
||||
try
|
||||
{
|
||||
request.Path = String.IsNullOrEmpty(request.Path) ? "/" : request.Path;
|
||||
SharedLibrary.HttpResponse requestedPage = WebService.GetPage(request.Path, querySet, request.Headers);
|
||||
|
||||
bool binaryContent = requestedPage.BinaryContent != null;
|
||||
if (requestedPage.content != null && requestedPage.content.GetType() != typeof(string))
|
||||
#if !DEBUG
|
||||
requestedPage.content = Newtonsoft.Json.JsonConvert.SerializeObject(requestedPage.content);
|
||||
#else
|
||||
requestedPage.content = Newtonsoft.Json.JsonConvert.SerializeObject(requestedPage.content, Newtonsoft.Json.Formatting.Indented);
|
||||
#endif
|
||||
|
||||
string maxAge = requestedPage.contentType == "application/json" ? "0" : "21600";
|
||||
var headers = new HttpResponseHead()
|
||||
{
|
||||
Status = "200 OK",
|
||||
Headers = new Dictionary<string, string>()
|
||||
{
|
||||
{ "Content-Type", requestedPage.contentType },
|
||||
{ "Content-Length", binaryContent ? requestedPage.BinaryContent.Length.ToString() : requestedPage.content.ToString().Length.ToString() },
|
||||
{ "Access-Control-Allow-Origin", "*" },
|
||||
{ "Cache-Control", $"public,max-age={maxAge}"}
|
||||
}
|
||||
};
|
||||
|
||||
foreach (var key in requestedPage.additionalHeaders.Keys)
|
||||
headers.Headers.Add(key, requestedPage.additionalHeaders[key]);
|
||||
if (!binaryContent)
|
||||
response.OnResponse(headers, new BufferedProducer((string)requestedPage.content));
|
||||
else
|
||||
response.OnResponse(headers, new BufferedProducer(requestedPage.BinaryContent));
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e.GetType() == typeof(FormatException))
|
||||
{
|
||||
ApplicationManager.GetInstance().Logger.WriteWarning("Request parameter data format was incorrect");
|
||||
ApplicationManager.GetInstance().Logger.WriteDebug($"Request Path {request.Path}");
|
||||
ApplicationManager.GetInstance().Logger.WriteDebug($"Request Query String {request.QueryString}");
|
||||
response.OnResponse(new HttpResponseHead()
|
||||
{
|
||||
Status = "400 Bad Request",
|
||||
Headers = new Dictionary<string, string>()
|
||||
{
|
||||
{ "Content-Type", "text/html" },
|
||||
{ "Content-Length", "0"},
|
||||
}
|
||||
}, new BufferedProducer(""));
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
ApplicationManager.GetInstance().Logger.WriteError($"Webfront error during request");
|
||||
ApplicationManager.GetInstance().Logger.WriteDebug($"Message: {e.Message}");
|
||||
ApplicationManager.GetInstance().Logger.WriteDebug($"Stack Trace: {e.StackTrace}");
|
||||
|
||||
response.OnResponse(new HttpResponseHead()
|
||||
{
|
||||
Status = "500 Internal Server Error",
|
||||
Headers = new Dictionary<string, string>()
|
||||
{
|
||||
{ "Content-Type", "text/html" },
|
||||
{ "Content-Length", "0"},
|
||||
}
|
||||
}, new BufferedProducer(""));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BufferedProducer : IDataProducer
|
||||
{
|
||||
ArraySegment<byte> data;
|
||||
|
||||
public BufferedProducer(string data) : this(data, Encoding.ASCII) { }
|
||||
public BufferedProducer(string data, Encoding encoding) : this(encoding.GetBytes(data)) { }
|
||||
public BufferedProducer(byte[] data) : this(new ArraySegment<byte>(data)) { }
|
||||
|
||||
public BufferedProducer(ArraySegment<byte> data)
|
||||
{
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public IDisposable Connect(IDataConsumer channel)
|
||||
{
|
||||
try
|
||||
{
|
||||
channel?.OnData(data, null);
|
||||
channel?.OnEnd();
|
||||
}
|
||||
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class BufferedConsumer : IDataConsumer
|
||||
{
|
||||
List<ArraySegment<byte>> buffer = new List<ArraySegment<byte>>();
|
||||
Action<string> resultCallback;
|
||||
Action<Exception> errorCallback;
|
||||
|
||||
public BufferedConsumer(Action<string> resultCallback, Action<Exception> errorCallback)
|
||||
{
|
||||
this.resultCallback = resultCallback;
|
||||
this.errorCallback = errorCallback;
|
||||
}
|
||||
|
||||
public bool OnData(ArraySegment<byte> data, Action continuation)
|
||||
{
|
||||
// this should hopefully clean the non ascii characters out.
|
||||
buffer?.Add(new ArraySegment<byte>(Encoding.ASCII.GetBytes(Encoding.ASCII.GetString(data.ToArray()))));
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnError(Exception error)
|
||||
{
|
||||
// errorCallback?.Invoke(error);
|
||||
}
|
||||
|
||||
public void OnEnd()
|
||||
{
|
||||
var str = buffer
|
||||
.Select(b => Encoding.ASCII.GetString(b.Array, b.Offset, b.Count))
|
||||
.Aggregate((result, next) => result + next);
|
||||
|
||||
resultCallback(str);
|
||||
}
|
||||
}
|
||||
}
|
@ -6,11 +6,7 @@ using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
using SharedLibrary.Objects;
|
||||
using System.Reflection;
|
||||
|
||||
#if DEBUG
|
||||
using SharedLibrary.Database;
|
||||
#endif
|
||||
|
||||
using System.Linq;
|
||||
|
||||
namespace IW4MAdmin
|
||||
{
|
||||
@ -20,7 +16,7 @@ namespace IW4MAdmin
|
||||
public static extern bool AllocConsole();
|
||||
static public double Version { get; private set; }
|
||||
static public ApplicationManager ServerManager = ApplicationManager.GetInstance();
|
||||
public static string OperatingDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + Path.DirectorySeparatorChar;
|
||||
public static string OperatingDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + Path.DirectorySeparatorChar;
|
||||
|
||||
public static void Start()
|
||||
{
|
||||
@ -29,7 +25,6 @@ namespace IW4MAdmin
|
||||
|
||||
Version = 1.6;
|
||||
|
||||
//double.TryParse(CheckUpdate(), out double latestVersion);
|
||||
Console.WriteLine("=====================================================");
|
||||
Console.WriteLine(" IW4M ADMIN");
|
||||
Console.WriteLine(" by RaidMax ");
|
||||
@ -40,13 +35,16 @@ namespace IW4MAdmin
|
||||
{
|
||||
CheckDirectories();
|
||||
|
||||
ServerManager = ApplicationManager.GetInstance();
|
||||
ServerManager.Init();
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
ServerManager = ApplicationManager.GetInstance();
|
||||
SharedLibrary.Database.Repair.Run(ServerManager.Logger);
|
||||
await ServerManager.Init();
|
||||
ServerManager.Start();
|
||||
});
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
ServerManager.Start();
|
||||
String userInput;
|
||||
Player Origin = ServerManager.GetClientService().Get(1).Result.AsPlayer();
|
||||
|
||||
@ -66,6 +64,8 @@ namespace IW4MAdmin
|
||||
Console.Write('>');
|
||||
|
||||
} while (ServerManager.Running);
|
||||
|
||||
Console.WriteLine("Shutdown complete");
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -11,8 +11,6 @@ using SharedLibrary.Commands;
|
||||
using SharedLibrary.Helpers;
|
||||
using SharedLibrary.Exceptions;
|
||||
using SharedLibrary.Objects;
|
||||
using SharedLibrary.Database;
|
||||
using SharedLibrary.Database.Models;
|
||||
using SharedLibrary.Services;
|
||||
|
||||
namespace IW4MAdmin
|
||||
@ -21,6 +19,7 @@ namespace IW4MAdmin
|
||||
{
|
||||
private List<Server> _servers;
|
||||
public List<Server> Servers => _servers.OrderByDescending(s => s.ClientNum).ToList();
|
||||
public List<int> AdministratorIPs { get; set; }
|
||||
public ILogger Logger { get; private set; }
|
||||
public bool Running { get; private set; }
|
||||
|
||||
@ -34,7 +33,7 @@ namespace IW4MAdmin
|
||||
#if FTP_LOG
|
||||
const int UPDATE_FREQUENCY = 700;
|
||||
#else
|
||||
const int UPDATE_FREQUENCY = 300;
|
||||
const int UPDATE_FREQUENCY = 750;
|
||||
#endif
|
||||
|
||||
private ApplicationManager()
|
||||
@ -47,6 +46,7 @@ namespace IW4MAdmin
|
||||
ClientSvc = new ClientService();
|
||||
AliasSvc = new AliasService();
|
||||
PenaltySvc = new PenaltyService();
|
||||
AdministratorIPs = new List<int>();
|
||||
}
|
||||
|
||||
public IList<Server> GetServers()
|
||||
@ -64,12 +64,12 @@ namespace IW4MAdmin
|
||||
return Instance ?? (Instance = new ApplicationManager());
|
||||
}
|
||||
|
||||
public void Init()
|
||||
public async Task Init()
|
||||
{
|
||||
#region WEBSERVICE
|
||||
// SharedLibrary.WebService.Init();
|
||||
//WebSvc = new WebService();
|
||||
//WebSvc.StartScheduler();
|
||||
#region DATABASE
|
||||
AdministratorIPs = (await ClientSvc.Find(c => c.Level > Player.Permission.Trusted))
|
||||
.Select(c => c.IPAddress)
|
||||
.ToList();
|
||||
#endregion
|
||||
|
||||
#region PLUGINS
|
||||
@ -79,7 +79,7 @@ namespace IW4MAdmin
|
||||
{
|
||||
try
|
||||
{
|
||||
Plugin.OnLoadAsync(this);
|
||||
await Plugin.OnLoadAsync(this);
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
@ -174,6 +174,7 @@ namespace IW4MAdmin
|
||||
Commands.Add(new CIP());
|
||||
Commands.Add(new CMask());
|
||||
Commands.Add(new CPruneAdmins());
|
||||
Commands.Add(new CRestartServer());
|
||||
|
||||
foreach (Command C in SharedLibrary.Plugins.PluginImporter.ActiveCommands)
|
||||
Commands.Add(C);
|
||||
|
@ -10,6 +10,8 @@ using SharedLibrary.Network;
|
||||
using SharedLibrary.Interfaces;
|
||||
using SharedLibrary.Objects;
|
||||
using System.Text.RegularExpressions;
|
||||
using SharedLibrary.Services;
|
||||
using SharedLibrary.Database.Models;
|
||||
|
||||
namespace IW4MAdmin
|
||||
{
|
||||
@ -99,7 +101,7 @@ namespace IW4MAdmin
|
||||
await Manager.GetClientService().Update(client);
|
||||
}
|
||||
|
||||
else if (existingAlias.Name == polledPlayer.Name)
|
||||
else if (existingAlias.Name == polledPlayer.Name)
|
||||
{
|
||||
client.CurrentAlias = existingAlias;
|
||||
client.CurrentAliasId = existingAlias.AliasId;
|
||||
@ -154,12 +156,13 @@ namespace IW4MAdmin
|
||||
if (cNum >= 0 && Players[cNum] != null)
|
||||
{
|
||||
Player Leaving = Players[cNum];
|
||||
Logger.WriteInfo($"Client {Leaving} disconnecting...");
|
||||
|
||||
await ExecuteEvent(new Event(Event.GType.Disconnect, "", Leaving, null, this));
|
||||
|
||||
Leaving.TotalConnectionTime += (int)(DateTime.UtcNow - Leaving.ConnectionTime).TotalSeconds;
|
||||
Leaving.LastConnection = DateTime.UtcNow;
|
||||
await Manager.GetClientService().Update(Leaving);
|
||||
|
||||
Logger.WriteInfo($"Client {Leaving} disconnecting...");
|
||||
await ExecuteEvent(new Event(Event.GType.Disconnect, "", Leaving, null, this));
|
||||
Players[cNum] = null;
|
||||
}
|
||||
}
|
||||
@ -357,7 +360,11 @@ namespace IW4MAdmin
|
||||
|
||||
async Task<int> PollPlayersAsync()
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
var CurrentPlayers = await this.GetStatusAsync();
|
||||
#if DEBUG
|
||||
Logger.WriteInfo($"Polling players took {(DateTime.Now - now).TotalMilliseconds}ms");
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < Players.Count; i++)
|
||||
{
|
||||
@ -385,9 +392,9 @@ namespace IW4MAdmin
|
||||
override public async Task<bool> ProcessUpdatesAsync(CancellationToken cts)
|
||||
{
|
||||
this.cts = cts;
|
||||
#if DEBUG == false
|
||||
//#if DEBUG == false
|
||||
try
|
||||
#endif
|
||||
//#endif
|
||||
{
|
||||
// first start
|
||||
if (firstRun)
|
||||
@ -504,7 +511,7 @@ namespace IW4MAdmin
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#if DEBUG == false
|
||||
//#if !DEBUG
|
||||
catch (SharedLibrary.Exceptions.NetworkException)
|
||||
{
|
||||
Logger.WriteError($"Could not communicate with {IP}:{Port}");
|
||||
@ -518,7 +525,7 @@ namespace IW4MAdmin
|
||||
Logger.WriteDebug("Error Trace: " + E.StackTrace);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
//#endif
|
||||
}
|
||||
|
||||
public async Task Initialize()
|
||||
@ -537,6 +544,7 @@ namespace IW4MAdmin
|
||||
await this.GetDvarAsync<int>("sv_maxclients");
|
||||
var gametype = await this.GetDvarAsync<string>("g_gametype");
|
||||
var basepath = await this.GetDvarAsync<string>("fs_basepath");
|
||||
WorkingDirectory = basepath.Value;
|
||||
var game = await this.GetDvarAsync<string>("fs_game");
|
||||
var logfile = await this.GetDvarAsync<string>("g_log");
|
||||
var logsync = await this.GetDvarAsync<int>("g_logsync");
|
||||
@ -705,6 +713,11 @@ namespace IW4MAdmin
|
||||
|
||||
string mapname = this.GetDvarAsync<string>("mapname").Result.Value;
|
||||
CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map(mapname, mapname);
|
||||
|
||||
// todo: make this more efficient
|
||||
((ApplicationManager)(Manager)).AdministratorIPs = (await new GenericRepository<EFClient>().FindAsync(c => c.Level > Player.Permission.Trusted))
|
||||
.Select(c => c.IPAddress)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
if (E.Type == Event.GType.MapEnd)
|
||||
|
25
WebfrontCore/Controllers/BaseController.cs
Normal file
25
WebfrontCore/Controllers/BaseController.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using IW4MAdmin;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using SharedLibrary;
|
||||
using SharedLibrary.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WebfrontCore.Controllers
|
||||
{
|
||||
public class BaseController : Controller
|
||||
{
|
||||
protected ApplicationManager Manager;
|
||||
protected bool Authorized { get; private set; }
|
||||
|
||||
public override void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
Manager = IW4MAdmin.Program.ServerManager;
|
||||
Authorized = Manager.AdministratorIPs.Contains(context.HttpContext.Connection.RemoteIpAddress.ToString().ConvertToIP());
|
||||
base.OnActionExecuting(context);
|
||||
}
|
||||
}
|
||||
}
|
@ -9,11 +9,11 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace WebfrontCore.Controllers
|
||||
{
|
||||
public class ClientController : Controller
|
||||
public class ClientController : BaseController
|
||||
{
|
||||
public async Task<IActionResult> ProfileAsync(int id)
|
||||
{
|
||||
var client = await IW4MAdmin.ApplicationManager.GetInstance().GetClientService().Get(id);
|
||||
var client = await Manager.GetClientService().Get(id);
|
||||
var clientDto = new PlayerInfo()
|
||||
{
|
||||
Name = client.Name,
|
||||
@ -38,11 +38,15 @@ namespace WebfrontCore.Controllers
|
||||
.OrderBy(i => i)
|
||||
.ToList(),
|
||||
};
|
||||
|
||||
clientDto.Meta.AddRange(await MetaService.GetMeta(client.ClientId));
|
||||
clientDto.Meta.AddRange(await IW4MAdmin.ApplicationManager.GetInstance().GetPenaltyService().ReadGetClientPenaltiesAsync(client.ClientId));
|
||||
clientDto.Meta.AddRange(await IW4MAdmin.ApplicationManager.GetInstance().GetPenaltyService().ReadGetClientPenaltiesAsync(client.ClientId, false));
|
||||
clientDto.Meta = clientDto.Meta.OrderByDescending(m => m.When).ToList();
|
||||
var meta = await MetaService.GetMeta(client.ClientId);
|
||||
clientDto.Meta.AddRange(Authorized ? meta : meta.Where(m => !m.Sensitive));
|
||||
clientDto.Meta.AddRange(await Manager.GetPenaltyService()
|
||||
.ReadGetClientPenaltiesAsync(client.ClientId));
|
||||
clientDto.Meta.AddRange(await Manager.GetPenaltyService()
|
||||
.ReadGetClientPenaltiesAsync(client.ClientId, false));
|
||||
clientDto.Meta = clientDto.Meta
|
||||
.OrderByDescending(m => m.When)
|
||||
.ToList();
|
||||
|
||||
ViewBag.Title = clientDto.Name;
|
||||
|
||||
@ -51,7 +55,7 @@ namespace WebfrontCore.Controllers
|
||||
|
||||
public async Task<IActionResult> PrivilegedAsync()
|
||||
{
|
||||
var admins = (await IW4MAdmin.ApplicationManager.GetInstance().GetClientService().GetPrivilegedClients())
|
||||
var admins = (await Manager.GetClientService().GetPrivilegedClients())
|
||||
.Where(a => a.Active)
|
||||
.OrderByDescending(a => a.Level);
|
||||
var adminsDict = new Dictionary<SharedLibrary.Objects.Player.Permission, IList<ClientInfo>>();
|
||||
@ -74,7 +78,7 @@ namespace WebfrontCore.Controllers
|
||||
|
||||
public async Task<IActionResult> FindAsync(string clientName)
|
||||
{
|
||||
var clients = (await IW4MAdmin.ApplicationManager.GetInstance().GetClientService().GetClientByName(clientName))
|
||||
var clients = (await Manager.GetClientService().GetClientByName(clientName))
|
||||
.OrderByDescending(c => c.LastConnection);
|
||||
var clientsDto = clients.Select(c => new PlayerInfo()
|
||||
{
|
||||
|
@ -9,11 +9,11 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace WebfrontCore.Controllers
|
||||
{
|
||||
public class ConsoleController : Controller
|
||||
public class ConsoleController : BaseController
|
||||
{
|
||||
public IActionResult Index()
|
||||
{
|
||||
var activeServers = IW4MAdmin.ApplicationManager.GetInstance().Servers.Select(s => new ServerInfo()
|
||||
var activeServers = Manager.Servers.Select(s => new ServerInfo()
|
||||
{
|
||||
Name = s.Hostname,
|
||||
ID = s.GetHashCode(),
|
||||
@ -38,10 +38,10 @@ namespace WebfrontCore.Controllers
|
||||
IPAddress = intIP
|
||||
};
|
||||
#else
|
||||
var origin = (await IW4MAdmin.ApplicationManager.GetInstance().GetClientService().GetUnique(0)).AsPlayer();
|
||||
var origin = (await Manager.GetClientService().GetUnique(0)).AsPlayer();
|
||||
#endif
|
||||
|
||||
var server = IW4MAdmin.ApplicationManager.GetInstance().Servers.First(s => s.GetHashCode() == serverId);
|
||||
var server = Manager.Servers.First(s => s.GetHashCode() == serverId);
|
||||
origin.CurrentServer = server;
|
||||
var remoteEvent = new Event(Event.GType.Say, command, origin, null, server);
|
||||
|
||||
|
@ -8,7 +8,7 @@ using SharedLibrary.Dtos;
|
||||
|
||||
namespace WebfrontCore.Controllers
|
||||
{
|
||||
public class HomeController : Controller
|
||||
public class HomeController : BaseController
|
||||
{
|
||||
public IActionResult Index()
|
||||
{
|
||||
|
@ -9,9 +9,9 @@ using WebfrontCore.ViewComponents;
|
||||
|
||||
namespace WebfrontCore.Controllers
|
||||
{
|
||||
public class PenaltyController : Controller
|
||||
public class PenaltyController : BaseController
|
||||
{
|
||||
public IActionResult List()
|
||||
public IActionResult List()
|
||||
{
|
||||
ViewBag.Title = "Penalty List";
|
||||
return View();
|
||||
|
@ -7,15 +7,14 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace WebfrontCore.Controllers
|
||||
{
|
||||
public class ServerController : Controller
|
||||
public class ServerController : BaseController
|
||||
{
|
||||
|
||||
[HttpGet]
|
||||
[ResponseCache(NoStore = true, Duration = 0)]
|
||||
public IActionResult ClientActivity(int id)
|
||||
{
|
||||
|
||||
var s = IW4MAdmin.Program.ServerManager.GetServers().FirstOrDefault(s2 => s2.GetHashCode() == id);
|
||||
var s = Manager.GetServers().FirstOrDefault(s2 => s2.GetHashCode() == id);
|
||||
if (s == null)
|
||||
return View("Error", "Invalid server!");
|
||||
|
||||
|
@ -12,6 +12,12 @@ namespace WebfrontCore.ViewComponents
|
||||
{
|
||||
public async Task<IViewComponentResult> InvokeAsync(int offset)
|
||||
{
|
||||
int ip = HttpContext.Connection.RemoteIpAddress
|
||||
.ToString().ConvertToIP();
|
||||
|
||||
bool authed = IW4MAdmin.ApplicationManager.GetInstance()
|
||||
.AdministratorIPs.Contains(ip);
|
||||
|
||||
var penalties = await IW4MAdmin.ApplicationManager.GetInstance().GetPenaltyService().GetRecentPenalties(15, offset);
|
||||
var penaltiesDto = penalties.Select(p => new PenaltyInfo()
|
||||
{
|
||||
@ -23,8 +29,11 @@ namespace WebfrontCore.ViewComponents
|
||||
Offense = p.Offense,
|
||||
Type = p.Type.ToString(),
|
||||
TimePunished = Utilities.GetTimePassed(p.When, false),
|
||||
TimeRemaining = DateTime.UtcNow > p.Expires ? "" : Utilities.TimeSpanText(p.Expires - DateTime.UtcNow)
|
||||
}).ToList();
|
||||
TimeRemaining = DateTime.UtcNow > p.Expires ? "" : Utilities.TimeSpanText(p.Expires - DateTime.UtcNow),
|
||||
Sensitive = p.Type == SharedLibrary.Objects.Penalty.PenaltyType.Flag
|
||||
});
|
||||
|
||||
penaltiesDto = authed ? penaltiesDto.ToList() : penaltiesDto.Where(p => !p.Sensitive).ToList();
|
||||
|
||||
return View("_List", penaltiesDto);
|
||||
}
|
||||
|
@ -8,7 +8,10 @@
|
||||
</div>
|
||||
<div id="profile_info" class="text-center text-sm-left pr-3 pl-3">
|
||||
<div id="profile_name">
|
||||
<h1><span class="client-name mr-4">@Model.Name<span id="profile_aliases_btn" class="oi oi-caret-bottom pl-2"></span></span></h1>
|
||||
@{
|
||||
string displayAliasButton = Model.Aliases.Count > 0 ? "" : "display: none;";
|
||||
}
|
||||
<h1><span class="client-name mr-4">@Model.Name<span id="profile_aliases_btn" class="oi oi-caret-bottom pl-2 @displayAliasButton"></span></span></h1>
|
||||
<div id="profile_aliases" class="pr-0 pr-sm-4 pb-2 mb-2 text-muted">
|
||||
@{
|
||||
foreach (string alias in Model.Aliases)
|
||||
|
@ -8,19 +8,22 @@
|
||||
<PackageId>WebfrontCore</PackageId>
|
||||
<Platforms>AnyCPU;x86</Platforms>
|
||||
<ApplicationIcon>wwwroot\favicon.ico</ApplicationIcon>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Application\Kayak.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="Application\Kayak.cs" />
|
||||
<None Update="wwwroot\**\*">
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</None>
|
||||
@ -60,12 +63,6 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="app.config">
|
||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="xcopy /Y "$(SolutionDir)BUILD\Plugins" "$(TargetDir)Plugins\"
xcopy /Y /I /E "$(SolutionDir)BUILD\Lib" "$(TargetDir)" 

xcopy /Y /I /E "$(ProjectDir)Application\Config" "$(TargetDir)Config"" />
|
||||
</Target>
|
||||
|
@ -31,6 +31,6 @@
|
||||
<connectionStrings>
|
||||
<add name="DefaultConnection"
|
||||
providerName="System.Data.SqlServerCe.4.0"
|
||||
connectionString="Data Source=|DataDirectory|\Database\Database.sdf"/>
|
||||
connectionString="Data Source=|DataDirectory|\Database.sdf"/>
|
||||
</connectionStrings>
|
||||
</configuration>
|
||||
|
@ -2,9 +2,9 @@
|
||||
"Logging": {
|
||||
"IncludeScopes": false,
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"Default": "Trace",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
"Microsoft": "None"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user