mirror of
https://github.com/dcronqvist/DotTiled.git
synced 2025-02-05 08:52:50 +02:00
Merge pull request #24 from dcronqvist/common-readers
Create easy-to-use Loader class with reader and cache
This commit is contained in:
commit
4034e68cf3
15 changed files with 728 additions and 49 deletions
|
@ -235,6 +235,7 @@ dotnet_diagnostic.IDE0004.severity = silent
|
||||||
dotnet_diagnostic.IDE0005.severity = error
|
dotnet_diagnostic.IDE0005.severity = error
|
||||||
dotnet_diagnostic.IDE0008.severity = silent
|
dotnet_diagnostic.IDE0008.severity = silent
|
||||||
dotnet_diagnostic.IDE0055.severity = silent
|
dotnet_diagnostic.IDE0055.severity = silent
|
||||||
|
dotnet_diagnostic.IDE0058.severity = silent
|
||||||
dotnet_diagnostic.IDE0160.severity = none
|
dotnet_diagnostic.IDE0160.severity = none
|
||||||
dotnet_diagnostic.CA1707.severity = silent
|
dotnet_diagnostic.CA1707.severity = silent
|
||||||
dotnet_diagnostic.CA1852.severity = none
|
dotnet_diagnostic.CA1852.severity = none
|
||||||
|
@ -243,7 +244,7 @@ dotnet_diagnostic.CA1720.severity = silent
|
||||||
dotnet_diagnostic.CA1711.severity = silent
|
dotnet_diagnostic.CA1711.severity = silent
|
||||||
dotnet_diagnostic.CA1716.severity = silent
|
dotnet_diagnostic.CA1716.severity = silent
|
||||||
|
|
||||||
[.github/**/*.yml]
|
[*.(yml|json|js)]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
8
Makefile
8
Makefile
|
@ -2,13 +2,15 @@ test:
|
||||||
dotnet build src/DotTiled.sln
|
dotnet build src/DotTiled.sln
|
||||||
dotnet test src/DotTiled.sln
|
dotnet test src/DotTiled.sln
|
||||||
|
|
||||||
docs-serve:
|
docs-serve: docs/index.md
|
||||||
docfx docs/docfx.json --serve
|
docfx docs/docfx.json --serve
|
||||||
|
|
||||||
docs-build:
|
docs-build: docs/index.md
|
||||||
cp README.md docs/index.md
|
|
||||||
docfx docs/docfx.json
|
docfx docs/docfx.json
|
||||||
|
|
||||||
|
docs/index.md:
|
||||||
|
cp README.md docs/index.md
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
dotnet format style --verify-no-changes src/DotTiled.sln
|
dotnet format style --verify-no-changes src/DotTiled.sln
|
||||||
dotnet format analyzers --verify-no-changes src/DotTiled.sln
|
dotnet format analyzers --verify-no-changes src/DotTiled.sln
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
"_appName": "DotTiled",
|
"_appName": "DotTiled",
|
||||||
"_appTitle": "DotTiled",
|
"_appTitle": "DotTiled",
|
||||||
"_enableSearch": true,
|
"_enableSearch": true,
|
||||||
"pdf": true
|
"pdf": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,15 +1,92 @@
|
||||||
# Loading maps
|
# Loading maps
|
||||||
|
|
||||||
Loading maps with DotTiled is straightforward and easy. The <xref:DotTiled.Map> class is a representation of a Tiled map, mimicking the structure of a Tiled map file. Map files can either be in the [`.tmx`/XML](https://doc.mapeditor.org/en/stable/reference/tmx-map-format/) or [`.tmj`/json](https://doc.mapeditor.org/en/stable/reference/json-map-format/) format. DotTiled supports **both** formats fully.
|
Loading maps with DotTiled is very flexible and allows you as a developer to freely choose how you want to load your maps and tilesets. This guide will show you how to customize the loading process to fit your needs. As the tip below suggests, you can also use the quickstart guide if you just want to load maps from the filesystem without any particular customization.
|
||||||
|
|
||||||
> [!NOTE]
|
> [!TIP]
|
||||||
> Using the `.tmj` file format will result in <xref:DotTiled.ImageLayer.Image> not having the same amount of information as for the `.tmx` format. This is due to the fact that the `.tmj` format does not include the full information that the `.tmx` format does. This is not a problem with DotTiled, but rather a limitation of the `.tmj` format.
|
> For a quick and easy way to load maps from the filesystem, please refer to the [quickstart guide](../quickstart.md).
|
||||||
|
|
||||||
## External resolution
|
## File format caveats
|
||||||
|
|
||||||
Tiled maps may consist of several external files, such as tilesets or object templates. In Tiled map files, they are typically referenced by their path relative to the map file. It would be annoying to have to first load all these external resources before loading a map (which is how some other similar libraries work), so loading a map with DotTiled is designed in a way that you only have to provide a function that resolves these external resources. This way, DotTiled will figure out which external resources are needed and will invoke the corresponding resolver function to load them.
|
The <xref:DotTiled.Map> class is a representation of a Tiled map, mimicking the structure of a Tiled XML map file. Map files can either be in the [`.tmx`/XML](https://doc.mapeditor.org/en/stable/reference/tmx-map-format/) or [`.tmj`/json](https://doc.mapeditor.org/en/stable/reference/json-map-format/) format. DotTiled supports **both** formats fully.
|
||||||
|
|
||||||
Loading a map, tileset, or template will require you to specify **three** resolver functions. We'll go through each of them below.
|
> [!WARNING]
|
||||||
|
> Using the `.tmj` file format will result in <xref:DotTiled.ImageLayer.Image> (the source image for image layers) not having the same amount of information as for the `.tmx` format. This is due to the fact that the `.tmj` format does not include the full information that the `.tmx` format does. This is not a problem with DotTiled, but rather a limitation of the `.tmj` format.
|
||||||
|
|
||||||
|
## The process of loading a map
|
||||||
|
|
||||||
|
Loading a map with DotTiled is not a complex process, but one that at least demands a basic understanding of how Tiled maps are structured. The process can be broken down into the following flow(-ish) chart:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
Z{{Loading a map
|
||||||
|
with DotTiled}} --> A
|
||||||
|
|
||||||
|
subgraph Parsing map
|
||||||
|
A[(Read map)] --> B(Parse map)
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Parsing tileset
|
||||||
|
B -.->|References
|
||||||
|
external tileset| C[(Read tileset)]
|
||||||
|
C --> D(Parse tileset) --o|Store in map| B
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Parsing template
|
||||||
|
B -.->|References external
|
||||||
|
template in object| E[(Read template)]
|
||||||
|
E --> F(Parse template) --o|Use as template
|
||||||
|
for object| B
|
||||||
|
end
|
||||||
|
|
||||||
|
F -.-> |References
|
||||||
|
external tileset| C
|
||||||
|
F -.-> |References
|
||||||
|
external template| E
|
||||||
|
```
|
||||||
|
|
||||||
|
As you can see, the process is quite simple. You read the map, parse it, and then read and parse any external tilesets or templates that are referenced in the map. The tilesets and templates are then stored in the map object that is returned to you.
|
||||||
|
|
||||||
|
However, because DotTiled works in the way that it does, you will need to provide a way to resolve these external resources. We'll go through how to do that in the next section.
|
||||||
|
|
||||||
|
## Loading a map with <xref:DotTiled.Serialization.Loader>
|
||||||
|
|
||||||
|
When using <xref:DotTiled.Serialization.Loader>, external resources like tilesets and templates will be resolved by the loader itself. Since Tiled saves the external resource paths relative to the map file, the loader will automatically resolve these paths and use the provided <xref:DotTiled.Serialization.IResourceReader> to read the external resources. Therefore, as long as the external resources are accessible in a "relative path" way using the provided <xref:DotTiled.Serialization.IResourceReader>, you don't have to worry about resolving them yourself.
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<tileset firstgid="1" source="tileset.tsx"/>
|
||||||
|
<layer id="1" name="Tile Layer 1" width="5" height="5">
|
||||||
|
<data encoding="csv">
|
||||||
|
1,1,0,0,7,
|
||||||
|
1,1,0,0,7,
|
||||||
|
0,0,1,0,7,
|
||||||
|
0,0,0,1,7,
|
||||||
|
21,21,21,21,1
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
</map>
|
||||||
|
```
|
||||||
|
|
||||||
|
A map like the one above that is loaded by the following code will result in the loader calling `IResourceReader.Read("path/to/tileset.tsx")` to read the external tileset, since it will use the path relative to the map file to resolve the tileset.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var loader = Loader.Default();
|
||||||
|
var map = loader.LoadMap("path/to/map.tmx");
|
||||||
|
```
|
||||||
|
|
||||||
|
Additionally, the loader will use an in-memory cache to avoid loading the same tileset or template multiple times. This is done using the <xref:DotTiled.Serialization.IResourceCache> that is provided to the loader. If you don't provide a cache, the loader will use the <xref:DotTiled.Serialization.DefaultResourceCache> by default.
|
||||||
|
|
||||||
|
## Loading a map manually with <xref:DotTiled.Serialization.MapReader>
|
||||||
|
|
||||||
|
While it is recommended to use the <xref:DotTiled.Serialization.Loader> class to perform the loading of maps and tilesets, you may have certain requirements that necessitate you to load maps in a more manual way. This section will guide you through how to load a map manually without the use of the provided loader.
|
||||||
|
|
||||||
|
### <xref:DotTiled.Serialization.MapReader>, <xref:DotTiled.Serialization.TilesetReader>, and <xref:DotTiled.Serialization.TemplateReader>
|
||||||
|
|
||||||
|
are the three classes that you will use to read the map, tileset, and template, respectively. They are designed to be used in a way that you can provide your own resolver functions to load external resources.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> The resolving functions will get the source path of the external resource as a parameter, *in the exact way it is written in the map file*. You will have to perform your own path resolution to load the external resources.
|
||||||
|
|
||||||
### `Func<string, Tileset>` - Tileset resolver
|
### `Func<string, Tileset>` - Tileset resolver
|
||||||
|
|
||||||
|
@ -38,17 +115,17 @@ Tileset ResolveTileset(string source)
|
||||||
|
|
||||||
### `Func<string, Template>` - Template resolver
|
### `Func<string, Template>` - Template resolver
|
||||||
|
|
||||||
This function is used to resolve external object templates by their source path. The function should return a <xref:DotTiled.Template> instance given the source path of the template. If you just want to load templates from the file system, you can use something very similar to the tileset resolver by replacing <xref:DotTiled.Serialization.TilesetReader> with <xref:DotTiled.Serialization.TemplateReader>.
|
This function is used to resolve external object templates by their source path. The function should return a <xref:DotTiled.Template> instance given the source path of the template. If you just want to load templates from the file system, you can use something very similar to the example tileset resolver by replacing <xref:DotTiled.Serialization.TilesetReader> with <xref:DotTiled.Serialization.TemplateReader>.
|
||||||
|
|
||||||
### `Func<string, CustomType>` - Custom type resolver
|
### `Func<string, ICustomTypeDefinition>` - Custom type resolver
|
||||||
|
|
||||||
This function is used to resolve custom types that are defined in Tiled maps. Please refer to the [custom properties](custom-properties.md) documentation for more information on custom types. The function should return a <xref:DotTiled.ICustomTypeDefinition> instance given the custom type's name.
|
This function is used to resolve custom types that are defined in your Tiled maps. Please refer to the [custom properties](custom-properties.md) documentation for more information on custom types. The function should return a <xref:DotTiled.ICustomTypeDefinition> instance given the custom type's name.
|
||||||
|
|
||||||
## Putting it all together
|
## Putting it all together
|
||||||
|
|
||||||
The following classes are the readers that you will need to use to read the map, tileset, and template: <xref:DotTiled.Serialization.MapReader>, <xref:DotTiled.Serialization.TilesetReader>, and <xref:DotTiled.Serialization.TemplateReader>.
|
The following classes are the readers that you will need to use to read the map, tileset, and template: <xref:DotTiled.Serialization.MapReader>, <xref:DotTiled.Serialization.TilesetReader>, and <xref:DotTiled.Serialization.TemplateReader>.
|
||||||
|
|
||||||
Here is an example of how you can load a map with DotTiled:
|
Here is an example of how you can load a map with DotTiled, and is very similar to how the <xref:DotTiled.Serialization.Loader> class works:
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
string mapPath = "path/to/map.tmx";
|
string mapPath = "path/to/map.tmx";
|
||||||
|
|
|
@ -6,41 +6,64 @@ Install DotTiled from NuGet:
|
||||||
dotnet add package DotTiled
|
dotnet add package DotTiled
|
||||||
```
|
```
|
||||||
|
|
||||||
Load a map from file system:
|
Use the `DotTiled` namespace (if you want).
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
string mapPath = "path/to/map.tmx";
|
using DotTiled;
|
||||||
string mapDirectory = Path.GetDirectoryName(mapPath);
|
|
||||||
|
|
||||||
Tileset ResolveTileset(string source)
|
|
||||||
{
|
|
||||||
string tilesetPath = Path.Combine(mapDirectory, source);
|
|
||||||
using var tilesetFileReader = new StreamReader(tilesetPath);
|
|
||||||
var tilesetString = tilesetReader.ReadToEnd();
|
|
||||||
using var tilesetReader = new TilesetReader(tilesetString, ResolveTileset, ResolveTemplate, ResolveCustomType);
|
|
||||||
return tilesetReader.ReadTileset();
|
|
||||||
}
|
|
||||||
|
|
||||||
Template ResolveTemplate(string source)
|
|
||||||
{
|
|
||||||
string templatePath = Path.Combine(mapDirectory, source);
|
|
||||||
using var templateFileReader = new StreamReader(templatePath);
|
|
||||||
var templateString = templateReader.ReadToEnd();
|
|
||||||
using var templateReader = new TemplateReader(templateString, ResolveTileset, ResolveTemplate, ResolveCustomType);
|
|
||||||
return templateReader.ReadTemplate();
|
|
||||||
}
|
|
||||||
|
|
||||||
ICustomTypeDefinition ResolveCustomType(string name)
|
|
||||||
{
|
|
||||||
var allDefinedTypes = [ ... ];
|
|
||||||
return allDefinedTypes.FirstOrDefault(type => type.Name == name);
|
|
||||||
}
|
|
||||||
|
|
||||||
using var mapFileReader = new StreamReader(mapPath);
|
|
||||||
var mapString = mapFileReader.ReadToEnd();
|
|
||||||
using var mapReader = new MapReader(mapString, ResolveTileset, ResolveTemplate, ResolveCustomType);
|
|
||||||
|
|
||||||
var map = mapReader.ReadMap();
|
|
||||||
```
|
```
|
||||||
|
|
||||||
If the above looks intimidating, don't worry! DotTiled is designed to be flexible and allow you to load maps from any source, such as a database or a custom file format. The above example is just one way to load a map from a file system. Please look at [Loading Maps](essentials/loading-maps.md) for more information on how to load maps from different sources.
|
Or fully qualify all `DotTiled` types e.g. `DotTiled.Loader`.
|
||||||
|
|
||||||
|
## Loading a map from the file system
|
||||||
|
|
||||||
|
This will create a loader that will load files from the underlying file system using <xref:DotTiled.Serialization.FileSystemResourceReader>. It will also be configured to use an in-memory cache to avoid loading the same tileset or template multiple times using <xref:DotTiled.Serialization.DefaultResourceCache>.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var loader = Loader.Default();
|
||||||
|
var map = loader.LoadMap("path/to/map.tmx");
|
||||||
|
```
|
||||||
|
|
||||||
|
## Loading a map from a different source
|
||||||
|
|
||||||
|
If you want to load resources (maps, tilesets, templates) from a different source than the underlying file system, you can override the <xref:DotTiled.Serialization.FileSystemResourceReader> that is being used with your own implementation of <xref:DotTiled.Serialization.IResourceReader>.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var loader = Loader.DefaultWith(
|
||||||
|
resourceReader: new MyCustomResourceReader());
|
||||||
|
var map = loader.LoadMap("path/to/map.tmx");
|
||||||
|
```
|
||||||
|
|
||||||
|
## Caching resources
|
||||||
|
|
||||||
|
Similarly, you can override the <xref:DotTiled.Serialization.DefaultResourceCache> that is being used with your own implementation of <xref:DotTiled.Serialization.IResourceCache>.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var loader = Loader.DefaultWith(
|
||||||
|
resourceReader: new MyCustomResourceReader(),
|
||||||
|
resourceCache: new MyCustomResourceCache());
|
||||||
|
var map = loader.LoadMap("path/to/map.tmx");
|
||||||
|
```
|
||||||
|
|
||||||
|
## Custom types
|
||||||
|
|
||||||
|
If you have custom types in your map, you can provide any `IEnumerable<ICustomTypeDefinition>` to the loader. This will allow the loader to deserialize the custom types in your map.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var monsterSpawnerDef = new CustomClassDefinition { ... };
|
||||||
|
var chestDef = new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = "Chest",
|
||||||
|
UseAs = CustomClassUseAs.All,
|
||||||
|
Members = [
|
||||||
|
new IntProperty { Name = "coins", Value = 0 },
|
||||||
|
new BoolProperty { Name = "locked", Value = true }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
var loader = Loader.DefaultWith(
|
||||||
|
customTypeDefinitions: [monsterSpawnerDef, chestDef]);
|
||||||
|
var map = loader.LoadMap("path/to/map.tmx");
|
||||||
|
|
||||||
|
var chest = map.GetProperty<CustomClassProperty>("chest").Value;
|
||||||
|
var coinsToSpawn = chest.GetProperty<IntProperty>("coins").Value;
|
||||||
|
```
|
9
docs/template/public/main.js
vendored
Normal file
9
docs/template/public/main.js
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
export default {
|
||||||
|
iconLinks: [
|
||||||
|
{
|
||||||
|
icon: 'github',
|
||||||
|
href: 'https://github.com/dcronqvist/DotTiled',
|
||||||
|
title: 'GitHub'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -11,6 +11,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||||
|
<PackageReference Include="NSubstitute" Version="5.1.0" />
|
||||||
<PackageReference Include="xunit" Version="2.5.3" />
|
<PackageReference Include="xunit" Version="2.5.3" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
using DotTiled.Serialization;
|
||||||
|
|
||||||
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
|
public class DefaultResourceCacheTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void GetTemplate_TemplateDoesNotExist_ReturnsEmptyOptional()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var cache = new DefaultResourceCache();
|
||||||
|
var path = "template.tsx";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = cache.GetTemplate(path);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result.HasValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetTemplate_TemplateHasBeenInserted_ReturnsTemplate()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var cache = new DefaultResourceCache();
|
||||||
|
var path = "template.tsx";
|
||||||
|
var template = new Template
|
||||||
|
{
|
||||||
|
Object = new EllipseObject { }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
cache.InsertTemplate(path, template);
|
||||||
|
var result = cache.GetTemplate(path);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result.HasValue);
|
||||||
|
Assert.Same(template, result.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetTileset_TilesetDoesNotExist_ReturnsEmptyOptional()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var cache = new DefaultResourceCache();
|
||||||
|
var path = "tileset.tsx";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = cache.GetTileset(path);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result.HasValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetTileset_TilesetHasBeenInserted_ReturnsTileset()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var cache = new DefaultResourceCache();
|
||||||
|
var path = "tileset.tsx";
|
||||||
|
var tileset = new Tileset
|
||||||
|
{
|
||||||
|
Name = "Tileset",
|
||||||
|
TileWidth = 32,
|
||||||
|
TileHeight = 32,
|
||||||
|
TileCount = 1,
|
||||||
|
Columns = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
cache.InsertTileset(path, tileset);
|
||||||
|
var result = cache.GetTileset(path);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result.HasValue);
|
||||||
|
Assert.Same(tileset, result.Value);
|
||||||
|
}
|
||||||
|
}
|
259
src/DotTiled.Tests/Serialization/LoaderTests.cs
Normal file
259
src/DotTiled.Tests/Serialization/LoaderTests.cs
Normal file
|
@ -0,0 +1,259 @@
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using DotTiled.Serialization;
|
||||||
|
using NSubstitute;
|
||||||
|
|
||||||
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
|
public class LoaderTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void LoadMap_Always_ReadsFromResourceReader()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var resourceReader = Substitute.For<IResourceReader>();
|
||||||
|
resourceReader.Read("map.tmx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<layer id="1" name="Tile Layer 1" width="5" height="5">
|
||||||
|
<data encoding="csv">
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
</map>
|
||||||
|
""");
|
||||||
|
|
||||||
|
var resourceCache = Substitute.For<IResourceCache>();
|
||||||
|
var customTypeDefinitions = Enumerable.Empty<ICustomTypeDefinition>();
|
||||||
|
var loader = new Loader(resourceReader, resourceCache, customTypeDefinitions);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
loader.LoadMap("map.tmx");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
resourceReader.Received(1).Read("map.tmx");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoadMap_MapReferencesExternalTileset_ReadsTilesetFromResourceReaderAndAttemptsToRetrieveFromCache()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var resourceReader = Substitute.For<IResourceReader>();
|
||||||
|
resourceReader.Read("map.tmx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<tileset firstgid="1" source="tileset.tsx"/>
|
||||||
|
<layer id="1" name="Tile Layer 1" width="5" height="5">
|
||||||
|
<data encoding="csv">
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
</map>
|
||||||
|
""");
|
||||||
|
|
||||||
|
resourceReader.Read("tileset.tsx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<tileset version="1.2" tiledversion="1.11.0" name="Tileset" tilewidth="32" tileheight="32" tilecount="1" columns="1">
|
||||||
|
<tile id="1">
|
||||||
|
<image width="32" height="32" source="tile.png"/>
|
||||||
|
</tile>
|
||||||
|
</tileset>
|
||||||
|
""");
|
||||||
|
|
||||||
|
var resourceCache = Substitute.For<IResourceCache>();
|
||||||
|
resourceCache.GetTileset(Arg.Any<string>()).Returns(Optional<Tileset>.Empty);
|
||||||
|
resourceCache.GetTemplate(Arg.Any<string>()).Returns(Optional<Template>.Empty);
|
||||||
|
|
||||||
|
var customTypeDefinitions = Enumerable.Empty<ICustomTypeDefinition>();
|
||||||
|
var loader = new Loader(resourceReader, resourceCache, customTypeDefinitions);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
loader.LoadMap("map.tmx");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
resourceReader.Received(1).Read("tileset.tsx");
|
||||||
|
resourceCache.Received(1).GetTileset("tileset.tsx");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoadMap_MapReferencesExternalTemplate_ReadsTemplateFromResourceReaderAndAttemptsToRetrieveFromCache()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var resourceReader = Substitute.For<IResourceReader>();
|
||||||
|
resourceReader.Read("map.tmx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<tileset firstgid="1" source="tileset.tsx"/>
|
||||||
|
<layer id="1" name="Tile Layer 1" width="5" height="5">
|
||||||
|
<data encoding="csv">
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
<objectgroup id="2" name="Object Layer 1" width="5" height="5">
|
||||||
|
<object id="1" name="Template" template="template.tx" x="0" y="0" width="32" height="32" gid="1"/>
|
||||||
|
</objectgroup>
|
||||||
|
</map>
|
||||||
|
""");
|
||||||
|
|
||||||
|
resourceReader.Read("tileset.tsx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<tileset version="1.2" tiledversion="1.11.0" name="Tileset" tilewidth="32" tileheight="32" tilecount="1" columns="1">
|
||||||
|
<tile id="1">
|
||||||
|
<image width="32" height="32" source="tile.png"/>
|
||||||
|
</tile>
|
||||||
|
</tileset>
|
||||||
|
""");
|
||||||
|
|
||||||
|
resourceReader.Read("template.tx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<template>
|
||||||
|
<object name="Poly">
|
||||||
|
<properties>
|
||||||
|
<property name="templateprop" value="helo there"/>
|
||||||
|
</properties>
|
||||||
|
<polygon points="0,0 104,20 35.6667,32.3333"/>
|
||||||
|
</object>
|
||||||
|
</template>
|
||||||
|
""");
|
||||||
|
|
||||||
|
var resourceCache = Substitute.For<IResourceCache>();
|
||||||
|
resourceCache.GetTileset(Arg.Any<string>()).Returns(Optional<Tileset>.Empty);
|
||||||
|
resourceCache.GetTemplate(Arg.Any<string>()).Returns(Optional<Template>.Empty);
|
||||||
|
|
||||||
|
var customTypeDefinitions = Enumerable.Empty<ICustomTypeDefinition>();
|
||||||
|
var loader = new Loader(resourceReader, resourceCache, customTypeDefinitions);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
loader.LoadMap("map.tmx");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
resourceReader.Received(1).Read("template.tx");
|
||||||
|
resourceCache.Received(1).GetTemplate("template.tx");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoadMap_CacheReturnsTileset_ReturnsTilesetFromCache()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var resourceReader = Substitute.For<IResourceReader>();
|
||||||
|
resourceReader.Read("map.tmx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<tileset firstgid="1" source="tileset.tsx"/>
|
||||||
|
<layer id="1" name="Tile Layer 1" width="5" height="5">
|
||||||
|
<data encoding="csv">
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
</map>
|
||||||
|
""");
|
||||||
|
|
||||||
|
var resourceCache = Substitute.For<IResourceCache>();
|
||||||
|
resourceCache.GetTileset("tileset.tsx").Returns(new Optional<Tileset>(new Tileset { Name = "Tileset", TileWidth = 32, TileHeight = 32, TileCount = 1, Columns = 1 }));
|
||||||
|
|
||||||
|
var customTypeDefinitions = Enumerable.Empty<ICustomTypeDefinition>();
|
||||||
|
var loader = new Loader(resourceReader, resourceCache, customTypeDefinitions);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
loader.LoadMap("map.tmx");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
resourceReader.DidNotReceive().Read("tileset.tsx");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoadMap_CacheReturnsTemplate_ReturnsTemplateFromCache()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var resourceReader = Substitute.For<IResourceReader>();
|
||||||
|
resourceReader.Read("map.tmx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="5" height="5" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<tileset firstgid="1" source="tileset.tsx"/>
|
||||||
|
<layer id="1" name="Tile Layer 1" width="5" height="5">
|
||||||
|
<data encoding="csv">
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0,
|
||||||
|
0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
<objectgroup id="2" name="Object Layer 1" width="5" height="5">
|
||||||
|
<object id="1" name="Template" template="template.tx" x="0" y="0" width="32" height="32" gid="1"/>
|
||||||
|
</objectgroup>
|
||||||
|
</map>
|
||||||
|
""");
|
||||||
|
|
||||||
|
resourceReader.Read("tileset.tsx").Returns(
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<tileset version="1.2" tiledversion="1.11.0" name="Tileset" tilewidth="32" tileheight="32" tilecount="1" columns="1">
|
||||||
|
<tile id="1">
|
||||||
|
<image width="32" height="32" source="tile.png"/>
|
||||||
|
</tile>
|
||||||
|
</tileset>
|
||||||
|
""");
|
||||||
|
|
||||||
|
var resourceCache = Substitute.For<IResourceCache>();
|
||||||
|
resourceCache.GetTileset(Arg.Any<string>()).Returns(Optional<Tileset>.Empty);
|
||||||
|
resourceCache.GetTemplate("template.tx").Returns(new Optional<Template>(new Template
|
||||||
|
{
|
||||||
|
Object = new PolygonObject
|
||||||
|
{
|
||||||
|
Points = [
|
||||||
|
new Vector2(0,0),
|
||||||
|
new Vector2(104,20),
|
||||||
|
new Vector2(35.6667f,32.3333f)
|
||||||
|
],
|
||||||
|
Properties = [
|
||||||
|
new StringProperty { Name = "templateprop", Value = "helo there" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
var customTypeDefinitions = Enumerable.Empty<ICustomTypeDefinition>();
|
||||||
|
var loader = new Loader(resourceReader, resourceCache, customTypeDefinitions);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
loader.LoadMap("map.tmx");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
resourceReader.DidNotReceive().Read("template.tx");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string WhereAmI([CallerFilePath] string callerFilePath = "") => callerFilePath;
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Test1()
|
||||||
|
{
|
||||||
|
var basePath = Path.GetDirectoryName(WhereAmI())!;
|
||||||
|
var mapPath = Path.Combine(basePath, "TestData/Map/map-with-external-tileset/map-with-external-tileset.tmx");
|
||||||
|
var loader = Loader.Default();
|
||||||
|
loader.LoadMap(mapPath);
|
||||||
|
}
|
||||||
|
}
|
|
@ -89,6 +89,11 @@ public class Optional<T>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public T GetValueOr(T defaultValue) => HasValue ? _value : defaultValue;
|
public T GetValueOr(T defaultValue) => HasValue ? _value : defaultValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the current <see cref="Optional{T}"/> object if it has a value; otherwise, returns the specified default value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="defaultValue">The <see cref="Optional{T}"/> object to be returned if the current <see cref="Optional{T}"/> object has no value.</param>
|
||||||
|
/// <returns></returns>
|
||||||
public Optional<T> GetValueOrOptional(Optional<T> defaultValue) => HasValue ? this : defaultValue;
|
public Optional<T> GetValueOrOptional(Optional<T> defaultValue) => HasValue ? this : defaultValue;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
36
src/DotTiled/Serialization/DefaultResourceCache.cs
Normal file
36
src/DotTiled/Serialization/DefaultResourceCache.cs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace DotTiled.Serialization;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A default implementation of <see cref="IResourceCache"/> that uses an in-memory dictionary to cache resources.
|
||||||
|
/// </summary>
|
||||||
|
public class DefaultResourceCache : IResourceCache
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, Template> _templates = [];
|
||||||
|
private readonly Dictionary<string, Tileset> _tilesets = [];
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public Optional<Template> GetTemplate(string path)
|
||||||
|
{
|
||||||
|
if (_templates.TryGetValue(path, out var template))
|
||||||
|
return new Optional<Template>(template);
|
||||||
|
|
||||||
|
return Optional<Template>.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public Optional<Tileset> GetTileset(string path)
|
||||||
|
{
|
||||||
|
if (_tilesets.TryGetValue(path, out var tileset))
|
||||||
|
return new Optional<Tileset>(tileset);
|
||||||
|
|
||||||
|
return Optional<Tileset>.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void InsertTemplate(string path, Template template) => _templates[path] = template;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void InsertTileset(string path, Tileset tileset) => _tilesets[path] = tileset;
|
||||||
|
}
|
21
src/DotTiled/Serialization/FileSystemResourceReader.cs
Normal file
21
src/DotTiled/Serialization/FileSystemResourceReader.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace DotTiled.Serialization;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uses the underlying host file system to read Tiled resources from a given path.
|
||||||
|
/// </summary>
|
||||||
|
public class FileSystemResourceReader : IResourceReader
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="FileSystemResourceReader"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public FileSystemResourceReader() { }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public string Read(string resourcePath)
|
||||||
|
{
|
||||||
|
using var streamReader = new StreamReader(resourcePath);
|
||||||
|
return streamReader.ReadToEnd();
|
||||||
|
}
|
||||||
|
}
|
35
src/DotTiled/Serialization/IResourceCache.cs
Normal file
35
src/DotTiled/Serialization/IResourceCache.cs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
namespace DotTiled.Serialization;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interface for a cache that stores Tiled resources for faster retrieval and reuse.
|
||||||
|
/// </summary>
|
||||||
|
public interface IResourceCache
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Inserts a tileset into the cache with the given <paramref name="path"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path to the tileset file.</param>
|
||||||
|
/// <param name="tileset">The tileset to insert into the cache.</param>
|
||||||
|
void InsertTileset(string path, Tileset tileset);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a tileset from the cache with the given <paramref name="path"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path to the tileset file.</param>
|
||||||
|
/// <returns>The tileset if it exists in the cache; otherwise, <see cref="Optional{Tileset}.Empty"/>.</returns>
|
||||||
|
Optional<Tileset> GetTileset(string path);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inserts a template into the cache with the given <paramref name="path"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path to the template file.</param>
|
||||||
|
/// <param name="template">The template to insert into the cache.</param>
|
||||||
|
void InsertTemplate(string path, Template template);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a template from the cache with the given <paramref name="path"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path to the template file.</param>
|
||||||
|
/// <returns>The template if it exists in the cache; otherwise, <see cref="Optional{Template}.Empty"/>.</returns>
|
||||||
|
Optional<Template> GetTemplate(string path);
|
||||||
|
}
|
14
src/DotTiled/Serialization/IResourceReader.cs
Normal file
14
src/DotTiled/Serialization/IResourceReader.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
namespace DotTiled.Serialization;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Able to read resources from a given path.
|
||||||
|
/// </summary>
|
||||||
|
public interface IResourceReader
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a Tiled resource from a given path.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resourcePath">The path to the Tiled resource, which can be a Map file, Tileset file, Template file, etc.</param>
|
||||||
|
/// <returns>The content of the resource as a string.</returns>
|
||||||
|
string Read(string resourcePath);
|
||||||
|
}
|
118
src/DotTiled/Serialization/Loader.cs
Normal file
118
src/DotTiled/Serialization/Loader.cs
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace DotTiled.Serialization;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Able to load Tiled resources from a given path.
|
||||||
|
/// </summary>
|
||||||
|
public class Loader
|
||||||
|
{
|
||||||
|
private readonly IResourceReader _resourceReader;
|
||||||
|
private readonly IResourceCache _resourceCache;
|
||||||
|
private readonly IDictionary<string, ICustomTypeDefinition> _customTypeDefinitions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Loader"/> class with the given <paramref name="resourceReader"/>, <paramref name="resourceCache"/>, and <paramref name="customTypeDefinitions"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resourceReader">A reader that is able to read Tiled resources from a given path.</param>
|
||||||
|
/// <param name="resourceCache">A cache that stores Tiled resources for faster retrieval and reuse.</param>
|
||||||
|
/// <param name="customTypeDefinitions">A collection of custom type definitions that can be used to resolve custom types in Tiled resources.</param>
|
||||||
|
public Loader(
|
||||||
|
IResourceReader resourceReader,
|
||||||
|
IResourceCache resourceCache,
|
||||||
|
IEnumerable<ICustomTypeDefinition> customTypeDefinitions)
|
||||||
|
{
|
||||||
|
_resourceReader = resourceReader;
|
||||||
|
_resourceCache = resourceCache;
|
||||||
|
_customTypeDefinitions = customTypeDefinitions.ToDictionary(ctd => ctd.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of a <see cref="Loader"/> with the default <see cref="FileSystemResourceReader"/> and <see cref="DefaultResourceCache"/>, and no available custom type definitions.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A new instance of a <see cref="Loader"/>.</returns>
|
||||||
|
public static Loader Default() => new Loader(new FileSystemResourceReader(), new DefaultResourceCache(), []);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of a <see cref="Loader"/> with the ability to override the default <see cref="FileSystemResourceReader"/>, <see cref="DefaultResourceCache"/>, and custom type definitions.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resourceReader">A reader that is able to read Tiled resources from a given path.</param>
|
||||||
|
/// <param name="resourceCache">A cache that stores Tiled resources for faster retrieval and reuse.</param>
|
||||||
|
/// <param name="customTypeDefinitions">A collection of custom type definitions that can be used to resolve custom types in Tiled resources.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Loader DefaultWith(
|
||||||
|
IResourceReader resourceReader = null,
|
||||||
|
IResourceCache resourceCache = null,
|
||||||
|
IEnumerable<ICustomTypeDefinition> customTypeDefinitions = null) =>
|
||||||
|
new Loader(
|
||||||
|
resourceReader ?? new FileSystemResourceReader(),
|
||||||
|
resourceCache ?? new DefaultResourceCache(),
|
||||||
|
customTypeDefinitions ?? Array.Empty<ICustomTypeDefinition>());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads a map from the given <paramref name="mapPath"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mapPath">The path to the map file.</param>
|
||||||
|
/// <returns>The loaded map.</returns>
|
||||||
|
public Map LoadMap(string mapPath)
|
||||||
|
{
|
||||||
|
var basePath = Path.GetDirectoryName(mapPath);
|
||||||
|
string mapContent = _resourceReader.Read(mapPath);
|
||||||
|
using var mapReader = new MapReader(mapContent, GetTilesetResolver(basePath), GetTemplateResolver(basePath), CustomTypeResolver);
|
||||||
|
return mapReader.ReadMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads a tileset from the given <paramref name="tilesetPath"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tilesetPath">The path to the tileset file.</param>
|
||||||
|
/// <returns>The loaded tileset.</returns>
|
||||||
|
public Tileset LoadTileset(string tilesetPath)
|
||||||
|
{
|
||||||
|
var basePath = Path.GetDirectoryName(tilesetPath);
|
||||||
|
string tilesetContent = _resourceReader.Read(tilesetPath);
|
||||||
|
using var tilesetReader = new TilesetReader(tilesetContent, GetTilesetResolver(basePath), GetTemplateResolver(basePath), CustomTypeResolver);
|
||||||
|
return tilesetReader.ReadTileset();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Func<string, T> GetResolverFunc<T>(
|
||||||
|
string basePath,
|
||||||
|
Func<string, Optional<T>> cacheResolver,
|
||||||
|
Action<string, T> cacheInsert,
|
||||||
|
Func<string, T> resolveFromContent)
|
||||||
|
{
|
||||||
|
return source =>
|
||||||
|
{
|
||||||
|
var resourcePath = Path.Combine(basePath, source);
|
||||||
|
var cachedResource = cacheResolver(resourcePath);
|
||||||
|
if (cachedResource.HasValue)
|
||||||
|
return cachedResource.Value;
|
||||||
|
|
||||||
|
string tilesetContent = _resourceReader.Read(resourcePath);
|
||||||
|
var resource = resolveFromContent(tilesetContent);
|
||||||
|
cacheInsert(resourcePath, resource);
|
||||||
|
return resource;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Func<string, Tileset> GetTilesetResolver(string basePath) =>
|
||||||
|
GetResolverFunc<Tileset>(basePath, _resourceCache.GetTileset, _resourceCache.InsertTileset,
|
||||||
|
tilesetContent =>
|
||||||
|
{
|
||||||
|
using var tilesetReader = new TilesetReader(tilesetContent, GetTilesetResolver(basePath), GetTemplateResolver(basePath), CustomTypeResolver);
|
||||||
|
return tilesetReader.ReadTileset();
|
||||||
|
});
|
||||||
|
|
||||||
|
private Func<string, Template> GetTemplateResolver(string basePath) =>
|
||||||
|
GetResolverFunc<Template>(basePath, _resourceCache.GetTemplate, _resourceCache.InsertTemplate,
|
||||||
|
templateContent =>
|
||||||
|
{
|
||||||
|
using var templateReader = new TemplateReader(templateContent, GetTilesetResolver(basePath), GetTemplateResolver(basePath), CustomTypeResolver);
|
||||||
|
return templateReader.ReadTemplate();
|
||||||
|
});
|
||||||
|
|
||||||
|
private ICustomTypeDefinition CustomTypeResolver(string name) => _customTypeDefinitions[name];
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue