feat: Implement Player Retention Analytics System (Phase 2.2)
Backend: - Add player_sessions table (migration 004) for session tracking - Implement retention calculation queries (24h/48h/72h post-wipe) - Add /api/plugin/player-event endpoint for join/leave tracking - Add /api/analytics/retention endpoint with CSV export - Track unique players, session duration, new vs returning ratio Frontend: - Create PlayerRetentionView with ECharts retention curves - Add multi-wipe comparison (last 3/6/10/20 wipes) - Display summary metrics and detailed wipe table - Add /retention route to router Plugin: - Update CorrosionCompanion.cs to send player events to new endpoint - Track player join/leave with license_key authentication Enables data-driven wipe timing optimization by answering: "What percentage of players return 24h/48h/72h after a wipe?" Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -107,19 +107,27 @@ namespace Oxide.Plugins
|
||||
|
||||
var data = new Dictionary<string, object>
|
||||
{
|
||||
{ "license_key", config.LicenseKey },
|
||||
{ "event", "player_connected" },
|
||||
{ "player_id", player.UserIDString },
|
||||
{ "player_name", player.displayName },
|
||||
{ "ip_address", player.net?.connection?.ipaddress ?? "unknown" },
|
||||
{ "timestamp", DateTimeOffset.UtcNow.ToUnixTimeSeconds() }
|
||||
};
|
||||
|
||||
SendEvent("player_connected", data);
|
||||
|
||||
if (config.DebugMode)
|
||||
SendApiRequest("/api/plugin/player-event", data, (code, response) =>
|
||||
{
|
||||
Puts($"Player connected: {player.displayName} ({player.UserIDString})");
|
||||
}
|
||||
if (config.DebugMode)
|
||||
{
|
||||
if (code == 200)
|
||||
{
|
||||
Puts($"Player join tracked: {player.displayName} ({player.UserIDString})");
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintWarning($"Player join tracking failed: HTTP {code}");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void OnPlayerDisconnected(BasePlayer player, string reason)
|
||||
@@ -128,19 +136,27 @@ namespace Oxide.Plugins
|
||||
|
||||
var data = new Dictionary<string, object>
|
||||
{
|
||||
{ "license_key", config.LicenseKey },
|
||||
{ "event", "player_disconnected" },
|
||||
{ "player_id", player.UserIDString },
|
||||
{ "player_name", player.displayName },
|
||||
{ "reason", reason ?? "unknown" },
|
||||
{ "timestamp", DateTimeOffset.UtcNow.ToUnixTimeSeconds() }
|
||||
};
|
||||
|
||||
SendEvent("player_disconnected", data);
|
||||
|
||||
if (config.DebugMode)
|
||||
SendApiRequest("/api/plugin/player-event", data, (code, response) =>
|
||||
{
|
||||
Puts($"Player disconnected: {player.displayName} (Reason: {reason})");
|
||||
}
|
||||
if (config.DebugMode)
|
||||
{
|
||||
if (code == 200)
|
||||
{
|
||||
Puts($"Player leave tracked: {player.displayName} (Reason: {reason})");
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintWarning($"Player leave tracking failed: HTTP {code}");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
object OnPlayerChat(BasePlayer player, string message)
|
||||
|
||||
Reference in New Issue
Block a user