Skip to content
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

Add ability to create new difficulties from within the editor #14799

Closed
wants to merge 16 commits into from

Conversation

bdach
Copy link
Collaborator

@bdach bdach commented Sep 18, 2021

Closes #12035.

UX likely is not final, but it's functional for now.

2021-09-18.20-42-12.mp4

The general flow is mostly a logical continuation of the preceding changes. By far the most finicky part here is the database stuff - there's a new BeatmapManager method here to facilitate appending a difficulty to an existing set, and some added safety checks in Save() to ensure uniquity of difficulty names - without them two or more difficulties could point at one .osu file and throw exceptions. With the changes here, an error notification will show instead:

osu_2021-09-18_21-21-14

I am aware of one more remaining problem - after a restart, if you go to edit an existing map with multiple difficulties, and save one of them, the rest will get truncated, but I'd prefer to tackle that one separately as this diff is a bit on the larger side already.

@bdach
Copy link
Collaborator Author

bdach commented Sep 19, 2021

Upon investigating the issue mentioned above (with difficulties getting truncated) I've decided to bundle the fix for that here, as the fix is short and the issue is nigh untestable anyway (and I've tried, believe me). Sometimes after launch EF - or more concretely, BeatmapStore.Beatmaps - will return BeatmapInfos that have incomplete BeatmapSet.Beatmap lists and will only contain that one difficulty, hence the explicit re-query during editor load added in 406d9c2.

@peppy
Copy link
Sponsor Member

peppy commented Sep 19, 2021

I'm actively wrangling with moving that stuff to realm, so should be fine to continue working around issues with local hacks as required.

@@ -118,6 +118,7 @@ public TextContainer()
{
NormalText = new OsuSpriteText
{
AlwaysPresent = true,
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This definitely requires an inline comment explaining why it's required.

@@ -128,5 +129,7 @@ public bool Equals(BeatmapMetadata other)
&& AudioFile == other.AudioFile
&& BackgroundFile == other.BackgroundFile;
}

public BeatmapMetadata DeepClone() => (BeatmapMetadata)MemberwiseClone();
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this is what we want. It means that individual difficulties can have differing artist/titles, which while technically is supported by stable and by the lazer database structure, is usually not what a mapper wants:

20210920.171322.dotnet.mp4

A good initial starting point might be to drop this and just have all difficulties share metadata until we have a better UX flow for exceptional cases in place.

@peppy
Copy link
Sponsor Member

peppy commented Sep 20, 2021

It looks like on this branch, difficulty name changes no longer save:

20210920.171624.dotnet.mp4

I can't immediately see why this is happening, you might have more insight into this?

@bdach
Copy link
Collaborator Author

bdach commented Sep 20, 2021

No idea off the top of my head. May be more EF chicanery. Will verify.

@bdach
Copy link
Collaborator Author

bdach commented Sep 20, 2021

Fired some commits re: the red review above.

61e2617 should also fix the difficulty names not saving, although I'm not very confident of that fix as database stuff seems quite brittle to the touch the longer I stare at it in the context of this pull (see my discord messages from earlier today for context).

@@ -519,7 +544,7 @@ public override bool OnExiting(IScreen next)
// To update the game-wide beatmap with any changes, perform a re-fetch on exit.
// This is required as the editor makes its local changes via EditorBeatmap
// (which are not propagated outwards to a potentially cached WorkingBeatmap).
var refetchedBeatmap = beatmapManager.GetWorkingBeatmap(Beatmap.Value.BeatmapInfo);
var refetchedBeatmap = beatmapManager.GetWorkingBeatmap(editorBeatmap.BeatmapInfo);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason this is changed is that GetWorkingBeatmap() silently stores back the provided BeatmapInfo to the WorkingBeatmap. And because the beatmapset is refetched anew from the db context in BDL, the old code would pass a stale BeatmapInfo reference to the working beatmap, leading to things like the difficulty name updating on the carousel in song select but not the beatmap wedge on the left.

Comment on lines +151 to +152
playableBeatmap.BeatmapInfo = refetchedBeatmapSet.Beatmaps.Single(b => b.ID == playableBeatmap.BeatmapInfo.ID);
playableBeatmap.BeatmapInfo.Metadata ??= refetchedBeatmapSet.Metadata;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this is now assigning to BeatmapInfo rather than just BeatmapInfo.BeatmapSet as the old code used to do, as per the comment above. As it turns out those two references could differ before, which is why the difficulty name changes didn't save.

As for the metadata shuffling, I needed to do this to prevent crashes in LegacyBeatmapEncoder, which would sometimes access EditorBeatmap.BeatmapInfo.Metadata (e.g. when trying to encode path to the background file) which isn't guaranteed to be present. This is a carbon copy of similar workarounds in BeatmapManager.GetWorkingBeatmap() and BeatmapCarousel.createCarouselSet().

/// <param name="beatmapSkin">The skin specific to the beatmap being added.</param>
/// <returns>The usable <see cref="WorkingBeatmap"/> corresponding to <paramref name="beatmap"/> and associated with the supplied <paramref name="beatmapSet"/>.</returns>
/// <exception cref="InvalidOperationException">If <paramref name="beatmapSet"/> does not exist in the database.</exception>
public virtual WorkingBeatmap AddToExistingSet(BeatmapSetInfo beatmapSet, IBeatmap beatmap, ISkin beatmapSkin = null)
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am suddenly regretting not merging this PR before my recent changes..

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll close and rebase on top whatever the new structure ends up being. No worry.

Copy link
Sponsor Member

@peppy peppy Oct 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either way works. Was more of a joke than a serious issue (unlike the other comment further down).

@peppy
Copy link
Sponsor Member

peppy commented Oct 1, 2021

I really want to get this in, and the code looks mostly fine, but I am still unfortunately able to break this in serious ways:

20211001.185226.dotnet.mp4

I can still get beatmaps into a state they disappear from my client permanently. Even after a restart they are gone.

@peppy
Copy link
Sponsor Member

peppy commented Oct 1, 2021

As an aside, I was also looking at getting the song setup screen to appear (and focus the difficulty name entry field) when creating a new difficulty, which seems to work quite well using the existing isNewBeatmap bool. Maybe best left for a follow-up PR? Feels weird to be dumped into the compose mode when you haven't yet named your difficulty.

@bdach
Copy link
Collaborator Author

bdach commented Oct 1, 2021

I am still unfortunately able to break this in serious ways

upon seeing that i'm leaning to closing this and reopening wholesale after realm. i have a distinct feeling that even if i fix whatever fresh hell is causing this there will be another terminal breakage somewhere else.

the way EF is set up is way too sensitive what with random attached or non attached model references being passed around (which was part of why i was arguing at length about that in discord today, until you explained what the plan for editor was which i can now agree with).

@peppy
Copy link
Sponsor Member

peppy commented Oct 1, 2021

Bit unfortunate. I think it's fine to leave this dangling and push forward as progress is made over the next few weeks (keeping it roughly in sync).

@bdach
Copy link
Collaborator Author

bdach commented Jan 23, 2022

I've attempted to come back to this today but too many things changed to make that feasible, so I will close this for now.

However, I have rewritten this under realm, and an early version is available for preview at https://github.com/bdach/osu/tree/new-difficulty-creation-v2. I won't be PRing it yet, because I need to figure out a few kinks (highlights: bdach@007e886, bdach@fe2b5f5), and I'd also like to split off a few incidental fixes and general changes out of there too (bdach@b2ae987, bdach@b482de0, maybe bdach@4e8ba02 too). I'll try to get the individual pieces together over the coming days.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add ability to create new difficulties in the editor
2 participants