Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Allow renaming project assets while preserving id #576

Merged
merged 3 commits into from
Sep 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/lib/helpers/clone.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const clone = <T>(input:T): T => {
return JSON.parse(JSON.stringify(input));
}
4 changes: 4 additions & 0 deletions src/lib/project/loadBackgroundData.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import glob from "glob";
import { promisify } from "util";
import uuid from "uuid/v4";
import sizeOf from "image-size";
import { stat } from "fs-extra";
import parseAssetPath from "../helpers/path/parseAssetPath";

const TILE_SIZE = 8;
Expand All @@ -13,6 +14,8 @@ const loadBackgroundData = projectRoot => async filename => {
const { file, plugin } = parseAssetPath(filename, projectRoot, "backgrounds");
try {
const size = await sizeOfAsync(filename);
const fileStat = await stat(filename, { bigint: true });
const inode = fileStat.ino.toString();
return {
id: uuid(),
plugin,
Expand All @@ -22,6 +25,7 @@ const loadBackgroundData = projectRoot => async filename => {
imageWidth: size.width,
imageHeight: size.height,
filename: file,
inode,
_v: Date.now()
};
} catch (e) {
Expand Down
5 changes: 4 additions & 1 deletion src/lib/project/loadMusicData.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import glob from "glob";
import { promisify } from "util";
import uuidv4 from "uuid/v4";
import { stat } from "fs-extra";
import parseAssetPath from "../helpers/path/parseAssetPath";

const globAsync = promisify(glob);

const loadMusicData = projectRoot => async filename => {
const { file, plugin } = parseAssetPath(filename, projectRoot, "music");

const fileStat = await stat(filename, { bigint: true });
const inode = fileStat.ino.toString();
return {
id: uuidv4(),
plugin,
name: file.replace(/.mod/i, ""),
filename: file,
settings: {},
inode,
_v: Date.now()
};
};
Expand Down
12 changes: 8 additions & 4 deletions src/lib/project/loadProjectData.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import loadAllBackgroundData from "./loadBackgroundData";
import loadAllSpriteData from "./loadSpriteData";
import loadAllMusicData from "./loadMusicData";
import migrateProject from "./migrateProject";
import { indexByFn } from "../helpers/array";
import { indexByFn, indexBy } from "../helpers/array";

const elemKey = (elem) => {
return (elem.plugin ? `${elem.plugin}/` : "") + elem.filename;
};

const indexByFilename = indexByFn(elemKey);
const indexByInode = indexBy("inode");

const sortByName = (a, b) => {
const aName = a.name.toUpperCase();
Expand All @@ -38,10 +39,11 @@ const loadProject = async (projectPath) => {

// Merge stored backgrounds data with file system data
const oldBackgroundByFilename = indexByFilename(json.backgrounds || []);
const oldBackgroundByInode = indexByInode(json.backgrounds || []);

const fixedBackgroundIds = backgrounds
.map((background) => {
const oldBackground = oldBackgroundByFilename[elemKey(background)];
const oldBackground = oldBackgroundByFilename[elemKey(background)] || oldBackgroundByInode[background.inode];
if (oldBackground) {
return {
...background,
Expand All @@ -54,10 +56,11 @@ const loadProject = async (projectPath) => {

// Merge stored sprite data with file system data
const oldSpriteByFilename = indexByFilename(json.spriteSheets || []);
const oldSpriteByInode = indexByInode(json.spriteSheets || []);

const fixedSpriteIds = sprites
.map((sprite) => {
const oldSprite = oldSpriteByFilename[elemKey(sprite)];
const oldSprite = oldSpriteByFilename[elemKey(sprite)] || oldSpriteByInode[sprite.inode];
if (oldSprite) {
return {
...sprite,
Expand All @@ -70,10 +73,11 @@ const loadProject = async (projectPath) => {

// Merge stored music data with file system data
const oldMusicByFilename = indexByFilename(json.music || []);
const oldMusicByInode = indexByInode(json.music || []);

const fixedMusicIds = music
.map((track) => {
const oldTrack = oldMusicByFilename[elemKey(track)];
const oldTrack = oldMusicByFilename[elemKey(track)] || oldMusicByInode[track.inode];
if (oldTrack) {
return {
...track,
Expand Down
5 changes: 4 additions & 1 deletion src/lib/project/loadSpriteData.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import glob from "glob";
import { promisify } from "util";
import uuidv4 from "uuid/v4";
import sizeOf from "image-size";
import { stat } from "fs-extra";
import parseAssetPath from "../helpers/path/parseAssetPath";
import { spriteTypeFromNumFrames } from "../helpers/gbstudio";

Expand All @@ -14,15 +15,17 @@ const loadSpriteData = projectRoot => async filename => {
const { file, plugin } = parseAssetPath(filename, projectRoot, "sprites");
try {
const size = await sizeOfAsync(filename);
const fileStat = await stat(filename, { bigint: true });
const inode = fileStat.ino.toString();
const numFrames = size.width / FRAME_SIZE;

return {
id: uuidv4(),
plugin,
name: file.replace(/.png/i, ""),
numFrames,
type: spriteTypeFromNumFrames(numFrames),
filename: file,
inode,
_v: Date.now()
};
} catch (e) {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/project/watchProject.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ const watchProject = async (
ignored: /^.*\.(?!(mod|MOD)$)[^.]+$/,
ignoreInitial: true,
persistent: true,
musicAwaitWriteFinish
awaitWriteFinish: musicAwaitWriteFinish
})
.on("add", onAddMusic)
.on("change", onChangedMusic)
Expand Down
69 changes: 42 additions & 27 deletions src/store/features/entities/entitiesState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,17 @@ import {
MusicSettings,
} from "./entitiesTypes";
import { normalizeEntities } from "./entitiesHelpers";
import { clone } from "../../../lib/helpers/clone";

const MIN_SCENE_X = 60;
const MIN_SCENE_Y = 30;
const MIN_SCENE_WIDTH = 20;
const MIN_SCENE_HEIGHT = 18;

const inodeToRecentBackground: Dictionary<Background> = {}
const inodeToRecentSpriteSheet: Dictionary<SpriteSheet> = {}
const inodeToRecentMusic: Dictionary<Music> = {}

const matchAsset = (assetA: Asset) => (assetB: Asset) => {
return assetA.filename === assetB.filename && assetA.plugin === assetB.plugin;
};
Expand Down Expand Up @@ -362,15 +367,16 @@ const loadBackground: CaseReducer<
}>
> = (state, action) => {
const backgrounds = localBackgroundSelectors.selectAll(state);
const existingAsset = backgrounds.find(matchAsset(action.payload.data));

if (existingAsset) {
backgroundsAdapter.updateOne(state.backgrounds, {
id: existingAsset.id,
changes: {
...action.payload.data,
id: existingAsset.id,
},
const existingAsset = backgrounds.find(matchAsset(action.payload.data))
|| inodeToRecentBackground[action.payload.data.inode]
const existingId = existingAsset?.id;

if (existingId) {
delete inodeToRecentBackground[action.payload.data.inode];
backgroundsAdapter.upsertOne(state.backgrounds, {
...existingAsset,
...action.payload.data,
id: existingId,
});
fixAllScenesWithModifiedBackgrounds(state);
} else {
Expand All @@ -388,6 +394,7 @@ const removeBackground: CaseReducer<
const backgrounds = localBackgroundSelectors.selectAll(state);
const existingAsset = backgrounds.find(matchAsset(action.payload));
if (existingAsset) {
inodeToRecentBackground[existingAsset.inode] = clone(existingAsset);
backgroundsAdapter.removeOne(state.backgrounds, existingAsset.id);
}
};
Expand All @@ -399,15 +406,16 @@ const loadSprite: CaseReducer<
}>
> = (state, action) => {
const spriteSheets = localSpriteSheetSelectors.selectAll(state);
const existingAsset = spriteSheets.find(matchAsset(action.payload.data));

if (existingAsset) {
spriteSheetsAdapter.updateOne(state.spriteSheets, {
id: existingAsset.id,
changes: {
...action.payload.data,
id: existingAsset.id,
},
const existingAsset = spriteSheets.find(matchAsset(action.payload.data))
|| inodeToRecentSpriteSheet[action.payload.data.inode];
const existingId = existingAsset?.id;

if (existingId) {
delete inodeToRecentSpriteSheet[action.payload.data.inode];
spriteSheetsAdapter.upsertOne(state.spriteSheets, {
...existingAsset,
...action.payload.data,
id: existingId,
});
} else {
spriteSheetsAdapter.addOne(state.spriteSheets, action.payload.data);
Expand All @@ -424,6 +432,7 @@ const removeSprite: CaseReducer<
const spriteSheets = localSpriteSheetSelectors.selectAll(state);
const existingAsset = spriteSheets.find(matchAsset(action.payload));
if (existingAsset) {
inodeToRecentSpriteSheet[existingAsset.inode] = clone(existingAsset);
spriteSheetsAdapter.removeOne(state.spriteSheets, existingAsset.id);
}
};
Expand All @@ -435,15 +444,20 @@ const loadMusic: CaseReducer<
}>
> = (state, action) => {
const music = localMusicSelectors.selectAll(state);
const existingAsset = music.find(matchAsset(action.payload.data));

if (existingAsset) {
musicAdapter.updateOne(state.music, {
id: existingAsset.id,
changes: {
...action.payload.data,
id: existingAsset.id,
},
const existingAsset = music.find(matchAsset(action.payload.data))
|| inodeToRecentMusic[action.payload.data.inode];
const existingId = existingAsset?.id;

if (existingId) {
delete inodeToRecentMusic[action.payload.data.inode];
musicAdapter.upsertOne(state.music, {
...existingAsset,
...action.payload.data,
id: existingId,
settings: {
...existingAsset?.settings,
...action.payload.data.settings
}
});
} else {
musicAdapter.addOne(state.music, action.payload.data);
Expand Down Expand Up @@ -478,6 +492,7 @@ const removeMusic: CaseReducer<
const music = localMusicSelectors.selectAll(state);
const existingAsset = music.find(matchAsset(action.payload));
if (existingAsset) {
inodeToRecentMusic[existingAsset.inode] = clone(existingAsset);
musicAdapter.removeOne(state.music, existingAsset.id);
}
};
Expand Down
3 changes: 3 additions & 0 deletions src/store/features/entities/entitiesTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export type Background = {
imageWidth: number;
imageHeight: number;
plugin?: string;
inode: string;
_v: number;
};

Expand All @@ -61,6 +62,7 @@ export type Music = {
filename: string;
plugin?: string;
settings: MusicSettings;
inode: string;
_v: number;
};

Expand Down Expand Up @@ -103,6 +105,7 @@ export type SpriteSheet = {
type: SpriteType;
numFrames: number;
plugin?: string;
inode: string;
_v: number;
};

Expand Down
3 changes: 3 additions & 0 deletions test/dummydata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export const dummyBackground: Background = {
height: 1,
imageWidth: 1,
imageHeight: 1,
inode: "0",
_v: 0,
};

Expand All @@ -90,13 +91,15 @@ export const dummySpriteSheet: SpriteSheet = {
filename: "",
numFrames: 1,
type: "static",
inode: "1",
_v: 0,
};

export const dummyMusic: Music = {
id: "",
name: "",
filename: "",
inode: "2",
_v: 0,
settings: {}
};
Expand Down