Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manifest validation fixes #2

Merged
merged 1 commit into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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