Start migration to asp .net core + entity framework core
This commit is contained in:
parent
aea09c2c8b
commit
f7761f200f
21
Application/Application.csproj
Normal file
21
Application/Application.csproj
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Domain\Domain.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="MediatR" Version="11.1.0" />
|
||||||
|
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="11.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.2" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Riok.Mapperly" Version="2.7.0-next.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
15
Application/DependencyInjection.cs
Normal file
15
Application/DependencyInjection.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Application;
|
||||||
|
|
||||||
|
public static class DependencyInjection
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddApplication(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddMediatR(Assembly.GetExecutingAssembly());
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
33
Application/Dto/CardDto.cs
Normal file
33
Application/Dto/CardDto.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
namespace Application.Dto;
|
||||||
|
|
||||||
|
public class CardDto
|
||||||
|
{
|
||||||
|
[XmlElement(ElementName = "card_id")]
|
||||||
|
public long CardId { get; set; }
|
||||||
|
|
||||||
|
[XmlElement(ElementName = "player_name")]
|
||||||
|
public string PlayerName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[XmlElement("score_i1")]
|
||||||
|
public long ScoreI1 { get; set; }
|
||||||
|
|
||||||
|
[XmlElement("fcol1")]
|
||||||
|
public long Fcol1 { get; set; }
|
||||||
|
|
||||||
|
[XmlElement("fcol2")]
|
||||||
|
public long Fcol2 { get; set; }
|
||||||
|
|
||||||
|
[XmlElement("fcol3")]
|
||||||
|
public long Fcol3 { get; set; }
|
||||||
|
|
||||||
|
[XmlElement("achieve_status")]
|
||||||
|
public string AchieveStatus { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[XmlElement("created")]
|
||||||
|
public string Created { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[XmlElement("modified")]
|
||||||
|
public string Modified { get; set; } = string.Empty;
|
||||||
|
}
|
62
Application/Game/Option/PlayCountQuery.cs
Normal file
62
Application/Game/Option/PlayCountQuery.cs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
using Application.Interfaces;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Application.Game.Option;
|
||||||
|
|
||||||
|
public record PlayCountQuery(long CardId) : IRequest<long>;
|
||||||
|
|
||||||
|
public class PlayCountQueryHandler : IRequestHandler<PlayCountQuery, long>
|
||||||
|
{
|
||||||
|
private readonly ICardDbContext context;
|
||||||
|
|
||||||
|
private readonly ILogger<PlayCountQueryHandler> logger;
|
||||||
|
|
||||||
|
public PlayCountQueryHandler(ICardDbContext context, ILogger<PlayCountQueryHandler> logger)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<long> Handle(PlayCountQuery request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return await GetPlayCount(request.CardId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<long> GetPlayCount(long cardId)
|
||||||
|
{
|
||||||
|
var record = await context.CardPlayCounts.FirstOrDefaultAsync(count => count.CardId == cardId);
|
||||||
|
if (record is null)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var now = DateTime.Now;
|
||||||
|
var lastPlayedTime = record.LastPlayedTime;
|
||||||
|
|
||||||
|
if (now <= lastPlayedTime)
|
||||||
|
{
|
||||||
|
logger.LogWarning("Clock skew detected! " +
|
||||||
|
"Current time: {Now}," +
|
||||||
|
"Last Play Time: {Last}", now, lastPlayedTime);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime start;
|
||||||
|
DateTime end;
|
||||||
|
if (now.Hour >= 8)
|
||||||
|
{
|
||||||
|
start = DateTime.Today.AddHours(8);
|
||||||
|
end = start.AddHours(24);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
end = DateTime.Today.AddHours(8);
|
||||||
|
start = end.AddHours(-24);
|
||||||
|
}
|
||||||
|
|
||||||
|
var inBetween = lastPlayedTime >= start && lastPlayedTime <= end;
|
||||||
|
return inBetween ? record.PlayCount : 0;
|
||||||
|
}
|
||||||
|
}
|
100
Application/Game/Server/CertifyCommand.cs
Normal file
100
Application/Game/Server/CertifyCommand.cs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Application.Interfaces;
|
||||||
|
using Domain.Config;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace Application.Game.Server;
|
||||||
|
|
||||||
|
public record CertifyCommand(string? Gid, string? Mac, string? Random, string? Md5, string Host) : IRequest<string>;
|
||||||
|
|
||||||
|
public partial class CertifyCommandHandler : IRequestHandler<CertifyCommand, string>
|
||||||
|
{
|
||||||
|
private readonly RelayConfig relayConfig;
|
||||||
|
|
||||||
|
|
||||||
|
public CertifyCommandHandler(IOptions<RelayConfig> relayOptions)
|
||||||
|
{
|
||||||
|
relayConfig = relayOptions.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<string> Handle(CertifyCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (request.Gid == null)
|
||||||
|
{
|
||||||
|
return Task.FromResult(QuitWithError(ErrorCode.ErrorNoGid));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.Mac == null)
|
||||||
|
{
|
||||||
|
return Task.FromResult(QuitWithError(ErrorCode.ErrorNoMac));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.Random == null)
|
||||||
|
{
|
||||||
|
return Task.FromResult(QuitWithError(ErrorCode.ErrorNoRandom));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.Md5 == null)
|
||||||
|
{
|
||||||
|
return Task.FromResult(QuitWithError(ErrorCode.ErrorNoHash));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MacValid(request.Mac))
|
||||||
|
{
|
||||||
|
return Task.FromResult(QuitWithError(ErrorCode.ErrorInvalidMac));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Md5Valid(request.Md5))
|
||||||
|
{
|
||||||
|
return Task.FromResult(QuitWithError(ErrorCode.ErrorInvalidHash));
|
||||||
|
}
|
||||||
|
|
||||||
|
var ticket = string.Join(string.Empty,
|
||||||
|
MD5.HashData(Encoding.UTF8.GetBytes(request.Gid)).Select(b => b.ToString("x2")));
|
||||||
|
|
||||||
|
var response = $"host=card_id=7020392000147361,relay_addr={relayConfig.RelayServer},relay_port={relayConfig.RelayPort}\n" +
|
||||||
|
"no=1337\n" +
|
||||||
|
"name=123\n" +
|
||||||
|
"pref=nesys\n" +
|
||||||
|
"addr=nesys@home\n" +
|
||||||
|
"x-next-time=15\n" +
|
||||||
|
$"x-img=http://{request.Host}/news.png\n" +
|
||||||
|
$"x-ranking=http://{request.Host}/ranking/ranking.php\n" +
|
||||||
|
$"ticket={ticket}";
|
||||||
|
|
||||||
|
return Task.FromResult(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool MacValid(string mac)
|
||||||
|
{
|
||||||
|
return MacRegex().IsMatch(mac);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool Md5Valid(string md5)
|
||||||
|
{
|
||||||
|
return Md5Regex().IsMatch(md5);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string QuitWithError(ErrorCode errorCode)
|
||||||
|
{
|
||||||
|
return $"error={(int)errorCode}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum ErrorCode
|
||||||
|
{
|
||||||
|
ErrorNoGid,
|
||||||
|
ErrorNoMac,
|
||||||
|
ErrorNoRandom,
|
||||||
|
ErrorNoHash,
|
||||||
|
ErrorInvalidMac,
|
||||||
|
ErrorInvalidHash
|
||||||
|
}
|
||||||
|
|
||||||
|
[GeneratedRegex("^[a-fA-F0-9]{12}$")]
|
||||||
|
private static partial Regex MacRegex();
|
||||||
|
[GeneratedRegex("^[a-fA-F0-9]{32}$")]
|
||||||
|
private static partial Regex Md5Regex();
|
||||||
|
}
|
48
Application/Game/Server/GetDataQuery.cs
Normal file
48
Application/Game/Server/GetDataQuery.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Application.Interfaces;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace Application.Game.Server;
|
||||||
|
|
||||||
|
public record GetDataQuery(string Host) : IRequest<string>;
|
||||||
|
|
||||||
|
public class GetDataQueryHandler : IRequestHandler<GetDataQuery, string>
|
||||||
|
{
|
||||||
|
private readonly IEventManagerService eventManagerService;
|
||||||
|
|
||||||
|
public GetDataQueryHandler(IEventManagerService eventManagerService)
|
||||||
|
{
|
||||||
|
this.eventManagerService = eventManagerService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<string> Handle(GetDataQuery request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var response = "count=0\n" +
|
||||||
|
"nexttime=0\n";
|
||||||
|
if (!eventManagerService.UseEvents())
|
||||||
|
{
|
||||||
|
return Task.FromResult(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
var host = request.Host;
|
||||||
|
var urlBase = $"http://{host}/events/";
|
||||||
|
var dataString = new StringBuilder();
|
||||||
|
var events = eventManagerService.GetEvents();
|
||||||
|
var count = 0;
|
||||||
|
foreach (var pair in events.Select((@event, i) => new {Value = @event, Index = i}))
|
||||||
|
{
|
||||||
|
var value = pair.Value;
|
||||||
|
var index = pair.Index;
|
||||||
|
var fileUrl = $"{urlBase}{value.Name}";
|
||||||
|
var eventString = $"{index},{fileUrl},{value.NotBefore},{value.NotAfter},{value.Md5},{value.Index}";
|
||||||
|
dataString.Append(eventString).Append('\n');
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
response = $"count={count}\n" +
|
||||||
|
"nexttime=1\n" +
|
||||||
|
$"{dataString}";
|
||||||
|
|
||||||
|
return Task.FromResult(response);
|
||||||
|
}
|
||||||
|
}
|
19
Application/Interfaces/ICardDbContext.cs
Normal file
19
Application/Interfaces/ICardDbContext.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using Domain.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Application.Interfaces;
|
||||||
|
|
||||||
|
public interface ICardDbContext
|
||||||
|
{
|
||||||
|
public DbSet<CardBdatum> CardBdata { get; set; }
|
||||||
|
|
||||||
|
public DbSet<CardDetail> CardDetails { get; set; }
|
||||||
|
|
||||||
|
public DbSet<CardMain> CardMains { get; set; }
|
||||||
|
|
||||||
|
public DbSet<CardPlayCount> CardPlayCounts { get; set; }
|
||||||
|
|
||||||
|
public Task<int> SaveChangesAsync(CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
12
Application/Interfaces/IEventManagerService.cs
Normal file
12
Application/Interfaces/IEventManagerService.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using Domain.Models;
|
||||||
|
|
||||||
|
namespace Application.Interfaces;
|
||||||
|
|
||||||
|
public interface IEventManagerService
|
||||||
|
{
|
||||||
|
public void InitializeEvents();
|
||||||
|
|
||||||
|
public bool UseEvents();
|
||||||
|
|
||||||
|
public IEnumerable<Event> GetEvents();
|
||||||
|
}
|
13
Application/Interfaces/IMusicDbContext.cs
Normal file
13
Application/Interfaces/IMusicDbContext.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using Domain.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Application.Interfaces;
|
||||||
|
|
||||||
|
public interface IMusicDbContext
|
||||||
|
{
|
||||||
|
public DbSet<MusicAou> MusicAous { get; set; }
|
||||||
|
|
||||||
|
public DbSet<MusicExtra> MusicExtras { get; set; }
|
||||||
|
|
||||||
|
public DbSet<MusicUnlock> MusicUnlocks { get; set; }
|
||||||
|
}
|
11
Application/Mappers/CardMapper.cs
Normal file
11
Application/Mappers/CardMapper.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using Application.Dto;
|
||||||
|
using Domain.Entities;
|
||||||
|
using Riok.Mapperly.Abstractions;
|
||||||
|
|
||||||
|
namespace Application.Mappers;
|
||||||
|
|
||||||
|
[Mapper]
|
||||||
|
public static partial class CardMapper
|
||||||
|
{
|
||||||
|
public static partial CardDto CardMainToCardDto(this CardMain cardMain);
|
||||||
|
}
|
17
Domain/Config/EventConfig.cs
Normal file
17
Domain/Config/EventConfig.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
namespace Domain.Config;
|
||||||
|
|
||||||
|
public class EventConfig
|
||||||
|
{
|
||||||
|
public const string EVENT_SECTION = "Events";
|
||||||
|
|
||||||
|
public bool UseEvents { get; set; }
|
||||||
|
|
||||||
|
public List<EventFile> EventFiles { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EventFile
|
||||||
|
{
|
||||||
|
public string FileName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public int Index { get; set; }
|
||||||
|
}
|
10
Domain/Config/RelayConfig.cs
Normal file
10
Domain/Config/RelayConfig.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace Domain.Config;
|
||||||
|
|
||||||
|
public class RelayConfig
|
||||||
|
{
|
||||||
|
public const string RELAY_SECTION = "Relay";
|
||||||
|
|
||||||
|
public string RelayServer { get; set; } = "127.0.0.1";
|
||||||
|
|
||||||
|
public int RelayPort { get; set; } = 3333;
|
||||||
|
}
|
9
Domain/Domain.csproj
Normal file
9
Domain/Domain.csproj
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
10
Domain/Entities/CardBdatum.cs
Normal file
10
Domain/Entities/CardBdatum.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace Domain.Entities;
|
||||||
|
|
||||||
|
public partial class CardBdatum
|
||||||
|
{
|
||||||
|
public long CardId { get; set; }
|
||||||
|
|
||||||
|
public string? Bdata { get; set; }
|
||||||
|
|
||||||
|
public long BdataSize { get; set; }
|
||||||
|
}
|
38
Domain/Entities/CardDetail.cs
Normal file
38
Domain/Entities/CardDetail.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
namespace Domain.Entities;
|
||||||
|
|
||||||
|
public partial class CardDetail
|
||||||
|
{
|
||||||
|
public long CardId { get; set; }
|
||||||
|
|
||||||
|
public long Pcol1 { get; set; }
|
||||||
|
|
||||||
|
public long Pcol2 { get; set; }
|
||||||
|
|
||||||
|
public long Pcol3 { get; set; }
|
||||||
|
|
||||||
|
public long ScoreI1 { get; set; }
|
||||||
|
|
||||||
|
public long ScoreUi1 { get; set; }
|
||||||
|
|
||||||
|
public long ScoreUi2 { get; set; }
|
||||||
|
|
||||||
|
public long ScoreUi3 { get; set; }
|
||||||
|
|
||||||
|
public long ScoreUi4 { get; set; }
|
||||||
|
|
||||||
|
public long ScoreUi5 { get; set; }
|
||||||
|
|
||||||
|
public long ScoreUi6 { get; set; }
|
||||||
|
|
||||||
|
public long ScoreBi1 { get; set; }
|
||||||
|
|
||||||
|
public string? LastPlayTenpoId { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public long Fcol1 { get; set; }
|
||||||
|
|
||||||
|
public long Fcol2 { get; set; }
|
||||||
|
|
||||||
|
public long Fcol3 { get; set; }
|
||||||
|
|
||||||
|
public DateTime LastPlayTime { get; set; }
|
||||||
|
}
|
22
Domain/Entities/CardMain.cs
Normal file
22
Domain/Entities/CardMain.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
namespace Domain.Entities;
|
||||||
|
|
||||||
|
public partial class CardMain
|
||||||
|
{
|
||||||
|
public long CardId { get; set; }
|
||||||
|
|
||||||
|
public string PlayerName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public long ScoreI1 { get; set; }
|
||||||
|
|
||||||
|
public long Fcol1 { get; set; }
|
||||||
|
|
||||||
|
public long Fcol2 { get; set; }
|
||||||
|
|
||||||
|
public long Fcol3 { get; set; }
|
||||||
|
|
||||||
|
public string AchieveStatus { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string? Created { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string? Modified { get; set; } = string.Empty;
|
||||||
|
}
|
10
Domain/Entities/CardPlayCount.cs
Normal file
10
Domain/Entities/CardPlayCount.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace Domain.Entities;
|
||||||
|
|
||||||
|
public partial class CardPlayCount
|
||||||
|
{
|
||||||
|
public long CardId { get; set; }
|
||||||
|
|
||||||
|
public long PlayCount { get; set; }
|
||||||
|
|
||||||
|
public DateTime LastPlayedTime { get; set; }
|
||||||
|
}
|
8
Domain/Entities/MusicAou.cs
Normal file
8
Domain/Entities/MusicAou.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace Domain.Entities;
|
||||||
|
|
||||||
|
public class MusicAou
|
||||||
|
{
|
||||||
|
public long MusicId { get; set; }
|
||||||
|
|
||||||
|
public bool UseFlag { get; set; }
|
||||||
|
}
|
8
Domain/Entities/MusicExtra.cs
Normal file
8
Domain/Entities/MusicExtra.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace Domain.Entities;
|
||||||
|
|
||||||
|
public class MusicExtra
|
||||||
|
{
|
||||||
|
public long MusicId { get; set; }
|
||||||
|
|
||||||
|
public bool UseFlag { get; set; }
|
||||||
|
}
|
18
Domain/Entities/MusicUnlock.cs
Normal file
18
Domain/Entities/MusicUnlock.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
namespace Domain.Entities;
|
||||||
|
|
||||||
|
public partial class MusicUnlock
|
||||||
|
{
|
||||||
|
public long MusicId { get; set; }
|
||||||
|
|
||||||
|
public string Title { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string Artist { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public DateTime ReleaseDate { get; set; }
|
||||||
|
|
||||||
|
public DateTime EndDate { get; set; }
|
||||||
|
|
||||||
|
public bool NewFlag { get; set; }
|
||||||
|
|
||||||
|
public bool UseFlag { get; set; }
|
||||||
|
}
|
14
Domain/Models/Event.cs
Normal file
14
Domain/Models/Event.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
namespace Domain.Models;
|
||||||
|
|
||||||
|
public class Event
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string Md5 { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public int Index { get; set; }
|
||||||
|
|
||||||
|
public string NotBefore { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string NotAfter { get; set; } = string.Empty;
|
||||||
|
}
|
@ -11,6 +11,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MudAdmin", "MudAdmin\MudAdm
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GCRelayServer", "GCRelayServer\GCRelayServer.csproj", "{268178DF-6345-4D9E-A389-9E809CBF39C9}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GCRelayServer", "GCRelayServer\GCRelayServer.csproj", "{268178DF-6345-4D9E-A389-9E809CBF39C9}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatchServer", "MatchServer\MatchServer.csproj", "{CB91C3D3-ED69-4DC6-A205-B262A08BEE49}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MainServer", "MainServer\MainServer.csproj", "{B8C6CA7E-5E58-43BC-8E03-84306916DE39}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Application", "Application\Application.csproj", "{B0691233-0D7E-4694-8923-646E7A3BDBF4}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Domain", "Domain\Domain.csproj", "{CB95F4B7-7627-4CCA-A2B6-2D6FD48446C1}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure", "Infrastructure\Infrastructure.csproj", "{2666D734-0E81-431E-A22D-216218FC9023}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -33,6 +43,26 @@ Global
|
|||||||
{268178DF-6345-4D9E-A389-9E809CBF39C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{268178DF-6345-4D9E-A389-9E809CBF39C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{268178DF-6345-4D9E-A389-9E809CBF39C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{268178DF-6345-4D9E-A389-9E809CBF39C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{268178DF-6345-4D9E-A389-9E809CBF39C9}.Release|Any CPU.Build.0 = Release|Any CPU
|
{268178DF-6345-4D9E-A389-9E809CBF39C9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{CB91C3D3-ED69-4DC6-A205-B262A08BEE49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{CB91C3D3-ED69-4DC6-A205-B262A08BEE49}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{CB91C3D3-ED69-4DC6-A205-B262A08BEE49}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{CB91C3D3-ED69-4DC6-A205-B262A08BEE49}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B8C6CA7E-5E58-43BC-8E03-84306916DE39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B8C6CA7E-5E58-43BC-8E03-84306916DE39}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B8C6CA7E-5E58-43BC-8E03-84306916DE39}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B8C6CA7E-5E58-43BC-8E03-84306916DE39}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B0691233-0D7E-4694-8923-646E7A3BDBF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B0691233-0D7E-4694-8923-646E7A3BDBF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B0691233-0D7E-4694-8923-646E7A3BDBF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B0691233-0D7E-4694-8923-646E7A3BDBF4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{CB95F4B7-7627-4CCA-A2B6-2D6FD48446C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{CB95F4B7-7627-4CCA-A2B6-2D6FD48446C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{CB95F4B7-7627-4CCA-A2B6-2D6FD48446C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{CB95F4B7-7627-4CCA-A2B6-2D6FD48446C1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{2666D734-0E81-431E-A22D-216218FC9023}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{2666D734-0E81-431E-A22D-216218FC9023}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{2666D734-0E81-431E-A22D-216218FC9023}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{2666D734-0E81-431E-A22D-216218FC9023}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=ADDR/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=ADDR/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=BDATA/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=BDATA/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Fcol/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Fcol/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
306
Infrastructure/Common/CertificateService.cs
Normal file
306
Infrastructure/Common/CertificateService.cs
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using CertificateManager;
|
||||||
|
using CertificateManager.Models;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Infrastructure.Common;
|
||||||
|
|
||||||
|
public class CertificateService
|
||||||
|
{
|
||||||
|
private const X509KeyUsageFlags ROOT_CA_X509_KEY_USAGE_FLAGS = X509KeyUsageFlags.KeyCertSign |
|
||||||
|
X509KeyUsageFlags.DataEncipherment |
|
||||||
|
X509KeyUsageFlags.KeyEncipherment |
|
||||||
|
X509KeyUsageFlags.DigitalSignature;
|
||||||
|
|
||||||
|
private const X509KeyStorageFlags X509_KEY_STORAGE_FLAGS_MACHINE = X509KeyStorageFlags.PersistKeySet |
|
||||||
|
X509KeyStorageFlags.MachineKeySet |
|
||||||
|
X509KeyStorageFlags.Exportable;
|
||||||
|
|
||||||
|
private const X509KeyUsageFlags CERT_X509_KEY_USAGE_FLAGS = X509KeyUsageFlags.DataEncipherment |
|
||||||
|
X509KeyUsageFlags.KeyEncipherment |
|
||||||
|
X509KeyUsageFlags.DigitalSignature;
|
||||||
|
|
||||||
|
private const string ROOT_CA_CN = "Taito Arcade Machine CA";
|
||||||
|
private const string CERT_CN = "GC local server";
|
||||||
|
private const string CERT_DIR = "Certificates";
|
||||||
|
private const string CERT_FILE_NAME = "cert.pfx";
|
||||||
|
private const string ROOT_CERT_FILE_NAME = "root.pfx";
|
||||||
|
private static readonly string CERT_PATH = Path.Combine(CERT_DIR, CERT_FILE_NAME);
|
||||||
|
private static readonly string ROOT_CERT_PATH = Path.Combine(CERT_DIR, ROOT_CERT_FILE_NAME);
|
||||||
|
|
||||||
|
private ILogger logger;
|
||||||
|
|
||||||
|
private static readonly DistinguishedName ROOT_CA_DISTINGUISHED_NAME = new()
|
||||||
|
{
|
||||||
|
CommonName = ROOT_CA_CN
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly DistinguishedName CERT_DISTINGUISHED_NAME = new()
|
||||||
|
{
|
||||||
|
CommonName = CERT_CN
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly BasicConstraints ROOT_CA_BASIC_CONSTRAINTS = new()
|
||||||
|
{
|
||||||
|
CertificateAuthority = true,
|
||||||
|
HasPathLengthConstraint = true,
|
||||||
|
PathLengthConstraint = 3,
|
||||||
|
Critical = true
|
||||||
|
};
|
||||||
|
|
||||||
|
public static readonly BasicConstraints CERT_BASIC_CONSTRAINTS = new()
|
||||||
|
{
|
||||||
|
CertificateAuthority = false,
|
||||||
|
HasPathLengthConstraint = false,
|
||||||
|
PathLengthConstraint = 0,
|
||||||
|
Critical = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly SubjectAlternativeName subjectAlternativeName;
|
||||||
|
|
||||||
|
private static readonly ValidityPeriod VALIDITY_PERIOD = new()
|
||||||
|
{
|
||||||
|
ValidFrom = DateTime.UtcNow,
|
||||||
|
ValidTo = DateTime.UtcNow.AddYears(3)
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly OidCollection OID_COLLECTION = new()
|
||||||
|
{
|
||||||
|
OidLookup.ServerAuthentication,
|
||||||
|
OidLookup.AnyPurpose
|
||||||
|
};
|
||||||
|
|
||||||
|
public CertificateService(string serverIp, ILogger logger)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
subjectAlternativeName = new SubjectAlternativeName
|
||||||
|
{
|
||||||
|
DnsName = new List<string>
|
||||||
|
{
|
||||||
|
"localhost",
|
||||||
|
"cert.nesys.jp",
|
||||||
|
"nesys.taito.co.jp",
|
||||||
|
"fjm170920zero.nesica.net"
|
||||||
|
},
|
||||||
|
IpAddress = System.Net.IPAddress.Parse(serverIp)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public X509Certificate2 InitializeCertificate()
|
||||||
|
{
|
||||||
|
return Environment.OSVersion.Platform == PlatformID.Win32NT
|
||||||
|
? InitializeCertificateWindows()
|
||||||
|
: InitializeCertificateOthers();
|
||||||
|
}
|
||||||
|
|
||||||
|
private X509Certificate2 InitializeCertificateOthers()
|
||||||
|
{
|
||||||
|
if (CertificateExists())
|
||||||
|
{
|
||||||
|
return new X509Certificate2(CERT_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogInformation("Existing certs not found! Removing old certificates and genrate new ones...");
|
||||||
|
|
||||||
|
File.Delete(CERT_PATH);
|
||||||
|
File.Delete(ROOT_CERT_PATH);
|
||||||
|
|
||||||
|
return GenerateCertificate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private X509Certificate2 InitializeCertificateWindows()
|
||||||
|
{
|
||||||
|
if (CertificateExists())
|
||||||
|
{
|
||||||
|
var existingCert = GetCertificate(StoreName.My, StoreLocation.LocalMachine, CERT_CN);
|
||||||
|
|
||||||
|
if (existingCert != null)
|
||||||
|
{
|
||||||
|
return existingCert;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogInformation("Existing CN not found! Removing old certificates and genrate new ones...");
|
||||||
|
}
|
||||||
|
|
||||||
|
RemovePreviousCert(StoreName.My, StoreLocation.LocalMachine);
|
||||||
|
RemovePreviousCert(StoreName.Root, StoreLocation.LocalMachine);
|
||||||
|
|
||||||
|
return GenerateCertificate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private X509Certificate2 GenerateCertificate()
|
||||||
|
{
|
||||||
|
var serviceProvider = new ServiceCollection()
|
||||||
|
.AddCertificateManager().BuildServiceProvider();
|
||||||
|
|
||||||
|
var createCertificates = serviceProvider.GetService<CreateCertificates>();
|
||||||
|
|
||||||
|
if (createCertificates == null)
|
||||||
|
{
|
||||||
|
logger.LogError("Cannot initialize CreateCertificates service!");
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
var rootCa = createCertificates.NewRsaSelfSignedCertificate(
|
||||||
|
ROOT_CA_DISTINGUISHED_NAME,
|
||||||
|
ROOT_CA_BASIC_CONSTRAINTS,
|
||||||
|
VALIDITY_PERIOD,
|
||||||
|
new SubjectAlternativeName(),
|
||||||
|
OID_COLLECTION,
|
||||||
|
ROOT_CA_X509_KEY_USAGE_FLAGS,
|
||||||
|
new RsaConfiguration()
|
||||||
|
);
|
||||||
|
|
||||||
|
var cert = createCertificates.NewRsaChainedCertificate(
|
||||||
|
CERT_DISTINGUISHED_NAME,
|
||||||
|
CERT_BASIC_CONSTRAINTS,
|
||||||
|
VALIDITY_PERIOD,
|
||||||
|
subjectAlternativeName,
|
||||||
|
rootCa,
|
||||||
|
OID_COLLECTION,
|
||||||
|
CERT_X509_KEY_USAGE_FLAGS,
|
||||||
|
new RsaConfiguration()
|
||||||
|
);
|
||||||
|
|
||||||
|
var exportService = serviceProvider.GetService<ImportExportCertificate>();
|
||||||
|
|
||||||
|
if (exportService == null)
|
||||||
|
{
|
||||||
|
logger.LogError("Cannot initialize ImportExportCertificate service!");
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
var rootCaPfxBytes = exportService.ExportRootPfx(null, rootCa);
|
||||||
|
var certPfxBytes = exportService.ExportChainedCertificatePfx(null, cert, rootCa);
|
||||||
|
|
||||||
|
var rootCaWithPrivateKey = new X509Certificate2(rootCaPfxBytes, (string)null!,
|
||||||
|
X509_KEY_STORAGE_FLAGS_MACHINE);
|
||||||
|
|
||||||
|
var certWithPrivateKey = new X509Certificate2(certPfxBytes, (string)null!,
|
||||||
|
X509_KEY_STORAGE_FLAGS_MACHINE);
|
||||||
|
|
||||||
|
|
||||||
|
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
|
||||||
|
{
|
||||||
|
AddCertToStore(rootCaWithPrivateKey, StoreName.My, StoreLocation.LocalMachine);
|
||||||
|
AddCertToStore(rootCaWithPrivateKey, StoreName.Root, StoreLocation.LocalMachine);
|
||||||
|
AddCertToStore(certWithPrivateKey, StoreName.My, StoreLocation.LocalMachine);
|
||||||
|
}
|
||||||
|
|
||||||
|
Directory.CreateDirectory(CERT_DIR);
|
||||||
|
|
||||||
|
File.WriteAllBytes(ROOT_CERT_PATH, rootCaWithPrivateKey.Export(X509ContentType.Pfx));
|
||||||
|
File.WriteAllBytes(CERT_PATH, certWithPrivateKey.Export(X509ContentType.Pfx));
|
||||||
|
|
||||||
|
return certWithPrivateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddCertToStore(X509Certificate2 cert, StoreName storeName, StoreLocation storeLocation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var store = new X509Store(storeName, storeLocation);
|
||||||
|
store.Open(OpenFlags.ReadWrite);
|
||||||
|
store.Add(cert);
|
||||||
|
|
||||||
|
store.Close();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.LogError(e, "An exception occurs when adding certificate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemovePreviousCert(StoreName storeName, StoreLocation storeLocation)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var store = new X509Store(storeName, storeLocation);
|
||||||
|
store.Open(OpenFlags.ReadWrite);
|
||||||
|
var result = store.Certificates.Find(X509FindType.FindByIssuerName, ROOT_CA_CN, true);
|
||||||
|
|
||||||
|
if (result.Any())
|
||||||
|
{
|
||||||
|
store.RemoveRange(result);
|
||||||
|
logger.LogInformation("Removed previous certificates!");
|
||||||
|
}
|
||||||
|
|
||||||
|
store.Close();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.LogError(e, "An exception occurs when removing previous certificates");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CertificateExists()
|
||||||
|
{
|
||||||
|
bool certificateExists;
|
||||||
|
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
|
||||||
|
{
|
||||||
|
// Non windows just use generated certificate file
|
||||||
|
certificateExists = Path.Exists(Path.Combine(CERT_DIR, "cert.pfx")) &&
|
||||||
|
Path.Exists(Path.Combine(CERT_DIR, "root.pfx"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
|
||||||
|
store.Open(OpenFlags.ReadOnly);
|
||||||
|
var result = store.Certificates.Find(X509FindType.FindByIssuerName, ROOT_CA_CN, true);
|
||||||
|
|
||||||
|
certificateExists = result.Count == 2;
|
||||||
|
|
||||||
|
store.Close();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.LogError(e, "An exception occurs when checking certificates");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (certificateExists)
|
||||||
|
{
|
||||||
|
logger.LogInformation("Certificate exists!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.LogInformation("Certificate not found! Will generate new certs...");
|
||||||
|
}
|
||||||
|
|
||||||
|
return certificateExists;
|
||||||
|
}
|
||||||
|
|
||||||
|
private X509Certificate2? GetCertificate(StoreName storeName, StoreLocation storeLocation, string commonName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var store = new X509Store(storeName, storeLocation);
|
||||||
|
store.Open(OpenFlags.ReadWrite);
|
||||||
|
var result = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName,
|
||||||
|
$"CN={commonName}", true);
|
||||||
|
|
||||||
|
if (result.Any())
|
||||||
|
{
|
||||||
|
logger.LogInformation("Certificate CN={CommonName} found!", commonName);
|
||||||
|
|
||||||
|
return result.First();
|
||||||
|
}
|
||||||
|
|
||||||
|
store.Close();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.LogError(e, "An exception occurs when getting certificate {CommonName}", commonName);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
Infrastructure/Common/PathHelper.cs
Normal file
31
Infrastructure/Common/PathHelper.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using Validation;
|
||||||
|
|
||||||
|
namespace Infrastructure.Common;
|
||||||
|
|
||||||
|
public static class PathHelper
|
||||||
|
{
|
||||||
|
public static string DatabasePath = Path.Combine(BasePath, "Database");
|
||||||
|
|
||||||
|
public static string ConfigurationPath = Path.Combine(BasePath, "Configurations");
|
||||||
|
|
||||||
|
public static string BasePath
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var assemblyPath = Environment.ProcessPath;
|
||||||
|
Assumes.NotNull(assemblyPath);
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
var parentFullName = Directory.GetParent(assemblyPath)?.Parent?.Parent?.Parent?.FullName;
|
||||||
|
|
||||||
|
return parentFullName ?? "";
|
||||||
|
|
||||||
|
#else
|
||||||
|
var parent = Directory.GetParent(assemblyPath);
|
||||||
|
Assumes.NotNull(parent);
|
||||||
|
return parent.ToString();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
Infrastructure/DependencyInjection.cs
Normal file
46
Infrastructure/DependencyInjection.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
using Application.Interfaces;
|
||||||
|
using Infrastructure.Common;
|
||||||
|
using Infrastructure.Persistence;
|
||||||
|
using Infrastructure.Services;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Infrastructure;
|
||||||
|
|
||||||
|
public static class DependencyInjection
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddSingleton<IEventManagerService, EventManagerService>();
|
||||||
|
|
||||||
|
services.AddDbContext<MusicDbContext>(option =>
|
||||||
|
{
|
||||||
|
var dbName = configuration["MusicDbName"];
|
||||||
|
if (string.IsNullOrEmpty(dbName))
|
||||||
|
{
|
||||||
|
dbName = "music471omni.db3";
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = Path.Combine(PathHelper.DatabasePath, dbName);
|
||||||
|
option.UseSqlite($"Data Source={path}");
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddDbContext<CardDbContext>(option =>
|
||||||
|
{
|
||||||
|
var dbName = configuration["CardDbName"];
|
||||||
|
if (string.IsNullOrEmpty(dbName))
|
||||||
|
{
|
||||||
|
dbName = "card.db3";
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = Path.Combine(PathHelper.DatabasePath, dbName);
|
||||||
|
option.UseSqlite($"Data Source={path}");
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddScoped<ICardDbContext>(provider => provider.GetService<CardDbContext>() ?? throw new InvalidOperationException());
|
||||||
|
services.AddScoped<IMusicDbContext>(provider => provider.GetService<MusicDbContext>() ?? throw new InvalidOperationException());
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
9
Infrastructure/Exceptions/EventExceptions.cs
Normal file
9
Infrastructure/Exceptions/EventExceptions.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace Infrastructure.Exceptions;
|
||||||
|
|
||||||
|
public class EventFileNotFoundException : Exception
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EventFileTypeUnknownException : Exception
|
||||||
|
{
|
||||||
|
}
|
29
Infrastructure/Infrastructure.csproj
Normal file
29
Infrastructure/Infrastructure.csproj
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CertificateManager" Version="1.0.8" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.2">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.2" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Validation" Version="2.6.13-beta" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Application\Application.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
182
Infrastructure/Migrations/20230208132952_Initial.Designer.cs
generated
Normal file
182
Infrastructure/Migrations/20230208132952_Initial.Designer.cs
generated
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using Infrastructure.Persistence;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Infrastructure.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(CardDbContext))]
|
||||||
|
[Migration("20230208132952_Initial")]
|
||||||
|
partial class Initial
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "7.0.2");
|
||||||
|
|
||||||
|
modelBuilder.Entity("Domain.Entities.CardBdatum", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("CardId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("card_id");
|
||||||
|
|
||||||
|
b.Property<string>("Bdata")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("bdata");
|
||||||
|
|
||||||
|
b.Property<long>("BdataSize")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("bdata_size");
|
||||||
|
|
||||||
|
b.HasKey("CardId");
|
||||||
|
|
||||||
|
b.ToTable("card_bdata", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Domain.Entities.CardDetail", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("CardId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("card_id");
|
||||||
|
|
||||||
|
b.Property<long>("Pcol1")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("pcol1");
|
||||||
|
|
||||||
|
b.Property<long>("Pcol2")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("pcol2");
|
||||||
|
|
||||||
|
b.Property<long>("Pcol3")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("pcol3");
|
||||||
|
|
||||||
|
b.Property<long>("Fcol1")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("fcol1");
|
||||||
|
|
||||||
|
b.Property<long>("Fcol2")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("fcol2");
|
||||||
|
|
||||||
|
b.Property<long>("Fcol3")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("fcol3");
|
||||||
|
|
||||||
|
b.Property<string>("LastPlayTenpoId")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last_play_tenpo_id");
|
||||||
|
|
||||||
|
b.Property<long>("LastPlayTime")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("last_play_time");
|
||||||
|
|
||||||
|
b.Property<long>("ScoreBi1")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("score_bi1");
|
||||||
|
|
||||||
|
b.Property<long>("ScoreI1")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("score_i1");
|
||||||
|
|
||||||
|
b.Property<long>("ScoreUi1")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("score_ui1");
|
||||||
|
|
||||||
|
b.Property<long>("ScoreUi2")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("score_ui2");
|
||||||
|
|
||||||
|
b.Property<long>("ScoreUi3")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("score_ui3");
|
||||||
|
|
||||||
|
b.Property<long>("ScoreUi4")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("score_ui4");
|
||||||
|
|
||||||
|
b.Property<long>("ScoreUi5")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("score_ui5");
|
||||||
|
|
||||||
|
b.Property<long>("ScoreUi6")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("score_ui6");
|
||||||
|
|
||||||
|
b.HasKey("CardId", "Pcol1", "Pcol2", "Pcol3");
|
||||||
|
|
||||||
|
b.ToTable("card_detail", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Domain.Entities.CardMain", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("CardId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("card_id");
|
||||||
|
|
||||||
|
b.Property<string>("AchieveStatus")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("achieve_status");
|
||||||
|
|
||||||
|
b.Property<string>("Created")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created");
|
||||||
|
|
||||||
|
b.Property<long>("Fcol1")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("fcol1");
|
||||||
|
|
||||||
|
b.Property<long>("Fcol2")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("fcol2");
|
||||||
|
|
||||||
|
b.Property<long>("Fcol3")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("fcol3");
|
||||||
|
|
||||||
|
b.Property<string>("Modified")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("modified");
|
||||||
|
|
||||||
|
b.Property<string>("PlayerName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("player_name");
|
||||||
|
|
||||||
|
b.Property<long>("ScoreI1")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("score_i1");
|
||||||
|
|
||||||
|
b.HasKey("CardId");
|
||||||
|
|
||||||
|
b.ToTable("card_main", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Domain.Entities.CardPlayCount", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("CardId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("card_id");
|
||||||
|
|
||||||
|
b.Property<long>("LastPlayedTime")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("last_played_time");
|
||||||
|
|
||||||
|
b.Property<long>("PlayCount")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("play_count");
|
||||||
|
|
||||||
|
b.HasKey("CardId");
|
||||||
|
|
||||||
|
b.ToTable("CardPlayCount", (string)null);
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
102
Infrastructure/Migrations/20230208132952_Initial.cs
Normal file
102
Infrastructure/Migrations/20230208132952_Initial.cs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Infrastructure.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class Initial : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "card_bdata",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
cardid = table.Column<long>(name: "card_id", type: "INTEGER", nullable: false),
|
||||||
|
bdata = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
bdatasize = table.Column<long>(name: "bdata_size", type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_card_bdata", x => x.cardid);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "card_detail",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
cardid = table.Column<long>(name: "card_id", type: "INTEGER", nullable: false),
|
||||||
|
pcol1 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
|
pcol2 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
|
pcol3 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
|
scorei1 = table.Column<long>(name: "score_i1", type: "INTEGER", nullable: false),
|
||||||
|
scoreui1 = table.Column<long>(name: "score_ui1", type: "INTEGER", nullable: false),
|
||||||
|
scoreui2 = table.Column<long>(name: "score_ui2", type: "INTEGER", nullable: false),
|
||||||
|
scoreui3 = table.Column<long>(name: "score_ui3", type: "INTEGER", nullable: false),
|
||||||
|
scoreui4 = table.Column<long>(name: "score_ui4", type: "INTEGER", nullable: false),
|
||||||
|
scoreui5 = table.Column<long>(name: "score_ui5", type: "INTEGER", nullable: false),
|
||||||
|
scoreui6 = table.Column<long>(name: "score_ui6", type: "INTEGER", nullable: false),
|
||||||
|
scorebi1 = table.Column<long>(name: "score_bi1", type: "INTEGER", nullable: false),
|
||||||
|
lastplaytenpoid = table.Column<string>(name: "last_play_tenpo_id", type: "TEXT", nullable: true),
|
||||||
|
fcol1 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
|
fcol2 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
|
fcol3 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
|
lastplaytime = table.Column<long>(name: "last_play_time", type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_card_detail", x => new { x.cardid, x.pcol1, x.pcol2, x.pcol3 });
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "card_main",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
cardid = table.Column<long>(name: "card_id", type: "INTEGER", nullable: false),
|
||||||
|
playername = table.Column<string>(name: "player_name", type: "TEXT", nullable: false),
|
||||||
|
scorei1 = table.Column<long>(name: "score_i1", type: "INTEGER", nullable: false),
|
||||||
|
fcol1 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
|
fcol2 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
|
fcol3 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
|
achievestatus = table.Column<string>(name: "achieve_status", type: "TEXT", nullable: false),
|
||||||
|
created = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
modified = table.Column<string>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_card_main", x => x.cardid);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "CardPlayCount",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
cardid = table.Column<long>(name: "card_id", type: "INTEGER", nullable: false),
|
||||||
|
playcount = table.Column<long>(name: "play_count", type: "INTEGER", nullable: false),
|
||||||
|
lastplayedtime = table.Column<long>(name: "last_played_time", type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_CardPlayCount", x => x.cardid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "card_bdata");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "card_detail");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "card_main");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "CardPlayCount");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
179
Infrastructure/Migrations/CardDbContextModelSnapshot.cs
Normal file
179
Infrastructure/Migrations/CardDbContextModelSnapshot.cs
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using Infrastructure.Persistence;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Infrastructure.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(CardDbContext))]
|
||||||
|
partial class CardDbContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "7.0.2");
|
||||||
|
|
||||||
|
modelBuilder.Entity("Domain.Entities.CardBdatum", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("CardId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("card_id");
|
||||||
|
|
||||||
|
b.Property<string>("Bdata")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("bdata");
|
||||||
|
|
||||||
|
b.Property<long>("BdataSize")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("bdata_size");
|
||||||
|
|
||||||
|
b.HasKey("CardId");
|
||||||
|
|
||||||
|
b.ToTable("card_bdata", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Domain.Entities.CardDetail", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("CardId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("card_id");
|
||||||
|
|
||||||
|
b.Property<long>("Pcol1")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("pcol1");
|
||||||
|
|
||||||
|
b.Property<long>("Pcol2")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("pcol2");
|
||||||
|
|
||||||
|
b.Property<long>("Pcol3")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("pcol3");
|
||||||
|
|
||||||
|
b.Property<long>("Fcol1")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("fcol1");
|
||||||
|
|
||||||
|
b.Property<long>("Fcol2")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("fcol2");
|
||||||
|
|
||||||
|
b.Property<long>("Fcol3")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("fcol3");
|
||||||
|
|
||||||
|
b.Property<string>("LastPlayTenpoId")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last_play_tenpo_id");
|
||||||
|
|
||||||
|
b.Property<long>("LastPlayTime")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("last_play_time");
|
||||||
|
|
||||||
|
b.Property<long>("ScoreBi1")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("score_bi1");
|
||||||
|
|
||||||
|
b.Property<long>("ScoreI1")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("score_i1");
|
||||||
|
|
||||||
|
b.Property<long>("ScoreUi1")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("score_ui1");
|
||||||
|
|
||||||
|
b.Property<long>("ScoreUi2")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("score_ui2");
|
||||||
|
|
||||||
|
b.Property<long>("ScoreUi3")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("score_ui3");
|
||||||
|
|
||||||
|
b.Property<long>("ScoreUi4")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("score_ui4");
|
||||||
|
|
||||||
|
b.Property<long>("ScoreUi5")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("score_ui5");
|
||||||
|
|
||||||
|
b.Property<long>("ScoreUi6")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("score_ui6");
|
||||||
|
|
||||||
|
b.HasKey("CardId", "Pcol1", "Pcol2", "Pcol3");
|
||||||
|
|
||||||
|
b.ToTable("card_detail", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Domain.Entities.CardMain", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("CardId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("card_id");
|
||||||
|
|
||||||
|
b.Property<string>("AchieveStatus")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("achieve_status");
|
||||||
|
|
||||||
|
b.Property<string>("Created")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created");
|
||||||
|
|
||||||
|
b.Property<long>("Fcol1")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("fcol1");
|
||||||
|
|
||||||
|
b.Property<long>("Fcol2")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("fcol2");
|
||||||
|
|
||||||
|
b.Property<long>("Fcol3")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("fcol3");
|
||||||
|
|
||||||
|
b.Property<string>("Modified")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("modified");
|
||||||
|
|
||||||
|
b.Property<string>("PlayerName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("player_name");
|
||||||
|
|
||||||
|
b.Property<long>("ScoreI1")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("score_i1");
|
||||||
|
|
||||||
|
b.HasKey("CardId");
|
||||||
|
|
||||||
|
b.ToTable("card_main", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Domain.Entities.CardPlayCount", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("CardId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("card_id");
|
||||||
|
|
||||||
|
b.Property<long>("LastPlayedTime")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("last_played_time");
|
||||||
|
|
||||||
|
b.Property<long>("PlayCount")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("play_count");
|
||||||
|
|
||||||
|
b.HasKey("CardId");
|
||||||
|
|
||||||
|
b.ToTable("CardPlayCount", (string)null);
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
Infrastructure/Migrations/MigrationHelper.cs
Normal file
34
Infrastructure/Migrations/MigrationHelper.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
using System.Data;
|
||||||
|
using Infrastructure.Common;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
namespace Infrastructure.Migrations;
|
||||||
|
|
||||||
|
public static class MigrationHelper
|
||||||
|
{
|
||||||
|
public static bool Exists(string tableName)
|
||||||
|
{
|
||||||
|
var options = new DbContextOptionsBuilder().UseSqlite($"Data Source={GetConnectionString()}");
|
||||||
|
using var context = new DbContext(options.Options);
|
||||||
|
using var command = context.Database.GetDbConnection().CreateCommand();
|
||||||
|
command.CommandText = $"SELECT count(*) FROM sqlite_master WHERE type='table' AND name='{tableName}';";
|
||||||
|
command.CommandType = CommandType.Text;
|
||||||
|
context.Database.OpenConnection();
|
||||||
|
|
||||||
|
using var reader = command.ExecuteReader();
|
||||||
|
|
||||||
|
return reader.Read()? (long)reader[0] == 1 : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetConnectionString()
|
||||||
|
{
|
||||||
|
var builder = new ConfigurationBuilder()
|
||||||
|
.SetBasePath(PathHelper.ConfigurationPath)
|
||||||
|
.AddJsonFile("database.json", optional: false, reloadOnChange: false);
|
||||||
|
|
||||||
|
var cardDbName = builder.Build()["CardDbName"];
|
||||||
|
var cardDbPath = Path.Combine(PathHelper.DatabasePath, cardDbName);
|
||||||
|
return cardDbPath;
|
||||||
|
}
|
||||||
|
}
|
118
Infrastructure/Persistence/CardDbContext.cs
Normal file
118
Infrastructure/Persistence/CardDbContext.cs
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
using Application.Interfaces;
|
||||||
|
using Domain.Entities;
|
||||||
|
using Infrastructure.Common;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
namespace Infrastructure.Persistence;
|
||||||
|
|
||||||
|
public partial class CardDbContext : DbContext, ICardDbContext
|
||||||
|
{
|
||||||
|
public CardDbContext()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public CardDbContext(DbContextOptions<CardDbContext> options)
|
||||||
|
: base(options)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual DbSet<CardBdatum> CardBdata { get; set; } = null!;
|
||||||
|
|
||||||
|
public virtual DbSet<CardDetail> CardDetails { get; set; } = null!;
|
||||||
|
|
||||||
|
public virtual DbSet<CardMain> CardMains { get; set; } = null!;
|
||||||
|
|
||||||
|
public virtual DbSet<CardPlayCount> CardPlayCounts { get; set; } = null!;
|
||||||
|
|
||||||
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
|
{
|
||||||
|
if (optionsBuilder.IsConfigured)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultDb = Path.Combine(PathHelper.DatabasePath, "card.db3");
|
||||||
|
optionsBuilder.UseSqlite($"Data Source={defaultDb}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<CardBdatum>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => e.CardId);
|
||||||
|
|
||||||
|
entity.ToTable("card_bdata");
|
||||||
|
|
||||||
|
entity.Property(e => e.CardId)
|
||||||
|
.ValueGeneratedNever()
|
||||||
|
.HasColumnName("card_id");
|
||||||
|
entity.Property(e => e.Bdata).HasColumnName("bdata");
|
||||||
|
entity.Property(e => e.BdataSize).HasColumnName("bdata_size");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<CardDetail>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => new { e.CardId, e.Pcol1, e.Pcol2, e.Pcol3 });
|
||||||
|
|
||||||
|
entity.ToTable("card_detail");
|
||||||
|
|
||||||
|
entity.Property(e => e.CardId).HasColumnName("card_id");
|
||||||
|
entity.Property(e => e.Pcol1).HasColumnName("pcol1");
|
||||||
|
entity.Property(e => e.Pcol2).HasColumnName("pcol2");
|
||||||
|
entity.Property(e => e.Pcol3).HasColumnName("pcol3");
|
||||||
|
entity.Property(e => e.Fcol1).HasColumnName("fcol1");
|
||||||
|
entity.Property(e => e.Fcol2).HasColumnName("fcol2");
|
||||||
|
entity.Property(e => e.Fcol3).HasColumnName("fcol3");
|
||||||
|
entity.Property(e => e.LastPlayTenpoId).HasColumnName("last_play_tenpo_id").IsRequired(false);
|
||||||
|
entity.Property(e => e.LastPlayTime).HasColumnName("last_play_time")
|
||||||
|
.HasConversion<DateTimeToTicksConverter>();
|
||||||
|
entity.Property(e => e.ScoreBi1).HasColumnName("score_bi1");
|
||||||
|
entity.Property(e => e.ScoreI1).HasColumnName("score_i1");
|
||||||
|
entity.Property(e => e.ScoreUi1).HasColumnName("score_ui1");
|
||||||
|
entity.Property(e => e.ScoreUi2).HasColumnName("score_ui2");
|
||||||
|
entity.Property(e => e.ScoreUi3).HasColumnName("score_ui3");
|
||||||
|
entity.Property(e => e.ScoreUi4).HasColumnName("score_ui4");
|
||||||
|
entity.Property(e => e.ScoreUi5).HasColumnName("score_ui5");
|
||||||
|
entity.Property(e => e.ScoreUi6).HasColumnName("score_ui6");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<CardMain>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => e.CardId);
|
||||||
|
|
||||||
|
entity.ToTable("card_main");
|
||||||
|
|
||||||
|
entity.Property(e => e.CardId)
|
||||||
|
.ValueGeneratedNever()
|
||||||
|
.HasColumnName("card_id");
|
||||||
|
entity.Property(e => e.AchieveStatus).HasColumnName("achieve_status");
|
||||||
|
entity.Property(e => e.Created).HasColumnName("created").IsRequired(false);
|
||||||
|
entity.Property(e => e.Fcol1).HasColumnName("fcol1");
|
||||||
|
entity.Property(e => e.Fcol2).HasColumnName("fcol2");
|
||||||
|
entity.Property(e => e.Fcol3).HasColumnName("fcol3");
|
||||||
|
entity.Property(e => e.Modified).HasColumnName("modified").IsRequired(false);
|
||||||
|
entity.Property(e => e.PlayerName).HasColumnName("player_name");
|
||||||
|
entity.Property(e => e.ScoreI1).HasColumnName("score_i1");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<CardPlayCount>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => e.CardId);
|
||||||
|
|
||||||
|
entity.ToTable("CardPlayCount");
|
||||||
|
|
||||||
|
entity.Property(e => e.CardId)
|
||||||
|
.ValueGeneratedNever()
|
||||||
|
.HasColumnName("card_id");
|
||||||
|
entity.Property(e => e.LastPlayedTime).HasColumnName("last_played_time")
|
||||||
|
.HasConversion<DateTimeToTicksConverter>();
|
||||||
|
entity.Property(e => e.PlayCount).HasColumnName("play_count");
|
||||||
|
});
|
||||||
|
|
||||||
|
OnModelCreatingPartial(modelBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
|
||||||
|
}
|
82
Infrastructure/Persistence/MusicDbContext.cs
Normal file
82
Infrastructure/Persistence/MusicDbContext.cs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
using Application.Interfaces;
|
||||||
|
using Domain.Entities;
|
||||||
|
using Infrastructure.Common;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Infrastructure.Persistence;
|
||||||
|
|
||||||
|
public partial class MusicDbContext : DbContext, IMusicDbContext
|
||||||
|
{
|
||||||
|
public MusicDbContext()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public MusicDbContext(DbContextOptions<MusicDbContext> options)
|
||||||
|
: base(options)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual DbSet<MusicAou> MusicAous { get; set; } = null!;
|
||||||
|
|
||||||
|
public virtual DbSet<MusicExtra> MusicExtras { get; set; } = null!;
|
||||||
|
|
||||||
|
public virtual DbSet<MusicUnlock> MusicUnlocks { get; set; } = null!;
|
||||||
|
|
||||||
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
|
{
|
||||||
|
if (optionsBuilder.IsConfigured)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultDb = Path.Combine(PathHelper.DatabasePath, "music471omni.db3");
|
||||||
|
optionsBuilder.UseSqlite($"Data Source={defaultDb}");
|
||||||
|
}
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<MusicAou>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => e.MusicId);
|
||||||
|
|
||||||
|
entity.ToTable("music_aou");
|
||||||
|
|
||||||
|
entity.Property(e => e.MusicId)
|
||||||
|
.ValueGeneratedNever()
|
||||||
|
.HasColumnName("music_id");
|
||||||
|
entity.Property(e => e.UseFlag).HasColumnName("use_flag");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<MusicExtra>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => e.MusicId);
|
||||||
|
|
||||||
|
entity.ToTable("music_extra");
|
||||||
|
|
||||||
|
entity.Property(e => e.MusicId)
|
||||||
|
.ValueGeneratedNever()
|
||||||
|
.HasColumnName("music_id");
|
||||||
|
entity.Property(e => e.UseFlag).HasColumnName("use_flag");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<MusicUnlock>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => e.MusicId);
|
||||||
|
|
||||||
|
entity.ToTable("music_unlock");
|
||||||
|
|
||||||
|
entity.Property(e => e.MusicId)
|
||||||
|
.ValueGeneratedNever()
|
||||||
|
.HasColumnName("music_id");
|
||||||
|
entity.Property(e => e.Artist).HasColumnName("artist");
|
||||||
|
entity.Property(e => e.Title).HasColumnName("title");
|
||||||
|
entity.Property(e => e.ReleaseDate).HasColumnName("release_date");
|
||||||
|
entity.Property(e => e.EndDate).HasColumnName("end_date");
|
||||||
|
entity.Property(e => e.NewFlag).HasColumnName("new_flag");
|
||||||
|
entity.Property(e => e.UseFlag).HasColumnName("use_flag");
|
||||||
|
});
|
||||||
|
|
||||||
|
OnModelCreatingPartial(modelBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
|
||||||
|
}
|
160
Infrastructure/Services/EventManagerService.cs
Normal file
160
Infrastructure/Services/EventManagerService.cs
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
using System.Security.Cryptography;
|
||||||
|
using Application.Interfaces;
|
||||||
|
using Domain.Config;
|
||||||
|
using Domain.Models;
|
||||||
|
using Infrastructure.Exceptions;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace Infrastructure.Services;
|
||||||
|
|
||||||
|
public class EventManagerService : IEventManagerService
|
||||||
|
{
|
||||||
|
#region Constants
|
||||||
|
|
||||||
|
private const string WWWROOT = "wwwroot";
|
||||||
|
|
||||||
|
private const string EVENT_FOLDER = "events";
|
||||||
|
|
||||||
|
private static readonly DateTimeOffset NOT_BEFORE = new(new DateTime(2013, 1, 1));
|
||||||
|
|
||||||
|
private static readonly DateTimeOffset NOT_AFTER = NOT_BEFORE + new TimeSpan(360 * 20, 0, 0, 0);
|
||||||
|
|
||||||
|
private static readonly string NOT_BEFORE_STRING = NOT_BEFORE.ToUnixTimeSeconds().ToString();
|
||||||
|
|
||||||
|
private static readonly string NOT_AFTER_STRING = NOT_AFTER.ToUnixTimeSeconds().ToString();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private readonly ILogger<EventManagerService> logger;
|
||||||
|
|
||||||
|
private readonly EventConfig config;
|
||||||
|
|
||||||
|
private readonly List<Event> events;
|
||||||
|
|
||||||
|
private readonly bool useEvents;
|
||||||
|
|
||||||
|
public EventManagerService(IOptions<EventConfig> config, ILogger<EventManagerService> logger)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
this.config = config.Value;
|
||||||
|
events = new List<Event>();
|
||||||
|
useEvents = this.config.UseEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool UseEvents()
|
||||||
|
{
|
||||||
|
return useEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Event> GetEvents()
|
||||||
|
{
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitializeEvents()
|
||||||
|
{
|
||||||
|
foreach (var eventData in config.EventFiles)
|
||||||
|
{
|
||||||
|
var filePath = Path.Combine(WWWROOT, EVENT_FOLDER, eventData.FileName);
|
||||||
|
if (!File.Exists(filePath))
|
||||||
|
{
|
||||||
|
logger.LogError("Event file {File} not found at path {Path}!", eventData.FileName,
|
||||||
|
filePath);
|
||||||
|
throw new EventFileNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var md5 = ComputeFileMd5(filePath);
|
||||||
|
var @event = new Event
|
||||||
|
{
|
||||||
|
Name = eventData.FileName,
|
||||||
|
Md5 = md5,
|
||||||
|
NotBefore = NOT_BEFORE_STRING,
|
||||||
|
NotAfter = NOT_AFTER_STRING
|
||||||
|
};
|
||||||
|
|
||||||
|
var eventType = DetermineFileType(eventData.FileName);
|
||||||
|
@event.Index = eventType switch
|
||||||
|
{
|
||||||
|
EventFileType.Event => 0,
|
||||||
|
EventFileType.EventRegPic => 1,
|
||||||
|
EventFileType.EventSgRegPic => 2,
|
||||||
|
EventFileType.NewsBigPic => eventData.Index,
|
||||||
|
EventFileType.NewsSmallPic => 1,
|
||||||
|
EventFileType.Telop => 0,
|
||||||
|
EventFileType.EventCmp => 8,
|
||||||
|
_ => throw new ArgumentOutOfRangeException()
|
||||||
|
};
|
||||||
|
events.Add(@event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (events.Exists(event1 => event1.Name.StartsWith("news_big_") && event1.Index == 0))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogWarning("No big news image with index 0! Changing a random one...");
|
||||||
|
events.First(event1 => event1.Name.StartsWith("news_big_")).Index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private EventFileType DetermineFileType(string fileName)
|
||||||
|
{
|
||||||
|
if (fileName.EndsWith(".evt"))
|
||||||
|
{
|
||||||
|
return EventFileType.Event;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileName.EndsWith(".cmp"))
|
||||||
|
{
|
||||||
|
return EventFileType.EventCmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileName.EndsWith(".txt"))
|
||||||
|
{
|
||||||
|
return EventFileType.Telop;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileName.Contains("_reg"))
|
||||||
|
{
|
||||||
|
return EventFileType.EventRegPic;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileName.Contains("_sgreg"))
|
||||||
|
{
|
||||||
|
return EventFileType.EventSgRegPic;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileName.StartsWith("news_big_"))
|
||||||
|
{
|
||||||
|
return EventFileType.NewsBigPic;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileName.StartsWith("news_small_"))
|
||||||
|
{
|
||||||
|
return EventFileType.NewsSmallPic;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogError("Unknown event file type for file {File}", fileName);
|
||||||
|
throw new EventFileTypeUnknownException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ComputeFileMd5(string filePath)
|
||||||
|
{
|
||||||
|
using var file = File.OpenRead(filePath);
|
||||||
|
var hash = MD5.HashData(file);
|
||||||
|
var result = BitConverter.ToString(hash).Replace("-", "").ToLower();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum EventFileType
|
||||||
|
{
|
||||||
|
Event,
|
||||||
|
EventRegPic,
|
||||||
|
EventSgRegPic,
|
||||||
|
NewsBigPic,
|
||||||
|
NewsSmallPic,
|
||||||
|
Telop,
|
||||||
|
EventCmp
|
||||||
|
}
|
||||||
|
}
|
442
MainServer/.gitignore
vendored
Normal file
442
MainServer/.gitignore
vendored
Normal file
@ -0,0 +1,442 @@
|
|||||||
|
### JetBrains template
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/artifacts
|
||||||
|
# .idea/compiler.xml
|
||||||
|
# .idea/jarRepositories.xml
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
|
### VisualStudio template
|
||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.rsuser
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Mono auto generated files
|
||||||
|
mono_crash.*
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
[Ww][Ii][Nn]32/
|
||||||
|
[Aa][Rr][Mm]/
|
||||||
|
[Aa][Rr][Mm]64/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
[Ll]ogs/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUnit
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
nunit-*.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# ASP.NET Scaffolding
|
||||||
|
ScaffoldingReadMe.txt
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_h.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*_wpftmp.csproj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Coverlet is a free, cross platform Code Coverage Tool
|
||||||
|
coverage*.json
|
||||||
|
coverage*.xml
|
||||||
|
coverage*.info
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# NuGet Symbol Packages
|
||||||
|
*.snupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
*.appxbundle
|
||||||
|
*.appxupload
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!?*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
*- [Bb]ackup.rdl
|
||||||
|
*- [Bb]ackup ([0-9]).rdl
|
||||||
|
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
.cr/personal
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
.localhistory/
|
||||||
|
|
||||||
|
# BeatPulse healthcheck temp database
|
||||||
|
healthchecksdb
|
||||||
|
|
||||||
|
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||||
|
MigrationBackup/
|
||||||
|
|
||||||
|
# Ionide (cross platform F# VS Code tools) working folder
|
||||||
|
.ionide/
|
||||||
|
|
||||||
|
# Fody - auto-generated XML schema
|
||||||
|
FodyWeavers.xsd
|
||||||
|
|
||||||
|
# Ignore card db since we should start from scratch
|
||||||
|
/db/card.db3
|
||||||
|
Certificates
|
||||||
|
wwwroot/events/
|
||||||
|
Database/*
|
4
MainServer/Configurations/database.json
Normal file
4
MainServer/Configurations/database.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"CardDbName": "card.db3",
|
||||||
|
"MusicDbName": "music471omni.db3"
|
||||||
|
}
|
39
MainServer/Configurations/events.json
Normal file
39
MainServer/Configurations/events.json
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"Events": {
|
||||||
|
"UseEvents": true,
|
||||||
|
"EventFiles": [
|
||||||
|
{
|
||||||
|
"FileName": "event_103_20201125.evt",
|
||||||
|
"Index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"FileName": "event_20201125_reg.jpg",
|
||||||
|
"Index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"FileName": "event_20201125_sgreg.png",
|
||||||
|
"Index": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"FileName": "news_big_20201125_0.jpg",
|
||||||
|
"Index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"FileName": "news_big_20201125_2.jpg",
|
||||||
|
"Index": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"FileName": "news_small_20201125_1.jpg",
|
||||||
|
"Index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"FileName": "telop_20201125.txt",
|
||||||
|
"Index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"FileName": "event_unlock_20201125.cmp",
|
||||||
|
"Index": 8
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
15
MainServer/Configurations/game.json
Normal file
15
MainServer/Configurations/game.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"AvatarCount": 356,
|
||||||
|
"NavigatorCount": 118,
|
||||||
|
"ItemCount": 21,
|
||||||
|
"SkinCount": 21,
|
||||||
|
"SeCount": 26,
|
||||||
|
"TitleCount": 5530,
|
||||||
|
"UnlockableSongIds":
|
||||||
|
[
|
||||||
|
11, 13, 149, 273, 291, 320, 321, 371, 378, 384, 464, 471, 474, 475, 492,
|
||||||
|
494, 498, 520, 548, 551, 558, 561, 565, 570, 577, 583, 612, 615, 622,
|
||||||
|
632, 659, 666, 668, 670, 672, 676, 680, 682, 685, 686, 697, 700, 701,
|
||||||
|
711, 720, 749, 875, 876, 877
|
||||||
|
]
|
||||||
|
}
|
27
MainServer/Configurations/logging.json
Normal file
27
MainServer/Configurations/logging.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"Serilog": {
|
||||||
|
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
|
||||||
|
"MinimumLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Override": {
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.AspNetCore": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Filter": [
|
||||||
|
{
|
||||||
|
"Name": "ByExcluding",
|
||||||
|
"Args": {
|
||||||
|
"expression": "@mt = 'An unhandled exception has occurred while executing the request.'"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"WriteTo": [
|
||||||
|
{
|
||||||
|
"Name": "File",
|
||||||
|
"Args": { "path": "./Logs/log-.txt", "rollingInterval": "Day" }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
6
MainServer/Configurations/matching.json
Normal file
6
MainServer/Configurations/matching.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"Relay": {
|
||||||
|
"RelayServer": "127.0.0.1",
|
||||||
|
"RelayPort": 3333
|
||||||
|
}
|
||||||
|
}
|
3
MainServer/Configurations/server.json
Normal file
3
MainServer/Configurations/server.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"ServerIp": "127.0.0.1"
|
||||||
|
}
|
24
MainServer/Controllers/API/TestController.cs
Normal file
24
MainServer/Controllers/API/TestController.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using Domain.Entities;
|
||||||
|
using Infrastructure.Persistence;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace MainServer.Controllers.API
|
||||||
|
{
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class TestController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly MusicDbContext context;
|
||||||
|
|
||||||
|
public TestController(MusicDbContext context)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public MusicUnlock GetOne()
|
||||||
|
{
|
||||||
|
return context.MusicUnlocks.First();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
MainServer/Controllers/BaseController.cs
Normal file
15
MainServer/Controllers/BaseController.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using MediatR;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace MainServer.Controllers;
|
||||||
|
|
||||||
|
public abstract class BaseController<T> : ControllerBase where T : BaseController<T>
|
||||||
|
{
|
||||||
|
private ILogger<T>? logger;
|
||||||
|
|
||||||
|
private ISender? mediator;
|
||||||
|
|
||||||
|
protected ISender Mediator => (mediator ??= HttpContext.RequestServices.GetService<ISender>()) ?? throw new InvalidOperationException();
|
||||||
|
|
||||||
|
protected ILogger<T> Logger => (logger ??= HttpContext.RequestServices.GetService<ILogger<T>>()) ?? throw new InvalidOperationException();
|
||||||
|
}
|
25
MainServer/Controllers/Game/AliveController.cs
Normal file
25
MainServer/Controllers/Game/AliveController.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace MainServer.Controllers.Game;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("alive")]
|
||||||
|
public class AliveController : ControllerBase
|
||||||
|
{
|
||||||
|
[HttpGet("i.php")]
|
||||||
|
public IActionResult AliveCheck()
|
||||||
|
{
|
||||||
|
var remoteIpAddress = Request.HttpContext.Connection.RemoteIpAddress;
|
||||||
|
var serverIpAddress = Request.HttpContext.Connection.LocalIpAddress;
|
||||||
|
var response = $"REMOTE ADDRESS:{remoteIpAddress}\n" +
|
||||||
|
"SERVER NAME:GCLocalServer\n" +
|
||||||
|
$"SERVER ADDR:{serverIpAddress}";
|
||||||
|
return Ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("/{id}/Alive.txt")]
|
||||||
|
public IActionResult GetAliveFile()
|
||||||
|
{
|
||||||
|
return Ok("");
|
||||||
|
}
|
||||||
|
}
|
22
MainServer/Controllers/Game/IncomController.cs
Normal file
22
MainServer/Controllers/Game/IncomController.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace MainServer.Controllers.Game;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("service/incom")]
|
||||||
|
public class IncomController : ControllerBase
|
||||||
|
{
|
||||||
|
private const string INCOM_RESPONSE = "1+1";
|
||||||
|
|
||||||
|
[HttpPost("incom.php")]
|
||||||
|
public IActionResult Incom()
|
||||||
|
{
|
||||||
|
return Ok(INCOM_RESPONSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("incomALL.php")]
|
||||||
|
public IActionResult IncomAll()
|
||||||
|
{
|
||||||
|
return Ok(INCOM_RESPONSE);
|
||||||
|
}
|
||||||
|
}
|
14
MainServer/Controllers/Game/ResponeController.cs
Normal file
14
MainServer/Controllers/Game/ResponeController.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace MainServer.Controllers.Game;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("service/respone")]
|
||||||
|
public class ResponeController : ControllerBase
|
||||||
|
{
|
||||||
|
[HttpPost("respone.php")]
|
||||||
|
public IActionResult Respone()
|
||||||
|
{
|
||||||
|
return Ok("1");
|
||||||
|
}
|
||||||
|
}
|
41
MainServer/Controllers/Game/ServerController.cs
Normal file
41
MainServer/Controllers/Game/ServerController.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using Application.Game.Server;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace MainServer.Controllers.Game;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("server")]
|
||||||
|
public class ServerController : BaseController<ServerController>
|
||||||
|
{
|
||||||
|
[HttpGet("cursel.php")]
|
||||||
|
public ActionResult<string> GetCursel()
|
||||||
|
{
|
||||||
|
return Ok("1\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("gameinfo.php")]
|
||||||
|
public ActionResult<string> GetGameInfo()
|
||||||
|
{
|
||||||
|
return Ok("0\n" +
|
||||||
|
"3\n" +
|
||||||
|
"301000,test1\n" +
|
||||||
|
"302000,test2\n" +
|
||||||
|
"303000,test3");
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("certify.php")]
|
||||||
|
public async Task<ActionResult<string>> Certify(string? gid, string? mac,
|
||||||
|
[FromQuery(Name = "r")]string? random, [FromQuery(Name = "md")]string? md5)
|
||||||
|
{
|
||||||
|
var host = Request.Host.Value;
|
||||||
|
var command = new CertifyCommand(gid, mac, random, md5, host);
|
||||||
|
return Ok(await Mediator.Send(command));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("data.php")]
|
||||||
|
public async Task<ActionResult<string>> GetData()
|
||||||
|
{
|
||||||
|
var query = new GetDataQuery(Request.Host.Value);
|
||||||
|
return Ok(await Mediator.Send(query));
|
||||||
|
}
|
||||||
|
}
|
19
MainServer/Controllers/Game/ServiceOptionController.cs
Normal file
19
MainServer/Controllers/Game/ServiceOptionController.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using Application.Game.Option;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace MainServer.Controllers.Game;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("service/option")]
|
||||||
|
public class ServiceOptionController : BaseController<ServiceOptionController>
|
||||||
|
{
|
||||||
|
[HttpGet("PlayInfo.php")]
|
||||||
|
public async Task<ActionResult<string>> GetPlayCount([FromQuery(Name = "card_id")] long cardId)
|
||||||
|
{
|
||||||
|
var query = new PlayCountQuery(cardId);
|
||||||
|
var count = await Mediator.Send(query);
|
||||||
|
|
||||||
|
return Ok("1\n" +
|
||||||
|
$"{count}");
|
||||||
|
}
|
||||||
|
}
|
15
MainServer/Controllers/Game/UpdateController.cs
Normal file
15
MainServer/Controllers/Game/UpdateController.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace MainServer.Controllers.Game;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("update/cgi")]
|
||||||
|
public class UpdateController : ControllerBase
|
||||||
|
{
|
||||||
|
// TODO: Check update properly
|
||||||
|
[HttpGet("check.php")]
|
||||||
|
public IActionResult UpdateCheck()
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
}
|
16
MainServer/Controllers/Game/UploadController.cs
Normal file
16
MainServer/Controllers/Game/UploadController.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace MainServer.Controllers.Game;
|
||||||
|
[ApiController]
|
||||||
|
[Route("service/upload")]
|
||||||
|
public class UploadController : ControllerBase
|
||||||
|
{
|
||||||
|
private const string UPLOAD_RESPONSE = "1\n" +
|
||||||
|
"OK";
|
||||||
|
|
||||||
|
[HttpPost("upload.php")]
|
||||||
|
public IActionResult Upload()
|
||||||
|
{
|
||||||
|
return Ok(UPLOAD_RESPONSE);
|
||||||
|
}
|
||||||
|
}
|
48
MainServer/MainServer.csproj
Normal file
48
MainServer/MainServer.csproj
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<LangVersion>11</LangVersion>
|
||||||
|
<Version>3.0.0-alpha</Version>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CertificateManager" Version="1.0.8" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.2" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.2">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.3" />
|
||||||
|
<PackageReference Include="Serilog.AspNetCore" Version="6.1.1-dev-00293" />
|
||||||
|
<PackageReference Include="Serilog.Expressions" Version="3.4.2-dev-00119" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||||
|
<PackageReference Include="System.Security.Cryptography.X509Certificates" Version="4.3.2" />
|
||||||
|
<PackageReference Include="Throw" Version="1.3.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Remove="Configurations\database.json" />
|
||||||
|
<None Include="Configurations\database.json" />
|
||||||
|
<Content Remove="Configurations\events.json" />
|
||||||
|
<None Include="Configurations\events.json" />
|
||||||
|
<Content Remove="Configurations\game.json" />
|
||||||
|
<None Include="Configurations\game.json" />
|
||||||
|
<Content Remove="Configurations\logging.json" />
|
||||||
|
<None Include="Configurations\logging.json" />
|
||||||
|
<Content Remove="Configurations\matching.json" />
|
||||||
|
<None Include="Configurations\matching.json" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Controllers\API" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
103
MainServer/Program.cs
Normal file
103
MainServer/Program.cs
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using Application;
|
||||||
|
using Application.Interfaces;
|
||||||
|
using Domain.Config;
|
||||||
|
using Infrastructure;
|
||||||
|
using Infrastructure.Common;
|
||||||
|
using Infrastructure.Persistence;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Extensions.Logging;
|
||||||
|
using Throw;
|
||||||
|
|
||||||
|
Log.Logger = new LoggerConfiguration()
|
||||||
|
.WriteTo.Console()
|
||||||
|
.CreateBootstrapLogger();
|
||||||
|
|
||||||
|
var version = Assembly.GetEntryAssembly()?.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?
|
||||||
|
.InformationalVersion;
|
||||||
|
Log.Information("GCLocalServer version {Version}", version);
|
||||||
|
|
||||||
|
Log.Information("Server starting up...");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
// Add services to the container.
|
||||||
|
const string configurationsDirectory = "Configurations";
|
||||||
|
builder.Configuration
|
||||||
|
.AddJsonFile($"{configurationsDirectory}/database.json", optional: false, reloadOnChange: false)
|
||||||
|
.AddJsonFile($"{configurationsDirectory}/game.json", optional: false, reloadOnChange: false)
|
||||||
|
.AddJsonFile($"{configurationsDirectory}/logging.json", optional: false, reloadOnChange: false)
|
||||||
|
.AddJsonFile($"{configurationsDirectory}/events.json", optional: true, reloadOnChange: false)
|
||||||
|
.AddJsonFile($"{configurationsDirectory}/matching.json", optional: true, reloadOnChange: false)
|
||||||
|
.AddJsonFile($"{configurationsDirectory}/server.json", optional: true, reloadOnChange: false);
|
||||||
|
|
||||||
|
builder.Services.Configure<EventConfig>(
|
||||||
|
builder.Configuration.GetSection(EventConfig.EVENT_SECTION));
|
||||||
|
builder.Services.Configure<RelayConfig>(
|
||||||
|
builder.Configuration.GetSection(RelayConfig.RELAY_SECTION));
|
||||||
|
|
||||||
|
var serverIp = builder.Configuration["ServerIp"] ?? "127.0.0.1";
|
||||||
|
var certificateManager = new CertificateService(serverIp, new SerilogLoggerFactory(Log.Logger).CreateLogger(""));
|
||||||
|
builder.WebHost.ConfigureKestrel(options =>
|
||||||
|
options.ConfigureHttpsDefaults(adapterOptions =>
|
||||||
|
adapterOptions.ServerCertificate = certificateManager.InitializeCertificate()
|
||||||
|
));
|
||||||
|
|
||||||
|
builder.Host.UseSerilog((context, configuration) =>
|
||||||
|
{
|
||||||
|
configuration.WriteTo.Console().ReadFrom.Configuration(context.Configuration);
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddControllers().AddXmlSerializerFormatters();
|
||||||
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddSwaggerGen();
|
||||||
|
|
||||||
|
builder.Services.AddApplication();
|
||||||
|
builder.Services.AddInfrastructure(builder.Configuration);
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
using (var scope = app.Services.CreateScope())
|
||||||
|
{
|
||||||
|
var db = scope.ServiceProvider.GetRequiredService<CardDbContext>();
|
||||||
|
db.Database.Migrate();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseSerilogRequestLogging(options =>
|
||||||
|
{
|
||||||
|
options.MessageTemplate = "HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms, " +
|
||||||
|
"request host: {RequestHost}";
|
||||||
|
options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
|
||||||
|
{
|
||||||
|
diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
var eventService = app.Services.GetService<IEventManagerService>();
|
||||||
|
eventService.ThrowIfNull();
|
||||||
|
eventService.InitializeEvents();
|
||||||
|
|
||||||
|
// Configure the HTTP request pipeline.
|
||||||
|
if (app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.MapControllers();
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Fatal(ex, "Unhandled exception");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Log.Information("Shut down complete");
|
||||||
|
Log.CloseAndFlush();
|
||||||
|
}
|
31
MainServer/Properties/launchSettings.json
Normal file
31
MainServer/Properties/launchSettings.json
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:55391",
|
||||||
|
"sslPort": 44317
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"MainServer": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"applicationUrl": "https://localhost:7114;http://localhost:5107",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
79
MainServer/app.manifest
Normal file
79
MainServer/app.manifest
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<!-- UAC 清单选项
|
||||||
|
如果想要更改 Windows 用户帐户控制级别,请使用
|
||||||
|
以下节点之一替换 requestedExecutionLevel 节点。
|
||||||
|
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||||
|
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||||
|
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
|
||||||
|
|
||||||
|
指定 requestedExecutionLevel 元素将禁用文件和注册表虚拟化。
|
||||||
|
如果你的应用程序需要此虚拟化来实现向后兼容性,则移除此
|
||||||
|
元素。
|
||||||
|
-->
|
||||||
|
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||||
|
</requestedPrivileges>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<!-- 设计此应用程序与其一起工作且已针对此应用程序进行测试的
|
||||||
|
Windows 版本的列表。取消评论适当的元素,
|
||||||
|
Windows 将自动选择最兼容的环境。 -->
|
||||||
|
|
||||||
|
<!-- Windows Vista -->
|
||||||
|
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
|
||||||
|
|
||||||
|
<!-- Windows 7 -->
|
||||||
|
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
|
||||||
|
|
||||||
|
<!-- Windows 8 -->
|
||||||
|
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
|
||||||
|
|
||||||
|
<!-- Windows 8.1 -->
|
||||||
|
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
|
||||||
|
|
||||||
|
<!-- Windows 10 -->
|
||||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||||
|
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
|
||||||
|
<!-- 指示该应用程序可感知 DPI 且 Windows 在 DPI 较高时将不会对其进行
|
||||||
|
自动缩放。Windows Presentation Foundation (WPF)应用程序自动感知 DPI,无需
|
||||||
|
选择加入。选择加入此设置的 Windows 窗体应用程序(面向 .NET Framework 4.6)还应
|
||||||
|
在其 app.config 中将 "EnableWindowsFormsHighDpiAutoResizing" 设置设置为 "true"。
|
||||||
|
|
||||||
|
将应用程序设为感知长路径。请参阅 https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
|
||||||
|
<!--
|
||||||
|
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<windowsSettings>
|
||||||
|
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
||||||
|
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
|
||||||
|
</windowsSettings>
|
||||||
|
</application>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- 启用 Windows 公共控件和对话框的主题(Windows XP 和更高版本) -->
|
||||||
|
<!--
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity
|
||||||
|
type="win32"
|
||||||
|
name="Microsoft.Windows.Common-Controls"
|
||||||
|
version="6.0.0.0"
|
||||||
|
processorArchitecture="*"
|
||||||
|
publicKeyToken="6595b64144ccf1df"
|
||||||
|
language="*"
|
||||||
|
/>
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
|
-->
|
||||||
|
|
||||||
|
</assembly>
|
3
MainServer/appsettings.json
Normal file
3
MainServer/appsettings.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user