mirror of
https://github.com/EpinelPS/EpinelPS.git
synced 2025-12-12 15:04:36 +01:00
admin panel work
This commit is contained in:
@@ -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<string, User> 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)])]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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")]
|
||||
|
||||
@@ -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}")]
|
||||
|
||||
@@ -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<User> Users = [];
|
||||
|
||||
public List<AccessToken> LauncherAccessTokens = [];
|
||||
public Dictionary<string, User> AdminAuthTokens = [];
|
||||
|
||||
public string ServerName = "<color=\"green\">Private Server</color>";
|
||||
public byte[] LauncherTokenKey = [];
|
||||
|
||||
@@ -43,9 +43,7 @@
|
||||
<None Include="wwwroot\admin\assets\login.css" />
|
||||
<None Include="wwwroot\admin\assets\login.jpg" />
|
||||
<None Include="wwwroot\admin\assets\style.css" />
|
||||
<None Include="wwwroot\admin\dashbrd.html" />
|
||||
<None Include="wwwroot\admin\index.html" />
|
||||
<None Include="wwwroot\admin\nav.html" />
|
||||
<None Include="wwwroot\nikke_launcher\index.html" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
11
EpinelPS/Models/Admin/LoginApiBody.cs
Normal file
11
EpinelPS/Models/Admin/LoginApiBody.cs
Normal file
@@ -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; } = "";
|
||||
}
|
||||
12
EpinelPS/Models/Admin/LoginApiResponse.cs
Normal file
12
EpinelPS/Models/Admin/LoginApiResponse.cs
Normal file
@@ -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; } = "";
|
||||
}
|
||||
15
EpinelPS/Models/Admin/ModUserModel.cs
Normal file
15
EpinelPS/Models/Admin/ModUserModel.cs
Normal file
@@ -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; }
|
||||
}
|
||||
22
EpinelPS/Models/Admin/RunCmdModel.cs
Normal file
22
EpinelPS/Models/Admin/RunCmdModel.cs
Normal file
@@ -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 };
|
||||
}
|
||||
10
EpinelPS/Models/Admin/ServerConfiguration.cs
Normal file
10
EpinelPS/Models/Admin/ServerConfiguration.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using EpinelPS.Utils;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace EpinelPS.Models.Admin;
|
||||
|
||||
public class ServerConfiguration
|
||||
{
|
||||
[BindProperty]
|
||||
public LogType LogType { get; set; }
|
||||
}
|
||||
@@ -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<byte> ResponseWithBytes =
|
||||
[ .. Encoding.UTF8.GetBytes("HTTP/1.1 500 Internal Server Error\r\n"),
|
||||
|
||||
155
EpinelPS/Utils/AdminCommands.cs
Normal file
155
EpinelPS/Utils/AdminCommands.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -1,8 +1,22 @@
|
||||
@model EpinelPS.Models.Admin.ServerConfiguration
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Configuration";
|
||||
}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Configuration</h1>
|
||||
<p>Coming soon!</p>
|
||||
<h1 class="display-4">Server configuration</h1>
|
||||
<form asp-action="Configuration">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
<dl class="row">
|
||||
<dt class = "col-sm-2">Log Level:</dt>
|
||||
<dd class = "col-sm-10">
|
||||
@Html.DropDownListFor(model => model.LogType, Html.GetEnumSelectList<LogType>(), "", new { @class = "form-control" })
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<div class="form-group">
|
||||
<input type="submit" value="Save" class="btn btn-primary" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Database configuration</h1>
|
||||
<p>Coming soon!</p>
|
||||
<button id="reloadDB" type="button" class="btn btn-danger" title="Loads changes from db.json into memory. Discards unsaved changes." onclick="runSimpleCmd('reloadDb')">Reload database</button>
|
||||
</div>
|
||||
|
||||
@@ -15,12 +15,13 @@
|
||||
@Html.DisplayNameFor(model => model.Username)
|
||||
</th>
|
||||
<th>
|
||||
@Html.DisplayNameFor(model => model.IsAdmin)
|
||||
@Html.DisplayNameFor(model => model.Nickname)
|
||||
</th>
|
||||
<th>
|
||||
@Html.DisplayNameFor(model => model.PlayerName)
|
||||
@Html.DisplayNameFor(model => model.IsAdmin)
|
||||
</th>
|
||||
<th></th>
|
||||
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -30,10 +31,10 @@
|
||||
@Html.DisplayFor(modelItem => item.Username)
|
||||
</td>
|
||||
<td>
|
||||
@Html.DisplayFor(modelItem => item.IsAdmin)
|
||||
@Html.DisplayFor(modelItem => item.Nickname)
|
||||
</td>
|
||||
<td>
|
||||
@Html.DisplayFor(modelItem => item.PlayerName)
|
||||
@Html.DisplayFor(modelItem => item.IsAdmin)
|
||||
</td>
|
||||
<td>
|
||||
<a asp-action="SetPassword" asp-route-id="@item.ID">Change Password</a> |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@model EpinelPS.Database.User
|
||||
@model EpinelPS.Models.Admin.ModUserModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Modify user";
|
||||
@@ -12,19 +12,23 @@
|
||||
<div class="col-md-4">
|
||||
<form asp-action="Modify">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
<input type="hidden" asp-for="ID" />
|
||||
<div class="form-group">
|
||||
<label asp-for="Username" class="control-label col-sm-2"></label>
|
||||
<div class="col-sm-10"><input asp-for="Username" class="form-control" /></div>
|
||||
<span asp-validation-for="Username" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="IsAdmin" class="control-label"></label>
|
||||
<label for="IsAdmin" class="control-label">Is Admin: </label>
|
||||
<input asp-for="IsAdmin" class="form-check-input" />
|
||||
<span asp-validation-for="IsAdmin" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="IsBanned" class="control-label"></label>
|
||||
<label class="control-label" title="allows for all characters to have equal chances of getting pulled">Disable Gacha System: </label>
|
||||
<input asp-for="sickpulls" class="form-check-input" title="allows for all characters to have equal chances of getting pulled"/>
|
||||
<span asp-validation-for="sickpulls" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="IsBanned" class="control-label">Banned:</label>
|
||||
<input asp-for="IsBanned" class="form-check-input" />
|
||||
<span asp-validation-for="IsBanned" class="text-danger"></span>
|
||||
</div>
|
||||
@@ -37,6 +41,21 @@
|
||||
<input type="submit" value="Save" class="btn btn-primary" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p>Campaign:</p>
|
||||
<button class="btn btn-secondary" onclick="runSimpleCmdWithPr('completestage', '@Model.ID', 'Enter chapter number and stage number seperated by -')">Skip stages</button>
|
||||
|
||||
<p>Characters:</p>
|
||||
<button class="btn btn-secondary" onclick="runSimpleCmd('addallcharacters', '@Model.ID')">Add all characters</button>
|
||||
<button class="btn btn-secondary" onclick="runSimpleCmdWithPr('AddCharacter', '@Model.ID', 'Enter character ID. Wrong ID may cause game not to boot.')">Add character</button>
|
||||
<button class="btn btn-secondary" onclick="runSimpleCmdWithPr('SetLevel', '@Model.ID', 'Enter level (1-999) to apply to all characters')">Set character levels</button>
|
||||
<button class="btn btn-secondary" onclick="runSimpleCmdWithPr('SetLevel', '@Model.ID', 'Enter skill level (1-10) to apply to all characters')">Set character skill levels</button>
|
||||
<button class="btn btn-secondary" onclick="runSimpleCmdWithPr('finishalltutorials', '@Model.ID', 'core level / 0-3 sets stars')">Set core level</button>
|
||||
<p>Inventory:</p>
|
||||
<button class="btn btn-secondary" onclick="runSimpleCmdWithPr('addallmaterials', '@Model.ID', 'Enter material amount:')">Add all equipment</button>
|
||||
<button class="btn btn-secondary" onclick="runSimpleCmdWithPr('AddItem', '@Model.ID', 'Enter item ID and amount seperated by -')">Add item</button>
|
||||
<p>Misc:</p>
|
||||
<button class="btn btn-secondary" onclick="runSimpleCmd('finishalltutorials', '@Model.ID')">Finish all tutorials</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
@using EpinelPS
|
||||
@using EpinelPS.Models
|
||||
@using EpinelPS.Database
|
||||
@using EpinelPS.Data
|
||||
@using EpinelPS.Utils
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
|
||||
@@ -19,4 +19,12 @@ html {
|
||||
|
||||
body {
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin-bottom: 3px !important;
|
||||
}
|
||||
|
||||
p{
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Security System Controller</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="/admin/assets/style.css">
|
||||
<link rel="icon" href="/favicon.ico" type="image/x-icon">
|
||||
</head>
|
||||
<body>
|
||||
{{navbar}}
|
||||
<div class="containter">
|
||||
<h1>Welcome to Nikke Private Server Admin Panel</h1>
|
||||
<p>There are no settings to display.</p>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<div class="navbar2">
|
||||
<a href="/admin/dashboard" class="navbar-item">Overview</a>
|
||||
</div>
|
||||
Reference in New Issue
Block a user