Skip to content

Commit 49762e2

Browse files
refactor: decouple settings definitions from pages in protocol
Introduce SettingsDefinition as a standalone message containing full setting metadata (id, scope, type with UI config). SettingsPage now only references settings by identifier, enabling settings to be mounted anywhere by namespace+key. - Add SettingsDefinition message to common.proto - Update SettingsPage to hold SettingsEntryIdentifier list - Add settings_definitions field to InstanceInfoResponse/ServerInfoResponse - Split SettingsPageRegistry into exportSettingsDefinitions/exportSettingsPages - Update CLIManager to use definitions lookup map Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent f16416f commit 49762e2

7 files changed

Lines changed: 94 additions & 49 deletions

File tree

mod/src/main/java/com/soulfiremc/bootstrap/client/cli/CLIManager.java

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -101,17 +101,32 @@ public void initCLI(String[] args) {
101101

102102
@SuppressWarnings("unchecked")
103103
private void registerOptions(CommandLine.Model.CommandSpec targetCommandSpec) {
104-
for (var page :
105-
rpcClient.instanceStubBlocking()
106-
.getInstanceInfo(InstanceInfoRequest.newBuilder().setId(cliInstanceId.toString()).build())
107-
.getInstanceSettingsList()) {
108-
for (var entry : page.getEntriesList()) {
109-
var propertyKey = new PropertyKey(entry.getId().getNamespace(), entry.getId().getKey());
110-
111-
var baseArg = "--%s-%s".formatted(entry.getId().getNamespace(), entry.getId().getKey());
112-
switch (entry.getValueCase()) {
104+
var instanceInfo = rpcClient.instanceStubBlocking()
105+
.getInstanceInfo(InstanceInfoRequest.newBuilder().setId(cliInstanceId.toString()).build());
106+
107+
// Build a lookup map for settings definitions by their identifier
108+
var definitionsMap = new HashMap<String, SettingsDefinition>();
109+
for (var definition : instanceInfo.getSettingsDefinitionsList()) {
110+
var id = definition.getId();
111+
var key = id.getNamespace() + ":" + id.getKey();
112+
definitionsMap.put(key, definition);
113+
}
114+
115+
for (var page : instanceInfo.getInstanceSettingsList()) {
116+
for (var entryId : page.getEntriesList()) {
117+
var propertyKey = new PropertyKey(entryId.getNamespace(), entryId.getKey());
118+
var definitionKey = entryId.getNamespace() + ":" + entryId.getKey();
119+
var definition = definitionsMap.get(definitionKey);
120+
121+
if (definition == null) {
122+
log.warn("No definition found for setting: {}", definitionKey);
123+
continue;
124+
}
125+
126+
var baseArg = "--%s-%s".formatted(entryId.getNamespace(), entryId.getKey());
127+
switch (definition.getTypeCase()) {
113128
case STRING -> {
114-
var stringEntry = entry.getString();
129+
var stringEntry = definition.getString();
115130
var description = escapeFormatSpecifiers(stringEntry.getDescription());
116131

117132
var reference = new AtomicReference<String>();
@@ -138,7 +153,7 @@ public <T> T set(T value) {
138153
targetCommandSpec.addOption(optionSpec);
139154
}
140155
case INT -> {
141-
var intEntry = entry.getInt();
156+
var intEntry = definition.getInt();
142157
var description = escapeFormatSpecifiers(intEntry.getDescription());
143158

144159
targetCommandSpec.addOption(addIntSetting(
@@ -149,7 +164,7 @@ public <T> T set(T value) {
149164
intEntry));
150165
}
151166
case DOUBLE -> {
152-
var doubleEntry = entry.getDouble();
167+
var doubleEntry = definition.getDouble();
153168
var description = escapeFormatSpecifiers(doubleEntry.getDescription());
154169

155170
targetCommandSpec.addOption(addDoubleSetting(
@@ -160,7 +175,7 @@ public <T> T set(T value) {
160175
doubleEntry));
161176
}
162177
case BOOL -> {
163-
var boolEntry = entry.getBool();
178+
var boolEntry = definition.getBool();
164179
var description = escapeFormatSpecifiers(boolEntry.getDescription());
165180

166181
var reference = new AtomicReference<Boolean>();
@@ -187,7 +202,7 @@ public <T> T set(T value) {
187202
targetCommandSpec.addOption(optionSpec);
188203
}
189204
case COMBO -> {
190-
var comboEntry = entry.getCombo();
205+
var comboEntry = definition.getCombo();
191206
var description = escapeFormatSpecifiers(comboEntry.getDescription());
192207

193208
var reference = new AtomicReference<String>();
@@ -217,7 +232,7 @@ public <T> T set(T value) {
217232
targetCommandSpec.addOption(optionSpec);
218233
}
219234
case STRING_LIST -> {
220-
var stringListEntry = entry.getStringList();
235+
var stringListEntry = definition.getStringList();
221236
var description = escapeFormatSpecifiers(stringListEntry.getDescription());
222237

223238
var reference = new AtomicReference<String[]>();
@@ -255,7 +270,7 @@ public <T> T set(T value) {
255270
targetCommandSpec.addOption(optionSpec);
256271
}
257272
case MIN_MAX -> {
258-
var minMaxEntry = entry.getMinMax();
273+
var minMaxEntry = definition.getMinMax();
259274

260275
var minEntry = minMaxEntry.getMinEntry();
261276
var minRef = new AtomicInteger();
@@ -307,8 +322,8 @@ public <T> T set(T value) {
307322
targetCommandSpec.addOption(minOptionSpec);
308323
targetCommandSpec.addOption(maxOptionSpec);
309324
}
310-
case VALUE_NOT_SET -> throw new IllegalStateException(
311-
"Unexpected value: " + entry.getValueCase());
325+
case TYPE_NOT_SET -> throw new IllegalStateException(
326+
"Unexpected value: " + definition.getTypeCase());
312327
}
313328
}
314329
}

mod/src/main/java/com/soulfiremc/server/grpc/InstanceServiceImpl.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,15 @@ public void getInstanceInfo(InstanceInfoRequest request, StreamObserver<Instance
125125
}
126126

127127
var instance = optionalInstance.get();
128+
var registry = instance.instanceSettingsPageRegistry();
128129
responseObserver.onNext(InstanceInfoResponse.newBuilder()
129130
.setFriendlyName(instanceEntity.friendlyName())
130131
.setIcon(instanceEntity.icon())
131132
.setConfig(instanceEntity.settings().toProto())
132133
.setState(instanceEntity.attackLifecycle().toProto())
133134
.addAllInstancePermissions(getInstancePermissions(instanceId))
134-
.addAllInstanceSettings(instance.instanceSettingsPageRegistry().exportSettingsMeta())
135+
.addAllSettingsDefinitions(registry.exportSettingsDefinitions())
136+
.addAllInstanceSettings(registry.exportSettingsPages())
135137
.build());
136138
responseObserver.onCompleted();
137139
} catch (Throwable t) {

mod/src/main/java/com/soulfiremc/server/grpc/ServerServiceImpl.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,11 @@ public void getServerInfo(ServerInfoRequest request, StreamObserver<ServerInfoRe
4747
config = configEntity.settings();
4848
}
4949

50+
var registry = soulFireServer.settingsPageRegistry();
5051
responseObserver.onNext(ServerInfoResponse.newBuilder()
5152
.setConfig(config.toProto())
52-
.addAllServerSettings(soulFireServer.settingsPageRegistry().exportSettingsMeta())
53+
.addAllSettingsDefinitions(registry.exportSettingsDefinitions())
54+
.addAllServerSettings(registry.exportSettingsPages())
5355
.build());
5456
responseObserver.onCompleted();
5557
} catch (Throwable t) {

mod/src/main/java/com/soulfiremc/server/settings/lib/SettingsPageRegistry.java

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -206,34 +206,49 @@ private SettingsPageRegistry addPage(
206206
return this;
207207
}
208208

209-
public List<SettingsPage> exportSettingsMeta() {
210-
var list = new ArrayList<SettingsPage>();
209+
/// Exports all settings definitions as a flat list.
210+
/// These can be rendered anywhere by their identifier (namespace + key).
211+
public List<SettingsDefinition> exportSettingsDefinitions() {
212+
var definitions = new ArrayList<SettingsDefinition>();
211213

212214
for (var pageDefinition : pageList) {
213-
var entries = new ArrayList<SettingsPageEntry>();
214215
for (var property : pageDefinition.properties) {
215-
var entryBuilder = SettingsPageEntry.newBuilder()
216+
var definitionBuilder = SettingsDefinition.newBuilder()
216217
.setId(property.toProtoIdentifier())
217218
.setScope(switch (property.sourceType()) {
218219
case SettingsSource.Server _ -> SettingsPageEntryScopeType.SERVER;
219220
case SettingsSource.Instance _ -> SettingsPageEntryScopeType.INSTANCE;
220221
case SettingsSource.Bot _ -> SettingsPageEntryScopeType.BOT;
221222
});
222-
entries.add(switch (property) {
223-
case BooleanProperty<?> booleanProperty -> entryBuilder.setBool(createBoolSetting(booleanProperty)).build();
224-
case IntProperty<?> intProperty -> entryBuilder.setInt(createIntSetting(intProperty)).build();
225-
case DoubleProperty<?> doubleProperty -> entryBuilder.setDouble(createDoubleSetting(doubleProperty)).build();
226-
case StringProperty<?> stringProperty -> entryBuilder.setString(createStringSetting(stringProperty)).build();
227-
case ComboProperty<?> comboProperty -> entryBuilder.setCombo(createComboSetting(comboProperty)).build();
228-
case StringListProperty<?> stringListProperty -> entryBuilder.setStringList(createStringListSetting(stringListProperty)).build();
229-
case MinMaxProperty<?> minMaxProperty -> entryBuilder.setMinMax(createMinMaxSetting(minMaxProperty)).build();
223+
definitions.add(switch (property) {
224+
case BooleanProperty<?> booleanProperty -> definitionBuilder.setBool(createBoolSetting(booleanProperty)).build();
225+
case IntProperty<?> intProperty -> definitionBuilder.setInt(createIntSetting(intProperty)).build();
226+
case DoubleProperty<?> doubleProperty -> definitionBuilder.setDouble(createDoubleSetting(doubleProperty)).build();
227+
case StringProperty<?> stringProperty -> definitionBuilder.setString(createStringSetting(stringProperty)).build();
228+
case ComboProperty<?> comboProperty -> definitionBuilder.setCombo(createComboSetting(comboProperty)).build();
229+
case StringListProperty<?> stringListProperty -> definitionBuilder.setStringList(createStringListSetting(stringListProperty)).build();
230+
case MinMaxProperty<?> minMaxProperty -> definitionBuilder.setMinMax(createMinMaxSetting(minMaxProperty)).build();
230231
});
231232
}
233+
}
234+
235+
return definitions;
236+
}
237+
238+
/// Exports page definitions that reference settings by identifier.
239+
/// Pages provide grouping and ordering of settings.
240+
public List<SettingsPage> exportSettingsPages() {
241+
var list = new ArrayList<SettingsPage>();
242+
243+
for (var pageDefinition : pageList) {
244+
var entryIdentifiers = pageDefinition.properties.stream()
245+
.map(Property::toProtoIdentifier)
246+
.toList();
232247

233248
var settingsPageBuilder = SettingsPage.newBuilder()
234249
.setId(pageDefinition.id)
235250
.setPageName(pageDefinition.pageName)
236-
.addAllEntries(entries)
251+
.addAllEntries(entryIdentifiers)
237252
.setIconId(pageDefinition.iconId);
238253

239254
if (pageDefinition.owningPlugin != null) {

proto/src/main/proto/soulfire/common.proto

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -235,32 +235,37 @@ enum SettingsPageEntryScopeType {
235235
BOT = 2;
236236
}
237237

238-
// A entry in the settings page
239-
message SettingsPageEntry {
240-
// id + namespace that this setting corresponds to
241-
SettingsEntryIdentifier id = 9;
242-
SettingsPageEntryScopeType scope = 10;
243-
oneof value {
244-
StringSetting string = 2;
245-
IntSetting int = 3;
246-
DoubleSetting double = 4;
247-
BoolSetting bool = 5;
248-
ComboSetting combo = 6;
249-
StringListSetting string_list = 7;
250-
MinMaxSetting min_max = 8;
238+
// A setting definition that can be rendered anywhere by identifier
239+
message SettingsDefinition {
240+
// Unique identifier for this setting (namespace + key)
241+
SettingsEntryIdentifier id = 1;
242+
// Which scope this setting belongs to (SERVER, INSTANCE, BOT)
243+
SettingsPageEntryScopeType scope = 2;
244+
// The actual setting type and UI configuration
245+
oneof type {
246+
StringSetting string = 3;
247+
IntSetting int = 4;
248+
DoubleSetting double = 5;
249+
BoolSetting bool = 6;
250+
ComboSetting combo = 7;
251+
StringListSetting string_list = 8;
252+
MinMaxSetting min_max = 9;
251253
}
252254
}
253255

256+
// A page definition that references settings by their identifiers
254257
message SettingsPage {
255258
// Unique page identifier (URL-safe, e.g., "bot", "account", "auto-reconnect")
256259
string id = 1;
260+
// Plugin that owns this settings page (optional for internal pages)
257261
optional string owning_plugin_id = 2;
258-
// The name of the page for these settings
262+
// The display name of the page
259263
string page_name = 3;
260-
repeated SettingsPageEntry entries = 5;
264+
// Ordered list of setting identifiers to render on this page
265+
repeated SettingsEntryIdentifier entries = 5;
261266
// https://lucide.dev icon id for this page (Usually rendered left of the page name)
262267
string icon_id = 6;
263-
// Key which makes this plugin "enabled" or "disabled"
268+
// Setting identifier that controls whether this page/plugin is enabled
264269
optional SettingsEntryIdentifier enabled_identifier = 7;
265270
}
266271

proto/src/main/proto/soulfire/instance.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ message InstanceInfoResponse {
7373
InstanceConfig config = 2;
7474
InstanceState state = 3;
7575
repeated InstancePermissionState instance_permissions = 4;
76+
// All available settings definitions that can be rendered by identifier
77+
repeated SettingsDefinition settings_definitions = 8;
78+
// Pages that group settings together (reference settings by identifier)
7679
repeated SettingsPage instance_settings = 6;
7780
repeated ServerPlugin plugins = 7;
7881
}

proto/src/main/proto/soulfire/server.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ message ServerInfoRequest {
1616

1717
message ServerInfoResponse {
1818
ServerConfig config = 1;
19+
// All available settings definitions that can be rendered by identifier
20+
repeated SettingsDefinition settings_definitions = 4;
21+
// Pages that group settings together (reference settings by identifier)
1922
repeated SettingsPage server_settings = 2;
2023
repeated ServerPlugin plugins = 3;
2124
}

0 commit comments

Comments
 (0)