fixes to PR

- use multicolumn primary key on UserMissionData
- simplification of GetUserMissionData and SetOrUpdateUserMissionData

also:
- AuthenticateUser endpoint compatible with games that use e-mail as login
- using option to disable loading non SoD data also for achievements
This commit is contained in:
Robert Paciorek 2025-02-11 19:51:14 +00:00
parent df979ca476
commit 3a66ffd864
5 changed files with 42 additions and 48 deletions

View File

@ -87,12 +87,14 @@ public class AuthenticationController : Controller {
[Route("v3/AuthenticationWebService.asmx/AuthenticateUser")] [Route("v3/AuthenticationWebService.asmx/AuthenticateUser")]
[DecryptRequest("username")] [DecryptRequest("username")]
[DecryptRequest("password")] [DecryptRequest("password")]
public bool AuthenticateUser() { public bool AuthenticateUser([FromForm] string apiKey) {
String username = Request.Form["username"]; String username = Request.Form["username"];
String password = Request.Form["password"]; String password = Request.Form["password"];
// Authenticate the user // Authenticate the user
User? user = ctx.Users.FirstOrDefault(e => e.Username == username); User? user = (ClientVersion.GetVersion(apiKey) <= ClientVersion.Max_OldJS)
? ctx.Users.FirstOrDefault(e => e.Email == username)
: ctx.Users.FirstOrDefault(e => e.Username == username);
if (user is null || new PasswordHasher<object>().VerifyHashedPassword(null, user.Password, password) != PasswordVerificationResult.Success) { if (user is null || new PasswordHasher<object>().VerifyHashedPassword(null, user.Password, password) != PasswordVerificationResult.Success) {
return false; return false;
} }

View File

@ -2135,7 +2135,7 @@ public class ContentController : Controller {
[HttpPost] [HttpPost]
// [Produces("application/xml")] // [Produces("application/xml")]
[Route("MissionWebService.asmx/GetMission")] // old ("step") missions - used by MB and WoJS lands [Route("MissionWebService.asmx/GetMission")] // old ("step") missions - used by MB and WoJS lands
public IActionResult GetMission([FromForm] int gameId, [FromForm] int type) { public IActionResult GetMission([FromForm] int gameId, [FromForm] string name) {
if (gameId == 1) return Ok(XmlUtil.ReadResourceXmlString("missions.step_missions_wojs_al")); if (gameId == 1) return Ok(XmlUtil.ReadResourceXmlString("missions.step_missions_wojs_al"));
if (gameId == 5) return Ok(XmlUtil.ReadResourceXmlString("missions.step_missions_mb")); if (gameId == 5) return Ok(XmlUtil.ReadResourceXmlString("missions.step_missions_mb"));
return Ok(); return Ok();

View File

@ -1,11 +1,11 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
namespace sodoff.Model namespace sodoff.Model
{ {
[PrimaryKey(nameof(VikingId), nameof(WorldId), nameof(MissionId))]
public class UserMissionData public class UserMissionData
{ {
[Key]
public int Id { get; set; }
public int VikingId { get; set; } public int VikingId { get; set; }
public int WorldId { get; set; } public int WorldId { get; set; }
public int MissionId { get; set; } public int MissionId { get; set; }

View File

@ -1,10 +1,12 @@
using sodoff.Schema; using sodoff.Schema;
using sodoff.Model; using sodoff.Model;
using sodoff.Util; using sodoff.Util;
using sodoff.Configuration;
using System.Reflection; using System.Reflection;
using System.Xml; using System.Xml;
using System.Xml.Linq; using System.Xml.Linq;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using Microsoft.Extensions.Options;
namespace sodoff.Services { namespace sodoff.Services {
public class AchievementStoreSingleton { public class AchievementStoreSingleton {
@ -34,7 +36,7 @@ namespace sodoff.Services {
int dragonAdultMinXP; int dragonAdultMinXP;
int dragonTitanMinXP; int dragonTitanMinXP;
public AchievementStoreSingleton() { public AchievementStoreSingleton(IOptions<ApiServerConfig> config) {
ArrayOfUserRank allranks = XmlUtil.DeserializeXml<ArrayOfUserRank>(XmlUtil.ReadResourceXmlString("ranks.allranks_sod")); ArrayOfUserRank allranks = XmlUtil.DeserializeXml<ArrayOfUserRank>(XmlUtil.ReadResourceXmlString("ranks.allranks_sod"));
foreach (var pointType in Enum.GetValues<AchievementPointTypes>()) { foreach (var pointType in Enum.GetValues<AchievementPointTypes>()) {
ranks[pointType] = allranks.UserRank.Where(r => r.PointTypeID == pointType).ToArray(); ranks[pointType] = allranks.UserRank.Where(r => r.PointTypeID == pointType).ToArray();
@ -46,11 +48,13 @@ namespace sodoff.Services {
} }
achievementsTasks[ClientVersion.Min_SoD] = new AchievementTasks("achievements.achievementtaskinfo_sod"); achievementsTasks[ClientVersion.Min_SoD] = new AchievementTasks("achievements.achievementtaskinfo_sod");
achievementsTasks[ClientVersion.MaM] = new AchievementTasks("achievements.achievementtaskinfo_mam"); if (config.Value.LoadNonSoDData) {
achievementsTasks[ClientVersion.MB] = new AchievementTasks("achievements.achievementtaskinfo_mb"); achievementsTasks[ClientVersion.MaM] = new AchievementTasks("achievements.achievementtaskinfo_mam");
achievementsTasks[ClientVersion.EMD] = new AchievementTasks("achievements.achievementtaskinfo_emd"); achievementsTasks[ClientVersion.MB] = new AchievementTasks("achievements.achievementtaskinfo_mb");
achievementsTasks[ClientVersion.SS] = new AchievementTasks("achievements.achievementtaskinfo_ss"); achievementsTasks[ClientVersion.EMD] = new AchievementTasks("achievements.achievementtaskinfo_emd");
achievementsTasks[ClientVersion.WoJS] = new AchievementTasks("achievements.achievementtaskinfo_wojs"); achievementsTasks[ClientVersion.SS] = new AchievementTasks("achievements.achievementtaskinfo_ss");
achievementsTasks[ClientVersion.WoJS] = new AchievementTasks("achievements.achievementtaskinfo_wojs");
}
dragonAdultMinXP = ranks[AchievementPointTypes.DragonXP][10].Value; dragonAdultMinXP = ranks[AchievementPointTypes.DragonXP][10].Value;
dragonTitanMinXP = ranks[AchievementPointTypes.DragonXP][20].Value; dragonTitanMinXP = ranks[AchievementPointTypes.DragonXP][20].Value;

View File

@ -54,24 +54,25 @@ public class MissionService {
return mission; return mission;
} }
public Schema.UserMissionData GetUserMissionData(Viking viking, int worldId) public Schema.UserMissionData GetUserMissionData(Viking viking, int worldId) {
{ Schema.UserMissionData umdRes = new();
Schema.UserMissionData umdRes = new Schema.UserMissionData();
// instantiate schema lists and int lists // instantiate schema lists and int lists
List<int> userMissionsCompletedIds = new List<int>(); List<int> userMissionsCompletedIds = new();
List<UserMissionDataMission> missions = new List<UserMissionDataMission>(); List<UserMissionDataMission> missions = new();
List<UserMissionDataMissionStep> steps = new List<UserMissionDataMissionStep>();
List<int> tasks = new List<int>();
// get all initiated missions // get all initiated missions
List<Model.UserMissionData> vikingUmds = viking.UserMissions.Where(e => e.WorldId == worldId).ToList(); List<Model.UserMissionData> vikingUmds = viking.UserMissions.Where(e => e.WorldId == worldId).ToList();
foreach (Model.UserMissionData mission in vikingUmds) foreach (Model.UserMissionData mission in vikingUmds) {
{ missions.Add(new UserMissionDataMission {
tasks.Add(mission.TaskId); MissionId = mission.MissionId,
steps.Add(new UserMissionDataMissionStep { StepId = mission.StepId, TaskId = tasks.ToArray() }); Step = new UserMissionDataMissionStep[] { new UserMissionDataMissionStep {
missions.Add(new UserMissionDataMission { MissionId = mission.MissionId, Step = steps.ToArray() }); // NOTE: we store in database only last StepId and TaskId this is different behavior than og
StepId = mission.StepId,
TaskId = new int[] {mission.TaskId}
}}
});
} }
// add completed mission id's to usermissionscompletedids // add completed mission id's to usermissionscompletedids
@ -104,37 +105,24 @@ public class MissionService {
return new UserBadge { BadgeId = completedBadgeIds.ToArray() }; return new UserBadge { BadgeId = completedBadgeIds.ToArray() };
} }
public Model.UserMissionData SetOrUpdateUserMissionData(Viking viking, int worldId, int missionId, int stepId, int taskId) public void SetOrUpdateUserMissionData(Viking viking, int worldId, int missionId, int stepId, int taskId) {
{
// find any existing records of this mission // find any existing records of this mission
Model.UserMissionData? existingMission = viking.UserMissions.Where(e => e.WorldId == worldId) Model.UserMissionData? missionData = viking.UserMissions.Where(e => e.WorldId == worldId)
.Where(e => e.MissionId == missionId) .Where(e => e.MissionId == missionId)
.FirstOrDefault(); .FirstOrDefault();
if (existingMission != null) if (missionData != null) {
{ missionData.StepId = stepId;
// update taskid and stepid missionData.TaskId = taskId;
existingMission.StepId = stepId; } else {
existingMission.TaskId = taskId; viking.UserMissions.Add(new Model.UserMissionData() {
ctx.SaveChanges(); WorldId = worldId,
MissionId = missionId,
return existingMission; StepId = stepId,
TaskId = taskId
});
} }
// add mission data to db
Model.UserMissionData missionData = new Model.UserMissionData()
{
WorldId = worldId,
MissionId = missionId,
StepId = stepId,
TaskId = taskId
};
viking.UserMissions.Add(missionData);
ctx.SaveChanges(); ctx.SaveChanges();
return missionData;
} }
public bool SetUserMissionCompleted(Viking viking, int worldId, int missionId, bool isCompleted) public bool SetUserMissionCompleted(Viking viking, int worldId, int missionId, bool isCompleted)