From c14dc2258c2f29c2bace65c5994edc6cc9ce845c Mon Sep 17 00:00:00 2001 From: Hipposgrumm Date: Tue, 29 Jul 2025 20:44:59 -0600 Subject: [PATCH 1/8] Clan System, please contact me if you have any questions. Please only contact me if you're Clan System /j --- README.md | 14 +- src/Controllers/Common/GroupController.cs | 545 ++++++++++++++++++-- src/Controllers/Common/ProfileController.cs | 8 +- src/Model/DBContext.cs | 20 +- src/Model/Group.cs | 24 +- src/Model/GroupJoinRequest.cs | 15 + src/Model/GroupViking.cs | 35 ++ src/Model/Viking.cs | 2 +- src/Schema/AssignRoleRequest.cs | 25 + src/Schema/AssignRoleResult.cs | 16 + src/Schema/AssignRoleStatus.cs | 18 + src/Schema/AuthorizeJoinRequest.cs | 25 + src/Schema/AuthorizeJoinResult.cs | 13 + src/Schema/AuthorizeJoinStatus.cs | 24 + src/Schema/CreateGroupRequest.cs | 40 ++ src/Schema/CreateGroupResult.cs | 16 + src/Schema/CreateGroupStatus.cs | 32 ++ src/Schema/EditGroupRequest.cs | 37 ++ src/Schema/EditGroupResult.cs | 16 + src/Schema/EditGroupStatus.cs | 28 + src/Schema/GetGroupsRequest.cs | 40 ++ src/Schema/GetGroupsResult.cs | 16 + src/Schema/GetPendingJoinRequest.cs | 16 + src/Schema/GetPendingJoinResult.cs | 14 + src/Schema/GroupJoinRequestStatus.cs | 16 + src/Schema/GroupJoinResult.cs | 15 + src/Schema/GroupMember.cs | 35 ++ src/Schema/GroupsFilter.cs | 16 + src/Schema/JoinGroupRequest.cs | 31 ++ src/Schema/JoinGroupStatus.cs | 28 + src/Schema/LeaveGroupRequest.cs | 19 + src/Schema/LeaveGroupResult.cs | 13 + src/Schema/LeaveGroupStatus.cs | 12 + src/Schema/PendingJoinRequest.cs | 22 + src/Schema/RemoveMemberRequest.cs | 22 + src/Schema/RemoveMemberResult.cs | 13 + src/Schema/RemoveMemberStatus.cs | 16 + src/Schema/RolePermission.cs | 16 + src/Schema/UserProfileGroupData.cs | 2 +- src/Schema/UserRole.cs | 12 + 40 files changed, 1257 insertions(+), 70 deletions(-) create mode 100644 src/Model/GroupJoinRequest.cs create mode 100644 src/Model/GroupViking.cs create mode 100644 src/Schema/AssignRoleRequest.cs create mode 100644 src/Schema/AssignRoleResult.cs create mode 100644 src/Schema/AssignRoleStatus.cs create mode 100644 src/Schema/AuthorizeJoinRequest.cs create mode 100644 src/Schema/AuthorizeJoinResult.cs create mode 100644 src/Schema/AuthorizeJoinStatus.cs create mode 100644 src/Schema/CreateGroupRequest.cs create mode 100644 src/Schema/CreateGroupResult.cs create mode 100644 src/Schema/CreateGroupStatus.cs create mode 100644 src/Schema/EditGroupRequest.cs create mode 100644 src/Schema/EditGroupResult.cs create mode 100644 src/Schema/EditGroupStatus.cs create mode 100644 src/Schema/GetGroupsRequest.cs create mode 100644 src/Schema/GetGroupsResult.cs create mode 100644 src/Schema/GetPendingJoinRequest.cs create mode 100644 src/Schema/GetPendingJoinResult.cs create mode 100644 src/Schema/GroupJoinRequestStatus.cs create mode 100644 src/Schema/GroupJoinResult.cs create mode 100644 src/Schema/GroupMember.cs create mode 100644 src/Schema/GroupsFilter.cs create mode 100644 src/Schema/JoinGroupRequest.cs create mode 100644 src/Schema/JoinGroupStatus.cs create mode 100644 src/Schema/LeaveGroupRequest.cs create mode 100644 src/Schema/LeaveGroupResult.cs create mode 100644 src/Schema/LeaveGroupStatus.cs create mode 100644 src/Schema/PendingJoinRequest.cs create mode 100644 src/Schema/RemoveMemberRequest.cs create mode 100644 src/Schema/RemoveMemberResult.cs create mode 100644 src/Schema/RemoveMemberStatus.cs create mode 100644 src/Schema/RolePermission.cs create mode 100644 src/Schema/UserRole.cs diff --git a/README.md b/README.md index afc55f1..f7d6ba2 100644 --- a/README.md +++ b/README.md @@ -79,13 +79,13 @@ Almost everything: - hideouts - farms - minigames +- clans - MMO (using sodoff-mmo) ### What doesn't work - play as Guest - friends -- clans - in-game messaging system (Terrible Mail) @@ -94,10 +94,13 @@ Almost everything: #### Fully implemented - AcceptMission - AddBattleItems +- AssignRole - AuthenticateUser +- CreateGroup - CreatePet - DeleteAccountNotification - DeleteProfile +- EditGroup - FuseItems - GetAchievementsByUserID - GetAllActivePetsByuserId @@ -108,12 +111,17 @@ Almost everything: - GetDetailedChildList - GetGameData - GetGameDataByUser +- GetGroups +- GetGroupsByGroupType +- GetGroupsByUserID - GetImage - GetImageByUserId - GetItem - GetKeyValuePair - GetKeyValuePairByUserID +- GetMembersByGroupID - GetMMOServerInfoWithZone (uses resource xml as response) +- GetPendingJoinRequests - GetPetAchievementsByUserID - GetSelectedRaisedPet - GetStore @@ -127,6 +135,8 @@ Almost everything: - GetUserRoomItemPositions - GetUserUpcomingMissionState - IsValidApiToken_V2 +- JoinGroup (V1 & V2) (User-given message for join request disabled, at least for now) +- LeaveGroup - LoginChild - LoginParent - PurchaseItems (V1) @@ -152,7 +162,6 @@ 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) @@ -171,7 +180,6 @@ 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) diff --git a/src/Controllers/Common/GroupController.cs b/src/Controllers/Common/GroupController.cs index 8ee0d9c..ddd6ceb 100644 --- a/src/Controllers/Common/GroupController.cs +++ b/src/Controllers/Common/GroupController.cs @@ -5,7 +5,57 @@ using sodoff.Schema; using sodoff.Util; namespace sodoff.Controllers.Common; + public class GroupController : Controller { + private static readonly List RolePermissions; + + static GroupController() { + RolePermissions = new List(); + for (GroupType type = GroupType.Public; type <= GroupType.Private; type++) { + // Anything commented out is not implemented. + RolePermissions.Add(new RolePermission { + GroupType = type, + Role = UserRole.Member, + Permissions = [ + "Delete Own Msg", + //"Post Message" + ] + }); + RolePermissions.Add(new RolePermission { + GroupType = type, + Role = UserRole.Elder, + Permissions = [ + //"Invite", + "Approve Join Request", + //"Post News", + //"Delete Own Msg", + //"Delete Any Msg", + //"Delete News", + //"Post Message", + "Remove Member" + ] + }); + RolePermissions.Add(new RolePermission { + GroupType = type, + Role = UserRole.Leader, + Permissions = [ + //"Invite", + "Approve Join Request", + "Assign Leader", + "Assign Elder", + "Demote Elder", + "Edit Group", + //"Post News", + //"Delete Own Msg", + //"Delete Any Msg", + //"Delete News", + //"Post Message", + "Remove Member" + ] + }); + } + } + public static readonly Schema.Group EMD_Dragons = new Schema.Group { GroupID = "8e68214a-c801-4759-8461-d01f28484134", Name = "Dragons", @@ -24,56 +74,7 @@ public class GroupController : Controller { 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 groups = new List(); - 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); @@ -84,7 +85,8 @@ public class GroupController : Controller { Color = EMD_Dragons.Color, Logo = EMD_Dragons.Logo, Type = GroupType.System, - ApiKey = "dd602cf1-cc98-4738-9a0a-56dde3026947" + GameID = ClientVersion.EMD, + MaxMemberLimit = int.MaxValue }); changed = true; } @@ -95,10 +97,451 @@ public class GroupController : Controller { Color = EMD_Scorpions.Color, Logo = EMD_Scorpions.Logo, Type = GroupType.System, - ApiKey = "dd602cf1-cc98-4738-9a0a-56dde3026947" + GameID = ClientVersion.EMD, + MaxMemberLimit = int.MaxValue }); changed = true; } if (changed) ctx.SaveChanges(); } + + [HttpPost] + [Produces("application/xml")] + [Route("V2/GroupWebService.asmx/CreateGroup")] + [VikingSession] + public IActionResult CreateGroup(Viking viking, [FromForm] string apiKey, [FromForm] string groupCreateRequest) { + uint gameId = ClientVersion.GetGameID(apiKey); + + if (viking.GroupRoles.Any(g => g.GameID == gameId)) { + return Ok(new CreateGroupResult { Success = false, Status = CreateGroupStatus.CreatorIsNotApproved }); + } + + CreateGroupRequest request = XmlUtil.DeserializeXml(groupCreateRequest); + request.Name = request.Name.Trim(); + + // Cue the gauntlet of validity checks. + if (request.Type <= GroupType.System) return Ok(new CreateGroupResult { Success = false, Status = CreateGroupStatus.GroupTypeIsInvalid }); + //if (request.MaxMemberLimit < 4) return Ok(new CreateGroupResult { Success = false, Status = CreateGroupStatus.GroupMaxMemberLimitInvalid }); // Not actually used by the game. + if (request.Name.Length == 0) return Ok(new CreateGroupResult { Success = false, Status = CreateGroupStatus.GroupNameIsEmpty }); + if (request.Description.Length == 0) return Ok(new CreateGroupResult { Success = false, Status = CreateGroupStatus.GroupDescriptionIsEmpty }); + if (viking.GroupRoles.Any(g => g.Name.Equals(request.Name, StringComparison.InvariantCultureIgnoreCase))) return Ok(new CreateGroupResult { Success = false, Status = CreateGroupStatus.GroupNameIsDuplicate }); + + Model.Group group = new Model.Group { + Name = request.Name, + Description = request.Description, + Logo = request.Logo, + Color = request.Color, + Type = request.Type, + CreateDate = DateTime.Now, + GameID = gameId, + MaxMemberLimit = 50, + GroupID = Guid.NewGuid(), + Vikings = new List() + }; + + ctx.Groups.Add(group); + group.Vikings.Add(new GroupViking { + Viking = viking, + Group = group, + UserRole = UserRole.Leader, + JoinDate = group.CreateDate + }); + group.LastActiveTime = group.CreateDate; + ctx.SaveChanges(); + + return Ok(new CreateGroupResult { + Success = true, + Status = CreateGroupStatus.Success, + Group = new Schema.Group { + Name = group.Name, + Description = group.Description, + Logo = group.Logo, + Color = group.Color, + Type = group.Type, + OwnerID = viking.Uid.ToString(), + Points = 0,//group.Points, + Active = false,//group.Vikings.Count >= 4, + MemberLimit = group.MaxMemberLimit, + GroupID = group.GroupID.ToString() + } + // TODO: Delete after 15 days of less than 4 members. + }); + } + + [HttpPost] + [Produces("application/xml")] + [Route("V2/GroupWebService.asmx/EditGroup")] + [VikingSession] + public IActionResult EditGroup(Viking viking, [FromForm] string apiKey, [FromForm] string groupEditRequest) { + EditGroupRequest request = XmlUtil.DeserializeXml(groupEditRequest); + request.Name = request.Name.Trim(); + + GroupViking? vikingRole = viking.GroupRoles.FirstOrDefault(gv => gv.Group.GroupID.ToString() == request.GroupID); + if (vikingRole == null) { + return Ok(new EditGroupResult { Success = false, Status = EditGroupStatus.GroupNotFound }); + } else if (vikingRole.UserRole < UserRole.Elder) { + return Ok(new EditGroupResult { Success = false, Status = EditGroupStatus.PermissionDenied }); + } + + // Cue the gauntlet of validity checks. + if (request.Type != null && request.Type <= GroupType.System) return Ok(new EditGroupResult { Success = false, Status = EditGroupStatus.GroupTypeIsInvalid }); + //if (request.MaxMemberLimit < 4) return Ok(new EditGroupResult { Success = false, Status = EditGroupStatus.GroupMaxMemberLimitInvalid }); // Not actually used by the game. + if ((request.Name?.Length ?? 0) == 0) request.Name = vikingRole.Name; + if ((request.Description?.Length ?? 0) == 0) request.Description = vikingRole.Description; + if (request.Name != vikingRole.Name && viking.GroupRoles.Any(g => g.Name.Equals(request.Name, StringComparison.InvariantCultureIgnoreCase))) return Ok(new EditGroupResult { Success = false, Status = EditGroupStatus.GroupNameIsDuplicate }); + + vikingRole.Group.Name = request.Name; + vikingRole.Group.Description = request.Description; + if (request.Type != null) vikingRole.Group.Type = (GroupType)request.Type; + if ((request.Color?.Length ?? 0) > 0) vikingRole.Group.Color = request.Color; + if ((request.Logo?.Length ?? 0) > 0) vikingRole.Group.Logo = request.Logo; + ctx.SaveChanges(); + + return Ok(new EditGroupResult { + Success = true, + Status = EditGroupStatus.Success, + NewRolePermissions = RolePermissions + }); + } + + [HttpPost] + [Produces("application/xml")] + [Route("GroupWebService.asmx/JoinGroup")] + [VikingSession] + public IActionResult JoinGroupV1(Viking viking, [FromForm] string apiKey, [FromForm] string groupID) { + uint gameId = ClientVersion.GetGameID(apiKey); + + // Check for loyalty. + if (viking.GroupRoles.Any(g => g.GameID == gameId)) { + return Ok(new JoinGroupResult { GroupStatus = GroupMembershipStatus.SELF_BLOCKED }); + } + Model.Group? group = ctx.Groups.FirstOrDefault(g => g.GroupID.ToString().Equals(groupID, StringComparison.OrdinalIgnoreCase)); + if (group != null) { + // This check is only on this side to prevent people from attempting to circumvent the join limit. + if (group.Type <= GroupType.System || group.Vikings.Count < group.MaxMemberLimit) { + group.Vikings.Add(new GroupViking { + Viking = viking, + Group = group, + UserRole = UserRole.Member + }); + ctx.SaveChanges(); + return Ok(new JoinGroupResult { GroupStatus = GroupMembershipStatus.APPROVED }); + } + } + return Ok(new JoinGroupResult { GroupStatus = GroupMembershipStatus.REJECTED }); + } + + [HttpPost] + [Produces("application/xml")] + [Route("V2/GroupWebService.asmx/JoinGroup")] + [VikingSession] + public IActionResult JoinGroup(Viking viking, [FromForm] string apiKey, [FromForm] string groupJoinRequest) { + uint gameId = ClientVersion.GetGameID(apiKey); + + JoinGroupRequest request = XmlUtil.DeserializeXml(groupJoinRequest); + Model.Group? group = ctx.Groups.FirstOrDefault(g => g.GroupID.ToString() == request.GroupID.ToUpper()); + if (group != null) { + if (group.Type >= GroupType.Private) { + return Ok(new GroupJoinResult { Success = false, Status = JoinGroupStatus.GroupTypeIsNotPublic }); + } + GroupViking? existing = viking.GroupRoles.FirstOrDefault(g => g.GameID == gameId); + if (existing != null) { + if (existing.Group == group) + return Ok(new GroupJoinResult { Success = false, Status = JoinGroupStatus.UserAlreadyMemberOfTheGroup }); + existing.Group.Vikings.Remove(existing); + if (!existing.Group.Vikings.Any()) ctx.Groups.Remove(existing.Group); + } + if (group.Type == GroupType.MembersOnly) { + if (!group.JoinRequests.Any(r => r.Viking == viking)) + group.JoinRequests.Add(new GroupJoinRequest { + Group = group, + Viking = viking, + //Message = request.Message // For future implemention, once moderation is possible. + }); + ctx.SaveChanges(); + return Ok(new GroupJoinResult { Success = false, Status = JoinGroupStatus.JoinRequestPending }); + } + + if (group.Vikings.Count < group.MaxMemberLimit) { + GroupViking joinee = new GroupViking { + Viking = viking, + Group = group, + UserRole = UserRole.Member, + JoinDate = DateTime.Now + }; + group.Vikings.Add(joinee); + group.LastActiveTime = joinee.JoinDate; + ctx.SaveChanges(); + return Ok(new GroupJoinResult { Success = true, Status = JoinGroupStatus.Success }); + } else { + return Ok(new GroupJoinResult { Success = false, Status = JoinGroupStatus.GroupIsFull }); + } + } + return Ok(new GroupJoinResult { Success = false, Status = JoinGroupStatus.Error }); + } + + [HttpPost] + [Produces("application/xml")] + [Route("V2/GroupWebService.asmx/LeaveGroup")] + [VikingSession] + public IActionResult LeaveGroup(Viking viking, [FromForm] string groupLeaveRequest) { + LeaveGroupRequest request = XmlUtil.DeserializeXml(groupLeaveRequest); + GroupViking? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); + if (vikingRole != null) { + GroupViking? targetRole = null; + if (viking.Uid.ToString().Equals(request.UserID, StringComparison.CurrentCultureIgnoreCase)) { + targetRole = vikingRole.Group.Vikings.FirstOrDefault(gv => gv.Viking == viking); + } else if (vikingRole.UserRole >= UserRole.Elder) { + targetRole = vikingRole.Group.Vikings.FirstOrDefault(gv => gv.Viking.Uid.ToString() == request.UserID); + } else { + return Ok(new LeaveGroupResult { Success = false, Status = LeaveGroupStatus.Error }); + } + if (targetRole != null) { + vikingRole.Group.Vikings.Remove(targetRole); + if (!vikingRole.Group.Vikings.Any()) ctx.Groups.Remove(vikingRole.Group); + ctx.SaveChanges(); + return Ok(new LeaveGroupResult { Success = true, Status = LeaveGroupStatus.Success }); + } + return Ok(new LeaveGroupResult { Success = false, Status = LeaveGroupStatus.UserNotAMemberOfTheGroup }); + } + return Ok(new LeaveGroupResult { Success = false, Status = LeaveGroupStatus.Error }); + } + + [HttpPost] + [Produces("application/xml")] + [Route("V2/GroupWebService.asmx/GetGroups")] + public IActionResult GetGroups([FromForm] string apiKey, [FromForm] string getGroupsRequest) { + uint gameId = ClientVersion.GetGameID(apiKey); + + GetGroupsRequest request = XmlUtil.DeserializeXml(getGroupsRequest); + IEnumerable groups = ctx.Groups; + if (request.ForUserID != null) { + Viking? target = ctx.Vikings.FirstOrDefault(v => request.ForUserID.ToUpper() == v.Uid.ToString()); + if (target == null) return Ok(new GetGroupsResult { Success = false }); + groups = target.GroupRoles.Select(gv => gv.Group); + } else { + groups = groups.Where(g => g.Type == GroupType.Public || g.Type == GroupType.MembersOnly); + } + if (request.Name != null) { + groups = groups.Where(g => g.Name?.Contains(request.Name, StringComparison.InvariantCultureIgnoreCase) == true); + } + int skip = 0; + if (request.PageSize != null) { + if ((request.PageNo ?? 0) > 1) skip = (int)((request.PageNo! - 1) * request.PageSize); + if (skip > 0) groups = groups.Skip(skip); + groups = groups.Take((int) request.PageSize); + } + groups = groups.Where(g => g.GameID == gameId).OrderByDescending(g => g.Points); + + return Ok(new GetGroupsResult { + Success = true, + Groups = groups.Select((g, i) => { + Schema.Group group = new Schema.Group { + Name = g.Name, + Description = g.Description, + GroupID = g.GroupID.ToString(), + OwnerID = (g.Vikings.FirstOrDefault(v => v.UserRole == UserRole.Leader)?.Viking?.Uid ?? Guid.Empty).ToString(), + Color = g.Color, + Logo = g.Logo, + Type = g.Type, + Rank = i+skip+1, + MemberLimit = g.MaxMemberLimit + }; + if (request.IncludeMemberCount) group.TotalMemberCount = g.Vikings.Count; + return group; + }).ToArray(), + RolePermissions = RolePermissions + }); + } + + [HttpPost] + [Produces("application/xml")] + [Route("V2/GroupWebService.asmx/RemoveMember")] + [VikingSession] + public IActionResult RemoveMember(Viking viking, [FromForm] string removeMemberRequest) { + RemoveMemberRequest request = XmlUtil.DeserializeXml(removeMemberRequest); + GroupViking? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); + if (vikingRole != null) { + if (vikingRole.UserRole < UserRole.Elder) { + return Ok(new RemoveMemberResult { Success = false, Status = RemoveMemberStatus.UserHasNoPermission }); + } + GroupViking? targetRole = vikingRole.Group.Vikings.FirstOrDefault(gv => gv.Viking.Uid.ToString() == request.RemoveUserID); + if (targetRole != null) { + vikingRole.Group.Vikings.Remove(targetRole); + if (!vikingRole.Group.Vikings.Any()) ctx.Groups.Remove(vikingRole.Group); + ctx.SaveChanges(); + return Ok(new RemoveMemberResult { Success = true, Status = RemoveMemberStatus.Success }); + } + return Ok(new RemoveMemberResult { Success = false, Status = RemoveMemberStatus.UserNotAMemberOfTheGroup }); + } + return Ok(new RemoveMemberResult { Success = false, Status = RemoveMemberStatus.Error }); + } + + [HttpPost] + [Produces("application/xml")] + [Route("V2/GroupWebService.asmx/AuthorizeJoinRequest")] + [VikingSession] + public IActionResult AuthorizeJoinRequest(Viking viking, [FromForm] string apiKey, [FromForm] string authorizeJoinRequest) { + uint gameId = ClientVersion.GetGameID(apiKey); + + AuthorizeJoinRequest request = XmlUtil.DeserializeXml(authorizeJoinRequest); + GroupViking? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); + if (vikingRole != null) { + if (vikingRole.UserRole < UserRole.Elder) { + return Ok(new AuthorizeJoinResult { Success = false, Status = AuthorizeJoinStatus.ApproverHasNoPermission }); + } + Viking? target = ctx.Vikings.FirstOrDefault(v => v.Uid.ToString() == request.UserID); + if (target != null) { + GroupViking? existing = target.GroupRoles.FirstOrDefault(g => g.GameID == gameId); + if (existing != null) { + if (existing.Group == vikingRole.Group) { + return Ok(new AuthorizeJoinResult { Success = false, Status = AuthorizeJoinStatus.UserAlreadyMemberOfTheGroup }); + } else { + return Ok(new AuthorizeJoinResult { Success = false, Status = AuthorizeJoinStatus.UserHasNoJoinRequest }); + } + } + if (vikingRole.Group.Vikings.Count < vikingRole.Group.MaxMemberLimit) { + if (request.Approved) { + GroupViking joinee = new GroupViking { + Viking = target, + Group = vikingRole.Group, + UserRole = UserRole.Member, + JoinDate = DateTime.Now + }; + vikingRole.Group.Vikings.Add(joinee); + vikingRole.Group.LastActiveTime = joinee.JoinDate; + } + GroupJoinRequest? joinRequest = ctx.GroupJoinRequests.Find(target.Id, vikingRole.GroupID); + if (joinRequest != null) ctx.GroupJoinRequests.Remove(joinRequest); + ctx.SaveChanges(); + return Ok(new AuthorizeJoinResult { Success = true, Status = AuthorizeJoinStatus.Success }); + } else { + return Ok(new AuthorizeJoinResult { Success = false, Status = AuthorizeJoinStatus.GroupIsFull }); + } + } else { + return Ok(new AuthorizeJoinResult { Success = false, Status = AuthorizeJoinStatus.Error }); + } + } + return Ok(new AuthorizeJoinResult { Success = false, Status = AuthorizeJoinStatus.ApproverNotInThisGroup }); + } + + [HttpPost] + [Produces("application/xml")] + [Route("V2/GroupWebService.asmx/AssignRole")] + [VikingSession] + public IActionResult AssignRole(Viking viking, [FromForm] string assignRoleRequest) { + AssignRoleRequest request = XmlUtil.DeserializeXml(assignRoleRequest); + GroupViking? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); + if (vikingRole != null) { + if (vikingRole.UserRole < UserRole.Elder) { + return Ok(new AssignRoleResult { Success = false, Status = AssignRoleStatus.ApproverHasNoPermission }); + } + GroupViking? targetRole = vikingRole.Group.Vikings.FirstOrDefault(gv => gv.Viking.Uid.ToString() == request.MemberID); + if (targetRole != null) { + if (targetRole.UserRole == request.NewRole) + return Ok(new AssignRoleResult { Success = false, Status = AssignRoleStatus.MemberAlreadyInTheRole }); + if (vikingRole.UserRole == UserRole.Leader) { // Disallow leader from simply demoting themself. + if (viking == targetRole.Viking) + return Ok(new AssignRoleResult { Success = false, Status = AssignRoleStatus.ApproverHasNoPermission }); + } else if (viking != targetRole.Viking || request.NewRole > vikingRole.UserRole) { // Disallow Elders from promoting themselves to leader, or promoting anyone else to elder, but allow them to demote themselves. + return Ok(new AssignRoleResult { Success = false, Status = AssignRoleStatus.ApproverHasNoPermission }); + } + targetRole.UserRole = request.NewRole; + if (request.NewRole == UserRole.Leader) vikingRole.UserRole = UserRole.Elder; // This is the only way a leader can demote themself. + ctx.SaveChanges(); + return Ok(new AssignRoleResult { Success = true, Status = AssignRoleStatus.Success }); + } else { + return Ok(new AssignRoleResult { Success = false, Status = AssignRoleStatus.MemberNotPartOfTheGroup }); + } + } + return Ok(new AssignRoleResult { Success = false, Status = AssignRoleStatus.ApproverNotMemberOfTheGroup }); + } + + [HttpPost] + [Produces("application/xml")] + [Route("V2/GroupWebService.asmx/GetPendingJoinRequest")] + [VikingSession] + public IActionResult GetPendingJoinRequests(Viking viking, [FromForm] string getPendingJoinRequest) { + GetPendingJoinRequest request = XmlUtil.DeserializeXml(getPendingJoinRequest); + GroupViking? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); + if (vikingRole?.UserRole >= UserRole.Elder) { + return Ok(new GetPendingJoinResult { + Success = true, + Requests = vikingRole.Group.JoinRequests + .Select(r => { + PendingJoinRequest req = new PendingJoinRequest { + UserID = r.Viking.Uid.ToString(), + GroupID = vikingRole.Group.GroupID.ToString(), + StatusID = GroupJoinRequestStatus.Pending, + Message = r.Message ?? "Hello! Please invite me to your Crew!" // Defualt from Math Blaster btw + }; + req.FromUserID = req.UserID; + return req; + }).ToArray() + }); + } + return Ok(new GetPendingJoinResult { Success = false }); + } + + [HttpPost] + [Produces("application/xml")] + [Route("GroupWebService.asmx/GetGroupsByUserID")] + public Schema.Group[] GetGroupsByUserID([FromForm] string apiKey, [FromForm] string userId) { + uint gameId = ClientVersion.GetGameID(apiKey); + + Viking? viking = ctx.Vikings.FirstOrDefault(v => v.Uid.ToString() == userId); + if (viking == null) return []; + + return viking.GroupRoles.Where(gv => gv.GameID == gameId).Select(gv => new Schema.Group { + GroupID = gv.Group.GroupID.ToString(), + Name = gv.Name, + Color = gv.Color, + Logo = gv.Logo, + MemberLimit = gv.Group.MaxMemberLimit + }).ToArray(); + } + + [HttpPost] + [Produces("application/xml")] + [Route("GroupWebService.asmx/GetGroupsByGroupType")] + public Schema.Group[] GetGroupsByGroupType([FromForm] string apiKey, [FromForm] string groupType) { + uint gameId = ClientVersion.GetGameID(apiKey); + + List groups = new List(); + foreach (Model.Group group in ctx.Groups) { + if (group.GameID == gameId && 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, + MemberLimit = group.MaxMemberLimit + }); + } + return groups.ToArray(); + } + + [HttpPost] + [Produces("application/xml")] + [Route("GroupWebService.asmx/GetMembersByGroupID")] + public GroupMember[] GetMembersByGroupID([FromForm] string groupID) { + groupID = groupID.ToUpper(); + Model.Group? group = ctx.Groups.FirstOrDefault( + g => g.GroupID.ToString() == groupID + ); + if (group == null) return []; + + return group.Vikings.Select(v => new GroupMember{ + DisplayName = v.Viking.AvatarSerialized != null + ? XmlUtil.DeserializeXml(v.Viking.AvatarSerialized).DisplayName + : v.Viking.Name, + UserID = v.Viking.Uid.ToString(), + JoinDate = v.JoinDate, + RoleID = (int) v.UserRole, + Online = false, // There's no way to check this. + Rank = 0, + GroupID = group.GroupID.ToString(), + Points = group.Points + }).ToArray(); + } } diff --git a/src/Controllers/Common/ProfileController.cs b/src/Controllers/Common/ProfileController.cs index c6928c2..68da31a 100644 --- a/src/Controllers/Common/ProfileController.cs +++ b/src/Controllers/Common/ProfileController.cs @@ -152,18 +152,18 @@ public class ProfileController : Controller { UserGameCurrency currency = achievementService.GetUserCurrency(viking); - ICollection groups = viking.Groups; + ICollection groups = viking.GroupRoles; UserProfileGroupData[] groupData = new UserProfileGroupData[groups.Count]; int i = 0; - foreach (Model.Group group in groups) { + foreach (GroupViking group in groups) { groupData[i] = new UserProfileGroupData { - GroupID = group.GroupID.ToString(), + GroupID = group.Group.GroupID.ToString(), Name = group.Name, Color = group.Color, Logo = group.Logo, TypeID = (int)group.Type, - RoleID = 0 + RoleID = group.UserRole }; i++; } diff --git a/src/Model/DBContext.cs b/src/Model/DBContext.cs index 1832763..9d0a38c 100644 --- a/src/Model/DBContext.cs +++ b/src/Model/DBContext.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using sodoff.Configuration; +using System.Text.Json; namespace sodoff.Model; public class DBContext : DbContext { @@ -26,6 +27,8 @@ public class DBContext : DbContext { public DbSet Neighborhoods { get; set; } = null!; // we had a brief debate on whether it's neighborhoods or neighborheed public DbSet Groups { get; set; } = null!; + public DbSet GroupViking { get; set; } = null!; + public DbSet GroupJoinRequests { get; set; } = null!; public DbSet Ratings { get; set; } = null!; public DbSet RatingRanks { get; set; } = null!; public DbSet UserMissionData { get; set; } = null!; @@ -66,6 +69,9 @@ public class DBContext : DbContext { } protected override void OnModelCreating(ModelBuilder builder) { + builder.Entity().HasKey(["VikingID", "GroupID"]); + builder.Entity().HasKey(["VikingID", "GroupID"]); + // Sessions builder.Entity().HasOne(s => s.User) .WithMany(e => e.Sessions) @@ -144,8 +150,8 @@ public class DBContext : DbContext { builder.Entity().HasOne(v => v.Neighborhood) .WithOne(e => e.Viking); - builder.Entity().HasMany(v => v.Groups) - .WithMany(e => e.Vikings); + builder.Entity().HasMany(v => v.GroupRoles) + .WithOne(g => g.Viking); builder.Entity().HasMany(v => v.Ratings) .WithOne(r => r.Viking); @@ -279,7 +285,15 @@ public class DBContext : DbContext { // Groups builder.Entity().HasMany(r => r.Vikings) - .WithMany(e => e.Groups); + .WithOne(v => v.Group); + builder.Entity().HasOne(r => r.Group) + .WithMany(g => g.Vikings); + builder.Entity().HasOne(r => r.Viking) + .WithMany(g => g.GroupRoles); + builder.Entity().HasMany(r => r.JoinRequests) + .WithOne(r => r.Group); + builder.Entity().HasOne(r => r.Group) + .WithMany(g => g.JoinRequests); // Rating builder.Entity().HasOne(r => r.Viking) diff --git a/src/Model/Group.cs b/src/Model/Group.cs index c06d450..b83b14f 100644 --- a/src/Model/Group.cs +++ b/src/Model/Group.cs @@ -3,23 +3,33 @@ 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 string? Name { get; set; } + + public string? Description { get; set; } public GroupType Type { get; set; } - public string Color { get; set; } + public string? Logo { get; set; } - public string Logo { get; set; } + public string? Color { get; set; } - public string ApiKey { get; set; } + public int Points { get; set; } - public virtual ICollection Vikings { get; set; } = null!; + public DateTime CreateDate { get; set; } + + public DateTime? LastActiveTime { get; set; } + + public uint GameID { get; set; } + + public int MaxMemberLimit { get; set; } + + public virtual ICollection Vikings { get; set; } = null!; + + public virtual ICollection JoinRequests { get; set; } = null!; } diff --git a/src/Model/GroupJoinRequest.cs b/src/Model/GroupJoinRequest.cs new file mode 100644 index 0000000..fc84077 --- /dev/null +++ b/src/Model/GroupJoinRequest.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; + +namespace sodoff.Model; + +public class GroupJoinRequest { + [Key] + public int VikingID { get; set; } + [Key] + public int GroupID { get; set; } + + public string? Message { get; set; } + + public virtual Viking Viking { get; set; } = null!; + public virtual Group Group { get; set; } = null!; +} diff --git a/src/Model/GroupViking.cs b/src/Model/GroupViking.cs new file mode 100644 index 0000000..51529f9 --- /dev/null +++ b/src/Model/GroupViking.cs @@ -0,0 +1,35 @@ +using sodoff.Schema; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace sodoff.Model; + +public class GroupViking { + [Key] + public int VikingID { get; set; } + [Key] + public int GroupID { get; set; } + public UserRole UserRole { get; set; } + public DateTime JoinDate { get; set; } + + public virtual Viking Viking { get; set; } = null!; + public virtual Group Group { get; set; } = null!; + + [NotMapped] + public string Name { get { return Group.Name; } } + + [NotMapped] + public string? Description { get { return Group.Description; } } + + [NotMapped] + public GroupType Type { get { return Group.Type; } } + + [NotMapped] + public string Logo { get { return Group.Logo; } } + + [NotMapped] + public string Color { get { return Group.Color; } } + + [NotMapped] + public uint GameID { get { return Group.GameID; } } +} diff --git a/src/Model/Viking.cs b/src/Model/Viking.cs index a787107..d67f0df 100644 --- a/src/Model/Viking.cs +++ b/src/Model/Viking.cs @@ -39,7 +39,7 @@ public class Viking { public virtual ICollection Parties { get; set; } = null!; public virtual ICollection MMORoles { get; set; } = null!; public virtual Neighborhood? Neighborhood { get; set; } = null!; - public virtual ICollection Groups { get; set; } = null!; + public virtual ICollection GroupRoles { get; set; } = null!; public virtual ICollection Ratings { get; set; } = null!; public virtual Dragon? SelectedDragon { get; set; } public virtual ICollection UserMissions { get; set; } = null!; diff --git a/src/Schema/AssignRoleRequest.cs b/src/Schema/AssignRoleRequest.cs new file mode 100644 index 0000000..6022310 --- /dev/null +++ b/src/Schema/AssignRoleRequest.cs @@ -0,0 +1,25 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "AssignRoleRequest", IsNullable = true)] +[Serializable] +public class AssignRoleRequest { + [XmlElement(ElementName = "UserID")] + public string UserID; + + [XmlElement(ElementName = "MemberID")] + public string MemberID; + + [XmlElement(ElementName = "NewRole")] + public UserRole NewRole; + + [XmlElement(ElementName = "GroupID")] + public string GroupID; + + [XmlElement(ElementName = "ProductGroupID")] + public int? ProductGroupID; + + [XmlElement(ElementName = "ProductID")] + public int? ProductID; +} diff --git a/src/Schema/AssignRoleResult.cs b/src/Schema/AssignRoleResult.cs new file mode 100644 index 0000000..59e9526 --- /dev/null +++ b/src/Schema/AssignRoleResult.cs @@ -0,0 +1,16 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "AssignRoleResult", IsNullable = true)] +[Serializable] +public class AssignRoleResult { + [XmlElement(ElementName = "Success")] + public bool Success; + + [XmlElement(ElementName = "InitiatorNewRole", IsNullable = true)] + public UserRole? InitiatorNewRole; + + [XmlElement(ElementName = "Status")] + public AssignRoleStatus Status; +} diff --git a/src/Schema/AssignRoleStatus.cs b/src/Schema/AssignRoleStatus.cs new file mode 100644 index 0000000..dc0c1a8 --- /dev/null +++ b/src/Schema/AssignRoleStatus.cs @@ -0,0 +1,18 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +public enum AssignRoleStatus { + [XmlEnum("1")] + Success = 1, + [XmlEnum("2")] + Error, + [XmlEnum("3")] + ApproverNotMemberOfTheGroup, + [XmlEnum("4")] + ApproverHasNoPermission, + [XmlEnum("5")] + MemberAlreadyInTheRole, + [XmlEnum("6")] + MemberNotPartOfTheGroup +} diff --git a/src/Schema/AuthorizeJoinRequest.cs b/src/Schema/AuthorizeJoinRequest.cs new file mode 100644 index 0000000..8847d49 --- /dev/null +++ b/src/Schema/AuthorizeJoinRequest.cs @@ -0,0 +1,25 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "AuthorizeJoinRequest", IsNullable = true)] +[Serializable] +public class AuthorizeJoinRequest { + [XmlElement(ElementName = "Approved")] + public bool Approved; + + [XmlElement(ElementName = "JoineeID")] + public string JoineeID; + + [XmlElement(ElementName = "UserID")] + public string UserID; + + [XmlElement(ElementName = "GroupID")] + public string GroupID; + + [XmlElement(ElementName = "ProductGroupID")] + public int? ProductGroupID; + + [XmlElement(ElementName = "ProductID")] + public int? ProductID; +} diff --git a/src/Schema/AuthorizeJoinResult.cs b/src/Schema/AuthorizeJoinResult.cs new file mode 100644 index 0000000..aadddc7 --- /dev/null +++ b/src/Schema/AuthorizeJoinResult.cs @@ -0,0 +1,13 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "AuthorizeJoinResult", IsNullable = true)] +[Serializable] +public class AuthorizeJoinResult { + [XmlElement(ElementName = "Success")] + public bool Success; + + [XmlElement(ElementName = "Status")] + public AuthorizeJoinStatus Status; +} diff --git a/src/Schema/AuthorizeJoinStatus.cs b/src/Schema/AuthorizeJoinStatus.cs new file mode 100644 index 0000000..ad40cf1 --- /dev/null +++ b/src/Schema/AuthorizeJoinStatus.cs @@ -0,0 +1,24 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +public enum AuthorizeJoinStatus { + [XmlEnum("1")] + Success = 1, + [XmlEnum("2")] + Error, + [XmlEnum("3")] + GroupIsFull, + [XmlEnum("4")] + GroupTypeIsNotPublic, + [XmlEnum("5")] + UserAlreadyMemberOfTheGroup, + [XmlEnum("6")] + GroupTypeIsPublic, + [XmlEnum("7")] + ApproverNotInThisGroup, + [XmlEnum("8")] + ApproverHasNoPermission, + [XmlEnum("9")] + UserHasNoJoinRequest +} diff --git a/src/Schema/CreateGroupRequest.cs b/src/Schema/CreateGroupRequest.cs new file mode 100644 index 0000000..890602b --- /dev/null +++ b/src/Schema/CreateGroupRequest.cs @@ -0,0 +1,40 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "CreateGroupRequest", IsNullable = true)] +[Serializable] +public class CreateGroupRequest { + [XmlElement(ElementName = "Name")] + public string Name; + + [XmlElement(ElementName = "Description")] + public string Description; + + [XmlElement(ElementName = "Type")] + public GroupType Type; + + [XmlElement(ElementName = "Logo")] + public string Logo; + + [XmlElement(ElementName = "Color")] + public string Color; + + [XmlElement(ElementName = "UserID")] + public string UserID; + + [XmlElement(ElementName = "DefaultLeaderID")] + public string DefaultLeaderID; + + [XmlElement(ElementName = "ParentGroupID")] + public string ParentGroupID; + + [XmlElement(ElementName = "ProductGroupID")] + public int? ProductGroupID; + + [XmlElement(ElementName = "ProductID")] + public int? ProductID; + + [XmlElement(ElementName = "MaxMemberLimit")] + public int? MaxMemberLimit; +} diff --git a/src/Schema/CreateGroupResult.cs b/src/Schema/CreateGroupResult.cs new file mode 100644 index 0000000..c08cfc3 --- /dev/null +++ b/src/Schema/CreateGroupResult.cs @@ -0,0 +1,16 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "CreateGroupResult", IsNullable = true)] +[Serializable] +public class CreateGroupResult { + [XmlElement(ElementName = "Success")] + public bool Success; + + [XmlElement(ElementName = "Status")] + public CreateGroupStatus Status; + + [XmlElement(ElementName = "Group")] + public Group Group; +} \ No newline at end of file diff --git a/src/Schema/CreateGroupStatus.cs b/src/Schema/CreateGroupStatus.cs new file mode 100644 index 0000000..72c965c --- /dev/null +++ b/src/Schema/CreateGroupStatus.cs @@ -0,0 +1,32 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +public enum CreateGroupStatus { + [XmlEnum("1")] + Success = 1, + [XmlEnum("2")] + Error, + [XmlEnum("3")] + GroupNameInvalid, + [XmlEnum("4")] + GroupDescriptionInvalid, + [XmlEnum("5")] + GroupNameIsDuplicate, + [XmlEnum("6")] + GroupOwnerHasInSufficientCurrency, + [XmlEnum("7")] + UnableToGetCreatorDetails, + [XmlEnum("8")] + CreatorIsNotApproved, + [XmlEnum("9")] + GroupNameIsEmpty, + [XmlEnum("10")] + GroupDescriptionIsEmpty, + [XmlEnum("11")] + GroupTypeIsInvalid, + [XmlEnum("12")] + GroupMaxMemberLimitInvalid, + [XmlEnum("13")] + AutomaticItemPurchaseFailed +} diff --git a/src/Schema/EditGroupRequest.cs b/src/Schema/EditGroupRequest.cs new file mode 100644 index 0000000..cec0ca4 --- /dev/null +++ b/src/Schema/EditGroupRequest.cs @@ -0,0 +1,37 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "EditGroupRequest", IsNullable = true)] +[Serializable] +public class EditGroupRequest { + [XmlElement(ElementName = "GroupID")] + public string GroupID; + + [XmlElement(ElementName = "UserID")] + public string UserID; + + [XmlElement(ElementName = "Name")] + public string Name; + + [XmlElement(ElementName = "Description")] + public string Description; + + [XmlElement(ElementName = "Type")] + public GroupType? Type; + + [XmlElement(ElementName = "Logo")] + public string Logo; + + [XmlElement(ElementName = "Color")] + public string Color; + + [XmlElement(ElementName = "ProductGroupID")] + public int? ProductGroupID; + + [XmlElement(ElementName = "ProductID")] + public int? ProductID; + + [XmlElement(ElementName = "MaxMemberLimit")] + public int? MaxMemberLimit; +} diff --git a/src/Schema/EditGroupResult.cs b/src/Schema/EditGroupResult.cs new file mode 100644 index 0000000..0c88ce1 --- /dev/null +++ b/src/Schema/EditGroupResult.cs @@ -0,0 +1,16 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "EditGroupResult", IsNullable = true)] +[Serializable] +public class EditGroupResult { + [XmlElement(ElementName = "Success")] + public bool Success; + + [XmlElement(ElementName = "Status")] + public EditGroupStatus Status; + + [XmlElement(ElementName = "NewRolePermissions", IsNullable = true)] + public List NewRolePermissions; +} diff --git a/src/Schema/EditGroupStatus.cs b/src/Schema/EditGroupStatus.cs new file mode 100644 index 0000000..9ed099c --- /dev/null +++ b/src/Schema/EditGroupStatus.cs @@ -0,0 +1,28 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +public enum EditGroupStatus { + [XmlEnum("1")] + Success = 1, + [XmlEnum("2")] + Error, + [XmlEnum("3")] + UnableToGetUserDetails, + [XmlEnum("4")] + GroupNameInvalid, + [XmlEnum("5")] + GroupDescriptionInvalid, + [XmlEnum("6")] + GroupNameIsDuplicate, + [XmlEnum("7")] + GroupTypeIsInvalid, + [XmlEnum("8")] + PermissionDenied, + [XmlEnum("9")] + GroupNotFound, + [XmlEnum("10")] + GroupMaxMemberLimitInvalid, + [XmlEnum("11")] + GroupOwnerHasInSufficientCurrency +} diff --git a/src/Schema/GetGroupsRequest.cs b/src/Schema/GetGroupsRequest.cs new file mode 100644 index 0000000..d196ce7 --- /dev/null +++ b/src/Schema/GetGroupsRequest.cs @@ -0,0 +1,40 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "GetGroupsRequest", IsNullable = true)] +[Serializable] +public class GetGroupsRequest { + [XmlElement(ElementName = "UserID")] + public string UserID; + + [XmlElement(ElementName = "ForUserID")] + public string ForUserID; + + [XmlElement(ElementName = "GroupID")] + public Guid? GroupID; + + [XmlElement(ElementName = "Name")] + public string Name; + + [XmlElement(ElementName = "ProductID")] + public int? ProductID; + + [XmlElement(ElementName = "ProductGroupID")] + public int? ProductGroupID; + + [XmlElement(ElementName = "IncludeMemberCount")] + public bool IncludeMemberCount; + + [XmlElement(ElementName = "IncludeMinFields")] + public bool IncludeMinFields; + + [XmlElement(ElementName = "PageNo")] + public int? PageNo; + + [XmlElement(ElementName = "PageSize")] + public int? PageSize; + + [XmlElement(ElementName = "GroupsFilter")] + public GroupsFilter GroupsFilter; +} diff --git a/src/Schema/GetGroupsResult.cs b/src/Schema/GetGroupsResult.cs new file mode 100644 index 0000000..1d2dbd7 --- /dev/null +++ b/src/Schema/GetGroupsResult.cs @@ -0,0 +1,16 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "GetGroupsResult", IsNullable = true)] +[Serializable] +public class GetGroupsResult { + [XmlElement(ElementName = "Success")] + public bool Success; + + [XmlElement(ElementName = "Groups")] + public Group[] Groups; + + [XmlElement(ElementName = "RolePermissions", IsNullable = true)] + public List RolePermissions; +} diff --git a/src/Schema/GetPendingJoinRequest.cs b/src/Schema/GetPendingJoinRequest.cs new file mode 100644 index 0000000..f7198f3 --- /dev/null +++ b/src/Schema/GetPendingJoinRequest.cs @@ -0,0 +1,16 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "GetPendingJoinRequest", IsNullable = true)] +[Serializable] +public class GetPendingJoinRequest { + [XmlElement(ElementName = "UserID")] + public string UserID; + + [XmlElement(ElementName = "GroupID")] + public string GroupID; + + [XmlElement(ElementName = "ProductGroupID")] + public int? ProductGroupID; +} diff --git a/src/Schema/GetPendingJoinResult.cs b/src/Schema/GetPendingJoinResult.cs new file mode 100644 index 0000000..42420bf --- /dev/null +++ b/src/Schema/GetPendingJoinResult.cs @@ -0,0 +1,14 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "GetPendingJoinResult", IsNullable = true)] +[Serializable] +public class GetPendingJoinResult { + // Token: 0x04000A48 RID: 2632 + [XmlElement(ElementName = "Success")] + public bool Success; + + [XmlElement(ElementName = "Requests")] + public PendingJoinRequest[] Requests; +} diff --git a/src/Schema/GroupJoinRequestStatus.cs b/src/Schema/GroupJoinRequestStatus.cs new file mode 100644 index 0000000..83d079b --- /dev/null +++ b/src/Schema/GroupJoinRequestStatus.cs @@ -0,0 +1,16 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +public enum GroupJoinRequestStatus { + [XmlEnum("1")] + Pending = 1, + [XmlEnum("2")] + Approved, + [XmlEnum("3")] + Rejected, + [XmlEnum("4")] + Cancelled, + [XmlEnum("5")] + PendingAccountOwnerRequest +} \ No newline at end of file diff --git a/src/Schema/GroupJoinResult.cs b/src/Schema/GroupJoinResult.cs new file mode 100644 index 0000000..c7ca46a --- /dev/null +++ b/src/Schema/GroupJoinResult.cs @@ -0,0 +1,15 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "GroupJoinResult", IsNullable = true)] +[Serializable] +public class GroupJoinResult { + // Token: 0x040009C3 RID: 2499 + [XmlElement(ElementName = "Success")] + public bool Success; + + // Token: 0x040009C4 RID: 2500 + [XmlElement(ElementName = "Status")] + public JoinGroupStatus Status; +} \ No newline at end of file diff --git a/src/Schema/GroupMember.cs b/src/Schema/GroupMember.cs new file mode 100644 index 0000000..b49a906 --- /dev/null +++ b/src/Schema/GroupMember.cs @@ -0,0 +1,35 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "GroupMember", IsNullable = true)] +[Serializable] +public class GroupMember { + [XmlElement(ElementName = "UserID", IsNullable = false)] + public string UserID; + + [XmlElement(ElementName = "GroupID", IsNullable = false)] + public string GroupID; + + [XmlElement(ElementName = "DisplayName", IsNullable = false)] + public string DisplayName; + + [XmlElement(ElementName = "JoinDate")] + public DateTime JoinDate; + + [XmlElement(ElementName = "Online")] + public bool Online; + + [XmlElement(ElementName = "RoleID", IsNullable = true)] + public int? RoleID; + + [XmlElement(ElementName = "Points", IsNullable = true)] + public int? Points; + + [XmlElement(ElementName = "Rank", IsNullable = true)] + public int? Rank; + + [XmlElement(ElementName = "RankTrend", IsNullable = true)] + public int? RankTrend; +} + diff --git a/src/Schema/GroupsFilter.cs b/src/Schema/GroupsFilter.cs new file mode 100644 index 0000000..0623ba2 --- /dev/null +++ b/src/Schema/GroupsFilter.cs @@ -0,0 +1,16 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "GroupsFilter", IsNullable = true)] +[Serializable] +public class GroupsFilter { + [XmlElement(ElementName = "GroupType")] + public GroupType? GroupType; + + [XmlElement(ElementName = "Locale")] + public string Locale; + + [XmlElement(ElementName = "PointTypeID")] + public int? PointTypeID; +} diff --git a/src/Schema/JoinGroupRequest.cs b/src/Schema/JoinGroupRequest.cs new file mode 100644 index 0000000..891befc --- /dev/null +++ b/src/Schema/JoinGroupRequest.cs @@ -0,0 +1,31 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "JoinGroupRequest", IsNullable = true)] +[Serializable] +public class JoinGroupRequest { + [XmlElement(ElementName = "UserID")] + public string UserID; + + [XmlElement(ElementName = "GroupID")] + public string GroupID; + + [XmlElement(ElementName = "ProductGroupID")] + public int? ProductGroupID; + + [XmlElement(ElementName = "ProductID")] + public int? ProductID; + + [XmlElement(ElementName = "Message")] + public string Message; + + [XmlElement(ElementName = "JoinRequestStatus")] + public GroupJoinRequestStatus? JoinRequestStatus; + + [XmlElement(ElementName = "JoinByInvite")] + public bool JoinByInvite; + + [XmlElement(ElementName = "InviterID")] + public string InviterID; +} diff --git a/src/Schema/JoinGroupStatus.cs b/src/Schema/JoinGroupStatus.cs new file mode 100644 index 0000000..699ad33 --- /dev/null +++ b/src/Schema/JoinGroupStatus.cs @@ -0,0 +1,28 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +public enum JoinGroupStatus { + [XmlEnum("1")] + Success = 1, + [XmlEnum("2")] + Error, + [XmlEnum("3")] + GroupIsFull, + [XmlEnum("4")] + GroupTypeIsNotPublic, + [XmlEnum("5")] + UserAlreadyMemberOfTheGroup, + [XmlEnum("6")] + GroupTypeIsPublic, + [XmlEnum("7")] + MessageInvalid, + [XmlEnum("8")] + JoinRequestPending, + [XmlEnum("9")] + InviteNeededToJoin, + [XmlEnum("10")] + NoValidInvite, + [XmlEnum("11")] + InviterHasNoPermission +} diff --git a/src/Schema/LeaveGroupRequest.cs b/src/Schema/LeaveGroupRequest.cs new file mode 100644 index 0000000..2eb775f --- /dev/null +++ b/src/Schema/LeaveGroupRequest.cs @@ -0,0 +1,19 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "LeaveGroupRequest", IsNullable = true)] +[Serializable] +public class LeaveGroupRequest { + [XmlElement(ElementName = "UserID")] + public string UserID; + + [XmlElement(ElementName = "GroupID")] + public string GroupID; + + [XmlElement(ElementName = "ProductGroupID")] + public int? ProductGroupID; + + [XmlElement(ElementName = "ProductID")] + public int? ProductID; +} diff --git a/src/Schema/LeaveGroupResult.cs b/src/Schema/LeaveGroupResult.cs new file mode 100644 index 0000000..c14624b --- /dev/null +++ b/src/Schema/LeaveGroupResult.cs @@ -0,0 +1,13 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "LeaveGroupResult", IsNullable = true)] +[Serializable] +public class LeaveGroupResult { + [XmlElement(ElementName = "Success")] + public bool Success; + + [XmlElement(ElementName = "Status")] + public LeaveGroupStatus Status; +} diff --git a/src/Schema/LeaveGroupStatus.cs b/src/Schema/LeaveGroupStatus.cs new file mode 100644 index 0000000..209b04a --- /dev/null +++ b/src/Schema/LeaveGroupStatus.cs @@ -0,0 +1,12 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +public enum LeaveGroupStatus { + [XmlEnum("1")] + Success = 1, + [XmlEnum("2")] + Error, + [XmlEnum("3")] + UserNotAMemberOfTheGroup +} diff --git a/src/Schema/PendingJoinRequest.cs b/src/Schema/PendingJoinRequest.cs new file mode 100644 index 0000000..0af54c2 --- /dev/null +++ b/src/Schema/PendingJoinRequest.cs @@ -0,0 +1,22 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "PendingJoinRequest", IsNullable = true)] +[Serializable] +public class PendingJoinRequest { + [XmlElement(ElementName = "UserID")] + public string UserID; + + [XmlElement(ElementName = "GroupID")] + public string GroupID; + + [XmlElement(ElementName = "FromUserID")] + public string FromUserID; + + [XmlElement(ElementName = "Message")] + public string Message; + + [XmlElement(ElementName = "StatusID", IsNullable = true)] + public GroupJoinRequestStatus? StatusID; +} diff --git a/src/Schema/RemoveMemberRequest.cs b/src/Schema/RemoveMemberRequest.cs new file mode 100644 index 0000000..35397f6 --- /dev/null +++ b/src/Schema/RemoveMemberRequest.cs @@ -0,0 +1,22 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "RemoveMemberRequest", IsNullable = true)] +[Serializable] +public class RemoveMemberRequest { + [XmlElement(ElementName = "RemoveUserID")] + public string RemoveUserID; + + [XmlElement(ElementName = "UserID")] + public string UserID; + + [XmlElement(ElementName = "GroupID")] + public string GroupID; + + [XmlElement(ElementName = "ProductGroupID")] + public int? ProductGroupID; + + [XmlElement(ElementName = "ProductID")] + public int? ProductID; +} diff --git a/src/Schema/RemoveMemberResult.cs b/src/Schema/RemoveMemberResult.cs new file mode 100644 index 0000000..a014e22 --- /dev/null +++ b/src/Schema/RemoveMemberResult.cs @@ -0,0 +1,13 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "RemoveMemberResult", IsNullable = true)] +[Serializable] +public class RemoveMemberResult { + [XmlElement(ElementName = "Success")] + public bool Success; + + [XmlElement(ElementName = "Status")] + public RemoveMemberStatus Status; +} diff --git a/src/Schema/RemoveMemberStatus.cs b/src/Schema/RemoveMemberStatus.cs new file mode 100644 index 0000000..90f3338 --- /dev/null +++ b/src/Schema/RemoveMemberStatus.cs @@ -0,0 +1,16 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +public enum RemoveMemberStatus { + [XmlEnum("1")] + Success = 1, + [XmlEnum("2")] + Error, + [XmlEnum("3")] + UserNotAMemberOfTheGroup, + [XmlEnum("4")] + UserHasNoPermission, + [XmlEnum("5")] + InvalidParameters +} diff --git a/src/Schema/RolePermission.cs b/src/Schema/RolePermission.cs new file mode 100644 index 0000000..93c65ba --- /dev/null +++ b/src/Schema/RolePermission.cs @@ -0,0 +1,16 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +[XmlRoot(ElementName = "RP", IsNullable = true)] +[Serializable] +public class RolePermission { + [XmlElement(ElementName = "G")] + public GroupType GroupType; + + [XmlElement(ElementName = "R")] + public UserRole Role; + + [XmlElement(ElementName = "P")] + public List Permissions; +} diff --git a/src/Schema/UserProfileGroupData.cs b/src/Schema/UserProfileGroupData.cs index 207edf4..acba960 100644 --- a/src/Schema/UserProfileGroupData.cs +++ b/src/Schema/UserProfileGroupData.cs @@ -9,7 +9,7 @@ public class UserProfileGroupData public string GroupID; [XmlElement(ElementName = "RoleID", IsNullable = true)] - public int? RoleID; + public UserRole? RoleID; [XmlElement(ElementName = "TypeID", IsNullable = true)] public int? TypeID; diff --git a/src/Schema/UserRole.cs b/src/Schema/UserRole.cs new file mode 100644 index 0000000..9680b50 --- /dev/null +++ b/src/Schema/UserRole.cs @@ -0,0 +1,12 @@ +using System.Xml.Serialization; + +namespace sodoff.Schema; + +public enum UserRole { + [XmlEnum("1")] + Member = 1, + [XmlEnum("2")] + Elder, + [XmlEnum("3")] + Leader +} From e45f3769bcb637c351707943215a241577f37090 Mon Sep 17 00:00:00 2001 From: Hipposgrumm Date: Sat, 18 Oct 2025 21:28:21 -0600 Subject: [PATCH 2/8] Un Merge Conflict --- src/Model/Viking.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Model/Viking.cs b/src/Model/Viking.cs index d67f0df..d02f47e 100644 --- a/src/Model/Viking.cs +++ b/src/Model/Viking.cs @@ -1,5 +1,6 @@ using Microsoft.EntityFrameworkCore; using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using sodoff.Schema; namespace sodoff.Model; @@ -39,6 +40,7 @@ public class Viking { public virtual ICollection Parties { get; set; } = null!; public virtual ICollection MMORoles { get; set; } = null!; public virtual Neighborhood? Neighborhood { get; set; } = null!; + [JsonIgnore] public virtual ICollection GroupRoles { get; set; } = null!; public virtual ICollection Ratings { get; set; } = null!; public virtual Dragon? SelectedDragon { get; set; } From 3e232f93777a9a8be676a8ef4a48f5fb740a63d3 Mon Sep 17 00:00:00 2001 From: Hipposgrumm Date: Sat, 18 Oct 2025 21:30:24 -0600 Subject: [PATCH 3/8] Roundabout way of solving a merge conflict --- src/Model/Viking.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Model/Viking.cs b/src/Model/Viking.cs index d02f47e..1bcc467 100644 --- a/src/Model/Viking.cs +++ b/src/Model/Viking.cs @@ -42,6 +42,7 @@ public class Viking { public virtual Neighborhood? Neighborhood { get; set; } = null!; [JsonIgnore] public virtual ICollection GroupRoles { get; set; } = null!; + public virtual ICollection Groups { get; set; } = null!; public virtual ICollection Ratings { get; set; } = null!; public virtual Dragon? SelectedDragon { get; set; } public virtual ICollection UserMissions { get; set; } = null!; From 890b62c10adde14a8977dce6976d3ba243751c79 Mon Sep 17 00:00:00 2001 From: Hipposgrumm Date: Sat, 18 Oct 2025 22:19:58 -0600 Subject: [PATCH 4/8] Made changes to the repository. Pushing them to see if I did a good job. --- src/Controllers/Common/GroupController.cs | 130 +++++++++++--------- src/Controllers/Common/ProfileController.cs | 17 +-- src/Model/DBContext.cs | 8 +- src/Model/Group.cs | 2 +- src/Model/GroupMember.cs | 16 +++ src/Model/GroupViking.cs | 35 ------ src/Model/Viking.cs | 3 +- 7 files changed, 102 insertions(+), 109 deletions(-) create mode 100644 src/Model/GroupMember.cs delete mode 100644 src/Model/GroupViking.cs diff --git a/src/Controllers/Common/GroupController.cs b/src/Controllers/Common/GroupController.cs index ddd6ceb..c62568a 100644 --- a/src/Controllers/Common/GroupController.cs +++ b/src/Controllers/Common/GroupController.cs @@ -1,8 +1,10 @@ using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; using sodoff.Attributes; using sodoff.Model; using sodoff.Schema; using sodoff.Util; +using GroupMember = sodoff.Model.GroupMember; namespace sodoff.Controllers.Common; @@ -72,37 +74,10 @@ public class GroupController : Controller { private readonly DBContext ctx; + private bool AddedEMD = false; + public GroupController(DBContext ctx) { this.ctx = ctx; - - 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, - GameID = ClientVersion.EMD, - MaxMemberLimit = int.MaxValue - }); - 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, - GameID = ClientVersion.EMD, - MaxMemberLimit = int.MaxValue - }); - changed = true; - } - if (changed) ctx.SaveChanges(); } [HttpPost] @@ -112,7 +87,7 @@ public class GroupController : Controller { public IActionResult CreateGroup(Viking viking, [FromForm] string apiKey, [FromForm] string groupCreateRequest) { uint gameId = ClientVersion.GetGameID(apiKey); - if (viking.GroupRoles.Any(g => g.GameID == gameId)) { + if (viking.GroupRoles.Any(g => g.Group.GameID == gameId)) { return Ok(new CreateGroupResult { Success = false, Status = CreateGroupStatus.CreatorIsNotApproved }); } @@ -124,7 +99,7 @@ public class GroupController : Controller { //if (request.MaxMemberLimit < 4) return Ok(new CreateGroupResult { Success = false, Status = CreateGroupStatus.GroupMaxMemberLimitInvalid }); // Not actually used by the game. if (request.Name.Length == 0) return Ok(new CreateGroupResult { Success = false, Status = CreateGroupStatus.GroupNameIsEmpty }); if (request.Description.Length == 0) return Ok(new CreateGroupResult { Success = false, Status = CreateGroupStatus.GroupDescriptionIsEmpty }); - if (viking.GroupRoles.Any(g => g.Name.Equals(request.Name, StringComparison.InvariantCultureIgnoreCase))) return Ok(new CreateGroupResult { Success = false, Status = CreateGroupStatus.GroupNameIsDuplicate }); + if (viking.GroupRoles.Any(g => g.Group.Name.Equals(request.Name, StringComparison.InvariantCultureIgnoreCase))) return Ok(new CreateGroupResult { Success = false, Status = CreateGroupStatus.GroupNameIsDuplicate }); Model.Group group = new Model.Group { Name = request.Name, @@ -136,11 +111,11 @@ public class GroupController : Controller { GameID = gameId, MaxMemberLimit = 50, GroupID = Guid.NewGuid(), - Vikings = new List() + Vikings = new List() }; ctx.Groups.Add(group); - group.Vikings.Add(new GroupViking { + group.Vikings.Add(new GroupMember { Viking = viking, Group = group, UserRole = UserRole.Leader, @@ -176,7 +151,7 @@ public class GroupController : Controller { EditGroupRequest request = XmlUtil.DeserializeXml(groupEditRequest); request.Name = request.Name.Trim(); - GroupViking? vikingRole = viking.GroupRoles.FirstOrDefault(gv => gv.Group.GroupID.ToString() == request.GroupID); + GroupMember? vikingRole = viking.GroupRoles.FirstOrDefault(gv => gv.Group.GroupID.ToString() == request.GroupID); if (vikingRole == null) { return Ok(new EditGroupResult { Success = false, Status = EditGroupStatus.GroupNotFound }); } else if (vikingRole.UserRole < UserRole.Elder) { @@ -186,9 +161,9 @@ public class GroupController : Controller { // Cue the gauntlet of validity checks. if (request.Type != null && request.Type <= GroupType.System) return Ok(new EditGroupResult { Success = false, Status = EditGroupStatus.GroupTypeIsInvalid }); //if (request.MaxMemberLimit < 4) return Ok(new EditGroupResult { Success = false, Status = EditGroupStatus.GroupMaxMemberLimitInvalid }); // Not actually used by the game. - if ((request.Name?.Length ?? 0) == 0) request.Name = vikingRole.Name; - if ((request.Description?.Length ?? 0) == 0) request.Description = vikingRole.Description; - if (request.Name != vikingRole.Name && viking.GroupRoles.Any(g => g.Name.Equals(request.Name, StringComparison.InvariantCultureIgnoreCase))) return Ok(new EditGroupResult { Success = false, Status = EditGroupStatus.GroupNameIsDuplicate }); + if ((request.Name?.Length ?? 0) == 0) request.Name = vikingRole.Group.Name; + if ((request.Description?.Length ?? 0) == 0) request.Description = vikingRole.Group.Description; + if (request.Name != vikingRole.Group.Name && viking.GroupRoles.Any(gm => gm.Group.Name.Equals(request.Name, StringComparison.InvariantCultureIgnoreCase))) return Ok(new EditGroupResult { Success = false, Status = EditGroupStatus.GroupNameIsDuplicate }); vikingRole.Group.Name = request.Name; vikingRole.Group.Description = request.Description; @@ -212,14 +187,14 @@ public class GroupController : Controller { uint gameId = ClientVersion.GetGameID(apiKey); // Check for loyalty. - if (viking.GroupRoles.Any(g => g.GameID == gameId)) { + if (viking.GroupRoles.Any(g => g.Group.GameID == gameId)) { return Ok(new JoinGroupResult { GroupStatus = GroupMembershipStatus.SELF_BLOCKED }); } Model.Group? group = ctx.Groups.FirstOrDefault(g => g.GroupID.ToString().Equals(groupID, StringComparison.OrdinalIgnoreCase)); if (group != null) { // This check is only on this side to prevent people from attempting to circumvent the join limit. if (group.Type <= GroupType.System || group.Vikings.Count < group.MaxMemberLimit) { - group.Vikings.Add(new GroupViking { + group.Vikings.Add(new GroupMember { Viking = viking, Group = group, UserRole = UserRole.Member @@ -244,7 +219,7 @@ public class GroupController : Controller { if (group.Type >= GroupType.Private) { return Ok(new GroupJoinResult { Success = false, Status = JoinGroupStatus.GroupTypeIsNotPublic }); } - GroupViking? existing = viking.GroupRoles.FirstOrDefault(g => g.GameID == gameId); + GroupMember? existing = viking.GroupRoles.FirstOrDefault(g => g.Group.GameID == gameId); if (existing != null) { if (existing.Group == group) return Ok(new GroupJoinResult { Success = false, Status = JoinGroupStatus.UserAlreadyMemberOfTheGroup }); @@ -263,7 +238,7 @@ public class GroupController : Controller { } if (group.Vikings.Count < group.MaxMemberLimit) { - GroupViking joinee = new GroupViking { + GroupMember joinee = new GroupMember { Viking = viking, Group = group, UserRole = UserRole.Member, @@ -286,9 +261,9 @@ public class GroupController : Controller { [VikingSession] public IActionResult LeaveGroup(Viking viking, [FromForm] string groupLeaveRequest) { LeaveGroupRequest request = XmlUtil.DeserializeXml(groupLeaveRequest); - GroupViking? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); + GroupMember? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); if (vikingRole != null) { - GroupViking? targetRole = null; + GroupMember? targetRole = null; if (viking.Uid.ToString().Equals(request.UserID, StringComparison.CurrentCultureIgnoreCase)) { targetRole = vikingRole.Group.Vikings.FirstOrDefault(gv => gv.Viking == viking); } else if (vikingRole.UserRole >= UserRole.Elder) { @@ -360,12 +335,12 @@ public class GroupController : Controller { [VikingSession] public IActionResult RemoveMember(Viking viking, [FromForm] string removeMemberRequest) { RemoveMemberRequest request = XmlUtil.DeserializeXml(removeMemberRequest); - GroupViking? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); + GroupMember? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); if (vikingRole != null) { if (vikingRole.UserRole < UserRole.Elder) { return Ok(new RemoveMemberResult { Success = false, Status = RemoveMemberStatus.UserHasNoPermission }); } - GroupViking? targetRole = vikingRole.Group.Vikings.FirstOrDefault(gv => gv.Viking.Uid.ToString() == request.RemoveUserID); + GroupMember? targetRole = vikingRole.Group.Vikings.FirstOrDefault(gv => gv.Viking.Uid.ToString() == request.RemoveUserID); if (targetRole != null) { vikingRole.Group.Vikings.Remove(targetRole); if (!vikingRole.Group.Vikings.Any()) ctx.Groups.Remove(vikingRole.Group); @@ -385,14 +360,14 @@ public class GroupController : Controller { uint gameId = ClientVersion.GetGameID(apiKey); AuthorizeJoinRequest request = XmlUtil.DeserializeXml(authorizeJoinRequest); - GroupViking? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); + GroupMember? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); if (vikingRole != null) { if (vikingRole.UserRole < UserRole.Elder) { return Ok(new AuthorizeJoinResult { Success = false, Status = AuthorizeJoinStatus.ApproverHasNoPermission }); } Viking? target = ctx.Vikings.FirstOrDefault(v => v.Uid.ToString() == request.UserID); if (target != null) { - GroupViking? existing = target.GroupRoles.FirstOrDefault(g => g.GameID == gameId); + GroupMember? existing = target.GroupRoles.FirstOrDefault(gm => gm.Group.GameID == gameId); if (existing != null) { if (existing.Group == vikingRole.Group) { return Ok(new AuthorizeJoinResult { Success = false, Status = AuthorizeJoinStatus.UserAlreadyMemberOfTheGroup }); @@ -402,7 +377,7 @@ public class GroupController : Controller { } if (vikingRole.Group.Vikings.Count < vikingRole.Group.MaxMemberLimit) { if (request.Approved) { - GroupViking joinee = new GroupViking { + GroupMember joinee = new GroupMember { Viking = target, Group = vikingRole.Group, UserRole = UserRole.Member, @@ -431,12 +406,12 @@ public class GroupController : Controller { [VikingSession] public IActionResult AssignRole(Viking viking, [FromForm] string assignRoleRequest) { AssignRoleRequest request = XmlUtil.DeserializeXml(assignRoleRequest); - GroupViking? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); + GroupMember? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); if (vikingRole != null) { if (vikingRole.UserRole < UserRole.Elder) { return Ok(new AssignRoleResult { Success = false, Status = AssignRoleStatus.ApproverHasNoPermission }); } - GroupViking? targetRole = vikingRole.Group.Vikings.FirstOrDefault(gv => gv.Viking.Uid.ToString() == request.MemberID); + GroupMember? targetRole = vikingRole.Group.Vikings.FirstOrDefault(gv => gv.Viking.Uid.ToString() == request.MemberID); if (targetRole != null) { if (targetRole.UserRole == request.NewRole) return Ok(new AssignRoleResult { Success = false, Status = AssignRoleStatus.MemberAlreadyInTheRole }); @@ -463,7 +438,7 @@ public class GroupController : Controller { [VikingSession] public IActionResult GetPendingJoinRequests(Viking viking, [FromForm] string getPendingJoinRequest) { GetPendingJoinRequest request = XmlUtil.DeserializeXml(getPendingJoinRequest); - GroupViking? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); + GroupMember? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); if (vikingRole?.UserRole >= UserRole.Elder) { return Ok(new GetPendingJoinResult { Success = true, @@ -492,12 +467,12 @@ public class GroupController : Controller { Viking? viking = ctx.Vikings.FirstOrDefault(v => v.Uid.ToString() == userId); if (viking == null) return []; - return viking.GroupRoles.Where(gv => gv.GameID == gameId).Select(gv => new Schema.Group { - GroupID = gv.Group.GroupID.ToString(), - Name = gv.Name, - Color = gv.Color, - Logo = gv.Logo, - MemberLimit = gv.Group.MaxMemberLimit + return viking.GroupRoles.Where(gm => gm.Group.GameID == gameId).Select(gm => new Schema.Group { + GroupID = gm.Group.GroupID.ToString(), + Name = gm.Group.Name, + Color = gm.Group.Color, + Logo = gm.Group.Logo, + MemberLimit = gm.Group.MaxMemberLimit }).ToArray(); } @@ -506,6 +481,11 @@ public class GroupController : Controller { [Route("GroupWebService.asmx/GetGroupsByGroupType")] public Schema.Group[] GetGroupsByGroupType([FromForm] string apiKey, [FromForm] string groupType) { uint gameId = ClientVersion.GetGameID(apiKey); + + if (!AddedEMD) { + AddEMDGroups(); + AddedEMD = true; + } List groups = new List(); foreach (Model.Group group in ctx.Groups) { @@ -524,14 +504,14 @@ public class GroupController : Controller { [HttpPost] [Produces("application/xml")] [Route("GroupWebService.asmx/GetMembersByGroupID")] - public GroupMember[] GetMembersByGroupID([FromForm] string groupID) { + public Schema.GroupMember[] GetMembersByGroupID([FromForm] string groupID) { groupID = groupID.ToUpper(); Model.Group? group = ctx.Groups.FirstOrDefault( g => g.GroupID.ToString() == groupID ); if (group == null) return []; - return group.Vikings.Select(v => new GroupMember{ + return group.Vikings.Select(v => new Schema.GroupMember{ DisplayName = v.Viking.AvatarSerialized != null ? XmlUtil.DeserializeXml(v.Viking.AvatarSerialized).DisplayName : v.Viking.Name, @@ -544,4 +524,36 @@ public class GroupController : Controller { Points = group.Points }).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, + GameID = ClientVersion.EMD, + MaxMemberLimit = int.MaxValue + }); + 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, + GameID = ClientVersion.EMD, + MaxMemberLimit = int.MaxValue + }); + changed = true; + } + if (changed) ctx.SaveChanges(); + + } } diff --git a/src/Controllers/Common/ProfileController.cs b/src/Controllers/Common/ProfileController.cs index 68da31a..d965250 100644 --- a/src/Controllers/Common/ProfileController.cs +++ b/src/Controllers/Common/ProfileController.cs @@ -7,6 +7,7 @@ using sodoff.Schema; using sodoff.Services; using sodoff.Util; using sodoff.Configuration; +using GroupMember = sodoff.Model.GroupMember; namespace sodoff.Controllers.Common; public class ProfileController : Controller { @@ -152,18 +153,18 @@ public class ProfileController : Controller { UserGameCurrency currency = achievementService.GetUserCurrency(viking); - ICollection groups = viking.GroupRoles; + ICollection groups = viking.GroupRoles; UserProfileGroupData[] groupData = new UserProfileGroupData[groups.Count]; int i = 0; - foreach (GroupViking group in groups) { + foreach (GroupMember role in groups) { groupData[i] = new UserProfileGroupData { - GroupID = group.Group.GroupID.ToString(), - Name = group.Name, - Color = group.Color, - Logo = group.Logo, - TypeID = (int)group.Type, - RoleID = group.UserRole + GroupID = role.Group.GroupID.ToString(), + Name = role.Group.Name, + Color = role.Group.Color, + Logo = role.Group.Logo, + TypeID = (int)role.Group.Type, + RoleID = role.UserRole }; i++; } diff --git a/src/Model/DBContext.cs b/src/Model/DBContext.cs index 1667feb..2abe239 100644 --- a/src/Model/DBContext.cs +++ b/src/Model/DBContext.cs @@ -30,7 +30,7 @@ public class DBContext : DbContext { public DbSet Neighborhoods { get; set; } = null!; // we had a brief debate on whether it's neighborhoods or neighborheed public DbSet Groups { get; set; } = null!; - public DbSet GroupViking { get; set; } = null!; + public DbSet GroupMembers { get; set; } = null!; public DbSet GroupJoinRequests { get; set; } = null!; public DbSet Ratings { get; set; } = null!; public DbSet RatingRanks { get; set; } = null!; @@ -72,7 +72,7 @@ public class DBContext : DbContext { } protected override void OnModelCreating(ModelBuilder builder) { - builder.Entity().HasKey(["VikingID", "GroupID"]); + builder.Entity().HasKey(["VikingID", "GroupID"]); builder.Entity().HasKey(["VikingID", "GroupID"]); // Sessions @@ -289,9 +289,9 @@ public class DBContext : DbContext { // Groups builder.Entity().HasMany(r => r.Vikings) .WithOne(v => v.Group); - builder.Entity().HasOne(r => r.Group) + builder.Entity().HasOne(r => r.Group) .WithMany(g => g.Vikings); - builder.Entity().HasOne(r => r.Viking) + builder.Entity().HasOne(r => r.Viking) .WithMany(g => g.GroupRoles); builder.Entity().HasMany(r => r.JoinRequests) .WithOne(r => r.Group); diff --git a/src/Model/Group.cs b/src/Model/Group.cs index b83b14f..0a469d1 100644 --- a/src/Model/Group.cs +++ b/src/Model/Group.cs @@ -29,7 +29,7 @@ public class Group { public int MaxMemberLimit { get; set; } - public virtual ICollection Vikings { get; set; } = null!; + public virtual ICollection Vikings { get; set; } = null!; public virtual ICollection JoinRequests { get; set; } = null!; } diff --git a/src/Model/GroupMember.cs b/src/Model/GroupMember.cs new file mode 100644 index 0000000..59d28ca --- /dev/null +++ b/src/Model/GroupMember.cs @@ -0,0 +1,16 @@ +using sodoff.Schema; +using System.ComponentModel.DataAnnotations; + +namespace sodoff.Model; + +public class GroupMember { + [Key] + public int VikingID { get; set; } + [Key] + public int GroupID { get; set; } + public UserRole UserRole { get; set; } + public DateTime JoinDate { get; set; } + + public virtual Viking Viking { get; set; } = null!; + public virtual Group Group { get; set; } = null!; +} diff --git a/src/Model/GroupViking.cs b/src/Model/GroupViking.cs deleted file mode 100644 index 51529f9..0000000 --- a/src/Model/GroupViking.cs +++ /dev/null @@ -1,35 +0,0 @@ -using sodoff.Schema; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - -namespace sodoff.Model; - -public class GroupViking { - [Key] - public int VikingID { get; set; } - [Key] - public int GroupID { get; set; } - public UserRole UserRole { get; set; } - public DateTime JoinDate { get; set; } - - public virtual Viking Viking { get; set; } = null!; - public virtual Group Group { get; set; } = null!; - - [NotMapped] - public string Name { get { return Group.Name; } } - - [NotMapped] - public string? Description { get { return Group.Description; } } - - [NotMapped] - public GroupType Type { get { return Group.Type; } } - - [NotMapped] - public string Logo { get { return Group.Logo; } } - - [NotMapped] - public string Color { get { return Group.Color; } } - - [NotMapped] - public uint GameID { get { return Group.GameID; } } -} diff --git a/src/Model/Viking.cs b/src/Model/Viking.cs index d791619..3ac5579 100644 --- a/src/Model/Viking.cs +++ b/src/Model/Viking.cs @@ -47,8 +47,7 @@ public class Viking { public virtual ICollection MMORoles { get; set; } = null!; public virtual Neighborhood? Neighborhood { get; set; } = null!; [JsonIgnore] - public virtual ICollection GroupRoles { get; set; } = null!; - public virtual ICollection Groups { get; set; } = null!; + public virtual ICollection GroupRoles { get; set; } = null!; public virtual ICollection Ratings { get; set; } = null!; public virtual Dragon? SelectedDragon { get; set; } public virtual ICollection UserMissions { get; set; } = null!; From 67732b63e9af853a3094db3637adc4c6cd18660a Mon Sep 17 00:00:00 2001 From: Hipposgrumm Date: Sun, 19 Oct 2025 11:26:12 -0600 Subject: [PATCH 5/8] Un-nested some ifs and made the initializing of the RolePermissions data hopefully more readable. --- src/Controllers/Common/GroupController.cs | 372 ++++++++++++---------- 1 file changed, 201 insertions(+), 171 deletions(-) diff --git a/src/Controllers/Common/GroupController.cs b/src/Controllers/Common/GroupController.cs index c62568a..8e40d78 100644 --- a/src/Controllers/Common/GroupController.cs +++ b/src/Controllers/Common/GroupController.cs @@ -9,54 +9,79 @@ using GroupMember = sodoff.Model.GroupMember; namespace sodoff.Controllers.Common; public class GroupController : Controller { - private static readonly List RolePermissions; - - static GroupController() { - RolePermissions = new List(); - for (GroupType type = GroupType.Public; type <= GroupType.Private; type++) { - // Anything commented out is not implemented. - RolePermissions.Add(new RolePermission { - GroupType = type, - Role = UserRole.Member, - Permissions = [ - "Delete Own Msg", - //"Post Message" - ] - }); - RolePermissions.Add(new RolePermission { - GroupType = type, - Role = UserRole.Elder, - Permissions = [ - //"Invite", - "Approve Join Request", - //"Post News", - //"Delete Own Msg", - //"Delete Any Msg", - //"Delete News", - //"Post Message", - "Remove Member" - ] - }); - RolePermissions.Add(new RolePermission { - GroupType = type, - Role = UserRole.Leader, - Permissions = [ - //"Invite", - "Approve Join Request", - "Assign Leader", - "Assign Elder", - "Demote Elder", - "Edit Group", - //"Post News", - //"Delete Own Msg", - //"Delete Any Msg", - //"Delete News", - //"Post Message", - "Remove Member" - ] - }); + // Any permission that is commented out is not implemented. + private static readonly List PermissionsMember = [ + //"Delete Own Msg", + //"Post Message" + ]; + private static readonly List PermissionsElder = [ + //"Invite", + "Approve Join Request", + //"Post News", + //"Delete Own Msg", + //"Delete Any Msg", + //"Delete News", + //"Post Message", + "Remove Member" + ]; + private static readonly List PermissionsLeader = [ + //"Invite", + "Approve Join Request", + "Assign Leader", + "Assign Elder", + "Demote Elder", + "Edit Group", + //"Post News", + //"Delete Own Msg", + //"Delete Any Msg", + //"Delete News", + //"Post Message", + "Remove Member" + ]; + + private static readonly List RolePermissions = [ + new RolePermission { + GroupType = GroupType.Public, + Role = UserRole.Member, + Permissions = PermissionsMember + }, new RolePermission { + GroupType = GroupType.Public, + Role = UserRole.Elder, + Permissions = PermissionsElder + }, new RolePermission { + GroupType = GroupType.Public, + Role = UserRole.Leader, + Permissions = PermissionsLeader + }, + + new RolePermission { + GroupType = GroupType.MembersOnly, + Role = UserRole.Member, + Permissions = PermissionsMember + }, new RolePermission { + GroupType = GroupType.MembersOnly, + Role = UserRole.Elder, + Permissions = PermissionsElder + }, new RolePermission { + GroupType = GroupType.MembersOnly, + Role = UserRole.Leader, + Permissions = PermissionsLeader + }, + + new RolePermission { + GroupType = GroupType.Private, + Role = UserRole.Member, + Permissions = PermissionsMember + }, new RolePermission { + GroupType = GroupType.Private, + Role = UserRole.Elder, + Permissions = PermissionsElder + }, new RolePermission { + GroupType = GroupType.Private, + Role = UserRole.Leader, + Permissions = PermissionsLeader } - } + ]; public static readonly Schema.Group EMD_Dragons = new Schema.Group { GroupID = "8e68214a-c801-4759-8461-d01f28484134", @@ -215,44 +240,44 @@ public class GroupController : Controller { JoinGroupRequest request = XmlUtil.DeserializeXml(groupJoinRequest); Model.Group? group = ctx.Groups.FirstOrDefault(g => g.GroupID.ToString() == request.GroupID.ToUpper()); - if (group != null) { - if (group.Type >= GroupType.Private) { - return Ok(new GroupJoinResult { Success = false, Status = JoinGroupStatus.GroupTypeIsNotPublic }); - } - GroupMember? existing = viking.GroupRoles.FirstOrDefault(g => g.Group.GameID == gameId); - if (existing != null) { - if (existing.Group == group) - return Ok(new GroupJoinResult { Success = false, Status = JoinGroupStatus.UserAlreadyMemberOfTheGroup }); - existing.Group.Vikings.Remove(existing); - if (!existing.Group.Vikings.Any()) ctx.Groups.Remove(existing.Group); - } - if (group.Type == GroupType.MembersOnly) { - if (!group.JoinRequests.Any(r => r.Viking == viking)) - group.JoinRequests.Add(new GroupJoinRequest { - Group = group, - Viking = viking, - //Message = request.Message // For future implemention, once moderation is possible. - }); - ctx.SaveChanges(); - return Ok(new GroupJoinResult { Success = false, Status = JoinGroupStatus.JoinRequestPending }); - } - - if (group.Vikings.Count < group.MaxMemberLimit) { - GroupMember joinee = new GroupMember { - Viking = viking, - Group = group, - UserRole = UserRole.Member, - JoinDate = DateTime.Now - }; - group.Vikings.Add(joinee); - group.LastActiveTime = joinee.JoinDate; - ctx.SaveChanges(); - return Ok(new GroupJoinResult { Success = true, Status = JoinGroupStatus.Success }); - } else { - return Ok(new GroupJoinResult { Success = false, Status = JoinGroupStatus.GroupIsFull }); - } + if (group == null) return Ok(new GroupJoinResult { Success = false, Status = JoinGroupStatus.Error }); + if (group.Type >= GroupType.Private) { + return Ok(new GroupJoinResult { Success = false, Status = JoinGroupStatus.GroupTypeIsNotPublic }); } - return Ok(new GroupJoinResult { Success = false, Status = JoinGroupStatus.Error }); + + GroupMember? existing = viking.GroupRoles.FirstOrDefault(g => g.Group.GameID == gameId); + if (existing != null) { + if (existing.Group == group) + return Ok(new GroupJoinResult { Success = false, Status = JoinGroupStatus.UserAlreadyMemberOfTheGroup }); + + existing.Group.Vikings.Remove(existing); + if (!existing.Group.Vikings.Any()) ctx.Groups.Remove(existing.Group); + } + + if (group.Type == GroupType.MembersOnly) { + if (!group.JoinRequests.Any(r => r.Viking == viking)) + group.JoinRequests.Add(new GroupJoinRequest { + Group = group, + Viking = viking, + //Message = request.Message // For future implemention, once moderation is possible. + }); + ctx.SaveChanges(); + return Ok(new GroupJoinResult { Success = false, Status = JoinGroupStatus.JoinRequestPending }); + } + + if (group.Vikings.Count >= group.MaxMemberLimit) + return Ok(new GroupJoinResult { Success = false, Status = JoinGroupStatus.GroupIsFull }); + + GroupMember joinee = new GroupMember { + Viking = viking, + Group = group, + UserRole = UserRole.Member, + JoinDate = DateTime.Now + }; + group.Vikings.Add(joinee); + group.LastActiveTime = joinee.JoinDate; + ctx.SaveChanges(); + return Ok(new GroupJoinResult { Success = true, Status = JoinGroupStatus.Success }); } [HttpPost] @@ -262,24 +287,20 @@ public class GroupController : Controller { public IActionResult LeaveGroup(Viking viking, [FromForm] string groupLeaveRequest) { LeaveGroupRequest request = XmlUtil.DeserializeXml(groupLeaveRequest); GroupMember? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); - if (vikingRole != null) { - GroupMember? targetRole = null; - if (viking.Uid.ToString().Equals(request.UserID, StringComparison.CurrentCultureIgnoreCase)) { - targetRole = vikingRole.Group.Vikings.FirstOrDefault(gv => gv.Viking == viking); - } else if (vikingRole.UserRole >= UserRole.Elder) { - targetRole = vikingRole.Group.Vikings.FirstOrDefault(gv => gv.Viking.Uid.ToString() == request.UserID); - } else { - return Ok(new LeaveGroupResult { Success = false, Status = LeaveGroupStatus.Error }); - } - if (targetRole != null) { - vikingRole.Group.Vikings.Remove(targetRole); - if (!vikingRole.Group.Vikings.Any()) ctx.Groups.Remove(vikingRole.Group); - ctx.SaveChanges(); - return Ok(new LeaveGroupResult { Success = true, Status = LeaveGroupStatus.Success }); - } + if (vikingRole == null) return Ok(new LeaveGroupResult { Success = false, Status = LeaveGroupStatus.Error }); + GroupMember? targetRole; + if (viking.Uid.ToString().Equals(request.UserID, StringComparison.CurrentCultureIgnoreCase)) { + targetRole = vikingRole.Group.Vikings.FirstOrDefault(gv => gv.Viking == viking); + } else if (vikingRole.UserRole >= UserRole.Elder) { + targetRole = vikingRole.Group.Vikings.FirstOrDefault(gv => gv.Viking.Uid.ToString() == request.UserID); + } else return Ok(new LeaveGroupResult { Success = false, Status = LeaveGroupStatus.Error }); + + if (targetRole == null) return Ok(new LeaveGroupResult { Success = false, Status = LeaveGroupStatus.UserNotAMemberOfTheGroup }); - } - return Ok(new LeaveGroupResult { Success = false, Status = LeaveGroupStatus.Error }); + vikingRole.Group.Vikings.Remove(targetRole); + if (!vikingRole.Group.Vikings.Any()) ctx.Groups.Remove(vikingRole.Group); + ctx.SaveChanges(); + return Ok(new LeaveGroupResult { Success = true, Status = LeaveGroupStatus.Success }); } [HttpPost] @@ -336,20 +357,20 @@ public class GroupController : Controller { public IActionResult RemoveMember(Viking viking, [FromForm] string removeMemberRequest) { RemoveMemberRequest request = XmlUtil.DeserializeXml(removeMemberRequest); GroupMember? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); - if (vikingRole != null) { - if (vikingRole.UserRole < UserRole.Elder) { - return Ok(new RemoveMemberResult { Success = false, Status = RemoveMemberStatus.UserHasNoPermission }); - } - GroupMember? targetRole = vikingRole.Group.Vikings.FirstOrDefault(gv => gv.Viking.Uid.ToString() == request.RemoveUserID); - if (targetRole != null) { - vikingRole.Group.Vikings.Remove(targetRole); - if (!vikingRole.Group.Vikings.Any()) ctx.Groups.Remove(vikingRole.Group); - ctx.SaveChanges(); - return Ok(new RemoveMemberResult { Success = true, Status = RemoveMemberStatus.Success }); - } + if (vikingRole == null) + return Ok(new RemoveMemberResult { Success = false, Status = RemoveMemberStatus.Error }); + + if (vikingRole.UserRole < UserRole.Elder) + return Ok(new RemoveMemberResult { Success = false, Status = RemoveMemberStatus.UserHasNoPermission }); + + GroupMember? targetRole = vikingRole.Group.Vikings.FirstOrDefault(gv => gv.Viking.Uid.ToString() == request.RemoveUserID); + if (targetRole == null) return Ok(new RemoveMemberResult { Success = false, Status = RemoveMemberStatus.UserNotAMemberOfTheGroup }); - } - return Ok(new RemoveMemberResult { Success = false, Status = RemoveMemberStatus.Error }); + + vikingRole.Group.Vikings.Remove(targetRole); + if (!vikingRole.Group.Vikings.Any()) ctx.Groups.Remove(vikingRole.Group); + ctx.SaveChanges(); + return Ok(new RemoveMemberResult { Success = true, Status = RemoveMemberStatus.Success }); } [HttpPost] @@ -361,43 +382,46 @@ public class GroupController : Controller { AuthorizeJoinRequest request = XmlUtil.DeserializeXml(authorizeJoinRequest); GroupMember? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); - if (vikingRole != null) { - if (vikingRole.UserRole < UserRole.Elder) { - return Ok(new AuthorizeJoinResult { Success = false, Status = AuthorizeJoinStatus.ApproverHasNoPermission }); - } - Viking? target = ctx.Vikings.FirstOrDefault(v => v.Uid.ToString() == request.UserID); - if (target != null) { - GroupMember? existing = target.GroupRoles.FirstOrDefault(gm => gm.Group.GameID == gameId); - if (existing != null) { - if (existing.Group == vikingRole.Group) { - return Ok(new AuthorizeJoinResult { Success = false, Status = AuthorizeJoinStatus.UserAlreadyMemberOfTheGroup }); - } else { - return Ok(new AuthorizeJoinResult { Success = false, Status = AuthorizeJoinStatus.UserHasNoJoinRequest }); - } - } - if (vikingRole.Group.Vikings.Count < vikingRole.Group.MaxMemberLimit) { - if (request.Approved) { - GroupMember joinee = new GroupMember { - Viking = target, - Group = vikingRole.Group, - UserRole = UserRole.Member, - JoinDate = DateTime.Now - }; - vikingRole.Group.Vikings.Add(joinee); - vikingRole.Group.LastActiveTime = joinee.JoinDate; - } - GroupJoinRequest? joinRequest = ctx.GroupJoinRequests.Find(target.Id, vikingRole.GroupID); - if (joinRequest != null) ctx.GroupJoinRequests.Remove(joinRequest); - ctx.SaveChanges(); - return Ok(new AuthorizeJoinResult { Success = true, Status = AuthorizeJoinStatus.Success }); - } else { - return Ok(new AuthorizeJoinResult { Success = false, Status = AuthorizeJoinStatus.GroupIsFull }); - } - } else { - return Ok(new AuthorizeJoinResult { Success = false, Status = AuthorizeJoinStatus.Error }); - } + if (vikingRole == null) + return Ok(new AuthorizeJoinResult { Success = false, Status = AuthorizeJoinStatus.ApproverNotInThisGroup }); + + if (vikingRole.UserRole < UserRole.Elder) + return Ok(new AuthorizeJoinResult { Success = false, Status = AuthorizeJoinStatus.ApproverHasNoPermission }); + + Viking? target = ctx.Vikings.FirstOrDefault(v => v.Uid.ToString() == request.UserID); + if (target == null) { + return Ok(new AuthorizeJoinResult { Success = false, Status = AuthorizeJoinStatus.Error }); } - return Ok(new AuthorizeJoinResult { Success = false, Status = AuthorizeJoinStatus.ApproverNotInThisGroup }); + + GroupMember? existing = target.GroupRoles.FirstOrDefault(gm => gm.Group.GameID == gameId); + if (existing != null) { + return Ok(new AuthorizeJoinResult { + Success = false, + Status = existing.Group == vikingRole.Group + ? AuthorizeJoinStatus.UserAlreadyMemberOfTheGroup + : AuthorizeJoinStatus.UserHasNoJoinRequest + }); + } + + if (vikingRole.Group.Vikings.Count >= vikingRole.Group.MaxMemberLimit) + return Ok(new AuthorizeJoinResult { Success = false, Status = AuthorizeJoinStatus.GroupIsFull }); + + if (request.Approved) { + GroupMember joinee = new GroupMember { + Viking = target, + Group = vikingRole.Group, + UserRole = UserRole.Member, + JoinDate = DateTime.Now + }; + vikingRole.Group.Vikings.Add(joinee); + vikingRole.Group.LastActiveTime = joinee.JoinDate; + } + + GroupJoinRequest? joinRequest = ctx.GroupJoinRequests.Find(target.Id, vikingRole.GroupID); + if (joinRequest != null) ctx.GroupJoinRequests.Remove(joinRequest); + ctx.SaveChanges(); + return Ok(new AuthorizeJoinResult { Success = true, Status = AuthorizeJoinStatus.Success }); + } [HttpPost] @@ -407,29 +431,35 @@ public class GroupController : Controller { public IActionResult AssignRole(Viking viking, [FromForm] string assignRoleRequest) { AssignRoleRequest request = XmlUtil.DeserializeXml(assignRoleRequest); GroupMember? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); - if (vikingRole != null) { - if (vikingRole.UserRole < UserRole.Elder) { + if (vikingRole == null) + return Ok(new AssignRoleResult { Success = false, Status = AssignRoleStatus.ApproverNotMemberOfTheGroup }); + + if (vikingRole.UserRole < UserRole.Elder) + return Ok(new AssignRoleResult { Success = false, Status = AssignRoleStatus.ApproverHasNoPermission }); + + GroupMember? targetRole = vikingRole.Group.Vikings.FirstOrDefault(gv => gv.Viking.Uid.ToString() == request.MemberID); + if (targetRole == null) + return Ok(new AssignRoleResult { Success = false, Status = AssignRoleStatus.MemberNotPartOfTheGroup }); + + if (targetRole.UserRole == request.NewRole) + return Ok(new AssignRoleResult { Success = false, Status = AssignRoleStatus.MemberAlreadyInTheRole }); + + if (vikingRole.UserRole == UserRole.Leader) { + // Disallow leader from simply demoting themself. + if (viking == targetRole.Viking) return Ok(new AssignRoleResult { Success = false, Status = AssignRoleStatus.ApproverHasNoPermission }); - } - GroupMember? targetRole = vikingRole.Group.Vikings.FirstOrDefault(gv => gv.Viking.Uid.ToString() == request.MemberID); - if (targetRole != null) { - if (targetRole.UserRole == request.NewRole) - return Ok(new AssignRoleResult { Success = false, Status = AssignRoleStatus.MemberAlreadyInTheRole }); - if (vikingRole.UserRole == UserRole.Leader) { // Disallow leader from simply demoting themself. - if (viking == targetRole.Viking) - return Ok(new AssignRoleResult { Success = false, Status = AssignRoleStatus.ApproverHasNoPermission }); - } else if (viking != targetRole.Viking || request.NewRole > vikingRole.UserRole) { // Disallow Elders from promoting themselves to leader, or promoting anyone else to elder, but allow them to demote themselves. - return Ok(new AssignRoleResult { Success = false, Status = AssignRoleStatus.ApproverHasNoPermission }); - } - targetRole.UserRole = request.NewRole; - if (request.NewRole == UserRole.Leader) vikingRole.UserRole = UserRole.Elder; // This is the only way a leader can demote themself. - ctx.SaveChanges(); - return Ok(new AssignRoleResult { Success = true, Status = AssignRoleStatus.Success }); - } else { - return Ok(new AssignRoleResult { Success = false, Status = AssignRoleStatus.MemberNotPartOfTheGroup }); - } + } else if (viking != targetRole.Viking || request.NewRole > vikingRole.UserRole) { + // Disallow Elders from promoting themselves to leader, or promoting anyone else to elder, but allow them to demote themselves. + return Ok(new AssignRoleResult { Success = false, Status = AssignRoleStatus.ApproverHasNoPermission }); } - return Ok(new AssignRoleResult { Success = false, Status = AssignRoleStatus.ApproverNotMemberOfTheGroup }); + + targetRole.UserRole = request.NewRole; + if (request.NewRole == UserRole.Leader) + vikingRole.UserRole = UserRole.Elder; // This is the only way a leader can demote themself. + + ctx.SaveChanges(); + return Ok(new AssignRoleResult { Success = true, Status = AssignRoleStatus.Success }); + } [HttpPost] From 67a0b9f36030d47190f68886f7ecee69c8406afd Mon Sep 17 00:00:00 2001 From: Hipposgrumm Date: Sun, 19 Oct 2025 11:36:08 -0600 Subject: [PATCH 6/8] Added group name validation. Also made name validation a switch statement (faster). --- src/Controllers/Common/ContentController.cs | 26 +++++++++++++-------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/Controllers/Common/ContentController.cs b/src/Controllers/Common/ContentController.cs index bfe965f..319ceec 100644 --- a/src/Controllers/Common/ContentController.cs +++ b/src/Controllers/Common/ContentController.cs @@ -204,17 +204,23 @@ public class ContentController : Controller { // Check if name populated NameValidationRequest request = XmlUtil.DeserializeXml(nameValidationRequest); - if (request.Category == NameCategory.Default) { - // This is an avatar we are checking - // Check if viking exists - bool exists = ctx.Vikings.Count(e => e.Name == request.Name) > 0; - NameValidationResult result = exists ? NameValidationResult.NotUnique : NameValidationResult.Ok; - return Ok(new NameValidationResponse { Result = result}); - - } else { - // TODO: pets, groups, default - return Ok(); + bool exists; + switch (request.Category) { + case NameCategory.Default: + // This is an avatar we are checking + // Check if viking exists + exists = ctx.Vikings.Any(e => e.Name == request.Name); + break; + case NameCategory.Group: + exists = ctx.Groups.Any(e => e.Name == request.Name); + break; + default: + // TODO: pets, default + return Ok(); } + + NameValidationResult result = exists ? NameValidationResult.NotUnique : NameValidationResult.Ok; + return Ok(new NameValidationResponse { Result = result }); } [HttpPost] From 6963a22f6f06bc5e511de8289406a6d93312ffe0 Mon Sep 17 00:00:00 2001 From: Hipposgrumm Date: Sun, 26 Oct 2025 22:14:38 -0600 Subject: [PATCH 7/8] Removed EMD code because Spirtix yelled at me. --- src/Controllers/Common/GroupController.cs | 40 ----------------------- 1 file changed, 40 deletions(-) diff --git a/src/Controllers/Common/GroupController.cs b/src/Controllers/Common/GroupController.cs index 8e40d78..a426855 100644 --- a/src/Controllers/Common/GroupController.cs +++ b/src/Controllers/Common/GroupController.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; using sodoff.Attributes; using sodoff.Model; using sodoff.Schema; @@ -99,8 +98,6 @@ public class GroupController : Controller { private readonly DBContext ctx; - private bool AddedEMD = false; - public GroupController(DBContext ctx) { this.ctx = ctx; } @@ -511,11 +508,6 @@ public class GroupController : Controller { [Route("GroupWebService.asmx/GetGroupsByGroupType")] public Schema.Group[] GetGroupsByGroupType([FromForm] string apiKey, [FromForm] string groupType) { uint gameId = ClientVersion.GetGameID(apiKey); - - if (!AddedEMD) { - AddEMDGroups(); - AddedEMD = true; - } List groups = new List(); foreach (Model.Group group in ctx.Groups) { @@ -554,36 +546,4 @@ public class GroupController : Controller { Points = group.Points }).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, - GameID = ClientVersion.EMD, - MaxMemberLimit = int.MaxValue - }); - 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, - GameID = ClientVersion.EMD, - MaxMemberLimit = int.MaxValue - }); - changed = true; - } - if (changed) ctx.SaveChanges(); - - } } From 5e5a66bf92aee119cd809c5239cd54062b698380 Mon Sep 17 00:00:00 2001 From: Hipposgrumm Date: Sun, 26 Oct 2025 22:21:43 -0600 Subject: [PATCH 8/8] Rename GroupRoles to GroupMemberships and... ...made displayname check in GetMembersByGroupID. --- src/Controllers/Common/GroupController.cs | 32 ++++++++++----------- src/Controllers/Common/ProfileController.cs | 2 +- src/Model/DBContext.cs | 4 +-- src/Model/Viking.cs | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Controllers/Common/GroupController.cs b/src/Controllers/Common/GroupController.cs index a426855..bb0a6ab 100644 --- a/src/Controllers/Common/GroupController.cs +++ b/src/Controllers/Common/GroupController.cs @@ -109,7 +109,7 @@ public class GroupController : Controller { public IActionResult CreateGroup(Viking viking, [FromForm] string apiKey, [FromForm] string groupCreateRequest) { uint gameId = ClientVersion.GetGameID(apiKey); - if (viking.GroupRoles.Any(g => g.Group.GameID == gameId)) { + if (viking.GroupMemberships.Any(g => g.Group.GameID == gameId)) { return Ok(new CreateGroupResult { Success = false, Status = CreateGroupStatus.CreatorIsNotApproved }); } @@ -121,7 +121,7 @@ public class GroupController : Controller { //if (request.MaxMemberLimit < 4) return Ok(new CreateGroupResult { Success = false, Status = CreateGroupStatus.GroupMaxMemberLimitInvalid }); // Not actually used by the game. if (request.Name.Length == 0) return Ok(new CreateGroupResult { Success = false, Status = CreateGroupStatus.GroupNameIsEmpty }); if (request.Description.Length == 0) return Ok(new CreateGroupResult { Success = false, Status = CreateGroupStatus.GroupDescriptionIsEmpty }); - if (viking.GroupRoles.Any(g => g.Group.Name.Equals(request.Name, StringComparison.InvariantCultureIgnoreCase))) return Ok(new CreateGroupResult { Success = false, Status = CreateGroupStatus.GroupNameIsDuplicate }); + if (viking.GroupMemberships.Any(g => g.Group.Name.Equals(request.Name, StringComparison.InvariantCultureIgnoreCase))) return Ok(new CreateGroupResult { Success = false, Status = CreateGroupStatus.GroupNameIsDuplicate }); Model.Group group = new Model.Group { Name = request.Name, @@ -173,7 +173,7 @@ public class GroupController : Controller { EditGroupRequest request = XmlUtil.DeserializeXml(groupEditRequest); request.Name = request.Name.Trim(); - GroupMember? vikingRole = viking.GroupRoles.FirstOrDefault(gv => gv.Group.GroupID.ToString() == request.GroupID); + GroupMember? vikingRole = viking.GroupMemberships.FirstOrDefault(gv => gv.Group.GroupID.ToString() == request.GroupID); if (vikingRole == null) { return Ok(new EditGroupResult { Success = false, Status = EditGroupStatus.GroupNotFound }); } else if (vikingRole.UserRole < UserRole.Elder) { @@ -185,7 +185,7 @@ public class GroupController : Controller { //if (request.MaxMemberLimit < 4) return Ok(new EditGroupResult { Success = false, Status = EditGroupStatus.GroupMaxMemberLimitInvalid }); // Not actually used by the game. if ((request.Name?.Length ?? 0) == 0) request.Name = vikingRole.Group.Name; if ((request.Description?.Length ?? 0) == 0) request.Description = vikingRole.Group.Description; - if (request.Name != vikingRole.Group.Name && viking.GroupRoles.Any(gm => gm.Group.Name.Equals(request.Name, StringComparison.InvariantCultureIgnoreCase))) return Ok(new EditGroupResult { Success = false, Status = EditGroupStatus.GroupNameIsDuplicate }); + if (request.Name != vikingRole.Group.Name && viking.GroupMemberships.Any(gm => gm.Group.Name.Equals(request.Name, StringComparison.InvariantCultureIgnoreCase))) return Ok(new EditGroupResult { Success = false, Status = EditGroupStatus.GroupNameIsDuplicate }); vikingRole.Group.Name = request.Name; vikingRole.Group.Description = request.Description; @@ -209,7 +209,7 @@ public class GroupController : Controller { uint gameId = ClientVersion.GetGameID(apiKey); // Check for loyalty. - if (viking.GroupRoles.Any(g => g.Group.GameID == gameId)) { + if (viking.GroupMemberships.Any(g => g.Group.GameID == gameId)) { return Ok(new JoinGroupResult { GroupStatus = GroupMembershipStatus.SELF_BLOCKED }); } Model.Group? group = ctx.Groups.FirstOrDefault(g => g.GroupID.ToString().Equals(groupID, StringComparison.OrdinalIgnoreCase)); @@ -242,7 +242,7 @@ public class GroupController : Controller { return Ok(new GroupJoinResult { Success = false, Status = JoinGroupStatus.GroupTypeIsNotPublic }); } - GroupMember? existing = viking.GroupRoles.FirstOrDefault(g => g.Group.GameID == gameId); + GroupMember? existing = viking.GroupMemberships.FirstOrDefault(g => g.Group.GameID == gameId); if (existing != null) { if (existing.Group == group) return Ok(new GroupJoinResult { Success = false, Status = JoinGroupStatus.UserAlreadyMemberOfTheGroup }); @@ -283,7 +283,7 @@ public class GroupController : Controller { [VikingSession] public IActionResult LeaveGroup(Viking viking, [FromForm] string groupLeaveRequest) { LeaveGroupRequest request = XmlUtil.DeserializeXml(groupLeaveRequest); - GroupMember? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); + GroupMember? vikingRole = viking.GroupMemberships.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); if (vikingRole == null) return Ok(new LeaveGroupResult { Success = false, Status = LeaveGroupStatus.Error }); GroupMember? targetRole; if (viking.Uid.ToString().Equals(request.UserID, StringComparison.CurrentCultureIgnoreCase)) { @@ -311,7 +311,7 @@ public class GroupController : Controller { if (request.ForUserID != null) { Viking? target = ctx.Vikings.FirstOrDefault(v => request.ForUserID.ToUpper() == v.Uid.ToString()); if (target == null) return Ok(new GetGroupsResult { Success = false }); - groups = target.GroupRoles.Select(gv => gv.Group); + groups = target.GroupMemberships.Select(gv => gv.Group); } else { groups = groups.Where(g => g.Type == GroupType.Public || g.Type == GroupType.MembersOnly); } @@ -353,7 +353,7 @@ public class GroupController : Controller { [VikingSession] public IActionResult RemoveMember(Viking viking, [FromForm] string removeMemberRequest) { RemoveMemberRequest request = XmlUtil.DeserializeXml(removeMemberRequest); - GroupMember? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); + GroupMember? vikingRole = viking.GroupMemberships.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); if (vikingRole == null) return Ok(new RemoveMemberResult { Success = false, Status = RemoveMemberStatus.Error }); @@ -378,7 +378,7 @@ public class GroupController : Controller { uint gameId = ClientVersion.GetGameID(apiKey); AuthorizeJoinRequest request = XmlUtil.DeserializeXml(authorizeJoinRequest); - GroupMember? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); + GroupMember? vikingRole = viking.GroupMemberships.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); if (vikingRole == null) return Ok(new AuthorizeJoinResult { Success = false, Status = AuthorizeJoinStatus.ApproverNotInThisGroup }); @@ -390,7 +390,7 @@ public class GroupController : Controller { return Ok(new AuthorizeJoinResult { Success = false, Status = AuthorizeJoinStatus.Error }); } - GroupMember? existing = target.GroupRoles.FirstOrDefault(gm => gm.Group.GameID == gameId); + GroupMember? existing = target.GroupMemberships.FirstOrDefault(gm => gm.Group.GameID == gameId); if (existing != null) { return Ok(new AuthorizeJoinResult { Success = false, @@ -427,7 +427,7 @@ public class GroupController : Controller { [VikingSession] public IActionResult AssignRole(Viking viking, [FromForm] string assignRoleRequest) { AssignRoleRequest request = XmlUtil.DeserializeXml(assignRoleRequest); - GroupMember? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); + GroupMember? vikingRole = viking.GroupMemberships.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); if (vikingRole == null) return Ok(new AssignRoleResult { Success = false, Status = AssignRoleStatus.ApproverNotMemberOfTheGroup }); @@ -465,7 +465,7 @@ public class GroupController : Controller { [VikingSession] public IActionResult GetPendingJoinRequests(Viking viking, [FromForm] string getPendingJoinRequest) { GetPendingJoinRequest request = XmlUtil.DeserializeXml(getPendingJoinRequest); - GroupMember? vikingRole = viking.GroupRoles.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); + GroupMember? vikingRole = viking.GroupMemberships.FirstOrDefault(g => g.Group.GroupID.ToString() == request.GroupID); if (vikingRole?.UserRole >= UserRole.Elder) { return Ok(new GetPendingJoinResult { Success = true, @@ -494,7 +494,7 @@ public class GroupController : Controller { Viking? viking = ctx.Vikings.FirstOrDefault(v => v.Uid.ToString() == userId); if (viking == null) return []; - return viking.GroupRoles.Where(gm => gm.Group.GameID == gameId).Select(gm => new Schema.Group { + return viking.GroupMemberships.Where(gm => gm.Group.GameID == gameId).Select(gm => new Schema.Group { GroupID = gm.Group.GroupID.ToString(), Name = gm.Group.Name, Color = gm.Group.Color, @@ -533,8 +533,8 @@ public class GroupController : Controller { ); if (group == null) return []; - return group.Vikings.Select(v => new Schema.GroupMember{ - DisplayName = v.Viking.AvatarSerialized != null + return group.Vikings.Select(v => new Schema.GroupMember { + DisplayName = group.GameID == ClientVersion.MB && v.Viking.AvatarSerialized != null ? XmlUtil.DeserializeXml(v.Viking.AvatarSerialized).DisplayName : v.Viking.Name, UserID = v.Viking.Uid.ToString(), diff --git a/src/Controllers/Common/ProfileController.cs b/src/Controllers/Common/ProfileController.cs index d965250..f039e97 100644 --- a/src/Controllers/Common/ProfileController.cs +++ b/src/Controllers/Common/ProfileController.cs @@ -153,7 +153,7 @@ public class ProfileController : Controller { UserGameCurrency currency = achievementService.GetUserCurrency(viking); - ICollection groups = viking.GroupRoles; + ICollection groups = viking.GroupMemberships; UserProfileGroupData[] groupData = new UserProfileGroupData[groups.Count]; int i = 0; diff --git a/src/Model/DBContext.cs b/src/Model/DBContext.cs index 2abe239..449545b 100644 --- a/src/Model/DBContext.cs +++ b/src/Model/DBContext.cs @@ -153,7 +153,7 @@ public class DBContext : DbContext { builder.Entity().HasOne(v => v.Neighborhood) .WithOne(e => e.Viking); - builder.Entity().HasMany(v => v.GroupRoles) + builder.Entity().HasMany(v => v.GroupMemberships) .WithOne(g => g.Viking); builder.Entity().HasMany(v => v.Ratings) @@ -292,7 +292,7 @@ public class DBContext : DbContext { builder.Entity().HasOne(r => r.Group) .WithMany(g => g.Vikings); builder.Entity().HasOne(r => r.Viking) - .WithMany(g => g.GroupRoles); + .WithMany(g => g.GroupMemberships); builder.Entity().HasMany(r => r.JoinRequests) .WithOne(r => r.Group); builder.Entity().HasOne(r => r.Group) diff --git a/src/Model/Viking.cs b/src/Model/Viking.cs index 3ac5579..0e30049 100644 --- a/src/Model/Viking.cs +++ b/src/Model/Viking.cs @@ -47,7 +47,7 @@ public class Viking { public virtual ICollection MMORoles { get; set; } = null!; public virtual Neighborhood? Neighborhood { get; set; } = null!; [JsonIgnore] - public virtual ICollection GroupRoles { get; set; } = null!; + public virtual ICollection GroupMemberships { get; set; } = null!; public virtual ICollection Ratings { get; set; } = null!; public virtual Dragon? SelectedDragon { get; set; } public virtual ICollection UserMissions { get; set; } = null!;