diff --git a/EpinelPS/Controllers/AdminApiController.cs b/EpinelPS/Controllers/AdminApiController.cs index b208414..d3b791b 100644 --- a/EpinelPS/Controllers/AdminApiController.cs +++ b/EpinelPS/Controllers/AdminApiController.cs @@ -1,8 +1,10 @@ -using EpinelPS.Database; -using EpinelPS.LobbyServer; +using EpinelPS.Controllers.AdminPanel; +using EpinelPS.Data; +using EpinelPS.Database; +using EpinelPS.LobbyServer.Stage; +using EpinelPS.Models.Admin; +using EpinelPS.Utils; using Microsoft.AspNetCore.Mvc; -using Org.BouncyCastle.Asn1.X509; -using System.ComponentModel.DataAnnotations; using System.Security.Cryptography; using System.Text; @@ -12,8 +14,7 @@ namespace EpinelPS.Controllers [ApiController] public class AdminApiController : ControllerBase { - public static Dictionary AdminAuthTokens = new(); - private static MD5 md5 = MD5.Create(); + private static readonly MD5 md5 = MD5.Create(); [HttpPost] [Route("login")] @@ -23,8 +24,8 @@ namespace EpinelPS.Controllers bool nullusernames = false; if (b.Username != null && b.Password != null) { - var passwordHash = Convert.ToHexString(md5.ComputeHash(Encoding.ASCII.GetBytes(b.Password))).ToLower(); - foreach (var item in JsonDb.Instance.Users) + string passwordHash = Convert.ToHexString(md5.ComputeHash(Encoding.ASCII.GetBytes(b.Password))).ToLower(); + foreach (User item in JsonDb.Instance.Users) { if (item.Username == b.Username) { @@ -37,25 +38,20 @@ namespace EpinelPS.Controllers } else { - nullusernames = true; + nullusernames = true; } if (user == null) { - if (nullusernames) - { - return new LoginApiResponse() { Message = "Please enter a username and password" }; - } - else - { - return new LoginApiResponse() { Message = "Username or password is incorrect" }; - } + return nullusernames + ? new LoginApiResponse() { Message = "Please enter a username and password" } + : new LoginApiResponse() { Message = "Username or password is incorrect" }; } else { if (user.IsAdmin) { - var tok = CreateAuthToken(user); + string tok = CreateAuthToken(user); HttpContext.Response.Cookies.Append("token", tok); return new LoginApiResponse() { OK = true, Token = tok }; } @@ -64,35 +60,48 @@ namespace EpinelPS.Controllers return new LoginApiResponse() { Message = "User is not an administrator." }; } } + } + [HttpPost("RunCmd")] + public RunCmdResponse RunCmd([FromBody] RunCmdRequest req) + { + if (!AdminController.CheckAuth(HttpContext)) return new RunCmdResponse() { error = "bad token" }; + + switch (req.cmdName) + { + case "reloadDb": + JsonDb.Reload(); + return RunCmdResponse.OK; + case "completestage": + return AdminCommands.CompleteStage(ulong.Parse(req.p1), req.p2); + case "addallcharacters": + { + var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == ulong.Parse(req.p1)); + if (user == null) return new RunCmdResponse() { error = "invalid user ID" }; + return AdminCommands.AddAllCharacters(user); + } + case "addallmaterials": + { + var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == ulong.Parse(req.p1)); + if (user == null) return new RunCmdResponse() { error = "invalid user ID" }; + return AdminCommands.AddAllMaterials(user, int.Parse(req.p2)); + } + } + return new RunCmdResponse() { error = "Not implemented" }; } private static string CreateAuthToken(User user) { - var tok = RandomString(128); - AdminAuthTokens.Add(tok, user); + string tok = RandomString(128); + JsonDb.Instance.AdminAuthTokens.Add(tok, user); + JsonDb.Save(); return tok; } public static string RandomString(int length) { const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - return new string(Enumerable.Repeat(chars, length) - .Select(s => s[new Random().Next(s.Length)]).ToArray()); - } - - public class LoginApiBody - { - [Required] - public string Username { get; set; } = ""; - [Required] - public string Password { get; set; } = ""; - } - public class LoginApiResponse - { - public string Message { get; set; } = ""; - public bool OK { get; set; } - public string Token { get; set; } = ""; + return new string([.. Enumerable.Repeat(chars, length).Select(static s => s[new Random().Next(s.Length)])]); } } } \ No newline at end of file diff --git a/EpinelPS/Controllers/AdminPanel/AdminController.cs b/EpinelPS/Controllers/AdminPanel/AdminController.cs index f67c4fd..7ecbb41 100644 --- a/EpinelPS/Controllers/AdminPanel/AdminController.cs +++ b/EpinelPS/Controllers/AdminPanel/AdminController.cs @@ -1,6 +1,9 @@ -using EpinelPS.Models; +using EpinelPS.Database; +using EpinelPS.Models; +using EpinelPS.Models.Admin; using Microsoft.AspNetCore.Mvc; using System.Diagnostics; +using System.Net.Security; namespace EpinelPS.Controllers.AdminPanel { @@ -14,8 +17,8 @@ namespace EpinelPS.Controllers.AdminPanel string? token = context.Request.Cookies["token"]; if (token == null) return false; - - foreach (var item in AdminApiController.AdminAuthTokens) + // TODO better authentication + foreach (var item in JsonDb.Instance.AdminAuthTokens) { if (item.Key == token) return true; } @@ -36,12 +39,33 @@ namespace EpinelPS.Controllers.AdminPanel return View(); } + [Route("Configuration")] public IActionResult Configuration() { if (!CheckAuth(HttpContext)) return Redirect("/admin/"); - return View(); + ServerConfiguration model = new() + { + LogType = JsonDb.Instance.LogLevel + }; + + return View(model); + } + + [Route("Configuration"), ActionName("Configuration")] + [HttpPost] + public IActionResult ConfigurationSave([FromForm] ServerConfiguration cfg) + { + if (!CheckAuth(HttpContext)) return Redirect("/admin/"); + + if (!ModelState.IsValid) + return View(); + + JsonDb.Instance.LogLevel = cfg.LogType; + JsonDb.Save(); + + return View(new ServerConfiguration() { LogType = cfg.LogType }); } [Route("Mail")] diff --git a/EpinelPS/Controllers/AdminPanel/UsersController.cs b/EpinelPS/Controllers/AdminPanel/UsersController.cs index dcb0763..b3944ec 100644 --- a/EpinelPS/Controllers/AdminPanel/UsersController.cs +++ b/EpinelPS/Controllers/AdminPanel/UsersController.cs @@ -1,7 +1,6 @@ using EpinelPS.Database; -using EpinelPS.Models; +using EpinelPS.Models.Admin; using Microsoft.AspNetCore.Mvc; -using System.Diagnostics; using System.Security.Cryptography; using System.Text; @@ -31,7 +30,53 @@ namespace EpinelPS.Controllers.AdminPanel return NotFound(); } - return View(user); + return View( + new ModUserModel() + { + IsAdmin = user.IsAdmin, + IsBanned = user.IsBanned, + Nickname = user.Nickname, + sickpulls = user.sickpulls, + Username = user.Username, + ID = user.ID + } + ); + } + + [Route("Modify/{id}"), ActionName("Modify")] + [HttpPost] + [ValidateAntiForgeryToken] + public IActionResult DoModifyUser(ulong id, [FromForm] ModUserModel toSet) + { + if (!AdminController.CheckAuth(HttpContext)) return Redirect("/admin/"); + + if (!ModelState.IsValid) throw new Exception("model state invalid"); + + var user = JsonDb.Instance.Users.Where(x => x.ID == id).FirstOrDefault(); + if (user == null) + { + return NotFound(); + } + + if (string.IsNullOrEmpty(toSet.Username)) + throw new Exception("username cannot be empty"); + + user.Username = toSet.Username; + user.IsAdmin = toSet.IsAdmin; + user.sickpulls = toSet.sickpulls; + user.IsBanned = toSet.IsBanned; + user.Nickname = toSet.Nickname; + JsonDb.Save(); + + return View(new ModUserModel() + { + IsAdmin = user.IsAdmin, + IsBanned = user.IsBanned, + Nickname = user.Nickname, + sickpulls = user.sickpulls, + Username = user.Username, + ID = user.ID + }); } [Route("SetPassword/{id}")] diff --git a/EpinelPS/Database/JsonDb.cs b/EpinelPS/Database/JsonDb.cs index e8ecdd2..8f289d1 100644 --- a/EpinelPS/Database/JsonDb.cs +++ b/EpinelPS/Database/JsonDb.cs @@ -1,5 +1,4 @@ -using EpinelPS.LobbyServer; -using EpinelPS.Data; +using EpinelPS.Data; using EpinelPS.Utils; using Newtonsoft.Json; using Paseto.Builder; @@ -578,6 +577,7 @@ namespace EpinelPS.Database public List Users = []; public List LauncherAccessTokens = []; + public Dictionary AdminAuthTokens = []; public string ServerName = "Private Server"; public byte[] LauncherTokenKey = []; diff --git a/EpinelPS/EpinelPS.csproj b/EpinelPS/EpinelPS.csproj index 2f549d1..b3a616e 100644 --- a/EpinelPS/EpinelPS.csproj +++ b/EpinelPS/EpinelPS.csproj @@ -43,9 +43,7 @@ - - diff --git a/EpinelPS/Models/Admin/LoginApiBody.cs b/EpinelPS/Models/Admin/LoginApiBody.cs new file mode 100644 index 0000000..b8d8f5b --- /dev/null +++ b/EpinelPS/Models/Admin/LoginApiBody.cs @@ -0,0 +1,11 @@ +using System.ComponentModel.DataAnnotations; + +namespace EpinelPS.Models.Admin; + +public class LoginApiBody +{ + [Required] + public string Username { get; set; } = ""; + [Required] + public string Password { get; set; } = ""; +} \ No newline at end of file diff --git a/EpinelPS/Models/Admin/LoginApiResponse.cs b/EpinelPS/Models/Admin/LoginApiResponse.cs new file mode 100644 index 0000000..23afc23 --- /dev/null +++ b/EpinelPS/Models/Admin/LoginApiResponse.cs @@ -0,0 +1,12 @@ + +using EpinelPS.Utils; +using Microsoft.AspNetCore.Mvc; + +namespace EpinelPS.Models.Admin; + +public class LoginApiResponse +{ + public string Message { get; set; } = ""; + public bool OK { get; set; } + public string Token { get; set; } = ""; +} \ No newline at end of file diff --git a/EpinelPS/Models/Admin/ModUserModel.cs b/EpinelPS/Models/Admin/ModUserModel.cs new file mode 100644 index 0000000..7b2acf6 --- /dev/null +++ b/EpinelPS/Models/Admin/ModUserModel.cs @@ -0,0 +1,15 @@ + +using EpinelPS.Utils; +using Microsoft.AspNetCore.Mvc; + +namespace EpinelPS.Models.Admin; + +public class ModUserModel +{ + public string Username { get; set; } = ""; + public string Nickname { get; set; } = ""; + public bool IsAdmin { get; set; } = false; + public bool sickpulls { get; set; } = false; + public bool IsBanned { get; set; } = false; + public ulong ID { get; set; } +} \ No newline at end of file diff --git a/EpinelPS/Models/Admin/RunCmdModel.cs b/EpinelPS/Models/Admin/RunCmdModel.cs new file mode 100644 index 0000000..6b8d43b --- /dev/null +++ b/EpinelPS/Models/Admin/RunCmdModel.cs @@ -0,0 +1,22 @@ + +using System.ComponentModel.DataAnnotations; +using EpinelPS.Utils; +using Microsoft.AspNetCore.Mvc; + +namespace EpinelPS.Models.Admin; + +public class RunCmdRequest +{ + [Required] + public string cmdName { get; set; } = ""; + public string p1 { get; set; } = ""; + public string p2 { get; set; } = ""; +} + +public class RunCmdResponse +{ + public bool ok { get; set; } + public string error { get; set; } = ""; + + public static readonly RunCmdResponse OK = new() { ok = true }; +} \ No newline at end of file diff --git a/EpinelPS/Models/Admin/ServerConfiguration.cs b/EpinelPS/Models/Admin/ServerConfiguration.cs new file mode 100644 index 0000000..30980b7 --- /dev/null +++ b/EpinelPS/Models/Admin/ServerConfiguration.cs @@ -0,0 +1,10 @@ +using EpinelPS.Utils; +using Microsoft.AspNetCore.Mvc; + +namespace EpinelPS.Models.Admin; + +public class ServerConfiguration +{ + [BindProperty] + public LogType LogType { get; set; } +} \ No newline at end of file diff --git a/EpinelPS/Program.cs b/EpinelPS/Program.cs index 71e2527..3dee259 100644 --- a/EpinelPS/Program.cs +++ b/EpinelPS/Program.cs @@ -168,7 +168,8 @@ namespace EpinelPS ] }".Replace("{GameMinVer}", GameConfig.Root.GameMinVer).Replace("{GameMaxVer}", GameConfig.Root.GameMaxVer)); - app.MapGet("/", () => { + app.MapGet("/", () => + { return $"EpinelPS v{Assembly.GetExecutingAssembly().GetName().Version} - https://github.com/EpinelPS/EpinelPS/"; }); @@ -204,15 +205,14 @@ namespace EpinelPS else if (input == "?" || input == "help") { Console.WriteLine("EpinelPS CLI"); + Console.WriteLine("NOTICE: Admin panel is available at https://localhost/admin/"); Console.WriteLine(); Console.WriteLine("Commands:"); Console.WriteLine(" help - show this help"); - Console.WriteLine(" ls /users - show all users"); - Console.WriteLine(" cd (user id) - select user by id"); + Console.WriteLine(" show users - show all users"); + Console.WriteLine(" user (user id) - select user by id"); Console.WriteLine(" rmuser - delete selected user"); Console.WriteLine(" r - load changes to database from disk. Discards data in memory."); - Console.WriteLine(" ban - ban selected user from game"); - Console.WriteLine(" unban - unban selected user from game"); Console.WriteLine(" exit - exit server application"); Console.WriteLine(" completestage (chapter num)-(stage number) - complete selected stage and get rewards (and all previous ones). Example completestage 15-1. Note that the exact stage number cleared may not be exact."); Console.WriteLine(" sickpulls (requires selecting user first) allows for all characters to have equal chances of getting pulled"); @@ -225,7 +225,7 @@ namespace EpinelPS Console.WriteLine(" AddItem (id) (amount) - Adds an item to the selected user (takes effect on game and server restart)"); Console.WriteLine(" AddCharacter (id) - Adds a character to the selected user (takes effect on game and server restart)"); } - else if (input == "ls /users") + else if (input == "show users") { Console.WriteLine("Id,Username,Nickname"); foreach (var item in JsonDb.Instance.Users) @@ -233,7 +233,7 @@ namespace EpinelPS Console.WriteLine($"{item.ID},{item.Username},{item.Nickname}"); } } - else if (input.StartsWith("cd")) + else if (input.StartsWith("user")) { if (args.Length == 2) { @@ -281,89 +281,39 @@ namespace EpinelPS } else { - // Group characters by name_code and always add those with grade_core_id == 11, 103, and include grade_core_id == 201 - var allCharacters = GameData.Instance.CharacterTable.Values - .GroupBy(c => c.name_code) // Group by name_code to treat same name_code as one character 3999 = marian - .SelectMany(g => g.Where(c => c.grade_core_id == 1 || c.grade_core_id == 101 || c.grade_core_id == 201 || c.name_code == 3999)) // Always add characters with grade_core_id == 11 and 103 - .ToList(); - - foreach (var character in allCharacters) - { - if (!user.HasCharacter(character.id)) - { - user.Characters.Add(new Database.Character() - { - CostumeId = 0, - Csn = user.GenerateUniqueCharacterId(), - Grade = 0, - Level = 1, - Skill1Lvl = 1, - Skill2Lvl = 1, - Tid = character.id, // Tid is the character ID - UltimateLevel = 1 - }); - - user.BondInfo.Add(new() { NameCode = character.name_code, Level = 1 }); - user.AddTrigger(TriggerType.ObtainCharacter, 1, character.name_code); - user.AddTrigger(TriggerType.ObtainCharacterNew, 1); - } - } - - Console.WriteLine("Added all missing characters to user " + user.Username); - JsonDb.Save(); + var rsp = AdminCommands.AddAllCharacters(user); + if (!rsp.ok) Console.WriteLine(rsp.error); } } } - else if (input.StartsWith("addallmaterials")) - { - if (selectedUser == 0) - { - Console.WriteLine("No user selected"); - } - else - { - var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser); - if (user == null) - { - Console.WriteLine("Selected user does not exist"); - selectedUser = 0; - prompt = "# "; - } - else - { - int amount = 1; // Default amount if not provided - if (args.Length >= 2 && int.TryParse(args[1], out int parsedAmount)) - { - amount = parsedAmount; - } - - foreach (var tableItem in GameData.Instance.itemMaterialTable.Values) - { - ItemData? item = user.Items.FirstOrDefault(i => i.ItemType == tableItem.id); - - if (item == null) - { - user.Items.Add(new ItemData - { - Isn = user.GenerateUniqueItemId(), - ItemType = tableItem.id, - Level = 1, - Exp = 1, - Count = amount - }); - } - else - { - item.Count += amount; - } - } - - Console.WriteLine($"Added {amount} of all materials to user " + user.Username); - JsonDb.Save(); - } - } - } + else if (input.StartsWith("addallmaterials")) + { + if (selectedUser == 0) + { + Console.WriteLine("No user selected"); + } + else + { + var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser); + if (user == null) + { + Console.WriteLine("Selected user does not exist"); + selectedUser = 0; + prompt = "# "; + } + else + { + int amount = 1; // Default amount if not provided + if (args.Length >= 2 && int.TryParse(args[1], out int parsedAmount)) + { + amount = parsedAmount; + } + var rsp = AdminCommands.AddAllMaterials(user, amount); + if (!rsp.ok) Console.WriteLine(rsp.error); + } + } + } else if (input == "finishalltutorials") { if (selectedUser == 0) @@ -515,9 +465,6 @@ namespace EpinelPS JsonDb.Save(); } - - - else if (input == "sickpulls") { if (selectedUser == 0) @@ -684,93 +631,15 @@ namespace EpinelPS } else { - var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser); - if (user == null) + if (args.Length == 2) { - Console.WriteLine("Selected user does not exist"); - selectedUser = 0; - prompt = "# "; + var input2 = args[1]; + var rsp = AdminCommands.CompleteStage(selectedUser, input2); + if (!rsp.ok) Console.WriteLine(rsp.error); } else { - if (args.Length == 2) - { - var input2 = args[1]; - try - { - var chapterParsed = int.TryParse(input2.Split('-')[0], out int chapterNumber); - var stageParsed = int.TryParse(input2.Split('-')[1], out int stageNumber); - - if (chapterParsed && stageParsed) - { - Console.WriteLine($"Chapter number: {chapterNumber}, Stage number: {stageNumber}"); - - // Complete main stages - for (int i = 0; i <= chapterNumber; i++) - { - var stages = GameData.Instance.GetStageIdsForChapter(i, true); - int target = 1; - foreach (var item in stages) - { - if (!user.IsStageCompleted(item, true)) - { - Console.WriteLine("Completing stage " + item); - ClearStage.CompleteStage(user, item, true); - } - - if (i == chapterNumber && target == stageNumber) - { - break; - } - - target++; - } - } - - // Process scenario and regular stages - Console.WriteLine($"Processing stages for chapters 0 to {chapterNumber}"); - - for (int chapter = 0; chapter <= chapterNumber; chapter++) - { - Console.WriteLine($"Processing chapter: {chapter}"); - - var stages = GameData.Instance.GetScenarioStageIdsForChapter(chapter) - .Where(stageId => GameData.Instance.IsValidScenarioStage(stageId, chapterNumber, stageNumber)) - .ToList(); - - Console.WriteLine($"Found {stages.Count} stages for chapter {chapter}"); - - foreach (var stage in stages) - { - if (!user.CompletedScenarios.Contains(stage)) - { - user.CompletedScenarios.Add(stage); - Console.WriteLine($"Added stage {stage} to CompletedScenarios"); - } - else - { - Console.WriteLine($"Stage {stage} is already completed"); - } - } - } - - // Save changes to user data - JsonDb.Save(); - } - else - { - Console.WriteLine("Chapter and stage number must be valid integers"); - } - } - catch (Exception ex) - { - Console.WriteLine("Exception: " + ex.ToString()); - } - } - else - { - Console.WriteLine("Invalid argument length, must be 1"); - } + Console.WriteLine("Invalid argument length, must be 1"); } } } @@ -892,14 +761,6 @@ namespace EpinelPS { Environment.Exit(0); } - else if (input == "ban") - { - Console.WriteLine("Not implemented"); - } - else if (input == "unban") - { - Console.WriteLine("Not implemented"); - } else if (input == "r") { JsonDb.Reload(); @@ -968,7 +829,7 @@ namespace EpinelPS response.AddRange([.. ResponseWithBytes]); } } - catch(Exception ex) + catch (Exception ex) { List ResponseWithBytes = [ .. Encoding.UTF8.GetBytes("HTTP/1.1 500 Internal Server Error\r\n"), diff --git a/EpinelPS/Utils/AdminCommands.cs b/EpinelPS/Utils/AdminCommands.cs new file mode 100644 index 0000000..88cb17a --- /dev/null +++ b/EpinelPS/Utils/AdminCommands.cs @@ -0,0 +1,155 @@ +using DnsClient; +using EpinelPS.Data; +using EpinelPS.Database; +using EpinelPS.LobbyServer.Stage; +using EpinelPS.Models.Admin; +using System.Net; + +namespace EpinelPS.Utils +{ + public class AdminCommands + { + + public static RunCmdResponse CompleteStage(ulong userId, string input2) + { + var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == userId); + if (user == null) return new RunCmdResponse() { error = "invalid user ID" }; + + try + { + var chapterParsed = int.TryParse(input2.Split('-')[0], out int chapterNumber); + var stageParsed = int.TryParse(input2.Split('-')[1], out int stageNumber); + + if (chapterParsed && stageParsed) + { + Console.WriteLine($"Chapter number: {chapterNumber}, Stage number: {stageNumber}"); + + // Complete main stages + for (int i = 0; i <= chapterNumber; i++) + { + var stages = GameData.Instance.GetStageIdsForChapter(i, true); + int target = 1; + foreach (var item in stages) + { + if (!user.IsStageCompleted(item, true)) + { + Console.WriteLine("Completing stage " + item); + ClearStage.CompleteStage(user, item, true); + } + + if (i == chapterNumber && target == stageNumber) + { + break; + } + + target++; + } + } + + // Process scenario and regular stages + Console.WriteLine($"Processing stages for chapters 0 to {chapterNumber}"); + + for (int chapter = 0; chapter <= chapterNumber; chapter++) + { + Console.WriteLine($"Processing chapter: {chapter}"); + + var stages = GameData.Instance.GetScenarioStageIdsForChapter(chapter) + .Where(stageId => GameData.Instance.IsValidScenarioStage(stageId, chapterNumber, stageNumber)) + .ToList(); + + Console.WriteLine($"Found {stages.Count} stages for chapter {chapter}"); + + foreach (var stage in stages) + { + if (!user.CompletedScenarios.Contains(stage)) + { + user.CompletedScenarios.Add(stage); + Console.WriteLine($"Added stage {stage} to CompletedScenarios"); + } + else + { + Console.WriteLine($"Stage {stage} is already completed"); + } + } + } + + // Save changes to user data + JsonDb.Save(); + } + else + { + return new RunCmdResponse() { error = "Chapter and stage number must be valid integers" }; + } + } + catch (Exception ex) + { + return new RunCmdResponse() { error = "Exception: " + ex.ToString() }; + } + + return RunCmdResponse.OK; + } + + public static RunCmdResponse AddAllCharacters(User user) + { + // Group characters by name_code and always add those with grade_core_id == 11, 103, and include grade_core_id == 201 + var allCharacters = GameData.Instance.CharacterTable.Values + .GroupBy(c => c.name_code) // Group by name_code to treat same name_code as one character 3999 = marian + .SelectMany(g => g.Where(c => c.grade_core_id == 1 || c.grade_core_id == 101 || c.grade_core_id == 201 || c.name_code == 3999)) // Always add characters with grade_core_id == 11 and 103 + .ToList(); + + foreach (var character in allCharacters) + { + if (!user.HasCharacter(character.id)) + { + user.Characters.Add(new Database.Character() + { + CostumeId = 0, + Csn = user.GenerateUniqueCharacterId(), + Grade = 0, + Level = 1, + Skill1Lvl = 1, + Skill2Lvl = 1, + Tid = character.id, // Tid is the character ID + UltimateLevel = 1 + }); + + user.BondInfo.Add(new() { NameCode = character.name_code, Level = 1 }); + user.AddTrigger(TriggerType.ObtainCharacter, 1, character.name_code); + user.AddTrigger(TriggerType.ObtainCharacterNew, 1); + } + } + + JsonDb.Save(); + + return RunCmdResponse.OK; + } + + public static RunCmdResponse AddAllMaterials(User user, int amount) + { + foreach (var tableItem in GameData.Instance.itemMaterialTable.Values) + { + ItemData? item = user.Items.FirstOrDefault(i => i.ItemType == tableItem.id); + + if (item == null) + { + user.Items.Add(new ItemData + { + Isn = user.GenerateUniqueItemId(), + ItemType = tableItem.id, + Level = 1, + Exp = 1, + Count = amount + }); + } + else + { + item.Count += amount; + } + } + + Console.WriteLine($"Added {amount} of all materials to user " + user.Username); + JsonDb.Save(); + return RunCmdResponse.OK; + } + } +} diff --git a/EpinelPS/Utils/Logging.cs b/EpinelPS/Utils/Logging.cs index 04f72d6..8067fc1 100644 --- a/EpinelPS/Utils/Logging.cs +++ b/EpinelPS/Utils/Logging.cs @@ -1,3 +1,4 @@ +using System.ComponentModel.DataAnnotations; using EpinelPS.Database; namespace EpinelPS.Utils @@ -25,7 +26,6 @@ namespace EpinelPS.Utils { case LogType.Debug: return ConsoleColor.DarkGray; case LogType.Info: return ConsoleColor.Gray; - case LogType.InfoSuccess: return ConsoleColor.Green; case LogType.Warning: return ConsoleColor.Yellow; case LogType.WarningAntiCheat: return ConsoleColor.DarkMagenta; case LogType.Error: return ConsoleColor.Red; @@ -36,11 +36,15 @@ namespace EpinelPS.Utils public enum LogType { + [Display(Name = "Debug")] Debug, - InfoSuccess, + [Display(Name = "Info")] Info, + [Display(Name = "Warning")] Warning, + [Display(Name = "Anticheat warnings")] WarningAntiCheat, + [Display(Name = "Errors")] Error } } \ No newline at end of file diff --git a/EpinelPS/Utils/RewardUtils.cs b/EpinelPS/Utils/RewardUtils.cs index cd39d74..3079556 100644 --- a/EpinelPS/Utils/RewardUtils.cs +++ b/EpinelPS/Utils/RewardUtils.cs @@ -13,8 +13,10 @@ namespace EpinelPS.Utils } public static NetRewardData RegisterRewardsForUser(User user, RewardTableRecord rewardData) { - NetRewardData ret = new(); - ret.PassPoint = new(); + NetRewardData ret = new() + { + PassPoint = new() + }; if (rewardData.rewards == null) return ret; if (rewardData.user_exp != 0) diff --git a/EpinelPS/Views/Admin/Configuration.cshtml b/EpinelPS/Views/Admin/Configuration.cshtml index 1a4e171..8973847 100644 --- a/EpinelPS/Views/Admin/Configuration.cshtml +++ b/EpinelPS/Views/Admin/Configuration.cshtml @@ -1,8 +1,22 @@ +@model EpinelPS.Models.Admin.ServerConfiguration + @{ ViewData["Title"] = "Configuration"; }
-

Configuration

-

Coming soon!

+

Server configuration

+
+
+
+
Log Level:
+
+ @Html.DropDownListFor(model => model.LogType, Html.GetEnumSelectList(), "", new { @class = "form-control" }) +
+
+ +
+ +
+
diff --git a/EpinelPS/Views/Admin/Database.cshtml b/EpinelPS/Views/Admin/Database.cshtml index 81ca1e6..3c37ff8 100644 --- a/EpinelPS/Views/Admin/Database.cshtml +++ b/EpinelPS/Views/Admin/Database.cshtml @@ -4,5 +4,5 @@

Database configuration

-

Coming soon!

+
diff --git a/EpinelPS/Views/Users/Index.cshtml b/EpinelPS/Views/Users/Index.cshtml index 1040822..d1d2f9a 100644 --- a/EpinelPS/Views/Users/Index.cshtml +++ b/EpinelPS/Views/Users/Index.cshtml @@ -15,12 +15,13 @@ @Html.DisplayNameFor(model => model.Username) - @Html.DisplayNameFor(model => model.IsAdmin) + @Html.DisplayNameFor(model => model.Nickname) - @Html.DisplayNameFor(model => model.PlayerName) + @Html.DisplayNameFor(model => model.IsAdmin) - + + @@ -30,10 +31,10 @@ @Html.DisplayFor(modelItem => item.Username) - @Html.DisplayFor(modelItem => item.IsAdmin) + @Html.DisplayFor(modelItem => item.Nickname) - @Html.DisplayFor(modelItem => item.PlayerName) + @Html.DisplayFor(modelItem => item.IsAdmin) Change Password | diff --git a/EpinelPS/Views/Users/Modify.cshtml b/EpinelPS/Views/Users/Modify.cshtml index 8d55c20..0560870 100644 --- a/EpinelPS/Views/Users/Modify.cshtml +++ b/EpinelPS/Views/Users/Modify.cshtml @@ -1,4 +1,4 @@ -@model EpinelPS.Database.User +@model EpinelPS.Models.Admin.ModUserModel @{ ViewData["Title"] = "Modify user"; @@ -12,19 +12,23 @@
-
- +
- + + + +
+
+
@@ -37,6 +41,21 @@
+ +

Campaign:

+ + +

Characters:

+ + + + + +

Inventory:

+ + +

Misc:

+ diff --git a/EpinelPS/Views/_ViewImports.cshtml b/EpinelPS/Views/_ViewImports.cshtml index 96772aa..12f3b83 100644 --- a/EpinelPS/Views/_ViewImports.cshtml +++ b/EpinelPS/Views/_ViewImports.cshtml @@ -1,4 +1,6 @@ @using EpinelPS @using EpinelPS.Models @using EpinelPS.Database +@using EpinelPS.Data +@using EpinelPS.Utils @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/EpinelPS/wwwroot/admin/css/site.css b/EpinelPS/wwwroot/admin/css/site.css index f8d98fc..897c48a 100644 --- a/EpinelPS/wwwroot/admin/css/site.css +++ b/EpinelPS/wwwroot/admin/css/site.css @@ -19,4 +19,12 @@ html { body { margin-bottom: 60px; +} + +.btn { + margin-bottom: 3px !important; +} + +p{ + margin-bottom: 0px !important; } \ No newline at end of file diff --git a/EpinelPS/wwwroot/admin/dashbrd.html b/EpinelPS/wwwroot/admin/dashbrd.html deleted file mode 100644 index f741e59..0000000 --- a/EpinelPS/wwwroot/admin/dashbrd.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - Security System Controller - - - - - - - {{navbar}} -
-

Welcome to Nikke Private Server Admin Panel

-

There are no settings to display.

-
- - - \ No newline at end of file diff --git a/EpinelPS/wwwroot/admin/js/site.js b/EpinelPS/wwwroot/admin/js/site.js index 0937657..b0c74cc 100644 --- a/EpinelPS/wwwroot/admin/js/site.js +++ b/EpinelPS/wwwroot/admin/js/site.js @@ -1,4 +1,43 @@ // Please see documentation at https://learn.microsoft.com/aspnet/core/client-side/bundling-and-minification // for details on configuring this project to bundle and minify static web assets. -// Write your JavaScript code. +function runCmd(cmdName, cb, p1, p2) +{ + fetch("/adminapi/RunCmd", { + method: "POST", + body: JSON.stringify({ + cmdName: cmdName, + p1: p1, + p2: p2 + }), + headers: { + "Content-type": "application/json; charset=UTF-8" + } + }) + .then((response) => response.json()) + .then((json) => cb(json)).catch((error) => { + alert(error) + }); +} + +function runSimpleCmd(cmdName, p1, p2) +{ + runCmd(cmdName, function(json){ + if (json.ok) + alert("Operation completed.") + else + alert("Error: " + json.error); + }, p1, p2); +} + +function runSimpleCmdWithPr(cmdName, p1, p2Title) +{ + let p2 = prompt(p2Title); + if (p2 === undefined || p2 == null || p2 == "") return; + runCmd(cmdName, function(json){ + if (json.ok) + alert("Operation completed.") + else + alert("Error: " + json.error); + }, p1, p2); +} diff --git a/EpinelPS/wwwroot/admin/nav.html b/EpinelPS/wwwroot/admin/nav.html deleted file mode 100644 index 6203726..0000000 --- a/EpinelPS/wwwroot/admin/nav.html +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file