@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
98 lines (73 loc) • 3.06 kB
JavaScript
/**
*
* @param {Function} Klass
* @param {Map<Function, string>} classNames
* @param {number} [max_depth] prevents infinite recursion
* @returns {*}
*/
function resolve_class_name(Klass, classNames, max_depth = 64) {
let typeName = classNames.get(Klass);
if (typeName === undefined && Klass.hasOwnProperty("typeName")) {
// try fallback to "typeName" field on constructor
typeName = Klass.typeName;
}
if (typeName === undefined && max_depth > 0) {
// try walking up prototype chain
const proto = Object.getPrototypeOf(Klass);
if (proto !== null && typeof proto === "object") {
return resolve_class_name(proto, classNames, max_depth - 1);
}
}
return typeName;
}
/**
* @template T
* @param {T} source
* @param {Map<string, function(v:Object):Object>} serializers
* @param {Map<*,string>} classNames
* @param {boolean} [constructorNameFallback]
*/
export function abstractJSONSerializer(source, serializers, classNames, constructorNameFallback = true) {
if (source === null || typeof source !== "object") {
// primitive type
return source;
}
// parameter is an object
// extract type name
const Klass = source.constructor;
let typeName = resolve_class_name(Klass, classNames);
if (typeName === undefined && constructorNameFallback) {
typeName = Klass.name;
console.warn(`No type name could be resolved, using constructor name '${typeName}' as a fallback.`);
const matching_class_entries = [];
classNames.forEach((value, key) => {
if (value === typeName) {
matching_class_entries.push(key);
}
});
if (matching_class_entries.length > 0) {
console.error(
`Found a class that resolved to name ${typeName}. className registry might be corrupted, possible reason is multiple versions of the same class being imported.`,
"offending object:", source,
"matching class name entries:", matching_class_entries,
"Note that constructor of offending object does not match any of these classes (!==)"
);
}
}
if (typeName === undefined) {
throw new Error(`Failed to resolve type name. No name found in classNames registry and no .typeName field on the constructor {name: '${Klass.name}}'`);
}
let serializer = serializers.get(typeName);
let serializerContext = undefined;
if (serializer === undefined) {
serializer = source.toJSON;
serializerContext = source;
}
if (serializer === undefined) {
throw new Error(`Failed to resolve serializer for object type '${typeName}'. No serializer found in serializers registry and no .toJSON method on object itself`);
}
return {
type: typeName,
data: serializer.call(serializerContext, source)
};
}