diff --git a/.github/workflows/branch-build.yml b/.github/workflows/branch-build.yml index 0ac66cb566..7a1cc85d87 100755 --- a/.github/workflows/branch-build.yml +++ b/.github/workflows/branch-build.yml @@ -78,7 +78,7 @@ jobs: key: dir-${{github.sha}} - run: npm run dev-docker - run: npm run dev-reinit - - run: npm run check-errors + - run: npx lerna run --scope '@etherealengine/*' check-errors timeout-minutes: 20 build-client: diff --git a/.github/workflows/projects-build.yml b/.github/workflows/projects-build.yml index d4b92e3ce3..45efc06139 100755 --- a/.github/workflows/projects-build.yml +++ b/.github/workflows/projects-build.yml @@ -27,7 +27,7 @@ jobs: uses: actions/checkout@v3 with: repository: etherealengine/ee-development-test-suite - path: './packages/projects/projects/ee-development-test-suite' + path: './packages/projects/projects/etherealengine/ee-development-test-suite' - name: Use Node.js uses: actions/setup-node@v3 with: @@ -43,7 +43,7 @@ jobs: - run: cp .env.local.default .env.local - run: npm install --production=false --loglevel notice --legacy-peer-deps - run: npm run lint - - run: npm run check-errors + - run: npx lerna run --scope '@etherealengine/*' check-errors - run: npm run check-eslint - run: npm run dev-docker - run: npm run dev-reinit diff --git a/lerna.json b/lerna.json index 92354c221d..91883feced 100755 --- a/lerna.json +++ b/lerna.json @@ -1,7 +1,7 @@ { "packages": [ "packages/*", - "packages/projects/projects/*" + "packages/projects/projects/**" ], "version": "1.6.0", "npmClient": "npm", diff --git a/package.json b/package.json index 83b8e47476..b34cb6b011 100755 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ }, "workspaces": [ "packages/*", - "packages/projects/projects/*" + "packages/projects/projects/**" ], "keywords": [ "three", @@ -41,10 +41,11 @@ "scripts": { "build-client": "cd packages/client && npm run build", "check": "npm run lint && npm run check-errors && npm run check-eslint && npm run test && npm run build-client", - "check-errors": "tsc --noemit && lerna run --scope '@etherealengine/*' check-errors && lerna run --ignore '@etherealengine/*' check-errors", + "check-errors": "lerna run --scope '@etherealengine/*' check-errors && lerna run --ignore '@etherealengine/*' check-errors", "check-eslint": "eslint --quiet .", "checkout-dev": "lerna exec 'git checkout dev' --parallel --no-bail", "clean-node-modules": "npx rimraf node_modules && npx rimraf package-lock.json && npx lerna exec npx rimraf node_modules && npx lerna exec npx rimraf package-lock.json", + "clone-project": "cross-env ts-node --swc scripts/clone-project.ts", "create-root-package-json": "cross-env ts-node --swc scripts/create-root-package-json", "create-project": "cross-env ts-node --swc scripts/create-project", "depcheck": "lerna exec --no-bail --stream -- depcheck", @@ -57,7 +58,7 @@ "dev-docker": "cd scripts && ./start-containers.sh", "dev-docker-windows": "cd scripts && docker-compose up -d && docker-compose up -d -f docker-compose-minio.yml", "dev-tabs": "npm run dev-docker && cd scripts && ./dev-tabs.sh", - "fetch-projects": "lerna exec 'git fetch -p && git rebase' --parallel --ignore @etherealengine/* --no-bail", + "fetch-projects": "lerna exec 'git fetch -p && git rebase' --parallel --no-bail", "dev-reinit": "./scripts/checkenv.sh && npm run dev-docker && cd packages/server && npm run dev-reinit-db", "dev-server": "cd packages/server && npm run dev", "dev-windows": "npm run dev-docker-windows && concurrently -n agones,server,client npm:dev-agones-silent npm:dev-server npm:dev-client", diff --git a/packages/client-core/src/admin/components/project/ProjectTable.tsx b/packages/client-core/src/admin/components/project/ProjectTable.tsx index f27d9e058b..6ee1b888b5 100644 --- a/packages/client-core/src/admin/components/project/ProjectTable.tsx +++ b/packages/client-core/src/admin/components/project/ProjectTable.tsx @@ -122,7 +122,7 @@ export default function ProjectTable(props: { search: string }) { startIcon={} size="small" className="mr-2 h-min whitespace-pre bg-theme-blue-secondary text-[#214AA6] disabled:opacity-50 dark:text-white" - disabled={project.name === 'default-project'} + disabled={project.name === 'etherealengine/default-project'} onClick={() => PopoverState.showPopupover( @@ -135,7 +135,7 @@ export default function ProjectTable(props: { search: string }) { startIcon={} size="small" className="mr-2 h-min whitespace-pre bg-theme-blue-secondary text-[#214AA6] disabled:opacity-50 dark:text-white" - disabled={!project || !project.repositoryPath || project.name === 'default-project'} + disabled={!project || !project.repositoryPath || project.name === 'etherealengine/default-project'} onClick={() => { PopoverState.showPopupover( } size="small" className="h-min whitespace-pre bg-theme-blue-secondary text-[#214AA6] disabled:opacity-50 dark:text-white" - disabled={project.name === 'default-project'} + disabled={project.name === 'etherealengine/default-project'} onClick={() => { PopoverState.showPopupover( handleEnabledChange(row)} /> diff --git a/packages/client-core/src/admin/components/project/UpdateEngineModal.tsx b/packages/client-core/src/admin/components/project/UpdateEngineModal.tsx index a2d3f43933..0dbb1ebd68 100644 --- a/packages/client-core/src/admin/components/project/UpdateEngineModal.tsx +++ b/packages/client-core/src/admin/components/project/UpdateEngineModal.tsx @@ -186,7 +186,7 @@ export default function UpdateEngineModal() {
{projectState.projects.value - .filter((project) => project.name !== 'default-project' && project.repositoryPath) + .filter((project) => project.name !== 'etherealengine/default-project' && project.repositoryPath) .map((project) => (
{ const userAvatar = useFind(userAvatarPath, { diff --git a/packages/common/src/regex/index.ts b/packages/common/src/regex/index.ts index 2b89670699..2b7b6d835a 100644 --- a/packages/common/src/regex/index.ts +++ b/packages/common/src/regex/index.ts @@ -45,12 +45,13 @@ export const CSS_URL_REGEX = /(@import\s+["']([^"']+)["']|url\((?!['"]?(?:data): export const ABSOLUTE_URL_PROTOCOL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/ /** - * Captures project name and asset path from a URL. + * Captures org name, project name and asset path from a URL. * For eg: `/path/to/projects/project123/assets/images/logo.png` will capture following groups - * - `project123` => Group 1 - * - `assets/images/logo.png` => Group 2 + * - `@org123` => Group 1 + * - `project123` => Group 2 + * - `assets/images/logo.png` => Group 3 */ -export const STATIC_ASSET_REGEX = /^(?:.*\/(?:projects|static-resources)\/([^\/]*)\/((?:assets\/|).*)$)/ +export const STATIC_ASSET_REGEX = /^(?:.*\/(?:projects|static-resources)\/([^\/]*)\/([^\/]*)\/((?:assets\/|).*)$)/ // ===================================================================== // ========================= ID Regex Patterns ========================= @@ -111,22 +112,22 @@ export const INSTALLATION_SIGNED_REGEX = /https:\/\/oauth2:[\w\d\s\-_]+@github.c /** * This regex is used to match specific file paths or directory structures that start with `projects/`, followed by one or more valid characters (letters, digits, hyphens, underscores, or forward slashes), and end with `/assets/` */ -export const ASSETS_REGEX = /projects\/[a-zA-Z0-9-_\/]+\/assets\// +export const ASSETS_REGEX = /projects\/+[a-zA-Z0-9-_@]+\/[a-zA-Z0-9-_]+\/assets\// /** * This regex matches strings that start with `projects/`, followed by one or more characters that can be letters, digits, hyphens, underscores, or forward slashes. */ -export const PROJECT_REGEX = /projects\/[a-zA-Z0-9-_\/]+/ +export const PROJECT_REGEX = /projects\/+[a-zA-Z0-9-_@]+\/[a-zA-Z0-9-_]/ /** * This regex matches strings that start with `projects/`, followed by one or more characters that can be letters, digits, hyphens, underscores, or forward slashes, and then `/public/`. */ -export const PROJECT_PUBLIC_REGEX = /projects\/[a-zA-Z0-9-_\/]+\/public\// +export const PROJECT_PUBLIC_REGEX = /projects\/+[a-zA-Z0-9-_@]+\/[a-zA-Z0-9-_]+\/public\// /** * This regex matches strings that start with `projects/`, followed by one or more characters that can be letters, digits, hyphens, underscores, or forward slashes, and then `/thumbnails/`. */ -export const PROJECT_THUMBNAIL_REGEX = /projects\/[a-zA-Z0-9-_\/]+\/thumbnails\// +export const PROJECT_THUMBNAIL_REGEX = /projects\/+[a-zA-Z0-9-_@]+\/[a-zA-Z0-9-_]+\/thumbnails\// export const VALID_PROJECT_NAME = /^(?!\s)[\w\-\s]+$/ diff --git a/packages/common/tests/regex.test.ts b/packages/common/tests/regex.test.ts index 6f3c7b4a4b..e2cc0c8617 100644 --- a/packages/common/tests/regex.test.ts +++ b/packages/common/tests/regex.test.ts @@ -252,31 +252,36 @@ describe('regex.test', () => { it('should match static asset URLs', () => { const positiveCases = [ { - url: 'https://example.com/projects/default-project/assets/images/logo.png', + url: 'https://example.com/projects/etherealengine/default-project/assets/images/logo.png', + orgName: 'etherealengine', projectName: 'default-project', assetPath: 'assets/images/logo.png' }, { - url: 'https://example.com/static-resources/default-project/assets/images/logo.png', + url: 'https://example.com/static-resources/etherealengine/default-project/assets/images/logo.png', + orgName: 'etherealengine', projectName: 'default-project', assetPath: 'assets/images/logo.png' }, { - url: 'https://example.com/projects/default-project/assets/animations/emotes.glb', + url: 'https://example.com/projects/etherealengine/default-project/assets/animations/emotes.glb', + orgName: 'etherealengine', projectName: 'default-project', assetPath: 'assets/animations/emotes.glb' }, { - url: 'https://example.com/projects/default-project/assets/animations/locomotion.glb', + url: 'https://example.com/projects/etherealengine/default-project/assets/animations/locomotion.glb', + orgName: 'etherealengine', projectName: 'default-project', assetPath: 'assets/animations/locomotion.glb' } ] - positiveCases.forEach(({ url, projectName, assetPath }) => { + positiveCases.forEach(({ url, orgName, projectName, assetPath }) => { const match = STATIC_ASSET_REGEX.exec(url) assert.ok(match, `Expected '${url}' to match STATIC_ASSET_REGEX`) - assert.equal(match?.[1], projectName, `Expected project name '${projectName}' in '${url}'. Found ${match?.[1]}`) - assert.equal(match?.[2], assetPath, `Expected asset path '${assetPath}' in '${url}'. Found ${match?.[2]}`) + assert.equal(match?.[1], orgName, `Expected org name name '${orgName}' in '${url}'. Found ${match?.[1]}`) + assert.equal(match?.[2], projectName, `Expected project name '${projectName}' in '${url}'. Found ${match?.[2]}`) + assert.equal(match?.[3], assetPath, `Expected asset path '${assetPath}' in '${url}'. Found ${match?.[3]}`) }) }) @@ -284,7 +289,7 @@ describe('regex.test', () => { const negativeCases = [ 'https://example.com/static-resources/', 'https://example.com/project/subdir/assets', - 'https://example.com/default-project/assets/animations/emotes.glb' + 'https://example.com/etherealengine/default-project/assets/animations/emotes.glb' ] negativeCases.forEach((url) => { assert.doesNotMatch(url, STATIC_ASSET_REGEX, `Expected '${url}' to not match STATIC_ASSET_REGEX`) @@ -581,9 +586,9 @@ describe('regex.test', () => { describe('ASSETS_REGEX', () => { it('should match assets URLs', () => { const positiveCases = [ - 'https://example.com/projects/default-project/assets/images/logo.png', - 'https://example.com/projects/default-project/assets/animations/emotes.glb', - 'https://example.com/projects/default-project/assets/animations/locomotion.glb' + 'https://example.com/projects/etherealengine/default-project/assets/images/logo.png', + 'https://example.com/projects/etherealengine/default-project/assets/animations/emotes.glb', + 'https://example.com/projects/etherealengine/default-project/assets/animations/locomotion.glb' ] positiveCases.forEach((url) => { assert.match(url, ASSETS_REGEX, `Expected '${url}' to match ASSETS_REGEX`) @@ -592,9 +597,9 @@ describe('regex.test', () => { it('should not match non-assets URLs', () => { const negativeCases = [ - 'https://example.com/projects/default-project/scene.json', - 'https://example.com/projects/default-project/assets', - 'https://example.com/default-project/assets/animations/emotes.glb' + 'https://example.com/projects/etherealengine/default-project/scene.json', + 'https://example.com/projects/etherealengine/default-project/assets', + 'https://example.com/etherealengine/default-project/assets/animations/emotes.glb' ] negativeCases.forEach((url) => { assert.doesNotMatch(url, ASSETS_REGEX, `Expected '${url}' to not match ASSETS_REGEX`) @@ -605,9 +610,9 @@ describe('regex.test', () => { describe('PROJECT_REGEX', () => { it('should match valid project paths', () => { const positiveCases = [ - 'projects/project123', - 'projects/project-name', - 'projects/project_name', + 'projects/etherealengine/project123', + 'projects/etherealengine/project-name', + 'projects/etherealengine/project_name', 'projects/project/123', 'projects/project/abc_def' ] @@ -630,9 +635,9 @@ describe('regex.test', () => { describe('PROJECT_PUBLIC_REGEX', () => { it('should match valid project paths', () => { const positiveCases = [ - 'projects/project123/public/', - 'projects/project-name/public/', - 'projects/project_name/public/', + 'projects/etherealengine/project123/public/', + 'projects/etherealengine/project-name/public/', + 'projects/etherealengine/project_name/public/', 'projects/project/123/public/', 'projects/project/abc_def/public/' ] @@ -643,10 +648,10 @@ describe('regex.test', () => { it('should not match invalid project paths', () => { const negativeCases = [ - 'projects/project123/public', // (missing trailing slash) - 'projects/project-name/private/', // (incorrect folder private instead of public) - 'projects/project$name/public/', // (contains invalid character $) - 'projects/project-@name/public/', // (contains invalid character @) + 'projects/etherealengine/project123/public', // (missing trailing slash) + 'projects/etherealengine/project-name/private/', // (incorrect folder private instead of public) + 'projects/etherealengine/project$name/public/', // (contains invalid character $) + 'projects/etherealengine/project-@name/public/', // (contains invalid character @) 'projects/' // (missing project name and /public/) ] negativeCases.forEach((value) => { @@ -658,9 +663,9 @@ describe('regex.test', () => { describe('PROJECT_THUMBNAIL_REGEX', () => { it('should match valid project thumbnail paths', () => { const positiveCases = [ - 'projects/project123/thumbnails/', - 'projects/project-name/thumbnails/', - 'projects/project_name/thumbnails/', + 'projects/etherealengine/project123/thumbnails/', + 'projects/etherealengine/project-name/thumbnails/', + 'projects/etherealengine/project_name/thumbnails/', 'projects/project/123/thumbnails/', 'projects/project/abc_def/thumbnails/' ] @@ -671,10 +676,10 @@ describe('regex.test', () => { it('should not match invalid project thumbnail paths', () => { const negativeCases = [ - 'projects/project123/thumbnails', // (missing trailing slash) - 'projects/project-name/private/', // (incorrect folder private instead of public) - 'projects/project$name/thumbnails/', // (contains invalid character $) - 'projects/project-@name/thumbnails/', // (contains invalid character @) + 'projects/etherealengine/project123/thumbnails', // (missing trailing slash) + 'projects/etherealengine/project-name/private/', // (incorrect folder private instead of public) + 'projects/etherealengine/project$name/thumbnails/', // (contains invalid character $) + 'projects/etherealengine/project-@name/thumbnails/', // (contains invalid character @) 'projects/' // (missing project name and /thumbnail/) ] negativeCases.forEach((value) => { diff --git a/packages/editor/src/components/prefabs/PrefabEditors.tsx b/packages/editor/src/components/prefabs/PrefabEditors.tsx index 3d56b4ef8d..21c319662c 100644 --- a/packages/editor/src/components/prefabs/PrefabEditors.tsx +++ b/packages/editor/src/components/prefabs/PrefabEditors.tsx @@ -41,106 +41,106 @@ export const PrefabShelfState = defineState({ [ { name: '3D Model', - url: `${config.client.fileServer}/projects/default-project/assets/prefabs/3d-model.prefab.gltf`, + url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/3d-model.prefab.gltf`, category: 'Geo', detail: 'Blank 3D model ready for your own assets' }, { name: 'Primitive Geometry', - url: `${config.client.fileServer}/projects/default-project/assets/prefabs/geo.prefab.gltf`, + url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/geo.prefab.gltf`, category: 'Geo' }, { name: 'Ground Plane', - url: `${config.client.fileServer}/projects/default-project/assets/prefabs/ground-plane.prefab.gltf`, + url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/ground-plane.prefab.gltf`, category: 'Geo' }, { name: 'Point Light', - url: `${config.client.fileServer}/projects/default-project/assets/prefabs/point-light.prefab.gltf`, + url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/point-light.prefab.gltf`, category: 'Lighting' }, { name: 'Spot Light', - url: `${config.client.fileServer}/projects/default-project/assets/prefabs/spot-light.prefab.gltf`, + url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/spot-light.prefab.gltf`, category: 'Lighting' }, { name: 'Directional Light', - url: `${config.client.fileServer}/projects/default-project/assets/prefabs/directional-light.prefab.gltf`, + url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/directional-light.prefab.gltf`, category: 'Lighting' }, { name: 'Ambient Light', - url: `${config.client.fileServer}/projects/default-project/assets/prefabs/ambient-light.prefab.gltf`, + url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/ambient-light.prefab.gltf`, category: 'Lighting' }, { name: 'Hemisphere Light', - url: `${config.client.fileServer}/projects/default-project/assets/prefabs/hemisphere-light.prefab.gltf`, + url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/hemisphere-light.prefab.gltf`, category: 'Lighting' }, { name: 'Box Collider', - url: `${config.client.fileServer}/projects/default-project/assets/prefabs/box-collider.prefab.gltf`, + url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/box-collider.prefab.gltf`, category: 'Collider', detail: 'Simple box collider' }, { name: 'Sphere Collider', - url: `${config.client.fileServer}/projects/default-project/assets/prefabs/sphere-collider.prefab.gltf`, + url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/sphere-collider.prefab.gltf`, category: 'Collider', detail: 'Simple sphere collider' }, { name: 'Cylinder Collider', - url: `${config.client.fileServer}/projects/default-project/assets/prefabs/cylinder-collider.prefab.gltf`, + url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/cylinder-collider.prefab.gltf`, category: 'Collider', detail: 'Simple cylinder collider' }, { name: 'Text', - url: `${config.client.fileServer}/projects/default-project/assets/prefabs/text.prefab.gltf`, + url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/text.prefab.gltf`, category: 'Text' }, { name: 'Title', - url: `${config.client.fileServer}/projects/default-project/assets/prefabs/title.prefab.gltf`, + url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/title.prefab.gltf`, category: 'Text' }, { name: 'Body', - url: `${config.client.fileServer}/projects/default-project/assets/prefabs/body.prefab.gltf`, + url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/body.prefab.gltf`, category: 'Text' }, { name: 'Image', - url: `${config.client.fileServer}/projects/default-project/assets/prefabs/image.prefab.gltf`, + url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/image.prefab.gltf`, category: 'Image' }, { name: 'Video', - url: `${config.client.fileServer}/projects/default-project/assets/prefabs/video.prefab.gltf`, + url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/video.prefab.gltf`, category: 'Video' }, { name: 'Skybox', - url: `${config.client.fileServer}/projects/default-project/assets/prefabs/skybox.prefab.gltf`, + url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/skybox.prefab.gltf`, category: 'Lookdev' }, { name: 'Postprocessing', - url: `${config.client.fileServer}/projects/default-project/assets/prefabs/postprocessing.prefab.gltf`, + url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/postprocessing.prefab.gltf`, category: 'Lookdev' }, { name: 'Fog', - url: `${config.client.fileServer}/projects/default-project/assets/prefabs/fog.prefab.gltf`, + url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/fog.prefab.gltf`, category: 'Lookdev' }, { name: 'Camera', - url: `${config.client.fileServer}/projects/default-project/assets/prefabs/camera.prefab.gltf`, + url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/camera.prefab.gltf`, category: 'Camera' } ] as PrefabShelfItem[], diff --git a/packages/editor/src/components/projects/CreateProjectDialog.tsx b/packages/editor/src/components/projects/CreateProjectDialog.tsx index d42b148dec..302a4d6027 100644 --- a/packages/editor/src/components/projects/CreateProjectDialog.tsx +++ b/packages/editor/src/components/projects/CreateProjectDialog.tsx @@ -32,8 +32,6 @@ import { useTranslation } from 'react-i18next' import ProjectFields from '@etherealengine/client-core/src/admin/common/Project/ProjectFields' import { ProjectUpdateState } from '@etherealengine/client-core/src/admin/services/ProjectUpdateService' -import { DefaultUpdateSchedule } from '@etherealengine/common/src/interfaces/ProjectPackageJsonType' -import { ProjectType } from '@etherealengine/common/src/schemas/projects/project.schema' import { getMutableState, useHookstate } from '@etherealengine/hyperflux' import { Button } from '../inputs/Button' @@ -41,7 +39,7 @@ import styles from './styles.module.scss' interface Props { open: boolean - onSuccess: (name: string, repositoryPath?: string) => Promise + onSuccess: (orgname: string, reponame: string, repositoryPath?: string) => Promise onClose: () => void } @@ -50,28 +48,19 @@ export const CreateProjectDialog = ({ open, onSuccess, onClose }: Props): any => const [processing, setProcessing] = useState(false) const [error, setError] = useState('') - const [projectName, setProjectName] = useState('') - - const project = { - id: '', - name: 'tempProject', - thumbnail: '', - repositoryPath: '', - needsRebuild: false, - updateType: 'none' as ProjectType['updateType'], - updateSchedule: DefaultUpdateSchedule, - commitSHA: '', - commitDate: new Date() - } + const [orgName, setOrgName] = useState('') + const [repoName, setRepoName] = useState('') + + const name = `${orgName}/${repoName}` - const projectUpdateStatus = useHookstate(getMutableState(ProjectUpdateState)[project.name]).value + const projectUpdateStatus = useHookstate(getMutableState(ProjectUpdateState)[name]).value const handleCreateProject = async () => { - if (!projectName) return + if (!repoName || !orgName) return setProcessing(true) try { - await onSuccess(projectName, projectUpdateStatus?.destinationURL) + await onSuccess(orgName, repoName, projectUpdateStatus?.destinationURL) closeDialog() } catch (err) { setError(err.message) @@ -87,7 +76,7 @@ export const CreateProjectDialog = ({ open, onSuccess, onClose }: Props): any => } const closeDialog = () => { - setProjectName('') + setRepoName('') onClose() } @@ -109,6 +98,23 @@ export const CreateProjectDialog = ({ open, onSuccess, onClose }: Props): any =>
) : ( + setOrgName(e.target.value.toLowerCase())} + onKeyDown={handleSubmitOnEnter} + /> + input: styles.input } }} - value={projectName} - onChange={(e) => setProjectName(e.target.value.toLowerCase())} + value={repoName} + onChange={(e) => setRepoName(e.target.value.toLowerCase())} onKeyDown={handleSubmitOnEnter} /> {error && error.length > 0 &&

{error}

} @@ -130,7 +136,7 @@ export const CreateProjectDialog = ({ open, onSuccess, onClose }: Props): any => diff --git a/packages/editor/src/components/projects/ProjectsPage.tsx b/packages/editor/src/components/projects/ProjectsPage.tsx index aa37488b6e..9318401f56 100644 --- a/packages/editor/src/components/projects/ProjectsPage.tsx +++ b/packages/editor/src/components/projects/ProjectsPage.tsx @@ -266,8 +266,8 @@ const ProjectsPage = ({ studioPath }: { studioPath: string }) => { } } - const onCreateProject = async (name: string, repositoryPath?: string) => { - projectCreateQuery({ name, repositoryPath }, { query: { action: 'studio' } }) + const onCreateProject = async (orgname: string, projectName: string, repositoryPath?: string) => { + projectCreateQuery({ name: `${orgname}/${projectName}`, repositoryPath }, { query: { action: 'studio' } }) } const onCreatePermission = async (userInviteCode: InviteCode, projectId: string) => { @@ -367,7 +367,7 @@ const ProjectsPage = ({ studioPath }: { studioPath: string }) => {

{project.name.replace(/-/g, ' ')}

- {project.name !== 'default-project' && ( + {project.name !== 'etherealengine/default-project' && ( { -
@@ -534,7 +534,7 @@ const ProjectsPage = ({ studioPath }: { studioPath: string }) => {
) : null} */} - {activeProjectValue?.name !== 'default-project' && ( + {activeProjectValue?.name !== 'etherealengine/default-project' && ( { const sceneData = await Engine.instance.api.service(fileBrowserPath).patch(null, { project: projectName, @@ -168,7 +168,7 @@ export const createScene = async ( } export const onNewScene = async ( - templateURL = config.client.fileServer + '/projects/default-project/public/scenes/default.gltf' + templateURL = config.client.fileServer + '/projects/etherealengine/default-project/public/scenes/default.gltf' ) => { const { projectName } = getState(EditorState) if (!projectName) return diff --git a/packages/engine/src/assets/exporters/gltf/extensions/ImageRoutingExtension.ts b/packages/engine/src/assets/exporters/gltf/extensions/ImageRoutingExtension.ts index 1dae7ae81d..c2a9602c1d 100644 --- a/packages/engine/src/assets/exporters/gltf/extensions/ImageRoutingExtension.ts +++ b/packages/engine/src/assets/exporters/gltf/extensions/ImageRoutingExtension.ts @@ -55,8 +55,8 @@ export default class ImageRoutingExtension extends ExporterExtension implements let projectSrc = getState(EditorState).projectName! let relativeSrc = './assets/' if (resolvedPath) { - projectSrc = resolvedPath[1] - relativeSrc = resolvedPath[2] + projectSrc = `${resolvedPath[1]}/${resolvedPath[2]}` + relativeSrc = resolvedPath[3] relativeSrc = relativeSrc.replace(/\/[^\/]*$/, '') } const dst = this.writer.options.relativePath!.replace(/\/[^\/]*$/, '') @@ -70,8 +70,8 @@ export default class ImageRoutingExtension extends ExporterExtension implements let oldURI = texture.userData.src if (!oldURI) { const resolved = STATIC_ASSET_REGEX.exec(texture.image.src)! - const oldProject = resolved[1] - const relativeOldURL = resolved[2] + const oldProject = `${resolved[1]}/${resolved[2]}` + const relativeOldURL = resolved[3] if (oldProject !== projectSrc) { const srcWithProject = pathJoin(projectSrc, relativeSrc) const dstWithProject = pathJoin(oldProject, relativeOldURL) diff --git a/packages/engine/src/assets/functions/pathResolver.ts b/packages/engine/src/assets/functions/pathResolver.ts index 8e55bef4ef..7686262372 100644 --- a/packages/engine/src/assets/functions/pathResolver.ts +++ b/packages/engine/src/assets/functions/pathResolver.ts @@ -35,11 +35,14 @@ export function getFileName(path: string) { } export function getRelativeURI(path: string) { - return STATIC_ASSET_REGEX.exec(path)?.[2] ?? '' + return STATIC_ASSET_REGEX.exec(path)?.[3] ?? '' } export function getProjectName(path: string) { - return STATIC_ASSET_REGEX.exec(path)?.[1] ?? '' + const match = STATIC_ASSET_REGEX.exec(path) + if (!match?.length) return '' + const [, orgName, projectName] = match! + return `${orgName}/${projectName}` } export function modelResourcesPath(modelName: string) { diff --git a/packages/engine/src/avatar/animation/Util.ts b/packages/engine/src/avatar/animation/Util.ts index 9ce0ea5140..545413198f 100644 --- a/packages/engine/src/avatar/animation/Util.ts +++ b/packages/engine/src/avatar/animation/Util.ts @@ -74,7 +74,7 @@ export const preloadedAnimations = { emotes: 'emotes' } -export const defaultAnimationPath = `${config.client.fileServer}/projects/default-project/assets/animations/` +export const defaultAnimationPath = `${config.client.fileServer}/projects/etherealengine/default-project/assets/animations/` export const matchesIkTarget = matches.some( ...Object.keys(ikTargets).map((k: keyof typeof ikTargets) => matches.literal(k)) diff --git a/packages/engine/src/avatar/functions/XRControllerFunctions.ts b/packages/engine/src/avatar/functions/XRControllerFunctions.ts index 34e0df4bf2..5e077f8917 100644 --- a/packages/engine/src/avatar/functions/XRControllerFunctions.ts +++ b/packages/engine/src/avatar/functions/XRControllerFunctions.ts @@ -45,7 +45,7 @@ export const initializeControllerModel = async (entity: Entity, handedness: stri if (avatarInputControllerType !== AvatarControllerType.OculusQuest) return const [gltf] = await getGLTFAsync( - `${config.client.fileServer}/projects/default-project/assets/controllers/${handedness}_controller.glb` + `${config.client.fileServer}/projects/etherealengine/default-project/assets/controllers/${handedness}_controller.glb` ) let handMesh = gltf?.scene?.children[0] @@ -85,7 +85,7 @@ export const initializeHandModel = async (entity: Entity, handedness: string) => if (avatarInputControllerType === AvatarControllerType.None) return const [gltf] = await getGLTFAsync( - `${config.client.fileServer}/projects/default-project/assets/controllers/${handedness}.glb` + `${config.client.fileServer}/projects/etherealengine/default-project/assets/controllers/${handedness}.glb` ) const handMesh = gltf?.scene?.children[0] diff --git a/packages/engine/src/avatar/systems/AvatarAnimationSystem.tsx b/packages/engine/src/avatar/systems/AvatarAnimationSystem.tsx index 252bcb3411..7d0f1e0276 100644 --- a/packages/engine/src/avatar/systems/AvatarAnimationSystem.tsx +++ b/packages/engine/src/avatar/systems/AvatarAnimationSystem.tsx @@ -331,7 +331,7 @@ const Reactor = () => { const animations = [preloadedAnimations.locomotion, preloadedAnimations.emotes] const [gltfs] = useBatchGLTF( animations.map((animationFile) => { - return `${config.client.fileServer}/projects/default-project/assets/animations/${animationFile}.glb` + return `${config.client.fileServer}/projects/etherealengine/default-project/assets/animations/${animationFile}.glb` }) ) const manager = useMutableState(AnimationState) diff --git a/packages/engine/src/scene/components/HyperspaceTagComponent.ts b/packages/engine/src/scene/components/HyperspaceTagComponent.ts index 6b080de8f3..3a0cc65d92 100644 --- a/packages/engine/src/scene/components/HyperspaceTagComponent.ts +++ b/packages/engine/src/scene/components/HyperspaceTagComponent.ts @@ -186,7 +186,7 @@ export const HyperspaceTagComponent = defineComponent({ reactor: () => { const entity = useEntityContext() const [galaxyTexture] = useTexture( - `${config.client.fileServer}/projects/default-project/assets/galaxyTexture.jpg`, + `${config.client.fileServer}/projects/etherealengine/default-project/assets/galaxyTexture.jpg`, entity ) diff --git a/packages/engine/src/scene/components/ImageComponent.ts b/packages/engine/src/scene/components/ImageComponent.ts index 1c41ca6fca..bc67730a1a 100644 --- a/packages/engine/src/scene/components/ImageComponent.ts +++ b/packages/engine/src/scene/components/ImageComponent.ts @@ -75,7 +75,7 @@ export const ImageComponent = defineComponent({ onInit: (entity) => { return { - source: `${config.client.fileServer}/projects/default-project/assets/sample_etc1s.ktx2`, + source: `${config.client.fileServer}/projects/etherealengine/default-project/assets/sample_etc1s.ktx2`, alphaMode: ImageAlphaMode.Opaque as ImageAlphaModeType, alphaCutoff: 0.5, projection: ImageProjection.Flat as ImageProjectionType, diff --git a/packages/engine/src/scene/components/SkyboxComponent.ts b/packages/engine/src/scene/components/SkyboxComponent.ts index 1a76064625..fd2156873c 100755 --- a/packages/engine/src/scene/components/SkyboxComponent.ts +++ b/packages/engine/src/scene/components/SkyboxComponent.ts @@ -55,7 +55,7 @@ export const SkyboxComponent = defineComponent({ return { backgroundColor: new Color(0x000000), equirectangularPath: '', - cubemapPath: `${config.client.fileServer}/projects/default-project/assets/skyboxsun25deg/`, + cubemapPath: `${config.client.fileServer}/projects/etherealengine/default-project/assets/skyboxsun25deg/`, backgroundType: 1, sky: null! as Sky | null, skyboxProps: { diff --git a/packages/engine/src/scene/components/UVOL2Component.ts b/packages/engine/src/scene/components/UVOL2Component.ts index 223910ae95..461e5ab161 100644 --- a/packages/engine/src/scene/components/UVOL2Component.ts +++ b/packages/engine/src/scene/components/UVOL2Component.ts @@ -704,7 +704,6 @@ transformed.z += mix(keyframeA.z, keyframeB.z, mixRatio); undefined, 'baseColor' ) - // media.src = 'https://localhost:8642/projects/default-project/ubx_kimberly_bird_t2_2k_std_30fps.mp4' mediaValue.preload = 'auto' media.addEventListener('loadeddata', () => { component.firstTextureFrameLoaded.set(true) diff --git a/packages/engine/src/scene/functions/loaders/VariantFunctions.ts b/packages/engine/src/scene/functions/loaders/VariantFunctions.ts index 1516505683..c58120d009 100644 --- a/packages/engine/src/scene/functions/loaders/VariantFunctions.ts +++ b/packages/engine/src/scene/functions/loaders/VariantFunctions.ts @@ -66,8 +66,8 @@ export function updateModelVariant( const levelIndex = variantComponent.levels.findIndex((level) => level.metadata['device'] === targetDevice) if (levelIndex < 0) return const deviceVariant = variantComponent.levels[levelIndex] - const modelRelativePath = STATIC_ASSET_REGEX.exec(modelComponent.src.value)?.[2] - const deviceRelativePath = deviceVariant ? STATIC_ASSET_REGEX.exec(deviceVariant.src.value)?.[2] : '' + const modelRelativePath = STATIC_ASSET_REGEX.exec(modelComponent.src.value)?.[3] + const deviceRelativePath = deviceVariant ? STATIC_ASSET_REGEX.exec(deviceVariant.src.value)?.[3] : '' if (deviceVariant && modelRelativePath !== deviceRelativePath) { variantComponent.currentLevel.set(levelIndex) } diff --git a/packages/engine/src/scene/systems/ShadowSystem.tsx b/packages/engine/src/scene/systems/ShadowSystem.tsx index 63a498a5c5..e7453f22cb 100644 --- a/packages/engine/src/scene/systems/ShadowSystem.tsx +++ b/packages/engine/src/scene/systems/ShadowSystem.tsx @@ -450,7 +450,9 @@ const reactor = () => { const useShadows = useShadowsEnabled() - const [shadowTexture] = useTexture(`${config.client.fileServer}/projects/default-project/assets/drop-shadow.png`) + const [shadowTexture] = useTexture( + `${config.client.fileServer}/projects/etherealengine/default-project/assets/drop-shadow.png` + ) useEffect(() => { if (!shadowTexture) return diff --git a/packages/projects/default-project/assets/prefabs/image.prefab.gltf b/packages/projects/default-project/assets/prefabs/image.prefab.gltf index dafc2d134c..23c9d4d97b 100644 --- a/packages/projects/default-project/assets/prefabs/image.prefab.gltf +++ b/packages/projects/default-project/assets/prefabs/image.prefab.gltf @@ -18,7 +18,7 @@ "EE_uuid": "0db4cf3f-8adb-42eb-8efa-3102182e61fc", "EE_visible": true, "EE_image": { - "source": "__$project$__/default-project/assets/sample_etc1s.ktx2", + "source": "__$project$__/etherealengine/default-project/assets/sample_etc1s.ktx2", "alphaMode": "Opaque", "alphaCutoff": 0.5, "projection": "Flat", diff --git a/packages/projects/default-project/assets/prefabs/skybox.prefab.gltf b/packages/projects/default-project/assets/prefabs/skybox.prefab.gltf index de80f852c8..32bb9fac4d 100644 --- a/packages/projects/default-project/assets/prefabs/skybox.prefab.gltf +++ b/packages/projects/default-project/assets/prefabs/skybox.prefab.gltf @@ -21,7 +21,7 @@ "EE_skybox": { "backgroundColor": 0, "equirectangularPath": "", - "cubemapPath": "__$project$__/default-project/assets/skyboxsun25deg/", + "cubemapPath": "__$project$__/etherealengine/default-project/assets/skyboxsun25deg/", "backgroundType": 1, "skyboxProps": { "turbidity": 10, diff --git a/packages/projects/default-project/assets/test-equippable.glb b/packages/projects/default-project/assets/test-equippable.glb deleted file mode 100644 index c0a90efad9..0000000000 Binary files a/packages/projects/default-project/assets/test-equippable.glb and /dev/null differ diff --git a/packages/projects/default-project/manifest.json b/packages/projects/default-project/manifest.json index e17132150e..08d70161da 100644 --- a/packages/projects/default-project/manifest.json +++ b/packages/projects/default-project/manifest.json @@ -1,5 +1,5 @@ { - "name": "default-project", + "name": "etherealengine/default-project", "version": "0.0.0", "engineVersion": "1.6.0", "description": "The default project for iR Engine", diff --git a/packages/projects/default-project/package.json b/packages/projects/default-project/package.json index b0ca0205e0..4821b66461 100644 --- a/packages/projects/default-project/package.json +++ b/packages/projects/default-project/package.json @@ -1,5 +1,5 @@ { - "name": "default-project", + "name": "@etherealengine/default-project", "version": "0.0.0", "main": "", "scripts": { diff --git a/packages/projects/default-project/public/scenes/apartment.gltf b/packages/projects/default-project/public/scenes/apartment.gltf index 27aa799c85..4d4b7181b2 100644 --- a/packages/projects/default-project/public/scenes/apartment.gltf +++ b/packages/projects/default-project/public/scenes/apartment.gltf @@ -57,7 +57,7 @@ "bakeType": "Baked", "resolution": 2048, "refreshMode": "OnAwake", - "envMapOrigin": "__$project$__/default-project/public/scenes/apartment.envmap.ktx2", + "envMapOrigin": "__$project$__/etherealengine/default-project/public/scenes/apartment.envmap.ktx2", "boxProjection": false }, "EE_camera_settings": { @@ -227,8 +227,8 @@ }, "EE_visible": true, "EE_scene_settings": { - "thumbnailURL": "__$project$__/default-project/public/scenes/apartment.thumbnail.jpg", - "loadingScreenURL": "__$project$__/default-project/public/scenes/apartment.loadingscreen.ktx2", + "thumbnailURL": "__$project$__/etherealengine/default-project/public/scenes/apartment.thumbnail.jpg", + "loadingScreenURL": "__$project$__/etherealengine/default-project/public/scenes/apartment.loadingscreen.ktx2", "primaryColor": "#A8B6C6", "backgroundColor": "rgb(64, 70, 71)", "alternativeColor": "#8EBBAE", @@ -268,7 +268,7 @@ "EE_visible": true, "EE_skybox": { "backgroundColor": 0, - "equirectangularPath": "__$project$__/default-project/assets/apartment_skybox.jpg", + "equirectangularPath": "__$project$__/etherealengine/default-project/assets/apartment_skybox.jpg", "cubemapPath": "", "backgroundType": 2, "skyboxProps": { @@ -428,14 +428,14 @@ 1 ], "extras": { - "src": "__$project$__/default-project/assets/apartment.glb" + "src": "__$project$__/etherealengine/default-project/assets/apartment.glb" }, "name": "model", "extensions": { "EE_uuid": "b46b57c1-bd37-43cc-b000-8b4411fe728b", "EE_visible": true, "EE_model": { - "src": "__$project$__/default-project/assets/apartment.glb", + "src": "__$project$__/etherealengine/default-project/assets/apartment.glb", "cameraOcclusion": true, "convertToVRM": false }, @@ -898,7 +898,7 @@ "redirect": false, "effectType": "Hyperspace", "previewType": "Spherical", - "previewImageURL": "__$project$__/default-project/public/scenes/apartment-Portal.ktx2", + "previewImageURL": "__$project$__/etherealengine/default-project/public/scenes/apartment-Portal.ktx2", "spawnPosition": { "x": -12, "y": 1, @@ -1068,7 +1068,7 @@ 1 ], "extras": { - "src": "__$project$__/default-project/assets/keycard.glb" + "src": "__$project$__/etherealengine/default-project/assets/keycard.glb" }, "name": "Model", "extensions": { @@ -1079,7 +1079,7 @@ "receive": true }, "EE_model": { - "src": "__$project$__/default-project/assets/keycard.glb", + "src": "__$project$__/etherealengine/default-project/assets/keycard.glb", "cameraOcclusion": false, "convertToVRM": false }, @@ -1100,7 +1100,7 @@ "type": "Skybox", "envMapTextureType": "Cubemap", "envMapSourceColor": 1193046, - "envMapSourceURL": "__$project$__/default-project/assets/skyboxsun25deg/", + "envMapSourceURL": "__$project$__/etherealengine/default-project/assets/skyboxsun25deg/", "envMapSourceEntityUUID": "", "envMapIntensity": 1 } @@ -1206,7 +1206,7 @@ "bakeType": "Baked", "resolution": 1024, "refreshMode": "OnAwake", - "envMapOrigin": "__$project$__/default-project/public/scenes/apartment-New-EnvMap%20Bake.ktx2", + "envMapOrigin": "__$project$__/etherealengine/default-project/public/scenes/apartment-New-EnvMap%20Bake.ktx2", "boxProjection": true }, "EE_visible": true diff --git a/packages/projects/default-project/public/scenes/default.gltf b/packages/projects/default-project/public/scenes/default.gltf index b3e277b80a..d6441c59da 100644 --- a/packages/projects/default-project/public/scenes/default.gltf +++ b/packages/projects/default-project/public/scenes/default.gltf @@ -41,7 +41,7 @@ "bakeType": "Baked", "resolution": 2048, "refreshMode": "OnAwake", - "envMapOrigin": "__$project$__/default-project/public/scenes/default.envmap.ktx2", + "envMapOrigin": "__$project$__/etherealengine/default-project/public/scenes/default.envmap.ktx2", "boxProjection": true }, "EE_fog": { @@ -334,8 +334,8 @@ "shadowMapType": 2 }, "EE_scene_settings": { - "thumbnailURL": "__$project$__/default-project/public/scenes/default.thumbnail.jpg", - "loadingScreenURL": "__$project$__/default-project/public/scenes/default.loadingscreen.ktx2", + "thumbnailURL": "__$project$__/etherealengine/default-project/public/scenes/default.thumbnail.jpg", + "loadingScreenURL": "__$project$__/etherealengine/default-project/public/scenes/default.loadingscreen.ktx2", "primaryColor": "#38620D", "backgroundColor": "rgb(214, 214, 211)", "alternativeColor": "#376312", @@ -377,7 +377,7 @@ "EE_skybox": { "backgroundColor": 3026478, "equirectangularPath": "", - "cubemapPath": "__$project$__/default-project/assets/skyboxsun25deg/", + "cubemapPath": "__$project$__/etherealengine/default-project/assets/skyboxsun25deg/", "backgroundType": 0, "skyboxProps": { "turbidity": 10, @@ -469,7 +469,7 @@ "name": "platform", "extensions": { "EE_model": { - "src": "__$project$__/default-project/assets/platform.glb", + "src": "__$project$__/etherealengine/default-project/assets/platform.glb", "cameraOcclusion": true, "convertToVRM": false }, diff --git a/packages/projects/default-project/public/scenes/sky-station.gltf b/packages/projects/default-project/public/scenes/sky-station.gltf index 137849f1ff..6f0ce31ae3 100644 --- a/packages/projects/default-project/public/scenes/sky-station.gltf +++ b/packages/projects/default-project/public/scenes/sky-station.gltf @@ -33,7 +33,7 @@ "bakeType": "Baked", "resolution": 1024, "refreshMode": "OnAwake", - "envMapOrigin": "__$project$__/default-project/public/scenes/sky-station.envmap.ktx2", + "envMapOrigin": "__$project$__/etherealengine/default-project/public/scenes/sky-station.envmap.ktx2", "boxProjection": true }, "EE_camera_settings": { @@ -203,8 +203,8 @@ }, "EE_visible": true, "EE_scene_settings": { - "thumbnailURL": "__$project$__/default-project/public/scenes/sky-station.thumbnail.jpg", - "loadingScreenURL": "__$project$__/default-project/public/scenes/sky-station.loadingscreen.ktx2", + "thumbnailURL": "__$project$__/etherealengine/default-project/public/scenes/sky-station.thumbnail.jpg", + "loadingScreenURL": "__$project$__/etherealengine/default-project/public/scenes/sky-station.loadingscreen.ktx2", "primaryColor": "#0A6493", "backgroundColor": "rgb(238, 232, 243)", "alternativeColor": "#316F9E", @@ -230,7 +230,7 @@ "EE_uuid": "35621369-7e83-4878-953c-f30bf81dc775", "EE_skybox": { "backgroundColor": 0, - "equirectangularPath": "__$project$__/default-project/assets/sky_skybox.jpg", + "equirectangularPath": "__$project$__/etherealengine/default-project/assets/sky_skybox.jpg", "cubemapPath": "", "backgroundType": 2, "skyboxProps": { @@ -289,7 +289,7 @@ { "matrix": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -6.099999904632568, 1.2999999523162842, 0, 1], "extras": { - "src": "__$project$__/default-project/assets/Skybase.glb" + "src": "__$project$__/etherealengine/default-project/assets/Skybase.glb" }, "name": "Model", "extensions": { @@ -299,7 +299,7 @@ "receive": true }, "EE_model": { - "src": "__$project$__/default-project/assets/Skybase.glb", + "src": "__$project$__/etherealengine/default-project/assets/Skybase.glb", "cameraOcclusion": true, "convertToVRM": false }, @@ -320,7 +320,7 @@ "type": "Texture", "envMapTextureType": "Equirectangular", "envMapSourceColor": 1193046, - "envMapSourceURL": "__$project$__/default-project/assets/sky_skybox.jpg", + "envMapSourceURL": "__$project$__/etherealengine/default-project/assets/sky_skybox.jpg", "envMapSourceEntityUUID": "", "envMapIntensity": 1.5 }, @@ -342,7 +342,7 @@ "redirect": false, "effectType": "Hyperspace", "previewType": "Spherical", - "previewImageURL": "__$project$__/default-project/public/scenes/sky-station-Portal--%20to%20Apartment.ktx2", + "previewImageURL": "__$project$__/etherealengine/default-project/public/scenes/sky-station-Portal--%20to%20Apartment.ktx2", "spawnPosition": { "x": 49.5, "y": 2, @@ -403,7 +403,7 @@ "redirect": false, "effectType": "None", "previewType": "Spherical", - "previewImageURL": "__$project$__/default-project/public/scenes/sky-station-Portal--%20Sky%20Station%20Interior.ktx2", + "previewImageURL": "__$project$__/etherealengine/default-project/public/scenes/sky-station-Portal--%20Sky%20Station%20Interior.ktx2", "spawnPosition": { "x": 15, "y": 9, @@ -464,7 +464,7 @@ "redirect": false, "effectType": "None", "previewType": "Spherical", - "previewImageURL": "__$project$__/default-project/public/scenes/sky-station-Portal--%20Sky%20Station%20Exterior.ktx2", + "previewImageURL": "__$project$__/etherealengine/default-project/public/scenes/sky-station-Portal--%20Sky%20Station%20Exterior.ktx2", "spawnPosition": { "x": -60, "y": 3, diff --git a/packages/projects/default-project/resources.json b/packages/projects/default-project/resources.json index 80c6cf0c6d..2f8ab4980c 100644 --- a/packages/projects/default-project/resources.json +++ b/packages/projects/default-project/resources.json @@ -5,7 +5,7 @@ "Model" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsanimationsdefault_skeleton.vrm-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsanimationsdefault_skeleton.vrm-thumbnail.png", "thumbnailMode": "automatic" }, "assets/animations/emotes.glb": { @@ -21,7 +21,7 @@ "Model" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsanimationslocomotion.glb-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsanimationslocomotion.glb-thumbnail.png", "thumbnailMode": "automatic" }, "assets/animations/optional/seated.fbx": { @@ -37,7 +37,7 @@ "Image" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsapartment_skybox.jpg-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsapartment_skybox.jpg-thumbnail.png", "thumbnailMode": "automatic" }, "assets/apartment-CubemapBake.png": { @@ -53,7 +53,7 @@ "Model" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsapartment.glb-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsapartment.glb-thumbnail.png", "thumbnailMode": "automatic" }, "assets/avatars/female_01.png": { @@ -62,7 +62,7 @@ "Image" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsavatarsfemale_01.png-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsavatarsfemale_01.png-thumbnail.png", "thumbnailMode": "automatic" }, "assets/avatars/female_01.vrm": { @@ -71,7 +71,7 @@ "Model" ], "dependencies": [ - "projects/default-project/assets/avatars/female_01.png" + "projects/etherealengine/default-project/assets/avatars/female_01.png" ] }, "assets/avatars/female_02.png": { @@ -80,7 +80,7 @@ "Image" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsavatarsfemale_02.png-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsavatarsfemale_02.png-thumbnail.png", "thumbnailMode": "automatic" }, "assets/avatars/female_02.vrm": { @@ -89,7 +89,7 @@ "Model" ], "dependencies": [ - "projects/default-project/assets/avatars/female_02.png" + "projects/etherealengine/default-project/assets/avatars/female_02.png" ] }, "assets/avatars/female_03.png": { @@ -98,7 +98,7 @@ "Image" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsavatarsfemale_03.png-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsavatarsfemale_03.png-thumbnail.png", "thumbnailMode": "automatic" }, "assets/avatars/female_03.vrm": { @@ -107,9 +107,9 @@ "Model" ], "dependencies": [ - "projects/default-project/assets/avatars/female_03.png" + "projects/etherealengine/default-project/assets/avatars/female_03.png" ], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsavatarsfemale_03.vrm-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsavatarsfemale_03.vrm-thumbnail.png", "thumbnailMode": "automatic" }, "assets/avatars/male_01.png": { @@ -118,7 +118,7 @@ "Image" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsavatarsmale_01.png-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsavatarsmale_01.png-thumbnail.png", "thumbnailMode": "automatic" }, "assets/avatars/male_01.vrm": { @@ -127,9 +127,9 @@ "Model" ], "dependencies": [ - "projects/default-project/assets/avatars/male_01.png" + "projects/etherealengine/default-project/assets/avatars/male_01.png" ], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsavatarsmale_01.vrm-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsavatarsmale_01.vrm-thumbnail.png", "thumbnailMode": "automatic" }, "assets/avatars/male_02.png": { @@ -145,9 +145,9 @@ "Model" ], "dependencies": [ - "projects/default-project/assets/avatars/male_02.png" + "projects/etherealengine/default-project/assets/avatars/male_02.png" ], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsavatarsmale_02.vrm-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsavatarsmale_02.vrm-thumbnail.png", "thumbnailMode": "automatic" }, "assets/avatars/male_03.png": { @@ -156,7 +156,7 @@ "Image" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsavatarsmale_03.png-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsavatarsmale_03.png-thumbnail.png", "thumbnailMode": "automatic" }, "assets/avatars/male_03.vrm": { @@ -165,9 +165,9 @@ "Model" ], "dependencies": [ - "projects/default-project/assets/avatars/male_03.png" + "projects/etherealengine/default-project/assets/avatars/male_03.png" ], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsavatarsmale_03.vrm-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsavatarsmale_03.vrm-thumbnail.png", "thumbnailMode": "automatic" }, "assets/cloud.png": { @@ -176,7 +176,7 @@ "Image" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetscloud.png-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetscloud.png-thumbnail.png", "thumbnailMode": "automatic" }, "assets/collisioncube.glb": { @@ -185,7 +185,7 @@ "Model" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetscollisioncube.glb-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetscollisioncube.glb-thumbnail.png", "thumbnailMode": "automatic" }, "assets/controllers/left_controller.glb": { @@ -194,7 +194,7 @@ "Model" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetscontrollersleft_controller.glb-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetscontrollersleft_controller.glb-thumbnail.png", "thumbnailMode": "automatic" }, "assets/controllers/left.glb": { @@ -203,7 +203,7 @@ "Model" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetscontrollersleft.glb-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetscontrollersleft.glb-thumbnail.png", "thumbnailMode": "automatic" }, "assets/controllers/right_controller.glb": { @@ -219,7 +219,7 @@ "Model" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetscontrollersright.glb-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetscontrollersright.glb-thumbnail.png", "thumbnailMode": "automatic" }, "assets/default-silhouette.svg": { @@ -235,7 +235,7 @@ "Image" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsdrop-shadow.png-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsdrop-shadow.png-thumbnail.png", "thumbnailMode": "automatic" }, "assets/galaxyTexture.jpg": { @@ -244,7 +244,7 @@ "Image" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsgalaxyTexture.jpg-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsgalaxyTexture.jpg-thumbnail.png", "thumbnailMode": "automatic" }, "assets/keycard.glb": { @@ -253,7 +253,7 @@ "Model" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetskeycard.glb-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetskeycard.glb-thumbnail.png", "thumbnailMode": "automatic" }, "assets/platform.glb": { @@ -269,7 +269,7 @@ "Model" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsportal_frame.glb-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsportal_frame.glb-thumbnail.png", "thumbnailMode": "automatic" }, "assets/prefabs/3d-model.prefab.gltf": { @@ -285,7 +285,7 @@ "Default Prefab" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsprefabsambient-light.prefab.gltf-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsprefabsambient-light.prefab.gltf-thumbnail.png", "thumbnailMode": "automatic" }, "assets/prefabs/body.prefab.gltf": { @@ -301,7 +301,7 @@ "Default Prefab" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsprefabsbox-collider.prefab.gltf-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsprefabsbox-collider.prefab.gltf-thumbnail.png", "thumbnailMode": "automatic" }, "assets/prefabs/camera.prefab.gltf": { @@ -317,7 +317,7 @@ "Default Prefab" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsprefabscylinder-collider.prefab.gltf-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsprefabscylinder-collider.prefab.gltf-thumbnail.png", "thumbnailMode": "automatic" }, "assets/prefabs/directional-light.prefab.gltf": { @@ -326,7 +326,7 @@ "Default Prefab" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsprefabsdirectional-light.prefab.gltf-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsprefabsdirectional-light.prefab.gltf-thumbnail.png", "thumbnailMode": "automatic" }, "assets/prefabs/fog.prefab.gltf": { @@ -335,7 +335,7 @@ "Default Prefab" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsprefabsfog.prefab.gltf-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsprefabsfog.prefab.gltf-thumbnail.png", "thumbnailMode": "automatic" }, "assets/prefabs/geo.prefab.gltf": { @@ -344,7 +344,7 @@ "Default Prefab" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsprefabsgeo.prefab.gltf-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsprefabsgeo.prefab.gltf-thumbnail.png", "thumbnailMode": "automatic" }, "assets/prefabs/ground-plane.prefab.gltf": { @@ -360,7 +360,7 @@ "Default Prefab" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsprefabshemisphere-light.prefab.gltf-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsprefabshemisphere-light.prefab.gltf-thumbnail.png", "thumbnailMode": "automatic" }, "assets/prefabs/image.prefab.gltf": { @@ -383,7 +383,7 @@ "Default Prefab" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsprefabspoint-light.prefab.gltf-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsprefabspoint-light.prefab.gltf-thumbnail.png", "thumbnailMode": "automatic" }, "assets/prefabs/postprocessing.prefab.gltf": { @@ -392,7 +392,7 @@ "Default Prefab" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsprefabspostprocessing.prefab.gltf-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsprefabspostprocessing.prefab.gltf-thumbnail.png", "thumbnailMode": "automatic" }, "assets/prefabs/skybox.prefab.gltf": { @@ -401,7 +401,7 @@ "Default Prefab" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsprefabsskybox.prefab.gltf-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsprefabsskybox.prefab.gltf-thumbnail.png", "thumbnailMode": "automatic" }, "assets/prefabs/sphere-collider.prefab.gltf": { @@ -410,7 +410,7 @@ "Default Prefab" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsprefabssphere-collider.prefab.gltf-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsprefabssphere-collider.prefab.gltf-thumbnail.png", "thumbnailMode": "automatic" }, "assets/prefabs/spot-light.prefab.gltf": { @@ -419,7 +419,7 @@ "Default Prefab" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsprefabsspot-light.prefab.gltf-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsprefabsspot-light.prefab.gltf-thumbnail.png", "thumbnailMode": "automatic" }, "assets/prefabs/text.prefab.gltf": { @@ -428,7 +428,7 @@ "Default Prefab" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsprefabstext.prefab.gltf-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsprefabstext.prefab.gltf-thumbnail.png", "thumbnailMode": "automatic" }, "assets/prefabs/title.prefab.gltf": { @@ -451,7 +451,7 @@ "Image" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetssample_etc1s.ktx2-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetssample_etc1s.ktx2-thumbnail.png", "thumbnailMode": "automatic" }, "assets/SampleAudio.mp3": { @@ -467,7 +467,7 @@ "Video" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsSampleVideo.mp4-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsSampleVideo.mp4-thumbnail.png", "thumbnailMode": "automatic" }, "assets/sky_skybox.jpg": { @@ -476,7 +476,7 @@ "Image" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetssky_skybox.jpg-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetssky_skybox.jpg-thumbnail.png", "thumbnailMode": "automatic" }, "assets/Skybase.glb": { @@ -485,7 +485,7 @@ "Model" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsSkybase.glb-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsSkybase.glb-thumbnail.png", "thumbnailMode": "automatic" }, "assets/skyboxsun25deg/negx.jpg": { @@ -494,7 +494,7 @@ "Image" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsskyboxsun25degnegx.jpg-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsskyboxsun25degnegx.jpg-thumbnail.png", "thumbnailMode": "automatic" }, "assets/skyboxsun25deg/negy.jpg": { @@ -510,7 +510,7 @@ "Image" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsskyboxsun25degnegz.jpg-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsskyboxsun25degnegz.jpg-thumbnail.png", "thumbnailMode": "automatic" }, "assets/skyboxsun25deg/posx.jpg": { @@ -519,7 +519,7 @@ "Image" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsskyboxsun25degposx.jpg-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsskyboxsun25degposx.jpg-thumbnail.png", "thumbnailMode": "automatic" }, "assets/skyboxsun25deg/posy.jpg": { @@ -528,7 +528,7 @@ "Image" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsskyboxsun25degposy.jpg-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsskyboxsun25degposy.jpg-thumbnail.png", "thumbnailMode": "automatic" }, "assets/skyboxsun25deg/posz.jpg": { @@ -544,7 +544,7 @@ "Model" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetstest-equippable.glb-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetstest-equippable.glb-thumbnail.png", "thumbnailMode": "automatic" }, "assets/UV.png": { @@ -553,7 +553,7 @@ "Image" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/thumbnails/default-projectassetsUV.png-thumbnail.png", + "thumbnailKey": "projects/etherealengine/default-project/public/thumbnails/default-projectassetsUV.png-thumbnail.png", "thumbnailMode": "automatic" }, "public/scenes/apartment-New-EnvMap Bake.ktx2": { @@ -583,7 +583,7 @@ "Model" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/scenes/apartment.thumbnail.jpg" + "thumbnailKey": "projects/etherealengine/default-project/public/scenes/apartment.thumbnail.jpg" }, "public/scenes/apartment.loadingscreen.ktx2": { "type": "asset", @@ -605,7 +605,7 @@ "Model" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/scenes/default.thumbnail.jpg" + "thumbnailKey": "projects/etherealengine/default-project/public/scenes/default.thumbnail.jpg" }, "public/scenes/default.loadingscreen.ktx2": { "type": "asset", @@ -648,7 +648,7 @@ "Model" ], "dependencies": [], - "thumbnailKey": "projects/default-project/public/scenes/sky-station.thumbnail.jpg" + "thumbnailKey": "projects/etherealengine/default-project/public/scenes/sky-station.thumbnail.jpg" }, "public/scenes/sky-station.loadingscreen.ktx2": { "type": "asset", diff --git a/packages/projects/default-project/tsconfig.json b/packages/projects/default-project/tsconfig.json index 272b27183b..3dc003ad1f 100644 --- a/packages/projects/default-project/tsconfig.json +++ b/packages/projects/default-project/tsconfig.json @@ -28,9 +28,9 @@ "**/node_modules/**" ], "include": [ - "../../../../__global.d.ts", - "../../../server-core/src/*", - "../**/*.ts", - "../**/*.tsx" + "../../../../../__global.d.ts", + "../../../../server-core/src/*", + "./**/*.ts", + "./**/*.tsx" ] } diff --git a/packages/projects/loadConfigForProject.ts b/packages/projects/loadConfigForProject.ts index 18237b35c0..9a37a41614 100644 --- a/packages/projects/loadConfigForProject.ts +++ b/packages/projects/loadConfigForProject.ts @@ -30,7 +30,9 @@ const configs = {} as Record export const loadConfigForProject = async (project: string): Promise => { try { if (configs[project]) return configs[project] - const projectConfig = (await import(`./projects/${project}/xrengine.config.ts`)).default as ProjectConfigInterface + const [orgname, projectName] = project.split('/') + const projectConfig = (await import(`./projects/${orgname}/${projectName}/xrengine.config.ts`)) + .default as ProjectConfigInterface configs[project] = projectConfig return projectConfig } catch (e) { diff --git a/packages/projects/template-project/.github/workflows/project-build.yml b/packages/projects/template-project/.github/workflows/project-build.yml index a00deb78f1..586d6b59b6 100755 --- a/packages/projects/template-project/.github/workflows/project-build.yml +++ b/packages/projects/template-project/.github/workflows/project-build.yml @@ -25,7 +25,7 @@ jobs: - name: Checkout Project uses: actions/checkout@v3 with: - path: './packages/projects/projects/${{ github.event.repository.name }}' + path: './packages/projects/projects/etherealengine/${{ github.event.repository.name }}' - name: Use Node.js uses: actions/setup-node@v3 with: diff --git a/packages/projects/template-project/manifest.json b/packages/projects/template-project/manifest.json index d1f15c9e8b..7e6ae79915 100644 --- a/packages/projects/template-project/manifest.json +++ b/packages/projects/template-project/manifest.json @@ -1,5 +1,5 @@ { - "name": "ee-template", + "name": "etherealengine/ee-template", "version": "0.0.0", "engineVersion": "1.6.0", "description": "The default project for iR Engine", diff --git a/packages/projects/template-project/package.json b/packages/projects/template-project/package.json index ff5734a2b3..6893a42b9e 100644 --- a/packages/projects/template-project/package.json +++ b/packages/projects/template-project/package.json @@ -1,5 +1,5 @@ { - "name": "ee-template", + "name": "@etherealengine/ee-template", "version": "0.0.0", "scripts": { "test": "mocha --config .mocharc.js", diff --git a/packages/projects/template-project/tsconfig.json b/packages/projects/template-project/tsconfig.json index 272b27183b..3dc003ad1f 100755 --- a/packages/projects/template-project/tsconfig.json +++ b/packages/projects/template-project/tsconfig.json @@ -28,9 +28,9 @@ "**/node_modules/**" ], "include": [ - "../../../../__global.d.ts", - "../../../server-core/src/*", - "../**/*.ts", - "../**/*.tsx" + "../../../../../__global.d.ts", + "../../../../server-core/src/*", + "./**/*.ts", + "./**/*.tsx" ] } diff --git a/packages/server-core/src/hooks/resolve-project-id.test.ts b/packages/server-core/src/hooks/resolve-project-id.test.ts index d744c0c0a5..3f2893ff05 100755 --- a/packages/server-core/src/hooks/resolve-project-id.test.ts +++ b/packages/server-core/src/hooks/resolve-project-id.test.ts @@ -73,7 +73,7 @@ describe('resolve-project-id', () => { it('should find project id by name', async () => { const resolveProject = resolveProjectId() const project = await app.service(projectPath).create({ - name: `Project #${Math.random()}` + name: `@org/project #${Math.random()}` }) const hookContext = mockHookContext(app, { project: project.name }) const contextUpdated = await resolveProject(hookContext) diff --git a/packages/server-core/src/hooks/verify-project-permission.test.ts b/packages/server-core/src/hooks/verify-project-permission.test.ts index 7d4e3d1906..c7b5092da7 100755 --- a/packages/server-core/src/hooks/verify-project-permission.test.ts +++ b/packages/server-core/src/hooks/verify-project-permission.test.ts @@ -113,7 +113,7 @@ describe('verify-project-permission', () => { scopes: [] }) const project = await app.service(projectPath).create({ - name: `Project #${Math.random()}` + name: `@org/project #${Math.random()}` }) await app.service(projectPermissionPath).create({ @@ -142,7 +142,7 @@ describe('verify-project-permission', () => { scopes: [] }) const project = await app.service(projectPath).create({ - name: `Project #${Math.random()}` + name: `@org/project #${Math.random()}` }) await app.service(projectPermissionPath).create({ diff --git a/packages/server-core/src/media/file-browser/file-browser.test.ts b/packages/server-core/src/media/file-browser/file-browser.test.ts index 577ec87280..640969211a 100644 --- a/packages/server-core/src/media/file-browser/file-browser.test.ts +++ b/packages/server-core/src/media/file-browser/file-browser.test.ts @@ -59,7 +59,7 @@ describe('file-browser.test', () => { }) describe('create', () => { - const testProjectName = getRandomizedName('directory') + const testProjectName = `@org/${getRandomizedName('directory')}` let project: ProjectType after(async () => { await app.service(projectPath).remove(project.id) @@ -75,7 +75,7 @@ describe('file-browser.test', () => { }) describe('find', () => { - const testProjectName = getRandomizedName('directory') + const testProjectName = `@org/${getRandomizedName('directory')}` let project: ProjectType before(async () => { project = await app.service(projectPath).create({ name: testProjectName }) @@ -123,7 +123,7 @@ describe('file-browser.test', () => { }) describe('patch', () => { - const testProjectName = getRandomizedName('directory') + const testProjectName = `@org/${getRandomizedName('directory')}` const testFileFullName = getRandomizedName('file', '.txt') const testFileFullPath = 'projects/' + testProjectName + '/public/' + testFileFullName const testFileName = testFileFullName.split('.')[0] @@ -197,8 +197,8 @@ describe('file-browser.test', () => { let project2: ProjectType beforeEach(async () => { - testProjectName = getRandomizedName('directory') - testProjectName2 = getRandomizedName('directory2') + testProjectName = `@org/${getRandomizedName('directory')}` + testProjectName2 = `@org/${getRandomizedName('directory2')}` project = await app.service(projectPath).create({ name: testProjectName }) project2 = await app.service(projectPath).create({ name: testProjectName2 }) @@ -361,7 +361,7 @@ describe('file-browser.test', () => { }) describe('remove', () => { - const testProjectName = getRandomizedName('directory') + const testProjectName = `@org/${getRandomizedName('directory')}` const testFileFullName = getRandomizedName('file', '.txt') let project: ProjectType diff --git a/packages/server-core/src/media/upload-asset/upload-asset.test.ts b/packages/server-core/src/media/upload-asset/upload-asset.test.ts index 5f8aa87f5c..cc4335b9e6 100644 --- a/packages/server-core/src/media/upload-asset/upload-asset.test.ts +++ b/packages/server-core/src/media/upload-asset/upload-asset.test.ts @@ -139,7 +139,7 @@ describe('upload-asset', () => { // it('should add asset as a new static resource from url', async () => { // const storageProvider = getStorageProvider() - // const url = storageProvider.getCachedURL('/projects/default-project/public/scenes/default.gltf') + // const url = storageProvider.getCachedURL('/projects/etherealengine/default-project/public/scenes/default.gltf') // const name = 'default.gltf' // const hash = createStaticResourceHash(url) diff --git a/packages/server-core/src/projects/project-check-source-destination-match/project-check-source-destination-match.test.ts b/packages/server-core/src/projects/project-check-source-destination-match/project-check-source-destination-match.test.ts index 97ed97a70a..b2cc2dc1c6 100644 --- a/packages/server-core/src/projects/project-check-source-destination-match/project-check-source-destination-match.test.ts +++ b/packages/server-core/src/projects/project-check-source-destination-match/project-check-source-destination-match.test.ts @@ -151,7 +151,7 @@ describe('project-check-source-destination-match.test', () => { before(async () => { createdProject = await app.service(projectPath).create({ - name: 'my-first-project' + name: 'myorg/my-first-project' }) }) diff --git a/packages/server-core/src/projects/project-github-push/project-github-push.test.ts b/packages/server-core/src/projects/project-github-push/project-github-push.test.ts index 4c8e1c69cd..fbf0d514ab 100644 --- a/packages/server-core/src/projects/project-github-push/project-github-push.test.ts +++ b/packages/server-core/src/projects/project-github-push/project-github-push.test.ts @@ -90,9 +90,10 @@ describe('project-github-push.test', () => { before(async () => { const projectName = `test-project-github-push-${uuidv4()}` + const fullName = `@etherealengine/${projectName}` testProject = await app .service(projectPath) - .create({ name: projectName, repositoryPath: `https://github.com/test-user/${projectName}` }) + .create({ name: fullName, repositoryPath: `https://github.com/test-user/${projectName}` }) }) beforeEach(() => { diff --git a/packages/server-core/src/projects/project-permission/project-permission.test.ts b/packages/server-core/src/projects/project-permission/project-permission.test.ts index 85d6406f2a..f84f22cd6b 100644 --- a/packages/server-core/src/projects/project-permission/project-permission.test.ts +++ b/packages/server-core/src/projects/project-permission/project-permission.test.ts @@ -43,10 +43,10 @@ import { destroyEngine } from '@etherealengine/ecs/src/Engine' import { Application } from '../../../declarations' import { createFeathersKoaApp } from '../../createApp' -const newProjectName1 = 'projecttest_test_project_name_1' +const newProjectName1 = 'org/projecttest_test_project_name_1' const cleanup = async (app: Application) => { - const project1Dir = path.resolve(appRootPath.path, `packages/projects/projects/${newProjectName1}/`) + const project1Dir = path.resolve(appRootPath.path, `packages/projects/projects/${newProjectName1.split('/')[0]}/`) deleteFolderRecursive(project1Dir) try { await app.service(projectPath).remove(null, { query: { name: newProjectName1 } }) diff --git a/packages/server-core/src/projects/project/downloadProjects.ts b/packages/server-core/src/projects/project/downloadProjects.ts index f3119e37bf..4ef92a9891 100755 --- a/packages/server-core/src/projects/project/downloadProjects.ts +++ b/packages/server-core/src/projects/project/downloadProjects.ts @@ -41,7 +41,7 @@ import logger from '../../ServerLogger' * @returns {Promise} */ export const download = async (projectName: string, storageProviderName?: string) => { - if (projectName === 'default-project') return + if (projectName === 'etherealengine/default-project') return const storageProvider = getStorageProvider(storageProviderName) try { @@ -73,7 +73,7 @@ export const download = async (projectName: string, storageProviderName?: string ) logger.info(`[ProjectLoader]: Successfully downloaded and mounted project "${projectName}".`) - // if (projectName !== 'default-project') { + // if (projectName !== 'etherealengine/default-project') { // const npmInstallPromise = new Promise((resolve) => { // const npmInstallProcess = spawn('npm', ['install', '--legacy-peer-deps'], { cwd: localProjectDirectory }) // npmInstallProcess.once('exit', () => { diff --git a/packages/server-core/src/projects/project/migrations/20240730104039_project-org-names.ts b/packages/server-core/src/projects/project/migrations/20240730104039_project-org-names.ts new file mode 100644 index 0000000000..3a5fc770cf --- /dev/null +++ b/packages/server-core/src/projects/project/migrations/20240730104039_project-org-names.ts @@ -0,0 +1,108 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { ProjectType, projectPath } from '@etherealengine/common/src/schemas/projects/project.schema' +import type { Knex } from 'knex' + +const routePath = 'route' +const staticResourcePath = 'static-resource' +const avatarPath = 'avatar' + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +export async function up(knex: Knex): Promise { + const nameColumnExists = await knex.schema.hasColumn(projectPath, 'name') + const repositoryPathColumnExists = await knex.schema.hasColumn(projectPath, 'repositoryPath') + if (nameColumnExists && repositoryPathColumnExists) { + const projects: ProjectType[] = await knex.select('*').from(projectPath) + + for (const project of projects) { + if (project.name === 'default-project') { + const newName = 'etherealengine/default-project' + await knex(projectPath).where('id', project.id).update({ + name: newName + }) + await knex(routePath).where('project', project.name).update({ + project: newName + }) + await knex(staticResourcePath).where('project', project.name).update({ + project: newName + }) + await knex(avatarPath).where('project', project.name).update({ + project: newName + }) + } else if (project.repositoryPath) { + const repositorySplit = project.repositoryPath.split('/') + const newName = `${repositorySplit[repositorySplit.length - 2].toLowerCase()}/${project.name}` + await knex(projectPath).where('id', project.id).update({ + name: newName + }) + await knex(routePath).where('project', project.name).update({ + project: newName + }) + await knex(staticResourcePath).where('project', project.name).update({ + project: newName + }) + await knex(avatarPath).where('project', project.name).update({ + project: newName + }) + } + } + } +} + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +export async function down(knex: Knex): Promise { + const nameColumnExists = await knex.schema.hasColumn(projectPath, 'name') + const repositoryPathColumnExists = await knex.schema.hasColumn(projectPath, 'repositoryPath') + if (nameColumnExists && repositoryPathColumnExists) { + const projects: ProjectType[] = await knex.select('*').from(projectPath) + + for (const project of projects) { + if (project.repositoryPath) { + const repositorySplit = project.repositoryPath.split('/') + const newName = `${repositorySplit[repositorySplit.length - 1]}` + const oldName = project.name + await knex(projectPath).where('id', project.id).update({ + name: newName + }) + await knex(routePath).where('project', oldName).update({ + project: newName + }) + await knex(staticResourcePath).where('project', oldName).update({ + project: newName + }) + await knex(avatarPath).where('project', oldName).update({ + project: newName + }) + } + } + } +} diff --git a/packages/server-core/src/projects/project/project-helper.ts b/packages/server-core/src/projects/project/project-helper.ts index 354e6eafc1..e6a879e259 100644 --- a/packages/server-core/src/projects/project/project-helper.ts +++ b/packages/server-core/src/projects/project/project-helper.ts @@ -325,24 +325,12 @@ export const getProjectConfig = (projectName: string) => { } } export const getProjectManifest = (projectName: string): ManifestJson => { - const packageJsonPath = path.resolve(projectsRootFolder, projectName, 'package.json') const manifestJsonPath = path.resolve(projectsRootFolder, projectName, 'manifest.json') if (fs.existsSync(manifestJsonPath)) { const data = fs.readFileSync(manifestJsonPath) return JSON.parse(data.toString()) as ManifestJson } - if (fs.existsSync(packageJsonPath)) { - const data = fs.readFileSync(packageJsonPath) - const packageJson = JSON.parse(data.toString()) as ProjectPackageJsonType - return { - name: packageJson.name!, - version: packageJson.version!, - engineVersion: packageJson.etherealEngine?.version, - description: packageJson.description, - thumbnail: packageJson.etherealEngine?.thumbnail - } - } - throw new Error(`No manifest.json or package.json found in project '${projectName}'`) + throw new Error(`No manifest.json found in project '${projectName}'`) } export const engineVersion = ( @@ -371,29 +359,8 @@ export const getProjectManifestFromRemote = async ( Buffer.from((blobResponse.data as { content: string }).content, 'base64').toString() ) as ManifestJson } catch (err) { - logger.warn("Error getting commit's package.json %s/%s %s", owner, repo, err.toString()) - - try { - const blobResponse = await octoKit.rest.repos.getContent({ - owner, - repo, - path: 'package.json', - ref: sha - }) - const packageJson = JSON.parse( - Buffer.from((blobResponse.data as { content: string }).content, 'base64').toString() - ) as ProjectPackageJsonType - return { - name: packageJson.name, - version: packageJson.version, - engineVersion: packageJson.etherealEngine?.version, - description: packageJson.description, - thumbnail: packageJson.etherealEngine?.thumbnail - } as ManifestJson - } catch (err) { - logger.error("Error getting commit's package.json %s/%s %s", owner, repo, err.toString()) - return Promise.reject(err) - } + logger.error("Error getting commit's package.json %s/%s %s", owner, repo, err.toString()) + return Promise.reject(err) } } @@ -584,7 +551,7 @@ export const checkProjectDestinationMatch = async ( error: 'invalidRepoProjectName', text: 'The repository you are attempting to update from contains a different project than the one you are updating' } - else return { sourceProjectMatchesDestination: true, projectName: sourceContent.name } + return { sourceProjectMatchesDestination: true, projectName: sourceContent.name } } export const checkDestination = async (app: Application, url: string, params?: ProjectParams) => { @@ -1298,7 +1265,10 @@ export const checkProjectAutoUpdate = async (app: Application, projectName: stri export const copyDefaultProject = () => { deleteFolderRecursive(path.join(projectsRootFolder, `default-project`)) - copyFolderRecursiveSync(path.join(appRootPath.path, 'packages/projects/default-project'), projectsRootFolder) + copyFolderRecursiveSync( + path.join(appRootPath.path, 'packages/projects/default-project'), + path.join(projectsRootFolder, 'etherealengine') + ) } export const getGitProjectData = (project) => { @@ -1347,9 +1317,9 @@ export const updateProject = async ( }, params?: ProjectParams ) => { - if (data.sourceURL === 'default-project') { + if (data.sourceURL === 'etherealengine/default-project') { copyDefaultProject() - await uploadLocalProjectToProvider(app, 'default-project') + await uploadLocalProjectToProvider(app, 'etherealengine/default-project') if (params?.jobId) { const date = await getDateTimeSql() await app.service(apiJobPath).patch(params.jobId as string, { @@ -1361,7 +1331,7 @@ export const updateProject = async ( (await app.service(projectPath).find({ query: { action: 'admin', - name: 'default-project', + name: 'etherealengine/default-project', $limit: 1 } })) as Paginated diff --git a/packages/server-core/src/projects/project/project.class.ts b/packages/server-core/src/projects/project/project.class.ts index 651d2afc2a..d44a3c3344 100644 --- a/packages/server-core/src/projects/project/project.class.ts +++ b/packages/server-core/src/projects/project/project.class.ts @@ -54,6 +54,7 @@ import { Application } from '../../../declarations' import logger from '../../ServerLogger' import { ServerMode, ServerState } from '../../ServerState' import config from '../../appconfig' +import { getStorageProvider } from '../../media/storageprovider/storageprovider' import { createStaticResourceHash } from '../../media/upload-asset/upload-asset.service' import { deleteProjectFilesInStorageProvider, @@ -88,6 +89,36 @@ export class ProjectService this._addOrgNameToProject()) + } + + async _addOrgNameToProject(): Promise { + if (getState(ServerState).serverMode !== ServerMode.API) return + + const storageProvider = getStorageProvider() + const data = (await super._find({ paginate: false })) as ProjectType[] + + for (const project of data) { + if (project.repositoryPath || project.name === 'etherealengine/default-project') { + const [orgName, projectName] = project.name.split('/') + + try { + if (await storageProvider.doesExist(projectName, `projects/`)) { + const files = await storageProvider.listObjects(`projects/${projectName}`, true) + for (const file of files.Contents) { + const fileName = file.Key.split('/').pop()! + const oldDirectory = file.Key.replace(fileName, '') + const newDirectory = `projects/${orgName}/${oldDirectory.replace('projects/', '')}` + await storageProvider.moveObject(fileName, fileName, oldDirectory, newDirectory, false) + } + } + } catch (error) { + logger.error(`[Projects]: Error moving project files for ${project.name}. Error: ${error}`) + } + } + } + return Promise.resolve() } async _seedProject(projectName: string): Promise { @@ -173,10 +204,18 @@ export class ProjectService const locallyInstalledProjects = fs .readdirSync(projectsRootFolder, { withFileTypes: true }) .filter((dirent) => dirent.isDirectory()) .map((dirent) => dirent.name) + .map((orgname) => { + return fs + .readdirSync(path.join(projectsRootFolder, orgname), { withFileTypes: true }) + .filter((dirent) => dirent.isDirectory()) + .map((dirent) => `${orgname}/${dirent.name}`) + }) + .flat() const promises: Promise[] = [] diff --git a/packages/server-core/src/projects/project/project.hooks.ts b/packages/server-core/src/projects/project/project.hooks.ts index feceb2ebda..f27c559506 100644 --- a/packages/server-core/src/projects/project/project.hooks.ts +++ b/packages/server-core/src/projects/project/project.hooks.ts @@ -229,11 +229,11 @@ const addDataToProjectResult = async (context: HookContext) => { const data: ProjectType[] = context.result!['data'] ? context.result!['data'] : context.result for (const item of data) { try { - const packageJson = getProjectManifest(item.name) - item.thumbnail = packageJson.thumbnail || '/static/IR_thumbnail.jpg' - item.version = packageJson.version - item.engineVersion = packageJson.engineVersion - item.description = packageJson.description + const manifestJson = getProjectManifest(item.name) + item.thumbnail = manifestJson.thumbnail || '/static/IR_thumbnail.jpg' + item.version = manifestJson.version + item.engineVersion = manifestJson.engineVersion + item.description = manifestJson.description item.hasWriteAccess = context.projectPushIds.indexOf(item.id) > -1 } catch (err) { // @@ -263,7 +263,13 @@ const checkIfProjectExists = async (context: HookContext) => { const data: ProjectData[] = Array.isArray(context.data) ? context.data : [context.data] - context.projectName = cleanString(data[0].name!).toLowerCase() + const projectName = data[0].name! + + const orgName = projectName.slice(0, projectName.indexOf('/')) + + const cleanedProjectName = cleanString(projectName.slice(projectName.indexOf('/'))) + + context.projectName = `${orgName}/${cleanedProjectName}`.toLowerCase() const projectExists = (await context.service._find({ query: { name: context.projectName, $limit: 1 } @@ -279,7 +285,7 @@ const checkIfProjectExists = async (context: HookContext) => { */ const checkIfNameIsValid = async (context: HookContext) => { if ( - (!config.db.forceRefresh && context.projectName === 'default-project') || + (!config.db.forceRefresh && context.projectName === 'etherealengine/default-project') || context.projectName === 'template-project' ) throw new Error(`[Projects]: Project name ${context.projectName} not allowed`) diff --git a/packages/server-core/src/projects/project/project.test.ts b/packages/server-core/src/projects/project/project.test.ts index 03eebc2586..986c36474d 100644 --- a/packages/server-core/src/projects/project/project.test.ts +++ b/packages/server-core/src/projects/project/project.test.ts @@ -46,7 +46,7 @@ import { identityProviderDataResolver } from '../../user/identity-provider/ident import { useGit } from '../../util/gitHelperFunctions' const cleanup = async (app: Application, projectName: string) => { - const projectDir = path.resolve(appRootPath.path, `packages/projects/projects/${projectName}/`) + const projectDir = path.resolve(appRootPath.path, `packages/projects/projects/${projectName.split('/')[0]}/`) deleteFolderRecursive(projectDir) const removingProjects = await app.service(projectPath).find({ query: { name: projectName } }) if (removingProjects.data.length) await app.service(projectPath).remove(removingProjects.data[0].id) @@ -106,7 +106,7 @@ describe('project.test', () => { }) it('should add new project', async () => { - const projectName = `test-project-${uuidv4().slice(0, 8)}` + const projectName = `@org/test-project-${uuidv4().slice(0, 8)}` testProject = await app.service(projectPath).create( { @@ -148,9 +148,9 @@ describe('project.test', () => { await git.add('.') await git.commit('initial commit') - testUpdateProjectName = `test-update-project-name-${uuidv4().slice(0, 8)}` + testUpdateProjectName = `@org1/test-update-project-name-${uuidv4().slice(0, 8)}` - const projectName = `test-project-${uuidv4().slice(0, 8)}` + const projectName = `@org1/test-project-${uuidv4().slice(0, 8)}` testProject = await app.service(projectPath).create( { name: projectName @@ -209,7 +209,7 @@ describe('project.test', () => { describe('patch', () => { beforeEach(async () => { - const projectName = `test-project-${uuidv4().slice(0, 8)}` + const projectName = `@org1/test-project-${uuidv4().slice(0, 8)}` testProject = await app.service(projectPath).create( { name: projectName @@ -233,7 +233,7 @@ describe('project.test', () => { describe('remove', () => { beforeEach(async () => { - const projectName = `test-project-${uuidv4().slice(0, 8)}` + const projectName = `@org1/test-project-${uuidv4().slice(0, 8)}` testProject = await app.service(projectPath).create( { name: projectName diff --git a/packages/server-core/src/projects/projects/projects.class.ts b/packages/server-core/src/projects/projects/projects.class.ts index 2cf8b639be..c7dae30fc0 100644 --- a/packages/server-core/src/projects/projects/projects.class.ts +++ b/packages/server-core/src/projects/projects/projects.class.ts @@ -41,7 +41,19 @@ export class ProjectsService implements ServiceInterface */ async find() { return fs - .readdirSync(projectsRootFolder) - .filter((projectFolder) => fs.existsSync(path.join(projectsRootFolder, projectFolder, 'xrengine.config.ts'))) + .readdirSync(projectsRootFolder, { withFileTypes: true }) + .filter((dirent) => dirent.isDirectory()) + .map((dirent) => dirent.name) + .map((orgname) => { + return fs + .readdirSync(path.join(projectsRootFolder, orgname), { withFileTypes: true }) + .filter( + (dirent) => + dirent.isDirectory() && + fs.existsSync(path.join(projectsRootFolder, orgname, dirent.name, 'xrengine.config.ts')) + ) + .map((dirent) => `${orgname}/${dirent.name}`) + }) + .flat() } } diff --git a/packages/server-core/src/projects/projects/projects.test.ts b/packages/server-core/src/projects/projects/projects.test.ts index 82ab144b22..933be45c6f 100644 --- a/packages/server-core/src/projects/projects/projects.test.ts +++ b/packages/server-core/src/projects/projects/projects.test.ts @@ -46,7 +46,7 @@ describe('projects.test', () => { it('should find the projects', async () => { const foundProjects = await app.service(projectsPath).find() assert.notEqual( - foundProjects.findIndex((project) => project === 'default-project'), + foundProjects.findIndex((project) => project === 'etherealengine/default-project'), -1 ) }) diff --git a/packages/server-core/src/route/route/route.seed.ts b/packages/server-core/src/route/route/route.seed.ts index 41f625c763..08bcd95563 100644 --- a/packages/server-core/src/route/route/route.seed.ts +++ b/packages/server-core/src/route/route/route.seed.ts @@ -37,31 +37,31 @@ export async function seed(knex: Knex): Promise { const seedData: RouteType[] = await Promise.all( [ { - project: 'default-project', + project: 'etherealengine/default-project', route: '/' }, { - project: 'default-project', + project: 'etherealengine/default-project', route: '/location' }, { - project: 'default-project', + project: 'etherealengine/default-project', route: '/admin' }, { - project: 'default-project', + project: 'etherealengine/default-project', route: '/studio' }, { - project: 'default-project', + project: 'etherealengine/default-project', route: '/studio-old' }, { - project: 'default-project', + project: 'etherealengine/default-project', route: '/capture' }, { - project: 'default-project', + project: 'etherealengine/default-project', route: '/chat' } ].map(async (item) => ({ diff --git a/packages/server-core/src/route/route/route.test.ts b/packages/server-core/src/route/route/route.test.ts index eb0bdc6346..be4efc6134 100644 --- a/packages/server-core/src/route/route/route.test.ts +++ b/packages/server-core/src/route/route/route.test.ts @@ -40,11 +40,11 @@ import { createFeathersKoaApp } from '../../createApp' const params = { isInternal: true } as any -const cleanup = async (app: Application, projectName: string) => { - const projectDir = path.resolve(appRootPath.path, `packages/projects/projects/${projectName}/`) +const cleanup = async (app: Application, projectName: string, projectId: string) => { + const projectDir = path.resolve(appRootPath.path, `packages/projects/projects/${projectName.split('/')[0]}/`) deleteFolderRecursive(projectDir) try { - await app.service(projectPath).remove(null, { query: { name: projectName } }) + await app.service(projectPath).remove(projectId) } catch (e) { // } @@ -80,6 +80,7 @@ describe('route.test', () => { let app: Application let testProject: string let testRoute: string + let testProjectId: string before(async () => { app = createFeathersKoaApp() @@ -87,15 +88,15 @@ describe('route.test', () => { }) after(async () => { - await cleanup(app, testProject) + await cleanup(app, testProject, testProjectId) await destroyEngine() }) it('should find the installed project routes', async () => { - testProject = `test-project-${uuidv4()}` + testProject = `@org1/test-project-${uuidv4()}` testRoute = `test-route-${uuidv4()}` - await app.service(projectPath).create({ name: testProject }, params) + testProjectId = await (await app.service(projectPath).create({ name: testProject }, params)).id updateXREngineConfigForTest(testProject, testRoute) const installedRoutes = await app.service('routes-installed').find() diff --git a/packages/server-core/src/route/route/route.ts b/packages/server-core/src/route/route/route.ts index 2f56fb017d..e7b7f39b6c 100644 --- a/packages/server-core/src/route/route/route.ts +++ b/packages/server-core/src/route/route/route.ts @@ -57,10 +57,18 @@ declare module '@etherealengine/common/declarations' { export const getInstalledRoutes = () => { return async () => { + const rootPath = path.resolve(__dirname, '../../../../projects/projects/') const projects = fs - .readdirSync(path.resolve(__dirname, '../../../../projects/projects/'), { withFileTypes: true }) + .readdirSync(rootPath, { withFileTypes: true }) .filter((dirent) => dirent.isDirectory()) .map((dirent) => dirent.name) + .map((orgname) => { + return fs + .readdirSync(path.join(rootPath, orgname), { withFileTypes: true }) + .filter((dirent) => dirent.isDirectory()) + .map((dirent) => `${orgname}/${dirent.name}`) + }) + .flat() const data: InstalledRoutesInterface[] = [] await Promise.all( diff --git a/packages/server-core/src/seeder.ts b/packages/server-core/src/seeder.ts index f5fb259e77..ac81522af2 100644 --- a/packages/server-core/src/seeder.ts +++ b/packages/server-core/src/seeder.ts @@ -45,6 +45,8 @@ export async function seeder(app: Application, forceRefresh: boolean, prepareDb: for (const seedFile of knexSeeds) { await seedFile.seed(knexClient) } + + await app.service(projectPath)._addOrgNameToProject() } if (prepareDb) return @@ -57,7 +59,8 @@ export async function seeder(app: Application, forceRefresh: boolean, prepareDb: if (fs.existsSync(uploadPath)) fs.rmSync(uploadPath, { recursive: true }) } copyDefaultProject() - if (config.kubernetes.enabled || config.testEnabled) await app.service(projectPath)._seedProject('default-project') + if (config.kubernetes.enabled || config.testEnabled) + await app.service(projectPath)._seedProject('etherealengine/default-project') } if (!config.kubernetes.enabled && !config.testEnabled) await app.service(projectPath)._syncDevLocalProjects() diff --git a/packages/server-core/src/services.ts b/packages/server-core/src/services.ts index d4f8661188..979b0769b0 100755 --- a/packages/server-core/src/services.ts +++ b/packages/server-core/src/services.ts @@ -49,13 +49,20 @@ import WorldServices from './world/services' const installedProjects = fs.existsSync(path.resolve(__dirname, '../../projects/projects')) ? fs .readdirSync(path.resolve(__dirname, '../../projects/projects'), { withFileTypes: true }) - .filter((dirent) => dirent.isDirectory()) - .map((dirent) => { + .filter((orgDir) => orgDir.isDirectory()) + .map((orgDir) => { + return fs + .readdirSync(path.resolve(__dirname, '../../projects/projects', orgDir.name), { withFileTypes: true }) + .filter((projectDir) => projectDir.isDirectory()) + .map((projectDir) => `${orgDir.name}/${projectDir.name}`) + }) + .flat() + .map((projectName) => { try { - const configPath = `../../projects/projects/${dirent.name}/xrengine.config.ts` + const configPath = `../../projects/projects/${projectName}/xrengine.config.ts` const config: ProjectConfigInterface = require(configPath).default if (!config.services) return null - return path.join(dirent.name, config.services) + return path.join(projectName, config.services) } catch (e) { // console.log(e) } diff --git a/packages/server-core/src/setting/project-setting/project-setting.test.ts b/packages/server-core/src/setting/project-setting/project-setting.test.ts index 285c431222..bc0354ed0c 100644 --- a/packages/server-core/src/setting/project-setting/project-setting.test.ts +++ b/packages/server-core/src/setting/project-setting/project-setting.test.ts @@ -43,7 +43,7 @@ import { import { createUser, createUserApiKey } from '../../test-utils/user-test-utils' function cleanup(name: string) { - const projectDir = path.resolve(appRootPath.path, `packages/projects/projects/${name}/`) + const projectDir = path.resolve(appRootPath.path, `packages/projects/projects/__test/`) deleteFolderRecursive(projectDir) } diff --git a/packages/server-core/src/social/location/location.test.ts b/packages/server-core/src/social/location/location.test.ts index 78b3947f41..fa87b11be5 100644 --- a/packages/server-core/src/social/location/location.test.ts +++ b/packages/server-core/src/social/location/location.test.ts @@ -55,7 +55,7 @@ describe('location.test', () => { const scene = await app.service(staticResourcePath).find({ query: { - key: 'projects/default-project/public/scenes/default.gltf' + key: 'projects/etherealengine/default-project/public/scenes/default.gltf' } }) diff --git a/packages/server-core/src/test-utils/project-test-utils.ts b/packages/server-core/src/test-utils/project-test-utils.ts index 0cf4ee2dea..9f941b68b5 100644 --- a/packages/server-core/src/test-utils/project-test-utils.ts +++ b/packages/server-core/src/test-utils/project-test-utils.ts @@ -38,7 +38,7 @@ import { createUser, getAuthParams } from './user-test-utils' export const createProject = async (app: Application, projectName?: string, user?: UserType) => { if (!projectName) { - projectName = `project-${v4()}` + projectName = `__test/project-${v4()}` } if (!user) { diff --git a/packages/server-core/src/user/avatar/avatar-helper.test.ts b/packages/server-core/src/user/avatar/avatar-helper.test.ts index e5db7388dc..699f60aa57 100644 --- a/packages/server-core/src/user/avatar/avatar-helper.test.ts +++ b/packages/server-core/src/user/avatar/avatar-helper.test.ts @@ -23,12 +23,10 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20 Ethereal Engine. All Rights Reserved. */ -import { projectPath } from '@etherealengine/common/src/schemas/projects/project.schema' import { destroyEngine } from '@etherealengine/ecs/src/Engine' import { Application } from '../../../declarations' import { createFeathersKoaApp } from '../../createApp' -import { copyDefaultProject, uploadLocalProjectToProvider } from '../../projects/project/project-helper' // import { generateAvatarThumbnail } from './generateAvatarThumbnail' // import fs from 'fs' @@ -43,11 +41,6 @@ describe('avatar-helper', () => { before(async () => { app = createFeathersKoaApp() await app.setup() - - // reset default project in case another test has tampered with it - copyDefaultProject() - await app.service(projectPath)._seedProject('default-project') - await uploadLocalProjectToProvider(app, 'default-project') }) after(() => { diff --git a/packages/server-core/src/util/mockOctokitResponses.ts b/packages/server-core/src/util/mockOctokitResponses.ts index 7e77372be7..d392ab951b 100644 --- a/packages/server-core/src/util/mockOctokitResponses.ts +++ b/packages/server-core/src/util/mockOctokitResponses.ts @@ -28,13 +28,13 @@ import packagejson from '../../package.json' const engineVersion = packagejson.version export const repo1ManifestJSON = { - name: 'my-first-project', + name: 'myorg/my-first-project', version: '0.0.0', engineVersion } export const repo2ManifestJSON = { - name: 'my-second-project', + name: 'myorg/my-second-project', version: '0.0.0', engineVersion } diff --git a/packages/server-core/tests/util/createTestLocation.ts b/packages/server-core/tests/util/createTestLocation.ts index 2d3d05d121..aa7f1c0f12 100644 --- a/packages/server-core/tests/util/createTestLocation.ts +++ b/packages/server-core/tests/util/createTestLocation.ts @@ -34,7 +34,7 @@ export const createTestLocation = async (app: Application, params = { isInternal const scene = await app.service(staticResourcePath).find({ query: { - key: 'projects/default-project/public/scenes/default.gltf' + key: 'projects/etherealengine/default-project/public/scenes/default.gltf' } }) diff --git a/packages/ui/src/components/editor/properties/model/index.tsx b/packages/ui/src/components/editor/properties/model/index.tsx index a33f34561c..adcf88e3a5 100644 --- a/packages/ui/src/components/editor/properties/model/index.tsx +++ b/packages/ui/src/components/editor/properties/model/index.tsx @@ -69,7 +69,12 @@ export const ModelNodeEditor: EditorComponentType = (props) => { const editorState = getState(EditorState) const projectState = getState(ProjectState) const loadedProjects = useState(() => projectState.projects.map((project) => project.name)) - const srcProject = useState(() => STATIC_ASSET_REGEX.exec(modelComponent.src.value)?.[1] ?? editorState.projectName!) + const srcProject = useState(() => { + const match = STATIC_ASSET_REGEX.exec(modelComponent.src.value) + if (!match?.length) return editorState.projectName! + const [_, orgName, projectName] = match + return `${orgName}/${projectName}` + }) const [dereferenceFeatureFlag, gltfTransformFeatureFlag] = useFeatureFlags([ FeatureFlags.Studio.Model.Dereference, diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index ea6ffd6600..86d0817572 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,4 +1,4 @@ packages: # all packages in subdirs of packages/ - 'packages/*' - - 'packages/projects/projects/*' + - 'packages/projects/projects/**' diff --git a/scripts/bump-project-versions.js b/scripts/bump-project-versions.js index 0ac8a9eab3..1c5fecd19a 100644 --- a/scripts/bump-project-versions.js +++ b/scripts/bump-project-versions.js @@ -1,4 +1,3 @@ - /* CPAL-1.0 License @@ -24,34 +23,36 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20 Ethereal Engine. All Rights Reserved. */ - /* eslint-disable @typescript-eslint/no-var-requires */ const fs = require('fs') const appRootPath = require('app-root-path') const cli = require('cli') const path = require('path') -cli.enable('status'); +cli.enable('status') cli.main(async () => { - try { - const serverPackageJSONPath = path.join(appRootPath.path, 'packages/server-core/package.json') - const defaultProjectJSONPath = path.join(appRootPath.path, 'packages/projects/default-project/package.json') - const templateProjectJSONPath = path.join(appRootPath.path, 'packages/projects/template-project/package.json') - const serverPackageJSON = JSON.parse(fs.readFileSync(serverPackageJSONPath, { encoding: 'utf-8' })) - const defaultProjectJSON = JSON.parse(fs.readFileSync(defaultProjectJSONPath, { encoding: 'utf-8' })) - const templateProjectJSON = JSON.parse(fs.readFileSync(templateProjectJSONPath, { encoding: 'utf-8' })) - if (!defaultProjectJSON.etherealEngine) defaultProjectJSON.etherealEngine = {} - if (!templateProjectJSON.etherealEngine) templateProjectJSON.etherealEngine = {} - defaultProjectJSON.etherealEngine.version = serverPackageJSON.version - templateProjectJSON.etherealEngine.version = serverPackageJSON.version - fs.writeFileSync(defaultProjectJSONPath,Buffer.from(JSON.stringify(defaultProjectJSON, null, 2))) - fs.writeFileSync(templateProjectJSONPath,Buffer.from(JSON.stringify(templateProjectJSON, null, 2))) - console.log('Updated default-project and template-project Ethereal Engine version to', serverPackageJSON.version) - process.exit(0) - } catch(err) { - console.log('Error bumping default-project and template project versions:'); - console.log(err); - cli.fatal(err) - } -}); + try { + const serverPackageJSONPath = path.join(appRootPath.path, 'packages/server-core/package.json') + const defaultProjectJSONPath = path.join( + appRootPath.path, + 'packages/projects/etherealengine/default-project/package.json' + ) + const templateProjectJSONPath = path.join(appRootPath.path, 'packages/projects/template-project/package.json') + const serverPackageJSON = JSON.parse(fs.readFileSync(serverPackageJSONPath, { encoding: 'utf-8' })) + const defaultProjectJSON = JSON.parse(fs.readFileSync(defaultProjectJSONPath, { encoding: 'utf-8' })) + const templateProjectJSON = JSON.parse(fs.readFileSync(templateProjectJSONPath, { encoding: 'utf-8' })) + if (!defaultProjectJSON.etherealEngine) defaultProjectJSON.etherealEngine = {} + if (!templateProjectJSON.etherealEngine) templateProjectJSON.etherealEngine = {} + defaultProjectJSON.etherealEngine.version = serverPackageJSON.version + templateProjectJSON.etherealEngine.version = serverPackageJSON.version + fs.writeFileSync(defaultProjectJSONPath, Buffer.from(JSON.stringify(defaultProjectJSON, null, 2))) + fs.writeFileSync(templateProjectJSONPath, Buffer.from(JSON.stringify(templateProjectJSON, null, 2))) + console.log('Updated default-project and template-project Ethereal Engine version to', serverPackageJSON.version) + process.exit(0) + } catch (err) { + console.log('Error bumping default-project and template project versions:') + console.log(err) + cli.fatal(err) + } +}) diff --git a/scripts/clone-project.ts b/scripts/clone-project.ts new file mode 100644 index 0000000000..cd26220e81 --- /dev/null +++ b/scripts/clone-project.ts @@ -0,0 +1,90 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ +import appRootPath from 'app-root-path' +import cli from 'cli' +import dotenv from 'dotenv-flow' +import fs from 'fs' +import path from 'path' + +import { execPromise } from '@etherealengine/server-core/src/util/execPromise' +dotenv.config({ + path: appRootPath.path, + silent: true +}) +cli.enable('status') + +/** + * Repo must be in the format https://github.com// + */ + +const options = cli.parse({ + url: [false, 'Repo URL', 'string'], + branch: ['b', 'Branch', 'string', 'dev'] +}) as { + url?: string + branch: string +} + +const cloneRepo = async () => { + const branch = options.branch + const url = options.url + if (!url) throw new Error('URL is required') + + const [org, repo] = new URL(url).pathname.split('/').slice(1, 3) + + const orgFolderPath = path.resolve(appRootPath.path, 'packages/projects/projects', '@' + org) + const orgExists = await fs.promises + .access(orgFolderPath) + .then(() => true) + .catch(() => false) + + if (!orgExists) { + await fs.promises.mkdir(orgFolderPath) + } + + const repoExists = await fs.promises + .access(path.resolve(orgFolderPath, repo)) + .then(() => true) + .catch(() => false) + if (!repoExists) { + await execPromise(`git clone ${url}`, { + cwd: path.resolve(orgFolderPath) + }) + } + + /** Checkout branch and rebase */ + await execPromise(`git checkout ${branch} && git fetch -p && git rebase`, { + cwd: path.resolve(appRootPath.path, `packages/projects/projects/${org}/${repo}`) + }) +} +cli.main(async () => { + try { + await cloneRepo() + cli.exit(0) + } catch (err) { + console.log(err) + cli.fatal(err) + } +}) diff --git a/scripts/create-project.ts b/scripts/create-project.ts index 28e9ef6495..5b8b26da60 100644 --- a/scripts/create-project.ts +++ b/scripts/create-project.ts @@ -70,6 +70,9 @@ cli.main(async () => { const name = options.name.replace(' ', '-') + if (!name.includes('/')) + throw new Error('Project name must be composed of both an organization and repository name separated by a /') + const projectLocalDirectory = path.resolve(projectsRootFolder, name) // get if folder exists diff --git a/scripts/create-root-package-json.ts b/scripts/create-root-package-json.ts index 27868af49f..8b65370fa6 100644 --- a/scripts/create-root-package-json.ts +++ b/scripts/create-root-package-json.ts @@ -46,7 +46,7 @@ cli.main(async () => { 'packages/spatial', 'packages/visual-script', 'packages/xrui', - 'packages/projects/projects/*' + 'packages/projects/projects/**' ] fs.writeFileSync(join(appRootPath.path, 'package-root-build.json'), Buffer.from(JSON.stringify(packageJson))) process.exit(0) diff --git a/scripts/install-projects.js b/scripts/install-projects.js index 8e6d7e61da..5c615a5d2b 100755 --- a/scripts/install-projects.js +++ b/scripts/install-projects.js @@ -30,7 +30,6 @@ import { createFeathersKoaApp, serverJobPipe } from '@etherealengine/server-core import { createDefaultStorageProvider } from '@etherealengine/server-core/src/media/storageprovider/storageprovider' import { download } from '@etherealengine/server-core/src/projects/project/downloadProjects' import { getProjectConfig, onProjectEvent } from '@etherealengine/server-core/src/projects/project/project-helper' -import { seedDefaultProject } from '@etherealengine/server-core/src/seeder' import appRootPath from 'app-root-path' import dotenv from 'dotenv' import fs from 'fs' @@ -62,9 +61,10 @@ async function installAllProjects() { await Promise.all(projects.map((project) => download(project.name))) const updatedProject = await app .service(projectPath) - .update('', { sourceURL: 'default-project' }, { isInternal: true, isJob: true }) - const projectConfig = getProjectConfig('default-project') - if (projectConfig && projectConfig.onEvent) await onProjectEvent(app, updatedProject, projectConfig.onEvent, 'onUpdate') + .update('', { sourceURL: 'etherealengine/default-project' }, { isInternal: true, isJob: true }) + const projectConfig = getProjectConfig('etherealengine/default-project') + if (projectConfig && projectConfig.onEvent) + await onProjectEvent(app, updatedProject, projectConfig.onEvent, 'onUpdate') process.exit(0) } catch (e) { logger.fatal(e)