mirror of
https://github.com/barsdeveloper/ueblueprint.git
synced 2026-02-04 08:50:33 +08:00
Generic unknown keys object entity
This commit is contained in:
@@ -113,8 +113,10 @@ export default class Configuration {
|
||||
knot: "/Script/BlueprintGraph.K2Node_Knot",
|
||||
macro: "/Script/BlueprintGraph.K2Node_MacroInstance",
|
||||
makeArray: "/Script/BlueprintGraph.K2Node_MakeArray",
|
||||
makeMap: "/Script/BlueprintGraph.K2Node_MakeMap",
|
||||
pawn: "/Script/Engine.Pawn",
|
||||
reverseForEachLoop: "/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:ReverseForEachLoop",
|
||||
select: "/Script/BlueprintGraph.K2Node_Select",
|
||||
variableGet: "/Script/BlueprintGraph.K2Node_VariableGet",
|
||||
variableSet: "/Script/BlueprintGraph.K2Node_VariableSet",
|
||||
whileLoop: "/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:WhileLoop",
|
||||
|
||||
@@ -135,6 +135,19 @@ export default class SVGIcon {
|
||||
</svg>
|
||||
`
|
||||
|
||||
static makeMap = html`
|
||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15 4H10V6H15V4Z" fill="white"/>
|
||||
<path d="M15 7H10V9H15V7Z" fill="white"/>
|
||||
<path d="M15 10H10V12H15V10Z" fill="white"/>
|
||||
<path d="M9 4H7V6H9V4Z" fill="white"/>
|
||||
<path d="M9 7H7V9H9V7Z" fill="white"/>
|
||||
<path d="M9 10H7V12H9V10Z" fill="white"/>
|
||||
<path d="M3 4L1 1.99995L2 1L4 3L5 1.99995L5 5L2 5L3 4Z" fill="white"/>
|
||||
<path d="M4 13L1.99995 15L1 14L3 12L1.99995 11L5 11L5 14L4 13Z" fill="white"/>
|
||||
</svg>
|
||||
`
|
||||
|
||||
static makeStruct = html`
|
||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3 4L1 1.99995L2 1L4 3L5 1.99995L5 5L2 5L3 4Z" fill="white"/>
|
||||
@@ -149,6 +162,17 @@ export default class SVGIcon {
|
||||
</svg>
|
||||
`
|
||||
|
||||
static select = html`
|
||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="1" y="2" width="6" height="2" fill="white"/>
|
||||
<rect x="10" y="7" width="3" height="2" fill="white"/>
|
||||
<path d="M12 5L15 8L12 11V5Z" fill="white"/>
|
||||
<rect x="1" y="7" width="8" height="2" fill="white"/>
|
||||
<rect x="5" y="4" width="2" height="9" fill="white"/>
|
||||
<rect x="1" y="12" width="6" height="2" fill="white"/>
|
||||
</svg>
|
||||
`
|
||||
|
||||
static sequence = html`
|
||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="3" y="2" width="5" height="2" fill="white"/>
|
||||
|
||||
@@ -109,4 +109,9 @@ export default class IEntity extends Observable {
|
||||
}
|
||||
defineAllAttributes(this, attributes, values)
|
||||
}
|
||||
|
||||
unexpectedKeys() {
|
||||
// @ts-expect-error
|
||||
return Object.getOwnPropertyNames(this).length - Object.getOwnPropertyNames(this.constructor.attributes).length
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ export default class ObjectEntity extends IEntity {
|
||||
}
|
||||
|
||||
static nameRegex = /^(\w+?)(?:_(\d+))?$/
|
||||
static sequencerScriptingNameRegex = /\/Script\/SequencerScripting\.MovieSceneScripting(.+)Channel/
|
||||
|
||||
constructor(options = {}) {
|
||||
super(options)
|
||||
@@ -96,6 +97,12 @@ export default class ObjectEntity extends IEntity {
|
||||
let name = ""
|
||||
switch (this.getType()) {
|
||||
case Configuration.nodeType.callFunction:
|
||||
if (this.FunctionReference.MemberName === "AddKey") {
|
||||
let result = this.FunctionReference.MemberParent.path.match(ObjectEntity.sequencerScriptingNameRegex)
|
||||
if (result) {
|
||||
return `Add Key (${Utility.formatStringName(result[1])})`
|
||||
}
|
||||
}
|
||||
return Utility.formatStringName(this.FunctionReference.MemberName)
|
||||
case Configuration.nodeType.dynamicCast:
|
||||
return `Cast To ${this.TargetType.getName()}`
|
||||
|
||||
14
js/entity/UnknownKeysEntity.js
Normal file
14
js/entity/UnknownKeysEntity.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import IEntity from "./IEntity"
|
||||
import TypeInitialization from "./TypeInitialization"
|
||||
|
||||
export default class UnknownKeysEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
lookbehind: new TypeInitialization(String, false, "", false, true)
|
||||
}
|
||||
|
||||
constructor(values = {}) {
|
||||
super(values)
|
||||
/** @type {String} */ this.lookbehind
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,20 @@
|
||||
import IEntity from "./IEntity"
|
||||
import GuidEntity from "./GuidEntity"
|
||||
import TypeInitialization from "./TypeInitialization"
|
||||
|
||||
export default class VariableReferenceEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
MemberScope: new TypeInitialization(String, false),
|
||||
MemberName: String,
|
||||
MemberGuid: GuidEntity,
|
||||
bSelfContext: false,
|
||||
bSelfContext: new TypeInitialization(Boolean, false, false)
|
||||
}
|
||||
|
||||
constructor(values = {}) {
|
||||
super(values)
|
||||
/** @type {String} */ this.MemberName
|
||||
/** @type {GuidEntity} */ this.GuidEntity
|
||||
/** @type {Boolean} */ this.bSelfContext
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,10 @@ import ISerializer from "./ISerializer"
|
||||
*/
|
||||
export default class GeneralSerializer extends ISerializer {
|
||||
|
||||
/** @param {AnyValueConstructor<T>} entityType */
|
||||
/**
|
||||
* @param {(value: String, entity: T) => String} wrap
|
||||
* @param {AnyValueConstructor<T>} entityType
|
||||
*/
|
||||
constructor(wrap, entityType, prefix, separator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter) {
|
||||
wrap = wrap ?? (v => `(${v})`)
|
||||
super(entityType, prefix, separator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter)
|
||||
@@ -43,7 +46,7 @@ export default class GeneralSerializer extends ISerializer {
|
||||
* @returns {String}
|
||||
*/
|
||||
write(entity, object, insideString = false) {
|
||||
let result = this.wrap(this.subWrite(entity, [], object, insideString))
|
||||
let result = this.wrap(this.subWrite(entity, [], object, insideString), object)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,10 @@ import SimpleSerializationRotatorEntity from "../entity/SimpleSerializationRotat
|
||||
import SimpleSerializationVectorEntity from "../entity/SimpleSerializationVectorEntity"
|
||||
import TypeInitialization from "../entity/TypeInitialization"
|
||||
import UnionType from "../entity/UnionType"
|
||||
import UnknownKeysEntity from "../entity/UnknownKeysEntity"
|
||||
import Utility from "../Utility"
|
||||
import VectorEntity from "../entity/VectorEntity"
|
||||
import VariableReferenceEntity from "../entity/VariableReferenceEntity"
|
||||
import VectorEntity from "../entity/VectorEntity"
|
||||
|
||||
let P = Parsimmon
|
||||
|
||||
@@ -126,7 +127,8 @@ export default class Grammar {
|
||||
|
||||
/** @param {Grammar} r */
|
||||
static createAttributeGrammar = (r, entityType, valueSeparator = P.string("=").trim(P.optWhitespace)) =>
|
||||
r.AttributeName.skip(valueSeparator)
|
||||
r.AttributeName
|
||||
.skip(valueSeparator)
|
||||
.chain(attributeName => {
|
||||
// Once the attribute name is known, look into entityType.attributes to get its type
|
||||
const attributeKey = attributeName.split(".")
|
||||
@@ -152,17 +154,30 @@ export default class Grammar {
|
||||
(_0, attributes, _2) => {
|
||||
let values = {}
|
||||
attributes.forEach(attributeSetter => attributeSetter(values))
|
||||
return new entityType(values)
|
||||
return values
|
||||
}
|
||||
)
|
||||
// Decide if we accept the entity or not. It is accepted if it doesn't have too many unexpected keys
|
||||
.chain(values => {
|
||||
let unexpectedKeysCount = 0
|
||||
let totalKeys = 0
|
||||
for (const key in values) {
|
||||
unexpectedKeysCount += key in entityType.attributes ? 0 : 1
|
||||
++totalKeys
|
||||
}
|
||||
if (unexpectedKeysCount + 0.5 > Math.sqrt(totalKeys)) {
|
||||
return P.fail()
|
||||
}
|
||||
return P.succeed().map(() => new entityType(values))
|
||||
})
|
||||
|
||||
/* --- General --- */
|
||||
|
||||
/** @param {Grammar} r */
|
||||
InlineWhitespace = r => P.regex(/[^\S\n]+/).desc("inline whitespace")
|
||||
InlineWhitespace = r => P.regex(/[^\S\n]+/).desc("single line whitespace")
|
||||
|
||||
/** @param {Grammar} r */
|
||||
InlineOptWhitespace = r => P.regex(/[^\S\n]*/).desc("inline optional whitespace")
|
||||
InlineOptWhitespace = r => P.regex(/[^\S\n]*/).desc("single line optional whitespace")
|
||||
|
||||
/** @param {Grammar} r */
|
||||
MultilineWhitespace = r => P.regex(/[^\S\n]*\n\s*/).desc("whitespace with at least a newline")
|
||||
@@ -275,18 +290,20 @@ export default class Grammar {
|
||||
|
||||
/** @param {Grammar} r */
|
||||
AttributeAnyValue = r => P.alt(
|
||||
r.Null,
|
||||
r.None,
|
||||
// Remember to keep the order, otherwise parsing might fail
|
||||
r.Boolean,
|
||||
r.Number,
|
||||
r.Integer,
|
||||
r.String,
|
||||
r.Guid,
|
||||
r.None,
|
||||
r.Null,
|
||||
r.Integer,
|
||||
r.Number,
|
||||
r.String,
|
||||
r.LocalizedText,
|
||||
r.InvariantText,
|
||||
r.ObjectReference,
|
||||
r.Vector,
|
||||
r.LinearColor,
|
||||
r.UnknownKeys,
|
||||
r.ObjectReference,
|
||||
)
|
||||
|
||||
/** @param {Grammar} r */
|
||||
@@ -455,4 +472,29 @@ export default class Grammar {
|
||||
r.LinearColorFromRGB,
|
||||
r.LinearColorFromRGBA,
|
||||
)
|
||||
|
||||
/** @param {Grammar} r */
|
||||
UnknownKeys = r => P.seqMap(
|
||||
P.regex(/\w*\s*/).skip(P.string("(")),
|
||||
P.seqMap(
|
||||
r.AttributeName,
|
||||
P.string("=").trim(P.optWhitespace),
|
||||
r.AttributeAnyValue,
|
||||
(attributeName, separator, attributeValue) =>
|
||||
entity => Utility.objectSet(entity, attributeName.split("."), attributeValue, true)
|
||||
)
|
||||
.trim(P.optWhitespace)
|
||||
.sepBy(P.string(",")) // Assignments are separated by comma
|
||||
.skip(P.regex(/,?/).then(P.optWhitespace)), // Optional trailing comma and maybe additional space
|
||||
P.string(")"),
|
||||
(lookbehind, attributes, _2) => {
|
||||
let values = {}
|
||||
attributes.forEach(attributeSetter => attributeSetter(values))
|
||||
let result = new UnknownKeysEntity(values)
|
||||
if (lookbehind) {
|
||||
result.lookbehind = lookbehind
|
||||
}
|
||||
return result
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import SerializerFactory from "./SerializerFactory"
|
||||
import SimpleSerializationRotatorEntity from "../entity/SimpleSerializationRotatorEntity"
|
||||
import SimpleSerializationVectorEntity from "../entity/SimpleSerializationVectorEntity"
|
||||
import ToStringSerializer from "./ToStringSerializer"
|
||||
import UnknownKeysEntity from "../entity/UnknownKeysEntity"
|
||||
import Utility from "../Utility"
|
||||
import VariableReferenceEntity from "../entity/VariableReferenceEntity"
|
||||
import VectorEntity from "../entity/VectorEntity"
|
||||
@@ -192,6 +193,11 @@ export default function initializeSerializerFactory() {
|
||||
)
|
||||
)
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
UnknownKeysEntity,
|
||||
new GeneralSerializer((string, entity) => `${entity.lookbehind ?? ""}(${string})`, UnknownKeysEntity)
|
||||
)
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
VariableReferenceEntity,
|
||||
new GeneralSerializer(bracketsWrapped, VariableReferenceEntity)
|
||||
|
||||
@@ -24,6 +24,8 @@ export default class NodeTemplate extends ISelectableDraggableTemplate {
|
||||
[Configuration.nodeType.forLoopWithBreak]: SVGIcon.loop,
|
||||
[Configuration.nodeType.ifThenElse]: SVGIcon.branchNode,
|
||||
[Configuration.nodeType.makeArray]: SVGIcon.makeArray,
|
||||
[Configuration.nodeType.makeMap]: SVGIcon.makeMap,
|
||||
[Configuration.nodeType.select]: SVGIcon.select,
|
||||
[Configuration.nodeType.whileLoop]: SVGIcon.loop,
|
||||
default: SVGIcon.functionSymbol
|
||||
}
|
||||
@@ -45,6 +47,8 @@ export default class NodeTemplate extends ISelectableDraggableTemplate {
|
||||
}
|
||||
return functionColor
|
||||
case Configuration.nodeType.makeArray:
|
||||
case Configuration.nodeType.makeMap:
|
||||
case Configuration.nodeType.select:
|
||||
return pureFunctionColor
|
||||
case Configuration.nodeType.macro:
|
||||
case Configuration.nodeType.executionSequence:
|
||||
|
||||
Reference in New Issue
Block a user