1+ // Copyright (c) 2025 GooGuTeam
2+ // Licensed under the MIT Licence. See the LICENCE file in the repository root for full licence text.
3+
4+ using osu . Game . Rulesets ;
5+ using osu . Game . Rulesets . Catch ;
6+ using osu . Game . Rulesets . Mania ;
7+ using osu . Game . Rulesets . Osu ;
8+ using osu . Game . Rulesets . Taiko ;
9+ using System . Reflection ;
10+
11+ namespace PerformanceServer . Rulesets
12+ {
13+ public class RulesetManager : IRulesetManager
14+ {
15+ private readonly ILogger < RulesetManager > _logger ;
16+
17+ private const string RulesetLibraryPrefix = "osu.Game.Rulesets" ;
18+
19+ private readonly Dictionary < string , Ruleset > _rulesets = new ( ) ;
20+ private readonly Dictionary < int , Ruleset > _rulesetsById = new ( ) ;
21+
22+ public RulesetManager ( ILogger < RulesetManager > logger )
23+ {
24+ _logger = logger ;
25+ LoadOfficialRulesets ( ) ;
26+ LoadFromDisk ( ) ;
27+ }
28+
29+ private void AddRuleset ( Ruleset ruleset )
30+ {
31+ if ( ! _rulesets . TryAdd ( ruleset . ShortName , ruleset ) )
32+ {
33+ _logger . LogWarning ( "Ruleset with short name {shortName} already exists, skipping." , ruleset . ShortName ) ;
34+ return ;
35+ }
36+
37+ if ( ruleset is not ILegacyRuleset legacyRuleset )
38+ {
39+ return ;
40+ }
41+
42+ if ( ! _rulesetsById . TryAdd ( legacyRuleset . LegacyID , ruleset ) )
43+ {
44+ _logger . LogWarning ( "Ruleset with ID {id} already exists, skipping." , legacyRuleset . LegacyID ) ;
45+ }
46+ }
47+
48+ private void LoadOfficialRulesets ( )
49+ {
50+ foreach ( Ruleset ruleset in ( List < Ruleset > )
51+ [ new OsuRuleset ( ) , new TaikoRuleset ( ) , new CatchRuleset ( ) , new ManiaRuleset ( ) ] )
52+ {
53+ AddRuleset ( ruleset ) ;
54+ }
55+
56+ _rulesets [ "catch" ] = _rulesets [ "fruits" ] ;
57+ }
58+
59+ private void LoadFromDisk ( )
60+ {
61+ if ( ! Directory . Exists ( AppSettings . RulesetsPath ) )
62+ {
63+ return ;
64+ }
65+
66+ string [ ] rulesets = Directory . GetFiles ( AppSettings . RulesetsPath , $ "{ RulesetLibraryPrefix } .*.dll") ;
67+
68+ foreach ( string ruleset in rulesets . Where ( f => ! f . Contains ( @"Tests" ) ) )
69+ {
70+ try
71+ {
72+ Assembly assembly = Assembly . LoadFrom ( ruleset ) ;
73+ Type ? rulesetType = assembly . GetTypes ( )
74+ . FirstOrDefault ( t => t . IsSubclassOf ( typeof ( Ruleset ) ) && ! t . IsAbstract ) ;
75+
76+ if ( rulesetType == null )
77+ {
78+ continue ;
79+ }
80+
81+ Ruleset instance = ( Ruleset ) Activator . CreateInstance ( rulesetType ) ! ;
82+ _logger . LogInformation ( "Loading ruleset {ruleset}" , ruleset ) ;
83+ AddRuleset ( instance ) ;
84+ }
85+ catch ( Exception ex )
86+ {
87+ _logger . LogWarning ( "Failed to load ruleset from {ruleset}: {ex}" , ruleset , ex ) ;
88+ }
89+ }
90+ }
91+
92+ public Ruleset GetRuleset ( int rulesetId )
93+ {
94+ return _rulesetsById . TryGetValue ( rulesetId , out Ruleset ? ruleset )
95+ ? ruleset
96+ : throw new ArgumentException ( "Invalid ruleset ID provided." ) ;
97+ }
98+
99+ public Ruleset GetRuleset ( string shortName )
100+ {
101+ return _rulesets . TryGetValue ( shortName , out Ruleset ? ruleset )
102+ ? ruleset
103+ : throw new ArgumentException ( "Invalid ruleset name provided." ) ;
104+ }
105+
106+ public Ruleset GetRuleset ( INeedsRuleset body , int defaultRulesetId = - 1 )
107+ {
108+ Ruleset ruleset ;
109+ if ( ! string . IsNullOrEmpty ( body . RulesetName ) )
110+ {
111+ ruleset = GetRuleset ( body . RulesetName ) ;
112+ }
113+ else if ( body . RulesetId != null )
114+ {
115+ ruleset = GetRuleset ( body . RulesetId . Value ) ;
116+ }
117+ else if ( defaultRulesetId >= - 1 )
118+ {
119+ ruleset = GetRuleset ( defaultRulesetId ) ;
120+ }
121+ else
122+ {
123+ throw new ArgumentException ( "No ruleset provided." ) ;
124+ }
125+
126+ return ruleset ;
127+ }
128+ }
129+ }
0 commit comments