Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
901136a
Update codebase to target .NET 8.0 and improve JSON serialization
Ayymoss Feb 5, 2024
912118a
Update SharedLibraryCore package version
Ayymoss Feb 9, 2024
e8466aa
add initial public zombie stats models, migrations, and events
RaidMax May 2, 2023
cdd7886
update zombie models
RaidMax May 7, 2023
65312ba
update server distribution calculations to account for performance bu…
RaidMax May 8, 2023
fa41021
add migrations
RaidMax May 8, 2023
de0c31c
Adjustments to stats to support zombie client
RaidMax May 10, 2023
569e8eb
add notice for closed source component
RaidMax May 10, 2023
b820300
Additional zombie stast work
RaidMax Feb 12, 2024
5eb1342
Additional updates to support performance bucket
RaidMax Feb 17, 2024
3f8b0b3
zombie stats code
RaidMax Jul 2, 2024
2ca66dc
Using Code from EFPerformanceBucket for references
Sep 7, 2024
6e97670
INIT - ALIGN W/ DEVELOP - COMPILES -- UNTESTED!
Ayymoss Apr 8, 2026
aa0438c
Refactor Stats plugin: replace `IResourceQueryHelper` with `StatManag…
Ayymoss Apr 8, 2026
697817a
Add performance bucket support for statistics views, menu navigation,…
Ayymoss Apr 8, 2026
7571e84
Update ZombieStats.csproj: add metadata properties and remove PostBui…
Ayymoss Apr 8, 2026
1b5da72
Introduce optional premium enhancements for Zombie Stats
Ayymoss Apr 9, 2026
e4ab0a4
Remove closed-source component and stale TODO notes from ZombieStats …
Ayymoss Apr 9, 2026
ba9c298
Remove ZombieClientStateManager and ZombieEventProcessor from ZombieS…
Ayymoss Apr 9, 2026
3ff4530
Add Zombie Leaderboard component with interactive UI and data virtual…
Ayymoss Apr 10, 2026
6326f1e
Add logging and error handling for Stats plugin; refactor bucket conf…
Ayymoss Apr 11, 2026
362c167
Remove verbose debug logs from Stats plugin and replace constructor-b…
Ayymoss Apr 11, 2026
ffc1887
Mark project references as private assets in Stats and ZombieStats pl…
Ayymoss Apr 11, 2026
0e69414
Refactor project references in ZombieStats and Stats plugins to expli…
Ayymoss Apr 11, 2026
c5e4075
Refactor hit calculation logic to simplify event handling; add LastRo…
Ayymoss Apr 11, 2026
d61d769
Port T4 ZombieStats to T5 and refactor callback handling
Ayymoss Apr 12, 2026
51203e5
Enhance Zombie leaderboard UI with tooltips, layout adjustments, and …
Ayymoss Apr 12, 2026
95a9dd8
Add performance bucket documentation, UI refinements, and calculation…
Ayymoss Apr 13, 2026
3bc4308
Track zombie round numbers across UI, backend, and events; remove obs…
Ayymoss Apr 13, 2026
96e8647
Add paginated match history loading and map records marquee in zombie…
Ayymoss Apr 13, 2026
3b44302
Add economy and combat events to Zombie stats system with T5-specific…
Ayymoss Apr 14, 2026
693d4d0
Add PostgreSQL migration for Zombie Economy Stats system.
Ayymoss Apr 14, 2026
abb1b88
Add `IsZombieServer` to server data DTO and expose zombie stats via n…
Ayymoss Apr 14, 2026
0e2ffc0
Add T4 ZombieStats integration, update deploy script, and improve REA…
Ayymoss Apr 15, 2026
0e1bfc1
Add `performanceBucketCode` support to GetTopPlayers API method
Ayymoss Apr 16, 2026
33a1eab
Expand XML documentation and endpoint response types in `ZombieStatsC…
Ayymoss Apr 17, 2026
48f2fbd
- Add `ServerUrlTransformer` to rewrite OpenAPI server URLs based on …
Ayymoss Apr 17, 2026
8adbf25
Refactor `ServerUrlTransformer` to improve OpenAPI server URL resolut…
Ayymoss Apr 17, 2026
f816a55
Simplify ServerUrlTransformer to use relative URL.
Ayymoss Apr 17, 2026
d4dcb02
Make `IZombieStatsEnhancer.OnMatchEnded` asynchronous to improve task…
Ayymoss Apr 18, 2026
c9e3827
Add `LastRoundReached` property to `ZombieMatchClientStat` model.
Ayymoss Apr 18, 2026
8f16e0f
Fix improper async handling in `RankedClientsCountAsync` to resolve D…
Ayymoss Apr 18, 2026
bedf10d
Add unique index on `EFMaps` table and handle duplicate entries in `S…
Ayymoss Apr 18, 2026
999f9e3
Refactor `ServerDataCollector` to prevent race conditions during map …
Ayymoss Apr 18, 2026
5032e46
Cap zombie damage logging at max HP in stats logic and replace `Match…
Ayymoss Apr 18, 2026
05c2b2c
Refine zombie stats filters by reclassifying perks under "economy" an…
Ayymoss Apr 18, 2026
2a4290f
Bootstrap round preservation for IW4MAdmin in zombie stats logic by i…
Ayymoss Apr 19, 2026
0a457dd
Add session join/leave event handling and max player count tracking i…
Ayymoss Apr 19, 2026
0aaeba9
Refactor Zombie Leaderboard: Update layout for better responsiveness,…
Ayymoss Apr 19, 2026
03df311
Adjust Zombie Leaderboard: Fix numerical formatting by adding tabular…
Ayymoss Apr 19, 2026
6fc8182
Why does this Tailwind bug still exist in 2026?
Ayymoss Apr 19, 2026
fe63b21
Remove unused player count properties from `MatchState` in zombie sta…
Ayymoss Apr 19, 2026
9cc95d8
Update Zombie Leaderboard: Adjust styles for improved layout consiste…
Ayymoss Apr 19, 2026
406e894
Refactor `MatchState`: Replace `RoundStates` dictionary key with comp…
Ayymoss Apr 25, 2026
8485e01
Introduce Zombie Live Match Snapshot: Add `ZombieTimelineGaps` for ca…
Ayymoss Apr 26, 2026
03e2bbf
Update Zombie Snapshot: Replace "Streak" with "Round" in compact row …
Ayymoss Apr 26, 2026
fcf5b72
Update T4/T6 Mystery Box Detection: Implement scoped teddy suppressio…
Ayymoss Apr 26, 2026
f882920
Replace virtualized components with infinite scrolling for streamline…
Ayymoss Apr 27, 2026
f726fe1
Localize Zombie Leaderboard and related components: Replace hardcoded…
Ayymoss Apr 27, 2026
87926e9
Refactor Pack-a-Punch logic: Replace `WeaponUpgradeGameEvent` with `P…
Ayymoss Apr 28, 2026
5407555
Konva scrubber, share page, buildables, EE detection + achievement ba…
Ayymoss Apr 28, 2026
7542b7f
Fix CI: stop bundling Konva (NUglify can't parse ES2018+ regex)
Ayymoss Apr 28, 2026
a15f70b
Fix prod scrubber load + redesign share-page achievements
Ayymoss Apr 28, 2026
ac7305d
Scrubber UX + buildables iconic/extras model
Ayymoss Apr 30, 2026
c1d91fd
Multi-quest EE model + scrubber UX + top-stats + Zombies bucket fixes
Ayymoss Apr 30, 2026
8859051
Match-17 fixes: scrubber lane auto-flip, leaderboard drop-in expander…
Ayymoss May 2, 2026
14f95f9
Fix bucket-aggregate top-stats: refit z-scores per bucket, not per-se…
Ayymoss May 2, 2026
fbd6b44
Tolerate NULL PerformanceBucketId on default-bucket queries (no backf…
Ayymoss May 2, 2026
29ec46f
Fix offset advancement bug in StatManager TopStats query
Ayymoss May 2, 2026
500d37c
Qualified-only leaderboard card + single SHOW_ALL drives scoreboard +…
Ayymoss May 2, 2026
0a03adb
T4 song-EE: detect via level.eggs flag (VR/SH); withdraw Nacht (Pluto…
Ayymoss May 2, 2026
c4dc3be
Top-stats: zombies-bucket KDR/Kills/Deaths now consistent across surf…
Ayymoss May 3, 2026
f1bbd84
GSC: T5/T6 EE step detection + dev helpers
Ayymoss May 3, 2026
4dcf593
EE branching quest model: writer hard-lock + DTO branch fields
Ayymoss May 3, 2026
4d1b3cc
Webfront EE UI overhaul
Ayymoss May 3, 2026
7555d48
JS infra: tooltip orphan safety + scrubber zoom canvas-cap + lane-mod…
Ayymoss May 3, 2026
262dd5a
AnnouncementManagement: fix invalid ph-check-circle-fill icon class
Ayymoss May 3, 2026
2893adf
DefaultSettings: add T4/T5/T6 zombies weapon DEV_NAME translations
Ayymoss May 3, 2026
4db04dd
Scrubber: keep position:relative on container via Tailwind class
Ayymoss May 3, 2026
54d0a21
Standardize `isSong` condition across components by removing undersco…
Ayymoss May 3, 2026
af577b5
Buildables UI: inventory-driven checklist mirroring EE step grid
Ayymoss May 3, 2026
d7f7892
Dedicated match: SHOW_ALL toggle drives tabs+timeline; merge Buildabl…
Ayymoss May 3, 2026
35a46a1
Match history: View Full Match nav drops client suffix; restyle as co…
Ayymoss May 3, 2026
f8402dd
GSC T6: Origins gramophone detection via placement flag
Ayymoss May 3, 2026
469c195
Match-detail UI: 4-col stat card grid with scoreboard parity
Ayymoss May 3, 2026
0d6087a
IsZombieServer: sticky-positive cache + null-guard
Ayymoss May 5, 2026
a604471
Solo-From tooltip: pass AssistedRounds, render percent placeholder
Ayymoss May 5, 2026
062bc69
Match scrubber: replace Konva canvas with DOM rendering
Ayymoss May 5, 2026
1817cfd
Match-detail page: render UTC timestamps in browser-local time
Ayymoss May 5, 2026
6666dea
ZombieStats: phase-1 skill-leak diagnostics; revert sticky cache
Ayymoss May 6, 2026
4c772df
Stats: lower-case PerformanceBucketCode at every code boundary
Ayymoss May 6, 2026
e27325e
Adjust RCon retry logging to dynamically switch log levels based on c…
Ayymoss May 7, 2026
ffc233f
Remove Marathon badge logic and references from ZombieAchievementBadges.
Ayymoss May 7, 2026
4c1aba3
Adjust ServerLatencyMonitoringService to re-order event registrations…
Ayymoss May 7, 2026
82973fe
Latency: anchor probe T1 at UDP send and report pure log ingest
Ayymoss May 7, 2026
5290e62
Latency: switch GameLogIngestMs to sliding-window median + probe jitter
Ayymoss May 7, 2026
cbb7bc3
ZombieStats: silently skip non-GSE script events
Ayymoss May 7, 2026
7c51d49
ServerCard: Ctrl/Cmd+click play icon copies connect command
Ayymoss May 7, 2026
02f9618
ZombieStats: PWR power-state event — detection, persistence, and UI
Ayymoss May 7, 2026
35323b9
Log phase-1.5 skill-leak diagnostics with output gating and leak cont…
Ayymoss May 7, 2026
d0ca450
ZombieStats: skill formula redesign + match boundary fixes
Ayymoss May 9, 2026
5928df2
ZombieStats: align Live Modal + Match Page to leaderboard weight
Ayymoss May 9, 2026
92f1b9a
ZombieStats: round-pace tinting on round table, leaderboard, live modal
Ayymoss May 9, 2026
f509a24
Top Stats: data-driven zombie-bucket classification + column trim
Ayymoss May 10, 2026
cf8e76e
ZombieStats: T7 (BO3) support + new event categories
Ayymoss May 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions .github/workflows/build_application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Application build

on:
push:
branches: [ develop, release/pre, master ]
branches: [ develop, release/pre, master, feature/zombie-stats ]
paths:
- Application/**
- WebfrontCore/**
Expand Down Expand Up @@ -217,6 +217,9 @@ jobs:
mkdir -p ${{ env.outputFolder }}/wwwroot/js/user
cp ${{ github.workspace }}/WebfrontCore/wwwroot/js/app.min.js ${{ env.outputFolder }}/wwwroot/js/app.min.js
cp ${{ github.workspace }}/WebfrontCore/wwwroot/js/user/user.js ${{ env.outputFolder }}/wwwroot/js/user/user.js
# zombie-scrubber.js is loaded standalone (kept out of the main bundle
# for cache isolation — UI changes ship without invalidating app.min.js).
cp ${{ github.workspace }}/WebfrontCore/wwwroot/js/zombie-scrubber.js ${{ env.outputFolder }}/wwwroot/js/zombie-scrubber.js
mkdir -p ${{ env.outputFolder }}/wwwroot/font
mkdir -p ${{ env.outputFolder }}/wwwroot/font/phosphor
rsync -ar ${{ github.workspace }}/WebfrontCore/wwwroot/font/phosphor/ ${{ env.outputFolder }}/wwwroot/font/phosphor/
Expand All @@ -235,7 +238,7 @@ jobs:
build_and_push_docker_dev:
runs-on: ubuntu-latest
needs: [ make_version, build ]
if: ${{ github.ref == 'refs/heads/develop' }}
if: ${{ github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/feature/zombie-stats' }}
permissions:
contents: read
packages: write
Expand Down Expand Up @@ -270,10 +273,10 @@ jobs:
with:
images: ghcr.io/${{ github.repository_owner }}/iw4madmin
tags: |
# push to develop branch: tags as 'develop'
type=raw,value=develop
# tag with the build number and -dev suffix (e.g., 2025.8.13.1-dev)
type=raw,value=${{ env.buildNumber }}-dev
# branch name as tag (e.g., 'develop' or 'zombie-stats')
type=ref,event=branch
# tag with the build number and branch suffix (e.g., 2025.8.13.1-dev)
type=raw,value=${{ env.buildNumber }}-${{ github.ref_name == 'develop' && 'dev' || github.ref_name }}
# tag with the short git sha (e.g., f15fbd3)
type=sha,prefix=
labels: |
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ Application/WebfrontCore/*
/Data/IW4MAdmin_Migration.db-wal
bundle/
WebfrontCore/Tools/*
_PRIVATE/
translation_temp.csv

# LLM folders
.claude/
Expand Down
2 changes: 1 addition & 1 deletion Application/Application.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<PackageId>RaidMax.IW4MAdmin.Application</PackageId>
<Version>2020.0.0.0</Version>
<Version>2024.0.0.0</Version>
<Authors>RaidMax</Authors>
<Company>Forever None</Company>
<Product>IW4MAdmin</Product>
Expand Down
6 changes: 6 additions & 0 deletions Application/ApplicationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ public ApplicationManager(ILogger<ApplicationManager> logger, IMiddlewareActionH
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 IList<Func<IList<ITopStatsMutable>, long?, string, Task>> CustomTopStatsTransformers { get; } =
new List<Func<IList<ITopStatsMutable>, long?, string, Task>>();

public async Task ExecuteEvent(GameEvent newEvent)
{
ProcessingEvents.TryAdd(newEvent.IncrementalId, newEvent);
Expand Down
13 changes: 8 additions & 5 deletions Application/BuildScripts/PostBuild.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,15 @@ mv "$PublishDir/DefaultSettings.json" "$PublishDir/Configuration/"
mkdir -p "$PublishDir/Lib"
rm -f "$PublishDir/Microsoft.CodeAnalysis*.dll"

# Get list of plugin DLLs from BUILD/Plugins (dynamically detected)
# Get list of plugin DLLs by matching .csproj project names in the Plugins/ source directory.
# This avoids treating transitive dependencies (e.g. EF Core, Serilog) that land in BUILD/Plugins
# as plugin assemblies when plugins use ProjectReferences instead of PackageReferences.
pluginDllNames=()
if [ -d "$SourceDir/BUILD/Plugins" ]; then
for pluginDll in "$SourceDir/BUILD/Plugins"/*.dll; do
if [ -f "$pluginDll" ]; then
pluginDllNames+=("$(basename "$pluginDll")")
if [ -d "$SourceDir/Plugins" ]; then
for csproj in "$SourceDir/Plugins"/*/*.csproj; do
if [ -f "$csproj" ]; then
projName=$(basename "$csproj" .csproj)
pluginDllNames+=("${projName}.dll")
fi
done
fi
Expand Down
10 changes: 7 additions & 3 deletions Application/Commands/SetLogLevelCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public SetLogLevelCommand(CommandConfiguration config, ITranslationLookup layout

Name = "loglevel";
Alias = "ll";
Description = "set minimum logging level";
Description = layout["COMMANDS_LOGLEVEL_DESC"];
Permission = EFClient.Permission.Owner;
Arguments = new CommandArgument[]
{
Expand Down Expand Up @@ -50,7 +50,8 @@ public override async Task ExecuteAsync(GameEvent gameEvent)
{
await gameEvent.Origin.TellAsync(new[]
{
$"Valid log values: {string.Join(",", Enum.GetValues<LogEventLevel>())}"
_translationLookup["COMMANDS_LOGLEVEL_VALID_VALUES"]
.FormatExt(string.Join(",", Enum.GetValues<LogEventLevel>()))
});
return;
}
Expand All @@ -75,6 +76,9 @@ await gameEvent.Origin.TellAsync(new[]
}

await gameEvent.Origin.TellAsync(new[]
{ $"Set minimum log level to {loggingSwitch.MinimumLevel.ToString()}" });
{
_translationLookup["COMMANDS_LOGLEVEL_SUCCESS"]
.FormatExt(loggingSwitch.MinimumLevel.ToString())
});
}
}
8 changes: 3 additions & 5 deletions Application/CoreEventHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,13 @@ public void QueueEvent(IManager manager, CoreEvent coreEvent)

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))
{
Expand All @@ -62,7 +60,7 @@ public void StartProcessing(CancellationToken token)
_onProcessingEvents.Release(1);
}

_onEventReady.Wait(_cancellationToken);
_onEventReady.Wait(token);
continue;
}

Expand Down
Loading
Loading