Skip to content

Commit

Permalink
Merge pull request #2 from Mojang/mammerla-manifestvalidationfixes
Browse files Browse the repository at this point in the history
Manifest validation fixes
  • Loading branch information
mammerla authored Dec 15, 2023
2 parents bc1b3c8 + b263d88 commit 9534de7
Show file tree
Hide file tree
Showing 11 changed files with 294 additions and 21 deletions.
10 changes: 7 additions & 3 deletions app/src/app/Project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1603,7 +1603,7 @@ export default class Project {

if (
folderContext === FolderContext.unknown &&
folder.files["manifest.json"] &&
(folder.files["manifest.json"] || folder.files["pack_manifest.json"]) &&
!folder.files["level.dat"] &&
!folder.files["levelname.txt"]
) {
Expand Down Expand Up @@ -1667,7 +1667,7 @@ export default class Project {
const baseName = StorageUtilities.getBaseFromName(candidateFile.name);
const folderPath = StorageUtilities.canonicalizePath(StorageUtilities.getPath(projectPath));

if (canonFileName === "manifest.json") {
if (canonFileName === "manifest.json" || canonFileName === "pack_manifest.json") {
if (folderContext === FolderContext.resourcePack) {
this.#defaultResourcePackFolder = folder;
this.#resourcePacksContainer = parentFolder;
Expand Down Expand Up @@ -2467,7 +2467,11 @@ export default class Project {
public async processProjectFolder(folder: IFolder) {
await folder.load(false);

const manifest = folder.files["manifest.json"];
let manifest = folder.files["manifest.json"];

if (manifest === undefined) {
manifest = folder.files["pack_manifest.json"];
}

if (!this.#projectFolder) {
throw new Error("Unexpectedly could not find a project folder.");
Expand Down
59 changes: 58 additions & 1 deletion app/src/info/AddOnItemRequirementsGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import BehaviorAnimation from "../minecraft/BehaviorAnimation";
import ResourceAnimationController from "../minecraft/ResourceAnimationController";
import ResourceAnimation from "../minecraft/ResourceAnimation";
import ResourceRenderController from "../minecraft/ResourceRenderController";
import ResourceManifestJson from "../minecraft/ResourceManifestJson";
import BehaviorManifestJson from "../minecraft/BehaviorManifestJson";

export default class AddOnItemRequirementsGenerator implements IProjectInfoItemGenerator {
id = "ADDONIREQ";
Expand All @@ -27,7 +29,62 @@ export default class AddOnItemRequirementsGenerator implements IProjectInfoItemG
async generate(projectItem: ProjectItem): Promise<ProjectInfoItem[]> {
const items: ProjectInfoItem[] = [];

if (projectItem.itemType === ProjectItemType.animationControllerBehaviorJson) {
if (projectItem.itemType === ProjectItemType.resourcePackManifestJson) {
await projectItem.ensureFileStorage();

if (projectItem.file) {
const rpManifest = await ResourceManifestJson.ensureOnFile(projectItem.file);

if (rpManifest) {
await rpManifest.load();

if (!rpManifest.packScope) {
items.push(
new ProjectInfoItem(
InfoItemType.testCompleteFail,
this.id,
140,
`Resource pack manifest does not specify that header/pack_scope should be 'world'`,
projectItem
)
);
}
if (!rpManifest.productType) {
items.push(
new ProjectInfoItem(
InfoItemType.testCompleteFail,
this.id,
141,
`Resource pack manifest does not specify a metadata/product_type that should be 'addon'`,
projectItem
)
);
}
}
}
} else if (projectItem.itemType === ProjectItemType.behaviorPackManifestJson) {
await projectItem.ensureFileStorage();

if (projectItem.file) {
const bpManifest = await BehaviorManifestJson.ensureOnFile(projectItem.file);

if (bpManifest) {
await bpManifest.load();

if (!bpManifest.productType) {
items.push(
new ProjectInfoItem(
InfoItemType.testCompleteFail,
this.id,
142,
`Behavior pack manifest does not specify a metadata/product_type that should be 'addon'`,
projectItem
)
);
}
}
}
} else if (projectItem.itemType === ProjectItemType.animationControllerBehaviorJson) {
await projectItem.ensureFileStorage();

if (projectItem.file) {
Expand Down
139 changes: 139 additions & 0 deletions app/src/info/AddOnRequirementsGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import { InfoItemType } from "./IInfoItemData";
import IFolder from "../storage/IFolder";
import StorageUtilities from "../storage/StorageUtilities";
import ProjectInfoSet from "./ProjectInfoSet";
import BehaviorManifestJson from "../minecraft/BehaviorManifestJson";
import { ProjectItemType } from "../app/IProjectItemData";
import ProjectItem from "../app/ProjectItem";
import Utilities from "../core/Utilities";
import ResourceManifestJson from "../minecraft/ResourceManifestJson";

const UniqueRegEx = new RegExp(/[a-zA-Z0-9]{2,}_[a-zA-Z0-9]{2,}:[\w]+/);

Expand Down Expand Up @@ -153,6 +158,139 @@ export default class AddOnRequirementsGenerator implements IProjectInfoGenerator
async generate(project: Project): Promise<ProjectInfoItem[]> {
const items: ProjectInfoItem[] = [];

let behaviorPackManifest: undefined | BehaviorManifestJson = undefined;
let behaviorPackItem: undefined | ProjectItem = undefined;
let resourcePackManifest: undefined | ResourceManifestJson = undefined;
let resourcePackItem: undefined | ProjectItem = undefined;

for (const projectItem of project.items) {
if (projectItem.file) {
if (projectItem.itemType === ProjectItemType.behaviorPackManifestJson) {
if (behaviorPackManifest) {
items.push(
new ProjectInfoItem(
InfoItemType.testCompleteFail,
this.id,
160,
`Found more than one behavior pack manifest, which is not supported`,
projectItem
)
);
}

behaviorPackManifest = await BehaviorManifestJson.ensureOnFile(projectItem.file);
behaviorPackItem = projectItem;

await behaviorPackManifest?.load();
} else if (projectItem.itemType === ProjectItemType.resourcePackManifestJson) {
if (resourcePackManifest) {
items.push(
new ProjectInfoItem(
InfoItemType.testCompleteFail,
this.id,
161,
`Found more than one resource pack manifest, which is not supported`,
projectItem
)
);
}

resourcePackManifest = await ResourceManifestJson.ensureOnFile(projectItem.file);
resourcePackItem = projectItem;

await resourcePackManifest?.load();
}
}
}

if (!behaviorPackManifest || !behaviorPackManifest.definition) {
items.push(
new ProjectInfoItem(
InfoItemType.testCompleteFail,
this.id,
163,
`Did not find a valid behavior pack manifest.`,
undefined
)
);
}

if (!resourcePackManifest || !resourcePackManifest.definition) {
items.push(
new ProjectInfoItem(
InfoItemType.testCompleteFail,
this.id,
164,
`Did not find a valid resource pack manifest.`,
undefined
)
);
}

if (
behaviorPackManifest &&
resourcePackManifest &&
behaviorPackManifest.definition &&
resourcePackManifest.definition
) {
if (!behaviorPackManifest.definition.dependencies || behaviorPackManifest.getNonInternalDependencyCount() !== 1) {
items.push(
new ProjectInfoItem(
InfoItemType.testCompleteFail,
this.id,
165,
`Did not find exactly one dependency on the corresponding resource pack in the behavior pack manifest.`,
behaviorPackItem,
behaviorPackManifest.getNonInternalDependencyCount()
)
);
} else if (
!behaviorPackManifest.definition.dependencies[0].uuid ||
!Utilities.uuidEqual(
behaviorPackManifest.definition.dependencies[0].uuid,
resourcePackManifest.definition.header.uuid
)
) {
items.push(
new ProjectInfoItem(
InfoItemType.testCompleteFail,
this.id,
167,
`Behavior pack manifest does not have a proper dependency on the resource pack identifier.`,
behaviorPackItem
)
);
}

if (!resourcePackManifest.definition.dependencies || resourcePackManifest.definition.dependencies.length !== 1) {
items.push(
new ProjectInfoItem(
InfoItemType.testCompleteFail,
this.id,
168,
`Did not find exactly one dependency on the corresponding behavior pack in the resource pack manifest.`,
resourcePackItem
)
);
} else if (
!resourcePackManifest.definition.dependencies[0].uuid ||
!Utilities.uuidEqual(
resourcePackManifest.definition.dependencies[0].uuid,
behaviorPackManifest.definition.header.uuid
)
) {
items.push(
new ProjectInfoItem(
InfoItemType.testCompleteFail,
this.id,
169,
`Resource pack manifest does not have a proper dependency on the behavior pack identifier.`,
behaviorPackItem
)
);
}
}

const bpFolder = await project.getDefaultBehaviorPackFolder();

if (bpFolder) {
Expand Down Expand Up @@ -266,6 +404,7 @@ export default class AddOnRequirementsGenerator implements IProjectInfoGenerator
folderNameCanon !== "materials" &&
folderNameCanon !== "blocks" &&
folderNameCanon !== "models" &&
folderNameCanon !== "attachables" &&
folderNameCanon !== "render_controllers" &&
folderNameCanon !== "animation_controllers" &&
folderNameCanon !== "animations"
Expand Down
4 changes: 2 additions & 2 deletions app/src/info/StrictPlatformInfoGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default class StrictPlatformInfoGenerator implements IProjectInfoGenerato
this.id,
100,
`Uses a minecraft: identifier override`,
undefined,
pi,
desc.identifier
)
);
Expand All @@ -76,7 +76,7 @@ export default class StrictPlatformInfoGenerator implements IProjectInfoGenerato
this.id,
100,
`Uses a runtime_identifier override`,
undefined,
pi,
desc.runtime_identifier
)
);
Expand Down
9 changes: 9 additions & 0 deletions app/src/local/IAuthenticationToken.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
export enum ServerPermissionLevel {
none = 0,
displayReadOnly = 1,
fullReadOnly = 2,
updateState = 3,
admin = 4,
}

export interface IAuthenticationToken {
time: number;
code: string;
permissionLevel: ServerPermissionLevel;
}
24 changes: 24 additions & 0 deletions app/src/minecraft/BehaviorManifestJson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ export default class BehaviorManifestJson {
return this._onLoaded.asEvent();
}

public get productType() {
if (!this.definition || !this.definition.metadata) {
return undefined;
}

return this.definition.metadata.product_type;
}

public get description() {
if (!this.definition || !this.definition.header) {
return undefined;
Expand Down Expand Up @@ -74,6 +82,22 @@ export default class BehaviorManifestJson {
this._id = newId;
}

public getNonInternalDependencyCount() {
if (!this.definition || !this.definition.dependencies) {
return 0;
}

let count = 0;

for (let dependency of this.definition.dependencies) {
if (dependency.uuid) {
count++;
}
}

return count;
}

static async ensureOnFile(file: IFile, loadHandler?: IEventHandler<BehaviorManifestJson, BehaviorManifestJson>) {
let bmj: BehaviorManifestJson | undefined = undefined;

Expand Down
24 changes: 24 additions & 0 deletions app/src/minecraft/IAddonManifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ export default interface IAddonManifest {
header: IAddonManifestHeader;
modules: IAddonModule[];
dependencies: IAddonDependency[];
metadata?: IAddonMetadata;
capabilities?: string[];
}

export interface IResourcePackManifest {
format_version: number;
__comment__?: string;
header: IResourceAddonManifestHeader;
modules: IAddonModule[];
dependencies: IAddonDependency[];
metadata?: IAddonMetadata;
capabilities?: string[];
}

export interface IAddonManifestHeader {
Expand All @@ -14,6 +26,10 @@ export interface IAddonManifestHeader {
min_engine_version: number[];
}

export interface IResourceAddonManifestHeader extends IAddonManifestHeader {
pack_scope?: "world" | "global" | "any";
}

export interface IAddonModule {
description: string;
type: string;
Expand All @@ -28,3 +44,11 @@ export interface IAddonDependency {
module_name?: string;
version: number[] | string;
}

export interface IAddonMetadata {
license?: string;
authors?: string[];
url?: string;
product_type?: "" | "addon";
generated_with?: { [toolName: string]: string[] };
}
Loading

0 comments on commit 9534de7

Please sign in to comment.