Skip to content

Commit

Permalink
Reload resources in the editor when the resource changes (#5631)
Browse files Browse the repository at this point in the history
GDevelop now refreshes resources (images, videos, 3D models, fonts, etc.) when a change is detected on the filesystem so that you can immediately see the changes.

This allows to efficiently use GDevelop alongside other tools (Tiled or LDtk for instance).

Notes:
- This logically only affects the desktop version for projects saved on the filesystem;
- You can deactivate it in the preferences.
  • Loading branch information
AlexandreSi authored Oct 12, 2023
1 parent 740485b commit 91db750
Show file tree
Hide file tree
Showing 55 changed files with 768 additions and 262 deletions.
4 changes: 2 additions & 2 deletions Core/GDCore/IDE/Project/ArbitraryResourceWorker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,8 +293,8 @@ void ResourceWorkerInObjectsWorker::DoVisitBehavior(gd::Behavior &behavior){
gd::ResourceWorkerInObjectsWorker
GetResourceWorkerOnObjects(const gd::Project &project,
gd::ArbitraryResourceWorker &worker) {
gd::ResourceWorkerInObjectsWorker eventsWorker(project, worker);
return eventsWorker;
gd::ResourceWorkerInObjectsWorker resourcesWorker(project, worker);
return resourcesWorker;
}

} // namespace gd
Expand Down
19 changes: 19 additions & 0 deletions Core/GDCore/IDE/Project/ObjectsUsingResourceCollector.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include "ObjectsUsingResourceCollector.h"

#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"

namespace gd {

void ObjectsUsingResourceCollector::DoVisitObject(gd::Object& object) {
gd::ResourceNameMatcher resourceNameMatcher(resourceName);

object.GetConfiguration().ExposeResources(resourceNameMatcher);
if (resourceNameMatcher.AnyResourceMatches()) {
objectNames.push_back(object.GetName());
}
};

ObjectsUsingResourceCollector::~ObjectsUsingResourceCollector() {}

} // namespace gd
89 changes: 89 additions & 0 deletions Core/GDCore/IDE/Project/ObjectsUsingResourceCollector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/

#ifndef ProjectObjectsUsingResourceCollector_H
#define ProjectObjectsUsingResourceCollector_H

#include <vector>

#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/String.h"

namespace gd {
class Object;
} // namespace gd

namespace gd {

class GD_CORE_API ObjectsUsingResourceCollector
: public ArbitraryObjectsWorker {
public:
ObjectsUsingResourceCollector(const gd::String& resourceName_)
: resourceName(resourceName_){};
virtual ~ObjectsUsingResourceCollector();

std::vector<gd::String>& GetObjectNames() { return objectNames; }

private:
void DoVisitObject(gd::Object& object) override;

std::vector<gd::String> objectNames;
gd::String resourceName;
};

class GD_CORE_API ResourceNameMatcher : public ArbitraryResourceWorker {
public:
ResourceNameMatcher(const gd::String& resourceName_)
: resourceName(resourceName_), matchesResourceName(false){};
virtual ~ResourceNameMatcher(){};

bool AnyResourceMatches() { return matchesResourceName; }
void Reset() { matchesResourceName = false; }

private:
virtual void ExposeFile(gd::String& resource) override{
/*Don't care, we just read resource names*/
};
virtual void ExposeImage(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeAudio(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeFont(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeJson(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeTilemap(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeTileset(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeVideo(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeBitmapFont(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeModel3D(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};

void MatchResourceName(gd::String& otherResourceName) {
if (otherResourceName == resourceName) matchesResourceName = true;
}

gd::String resourceName;
bool matchesResourceName;
};

}; // namespace gd

#endif // ProjectObjectsUsingResourceCollector_H
1 change: 1 addition & 0 deletions Extensions/JsExtensionTypes.flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export type ObjectsRenderingService = {
requireModule: (dirname: string, moduleName: string) => any,
getThumbnail: (project: gdProject, objectConfiguration: gdObjectConfiguration) => string,
rgbOrHexToHexNumber: (value: string) => number,
registerClearCache: (clearCache: any => void) => void,
};
export type ObjectsEditorService = {
registerEditorConfiguration: (objectType: string, editorConfiguration: any) => void,
Expand Down
23 changes: 22 additions & 1 deletion Extensions/TileMap/JsExtension.js
Original file line number Diff line number Diff line change
Expand Up @@ -1044,7 +1044,9 @@ module.exports = {
.setExtensionInformation(
'TileMap',
_('Tilemap'),
"The Tilemap object can be used to display tile-based objects. It's a good way to create maps for RPG, strategy games or create objects by assembling tiles, useful for platformer, retro-looking games, etc...",
_(
"The Tilemap object can be used to display tile-based objects. It's a good way to create maps for RPG, strategy games or create objects by assembling tiles, useful for platformer, retro-looking games, etc..."
),
'Todor Imreorov',
'Open source (MIT License)'
)
Expand All @@ -1061,6 +1063,25 @@ module.exports = {
return extension;
},

registerClearCache: function (
objectsRenderingService /*: ObjectsRenderingService */
) {
const TilemapHelper = objectsRenderingService.requireModule(
__dirname,
'helper/TileMapHelper'
);

const clearCaches = (
project /* InstanceHolder - gdProject in the editor */
) => {
/** @type {TileMapHelper.TileMapManager} */
const manager = TilemapHelper.TileMapManager.getManager(project);
manager.clearCaches();
};

objectsRenderingService.registerClearCache(clearCaches);
},

/**
* You can optionally add sanity tests that will check the basic working
* of your extension behaviors/objects by instantiating behaviors/objects
Expand Down
2 changes: 1 addition & 1 deletion Extensions/TileMap/helper/TileMapHelper.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Extensions/TileMap/helper/TileMapHelper.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Extensions/TileMap/helper/dts/load/TileMapFileContent.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { LDtkTileMap } from '../load/ldtk/LDtkFormat';
import { TiledTileMap } from '../load/tiled/TiledFormat';
export declare type TileMapFileContent =
export type TileMapFileContent =
| {
kind: 'tiled';
data: TiledTileMap;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 24 additions & 24 deletions Extensions/TileMap/helper/dts/load/ldtk/LDtkFormat.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { integer } from '../../model/CommonTypes';
/**
* version 1.1.3 - https://github.com/deepnight/ldtk/blob/66fff7199932357f3ab9b044c2fc2a856f527831/docs/JSON_SCHEMA.json
*/
export declare type LDtkTileMap = {
export type LDtkTileMap = {
/** LDtk application build identifier.<br/> This is only used to identify the LDtk version that generated this particular project file, which can be useful for specific bug fixing. Note that the build identifier is just the date of the release, so it's not unique to each user (one single global ID per LDtk public release), and as a result, completely anonymous. */
appBuildId: number;
/** Number of backup files to keep, if the `backupOnSave` is TRUE */
Expand Down Expand Up @@ -72,7 +72,7 @@ export declare type LDtkTileMap = {
| null;
};
/** Auto-layer rule group */
declare type LDtkAutoLayerRuleGroup = {
type LDtkAutoLayerRuleGroup = {
/** */
active: boolean;
/** *This field was removed in 1.0.0 and should no longer be used.* */
Expand All @@ -87,9 +87,9 @@ declare type LDtkAutoLayerRuleGroup = {
uid: integer;
};
/** This complex section isn't meant to be used by game devs at all, as these rules are completely resolved internally by the editor before any saving. You should just ignore this part. */
declare type LDtkAutoRuleDef = {};
type LDtkAutoRuleDef = {};
/** If you're writing your own LDtk importer, you should probably just ignore *most* stuff in the `defs` section, as it contains data that are mostly important to the editor. To keep you away from the `defs` section and avoid some unnecessary JSON parsing, important data from definitions is often duplicated in fields prefixed with a double underscore (eg. `__identifier` or `__type`). The 2 only definition types you might need here are **Tilesets** and **Enums**. */
declare type LDtkDefinition = {
type LDtkDefinition = {
/** All entities definitions, including their custom fields */
entities: LDtkEntityDef[];
/** All internal enums */
Expand All @@ -104,7 +104,7 @@ declare type LDtkDefinition = {
tilesets: LDtkTilesetDef[];
};
/** Entity definition */
declare type LDtkEntityDef = {
type LDtkEntityDef = {
/** Base entity color */
color: string;
/** Array of field definitions */
Expand Down Expand Up @@ -166,7 +166,7 @@ declare type LDtkEntityDef = {
width: integer;
};
/** Entity instance */
declare type LDtkEntityInstance = {
type LDtkEntityInstance = {
/** Grid-based coordinates (`[x,y]` format) */
__grid: integer[];
/** Entity definition identifier */
Expand All @@ -193,7 +193,7 @@ declare type LDtkEntityInstance = {
width: integer;
};
/** Enum definition */
declare type LDtkEnumDef = {
type LDtkEnumDef = {
/** */
externalFileChecksum: string | null;
/** Relative path to the external file providing this Enum */
Expand All @@ -210,7 +210,7 @@ declare type LDtkEnumDef = {
values: LDtkEnumDefValues[];
};
/** Enum value definition */
declare type LDtkEnumDefValues = {
type LDtkEnumDefValues = {
/** An array of 4 Int values that refers to the tile in the tileset image: `[ x, y, width, height ]` */
__tileSrcRect: integer[] | null;
/** Optional color */
Expand All @@ -221,14 +221,14 @@ declare type LDtkEnumDefValues = {
tileId: integer | null;
};
/** In a tileset definition, enum based tag infos */
declare type LDtkEnumTagValue = {
type LDtkEnumTagValue = {
/** */
enumValueId: string;
/** */
tileIds: integer[];
};
/** This section is mostly only intended for the LDtk editor app itself. You can safely ignore it. */
declare type LDtkFieldDef = {
type LDtkFieldDef = {
/** Human readable value type. Possible values: `Int, Float, String, Bool, Color, ExternEnum.XXX, LocalEnum.XXX, Point, FilePath`.<br/> If the field is an array, this field will look like `Array<...>` (eg. `Array<Int>`, `Array<Point>` etc.)<br/> NOTE: if you enable the advanced option **Use Multilines type**, you will have \"*Multilines*\" instead of \"*String*\" when relevant. */
__type: string;
/** Optional list of accepted file extensions for FilePath value type. Includes the dot: `.ext` */
Expand Down Expand Up @@ -310,7 +310,7 @@ declare type LDtkFieldDef = {
useForSmartColor: boolean;
};
/** Field instance */
declare type LDtkFieldInstance = {
type LDtkFieldInstance = {
/** Field definition identifier */
__identifier: string;
/** Optional TilesetRect used to display this field (this can be the field own Tile, or some other Tile guessed from the value, like an Enum). */
Expand All @@ -324,15 +324,15 @@ declare type LDtkFieldInstance = {
/** Editor internal raw values */
realEditorValues: any[];
};
declare type LDtkFlag =
type LDtkFlag =
| 'DiscardPreCsvIntGrid'
| 'ExportPreCsvIntGridFormat'
| 'IgnoreBackupSuggest'
| 'PrependIndexToLevelFileNames'
| 'MultiWorlds'
| 'UseMultilinesType';
/** IntGrid value definition */
declare type LDtkIntGridValueDef = {
type LDtkIntGridValueDef = {
/** */
color: string;
/** User defined unique identifier */
Expand All @@ -341,14 +341,14 @@ declare type LDtkIntGridValueDef = {
value: integer;
};
/** IntGrid value instance */
declare type LDtkIntGridValueInstance = {
type LDtkIntGridValueInstance = {
/** Coordinate ID in the layer grid */
coordId: integer;
/** IntGrid value */
v: integer;
};
/** Layer definition */
declare type LDtkLayerDef = {
type LDtkLayerDef = {
/** Type of the layer (*IntGrid, Entities, Tiles or AutoLayer*) */
__type: string;
/** Contains all the auto-layer rule definitions. */
Expand Down Expand Up @@ -401,7 +401,7 @@ declare type LDtkLayerDef = {
uid: integer;
};
/** Layer instance */
declare type LDtkLayerInstance = {
type LDtkLayerInstance = {
/** Grid-based height */
__cHei: integer;
/** Grid-based width */
Expand Down Expand Up @@ -452,7 +452,7 @@ declare type LDtkLayerInstance = {
visible: boolean;
};
/** This section contains all the level data. It can be found in 2 distinct forms, depending on Project current settings: - If \"*Separate level files*\" is **disabled** (default): full level data is *embedded* inside the main Project JSON file, - If \"*Separate level files*\" is **enabled**: level data is stored in *separate* standalone `.ldtkl` files (one per level). In this case, the main Project JSON file will still contain most level data, except heavy sections, like the `layerInstances` array (which will be null). The `externalRelPath` string points to the `ldtkl` file. A `ldtkl` file is just a JSON file containing exactly what is described below. */
declare type LDtkLevel = {
type LDtkLevel = {
/** Background color of the level (same as `bgColor`, except the default value is automatically used here if its value is `null`) */
__bgColor: string;
/** Position informations of the background image, if there is one. */
Expand Down Expand Up @@ -497,7 +497,7 @@ declare type LDtkLevel = {
worldY: integer;
};
/** Level background image position info */
declare type LDtkLevelBgPosInfos = {
type LDtkLevelBgPosInfos = {
/** An array of 4 float values describing the cropped sub-rectangle of the displayed background image. This cropping happens when original is larger than the level bounds. Array format: `[ cropX, cropY, cropWidth, cropHeight ]` */
cropRect: number[];
/** An array containing the `[scaleX,scaleY]` values of the **cropped** background image, depending on `bgPos` option. */
Expand All @@ -506,7 +506,7 @@ declare type LDtkLevelBgPosInfos = {
topLeftPx: integer[];
};
/** Nearby level info */
declare type LDtkNeighbourLevel = {
type LDtkNeighbourLevel = {
/** A single lowercase character tipping on the level location (`n`orth, `s`outh, `w`est, `e`ast). */
dir: string;
/** Neighbour Instance Identifier */
Expand All @@ -515,7 +515,7 @@ declare type LDtkNeighbourLevel = {
levelUid: integer;
};
/** This structure represents a single tile from a given Tileset. */
declare type LDtkTile = {
type LDtkTile = {
/** Internal data used by the editor.<br/> For auto-layer tiles: `[ruleId, coordId]`.<br/> For tile-layer tiles: `[coordId]`. */
d: integer[];
/** \"Flip bits\", a 2-bits integer to represent the mirror transformations of the tile.<br/> - Bit 0 = X flip<br/> - Bit 1 = Y flip<br/> Examples: f=0 (no flip), f=1 (X flip only), f=2 (Y flip only), f=3 (both flips) */
Expand All @@ -528,7 +528,7 @@ declare type LDtkTile = {
t: integer;
};
/** The `Tileset` definition is the most important part among project definitions. It contains some extra informations about each integrated tileset. If you only had to parse one definition section, that would be the one. */
export declare type LDtkTilesetDef = {
export type LDtkTilesetDef = {
/** Grid-based height */
__cHei: integer;
/** Grid-based width */
Expand Down Expand Up @@ -565,14 +565,14 @@ export declare type LDtkTilesetDef = {
uid: integer;
};
/** In a tileset definition, user defined meta-data of a tile. */
declare type LDtkTileCustomMetadata = {
type LDtkTileCustomMetadata = {
/** */
data: string;
/** */
tileId: integer;
};
/** This object represents a custom sub rectangle in a Tileset image. */
declare type LDtkTilesetRect = {
type LDtkTilesetRect = {
/** Height in pixels */
h: integer;
/** UID of the tileset */
Expand All @@ -584,6 +584,6 @@ declare type LDtkTilesetRect = {
/** Y pixels coordinate of the top-left corner in the Tileset image */
y: integer;
};
declare type LDtkWorld = {};
type LDtkWorld = {};
export {};
//# sourceMappingURL=LDtkFormat.d.ts.map
Loading

0 comments on commit 91db750

Please sign in to comment.