Start tracking play sessions

net8.0
Pat Hartl 2023-11-17 02:28:46 -06:00
parent aff2e991ed
commit 5324723cee
12 changed files with 2109 additions and 2 deletions

View File

@ -331,12 +331,26 @@ namespace LANCommander.PlaynitePlugin
public override void OnGameStarting(OnGameStartingEventArgs args)
{
SaveController.Download(args.Game);
if (args.Game.PluginId == Id)
{
SaveController.Download(args.Game);
var gameId = Guid.Parse(args.Game.GameId);
LANCommanderClient.StartPlaySession(gameId);
}
}
public override void OnGameStopped(OnGameStoppedEventArgs args)
{
SaveController.Upload(args.Game);
if (args.Game.PluginId == Id)
{
SaveController.Upload(args.Game);
var gameId = Guid.Parse(args.Game.GameId);
LANCommanderClient.EndPlaySession(gameId);
}
}
public override IEnumerable<TopPanelItem> GetTopPanelItems()

View File

@ -51,6 +51,16 @@ namespace LANCommander.SDK
return response.Data;
}
private T PostRequest<T>(string route)
{
var request = new RestRequest(route)
.AddHeader("Authorization", $"Bearer {Token.AccessToken}");
var response = ApiClient.Post<T>(request);
return response.Data;
}
private T GetRequest<T>(string route)
{
var request = new RestRequest(route)
@ -364,6 +374,20 @@ namespace LANCommander.SDK
return alias;
}
public void StartPlaySession(Guid gameId)
{
Logger?.LogTrace("Starting a game session...");
PostRequest<object>($"/api/PlaySession/Start/{gameId}");
}
public void EndPlaySession(Guid gameId)
{
Logger?.LogTrace("Ending a game session...");
PostRequest<object>($"/api/PlaySession/End/{gameId}");
}
private string GetMacAddress()
{
return NetworkInterface.GetAllNetworkInterfaces()

View File

@ -0,0 +1,53 @@
using LANCommander.Data.Models;
using LANCommander.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
namespace LANCommander.Controllers.Api
{
[Authorize(AuthenticationSchemes = "Bearer")]
[Route("api/[controller]")]
[ApiController]
public class PlaySessionsController : ControllerBase
{
private readonly PlaySessionService PlaySessionService;
private readonly GameService GameService;
private readonly UserManager<User> UserManager;
public PlaySessionsController(PlaySessionService playSessionService, GameService gameService, UserManager<User> userManager)
{
PlaySessionService = playSessionService;
GameService = gameService;
UserManager = userManager;
}
[HttpPost("Start/{id}")]
public async Task<IActionResult> Start(Guid id)
{
var user = await UserManager.FindByNameAsync(User.Identity.Name);
var game = await GameService.Get(id);
if (game == null || user == null)
return BadRequest();
await PlaySessionService.StartSession(game.Id, user.Id);
return Ok();
}
[HttpPost("End/{id}")]
public async Task<IActionResult> End(Guid id)
{
var user = await UserManager.FindByNameAsync(User.Identity.Name);
var game = await GameService.Get(id);
if (game == null || user == null)
return BadRequest();
await PlaySessionService.EndSession(game.Id, user.Id);
return Ok();
}
}
}

View File

@ -107,6 +107,18 @@ namespace LANCommander.Data
.IsRequired(true)
.OnDelete(DeleteBehavior.NoAction);
builder.Entity<User>()
.HasMany(u => u.PlaySessions)
.WithOne(ps => ps.User)
.IsRequired(true)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<Game>()
.HasMany(g => g.PlaySessions)
.WithOne(ps => ps.Game)
.IsRequired(true)
.OnDelete(DeleteBehavior.NoAction);
builder.Entity<Server>()
.HasOne(s => s.Game)
.WithMany(g => g.Servers)
@ -152,6 +164,8 @@ namespace LANCommander.Data
public DbSet<GameSave>? GameSaves { get; set; }
public DbSet<PlaySession>? PlaySessions { get; set; }
public DbSet<Server>? Servers { get; set; }
public DbSet<ServerConsole>? ServerConsoles { get; set; }

View File

@ -31,6 +31,7 @@ namespace LANCommander.Data.Models
public virtual ICollection<Archive>? Archives { get; set; }
public virtual ICollection<Script>? Scripts { get; set; }
public virtual ICollection<GameSave>? GameSaves { get; set; }
public virtual ICollection<PlaySession>? PlaySessions { get; set; }
public virtual ICollection<SavePath>? SavePaths { get; set; }
public virtual ICollection<Server>? Servers { get; set; }
public virtual ICollection<Redistributable>? Redistributables { get; set; }

View File

@ -0,0 +1,27 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
namespace LANCommander.Data.Models
{
[Table("PlaySessions")]
public class PlaySession : BaseModel
{
public Guid GameId { get; set; }
[JsonIgnore]
[ForeignKey(nameof(GameId))]
[InverseProperty("PlaySessions")]
public virtual Game? Game { get; set; }
public Guid UserId { get; set; }
[ForeignKey(nameof(UserId))]
[InverseProperty("PlaySessions")]
public virtual User? User { get; set; }
[Display(Name = "Start")]
public DateTime? Start { get; set; }
[Display(Name = "End")]
public DateTime? End { get; set; }
}
}

View File

@ -46,6 +46,9 @@ namespace LANCommander.Data.Models
[JsonIgnore]
public virtual ICollection<GameSave>? GameSaves { get; set; }
[JsonIgnore]
public virtual ICollection<PlaySession>? PlaySessions { get; set; }
[JsonIgnore]
public bool Approved { get; set; }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,82 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace LANCommander.Migrations
{
/// <inheritdoc />
public partial class AddPlaySessions : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "PlaySessions",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
GameId = table.Column<Guid>(type: "TEXT", nullable: false),
UserId = table.Column<Guid>(type: "TEXT", nullable: false),
Start = table.Column<DateTime>(type: "TEXT", nullable: true),
End = table.Column<DateTime>(type: "TEXT", nullable: true),
CreatedOn = table.Column<DateTime>(type: "TEXT", nullable: false),
CreatedById = table.Column<Guid>(type: "TEXT", nullable: true),
UpdatedOn = table.Column<DateTime>(type: "TEXT", nullable: false),
UpdatedById = table.Column<Guid>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PlaySessions", x => x.Id);
table.ForeignKey(
name: "FK_PlaySessions_AspNetUsers_CreatedById",
column: x => x.CreatedById,
principalTable: "AspNetUsers",
principalColumn: "Id");
table.ForeignKey(
name: "FK_PlaySessions_AspNetUsers_UpdatedById",
column: x => x.UpdatedById,
principalTable: "AspNetUsers",
principalColumn: "Id");
table.ForeignKey(
name: "FK_PlaySessions_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_PlaySessions_Games_GameId",
column: x => x.GameId,
principalTable: "Games",
principalColumn: "Id");
});
migrationBuilder.CreateIndex(
name: "IX_PlaySessions_CreatedById",
table: "PlaySessions",
column: "CreatedById");
migrationBuilder.CreateIndex(
name: "IX_PlaySessions_GameId",
table: "PlaySessions",
column: "GameId");
migrationBuilder.CreateIndex(
name: "IX_PlaySessions_UpdatedById",
table: "PlaySessions",
column: "UpdatedById");
migrationBuilder.CreateIndex(
name: "IX_PlaySessions_UserId",
table: "PlaySessions",
column: "UserId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "PlaySessions");
}
}
}

View File

@ -570,6 +570,49 @@ namespace LANCommander.Migrations
b.ToTable("MultiplayerModes");
});
modelBuilder.Entity("LANCommander.Data.Models.PlaySession", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<Guid?>("CreatedById")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedOn")
.HasColumnType("TEXT");
b.Property<DateTime?>("End")
.HasColumnType("TEXT");
b.Property<Guid>("GameId")
.HasColumnType("TEXT");
b.Property<DateTime?>("Start")
.HasColumnType("TEXT");
b.Property<Guid?>("UpdatedById")
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedOn")
.HasColumnType("TEXT");
b.Property<Guid>("UserId")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("CreatedById");
b.HasIndex("GameId");
b.HasIndex("UpdatedById");
b.HasIndex("UserId");
b.ToTable("PlaySessions");
});
modelBuilder.Entity("LANCommander.Data.Models.Redistributable", b =>
{
b.Property<Guid>("Id")
@ -1431,6 +1474,37 @@ namespace LANCommander.Migrations
b.Navigation("UpdatedBy");
});
modelBuilder.Entity("LANCommander.Data.Models.PlaySession", b =>
{
b.HasOne("LANCommander.Data.Models.User", "CreatedBy")
.WithMany()
.HasForeignKey("CreatedById");
b.HasOne("LANCommander.Data.Models.Game", "Game")
.WithMany("PlaySessions")
.HasForeignKey("GameId")
.OnDelete(DeleteBehavior.NoAction)
.IsRequired();
b.HasOne("LANCommander.Data.Models.User", "UpdatedBy")
.WithMany()
.HasForeignKey("UpdatedById");
b.HasOne("LANCommander.Data.Models.User", "User")
.WithMany("PlaySessions")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("CreatedBy");
b.Navigation("Game");
b.Navigation("UpdatedBy");
b.Navigation("User");
});
modelBuilder.Entity("LANCommander.Data.Models.Redistributable", b =>
{
b.HasOne("LANCommander.Data.Models.User", "CreatedBy")
@ -1657,6 +1731,8 @@ namespace LANCommander.Migrations
b.Navigation("MultiplayerModes");
b.Navigation("PlaySessions");
b.Navigation("SavePaths");
b.Navigation("Scripts");
@ -1681,6 +1757,8 @@ namespace LANCommander.Migrations
modelBuilder.Entity("LANCommander.Data.Models.User", b =>
{
b.Navigation("GameSaves");
b.Navigation("PlaySessions");
});
#pragma warning restore 612, 618
}

View File

@ -142,6 +142,7 @@ namespace LANCommander
builder.Services.AddScoped<ServerService>();
builder.Services.AddScoped<ServerConsoleService>();
builder.Services.AddScoped<GameSaveService>();
builder.Services.AddScoped<PlaySessionService>();
builder.Services.AddScoped<MediaService>();
builder.Services.AddScoped<RedistributableService>();
builder.Services.AddScoped<IMediaGrabberService, SteamGridDBMediaGrabber>();

View File

@ -0,0 +1,41 @@
using LANCommander.Data;
using LANCommander.Data.Models;
using LANCommander.Helpers;
using LANCommander.Models;
namespace LANCommander.Services
{
public class PlaySessionService : BaseDatabaseService<PlaySession>
{
public PlaySessionService(DatabaseContext dbContext, IHttpContextAccessor httpContextAccessor) : base(dbContext, httpContextAccessor) { }
public async Task StartSession(Guid gameId, Guid userId)
{
var existingSession = Get(ps => ps.GameId == gameId && ps.UserId == userId && ps.End == null).FirstOrDefault();
if (existingSession != null)
await Delete(existingSession);
var session = new PlaySession()
{
GameId = gameId,
UserId = userId,
Start = DateTime.UtcNow
};
await Add(session);
}
public async Task EndSession(Guid gameId, Guid userId)
{
var existingSession = Get(ps => ps.GameId == gameId && ps.UserId == userId && ps.End == null).FirstOrDefault();
if (existingSession != null)
{
existingSession.End = DateTime.UtcNow;
await Update(existingSession);
}
}
}
}