diff --git a/.github/workflows/validate-schema.yml b/.github/workflows/validate-schema.yml index 2b126cc..cbb4548 100644 --- a/.github/workflows/validate-schema.yml +++ b/.github/workflows/validate-schema.yml @@ -40,6 +40,10 @@ jobs: # fallback to base_dir or a global schema. The leading */ covers eaw/ and foc/ alike, so a # second game's directories are picked up automatically when they appear. schema_mappings: | + - type: json + schema: ./.schemas/index-file.schema.json + files: + - ./*/_index.json - type: json schema: ./.schemas/tag-file.schema.json files: diff --git a/.schemas/index-file.schema.json b/.schemas/index-file.schema.json new file mode 100644 index 0000000..ceb1c32 --- /dev/null +++ b/.schemas/index-file.schema.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/AlamoEngine-Tools/pg-starwarsgame-lsp-schema/.schemas/index-file.schema.json", + "title": "schema manifest (_index.json)", + "description": "Shape of */_index.json - the manifest the server fetches before anything else. Mirrors SchemaManifest in PG.StarWarsGame.LSP.Schema. Every file the server loads over HTTP must be listed here: a YAML file that exists in the repo but is missing from the manifest is simply never fetched, and no other check catches that.", + "type": "object", + "additionalProperties": false, + "required": ["schemaVersion"], + "properties": { + "schemaVersion": { + "description": "Semver version of the schema CONTRACT, not of its contents. MAJOR: a change the server must understand or it will misread files (structural change to a file's shape, a removed or renamed value). MINOR: new values for referenceKind/semanticType/type, or a new manifest category - an older server still loads the schema but silently loses the capability. PATCH: content only (new tags, descriptions, corrections), always safe. Servers declare a supported range and refuse a schema whose MAJOR falls outside it.", + "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(-[0-9A-Za-z-.]+)?(\\+[0-9A-Za-z-.]+)?$" + }, + "types": { "$ref": "#/$defs/fileList" }, + "tags": { "$ref": "#/$defs/fileList" }, + "enums": { "$ref": "#/$defs/fileList" }, + "hardcoded": { "$ref": "#/$defs/fileList" }, + "meta": { "$ref": "#/$defs/fileList" }, + "baselineHash": { + "description": "SHA-256 over every file listed above, in manifest order. Maintained by UpdateSchemaHash.ps1 - do not edit by hand.", + "type": "string", + "pattern": "^[0-9a-f]{64}$" + } + }, + "$defs": { + "fileList": { + "description": "Repository-relative paths, forward slashes, relative to this manifest's own directory.", + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "pattern": "^[^/\\\\][^\\\\]*\\.yaml$" + }, + "uniqueItems": true + } + } +} diff --git a/.schemas/tag-file.schema.json b/.schemas/tag-file.schema.json index 73059e2..268366b 100644 --- a/.schemas/tag-file.schema.json +++ b/.schemas/tag-file.schema.json @@ -37,7 +37,7 @@ }, "referenceKind": { "$ref": "#/$defs/referenceKind" }, "referenceType": { - "description": "Target object type when referenceKind is xmlObject (e.g. GameObjectType, Faction, SFXEvent).", + "description": "The reference target. With referenceKind xmlObject it names the object type (e.g. GameObjectType, Faction, SFXEvent); with workspaceFile it names the file type in the file-type registry (e.g. StoryPlotManifest, StoryParser, LuaScript).", "type": "string", "minLength": 1 }, @@ -95,6 +95,7 @@ "enum": [ "none", "None", "xmlObject", "XmlObject", + "workspaceFile", "WorkspaceFile", "modelFile", "ModelFile", "textureFile", "TextureFile", "audioFile", "AudioFile", @@ -120,7 +121,9 @@ "variantParent", "VariantParent", "ownerScopedReference", "OwnerScopedReference", "ownerAgnosticReference", "OwnerAgnosticReference", - "planetModePairList", "PlanetModePairList" + "planetModePairList", "PlanetModePairList", + "factionPlotFilePairList", "FactionPlotFilePairList", + "factionMarkupPairList", "FactionMarkupPairList" ] }, "valueType": { @@ -141,8 +144,7 @@ "HardPointTypeToTextureMap", "HardPointSfxMap", "FactionReference", "Type68", "Type69", "UnitSpawnTable", "UnitSpawnProbabilityTable", "CategoryToIntegerMap", "AbilityType", "ProjectileCategory", "CableRenderMode", "AbilitySfxMap", "AbilityModMultiplier", - "AbilityModFlag", "MovieFrameTrigger", "CommandBarProperty", "Type81", "Type82", - "GameObjectTypeReference" + "AbilityModFlag", "MovieFrameTrigger", "CommandBarProperty", "Type81", "Type82" ] } } diff --git a/UpdateSchemaHash.ps1 b/UpdateSchemaHash.ps1 index eef71b9..c0ac839 100644 --- a/UpdateSchemaHash.ps1 +++ b/UpdateSchemaHash.ps1 @@ -7,6 +7,13 @@ $indexPath = Join-Path $root "_index.json" $index = Get-Content $indexPath -Raw | ConvertFrom-Json +# The rewrite below reconstructs the manifest from a fixed field list, so anything not named there +# is dropped. schemaVersion is load-bearing (the server refuses a schema whose MAJOR it does not +# support), so fail loudly rather than silently emitting a manifest without it. +if (-not $index.schemaVersion) { + throw "schemaVersion missing from $indexPath - add it before running this script." +} + $allRels = @($index.tags) + @($index.types) + @($index.enums) + @($index.hardcoded) + @($index.meta) $sha = [System.Security.Cryptography.IncrementalHash]::CreateHash( @@ -22,12 +29,13 @@ if ($index.baselineHash -eq $hash) { } [ordered]@{ - types = $index.types - tags = $index.tags - enums = $index.enums - hardcoded = $index.hardcoded - meta = $index.meta - baselineHash = $hash + schemaVersion = $index.schemaVersion + types = $index.types + tags = $index.tags + enums = $index.enums + hardcoded = $index.hardcoded + meta = $index.meta + baselineHash = $hash } | ConvertTo-Json -Depth 3 | Set-Content $indexPath -Encoding utf8NoBOM git add $indexPath diff --git a/eaw/_index.json b/eaw/_index.json index 14317ee..d942b64 100644 --- a/eaw/_index.json +++ b/eaw/_index.json @@ -1,4 +1,5 @@ { + "schemaVersion": "1.0.0", "types": [ "types.yaml" ], @@ -147,6 +148,7 @@ "enums/ForceAlignment.yaml", "enums/FormationGrouping.yaml", "enums/GalacticAbilitySlot.yaml", + "enums/GalacticVictoryCondition.yaml", "enums/GameObjectCategoryType.yaml", "enums/GameObjectPropertiesType.yaml", "enums/HardPointType.yaml", @@ -190,5 +192,5 @@ "meta": [ "meta/metafiles.yaml" ], - "baselineHash": "f7292f2865d85c3db4ba2267cfe75fb0a495606a1ada9079782181bb5a7a9ce2" + "baselineHash": "a70831ad3e41f6aeb4b8793d1cdde92445b7e21a4ced16d1ca0166eeb33e1669" } diff --git a/eaw/enums/GalacticVictoryCondition.yaml b/eaw/enums/GalacticVictoryCondition.yaml new file mode 100644 index 0000000..0206c46 --- /dev/null +++ b/eaw/enums/GalacticVictoryCondition.yaml @@ -0,0 +1,14 @@ +name: GalacticVictoryCondition +kind: schemaFixed +description: + en: "Galactic-Conquest victory condition. Listed (comma-separated) on a Campaign's Good_/Evil_/Human_/AI_Victory_Conditions tags; the campaign is won when any listed condition is met. Engine XML value type 69." +values: + - name: Galactic_All_Planets_Controlled + - name: Galactic_Conquer_Enemy_Homeworld_And_Leader + - name: Galactic_Control_Named_Planets + - name: Galactic_Cycles_Elapsed + - name: Galactic_Kill_Enemy_Leader + - name: Galactic_Opponent_Controls_No_Planets + - name: Galactic_Super_Weapon_Destroys_Last_Enemy_Planet + - name: Galactic_Super_Weapon_Destruction + - name: Galactic_Super_Weapon_Kills_Enemy_Leader diff --git a/eaw/tags/Campaign.yaml b/eaw/tags/Campaign.yaml index d74d793..5f5c7c4 100644 --- a/eaw/tags/Campaign.yaml +++ b/eaw/tags/Campaign.yaml @@ -71,7 +71,11 @@ referenceKind: xmlObject - tag: Campaign_Set type: NameReference - referenceKind: unknown + referenceKind: xmlObject + referenceType: Campaign + semanticType: referenceGroup + description: + en: "Grouping key (not a reference to a declared object): every Campaign sharing this value belongs to the same selectable Galactic Conquest set. The engine collects them into one menu entry, then instantiates the campaign matching the chosen faction. Find-references lists the co-members; renaming rewrites the value across the whole set." - tag: Is_Multiplayer type: Boolean - tag: Is_Listed @@ -81,7 +85,10 @@ - tag: Markup_Filename type: NameReferenceList referenceKind: unknown + semanticType: factionMarkupPairList multipleAllowed: true + description: + en: "A 'Faction, MarkupFile' pair attaching a GUI hint-markup set to a faction (e.g. 'Empire, DefaultGalacticHints'). The faction is validated and navigable; the markup file is loaded from the engine's GUI markup set, not the XML workspace, so it is neither validated nor navigable." - tag: Planet_Auto_Reveal type: Boolean - tag: Max_Tech_Level @@ -104,8 +111,22 @@ type: Boolean - tag: Human_Victory_Conditions type: Type69 + referenceKind: enum + enumName: GalacticVictoryCondition - tag: AI_Victory_Conditions type: Type69 + referenceKind: enum + enumName: GalacticVictoryCondition + # The values the shipped campaigns actually use are the Good_/Evil_ pair (Human_/AI_ appear in the + # DatabaseMapExport type table); all four carry the same comma-separated victory-condition list. + - tag: Good_Victory_Conditions + type: Type69 + referenceKind: enum + enumName: GalacticVictoryCondition + - tag: Evil_Victory_Conditions + type: Type69 + referenceKind: enum + enumName: GalacticVictoryCondition - tag: Human_Victory_Credit_Target type: Int - tag: Human_Victory_Galactic_Control diff --git a/foc/_index.json b/foc/_index.json index 401b1ed..7a54352 100644 --- a/foc/_index.json +++ b/foc/_index.json @@ -1,4 +1,5 @@ { + "schemaVersion": "1.0.0", "tags": [], "types": [ "types.yaml"