Refactoring entities (#23)

* Still WIP

* WIP

* ArrayEntity parsing fixed

* Fix format text entity

* Tests for various entity classes and update entity class implementations

* More tests and fixed

* More entities fixed

* Simple entities serialization fixed

* Entities tests fixed

* Remove serialization bits

* Fix Function reference

* CustomProperties creating fixed

* WIP

* Better typing for grammars

* Decoding code fixes

* Fixing still

* Several fixes

* rename toString to serialize

* Several fixes

* More fixes

* Moving more stuff out of Utility

* Several fixes

* Fixing Linear color entity print

* Serialization fixes

* Fix serialization

* Method to compute grammar

* Renaming fix

* Fix array grammar and equality check

* Fix inlined keys

* Fix type

* Several serialization fixes

* Fix undefined dereference

* Several fixes

* More fixes and cleanup

* Fix keys quoting mechanism

* Fix natural number assignment

* Fix Int64 toString()

* Fix quoted keys for inlined arrays

* Fix PG pins

* Fix several test cases

* Types fixes

* New pin default value empty

* Fix non existing DefaultValue for variadic nodes

* Smaller fixes for crashes

* Fix link color when attached to knot

* Linking test and more reliability operations for adding pins

* Improve issue 18 test

* More tests and fixes

* Fix enum pin entity

* Remove failing test
This commit is contained in:
barsdeveloper
2024-09-08 11:46:36 +02:00
committed by GitHub
parent 31a07b992d
commit 23ee628e28
129 changed files with 8888 additions and 8584 deletions

View File

@@ -8,23 +8,22 @@ export default function nodeColor(entity) {
case Configuration.paths.materialExpressionConstant3Vector:
case Configuration.paths.materialExpressionConstant4Vector:
return Configuration.nodeColors.yellow
case Configuration.paths.materialExpressionFunctionInput:
case Configuration.paths.materialExpressionTextureCoordinate:
case Configuration.paths.materialExpressionWorldPosition:
case Configuration.paths.pcgEditorGraphNodeInput:
case Configuration.paths.pcgEditorGraphNodeOutput:
return Configuration.nodeColors.red
case Configuration.paths.makeStruct:
return Configuration.nodeColors.darkBlue
case Configuration.paths.materialExpressionMaterialFunctionCall:
return Configuration.nodeColors.blue
case Configuration.paths.materialExpressionFunctionInput:
return Configuration.nodeColors.red
case Configuration.paths.materialExpressionTextureSample:
return Configuration.nodeColors.darkTurquoise
case Configuration.paths.materialExpressionTextureCoordinate:
return Configuration.nodeColors.red
case Configuration.paths.pcgEditorGraphNodeInput:
case Configuration.paths.pcgEditorGraphNodeOutput:
return Configuration.nodeColors.red
}
switch (entity.getClass()) {
case Configuration.paths.callFunction:
return entity.bIsPureFunc
return entity.bIsPureFunc?.valueOf()
? Configuration.nodeColors.green
: Configuration.nodeColors.blue
case Configuration.paths.niagaraNodeFunctionCall:
@@ -74,7 +73,7 @@ export default function nodeColor(entity) {
return Configuration.nodeColors.intenseGreen
}
}
if (entity.bIsPureFunc) {
if (entity.bIsPureFunc?.valueOf()) {
return Configuration.nodeColors.green
}
return Configuration.nodeColors.blue

View File

@@ -20,7 +20,7 @@ export default function nodeTemplateClass(nodeEntity) {
|| nodeEntity.getClass() === Configuration.paths.callArrayFunction
) {
const memberParent = nodeEntity.FunctionReference?.MemberParent?.path ?? ""
const memberName = nodeEntity.FunctionReference?.MemberName
const memberName = nodeEntity.FunctionReference?.MemberName?.toString()
if (
memberName && (
memberParent === Configuration.paths.kismetMathLibrary
@@ -96,13 +96,15 @@ export default function nodeTemplateClass(nodeEntity) {
}
return MetasoundNodeTemplate
case Configuration.paths.niagaraNodeOp:
if ([
"Boolean::LogicEq",
"Boolean::LogicNEq",
"Numeric::Abs",
"Numeric::Add",
"Numeric::Mul",
].includes(nodeEntity.OpName)) {
if (
[
"Boolean::LogicEq",
"Boolean::LogicNEq",
"Numeric::Abs",
"Numeric::Add",
"Numeric::Mul",
].includes(nodeEntity.OpName?.toString())
) {
return VariableOperationNodeTemplate
}
break

View File

@@ -1,5 +1,9 @@
import Configuration from "../Configuration.js"
import Utility from "../Utility.js"
import BooleanEntity from "../entity/BooleanEntity.js"
import LinearColorEntity from "../entity/LinearColorEntity.js"
import MirroredEntity from "../entity/MirroredEntity.js"
import VectorEntity from "../entity/VectorEntity.js"
const sequencerScriptingNameRegex = /\/Script\/SequencerScripting\.MovieSceneScripting(.+)Channel/
const keyNameValue = {
@@ -31,43 +35,63 @@ const keyNameValue = {
"Tilde": "`",
}
/** @param {String} value */
function numberFromText(value = "") {
value = value.toLowerCase()
switch (value) {
case "zero": return 0
case "one": return 1
case "two": return 2
case "three": return 3
case "four": return 4
case "five": return 5
case "six": return 6
case "seven": return 7
case "eight": return 8
case "nine": return 9
}
}
function keyName(value) {
/** @type {String} */
let result = keyNameValue[value]
if (result) {
return result
}
result = Utility.numberFromText(value)?.toString()
result = numberFromText(value)?.toString()
if (result) {
return result
}
const match = value.match(/NumPad([a-zA-Z]+)/)
if (match) {
result = Utility.numberFromText(match[1]).toString()
result = numberFromText(match[1]).toString()
if (result) {
return "Num " + result
}
}
}
/** @param {ObjectEntity} entity */
/**
* @param {ObjectEntity} entity
* @returns {String}
*/
export default function nodeTitle(entity) {
let input
switch (entity.getType()) {
case Configuration.paths.asyncAction:
if (entity.ProxyFactoryFunctionName) {
return Utility.formatStringName(entity.ProxyFactoryFunctionName)
return Utility.formatStringName(entity.ProxyFactoryFunctionName?.toString())
}
case Configuration.paths.actorBoundEvent:
case Configuration.paths.componentBoundEvent:
return `${Utility.formatStringName(entity.DelegatePropertyName)} (${entity.ComponentPropertyName ?? "Unknown"})`
return `${Utility.formatStringName(entity.DelegatePropertyName?.toString())} (${entity.ComponentPropertyName?.toString() ?? "Unknown"})`
case Configuration.paths.callDelegate:
return `Call ${entity.DelegateReference?.MemberName ?? "None"}`
return `Call ${entity.DelegateReference?.MemberName?.toString() ?? "None"}`
case Configuration.paths.createDelegate:
return "Create Event"
case Configuration.paths.customEvent:
if (entity.CustomFunctionName) {
return entity.CustomFunctionName
return entity.CustomFunctionName?.toString()
}
case Configuration.paths.dynamicCast:
if (!entity.TargetType) {
@@ -77,7 +101,7 @@ export default function nodeTitle(entity) {
case Configuration.paths.enumLiteral:
return `Literal enum ${entity.Enum?.getName()}`
case Configuration.paths.event:
return `Event ${(entity.EventReference?.MemberName ?? "").replace(/^Receive/, "")}`
return `Event ${(entity.EventReference?.MemberName?.toString() ?? "").replace(/^Receive/, "")}`
case Configuration.paths.executionSequence:
return "Sequence"
case Configuration.paths.forEachElementInEnum:
@@ -85,9 +109,9 @@ export default function nodeTitle(entity) {
case Configuration.paths.forEachLoopWithBreak:
return "For Each Loop with Break"
case Configuration.paths.functionEntry:
return entity.FunctionReference?.MemberName === "UserConstructionScript"
return entity.FunctionReference?.MemberName?.toString() === "UserConstructionScript"
? "Construction Script"
: entity.FunctionReference?.MemberName
: entity.FunctionReference?.MemberName?.toString()
case Configuration.paths.functionResult:
return "Return Node"
case Configuration.paths.ifThenElse:
@@ -98,36 +122,32 @@ export default function nodeTitle(entity) {
}
case Configuration.paths.materialExpressionComponentMask: {
const materialObject = entity.getMaterialSubobject()
return `Mask ( ${Configuration.rgba
.filter(k => /** @type {MirroredEntity<any>} */(materialObject[k]).get() === true)
.map(v => v + " ")
.join("")})`
if (materialObject) {
return `Mask ( ${Configuration.rgba
.filter(k => /** @type {MirroredEntity<typeof BooleanEntity>} */(materialObject[k]).getter().value === true)
.map(v => v + " ")
.join("")})`
}
}
case Configuration.paths.materialExpressionConstant:
input ??= [entity.getCustomproperties().find(pinEntity => pinEntity.PinName == "Value")?.DefaultValue]
input ??= [entity.getCustomproperties().find(pinEntity => pinEntity.PinName.toString() == "Value")?.DefaultValue]
case Configuration.paths.materialExpressionConstant2Vector:
input ??= [
entity.getCustomproperties().find(pinEntity => pinEntity.PinName == "X")?.DefaultValue,
entity.getCustomproperties().find(pinEntity => pinEntity.PinName == "Y")?.DefaultValue,
entity.getCustomproperties().find(pinEntity => pinEntity.PinName?.toString() == "X")?.DefaultValue,
entity.getCustomproperties().find(pinEntity => pinEntity.PinName?.toString() == "Y")?.DefaultValue,
]
case Configuration.paths.materialExpressionConstant3Vector:
if (!input) {
/** @type {VectorEntity} */
const vector = entity.getCustomproperties()
.find(pinEntity => pinEntity.PinName == "Constant")
?.DefaultValue
input = [vector.X, vector.Y, vector.Z]
}
case Configuration.paths.materialExpressionConstant4Vector:
if (!input) {
/** @type {LinearColorEntity} */
const vector = entity.getCustomproperties()
.find(pinEntity => pinEntity.PinName == "Constant")
.find(pinEntity => pinEntity.PinName?.toString() == "Constant")
?.DefaultValue
input = [vector.R, vector.G, vector.B, vector.A].map(v => v.valueOf())
input = vector instanceof VectorEntity ? [vector.X, vector.Y, vector.Z].map(v => v.valueOf())
: vector instanceof LinearColorEntity ? [vector.R, vector.G, vector.B, vector.A].map(v => v.valueOf())
: /** @type {Number[]} */([])
}
if (input.length > 0) {
return input.map(v => Utility.printExponential(v)).reduce((acc, cur) => acc + "," + cur)
return input.map(v => Utility.printExponential(v)).join(",")
}
break
case Configuration.paths.materialExpressionFunctionInput: {
@@ -150,6 +170,11 @@ export default function nodeTitle(entity) {
break
case Configuration.paths.materialExpressionSquareRoot:
return "Sqrt"
case Configuration.paths.materialExpressionSubtract:
const materialObject = entity.getMaterialSubobject()
if (materialObject) {
return `Subtract(${materialObject.ConstA ?? "1"},${materialObject.ConstB ?? "1"})`
}
case Configuration.paths.metasoundEditorGraphExternalNode: {
const name = entity["ClassName"]?.["Name"]
if (name) {
@@ -165,7 +190,7 @@ export default function nodeTitle(entity) {
return "Output"
case Configuration.paths.spawnActorFromClass:
let className = entity.getCustomproperties()
.find(pinEntity => pinEntity.PinName == "ReturnValue")
.find(pinEntity => pinEntity.PinName.toString() == "ReturnValue")
?.PinType
?.PinSubCategoryObject
?.getName()
@@ -190,7 +215,7 @@ export default function nodeTitle(entity) {
return `Switch on ${switchTarget}`
}
if (entity.isComment()) {
return entity.NodeComment
return entity.NodeComment.toString()
}
const keyNameSymbol = entity.getHIDAttribute()
if (keyNameSymbol) {
@@ -213,7 +238,7 @@ export default function nodeTitle(entity) {
}
if (entity.isPcg() && entity.getPcgSubobject()) {
let pcgSubobject = entity.getPcgSubobject()
let result = pcgSubobject.NodeTitle ? pcgSubobject.NodeTitle : nodeTitle(pcgSubobject)
let result = pcgSubobject.NodeTitle ? pcgSubobject.NodeTitle.toString() : nodeTitle(pcgSubobject)
return result
}
const subgraphObject = entity.getSubgraphObject()
@@ -232,7 +257,7 @@ export default function nodeTitle(entity) {
return Utility.formatStringName(settingsObject.BlueprintElementType.getName())
}
if (settingsObject.Operation) {
const match = settingsObject.Name.match(/PCGMetadata(\w+)Settings_\d+/)
const match = settingsObject.Name?.toString().match(/PCGMetadata(\w+)Settings_\d+/)
if (match) {
return Utility.formatStringName(match[1] + ": " + settingsObject.Operation)
}
@@ -242,7 +267,7 @@ export default function nodeTitle(entity) {
return settingsSubgraphObject.Graph.getName()
}
}
let memberName = entity.FunctionReference?.MemberName
let memberName = entity.FunctionReference?.MemberName?.toString()
if (memberName) {
const memberParent = entity.FunctionReference.MemberParent?.path ?? ""
switch (memberName) {
@@ -379,7 +404,7 @@ export default function nodeTitle(entity) {
return Utility.formatStringName(memberName)
}
if (entity.OpName) {
switch (entity.OpName) {
switch (entity.OpName.toString()) {
case "Boolean::LogicAnd": return "Logic AND"
case "Boolean::LogicEq": return "=="
case "Boolean::LogicNEq": return "!="
@@ -392,10 +417,10 @@ export default function nodeTitle(entity) {
case "Numeric::DistancePos": return "Distance"
case "Numeric::Mul": return String.fromCharCode(0x2a2f)
}
return Utility.formatStringName(entity.OpName).replaceAll("::", " ")
return Utility.formatStringName(entity.OpName.toString()).replaceAll("::", " ")
}
if (entity.FunctionDisplayName) {
return Utility.formatStringName(entity.FunctionDisplayName)
return Utility.formatStringName(entity.FunctionDisplayName.toString())
}
if (entity.ObjectRef) {
return entity.ObjectRef.getName()

View File

@@ -1,10 +1,13 @@
import Configuration from "../Configuration.js"
import ArrayEntity from "../entity/ArrayEntity.js"
import GuidEntity from "../entity/GuidEntity.js"
import NaturalNumberEntity from "../entity/NaturalNumberEntity.js"
import PinEntity from "../entity/PinEntity.js"
import StringEntity from "../entity/StringEntity.js"
/** @param {PinEntity} pinEntity */
const indexFromUpperCaseLetterName = pinEntity =>
pinEntity.PinName.match(/^\s*([A-Z])\s*$/)?.[1]?.charCodeAt(0) - "A".charCodeAt(0)
pinEntity.PinName?.toString().match(/^\s*([A-Z])\s*$/)?.[1]?.charCodeAt(0) - "A".charCodeAt(0)
/** @param {ObjectEntity} entity */
export default function nodeVariadic(entity) {
@@ -15,11 +18,12 @@ export default function nodeVariadic(entity) {
/** @type {(newPinIndex: Number, minIndex: Number, maxIndex: Number, newPin: PinEntity) => String} */
let pinNameFromIndex
const type = entity.getType()
let prefix
let name
switch (type) {
case Configuration.paths.commutativeAssociativeBinaryOperator:
case Configuration.paths.promotableOperator:
name = entity.FunctionReference?.MemberName
name = entity.FunctionReference?.MemberName?.toString()
switch (name) {
default:
if (
@@ -50,17 +54,22 @@ export default function nodeVariadic(entity) {
pinIndexFromEntity ??= indexFromUpperCaseLetterName
pinNameFromIndex ??= (index, min = -1, max = -1) => {
const result = String.fromCharCode(index >= 0 ? index : max + "A".charCodeAt(0) + 1)
entity.NumAdditionalInputs = pinEntities().length - 1
entity.NumAdditionalInputs = new NaturalNumberEntity(pinEntities().length - 1)
return result
}
break
}
break
case Configuration.paths.executionSequence:
prefix ??= "Then"
case Configuration.paths.multiGate:
prefix ??= "Out"
pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isOutput())
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*Out[_\s]+(\d+)\s*$/i)?.[1])
pinIndexFromEntity ??= pinEntity => Number(
pinEntity.PinName?.toString().match(new RegExp(String.raw`^\s*${prefix}[_\s]+(\d+)\s*$`, "i"))?.[1]
)
pinNameFromIndex ??= (index, min = -1, max = -1, newPin) =>
`Out ${index >= 0 ? index : min > 0 ? "Out 0" : max + 1}`
`${prefix} ${index >= 0 ? index : min > 0 ? `${prefix} 0` : max + 1}`
break
// case Configuration.paths.niagaraNodeOp:
// pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isInput())
@@ -74,26 +83,26 @@ export default function nodeVariadic(entity) {
// break
case Configuration.paths.switchInteger:
pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isOutput())
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*(\d+)\s*$/)?.[1])
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName?.toString().match(/^\s*(\d+)\s*$/)?.[1])
pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => (index < 0 ? max + 1 : index).toString()
break
case Configuration.paths.switchGameplayTag:
pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => {
const result = `Case_${index >= 0 ? index : min > 0 ? "0" : max + 1}`
entity.PinNames ??= []
entity.PinNames.push(result)
delete entity.PinTags[entity.PinTags.length - 1]
entity.PinTags[entity.PinTags.length] = null
entity.PinNames ??= new ArrayEntity()
entity.PinNames.valueOf().push(new StringEntity(result))
delete entity.PinTags.valueOf()[entity.PinTags.length - 1]
entity.PinTags.valueOf()[entity.PinTags.length] = null
return result
}
case Configuration.paths.switchName:
case Configuration.paths.switchString:
pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isOutput())
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*Case[_\s]+(\d+)\s*$/i)?.[1])
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.toString().match(/^\s*Case[_\s]+(\d+)\s*$/i)?.[1])
pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => {
const result = `Case_${index >= 0 ? index : min > 0 ? "0" : max + 1}`
entity.PinNames ??= []
entity.PinNames.push(result)
entity.PinNames ??= new ArrayEntity()
entity.PinNames.valueOf().push(new StringEntity(result))
return result
}
break
@@ -138,9 +147,13 @@ export default function nodeVariadic(entity) {
}
)
const newPin = new PinEntity(modelPin)
newPin.PinId = GuidEntity.generateGuid()
newPin.PinName = pinNameFromIndex(index, min, max, newPin)
newPin.PinId = new GuidEntity()
newPin.PinName = new StringEntity(pinNameFromIndex(index, min, max, newPin))
newPin.PinToolTip = undefined
if (newPin.DefaultValue) {
// @ts-expect-error
newPin.DefaultValue = new (newPin.DefaultValue.constructor)()
}
entity.getCustomproperties(true).push(newPin)
return newPin
}

View File

@@ -52,17 +52,17 @@ const colors = {
const pinColorMaterial = css`120, 120, 120`
/** @param {PinEntity} entity */
/** @param {PinEntity<IEntity>} entity */
export default function pinColor(entity) {
if (entity.PinType.PinCategory == "mask") {
if (entity.PinType.PinCategory?.toString() === "mask") {
const result = colors[entity.PinType.PinSubCategory]
if (result) {
return result
}
} else if (entity.PinType.PinCategory == "optional") {
} else if (entity.PinType.PinCategory?.toString() === "optional") {
return pinColorMaterial
}
return colors[entity.getType()]
?? colors[entity.PinType.PinCategory.toLowerCase()]
?? colors[entity.PinType.PinCategory?.toString().toLowerCase()]
?? colors["default"]
}

View File

@@ -16,6 +16,16 @@ import Vector4DPinTemplate from "../template/pin/Vector4DPinTemplate.js"
import VectorPinTemplate from "../template/pin/VectorPinTemplate.js"
const inputPinTemplates = {
"bool": BoolPinTemplate,
"byte": IntPinTemplate,
"enum": EnumPinTemplate,
"int": IntPinTemplate,
"int64": Int64PinTemplate,
"MUTABLE_REFERENCE": ReferencePinTemplate,
"name": NamePinTemplate,
"real": RealPinTemplate,
"rg": Vector2DPinTemplate,
"string": StringPinTemplate,
[Configuration.paths.linearColor]: LinearColorPinTemplate,
[Configuration.paths.niagaraBool]: BoolPinTemplate,
[Configuration.paths.niagaraPosition]: VectorPinTemplate,
@@ -24,28 +34,19 @@ const inputPinTemplates = {
[Configuration.paths.vector2D]: Vector2DPinTemplate,
[Configuration.paths.vector3f]: VectorPinTemplate,
[Configuration.paths.vector4f]: Vector4DPinTemplate,
"bool": BoolPinTemplate,
"byte": IntPinTemplate,
"enum": EnumPinTemplate,
"int": IntPinTemplate,
"int64": Int64PinTemplate,
"MUTABLE_REFERENCE": ReferencePinTemplate,
"name": NamePinTemplate,
"rg": Vector2DPinTemplate,
"real": RealPinTemplate,
"string": StringPinTemplate,
}
/** @param {PinEntity} entity */
/** @param {PinEntity<IEntity>} entity */
export default function pinTemplate(entity) {
if (entity.PinType.ContainerType?.toString() === "Array") {
return PinTemplate
}
if (entity.PinType.bIsReference && !entity.PinType.bIsConst) {
if (entity.PinType.bIsReference?.valueOf() && !entity.PinType.bIsConst?.valueOf()) {
return inputPinTemplates["MUTABLE_REFERENCE"]
}
if (entity.getType() === "exec") {
const type = entity.getType()
if (type === "exec") {
return ExecPinTemplate
}
return (entity.isInput() ? inputPinTemplates[entity.getType()] : PinTemplate) ?? PinTemplate
return (entity.isInput() ? inputPinTemplates[type] : PinTemplate) ?? PinTemplate
}

View File

@@ -1,16 +1,12 @@
import Utility from "../Utility.js"
/** @param {PinEntity} entity */
/** @param {PinEntity<IEntity>} entity */
export default function pinTitle(entity) {
let result = entity.PinFriendlyName
? entity.PinFriendlyName.toString()
: Utility.formatStringName(entity.PinName ?? "")
: Utility.formatStringName(entity.PinName?.toString() ?? "")
let match
if (
entity.PinToolTip
// Match up until the first \n excluded or last character
&& (match = entity.PinToolTip.match(/\s*(.+?(?=\n)|.+\S)\s*/))
) {
if (match = entity.PinToolTip?.toString().match(/\s*(.+?(?=\n)|.+\S)\s*/)) {
if (match[1].toLowerCase() === result.toLowerCase()) {
return match[1] // In case they match, then keep the case of the PinToolTip
}