전투 대상 표시 UI 및 정수 표기 검증 반영 #118

Merged
maple merged 2 commits from codex/combat-target-indicators into main 2026-07-16 01:56:43 +09:00
5 changed files with 385 additions and 212 deletions

190
Global/UIText.model Normal file
View File

@@ -0,0 +1,190 @@
{
"Id": "",
"GameId": "",
"EntryKey": "model://uitext",
"ContentType": "x-mod/model",
"Content": "",
"Usage": 0,
"UsePublish": 1,
"UseService": 0,
"CoreVersion": "26.5.0.0",
"StudioVersion": "0.1.0.0",
"DynamicLoading": 0,
"ContentProto": {
"Use": "Json",
"Json": {
"Version": 1,
"Name": "UIText",
"BaseModelId": null,
"Id": "uitext",
"Components": [
"MOD.Core.UITransformComponent",
"MOD.Core.SpriteGUIRendererComponent"
],
"Properties": [
{
"Type": {
"$type": "MODNativeType",
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
},
"Name": "RectSize",
"DisplayName": "RectSize",
"ShowInInspector": true,
"Link": {
"Target": {
"$type": "MODNativeType",
"type": "MOD.Core.UITransformComponent, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
},
"Property": "RectSize"
}
},
{
"Type": {
"$type": "MODNativeType",
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
},
"Name": "Text",
"DisplayName": "Text",
"ShowInInspector": true,
"Link": {
"Target": {
"$type": "MODNativeType",
"type": "MOD.Core.TextComponent, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
},
"Property": "Text"
}
},
{
"Type": {
"$type": "MODNativeType",
"type": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
},
"Name": "FontSize",
"DisplayName": "FontSize",
"ShowInInspector": true,
"Link": {
"Target": {
"$type": "MODNativeType",
"type": "MOD.Core.TextComponent, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
},
"Property": "FontSize"
}
},
{
"Type": {
"$type": "MODNativeType",
"type": "MOD.Core.MODColor, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
},
"Name": "FontColor",
"DisplayName": "FontColor",
"ShowInInspector": true,
"Link": {
"Target": {
"$type": "MODNativeType",
"type": "MOD.Core.TextComponent, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
},
"Property": "FontColor"
}
},
{
"Type": {
"$type": "MODNativeType",
"type": "MOD.Core.MODDataRef, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
},
"Name": "ImageRUID",
"DisplayName": "ImageRUID",
"ShowInInspector": true,
"Link": {
"Target": {
"$type": "MODNativeType",
"type": "MOD.Core.SpriteGUIRendererComponent, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
},
"Property": "ImageRUID"
}
},
{
"Type": {
"$type": "MODNativeType",
"type": "MOD.Core.MODColor, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
},
"Name": "Color",
"DisplayName": "Color",
"ShowInInspector": true,
"Link": {
"Target": {
"$type": "MODNativeType",
"type": "MOD.Core.SpriteGUIRendererComponent, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
},
"Property": "Color"
}
}
],
"Values": [
{
"TargetType": "MOD.Core.UITransformComponent",
"Name": "anchoredPosition",
"ValueType": {
"$type": "MODNativeType",
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
},
"Value": {
"$type": "MOD.Core.MODVector2, MOD.Core",
"x": 0.0,
"y": 0.0
}
},
{
"TargetType": "MOD.Core.UITransformComponent",
"Name": "RectSize",
"ValueType": {
"$type": "MODNativeType",
"type": "MOD.Core.MODVector2, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
},
"Value": {
"$type": "MOD.Core.MODVector2, MOD.Core",
"x": 100.0,
"y": 100.0
}
},
{
"TargetType": "MOD.Core.UITransformComponent",
"Name": "AlignmentOption",
"ValueType": {
"$type": "MODNativeType",
"type": "MOD.Core.AlignmentType, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
},
"Value": 0
},
{
"TargetType": "MOD.Core.SpriteGUIRendererComponent",
"Name": "ImageRUID",
"ValueType": {
"$type": "MODNativeType",
"type": "MOD.Core.MODDataRef, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
},
"Value": {
"$type": "MOD.Core.MODDataRef, MOD.Core",
"DataId": ""
}
},
{
"TargetType": "MOD.Core.SpriteGUIRendererComponent",
"Name": "Color",
"ValueType": {
"$type": "MODNativeType",
"type": "MOD.Core.MODColor, MOD.Core, Version=26.5.0.0, Culture=neutral, PublicKeyToken=null"
},
"Value": {
"$type": "MOD.Core.MODColor, MOD.Core",
"r": 0.0,
"g": 0.0,
"b": 0.0,
"a": 0.0
}
}
],
"EventLinks": [],
"Children": []
}
}
}

18
docs/integer-format.md Normal file
View File

@@ -0,0 +1,18 @@
# Integer Formatting Rules
MapleStory Worlds may serialize numeric properties as strings such as `1.0`.
This is valid numeric data, but it must never be used directly for UI entity
names or paths because `Card1.0` and `DmgPop_1.0` do not exist.
- Use `self:IntStr(value)` for every numeric suffix in a UI path or entity name.
- Use `string.format("%d", math.floor(value or 0))` or `FormatNumber` for an
integer shown in UI text.
- Do not concatenate `tostring(number)` into a `/ui/...` path.
- `tools/deck/gen-slaydeck.mjs` normalizes exact integer strings such as `0.0`
to `0` when regenerating `Global/common.gamelogic`.
Run this check after editing card-controller UI paths:
```powershell
node tools/verify/integer-format.mjs
```

View File

@@ -213,6 +213,21 @@ function writeCodeblocks() {
writeFileSync('RootDesk/MyDesk/SlayDeckController.codeblock', JSON.stringify(combat, null, 2), 'utf8'); writeFileSync('RootDesk/MyDesk/SlayDeckController.codeblock', JSON.stringify(combat, null, 2), 'utf8');
} }
function normalizeIntegerStrings(value) {
if (Array.isArray(value)) {
for (let index = 0; index < value.length; index++) normalizeIntegerStrings(value[index]);
return;
}
if (value == null || typeof value !== 'object') return;
for (const [key, child] of Object.entries(value)) {
if (typeof child === 'string') {
value[key] = child.replace(/^(-?\d+)\.0+$/, '$1');
} else {
normalizeIntegerStrings(child);
}
}
}
function patchCommon() { function patchCommon() {
const common = JSON.parse(readFileSync(COMMON_FILE, 'utf8')); const common = JSON.parse(readFileSync(COMMON_FILE, 'utf8'));
const entity = common.ContentProto.Entities.find((e) => e.path === '/common'); const entity = common.ContentProto.Entities.find((e) => e.path === '/common');
@@ -220,6 +235,7 @@ function patchCommon() {
entity.jsonString['@components'] = [ entity.jsonString['@components'] = [
{ '@type': 'script.SlayDeckController', Enable: true, Energy: 0, MaxEnergy: 3, Turn: 0, TweenEventId: 0 }, { '@type': 'script.SlayDeckController', Enable: true, Energy: 0, MaxEnergy: 3, Turn: 0, TweenEventId: 0 },
]; ];
normalizeIntegerStrings(common);
JSON.parse(JSON.stringify(common)); JSON.parse(JSON.stringify(common));
writeFileSync(COMMON_FILE, JSON.stringify(common, null, 2), 'utf8'); writeFileSync(COMMON_FILE, JSON.stringify(common, null, 2), 'utf8');
} }

View File

@@ -0,0 +1,31 @@
import { readFileSync, readdirSync } from 'node:fs';
import { join } from 'node:path';
const cbDir = 'tools/deck/cb';
const files = readdirSync(cbDir).filter((name) => name.endsWith('.mjs'));
const violations = [];
for (const file of files) {
const source = readFileSync(join(cbDir, file), 'utf8');
const matches = source.match(/["']\/ui\/[^"']*["']\s*\.\.\s*tostring\(/g) || [];
for (const match of matches) violations.push(`${file}: ${match}`);
}
const generator = readFileSync('tools/deck/gen-slaydeck.mjs', 'utf8');
if (!generator.includes('normalizeIntegerStrings(common)')) {
violations.push('gen-slaydeck.mjs: common.gamelogic integer-string normalization is missing');
}
const common = readFileSync('Global/common.gamelogic', 'utf8');
const commonIntegerDecimals = common.match(/(?<![0-9.])-?[0-9]+\.0+(?![0-9])/g) || [];
if (commonIntegerDecimals.length > 0) {
violations.push(`Global/common.gamelogic: integer decimal values ${commonIntegerDecimals.length}`);
}
if (violations.length > 0) {
console.error('UI path integer formatting violations:');
for (const violation of violations) console.error(`- ${violation}`);
process.exit(1);
}
console.log(`integer format: UI path tostring violations 0, common integer decimals 0 (${files.length} modules checked)`);

View File

@@ -53043,7 +53043,7 @@
{ {
"id": "0cb0016a-0000-4000-8000-00000cb0016a", "id": "0cb0016a-0000-4000-8000-00000cb0016a",
"path": "/ui/RunUIGroup/CombatHud/MonsterStatus1/TargetMarker", "path": "/ui/RunUIGroup/CombatHud/MonsterStatus1/TargetMarker",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent",
"jsonString": { "jsonString": {
"name": "TargetMarker", "name": "TargetMarker",
"path": "/ui/RunUIGroup/CombatHud/MonsterStatus1/TargetMarker", "path": "/ui/RunUIGroup/CombatHud/MonsterStatus1/TargetMarker",
@@ -53126,7 +53126,7 @@
"AnimClipPlayType": 0, "AnimClipPlayType": 0,
"EndFrameIndex": 2147483647, "EndFrameIndex": 2147483647,
"ImageRUID": { "ImageRUID": {
"DataId": "" "DataId": "f86110df71ce48dbbcbc8d38bd1076ad"
}, },
"LocalPosition": { "LocalPosition": {
"x": 0.0, "x": 0.0,
@@ -53141,10 +53141,10 @@
"PreserveSprite": 0, "PreserveSprite": 0,
"StartFrameIndex": 0, "StartFrameIndex": 0,
"Color": { "Color": {
"r": 0.95, "r": 1.0,
"g": 0.08, "g": 1.0,
"b": 0.05, "b": 1.0,
"a": 0.0 "a": 1.0
}, },
"DropShadow": false, "DropShadow": false,
"DropShadowAngle": 30.0, "DropShadowAngle": 30.0,
@@ -53176,53 +53176,6 @@
"RaycastTarget": false, "RaycastTarget": false,
"Type": 1, "Type": 1,
"Enable": true "Enable": true
},
{
"@type": "MOD.Core.TextComponent",
"Alignment": 4,
"Bold": true,
"DropShadow": false,
"DropShadowAngle": 30.0,
"DropShadowColor": {
"r": 0.0,
"g": 0.0,
"b": 0.0,
"a": 0.72
},
"DropShadowDistance": 32.0,
"Font": 0,
"FontColor": {
"r": 1.0,
"g": 0.94,
"b": 0.28,
"a": 1.0
},
"FontSize": 30,
"MaxSize": 72,
"MinSize": 8,
"OutlineColor": {
"r": 0.08,
"g": 0.08,
"b": 0.08,
"a": 1.0
},
"OutlineDistance": {
"x": 1.0,
"y": -1.0
},
"OutlineWidth": 4.0,
"Overflow": 0,
"OverrideSorting": false,
"Padding": {
"left": 0,
"right": 0,
"top": 0,
"bottom": 0
},
"SizeFit": false,
"Text": "△",
"UseOutLine": true,
"Enable": true
} }
], ],
"@version": 1 "@version": 1
@@ -54783,7 +54736,7 @@
{ {
"id": "2d9fa3fe-1072-4215-931d-c52b770f5e3b", "id": "2d9fa3fe-1072-4215-931d-c52b770f5e3b",
"path": "/ui/RunUIGroup/CombatHud/MonsterStatus2/TargetMarker", "path": "/ui/RunUIGroup/CombatHud/MonsterStatus2/TargetMarker",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent",
"jsonString": { "jsonString": {
"name": "TargetMarker", "name": "TargetMarker",
"path": "/ui/RunUIGroup/CombatHud/MonsterStatus2/TargetMarker", "path": "/ui/RunUIGroup/CombatHud/MonsterStatus2/TargetMarker",
@@ -54866,7 +54819,7 @@
"AnimClipPlayType": 0, "AnimClipPlayType": 0,
"EndFrameIndex": 2147483647, "EndFrameIndex": 2147483647,
"ImageRUID": { "ImageRUID": {
"DataId": "" "DataId": "f86110df71ce48dbbcbc8d38bd1076ad"
}, },
"LocalPosition": { "LocalPosition": {
"x": 0.0, "x": 0.0,
@@ -54882,10 +54835,10 @@
"SortingLayer": "MapLayer1", "SortingLayer": "MapLayer1",
"StartFrameIndex": 0, "StartFrameIndex": 0,
"Color": { "Color": {
"r": 0.95, "r": 1.0,
"g": 0.08, "g": 1.0,
"b": 0.05, "b": 1.0,
"a": 0.0 "a": 1.0
}, },
"DropShadow": false, "DropShadow": false,
"DropShadowAngle": 30.0, "DropShadowAngle": 30.0,
@@ -54917,53 +54870,6 @@
"RaycastTarget": false, "RaycastTarget": false,
"Type": 1, "Type": 1,
"Enable": true "Enable": true
},
{
"@type": "MOD.Core.TextComponent",
"Alignment": 4,
"Bold": true,
"DropShadow": false,
"DropShadowAngle": 30.0,
"DropShadowColor": {
"r": 0.0,
"g": 0.0,
"b": 0.0,
"a": 0.72
},
"DropShadowDistance": 32.0,
"Font": 0,
"FontColor": {
"r": 1.0,
"g": 0.94,
"b": 0.28,
"a": 1.0
},
"FontSize": 30,
"MaxSize": 72,
"MinSize": 8,
"OutlineColor": {
"r": 0.08,
"g": 0.08,
"b": 0.08,
"a": 1.0
},
"OutlineDistance": {
"x": 1.0,
"y": -1.0
},
"OutlineWidth": 4.0,
"Overflow": 0,
"OverrideSorting": false,
"Padding": {
"left": 0,
"right": 0,
"top": 0,
"bottom": 0
},
"SizeFit": false,
"Text": "△",
"UseOutLine": true,
"Enable": true
} }
], ],
"@version": 1 "@version": 1
@@ -56532,7 +56438,7 @@
{ {
"id": "d855de11-ba35-4d79-90be-144e4a4f5127", "id": "d855de11-ba35-4d79-90be-144e4a4f5127",
"path": "/ui/RunUIGroup/CombatHud/MonsterStatus3/TargetMarker", "path": "/ui/RunUIGroup/CombatHud/MonsterStatus3/TargetMarker",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent",
"jsonString": { "jsonString": {
"name": "TargetMarker", "name": "TargetMarker",
"path": "/ui/RunUIGroup/CombatHud/MonsterStatus3/TargetMarker", "path": "/ui/RunUIGroup/CombatHud/MonsterStatus3/TargetMarker",
@@ -56615,7 +56521,7 @@
"AnimClipPlayType": 0, "AnimClipPlayType": 0,
"EndFrameIndex": 2147483647, "EndFrameIndex": 2147483647,
"ImageRUID": { "ImageRUID": {
"DataId": "" "DataId": "f86110df71ce48dbbcbc8d38bd1076ad"
}, },
"LocalPosition": { "LocalPosition": {
"x": 0.0, "x": 0.0,
@@ -56631,10 +56537,10 @@
"SortingLayer": "MapLayer1", "SortingLayer": "MapLayer1",
"StartFrameIndex": 0, "StartFrameIndex": 0,
"Color": { "Color": {
"r": 0.95, "r": 1.0,
"g": 0.08, "g": 1.0,
"b": 0.05, "b": 1.0,
"a": 0.0 "a": 1.0
}, },
"DropShadow": false, "DropShadow": false,
"DropShadowAngle": 30.0, "DropShadowAngle": 30.0,
@@ -56666,53 +56572,6 @@
"RaycastTarget": false, "RaycastTarget": false,
"Type": 1, "Type": 1,
"Enable": true "Enable": true
},
{
"@type": "MOD.Core.TextComponent",
"Alignment": 4,
"Bold": true,
"DropShadow": false,
"DropShadowAngle": 30.0,
"DropShadowColor": {
"r": 0.0,
"g": 0.0,
"b": 0.0,
"a": 0.72
},
"DropShadowDistance": 32.0,
"Font": 0,
"FontColor": {
"r": 1.0,
"g": 0.94,
"b": 0.28,
"a": 1.0
},
"FontSize": 30,
"MaxSize": 72,
"MinSize": 8,
"OutlineColor": {
"r": 0.08,
"g": 0.08,
"b": 0.08,
"a": 1.0
},
"OutlineDistance": {
"x": 1.0,
"y": -1.0
},
"OutlineWidth": 4.0,
"Overflow": 0,
"OverrideSorting": false,
"Padding": {
"left": 0,
"right": 0,
"top": 0,
"bottom": 0
},
"SizeFit": false,
"Text": "△",
"UseOutLine": true,
"Enable": true
} }
], ],
"@version": 1 "@version": 1
@@ -58281,7 +58140,7 @@
{ {
"id": "dc4ad9ad-af51-492d-9dda-dc904df8395a", "id": "dc4ad9ad-af51-492d-9dda-dc904df8395a",
"path": "/ui/RunUIGroup/CombatHud/MonsterStatus4/TargetMarker", "path": "/ui/RunUIGroup/CombatHud/MonsterStatus4/TargetMarker",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent",
"jsonString": { "jsonString": {
"name": "TargetMarker", "name": "TargetMarker",
"path": "/ui/RunUIGroup/CombatHud/MonsterStatus4/TargetMarker", "path": "/ui/RunUIGroup/CombatHud/MonsterStatus4/TargetMarker",
@@ -58364,7 +58223,7 @@
"AnimClipPlayType": 0, "AnimClipPlayType": 0,
"EndFrameIndex": 2147483647, "EndFrameIndex": 2147483647,
"ImageRUID": { "ImageRUID": {
"DataId": "" "DataId": "f86110df71ce48dbbcbc8d38bd1076ad"
}, },
"LocalPosition": { "LocalPosition": {
"x": 0.0, "x": 0.0,
@@ -58380,10 +58239,10 @@
"SortingLayer": "MapLayer1", "SortingLayer": "MapLayer1",
"StartFrameIndex": 0, "StartFrameIndex": 0,
"Color": { "Color": {
"r": 0.95, "r": 1.0,
"g": 0.08, "g": 1.0,
"b": 0.05, "b": 1.0,
"a": 0.0 "a": 1.0
}, },
"DropShadow": false, "DropShadow": false,
"DropShadowAngle": 30.0, "DropShadowAngle": 30.0,
@@ -58415,53 +58274,6 @@
"RaycastTarget": false, "RaycastTarget": false,
"Type": 1, "Type": 1,
"Enable": true "Enable": true
},
{
"@type": "MOD.Core.TextComponent",
"Alignment": 4,
"Bold": true,
"DropShadow": false,
"DropShadowAngle": 30.0,
"DropShadowColor": {
"r": 0.0,
"g": 0.0,
"b": 0.0,
"a": 0.72
},
"DropShadowDistance": 32.0,
"Font": 0,
"FontColor": {
"r": 1.0,
"g": 0.94,
"b": 0.28,
"a": 1.0
},
"FontSize": 30,
"MaxSize": 72,
"MinSize": 8,
"OutlineColor": {
"r": 0.08,
"g": 0.08,
"b": 0.08,
"a": 1.0
},
"OutlineDistance": {
"x": 1.0,
"y": -1.0
},
"OutlineWidth": 4.0,
"Overflow": 0,
"OverrideSorting": false,
"Padding": {
"left": 0,
"right": 0,
"top": 0,
"bottom": 0
},
"SizeFit": false,
"Text": "△",
"UseOutLine": true,
"Enable": true
} }
], ],
"@version": 1 "@version": 1
@@ -86245,6 +86057,112 @@
], ],
"@version": 1 "@version": 1
} }
},
{
"id": "adb87906-c48e-4425-ad89-299bf9f9d96c",
"path": "/ui/RunUIGroup/CombatHud/PlayerTargetMarker",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent",
"jsonString": {
"name": "PlayerTargetMarker",
"path": "/ui/RunUIGroup/CombatHud/PlayerTargetMarker",
"nameEditable": true,
"enable": true,
"visible": true,
"localize": true,
"displayOrder": 30,
"pathConstraints": "////",
"revision": 1,
"origin": {
"type": "Model",
"entry_id": "uiempty",
"sub_entity_id": null,
"root_entity_id": "adb87906-c48e-4425-ad89-299bf9f9d96c",
"replaced_model_id": null
},
"modelId": "uiempty",
"@components": [
{
"@type": "MOD.Core.UITransformComponent",
"AlignmentOption": 0,
"AnchorsMax": {
"x": 0.5,
"y": 0.5
},
"AnchorsMin": {
"x": 0.5,
"y": 0.5
},
"MobileOnly": false,
"OffsetMax": {
"x": -1516.52332,
"y": -970.8926
},
"OffsetMin": {
"x": -1596.52332,
"y": -1050.89258
},
"Pivot": {
"x": 0.5,
"y": 0.5
},
"RectSize": {
"x": 80.0,
"y": 80.0
},
"UIMode": 1,
"UIScale": {
"x": 1.0,
"y": 1.0,
"z": 1.0
},
"UIVersion": 2,
"anchoredPosition": {
"x": -1556.52332,
"y": -1010.89258
},
"Rotation": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"Position": {
"x": -1556.52332,
"y": -1010.89258,
"z": 1254.9397
},
"QuaternionRotation": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"w": -1.0
},
"Scale": {
"x": 1.0,
"y": 1.0,
"z": 1.0
},
"ZRotation": 0.0,
"Enable": true
},
{
"@type": "MOD.Core.SpriteGUIRendererComponent",
"ImageRUID": {
"DataId": "f86110df71ce48dbbcbc8d38bd1076ad"
},
"OverrideSorting": false,
"Color": {
"r": 1.0,
"g": 1.0,
"b": 1.0,
"a": 1.0
},
"DropShadow": false,
"RaycastTarget": false,
"Enable": true
}
],
"@version": 1
}
} }
] ]
} }