Core architecture for beatmap management, conversion, and processing in osu!
The beatmap processing system in osu! handles the entire lifecycle of beatmaps from import to gameplay-ready conversion. This system is responsible for managing beatmap storage, converting between game modes, and processing hit objects.
The BeatmapManager class is the central hub for all beatmap-related operations. It implements IModelImporter<BeatmapSetInfo> and IWorkingBeatmapCache to provide comprehensive beatmap management.Location: osu.Game/Beatmaps/BeatmapManager.cs:37
// Create a new beatmap set with a single difficultypublic WorkingBeatmap CreateNew(RulesetInfo ruleset, APIUser user)// Get a working beatmap instance for gameplaypublic WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, bool refetch = false)// Save beatmap content to diskpublic virtual void Save(BeatmapInfo beatmapInfo, IBeatmap beatmapContent, ISkin beatmapSkin = null)// Import beatmaps from filespublic Task Import(params string[] paths)
Key Properties:
BeatmapTrackStore: Audio track store for beatmap music (line 39)
ProcessBeatmap: Delegate for post-import processing (line 49)
DefaultBeatmap: Fallback beatmap when none is available (line 350)
The WorkingBeatmap class represents a usable beatmap instance with all its resources (audio, background, storyboard, skin).Location: osu.Game/Beatmaps/WorkingBeatmap.cs:27
WorkingBeatmap uses lazy loading for expensive resources like storyboards and skins to optimize performance. Resources are only loaded when accessed.
Converts beatmaps between different game modes (e.g., osu!standard to osu!taiko).Location: osu.Game/Beatmaps/BeatmapConverter.cs:21
public abstract class BeatmapConverter<T> : IBeatmapConverter where T : HitObject{ // Check if conversion is possible public abstract bool CanConvert(); // Perform the conversion public IBeatmap Convert(CancellationToken cancellationToken = default) { // Clone the original beatmap var original = Beatmap.Clone(); original.BeatmapInfo = original.BeatmapInfo.Clone(); original.ControlPointInfo = original.ControlPointInfo.DeepClone(); return ConvertBeatmap(original, cancellationToken); } // Convert individual hit objects protected abstract IEnumerable<T> ConvertHitObject( HitObject original, IBeatmap beatmap, CancellationToken cancellationToken);}
BeatmapConverter always operates on a clone of the original beatmap to avoid modifying the game-wide instance. This includes deep cloning the BeatmapInfo and ControlPointInfo.
Post-processes converted beatmaps to set up ruleset-specific attributes.Location: osu.Game/Beatmaps/BeatmapProcessor.cs:12
public class BeatmapProcessor : IBeatmapProcessor{ public IBeatmap Beatmap { get; } // Called before hit object defaults are applied public virtual void PreProcess() { IHasComboInformation lastObj = null; foreach (var obj in Beatmap.HitObjects.OfType<IHasComboInformation>()) { obj.UpdateComboInformation(lastObj); lastObj = obj; } } // Called after all mods and defaults are applied public virtual void PostProcess() { }}
The WorkingBeatmapCache maintains active beatmap instances to avoid repeated parsing and loading.Key Features:
LRU caching strategy
Automatic invalidation on beatmap updates
Track transfer optimization (reuse audio between difficulties)
Thread-safe access
// Get cached or create new working beatmapvar working = beatmapManager.GetWorkingBeatmap(beatmapInfo);// Force refresh from databasevar working = beatmapManager.GetWorkingBeatmap(beatmapInfo, refetch: true);// Invalidate cache entrybeatmapManager.Invalidate(beatmapSetInfo);
// Create new beatmap setvar working = beatmapManager.CreateNew(rulesetInfo, currentUser);// Modify the beatmapvar beatmap = working.Beatmap;beatmap.HitObjects.Add(new HitCircle{ StartTime = 1000, Position = new Vector2(256, 192) });// Save changesbeatmapManager.Save( working.BeatmapInfo, beatmap, working.Skin);// The beatmap is now marked as LocallyModified// Hash is recalculated automatically