registerFlutter3dExtension function
Registers name, an ext.flutter3d.* extension, to be answered by
handler, and lists it in ext.flutter3d.version.
answers are the keys of the JSON object handler answers with, the
optional ones included: what api/<package>.vm snapshots as the
extension's -> line, so a key dropped from an answer is a break the
structure check names. Nothing reads it at run time; it is declared here,
beside the handler, because an answer built by a toJson() or a helper
cannot be read off the source.
aliases are old names of an extension that was renamed. Each is
registered too, answers exactly as name does, and is listed under
aliases in ext.flutter3d.version, until the next major removes it
(CONTRIBUTING.md, "Tools for agents are a contract too": a rename keeps
the old name as an alias). An alias follows name on and off.
The one door for the engine's extensions. It is
dart:developer's registerExtension with two things added: the name is
held to the ext.flutter3d. prefix, and the first call registers
vmVersionExtension, so a tool attached to a running game asks one
extension what the others are and which schema they speak.
The engine's alone. Exported from
package:flutter3d_plugin_api/internal.dart, not from the public
library: it takes any name under ext.flutter3d., the areas a minor
adds to included, and the VM service cannot forget a name. A plugin and
an application register through VmExtensions, which keeps them out of
the engine's areas.
Registering a name twice, or an alias another extension has, throws a NameTakenException. Returns the Registration that switches it off: the VM service cannot forget a name, so a switched-off extension answers an error saying so until it is registered again.
Implementation
Registration registerFlutter3dExtension(
String name,
VmExtensionHandler handler, {
Set<String> answers = const <String>{},
List<String> aliases = const <String>[],
}) {
_checkName(name, 'name');
if (_registered[name] != null) {
throw NameTakenException(name, what: 'the VM service extension');
}
for (final alias in aliases) {
_checkName(alias, 'aliases');
final held = _aliases[alias];
if (alias == name ||
_registered.containsKey(alias) ||
(held != null && held != name)) {
throw NameTakenException(
alias,
what: 'the VM service extension alias',
detail: 'it is an extension\'s name or another extension\'s alias',
);
}
}
if (_registered.isEmpty) {
// Spelled out rather than [vmVersionExtension], so the snapshot of this
// package lists it.
developer.registerExtension(
'ext.flutter3d.version',
(String method, Map<String, String> parameters) async =>
developer.ServiceExtensionResponse.result(
jsonEncode(<String, Object?>{
'schemaVersion': vmSchemaVersion,
'extensions': <String>[
for (final MapEntry(:key, :value) in _registered.entries)
if (value != null) key,
]..sort(),
'aliases': <String, String>{
for (final old in _aliases.keys.toList()..sort())
if (_registered[_aliases[old]] != null) old: _aliases[old]!,
},
}),
),
);
}
final known = _registered.containsKey(name);
_registered[name] = handler;
if (!known) developer.registerExtension(name, _dispatchTo(name));
for (final alias in aliases) {
if (_aliases.containsKey(alias)) continue;
_aliases[alias] = name;
developer.registerExtension(alias, _dispatchTo(name));
}
return Registration(() {
if (identical(_registered[name], handler)) _registered[name] = null;
});
}