support for SoD old tutorial and for Magic And Mythies (#17)

* old tutorial, works on 3.0.0 (partial on 2.9 also)

thanks to BrokenTV for quest data

* support for Magic And Mythies

* oldclients - avatar fix for 3.12

* register and create viking on 1.13

* fix Data Validation Failed on viking create in 2.9

* fix tutorial on 2.9

- fix opening cryptex cage
- replace initial dialogs (Grimmel version) by old version (based on wiki info)
- replace final dialogs (Harald bomb reference)

* missions support and better init profile on 1.13

- add support for mission in old SoD
- add stub for "New Student" quest for pre 2.3
- properly set HubBerkDOFirstCommon (via tutorial/quest, not via RegisterChild)
  - this allow show intro movie
- allow select and hatch first dragon (in hatchery as part of New Student quest)
  - this also fix stables on 1.13

* disable mmo on old clients
This commit is contained in:
rpaciorek 2023-09-18 17:22:45 +00:00 committed by GitHub
parent c7b3a74390
commit 44ea10ed71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1948 additions and 62 deletions

View File

@ -119,6 +119,22 @@ public class AchievementController : Controller {
return null;
}
[HttpPost]
[Produces("application/xml")]
[Route("AchievementWebService.asmx/GetUserAchievements")] // used by Magic & Mythies
[VikingSession]
public IActionResult GetUserAchievements(Viking viking) {
ArrayOfUserAchievementInfo arrAchievements = new ArrayOfUserAchievementInfo {
UserAchievementInfo = new UserAchievementInfo[]{
achievementService.CreateUserAchievementInfo(viking, AchievementPointTypes.PlayerXP),
achievementService.CreateUserAchievementInfo(viking.Id, 60000, AchievementPointTypes.PlayerFarmingXP), // TODO: placeholder until there is no leveling for farm XP
achievementService.CreateUserAchievementInfo(viking.Id, 20000, AchievementPointTypes.PlayerFishingXP), // TODO: placeholder until there is no leveling for fishing XP
}
};
return Ok(arrAchievements);
}
[HttpPost]
[Produces("application/xml")]
[Route("AchievementWebService.asmx/SetAchievementAndGetReward")]

View File

@ -86,7 +86,7 @@ public class AuthenticationController : Controller {
[HttpPost]
[Produces("application/xml")]
[Route("AuthenticationWebService.asmx/GetUserInfoByApiToken")]
public IActionResult GetUserInfoByApiToken([FromForm] string apiToken) {
public IActionResult GetUserInfoByApiToken([FromForm] string apiToken, [FromForm] string apiKey) {
// First check if this is a user session
User? user = ctx.Sessions.FirstOrDefault(e => e.ApiToken == apiToken)?.User;
if (user is not null) {
@ -95,7 +95,8 @@ public class AuthenticationController : Controller {
Username = user.Username,
MembershipID = "ef84db9-59c6-4950-b8ea-bbc1521f899b", // placeholder
FacebookUserID = 0,
MultiplayerEnabled = true,
MultiplayerEnabled = (apiKey != "a1a13a0a-7c6e-4e9b-b0f7-22034d799013" && apiKey != "a2a09a0a-7c6e-4e9b-b0f7-22034d799013" && apiKey != "a3a12a0a-7c6e-4e9b-b0f7-22034d799013"),
IsApproved = true,
Age = 24,
OpenChatEnabled = true
});
@ -108,7 +109,9 @@ public class AuthenticationController : Controller {
return Ok(new UserInfo {
UserID = viking.Id,
Username = viking.Name,
MultiplayerEnabled = true,
FacebookUserID = 0,
MultiplayerEnabled = (apiKey != "a1a13a0a-7c6e-4e9b-b0f7-22034d799013" && apiKey != "a2a09a0a-7c6e-4e9b-b0f7-22034d799013" && apiKey != "a3a12a0a-7c6e-4e9b-b0f7-22034d799013"),
IsApproved = true,
Age = 24,
OpenChatEnabled = true
});

View File

@ -9,8 +9,12 @@ public class ConfigurationController : Controller {
[HttpPost]
//[Produces("application/xml")]
[Route("ConfigurationWebService.asmx/GetMMOServerInfoWithZone")]
public IActionResult GetMMOServerInfoWithZone() {
public IActionResult GetMMOServerInfoWithZone([FromForm] string apiKey) {
// TODO: this is a placeholder
if (apiKey == "A1A13A0A-7C6E-4E9B-B0F7-22034D799013" || apiKey == "A2A09A0A-7C6E-4E9B-B0F7-22034D799013" || apiKey == "A3A12A0A-7C6E-4E9B-B0F7-22034D799013") { // NOTE: in this request apiKey is send uppercase
// do not send MMO servers to old (incompatibility with MMO server) client
return Ok(XmlUtil.SerializeXml(new MMOServerInformation[0]));
}
return Ok(XmlUtil.ReadResourceXmlString("mmo"));
}
}

View File

@ -230,26 +230,33 @@ public class ContentController : Controller {
return Ok(new DateTime(DateTime.Now.Ticks));
}
private int GetAvatarVersion(AvatarData avatarData) {
foreach (AvatarDataPart part in avatarData.Part) {
if (part.PartType == "Version") {
return (int)part.Offsets[0].X * 100 + (int)part.Offsets[0].Y * 10 + (int)part.Offsets[0].Z;
}
}
return 0;
}
[HttpPost]
[Produces("application/xml")]
[Route("V2/ContentWebService.asmx/SetAvatar")]
[VikingSession]
public IActionResult SetAvatar(Viking viking, [FromForm] string contentXML) {
AvatarData avatarData = XmlUtil.DeserializeXml<AvatarData>(contentXML);
foreach (AvatarDataPart part in avatarData.Part) {
if (part.PartType == "Version") {
if (part.Offsets[0].X < 6 || part.Offsets[0].X == 6 && part.Offsets[0].Y < 1) {
// do not allow to save avatar data from old clients (avatar data version < 6.1) ... it's broke profile on 3.31
// but return as true to trick the client to avoid re-show change viking name dialog
// TODO: maybe better set pair AvatarNameCustomizationDone -> 1 (in "2017" pairs) and return error here?
if (viking.AvatarSerialized != null) {
AvatarData dbAvatarData = XmlUtil.DeserializeXml<AvatarData>(viking.AvatarSerialized);
AvatarData reqAvatarData = XmlUtil.DeserializeXml<AvatarData>(contentXML);
int dbAvatarVersion = GetAvatarVersion(dbAvatarData);
int reqAvatarVersion = GetAvatarVersion(reqAvatarData);
if (dbAvatarVersion > reqAvatarVersion) {
// do not allow override newer version avatar data by older version
return Ok(new SetAvatarResult {
Success = true,
DisplayName = viking.Name,
StatusCode = AvatarValidationResult.Valid
Success = false,
});
}
break;
}
}
viking.AvatarSerialized = contentXML;
@ -323,6 +330,30 @@ public class ContentController : Controller {
});
}
[HttpPost]
[Produces("application/xml")]
[Route("V2/ContentWebService.asmx/SetRaisedPet")] // used by Magic & Mythies
[VikingSession]
public IActionResult SetRaisedPetv2(Viking viking, [FromForm] string raisedPetData) {
RaisedPetData petData = XmlUtil.DeserializeXml<RaisedPetData>(raisedPetData);
// Find the dragon
Dragon? dragon = viking.Dragons.FirstOrDefault(e => e.Id == petData.RaisedPetID);
if (dragon is null) {
return Ok(new SetRaisedPetResponse {
RaisedPetSetResult = RaisedPetSetResult.Invalid
});
}
dragon.RaisedPetData = XmlUtil.SerializeXml(UpdateDragon(dragon, petData));
ctx.Update(dragon);
ctx.SaveChanges();
return Ok(new SetRaisedPetResponse {
RaisedPetSetResult = RaisedPetSetResult.Success
});
}
[HttpPost]
[Produces("application/xml")]
[Route("v3/ContentWebService.asmx/SetRaisedPet")]
@ -392,7 +423,7 @@ public class ContentController : Controller {
[HttpPost]
[Produces("application/xml")]
[Route("ContentWebService.asmx/GetUnselectedPetByTypes")]
[Route("ContentWebService.asmx/GetUnselectedPetByTypes")] // used by old SoD (e.g. 1.13)
[VikingSession(UseLock=false)]
public RaisedPetData[]? GetUnselectedPetByTypes(Viking viking, [FromForm] string petTypeIDs, [FromForm] bool active) {
RaisedPetData[] dragons = viking.Dragons
@ -491,14 +522,14 @@ public class ContentController : Controller {
[HttpPost]
[Produces("application/xml")]
[Route("V2/ContentWebService.asmx/GetUserUpcomingMissionState")]
public IActionResult GetUserUpcomingMissionState([FromForm] string apiToken, [FromForm] string userId) {
public IActionResult GetUserUpcomingMissionState([FromForm] string apiToken, [FromForm] string userId, [FromForm] string apiKey) {
Viking? viking = ctx.Vikings.FirstOrDefault(x => x.Id == userId);
if (viking is null)
return Ok("error");
UserMissionStateResult result = new UserMissionStateResult { Missions = new List<Mission>() };
foreach (var mission in viking.MissionStates.Where(x => x.MissionStatus == MissionStatus.Upcoming))
result.Missions.Add(missionService.GetMissionWithProgress(mission.MissionId, viking.Id));
result.Missions.Add(missionService.GetMissionWithProgress(mission.MissionId, viking.Id, apiKey));
result.UserID = Guid.Parse(viking.Id);
return Ok(result);
@ -507,14 +538,14 @@ public class ContentController : Controller {
[HttpPost]
[Produces("application/xml")]
[Route("V2/ContentWebService.asmx/GetUserActiveMissionState")]
public IActionResult GetUserActiveMissionState([FromForm] string apiToken, [FromForm] string userId) {
public IActionResult GetUserActiveMissionState([FromForm] string apiToken, [FromForm] string userId, [FromForm] string apiKey) {
Viking? viking = ctx.Vikings.FirstOrDefault(x => x.Id == userId);
if (viking is null)
return Ok("error");
UserMissionStateResult result = new UserMissionStateResult { Missions = new List<Mission>() };
foreach (var mission in viking.MissionStates.Where(x => x.MissionStatus == MissionStatus.Active)) {
Mission updatedMission = missionService.GetMissionWithProgress(mission.MissionId, viking.Id);
Mission updatedMission = missionService.GetMissionWithProgress(mission.MissionId, viking.Id, apiKey);
if (mission.UserAccepted != null)
updatedMission.Accepted = (bool)mission.UserAccepted;
result.Missions.Add(updatedMission);
@ -527,14 +558,14 @@ public class ContentController : Controller {
[HttpPost]
[Produces("application/xml")]
[Route("V2/ContentWebService.asmx/GetUserCompletedMissionState")]
public IActionResult GetUserCompletedMissionState([FromForm] string apiToken, [FromForm] string userId) {
public IActionResult GetUserCompletedMissionState([FromForm] string apiToken, [FromForm] string userId, [FromForm] string apiKey) {
Viking? viking = ctx.Vikings.FirstOrDefault(x => x.Id == userId);
if (viking is null)
return Ok("error");
UserMissionStateResult result = new UserMissionStateResult { Missions = new List<Mission>() };
foreach (var mission in viking.MissionStates.Where(x => x.MissionStatus == MissionStatus.Completed))
result.Missions.Add(missionService.GetMissionWithProgress(mission.MissionId, viking.Id));
result.Missions.Add(missionService.GetMissionWithProgress(mission.MissionId, viking.Id, apiKey));
result.UserID = Guid.Parse(viking.Id);
return Ok(result);
@ -560,18 +591,19 @@ public class ContentController : Controller {
[HttpPost]
[Produces("application/xml")]
[Route("V2/ContentWebService.asmx/GetUserMissionState")]
//[VikingSession(UseLock=false)]
public IActionResult GetUserMissionState([FromForm] string userId, [FromForm] string filter) {
MissionRequestFilterV2 filterV2 = XmlUtil.DeserializeXml<MissionRequestFilterV2>(filter);
[Route("ContentWebService.asmx/GetUserMissionState")] // used by SoD 1.13
public IActionResult GetUserMissionStatev1([FromForm] string userId, [FromForm] string filter, [FromForm] string apiKey) {
Viking? viking = ctx.Vikings.FirstOrDefault(x => x.Id == userId);
if (viking is null)
return Ok("error");
UserMissionStateResult result = new UserMissionStateResult { Missions = new List<Mission>() };
foreach (var m in filterV2.MissionPair)
if (m.MissionID != null)
result.Missions.Add(missionService.GetMissionWithProgress((int)m.MissionID, viking.Id));
foreach (var mission in viking.MissionStates.Where(x => x.MissionStatus != MissionStatus.Completed)) {
Mission updatedMission = missionService.GetMissionWithProgress(mission.MissionId, viking.Id, apiKey);
if (mission.UserAccepted != null)
updatedMission.Accepted = (bool)mission.UserAccepted;
result.Missions.Add(updatedMission);
}
result.UserID = Guid.Parse(viking.Id);
return Ok(result);
@ -579,13 +611,64 @@ public class ContentController : Controller {
[HttpPost]
[Produces("application/xml")]
[Route("V2/ContentWebService.asmx/SetTaskState")]
[VikingSession]
public IActionResult SetTaskState(Viking viking, [FromForm] string userId, [FromForm] int missionId, [FromForm] int taskId, [FromForm] bool completed, [FromForm] string xmlPayload) {
[Route("V2/ContentWebService.asmx/GetUserMissionState")]
//[VikingSession(UseLock=false)]
public IActionResult GetUserMissionState([FromForm] string userId, [FromForm] string filter, [FromForm] string apiKey) {
MissionRequestFilterV2 filterV2 = XmlUtil.DeserializeXml<MissionRequestFilterV2>(filter);
Viking? viking = ctx.Vikings.FirstOrDefault(x => x.Id == userId);
if (viking is null)
return Ok("error");
UserMissionStateResult result = new UserMissionStateResult { Missions = new List<Mission>() };
if (filterV2.MissionPair.Count > 0) {
foreach (var m in filterV2.MissionPair)
if (m.MissionID != null)
result.Missions.Add(missionService.GetMissionWithProgress((int)m.MissionID, viking.Id, apiKey));
// TODO: probably should also check for msiion based on filterV2.ProductGroupID vs mission.GroupID
} else {
if (filterV2.GetCompletedMission ?? false) {
foreach (var mission in viking.MissionStates.Where(x => x.MissionStatus == MissionStatus.Completed))
result.Missions.Add(missionService.GetMissionWithProgress(mission.MissionId, viking.Id, apiKey));
} else {
foreach (var mission in viking.MissionStates.Where(x => x.MissionStatus != MissionStatus.Completed))
result.Missions.Add(missionService.GetMissionWithProgress(mission.MissionId, viking.Id, apiKey));
}
}
result.UserID = Guid.Parse(viking.Id);
return Ok(result);
}
[HttpPost]
[Produces("application/xml")]
[Route("ContentWebService.asmx/SetTaskState")] // used by SoD 1.13
[VikingSession(UseLock=true)]
public IActionResult SetTaskStatev1(Viking viking, [FromForm] string userId, [FromForm] int missionId, [FromForm] int taskId, [FromForm] bool completed, [FromForm] string xmlPayload, [FromForm] string apiKey) {
if (viking.Id != userId)
return Unauthorized("Can't set not owned task");
List<MissionCompletedResult> results = missionService.UpdateTaskProgress(missionId, taskId, userId, completed, xmlPayload);
List<MissionCompletedResult> results = missionService.UpdateTaskProgress(missionId, taskId, userId, completed, xmlPayload, apiKey);
SetTaskStateResult taskResult = new SetTaskStateResult {
Success = true,
Status = SetTaskStateStatus.TaskCanBeDone,
};
if (results.Count > 0)
taskResult.MissionsCompleted = results.ToArray();
return Ok(taskResult);
}
[HttpPost]
[Produces("application/xml")]
[Route("V2/ContentWebService.asmx/SetTaskState")]
[VikingSession]
public IActionResult SetTaskState(Viking viking, [FromForm] string userId, [FromForm] int missionId, [FromForm] int taskId, [FromForm] bool completed, [FromForm] string xmlPayload, [FromForm] string apiKey) {
if (viking.Id != userId)
return Unauthorized("Can't set not owned task");
List<MissionCompletedResult> results = missionService.UpdateTaskProgress(missionId, taskId, userId, completed, xmlPayload, apiKey);
SetTaskStateResult taskResult = new SetTaskStateResult {
Success = true,

View File

@ -25,9 +25,12 @@ public class MembershipController : Controller {
[HttpPost]
[Produces("application/xml")]
[Route("MembershipWebService.asmx/GetChildList")]
[Route("MembershipWebService.asmx/GetChildList")] // used by old SoD (e.g. 2.9)
[VikingSession(Mode=VikingSession.Modes.USER, UseLock=false)]
public IActionResult GetChildList(User user) {
if (user.Vikings.Count <= 0)
return Ok();
ChildList profiles = new ChildList();
profiles.strings = user.Vikings.Select(viking => viking.Id + ", " + viking.Name).ToArray();

View File

@ -19,7 +19,7 @@ public class ProfileController : Controller {
[HttpPost]
[Produces("application/xml")]
[Route("ProfileWebService.asmx/GetUserProfileByUserID")]
public IActionResult GetUserProfileByUserID([FromForm] string userId) {
public IActionResult GetUserProfileByUserID([FromForm] string userId, [FromForm] string apiKey) {
// NOTE: this is public info (for mmo) - no session check
Viking? viking = ctx.Vikings.FirstOrDefault(e => e.Id == userId);
@ -29,26 +29,26 @@ public class ProfileController : Controller {
// (not Ok response cause soft-lock client - can't close error message)
}
return Ok(GetProfileDataFromViking(viking));
return Ok(GetProfileDataFromViking(viking, apiKey));
}
[HttpPost]
[Produces("application/xml")]
[Route("ProfileWebService.asmx/GetUserProfile")]
[VikingSession(UseLock=false)]
public IActionResult GetUserProfile(Viking viking) {
return Ok(GetProfileDataFromViking(viking));
public IActionResult GetUserProfile(Viking viking, [FromForm] string apiKey) {
return Ok(GetProfileDataFromViking(viking, apiKey));
}
[HttpPost]
[Produces("application/xml")]
[Route("ProfileWebService.asmx/GetDetailedChildList")]
[VikingSession(Mode=VikingSession.Modes.USER, ApiToken="parentApiToken", UseLock=false)]
public Schema.UserProfileDataList? GetDetailedChildList(User user) {
public Schema.UserProfileDataList? GetDetailedChildList(User user, [FromForm] string apiKey) {
if (user.Vikings.Count <= 0)
return null;
UserProfileData[] profiles = user.Vikings.Select(GetProfileDataFromViking).ToArray();
UserProfileData[] profiles = user.Vikings.Select(v => GetProfileDataFromViking(v, apiKey)).ToArray();
return new UserProfileDataList {
UserProfiles = profiles
};
@ -100,7 +100,15 @@ public class ProfileController : Controller {
});
}
private UserProfileData GetProfileDataFromViking(Viking viking) {
[HttpPost]
//[Produces("application/xml")]
[Route("ProfileWebService.asmx/GetProfileTagAll")] // used by Magic & Mythies
public IActionResult GetProfileTagAll() {
// TODO: This is a placeholder
return Ok("<?xml version='1.0' encoding='UTF-8'?><ArrayOfProfileTag xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:nil=\"true\"/>");
}
private UserProfileData GetProfileDataFromViking(Viking viking, [FromForm] string apiKey) {
// Get the avatar data
AvatarData avatarData = null;
if (viking.AvatarSerialized is not null) {
@ -108,6 +116,20 @@ public class ProfileController : Controller {
avatarData.Id = viking.Inventory.Id;
}
if (avatarData != null && (apiKey == "a3a12a0a-7c6e-4e9b-b0f7-22034d799013")) {
if (avatarData.Part.FirstOrDefault(e => e.PartType == "Sword") is null) {
var extraParts = new AvatarDataPart[] {
new AvatarDataPart {
PartType = "Sword",
Geometries = new string[] {"NULL"},
Textures = new string[] {"__EMPTY__"},
UserInventoryId = null,
}
};
avatarData.Part = extraParts.Concat(avatarData.Part).ToArray();
}
}
// Build the AvatarDisplayData
AvatarDisplayData avatar = new AvatarDisplayData {
AvatarData = avatarData,
@ -117,7 +139,7 @@ public class ProfileController : Controller {
ParentUserID = viking.UserId,
Username = viking.Name,
FirstName = viking.Name,
MultiplayerEnabled = true,
MultiplayerEnabled = (apiKey != "a1a13a0a-7c6e-4e9b-b0f7-22034d799013" && apiKey != "a2a09a0a-7c6e-4e9b-b0f7-22034d799013" && apiKey != "a3a12a0a-7c6e-4e9b-b0f7-22034d799013"),
Locale = "en-US", // placeholder
GenderID = Gender.Male, // placeholder
OpenChatEnabled = true,

View File

@ -14,12 +14,14 @@ public class RegistrationController : Controller {
private ItemService itemService;
private MissionService missionService;
private RoomService roomService;
private KeyValueService keyValueService;
public RegistrationController(DBContext ctx, ItemService itemService, MissionService missionService, RoomService roomService) {
public RegistrationController(DBContext ctx, ItemService itemService, MissionService missionService, RoomService roomService, KeyValueService keyValueService) {
this.ctx = ctx;
this.itemService = itemService;
this.missionService = missionService;
this.roomService = roomService;
this.keyValueService = keyValueService;
}
[HttpPost]
@ -91,10 +93,11 @@ public class RegistrationController : Controller {
[HttpPost]
[Produces("application/xml")]
[Route("V3/RegistrationWebService.asmx/RegisterChild")] // used by Magic & Mythies
[Route("V4/RegistrationWebService.asmx/RegisterChild")]
[DecryptRequest("childRegistrationData")]
[EncryptResponse]
public IActionResult RegisterChild([FromForm] string parentApiToken) {
public IActionResult RegisterChild([FromForm] string parentApiToken, [FromForm] string apiKey) {
User? user = ctx.Sessions.FirstOrDefault(e => e.ApiToken == parentApiToken)?.User;
if (user is null) {
return Ok(new RegistrationResult{
@ -115,6 +118,7 @@ public class RegistrationController : Controller {
Inventory inv = new Inventory { InventoryItems = new List<InventoryItem>() };
inv.InventoryItems.Add(new InventoryItem { ItemId = 8977, Quantity = 1 }); // DragonStableINTDO - Dragons Dragon Stable
Viking v = new Viking {
Id = Guid.NewGuid().ToString(),
Name = data.ChildName,
@ -124,9 +128,21 @@ public class RegistrationController : Controller {
Rooms = new List<Room>()
};
missionService.SetUpMissions(v);
missionService.SetUpMissions(v, apiKey);
ctx.Vikings.Add(v);
if (apiKey == "a1a13a0a-7c6e-4e9b-b0f7-22034d799013") {
keyValueService.SetPairData(null, v, null, 2017, new Schema.PairData {
Pairs = new Schema.Pair[]{
new Schema.Pair {
// avoid show change viking name dialog
PairKey = "AvatarNameCustomizationDone",
PairValue = "1"
},
}
});
}
ctx.SaveChanges();
roomService.CreateRoom(v, "MyRoomINT");

View File

@ -1,5 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<DefaultMissions>
<!-- list of default mission for most versions (2.9, 3.12, 3.31) -->
<Active>
<id>999</id>
<id>1035</id>

View File

@ -0,0 +1,700 @@
<?xml version='1.0' encoding='utf-8'?>
<DefaultMissions>
<!-- list of default mission for oldest versions (1.13)
* must not contain contain missions with Type == 7 <Prerequisites> (causes xml deserialize error in client)
* "New Student" (999) must be Upcoming not Active mission
* should not contain any visible missions not supported in oldest versions
* TODO: remove all missions not supported in oldest versions
-->
<Active>
<id>1035</id>
<id>1036</id>
<id>1037</id>
<id>1046</id>
<id>1047</id>
<id>1048</id>
<id>1185</id>
<id>1186</id>
<id>1187</id>
<id>1188</id>
<id>1189</id>
<id>1191</id>
<id>1192</id>
<id>1193</id>
<id>1194</id>
<id>1195</id>
<id>1196</id>
<id>1197</id>
<id>1198</id>
<id>1199</id>
<id>1200</id>
<id>1201</id>
<id>1202</id>
<id>1203</id>
<id>1204</id>
<id>1205</id>
<id>1206</id>
<id>1207</id>
<id>1208</id>
<id>1209</id>
<id>1210</id>
<id>1211</id>
<id>1212</id>
<id>1213</id>
<id>1214</id>
<id>1215</id>
<id>1216</id>
<id>1217</id>
<id>1218</id>
<id>1219</id>
<id>1220</id>
<id>1221</id>
<id>1222</id>
<id>1223</id>
<id>1224</id>
<id>1225</id>
<id>1226</id>
<id>1227</id>
<id>1228</id>
<id>1229</id>
<id>1230</id>
<id>1231</id>
<id>1232</id>
<id>1233</id>
<id>1234</id>
<id>1235</id>
<id>1236</id>
<id>1237</id>
<id>1238</id>
<id>1239</id>
<id>1240</id>
<id>1241</id>
<id>1242</id>
<id>1243</id>
<id>1244</id>
<id>1245</id>
<id>1250</id>
<id>1251</id>
<id>1252</id>
<id>1253</id>
<id>1254</id>
<id>1255</id>
<id>1256</id>
<id>1257</id>
<id>1258</id>
<id>1259</id>
<id>1260</id>
<id>1261</id>
<id>1262</id>
<id>1263</id>
<id>1264</id>
<id>1265</id>
<id>1266</id>
<id>1267</id>
<id>1268</id>
<id>1269</id>
<id>1270</id>
<id>1271</id>
<id>1272</id>
<id>1273</id>
<id>1274</id>
<id>1275</id>
<id>1276</id>
<id>1277</id>
<id>1278</id>
<id>1279</id>
<id>1280</id>
<id>1281</id>
<id>1282</id>
<id>1283</id>
<id>1284</id>
<id>1285</id>
<id>1286</id>
<id>1287</id>
<id>1288</id>
<id>1289</id>
<id>1290</id>
<id>1291</id>
<id>1292</id>
<id>1293</id>
<id>1294</id>
<id>1295</id>
<id>1307</id>
<id>1308</id>
<id>1309</id>
<id>1310</id>
<id>1311</id>
<id>1312</id>
<id>1313</id>
<id>1314</id>
<id>1315</id>
<id>1316</id>
<id>1321</id>
<id>1322</id>
<id>1323</id>
<id>1324</id>
<id>1325</id>
<id>1326</id>
<id>1327</id>
<id>1328</id>
<id>1329</id>
<id>1330</id>
<id>1345</id>
<id>1346</id>
<id>1347</id>
<id>1348</id>
<id>1349</id>
<id>1350</id>
<id>1351</id>
<id>1352</id>
<id>1353</id>
<id>1354</id>
<id>1689</id>
<id>1690</id>
<id>1691</id>
<id>1692</id>
<id>1693</id>
<id>1694</id>
<id>1695</id>
<id>1696</id>
<id>1697</id>
<id>1698</id>
<id>1699</id>
<id>1700</id>
<id>1701</id>
<id>1702</id>
<id>1703</id>
<id>1704</id>
<id>1705</id>
<id>1706</id>
<id>1707</id>
<id>1708</id>
<id>1709</id>
<id>1710</id>
<id>1711</id>
<id>1712</id>
<id>1713</id>
<id>1714</id>
<id>1715</id>
<id>1716</id>
<id>1717</id>
<id>1718</id>
<id>1719</id>
<id>1720</id>
<id>1721</id>
<id>1722</id>
<id>1723</id>
<id>1724</id>
<id>1725</id>
<id>1726</id>
<id>1727</id>
<id>1728</id>
<id>1729</id>
<id>1730</id>
<id>2176</id>
<id>2287</id>
<id>2303</id>
<id>2304</id>
<id>2309</id>
<id>2392</id>
<id>2393</id>
<id>2394</id>
<id>2395</id>
<id>2396</id>
<id>2397</id>
<id>2398</id>
<id>2399</id>
<id>2400</id>
<id>2401</id>
<id>2402</id>
<id>2403</id>
<id>2404</id>
<id>2405</id>
<id>2406</id>
<id>2407</id>
<id>2421</id>
<id>2422</id>
<id>2424</id>
<id>2425</id>
<id>2426</id>
<id>2427</id>
<id>2428</id>
<id>2429</id>
<id>2430</id>
<id>2431</id>
<id>2432</id>
<id>2433</id>
<id>2434</id>
<id>2435</id>
<id>2436</id>
<id>2437</id>
<id>2438</id>
<id>2439</id>
<id>2457</id>
<id>2458</id>
<id>2510</id>
<id>2511</id>
<id>2512</id>
<id>2513</id>
<id>2514</id>
<id>2515</id>
<id>2516</id>
<id>2517</id>
<id>2518</id>
<id>2519</id>
<id>2520</id>
<id>2521</id>
<id>2562</id>
<id>2563</id>
<id>2564</id>
<id>2565</id>
<id>2566</id>
<id>2567</id>
<id>2568</id>
<id>2569</id>
<id>2570</id>
<id>2571</id>
<id>2572</id>
<id>2573</id>
<id>2578</id>
<id>2579</id>
<id>2580</id>
<id>2581</id>
<id>2582</id>
<id>2583</id>
<id>2584</id>
<id>2585</id>
<id>2586</id>
<id>2587</id>
<id>2588</id>
<id>2606</id>
<id>2607</id>
<id>2631</id>
<id>2632</id>
<id>2633</id>
<id>2634</id>
<id>2635</id>
<id>2636</id>
<id>2637</id>
<id>2638</id>
<id>2639</id>
<id>2640</id>
<id>2645</id>
<id>2646</id>
<id>2649</id>
<id>2650</id>
<id>2652</id>
<id>2653</id>
<id>2655</id>
<id>2656</id>
<id>2661</id>
<id>2662</id>
<id>2663</id>
<id>2664</id>
<id>2665</id>
<id>2666</id>
<id>2667</id>
<id>2668</id>
<id>2669</id>
<id>2670</id>
<id>2673</id>
<id>2674</id>
<id>2675</id>
<id>2676</id>
<id>2677</id>
<id>2678</id>
<id>2679</id>
<id>2680</id>
<id>2681</id>
<id>2682</id>
<id>2683</id>
<id>2684</id>
<id>2685</id>
<id>2686</id>
<id>2687</id>
<id>2786</id>
<id>2787</id>
<id>2790</id>
<id>2806</id>
<id>2807</id>
<id>2808</id>
<id>2809</id>
<id>2810</id>
<id>2811</id>
<id>2812</id>
<id>2813</id>
<id>2814</id>
<id>2815</id>
<id>2816</id>
<id>2817</id>
<id>2818</id>
<id>2819</id>
<id>2843</id>
<id>2844</id>
<id>2845</id>
<id>2878</id>
<id>2879</id>
<id>2894</id>
<id>2895</id>
<id>2931</id>
<id>2932</id>
<id>2946</id>
<id>2947</id>
<id>2972</id>
<id>2973</id>
<id>3022</id>
<id>3023</id>
<id>3036</id>
<id>3037</id>
<id>3052</id>
<id>3053</id>
<id>3071</id>
<id>3072</id>
<id>3073</id>
<id>3074</id>
<id>3075</id>
<id>3076</id>
<id>3077</id>
<id>3078</id>
<id>3079</id>
<id>3080</id>
<id>3099</id>
<id>3100</id>
<id>3113</id>
<id>3114</id>
<id>3115</id>
<id>3116</id>
<id>3117</id>
<id>3118</id>
<id>3119</id>
<id>3120</id>
<id>3121</id>
<id>3122</id>
<id>3123</id>
<id>3124</id>
<id>3127</id>
<id>3128</id>
<id>3129</id>
<id>3130</id>
<id>3131</id>
<id>3132</id>
<id>3133</id>
<id>3134</id>
<id>3135</id>
<id>3136</id>
<id>3148</id>
<id>3149</id>
</Active>
<Upcoming>
<id>999</id>
<id>1003</id>
<id>1014</id>
<id>1015</id>
<id>1016</id>
<id>1017</id>
<id>1027</id>
<id>1028</id>
<id>1029</id>
<id>1031</id>
<id>1033</id>
<id>1038</id>
<id>1044</id>
<id>1053</id>
<id>1054</id>
<id>1055</id>
<id>1057</id>
<id>1058</id>
<id>1062</id>
<id>1067</id>
<id>1074</id>
<id>1085</id>
<id>1089</id>
<id>1090</id>
<id>1091</id>
<id>1093</id>
<id>1095</id>
<id>1096</id>
<id>1097</id>
<id>1099</id>
<id>1101</id>
<id>1102</id>
<id>1106</id>
<id>1108</id>
<id>1110</id>
<id>1111</id>
<id>1114</id>
<id>1120</id>
<id>1121</id>
<id>1128</id>
<id>1134</id>
<id>1140</id>
<id>1143</id>
<id>1144</id>
<id>1150</id>
<id>1153</id>
<id>1155</id>
<id>1159</id>
<id>1163</id>
<id>1164</id>
<id>1166</id>
<id>1167</id>
<id>1168</id>
<id>1169</id>
<id>1171</id>
<id>1173</id>
<id>1179</id>
<id>1247</id>
<id>1296</id>
<id>1304</id>
<id>1305</id>
<id>1317</id>
<id>1318</id>
<id>1331</id>
<id>1333</id>
<id>1335</id>
<id>1338</id>
<id>1343</id>
<id>1344</id>
<id>1357</id>
<id>1361</id>
<id>1362</id>
<id>1390</id>
<id>1508</id>
<id>1529</id>
<id>1530</id>
<id>1575</id>
<id>1579</id>
<id>1605</id>
<id>1606</id>
<id>1607</id>
<id>1608</id>
<id>1611</id>
<id>1612</id>
<id>1613</id>
<id>1614</id>
<id>1615</id>
<id>1617</id>
<id>1618</id>
<id>1619</id>
<id>1620</id>
<id>1622</id>
<id>1623</id>
<id>1624</id>
<id>1625</id>
<id>1626</id>
<id>1627</id>
<id>1628</id>
<id>1629</id>
<id>1630</id>
<id>1632</id>
<id>1633</id>
<id>1634</id>
<id>1636</id>
<id>1638</id>
<id>1640</id>
<id>1641</id>
<id>1642</id>
<id>1646</id>
<id>1648</id>
<id>1652</id>
<id>1655</id>
<id>1656</id>
<id>1657</id>
<id>1658</id>
<id>1660</id>
<id>1661</id>
<id>1663</id>
<id>1666</id>
<id>1667</id>
<id>1669</id>
<id>1671</id>
<id>1672</id>
<id>1673</id>
<id>1674</id>
<id>1675</id>
<id>1676</id>
<id>1678</id>
<id>1681</id>
<id>1683</id>
<id>1736</id>
<id>1749</id>
<id>1769</id>
<id>1771</id>
<id>1777</id>
<id>1781</id>
<id>1787</id>
<id>1788</id>
<id>1813</id>
<id>1815</id>
<id>1818</id>
<id>1822</id>
<id>1828</id>
<id>1961</id>
<id>1967</id>
<id>1969</id>
<id>1970</id>
<id>1971</id>
<id>1972</id>
<id>1973</id>
<id>1974</id>
<id>1978</id>
<id>2175</id>
<id>2178</id>
<id>2180</id>
<id>2182</id>
<id>2195</id>
<id>2196</id>
<id>2199</id>
<id>2206</id>
<id>2207</id>
<id>2208</id>
<id>2212</id>
<id>2213</id>
<id>2215</id>
<id>2217</id>
<id>2218</id>
<id>2219</id>
<id>2223</id>
<id>2225</id>
<id>2226</id>
<id>2228</id>
<id>2229</id>
<id>2232</id>
<id>2233</id>
<id>2235</id>
<id>2284</id>
<id>2288</id>
<id>2300</id>
<id>2302</id>
<id>2307</id>
<id>2308</id>
<id>2311</id>
<id>2314</id>
<id>2315</id>
<id>2318</id>
<id>2319</id>
<id>2320</id>
<id>2324</id>
<id>2328</id>
<id>2329</id>
<id>2330</id>
<id>2331</id>
<id>2332</id>
<id>2333</id>
<id>2335</id>
<id>2337</id>
<id>2338</id>
<id>2339</id>
<id>2344</id>
<id>2346</id>
<id>2347</id>
<id>2353</id>
<id>2354</id>
<id>2357</id>
<id>2363</id>
<id>2372</id>
<id>2389</id>
<id>2408</id>
<id>2415</id>
<id>2416</id>
<id>2423</id>
<id>2444</id>
<id>2459</id>
<id>2472</id>
<id>2474</id>
<id>2475</id>
<id>2476</id>
<id>2477</id>
<id>2478</id>
<id>2479</id>
<id>2481</id>
<id>2482</id>
<id>2483</id>
<id>2485</id>
<id>2486</id>
<id>2493</id>
<id>2494</id>
<id>2496</id>
<id>2498</id>
<id>2499</id>
<id>2500</id>
<id>2503</id>
<id>2506</id>
<id>2522</id>
<id>2526</id>
<id>2528</id>
<id>2529</id>
<id>2532</id>
<id>2533</id>
<id>2536</id>
<id>2538</id>
<id>2542</id>
<id>2548</id>
<id>2551</id>
<id>2554</id>
<id>2556</id>
<id>2557</id>
<id>2574</id>
<id>2589</id>
<id>2590</id>
<id>2591</id>
<id>2592</id>
<id>2593</id>
<id>2594</id>
<id>2596</id>
<id>2597</id>
<id>2598</id>
<id>2608</id>
<id>2613</id>
<id>2614</id>
<id>2622</id>
<id>2626</id>
<id>2641</id>
<id>2643</id>
<id>2659</id>
<id>2689</id>
<id>2690</id>
<id>2691</id>
<id>2692</id>
<id>2693</id>
<id>2694</id>
<id>2695</id>
<id>2697</id>
<id>2699</id>
<id>2700</id>
<id>2705</id>
<id>2707</id>
<id>2708</id>
<id>2711</id>
<id>2714</id>
<id>2717</id>
<id>2718</id>
<id>2719</id>
<id>2723</id>
<id>2730</id>
<id>2731</id>
<id>2874</id>
<id>2884</id>
<id>2887</id>
<id>2889</id>
<id>2892</id>
<id>2893</id>
<id>2937</id>
<id>2974</id>
<id>2986</id>
<id>2988</id>
<id>2989</id>
<id>2990</id>
<id>2992</id>
<id>2993</id>
<id>2996</id>
<id>3013</id>
<id>3041</id>
<id>3057</id>
<id>3150</id>
<id>3167</id>
<id>3176</id>
<id>3177</id>
<id>3178</id>
</Upcoming>
</DefaultMissions>

File diff suppressed because it is too large Load Diff

View File

@ -95,7 +95,7 @@ public class KeyValueService {
Model.PairData? pair = null;
if (viking != null)
pair = viking.PairData.FirstOrDefault(e => e.PairId == pairId);
pair = viking.PairData?.FirstOrDefault(e => e.PairId == pairId);
else if (user != null)
pair = user.PairData.FirstOrDefault(e => e.PairId == pairId);
else if (dragon != null)

View File

@ -16,13 +16,25 @@ public class MissionService {
this.achievementService = achievementService;
}
public Mission GetMissionWithProgress(int missionId, string userId) {
Mission mission = missionStore.GetMission(missionId);
public Mission GetMissionWithProgress(int missionId, string userId, string apiKey) {
Mission mission;
if (missionId == 999 && apiKey == "a3a12a0a-7c6e-4e9b-b0f7-22034d799013") { // TODO This is not a pretty solution with hard-coded values.
mission = missionStore.GetMission(10999);
mission.MissionID = 999;
} else if (missionId == 999 && apiKey == "a2a09a0a-7c6e-4e9b-b0f7-22034d799013") {
mission = missionStore.GetMission(20999);
mission.MissionID = 999;
} else if (missionId == 999 && apiKey == "a1a13a0a-7c6e-4e9b-b0f7-22034d799013") {
mission = missionStore.GetMission(30999);
mission.MissionID = 999;
} else {
mission = missionStore.GetMission(missionId);
}
UpdateMissionRecursive(mission, userId);
return mission;
}
public List<MissionCompletedResult> UpdateTaskProgress(int missionId, int taskId, string userId, bool completed, string xmlPayload) {
public List<MissionCompletedResult> UpdateTaskProgress(int missionId, int taskId, string userId, bool completed, string xmlPayload, string apiKey) {
SetTaskProgressDB(missionId, taskId, userId, completed, xmlPayload);
// NOTE: This won't work if a mission can be completed by completing an inner mission
@ -36,7 +48,7 @@ public class MissionService {
// I do know that outer missions have inner missions as RuleItems, and if the RuleItem is supposed to be "complete" and it isn't, the quest breaks when the player quits the game and loads the quest again
List<MissionCompletedResult> result = new();
if (completed) {
Mission mission = GetMissionWithProgress(missionId, userId);
Mission mission = GetMissionWithProgress(missionId, userId, apiKey);
if (MissionCompleted(mission)) {
// Update mission from active to completed
Viking viking = ctx.Vikings.FirstOrDefault(x => x.Id == userId)!;
@ -100,16 +112,17 @@ public class MissionService {
mission.Completed = 1;
}
public void SetUpMissions(Viking viking) {
public void SetUpMissions(Viking viking, string apiKey) {
viking.MissionStates = new List<MissionState>();
foreach (int m in missionStore.GetActiveMissions()) {
foreach (int m in missionStore.GetActiveMissions(apiKey)) {
viking.MissionStates.Add(new MissionState {
MissionId = m,
MissionStatus = MissionStatus.Active
});
}
foreach (int m in missionStore.GetUpcomingMissions()) {
foreach (int m in missionStore.GetUpcomingMissions(apiKey)) {
viking.MissionStates.Add(new MissionState {
MissionId = m,
MissionStatus = MissionStatus.Upcoming

View File

@ -8,6 +8,8 @@ public class MissionStoreSingleton {
private Dictionary<int, Mission> missions = new();
private int[] activeMissions;
private int[] upcomingMissions;
private int[] activeMissionsV1;
private int[] upcomingMissionsV1;
public MissionStoreSingleton() {
ServerMissionArray missionArray = XmlUtil.DeserializeXml<ServerMissionArray>(XmlUtil.ReadResourceXmlString("missions"));
@ -17,17 +19,27 @@ public class MissionStoreSingleton {
}
activeMissions = defaultMissions.Active;
upcomingMissions = defaultMissions.Upcoming;
defaultMissions = XmlUtil.DeserializeXml<DefaultMissions>(XmlUtil.ReadResourceXmlString("defaultmissionlistv1"));
activeMissionsV1 = defaultMissions.Active;
upcomingMissionsV1 = defaultMissions.Upcoming;
}
public Mission GetMission(int missionID) {
return DeepCopy(missions[missionID]);
}
public int[] GetActiveMissions() {
public int[] GetActiveMissions(string apiKey) {
if (apiKey == "a1a13a0a-7c6e-4e9b-b0f7-22034d799013") {
return activeMissionsV1;
}
return activeMissions;
}
public int[] GetUpcomingMissions() {
public int[] GetUpcomingMissions(string apiKey) {
if (apiKey == "a1a13a0a-7c6e-4e9b-b0f7-22034d799013") {
return upcomingMissionsV1;
}
return upcomingMissions;
}

View File

@ -24,6 +24,7 @@
<None Remove="Resources\achievementsids.xml" />
<None Remove="Resources\achievementstasks.xml" />
<None Remove="Resources\defaultmissionlist.xml" />
<None Remove="Resources\defaultmissionlistv1.xml" />
</ItemGroup>
<ItemGroup>
<None Update="Resources\childlist.xml">
@ -85,5 +86,8 @@
<EmbeddedResource Include="Resources\defaultmissionlist.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="Resources\defaultmissionlistv1.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
</Project>