Eat My Dust support (#12)

DB MODEL CHANGES! I don't know how to deal with those.
Support for EMD's MMO rooms and API token.
Groundwork for group/clan system (hence schema changes). Only functional for EMD right now (if I implemented it correctly).

* Removed debug logged messages.
* Update comments in GroupController.cs
* Fixed position of parenthesis in ConfigurationController.cs
* Noted changes in README
* Amendment to previous commit, these are supposed to be alphabetical.
* Changed unnecessary extra check for EMD ClientVersion.
This commit is contained in:
Hipposgrumm 2024-12-29 09:00:21 -07:00 committed by GitHub
parent dcefeb5fc3
commit 4b6f782e21
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 269 additions and 2 deletions

View File

@ -150,6 +150,7 @@ Almost everything:
#### Implemented enough (probably)
- GetCommonInventory (V1 - returns the viking's inventory if it is called with a viking; otherwise returns 8 viking slots)
- GetGroupsByGroupType (only useful for Eat My Dust at the moment)
- GetQuestions (doesn't return all questions, probably doesn't need to)
- GetRules (doesn't return any rules, probably doesn't need to)
- GetSubscriptionInfo (always returns member, with end date 10 years from now)
@ -168,6 +169,7 @@ Almost everything:
- GetTopAchievementPointUsers (ignores type [all, buddy, hall of fame, ...] and mode [overall, monthly, weekly] properties)
- GetUserAchievements (used by Magic & Mythies)
- GetUserRoomList (room categories are not implemented, but it's enough for SoD)
- JoinGroup (for Eat My Dust only)
- ProcessRewardedItems (gives gems, but doesn't give gold, gold is not yet implemented)
- SellItems (gives gems, but doesn't give gold, gold is not yet implemented)
- SetUserAchievementTask (returns a real reward but still use task placeholder)

View File

@ -0,0 +1,104 @@
using Microsoft.AspNetCore.Mvc;
using sodoff.Attributes;
using sodoff.Model;
using sodoff.Schema;
using sodoff.Util;
namespace sodoff.Controllers.Common;
public class GroupController : Controller {
public static readonly Schema.Group EMD_Dragons = new Schema.Group {
GroupID = "8e68214a-c801-4759-8461-d01f28484134",
Name = "Dragons",
Color = "234,57,23",
Logo = "RS_DATA/Content/PlayerData/EMD/IcoEMDTeamDragons.png"
};
public static readonly Schema.Group EMD_Scorpions = new Schema.Group {
GroupID = "db0aa225-2f0e-424c-83a7-73783fe63fef",
Name = "Scorpions",
Color = "120,183,53",
Logo = "RS_DATA/Content/PlayerData/EMD/IcoEMDTeamScorpions.png"
};
private readonly DBContext ctx;
public GroupController(DBContext ctx) {
this.ctx = ctx;
}
[HttpPost]
[Produces("application/xml")]
[Route("GroupWebService.asmx/JoinGroup")]
[VikingSession]
public IActionResult JoinGroup(Viking viking, [FromForm] string apiKey, [FromForm] string groupID) {
AddEMDGroups();
uint version = ClientVersion.GetVersion(apiKey);
// Only implemented for EMD so far.
if (version == ClientVersion.EMD) {
if (viking.Groups.Any(g => {
// Check for loyalty.
string id = g.GroupID.ToString();
return id == EMD_Dragons.GroupID || id == EMD_Scorpions.GroupID;
})) {
return Ok(new JoinGroupResult { GroupStatus = GroupMembershipStatus.ALREADY_MEMBER });
}
groupID = groupID.ToUpper();
Model.Group? group = ctx.Groups.FirstOrDefault(g => g.GroupID.ToString() == groupID);
if (group != null) {
group.Vikings.Add(viking);
ctx.SaveChanges();
return Ok(new JoinGroupResult { GroupStatus = GroupMembershipStatus.APPROVED });
}
}
return Ok(new JoinGroupResult { GroupStatus = GroupMembershipStatus.REJECTED });
}
[HttpPost]
[Produces("application/xml")]
[Route("GroupWebService.asmx/GetGroupsByGroupType")]
[VikingSession]
public Schema.Group[] GetGroupsByGroupType([FromForm] string apiKey, [FromForm] string groupType) {
AddEMDGroups();
List<Schema.Group> groups = new List<Schema.Group>();
foreach (Model.Group group in ctx.Groups) {
if (group.ApiKey == apiKey && group.Type.ToString() == groupType) groups.Add(new Schema.Group {
GroupID = group.GroupID.ToString(),
Name = group.Name,
Color = group.Color,
Logo = group.Logo,
Type = group.Type
});
}
return groups.ToArray();
}
private void AddEMDGroups() {
bool changed = false;
Guid DragonString = new Guid(EMD_Dragons.GroupID);
Guid ScorpionString = new Guid(EMD_Scorpions.GroupID);
if (!ctx.Groups.Any(g => g.GroupID == DragonString)) {
ctx.Groups.Add(new Model.Group {
GroupID = DragonString,
Name = EMD_Dragons.Name,
Color = EMD_Dragons.Color,
Logo = EMD_Dragons.Logo,
Type = GroupType.System,
ApiKey = "dd602cf1-cc98-4738-9a0a-56dde3026947"
});
changed = true;
}
if (!ctx.Groups.Any(g => g.GroupID == ScorpionString)) {
ctx.Groups.Add(new Model.Group {
GroupID = ScorpionString,
Name = EMD_Scorpions.Name,
Color = EMD_Scorpions.Color,
Logo = EMD_Scorpions.Logo,
Type = GroupType.System,
ApiKey = "dd602cf1-cc98-4738-9a0a-56dde3026947"
});
changed = true;
}
if (changed) ctx.SaveChanges();
}
}

View File

@ -152,6 +152,22 @@ public class ProfileController : Controller {
UserGameCurrency currency = achievementService.GetUserCurrency(viking);
ICollection<Model.Group> groups = viking.Groups;
UserProfileGroupData[] groupData = new UserProfileGroupData[groups.Count];
int i = 0;
foreach (Model.Group group in groups) {
groupData[i] = new UserProfileGroupData {
GroupID = group.GroupID.ToString(),
Name = group.Name,
Color = group.Color,
Logo = group.Logo,
TypeID = (int)group.Type,
RoleID = 0
};
i++;
}
return new UserProfileData {
ID = viking.Uid.ToString(),
AvatarInfo = avatar,
@ -169,7 +185,8 @@ public class ProfileController : Controller {
ProfileTags = new List<ProfileTag>(),
UserID = viking.Uid,
UserProfileTagID = 1
}
},
Groups = groupData
};
}
}

View File

@ -25,6 +25,7 @@ public class DBContext : DbContext {
public DbSet<Party> Parties { get; set; } = null!;
public DbSet<Neighborhood> Neighborhoods { get; set; } = null!;
// we had a brief debate on whether it's neighborhoods or neighborheed
public DbSet<Group> Groups { get; set; } = null!;
private readonly IOptions<ApiServerConfig> config;
@ -136,6 +137,9 @@ public class DBContext : DbContext {
builder.Entity<Viking>().HasOne(v => v.Neighborhood)
.WithOne(e => e.Viking);
builder.Entity<Viking>().HasMany(v => v.Groups)
.WithMany(e => e.Vikings);
// Dragons
builder.Entity<Dragon>().HasOne(d => d.Viking)
.WithMany(e => e.Dragons)
@ -252,5 +256,9 @@ public class DBContext : DbContext {
builder.Entity<Neighborhood>().HasOne(r => r.Viking)
.WithOne(e => e.Neighborhood)
.HasForeignKey<Neighborhood>(e => e.VikingId);
// Groups
builder.Entity<Group>().HasMany(r => r.Vikings)
.WithMany(e => e.Groups);
}
}

25
src/Model/Group.cs Normal file
View File

@ -0,0 +1,25 @@
using sodoff.Schema;
using System.ComponentModel.DataAnnotations;
namespace sodoff.Model;
// Implementation for EMD, add whatever else if needed
public class Group {
[Key]
public int Id { get; set; }
[Required]
public Guid GroupID { get; set; }
public string Name { get; set; }
public GroupType Type { get; set; }
public string Color { get; set; }
public string Logo { get; set; }
public string ApiKey { get; set; }
public virtual ICollection<Viking> Vikings { get; set; } = null!;
}

View File

@ -38,6 +38,7 @@ public class Viking {
public virtual ICollection<Party> Parties { get; set; } = null!;
public virtual ICollection<MMORole> MMORoles { get; set; } = null!;
public virtual Neighborhood? Neighborhood { get; set; } = null!;
public virtual ICollection<Group> Groups { get; set; } = null!;
public virtual Dragon? SelectedDragon { get; set; }
public DateTime? CreationDate { get; set; }

View File

@ -35,7 +35,14 @@
</MMOServerInfo>
</Version>
<!-- Multiple <MMOServerInfo> child can be used instead of <ZoneList>
<Version>
<!-- Eat My Dust -->
<VersionMin>0x04000000</VersionMin>
<VersionMax>0x04ffffff</VersionMax>
<ZoneList>BadlandsEMD RoadSideAttractionEMD JunkYardEMD PitRowEMD RacingCircuitEMD MyGarageEMDInt DragonHomeEMD ScorpionHomeEMD</ZoneList>
</Version>
<!-- Multiple <MMOServerInfo> child can be used instead of <ZoneList>
to provide different MMO servers for different zones (<ZN> tag in <MMOServerInfo>).
For zones not specified in this config, default entry from DWADragonsMain.xml will be used.

52
src/Schema/Group.cs Normal file
View File

@ -0,0 +1,52 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[Serializable]
[XmlRoot(ElementName = "GP", IsNullable = true)]
public class Group {
[XmlElement(ElementName = "G", IsNullable = false)]
public string GroupID;
[XmlElement(ElementName = "N", IsNullable = false)]
public string Name;
[XmlElement(ElementName = "D", IsNullable = false)]
public string Description;
[XmlElement(ElementName = "T", IsNullable = false)]
public GroupType Type;
[XmlElement(ElementName = "O", IsNullable = true)]
public string OwnerID;
[XmlElement(ElementName = "L", IsNullable = true)]
public string Logo;
[XmlElement(ElementName = "C", IsNullable = true)]
public string Color;
[XmlElement(ElementName = "M", IsNullable = true)]
public int? MemberLimit;
[XmlElement(ElementName = "TC", IsNullable = true)]
public int? TotalMemberCount;
[XmlElement(ElementName = "A", IsNullable = false)]
public bool Active;
[XmlElement(ElementName = "P", IsNullable = true)]
public string ParentGroupID;
[XmlElement(ElementName = "PS", IsNullable = true)]
public int? Points;
[XmlElement(ElementName = "RK", IsNullable = true)]
public int? Rank;
[XmlElement(ElementName = "RT", IsNullable = true)]
public int? RankTrend;
[XmlElement(ElementName = "CD")]
public DateTime CreateDate;
}

View File

@ -0,0 +1,18 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
public enum GroupMembershipStatus {
[XmlEnum("1")]
APPROVAL_PENDING = 1,
[XmlEnum("2")]
APPROVED,
[XmlEnum("3")]
REJECTED,
[XmlEnum("4")]
SELF_BLOCKED,
[XmlEnum("5")]
ALREADY_MEMBER,
[XmlEnum("6")]
OTHERS
}

20
src/Schema/GroupType.cs Normal file
View File

@ -0,0 +1,20 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "GroupType")]
[Serializable]
public enum GroupType {
[XmlEnum("0")]
None = 0,
[XmlEnum("1")]
System = 1,
[XmlEnum("2")]
Public = 2,
[XmlEnum("3")]
MembersOnly = 3,
[XmlEnum("4")]
Private = 4,
[XmlEnum("5")]
Others = 5
}

View File

@ -0,0 +1,9 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "JoinGroupResult", IsNullable = true, Namespace = "")]
public class JoinGroupResult {
[XmlElement(ElementName = "GroupStatus")]
public GroupMembershipStatus GroupStatus;
}

View File

@ -29,6 +29,10 @@ public class ClientVersion {
apiKey == "6738196d-2a2c-4ef8-9b6e-1252c6ec7325"
) {
return MB;
} else if (
apiKey == "dd602cf1-cc98-4738-9a0a-56dde3026947"
) {
return EMD;
} else if (
apiKey == "34b0ae13-eccc-4d64-b6d0-733d2562080e"
) {