Skip to content

Commit eba96a9

Browse files
ezpz
1 parent c203291 commit eba96a9

14 files changed

Lines changed: 222 additions & 104 deletions

lib/components/inspector/scoreboard_inspector.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,19 @@ class _ScoreboardInspectorState extends State<ScoreboardInspector> {
222222
Widget _selection(GlossScoreboardDoc doc) => InspectorSection(
223223
title: 'Selection',
224224
children: <Widget>[
225+
HuiSwitchRow(
226+
label: 'Hide score numbers',
227+
help:
228+
'Removes the red numeric column for 1.20.3+ clients. Older '
229+
'server versions need ViaVersion\'s hide-scoreboard-numbers '
230+
'fallback for newer clients.',
231+
trailing: const HuiFieldHelp('scoreboard.hideNumbers'),
232+
value: doc.hideNumbers,
233+
onChanged: (bool value) => _store.mutateScoreboard(
234+
'scoreboard number visibility',
235+
(GlossScoreboardDoc edited) => edited.hideNumbers = value,
236+
),
237+
),
225238
HuiSwitchRow(
226239
label: 'Primary board',
227240
help:

lib/components/panels/editor_rail.dart

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -389,12 +389,6 @@ class _EditorRailState extends State<EditorRail> {
389389
},
390390
<Widget>[ArcaneIcon.triangleAlert(size: IconSize.sm)],
391391
),
392-
if (active)
393-
const dom.span(
394-
classes: 'hui-library-active-dot',
395-
attributes: <String, String>{'aria-label': 'Open'},
396-
<Widget>[],
397-
),
398392
],
399393
),
400394
_rowMenuButton(

lib/components/scoreboard/scoreboard_view.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,10 @@ class _ScoreboardViewState extends State<ScoreboardView> {
154154
),
155155
),
156156
]),
157-
dom.span(classes: 'hui-scoreboard-score', <Widget>[
158-
Text('${glossBoardScoreForRow(index)}'),
159-
]),
157+
if (!doc.hideNumbers)
158+
dom.span(classes: 'hui-scoreboard-score', <Widget>[
159+
Text('${glossBoardScoreForRow(index)}'),
160+
]),
160161
]),
161162
]),
162163
),
@@ -180,6 +181,7 @@ class _ScoreboardViewState extends State<ScoreboardView> {
180181
String _readout(GlossScoreboardDoc doc, int clipped, bool titleTruncated) {
181182
final List<String> parts = <String>[
182183
doc.primary ? 'primary' : 'not primary',
184+
doc.hideNumbers ? 'score numbers hidden' : 'score numbers visible',
183185
doc.permissionGated
184186
? 'needs $glossBoardPermissionNodePrefix${doc.effectivePermission}'
185187
: 'everyone',

lib/config/field_docs.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,15 @@ const Map<String, HuiFieldDoc> huiFieldDocs = <String, HuiFieldDoc>{
527527
'sidebar on anyone.',
528528
citation: 'GlossBoardMeta.java:83-85',
529529
),
530+
'scoreboard.hideNumbers': HuiFieldDoc(
531+
title: 'Hide score numbers',
532+
body:
533+
'Uses Minecraft\'s blank score number format so 1.20.3+ clients do '
534+
'not draw the red 15..1 column. On an older server, ViaVersion\'s '
535+
'global hide-scoreboard-numbers option provides the equivalent '
536+
'translation for 1.20.3+ clients.',
537+
citation: 'BoardDoc.java:10-11',
538+
),
530539
'scoreboard.permission': HuiFieldDoc(
531540
title: 'Permission',
532541
body:

lib/config/gloss_templates.dart

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ const String kGlossHologramBaselineJson = r'''
2626
''';
2727

2828
/// A richer starter showing the text pipeline: bracket hex, legacy codes,
29-
/// a PAPI placeholder and an animation reference.
29+
/// native player/server values, an optional PAPI expansion and an animation
30+
/// reference.
3031
const String kGlossHologramShowcaseJson = r'''
3132
{
3233
"schemaVersion": 1,
@@ -37,10 +38,11 @@ const String kGlossHologramShowcaseJson = r'''
3738
},
3839
"lines": [
3940
"{{ hex(mix(#FF55FF, #55FFFF, (sin(time.seconds * 2) + 1) / 2)) }}&lWelcome",
40-
"&7Hello, &f{{ papi('player_name') }}&7!",
41-
"&7Health &a{{ bar(papiNumber('player_health'), 20, 10, '■', '□') }}",
41+
"&7Hello, &f{{ player.name }}&7!",
42+
"&7Health &a{{ bar(player.health, 20, 10, '■', '□') }}",
43+
"&7Rank {{ papi('vault_prefix', '&7Member') }}",
4244
"|animation.rainbow|&lLive colour prefix",
43-
"&7TPS &a{{ fixed(metric('react.tps'), 1) }}"
45+
"&7TPS &a{{ fixed(server.tps, 1) }}"
4446
]
4547
}
4648
''';
@@ -116,19 +118,22 @@ const String kGlossScoreboardShowcaseJson = r'''
116118
"revision": 1,
117119
"title": "[FF55FF]&lMY SERVER",
118120
"lines": [
119-
"&7Player &f{{ papi('player_name') }}",
120-
"&7Rank: &6VIP",
121+
"&7Player &f{{ player.name }}",
122+
"&7Rank {{ papi('vault_prefix', '&6VIP') }}",
121123
"",
122-
"&7Ping {{ papiNumber('player_ping') < 100 ? '&a' : '&e' }}{{ papi('player_ping') }}ms",
123-
"&7Health &a{{ bar(papiNumber('player_health'), 20, 8, '■', '□') }}",
124-
"&7Online &a{{ papi('server_online') }}&8/&a{{ papi('server_max_players') }}",
125-
"&7TPS &a{{ fixed(metric('react.tps'), 1) }}",
124+
"&7Ping {{ player.ping < 100 ? '&a' : '&e' }}{{ player.ping }}ms",
125+
"&7Health &a{{ bar(player.health, 20, 8, '■', '□') }}",
126+
"&7Online &a{{ server.online }}&8/&a{{ server.maxPlayers }}",
127+
"&7TPS &a{{ fixed(server.tps, 1) }}",
128+
"&7Tick &f{{ fixed(metric('react.tick-ms', 1000 / server.tps), 1) }}ms",
129+
"&7Balance &6${{ fixed(papiNumber('vault_eco_balance', 0), 2) }}",
126130
"",
127131
"|animation.rainbow|&lLIVE EVENT",
128132
"&d:heart: &fWelcome!",
129133
"[55FFFF]play.example.net"
130134
],
131135
"primary": true,
136+
"hideNumbers": true,
132137
"permission": "vip",
133138
"groups": ["vip", "mvp"]
134139
}
@@ -300,8 +305,8 @@ const String kGlossTablistShowcaseJson = r'''
300305
"schemaVersion": 1,
301306
"revision": 1,
302307
"useHeaderFooter": true,
303-
"header": "{{ hex(mix(#FF55FF, #55FFFF, (sin(time.seconds * 2) + 1) / 2)) }}&lMy Server\n&7Welcome &f{{ papi('player_name') }} &8• &a{{ papi('player_ping') }}ms",
304-
"footer": "|animation.rainbow|&lONLINE &8• &7TPS &a{{ fixed(metric('react.tps'), 1) }}",
308+
"header": "{{ hex(mix(#FF55FF, #55FFFF, (sin(time.seconds * 2) + 1) / 2)) }}&lMy Server\n&7Welcome &f{{ player.name }} &8• {{ papi('vault_prefix', '&7Member') }} &8• &a{{ player.ping }}ms",
309+
"footer": "|animation.rainbow|&lONLINE &8• &7TPS &a{{ fixed(server.tps, 1) }}",
305310
"groupListNames": true,
306311
"nameFormats": {
307312
"default": "&7$player",

lib/logic/gloss_text.dart

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -437,10 +437,12 @@ final class GlossTextExpressionSamples {
437437
const GlossTextExpressionSamples({
438438
this.placeholders = _defaultExpressionPlaceholders,
439439
this.metrics = _defaultExpressionMetrics,
440+
this.serverTps = 19.8,
440441
});
441442

442443
final Map<String, Object> placeholders;
443444
final Map<String, double> metrics;
445+
final double serverTps;
444446
}
445447

446448
const Map<String, Object> _defaultExpressionPlaceholders = <String, Object>{
@@ -528,6 +530,8 @@ final class _GlossTextExpressionScope extends PExprScope {
528530
return _sampleNumber('server_online');
529531
case 'server.maxPlayers':
530532
return _sampleNumber('server_max_players');
533+
case 'server.tps':
534+
return samples.serverTps;
531535
default:
532536
return samples.metrics[dottedName];
533537
}
@@ -541,33 +545,64 @@ final class _GlossTextExpressionScope extends PExprScope {
541545
case 'papiNumber':
542546
return _papi(args, true);
543547
case 'metric':
544-
if (args.length != 1 || args.single is! String) {
548+
if ((args.length != 1 && args.length != 2) || args.first is! String) {
545549
throw const PExprException(
546-
'metric expects one string',
550+
'metric expects a key and optional numeric fallback',
547551
previewNoPosition,
548552
);
549553
}
550-
return samples.metrics[args.single as String];
554+
final Object? fallback = args.length == 2 ? args[1] : null;
555+
if (fallback != null && fallback is! double) {
556+
throw const PExprException(
557+
'metric fallback must be a number',
558+
previewNoPosition,
559+
);
560+
}
561+
final double? value = samples.metrics[args.first as String];
562+
if (value != null) return value;
563+
if (fallback != null) return fallback;
564+
throw PExprException(
565+
'unknown metric: ${args.first as String}',
566+
previewNoPosition,
567+
);
551568
default:
552569
return previewStdFunction(name, args);
553570
}
554571
}
555572

556573
Object _papi(List<Object?> args, bool numeric) {
557-
if (args.length != 1 || args.single is! String) {
574+
if ((args.length != 1 && args.length != 2) || args.first is! String) {
558575
throw PExprException(
559-
'${numeric ? 'papiNumber' : 'papi'} expects one string',
576+
'${numeric ? 'papiNumber' : 'papi'} expects a key and optional fallback',
560577
previewNoPosition,
561578
);
562579
}
563-
final String raw = args.single as String;
580+
final Object? fallback = args.length == 2 ? args[1] : null;
581+
if (numeric && fallback != null && fallback is! double) {
582+
throw const PExprException(
583+
'papiNumber fallback must be a number',
584+
previewNoPosition,
585+
);
586+
}
587+
if (!numeric && fallback != null && fallback is! String) {
588+
throw const PExprException(
589+
'papi fallback must be a string',
590+
previewNoPosition,
591+
);
592+
}
593+
final String raw = args.first as String;
564594
final String key = raw.startsWith('%') && raw.endsWith('%')
565595
? raw.substring(1, raw.length - 1)
566596
: raw;
567597
final Object? value = samples.placeholders[key];
568-
if (value == null) return '%$key%';
598+
if (value == null) return fallback ?? '%$key%';
569599
if (!numeric) return previewStringify(value);
570-
return previewStdFunction('number', <Object?>[value])!;
600+
try {
601+
return previewStdFunction('number', <Object?>[value])!;
602+
} on PExprException {
603+
if (fallback != null) return fallback;
604+
rethrow;
605+
}
571606
}
572607

573608
double? _sampleNumber(String key) {

lib/model/gloss_scoreboard.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
/// "title": "&d&lGloss",
99
/// "lines": ["&fWelcome!"],
1010
/// "primary": false,
11+
/// "hideNumbers": false,
1112
/// "permission": "default",
1213
/// "groups": []
1314
/// }
@@ -81,6 +82,7 @@ const Set<String> _docKnown = <String>{
8182
'title',
8283
'lines',
8384
'primary',
85+
'hideNumbers',
8486
'permission',
8587
'groups',
8688
};
@@ -92,6 +94,7 @@ final class GlossScoreboardDoc extends GlossDoc {
9294
this.title = '',
9395
List<String>? lines,
9496
this.primary = false,
97+
this.hideNumbers = false,
9598
this.permission = glossBoardUnrestrictedPermission,
9699
List<String>? groups,
97100
Map<String, dynamic>? extras,
@@ -111,6 +114,9 @@ final class GlossScoreboardDoc extends GlossDoc {
111114
/// group or permission steers elsewhere.
112115
bool primary;
113116

117+
/// Uses Minecraft's blank score number format on 1.20.3+ clients.
118+
bool hideNumbers;
119+
114120
/// As written; [effectivePermission] is what the server runs.
115121
String permission;
116122

@@ -153,6 +159,7 @@ final class GlossScoreboardDoc extends GlossDoc {
153159
title: huiReadString(map, 'title'),
154160
lines: glossReadStringList(map['lines']),
155161
primary: huiReadBool(map, 'primary'),
162+
hideNumbers: huiReadBool(map, 'hideNumbers'),
156163
permission: huiReadString(map, 'permission'),
157164
groups: glossReadStringList(map['groups']),
158165
extras: huiCollectExtras(map, _docKnown),
@@ -161,6 +168,7 @@ final class GlossScoreboardDoc extends GlossDoc {
161168
if (map['title'] == null) 'title',
162169
if (map['lines'] == null) 'lines',
163170
if (map['primary'] == null) 'primary',
171+
if (map['hideNumbers'] == null) 'hideNumbers',
164172
if (map['permission'] == null) 'permission',
165173
if (map['groups'] == null) 'groups',
166174
},
@@ -176,6 +184,8 @@ final class GlossScoreboardDoc extends GlossDoc {
176184
if (!absentKeys.contains('lines') || lines.isNotEmpty)
177185
'lines': List<String>.of(lines),
178186
if (!absentKeys.contains('primary') || primary) 'primary': primary,
187+
if (!absentKeys.contains('hideNumbers') || hideNumbers)
188+
'hideNumbers': hideNumbers,
179189
if (!absentKeys.contains('permission') || permission.isNotEmpty)
180190
'permission': permission,
181191
if (!absentKeys.contains('groups') || groups.isNotEmpty)
@@ -190,6 +200,7 @@ final class GlossScoreboardDoc extends GlossDoc {
190200
title: title,
191201
lines: List<String>.of(lines),
192202
primary: primary,
203+
hideNumbers: hideNumbers,
193204
permission: permission,
194205
groups: List<String>.of(groups),
195206
extras: huiDeepCopyMap(extras),

lib/services/showcase_randomizer.dart

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -383,10 +383,10 @@ GlossHologramDoc buildRandomHologramShowcase(
383383
final String event = _pick(random, _events);
384384
final List<String> lines = <String>[
385385
'${_animatedColor(random)}&l$server',
386-
"&7Hello, &f{{ papi('player_name') }}&7!",
387-
"&7Health {{ papiNumber('player_health') < 7 ? '&c' : '&a' }}"
388-
"{{ bar(papiNumber('player_health'), 20, 10, '■', '□') }}",
389-
"&7TPS &a{{ fixed(metric('react.tps'), 1) }} &8• &7$event",
386+
'&7Hello, &f{{ player.name }}&7!',
387+
"&7Health {{ player.health < 7 ? '&c' : '&a' }}"
388+
"{{ bar(player.health, 20, 10, '■', '□') }}",
389+
"&7TPS &a{{ fixed(server.tps, 1) }} &8• &7$event",
390390
'&d:heart: &7${_pick(random, _hologramNotes)}',
391391
_easterEgg(random),
392392
];
@@ -442,31 +442,35 @@ GlossScoreboardDoc buildRandomScoreboardShowcase(
442442
final String rank = _pick(random, _ranks);
443443
final String event = _pick(random, _events);
444444
final List<String> lines = <String>[
445-
"&7Player &f{{ papi('player_name') }}",
446-
'&7Rank &6$rank',
445+
'&7Player &f{{ player.name }}',
446+
"&7Rank {{ papi('vault_prefix', '&6$rank') }}",
447447
'',
448-
"&7Ping {{ papiNumber('player_ping') < 80 ? '&a' : "
449-
"papiNumber('player_ping') < 160 ? '&e' : '&c' }}"
450-
"{{ papi('player_ping') }}ms",
451-
"&7Health &a{{ bar(papiNumber('player_health'), 20, 8, '■', '□') }}",
452-
"&7Online &a{{ papi('server_online') }}&8/&a"
453-
"{{ papi('server_max_players') }}",
454-
"&7TPS &a{{ fixed(metric('react.tps'), 1) }}",
448+
"&7Ping {{ player.ping < 80 ? '&a' : "
449+
"player.ping < 160 ? '&e' : '&c' }}{{ player.ping }}ms",
450+
"&7Health &a{{ bar(player.health, 20, 8, '■', '□') }}",
451+
'&7Online &a{{ server.online }}&8/&a{{ server.maxPlayers }}',
452+
'&7TPS &a{{ fixed(server.tps, 1) }}',
453+
"&7Tick &f{{ fixed(metric('react.tick-ms', 1000 / server.tps), 1) }}ms",
455454
'',
456455
'${_animatedColor(random)}&l${_pick(random, _statusWords)}',
457456
'&7$event',
458457
_easterEgg(random),
459458
'&8${_pick(random, _domains)}',
460459
];
461460
if (random.nextBool()) {
462-
lines.insert(7, r'&7Balance &6%vault_eco_balance_formatted%');
461+
lines.insert(
462+
8,
463+
"&7Balance &6\$"
464+
"{{ fixed(papiNumber('vault_eco_balance', 0), 2) }}",
465+
);
463466
}
464467
return GlossScoreboardDoc(
465468
schemaVersion: current.schemaVersion,
466469
revision: current.revision,
467470
title: '${_pick(random, _legacyPalette)}&l${server.toUpperCase()}',
468471
lines: lines,
469472
primary: random.nextBool(),
473+
hideNumbers: true,
470474
permission: random.nextBool() ? 'default' : 'vip',
471475
groups: random.nextBool() ? <String>['vip', 'mvp'] : <String>[],
472476
);
@@ -556,12 +560,11 @@ GlossTablistDoc buildRandomTablistShowcase(
556560
useHeaderFooter: true,
557561
header:
558562
'${_animatedColor(random)}&l$server\n'
559-
"&7Welcome &f{{ papi('player_name') }} &8• "
560-
"{{ papiNumber('player_ping') < 100 ? '&a' : '&e' }}"
561-
"{{ papi('player_ping') }}ms\n"
562-
"&7Online &a{{ papi('server_online') }}&8/&a"
563-
"{{ papi('server_max_players') }} &8• &7TPS &a"
564-
"{{ fixed(metric('react.tps'), 1) }}",
563+
"&7Welcome &f{{ player.name }} &8• "
564+
"{{ papi('vault_prefix', '&7Member') }} &8• "
565+
"{{ player.ping < 100 ? '&a' : '&e' }}{{ player.ping }}ms\n"
566+
'&7Online &a{{ server.online }}&8/&a{{ server.maxPlayers }} '
567+
'&8• &7TPS &a{{ fixed(server.tps, 1) }}',
565568
footer:
566569
'$easterEgg\n'
567570
"{{ select(['&d✦', '&b✧', '&6✦'], floor(time.seconds * 2)) }} "

test/field_docs_test.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const List<String> _contractKeys = <String>[
6363
'scoreboard.title',
6464
'scoreboard.lines',
6565
'scoreboard.primary',
66+
'scoreboard.hideNumbers',
6667
'scoreboard.permission',
6768
'scoreboard.groups',
6869
];

0 commit comments

Comments
 (0)