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
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
## files generated by popular Visual Studio add-ons.
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|
||||||
|
Detection.cs
|
||||||
|
DetectionPenaltyResult.cs
|
||||||
|
Thresholds.cs
|
||||||
|
|
||||||
# User-specific files
|
# User-specific files
|
||||||
*.suo
|
*.suo
|
||||||
*.user
|
*.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
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 15
|
# Visual Studio 15
|
||||||
VisualStudioVersion = 15.0.27130.2036
|
VisualStudioVersion = 15.0.27004.2006
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StatsPlugin", "Plugins\SimpleStats\StatsPlugin.csproj", "{4785AB75-66F3-4391-985D-63A5A049A0FA}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StatsPlugin", "Plugins\SimpleStats\StatsPlugin.csproj", "{4785AB75-66F3-4391-985D-63A5A049A0FA}"
|
||||||
ProjectSection(ProjectDependencies) = postProject
|
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|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.ActiveCfg = Release|Any CPU
|
||||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Nightly|x64.Build.0 = 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.ActiveCfg = Release|x86
|
||||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Nightly|x86.Build.0 = Release|Any CPU
|
{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.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|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.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|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.ActiveCfg = Release|Any CPU
|
||||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Stable|x64.Build.0 = 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.ActiveCfg = Release|x86
|
||||||
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Stable|x86.Build.0 = Release|Any CPU
|
{65340D7D-5831-406C-ACAD-B13BA634BDE2}.Release-Stable|x86.Build.0 = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using SharedLibrary.Interfaces;
|
using SharedLibrary.Interfaces;
|
||||||
|
using SharedLibrary.Objects;
|
||||||
using StatsPlugin.Models;
|
using StatsPlugin.Models;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -30,62 +31,276 @@ namespace StatsPlugin.Cheat
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="kill">kill performed by the player</param>
|
/// <param name="kill">kill performed by the player</param>
|
||||||
/// <returns>true if detection reached thresholds, false otherwise</returns>
|
/// <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)
|
if ((kill.DeathType != IW4Info.MeansOfDeath.MOD_PISTOL_BULLET &&
|
||||||
return false;
|
kill.DeathType != IW4Info.MeansOfDeath.MOD_RIFLE_BULLET) ||
|
||||||
|
kill.HitLoc == IW4Info.HitLocation.none)
|
||||||
bool thresholdReached = false;
|
return new DetectionPenaltyResult()
|
||||||
|
{
|
||||||
|
ClientPenalty = Penalty.PenaltyType.Any,
|
||||||
|
RatioAmount = 0
|
||||||
|
};
|
||||||
|
|
||||||
HitLocationCount[kill.HitLoc]++;
|
HitLocationCount[kill.HitLoc]++;
|
||||||
Kills++;
|
Kills++;
|
||||||
AverageKillTime = (AverageKillTime + (DateTime.UtcNow - LastKill).TotalSeconds) / Kills;
|
AverageKillTime = (AverageKillTime + (DateTime.UtcNow - LastKill).TotalSeconds) / Kills;
|
||||||
|
|
||||||
if (Kills > Thresholds.LowSampleMinKills)
|
#region SESSION_RATIOS
|
||||||
|
if (Kills >= Thresholds.LowSampleMinKills)
|
||||||
{
|
{
|
||||||
double marginOfError = Thresholds.GetMarginOfError(Kills);
|
double marginOfError = Thresholds.GetMarginOfError(Kills);
|
||||||
// determine what the max headshot percentage can be for current number of 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 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
|
// 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
|
// 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
|
// 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++;
|
// ban on headshot
|
||||||
Log.WriteDebug("**Maximum Headshot Ratio Reached**");
|
if (currentHeadshotRatio > maxHeadshotLerpValueForFlag)
|
||||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
{
|
||||||
Log.WriteDebug($"**Kills: {Kills}");
|
AboveThresholdCount++;
|
||||||
Log.WriteDebug($"**Ratio {headshotRatio}");
|
Log.WriteDebug("**Maximum Headshot Ratio Reached For Ban**");
|
||||||
Log.WriteDebug($"**MaxRatio {maxHeadshotLerpValue}");
|
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
||||||
var sb = new StringBuilder();
|
Log.WriteDebug($"**Kills: {Kills}");
|
||||||
foreach (var kvp in HitLocationCount)
|
Log.WriteDebug($"**Ratio {currentHeadshotRatio}");
|
||||||
sb.Append($"HitLocation: {kvp.Key} Count: {kvp.Value}");
|
Log.WriteDebug($"**MaxRatio {maxHeadshotLerpValueForFlag}");
|
||||||
Log.WriteDebug(sb.ToString());
|
var sb = new StringBuilder();
|
||||||
Log.WriteDebug($"ThresholdReached: {AboveThresholdCount}");
|
foreach (var kvp in HitLocationCount)
|
||||||
thresholdReached = true;
|
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**");
|
// ban on bone ratio
|
||||||
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
if (currentMaxBoneRatio > maxBoneRatioLerpValueForBan)
|
||||||
Log.WriteDebug($"**Kills: {Kills}");
|
{
|
||||||
Log.WriteDebug($"**Ratio {maximumBoneRatio}");
|
Log.WriteDebug("**Maximum Bone Ratio Reached For Ban**");
|
||||||
Log.WriteDebug($"**MaxRatio {maxBoneRatioLerpValue}");
|
Log.WriteDebug($"ClientId: {kill.AttackerId}");
|
||||||
var sb = new StringBuilder();
|
Log.WriteDebug($"**Kills: {Kills}");
|
||||||
foreach (var kvp in HitLocationCount)
|
Log.WriteDebug($"**Ratio {currentMaxBoneRatio}");
|
||||||
sb.Append($"HitLocation: {kvp.Key} Count: {kvp.Value}");
|
Log.WriteDebug($"**MaxRatio {maxBoneRatioLerpValueForBan}");
|
||||||
Log.WriteDebug(sb.ToString());
|
var sb = new StringBuilder();
|
||||||
thresholdReached = true;
|
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
|
class Thresholds
|
||||||
{
|
{
|
||||||
private const double Deviations = 3.33;
|
public static double HeadshotRatioThresholdLowSample(double deviations) => HeadshotRatioStandardDeviationLowSample * deviations + HeadshotRatioMean;
|
||||||
|
public static double HeadshotRatioThresholdHighSample(double deviations) => HeadshotRatioStandardDeviationHighSample * deviations + HeadshotRatioMean;
|
||||||
public const double HeadshotRatioThresholdLowSample = HeadshotRatioStandardDeviationLowSample * Deviations + HeadshotRatioMean;
|
|
||||||
public const double HeadshotRatioThresholdHighSample = HeadshotRatioStandardDeviationHighSample * Deviations + HeadshotRatioMean;
|
|
||||||
public const double HeadshotRatioStandardDeviationLowSample = 0.1769994181;
|
public const double HeadshotRatioStandardDeviationLowSample = 0.1769994181;
|
||||||
public const double HeadshotRatioStandardDeviationHighSample = 0.03924263235;
|
public const double HeadshotRatioStandardDeviationHighSample = 0.03924263235;
|
||||||
//public const double HeadshotRatioMean = 0.09587712258;
|
|
||||||
public const double HeadshotRatioMean = 0.222;
|
public const double HeadshotRatioMean = 0.222;
|
||||||
|
|
||||||
public const double BoneRatioThresholdLowSample = BoneRatioStandardDeviationLowSample * Deviations + BoneRatioMean;
|
public static double BoneRatioThresholdLowSample(double deviations) => BoneRatioStandardDeviationLowSample * deviations + BoneRatioMean;
|
||||||
public const double BoneRatioThresholdHighSample = BoneRatioStandardDeviationHighSample * Deviations + BoneRatioMean;
|
public static double BoneRatioThresholdHighSample(double deviations) => BoneRatioStandardDeviationHighSample * deviations + BoneRatioMean;
|
||||||
public const double BoneRatioStandardDeviationLowSample = 0.1324612879;
|
public const double BoneRatioStandardDeviationLowSample = 0.1324612879;
|
||||||
public const double BoneRatioStandardDeviationHighSample = 0.0515753935;
|
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 LowSampleMinKills = 15;
|
||||||
|
public const int MediumSampleMinKills = 30;
|
||||||
public const int HighSampleMinKills = 100;
|
public const int HighSampleMinKills = 100;
|
||||||
public const double KillTimeThreshold = 0.2;
|
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)
|
public static double Lerp(double v1, double v2, double amount)
|
||||||
{
|
{
|
||||||
|
@ -27,6 +27,9 @@ namespace StatsPlugin.Commands
|
|||||||
stats.SPM = 0;
|
stats.SPM = 0;
|
||||||
stats.Skill = 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
|
// fixme: this doesn't work properly when another context exists
|
||||||
await svc.SaveChangesAsync();
|
await svc.SaveChangesAsync();
|
||||||
await E.Origin.Tell("Your stats have been reset");
|
await E.Origin.Tell("Your stats have been reset");
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using StatsPlugin.Cheat;
|
using StatsPlugin.Cheat;
|
||||||
using StatsPlugin.Models;
|
using StatsPlugin.Models;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@ -10,15 +11,15 @@ using System.Threading.Tasks;
|
|||||||
namespace StatsPlugin.Helpers
|
namespace StatsPlugin.Helpers
|
||||||
{
|
{
|
||||||
class ServerStats {
|
class ServerStats {
|
||||||
public Dictionary<int, EFClientStatistics> PlayerStats { get; set; }
|
public ConcurrentDictionary<int, EFClientStatistics> PlayerStats { get; set; }
|
||||||
public Dictionary<int, Detection> PlayerDetections { get; set; }
|
public ConcurrentDictionary<int, Detection> PlayerDetections { get; set; }
|
||||||
public EFServerStatistics ServerStatistics { get; private set; }
|
public EFServerStatistics ServerStatistics { get; private set; }
|
||||||
public EFServer Server { get; private set; }
|
public EFServer Server { get; private set; }
|
||||||
|
|
||||||
public ServerStats(EFServer sv, EFServerStatistics st)
|
public ServerStats(EFServer sv, EFServerStatistics st)
|
||||||
{
|
{
|
||||||
PlayerStats = new Dictionary<int, EFClientStatistics>();
|
PlayerStats = new ConcurrentDictionary<int, EFClientStatistics>();
|
||||||
PlayerDetections = new Dictionary<int, Detection>();
|
PlayerDetections = new ConcurrentDictionary<int, Detection>();
|
||||||
ServerStatistics = st;
|
ServerStatistics = st;
|
||||||
Server = sv;
|
Server = sv;
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ using SharedLibrary.Interfaces;
|
|||||||
using SharedLibrary.Objects;
|
using SharedLibrary.Objects;
|
||||||
using SharedLibrary.Services;
|
using SharedLibrary.Services;
|
||||||
using StatsPlugin.Models;
|
using StatsPlugin.Models;
|
||||||
|
using SharedLibrary.Commands;
|
||||||
|
|
||||||
namespace StatsPlugin.Helpers
|
namespace StatsPlugin.Helpers
|
||||||
{
|
{
|
||||||
@ -75,7 +76,7 @@ namespace StatsPlugin.Helpers
|
|||||||
|
|
||||||
catch (Exception e)
|
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>
|
/// </summary>
|
||||||
/// <param name="pl">Player to add/retrieve stats for</param>
|
/// <param name="pl">Player to add/retrieve stats for</param>
|
||||||
/// <returns>EFClientStatistic of specified player</returns>
|
/// <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();
|
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 playerStats = Servers[serverId].PlayerStats;
|
||||||
var statsSvc = ContextThreads[serverId];
|
var statsSvc = ContextThreads[serverId];
|
||||||
|
|
||||||
@ -105,33 +114,50 @@ namespace StatsPlugin.Helpers
|
|||||||
ServerId = serverId,
|
ServerId = serverId,
|
||||||
Skill = 0.0,
|
Skill = 0.0,
|
||||||
SPM = 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);
|
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
|
// set these on connecting
|
||||||
clientStats.LastActive = DateTime.UtcNow;
|
clientStats.LastActive = DateTime.UtcNow;
|
||||||
clientStats.LastStatCalculation = 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 ClientId in stats {pl.ClientId} vs {playerStats[pl.ClientId].ClientId}");
|
||||||
{
|
playerStats.TryRemove(pl.ClientId, out EFClientStatistics removedValue);
|
||||||
Log.WriteWarning($"Duplicate clientnumber in stats {pl.ClientId} vs {playerStats[pl.ClientNumber].ClientId}");
|
|
||||||
playerStats.Remove(pl.ClientNumber);
|
|
||||||
}
|
|
||||||
playerStats.Add(pl.ClientNumber, clientStats);
|
|
||||||
}
|
}
|
||||||
|
playerStats.TryAdd(pl.ClientId, clientStats);
|
||||||
|
|
||||||
var detectionStats = Servers[serverId].PlayerDetections;
|
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;
|
return clientStats;
|
||||||
}
|
}
|
||||||
@ -143,19 +169,27 @@ namespace StatsPlugin.Helpers
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task RemovePlayer(Player pl)
|
public async Task RemovePlayer(Player pl)
|
||||||
{
|
{
|
||||||
|
Log.WriteInfo($"Removing {pl} from stats");
|
||||||
|
|
||||||
int serverId = pl.CurrentServer.GetHashCode();
|
int serverId = pl.CurrentServer.GetHashCode();
|
||||||
var playerStats = Servers[serverId].PlayerStats;
|
var playerStats = Servers[serverId].PlayerStats;
|
||||||
var detectionStats = Servers[serverId].PlayerDetections;
|
var detectionStats = Servers[serverId].PlayerDetections;
|
||||||
var serverStats = Servers[serverId].ServerStatistics;
|
var serverStats = Servers[serverId].ServerStatistics;
|
||||||
var statsSvc = ContextThreads[serverId];
|
var statsSvc = ContextThreads[serverId];
|
||||||
|
|
||||||
|
if (!playerStats.ContainsKey(pl.ClientId))
|
||||||
|
{
|
||||||
|
Log.WriteWarning($"Client disconnecting not in stats {pl}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// get individual client's stats
|
// 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
|
// remove the client from the stats dictionary as they're leaving
|
||||||
lock (playerStats)
|
playerStats.TryRemove(pl.ClientId, out EFClientStatistics removedValue);
|
||||||
playerStats.Remove(pl.ClientNumber);
|
detectionStats.TryRemove(pl.ClientId, out Cheat.Detection removedValue2);
|
||||||
lock (detectionStats)
|
|
||||||
detectionStats.Remove(pl.ClientNumber);
|
|
||||||
|
|
||||||
// sync their stats before they leave
|
// sync their stats before they leave
|
||||||
UpdateStats(clientStats);
|
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,
|
public async Task AddScriptKill(Player attacker, Player victim, int serverId, string map, string hitLoc, string type,
|
||||||
string damage, string weapon, string killOrigin, string deathOrigin)
|
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 statsSvc = ContextThreads[serverId];
|
||||||
var playerDetection = Servers[serverId].PlayerDetections[attacker.ClientNumber];
|
|
||||||
|
|
||||||
var kill = new EFClientKill()
|
var kill = new EFClientKill()
|
||||||
{
|
{
|
||||||
@ -200,30 +225,88 @@ namespace StatsPlugin.Helpers
|
|||||||
Weapon = ParseEnum<IW4Info.WeaponName>.Get(weapon, typeof(IW4Info.WeaponName))
|
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);
|
var playerDetection = Servers[serverId].PlayerDetections[attacker.ClientId];
|
||||||
await statsSvc.KillStatsSvc.SaveChangesAsync();
|
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)
|
public async Task AddStandardKill(Player attacker, Player victim)
|
||||||
{
|
{
|
||||||
int serverId = attacker.CurrentServer.GetHashCode();
|
int serverId = attacker.CurrentServer.GetHashCode();
|
||||||
var attackerStats = Servers[serverId].PlayerStats[attacker.ClientNumber];
|
EFClientStatistics attackerStats = null;
|
||||||
|
try
|
||||||
if (victim == null)
|
|
||||||
{
|
{
|
||||||
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;
|
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
|
// update the total stats
|
||||||
Servers[serverId].ServerStatistics.TotalKills += 1;
|
Servers[serverId].ServerStatistics.TotalKills += 1;
|
||||||
|
|
||||||
|
attackerStats.SessionScore = attacker.Score;
|
||||||
|
victimStats.SessionScore = victim.Score;
|
||||||
|
|
||||||
// calculate for the clients
|
// calculate for the clients
|
||||||
CalculateKill(attackerStats, victimStats);
|
CalculateKill(attackerStats, victimStats);
|
||||||
|
|
||||||
@ -292,9 +375,7 @@ namespace StatsPlugin.Helpers
|
|||||||
return clientStats;
|
return clientStats;
|
||||||
|
|
||||||
// calculate the players Score Per Minute for the current session
|
// calculate the players Score Per Minute for the current session
|
||||||
int currentScore = Manager.GetActiveClients()
|
int currentScore = clientStats.SessionScore;
|
||||||
.First(c => c.ClientId == clientStats.ClientId)
|
|
||||||
.Score;
|
|
||||||
double killSPM = currentScore / (timeSinceLastCalc * 60.0);
|
double killSPM = currentScore / (timeSinceLastCalc * 60.0);
|
||||||
|
|
||||||
// calculate how much the KDR should weigh
|
// 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)
|
public async Task AddMessageAsync(int clientId, int serverId, string message)
|
||||||
{
|
{
|
||||||
// the web users can have no account
|
// the web users can have no account
|
||||||
|
@ -10,7 +10,6 @@ namespace StatsPlugin.Helpers
|
|||||||
{
|
{
|
||||||
public class ThreadSafeStatsService
|
public class ThreadSafeStatsService
|
||||||
{
|
{
|
||||||
|
|
||||||
public GenericRepository<EFClientStatistics> ClientStatSvc { get; private set; }
|
public GenericRepository<EFClientStatistics> ClientStatSvc { get; private set; }
|
||||||
public GenericRepository<EFServer> ServerSvc { get; private set; }
|
public GenericRepository<EFServer> ServerSvc { get; private set; }
|
||||||
public GenericRepository<EFClientKill> KillStatsSvc { get; private set; }
|
public GenericRepository<EFClientKill> KillStatsSvc { get; private set; }
|
||||||
|
@ -1358,7 +1358,9 @@ namespace StatsPlugin
|
|||||||
m40a3_mp,
|
m40a3_mp,
|
||||||
peacekeeper_mp,
|
peacekeeper_mp,
|
||||||
dragunov_mp,
|
dragunov_mp,
|
||||||
cobra_player_minigun_mp
|
cobra_player_minigun_mp,
|
||||||
|
destructible_car,
|
||||||
|
sentry_minigun_mp
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum MapName
|
public enum MapName
|
||||||
|
@ -24,7 +24,9 @@ namespace StatsPlugin.Models
|
|||||||
public int Kills { get; set; }
|
public int Kills { get; set; }
|
||||||
[Required]
|
[Required]
|
||||||
public int Deaths { get; set; }
|
public int Deaths { get; set; }
|
||||||
[Required]
|
|
||||||
|
public virtual ICollection<EFHitLocationCount> HitLocations { get; set; }
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public double KDR
|
public double KDR
|
||||||
{
|
{
|
||||||
@ -51,5 +53,7 @@ namespace StatsPlugin.Models
|
|||||||
public int LastScore { get; set; }
|
public int LastScore { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public DateTime LastActive { get; set; }
|
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";
|
public string Author => "RaidMax";
|
||||||
|
|
||||||
private StatManager Manager;
|
public static StatManager Manager { get; private set; }
|
||||||
private IManager ServerManager;
|
private IManager ServerManager;
|
||||||
|
|
||||||
public async Task OnEventAsync(Event E, Server S)
|
public async Task OnEventAsync(Event E, Server S)
|
||||||
@ -35,19 +35,20 @@ namespace StatsPlugin
|
|||||||
case Event.GType.Stop:
|
case Event.GType.Stop:
|
||||||
break;
|
break;
|
||||||
case Event.GType.Connect:
|
case Event.GType.Connect:
|
||||||
Manager.AddPlayer(E.Origin);
|
await Manager.AddPlayer(E.Origin);
|
||||||
break;
|
break;
|
||||||
case Event.GType.Disconnect:
|
case Event.GType.Disconnect:
|
||||||
await Manager.RemovePlayer(E.Origin);
|
await Manager.RemovePlayer(E.Origin);
|
||||||
break;
|
break;
|
||||||
case Event.GType.Say:
|
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);
|
await Manager.AddMessageAsync(E.Origin.ClientId, E.Owner.GetHashCode(), E.Data);
|
||||||
break;
|
break;
|
||||||
case Event.GType.MapChange:
|
case Event.GType.MapChange:
|
||||||
|
Manager.ResetKillstreaks(S.GetHashCode());
|
||||||
|
await Manager.Sync(S);
|
||||||
break;
|
break;
|
||||||
case Event.GType.MapEnd:
|
case Event.GType.MapEnd:
|
||||||
await Manager.Sync(S);
|
|
||||||
break;
|
break;
|
||||||
case Event.GType.Broadcast:
|
case Event.GType.Broadcast:
|
||||||
break;
|
break;
|
||||||
@ -92,6 +93,25 @@ namespace StatsPlugin
|
|||||||
double kdr = Math.Round(kills / (double)deaths, 2);
|
double kdr = Math.Round(kills / (double)deaths, 2);
|
||||||
double skill = Math.Round(clientStats.Sum(c => c.Skill) / clientStats.Count, 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>()
|
return new List<ProfileMeta>()
|
||||||
{
|
{
|
||||||
new ProfileMeta()
|
new ProfileMeta()
|
||||||
@ -113,6 +133,24 @@ namespace StatsPlugin
|
|||||||
{
|
{
|
||||||
Key = "Skill",
|
Key = "Skill",
|
||||||
Value = 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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Cheat\Detection.cs" />
|
<Compile Include="Cheat\Detection.cs" />
|
||||||
|
<Compile Include="Cheat\DetectionPenaltyResult.cs" />
|
||||||
<Compile Include="Cheat\Thresholds.cs" />
|
<Compile Include="Cheat\Thresholds.cs" />
|
||||||
<Compile Include="Commands\ResetStats.cs" />
|
<Compile Include="Commands\ResetStats.cs" />
|
||||||
<Compile Include="Commands\TopStats.cs" />
|
<Compile Include="Commands\TopStats.cs" />
|
||||||
@ -131,6 +132,7 @@
|
|||||||
<Compile Include="MinimapConfig.cs" />
|
<Compile Include="MinimapConfig.cs" />
|
||||||
<Compile Include="Models\EFClientKill.cs" />
|
<Compile Include="Models\EFClientKill.cs" />
|
||||||
<Compile Include="Models\EFClientMessage.cs" />
|
<Compile Include="Models\EFClientMessage.cs" />
|
||||||
|
<Compile Include="Models\EFHitLocationCount.cs" />
|
||||||
<Compile Include="Models\EFServer.cs" />
|
<Compile Include="Models\EFServer.cs" />
|
||||||
<Compile Include="Models\EFClientStatistics.cs" />
|
<Compile Include="Models\EFClientStatistics.cs" />
|
||||||
<Compile Include="Models\EFServerStatistics.cs" />
|
<Compile Include="Models\EFServerStatistics.cs" />
|
||||||
@ -146,7 +148,6 @@
|
|||||||
<ProjectReference Include="..\..\SharedLibrary\SharedLibrary.csproj">
|
<ProjectReference Include="..\..\SharedLibrary\SharedLibrary.csproj">
|
||||||
<Project>{d51eeceb-438a-47da-870f-7d7b41bc24d6}</Project>
|
<Project>{d51eeceb-438a-47da-870f-7d7b41bc24d6}</Project>
|
||||||
<Name>SharedLibrary</Name>
|
<Name>SharedLibrary</Name>
|
||||||
<Private>False</Private>
|
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
@ -126,7 +126,7 @@ namespace Welcome_Plugin
|
|||||||
msg = msg.Replace("{{ClientLevel}}", Utilities.ConvertLevelToColor(joining.Level));
|
msg = msg.Replace("{{ClientLevel}}", Utilities.ConvertLevelToColor(joining.Level));
|
||||||
try
|
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));
|
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 System.Data.Entity;
|
||||||
using SharedLibrary.Database.Models;
|
using SharedLibrary.Database.Models;
|
||||||
using SharedLibrary.Services;
|
using SharedLibrary.Services;
|
||||||
|
using SharedLibrary.Exceptions;
|
||||||
|
|
||||||
namespace SharedLibrary.Commands
|
namespace SharedLibrary.Commands
|
||||||
{
|
{
|
||||||
@ -170,6 +171,9 @@ namespace SharedLibrary.Commands
|
|||||||
{
|
{
|
||||||
String Message = Utilities.RemoveWords(E.Data, 1).Trim();
|
String Message = Utilities.RemoveWords(E.Data, 1).Trim();
|
||||||
var length = E.Data.Split(' ')[0].ToLower().ParseTimespan();
|
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)
|
if (E.Origin.Level > E.Target.Level)
|
||||||
{
|
{
|
||||||
@ -428,8 +432,8 @@ namespace SharedLibrary.Commands
|
|||||||
|
|
||||||
if (newPerm > Player.Permission.Banned)
|
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)
|
if (ActiveClient != null)
|
||||||
{
|
{
|
||||||
@ -444,7 +448,6 @@ namespace SharedLibrary.Commands
|
|||||||
}
|
}
|
||||||
|
|
||||||
await E.Origin.Tell($"{E.Target.Name} was successfully promoted!");
|
await E.Origin.Tell($"{E.Target.Name} was successfully promoted!");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
@ -485,17 +488,22 @@ namespace SharedLibrary.Commands
|
|||||||
|
|
||||||
public override async Task ExecuteAsync(Event E)
|
public override async Task ExecuteAsync(Event E)
|
||||||
{
|
{
|
||||||
|
int numOnline = 0;
|
||||||
for (int i = 0; i < E.Owner.Players.Count; i++)
|
for (int i = 0; i < E.Owner.Players.Count; i++)
|
||||||
{
|
{
|
||||||
var P = E.Owner.Players[i];
|
var P = E.Owner.Players[i];
|
||||||
if (P != null && P.Level > Player.Permission.Flagged && !P.Masked)
|
if (P != null && P.Level > Player.Permission.Flagged && !P.Masked)
|
||||||
{
|
{
|
||||||
|
numOnline++;
|
||||||
if (E.Message[0] == '@')
|
if (E.Message[0] == '@')
|
||||||
await E.Owner.Broadcast(String.Format("[^3{0}^7] {1}", Utilities.ConvertLevelToColor(P.Level), P.Name));
|
await E.Owner.Broadcast(String.Format("[^3{0}^7] {1}", Utilities.ConvertLevelToColor(P.Level), P.Name));
|
||||||
else
|
else
|
||||||
await E.Origin.Tell(String.Format("[^3{0}^7] {1}", Utilities.ConvertLevelToColor(P.Level), P.Name));
|
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)
|
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();
|
Task.Delay(5000).Wait();
|
||||||
await E.Owner.LoadMap(m.Name);
|
await E.Owner.LoadMap(m.Name);
|
||||||
return;
|
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();
|
Task.Delay(5000).Wait();
|
||||||
await E.Owner.LoadMap(newMap);
|
await E.Owner.LoadMap(newMap);
|
||||||
}
|
}
|
||||||
@ -927,7 +935,7 @@ namespace SharedLibrary.Commands
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
public override async Task ExecuteAsync(Event E)
|
public override async Task ExecuteAsync(Event E)
|
||||||
{
|
{
|
||||||
int inactiveDays = 30;
|
int inactiveDays = 30;
|
||||||
@ -961,7 +969,89 @@ namespace SharedLibrary.Commands
|
|||||||
inactiveUsers.ForEach(c => c.Level = Player.Permission.User);
|
inactiveUsers.ForEach(c => c.Level = Player.Permission.User);
|
||||||
await context.SaveChangesAsync();
|
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.Reflection;
|
||||||
using System.Data.Entity.Infrastructure;
|
using System.Data.Entity.Infrastructure;
|
||||||
using System.Data.Entity.SqlServerCompact;
|
using System.Data.Entity.SqlServerCompact;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace SharedLibrary.Database
|
namespace SharedLibrary.Database
|
||||||
{
|
{
|
||||||
@ -23,7 +24,8 @@ namespace SharedLibrary.Database
|
|||||||
|
|
||||||
public DatabaseContext() : base("DefaultConnection")
|
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;
|
Configuration.LazyLoadingEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +52,12 @@ namespace SharedLibrary.Database
|
|||||||
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
|
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
|
||||||
|
|
||||||
// https://aleemkhan.wordpress.com/2013/02/28/dynamically-adding-dbset-properties-in-dbcontext-for-entity-framework-code-first/
|
// 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"))
|
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;
|
Assembly library;
|
||||||
try
|
try
|
||||||
@ -64,7 +71,7 @@ namespace SharedLibrary.Database
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(var type in library.ExportedTypes)
|
foreach (var type in library.ExportedTypes)
|
||||||
{
|
{
|
||||||
if (type.IsClass && type.IsSubclassOf(typeof(SharedEntity)))
|
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
|
namespace SharedLibrary.Dtos
|
||||||
{
|
{
|
||||||
public class PenaltyInfo
|
public class PenaltyInfo : SharedInfo
|
||||||
{
|
{
|
||||||
public string OffenderName { get; set; }
|
public string OffenderName { get; set; }
|
||||||
public int OffenderId { get; set; }
|
public int OffenderId { get; set; }
|
||||||
|
@ -7,9 +7,10 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace SharedLibrary.Dtos
|
namespace SharedLibrary.Dtos
|
||||||
{
|
{
|
||||||
public class ProfileMeta
|
public class ProfileMeta : SharedInfo
|
||||||
{
|
{
|
||||||
public DateTime When { get; set; }
|
public DateTime When { get; set; }
|
||||||
|
public bool Sensitive { get; set; }
|
||||||
public string WhenString => Utilities.GetTimePassed(When, false);
|
public string WhenString => Utilities.GetTimePassed(When, false);
|
||||||
public string Key { get; set; }
|
public string Key { get; set; }
|
||||||
public dynamic Value { 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.Objects;
|
||||||
using SharedLibrary.Database.Models;
|
using SharedLibrary.Database.Models;
|
||||||
using SharedLibrary.Services;
|
using SharedLibrary.Services;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace SharedLibrary.Interfaces
|
namespace SharedLibrary.Interfaces
|
||||||
{
|
{
|
||||||
public interface IManager
|
public interface IManager
|
||||||
{
|
{
|
||||||
void Init();
|
Task Init();
|
||||||
void Start();
|
void Start();
|
||||||
void Stop();
|
void Stop();
|
||||||
ILogger GetLogger();
|
ILogger GetLogger();
|
||||||
@ -15,7 +16,7 @@ namespace SharedLibrary.Interfaces
|
|||||||
IList<Command> GetCommands();
|
IList<Command> GetCommands();
|
||||||
IList<Helpers.MessageToken> GetMessageTokens();
|
IList<Helpers.MessageToken> GetMessageTokens();
|
||||||
IList<Player> GetActiveClients();
|
IList<Player> GetActiveClients();
|
||||||
ClientService GetClientService();
|
ClientService GetClientService();
|
||||||
AliasService GetAliasService();
|
AliasService GetAliasService();
|
||||||
PenaltyService GetPenaltyService();
|
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
|
do
|
||||||
{
|
{
|
||||||
ReceiveBuffer = ServerOOBConnection.Receive(ref Endpoint);
|
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);
|
} while (ServerOOBConnection.Available > 0 && ServerOOBConnection.Client.Connected);
|
||||||
|
|
||||||
if (QueryResponseString.ToString().Contains("Invalid password"))
|
if (QueryResponseString.ToString().Contains("Invalid password"))
|
||||||
|
@ -385,6 +385,7 @@ namespace SharedLibrary
|
|||||||
public string Password { get; private set; }
|
public string Password { get; private set; }
|
||||||
public bool Throttled { get; protected set; }
|
public bool Throttled { get; protected set; }
|
||||||
public bool CustomCallback { get; protected set; }
|
public bool CustomCallback { get; protected set; }
|
||||||
|
public string WorkingDirectory { get; protected set; }
|
||||||
|
|
||||||
// Internal
|
// Internal
|
||||||
protected string IP;
|
protected string IP;
|
||||||
|
@ -10,6 +10,8 @@ namespace SharedLibrary
|
|||||||
public string FtpPrefix;
|
public string FtpPrefix;
|
||||||
public bool AllowMultipleOwners;
|
public bool AllowMultipleOwners;
|
||||||
public bool AllowTrustedRank;
|
public bool AllowTrustedRank;
|
||||||
|
public string RestartUsername;
|
||||||
|
public string RestartPassword;
|
||||||
|
|
||||||
public override string Filename()
|
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
|
// set remaining non-navigation properties that may have been updated
|
||||||
client.Level = entity.Level;
|
client.Level = entity.Level;
|
||||||
client.LastConnection = entity.LastConnection;
|
client.LastConnection = entity.LastConnection;
|
||||||
|
@ -15,6 +15,21 @@
|
|||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
<NuGetPackageImportStamp>
|
<NuGetPackageImportStamp>
|
||||||
</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>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
@ -118,22 +133,10 @@
|
|||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<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" />
|
||||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
|
<Reference Include="System.Management" />
|
||||||
<HintPath>..\packages\Microsoft.SqlServer.Compact.4.0.8876.1\lib\net40\System.Data.SqlServerCe.dll</HintPath>
|
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
<Reference Include="System.Web" />
|
<Reference Include="System.Web" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
@ -144,19 +147,20 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Database\Importer.cs" />
|
<Compile Include="Database\Importer.cs" />
|
||||||
<Compile Include="Database\Initializer.cs" />
|
|
||||||
<Compile Include="Database\DatabaseContext.cs" />
|
<Compile Include="Database\DatabaseContext.cs" />
|
||||||
<Compile Include="Database\Models\EFAlias.cs" />
|
<Compile Include="Database\Models\EFAlias.cs" />
|
||||||
<Compile Include="Database\Models\EFAliasLink.cs" />
|
<Compile Include="Database\Models\EFAliasLink.cs" />
|
||||||
<Compile Include="Database\Models\EFClient.cs" />
|
<Compile Include="Database\Models\EFClient.cs" />
|
||||||
<Compile Include="Database\Models\EFPenalty.cs" />
|
<Compile Include="Database\Models\EFPenalty.cs" />
|
||||||
<Compile Include="Database\Models\SharedEntity.cs" />
|
<Compile Include="Database\Models\SharedEntity.cs" />
|
||||||
|
<Compile Include="Database\Repair.cs" />
|
||||||
<Compile Include="Dtos\CommandResponseInfo.cs" />
|
<Compile Include="Dtos\CommandResponseInfo.cs" />
|
||||||
<Compile Include="Dtos\PlayerInfo.cs" />
|
<Compile Include="Dtos\PlayerInfo.cs" />
|
||||||
<Compile Include="Dtos\ClientInfo.cs" />
|
<Compile Include="Dtos\ClientInfo.cs" />
|
||||||
<Compile Include="Dtos\ProfileMeta.cs" />
|
<Compile Include="Dtos\ProfileMeta.cs" />
|
||||||
<Compile Include="Dtos\PenaltyInfo.cs" />
|
<Compile Include="Dtos\PenaltyInfo.cs" />
|
||||||
<Compile Include="Dtos\ServerInfo.cs" />
|
<Compile Include="Dtos\ServerInfo.cs" />
|
||||||
|
<Compile Include="Dtos\SharedInfo.cs" />
|
||||||
<Compile Include="Exceptions\DatabaseException.cs" />
|
<Compile Include="Exceptions\DatabaseException.cs" />
|
||||||
<Compile Include="Helpers\AsyncStatus.cs" />
|
<Compile Include="Helpers\AsyncStatus.cs" />
|
||||||
<Compile Include="Commands\NativeCommands.cs" />
|
<Compile Include="Commands\NativeCommands.cs" />
|
||||||
@ -173,6 +177,11 @@
|
|||||||
<Compile Include="Interfaces\IManager.cs" />
|
<Compile Include="Interfaces\IManager.cs" />
|
||||||
<Compile Include="Interfaces\ISerializable.cs" />
|
<Compile Include="Interfaces\ISerializable.cs" />
|
||||||
<Compile Include="Helpers\MessageToken.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\Alias.cs" />
|
||||||
<Compile Include="Objects\Penalty.cs" />
|
<Compile Include="Objects\Penalty.cs" />
|
||||||
<Compile Include="Command.cs" />
|
<Compile Include="Command.cs" />
|
||||||
@ -197,13 +206,37 @@
|
|||||||
<Compile Include="Utilities.cs" />
|
<Compile Include="Utilities.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Content Include="app.config" />
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<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">
|
<PackageReference Include="Newtonsoft.Json">
|
||||||
<Version>11.0.1</Version>
|
<Version>11.0.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</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" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PostBuildEvent>copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)BUILD\lib"
|
<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>
|
xcopy /Y /I /E "$(TargetDir)*" "$(SolutionDir)BUILD\Lib"</PostBuildEvent>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PreBuildEvent>if exist "$(SolutionDir)BUILD\Plugins" rmdir /Q /S "$(SolutionDir)BUILD\Plugins"
|
<PreBuildEvent>
|
||||||
mkdir "$(SolutionDir)BUILD\Plugins"
|
|
||||||
|
|
||||||
if not exist "$(SolutionDir)BUILD" mkdir "$(SolutionDir)BUILD"
|
if not exist "$(SolutionDir)BUILD" mkdir "$(SolutionDir)BUILD"
|
||||||
if not exist "$(SolutionDir)BUILD\Lib" mkdir "$(SolutionDir)BUILD\Lib"
|
if not exist "$(SolutionDir)BUILD\Lib" mkdir "$(SolutionDir)BUILD\Lib"
|
||||||
if not exist "$(SolutionDir)BUILD\userraw\scripts" mkdir "$(SolutionDir)BUILD\userraw\scripts"</PreBuildEvent>
|
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.Text.RegularExpressions;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Management;
|
||||||
|
|
||||||
using SharedLibrary.Objects;
|
using SharedLibrary.Objects;
|
||||||
using static SharedLibrary.Server;
|
using static SharedLibrary.Server;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace SharedLibrary
|
namespace SharedLibrary
|
||||||
{
|
{
|
||||||
@ -61,14 +63,14 @@ namespace SharedLibrary
|
|||||||
int cID = -1;
|
int cID = -1;
|
||||||
int Ping = -1;
|
int Ping = -1;
|
||||||
Int32.TryParse(playerInfo[2], out Ping);
|
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();
|
long npID = Regex.Match(responseLine, @"([a-z]|[0-9]){16}", RegexOptions.IgnoreCase).Value.ConvertLong();
|
||||||
int.TryParse(playerInfo[0], out cID);
|
int.TryParse(playerInfo[0], out cID);
|
||||||
var regex = Regex.Match(responseLine, @"\d+\.\d+\.\d+.\d+\:\d{1,5}");
|
var regex = Regex.Match(responseLine, @"\d+\.\d+\.\d+.\d+\:\d{1,5}");
|
||||||
int cIP = regex.Value.Split(':')[0].ConvertToIP();
|
int cIP = regex.Value.Split(':')[0].ConvertToIP();
|
||||||
regex = Regex.Match(responseLine, @"[0-9]{1,2}\s+[0-9]+\s+");
|
regex = Regex.Match(responseLine, @"[0-9]{1,2}\s+[0-9]+\s+");
|
||||||
int score = Int32.Parse(regex.Value.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)[1]);
|
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);
|
StatusPlayers.Add(P);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,8 +98,11 @@ namespace SharedLibrary
|
|||||||
{
|
{
|
||||||
if (str == null)
|
if (str == null)
|
||||||
return "";
|
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("/", " /");
|
.Replace("/", " /");
|
||||||
|
return str2.Length > 0 ? str2 : str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -334,11 +339,6 @@ namespace SharedLibrary
|
|||||||
return "1 hour";
|
return "1 hour";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string EscapeMarkdown(this string markdownString)
|
|
||||||
{
|
|
||||||
return markdownString.Replace("<", "\\<").Replace(">", "\\>").Replace("|", "\\|");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Player AsPlayer(this Database.Models.EFClient client)
|
public static Player AsPlayer(this Database.Models.EFClient client)
|
||||||
{
|
{
|
||||||
return client == null ? null : new Player()
|
return client == null ? null : new Player()
|
||||||
@ -361,5 +361,35 @@ namespace SharedLibrary
|
|||||||
CurrentAliasId = client.CurrentAlias.AliasId
|
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 System.IO;
|
||||||
using SharedLibrary.Objects;
|
using SharedLibrary.Objects;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Linq;
|
||||||
#if DEBUG
|
|
||||||
using SharedLibrary.Database;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
namespace IW4MAdmin
|
namespace IW4MAdmin
|
||||||
{
|
{
|
||||||
@ -20,7 +16,7 @@ namespace IW4MAdmin
|
|||||||
public static extern bool AllocConsole();
|
public static extern bool AllocConsole();
|
||||||
static public double Version { get; private set; }
|
static public double Version { get; private set; }
|
||||||
static public ApplicationManager ServerManager = ApplicationManager.GetInstance();
|
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()
|
public static void Start()
|
||||||
{
|
{
|
||||||
@ -29,7 +25,6 @@ namespace IW4MAdmin
|
|||||||
|
|
||||||
Version = 1.6;
|
Version = 1.6;
|
||||||
|
|
||||||
//double.TryParse(CheckUpdate(), out double latestVersion);
|
|
||||||
Console.WriteLine("=====================================================");
|
Console.WriteLine("=====================================================");
|
||||||
Console.WriteLine(" IW4M ADMIN");
|
Console.WriteLine(" IW4M ADMIN");
|
||||||
Console.WriteLine(" by RaidMax ");
|
Console.WriteLine(" by RaidMax ");
|
||||||
@ -40,13 +35,16 @@ namespace IW4MAdmin
|
|||||||
{
|
{
|
||||||
CheckDirectories();
|
CheckDirectories();
|
||||||
|
|
||||||
ServerManager = ApplicationManager.GetInstance();
|
Task.Run(async () =>
|
||||||
ServerManager.Init();
|
{
|
||||||
|
ServerManager = ApplicationManager.GetInstance();
|
||||||
|
SharedLibrary.Database.Repair.Run(ServerManager.Logger);
|
||||||
|
await ServerManager.Init();
|
||||||
|
ServerManager.Start();
|
||||||
|
});
|
||||||
|
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
ServerManager.Start();
|
|
||||||
String userInput;
|
String userInput;
|
||||||
Player Origin = ServerManager.GetClientService().Get(1).Result.AsPlayer();
|
Player Origin = ServerManager.GetClientService().Get(1).Result.AsPlayer();
|
||||||
|
|
||||||
@ -66,6 +64,8 @@ namespace IW4MAdmin
|
|||||||
Console.Write('>');
|
Console.Write('>');
|
||||||
|
|
||||||
} while (ServerManager.Running);
|
} while (ServerManager.Running);
|
||||||
|
|
||||||
|
Console.WriteLine("Shutdown complete");
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,6 @@ using SharedLibrary.Commands;
|
|||||||
using SharedLibrary.Helpers;
|
using SharedLibrary.Helpers;
|
||||||
using SharedLibrary.Exceptions;
|
using SharedLibrary.Exceptions;
|
||||||
using SharedLibrary.Objects;
|
using SharedLibrary.Objects;
|
||||||
using SharedLibrary.Database;
|
|
||||||
using SharedLibrary.Database.Models;
|
|
||||||
using SharedLibrary.Services;
|
using SharedLibrary.Services;
|
||||||
|
|
||||||
namespace IW4MAdmin
|
namespace IW4MAdmin
|
||||||
@ -21,6 +19,7 @@ namespace IW4MAdmin
|
|||||||
{
|
{
|
||||||
private List<Server> _servers;
|
private List<Server> _servers;
|
||||||
public List<Server> Servers => _servers.OrderByDescending(s => s.ClientNum).ToList();
|
public List<Server> Servers => _servers.OrderByDescending(s => s.ClientNum).ToList();
|
||||||
|
public List<int> AdministratorIPs { get; set; }
|
||||||
public ILogger Logger { get; private set; }
|
public ILogger Logger { get; private set; }
|
||||||
public bool Running { get; private set; }
|
public bool Running { get; private set; }
|
||||||
|
|
||||||
@ -34,7 +33,7 @@ namespace IW4MAdmin
|
|||||||
#if FTP_LOG
|
#if FTP_LOG
|
||||||
const int UPDATE_FREQUENCY = 700;
|
const int UPDATE_FREQUENCY = 700;
|
||||||
#else
|
#else
|
||||||
const int UPDATE_FREQUENCY = 300;
|
const int UPDATE_FREQUENCY = 750;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private ApplicationManager()
|
private ApplicationManager()
|
||||||
@ -47,6 +46,7 @@ namespace IW4MAdmin
|
|||||||
ClientSvc = new ClientService();
|
ClientSvc = new ClientService();
|
||||||
AliasSvc = new AliasService();
|
AliasSvc = new AliasService();
|
||||||
PenaltySvc = new PenaltyService();
|
PenaltySvc = new PenaltyService();
|
||||||
|
AdministratorIPs = new List<int>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IList<Server> GetServers()
|
public IList<Server> GetServers()
|
||||||
@ -64,12 +64,12 @@ namespace IW4MAdmin
|
|||||||
return Instance ?? (Instance = new ApplicationManager());
|
return Instance ?? (Instance = new ApplicationManager());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Init()
|
public async Task Init()
|
||||||
{
|
{
|
||||||
#region WEBSERVICE
|
#region DATABASE
|
||||||
// SharedLibrary.WebService.Init();
|
AdministratorIPs = (await ClientSvc.Find(c => c.Level > Player.Permission.Trusted))
|
||||||
//WebSvc = new WebService();
|
.Select(c => c.IPAddress)
|
||||||
//WebSvc.StartScheduler();
|
.ToList();
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region PLUGINS
|
#region PLUGINS
|
||||||
@ -79,7 +79,7 @@ namespace IW4MAdmin
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Plugin.OnLoadAsync(this);
|
await Plugin.OnLoadAsync(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@ -174,6 +174,7 @@ namespace IW4MAdmin
|
|||||||
Commands.Add(new CIP());
|
Commands.Add(new CIP());
|
||||||
Commands.Add(new CMask());
|
Commands.Add(new CMask());
|
||||||
Commands.Add(new CPruneAdmins());
|
Commands.Add(new CPruneAdmins());
|
||||||
|
Commands.Add(new CRestartServer());
|
||||||
|
|
||||||
foreach (Command C in SharedLibrary.Plugins.PluginImporter.ActiveCommands)
|
foreach (Command C in SharedLibrary.Plugins.PluginImporter.ActiveCommands)
|
||||||
Commands.Add(C);
|
Commands.Add(C);
|
||||||
|
@ -10,6 +10,8 @@ using SharedLibrary.Network;
|
|||||||
using SharedLibrary.Interfaces;
|
using SharedLibrary.Interfaces;
|
||||||
using SharedLibrary.Objects;
|
using SharedLibrary.Objects;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using SharedLibrary.Services;
|
||||||
|
using SharedLibrary.Database.Models;
|
||||||
|
|
||||||
namespace IW4MAdmin
|
namespace IW4MAdmin
|
||||||
{
|
{
|
||||||
@ -99,7 +101,7 @@ namespace IW4MAdmin
|
|||||||
await Manager.GetClientService().Update(client);
|
await Manager.GetClientService().Update(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (existingAlias.Name == polledPlayer.Name)
|
else if (existingAlias.Name == polledPlayer.Name)
|
||||||
{
|
{
|
||||||
client.CurrentAlias = existingAlias;
|
client.CurrentAlias = existingAlias;
|
||||||
client.CurrentAliasId = existingAlias.AliasId;
|
client.CurrentAliasId = existingAlias.AliasId;
|
||||||
@ -154,12 +156,13 @@ namespace IW4MAdmin
|
|||||||
if (cNum >= 0 && Players[cNum] != null)
|
if (cNum >= 0 && Players[cNum] != null)
|
||||||
{
|
{
|
||||||
Player Leaving = Players[cNum];
|
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.TotalConnectionTime += (int)(DateTime.UtcNow - Leaving.ConnectionTime).TotalSeconds;
|
||||||
Leaving.LastConnection = DateTime.UtcNow;
|
Leaving.LastConnection = DateTime.UtcNow;
|
||||||
await Manager.GetClientService().Update(Leaving);
|
await Manager.GetClientService().Update(Leaving);
|
||||||
|
|
||||||
Logger.WriteInfo($"Client {Leaving} disconnecting...");
|
|
||||||
await ExecuteEvent(new Event(Event.GType.Disconnect, "", Leaving, null, this));
|
|
||||||
Players[cNum] = null;
|
Players[cNum] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -357,7 +360,11 @@ namespace IW4MAdmin
|
|||||||
|
|
||||||
async Task<int> PollPlayersAsync()
|
async Task<int> PollPlayersAsync()
|
||||||
{
|
{
|
||||||
|
var now = DateTime.Now;
|
||||||
var CurrentPlayers = await this.GetStatusAsync();
|
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++)
|
for (int i = 0; i < Players.Count; i++)
|
||||||
{
|
{
|
||||||
@ -385,9 +392,9 @@ namespace IW4MAdmin
|
|||||||
override public async Task<bool> ProcessUpdatesAsync(CancellationToken cts)
|
override public async Task<bool> ProcessUpdatesAsync(CancellationToken cts)
|
||||||
{
|
{
|
||||||
this.cts = cts;
|
this.cts = cts;
|
||||||
#if DEBUG == false
|
//#if DEBUG == false
|
||||||
try
|
try
|
||||||
#endif
|
//#endif
|
||||||
{
|
{
|
||||||
// first start
|
// first start
|
||||||
if (firstRun)
|
if (firstRun)
|
||||||
@ -504,7 +511,7 @@ namespace IW4MAdmin
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#if DEBUG == false
|
//#if !DEBUG
|
||||||
catch (SharedLibrary.Exceptions.NetworkException)
|
catch (SharedLibrary.Exceptions.NetworkException)
|
||||||
{
|
{
|
||||||
Logger.WriteError($"Could not communicate with {IP}:{Port}");
|
Logger.WriteError($"Could not communicate with {IP}:{Port}");
|
||||||
@ -518,7 +525,7 @@ namespace IW4MAdmin
|
|||||||
Logger.WriteDebug("Error Trace: " + E.StackTrace);
|
Logger.WriteDebug("Error Trace: " + E.StackTrace);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
//#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Initialize()
|
public async Task Initialize()
|
||||||
@ -537,6 +544,7 @@ namespace IW4MAdmin
|
|||||||
await this.GetDvarAsync<int>("sv_maxclients");
|
await this.GetDvarAsync<int>("sv_maxclients");
|
||||||
var gametype = await this.GetDvarAsync<string>("g_gametype");
|
var gametype = await this.GetDvarAsync<string>("g_gametype");
|
||||||
var basepath = await this.GetDvarAsync<string>("fs_basepath");
|
var basepath = await this.GetDvarAsync<string>("fs_basepath");
|
||||||
|
WorkingDirectory = basepath.Value;
|
||||||
var game = await this.GetDvarAsync<string>("fs_game");
|
var game = await this.GetDvarAsync<string>("fs_game");
|
||||||
var logfile = await this.GetDvarAsync<string>("g_log");
|
var logfile = await this.GetDvarAsync<string>("g_log");
|
||||||
var logsync = await this.GetDvarAsync<int>("g_logsync");
|
var logsync = await this.GetDvarAsync<int>("g_logsync");
|
||||||
@ -705,6 +713,11 @@ namespace IW4MAdmin
|
|||||||
|
|
||||||
string mapname = this.GetDvarAsync<string>("mapname").Result.Value;
|
string mapname = this.GetDvarAsync<string>("mapname").Result.Value;
|
||||||
CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map(mapname, mapname);
|
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)
|
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
|
namespace WebfrontCore.Controllers
|
||||||
{
|
{
|
||||||
public class ClientController : Controller
|
public class ClientController : BaseController
|
||||||
{
|
{
|
||||||
public async Task<IActionResult> ProfileAsync(int id)
|
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()
|
var clientDto = new PlayerInfo()
|
||||||
{
|
{
|
||||||
Name = client.Name,
|
Name = client.Name,
|
||||||
@ -38,11 +38,15 @@ namespace WebfrontCore.Controllers
|
|||||||
.OrderBy(i => i)
|
.OrderBy(i => i)
|
||||||
.ToList(),
|
.ToList(),
|
||||||
};
|
};
|
||||||
|
var meta = await MetaService.GetMeta(client.ClientId);
|
||||||
clientDto.Meta.AddRange(await MetaService.GetMeta(client.ClientId));
|
clientDto.Meta.AddRange(Authorized ? meta : meta.Where(m => !m.Sensitive));
|
||||||
clientDto.Meta.AddRange(await IW4MAdmin.ApplicationManager.GetInstance().GetPenaltyService().ReadGetClientPenaltiesAsync(client.ClientId));
|
clientDto.Meta.AddRange(await Manager.GetPenaltyService()
|
||||||
clientDto.Meta.AddRange(await IW4MAdmin.ApplicationManager.GetInstance().GetPenaltyService().ReadGetClientPenaltiesAsync(client.ClientId, false));
|
.ReadGetClientPenaltiesAsync(client.ClientId));
|
||||||
clientDto.Meta = clientDto.Meta.OrderByDescending(m => m.When).ToList();
|
clientDto.Meta.AddRange(await Manager.GetPenaltyService()
|
||||||
|
.ReadGetClientPenaltiesAsync(client.ClientId, false));
|
||||||
|
clientDto.Meta = clientDto.Meta
|
||||||
|
.OrderByDescending(m => m.When)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
ViewBag.Title = clientDto.Name;
|
ViewBag.Title = clientDto.Name;
|
||||||
|
|
||||||
@ -51,7 +55,7 @@ namespace WebfrontCore.Controllers
|
|||||||
|
|
||||||
public async Task<IActionResult> PrivilegedAsync()
|
public async Task<IActionResult> PrivilegedAsync()
|
||||||
{
|
{
|
||||||
var admins = (await IW4MAdmin.ApplicationManager.GetInstance().GetClientService().GetPrivilegedClients())
|
var admins = (await Manager.GetClientService().GetPrivilegedClients())
|
||||||
.Where(a => a.Active)
|
.Where(a => a.Active)
|
||||||
.OrderByDescending(a => a.Level);
|
.OrderByDescending(a => a.Level);
|
||||||
var adminsDict = new Dictionary<SharedLibrary.Objects.Player.Permission, IList<ClientInfo>>();
|
var adminsDict = new Dictionary<SharedLibrary.Objects.Player.Permission, IList<ClientInfo>>();
|
||||||
@ -74,7 +78,7 @@ namespace WebfrontCore.Controllers
|
|||||||
|
|
||||||
public async Task<IActionResult> FindAsync(string clientName)
|
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);
|
.OrderByDescending(c => c.LastConnection);
|
||||||
var clientsDto = clients.Select(c => new PlayerInfo()
|
var clientsDto = clients.Select(c => new PlayerInfo()
|
||||||
{
|
{
|
||||||
|
@ -9,11 +9,11 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace WebfrontCore.Controllers
|
namespace WebfrontCore.Controllers
|
||||||
{
|
{
|
||||||
public class ConsoleController : Controller
|
public class ConsoleController : BaseController
|
||||||
{
|
{
|
||||||
public IActionResult Index()
|
public IActionResult Index()
|
||||||
{
|
{
|
||||||
var activeServers = IW4MAdmin.ApplicationManager.GetInstance().Servers.Select(s => new ServerInfo()
|
var activeServers = Manager.Servers.Select(s => new ServerInfo()
|
||||||
{
|
{
|
||||||
Name = s.Hostname,
|
Name = s.Hostname,
|
||||||
ID = s.GetHashCode(),
|
ID = s.GetHashCode(),
|
||||||
@ -38,10 +38,10 @@ namespace WebfrontCore.Controllers
|
|||||||
IPAddress = intIP
|
IPAddress = intIP
|
||||||
};
|
};
|
||||||
#else
|
#else
|
||||||
var origin = (await IW4MAdmin.ApplicationManager.GetInstance().GetClientService().GetUnique(0)).AsPlayer();
|
var origin = (await Manager.GetClientService().GetUnique(0)).AsPlayer();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var server = IW4MAdmin.ApplicationManager.GetInstance().Servers.First(s => s.GetHashCode() == serverId);
|
var server = Manager.Servers.First(s => s.GetHashCode() == serverId);
|
||||||
origin.CurrentServer = server;
|
origin.CurrentServer = server;
|
||||||
var remoteEvent = new Event(Event.GType.Say, command, origin, null, server);
|
var remoteEvent = new Event(Event.GType.Say, command, origin, null, server);
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ using SharedLibrary.Dtos;
|
|||||||
|
|
||||||
namespace WebfrontCore.Controllers
|
namespace WebfrontCore.Controllers
|
||||||
{
|
{
|
||||||
public class HomeController : Controller
|
public class HomeController : BaseController
|
||||||
{
|
{
|
||||||
public IActionResult Index()
|
public IActionResult Index()
|
||||||
{
|
{
|
||||||
|
@ -9,9 +9,9 @@ using WebfrontCore.ViewComponents;
|
|||||||
|
|
||||||
namespace WebfrontCore.Controllers
|
namespace WebfrontCore.Controllers
|
||||||
{
|
{
|
||||||
public class PenaltyController : Controller
|
public class PenaltyController : BaseController
|
||||||
{
|
{
|
||||||
public IActionResult List()
|
public IActionResult List()
|
||||||
{
|
{
|
||||||
ViewBag.Title = "Penalty List";
|
ViewBag.Title = "Penalty List";
|
||||||
return View();
|
return View();
|
||||||
|
@ -7,15 +7,14 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace WebfrontCore.Controllers
|
namespace WebfrontCore.Controllers
|
||||||
{
|
{
|
||||||
public class ServerController : Controller
|
public class ServerController : BaseController
|
||||||
{
|
{
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ResponseCache(NoStore = true, Duration = 0)]
|
[ResponseCache(NoStore = true, Duration = 0)]
|
||||||
public IActionResult ClientActivity(int id)
|
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)
|
if (s == null)
|
||||||
return View("Error", "Invalid server!");
|
return View("Error", "Invalid server!");
|
||||||
|
|
||||||
|
@ -12,6 +12,12 @@ namespace WebfrontCore.ViewComponents
|
|||||||
{
|
{
|
||||||
public async Task<IViewComponentResult> InvokeAsync(int offset)
|
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 penalties = await IW4MAdmin.ApplicationManager.GetInstance().GetPenaltyService().GetRecentPenalties(15, offset);
|
||||||
var penaltiesDto = penalties.Select(p => new PenaltyInfo()
|
var penaltiesDto = penalties.Select(p => new PenaltyInfo()
|
||||||
{
|
{
|
||||||
@ -23,8 +29,11 @@ namespace WebfrontCore.ViewComponents
|
|||||||
Offense = p.Offense,
|
Offense = p.Offense,
|
||||||
Type = p.Type.ToString(),
|
Type = p.Type.ToString(),
|
||||||
TimePunished = Utilities.GetTimePassed(p.When, false),
|
TimePunished = Utilities.GetTimePassed(p.When, false),
|
||||||
TimeRemaining = DateTime.UtcNow > p.Expires ? "" : Utilities.TimeSpanText(p.Expires - DateTime.UtcNow)
|
TimeRemaining = DateTime.UtcNow > p.Expires ? "" : Utilities.TimeSpanText(p.Expires - DateTime.UtcNow),
|
||||||
}).ToList();
|
Sensitive = p.Type == SharedLibrary.Objects.Penalty.PenaltyType.Flag
|
||||||
|
});
|
||||||
|
|
||||||
|
penaltiesDto = authed ? penaltiesDto.ToList() : penaltiesDto.Where(p => !p.Sensitive).ToList();
|
||||||
|
|
||||||
return View("_List", penaltiesDto);
|
return View("_List", penaltiesDto);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="profile_info" class="text-center text-sm-left pr-3 pl-3">
|
<div id="profile_info" class="text-center text-sm-left pr-3 pl-3">
|
||||||
<div id="profile_name">
|
<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">
|
<div id="profile_aliases" class="pr-0 pr-sm-4 pb-2 mb-2 text-muted">
|
||||||
@{
|
@{
|
||||||
foreach (string alias in Model.Aliases)
|
foreach (string alias in Model.Aliases)
|
||||||
|
@ -8,19 +8,22 @@
|
|||||||
<PackageId>WebfrontCore</PackageId>
|
<PackageId>WebfrontCore</PackageId>
|
||||||
<Platforms>AnyCPU;x86</Platforms>
|
<Platforms>AnyCPU;x86</Platforms>
|
||||||
<ApplicationIcon>wwwroot\favicon.ico</ApplicationIcon>
|
<ApplicationIcon>wwwroot\favicon.ico</ApplicationIcon>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
|
||||||
|
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Remove="Application\Kayak.cs" />
|
<Compile Remove="Application\Kayak.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="App.config" />
|
|
||||||
<None Include="Application\Kayak.cs" />
|
|
||||||
<None Update="wwwroot\**\*">
|
<None Update="wwwroot\**\*">
|
||||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
</None>
|
</None>
|
||||||
@ -60,12 +63,6 @@
|
|||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Update="app.config">
|
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
<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"" />
|
<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>
|
</Target>
|
||||||
|
@ -31,6 +31,6 @@
|
|||||||
<connectionStrings>
|
<connectionStrings>
|
||||||
<add name="DefaultConnection"
|
<add name="DefaultConnection"
|
||||||
providerName="System.Data.SqlServerCe.4.0"
|
providerName="System.Data.SqlServerCe.4.0"
|
||||||
connectionString="Data Source=|DataDirectory|\Database\Database.sdf"/>
|
connectionString="Data Source=|DataDirectory|\Database.sdf"/>
|
||||||
</connectionStrings>
|
</connectionStrings>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
"Logging": {
|
"Logging": {
|
||||||
"IncludeScopes": false,
|
"IncludeScopes": false,
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Debug",
|
"Default": "Trace",
|
||||||
"System": "Information",
|
"System": "Information",
|
||||||
"Microsoft": "Information"
|
"Microsoft": "None"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user