Skip to content
This repository was archived by the owner on Feb 2, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
fe27562
missiontext: correct "preposition" to "proposition"
J-128 Oct 31, 2023
8b3ed12
wallets
Spirtix Dec 7, 2023
474605d
fix highscores involving time
Spirtix Dec 7, 2023
5b8c952
wallet-store fix for old SoD (1.13, 2.9)
rpaciorek Dec 7, 2023
8048171
include discounts in purchase prices
Spirtix Dec 9, 2023
47e1bfb
SoD 1.6 fixes
rpaciorek Dec 11, 2023
e5570f7
Magic and Mythies fixes:
rpaciorek Dec 9, 2023
25ebba6
fix MissionID restore bug
rpaciorek Dec 11, 2023
5301de6
missiontext: correct "preposition" to "proposition"
J-128 Oct 31, 2023
d021cbc
Merge branch 'grammar-fixes' of https://github.com/J-128/sodoff-j int…
J-128 Dec 24, 2023
3272360
missiontext: correct "Skrill" to "Skrills"
J-128 Dec 24, 2023
3b0dfa8
taskdesc: correct 12 to 15
J-128 Dec 24, 2023
5123483
fix missing blueprints
rpaciorek Dec 29, 2023
e50207a
add support for customizable items
rpaciorek Dec 29, 2023
5c80292
job board and quests bugfixes
rpaciorek Jan 4, 2024
ea3de10
disable sending email in LoginParent reply
rpaciorek Jan 7, 2024
7e5a90b
use utf-8 in xml headers from SerializeXml
rpaciorek Jan 9, 2024
36d009d
implemented SetDisplayName
rpaciorek Jan 9, 2024
2913245
missiontext: correct "preposition" to "proposition"
J-128 Oct 31, 2023
da238de
missiontext: correct "Skrill" to "Skrills"
J-128 Dec 24, 2023
de914d6
taskdesc: correct 12 to 15
J-128 Dec 24, 2023
f66c3e4
missiontext: correct "aren't" to "didn't"
J-128 Jan 10, 2024
4abe365
Merge branch 'grammar-fixes' of https://github.com/J-128/sodoff-j int…
J-128 Jan 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ Then run School of Dragons.
- SetAchievementByEntityIDs
- SetAvatar
- SetCommonInventory
- SetDisplayName (V2)
- SetDragonXP (used by account import tools)
- SetImage
- SetKeyValuePair
Expand Down
1 change: 1 addition & 0 deletions mitm-redirect.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
'GetGameDataByGame',
'GetGameDataByGameForDateRange',
'GetTopAchievementPointUsers',
'SetDisplayName',
]

def routable(path):
Expand Down
6 changes: 3 additions & 3 deletions src/Controllers/Common/AuthenticationController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public IActionResult LoginParent() {

var response = new ParentLoginInfo {
UserName = user.Username,
Email = user.Email,
//Email = user.Email, /* disabled to avoid put email in client debug logs */
ApiToken = session.ApiToken.ToString(),
UserID = user.Id.ToString(),
Status = MembershipUserStatus.Success,
Expand Down Expand Up @@ -95,7 +95,7 @@ public IActionResult GetUserInfoByApiToken([FromForm] Guid apiToken, [FromForm]
Username = user.Username,
MembershipID = "ef84db9-59c6-4950-b8ea-bbc1521f899b", // placeholder
FacebookUserID = 0,
MultiplayerEnabled = (apiKey != "a1a13a0a-7c6e-4e9b-b0f7-22034d799013" && apiKey != "a2a09a0a-7c6e-4e9b-b0f7-22034d799013" && apiKey != "a3a12a0a-7c6e-4e9b-b0f7-22034d799013"),
MultiplayerEnabled = !ClientVersion.IsOldSoD(apiKey),
IsApproved = true,
Age = 24,
OpenChatEnabled = true
Expand All @@ -110,7 +110,7 @@ public IActionResult GetUserInfoByApiToken([FromForm] Guid apiToken, [FromForm]
UserID = viking.Uid.ToString(),
Username = viking.Name,
FacebookUserID = 0,
MultiplayerEnabled = (apiKey != "a1a13a0a-7c6e-4e9b-b0f7-22034d799013" && apiKey != "a2a09a0a-7c6e-4e9b-b0f7-22034d799013" && apiKey != "a3a12a0a-7c6e-4e9b-b0f7-22034d799013"),
MultiplayerEnabled = !ClientVersion.IsOldSoD(apiKey),
IsApproved = true,
Age = 24,
OpenChatEnabled = true
Expand Down
188 changes: 179 additions & 9 deletions src/Controllers/Common/ContentController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,33 @@ public IActionResult ValidateName([FromForm] string nameValidationRequest) {
}
}

[HttpPost]
[Produces("application/xml")]
[Route("/V2/ContentWebService.asmx/SetDisplayName")]
[VikingSession]
public IActionResult SetDisplayName(Viking viking, [FromForm] string request) {
string newName = XmlUtil.DeserializeXml<SetDisplayNameRequest>(request).DisplayName;

if (String.IsNullOrWhiteSpace(newName) || ctx.Vikings.Count(e => e.Name == newName) > 0) {
return Ok(new SetAvatarResult {
Success = false,
StatusCode = AvatarValidationResult.AvatarDisplayNameInvalid
});
}

viking.Name = newName;
AvatarData avatarData = XmlUtil.DeserializeXml<AvatarData>(viking.AvatarSerialized);
avatarData.DisplayName = newName;
viking.AvatarSerialized = XmlUtil.SerializeXml(avatarData);
ctx.SaveChanges();

return Ok(new SetAvatarResult {
Success = true,
DisplayName = viking.Name,
StatusCode = AvatarValidationResult.Valid
});
}

[HttpPost]
[Produces("application/xml")]
[Route("ContentWebService.asmx/GetKeyValuePair")]
Expand Down Expand Up @@ -208,6 +235,42 @@ public IActionResult SetCommonInventory(Viking viking, [FromForm] string commonI
return Ok(response);
}

[HttpPost]
[Produces("application/xml")]
[Route("ContentWebService.asmx/SetCommonInventoryAttribute")]
[VikingSession]
public IActionResult SetCommonInventoryAttribute(Viking viking, [FromForm] int commonInventoryID, [FromForm] string pairxml) {
InventoryItem? item = viking.InventoryItems.FirstOrDefault(e => e.Id == commonInventoryID);

List<Schema.Pair> itemAttributes;
if (item.AttributesSerialized != null) {
itemAttributes = XmlUtil.DeserializeXml<Schema.PairData>(item.AttributesSerialized).Pairs.ToList();
} else {
itemAttributes = new List<Schema.Pair>();
}

Schema.PairData newItemAttributes = XmlUtil.DeserializeXml<Schema.PairData>(pairxml);
foreach (var p in newItemAttributes.Pairs) {
var pairItem = itemAttributes.FirstOrDefault(e => e.PairKey == p.PairKey);
if (pairItem != null){
pairItem.PairValue = p.PairValue;
} else {
itemAttributes.Add(p);
}
}

if (itemAttributes.Count > 0) {
item.AttributesSerialized = XmlUtil.SerializeXml(
new Schema.PairData{
Pairs = itemAttributes.ToArray()
}
);
}

ctx.SaveChanges();
return Ok(true);
}

[HttpPost]
[Produces("application/xml")]
[Route("ContentWebService.asmx/UseInventory")]
Expand Down Expand Up @@ -271,6 +334,53 @@ public IActionResult SetAvatar(Viking viking, [FromForm] string contentXML) {
});
}

[HttpPost]
[Produces("application/xml")]
[Route("ContentWebService.asmx/CreateRaisedPet")] // used by SoD 1.6
[VikingSession]
public RaisedPetData? CreateRaisedPet(Viking viking, int petTypeID) {
// Update the RaisedPetData with the info
String dragonId = Guid.NewGuid().ToString();

var raisedPetData = new RaisedPetData();
raisedPetData.IsPetCreated = true;
raisedPetData.PetTypeID = petTypeID;
raisedPetData.RaisedPetID = 0; // Initially make zero, so the db auto-fills
raisedPetData.EntityID = Guid.Parse(dragonId);
raisedPetData.Name = string.Concat("Dragon-", dragonId.AsSpan(0, 8)); // Start off with a random name
raisedPetData.IsSelected = false; // The api returns false, not sure why
raisedPetData.CreateDate = new DateTime(DateTime.Now.Ticks);
raisedPetData.UpdateDate = new DateTime(DateTime.Now.Ticks);
raisedPetData.GrowthState = new RaisedPetGrowthState { Name = "BABY" };
int imageSlot = (viking.Images.Select(i => i.ImageSlot).DefaultIfEmpty(-1).Max() + 1);
raisedPetData.ImagePosition = imageSlot;
// NOTE: Placing an egg into a hatchery slot calls CreatePet, but doesn't SetImage.
// NOTE: We need to force create an image slot because hatching multiple eggs at once would create dragons with the same slot
Image image = new Image {
ImageType = "EggColor", // NOTE: The game doesn't seem to use anything other than EggColor.
ImageSlot = imageSlot,
Viking = viking,
};
// Save the dragon in the db
Dragon dragon = new Dragon {
EntityId = Guid.NewGuid(),
Viking = viking,
RaisedPetData = XmlUtil.SerializeXml(raisedPetData),
};

ctx.Dragons.Add(dragon);
ctx.Images.Add(image);

if (petTypeID != 2) {
// Minisaurs should not be set as active pet
viking.SelectedDragon = dragon;
ctx.Update(viking);
}
ctx.SaveChanges();

return GetRaisedPetDataFromDragon(dragon);
}

[HttpPost]
[Produces("application/xml")]
[Route("V2/ContentWebService.asmx/CreatePet")]
Expand Down Expand Up @@ -676,7 +786,7 @@ public IActionResult SetTaskStatev1(Viking viking, [FromForm] Guid userId, [From
[Produces("application/xml")]
[Route("V2/ContentWebService.asmx/SetTaskState")]
[VikingSession]
public IActionResult SetTaskState(Viking viking, [FromForm] Guid userId, [FromForm] int missionId, [FromForm] int taskId, [FromForm] bool completed, [FromForm] string xmlPayload, [FromForm] string apiKey) {
public IActionResult SetTaskState(Viking viking, [FromForm] Guid userId, [FromForm] int missionId, [FromForm] int taskId, [FromForm] bool completed, [FromForm] string xmlPayload, [FromForm] string commonInventoryRequestXml, [FromForm] string apiKey) {
if (viking.Uid != userId)
return Unauthorized("Can't set not owned task");

Expand All @@ -687,6 +797,12 @@ public IActionResult SetTaskState(Viking viking, [FromForm] Guid userId, [FromFo
Status = SetTaskStateStatus.TaskCanBeDone,
};

if (commonInventoryRequestXml.Length > 44) { // avoid process inventory on empty xml request,
// NOTE: client do not set this on empty string when no inventory change request, but send <?xml version="1.0" encoding="utf-8"?>
SetCommonInventory(viking, commonInventoryRequestXml);
taskResult.CommonInvRes = new CommonInventoryResponse { Success = true };
}

if (results.Count > 0)
taskResult.MissionsCompleted = results.ToArray();

Expand Down Expand Up @@ -749,8 +865,19 @@ public IActionResult PurchaseItems(Viking viking, [FromForm] string purchaseItem
PurchaseStoreItemRequest request = XmlUtil.DeserializeXml<PurchaseStoreItemRequest>(purchaseItemRequest);
List<CommonInventoryResponseItem> items = new List<CommonInventoryResponseItem>();
Gender gender = XmlUtil.DeserializeXml<AvatarData>(viking.AvatarSerialized).GenderType;
bool success = true;
for (int i = 0; i < request.Items.Length; i++) {
int itemId = request.Items[i];
ItemData item = itemService.GetItem(itemId);
UserGameCurrency currency = achievementService.GetUserCurrency(viking);
int coinCost = (int)Math.Round(item.FinalDiscoutModifier * item.Cost);
int gemCost = (int)Math.Round(item.FinalDiscoutModifier * item.CashCost);
if (currency.GameCurrency - coinCost < 0 && currency.CashCurrency - gemCost < 0) {
success = false;
break;
}
achievementService.AddAchievementPoints(viking, AchievementPointTypes.GameCurrency, -coinCost);
achievementService.AddAchievementPoints(viking, AchievementPointTypes.CashCurrency, -gemCost);
if (request.AddMysteryBoxToInventory) {
// force add boxes as item (without "opening")
items.Add(inventoryService.AddItemToInventoryAndGetResponse(viking, itemId, 1));
Expand All @@ -762,7 +889,22 @@ public IActionResult PurchaseItems(Viking viking, [FromForm] string purchaseItem
for (int j=0; j<quantity; ++j)
items.Add(inventoryService.AddItemToInventoryAndGetResponse(viking, reward.ItemId, 1));
}
} else {
} else if (itemService.IsGemBundle(itemId, out int gems)) {
achievementService.AddAchievementPoints(viking, AchievementPointTypes.CashCurrency, gems);
items.Add(new CommonInventoryResponseItem {
CommonInventoryID = 0,
ItemID = itemId,
Quantity = 0
});
} else if (itemService.IsCoinBundle(itemId, out int coins)) {
achievementService.AddAchievementPoints(viking, AchievementPointTypes.GameCurrency, coins);
items.Add(new CommonInventoryResponseItem {
CommonInventoryID = 0,
ItemID = itemId,
Quantity = 0
});
}
else {
// check for mystery box ... open if need
itemService.CheckAndOpenBox(itemId, gender, out itemId, out int quantity);
for (int j=0; j<quantity; ++j) {
Expand All @@ -774,7 +916,7 @@ public IActionResult PurchaseItems(Viking viking, [FromForm] string purchaseItem
}

CommonInventoryResponse response = new CommonInventoryResponse {
Success = true,
Success = success,
CommonInventoryIDs = items.ToArray(),
UserGameCurrency = achievementService.GetUserCurrency(viking)
};
Expand All @@ -789,17 +931,45 @@ public IActionResult PurchaseItemsV1(Viking viking, [FromForm] string itemIDArra
int[] itemIdArr = XmlUtil.DeserializeXml<int[]>(itemIDArrayXml);
List<CommonInventoryResponseItem> items = new List<CommonInventoryResponseItem>();
Gender gender = XmlUtil.DeserializeXml<AvatarData>(viking.AvatarSerialized).GenderType;
bool success = true;
for (int i = 0; i < itemIdArr.Length; i++) {
itemService.CheckAndOpenBox(itemIdArr[i], gender, out int itemId, out int quantity);
for (int j=0; j<quantity; ++j) {
items.Add(inventoryService.AddItemToInventoryAndGetResponse(viking, itemId, 1));
// NOTE: The quantity of purchased items is always 0 and the items are instead duplicated in both the request and the response.
// Call AddItemToInventoryAndGetResponse with Quantity == 1 we get response with quantity == 0.
ItemData item = itemService.GetItem(itemIdArr[i]);
UserGameCurrency currency = achievementService.GetUserCurrency(viking);
int coinCost = (int)Math.Round(item.FinalDiscoutModifier * item.Cost);
int gemCost = (int)Math.Round(item.FinalDiscoutModifier * item.CashCost);
if (currency.GameCurrency - coinCost < 0 && currency.CashCurrency - gemCost < 0) {
success = false;
break;
}
achievementService.AddAchievementPoints(viking, AchievementPointTypes.GameCurrency, -coinCost);
achievementService.AddAchievementPoints(viking, AchievementPointTypes.CashCurrency, -gemCost);
if (itemService.IsGemBundle(itemIdArr[i], out int gems)) {
achievementService.AddAchievementPoints(viking, AchievementPointTypes.CashCurrency, gems);
items.Add(new CommonInventoryResponseItem {
CommonInventoryID = 0,
ItemID = itemIdArr[i],
Quantity = 0
});
} else if (itemService.IsCoinBundle(itemIdArr[i], out int coins)) {
achievementService.AddAchievementPoints(viking, AchievementPointTypes.GameCurrency, coins);
items.Add(new CommonInventoryResponseItem {
CommonInventoryID = 0,
ItemID = itemIdArr[i],
Quantity = 0
});
}
else {
itemService.CheckAndOpenBox(itemIdArr[i], gender, out int itemId, out int quantity);
for (int j=0; j<quantity; ++j) {
items.Add(inventoryService.AddItemToInventoryAndGetResponse(viking, itemId, 1));
// NOTE: The quantity of purchased items is always 0 and the items are instead duplicated in both the request and the response.
// Call AddItemToInventoryAndGetResponse with Quantity == 1 we get response with quantity == 0.
}
}
}

CommonInventoryResponse response = new CommonInventoryResponse {
Success = true,
Success = success,
CommonInventoryIDs = items.ToArray(),
UserGameCurrency = achievementService.GetUserCurrency(viking)
};
Expand Down
10 changes: 6 additions & 4 deletions src/Controllers/Common/ProfileController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ private UserProfileData GetProfileDataFromViking(Viking viking, [FromForm] strin
avatarData.Id = viking.Id;
}

if (avatarData != null && (apiKey == "a3a12a0a-7c6e-4e9b-b0f7-22034d799013")) {
if (avatarData != null && ClientVersion.Use2019SoDTutorial(apiKey)) {
if (avatarData.Part.FirstOrDefault(e => e.PartType == "Sword") is null) {
var extraParts = new AvatarDataPart[] {
new AvatarDataPart {
Expand All @@ -139,7 +139,7 @@ private UserProfileData GetProfileDataFromViking(Viking viking, [FromForm] strin
ParentUserID = viking.UserId.ToString(),
Username = viking.Name,
FirstName = viking.Name,
MultiplayerEnabled = (apiKey != "a1a13a0a-7c6e-4e9b-b0f7-22034d799013" && apiKey != "a2a09a0a-7c6e-4e9b-b0f7-22034d799013" && apiKey != "a3a12a0a-7c6e-4e9b-b0f7-22034d799013"),
MultiplayerEnabled = !ClientVersion.IsOldSoD(apiKey),
Locale = "en-US", // placeholder
GenderID = Gender.Male, // placeholder
OpenChatEnabled = true,
Expand Down Expand Up @@ -167,14 +167,16 @@ private UserProfileData GetProfileDataFromViking(Viking viking, [FromForm] strin
}
};

UserGameCurrency currency = achievementService.GetUserCurrency(viking);

return new UserProfileData {
ID = viking.Uid.ToString(),
AvatarInfo = avatar,
AchievementCount = 0,
MythieCount = 0,
AnswerData = new UserAnswerData { UserID = viking.Uid.ToString() },
GameCurrency = 65536,
CashCurrency = 65536,
GameCurrency = currency.GameCurrency,
CashCurrency = currency.CashCurrency,
ActivityCount = 0,
BuddyCount = 0,
UserGradeData = new UserGrade { UserGradeID = 0 }
Expand Down
2 changes: 1 addition & 1 deletion src/Controllers/Common/RegistrationController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public IActionResult RegisterChild([FromForm] Guid parentApiToken, [FromForm] st
ctx.Vikings.Add(v);
ctx.SaveChanges();

if (apiKey == "a1a13a0a-7c6e-4e9b-b0f7-22034d799013") {
if (ClientVersion.Use2013SoDTutorial(apiKey)) {
keyValueService.SetPairData(null, v, null, 2017, new Schema.PairData {
Pairs = new Schema.Pair[]{
new Schema.Pair {
Expand Down
4 changes: 3 additions & 1 deletion src/Model/InventoryItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ public class InventoryItem {
public int ItemId { get; set; }

public int VikingId { get; set; }

public string? StatsSerialized { get; set; }

public string? AttributesSerialized { get; set; }

public virtual Viking Viking { get; set; } = null!;

public int Quantity { get; set; }
Expand Down
12 changes: 12 additions & 0 deletions src/Resources/defaultmissionlistmam.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version='1.0' encoding='utf-8'?>
<DefaultMissions>
<!-- list of default mission for most versions (2.9, 3.12, 3.31) -->
<Active>
<id>1750</id>
<id>2298</id>
<id>1044</id>
<id>1074</id>
</Active>
<Upcoming>
</Upcoming>
</DefaultMissions>
8 changes: 4 additions & 4 deletions src/Resources/items.xml
Original file line number Diff line number Diff line change
Expand Up @@ -626930,8 +626930,8 @@ Contains 5x Perfect Aim power-ups.</d>
<id>12732</id>
</at>
<c>
<cid>436</cid>
<cn>Dragons Bundle</cn>
<cid>425</cid>
<cn>Dragons Treasure</cn>
<i xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
<id>12732</id>
</c>
Expand Down Expand Up @@ -909766,8 +909766,8 @@ This bundle contains 10 Frostbite Powerups.</d>
<id>12721</id>
</at>
<c>
<cid>436</cid>
<cn>Dragons Bundle</cn>
<cid>425</cid>
<cn>Dragons Treasure</cn>
<i xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
<id>12721</id>
</c>
Expand Down
Loading