mirror of
https://github.com/RaidMax/IW4M-Admin.git
synced 2025-06-07 21:58:06 -05:00
Additional zombie stast work
This commit is contained in:
parent
122b6dc79d
commit
e1461582fa
@ -136,6 +136,9 @@ namespace IW4MAdmin.Application
|
||||
public IEnumerable<IPlugin> Plugins { get; }
|
||||
public IInteractionRegistration InteractionRegistration { get; }
|
||||
|
||||
public IList<Func<Dictionary<int, List<EFMeta>>, long?, string, bool, Task>> CustomStatsMetrics { get; } =
|
||||
new List<Func<Dictionary<int, List<EFMeta>>, long?, string, bool, Task>>();
|
||||
|
||||
public async Task ExecuteEvent(GameEvent newEvent)
|
||||
{
|
||||
ProcessingEvents.TryAdd(newEvent.IncrementalId, newEvent);
|
||||
@ -249,7 +252,13 @@ namespace IW4MAdmin.Application
|
||||
{
|
||||
var thisIndex = index;
|
||||
Interlocked.Increment(ref index);
|
||||
return ProcessUpdateHandler(server, thisIndex);
|
||||
return ProcessUpdateHandler(server, thisIndex).ContinueWith(result =>
|
||||
{
|
||||
if (result.IsFaulted)
|
||||
{
|
||||
_logger.LogError(result.Exception, "Encountered unexpected error processing updates");
|
||||
}
|
||||
}, CancellationToken);
|
||||
}));
|
||||
}
|
||||
|
||||
@ -599,18 +608,21 @@ namespace IW4MAdmin.Application
|
||||
{
|
||||
_eventHandlerTokenSource = new CancellationTokenSource();
|
||||
|
||||
var eventHandlerThread = new Thread(() =>
|
||||
var eventHandlerTask = Task.Run(() =>
|
||||
{
|
||||
_coreEventHandler.StartProcessing(_eventHandlerTokenSource.Token);
|
||||
})
|
||||
{
|
||||
Name = nameof(CoreEventHandler)
|
||||
};
|
||||
}, _eventHandlerTokenSource.Token);
|
||||
|
||||
eventHandlerThread.Start();
|
||||
await UpdateServerStates();
|
||||
_eventHandlerTokenSource.Cancel();
|
||||
eventHandlerThread.Join();
|
||||
|
||||
try
|
||||
{
|
||||
await eventHandlerTask;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Stop()
|
||||
|
@ -45,15 +45,13 @@ namespace IW4MAdmin.Application
|
||||
|
||||
public void StartProcessing(CancellationToken token)
|
||||
{
|
||||
_cancellationToken = token;
|
||||
|
||||
while (!_cancellationToken.IsCancellationRequested)
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
_onEventReady.Reset();
|
||||
|
||||
try
|
||||
{
|
||||
_onProcessingEvents.Wait(_cancellationToken);
|
||||
_onProcessingEvents.Wait(token);
|
||||
|
||||
if (!_runningEventTasks.TryDequeue(out var coreEvent))
|
||||
{
|
||||
@ -62,7 +60,7 @@ namespace IW4MAdmin.Application
|
||||
_onProcessingEvents.Release(1);
|
||||
}
|
||||
|
||||
_onEventReady.Wait(_cancellationToken);
|
||||
_onEventReady.Wait(token);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -2439,6 +2439,7 @@
|
||||
"left_foot": "Left Foot",
|
||||
"left_arm_upper": "Upper Left Arm",
|
||||
"left_arm_lower": "Lower Left Arm",
|
||||
"head": "Head",
|
||||
"gl": "Rifle Grenade",
|
||||
"bigammo": "Round Drum",
|
||||
"scoped": "Sniper Scope",
|
||||
@ -2446,34 +2447,238 @@
|
||||
"aperture": "Aperture Sight",
|
||||
"flash": "Flash Hider",
|
||||
"silenced": "Silencer",
|
||||
"molotov": "Molotov Cocktail",
|
||||
"sticky": "N° 74 ST",
|
||||
"m2": "M2 Flamethrower",
|
||||
"artillery": "Artillery Strike",
|
||||
"dog": "Attack Dogs",
|
||||
"colt": "Colt M1911",
|
||||
"357magnum": ".357 Magnum",
|
||||
"sw_357": ".357 Magnum",
|
||||
"walther": "Walther P38",
|
||||
"tokarev": "Tokarev TT-33",
|
||||
"shotgun": "M1897 Trench Gun",
|
||||
"doublebarreledshotgun": "Double-Barreled Shotgun",
|
||||
"mp40": "MP40",
|
||||
"type100smg": "Type 100",
|
||||
"ppsh": "PPSh-41",
|
||||
"svt40": "SVT-40",
|
||||
"gewehr43": "Gewehr 43",
|
||||
"doublebarrel": "Double-Barreled Shotgun",
|
||||
"30cal": "Browning M1919",
|
||||
"bar": "BAR",
|
||||
"fg42": "FG42",
|
||||
"m1garand": "M1 Garand",
|
||||
"mp40": "MP40",
|
||||
"ppsh": "PPSh-41",
|
||||
"gewehr43": "Gewehr 43",
|
||||
"svt40": "SVT-40",
|
||||
"nambu": "Nambu",
|
||||
"m1garand_bayonet": "M1 Garand Bayonet",
|
||||
"m1a1carbine_bayonet": "M1A1 Carbine Bayonet",
|
||||
"kar98k_bayonet": "Kar98k Bayonet",
|
||||
"mosin_rifle_bayonet": "Mosin-Nagant Bayonet",
|
||||
"mg42": "MG42",
|
||||
"colt45": "Colt M1911",
|
||||
"sten_silenced": "Silenced Sten",
|
||||
"type99_lmg": "Type 99",
|
||||
"dp28": "DP-28",
|
||||
"mine_shoebox": "PMD-6",
|
||||
"mine_bouncing_betty": "Bouncing Betty",
|
||||
"357magnum": ".357 Magnum",
|
||||
"ptrs41": "PTRS-41",
|
||||
"remingtonmodel11": "Remington Model 11",
|
||||
"tabun_grenade": "Tabun Gas",
|
||||
"signal_flare": "Signal Flare",
|
||||
"shotgun_double_barreled": "Double-Barreled Shotgun",
|
||||
"m7_launcher": "M7 Grenade Launcher",
|
||||
"fg42_telescopic": "Telescopic Sight",
|
||||
"springfield_scoped": "Scoped Springfield",
|
||||
"m1garand_gl": "M1 Garand w/ Launcher",
|
||||
"sticky_grenade": "N\u00ba 74 ST",
|
||||
"tokarev_tt30": "Tokarev TT-33",
|
||||
"mg42_bipod": "Deployable MG42",
|
||||
"dp28_bipod": "Deployable DP-28",
|
||||
"fg42_bipod": "Deployable FG42",
|
||||
"bar_bipod": "Deployable BAR",
|
||||
"30cal_bipod": "Deployable Browning M1919",
|
||||
"type99_lmg_bipod": "Deployable Type 99",
|
||||
"type100smg": "Type 100",
|
||||
"stg44": "STG-44",
|
||||
"m1carbine": "M1A1 Carbine",
|
||||
"type99lmg": "Type 99",
|
||||
"bar": "BAR",
|
||||
"dp28": "DP-28",
|
||||
"mg42": "MG42",
|
||||
"fg42": "FG42",
|
||||
"30cal": "Browning M1919",
|
||||
"type99rifle": "Arisaka",
|
||||
"mosinrifle": "Mosin-Nagant",
|
||||
"ptrs41": "PTRS-41"
|
||||
"syrette": "Syrette",
|
||||
"supportgunner": "Support Gunner",
|
||||
"bren": "Bren LMG",
|
||||
"rifleman": "Rifleman",
|
||||
"lee_enfield": "Lee-Enfield",
|
||||
"kar98k": "Kar98k",
|
||||
"luger": "Luger",
|
||||
"m1a1carbine": "M1A1 Carbine",
|
||||
"mosin_rifle": "Mosin-Nagant",
|
||||
"mosin_rifle_scoped": "Scoped Mosin-Nagant",
|
||||
"sniper": "Sniper",
|
||||
"submachinegunner": "Submachine Gunner",
|
||||
"mp44": "MP44",
|
||||
"springfield": "Springfield",
|
||||
"mosinnagantammo": "Mosin-Nagant Ammo",
|
||||
"sten": "Sten",
|
||||
"armyengineer": "Army Engineer",
|
||||
"thompson": "Thompson",
|
||||
"fastauto": "Fast-Auto",
|
||||
"slowauto": "Slow-Auto",
|
||||
"fullauto": "Full-Auto",
|
||||
"semiauto": "Semi-Auto",
|
||||
"m2fraggrenade": "M2 Frag Grenade",
|
||||
"mk1_frag_grenade": "MK1 Frag Grenade",
|
||||
"russiangrenade": "RGD-33 Stick Grenade",
|
||||
"germangrenade": "Stielhandgranate",
|
||||
"panzerschrek": "Panzerschrek",
|
||||
"panzerfaust": "Panzerfaust 60",
|
||||
"scopedkar98k": "Scoped Kar98k",
|
||||
"holdpin": "Hold-Pin",
|
||||
"cookoff": "Cook-Off",
|
||||
"medicplaceholder": "Medic",
|
||||
"fraggrenade": "Frag",
|
||||
"m8_white_smoke": "Smoke",
|
||||
"shotgun": "M1897 Trench Gun",
|
||||
"greasegun": "Grease Gun",
|
||||
"pps42": "PPS42",
|
||||
"webley": "Webley",
|
||||
"scopedg43": "Scoped Gewehr 43",
|
||||
"defaultweapon": "Default Weapon",
|
||||
"satchel": "Satchel Charge",
|
||||
"anm8_smoke_grenade": "AN-M8 Smoke Grenade",
|
||||
"no77_wp_smoke_grenade": "No.77 WP Smoke Grenade",
|
||||
"nebelhandgranate": "Nebelhandgranate",
|
||||
"rgd1_smoke_grenade": "RGD-1 Smoke Grenade",
|
||||
"potato": "Potato",
|
||||
"no_ammo": "No Ammo",
|
||||
"no_frag_grenade": "No Primary Grenades Remaining",
|
||||
"no_smoke_grenade": "No Smoke Grenades Remaining",
|
||||
"no_flash_grenade": "No Flashbang Grenades Remaining",
|
||||
"noecial_grenade": "No Special Grenades Remaining",
|
||||
"location_selector": "Select a location",
|
||||
"smoke_grenade": "Smoke Grenade",
|
||||
"flash_grenade": "Flash Grenade",
|
||||
"concussion_grenade": "Stun Grenade",
|
||||
"smgs": "Submachine Guns",
|
||||
"assaultrifles": "Assault Rifles",
|
||||
"shotguns": "Shotguns",
|
||||
"sniperrifles": "Sniper Rifles",
|
||||
"target_too_close": "Too Close to Target",
|
||||
"lockon_required": "Lock-On Required",
|
||||
"target_not_enough_clearance": "Not Enough Room To Fire",
|
||||
"no_attachment": "No Attachment",
|
||||
"silencer": "Suppressor",
|
||||
"grenade_launcher": "Grenade Launcher",
|
||||
"no_camo": "No Camo",
|
||||
"golden_camo": "Golden",
|
||||
"prestige_camo": "Prestige",
|
||||
"binoculars": "Binoculars",
|
||||
"grip": "Grip",
|
||||
"m16a4_grenadier": "M16A4 Grenadier",
|
||||
"panzershrek": "Panzershrek",
|
||||
"bazooka": "M9A1 Bazooka",
|
||||
"bazooka_man": "Bazooka",
|
||||
"tokarev": "Tokarev TT-33",
|
||||
"russian_flag": "Red Army Banner",
|
||||
"stg-44": "STG-44",
|
||||
"mortar_round": "Mortar Round",
|
||||
"molotov": "Molotov Cocktail",
|
||||
"fireblob": "Napalm Blob (Fire on Ground)",
|
||||
"m2_flamethrower": "M2 Flamethrower",
|
||||
"flamethrower_gunner": "Flamethrower",
|
||||
"kar98k_scoped": "Scoped Kar98k",
|
||||
"lee_enfield_scoped": "Scoped Lee-Enfield",
|
||||
"type100_smg": "Type 100",
|
||||
"type99_rifle": "Arisaka",
|
||||
"type99_rifle_bayonet": "Arisaka Bayonet",
|
||||
"type99_rifle_scoped": "Scoped Arisaka",
|
||||
"walther_p38": "Walther P38",
|
||||
"shotgunner": "Shotgunner",
|
||||
"doublebarrel_sawed_grip": "Sawed-Off Double-Barreled Shotgun w/ Grip",
|
||||
"antitank_gunner": "Anti-Tank Gunner",
|
||||
"springfield_no_attachment": "No Attachment",
|
||||
"springfield_bayonet": "Springfield Bayonet",
|
||||
"springfield_rifle_grenade": "Rifle Grenade",
|
||||
"type99_rifle_rifle_grenade": "Rifle Grenade",
|
||||
"type99_rifle_no_attachment": "No Attachment",
|
||||
"kar98k_no_attachment": "No Attachment",
|
||||
"kar98k_rifle_grenade": "Rifle Grenade",
|
||||
"mosin_rifle_no_attachment": "No Attachment",
|
||||
"mosin_rifle_rifle_grenade": "Rifle Grenade",
|
||||
"svt40_no_attachment": "No Attachment",
|
||||
"svt40_flash": "Flash Hider",
|
||||
"svt40_aperture": "Aperture Sight",
|
||||
"svt40_telescopic": "Telescopic Sight",
|
||||
"svt40_select_fire": "Select Fire",
|
||||
"gewehr43_no_attachment": "No Attachment",
|
||||
"gewehr43_silenced": "Suppressor",
|
||||
"gewehr43_aperture": "Aperture Sight",
|
||||
"gewehr43_telescopic": "Telescopic Sight",
|
||||
"gewehr43_rifle_grenade": "Rifle Grenade",
|
||||
"m1garand_no_attachment": "No Attachment",
|
||||
"m1garand_rifle_grenade": "Rifle Grenade",
|
||||
"m1garand_scoped": "Sniper Scope",
|
||||
"m1garand_flash": "Flash Hider",
|
||||
"m1a1carbine_no_attachment": "No Attachment",
|
||||
"m1a1carbine_flash": "Flash Hider",
|
||||
"m1a1carbine_bigammo": "Box Magazine",
|
||||
"m1a1carbine_aperture": "Aperture Sight",
|
||||
"stg-44_no_attachment": "No Attachment",
|
||||
"stg-44_flash": "Flash Hider",
|
||||
"stg-44_aperture": "Aperture Sight",
|
||||
"stg-44_telescopic": "Telescopic Sight",
|
||||
"stg-44_select_fire": "Select Fire",
|
||||
"thompson_no_attachment": "No Attachment",
|
||||
"thompson_silenced": "Suppressor",
|
||||
"thompson_aperture": "Aperture Sight",
|
||||
"thompson_bigammo": "Round Drum",
|
||||
"type100_smg_no_attachment": "No Attachment",
|
||||
"type100_smg_silenced": "Suppressor",
|
||||
"type100_smg_bigammo": "Box Magazine",
|
||||
"type100_smg_aperture": "Aperture Sight",
|
||||
"mp40_no_attachment": "No Attachment",
|
||||
"mp40_silenced": "Suppressor",
|
||||
"mp40_aperture": "Aperture Sight",
|
||||
"mp40_bigammo": "Dual Magazines",
|
||||
"ppsh_no_attachment": "No Attachment",
|
||||
"ppsh_aperture": "Aperture Sight",
|
||||
"ppsh_bigammo": "Round Drum",
|
||||
"shotgun_no_attachment": "No Attachment",
|
||||
"shotgun_grip": "Grip",
|
||||
"shotgun_bayonet": "Bayonet",
|
||||
"shotgun_double_barreled_no_attachment": "No Attachment",
|
||||
"shotgun_double_barreled_grip": "Grip",
|
||||
"doublebarrel_sawed": "Sawed-Off Shotgun",
|
||||
"sailor": "Crewman",
|
||||
"fg42_scoped": "Scoped FG42",
|
||||
"mosin_launcher": "Mosin-Nagant w/ Launcher",
|
||||
"zombie_melee": "BRAAAINS...",
|
||||
"ray_gun": "Ray Gun",
|
||||
"nomad": "Nomad",
|
||||
"tesla_gun": "Wunderwaffe DG-2",
|
||||
"30cal_upgraded": "B115 accelerator",
|
||||
"bar_upgraded": "The Widow Maker",
|
||||
"colt_upgraded": "C-3000 b1at-ch35",
|
||||
"shotgun_double_barreled_sawed_grip_upgraded": "The Snuff Box",
|
||||
"shotgun_double_barreled_upgraded": "24 Bore long range",
|
||||
"fg42_upgraded": "420 Impeller",
|
||||
"gewehr43_upgraded": "G115 Compressor",
|
||||
"m1a1carbine_upgraded": "Widdershins RC-1",
|
||||
"m1garand_upgraded": "M1000",
|
||||
"mg42_upgraded": "Barracuda FU-A11",
|
||||
"mp40_upgraded": "The Afterburner",
|
||||
"ppsh_upgraded": "The Reaper",
|
||||
"shotgun_upgraded": "Gut Shot",
|
||||
"stg-44_upgraded": "Spatz-447 +",
|
||||
"sw_357_upgraded": ".357 Plus 1 K1L-u",
|
||||
"thompson_upgraded": "Gibs-o-matic",
|
||||
"type100_smg_upgraded": "1001 Samurais",
|
||||
"type99_rifle_upgraded": "The Eviscerator",
|
||||
"panzerschrek_upgraded": "Longinus",
|
||||
"ray_gun_upgraded": "Porter's X2 Ray Gun",
|
||||
"tesla_gun_upgraded": "Wunderwaffe DG-3 JZ",
|
||||
"m2_flamethrower_upgraded": "FIW Nitrogen cooled",
|
||||
"ptrs41_upgraded": "The Penetrator",
|
||||
"m7_launcher_upgraded": "The Imploder",
|
||||
"nazi_zombies_cap": "NAZI ZOMBIES",
|
||||
"kar98k_upgraded": "Armageddon",
|
||||
"m7_launcher_upgraded_nonade": "The Imploder",
|
||||
"zombie_knuckle_crack": "Pack A Punch Knuckle Crack",
|
||||
"cymbal_monkey": "Cymbal Monkey"
|
||||
},
|
||||
|
||||
"T6" : {
|
||||
|
@ -98,10 +98,12 @@ namespace IW4MAdmin
|
||||
ServerLogger.LogDebug("Client slot #{clientNumber} now reserved", clientFromLog.ClientNumber);
|
||||
|
||||
var client = await Manager.GetClientService().GetUnique(clientFromLog.NetworkId, GameName);
|
||||
var foundClient = true;
|
||||
|
||||
// first time client is connecting to server
|
||||
if (client == null)
|
||||
{
|
||||
foundClient = false;
|
||||
ServerLogger.LogDebug("Client {client} first time connecting", clientFromLog.ToString());
|
||||
clientFromLog.CurrentServer = this;
|
||||
client = await Manager.GetClientService().Create(clientFromLog);
|
||||
@ -109,12 +111,16 @@ namespace IW4MAdmin
|
||||
|
||||
client.CopyAdditionalProperties(clientFromLog);
|
||||
|
||||
// this is only a temporary version until the IPAddress is transmitted
|
||||
client.CurrentAlias = new EFAlias()
|
||||
if (foundClient)
|
||||
{
|
||||
Name = clientFromLog.Name,
|
||||
IPAddress = clientFromLog.IPAddress
|
||||
};
|
||||
client.CurrentAlias = new EFAlias
|
||||
{
|
||||
AliasId = client.CurrentAliasId,
|
||||
LinkId = client.AliasLinkId,
|
||||
Name = clientFromLog.Name,
|
||||
IPAddress = clientFromLog.IPAddress
|
||||
};
|
||||
}
|
||||
|
||||
// Do the player specific stuff
|
||||
client.ClientNumber = clientFromLog.ClientNumber;
|
||||
@ -413,10 +419,7 @@ namespace IW4MAdmin
|
||||
{
|
||||
if (E.Origin.State != ClientState.Connected)
|
||||
{
|
||||
E.Origin.State = ClientState.Connected;
|
||||
E.Origin.Connections += 1;
|
||||
|
||||
ChatHistory.Add(new ChatInfo()
|
||||
ChatHistory.Add(new ChatInfo
|
||||
{
|
||||
Name = E.Origin.Name,
|
||||
Message = "CONNECTED",
|
||||
@ -431,6 +434,10 @@ namespace IW4MAdmin
|
||||
E.Origin.Tag = clientTag.Value;
|
||||
}
|
||||
|
||||
await E.Origin.OnJoin(E.Origin.IPAddress, Manager.GetApplicationSettings().Configuration().EnableImplicitAccountLinking);
|
||||
E.Origin.State = ClientState.Connected;
|
||||
E.Origin.Connections += 1;
|
||||
|
||||
try
|
||||
{
|
||||
var factory = _serviceProvider.GetRequiredService<IDatabaseContextFactory>();
|
||||
@ -449,8 +456,6 @@ namespace IW4MAdmin
|
||||
ServerLogger.LogError(ex, "Could not get offline message count for {Client}", E.Origin.ToString());
|
||||
throw;
|
||||
}
|
||||
|
||||
await E.Origin.OnJoin(E.Origin.IPAddress, Manager.GetApplicationSettings().Configuration().EnableImplicitAccountLinking);
|
||||
}
|
||||
}
|
||||
|
||||
@ -907,6 +912,12 @@ namespace IW4MAdmin
|
||||
context.Entry(gameServer).Property(property => property.HostName).IsModified = true;
|
||||
}
|
||||
|
||||
if (gameServer.PerformanceBucket != PerformanceBucket)
|
||||
{
|
||||
gameServer.PerformanceBucket = PerformanceBucket;
|
||||
context.Entry(gameServer).Property(property => property.PerformanceBucket).IsModified = true;
|
||||
}
|
||||
|
||||
if (gameServer.IsPasswordProtected != !string.IsNullOrEmpty(GamePassword))
|
||||
{
|
||||
gameServer.IsPasswordProtected = !string.IsNullOrEmpty(GamePassword);
|
||||
|
@ -57,6 +57,7 @@ namespace Data.Context
|
||||
public DbSet<ZombieRoundClientStat> ZombieRoundClientStats { get; set; }
|
||||
public DbSet<ZombieAggregateClientStat> ZombieClientStatAggregates { get; set; }
|
||||
public DbSet<ZombieClientStatRecord> ZombieClientStatRecords { get; set; }
|
||||
public DbSet<ZombieEventLog> ZombieEvents { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@ -102,6 +103,19 @@ namespace Data.Context
|
||||
client.NetworkId,
|
||||
client.GameName
|
||||
});
|
||||
|
||||
|
||||
/* entity.HasMany(prop => prop.ZombieMatchClientStats)
|
||||
.WithOne(prop => prop.Client)
|
||||
.HasForeignKey(prop => prop.ClientId);
|
||||
|
||||
entity.HasMany(prop => prop.ZombieRoundClientStats)
|
||||
.WithOne(prop => prop.Client)
|
||||
.HasForeignKey(prop => prop.ClientId);
|
||||
|
||||
entity.HasMany(prop => prop.ZombieAggregateClientStats)
|
||||
.WithOne(prop => prop.Client)
|
||||
.HasForeignKey(prop => prop.ClientId);*/
|
||||
});
|
||||
|
||||
modelBuilder.Entity<EFPenalty>(entity =>
|
||||
@ -171,12 +185,34 @@ namespace Data.Context
|
||||
modelBuilder.Entity<EFServerSnapshot>().ToTable(nameof(EFServerSnapshot));
|
||||
modelBuilder.Entity<EFClientConnectionHistory>().ToTable(nameof(EFClientConnectionHistory));
|
||||
|
||||
modelBuilder.Entity(typeof(ZombieMatch)).ToTable($"EF{nameof(ZombieMatch)}");
|
||||
modelBuilder.Entity(typeof(ZombieMatchClientStat)).ToTable($"EF{nameof(ZombieMatchClientStat)}");
|
||||
modelBuilder.Entity(typeof(ZombieRoundClientStat)).ToTable($"EF{nameof(ZombieRoundClientStat)}");
|
||||
modelBuilder.Entity(typeof(ZombieAggregateClientStat)).ToTable($"EF{nameof(ZombieAggregateClientStat)}");
|
||||
modelBuilder.Entity(typeof(ZombieClientStat)).ToTable($"EF{nameof(ZombieClientStat)}");
|
||||
modelBuilder.Entity(typeof(ZombieClientStatRecord)).ToTable($"EF{nameof(ZombieClientStatRecord)}");
|
||||
modelBuilder.Entity<ZombieMatch>().ToTable($"EF{nameof(ZombieMatch)}");
|
||||
|
||||
modelBuilder.Entity<ZombieClientStat>(ent =>
|
||||
{
|
||||
ent.ToTable($"EF{nameof(ZombieClientStat)}");
|
||||
ent.HasOne(prop => prop.Client)
|
||||
.WithMany(prop => prop.ZombieClientStats)
|
||||
.HasForeignKey(prop => prop.ClientId);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<ZombieMatchClientStat>(ent =>
|
||||
{
|
||||
ent.ToTable($"EF{nameof(ZombieMatchClientStat)}");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<ZombieRoundClientStat>(ent =>
|
||||
{
|
||||
ent.ToTable($"EF{nameof(ZombieRoundClientStat)}");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<ZombieAggregateClientStat>(ent =>
|
||||
{
|
||||
ent.ToTable($"EF{nameof(ZombieAggregateClientStat)}");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<ZombieEventLog>().ToTable($"EF{nameof(ZombieEvents)}");
|
||||
|
||||
modelBuilder.Entity<ZombieClientStatRecord>().ToTable($"EF{nameof(ZombieClientStatRecord)}");
|
||||
|
||||
Models.Configuration.StatsModelConfiguration.Configure(modelBuilder);
|
||||
|
||||
|
2083
Data/Migrations/Postgresql/20240212035747_InitialZombieStats.Designer.cs
generated
Normal file
2083
Data/Migrations/Postgresql/20240212035747_InitialZombieStats.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
319
Data/Migrations/Postgresql/20240212035747_InitialZombieStats.cs
Normal file
319
Data/Migrations/Postgresql/20240212035747_InitialZombieStats.cs
Normal file
@ -0,0 +1,319 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.Postgresql
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class InitialZombieStats : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "PerformanceBucket",
|
||||
table: "EFServers",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "PerformanceBucket",
|
||||
table: "EFClientRankingHistory",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EFZombieMatch",
|
||||
columns: table => new
|
||||
{
|
||||
ZombieMatchId = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
MapId = table.Column<int>(type: "integer", nullable: true),
|
||||
ServerId = table.Column<long>(type: "bigint", nullable: true),
|
||||
ClientsCompleted = table.Column<int>(type: "integer", nullable: false),
|
||||
MatchStartDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
MatchEndDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
CreatedDateTime = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedDateTime = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EFZombieMatch", x => x.ZombieMatchId);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieMatch_EFMaps_MapId",
|
||||
column: x => x.MapId,
|
||||
principalTable: "EFMaps",
|
||||
principalColumn: "MapId");
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieMatch_EFServers_ServerId",
|
||||
column: x => x.ServerId,
|
||||
principalTable: "EFServers",
|
||||
principalColumn: "ServerId");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EFZombieClientStat",
|
||||
columns: table => new
|
||||
{
|
||||
ZombieClientStatId = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
MatchId = table.Column<int>(type: "integer", nullable: true),
|
||||
ClientId = table.Column<int>(type: "integer", nullable: false),
|
||||
Kills = table.Column<int>(type: "integer", nullable: false),
|
||||
Deaths = table.Column<int>(type: "integer", nullable: false),
|
||||
DamageDealt = table.Column<long>(type: "bigint", nullable: false),
|
||||
DamageReceived = table.Column<int>(type: "integer", nullable: false),
|
||||
Headshots = table.Column<int>(type: "integer", nullable: false),
|
||||
HeadshotKills = table.Column<int>(type: "integer", nullable: false),
|
||||
Melees = table.Column<int>(type: "integer", nullable: false),
|
||||
Downs = table.Column<int>(type: "integer", nullable: false),
|
||||
Revives = table.Column<int>(type: "integer", nullable: false),
|
||||
PointsEarned = table.Column<long>(type: "bigint", nullable: false),
|
||||
PointsSpent = table.Column<long>(type: "bigint", nullable: false),
|
||||
PerksConsumed = table.Column<int>(type: "integer", nullable: false),
|
||||
PowerupsGrabbed = table.Column<int>(type: "integer", nullable: false),
|
||||
CreatedDateTime = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedDateTime = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EFZombieClientStat", x => x.ZombieClientStatId);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieClientStat_EFClients_ClientId",
|
||||
column: x => x.ClientId,
|
||||
principalTable: "EFClients",
|
||||
principalColumn: "ClientId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieClientStat_EFZombieMatch_MatchId",
|
||||
column: x => x.MatchId,
|
||||
principalTable: "EFZombieMatch",
|
||||
principalColumn: "ZombieMatchId");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EFZombieEvents",
|
||||
columns: table => new
|
||||
{
|
||||
ZombieEventLogId = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
EventType = table.Column<int>(type: "integer", nullable: false),
|
||||
SourceClientId = table.Column<int>(type: "integer", nullable: true),
|
||||
AssociatedClientId = table.Column<int>(type: "integer", nullable: true),
|
||||
NumericalValue = table.Column<double>(type: "double precision", nullable: true),
|
||||
TextualValue = table.Column<string>(type: "text", nullable: true),
|
||||
MatchId = table.Column<int>(type: "integer", nullable: true),
|
||||
CreatedDateTime = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedDateTime = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EFZombieEvents", x => x.ZombieEventLogId);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieEvents_EFClients_AssociatedClientId",
|
||||
column: x => x.AssociatedClientId,
|
||||
principalTable: "EFClients",
|
||||
principalColumn: "ClientId");
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieEvents_EFClients_SourceClientId",
|
||||
column: x => x.SourceClientId,
|
||||
principalTable: "EFClients",
|
||||
principalColumn: "ClientId");
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieEvents_EFZombieMatch_MatchId",
|
||||
column: x => x.MatchId,
|
||||
principalTable: "EFZombieMatch",
|
||||
principalColumn: "ZombieMatchId");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EFZombieAggregateClientStat",
|
||||
columns: table => new
|
||||
{
|
||||
ZombieClientStatId = table.Column<long>(type: "bigint", nullable: false),
|
||||
ServerId = table.Column<long>(type: "bigint", nullable: true),
|
||||
AverageKillsPerDown = table.Column<double>(type: "double precision", nullable: false),
|
||||
AverageDowns = table.Column<double>(type: "double precision", nullable: false),
|
||||
AverageRevives = table.Column<double>(type: "double precision", nullable: false),
|
||||
HeadshotPercentage = table.Column<double>(type: "double precision", nullable: false),
|
||||
AlivePercentage = table.Column<double>(type: "double precision", nullable: false),
|
||||
AverageMelees = table.Column<double>(type: "double precision", nullable: false),
|
||||
AverageRoundReached = table.Column<double>(type: "double precision", nullable: false),
|
||||
AveragePoints = table.Column<double>(type: "double precision", nullable: false),
|
||||
HighestRound = table.Column<int>(type: "integer", nullable: false),
|
||||
TotalRoundsPlayed = table.Column<int>(type: "integer", nullable: false),
|
||||
TotalMatchesPlayed = table.Column<int>(type: "integer", nullable: false),
|
||||
TotalMatchesCompleted = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EFZombieAggregateClientStat", x => x.ZombieClientStatId);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieAggregateClientStat_EFServers_ServerId",
|
||||
column: x => x.ServerId,
|
||||
principalTable: "EFServers",
|
||||
principalColumn: "ServerId");
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieAggregateClientStat_EFZombieClientStat_ZombieClient~",
|
||||
column: x => x.ZombieClientStatId,
|
||||
principalTable: "EFZombieClientStat",
|
||||
principalColumn: "ZombieClientStatId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EFZombieMatchClientStat",
|
||||
columns: table => new
|
||||
{
|
||||
ZombieClientStatId = table.Column<long>(type: "bigint", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EFZombieMatchClientStat", x => x.ZombieClientStatId);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieMatchClientStat_EFZombieClientStat_ZombieClientStat~",
|
||||
column: x => x.ZombieClientStatId,
|
||||
principalTable: "EFZombieClientStat",
|
||||
principalColumn: "ZombieClientStatId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EFZombieRoundClientStat",
|
||||
columns: table => new
|
||||
{
|
||||
ZombieClientStatId = table.Column<long>(type: "bigint", nullable: false),
|
||||
StartTime = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
EndTime = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
Duration = table.Column<TimeSpan>(type: "interval", nullable: true),
|
||||
TimeAlive = table.Column<TimeSpan>(type: "interval", nullable: true),
|
||||
RoundNumber = table.Column<int>(type: "integer", nullable: false),
|
||||
Points = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EFZombieRoundClientStat", x => x.ZombieClientStatId);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieRoundClientStat_EFZombieClientStat_ZombieClientStat~",
|
||||
column: x => x.ZombieClientStatId,
|
||||
principalTable: "EFZombieClientStat",
|
||||
principalColumn: "ZombieClientStatId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EFZombieClientStatRecord",
|
||||
columns: table => new
|
||||
{
|
||||
ZombieClientStatRecordId = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Name = table.Column<string>(type: "text", nullable: false),
|
||||
Type = table.Column<string>(type: "text", nullable: false),
|
||||
Value = table.Column<string>(type: "text", nullable: false),
|
||||
ClientId = table.Column<int>(type: "integer", nullable: true),
|
||||
RoundId = table.Column<long>(type: "bigint", nullable: true),
|
||||
CreatedDateTime = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedDateTime = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EFZombieClientStatRecord", x => x.ZombieClientStatRecordId);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieClientStatRecord_EFClients_ClientId",
|
||||
column: x => x.ClientId,
|
||||
principalTable: "EFClients",
|
||||
principalColumn: "ClientId");
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieClientStatRecord_EFZombieRoundClientStat_RoundId",
|
||||
column: x => x.RoundId,
|
||||
principalTable: "EFZombieRoundClientStat",
|
||||
principalColumn: "ZombieClientStatId");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFZombieAggregateClientStat_ServerId",
|
||||
table: "EFZombieAggregateClientStat",
|
||||
column: "ServerId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFZombieClientStat_ClientId",
|
||||
table: "EFZombieClientStat",
|
||||
column: "ClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFZombieClientStat_MatchId",
|
||||
table: "EFZombieClientStat",
|
||||
column: "MatchId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFZombieClientStatRecord_ClientId",
|
||||
table: "EFZombieClientStatRecord",
|
||||
column: "ClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFZombieClientStatRecord_RoundId",
|
||||
table: "EFZombieClientStatRecord",
|
||||
column: "RoundId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFZombieEvents_AssociatedClientId",
|
||||
table: "EFZombieEvents",
|
||||
column: "AssociatedClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFZombieEvents_MatchId",
|
||||
table: "EFZombieEvents",
|
||||
column: "MatchId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFZombieEvents_SourceClientId",
|
||||
table: "EFZombieEvents",
|
||||
column: "SourceClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFZombieMatch_MapId",
|
||||
table: "EFZombieMatch",
|
||||
column: "MapId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFZombieMatch_ServerId",
|
||||
table: "EFZombieMatch",
|
||||
column: "ServerId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "EFZombieAggregateClientStat");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "EFZombieClientStatRecord");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "EFZombieEvents");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "EFZombieMatchClientStat");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "EFZombieRoundClientStat");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "EFZombieClientStat");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "EFZombieMatch");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PerformanceBucket",
|
||||
table: "EFServers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PerformanceBucket",
|
||||
table: "EFClientRankingHistory");
|
||||
}
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ namespace Data.Migrations.Postgresql
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "6.0.1")
|
||||
.HasAnnotation("ProductVersion", "8.0.1")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
@ -459,6 +459,9 @@ namespace Data.Migrations.Postgresql
|
||||
b.Property<bool>("Newest")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("PerformanceBucket")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<double?>("PerformanceMetric")
|
||||
.HasColumnType("double precision");
|
||||
|
||||
@ -1125,6 +1128,9 @@ namespace Data.Migrations.Postgresql
|
||||
b.Property<bool>("IsPasswordProtected")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("PerformanceBucket")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Port")
|
||||
.HasColumnType("integer");
|
||||
|
||||
@ -1222,6 +1228,278 @@ namespace Data.Migrations.Postgresql
|
||||
b.ToTable("Vector3", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieClientStat", b =>
|
||||
{
|
||||
b.Property<long>("ZombieClientStatId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("ZombieClientStatId"));
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedDateTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long>("DamageDealt")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("DamageReceived")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Deaths")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Downs")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("HeadshotKills")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Headshots")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Kills")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int?>("MatchId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Melees")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("PerksConsumed")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long>("PointsEarned")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("PointsSpent")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("PowerupsGrabbed")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Revives")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedDateTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("ZombieClientStatId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("MatchId");
|
||||
|
||||
b.ToTable("EFZombieClientStat", (string)null);
|
||||
|
||||
b.UseTptMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieClientStatRecord", b =>
|
||||
{
|
||||
b.Property<int>("ZombieClientStatRecordId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("ZombieClientStatRecordId"));
|
||||
|
||||
b.Property<int?>("ClientId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedDateTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long?>("RoundId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedDateTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("ZombieClientStatRecordId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("RoundId");
|
||||
|
||||
b.ToTable("EFZombieClientStatRecord", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieEventLog", b =>
|
||||
{
|
||||
b.Property<long>("ZombieEventLogId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("ZombieEventLogId"));
|
||||
|
||||
b.Property<int?>("AssociatedClientId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedDateTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("EventType")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int?>("MatchId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<double?>("NumericalValue")
|
||||
.HasColumnType("double precision");
|
||||
|
||||
b.Property<int?>("SourceClientId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("TextualValue")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedDateTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("ZombieEventLogId");
|
||||
|
||||
b.HasIndex("AssociatedClientId");
|
||||
|
||||
b.HasIndex("MatchId");
|
||||
|
||||
b.HasIndex("SourceClientId");
|
||||
|
||||
b.ToTable("EFZombieEvents", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieMatch", b =>
|
||||
{
|
||||
b.Property<int>("ZombieMatchId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("ZombieMatchId"));
|
||||
|
||||
b.Property<int>("ClientsCompleted")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedDateTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int?>("MapId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTimeOffset?>("MatchEndDate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTimeOffset>("MatchStartDate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long?>("ServerId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedDateTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("ZombieMatchId");
|
||||
|
||||
b.HasIndex("MapId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFZombieMatch", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieAggregateClientStat", b =>
|
||||
{
|
||||
b.HasBaseType("Data.Models.Zombie.ZombieClientStat");
|
||||
|
||||
b.Property<double>("AlivePercentage")
|
||||
.HasColumnType("double precision");
|
||||
|
||||
b.Property<double>("AverageDowns")
|
||||
.HasColumnType("double precision");
|
||||
|
||||
b.Property<double>("AverageKillsPerDown")
|
||||
.HasColumnType("double precision");
|
||||
|
||||
b.Property<double>("AverageMelees")
|
||||
.HasColumnType("double precision");
|
||||
|
||||
b.Property<double>("AveragePoints")
|
||||
.HasColumnType("double precision");
|
||||
|
||||
b.Property<double>("AverageRevives")
|
||||
.HasColumnType("double precision");
|
||||
|
||||
b.Property<double>("AverageRoundReached")
|
||||
.HasColumnType("double precision");
|
||||
|
||||
b.Property<double>("HeadshotPercentage")
|
||||
.HasColumnType("double precision");
|
||||
|
||||
b.Property<int>("HighestRound")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long?>("ServerId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("TotalMatchesCompleted")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("TotalMatchesPlayed")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("TotalRoundsPlayed")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFZombieAggregateClientStat", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieMatchClientStat", b =>
|
||||
{
|
||||
b.HasBaseType("Data.Models.Zombie.ZombieClientStat");
|
||||
|
||||
b.ToTable("EFZombieMatchClientStat", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieRoundClientStat", b =>
|
||||
{
|
||||
b.HasBaseType("Data.Models.Zombie.ZombieClientStat");
|
||||
|
||||
b.Property<TimeSpan?>("Duration")
|
||||
.HasColumnType("interval");
|
||||
|
||||
b.Property<DateTimeOffset?>("EndTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("Points")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("RoundNumber")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTimeOffset>("StartTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<TimeSpan?>("TimeAlive")
|
||||
.HasColumnType("interval");
|
||||
|
||||
b.ToTable("EFZombieRoundClientStat", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFACSnapshotVector3", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.Stats.EFACSnapshot", "Snapshot")
|
||||
@ -1663,6 +1941,107 @@ namespace Data.Migrations.Postgresql
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieClientStat", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.EFClient", "Client")
|
||||
.WithMany("ZombieClientStats")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Data.Models.Zombie.ZombieMatch", "Match")
|
||||
.WithMany()
|
||||
.HasForeignKey("MatchId");
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("Match");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieClientStatRecord", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId");
|
||||
|
||||
b.HasOne("Data.Models.Zombie.ZombieRoundClientStat", "Round")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoundId");
|
||||
|
||||
b.Navigation("Client");
|
||||
|
||||
b.Navigation("Round");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieEventLog", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.EFClient", "AssociatedClient")
|
||||
.WithMany()
|
||||
.HasForeignKey("AssociatedClientId");
|
||||
|
||||
b.HasOne("Data.Models.Zombie.ZombieMatch", "Match")
|
||||
.WithMany()
|
||||
.HasForeignKey("MatchId");
|
||||
|
||||
b.HasOne("Data.Models.Client.EFClient", "SourceClient")
|
||||
.WithMany()
|
||||
.HasForeignKey("SourceClientId");
|
||||
|
||||
b.Navigation("AssociatedClient");
|
||||
|
||||
b.Navigation("Match");
|
||||
|
||||
b.Navigation("SourceClient");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieMatch", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.Stats.Reference.EFMap", "Map")
|
||||
.WithMany()
|
||||
.HasForeignKey("MapId");
|
||||
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
|
||||
b.Navigation("Map");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieAggregateClientStat", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
|
||||
b.HasOne("Data.Models.Zombie.ZombieClientStat", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("Data.Models.Zombie.ZombieAggregateClientStat", "ZombieClientStatId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieMatchClientStat", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Zombie.ZombieClientStat", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("Data.Models.Zombie.ZombieMatchClientStat", "ZombieClientStatId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieRoundClientStat", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Zombie.ZombieClientStat", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("Data.Models.Zombie.ZombieRoundClientStat", "ZombieClientStatId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFClient", b =>
|
||||
{
|
||||
b.Navigation("AdministeredPenalties");
|
||||
@ -1670,6 +2049,8 @@ namespace Data.Migrations.Postgresql
|
||||
b.Navigation("Meta");
|
||||
|
||||
b.Navigation("ReceivedPenalties");
|
||||
|
||||
b.Navigation("ZombieClientStats");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b =>
|
||||
|
@ -11,8 +11,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
namespace Data.Migrations.Sqlite
|
||||
{
|
||||
[DbContext(typeof(SqliteDatabaseContext))]
|
||||
[Migration("20230507181011_AddZombieStatsInitial")]
|
||||
partial class AddZombieStatsInitial
|
||||
[Migration("20230905194120_IntitialZombieStats")]
|
||||
partial class IntitialZombieStats
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
@ -1117,6 +1117,8 @@ namespace Data.Migrations.Sqlite
|
||||
|
||||
b.HasKey("ServerSnapshotId");
|
||||
|
||||
b.HasIndex("CapturedAt");
|
||||
|
||||
b.HasIndex("MapId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
@ -1181,7 +1183,7 @@ namespace Data.Migrations.Sqlite
|
||||
b.Property<DateTimeOffset>("CreatedDateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("DamageDealt")
|
||||
b.Property<long>("DamageDealt")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("DamageReceived")
|
||||
@ -1337,15 +1339,15 @@ namespace Data.Migrations.Sqlite
|
||||
b.Property<double>("AverageRoundReached")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int?>("EFClientClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("HeadshotPercentage")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("HighestRound")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long?>("ServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TotalMatchesCompleted")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@ -1355,7 +1357,7 @@ namespace Data.Migrations.Sqlite
|
||||
b.Property<int>("TotalRoundsPlayed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasIndex("EFClientClientId");
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFZombieAggregateClientStat", (string)null);
|
||||
});
|
||||
@ -1364,11 +1366,6 @@ namespace Data.Migrations.Sqlite
|
||||
{
|
||||
b.HasBaseType("Data.Models.Zombie.ZombieClientStat");
|
||||
|
||||
b.Property<int?>("EFClientClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasIndex("EFClientClientId");
|
||||
|
||||
b.ToTable("EFZombieMatchClientStat", (string)null);
|
||||
});
|
||||
|
||||
@ -1379,9 +1376,6 @@ namespace Data.Migrations.Sqlite
|
||||
b.Property<TimeSpan?>("Duration")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("EFClientClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset?>("EndTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@ -1397,8 +1391,6 @@ namespace Data.Migrations.Sqlite
|
||||
b.Property<TimeSpan?>("TimeAlive")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasIndex("EFClientClientId");
|
||||
|
||||
b.ToTable("EFZombieRoundClientStat", (string)null);
|
||||
});
|
||||
|
||||
@ -1852,7 +1844,7 @@ namespace Data.Migrations.Sqlite
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Data.Models.Zombie.ZombieMatch", "Match")
|
||||
.WithMany("ClientStats")
|
||||
.WithMany()
|
||||
.HasForeignKey("MatchId");
|
||||
|
||||
b.Navigation("Client");
|
||||
@ -1896,23 +1888,21 @@ namespace Data.Migrations.Sqlite
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieAggregateClientStat", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.EFClient", null)
|
||||
.WithMany("ZombieAggregateClientStats")
|
||||
.HasForeignKey("EFClientClientId");
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
|
||||
b.HasOne("Data.Models.Zombie.ZombieClientStat", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("Data.Models.Zombie.ZombieAggregateClientStat", "ZombieClientStatId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieMatchClientStat", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.EFClient", null)
|
||||
.WithMany("ZombieMatchClientStats")
|
||||
.HasForeignKey("EFClientClientId");
|
||||
|
||||
b.HasOne("Data.Models.Zombie.ZombieClientStat", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("Data.Models.Zombie.ZombieMatchClientStat", "ZombieClientStatId")
|
||||
@ -1922,10 +1912,6 @@ namespace Data.Migrations.Sqlite
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieRoundClientStat", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.EFClient", null)
|
||||
.WithMany("ZombieRoundClientStats")
|
||||
.HasForeignKey("EFClientClientId");
|
||||
|
||||
b.HasOne("Data.Models.Zombie.ZombieClientStat", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("Data.Models.Zombie.ZombieRoundClientStat", "ZombieClientStatId")
|
||||
@ -1941,15 +1927,9 @@ namespace Data.Migrations.Sqlite
|
||||
|
||||
b.Navigation("ReceivedPenalties");
|
||||
|
||||
b.Navigation("ZombieAggregateClientStats");
|
||||
|
||||
b.Navigation("ZombieClientStats");
|
||||
|
||||
b.Navigation("ZombieMatchClientStats");
|
||||
|
||||
b.Navigation("ZombieMatches");
|
||||
|
||||
b.Navigation("ZombieRoundClientStats");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b =>
|
||||
@ -1973,11 +1953,6 @@ namespace Data.Migrations.Sqlite
|
||||
|
||||
b.Navigation("ReceivedPenalties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieMatch", b =>
|
||||
{
|
||||
b.Navigation("ClientStats");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Data.Migrations.Sqlite
|
||||
{
|
||||
public partial class AddZombieStatsInitial : Migration
|
||||
public partial class IntitialZombieStats : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
@ -66,7 +66,7 @@ namespace Data.Migrations.Sqlite
|
||||
ClientId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Kills = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Deaths = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
DamageDealt = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
DamageDealt = table.Column<long>(type: "INTEGER", nullable: false),
|
||||
DamageReceived = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Headshots = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Melees = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
@ -101,6 +101,7 @@ namespace Data.Migrations.Sqlite
|
||||
{
|
||||
ZombieClientStatId = table.Column<long>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
ServerId = table.Column<long>(type: "INTEGER", nullable: true),
|
||||
AverageKillsPerDown = table.Column<double>(type: "REAL", nullable: false),
|
||||
AverageDowns = table.Column<double>(type: "REAL", nullable: false),
|
||||
AverageRevives = table.Column<double>(type: "REAL", nullable: false),
|
||||
@ -112,17 +113,16 @@ namespace Data.Migrations.Sqlite
|
||||
HighestRound = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
TotalRoundsPlayed = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
TotalMatchesPlayed = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
TotalMatchesCompleted = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
EFClientClientId = table.Column<int>(type: "INTEGER", nullable: true)
|
||||
TotalMatchesCompleted = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EFZombieAggregateClientStat", x => x.ZombieClientStatId);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieAggregateClientStat_EFClients_EFClientClientId",
|
||||
column: x => x.EFClientClientId,
|
||||
principalTable: "EFClients",
|
||||
principalColumn: "ClientId");
|
||||
name: "FK_EFZombieAggregateClientStat_EFServers_ServerId",
|
||||
column: x => x.ServerId,
|
||||
principalTable: "EFServers",
|
||||
principalColumn: "ServerId");
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieAggregateClientStat_EFZombieClientStat_ZombieClientStatId",
|
||||
column: x => x.ZombieClientStatId,
|
||||
@ -136,17 +136,11 @@ namespace Data.Migrations.Sqlite
|
||||
columns: table => new
|
||||
{
|
||||
ZombieClientStatId = table.Column<long>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
EFClientClientId = table.Column<int>(type: "INTEGER", nullable: true)
|
||||
.Annotation("Sqlite:Autoincrement", true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EFZombieMatchClientStat", x => x.ZombieClientStatId);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieMatchClientStat_EFClients_EFClientClientId",
|
||||
column: x => x.EFClientClientId,
|
||||
principalTable: "EFClients",
|
||||
principalColumn: "ClientId");
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieMatchClientStat_EFZombieClientStat_ZombieClientStatId",
|
||||
column: x => x.ZombieClientStatId,
|
||||
@ -166,17 +160,11 @@ namespace Data.Migrations.Sqlite
|
||||
Duration = table.Column<TimeSpan>(type: "TEXT", nullable: true),
|
||||
TimeAlive = table.Column<TimeSpan>(type: "TEXT", nullable: true),
|
||||
RoundNumber = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Points = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
EFClientClientId = table.Column<int>(type: "INTEGER", nullable: true)
|
||||
Points = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EFZombieRoundClientStat", x => x.ZombieClientStatId);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieRoundClientStat_EFClients_EFClientClientId",
|
||||
column: x => x.EFClientClientId,
|
||||
principalTable: "EFClients",
|
||||
principalColumn: "ClientId");
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieRoundClientStat_EFZombieClientStat_ZombieClientStatId",
|
||||
column: x => x.ZombieClientStatId,
|
||||
@ -215,9 +203,9 @@ namespace Data.Migrations.Sqlite
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFZombieAggregateClientStat_EFClientClientId",
|
||||
name: "IX_EFZombieAggregateClientStat_ServerId",
|
||||
table: "EFZombieAggregateClientStat",
|
||||
column: "EFClientClientId");
|
||||
column: "ServerId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFZombieClientStat_ClientId",
|
||||
@ -253,16 +241,6 @@ namespace Data.Migrations.Sqlite
|
||||
name: "IX_EFZombieMatch_ServerId",
|
||||
table: "EFZombieMatch",
|
||||
column: "ServerId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFZombieMatchClientStat_EFClientClientId",
|
||||
table: "EFZombieMatchClientStat",
|
||||
column: "EFClientClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFZombieRoundClientStat_EFClientClientId",
|
||||
table: "EFZombieRoundClientStat",
|
||||
column: "EFClientClientId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
2010
Data/Migrations/Sqlite/20230906230124_AddZombieStatsEventLog.Designer.cs
generated
Normal file
2010
Data/Migrations/Sqlite/20230906230124_AddZombieStatsEventLog.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.Sqlite
|
||||
{
|
||||
public partial class AddZombieStatsEventLog : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_EFZombieMatch_EFClients_EFClientClientId",
|
||||
table: "EFZombieMatch");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_EFZombieMatch_EFClientClientId",
|
||||
table: "EFZombieMatch");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "EFClientClientId",
|
||||
table: "EFZombieMatch");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EFZombieEvents",
|
||||
columns: table => new
|
||||
{
|
||||
ZombieEventLogId = table.Column<long>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
EventType = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
SourceClientId = table.Column<int>(type: "INTEGER", nullable: true),
|
||||
AssociatedClientId = table.Column<int>(type: "INTEGER", nullable: true),
|
||||
NumericalValue = table.Column<double>(type: "REAL", nullable: true),
|
||||
TextualValue = table.Column<string>(type: "TEXT", nullable: true),
|
||||
MatchId = table.Column<int>(type: "INTEGER", nullable: true),
|
||||
CreatedDateTime = table.Column<DateTimeOffset>(type: "TEXT", nullable: false),
|
||||
UpdatedDateTime = table.Column<DateTimeOffset>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EFZombieEvents", x => x.ZombieEventLogId);
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieEvents_EFClients_AssociatedClientId",
|
||||
column: x => x.AssociatedClientId,
|
||||
principalTable: "EFClients",
|
||||
principalColumn: "ClientId");
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieEvents_EFClients_SourceClientId",
|
||||
column: x => x.SourceClientId,
|
||||
principalTable: "EFClients",
|
||||
principalColumn: "ClientId");
|
||||
table.ForeignKey(
|
||||
name: "FK_EFZombieEvents_EFZombieMatch_MatchId",
|
||||
column: x => x.MatchId,
|
||||
principalTable: "EFZombieMatch",
|
||||
principalColumn: "ZombieMatchId");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFZombieEvents_AssociatedClientId",
|
||||
table: "EFZombieEvents",
|
||||
column: "AssociatedClientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFZombieEvents_MatchId",
|
||||
table: "EFZombieEvents",
|
||||
column: "MatchId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFZombieEvents_SourceClientId",
|
||||
table: "EFZombieEvents",
|
||||
column: "SourceClientId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "EFZombieEvents");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "EFClientClientId",
|
||||
table: "EFZombieMatch",
|
||||
type: "INTEGER",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFZombieMatch_EFClientClientId",
|
||||
table: "EFZombieMatch",
|
||||
column: "EFClientClientId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_EFZombieMatch_EFClients_EFClientClientId",
|
||||
table: "EFZombieMatch",
|
||||
column: "EFClientClientId",
|
||||
principalTable: "EFClients",
|
||||
principalColumn: "ClientId");
|
||||
}
|
||||
}
|
||||
}
|
2016
Data/Migrations/Sqlite/20240212024708_AddHeadshotKillsToZombieClientState.Designer.cs
generated
Normal file
2016
Data/Migrations/Sqlite/20240212024708_AddHeadshotKillsToZombieClientState.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations.Sqlite
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddHeadshotKillsToZombieClientState : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "HeadshotKills",
|
||||
table: "EFZombieClientStat",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "HeadshotKills",
|
||||
table: "EFZombieClientStat");
|
||||
}
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ namespace Data.Migrations.Sqlite
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.1");
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.1");
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.EFACSnapshotVector3", b =>
|
||||
{
|
||||
@ -1181,7 +1181,7 @@ namespace Data.Migrations.Sqlite
|
||||
b.Property<DateTimeOffset>("CreatedDateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("DamageDealt")
|
||||
b.Property<long>("DamageDealt")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("DamageReceived")
|
||||
@ -1193,6 +1193,9 @@ namespace Data.Migrations.Sqlite
|
||||
b.Property<int>("Downs")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("HeadshotKills")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Headshots")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@ -1230,6 +1233,8 @@ namespace Data.Migrations.Sqlite
|
||||
b.HasIndex("MatchId");
|
||||
|
||||
b.ToTable("EFZombieClientStat", (string)null);
|
||||
|
||||
b.UseTptMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieClientStatRecord", b =>
|
||||
@ -1271,6 +1276,47 @@ namespace Data.Migrations.Sqlite
|
||||
b.ToTable("EFZombieClientStatRecord", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieEventLog", b =>
|
||||
{
|
||||
b.Property<long>("ZombieEventLogId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("AssociatedClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedDateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EventType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("MatchId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double?>("NumericalValue")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int?>("SourceClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TextualValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedDateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("ZombieEventLogId");
|
||||
|
||||
b.HasIndex("AssociatedClientId");
|
||||
|
||||
b.HasIndex("MatchId");
|
||||
|
||||
b.HasIndex("SourceClientId");
|
||||
|
||||
b.ToTable("EFZombieEvents", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieMatch", b =>
|
||||
{
|
||||
b.Property<int>("ZombieMatchId")
|
||||
@ -1283,9 +1329,6 @@ namespace Data.Migrations.Sqlite
|
||||
b.Property<DateTimeOffset>("CreatedDateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("EFClientClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("MapId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@ -1303,8 +1346,6 @@ namespace Data.Migrations.Sqlite
|
||||
|
||||
b.HasKey("ZombieMatchId");
|
||||
|
||||
b.HasIndex("EFClientClientId");
|
||||
|
||||
b.HasIndex("MapId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
@ -1337,15 +1378,15 @@ namespace Data.Migrations.Sqlite
|
||||
b.Property<double>("AverageRoundReached")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int?>("EFClientClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("HeadshotPercentage")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("HighestRound")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long?>("ServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TotalMatchesCompleted")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@ -1355,7 +1396,7 @@ namespace Data.Migrations.Sqlite
|
||||
b.Property<int>("TotalRoundsPlayed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasIndex("EFClientClientId");
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFZombieAggregateClientStat", (string)null);
|
||||
});
|
||||
@ -1364,11 +1405,6 @@ namespace Data.Migrations.Sqlite
|
||||
{
|
||||
b.HasBaseType("Data.Models.Zombie.ZombieClientStat");
|
||||
|
||||
b.Property<int?>("EFClientClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasIndex("EFClientClientId");
|
||||
|
||||
b.ToTable("EFZombieMatchClientStat", (string)null);
|
||||
});
|
||||
|
||||
@ -1379,9 +1415,6 @@ namespace Data.Migrations.Sqlite
|
||||
b.Property<TimeSpan?>("Duration")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("EFClientClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset?>("EndTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@ -1397,8 +1430,6 @@ namespace Data.Migrations.Sqlite
|
||||
b.Property<TimeSpan?>("TimeAlive")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasIndex("EFClientClientId");
|
||||
|
||||
b.ToTable("EFZombieRoundClientStat", (string)null);
|
||||
});
|
||||
|
||||
@ -1852,7 +1883,7 @@ namespace Data.Migrations.Sqlite
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Data.Models.Zombie.ZombieMatch", "Match")
|
||||
.WithMany("ClientStats")
|
||||
.WithMany()
|
||||
.HasForeignKey("MatchId");
|
||||
|
||||
b.Navigation("Client");
|
||||
@ -1875,12 +1906,29 @@ namespace Data.Migrations.Sqlite
|
||||
b.Navigation("Round");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieEventLog", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.EFClient", "AssociatedClient")
|
||||
.WithMany()
|
||||
.HasForeignKey("AssociatedClientId");
|
||||
|
||||
b.HasOne("Data.Models.Zombie.ZombieMatch", "Match")
|
||||
.WithMany()
|
||||
.HasForeignKey("MatchId");
|
||||
|
||||
b.HasOne("Data.Models.Client.EFClient", "SourceClient")
|
||||
.WithMany()
|
||||
.HasForeignKey("SourceClientId");
|
||||
|
||||
b.Navigation("AssociatedClient");
|
||||
|
||||
b.Navigation("Match");
|
||||
|
||||
b.Navigation("SourceClient");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieMatch", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.EFClient", null)
|
||||
.WithMany("ZombieMatches")
|
||||
.HasForeignKey("EFClientClientId");
|
||||
|
||||
b.HasOne("Data.Models.Client.Stats.Reference.EFMap", "Map")
|
||||
.WithMany()
|
||||
.HasForeignKey("MapId");
|
||||
@ -1896,23 +1944,21 @@ namespace Data.Migrations.Sqlite
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieAggregateClientStat", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.EFClient", null)
|
||||
.WithMany("ZombieAggregateClientStats")
|
||||
.HasForeignKey("EFClientClientId");
|
||||
b.HasOne("Data.Models.Server.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
|
||||
b.HasOne("Data.Models.Zombie.ZombieClientStat", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("Data.Models.Zombie.ZombieAggregateClientStat", "ZombieClientStatId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieMatchClientStat", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.EFClient", null)
|
||||
.WithMany("ZombieMatchClientStats")
|
||||
.HasForeignKey("EFClientClientId");
|
||||
|
||||
b.HasOne("Data.Models.Zombie.ZombieClientStat", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("Data.Models.Zombie.ZombieMatchClientStat", "ZombieClientStatId")
|
||||
@ -1922,10 +1968,6 @@ namespace Data.Migrations.Sqlite
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieRoundClientStat", b =>
|
||||
{
|
||||
b.HasOne("Data.Models.Client.EFClient", null)
|
||||
.WithMany("ZombieRoundClientStats")
|
||||
.HasForeignKey("EFClientClientId");
|
||||
|
||||
b.HasOne("Data.Models.Zombie.ZombieClientStat", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("Data.Models.Zombie.ZombieRoundClientStat", "ZombieClientStatId")
|
||||
@ -1941,15 +1983,7 @@ namespace Data.Migrations.Sqlite
|
||||
|
||||
b.Navigation("ReceivedPenalties");
|
||||
|
||||
b.Navigation("ZombieAggregateClientStats");
|
||||
|
||||
b.Navigation("ZombieClientStats");
|
||||
|
||||
b.Navigation("ZombieMatchClientStats");
|
||||
|
||||
b.Navigation("ZombieMatches");
|
||||
|
||||
b.Navigation("ZombieRoundClientStats");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Client.Stats.EFACSnapshot", b =>
|
||||
@ -1973,11 +2007,6 @@ namespace Data.Migrations.Sqlite
|
||||
|
||||
b.Navigation("ReceivedPenalties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Data.Models.Zombie.ZombieMatch", b =>
|
||||
{
|
||||
b.Navigation("ClientStats");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
|
@ -84,10 +84,6 @@ namespace Data.Models.Client
|
||||
public virtual ICollection<EFMeta> Meta { get; set; }
|
||||
public virtual ICollection<EFPenalty> ReceivedPenalties { get; set; }
|
||||
public virtual ICollection<EFPenalty> AdministeredPenalties { get; set; }
|
||||
public virtual ICollection<ZombieAggregateClientStat> ZombieAggregateClientStats { get; set; }
|
||||
public virtual ICollection<ZombieClientStat> ZombieClientStats { get; set; }
|
||||
public virtual ICollection<ZombieMatch> ZombieMatches { get; set; }
|
||||
public virtual ICollection<ZombieMatchClientStat> ZombieMatchClientStats { get; set; }
|
||||
public virtual ICollection<ZombieRoundClientStat> ZombieRoundClientStats { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,9 @@
|
||||
|
||||
namespace Data.Models;
|
||||
|
||||
public class DatedRecord
|
||||
public class DatedRecord : IdentifierRecord
|
||||
{
|
||||
public DateTimeOffset CreatedDateTime { get; set; } = DateTimeOffset.UtcNow;
|
||||
public DateTimeOffset? UpdatedDateTime { get; set; }
|
||||
public override long Id { get; }
|
||||
}
|
||||
|
6
Data/Models/IdentifierRecord.cs
Normal file
6
Data/Models/IdentifierRecord.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace Data.Models;
|
||||
|
||||
public abstract class IdentifierRecord
|
||||
{
|
||||
public abstract long Id { get; }
|
||||
}
|
@ -1,9 +1,15 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using Data.Models.Server;
|
||||
|
||||
namespace Data.Models.Zombie;
|
||||
|
||||
public class ZombieAggregateClientStat : ZombieClientStat
|
||||
{
|
||||
public long? ServerId { get; set; }
|
||||
[ForeignKey(nameof(ServerId))]
|
||||
public EFServer Server { get; set; }
|
||||
|
||||
#region Average
|
||||
|
||||
public double AverageKillsPerDown { get; set; }
|
||||
@ -41,4 +47,8 @@ public class ZombieAggregateClientStat : ZombieClientStat
|
||||
nameof(TotalRoundsPlayed),
|
||||
nameof(TotalMatchesPlayed)
|
||||
};
|
||||
|
||||
public static readonly string[] SkillKeys =
|
||||
RecordsKeys.Except(new[] { nameof(TotalMatchesPlayed), nameof(TotalRoundsPlayed), nameof(AverageDowns) })
|
||||
.ToArray();
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ public abstract class ZombieClientStat : DatedRecord
|
||||
[Key]
|
||||
public long ZombieClientStatId { get; set; }
|
||||
|
||||
[NotMapped] public override long Id => ZombieClientStatId;
|
||||
|
||||
public int? MatchId { get; set; }
|
||||
|
||||
[ForeignKey(nameof(MatchId))]
|
||||
@ -17,13 +19,14 @@ public abstract class ZombieClientStat : DatedRecord
|
||||
|
||||
public int ClientId { get; set; }
|
||||
[ForeignKey(nameof(ClientId))]
|
||||
public virtual EFClient? Client { get; set; }
|
||||
public virtual EFClient Client { get; set; }
|
||||
|
||||
public int Kills { get; set; }
|
||||
public int Deaths { get; set; }
|
||||
public int DamageDealt { get; set; }
|
||||
public long DamageDealt { get; set; }
|
||||
public int DamageReceived { get; set; }
|
||||
public int Headshots { get; set; }
|
||||
public int HeadshotKills { get; set; }
|
||||
public int Melees { get; set; }
|
||||
public int Downs { get; set; }
|
||||
public int Revives { get; set; }
|
||||
|
@ -15,6 +15,9 @@ public class ZombieClientStatRecord : DatedRecord
|
||||
{
|
||||
[Key]
|
||||
public int ZombieClientStatRecordId { get; set; }
|
||||
|
||||
[NotMapped] public override long Id => ZombieClientStatRecordId;
|
||||
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Type { get; set; } = string.Empty;
|
||||
public string Value { get; set; } = string.Empty;
|
||||
|
48
Data/Models/Zombie/ZombieEventLog.cs
Normal file
48
Data/Models/Zombie/ZombieEventLog.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Data.Models.Client;
|
||||
|
||||
namespace Data.Models.Zombie;
|
||||
|
||||
public enum EventLogType
|
||||
{
|
||||
Default = 0,
|
||||
PerformanceCluster = 1,
|
||||
DamageTaken = 2,
|
||||
Downed = 3,
|
||||
Died = 4,
|
||||
Revived = 5,
|
||||
WasRevived = 6,
|
||||
PerkConsumed = 7,
|
||||
PowerupGrabbed = 8,
|
||||
RoundCompleted = 9,
|
||||
JoinedMatch = 10,
|
||||
LeftMatch = 11,
|
||||
MatchStarted = 12,
|
||||
MatchEnded = 13
|
||||
}
|
||||
|
||||
public class ZombieEventLog : DatedRecord
|
||||
{
|
||||
[Key]
|
||||
public long ZombieEventLogId { get; set; }
|
||||
|
||||
[NotMapped] public override long Id => ZombieEventLogId;
|
||||
|
||||
public EventLogType EventType { get; set; }
|
||||
|
||||
public int? SourceClientId { get; set; }
|
||||
[ForeignKey(nameof(SourceClientId))]
|
||||
public EFClient SourceClient { get; set; }
|
||||
|
||||
public int? AssociatedClientId { get; set; }
|
||||
[ForeignKey(nameof(AssociatedClientId))]
|
||||
public EFClient AssociatedClient { get; set; }
|
||||
|
||||
public double? NumericalValue { get; set; }
|
||||
public string TextualValue { get; set; }
|
||||
|
||||
public int? MatchId { get; set; }
|
||||
[ForeignKey(nameof(MatchId))]
|
||||
public ZombieMatch Match { get; set; }
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Data.Models.Client.Stats.Reference;
|
||||
@ -13,6 +12,8 @@ public class ZombieMatch : DatedRecord
|
||||
[Key]
|
||||
public int ZombieMatchId { get; set; }
|
||||
|
||||
[NotMapped] public override long Id => ZombieMatchId;
|
||||
|
||||
public int? MapId { get; set; }
|
||||
[ForeignKey(nameof(MapId))]
|
||||
public virtual EFMap? Map { get; set; }
|
||||
@ -23,8 +24,6 @@ public class ZombieMatch : DatedRecord
|
||||
|
||||
public int ClientsCompleted { get; set; }
|
||||
|
||||
public virtual ICollection<ZombieClientStat>? ClientStats { get; set; }
|
||||
|
||||
public DateTimeOffset MatchStartDate { get; set; } = DateTimeOffset.UtcNow;
|
||||
public DateTimeOffset? MatchEndDate { get; set; }
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
namespace Data.Models.Zombie;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Data.Models.Zombie;
|
||||
|
||||
public class ZombieMatchClientStat : ZombieClientStat
|
||||
{
|
||||
|
||||
[NotMapped] public int? JoinedRound { get; set; }
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ namespace Stats.Client
|
||||
|
||||
var iqPerformances = set
|
||||
.Where(s => s.Skill > 0)
|
||||
.Where(s => s.EloRating > 0)
|
||||
.Where(s => s.EloRating >= 0)
|
||||
.Where(s => s.Client.Level != EFClient.Permission.Banned);
|
||||
|
||||
foreach (var serverId in _serverIds)
|
||||
@ -71,30 +71,33 @@ namespace Stats.Client
|
||||
distributions.Add(serverId.ToString(), distributionParams);
|
||||
}
|
||||
|
||||
foreach (var server in _appConfig.Servers)
|
||||
foreach (var performanceBucketGroup in _appConfig.Servers.GroupBy(server => server.PerformanceBucket))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(server.PerformanceBucket))
|
||||
if (string.IsNullOrWhiteSpace(performanceBucketGroup.Key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var performanceBucket = performanceBucketGroup.Key;
|
||||
|
||||
var bucketConfig =
|
||||
_configuration.PerformanceBuckets.FirstOrDefault(bucket =>
|
||||
bucket.Name == server.PerformanceBucket) ?? new PerformanceBucketConfiguration();
|
||||
bucket.Name == performanceBucket) ?? new PerformanceBucketConfiguration();
|
||||
|
||||
var oldestPerf = DateTimeOffset.UtcNow - bucketConfig.RankingExpiration;
|
||||
var oldestPerf = DateTime.UtcNow - bucketConfig.RankingExpiration;
|
||||
var performances = await iqPerformances
|
||||
.Where(perf => perf.Server.PerformanceBucket == server.PerformanceBucket)
|
||||
.Where(perf => perf.Server.PerformanceBucket == performanceBucket)
|
||||
.Where(perf => perf.TimePlayed >= bucketConfig.ClientMinPlayTime.TotalSeconds)
|
||||
.Where(perf => perf.UpdatedAt >= oldestPerf)
|
||||
.Where(perf => perf.Skill < 999999)
|
||||
.Select(s => s.EloRating * 1 / 3.0 + s.Skill * 2 / 3.0)
|
||||
.ToListAsync(token);
|
||||
var distributionParams = performances.GenerateDistributionParameters();
|
||||
distributions.Add(server.PerformanceBucket, distributionParams);
|
||||
distributions.Add(performanceBucket, distributionParams);
|
||||
}
|
||||
|
||||
return distributions;
|
||||
}, DistributionCacheKey, Utilities.IsDevelopment ? TimeSpan.FromMinutes(5) : TimeSpan.FromHours(1));
|
||||
}, DistributionCacheKey, Utilities.IsDevelopment ? TimeSpan.FromMinutes(1) : TimeSpan.FromHours(1));
|
||||
|
||||
foreach (var server in _appConfig.Servers)
|
||||
{
|
||||
@ -117,7 +120,7 @@ namespace Stats.Client
|
||||
var zScore = await set
|
||||
.Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc(validPlayTime, oldestStat))
|
||||
.Where(s => s.Skill > 0)
|
||||
.Where(s => s.EloRating > 0)
|
||||
.Where(s => s.EloRating >= 1)
|
||||
.Where(stat =>
|
||||
performanceBucket == null || performanceBucket == stat.Server.PerformanceBucket)
|
||||
.GroupBy(stat => stat.ClientId)
|
||||
@ -127,7 +130,7 @@ namespace Stats.Client
|
||||
|
||||
return zScore ?? 0;
|
||||
}, MaxZScoreCacheKey, new[] { server.PerformanceBucket },
|
||||
Utilities.IsDevelopment ? TimeSpan.FromMinutes(5) : TimeSpan.FromMinutes(30));
|
||||
Utilities.IsDevelopment ? TimeSpan.FromMinutes(1) : TimeSpan.FromMinutes(30));
|
||||
|
||||
await _maxZScoreCache.GetCacheItem(MaxZScoreCacheKey, new[] { server.PerformanceBucket });
|
||||
}
|
||||
@ -199,6 +202,8 @@ namespace Stats.Client
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
value = Math.Max(1, value);
|
||||
|
||||
var zScore = (Math.Log(value) - sdParams.Mean) / sdParams.Sigma;
|
||||
return zScore;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using Data.Models;
|
||||
using Data.Models.Client;
|
||||
using Data.Models.Client.Stats;
|
||||
using SharedLibraryCore.Dtos;
|
||||
@ -25,5 +26,6 @@ namespace Stats.Dtos
|
||||
public List<EFClientHitStatistic> ByAttachmentCombo { get; set; }
|
||||
public List<EFClientRankingHistory> Ratings { get; set; }
|
||||
public List<EFClientStatistics> LegacyStats { get; set; }
|
||||
public List<EFMeta> CustomMetrics { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
using SharedLibraryCore.Dtos;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Data.Models;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Stats.Web.Dtos
|
||||
{
|
||||
@ -22,6 +22,7 @@ namespace IW4MAdmin.Plugins.Stats.Web.Dtos
|
||||
public List<PerformanceHistory> PerformanceHistory { get; set; }
|
||||
public double? ZScore { get; set; }
|
||||
public long? ServerId { get; set; }
|
||||
public List<EFMeta> Metrics { get; } = new();
|
||||
}
|
||||
|
||||
public class PerformanceHistory
|
||||
|
@ -110,25 +110,26 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Expression<Func<EFClientRankingHistory, bool>> GetNewRankingFunc(int? clientId = null,
|
||||
long? serverId = null)
|
||||
private Expression<Func<EFClientRankingHistory, bool>> GetNewRankingFunc(TimeSpan oldestStat, TimeSpan minPlayTime, long? serverId = null)
|
||||
{
|
||||
return (ranking) => ranking.ServerId == serverId
|
||||
&& ranking.Client.Level != Data.Models.Client.EFClient.Permission.Banned
|
||||
&& ranking.CreatedDateTime >= Extensions.FifteenDaysAgo()
|
||||
&& ranking.ZScore != null
|
||||
&& ranking.PerformanceMetric != null
|
||||
&& ranking.Newest
|
||||
&& ranking.Client.TotalConnectionTime >=
|
||||
_config.TopPlayersMinPlayTime;
|
||||
var oldestDate = DateTime.UtcNow - oldestStat;
|
||||
return ranking => ranking.ServerId == serverId
|
||||
&& ranking.Client.Level != Data.Models.Client.EFClient.Permission.Banned
|
||||
&& ranking.CreatedDateTime >= oldestDate
|
||||
&& ranking.ZScore != null
|
||||
&& ranking.PerformanceMetric != null
|
||||
&& ranking.Newest
|
||||
&& ranking.Client.TotalConnectionTime >= (int)minPlayTime.TotalSeconds;
|
||||
}
|
||||
|
||||
public async Task<int> GetTotalRankedPlayers(long serverId)
|
||||
{
|
||||
var bucketConfig = await GetBucketConfig(serverId);
|
||||
|
||||
await using var context = _contextFactory.CreateContext(enableTracking: false);
|
||||
|
||||
return await context.Set<EFClientRankingHistory>()
|
||||
.Where(GetNewRankingFunc(serverId: serverId))
|
||||
.Where(GetNewRankingFunc(bucketConfig.RankingExpiration, bucketConfig.ClientMinPlayTime, serverId: serverId))
|
||||
.CountAsync();
|
||||
}
|
||||
|
||||
@ -143,12 +144,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
public DateTime CreatedDateTime { get; set; }
|
||||
}
|
||||
|
||||
public async Task<List<TopStatsInfo>> GetNewTopStats(int start, int count, long? serverId = null)
|
||||
public async Task<List<TopStatsInfo>> GetNewTopStats(int start, int count, long? serverId = null, string performanceBucket = null)
|
||||
{
|
||||
await using var context = _contextFactory.CreateContext(false);
|
||||
var bucketConfig = await GetBucketConfig(serverId);
|
||||
|
||||
await using var context = _contextFactory.CreateContext(false);
|
||||
var clientIdsList = await context.Set<EFClientRankingHistory>()
|
||||
.Where(GetNewRankingFunc(serverId: serverId))
|
||||
.Where(GetNewRankingFunc(bucketConfig.RankingExpiration, bucketConfig.ClientMinPlayTime, serverId: serverId))
|
||||
.OrderByDescending(ranking => ranking.PerformanceMetric)
|
||||
.Select(ranking => ranking.ClientId)
|
||||
.Skip(start)
|
||||
@ -233,9 +235,77 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
.OrderBy(r => r.Ranking)
|
||||
.ToList();
|
||||
|
||||
foreach (var topStatsInfo in finished)
|
||||
{
|
||||
topStatsInfo.Metrics.AddRange(new EFMeta[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
Extra = "Kills",
|
||||
Value = topStatsInfo.Kills.ToNumericalString(),
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_KILLS"]
|
||||
},
|
||||
new()
|
||||
{
|
||||
Extra = "Deaths",
|
||||
Value = topStatsInfo.Deaths.ToNumericalString(),
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_DEATHS"]
|
||||
},
|
||||
new()
|
||||
{
|
||||
Extra = "KDR",
|
||||
Value = topStatsInfo.KDR.ToNumericalString(),
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_KDR"]
|
||||
},
|
||||
new()
|
||||
{
|
||||
Extra = "TimePlayed",
|
||||
Value = topStatsInfo.TimePlayedValue.HumanizeForCurrentCulture(),
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_PLAYER"]
|
||||
},
|
||||
new()
|
||||
{
|
||||
Extra = "LastSeen",
|
||||
Value = topStatsInfo.LastSeenValue.HumanizeForCurrentCulture(),
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_LSEEN"]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var customMetricFunc in Plugin.ServerManager.CustomStatsMetrics)
|
||||
{
|
||||
await customMetricFunc(finished.ToDictionary(kvp => kvp.ClientId, kvp => kvp.Metrics), serverId,
|
||||
performanceBucket, true);
|
||||
}
|
||||
|
||||
return finished;
|
||||
}
|
||||
|
||||
private async Task<PerformanceBucketConfiguration> GetBucketConfig(long? serverId)
|
||||
{
|
||||
var defaultConfig = new PerformanceBucketConfiguration
|
||||
{
|
||||
ClientMinPlayTime = TimeSpan.FromSeconds(_config.TopPlayersMinPlayTime),
|
||||
RankingExpiration = DateTime.UtcNow - Extensions.FifteenDaysAgo()
|
||||
};
|
||||
|
||||
if (serverId is null)
|
||||
{
|
||||
return defaultConfig;
|
||||
}
|
||||
|
||||
var performanceBucket =
|
||||
(await _serverCache.FirstAsync(server => server.Id == serverId)).PerformanceBucket;
|
||||
|
||||
if (string.IsNullOrEmpty(performanceBucket))
|
||||
{
|
||||
return defaultConfig;
|
||||
}
|
||||
|
||||
return _config.PerformanceBuckets.FirstOrDefault(bucket => bucket.Name == performanceBucket) ??
|
||||
defaultConfig;
|
||||
}
|
||||
|
||||
public async Task<List<TopStatsInfo>> GetTopStats(int start, int count, long? serverId = null)
|
||||
{
|
||||
if (_config.EnableAdvancedMetrics)
|
||||
@ -1179,54 +1249,41 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
public async Task UpdateHistoricalRanking(int clientId, EFClientStatistics clientStats, long serverId)
|
||||
{
|
||||
var bucketConfig = await GetBucketConfig(serverId);
|
||||
|
||||
await using var context = _contextFactory.CreateContext();
|
||||
var minPlayTime = _config.TopPlayersMinPlayTime;
|
||||
var oldestStat = DateTimeOffset.UtcNow - Extensions.FifteenDaysAgo();
|
||||
|
||||
var performanceBucket =
|
||||
(await _serverCache.FirstAsync(server => server.Id == serverId)).PerformanceBucket;
|
||||
|
||||
if (!string.IsNullOrEmpty(performanceBucket))
|
||||
{
|
||||
var bucketConfig = _config.PerformanceBuckets.FirstOrDefault(cfg => cfg.Name == performanceBucket) ??
|
||||
new PerformanceBucketConfiguration();
|
||||
|
||||
minPlayTime = (int)bucketConfig.ClientMinPlayTime.TotalSeconds;
|
||||
oldestStat = bucketConfig.RankingExpiration;
|
||||
}
|
||||
|
||||
var oldestStateDate = DateTime.UtcNow - oldestStat;
|
||||
var oldestStateDate = DateTime.UtcNow - bucketConfig.RankingExpiration;
|
||||
var performances = await context.Set<EFClientStatistics>()
|
||||
.AsNoTracking()
|
||||
.Include(stat => stat.Server)
|
||||
.Where(stat => stat.ClientId == clientId)
|
||||
.Where(stat => stat.ServerId != serverId) // ignore the one we're currently tracking
|
||||
.Where(stats => stats.UpdatedAt >= oldestStateDate)
|
||||
.Where(stats => stats.TimePlayed >= minPlayTime)
|
||||
.Where(stats => stats.TimePlayed >= (int)bucketConfig.ClientMinPlayTime.TotalSeconds)
|
||||
.ToListAsync();
|
||||
|
||||
if (clientStats.TimePlayed >= minPlayTime)
|
||||
if (clientStats.TimePlayed >= bucketConfig.ClientMinPlayTime.TotalSeconds)
|
||||
{
|
||||
await UpdateForServer(clientId, clientStats, context, minPlayTime, oldestStat, serverId);
|
||||
await UpdateForServer(clientId, clientStats, context, (int)bucketConfig.ClientMinPlayTime.TotalSeconds, bucketConfig.RankingExpiration, serverId);
|
||||
clientStats.Server = await _serverCache.FirstAsync(server => server.Id == serverId);
|
||||
performances.Add(clientStats);
|
||||
}
|
||||
|
||||
if (performances.Any(performance => performance.TimePlayed >= minPlayTime))
|
||||
if (performances.Any(performance => performance.TimePlayed >= (int)bucketConfig.ClientMinPlayTime.TotalSeconds))
|
||||
{
|
||||
await UpdateAggregateForServerOrBucket(clientId, clientStats, context, performances, minPlayTime,
|
||||
oldestStat, performanceBucket);
|
||||
await UpdateAggregateForServerOrBucket(clientId, clientStats, context, performances, bucketConfig);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateAggregateForServerOrBucket(int clientId, EFClientStatistics clientStats, DatabaseContext context, List<EFClientStatistics> performances,
|
||||
int minPlayTime, TimeSpan oldestStat, string performanceBucket)
|
||||
private async Task UpdateAggregateForServerOrBucket(int clientId, EFClientStatistics clientStats, DatabaseContext context, List<EFClientStatistics> performances, PerformanceBucketConfiguration bucketConfig)
|
||||
{
|
||||
var aggregateZScore =
|
||||
performances.Where(performance => performance.Server.PerformanceBucket == performanceBucket)
|
||||
.WeightValueByPlaytime(nameof(EFClientStatistics.ZScore), minPlayTime);
|
||||
performances.Where(performance => performance.Server.PerformanceBucket == bucketConfig.Name)
|
||||
.WeightValueByPlaytime(nameof(EFClientStatistics.ZScore), (int)bucketConfig.ClientMinPlayTime.TotalSeconds);
|
||||
|
||||
int? aggregateRanking = await context.Set<EFClientStatistics>()
|
||||
.Where(stat => stat.ClientId != clientId)
|
||||
.Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc(minPlayTime, oldestStat))
|
||||
.Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc((int)bucketConfig.ClientMinPlayTime.TotalSeconds, bucketConfig.RankingExpiration))
|
||||
.GroupBy(stat => stat.ClientId)
|
||||
.Where(group =>
|
||||
group.Sum(stat => stat.ZScore * stat.TimePlayed) / group.Sum(stat => stat.TimePlayed) >
|
||||
@ -1234,7 +1291,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
.Select(c => c.Key)
|
||||
.CountAsync();
|
||||
|
||||
var newPerformanceMetric = await _serverDistributionCalculator.GetRatingForZScore(aggregateZScore, performanceBucket);
|
||||
var newPerformanceMetric = await _serverDistributionCalculator.GetRatingForZScore(aggregateZScore, bucketConfig.Name);
|
||||
|
||||
if (newPerformanceMetric == null)
|
||||
{
|
||||
@ -1249,13 +1306,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
ZScore = aggregateZScore,
|
||||
Ranking = aggregateRanking,
|
||||
PerformanceMetric = newPerformanceMetric,
|
||||
PerformanceBucket = performanceBucket,
|
||||
PerformanceBucket = bucketConfig.Name,
|
||||
Newest = true,
|
||||
};
|
||||
|
||||
context.Add(aggregateRankingSnapshot);
|
||||
|
||||
await PruneOldRankings(context, clientId);
|
||||
await PruneOldRankings(context, clientId, performanceBucket: bucketConfig.Name);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
@ -1364,6 +1421,18 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
attackerStats.EloRating = Math.Max(0, Math.Round(attackerStats.EloRating, 2));
|
||||
victimStats.EloRating = Math.Max(0, Math.Round(victimStats.EloRating, 2));
|
||||
|
||||
var attackerEloRatingFunc =
|
||||
attacker.GetAdditionalProperty<Func<EFClient, EFClientStatistics, double>>("EloRatingFunction");
|
||||
|
||||
attackerStats.EloRating =
|
||||
attackerEloRatingFunc?.Invoke(attacker, attackerStats) ?? attackerStats.EloRating;
|
||||
|
||||
var victimEloRatingFunc =
|
||||
victim.GetAdditionalProperty<Func<EFClient, EFClientStatistics, double>>("EloRatingFunction");
|
||||
|
||||
victimStats.EloRating =
|
||||
attackerEloRatingFunc?.Invoke(victim, victimStats) ?? victimStats.EloRating;
|
||||
|
||||
// update after calculation
|
||||
attackerStats.TimePlayed += (int)(DateTime.UtcNow - attackerStats.LastActive).TotalSeconds;
|
||||
victimStats.TimePlayed += (int)(DateTime.UtcNow - victimStats.LastActive).TotalSeconds;
|
||||
@ -1428,7 +1497,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
? (int)(DateTime.UtcNow - clientStats.LastActive).TotalSeconds
|
||||
: clientStats.TimePlayed + (int)(DateTime.UtcNow - clientStats.LastActive).TotalSeconds;
|
||||
|
||||
double SPMAgainstPlayWeight = timeSinceLastCalc / Math.Min(600, (totalPlayTime / 60.0));
|
||||
double SPMAgainstPlayWeight = totalPlayTime == 0 ? killSpm : timeSinceLastCalc / Math.Min(600, (totalPlayTime / 60.0));
|
||||
|
||||
// calculate the new weight against average times the weight against play time
|
||||
clientStats.SPM = (killSpm * SPMAgainstPlayWeight) + (clientStats.SPM * (1 - SPMAgainstPlayWeight));
|
||||
@ -1446,7 +1515,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
skillFunction?.Invoke(client, clientStats) ?? Math.Round(clientStats.SPM * KDRWeight, 3);
|
||||
|
||||
// fixme: how does this happen?
|
||||
if (double.IsNaN(clientStats.SPM) || double.IsNaN(clientStats.Skill))
|
||||
if (double.IsNaN(clientStats.SPM) || double.IsNaN(clientStats.Skill) || double.IsInfinity(clientStats.Skill))
|
||||
{
|
||||
_log.LogWarning("clientStats SPM/Skill NaN {@killInfo}",
|
||||
new
|
||||
|
@ -1,10 +1,20 @@
|
||||
using Data.Models.Client.Stats;
|
||||
using System.Linq;
|
||||
using Data.Models.Client.Stats;
|
||||
|
||||
namespace Stats.Helpers
|
||||
{
|
||||
public static class WeaponNameExtensions
|
||||
{
|
||||
public static string RebuildWeaponName(this EFClientHitStatistic stat) =>
|
||||
$"{stat.Weapon?.Name}{string.Join("_", stat.WeaponAttachmentCombo?.Attachment1?.Name, stat.WeaponAttachmentCombo?.Attachment2?.Name, stat.WeaponAttachmentCombo?.Attachment3?.Name)}";
|
||||
public static string RebuildWeaponName(this EFClientHitStatistic stat)
|
||||
{
|
||||
var attachments =
|
||||
new[]
|
||||
{
|
||||
stat.WeaponAttachmentCombo?.Attachment1?.Name, stat.WeaponAttachmentCombo?.Attachment2?.Name,
|
||||
stat.WeaponAttachmentCombo?.Attachment3?.Name
|
||||
}.Where(a => !string.IsNullOrEmpty(a));
|
||||
|
||||
return $"{stat.Weapon?.Name?.Replace("zombie_", "").Replace("_zombie", "")}{string.Join("_", attachments)}";
|
||||
}
|
||||
}
|
||||
}
|
@ -117,6 +117,7 @@ public class Plugin : IPluginV2
|
||||
}
|
||||
};
|
||||
IGameEventSubscriptions.MatchEnded += OnMatchEvent;
|
||||
IGameEventSubscriptions.RoundEnded += (roundEndedEvent, token) => _statManager.Sync(roundEndedEvent.Server, token);
|
||||
IGameEventSubscriptions.MatchStarted += OnMatchEvent;
|
||||
IGameEventSubscriptions.ScriptEventTriggered += OnScriptEvent;
|
||||
IGameEventSubscriptions.ClientKilled += OnClientKilled;
|
||||
|
@ -1,21 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>IW4MAdmin.Plugins.ZombieStats</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.16">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<!--<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2023.4.15.3" />-->
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Data\Data.csproj" />
|
||||
<ProjectReference Include="..\..\SharedLibraryCore\SharedLibraryCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -15,11 +15,11 @@ namespace SharedLibraryCore.Configuration
|
||||
|
||||
if (!ContainsKey(game.Value))
|
||||
{
|
||||
return key.Transform(To.TitleCase);
|
||||
return key;
|
||||
}
|
||||
|
||||
var strings = this[game.Value];
|
||||
return !strings.ContainsKey(key) ? key.Transform(To.TitleCase) : strings[key];
|
||||
return !strings.TryGetValue(key, out var value) ? key : value;
|
||||
}
|
||||
}
|
||||
}
|
@ -24,8 +24,16 @@ public static class EventExtensions
|
||||
{
|
||||
if (token == CancellationToken.None)
|
||||
{
|
||||
// special case to allow tasks like request after delay to run longer
|
||||
await handler(eventArgType, token);
|
||||
try
|
||||
{
|
||||
// special case to allow tasks like request after delay to run longer
|
||||
await handler(eventArgType, token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// todo: static logger
|
||||
Console.WriteLine("InvokeAsync: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
using var timeoutToken = new CancellationTokenSource(Utilities.DefaultCommandTimeout);
|
||||
@ -36,9 +44,10 @@ public static class EventExtensions
|
||||
{
|
||||
await handler(eventArgType, tokenSource.Token);
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
// ignored
|
||||
// todo: static logger
|
||||
Console.WriteLine("InvokeAsync: " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,5 +4,6 @@ public class PlayerRoundDataGameEvent : ClientGameEvent
|
||||
{
|
||||
public int TotalScore { get; init; }
|
||||
public int CurrentScore { get; init; }
|
||||
public int CurrentRound { get; init; }
|
||||
public bool IsGameOver { get; init; }
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
namespace SharedLibraryCore.Events.Game.GameScript.Zombie;
|
||||
|
||||
public class RoundCompleteGameEvent : GameEventV2
|
||||
public class RoundEndEvent : GameEventV2
|
||||
{
|
||||
public int RoundNumber { get; init; }
|
||||
}
|
@ -3,6 +3,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharedLibraryCore.Events;
|
||||
using SharedLibraryCore.Events.Game;
|
||||
using SharedLibraryCore.Events.Game.GameScript.Zombie;
|
||||
|
||||
namespace SharedLibraryCore.Interfaces.Events;
|
||||
|
||||
@ -22,6 +23,12 @@ public interface IGameEventSubscriptions
|
||||
/// </summary>
|
||||
static event Func<MatchEndEvent, CancellationToken, Task> MatchEnded;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when game log prints round ended
|
||||
/// <remarks>typically only triggered when using a script integration</remarks>
|
||||
/// </summary>
|
||||
static event Func<RoundEndEvent, CancellationToken, Task> RoundEnded;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when game log printed that client has entered the match
|
||||
/// <remarks>J;clientNetworkId;clientSlotNumber;clientName</remarks>
|
||||
@ -97,6 +104,7 @@ public interface IGameEventSubscriptions
|
||||
{
|
||||
MatchStartEvent matchStartEvent => MatchStarted?.InvokeAsync(matchStartEvent, token) ?? Task.CompletedTask,
|
||||
MatchEndEvent matchEndEvent => MatchEnded?.InvokeAsync(matchEndEvent, token) ?? Task.CompletedTask,
|
||||
RoundEndEvent roundEndEvent => RoundEnded?.InvokeAsync(roundEndEvent, token) ?? Task.CompletedTask,
|
||||
ClientEnterMatchEvent clientEnterMatchEvent => ClientEnteredMatch?.InvokeAsync(clientEnterMatchEvent, token) ?? Task.CompletedTask,
|
||||
ClientExitMatchEvent clientExitMatchEvent => ClientExitedMatch?.InvokeAsync(clientExitMatchEvent, token) ?? Task.CompletedTask,
|
||||
ClientJoinTeamEvent clientJoinTeamEvent => ClientJoinedTeam?.InvokeAsync(clientJoinTeamEvent, token) ?? Task.CompletedTask,
|
||||
|
@ -4,6 +4,7 @@ using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Models;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Events;
|
||||
@ -113,5 +114,6 @@ namespace SharedLibraryCore.Interfaces
|
||||
|
||||
IAlertManager AlertManager { get; }
|
||||
IInteractionRegistration InteractionRegistration { get; }
|
||||
IList<Func<Dictionary<int, List<EFMeta>>, long?, string, bool, Task>> CustomStatsMetrics { get; }
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,7 @@ namespace SharedLibraryCore
|
||||
RConConnectionFactory = rconConnectionFactory;
|
||||
ServerLogger = logger;
|
||||
DefaultSettings = serviceProvider.GetRequiredService<DefaultSettings>();
|
||||
PerformanceBucket = ServerConfig.PerformanceBucket;
|
||||
InitializeTokens();
|
||||
InitializeAutoMessages();
|
||||
}
|
||||
@ -163,6 +164,7 @@ namespace SharedLibraryCore
|
||||
public bool IsInitialized { get; set; }
|
||||
public int Port { get; protected set; }
|
||||
public int ListenPort => Port;
|
||||
public string PerformanceBucket { get; init; }
|
||||
public abstract Task Kick(string reason, EFClient target, EFClient origin, EFPenalty originalPenalty);
|
||||
public abstract Task<string[]> ExecuteCommandAsync(string command, CancellationToken token = default);
|
||||
public abstract Task SetDvarAsync(string name, object value, CancellationToken token = default);
|
||||
|
@ -40,7 +40,8 @@ namespace SharedLibraryCore.Services
|
||||
AliasLink = client.AliasLink,
|
||||
Password = client.Password,
|
||||
PasswordSalt = client.PasswordSalt,
|
||||
GameName = client.GameName
|
||||
GameName = client.GameName,
|
||||
CurrentAliasId = client.CurrentAliasId
|
||||
})
|
||||
.FirstOrDefault(client => client.NetworkId == networkId && client.GameName == game)
|
||||
);
|
||||
@ -595,7 +596,6 @@ namespace SharedLibraryCore.Services
|
||||
|
||||
entity.CurrentAlias = newAlias;
|
||||
await context.SaveChangesAsync();
|
||||
entity.CurrentAliasId = newAlias.AliasId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -46,7 +46,7 @@ namespace SharedLibraryCore
|
||||
public static Encoding EncodingType;
|
||||
public static Layout CurrentLocalization = new Layout(new Dictionary<string, string>());
|
||||
|
||||
public static TimeSpan DefaultCommandTimeout { get; set; } = new(0, 0, /*Utilities.IsDevelopment ? 360 : */25);
|
||||
public static TimeSpan DefaultCommandTimeout { get; set; } = new(0, 0, Utilities.IsDevelopment ? 360 : 25);
|
||||
public static char[] DirectorySeparatorChars = { '\\', '/' };
|
||||
public static char CommandPrefix { get; set; } = '!';
|
||||
|
||||
@ -1224,6 +1224,16 @@ namespace SharedLibraryCore
|
||||
}
|
||||
|
||||
public static string ToNumericalString(this int value)
|
||||
{
|
||||
return ToNumericalString((long)value);
|
||||
}
|
||||
|
||||
public static string ToNumericalString(this long? value)
|
||||
{
|
||||
return value?.ToNumericalString();
|
||||
}
|
||||
|
||||
public static string ToNumericalString(this long value)
|
||||
{
|
||||
return value.ToString("#,##0", CurrentLocalization.Culture);
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Configuration;
|
||||
@ -48,6 +50,11 @@ namespace WebfrontCore.Controllers
|
||||
matchedServerId = server.LegacyDatabaseId;
|
||||
}
|
||||
|
||||
foreach (var statMetricFunc in Manager.CustomStatsMetrics)
|
||||
{
|
||||
await statMetricFunc(new Dictionary<int, List<EFMeta>> { { id, hitInfo.CustomMetrics } }, matchedServerId, null, false);
|
||||
}
|
||||
|
||||
hitInfo.TotalRankedClients = await _serverDataViewer.RankedClientsCountAsync(matchedServerId, token);
|
||||
|
||||
return View("~/Views/Client/Statistics/Advanced.cshtml", hitInfo);
|
||||
|
@ -21,7 +21,7 @@
|
||||
}
|
||||
</td>
|
||||
<td colspan="20%" class="text-break">
|
||||
<color-code value="@(message.ServerName ?? "--")"></color-code>
|
||||
<color-code value="@(message.ServerName ?? "-")"></color-code>
|
||||
</td>
|
||||
<td colspan="15%" class="text-right text-break">
|
||||
@message.When.ToStandardFormat()
|
||||
@ -51,7 +51,7 @@
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
<color-code value="@(message.ServerName ?? "--")"></color-code>
|
||||
<color-code value="@(message.ServerName ?? "-")"></color-code>
|
||||
</div>
|
||||
<div>@message.When.ToStandardFormat()</div>
|
||||
</td>
|
||||
|
@ -31,9 +31,15 @@
|
||||
}
|
||||
var rebuiltName = stat.RebuildWeaponName();
|
||||
var name = config.GetStringForGame(rebuiltName, stat.Weapon?.Game);
|
||||
return !rebuiltName.Equals(name, StringComparison.InvariantCultureIgnoreCase)
|
||||
? name
|
||||
: config.GetStringForGame(stat.Weapon.Name, stat.Weapon.Game);
|
||||
|
||||
if (!rebuiltName.Equals(name))
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
rebuiltName = config.GetStringForGame(stat.Weapon?.Name, stat.Weapon.Game);
|
||||
|
||||
return rebuiltName.Equals(name) ? name.Transform(To.TitleCase) : rebuiltName;
|
||||
}
|
||||
|
||||
string GetWeaponAttachmentName(EFWeaponAttachmentCombo attachment)
|
||||
@ -230,7 +236,11 @@
|
||||
Name = ViewBag.Localization["WEBFRONT_ADV_STATS_TOTAL_ACTIVE_TIME"] as string,
|
||||
Value = activeTime?.HumanizeForCurrentCulture()
|
||||
}
|
||||
};
|
||||
}.Concat(Model.CustomMetrics.Select(metric => new
|
||||
{
|
||||
Name = metric.Key,
|
||||
metric.Value
|
||||
}));
|
||||
}
|
||||
|
||||
<div class="content row mt-20">
|
||||
@ -362,7 +372,7 @@
|
||||
}).WithRows(weapons, weapon => new[]
|
||||
{
|
||||
GetWeaponNameForHit(weapon),
|
||||
GetWeaponAttachmentName(weapon.WeaponAttachmentCombo) ?? "--",
|
||||
GetWeaponAttachmentName(weapon.WeaponAttachmentCombo) ?? "-",
|
||||
weapon.KillCount.ToNumericalString(),
|
||||
weapon.HitCount.ToNumericalString(),
|
||||
weapon.DamageInflicted.ToNumericalString(),
|
||||
|
@ -67,21 +67,13 @@
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-column font-size-12 text-right text-md-left">
|
||||
<div>
|
||||
<span class="text-primary">@stat.Kills.ToNumericalString()</span><span class="text-muted"> @loc["PLUGINS_STATS_TEXT_KILLS"]</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-primary">@stat.Deaths.ToNumericalString()</span><span class="text-muted"> @loc["PLUGINS_STATS_TEXT_DEATHS"]</span><br/>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-primary">@stat.KDR</span><span class="text-muted"> @loc["PLUGINS_STATS_TEXT_KDR"]</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-primary">@stat.TimePlayedValue.HumanizeForCurrentCulture() </span><span class="text-muted">@loc["WEBFRONT_PROFILE_PLAYER"]</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-primary"> @stat.LastSeenValue.HumanizeForCurrentCulture() </span><span class="text-muted">@loc["WEBFRONT_PROFILE_LSEEN"]</span>
|
||||
</div>
|
||||
@foreach (var meta in stat.Metrics)
|
||||
{
|
||||
<div>
|
||||
<span class="text-primary">@meta.Value</span>
|
||||
<span class="text-muted">@meta.Key</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full w-md-half client-rating-graph pt-10 pb-10">
|
||||
|
@ -69,7 +69,7 @@ else
|
||||
<td>@(client.Deaths ?? 0)</td>
|
||||
<td>@Math.Round(client.Kdr ?? 0, 2)</td>
|
||||
<td>@Math.Round(client.ScorePerMinute ?? 0)</td>
|
||||
<td>@(client.ZScore is null or 0 ? "--" : Math.Round(client.ZScore.Value, 2).ToString(CultureInfo.CurrentCulture))</td>
|
||||
<td>@(client.ZScore is null or 0 ? "-" : Math.Round(client.ZScore.Value, 2).ToString(CultureInfo.CurrentCulture))</td>
|
||||
<td class="text-right">@client.Ping</td>
|
||||
</tr>
|
||||
|
||||
@ -94,7 +94,7 @@ else
|
||||
<div>@(client.Deaths ?? 0)</div>
|
||||
<div>@Math.Round(client.Kdr ?? 0, 2)</div>
|
||||
<div>@Math.Round(client.ScorePerMinute ?? 0)</div>
|
||||
<div>@(client.ZScore is null or 0 ? "--" : Math.Round(client.ZScore.Value, 2).ToString(CultureInfo.CurrentCulture))</div>
|
||||
<div>@(client.ZScore is null or 0 ? "-" : Math.Round(client.ZScore.Value, 2).ToString(CultureInfo.CurrentCulture))</div>
|
||||
<div>@client.Ping</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
Loading…
x
Reference in New Issue
Block a user