Added additional properties method to allow easier extension to client properties

updated VPN plugin to use WebClient
message is sent to client trying to execute commands before they are authenticated
fixed rare issue with ToAdmins failing
record bullet distance fraction for client kills (_customcallbacks)
change client level/permissions through webfront
ability to tempban through webfront
This commit is contained in:
RaidMax 2018-09-02 16:59:27 -05:00
parent cc7628d058
commit e77ef69ee8
33 changed files with 1099 additions and 177 deletions

View File

@ -17,4 +17,8 @@ xcopy /y "%SolutionDir%Build\Plugins" "%TargetDir%Plugins\"
echo Copying plugins for publish
del %SolutionDir%BUILD\Plugins\Tests.dll
xcopy /Y "%SolutionDir%BUILD\Plugins" "%SolutionDir%Publish\Windows\Plugins\"
xcopy /Y "%SolutionDir%BUILD\Plugins" "%SolutionDir%Publish\WindowsPrerelease\Plugins\"
xcopy /Y "%SolutionDir%BUILD\Plugins" "%SolutionDir%Publish\WindowsPrerelease\Plugins\"
echo Copying script plugins for publish
xcopy /Y "%SolutionDir%Plugins\ScriptPlugins" "%SolutionDir%Publish\Windows\Plugins\"
xcopy /Y "%SolutionDir%Plugins\ScriptPlugins" "%SolutionDir%Publish\WindowsPrerelease\Plugins\"

View File

@ -78,6 +78,7 @@ namespace IW4MAdmin.Application.EventParsers
Type = GameEvent.EventType.Kill,
Data = logLine,
Origin = origin,
Target = target,
Owner = server
};
}
@ -116,19 +117,22 @@ namespace IW4MAdmin.Application.EventParsers
// damage
if (eventType == "D")
{
if (Regex.Match(eventType, @"^(D);((?:bot[0-9]+)|(?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$").Success)
if (!server.CustomCallback)
{
var origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[5].ConvertLong());
var target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
return new GameEvent()
if (Regex.Match(eventType, @"^(D);((?:bot[0-9]+)|(?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$").Success)
{
Type = GameEvent.EventType.Damage,
Data = eventType,
Origin = origin,
Target = target,
Owner = server
};
var origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[5].ConvertLong());
var target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
return new GameEvent()
{
Type = GameEvent.EventType.Damage,
Data = eventType,
Origin = origin,
Target = target,
Owner = server
};
}
}
}

View File

@ -27,56 +27,54 @@ namespace IW4MAdmin.Application
#if DEBUG
Manager.GetLogger().WriteDebug($"Got new event of type {gameEvent.Type} for {gameEvent.Owner} with id {gameEvent.Id}");
#endif
GameEvent sortedEvent = null;
lock (OutOfOrderEvents)
{
sortedEvent = OutOfOrderEvents.Values.FirstOrDefault();
}
// GameEvent sortedEvent = null;
// lock (OutOfOrderEvents)
// {
// sortedEvent = OutOfOrderEvents.Values.FirstOrDefault();
while (sortedEvent?.Id == Interlocked.Read(ref NextEventId))
{
lock (OutOfOrderEvents)
{
OutOfOrderEvents.RemoveAt(0);
}
AddEvent(sortedEvent);
// while (sortedEvent?.Id == Interlocked.Read(ref NextEventId))
// {
// if (OutOfOrderEvents.Count > 0)
// {
// OutOfOrderEvents.RemoveAt(0);
// }
lock (OutOfOrderEvents)
{
sortedEvent = OutOfOrderEvents.Values.FirstOrDefault();
}
}
// AddEvent(sortedEvent);
// sortedEvent = OutOfOrderEvents.Values.FirstOrDefault();
// }
// }
// both the gameEvent Id and the LastEventId are thread safe because we want to synchronize when the
// event occurs
if (gameEvent.Id == Interlocked.Read(ref NextEventId))
{
#if DEBUG == true
Manager.GetLogger().WriteDebug($"sent event with id {gameEvent.Id} to be processed");
#endif
// // both the gameEvent Id and the LastEventId are thread safe because we want to synchronize when the
// // event occurs
// if (gameEvent.Id == Interlocked.Read(ref NextEventId))
// {
//#if DEBUG == true
// Manager.GetLogger().WriteDebug($"sent event with id {gameEvent.Id} to be processed");
//#endif
((Manager as ApplicationManager).OnServerEvent)(this, new GameEventArgs(null, false, gameEvent));
Interlocked.Increment(ref NextEventId);
}
return true;
// Interlocked.Increment(ref NextEventId);
// }
// a "newer" event has been added before and "older" one has been added (due to threads and context switching)
// so me must wait until the next expected one arrives
else
{
#if DEBUG == true
Manager.GetLogger().WriteWarning("Incoming event is out of order");
Manager.GetLogger().WriteDebug($"Expected event Id is {Interlocked.Read(ref NextEventId)}, but got {gameEvent.Id} instead");
#endif
// // a "newer" event has been added before and "older" one has been added (due to threads and context switching)
// // so me must wait until the next expected one arrives
// else
// {
//#if DEBUG == true
// Manager.GetLogger().WriteWarning("Incoming event is out of order");
// Manager.GetLogger().WriteDebug($"Expected event Id is {Interlocked.Read(ref NextEventId)}, but got {gameEvent.Id} instead");
//#endif
// this prevents multiple threads from adding simultaneously
lock (OutOfOrderEvents)
{
if (!OutOfOrderEvents.TryGetValue(gameEvent.Id, out GameEvent discard))
{
OutOfOrderEvents.Add(gameEvent.Id, gameEvent);
}
}
}
return true;
// // this prevents multiple threads from adding simultaneously
// lock (OutOfOrderEvents)
// {
// if (!OutOfOrderEvents.TryGetValue(gameEvent.Id, out GameEvent discard))
// {
// OutOfOrderEvents.Add(gameEvent.Id, gameEvent);
// }
// }
// }
// return true;
}
}
}

View File

@ -50,6 +50,8 @@ namespace IW4MAdmin.Application
Localization.Configure.Initialize(ServerManager.GetApplicationSettings().Configuration()?.CustomLocale);
loc = Utilities.CurrentLocalization.LocalizationIndex;
ServerManager.Logger.WriteInfo($"Version is {Version}");
var api = API.Master.Endpoint.Get();
var version = new API.Master.VersionInfo()

View File

@ -37,7 +37,7 @@ namespace IW4MAdmin.Application
// define what the delagate function looks like
public delegate void OnServerEventEventHandler(object sender, GameEventArgs e);
// expose the event handler so we can execute the events
public OnServerEventEventHandler OnServerEvent { get; set; }
public OnServerEventEventHandler OnServerEvent { get; set; }
public DateTime StartTime { get; private set; }
static ApplicationManager Instance;
@ -81,6 +81,11 @@ namespace IW4MAdmin.Application
if (GameEvent.ShouldOriginEventBeDelayed(newEvent))
{
Logger.WriteDebug($"Delaying origin execution of event type {newEvent.Type} for {newEvent.Origin} because they are not authed");
if (newEvent.Type == GameEvent.EventType.Command)
{
await newEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["SERVER_DELAYED_EVENT_WAIT"]);
}
// offload it to the player to keep
newEvent.Origin.DelayedEvents.Enqueue(newEvent);
return;
@ -95,7 +100,7 @@ namespace IW4MAdmin.Application
return;
}
//// todo: this is a hacky mess
if (newEvent.Origin?.DelayedEvents.Count > 0 &&
@ -104,7 +109,7 @@ namespace IW4MAdmin.Application
var events = newEvent.Origin.DelayedEvents;
// add the delayed event to the queue
while(events.Count > 0)
while (events.Count > 0)
{
var oldEvent = events.Dequeue();
@ -143,7 +148,7 @@ namespace IW4MAdmin.Application
await newEvent.Owner.ExecuteEvent(newEvent);
#if DEBUG
Logger.WriteDebug($"Processed event with id {newEvent.Id}");
Logger.WriteDebug($"Processed event with id {newEvent.Id}");
#endif
}

View File

@ -19,7 +19,6 @@ using SharedLibraryCore.Localization;
using IW4MAdmin.Application.RconParsers;
using IW4MAdmin.Application.EventParsers;
using IW4MAdmin.Application.IO;
using IW4MAdmin.Application.Core;
namespace IW4MAdmin
{
@ -300,40 +299,6 @@ namespace IW4MAdmin
}
}
//// this allows us to catch exceptions but still run it parallel
//async Task pluginHandlingAsync(Task onEvent, string pluginName)
//{
// try
// {
// await onEvent;
// }
// // this happens if a plugin (login) wants to stop commands from executing
// catch (AuthorizationException e)
// {
// await E.Origin.Tell($"{loc["COMMAND_NOTAUTHORIZED"]} - {e.Message}");
// canExecuteCommand = false;
// }
// catch (Exception Except)
// {
// Logger.WriteError($"{loc["SERVER_PLUGIN_ERROR"]} [{pluginName}]");
// Logger.WriteDebug(String.Format("Error Message: {0}", Except.Message));
// Logger.WriteDebug(String.Format("Error Trace: {0}", Except.StackTrace));
// while (Except.InnerException != null)
// {
// Except = Except.InnerException;
// Logger.WriteDebug($"Inner exception: {Except.Message}");
// }
// }
//}
//var pluginTasks = SharedLibraryCore.Plugins.PluginImporter.ActivePlugins.
// Select(p => pluginHandlingAsync(p.OnEventAsync(E, this), p.Name));
//// execute all the plugin updates simultaneously
//await Task.WhenAll(pluginTasks);
foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
{
try
@ -629,6 +594,13 @@ namespace IW4MAdmin
// this are our new connecting clients
foreach (var client in polledClients[0])
{
// this prevents duplicate events from being sent to the event api
if (GetPlayersAsList().Count(c => c.NetworkId == client.NetworkId &&
c.State == Player.ClientState.Connected) != 0)
{
continue;
}
var e = new GameEvent()
{
Type = GameEvent.EventType.Connect,
@ -810,7 +782,7 @@ namespace IW4MAdmin
logfile = await this.GetDvarAsync<string>("g_log");
}
CustomCallback = await ScriptLoaded();
//CustomCallback = await ScriptLoaded();
string mainPath = EventParser.GetGameDir();
#if DEBUG
basepath.Value = @"D:\";

View File

@ -24,20 +24,20 @@ const plugin = {
let usingVPN = false;
try {
let httpRequest = System.Net.WebRequest.Create('https://api.xdefcon.com/proxy/check/?ip=' + origin.IPAddressString);
let response = httpRequest.GetResponse();
let data = response.GetResponseStream();
let streamReader = new System.IO.StreamReader(data);
let jsonResponse = streamReader.ReadToEnd();
streamReader.Dispose();
response.Close();
let parsedJSON = JSON.parse(jsonResponse);
let cl = new System.Net.Http.HttpClient();
let re = cl.GetAsync('https://api.xdefcon.com/proxy/check/?ip=' + origin.IPAddressString).Result;
let co = re.Content;
let parsedJSON = JSON.parse(co.ReadAsStringAsync().Result);
//co.Dispose();
//re.Dispose();
//cl.Dispose();
usingVPN = parsedJSON['success'] && parsedJSON['proxy'];
} catch (e) {
this.logger.WriteError(e.message);
}
if (usingVPN) {
this.logger.WriteInfo(origin + ' is using a VPN (' + origin.IPAddressString + ')');
let library = importNamespace('SharedLibraryCore');
let kickOrigin = new library.Objects.Player();
kickOrigin.ClientId = 1;

View File

@ -324,7 +324,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
/// </summary>
/// <returns></returns>
public async Task AddScriptHit(bool isDamage, DateTime time, Player attacker, Player victim, int serverId, string map, string hitLoc, string type,
string damage, string weapon, string killOrigin, string deathOrigin, string viewAngles, string offset, string isKillstreakKill, string Ads, string snapAngles)
string damage, string weapon, string killOrigin, string deathOrigin, string viewAngles, string offset, string isKillstreakKill, string Ads, string fraction, string snapAngles)
{
var statsSvc = ContextThreads[serverId];
Vector3 vDeathOrigin = null;
@ -362,7 +362,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
return;
}
var kill = new EFClientKill()
var hit = new EFClientKill()
{
Active = true,
AttackerId = attacker.ClientId,
@ -380,11 +380,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
When = time,
IsKillstreakKill = isKillstreakKill[0] != '0',
AdsPercent = float.Parse(Ads),
Fraction = double.Parse(fraction),
IsKill = !isDamage,
AnglesList = snapshotAngles
};
if (kill.DeathType == IW4Info.MeansOfDeath.MOD_SUICIDE &&
kill.Damage == 100000)
if (hit.DeathType == IW4Info.MeansOfDeath.MOD_SUICIDE &&
hit.Damage == 100000)
{
// suicide by switching teams so let's not count it against them
return;
@ -395,7 +397,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
await AddStandardKill(attacker, victim);
}
if (kill.IsKillstreakKill)
if (hit.IsKillstreakKill)
{
return;
}
@ -406,11 +408,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
clientStatsSvc.Update(clientStats);
// increment their hit count
if (kill.DeathType == IW4Info.MeansOfDeath.MOD_PISTOL_BULLET ||
kill.DeathType == IW4Info.MeansOfDeath.MOD_RIFLE_BULLET ||
kill.DeathType == IW4Info.MeansOfDeath.MOD_HEAD_SHOT)
if (hit.DeathType == IW4Info.MeansOfDeath.MOD_PISTOL_BULLET ||
hit.DeathType == IW4Info.MeansOfDeath.MOD_RIFLE_BULLET ||
hit.DeathType == IW4Info.MeansOfDeath.MOD_HEAD_SHOT)
{
clientStats.HitLocations.Single(hl => hl.Location == kill.HitLoc).HitCount += 1;
clientStats.HitLocations.Single(hl => hl.Location == hit.HitLoc).HitCount += 1;
}
if (Plugin.Config.Configuration().EnableAntiCheat)
@ -478,11 +480,17 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
}
}
await executePenalty(clientDetection.ProcessKill(kill, isDamage));
await executePenalty(clientDetection.ProcessKill(hit, isDamage));
await executePenalty(clientDetection.ProcessTotalRatio(clientStats));
await clientStatsSvc.SaveChangesAsync();
}
using (var ctx = new DatabaseContext())
{
ctx.Set<EFClientKill>().Add(hit);
await ctx.SaveChangesAsync();
}
}
public async Task AddStandardKill(Player attacker, Player victim)

View File

@ -29,6 +29,8 @@ namespace IW4MAdmin.Plugins.Stats.Models
public Vector3 DeathOrigin { get; set; }
public Vector3 ViewAngles { get; set; }
public DateTime When { get; set; }
public double Fraction { get; set; }
public bool IsKill { get; set; }
// http://wiki.modsrepository.com/index.php?title=Call_of_Duty_5:_Gameplay_standards for conversion to meters
[NotMapped]
public double Distance => Vector3.Distance(KillOrigin, DeathOrigin) * 0.0254;

View File

@ -73,10 +73,10 @@ namespace IW4MAdmin.Plugins.Stats
break;
case GameEvent.EventType.ScriptKill:
string[] killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
if (killInfo.Length >= 13)
if (killInfo.Length >= 14)
{
await Manager.AddScriptHit(false, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8],
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13]);
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14]);
}
break;
case GameEvent.EventType.Kill:
@ -84,15 +84,15 @@ namespace IW4MAdmin.Plugins.Stats
await Manager.AddStandardKill(E.Origin, E.Target);
break;
case GameEvent.EventType.Damage:
// if (!E.Owner.CustomCallback)
Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Target.ClientId, E.Owner.GetHashCode());
if (!E.Owner.CustomCallback)
Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Target.ClientId, E.Owner.GetHashCode());
break;
case GameEvent.EventType.ScriptDamage:
killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
if (killInfo.Length >= 13)
if (killInfo.Length >= 14)
{
await Manager.AddScriptHit(true, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8],
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13]);
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14]);
}
break;
}

View File

@ -59,7 +59,7 @@ namespace IW4MAdmin.Plugins.Stats.Web.Controllers
}
[HttpGet]
[Authorize]
// [Authorize]
public async Task<IActionResult> GetAutomatedPenaltyInfoAsync(int clientId)
{
using (var ctx = new SharedLibraryCore.Database.DatabaseContext())

View File

@ -0,0 +1,29 @@
using SharedLibraryCore.Objects;
using System;
using System.Collections.Generic;
using System.Text;
using Xunit;
namespace Tests
{
public class ClientTests
{
[Fact]
public void SetAdditionalPropertyShouldSucceed()
{
var client = new Player();
int newProp = 5;
client.SetAdditionalProperty("NewProp", newProp);
}
[Fact]
public void GetAdditionalPropertyShouldSucceed()
{
var client = new Player();
int newProp = 5;
client.SetAdditionalProperty("NewProp", newProp);
Assert.True(client.GetAdditionalProperty<int>("NewProp") == 5, "added property does not match retrieved property");
}
}
}

View File

@ -43,7 +43,6 @@ namespace SharedLibraryCore.Database
currentPath = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
$"{Path.DirectorySeparatorChar}{currentPath}" :
currentPath;
// todo: fix later
var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = $"{currentPath}{Path.DirectorySeparatorChar}Database.db".Substring(6) };
var connectionString = connectionStringBuilder.ToString();
@ -100,10 +99,6 @@ namespace SharedLibraryCore.Database
// adapted from
// https://aleemkhan.wordpress.com/2013/02/28/dynamically-adding-dbset-properties-in-dbcontext-for-entity-framework-code-first/
//#if DEBUG == TRUE
// // foreach (string dllPath in Directory.GetFiles($"{Utilities.OperatingDirectory}Plugins"))
//#else
//todo: fix the debug thingie for entity scanning
IEnumerable<string> directoryFiles;
string pluginDir = $@"{Environment.CurrentDirectory}{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}Debug{Path.DirectorySeparatorChar}netcoreapp2.0{Path.DirectorySeparatorChar}Plugins";
@ -119,9 +114,11 @@ namespace SharedLibraryCore.Database
}
directoryFiles = Directory.GetFiles(pluginDir).Where(f => f.Contains(".dll"));
foreach (string dllPath in directoryFiles)
//#endif
#if DEBUG == TRUE
foreach (string dllPath in Directory.GetFiles(@"C:\Projects\IW4M-Admin\Application\bin\Debug\netcoreapp2.1\Plugins"))
#else
foreach (string dllPath in directoryFiles)
#endif
{
Assembly library;
try

View File

@ -16,6 +16,7 @@ namespace SharedLibraryCore.Dtos
public long NetworkId { get; set; }
public List<string> Aliases { get; set; }
public List<string> IPs { get; set; }
public bool HasActivePenalty { get; set; }
public int ConnectionCount { get; set; }
public string LastSeen { get; set; }
public string FirstSeen { get; set; }

View File

@ -0,0 +1,665 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using SharedLibraryCore.Database;
namespace SharedLibraryCore.Migrations
{
[DbContext(typeof(DatabaseContext))]
[Migration("20180902035612_AddFractionAndIsKill")]
partial class AddFractionAndIsKill
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.2-rtm-30932");
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
{
b.Property<int>("SnapshotId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ClientId");
b.Property<int>("CurrentSessionLength");
b.Property<double>("CurrentStrain");
b.Property<int?>("CurrentViewAngleVector3Id");
b.Property<int>("Deaths");
b.Property<double>("Distance");
b.Property<double>("EloRating");
b.Property<int?>("HitDestinationVector3Id");
b.Property<int>("HitLocation");
b.Property<int?>("HitOriginVector3Id");
b.Property<int>("HitType");
b.Property<int>("Hits");
b.Property<int>("Kills");
b.Property<int?>("LastStrainAngleVector3Id");
b.Property<double>("SessionAngleOffset");
b.Property<double>("SessionSPM");
b.Property<int>("SessionScore");
b.Property<double>("StrainAngleBetween");
b.Property<int>("TimeSinceLastEvent");
b.Property<int>("WeaponId");
b.Property<DateTime>("When");
b.HasKey("SnapshotId");
b.HasIndex("ClientId");
b.HasIndex("CurrentViewAngleVector3Id");
b.HasIndex("HitDestinationVector3Id");
b.HasIndex("HitOriginVector3Id");
b.HasIndex("LastStrainAngleVector3Id");
b.ToTable("EFACSnapshot");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
{
b.Property<long>("KillId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("AttackerId");
b.Property<int>("Damage");
b.Property<int?>("DeathOriginVector3Id");
b.Property<int>("DeathType");
b.Property<double>("Fraction");
b.Property<int>("HitLoc");
b.Property<bool>("IsKill");
b.Property<int?>("KillOriginVector3Id");
b.Property<int>("Map");
b.Property<int>("ServerId");
b.Property<int>("VictimId");
b.Property<int?>("ViewAnglesVector3Id");
b.Property<int>("Weapon");
b.Property<DateTime>("When");
b.HasKey("KillId");
b.HasIndex("AttackerId");
b.HasIndex("DeathOriginVector3Id");
b.HasIndex("KillOriginVector3Id");
b.HasIndex("ServerId");
b.HasIndex("VictimId");
b.HasIndex("ViewAnglesVector3Id");
b.ToTable("EFClientKills");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
{
b.Property<long>("MessageId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ClientId");
b.Property<string>("Message");
b.Property<int>("ServerId");
b.Property<DateTime>("TimeSent");
b.HasKey("MessageId");
b.HasIndex("ClientId");
b.HasIndex("ServerId");
b.ToTable("EFClientMessages");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
{
b.Property<int>("RatingHistoryId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ClientId");
b.HasKey("RatingHistoryId");
b.HasIndex("ClientId");
b.ToTable("EFClientRatingHistory");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
{
b.Property<int>("ClientId");
b.Property<int>("ServerId");
b.Property<bool>("Active");
b.Property<int>("Deaths");
b.Property<double>("EloRating");
b.Property<int>("Kills");
b.Property<double>("MaxStrain");
b.Property<double>("RollingWeightedKDR");
b.Property<double>("SPM");
b.Property<double>("Skill");
b.Property<int>("TimePlayed");
b.HasKey("ClientId", "ServerId");
b.HasIndex("ServerId");
b.ToTable("EFClientStatistics");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
{
b.Property<int>("HitLocationCountId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ClientId")
.HasColumnName("EFClientStatistics_ClientId");
b.Property<int>("HitCount");
b.Property<float>("HitOffsetAverage");
b.Property<int>("Location");
b.Property<float>("MaxAngleDistance");
b.Property<int>("ServerId")
.HasColumnName("EFClientStatistics_ServerId");
b.HasKey("HitLocationCountId");
b.HasIndex("ServerId");
b.HasIndex("ClientId", "ServerId");
b.ToTable("EFHitLocationCounts");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
{
b.Property<int>("RatingId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ActivityAmount");
b.Property<bool>("Newest");
b.Property<double>("Performance");
b.Property<int>("Ranking");
b.Property<int>("RatingHistoryId");
b.Property<int?>("ServerId");
b.HasKey("RatingId");
b.HasIndex("RatingHistoryId");
b.HasIndex("ServerId");
b.ToTable("EFRating");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServer", b =>
{
b.Property<int>("ServerId");
b.Property<bool>("Active");
b.Property<int>("Port");
b.HasKey("ServerId");
b.ToTable("EFServers");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
{
b.Property<int>("StatisticId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ServerId");
b.Property<long>("TotalKills");
b.Property<long>("TotalPlayTime");
b.HasKey("StatisticId");
b.HasIndex("ServerId");
b.ToTable("EFServerStatistics");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
{
b.Property<int>("AliasId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<DateTime>("DateAdded");
b.Property<int>("IPAddress");
b.Property<int>("LinkId");
b.Property<string>("Name")
.IsRequired();
b.HasKey("AliasId");
b.HasIndex("IPAddress");
b.HasIndex("LinkId");
b.ToTable("EFAlias");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAliasLink", b =>
{
b.Property<int>("AliasLinkId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.HasKey("AliasLinkId");
b.ToTable("EFAliasLinks");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b =>
{
b.Property<int>("ChangeHistoryId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<string>("Comment")
.HasMaxLength(128);
b.Property<int>("OriginEntityId");
b.Property<int>("TargetEntityId");
b.Property<DateTime>("TimeChanged");
b.Property<int>("TypeOfChange");
b.HasKey("ChangeHistoryId");
b.ToTable("EFChangeHistory");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
{
b.Property<int>("ClientId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("AliasLinkId");
b.Property<int>("Connections");
b.Property<int>("CurrentAliasId");
b.Property<DateTime>("FirstConnection");
b.Property<DateTime>("LastConnection");
b.Property<int>("Level");
b.Property<bool>("Masked");
b.Property<long>("NetworkId");
b.Property<string>("Password");
b.Property<string>("PasswordSalt");
b.Property<int>("TotalConnectionTime");
b.HasKey("ClientId");
b.HasIndex("AliasLinkId");
b.HasIndex("CurrentAliasId");
b.HasIndex("NetworkId")
.IsUnique();
b.ToTable("EFClients");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
{
b.Property<int>("MetaId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<int>("ClientId");
b.Property<DateTime>("Created");
b.Property<string>("Extra");
b.Property<string>("Key")
.IsRequired();
b.Property<DateTime>("Updated");
b.Property<string>("Value")
.IsRequired();
b.HasKey("MetaId");
b.HasIndex("ClientId");
b.ToTable("EFMeta");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
{
b.Property<int>("PenaltyId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<string>("AutomatedOffense");
b.Property<DateTime>("Expires");
b.Property<int>("LinkId");
b.Property<int>("OffenderId");
b.Property<string>("Offense")
.IsRequired();
b.Property<int>("PunisherId");
b.Property<int>("Type");
b.Property<DateTime>("When");
b.HasKey("PenaltyId");
b.HasIndex("LinkId");
b.HasIndex("OffenderId");
b.HasIndex("PunisherId");
b.ToTable("EFPenalties");
});
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
{
b.Property<int>("Vector3Id")
.ValueGeneratedOnAdd();
b.Property<int?>("EFACSnapshotSnapshotId");
b.Property<float>("X");
b.Property<float>("Y");
b.Property<float>("Z");
b.HasKey("Vector3Id");
b.HasIndex("EFACSnapshotSnapshotId");
b.ToTable("Vector3");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
.WithMany()
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Helpers.Vector3", "CurrentViewAngle")
.WithMany()
.HasForeignKey("CurrentViewAngleVector3Id");
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitDestination")
.WithMany()
.HasForeignKey("HitDestinationVector3Id");
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitOrigin")
.WithMany()
.HasForeignKey("HitOriginVector3Id");
b.HasOne("SharedLibraryCore.Helpers.Vector3", "LastStrainAngle")
.WithMany()
.HasForeignKey("LastStrainAngleVector3Id");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Attacker")
.WithMany()
.HasForeignKey("AttackerId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Helpers.Vector3", "DeathOrigin")
.WithMany()
.HasForeignKey("DeathOriginVector3Id");
b.HasOne("SharedLibraryCore.Helpers.Vector3", "KillOrigin")
.WithMany()
.HasForeignKey("KillOriginVector3Id");
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
.WithMany()
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Victim")
.WithMany()
.HasForeignKey("VictimId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Helpers.Vector3", "ViewAngles")
.WithMany()
.HasForeignKey("ViewAnglesVector3Id");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
.WithMany()
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
.WithMany()
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
.WithMany()
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
.WithMany()
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
.WithMany()
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
.WithMany()
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
.WithMany()
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics")
.WithMany("HitLocations")
.HasForeignKey("ClientId", "ServerId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
{
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", "RatingHistory")
.WithMany("Ratings")
.HasForeignKey("RatingHistoryId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
.WithMany()
.HasForeignKey("ServerId");
});
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
{
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
.WithMany()
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
.WithMany("Children")
.HasForeignKey("LinkId")
.OnDelete(DeleteBehavior.Restrict);
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "AliasLink")
.WithMany()
.HasForeignKey("AliasLinkId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Database.Models.EFAlias", "CurrentAlias")
.WithMany()
.HasForeignKey("CurrentAliasId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
.WithMany("Meta")
.HasForeignKey("ClientId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
{
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
.WithMany("ReceivedPenalties")
.HasForeignKey("LinkId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Offender")
.WithMany("ReceivedPenalties")
.HasForeignKey("OffenderId")
.OnDelete(DeleteBehavior.Restrict);
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Punisher")
.WithMany("AdministeredPenalties")
.HasForeignKey("PunisherId")
.OnDelete(DeleteBehavior.Restrict);
});
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
{
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot")
.WithMany("PredictedViewAngles")
.HasForeignKey("EFACSnapshotSnapshotId");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,59 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace SharedLibraryCore.Migrations
{
public partial class AddFractionAndIsKill : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<double>(
name: "Fraction",
table: "EFClientKills",
nullable: false,
defaultValue: 0.0);
migrationBuilder.AddColumn<bool>(
name: "IsKill",
table: "EFClientKills",
nullable: false,
defaultValue: false);
migrationBuilder.CreateTable(
name: "EFChangeHistory",
columns: table => new
{
Active = table.Column<bool>(nullable: false),
ChangeHistoryId = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
OriginEntityId = table.Column<int>(nullable: false),
TargetEntityId = table.Column<int>(nullable: false),
TypeOfChange = table.Column<int>(nullable: false),
TimeChanged = table.Column<DateTime>(nullable: false),
Comment = table.Column<string>(maxLength: 128, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_EFChangeHistory", x => x.ChangeHistoryId);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "EFChangeHistory");
migrationBuilder.DropIndex(
name: "IX_Vector3_EFACSnapshotSnapshotId",
table: "Vector3");
migrationBuilder.DropColumn(
name: "Fraction",
table: "EFClientKills");
migrationBuilder.DropColumn(
name: "IsKill",
table: "EFClientKills");
}
}
}

View File

@ -1,13 +1,9 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Storage.Internal;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using SharedLibraryCore.Database;
using SharedLibraryCore.Objects;
using System;
namespace SharedLibraryCore.Migrations
{
@ -18,7 +14,7 @@ namespace SharedLibraryCore.Migrations
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.0.2-rtm-10011");
.HasAnnotation("ProductVersion", "2.1.2-rtm-30932");
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
{
@ -99,8 +95,12 @@ namespace SharedLibraryCore.Migrations
b.Property<int>("DeathType");
b.Property<double>("Fraction");
b.Property<int>("HitLoc");
b.Property<bool>("IsKill");
b.Property<int?>("KillOriginVector3Id");
b.Property<int>("Map");
@ -331,6 +331,29 @@ namespace SharedLibraryCore.Migrations
b.ToTable("EFAliasLinks");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b =>
{
b.Property<int>("ChangeHistoryId")
.ValueGeneratedOnAdd();
b.Property<bool>("Active");
b.Property<string>("Comment")
.HasMaxLength(128);
b.Property<int>("OriginEntityId");
b.Property<int>("TargetEntityId");
b.Property<DateTime>("TimeChanged");
b.Property<int>("TypeOfChange");
b.HasKey("ChangeHistoryId");
b.ToTable("EFChangeHistory");
});
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
{
b.Property<int>("ClientId")

View File

@ -34,15 +34,45 @@ namespace SharedLibraryCore.Objects
public enum Permission
{
/// <summary>
/// client has been banned
/// </summary>
Banned = -1,
/// <summary>
/// default client state upon first connect
/// </summary>
User = 0,
/// <summary>
/// client has been flagged
/// </summary>
Flagged = 1,
/// <summary>
/// client is trusted
/// </summary>
Trusted = 2,
/// <summary>
/// client is a moderator
/// </summary>
Moderator = 3,
/// <summary>
/// client is an administrator
/// </summary>
Administrator = 4,
/// <summary>
/// client is a senior administrator
/// </summary>
SeniorAdmin = 5,
/// <summary>
/// client is a owner
/// </summary>
Owner = 6,
/// <summary>
/// not used
/// </summary>
Creator = 7,
/// <summary>
/// reserved for default account
/// </summary>
Console = 8
}
@ -51,12 +81,10 @@ namespace SharedLibraryCore.Objects
ConnectionTime = DateTime.UtcNow;
ClientNumber = -1;
DelayedEvents = new Queue<GameEvent>();
_additionalProperties = new Dictionary<string, object>();
}
public override string ToString()
{
return $"{Name}::{NetworkId}";
}
public override string ToString() => $"{Name}::{NetworkId}";
public String GetLastConnection()
{
@ -105,7 +133,10 @@ namespace SharedLibraryCore.Objects
{
await CurrentServer.Ban(Message, this, Sender);
}
[NotMapped]
Dictionary<string, object> _additionalProperties;
public T GetAdditionalProperty<T>(string name) => (T)_additionalProperties[name];
public void SetAdditionalProperty(string name, object value) => _additionalProperties.Add(name, value);
[NotMapped]
public int ClientNumber { get; set; }
[NotMapped]
@ -150,9 +181,6 @@ namespace SharedLibraryCore.Objects
return ((Player)obj).NetworkId == NetworkId;
}
public override int GetHashCode()
{
return NetworkId.GetHashCode();
}
public override int GetHashCode() => (int)NetworkId;
}
}

View File

@ -61,7 +61,7 @@ namespace SharedLibraryCore
ScriptEngine = new Jint.Engine(cfg =>
cfg.AllowClr(new[]
{
typeof(System.Net.WebRequest).Assembly,
typeof(System.Net.Http.HttpClient).Assembly,
typeof(Objects.Player).Assembly,
})
.CatchClrExceptions());

View File

@ -179,13 +179,9 @@ namespace SharedLibraryCore
/// <param name="message">Message to send out</param>
public async Task ToAdmins(String message)
{
foreach (Player P in Players)
foreach (var client in GetPlayersAsList().Where(c => c.Level > Player.Permission.Flagged))
{
if (P == null)
continue;
if (P.Level > Player.Permission.Flagged)
await P.Tell(message);
await client.Tell(message);
}
}

View File

@ -181,7 +181,7 @@ namespace SharedLibraryCore.Services
var pi = ((PenaltyInfo)p.Value);
if (pi.TimeRemaining.Length > 0)
pi.TimeRemaining = (DateTime.Parse(((PenaltyInfo)p.Value).TimeRemaining) - now).TimeSpanText();
});
return list;
}
@ -235,6 +235,8 @@ namespace SharedLibraryCore.Services
public async Task<List<EFPenalty>> GetActivePenaltiesAsync(int aliasId, int ip = 0)
{
var now = DateTime.UtcNow;
using (var context = new DatabaseContext())
{
var iqPenalties = await (from link in context.AliasLinks
@ -242,6 +244,8 @@ namespace SharedLibraryCore.Services
join penalty in context.Penalties
on link.AliasLinkId equals penalty.LinkId
where penalty.Active
where penalty.Expires > now
orderby penalty.When descending
select penalty).ToListAsync();
if (ip != 0)
{
@ -250,6 +254,8 @@ namespace SharedLibraryCore.Services
join penalty in context.Penalties
on alias.LinkId equals penalty.LinkId
where penalty.Active
where penalty.Expires > now
orderby penalty.When descending
select penalty).ToListAsync());
}
return iqPenalties;

View File

@ -20,6 +20,7 @@
<PackageReference Include="Jint" Version="3.0.0-beta-1249" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.2" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="2.1.1" />

View File

@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using SharedLibraryCore;
using WebfrontCore.ViewModels;
using static SharedLibraryCore.Objects.Player;
namespace WebfrontCore.Controllers
{
@ -22,6 +23,20 @@ namespace WebfrontCore.Controllers
{
Name = "Reason",
Label = Localization["WEBFRONT_ACTION_LABEL_REASON"],
},
new InputInfo()
{
Name ="Duration",
Label=Localization["WEBFRONT_ACTION_LABEL_DURATION"],
Type="select",
Values = new Dictionary<string, string>()
{
{"1", $"1 {Localization["GLOBAL_TIME_HOUR"]}" },
{"2", $"6 {Localization["GLOBAL_TIME_HOURS"]}" },
{"3", $"1 {Localization["GLOBAL_TIME_DAY"]}" },
{"4", $"1 {Localization["GLOBAL_TIME_WEEK"]}" },
{"5", $"{Localization["WEBFRONT_ACTION_SELECTION_PERMANENT"]}" },
}
}
},
Action = "BanAsync"
@ -30,14 +45,36 @@ namespace WebfrontCore.Controllers
return View("_ActionForm", info);
}
public async Task<IActionResult> BanAsync(int targetId, string Reason)
public async Task<IActionResult> BanAsync(int targetId, string Reason, int Duration)
{
string duration = string.Empty;
switch (Duration)
{
case 1:
duration = "1h";
break;
case 2:
duration = "6h";
break;
case 3:
duration = "1d";
break;
case 4:
duration = "1w";
break;
}
string command = Duration == 5 ?
$"!ban @{targetId} {Reason}" :
$"!tempban @{targetId} {duration} {Reason}";
var server = Manager.GetServers().First();
return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new
{
serverId = server.GetHashCode(),
command = $"!ban @{targetId} {Reason}"
command
}));
}
@ -102,5 +139,42 @@ namespace WebfrontCore.Controllers
{
return await Task.FromResult(RedirectToAction("LoginAsync", "Account", new { clientId, password }));
}
public IActionResult EditForm()
{
var info = new ActionInfo()
{
ActionButtonLabel = Localization["WEBFRONT_ACTION_LABEL_EDIT"],
Name = "Edit",
Inputs = new List<InputInfo>()
{
new InputInfo()
{
Name ="level",
Label=Localization["WEBFRONT_PROFILE_LEVEL"],
Type="select",
Values = Enum.GetValues(typeof(Permission)).OfType<Permission>()
.Where(p => p <= Client.Level)
.Where(p => p != Permission.Banned)
.Where(p => p != Permission.Flagged)
.ToDictionary(p => p.ToString(), p=> p.ToLocalizedLevelName())
},
},
Action = "EditAsync"
};
return View("_ActionForm", info);
}
public async Task<IActionResult> EditAsync(int targetId, string level)
{
var server = Manager.GetServers().First();
return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new
{
serverId = server.GetHashCode(),
command = $"!setlevel @{targetId} {level}"
}));
}
}
}

View File

@ -7,6 +7,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using static SharedLibraryCore.Objects.Penalty;
namespace WebfrontCore.Controllers
{
@ -20,6 +21,8 @@ namespace WebfrontCore.Controllers
return NotFound();
}
var activePenalties = await Manager.GetPenaltyService().GetActivePenaltiesAsync(client.AliasLinkId, client.IPAddress);
#if DEBUG
Authorized = true;
#endif
@ -48,6 +51,7 @@ namespace WebfrontCore.Controllers
.Distinct()
.OrderBy(i => i)
.ToList(),
HasActivePenalty = activePenalties.Count > 0,
Online = Manager.GetActiveClients().FirstOrDefault(c => c.ClientId == client.ClientId) != null,
TimeOnline = (DateTime.UtcNow - client.LastConnection).TimeSpanText(),
LinkedAccounts = client.LinkedAccounts

View File

@ -12,5 +12,7 @@ namespace WebfrontCore.ViewModels
public string Placeholder { get; set; }
public string Type { get; set; }
public string Value { get; set; }
public Dictionary<string, string> Values { get; set; }
public bool Checked { get; set; }
}
}

View File

@ -5,19 +5,41 @@
<form class="action-form" action="/Action/@Model.Action">
@foreach (var input in Model.Inputs)
{
<div class="input-group mb-3">
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon-@input.Name">@input.Label</span>
</div>
@{
string inputType = input.Type ?? "text";
string value = input.Value ?? "";
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon-@input.Name">@input.Label</span>
</div>
@{
string inputType = input.Type ?? "text";
string value = input.Value ?? "";
<input type="@inputType" name="@input.Name" value="@value" class="form-control" placeholder="@input.Placeholder" aria-label="@input.Name" aria-describedby="basic-addon-@input.Name">
if (inputType == "select")
{
<select name="@input.Name" class="form-control" aria-label="@input.Name" aria-describedby="basic-addon-@input.Name">
@foreach (var item in input.Values)
{
<option value="@item.Key">@item.Value</option>
}
</select>
}
</div>
else if (inputType == "checkbox")
{
<div class="btn-group-toggle" data-toggle="buttons">
<label class="btn btn-primary active">
<input type="checkbox" name="@input.Name" @(input.Checked ? "checked" : "") autocomplete="off">@input.Label
</label>
</div>
}
else
{
<input type="@inputType" name="@input.Name" value="@value" class="form-control" placeholder="@input.Placeholder" aria-label="@input.Name" aria-describedby="basic-addon-@input.Name">
}
}
</div>
}
<button type="submit" class="btn btn-block btn-primary">@Model.ActionButtonLabel</button>
</form>

View File

@ -33,18 +33,20 @@
{
<div class="d-flex d-md-inline-flex justify-content-center order-1">
<div id="profile_aliases_btn" class="oi oi-caret-bottom h3 ml-0 ml-md-2"></div>
@if (Model.LevelInt < (int)ViewBag.User.Level &&
(SharedLibraryCore.Objects.Player.Permission)Model.LevelInt != SharedLibraryCore.Objects.Player.Permission.Banned)
@if (Model.LevelInt != -1)
{
<div id="profile_action_edit_btn" class="profile-action oi oi-cog text-muted h3 ml-2" title="Client Options" data-action="edit" aria-hidden="true"></div>
}
@if (Model.LevelInt < (int)ViewBag.User.Level && !Model.HasActivePenalty)
{
<div id="profile_action_ban_btn" class="profile-action oi oi-lock-unlocked text-success h3 ml-2" title="Ban Client" data-action="ban" aria-hidden="true"></div>
}
@if (Model.LevelInt < (int)ViewBag.User.Level &&
(SharedLibraryCore.Objects.Player.Permission)Model.LevelInt == SharedLibraryCore.Objects.Player.Permission.Banned)
@if (Model.LevelInt < (int)ViewBag.User.Level && Model.HasActivePenalty)
{
<div id="profile_action_unban_btn" class="profile-action oi oi-lock-locked text-danger h3 ml-2" title="Unban Client" data-action="unban" aria-hidden="true"></div>
}
</div>
<div id="profile_aliases" class="pr-0 pr-sm-4 pb-2 mb-2 text-muted order-0">

View File

@ -6,6 +6,7 @@
@{
foreach (var response in Model)
{
<span class="text-success">@response.Response</span><br />
<span class="text-muted">@response.Response</span><br />
}
<hr />
}

View File

@ -6321,6 +6321,9 @@ a.link-inverse:hover {
#console_command_response {
min-height: 20rem; }
#console_command_response hr {
border-color: #6c757d; }
#console .form-control, #console button {
border-radius: 0;
border-color: #007ACC;
@ -6339,7 +6342,7 @@ a.link-inverse:hover {
form *, select {
border-radius: 0 !important; }
select {
#penalty_filter_selection {
border-left: none !important;
border-right: none !important;
border-bottom: none !important;

View File

@ -96,6 +96,10 @@ a.link-inverse:hover {
min-height: 20rem;
}
#console_command_response hr {
border-color: #6c757d;
}
#console .form-control, #console button {
border-radius: 0;
border-color: $primary;
@ -119,7 +123,7 @@ form *, select {
border-radius: 0 !important;
}
select {
#penalty_filter_selection {
border-left: none !important;
border-right: none !important;
border-bottom: none !important;
@ -205,4 +209,3 @@ select {
.client-message, .automated-penalty-info-detailed {
cursor: pointer;
}

View File

@ -24,7 +24,7 @@ $(document).ready(function () {
* hide loader when clicking
*/
$(document).click(function (e) {
hideLoader()
//hideLoader()
});
/*
@ -39,7 +39,7 @@ $(document).ready(function () {
$('#actionModal').modal();
})
.fail(function (jqxhr, textStatus, error) {
$('#actionModal .modal-message').text('Error &mdash ' + error);
$('#actionModal .modal-message').text('Error ' + error);
$('#actionModal').modal();
$('#actionModal .modal-message').fadeIn('fast');
});

View File

@ -14,7 +14,7 @@
$.get('/Console/ExecuteAsync', { serverId: serverId, command: command })
.done(function (response) {
hideLoader();
$('#console_command_response').html(response);
$('#console_command_response').append(response);
$('#console_command_value').val("");
})
.fail(function (jqxhr, textStatus, error) {

View File

@ -14,6 +14,7 @@ init()
level.callbackPlayerDamage = ::Callback_PlayerDamage;
}
onPlayerConnect(player)
{
for(;;)
@ -111,6 +112,11 @@ waitForAdditionalAngles(logString)
logPrint(logString + ";" + anglesStr + "\n");
}
vectorScale(vector, scale)
{
return (vector[0] * scale, vector[1] * scale, vector[2] * scale);
}
Process_Hit(type, attacker, sHitLoc, sMeansOfDeath, iDamage, sWeapon)
{
victim = self;
@ -123,7 +129,12 @@ Process_Hit(type, attacker, sHitLoc, sMeansOfDeath, iDamage, sWeapon)
location = victim GetTagOrigin(hitLocationToBone(sHitLoc));
isKillstreakKill = !isPlayer(attacker) || isKillstreakWeapon(sWeapon);
logLine = "Script" + type + ";" + _attacker.guid + ";" + victim.guid + ";" + _attacker GetTagOrigin("tag_eye") + ";" + location + ";" + iDamage + ";" + sWeapon + ";" + sHitLoc + ";" + sMeansOfDeath + ";" + _attacker getPlayerAngles() + ";" + gettime() + ";" + isKillstreakKill + ";" + _attacker playerADS();
// do the tracing stuff
start = _attacker getTagOrigin("tag_eye");
end = location;
trace = bulletTrace(start, end, true, _attacker);
logLine = "Script" + type + ";" + _attacker.guid + ";" + victim.guid + ";" + _attacker GetTagOrigin("tag_eye") + ";" + location + ";" + iDamage + ";" + sWeapon + ";" + sHitLoc + ";" + sMeansOfDeath + ";" + _attacker getPlayerAngles() + ";" + gettime() + ";" + isKillstreakKill + ";" + _attacker playerADS() + ";" + trace["fraction"];
attacker thread waitForAdditionalAngles(logLine);
}