-
Notifications
You must be signed in to change notification settings - Fork 64
JewelRuby #662
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
JewelRuby #662
Changes from all commits
3c5c5d5
55c8d2a
e11eec4
af10dca
3f5414d
f633b68
24c70fa
059fa69
dff14e7
5d6f087
a31f930
d1c3b13
2139067
80ffb23
8adfa4d
1dadbe8
94f6024
c034216
be65783
ba981db
18655d1
517fe84
c62e56d
6bf3d5e
6ff3785
b1d810c
6770210
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
| using Maple2.Model.Enum; | ||
| using Maple2.Model.Game; | ||
| using Maple2.Tools.Extensions; | ||
| using Microsoft.EntityFrameworkCore; | ||
| using Z.EntityFramework.Plus; | ||
|
|
||
| namespace Maple2.Database.Storage; | ||
|
|
@@ -42,6 +43,140 @@ public IList<GuildMember> GetGuildMembers(IPlayerInfoProvider provider, long gui | |
| .ToList(); | ||
| } | ||
|
|
||
|
|
||
| public IList<Guild> SearchGuilds(IPlayerInfoProvider provider, string guildName = "", GuildFocus? focus = null, int limit = 50) { | ||
| IQueryable<Model.Guild> query = Context.Guild; | ||
| if (!string.IsNullOrWhiteSpace(guildName)) { | ||
| query = query.Where(guild => EF.Functions.Like(guild.Name, $"%{guildName}%")); | ||
| } | ||
| if (focus.HasValue && (int) focus.Value != 0) { | ||
| query = query.Where(guild => guild.Focus == focus.Value); | ||
| } | ||
|
|
||
| List<long> guildIds = query.OrderBy(guild => guild.Name) | ||
| .Take(limit) | ||
| .Select(guild => guild.Id) | ||
| .ToList(); | ||
|
|
||
| var result = new List<Guild>(); | ||
| foreach (long id in guildIds) { | ||
| Guild? guild = LoadGuild(id, string.Empty); | ||
| if (guild == null) { | ||
| continue; | ||
| } | ||
|
|
||
| foreach (GuildMember member in GetGuildMembers(provider, id)) { | ||
| guild.Members.TryAdd(member.CharacterId, member); | ||
| guild.AchievementInfo += member.Info.AchievementInfo; | ||
| } | ||
| result.Add(guild); | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| public GuildApplication? CreateGuildApplication(IPlayerInfoProvider provider, long guildId, long applicantId) { | ||
| Guild? guild = LoadGuild(guildId, string.Empty); | ||
| PlayerInfo? applicant = provider.GetPlayerInfo(applicantId); | ||
| if (guild == null || applicant == null) { | ||
| return null; | ||
| } | ||
|
|
||
| if (Context.GuildApplication.Any(app => app.GuildId == guildId && app.ApplicantId == applicantId)) { | ||
| Model.GuildApplication existing = Context.GuildApplication.First(app => app.GuildId == guildId && app.ApplicantId == applicantId); | ||
| return new GuildApplication { | ||
| Id = existing.Id, | ||
| Guild = guild, | ||
| Applicant = applicant, | ||
| CreationTime = existing.CreationTime.ToEpochSeconds(), | ||
| }; | ||
| } | ||
|
|
||
| var app = new Model.GuildApplication { | ||
| GuildId = guildId, | ||
| ApplicantId = applicantId, | ||
| }; | ||
| Context.GuildApplication.Add(app); | ||
| if (!SaveChanges()) { | ||
| return null; | ||
| } | ||
|
|
||
| return new GuildApplication { | ||
| Id = app.Id, | ||
| Guild = guild, | ||
| Applicant = applicant, | ||
| CreationTime = app.CreationTime.ToEpochSeconds(), | ||
| }; | ||
| } | ||
|
|
||
| public GuildApplication? GetGuildApplication(IPlayerInfoProvider provider, long applicationId) { | ||
| Model.GuildApplication? app = Context.GuildApplication.FirstOrDefault(app => app.Id == applicationId); | ||
| if (app == null) { | ||
| return null; | ||
| } | ||
|
|
||
| Guild? guild = LoadGuild(app.GuildId, string.Empty); | ||
| PlayerInfo? applicant = provider.GetPlayerInfo(app.ApplicantId); | ||
| if (guild == null || applicant == null) { | ||
| return null; | ||
| } | ||
|
|
||
| return new GuildApplication { | ||
| Id = app.Id, | ||
| Guild = guild, | ||
| Applicant = applicant, | ||
| CreationTime = app.CreationTime.ToEpochSeconds(), | ||
| }; | ||
| } | ||
|
|
||
| public IList<GuildApplication> GetGuildApplications(IPlayerInfoProvider provider, long guildId) { | ||
| List<Model.GuildApplication> applications = Context.GuildApplication.Where(app => app.GuildId == guildId) | ||
| .OrderByDescending(app => app.CreationTime) | ||
| .ToList(); | ||
|
|
||
| return applications | ||
| .Select(app => { | ||
| Guild? guild = LoadGuild(app.GuildId, string.Empty); | ||
| PlayerInfo? applicant = provider.GetPlayerInfo(app.ApplicantId); | ||
| if (guild == null || applicant == null) { | ||
| return null; | ||
| } | ||
|
|
||
| return new GuildApplication { | ||
| Id = app.Id, | ||
| Guild = guild, | ||
| Applicant = applicant, | ||
| CreationTime = app.CreationTime.ToEpochSeconds(), | ||
| }; | ||
| }) | ||
| .WhereNotNull() | ||
| .ToList(); | ||
| } | ||
|
|
||
| public IList<GuildApplication> GetGuildApplicationsByApplicant(IPlayerInfoProvider provider, long applicantId) { | ||
| List<Model.GuildApplication> applications = Context.GuildApplication.Where(app => app.ApplicantId == applicantId) | ||
| .OrderByDescending(app => app.CreationTime) | ||
| .ToList(); | ||
|
|
||
| return applications | ||
| .Select(app => { | ||
| Guild? guild = LoadGuild(app.GuildId, string.Empty); | ||
| PlayerInfo? applicant = provider.GetPlayerInfo(app.ApplicantId); | ||
| if (guild == null || applicant == null) { | ||
| return null; | ||
| } | ||
|
|
||
| return new GuildApplication { | ||
| Id = app.Id, | ||
| Guild = guild, | ||
| Applicant = applicant, | ||
| CreationTime = app.CreationTime.ToEpochSeconds(), | ||
| }; | ||
| }) | ||
| .WhereNotNull() | ||
| .ToList(); | ||
|
Comment on lines
+156
to
+177
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| public Guild? CreateGuild(string name, long leaderId) { | ||
| BeginTransaction(); | ||
|
|
||
|
|
@@ -155,20 +290,18 @@ public bool DeleteGuildApplications(long characterId) { | |
| public bool SaveGuildMembers(long guildId, ICollection<GuildMember> members) { | ||
| Dictionary<long, GuildMember> saveMembers = members | ||
| .ToDictionary(member => member.CharacterId, member => member); | ||
| IEnumerable<Model.GuildMember> existingMembers = Context.GuildMember | ||
| HashSet<long> existingMembers = Context.GuildMember | ||
| .Where(member => member.GuildId == guildId) | ||
| .Select(member => new Model.GuildMember { | ||
| CharacterId = member.CharacterId, | ||
| }); | ||
| .Select(member => member.CharacterId) | ||
| .ToHashSet(); | ||
|
|
||
| foreach (Model.GuildMember member in existingMembers) { | ||
| if (saveMembers.Remove(member.CharacterId, out GuildMember? gameMember)) { | ||
| foreach ((long characterId, GuildMember gameMember) in saveMembers) { | ||
| if (existingMembers.Contains(characterId)) { | ||
| Context.GuildMember.Update(gameMember); | ||
| } else { | ||
| Context.GuildMember.Remove(member); | ||
| Context.GuildMember.Add(gameMember); | ||
| } | ||
| } | ||
| Context.GuildMember.AddRange(saveMembers.Values.Select<GuildMember, Model.GuildMember>(member => member)); | ||
|
|
||
| return SaveChanges(); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make guild-application creation atomic.
The
Any(...)check at Line 85 and the laterAdd(...)at Line 99 are separate round trips. Concurrent apply requests can both observe “no row” here; without a uniqueness guarantee one inserts a duplicate, and with one the loser still falls back to the generic error path instead of returning the existing application. Collapse this into one atomic path so repeated applies are idempotent.As per coding guidelines, "Use locks for concurrent database operations."
🤖 Prompt for AI Agents