Files
MingSimulator/Plugins/Puerts/PuertsEditor/UEMeta.ts
T

1934 lines
69 KiB
TypeScript

import * as UE from 'ue'
import * as ts from "typescript";
/**
* @brief the specifier read from the decorator
*/
class MetaSpecifier
{
/**
* the identity of the specifier
*/
readonly Specifier: string = "";
/**
* the value
*/
Values?: Array<string>;
/**
* the constructor
* @param specifier
* @param values
* @returns
*/
constructor(specifier: string, values?: Array<string>)
{
this.Specifier = specifier;
this.Values = values;
}
/**
* apply the specifier to the meta data, return if the specifier is consumed
* null indicate the specifier is invalid in call context
* this function should be called when parse the meta data defined via umeta
* @param metaData
*/
ApplyInMeta(metaData: Map<string, string>): boolean | null
{
// for specifier used in meta, only meta key and meta key value is valid
if (this.Specifier == '' || (this.Values != null && this.Values.length != 1))
{
return null;
}
// the meta data set via umeta, we only treat it as key value pair
metaData.set(this.Specifier, this.Values == null ? '' : this.Values[0]);
return true;
}
/**
* apply the specifier to the meta data, return if the specifier is consumed
* this function should be called when parse the meta data defined via prefix e.g uclass/ufunction
* @param metaData
*/
ApplyInIdentity(metaData: Map<string, string>): boolean | null
{
if (this.Specifier == '')
{
return null;
}
if (!MetaSpecifier.CommonMetaData.has(this.Specifier))
{// unknown specifier, need context to parse, don't do here
return false;
}
if (!MetaSpecifier.CommonMetaData.get(this.Specifier).call(null, this, metaData))
{// we know the specifier is invalid for specific key
return null;
}
return true;
}
/**
* check if the specifier is meta key
*/
IsMetaKey(): boolean
{
return this.Values == null;
}
/**
* check if the specifier is meta key value
* @returns
*/
IsMetaKeyValue(): boolean
{
return this.Values != null && this.Values.length == 1;
}
/**
* check if the specifier is meta key values
* @returns
*/
IsMetaKeyValues(): boolean
{
return this.Values != null;
}
/**
* the common meta data, the behavior is sync with unreal engine 5.0 early preview
*/
private static readonly CommonMetaData: Map<string, (specifier: MetaSpecifier, metaData: Map<string, string>)=>boolean> = new Map([
["DisplayName", (specifier: MetaSpecifier, metaData: Map<string, string>)=>{
if (specifier.IsMetaKeyValue())
{
metaData.set("DisplayName", specifier.Values[0]);
return true;
}
return false;
}],
["FriendlyName", (specifier: MetaSpecifier, metaData: Map<string, string>)=>{
if (specifier.IsMetaKeyValue())
{
metaData.set("FriendlyName", specifier.Values[0]);
return true;
}
return false;
}],
["BlueprintInternalUseOnly", (specifier: MetaSpecifier, metaData: Map<string, string>)=>{
if (specifier.IsMetaKey())
{
metaData.set("BlueprintInternalUseOnly", 'true');
metaData.set("BlueprintType", 'true');
return true;
}
return false;
}],
["BlueprintType", (specifier: MetaSpecifier, metaData: Map<string, string>)=>{
if (specifier.IsMetaKey())
{
metaData.set("BlueprintType", 'true');
return true;
}
return false;
}],
["NotBlueprintType", (specifier: MetaSpecifier, metaData: Map<string, string>)=>{
if (specifier.IsMetaKey())
{
metaData.set("NotBlueprintType", 'true');
metaData.delete('BlueprintType');
return true;
}
return false;
}],
["Blueprintable", (specifier: MetaSpecifier, metaData: Map<string, string>)=>{
if (specifier.IsMetaKey())
{
metaData.set("IsBlueprintBase", 'true');
metaData.set("BlueprintType", 'true');
return true;
}
return false;
}],
["CallInEditor", (specifier: MetaSpecifier, metaData: Map<string, string>)=>{
if (specifier.IsMetaKey())
{
metaData.set("CallInEditor", 'true');
return true;
}
return false;
}],
["NotBlueprintable", (specifier: MetaSpecifier, metaData: Map<string, string>)=>{
if (specifier.IsMetaKey())
{
metaData.set("IsBlueprintBase", 'false');
metaData.delete("BlueprintType");
return true;
}
return false;
}],
["Category", (specifier: MetaSpecifier, metaData: Map<string, string>)=>{
if (specifier.IsMetaKeyValue())
{
metaData.set("Category", specifier.Values[0]);
return true;
}
return false;
}],
["Experimental", (specifier: MetaSpecifier, metaData: Map<string, string>)=>{
if (specifier.IsMetaKey())
{
metaData.set("DevelopmentStatus", "Experimental");
return true;
}
return false;
}],
["EarlyAccessPreview", (specifier: MetaSpecifier, metaData: Map<string, string>)=>{
if (specifier.IsMetaKey())
{
metaData.set("DevelopmentStatus", "EarlyAccessPreview");
return true;
}
return false;
}],
["DocumentationPolicy", (specifier: MetaSpecifier, metaData: Map<string, string>)=>{
if (specifier.IsMetaKey())
{
metaData.set("DocumentationPolicy", 'Strict');
return true;
}
return false;
}],
["SparseClassDataType", (specifier: MetaSpecifier, metaData: Map<string, string>)=>{
if (specifier.IsMetaKeyValue())
{
metaData.set("SparseClassDataType", specifier.Values[0]);
return true;
}
return false;
}]
]);
};
/**
* a helper function used to extract the meta key from an expression
* @param expression
* @param prefix
* @param regExp
* @returns
*/
function extractMetaSpecifierFromExpression(expression: ts.Expression, prefix: string, regExp?: RegExp) : MetaSpecifier | null
{
const execRegExp = regExp == null ? new RegExp(`^${prefix}\.([A-Za-z]+)$`) : regExp;
const execResult = execRegExp.exec(expression.getText().trim());
if (execResult == null)
{// should capture the result
return null;
}
return new MetaSpecifier(execResult[1]);
}
/**
* a helper function used to extract the meta key value from an expression
* @param expression
* @param prefix
* @param regExp
* @returns
*/
function extractMetaSpecifierFromBinaryExpression(expression: ts.BinaryExpression, prefix: string, regExp?: RegExp) : MetaSpecifier | null
{
const execRegExp = regExp == null ? new RegExp(`^${prefix}\.([A-Za-z]+)$`) : regExp;
const execResult = execRegExp.exec(expression.left.getText().trim());
if (execResult == null)
{
return null;
}
let values = new Array<string>();
if (ts.isStringLiteral(expression.right))
{// specifier = value
values.push(expression.right.text);
}
else if (ts.isArrayLiteralExpression(expression.right))
{// specifier = [value1, value2, value3]
let bValid: boolean = true;
expression.right.elements.forEach((value)=>{
if (!bValid)
{
return;
}
if (!ts.isStringLiteral(value))
{
bValid = false;
return;
}
values.push(value.text);
});
if (!bValid)
{
return null;
}
}
else
{// invalid format
return null;
}
return new MetaSpecifier(execResult[1], values);
}
/**
* collect the meta data from the prefix section, @see ObjectMacros.h namespace uc,
* for meta data defined in umeta section, all kinds of meta data are valid
* @param expressions
* @param prefix
* @param specifiers
* @param metaData
* @param keyRegExp
* @param keyValueRegExp
*/
function collectMetaDataFromIdentifyDecorator(expressions: ts.NodeArray<ts.Expression>, prefix: string, specifiers: Array<MetaSpecifier>, metaData: Map<string, string>, keyRegExp?: RegExp, keyValueRegExp?: RegExp): void
{
const MetaKeyValueRegExp = keyValueRegExp == null ? new RegExp(`^${prefix}\.([A-Za-z]+)$`) : keyValueRegExp;
const MetaKeyRegExp = keyRegExp == null ? new RegExp(`^${prefix}\.([A-Za-z]+)$`) : keyRegExp;
expressions.forEach((value)=>
{
let metaSpecifier: MetaSpecifier;
if (ts.isBinaryExpression(value))
{// should be the meta key value or , ${prefix}.identifier = (value);
metaSpecifier = extractMetaSpecifierFromBinaryExpression(value, prefix, MetaKeyValueRegExp);
}
else
{// should be the meta key
metaSpecifier = extractMetaSpecifierFromExpression(value, prefix, MetaKeyRegExp);
}
if (metaSpecifier == null)
{
console.warn(`the ${prefix}: ${value.getFullText()} is not valid meta data`);
return;
}
const applyResult= metaSpecifier.ApplyInIdentity(metaData);
if (applyResult == null)
{
console.warn(`the ${prefix}: ${value.getFullText()} is not valid meta data`);
}
else if (applyResult == false)
{// unknown specifier currently
specifiers.push(metaSpecifier);
}
});
}
/**
* collect the meta data from the umeta section, @see ObjectMacros.h namespace um,
* for meta data defined in umeta section, only key or key value is legal
* @param expressions
* @param prefix
* @param specifiers
* @param metaData
* @param keyRegExp
* @param keyValueRegExp
*/
function collectMetaDataFromMetaDecorator(expressions: ts.NodeArray<ts.Expression>, prefix: string, specifiers: Array<MetaSpecifier>, metaData: Map<string, string>, keyRegExp?: RegExp, keyValueRegExp?: RegExp): void
{
const MetaKeyValueRegExp = keyValueRegExp == null ? new RegExp(`^${prefix}\.([A-Za-z]+)$`) : keyValueRegExp;
const MetaKeyRegExp = keyRegExp == null ? new RegExp(`^${prefix}\.([A-Za-z]+)$`) : keyRegExp;
expressions.forEach((value)=>{
let metaSpecifier: MetaSpecifier;
if (ts.isBinaryExpression(value))
{// should be the meta key value or , ${prefix}.identifier.assign(value);
metaSpecifier = extractMetaSpecifierFromBinaryExpression(value, prefix, MetaKeyValueRegExp);
}
else
{// should be the meta key
metaSpecifier = extractMetaSpecifierFromExpression(value, prefix, MetaKeyRegExp);
}
if (metaSpecifier == null)
{
console.warn(`the umeta: ${value.getFullText()} is not valid meta data`);
return;
}
const applyResult= metaSpecifier.ApplyInMeta(metaData);
if (applyResult == null)
{
console.warn(`the umeta: ${value.getFullText()} is not valid meta data`);
}
else if (applyResult == false)
{// unknown specifier currently, this should never happen
console.warn(`logic error: umeta data should never be unrecognized`);
}
});
}
/**
* collect the meta data from a specific decorator, the format of decorator @see getMetaDataFromDecorators
* @param decorator
* @param prefix
* @param specifiers
* @param metaData
*/
function collectMetaDataFromDecorator(decorator: ts.Decorator, prefix: string, specifiers: Array<MetaSpecifier>, metaData: Map<string, string>): void
{
let expression = decorator.expression;
if (!ts.isCallExpression(expression))
{
return;
}
const expressionText = expression.expression.getFullText(); // get the callable signature
// should use cache to hold the reg exp object ?
if (new RegExp(`^${prefix}\.${prefix}$`).test(expressionText))
{// the decorator match @prefix.prefix
collectMetaDataFromIdentifyDecorator(expression.arguments, prefix, specifiers, metaData);
}
else if (new RegExp(`^${prefix}\.umeta$`).test(expressionText))
{// the decorator match @prefix.umeta
collectMetaDataFromMetaDecorator(expression.arguments, prefix, specifiers, metaData);
}
}
/**
* extract meta data from specific decorators, the format of the decorators follows:
* 1. @${prefix}.${prefix}(meta1, ...), e.g, @uclass.uclass(meta1, meta2, meta3...)
* 2. @${prefix}.umeta(meta1, ...), e.g, @uclass.umeta(meta1, meta2, meta3)
* the meta data has there formats, the values used in meta data should be string literals
* 1. ${prefix}.{identifier}, e.g, @uclass.editinlinenew, this is a meta data key, put its name in array result
* 2. ${prefix}.{identifier}.assign({value}) e.g, @uclass.DisplayName.assign("name") this is a meta data key value, put its key and value in map result
* 3. ${prefix}.{Identifier}.assign{{value1}, ... } e.g, @uclass.hideCategories.assign("a", 'b') this is a meta data key values, the values will pack into a string
* @param decorators
* @param prefix
*/
function getMetaDataFromDecorators(decorators:ts.NodeArray<ts.Decorator> | null, prefix: string): [Array<MetaSpecifier>, Map<string, string>]
{
let specifiers = new Array<MetaSpecifier>();
let metaData = new Map<string, string>();
if (decorators == null)
{
return [specifiers, metaData];
}
decorators.forEach((value)=>
{
collectMetaDataFromDecorator(value, prefix, specifiers, metaData);
});
return [specifiers, metaData];
}
/**
* process the specifiers specific to the class
* @param specifiers
* @param metaData
*/
function processClassMetaData(specifiers: Array<MetaSpecifier>, metaData: Map<string, string>): UE.PEClassMetaData | null
{
let bValidSpecifiers: boolean = true;
let InvalidSpecifier: string;
let bWantsPlacable: boolean = false;
let ClassFlags = UE.ClassFlags.CLASS_None;
let WithIn: string = "";
let ConfigName: string = "";
let ShowCategories: Set<string> = new Set<string>();
let HideCategories: Set<string> = new Set<string>();
let ShowSubCategories: Set<string> = new Set<string>();
let HideFunctions: Set<string> = new Set<string>();
let ShowFunctions: Set<string> = new Set<string>();
let AutoExpandCategories: Set<string> = new Set<string>();
let AutoCollapseCategories: Set<string> = new Set<string>();
let DontAutoCollapseCategories: Set<string> = new Set<string>();
let ClassGroupNames: Set<string> = new Set<string>();
let SparseClassDataTypes: Set<string> = new Set<string>();
/**
* a helper function used to mark process error information
* @param specifier
*/
function markInvalidSpecifier(specifier: string): void
{
bValidSpecifiers = false;
InvalidSpecifier = specifier;
}
/**
* parse single meta specifier
* @param value
*/
function parseClassMetaSpecifier(value: MetaSpecifier): void
{
if (!bValidSpecifiers)
{
return;
}
switch(value.Specifier.toLowerCase())
{
case 'NoExport'.toLowerCase():
if (!value.IsMetaKey())
{// should be the meta key
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags | UE.ClassFlags.CLASS_NoExport;
break;
case 'Intrinsic'.toLowerCase():
if (!value.IsMetaKey())
{// should be the meta key
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags | UE.ClassFlags.CLASS_Intrinsic;
break;
case 'ComponentWrapperClass'.toLowerCase():
if (!value.IsMetaKey())
{// should be the meta key
return markInvalidSpecifier(`${value.Specifier}`);
}
metaData.set('IgnoreCategoryKeywordsInSubclasses', 'true');
break;
case 'Within'.toLowerCase():
if (!value.IsMetaKeyValue())
{
return markInvalidSpecifier(`${value.Specifier}`);
}
WithIn = value.Values[0];
break;
case 'EditInlineNew'.toLowerCase():
if (!value.IsMetaKey())
{// should be the meta key
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags | UE.ClassFlags.CLASS_EditInlineNew;
break;
case 'NotEditInlineNew'.toLowerCase():
if (!value.IsMetaKey())
{// should be the meta key
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags & ~UE.ClassFlags.CLASS_EditInlineNew;
break;
case 'Placeable'.toLowerCase():
if (!value.IsMetaKey())
{// should be the meta key
return markInvalidSpecifier(`${value.Specifier}`);
}
bWantsPlacable = true;
ClassFlags = ClassFlags & ~UE.ClassFlags.CLASS_NotPlaceable;
break;
case 'DefaultToInstanced'.toLowerCase():
if (!value.IsMetaKey())
{// should be the meta key
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags | UE.ClassFlags.CLASS_DefaultToInstanced;
break;
case 'NotPlaceable'.toLowerCase():
if (!value.IsMetaKey())
{// should be the meta key
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags | UE.ClassFlags.CLASS_NotPlaceable;
break;
case 'HideDropdown'.toLowerCase():
if (!value.IsMetaKey())
{// should be the meta key
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags | UE.ClassFlags.CLASS_HideDropDown;
break;
case 'DependsOn'.toLowerCase():
console.log('currently depend on meta data specifier is not supported');
break;
case 'MinimalAPI'.toLowerCase():
if (!value.IsMetaKey())
{// should be the meta key
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags | UE.ClassFlags.CLASS_MinimalAPI;
break;
case 'Const'.toLowerCase():
if (!value.IsMetaKey())
{// should be the meta key
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags | UE.ClassFlags.CLASS_Const;
break;
case 'PerObjectConfig'.toLowerCase():
if (!value.IsMetaKey())
{// should be the meta key
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags | UE.ClassFlags.CLASS_PerObjectConfig;
break;
case 'ConfigDoNotCheckDefaults'.toLowerCase():
if (!value.IsMetaKey())
{// should be the meta key
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags | UE.ClassFlags.CLASS_ConfigDoNotCheckDefaults;
break;
case 'Abstract'.toLowerCase():
if (!value.IsMetaKey())
{// should be the meta key
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags | UE.ClassFlags.CLASS_Abstract;
break;
case 'Deprecated'.toLowerCase():
if (!value.IsMetaKey())
{// should be the meta key
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags | UE.ClassFlags.CLASS_Deprecated;
ClassFlags = ClassFlags | UE.ClassFlags.CLASS_NotPlaceable;
break;
case 'Transient'.toLowerCase():
if (!value.IsMetaKey())
{// should be the meta key
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags | UE.ClassFlags.CLASS_Transient;
break;
case 'NonTransient'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags & ~UE.ClassFlags.CLASS_Transient;
break;
case 'CustomConstructor'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags | UE.ClassFlags.CLASS_CustomConstructor;
break;
case 'Config'.toLowerCase():
if (!value.IsMetaKeyValue())
{
return markInvalidSpecifier(`${value.Specifier}`);
}
ConfigName = value.Values[0];
break;
case 'DefaultConfig'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags | UE.ClassFlags.CLASS_DefaultConfig;
break;
case 'GlobalUserConfig'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags | UE.ClassFlags.CLASS_GlobalUserConfig;
break;
case 'ProjectUserConfig'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags | UE.ClassFlags.CLASS_ProjectUserConfig;
break;
case 'ShowCategories'.toLowerCase():
if (!value.IsMetaKeyValues())
{
return markInvalidSpecifier(`${value.Specifier}`);
}
value.Values.forEach((value)=>{ShowCategories.add(value);});
break;
case 'HideCategories'.toLowerCase():
if (!value.IsMetaKeyValues())
{
return markInvalidSpecifier(`${value.Specifier}`);
}
value.Values.forEach((value)=>{HideCategories.add(value);});
break;
case 'ShowFunctions'.toLowerCase():
if (!value.IsMetaKeyValues())
{
return markInvalidSpecifier(`${value.Specifier}`);
}
value.Values.forEach((value)=>{ShowFunctions.add(value);});
break;
case 'HideFunctions'.toLowerCase():
if (!value.IsMetaKeyValues())
{
return markInvalidSpecifier(`${value.Specifier}`);
}
value.Values.forEach((value)=>{HideFunctions.add(value);});
break;
case 'SparseClassDataTypes'.toLowerCase():
if (!value.IsMetaKeyValue())
{// currently only one sparse class data type is supported
return markInvalidSpecifier(`${value.Specifier}`);
}
SparseClassDataTypes.add(value.Values[0]);
break;
case 'ClassGroup'.toLowerCase():
if (!value.IsMetaKeyValues())
{
return markInvalidSpecifier(`${value.Specifier}`);
}
value.Values.forEach((value)=>{ClassGroupNames.add(value);});
break;
case 'AutoExpandCategories'.toLowerCase():
if (!value.IsMetaKeyValues())
{
return markInvalidSpecifier(`${value.Specifier}`);
}
value.Values.forEach((value)=>{
AutoCollapseCategories.delete(value);
AutoExpandCategories.add(value);
});
break;
case 'AutoCollapseCategories'.toLowerCase():
if (!value.IsMetaKeyValues())
{
return markInvalidSpecifier(`${value.Specifier}`);
}
value.Values.forEach((value)=>{
AutoExpandCategories.delete(value);
AutoCollapseCategories.add(value);
});
break;
case 'DontAutoCollapseCategories'.toLowerCase():
if (!value.IsMetaKeyValues())
{
return markInvalidSpecifier(`${value.Specifier}`);
}
value.Values.forEach((value)=>{
AutoCollapseCategories.delete(value);
})
break;
case 'CollapseCategories'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags | UE.ClassFlags.CLASS_CollapseCategories;
break;
case 'DontCollapseCategories'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSpecifier(`${value.Specifier}`);
}
ClassFlags = ClassFlags & ~UE.ClassFlags.CLASS_CollapseCategories;
break;
case 'AdvancedClassDisplay'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSpecifier(`${value.Specifier}`);
}
metaData.set('AdvancedClassDisplay', 'true');
break;
case 'ConversionRoot'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSpecifier(`${value.Specifier}`);
}
metaData.set('IsConversionRoot', 'true');
break;
default:
markInvalidSpecifier(`${value.Specifier}`);
break;
}
}
/**
* @brief
* function body
*/
specifiers.forEach((value)=>{
parseClassMetaSpecifier(value);
});
if (!bValidSpecifiers)
{
console.warn(`invalid specifier for uclass: ${InvalidSpecifier}`);
return null;
}
let metaDataResult = new UE.PEClassMetaData();
// fill data to the class meta data structure
metaDataResult.SetClassFlags(ClassFlags, bWantsPlacable);
metaData.forEach((value, key)=>{ metaDataResult.SetMetaData(key, value); });
metaDataResult.SetClassWithIn(WithIn);
metaDataResult.SetConfig(ConfigName);
HideCategories.forEach((value)=>{metaDataResult.AddHideCategory(value); });
ShowCategories.forEach((value)=>{metaDataResult.AddShowCategory(value); });
ShowSubCategories.forEach((value)=>{metaDataResult.AddShowSubCategory(value); });
HideFunctions.forEach((value)=>{metaDataResult.AddHideFunction(value); });
ShowFunctions.forEach((value)=>{metaDataResult.AddShowFunction(value); });
AutoExpandCategories.forEach((value)=>{metaDataResult.AddAutoExpandCategory(value); });
AutoCollapseCategories.forEach((value)=>{metaDataResult.AddAutoCollapseCategory(value); });
DontAutoCollapseCategories.forEach((value)=>{metaDataResult.AddDontAutoCollapseCategory(value); });
ClassGroupNames.forEach((value)=>{metaDataResult.AddClassGroup(value); });
SparseClassDataTypes.forEach((value)=>{metaDataResult.AddSparseDataType(value); });
return metaDataResult;
}
/**
* process the meta data, some validation should do with owner class, for simplicity, we ignore it here
* @param specifiers
* @param metaData
* @returns
*/
function processFunctionMetaData(specifiers: Array<MetaSpecifier>, metaData: Map<string, string>): UE.PEFunctionMetaData | null
{
let bValidSpecifiers: boolean = true;
let InvalidMessage: string;
let FunctionFlags: bigint = BigInt(UE.FunctionFlags.FUNC_None);
let FunctionExportFlags: bigint = 0n;// BigInt(UE.FunctionExportFlags.FUNCEXPORT_Final);
let bSpecifiedUnreliable: boolean = false;
let bSawPropertyAccessor: boolean = false;
let bSealedEvent: boolean = false;
let RPCId: number = 0;
let RPCResponseId: number = 0;
let EndpointName: string = '';
let bForceBlueprintImpure: boolean = false;
let CppValidationImplName: string = '';
let CppImpName: string = '';
let bAutomaticallyFinal: boolean = true;
/**
* a helper function used to mark the meta data is invalid
* @param reason
*/
function markInvalidSince(reason: string): void
{
bValidSpecifiers = false;
InvalidMessage = reason;
}
/**
* a helper function used parse the net service identifier
* @param InIdentifiers
*/
function parseNetServiceIdentifiers(InIdentifiers: Array<string>): boolean
{
const IdTag: string = "Id";
const ResponseIdTag: string = "ResponseId";
const JSBridgePriTag: string = "Priority";
let bResult: boolean = true;
InIdentifiers.forEach((value)=>{
if (!bResult)
{
return;
}
if (value.indexOf('=') != -1)
{// a tag with an argument
let TagAndArgument = value.split('=');
if (TagAndArgument.length != 2)
{
return markInvalidSince(`Invalid format for net service identifers: ${value}`);
}
let Argument = parseInt(TagAndArgument[1]);
if (Number.isNaN(Argument) || Argument < 0 || Argument > (1 << 16))
{
return markInvalidSince(`Invalid network identifier ${value} for function`);
}
if (TagAndArgument[0] == IdTag)
{
RPCId = Argument;
return;
}
if (TagAndArgument[0] == ResponseIdTag || TagAndArgument[0] == JSBridgePriTag)
{
RPCResponseId = Argument;
return;
}
}
else
{ // an endpoint name
if (EndpointName.length != 0)
{
bResult = false;
return markInvalidSince(`Function should not specify multiple endpoints - '${value}' found but already using '${EndpointName}'`);
}
EndpointName = value;
}
});
return bResult;
}
/**
* a helper function used to parse teh meta specifier
* @param value
*/
function parseFunctionMetaSpecifier(value: MetaSpecifier): void
{
if (!bValidSpecifiers)
{
return;
}
switch (value.Specifier.toLowerCase())
{
case 'BlueprintNativeEvent'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince('BlueprintNativeEvent should be meta key');
}
if (FunctionFlags & BigInt(UE.FunctionFlags.FUNC_Net))
{
return markInvalidSince('BlueprintNativeEvent functions cannot be replicated!');
}
if ((FunctionFlags & BigInt(UE.FunctionFlags.FUNC_BlueprintEvent)) && !(FunctionFlags & BigInt(UE.FunctionFlags.FUNC_Native)))
{
return markInvalidSince('A function cannot be both BlueprintNativeEvent and BlueprintImplementableEvent!');
}
if (bSawPropertyAccessor)
{
return markInvalidSince("A function cannot be both BlueprintNativeEvent and a Blueprint Property accessor!");
}
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_Event);
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_BlueprintEvent);
break;
case 'BlueprintImplementableEvent'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be meta key`);
}
if (FunctionFlags & BigInt(UE.FunctionFlags.FUNC_Net))
{
return markInvalidSince('BlueprintImplementableEvent functions cannot be replicated!');
}
if ((FunctionFlags & BigInt(UE.FunctionFlags.FUNC_BlueprintEvent)) && (FunctionFlags & BigInt(UE.FunctionFlags.FUNC_Native)))
{
return markInvalidSince('A function cannot be both BlueprintNativeEvent and BlueprintImplementableEvent!');
}
if (bSawPropertyAccessor)
{
return markInvalidSince('A function cannot be both BlueprintImplementableEvent and a Blueprint Property accessor!');
}
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_Event);
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_BlueprintEvent);
FunctionFlags &= ~BigInt(UE.FunctionFlags.FUNC_Native);
break;
case 'Exec'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be meta key`);
}
if (FunctionFlags & BigInt(UE.FunctionFlags.FUNC_Net))
{
return markInvalidSince('Exec functions cannot be replicated!');
}
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_Exec);
break;
case 'SealedEvent'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be meta key`);
}
bSealedEvent = true;
break;
case 'Server'.toLowerCase():
if (!value.IsMetaKey() && !value.IsMetaKeyValue())
{
return markInvalidSince(`${value.Specifier} should be meta key`);
}
if (FunctionFlags & BigInt(UE.FunctionFlags.FUNC_BlueprintEvent))
{
return markInvalidSince('BlueprintImplementableEvent or BlueprintNativeEvent functions cannot be declared as Client or Server');
}
if (FunctionFlags & BigInt(UE.FunctionFlags.FUNC_Exec))
{
return markInvalidSince('Exec functions cannot be replicated!');
}
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_Net);
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_NetServer);
if (value.IsMetaKeyValue())
{
CppImpName = value.Values[0];
}
break;
case 'Client'.toLowerCase():
if (!value.IsMetaKey() && !value.IsMetaKeyValue())
{
return markInvalidSince(`${value.Specifier} should be meta key`);
}
if (FunctionFlags & BigInt(UE.FunctionFlags.FUNC_BlueprintEvent))
{
return markInvalidSince('BlueprintImplementableEvent or BlueprintNativeEvent functions cannot be declared as Client or Server');
}
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_Net);
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_NetClient);
if (value.IsMetaKeyValue())
{
CppImpName = value.Values[0];
}
break;
case 'NetMulticast'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be meta key`);
}
if (FunctionFlags & BigInt(UE.FunctionFlags.FUNC_BlueprintEvent))
{
return markInvalidSince('BlueprintImplementableEvent or BlueprintNativeEvent functions cannot be declared as Multicast');
}
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_Net);
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_NetMulticast);
break;
case 'ServiceRequest'.toLowerCase():
if (!value.IsMetaKeyValues())
{
return markInvalidSince(`${value.Specifier} should be meta values`);
}
if (FunctionFlags & BigInt(UE.FunctionFlags.FUNC_BlueprintEvent))
{
return markInvalidSince('BlueprintImplementableEvent or BlueprintNativeEvent functions cannot be declared as a ServiceRequest');
}
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_Net);
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_NetReliable);
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_NetRequest);
FunctionExportFlags |= BigInt(UE.FunctionExportFlags.FUNCEXPORT_CustomThunk);
parseNetServiceIdentifiers(value.Values);
if (bValidSpecifiers && EndpointName.length == 0)
{
markInvalidSince('ServiceRequest needs to specify an endpoint name');
}
break;
case 'ServiceResponse'.toLowerCase():
if (!value.IsMetaKeyValues())
{
return markInvalidSince(`${value.Specifier} should be meta values`);
}
if (FunctionFlags & BigInt(UE.FunctionFlags.FUNC_BlueprintEvent))
{
return markInvalidSince('BlueprintImplementableEvent or BlueprintNativeEvent functions cannot be declared as a ServiceResponse');
}
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_Net);
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_NetReliable);
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_NetResponse);
parseNetServiceIdentifiers(value.Values);
if (bValidSpecifiers && EndpointName.length == 0)
{
markInvalidSince('ServiceResponse needs to specify an endpoint name');
}
break;
case 'Reliable'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be meta key`);
}
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_NetReliable);
break;
case 'Unreliable'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be meta key`);
}
bSpecifiedUnreliable = true;
break;
case 'CustomThunk'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be meta key`);
}
FunctionExportFlags |= BigInt(UE.FunctionExportFlags.FUNCEXPORT_CustomThunk);
break;
case 'BlueprintCallable'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be meta key`);
}
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_BlueprintCallable);
break;
case 'BlueprintGetter'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be meta key`);
}
if (FunctionFlags & BigInt(UE.FunctionFlags.FUNC_Event))
{
return markInvalidSince(`Function cannot be a blueprint event and a blueprint getter.`);
}
bSawPropertyAccessor = true;
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_BlueprintCallable);
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_BlueprintPure);
metaData.set("BlueprintGetter", "");
break;
case 'BlueprintSetter'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be meta key`);
}
if (FunctionFlags & BigInt(UE.FunctionFlags.FUNC_Event))
{
return markInvalidSince(`Function cannot be a blueprint event and a blueprint setter.`);
}
bSawPropertyAccessor = true;
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_BlueprintCallable);
metaData.set("BlueprintSetter", "");
break;
case 'BlueprintPure'.toLowerCase():
{
if (!value.IsMetaKey() && !value.IsMetaKeyValue())
{
return markInvalidSince(`${value.Specifier} should be meta key or meta value`);
}
let bPure = true;
if (value.IsMetaKeyValue())
{
bPure = value.Values[0].toLowerCase() == 'true';
}
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_BlueprintCallable);
if (bPure)
{
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_BlueprintPure);
}
else
{
bForceBlueprintImpure = true;
}
break;
}
case 'BlueprintAuthorityOnly'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be meta key`);
}
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_BlueprintAuthorityOnly);
break;
case 'BlueprintCosmetic'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be meta key`);
}
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_BlueprintCosmetic);
break;
case 'WithValidation'.toLowerCase():
if (!value.IsMetaKey() && !value.IsMetaKeyValue())
{
return markInvalidSince(`${value.Specifier} should be meta key or meta value`);
}
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_NetValidate);
if (value.IsMetaKeyValue())
{
CppValidationImplName = value.Values[0];
}
break;
default:
markInvalidSince(`${value.Specifier} is not a valid specifier`);
break;
}
}
/**
* a helper function used to valid the function flags
*/
function validateFunctionFlags(): void
{
if (FunctionFlags & BigInt(UE.FunctionFlags.FUNC_Net))
{
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_Event);
const bIsNetService = !!(FunctionFlags & (BigInt(UE.FunctionFlags.FUNC_NetRequest) | BigInt(UE.FunctionFlags.FUNC_NetResponse)));
const bIsNetReliable = !!(FunctionFlags & BigInt(UE.FunctionFlags.FUNC_NetReliable));
// replated function
// 1. not static
// 2. reliable / unreliable should be specified, but never both
if (FunctionFlags & BigInt(UE.FunctionFlags.FUNC_Static))
{
return markInvalidSince("Static functions can't be replicated");
}
if (!bIsNetReliable && !bSpecifiedUnreliable && !bIsNetService)
{
return markInvalidSince("Replicated function: 'reliable' or 'unreliable' is required");
}
if (bIsNetReliable && bSpecifiedUnreliable && !bIsNetService)
{
return markInvalidSince("'reliable' and 'unreliable' are mutually exclusive");
}
}
else if (FunctionFlags & BigInt(UE.FunctionFlags.FUNC_NetReliable)) // only net function could mark reliable or unreliable
{
return markInvalidSince("'reliable' specified without 'client' or 'server'");
}
else if (bSpecifiedUnreliable)
{
return markInvalidSince("'unreliable' specified without 'client' or 'server'");
}
if (bSealedEvent && !(FunctionFlags & BigInt(UE.FunctionFlags.FUNC_Event))) // sealed event could only used on events
{
return markInvalidSince("SealedEvent may only be used on events");
}
// blueprint event could not be sealed
if (bSealedEvent && (FunctionFlags & BigInt(UE.FunctionFlags.FUNC_BlueprintEvent)))
{
return markInvalidSince("SealedEvent cannot be used on Blueprint events");
}
if (bForceBlueprintImpure && (FunctionFlags & BigInt(UE.FunctionFlags.FUNC_BlueprintPure)))
{
return markInvalidSince("BlueprintPure (or BlueprintPure=true) and BlueprintPure=false should not both appear on the same function, they are mutually exclusive");
}
// set custom thunk meta data
if ((FunctionExportFlags & BigInt(UE.FunctionExportFlags.FUNCEXPORT_CustomThunk)) && !metaData.has("CustomThunk"))
{
metaData.set("CustomThunk", 'true');
}
if (FunctionFlags & BigInt(UE.FunctionFlags.FUNC_Net))
{
bAutomaticallyFinal = false;
}
if (FunctionFlags & BigInt(UE.FunctionFlags.FUNC_BlueprintEvent))
{
bAutomaticallyFinal = false;
}
if (bAutomaticallyFinal || bSealedEvent)
{
FunctionFlags |= BigInt(UE.FunctionFlags.FUNC_Final);
FunctionExportFlags != BigInt(UE.FunctionExportFlags.FUNCEXPORT_Final);
}
}
/**
* @brief
* function body
*/
if (metaData.has("CppFromBpEvent"))
{
FunctionFlags = FunctionFlags | BigInt(UE.FunctionFlags.FUNC_Event);
}
specifiers.forEach((value)=>{
parseFunctionMetaSpecifier(value);
});
if (bValidSpecifiers)
{
validateFunctionFlags();
}
if (!bValidSpecifiers)
{
console.warn(`invalid meta data for ufunction: ${InvalidMessage}`);
return null;
}
let metaDataResult = new UE.PEFunctionMetaData();
metaDataResult.SetFunctionFlags(Number(FunctionFlags >> 32n), Number(FunctionFlags & 0xffffffffn));
metaDataResult.SetFunctionExportFlags(Number(FunctionExportFlags));
metaData.forEach((value, key)=>{metaDataResult.SetMetaData(key, value);});
metaDataResult.SetCppImplName(CppImpName);
metaDataResult.SetCppValidationImplName(CppValidationImplName);
metaDataResult.SetEndpointName(EndpointName);
metaDataResult.SetRPCId(RPCId);
metaDataResult.SetRPCResponseId(RPCResponseId);
metaDataResult.SetIsSealedEvent(bSealedEvent);
metaDataResult.SetForceBlueprintImpure(bForceBlueprintImpure);
return metaDataResult;
}
/**
* process the meta data of function parameters
* @param specifiers
* @param metaData
*/
function processParamMetaData(specifiers: Array<MetaSpecifier>, metaData: Map<string, string>): UE.PEParamMetaData | null
{
let bValidSpecifiers: boolean = true;
let InvalidMessage: string;
let PropertyFlags: bigint = BigInt(UE.PropertyFlags.CPF_None);
/**
* a helper function used to mark the meta data is invalid
* @param reason
*/
function markInvalidSince(reason: string): void
{
bValidSpecifiers = false;
InvalidMessage = reason;
}
/**
* a helper function used to parse the meta specifier
* @param value
*/
function parseParamMetaSpecifier(value: MetaSpecifier): void
{
if (!bValidSpecifiers)
{
return;
}
switch (value.Specifier.toLowerCase())
{
case 'Const'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_ConstParm);
break;
case 'Ref'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | (BigInt(UE.PropertyFlags.CPF_OutParm) | BigInt(UE.PropertyFlags.CPF_ReferenceParm));
break;
case 'NotReplicated'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | (BigInt(UE.PropertyFlags.CPF_RepSkip))
break;
default:
markInvalidSince(`${value.Specifier} is not a valid specifier`);
break;
}
}
/**
* @brief
* function body
*/
specifiers.forEach((value)=>{
parseParamMetaSpecifier(value);
});
if (!bValidSpecifiers)
{
console.warn(`invalid meta data for uparam: ${InvalidMessage}`);
return null;
}
let metaDataResult = new UE.PEParamMetaData();
metaDataResult.SetParamFlags(Number(PropertyFlags << 32n), Number(PropertyFlags & 0xffffffffn));
metaData.forEach((value, key)=>{metaDataResult.SetMetaData(key, value);});
return metaDataResult;
}
/**
* process the meta data of the property
* @param specifiers
* @param metaData
* @returns
*/
function processPropertyMetaData(specifiers: Array<MetaSpecifier>, metaData: Map<string, string>): UE.PEPropertyMetaData | null
{
let bValidSpecifiers: boolean = true;
let InvalidMessage: string;
let PropertyFlags: bigint = BigInt(UE.PropertyFlags.CPF_None);
let ImpliedPropertyFlags: bigint = BigInt(UE.PropertyFlags.CPF_None);
let bSeenEditSpecifier: boolean = false;
let bSeenBlueprintWriteSpecifier: boolean = false;
let bSeenBlueprintReadOnlySpecifier: boolean = false;
let bSeenBlueprintGetterSpecifier: boolean = false;
let RepCallbackName: string;
/**
* a helper function used to mark the meta data is invalid
* @param reason
*/
function markInvalidSince(reason: string): void
{
bValidSpecifiers = false;
InvalidMessage = reason;
}
/**
* a helper function used to parse the meta specifier
* @param value
*/
function parsePropertyMetaSpecifier(value: MetaSpecifier): void
{
if (!bValidSpecifiers)
{
return;
}
switch (value.Specifier.toLowerCase())
{
case 'EditAnywhere'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
if (bSeenEditSpecifier)
{
return markInvalidSince(`found more than one edit/visibility specifier ${value.Specifier}, only one is allowed`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_Edit);
bSeenEditSpecifier = true;
break;
case 'EditInstanceOnly'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
if (bSeenEditSpecifier)
{
return markInvalidSince(`found more than one edit/visibility specifier ${value.Specifier}, only one is allowed`);
}
PropertyFlags = PropertyFlags | (BigInt(UE.PropertyFlags.CPF_Edit) | BigInt(UE.PropertyFlags.CPF_DisableEditOnTemplate));
bSeenEditSpecifier = true;
break;
case 'EditDefaultOnly'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
if (bSeenEditSpecifier)
{
return markInvalidSince(`found more than one edit/visibility specifier ${value.Specifier}, only one is allowed`);
}
PropertyFlags = PropertyFlags | (BigInt(UE.PropertyFlags.CPF_Edit) | BigInt(UE.PropertyFlags.CPF_DisableEditOnInstance));
bSeenEditSpecifier = true;
break;
case 'VisibleAnywhere'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
if (bSeenEditSpecifier)
{
return markInvalidSince(`found more than one edit/visibility specifier ${value.Specifier}, only one is allowed`);
}
PropertyFlags = PropertyFlags | (BigInt(UE.PropertyFlags.CPF_Edit) | BigInt(UE.PropertyFlags.CPF_EditConst));
bSeenEditSpecifier = true;
break;
case `VisibleInstanceOnly`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
if (bSeenEditSpecifier)
{
return markInvalidSince(`found more than one edit/visibility specifier ${value.Specifier}, only one is allowed`);
}
PropertyFlags = PropertyFlags | (BigInt(UE.PropertyFlags.CPF_Edit) | BigInt(UE.PropertyFlags.CPF_EditConst ) | BigInt(UE.PropertyFlags.CPF_DisableEditOnTemplate));
bSeenEditSpecifier = true;
break;
case 'VisibleDefaultOnly'.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
if (bSeenEditSpecifier)
{
return markInvalidSince(`found more than one edit/visibility specifier ${value.Specifier}, only one is allowed`);
}
PropertyFlags = PropertyFlags | (BigInt(UE.PropertyFlags.CPF_Edit) | BigInt(UE.PropertyFlags.CPF_EditConst) | BigInt(UE.PropertyFlags.CPF_DisableEditOnInstance));
bSeenEditSpecifier = true;
break;
case `BlueprintReadWrite`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
if (bSeenBlueprintReadOnlySpecifier)
{
return markInvalidSince(`cannot specify a property as being both BlueprintReadOnly and BlueprintReadWrite`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_BlueprintVisible);
bSeenBlueprintWriteSpecifier = true;
break;
case `BlueprintSetter`.toLowerCase():
if (!value.IsMetaKeyValue())
{
return markInvalidSince(`${value.Specifier} should be a meta key value`);
}
if (bSeenBlueprintReadOnlySpecifier)
{
return markInvalidSince(`can not specify a property as being both BlueprintReadOnly and having a BlueprintSetter`);
}
metaData.set('BlueprintSetter', value.Values[0]);
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_BlueprintVisible);
bSeenBlueprintWriteSpecifier = true;
break;
case `BlueprintReadOnly`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
if (bSeenBlueprintWriteSpecifier)
{
return markInvalidSince(`can not specify both BlueprintReadOnly and BlueprintReadWrite or BlueprintSetter for ${value.Specifier}`)
}
PropertyFlags = PropertyFlags | (BigInt(UE.PropertyFlags.CPF_BlueprintVisible) | BigInt(UE.PropertyFlags.CPF_BlueprintReadOnly));
ImpliedPropertyFlags = ImpliedPropertyFlags & (~BigInt(UE.PropertyFlags.CPF_BlueprintReadOnly));
bSeenBlueprintReadOnlySpecifier = true;
break;
case `BlueprintGetter`.toLowerCase():
if (!value.IsMetaKeyValue())
{
return markInvalidSince(`${value.Specifier}, should be a meta key value`)
}
metaData.set("BlueprintGetter", value.Values[0]);
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_BlueprintVisible)
bSeenBlueprintGetterSpecifier = true;
break;
case `Config`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_Config);
break;
case `GlobalConfig`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | (BigInt(UE.PropertyFlags.CPF_GlobalConfig) | BigInt(UE.PropertyFlags.CPF_Config));
break;
case `Localized`.toLowerCase():
console.warn(`the localized specifier is deprecated`);
break;
case `Transient`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_Transient);
break;
case `DuplicateTransient`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_DuplicateTransient);
break;
case `TextExportTransient`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_TextExportTransient);
break;
case `NonPIETransient`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
console.warn('NonPIETransient is deprecated - NonPIEDuplicateTransient should be used instead');
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_NonPIEDuplicateTransient);
break;
case `NonPIEDuplicateTransient`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_NonPIEDuplicateTransient);
break;
case `Export`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_ExportObject);
break;
case `EditInline`.toLowerCase():
return markInvalidSince(`EditInline is deprecated. Remove it, or use Instanced instead`);
case `NoClear`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_NoClear);
break;
case `EditFixedSize`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_EditFixedSize);
break;
case `Replicated`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_Net);
break;
case `ReplicatedUsing`.toLowerCase():
if (!value.IsMetaKeyValue())
{
return markInvalidSince(`${value.Specifier} should be a meta key value`);
}
RepCallbackName = value.Values[0];
PropertyFlags = PropertyFlags | (BigInt(UE.PropertyFlags.CPF_Net) | BigInt(UE.PropertyFlags.CPF_RepNotify));
break;
case `NotReplicated`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_RepSkip);
break;
case `RepRetry`.toLowerCase():
console.error('RepRetry is deprecated');
break;
case `Interp`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | (BigInt(UE.PropertyFlags.CPF_Edit) | BigInt(UE.PropertyFlags.CPF_BlueprintVisible) | BigInt(UE.PropertyFlags.CPF_Interp));
break;
case `NonTransactional`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_NonTransactional);
break;
case `Instanced`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | (BigInt(UE.PropertyFlags.CPF_PersistentInstance) | BigInt(UE.PropertyFlags.CPF_ExportObject) | BigInt(UE.PropertyFlags.CPF_InstancedReference));
metaData.set(`EditInline`, 'true');
break;
case `BlueprintAssignable`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_BlueprintAssignable);
break;
case `BlueprintCallable`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_BlueprintCallable);
break;
case `BlueprintAuthorityOnly`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_BlueprintAuthorityOnly);
break;
case `AssetRegistrySearchable`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_AssetRegistrySearchable);
break;
case `SimpleDisplay`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_SimpleDisplay);
break;
case `AdvancedDisplay`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_AdvancedDisplay);
break;
case `SaveGame`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_SaveGame);
break;
case `SkipSerialization`.toLowerCase():
if (!value.IsMetaKey())
{
return markInvalidSince(`${value.Specifier} should be a meta key`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_SkipSerialization);
break;
default:
markInvalidSince(`${value.Specifier} is not a valid specifier`);
break;
}
}
/**
* a helper function used to validate the property flags
* @returns
*/
function validatePropertyFlags(): void
{
// If we saw a BlueprintGetter but did not see BlueprintSetter or
// or BlueprintReadWrite then treat as BlueprintReadOnly
if (bSeenBlueprintGetterSpecifier && !bSeenBlueprintWriteSpecifier)
{
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_BlueprintReadOnly);
ImpliedPropertyFlags = ImpliedPropertyFlags & (~BigInt(UE.PropertyFlags.CPF_BlueprintReadOnly));
}
if (metaData.has(`ExposeOnSpawn`))
{
if ((PropertyFlags & BigInt(UE.PropertyFlags.CPF_DisableEditOnInstance)) != 0n)
{
return markInvalidSince(`property cannot have both DisableEditOnInstance and ExposeOnSpawn flags`);
}
if ((PropertyFlags & BigInt(UE.PropertyFlags.CPF_BlueprintVisible)) == 0n)
{
return markInvalidSince(`property cannot have ExposeOnSpawn without BlueprintVisible flags`);
}
PropertyFlags = PropertyFlags | BigInt(UE.PropertyFlags.CPF_ExposeOnSpawn);
}
if (!(PropertyFlags & BigInt(UE.PropertyFlags.CPF_Edit)))
{
if (PropertyFlags & BigInt(UE.PropertyFlags.CPF_DisableEditOnInstance))
{
return markInvalidSince(`property cannot have DisableEditOnInstance without being editable`);
}
if (PropertyFlags & BigInt(UE.PropertyFlags.CPF_DisableEditOnTemplate))
{
return markInvalidSince(`property cannot have DisableEditOnTemplate without being editable`);
}
}
const ParamFlags = BigInt(UE.PropertyFlags.CPF_Parm)
| BigInt(UE.PropertyFlags.CPF_OutParm)
| BigInt(UE.PropertyFlags.CPF_ReturnParm)
| BigInt(UE.PropertyFlags.CPF_ReferenceParm)
| BigInt(UE.PropertyFlags.CPF_ConstParm);
if (PropertyFlags & ParamFlags)
{
return markInvalidSince(`Illegal type modifiers in member variable declaration`);
}
}
/**
* @brief
* function body
*/
specifiers.forEach((value)=>{
parsePropertyMetaSpecifier(value);
})
if (bValidSpecifiers)
{
validatePropertyFlags();
}
if (!bValidSpecifiers)
{
console.warn(`invalid meta data for uproperty: ${InvalidMessage}`);
return null;
}
let metaDataResult = new UE.PEPropertyMetaData();
const FinalFlags = PropertyFlags | ImpliedPropertyFlags;
metaDataResult.SetPropertyFlags(Number(FinalFlags >> 32n), Number(FinalFlags & 0xffffffffn));
metaData.forEach((value, key)=>{metaDataResult.SetMetaData(key, value);});
metaDataResult.SetRepCallbackName(RepCallbackName);
return metaDataResult;
}
/**
* compile the class data
* @param type
*/
export function compileClassMetaData(type: ts.Type): UE.PEClassMetaData
{
// fetch the decorator
let decorators = null;
if (type.getSymbol().valueDeclaration != null)
{
decorators = type.getSymbol().valueDeclaration.decorators;
}
if (decorators == null)
{ // no decorators
return null;
}
let [specifiers, metaData] = getMetaDataFromDecorators(decorators, 'uclass');
return processClassMetaData(specifiers, metaData);
}
/**
* compile the function meta data
* @param func
*/
export function compileFunctionMetaData(func: ts.Symbol): UE.PEFunctionMetaData
{
// fetch the decorator
const decorators = func.valueDeclaration != null ? func.valueDeclaration.decorators : null;
if (decorators == null)
{ // no decorators
return null;
}
let [specifiers, metaData] = getMetaDataFromDecorators(decorators, 'ufunction');
return processFunctionMetaData(specifiers, metaData);
}
/**
* compile the function parameter meta data
* @param param
* @returns
*/
export function compileParamMetaData(param: ts.Symbol): UE.PEParamMetaData
{
// fetch the decorator
const decorators = param.valueDeclaration != null ? param.valueDeclaration.decorators : null;
if (decorators == null)
{
return null;
}
let [specifiers, metaData] = getMetaDataFromDecorators(decorators, 'uparam');
return processParamMetaData(specifiers, metaData);
}
/**
* compile the property meta data
* @param prop
* @returns
*/
export function compilePropertyMetaData(prop: ts.Symbol): UE.PEPropertyMetaData
{
// fetch the decorator
const decorators = prop.valueDeclaration != null ? prop.valueDeclaration.decorators : null;
if (decorators == null)
{
return null;
}
let [specifiers, metaData] = getMetaDataFromDecorators(decorators, 'uproperty');
return processPropertyMetaData(specifiers, metaData);
}