Added uploading of saves
parent
676fa8e48b
commit
32d6e109df
|
@ -351,3 +351,4 @@ MigrationBackup/
|
|||
Upload/
|
||||
LANCommander/Icon/
|
||||
LANCommander/Settings.yml
|
||||
LANCommander/Saves/
|
||||
|
|
|
@ -12,6 +12,7 @@ using System.Net.NetworkInformation;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media.Converters;
|
||||
using YamlDotNet.Core;
|
||||
|
||||
namespace LANCommander.PlaynitePlugin
|
||||
{
|
||||
|
@ -170,6 +171,28 @@ namespace LANCommander.PlaynitePlugin
|
|||
return DownloadRequest($"/api/Archives/Download/{id}", progressHandler, completeHandler);
|
||||
}
|
||||
|
||||
public string DownloadSave(Guid id, Action<DownloadProgressChangedEventArgs> progressHandler, Action<AsyncCompletedEventArgs> completeHandler)
|
||||
{
|
||||
return DownloadRequest($"/api/Saves/Download/{id}", progressHandler, completeHandler);
|
||||
}
|
||||
|
||||
public string DownloadLatestSave(Guid gameId, Action<DownloadProgressChangedEventArgs> progressHandler, Action<AsyncCompletedEventArgs> completeHandler)
|
||||
{
|
||||
return DownloadRequest($"/api/Saves/DownloadLatest/{gameId}", progressHandler, completeHandler);
|
||||
}
|
||||
|
||||
public GameSave UploadSave(string gameId, byte[] data)
|
||||
{
|
||||
var request = new RestRequest($"/api/Saves/Upload/{gameId}", Method.POST)
|
||||
.AddHeader("Authorization", $"Bearer {Token.AccessToken}");
|
||||
|
||||
request.AddFile(gameId, data, gameId);
|
||||
|
||||
var response = Client.Post<GameSave>(request);
|
||||
|
||||
return response.Data;
|
||||
}
|
||||
|
||||
public string GetKey(Guid id)
|
||||
{
|
||||
var macAddress = GetMacAddress();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using LANCommander.PlaynitePlugin.Extensions;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using LANCommander.PlaynitePlugin.Extensions;
|
||||
using LANCommander.SDK;
|
||||
using Playnite.SDK;
|
||||
using Playnite.SDK.Events;
|
||||
|
@ -175,6 +176,7 @@ namespace LANCommander.PlaynitePlugin
|
|||
{
|
||||
var nameChangeScriptPath = PowerShellRuntime.GetScriptFilePath(args.Games.First(), SDK.Enums.ScriptType.NameChange);
|
||||
var keyChangeScriptPath = PowerShellRuntime.GetScriptFilePath(args.Games.First(), SDK.Enums.ScriptType.KeyChange);
|
||||
var installScriptPath = PowerShellRuntime.GetScriptFilePath(args.Games.First(), SDK.Enums.ScriptType.Install);
|
||||
|
||||
if (File.Exists(nameChangeScriptPath))
|
||||
yield return new GameMenuItem
|
||||
|
@ -215,6 +217,25 @@ namespace LANCommander.PlaynitePlugin
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (File.Exists(installScriptPath))
|
||||
yield return new GameMenuItem
|
||||
{
|
||||
Description = "Run Install Script",
|
||||
Action = (installArgs) =>
|
||||
{
|
||||
Guid gameId;
|
||||
|
||||
if (Guid.TryParse(installArgs.Games.First().GameId, out gameId))
|
||||
{
|
||||
PowerShellRuntime.RunScript(installArgs.Games.First(), SDK.Enums.ScriptType.Install);
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayniteApi.Dialogs.ShowErrorMessage("This game could not be found on the server. Your game may be corrupted.");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -246,6 +267,77 @@ namespace LANCommander.PlaynitePlugin
|
|||
};
|
||||
}
|
||||
|
||||
public override void OnGameStopped(OnGameStoppedEventArgs args)
|
||||
{
|
||||
var manifestPath = Path.Combine(args.Game.InstallDirectory, "_manifest.yml");
|
||||
|
||||
if (File.Exists(manifestPath))
|
||||
{
|
||||
var deserializer = new DeserializerBuilder()
|
||||
.WithNamingConvention(new PascalCaseNamingConvention())
|
||||
.Build();
|
||||
|
||||
var manifest = deserializer.Deserialize<GameManifest>(File.ReadAllText(manifestPath));
|
||||
var temp = Path.GetTempFileName();
|
||||
|
||||
using (ZipOutputStream zipStream = new ZipOutputStream(File.Create(temp)))
|
||||
{
|
||||
zipStream.SetLevel(5);
|
||||
|
||||
foreach (var savePath in manifest.SavePaths)
|
||||
{
|
||||
savePath.Path = savePath.Path.Replace('/', '\\').Replace("{InstallDir}", args.Game.InstallDirectory);
|
||||
|
||||
if (Directory.Exists(savePath.Path))
|
||||
{
|
||||
AddDirectoryToZip(zipStream, savePath.Path);
|
||||
}
|
||||
else if (File.Exists(savePath.Path))
|
||||
{
|
||||
var entry = new ZipEntry(Path.Combine(savePath.Id.ToString(), Path.GetFileName(savePath.Path)));
|
||||
|
||||
zipStream.PutNextEntry(entry);
|
||||
|
||||
byte[] buffer = File.ReadAllBytes(savePath.Path);
|
||||
|
||||
zipStream.Write(buffer, 0, buffer.Length);
|
||||
zipStream.CloseEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var save = LANCommander.UploadSave(args.Game.GameId, File.ReadAllBytes(temp));
|
||||
|
||||
File.Delete(temp);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddDirectoryToZip(ZipOutputStream zipStream, string path)
|
||||
{
|
||||
foreach (var file in Directory.GetFiles(path))
|
||||
{
|
||||
var entry = new ZipEntry(Path.GetFileName(file));
|
||||
|
||||
zipStream.PutNextEntry(entry);
|
||||
|
||||
byte[] buffer = File.ReadAllBytes(file);
|
||||
|
||||
zipStream.Write(buffer, 0, buffer.Length);
|
||||
|
||||
zipStream.CloseEntry();
|
||||
}
|
||||
|
||||
foreach (var child in Directory.GetDirectories(path))
|
||||
{
|
||||
ZipEntry entry = new ZipEntry(Path.GetFileName(path));
|
||||
|
||||
zipStream.PutNextEntry(entry);
|
||||
zipStream.CloseEntry();
|
||||
|
||||
AddDirectoryToZip(zipStream, child);
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<TopPanelItem> GetTopPanelItems()
|
||||
{
|
||||
yield return new TopPanelItem
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace LANCommander.SDK.Enums
|
||||
{
|
||||
public enum SavePathType
|
||||
{
|
||||
File,
|
||||
Registry
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using LANCommander.SDK.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LANCommander.SDK
|
||||
|
@ -20,6 +21,7 @@ namespace LANCommander.SDK
|
|||
public MultiplayerInfo LocalMultiplayer { get; set; }
|
||||
public MultiplayerInfo LanMultiplayer { get; set; }
|
||||
public MultiplayerInfo OnlineMultiplayer { get; set; }
|
||||
public IEnumerable<SavePath> SavePaths { get; set; }
|
||||
|
||||
public GameManifest() { }
|
||||
}
|
||||
|
@ -39,4 +41,11 @@ namespace LANCommander.SDK
|
|||
public int MinPlayers { get; set; }
|
||||
public int MaxPlayers { get; set; }
|
||||
}
|
||||
|
||||
public class SavePath
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string Path { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace LANCommander.SDK.Models
|
||||
{
|
||||
public class GameSave : BaseModel
|
||||
{
|
||||
public Guid GameId { get; set; }
|
||||
public virtual Game Game { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
public virtual User User { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using LANCommander.SDK.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace LANCommander.SDK.Models
|
||||
{
|
||||
public class SavePath : BaseModel
|
||||
{
|
||||
public SavePathType Type { get; set; }
|
||||
public string Path { get; set; }
|
||||
public virtual Game Game { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
@using LANCommander.Data.Enums
|
||||
@using LANCommander.SDK.Enums;
|
||||
|
||||
<Space Direction="DirectionVHType.Vertical" Size="@("large")" Style="width: 100%">
|
||||
<SpaceItem>
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
using LANCommander.Data;
|
||||
using LANCommander.Data.Models;
|
||||
using LANCommander.Extensions;
|
||||
using LANCommander.Models;
|
||||
using LANCommander.SDK;
|
||||
using LANCommander.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
|
||||
namespace LANCommander.Controllers.Api
|
||||
{
|
||||
[Authorize(AuthenticationSchemes = "Bearer")]
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class SavesController : ControllerBase
|
||||
{
|
||||
private readonly GameService GameService;
|
||||
private readonly GameSaveService GameSaveService;
|
||||
private readonly UserManager<User> UserManager;
|
||||
|
||||
public SavesController(GameService gameService, GameSaveService gameSaveService, UserManager<User> userManager)
|
||||
{
|
||||
GameService = gameService;
|
||||
GameSaveService = gameSaveService;
|
||||
UserManager = userManager;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IEnumerable<GameSave> Get()
|
||||
{
|
||||
return GameSaveService.Get();
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<GameSave> Get(Guid id)
|
||||
{
|
||||
return await GameSaveService.Get(id);
|
||||
}
|
||||
|
||||
[HttpGet("DownloadLatest/{gameId}")]
|
||||
public async Task<IActionResult> DownloadLatest(Guid gameId)
|
||||
{
|
||||
var user = await UserManager.FindByNameAsync(User.Identity.Name);
|
||||
|
||||
if (user == null)
|
||||
return NotFound();
|
||||
|
||||
var save = await GameSaveService
|
||||
.Get(gs => gs.GameId == gameId && gs.UserId == user.Id)
|
||||
.OrderByDescending(gs => gs.CreatedOn)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (save == null)
|
||||
return NotFound();
|
||||
|
||||
var filename = save.GetUploadPath();
|
||||
|
||||
if (!System.IO.File.Exists(filename))
|
||||
return NotFound();
|
||||
|
||||
return File(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read), "application/octet-stream", $"{save.Id.ToString().SanitizeFilename()}.zip");
|
||||
}
|
||||
|
||||
[HttpGet("Download/{id}")]
|
||||
public async Task<IActionResult> Download(Guid id)
|
||||
{
|
||||
var save = await GameSaveService.Get(id);
|
||||
|
||||
if (save == null)
|
||||
return NotFound();
|
||||
|
||||
var filename = save.GetUploadPath();
|
||||
|
||||
if (!System.IO.File.Exists(filename))
|
||||
return NotFound();
|
||||
|
||||
return File(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read), "application/octet-stream", $"{save.Id.ToString().SanitizeFilename()}.zip");
|
||||
}
|
||||
|
||||
[HttpPost("Upload/{id}")]
|
||||
public async Task<IActionResult> Upload(Guid id)
|
||||
{
|
||||
var file = Request.Form.Files.First();
|
||||
|
||||
var user = await UserManager.FindByNameAsync(User.Identity.Name);
|
||||
var game = await GameService.Get(id);
|
||||
|
||||
if (game == null)
|
||||
return NotFound();
|
||||
|
||||
var save = new GameSave()
|
||||
{
|
||||
GameId = id,
|
||||
UserId = user.Id
|
||||
};
|
||||
|
||||
save = await GameSaveService.Add(save);
|
||||
|
||||
var saveUploadPath = Path.GetDirectoryName(save.GetUploadPath());
|
||||
|
||||
if (!Directory.Exists(saveUploadPath))
|
||||
Directory.CreateDirectory(saveUploadPath);
|
||||
|
||||
using (var stream = System.IO.File.Create(save.GetUploadPath()))
|
||||
{
|
||||
await file.CopyToAsync(stream);
|
||||
}
|
||||
|
||||
return Ok(save);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
namespace LANCommander.Data.Enums
|
||||
{
|
||||
public enum SavePathType
|
||||
{
|
||||
File,
|
||||
Registry
|
||||
}
|
||||
}
|
|
@ -16,5 +16,10 @@ namespace LANCommander.Data.Models
|
|||
[ForeignKey(nameof(UserId))]
|
||||
[InverseProperty("GameSaves")]
|
||||
public virtual User? User { get; set; }
|
||||
|
||||
public string GetUploadPath()
|
||||
{
|
||||
return Path.Combine("Saves", UserId.ToString(), GameId.ToString(), Id.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using LANCommander.Data.Enums;
|
||||
using LANCommander.SDK.Enums;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
|
|
|
@ -44,5 +44,10 @@ namespace LANCommander.Data.Models
|
|||
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<GameSave>? GameSaves { get; set; }
|
||||
|
||||
public string GetGameSaveUploadPath()
|
||||
{
|
||||
return Path.Combine("Saves", Id.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,12 +55,12 @@
|
|||
|
||||
foreach (var user in UserManager.Users)
|
||||
{
|
||||
var savePath = Path.Combine("Save", user.Id.ToString());
|
||||
var savePath = user.GetGameSaveUploadPath();
|
||||
|
||||
long saveSize = 0;
|
||||
|
||||
if (Directory.Exists(savePath))
|
||||
saveSize = new DirectoryInfo(savePath).EnumerateFiles().Sum(f => f.Length);
|
||||
saveSize = new DirectoryInfo(savePath).EnumerateFiles("*", SearchOption.AllDirectories).Sum(f => f.Length);
|
||||
|
||||
UserList.Add(new UserViewModel()
|
||||
{
|
||||
|
|
|
@ -114,6 +114,7 @@ builder.Services.AddScoped<TagService>();
|
|||
builder.Services.AddScoped<CompanyService>();
|
||||
builder.Services.AddScoped<IGDBService>();
|
||||
builder.Services.AddScoped<ServerService>();
|
||||
builder.Services.AddScoped<GameSaveService>();
|
||||
|
||||
builder.Services.AddSingleton<ServerProcessService>();
|
||||
|
||||
|
@ -161,8 +162,8 @@ if (!Directory.Exists("Upload"))
|
|||
if (!Directory.Exists("Icon"))
|
||||
Directory.CreateDirectory("Icon");
|
||||
|
||||
if (!Directory.Exists("Save"))
|
||||
Directory.CreateDirectory("Save");
|
||||
if (!Directory.Exists("Saves"))
|
||||
Directory.CreateDirectory("Saves");
|
||||
|
||||
if (!Directory.Exists("Snippets"))
|
||||
Directory.CreateDirectory("Snippets");
|
||||
|
|
|
@ -99,6 +99,16 @@ namespace LANCommander.Services
|
|||
};
|
||||
}
|
||||
|
||||
if (game.SavePaths != null && game.SavePaths.Count > 0)
|
||||
{
|
||||
manifest.SavePaths = game.SavePaths.Select(p => new SDK.SavePath()
|
||||
{
|
||||
Id = p.Id,
|
||||
Path = p.Path,
|
||||
Type = p.Type.ToString()
|
||||
});
|
||||
}
|
||||
|
||||
return manifest;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue