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

Fix multiple external tilesets #37

Merged
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
8 changes: 6 additions & 2 deletions lib/src/tile_map_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ class TileMapParser {
return TiledMap.parse(parser);
}

static TiledMap parseTmx(String xml, {TsxProvider? tsx}) {
/// Parses the provided map xml.
///
/// Accepts an optional list of external TsxProviders for external tilesets
/// referenced in the map file.
static TiledMap parseTmx(String xml, {List<TsxProvider>? tsxList}) {
final xmlElement = XmlDocument.parse(xml).rootElement;
if (xmlElement.name.local != 'map') {
throw 'XML is not in TMX format';
}
final parser = XmlParser(xmlElement);
return TiledMap.parse(parser, tsx: tsx);
return TiledMap.parse(parser, tsxList: tsxList);
}
}
17 changes: 15 additions & 2 deletions lib/src/tiled_map.dart
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ class TiledMap {
);
}

static TiledMap parse(Parser parser, {TsxProvider? tsx}) {
static TiledMap parse(Parser parser, {List<TsxProvider>? tsxList}) {
final backgroundColor = parser.getStringOrNull('backgroundcolor');
final compressionLevel = parser.getInt('compressionlevel', defaults: -1);
final height = parser.getInt('height');
Expand All @@ -220,7 +220,20 @@ class TiledMap {

final tilesets = parser.getChildrenAs(
'tileset',
(e) => Tileset.parse(e, tsx: tsx),
(tilesetData) {
final tilesetSource = tilesetData.getStringOrNull('source');
if (tilesetSource == null || tsxList == null) {
return Tileset.parse(tilesetData);
}
final matchingTsx = tsxList.where(
(tsx) => tsx.filename == tilesetSource,
);

return Tileset.parse(
tilesetData,
tsx: matchingTsx.isNotEmpty ? matchingTsx.first : null,
);
},
);
final layers = Layer.parseLayers(parser);
final properties = parser.getProperties();
Expand Down
4 changes: 3 additions & 1 deletion lib/src/tileset/tileset.dart
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,9 @@ class Tileset {

void _checkIfExtenalTsx(String? source, TsxProvider? tsx) {
if (tsx != null && source != null) {
final tileset = Tileset.parse(tsx.getSource(source));
final tileset = Tileset.parse(
tsx.getChachedSource() ?? tsx.getSource(source),
);
// Copy attributes if not null
backgroundColor = tileset.backgroundColor ?? backgroundColor;
columns = tileset.columns ?? columns;
Expand Down
10 changes: 10 additions & 0 deletions lib/src/tsx_provider.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
part of tiled;

/// abstract class to be implemented for an external tileset data provider.
abstract class TsxProvider {
/// Unique filename for the tileset to be loaded. This should match the
/// 'source' property in the map.tmx file.
String get filename;

/// Retrieves the external tileset data given the tileset filename.
Parser getSource(String filename);

/// Used when provider implementations cache the data. Returns the cached
/// data for the exernal tileset.
Parser? getChachedSource();
}
14 changes: 14 additions & 0 deletions test/fixtures/external_tileset_1.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.2" tiledversion="2018.11.29" name="level1" tilewidth="16" tileheight="16" tilecount="136" columns="17">
<image source="../images/map-level1.png" width="272" height="128"/>
<tile id="64">
<properties>
<property name="type" value="sky"/>
</properties>
</tile>
<tile id="65">
<properties>
<property name="type" value="xpto"/>
</properties>
</tile>
</tileset>
4 changes: 4 additions & 0 deletions test/fixtures/external_tileset_2.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.2" tiledversion="2018.11.29" name="level2" tilewidth="16" tileheight="16" tilecount="288" columns="24">
<image source="../images/map-level2.png" width="384" height="192" />
</tileset>
30 changes: 30 additions & 0 deletions test/fixtures/map_with_multiple_tilesets.tmx

Large diffs are not rendered by default.

108 changes: 103 additions & 5 deletions test/parser_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,10 @@ void main() {
group('Parser.parse fills Map with tileset & different img configs', () {
setUp(() {
return File('./test/fixtures/map_images.tmx').readAsString().then((xml) {
map = TileMapParser.parseTmx(xml, tsx: CustomTsxProvider());
map = TileMapParser.parseTmx(
xml,
tsxList: [CustomTsxProvider.parse('tileset.tsx')],
);
});
});

Expand Down Expand Up @@ -222,7 +225,10 @@ void main() {
group('Parser.parse with tsx provider', () {
test('it loads external tsx', () {
return File('./test/fixtures/map_images.tmx').readAsString().then((xml) {
map = TileMapParser.parseTmx(xml, tsx: CustomTsxProvider());
map = TileMapParser.parseTmx(
xml,
tsxList: [CustomTsxProvider.parse('tileset.tsx')],
);
expect(
map.tilesetByName('external').image!.source,
equals('level1.png'),
Expand All @@ -234,18 +240,110 @@ void main() {
group('Parser.parse with multiple layers', () {
test('it has 2 layers', () {
return File('./test/fixtures/map_images.tmx').readAsString().then((xml) {
map = TileMapParser.parseTmx(xml, tsx: CustomTsxProvider());
map = TileMapParser.parseTmx(
xml,
tsxList: [CustomTsxProvider.parse('tileset.tsx')],
);
expect(map.layers.length, equals(2));
});
});
});

group('Map Parses Multiple Tilesets', () {
late TiledMap map;
setUp(() {
return File('./test/fixtures/map_with_multiple_tilesets.tmx')
.readAsString()
.then((xml) {
final tilemapXml = XmlDocument.parse(xml).rootElement;
final tsxSourcePaths = tilemapXml.children
.whereType<XmlElement>()
.where((element) => element.name.local == 'tileset')
.map((tsx) => tsx.getAttribute('source'));

final tsxProviders = tsxSourcePaths
.where((key) => key != null)
.map((key) => CustomTsxProvider.parse(key!));

map = TileMapParser.parseTmx(
xml,
tsxList: tsxProviders.isEmpty ? null : tsxProviders.toList(),
);
return;
});
});
test(
'correct number of tilests',
() => expect(
map.tilesets.length == 3,
true,
),
);

test('tilesets firstgid correct', () {
expect(
map.tilesets.first.firstGid == 1,
true,
);

expect(
map.tilesets.last.firstGid == 273,
true,
);
});
test('first tileset details correct', () {
expect(
map.tilesets.first.name == 'level1',
true,
);

expect(
map.tilesets.first.tileCount == 136,
true,
);
});
test('embedded tileset details correct', () {
expect(
map.tilesets[1].name == 'level_embed',
true,
);
});
test('third tileset details correct', () {
expect(
map.tilesets.last.name == 'level2',
true,
);

expect(
map.tilesets.last.tileCount == 288,
true,
);
});
});
}

class CustomTsxProvider extends TsxProvider {
final String _filename;
final String data;

CustomTsxProvider._(this.data, this._filename);

@override
String get filename => _filename;

@override
Parser getSource(String key) {
final xml = File('./test/fixtures/tileset.tsx').readAsStringSync();
final node = XmlDocument.parse(xml).rootElement;
final node = XmlDocument.parse(data).rootElement;
return XmlParser(node);
}

@override
Parser? getChachedSource() {
return getSource('');
}

static CustomTsxProvider parse(String filename) {
final xml = File('./test/fixtures/$filename').readAsStringSync();
return CustomTsxProvider._(xml, filename);
}
}