1
0
mirror of https://github.com/RaidMax/IW4M-Admin.git synced 2025-06-10 15:20:48 -05:00

huge commit for advanced stats feature.

broke data out into its own library.
may be breaking changes with existing plugins
This commit is contained in:
RaidMax
2021-03-22 11:09:25 -05:00
parent 267b045883
commit 434392a7e4
505 changed files with 13671 additions and 3271 deletions

View File

@ -0,0 +1,12 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace Stats.Models
{
public class AuditFields
{
[Required]
public DateTime CreatedDateTime { get; set; } = DateTime.UtcNow;
public DateTime? UpdatedDateTime { get; set; }
}
}

View File

@ -0,0 +1,23 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Numerics;
using Data.Models.Client.Stats;
namespace Data.Models.Client
{
public class EFACSnapshotVector3 : SharedEntity
{
[Key]
public int ACSnapshotVector3Id { get; set; }
public int SnapshotId { get; set; }
[ForeignKey("SnapshotId")]
public EFACSnapshot Snapshot { get; set; }
public int Vector3Id { get; set; }
[ForeignKey("Vector3Id")]
public Vector3 Vector { get; set;}
}
}

View File

@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Data.Models.Client
{
public class EFClient : SharedEntity
{
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
}
[Key]
public int ClientId { get; set; }
public long NetworkId { get; set; }
[Required]
public int Connections { get; set; }
[Required]
// in seconds
public int TotalConnectionTime { get; set; }
[Required]
public DateTime FirstConnection { get; set; }
[Required]
public DateTime LastConnection { get; set; }
public bool Masked { get; set; }
[Required]
public int AliasLinkId { get; set; }
[ForeignKey("AliasLinkId")]
public virtual EFAliasLink AliasLink { get; set; }
[Required]
public Permission Level { get; set; }
[Required]
public int CurrentAliasId { get; set; }
[ForeignKey("CurrentAliasId")]
public virtual EFAlias CurrentAlias { get; set; }
public string Password { get; set; }
public string PasswordSalt { get; set; }
// list of meta for the client
public virtual ICollection<EFMeta> Meta { get; set; }
public virtual ICollection<EFPenalty> ReceivedPenalties { get; set; }
public virtual ICollection<EFPenalty> AdministeredPenalties { get; set; }
}
}

View File

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Data.Models.Server;
namespace Data.Models.Client
{
public class EFClientKill : SharedEntity
{
[Key] public long KillId { get; set; }
public int VictimId { get; set; }
[ForeignKey("VictimId")] public virtual EFClient Victim { get; set; }
public int AttackerId { get; set; }
[ForeignKey("AttackerId")] public virtual EFClient Attacker { get; set; }
public long ServerId { get; set; }
[ForeignKey("ServerId")] public virtual EFServer Server { get; set; }
public int HitLoc { get; set; }
public int DeathType { get; set; }
public int Damage { get; set; }
public int Weapon { get; set; }
public Vector3 KillOrigin { get; set; }
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; }
public double VisibilityPercentage { 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;
public int Map { get; set; }
[NotMapped] public long TimeOffset { get; set; }
[NotMapped] public bool IsKillstreakKill { get; set; }
[NotMapped] public float AdsPercent { get; set; }
[NotMapped] public List<Vector3> AnglesList { get; set; }
[NotMapped] public int GameName { get; set; }
/// <summary>
/// Indicates if the attacker was alive after last captured angle
/// </summary>
[NotMapped]
public bool IsAlive { get; set; }
/// <summary>
/// Specifies the last time the attack button was detected as pressed
/// </summary>
[NotMapped]
public long TimeSinceLastAttack { get; set; }
}
}

View File

@ -0,0 +1,22 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Data.Models.Server;
namespace Data.Models.Client
{
public class EFClientMessage : SharedEntity
{
[Key]
public long MessageId { get; set; }
public long ServerId { get; set; }
[ForeignKey("ServerId")]
public virtual EFServer Server { get; set; }
public int ClientId { get; set; }
[ForeignKey("ClientId")]
public virtual EFClient Client { get; set; }
public string Message { get; set; }
public DateTime TimeSent { get; set; }
public bool SentIngame { get; set; }
}
}

View File

@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Numerics;
namespace Data.Models.Client.Stats
{
/// <summary>
/// This class houses the information for anticheat snapshots (used for validating a ban)
/// </summary>
public class EFACSnapshot : SharedEntity
{
[Key]
public int SnapshotId { get; set; }
public int ClientId { get; set; }
[ForeignKey("ClientId")]
public EFClient Client { get; set; }
public DateTime When { get; set; }
public int CurrentSessionLength { get; set; }
public int TimeSinceLastEvent { get; set; }
public double EloRating { get; set; }
public int SessionScore { get; set; }
public double SessionSPM { get; set; }
public int Hits { get; set; }
public int Kills { get; set; }
public int Deaths { get; set; }
public double CurrentStrain { get; set; }
public double StrainAngleBetween { get; set; }
public double SessionAngleOffset { get; set; }
public double RecoilOffset { get; set; }
public int LastStrainAngleId { get; set; }
[ForeignKey("LastStrainAngleId")]
public Vector3 LastStrainAngle { get; set; }
public int HitOriginId { get; set; }
[ForeignKey("HitOriginId")]
public Vector3 HitOrigin { get; set; }
public int HitDestinationId { get; set; }
[ForeignKey("HitDestinationId")]
public Vector3 HitDestination { get; set; }
public double Distance { get; set; }
public double SessionAverageSnapValue { get; set; }
public int SessionSnapHits { get; set; }
public int CurrentViewAngleId { get; set; }
[ForeignKey("CurrentViewAngleId")]
public Vector3 CurrentViewAngle { get; set; }
public int WeaponId { get; set; }
public int HitLocation { get; set; }
public int HitType { get; set; }
public virtual ICollection<EFACSnapshotVector3> PredictedViewAngles { get; set; }
[NotMapped]
public string CapturedViewAngles => PredictedViewAngles?.Count > 0 ?
string.Join(", ", PredictedViewAngles.OrderBy(_angle => _angle.ACSnapshotVector3Id).Select(_angle => _angle.Vector.ToString())) :
"";
}
}

View File

@ -0,0 +1,91 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Data.Models.Client.Stats.Reference;
using Data.Models.Server;
using Stats.Models;
namespace Data.Models.Client.Stats
{
public class EFClientHitStatistic : AuditFields
{
[Key]
public int ClientHitStatisticId { get; set; }
[Required]
public int ClientId { get; set; }
[ForeignKey(nameof(ClientId))]
public virtual EFClient Client { get; set; }
public long? ServerId { get; set; }
[ForeignKey(nameof(ServerId))]
public virtual EFServer Server { get; set; }
public int? HitLocationId { get; set; }
[ForeignKey(nameof(HitLocationId))]
public virtual EFHitLocation HitLocation { get; set; }
public int? MeansOfDeathId { get; set; }
[ForeignKey(nameof(MeansOfDeathId))]
public virtual EFMeansOfDeath MeansOfDeath { get; set; }
public int? WeaponId { get; set; }
[ForeignKey(nameof(WeaponId))]
public virtual EFWeapon Weapon { get; set; }
public int? WeaponAttachmentComboId { get; set; }
[ForeignKey(nameof(WeaponAttachmentComboId))]
public virtual EFWeaponAttachmentCombo WeaponAttachmentCombo { get; set; }
/// <summary>
/// how many hits the player got
/// </summary>
public int HitCount { get; set; }
/// <summary>
/// how many kills the player got
/// </summary>
public int KillCount { get; set; }
/// <summary>
/// how much damage the player inflicted
/// </summary>
public int DamageInflicted { get; set; }
/// <summary>
/// how many hits the player received
/// </summary>
public int ReceivedHitCount { get; set; }
/// <summary>
/// how many kills the player received
/// </summary>
public int DeathCount { get; set; }
/// <summary>
/// how much damage the player received
/// </summary>
public int DamageReceived { get; set; }
/// <summary>
/// how many times the player killed themself
/// </summary>
public int SuicideCount { get; set; }
/// <summary>
/// estimation of time spent with the configuration
/// </summary>
public int? UsageSeconds { get; set; }
/// <summary>
/// total in-game score
/// </summary>
public int? Score { get; set; }
}
}

View File

@ -0,0 +1,31 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Data.Models.Server;
using Stats.Models;
namespace Data.Models.Client.Stats
{
public class EFClientRankingHistory: AuditFields
{
public const int MaxRankingCount = 30;
[Key]
public long ClientRankingHistoryId { get; set; }
[Required]
public int ClientId { get; set; }
[ForeignKey(nameof(ClientId))]
public virtual EFClient Client { get; set; }
public long? ServerId { get; set; }
[ForeignKey(nameof(ServerId))]
public virtual EFServer Server { get; set; }
public bool Newest { get; set; }
public int? Ranking { get; set; }
public double? ZScore { get; set; }
public double? PerformanceMetric { get; set; }
}
}

View File

@ -0,0 +1,16 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Data.Models.Client.Stats
{
public class EFClientRatingHistory : SharedEntity
{
[Key]
public int RatingHistoryId { get; set; }
public int ClientId { get; set; }
[ForeignKey("ClientId")]
public virtual EFClient Client { get; set; }
public virtual ICollection<EFRating> Ratings { get; set; }
}
}

View File

@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading;
using Data.Models.Server;
namespace Data.Models.Client.Stats
{
public class EFClientStatistics : SharedEntity
{
public EFClientStatistics()
{
ProcessingHit = new SemaphoreSlim(1, 1);
}
~EFClientStatistics()
{
ProcessingHit.Dispose();
}
public int ClientId { get; set; }
[ForeignKey("ClientId")]
public virtual EFClient Client { get; set; }
public long ServerId { get; set; }
[ForeignKey("ServerId")]
public virtual EFServer Server { get; set; }
[Required]
public int Kills { get; set; }
[Required]
public int Deaths { get; set; }
public double EloRating { get; set; }
public double ZScore { get; set; }
public DateTime? UpdatedAt { get; set; }
public virtual ICollection<EFHitLocationCount> HitLocations { get; set; }
public double RollingWeightedKDR { get; set; }
public double AverageSnapValue { get; set; }
public int SnapHitCount { get; set; }
[NotMapped]
public double Performance
{
get => Math.Round(EloRating * 1/3.0 + Skill * 2/3.0, 2);
}
[NotMapped]
public double KDR
{
get => Deaths == 0 ? Kills : Math.Round(Kills / (double)Deaths, 2);
}
[Required]
public double SPM { get; set; }
[Required]
public double Skill { get; set; }
[Required]
public int TimePlayed { get; set; }
[Required]
public double MaxStrain { get; set; }
[NotMapped]
public float AverageHitOffset
{
get => (float)Math.Round(HitLocations.Sum(c => c.HitOffsetAverage) / Math.Max(1, HitLocations.Where(c => c.HitOffsetAverage > 0).Count()), 4);
}
[NotMapped]
public int SessionKills { get; set; }
[NotMapped]
public int SessionDeaths { get; set; }
[NotMapped]
public int KillStreak { get; set; }
[NotMapped]
public int DeathStreak { get; set; }
[NotMapped]
public DateTime LastStatCalculation { get; set; }
[NotMapped]
public int LastScore { get; set; }
[NotMapped]
public DateTime LastActive { get; set; }
[NotMapped]
public double MaxSessionStrain { get; set; }
public void StartNewSession()
{
KillStreak = 0;
DeathStreak = 0;
LastScore = 0;
SessionScores.Add(0);
Team = 0;
}
[NotMapped]
public int SessionScore
{
set => SessionScores[SessionScores.Count - 1] = value;
get
{
lock (SessionScores)
{
return new List<int>(SessionScores).Sum();
}
}
}
[NotMapped]
public int RoundScore => SessionScores[SessionScores.Count - 1];
[NotMapped]
private readonly List<int> SessionScores = new List<int>() { 0 };
[NotMapped]
public int Team { get; set; }
[NotMapped]
public DateTime LastStatHistoryUpdate { get; set; } = DateTime.UtcNow;
[NotMapped]
public double SessionSPM { get; set; }
[NotMapped]
public SemaphoreSlim ProcessingHit { get; private set; }
}
}

View File

@ -0,0 +1,29 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Data.Models.Server;
namespace Data.Models.Client.Stats
{
public class EFHitLocationCount : SharedEntity
{
[Key]
public int HitLocationCountId { get; set; }
[Required]
public int Location { get; set; }
[Required]
public int HitCount { get; set; }
[Required]
public float HitOffsetAverage { get; set; }
[Required]
public float MaxAngleDistance { get; set; }
[Required]
[Column("EFClientStatisticsClientId")]
public int EFClientStatisticsClientId { get; set; }
[ForeignKey("EFClientStatisticsClientId")]
public EFClient Client { get; set; }
[Column("EFClientStatisticsServerId")]
public long EFClientStatisticsServerId { get; set; }
[ForeignKey("EFClientStatisticsServerId")]
public EFServer Server { get; set; }
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Data.Models.Server;
namespace Data.Models.Client.Stats
{
public class EFRating : SharedEntity
{
[Key]
public int RatingId { get; set; }
public int RatingHistoryId { get; set; }
[ForeignKey("RatingHistoryId")]
public virtual EFClientRatingHistory RatingHistory { get; set; }
// if null, indicates that the rating is an average rating
public long? ServerId { get; set; }
// [ForeignKey("ServerId")] can't make this nullable if this annotation is set
public virtual EFServer Server { get; set; }
[Required]
public double Performance { get; set; }
[Required]
public int Ranking { get; set; }
[Required]
// indicates if the rating is the latest
public bool Newest { get; set; }
[Required]
public int ActivityAmount { get; set; }
[Required]
public DateTime When { get; set; } = DateTime.UtcNow;
}
}

View File

@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations;
using Data.Abstractions;
using Stats.Models;
namespace Data.Models.Client.Stats.Reference
{
public class EFHitLocation : AuditFields, IUniqueId
{
[Key]
public int HitLocationId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public Models.Reference.Game Game { get; set; }
public long Id => HitLocationId;
public string Value => Name;
}
}

View File

@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations;
using Data.Abstractions;
using Stats.Models;
namespace Data.Models.Client.Stats.Reference
{
public class EFMap : AuditFields, IUniqueId
{
[Key]
public int MapId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public Models.Reference.Game Game { get; set; }
public long Id => MapId;
public string Value => Name;
}
}

View File

@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations;
using Data.Abstractions;
using Stats.Models;
namespace Data.Models.Client.Stats.Reference
{
public class EFMeansOfDeath: AuditFields, IUniqueId
{
[Key]
public int MeansOfDeathId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public Models.Reference.Game Game { get; set; }
public long Id => MeansOfDeathId;
public string Value => Name;
}
}

View File

@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations;
using Data.Abstractions;
using Stats.Models;
namespace Data.Models.Client.Stats.Reference
{
public class EFWeapon : AuditFields, IUniqueId
{
[Key]
public int WeaponId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public Models.Reference.Game Game { get; set; }
public long Id => WeaponId;
public string Value => Name;
}
}

View File

@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations;
using Data.Abstractions;
using Stats.Models;
namespace Data.Models.Client.Stats.Reference
{
public class EFWeaponAttachment : AuditFields, IUniqueId
{
[Key]
public int WeaponAttachmentId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public Models.Reference.Game Game { get; set; }
public long Id => WeaponAttachmentId;
public string Value => Name;
}
}

View File

@ -0,0 +1,35 @@
using Data.Abstractions;
using Stats.Models;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Data.Models.Client.Stats.Reference
{
public class EFWeaponAttachmentCombo : AuditFields, IUniqueId
{
[Key]
public int WeaponAttachmentComboId { get; set; }
[Required]
public Models.Reference.Game Game { get; set; }
[Required]
public int Attachment1Id { get; set; }
[ForeignKey(nameof(Attachment1Id))]
public virtual EFWeaponAttachment Attachment1 { get; set; }
public int? Attachment2Id { get; set; }
[ForeignKey(nameof(Attachment2Id))]
public virtual EFWeaponAttachment Attachment2 { get; set; }
public int? Attachment3Id { get; set; }
[ForeignKey(nameof(Attachment3Id))]
public virtual EFWeaponAttachment Attachment3 { get; set; }
public long Id => WeaponAttachmentComboId;
public string Value => $"{Attachment1Id}{Attachment2Id}{Attachment3Id}";
}
}

View File

@ -0,0 +1,92 @@
using Data.Models.Client;
using Data.Models.Client.Stats;
using Data.Models.Client.Stats.Reference;
using Data.Models.Server;
using Microsoft.EntityFrameworkCore;
namespace Data.Models.Configuration
{
public class StatsModelConfiguration
{
public static void Configure(ModelBuilder builder)
{
builder.Entity<EFClientStatistics>(entity =>
{
entity.HasKey(cs => new {cs.ClientId, cs.ServerId});
entity.HasIndex(cs => new {cs.ClientId, cs.TimePlayed, PerformancePercentile = cs.ZScore});
entity.HasIndex(cs => new {PerformancePercentile = cs.ZScore});
entity.ToTable("EFClientStatistics");
});
// fix linking from SQLCe
builder.Entity<EFHitLocationCount>(entity =>
{
entity.Property(c => c.EFClientStatisticsClientId)
.HasColumnName("EFClientStatisticsClientId");
entity.Property(c => c.EFClientStatisticsServerId)
.HasColumnName("EFClientStatisticsServerId");
entity.ToTable("EFHitLocationCounts");
});
builder.Entity<EFRating>(entity =>
{
entity.HasIndex(p => new {p.Performance, p.Ranking, p.When});
entity.HasIndex(p => new {p.When, p.ServerId, p.Performance, p.ActivityAmount});
entity.ToTable(nameof(EFRating));
});
builder.Entity<EFClientMessage>(entity =>
{
entity.HasIndex(p => p.TimeSent);
entity.ToTable("EFClientMessages");
});
builder.Entity<EFClientStatistics>(entity => { entity.ToTable(nameof(EFClientStatistics)); });
builder.Entity<EFRating>(entity => { entity.ToTable(nameof(EFRating)); });
builder.Entity<EFClientRatingHistory>(entity => { entity.ToTable(nameof(EFClientRatingHistory)); });
builder.Entity<EFHitLocationCount>(entity => { entity.ToTable("EFHitLocationCounts"); });
builder.Entity<EFServerStatistics>(entity => { entity.ToTable("EFServerStatistics"); });
builder.Entity<EFServer>(entity => { entity.ToTable("EFServers"); });
builder.Entity<EFClientKill>(entity => { entity.ToTable("EFClientKills"); });
builder.Entity<Vector3>().ToTable(nameof(Vector3));
builder.Entity<EFACSnapshot>().ToTable(nameof(EFACSnapshot));
builder.Entity<EFACSnapshotVector3>().ToTable(nameof(EFACSnapshotVector3));
builder.Entity<EFHitLocation>(entity =>
{
entity.HasIndex(loc => loc.Name);
entity.ToTable("EFHitLocations");
});
builder.Entity<EFWeapon>(entity =>
{
entity.HasIndex(weapon => weapon.Name);
entity.ToTable("EFWeapons");
});
builder.Entity<EFMap>(entity => { entity.ToTable("EFMaps"); });
builder.Entity<EFClientHitStatistic>(entity => { entity.ToTable("EFClientHitStatistics"); });
builder.Entity<EFWeaponAttachment>(entity => { entity.ToTable("EFWeaponAttachments"); });
builder.Entity<EFWeaponAttachmentCombo>(entity => { entity.ToTable("EFWeaponAttachmentCombos"); });
builder.Entity<EFMeansOfDeath>(entity => { entity.ToTable("EFMeansOfDeath"); });
builder.Entity<EFClientRankingHistory>(entity =>
{
entity.ToTable(nameof(EFClientRankingHistory));
entity.HasIndex(ranking => ranking.Ranking);
entity.HasIndex(ranking => ranking.ZScore);
entity.HasIndex(ranking => ranking.UpdatedDateTime);
});
}
}
}

31
Data/Models/EFAlias.cs Normal file
View File

@ -0,0 +1,31 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Data.Models
{
public partial class EFAlias : SharedEntity
{
[Key]
public int AliasId { get; set; }
[Required]
public int LinkId { get; set; }
[ForeignKey("LinkId")]
public virtual EFAliasLink Link { get; set; }
[Required]
[MaxLength(MAX_NAME_LENGTH)]
public string Name { get; set; }
[MaxLength(MAX_NAME_LENGTH)]
public string SearchableName { get; set; }
[Required]
public int? IPAddress { get; set; }
[Required]
public DateTime DateAdded { get; set; }
[NotMapped]
public const int MAX_NAME_LENGTH = 24;
[NotMapped]
public const int MIN_NAME_LENGTH = 3;
}
}

View File

@ -0,0 +1,19 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Data.Models
{
public class EFAliasLink : SharedEntity
{
[Key]
public int AliasLinkId { get; set; }
public virtual ICollection<EFAlias> Children { get; set; }
public virtual ICollection<EFPenalty> ReceivedPenalties { get; set; }
public EFAliasLink()
{
Children = new List<EFAlias>();
ReceivedPenalties = new List<EFPenalty>();
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace Data.Models
{
/// <summary>
/// This class models the change to different entities
/// </summary>
public class EFChangeHistory : SharedEntity
{
public enum ChangeType
{
Permission,
Ban,
Command
}
[Key]
public int ChangeHistoryId { get; set; }
public int OriginEntityId { get; set; }
public int TargetEntityId { get; set; }
public int? ImpersonationEntityId { get; set; }
public ChangeType TypeOfChange { get; set; }
public DateTime TimeChanged { get; set; } = DateTime.UtcNow;
[MaxLength(128)]
public string Comment { get; set; }
public string PreviousValue { get; set; }
public string CurrentValue { get; set; }
}
}

39
Data/Models/EFMeta.cs Normal file
View File

@ -0,0 +1,39 @@
using Data.Models.Client;
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Data.Models
{
/// <summary>
/// This class encapsulates any meta fields as a simple string
/// </summary>
public class EFMeta : SharedEntity
{
public const string ClientTagName = nameof(ClientTagName);
public const string ClientTag = nameof(ClientTag);
[Key]
public int MetaId { get; set; }
[Required]
public DateTime Created { get; set; } = DateTime.UtcNow;
[Required]
public DateTime Updated { get; set; } = DateTime.UtcNow;
public int? ClientId { get; set; }
// this is the client that the meta could belong to
[ForeignKey(nameof(ClientId))]
public virtual EFClient Client { get; set; }
[Required]
[MinLength(3)]
[StringLength(32)]
[MaxLength(32)]
public string Key { get; set; }
[Required]
public string Value { get; set; }
public string Extra { get; set; }
public int? LinkedMetaId { get; set; }
[ForeignKey(nameof(LinkedMetaId))]
public virtual EFMeta LinkedMeta { get; set; }
}
}

49
Data/Models/EFPenalty.cs Normal file
View File

@ -0,0 +1,49 @@
using Data.Models.Client;
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Data.Models
{
public class EFPenalty : SharedEntity
{
public enum PenaltyType
{
Report,
Warning,
Flag,
Kick,
TempBan,
Ban,
Unban,
Any,
Unflag,
Other = 100
}
[Key]
public int PenaltyId { get; set; }
[Required]
public int LinkId { get; set; }
[ForeignKey("LinkId")]
public virtual EFAliasLink Link { get; set; }
[Required]
public int OffenderId { get; set; }
[ForeignKey("OffenderId")]
public virtual EFClient Offender { get; set; }
[Required]
public int PunisherId { get; set; }
[ForeignKey("PunisherId")]
public virtual EFClient Punisher { get; set; }
[Required]
public DateTime When { get; set; }
[Required]
public DateTime? Expires { get; set; }
[Required]
public string Offense { get; set; }
public string AutomatedOffense { get; set; }
[Required]
public bool IsEvadedOffense { get; set; }
public PenaltyType Type { get; set; }
}
}

19
Data/Models/Reference.cs Normal file
View File

@ -0,0 +1,19 @@
namespace Data.Models
{
public class Reference
{
public enum Game
{
COD = -1,
UKN = 0,
IW3 = 1,
IW4 = 2,
IW5 = 3,
IW6 = 4,
T4 = 5,
T5 = 6,
T6 = 7,
T7 = 8
}
}
}

View File

@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Data.Abstractions;
namespace Data.Models.Server
{
public class EFServer : SharedEntity, IUniqueId
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long ServerId { get; set; }
[Required]
public int Port { get; set; }
public string EndPoint { get; set; }
public Reference.Game? GameName { get; set; }
public string HostName { get; set; }
public bool IsPasswordProtected { get; set; }
public long Id => ServerId;
public string Value => EndPoint;
}
}

View File

@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Data.Models.Server
{
public class EFServerStatistics : SharedEntity
{
[Key]
public int StatisticId { get; set; }
public long ServerId { get; set; }
[ForeignKey("ServerId")]
public virtual EFServer Server { get; set; }
public long TotalKills { get; set; }
public long TotalPlayTime { get; set; }
}
}

View File

@ -0,0 +1,37 @@
using Data.Abstractions;
using System.Collections.Concurrent;
namespace Data.Models
{
public class SharedEntity : IPropertyExtender
{
private readonly ConcurrentDictionary<string, object> _additionalProperties;
/// <summary>
/// indicates if the entity is active
/// </summary>
public bool Active { get; set; } = true;
public SharedEntity()
{
_additionalProperties = new ConcurrentDictionary<string, object>();
}
public T GetAdditionalProperty<T>(string name)
{
return _additionalProperties.ContainsKey(name) ? (T)_additionalProperties[name] : default;
}
public void SetAdditionalProperty(string name, object value)
{
if (_additionalProperties.ContainsKey(name))
{
_additionalProperties[name] = value;
}
else
{
_additionalProperties.TryAdd(name, value);
}
}
}
}

113
Data/Models/Vector3.cs Normal file
View File

@ -0,0 +1,113 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
namespace Data.Models
{
public class Vector3
{
[Key] public int Vector3Id { get; set; }
public float X { get; protected set; }
public float Y { get; protected set; }
public float Z { get; protected set; }
// this is for EF and really should be somewhere else
public Vector3()
{
}
public Vector3(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
public override string ToString()
{
return $"({X}, {Y}, {Z})";
}
public override bool Equals(object obj)
{
if (obj is Vector3 vec)
{
return vec.X == X && vec.Y == Y && vec.Z == Z;
}
return false;
}
public static Vector3 Parse(string s)
{
bool valid = Regex.Match(s,
@"\((-?[0-9]+\.?[0-9]*|-?[0-9]+\.?[0-9]*e-[0-9]+),\ (-?[0-9]+\.?[0-9]*|-?[0-9]+\.?[0-9]*e-[0-9]+),\ (-?[0-9]+\.?[0-9]*|-?[0-9]+\.?[0-9]*e-[0-9]+)\)")
.Success;
if (!valid)
{
throw new FormatException("Vector3 is not in correct format");
}
string removeParenthesis = s.Substring(1, s.Length - 2);
string[] eachPoint = removeParenthesis.Split(',');
return new Vector3(
float.Parse(eachPoint[0], System.Globalization.NumberStyles.Any,
System.Globalization.CultureInfo.InvariantCulture),
float.Parse(eachPoint[1], System.Globalization.NumberStyles.Any,
System.Globalization.CultureInfo.InvariantCulture),
float.Parse(eachPoint[2], System.Globalization.NumberStyles.Any,
System.Globalization.CultureInfo.InvariantCulture));
}
public static double Distance(Vector3 a, Vector3 b)
{
return Math.Sqrt(Math.Pow(b.X - a.X, 2) + Math.Pow(b.Y - a.Y, 2) + Math.Pow(b.Z - a.Z, 2));
}
public static double AbsoluteDistance(Vector3 a, Vector3 b)
{
double deltaX = Math.Abs(b.X - a.X);
double deltaY = Math.Abs(b.Y - a.Y);
// this 'fixes' the roll-over angles
double dx = deltaX < 360.0 / 2 ? deltaX : 360.0 - deltaX;
double dy = deltaY < 360.0 / 2 ? deltaY : 360.0 - deltaY;
return Math.Sqrt((dx * dx) + (dy * dy));
}
public static double ViewAngleDistance(Vector3 a, Vector3 b, Vector3 c)
{
double dabX = Math.Abs(a.X - b.X);
dabX = dabX < 360.0 / 2 ? dabX : 360.0 - dabX;
double dabY = Math.Abs(a.Y - b.Y);
dabY = dabY < 360.0 / 2 ? dabY : 360.0 - dabY;
double dacX = Math.Abs(a.X - c.X);
dacX = dacX < 360.0 / 2 ? dacX : 360.0 - dacX;
double dacY = Math.Abs(a.Y - c.Y);
dacY = dacY < 360.0 / 2 ? dacY : 360.0 - dacY;
double dbcX = Math.Abs(b.X - c.X);
dbcX = dbcX < 360.0 / 2 ? dbcX : 360.0 - dbcX;
double dbcY = Math.Abs(b.Y - c.Y);
dbcY = dbcY < 360.0 / 2 ? dbcY : 360.0 - dbcY;
double deltaX = (dabX - dacX - dbcX) / 2.0;
deltaX = deltaX < 360.0 / 2 ? deltaX : 360.0 - deltaX;
double deltaY = (dabY - dacY - dbcY) / 2.0;
deltaY = deltaY < 360.0 / 2 ? deltaY : 360.0 - deltaY;
return Math.Round(Math.Sqrt((deltaX * deltaX) + (deltaY * deltaY)), 4);
}
public static Vector3 Subtract(Vector3 a, Vector3 b) => new Vector3(b.X - a.X, b.Y - a.Y, b.Z - a.Z);
public double DotProduct(Vector3 a) => (a.X * this.X) + (a.Y * this.Y) + (a.Z * this.Z);
public double Magnitude() => Math.Sqrt((X * X) + (Y * Y) + (Z * Z));
public double AngleBetween(Vector3 a) => Math.Acos(this.DotProduct(a) / (a.Magnitude() * this.Magnitude()));
}
}