diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e292f594..fd4dcb14 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -71,3 +71,26 @@ jobs: - name: Run cargo clippy --all-targets --package tiled run: cargo clippy --all-targets --package tiled + + docs: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Update package manager + run: sudo apt-get update + + - name: Install dependencies + run: sudo apt-get install -y libsfml-dev libcsfml-dev libasound2-dev libudev-dev + + - name: Get stable Rust toolchain with doc + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + components: rust-docs + + - name: Run cargo doc -p tiled --no-deps + run: cargo doc -p tiled --no-deps diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ca18199..e1c5c9e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,9 +25,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Panic on unexpected XML in `ObjectData` content. (#291) - Divide by zero when parsing a tileset with height/width dimension of 0. (#292) -## [Unreleased] +## [0.11.3] +## Changed +- Replace `libflate` with `flate2`. (#281) + +## [0.11.2] ## Changed - Updated `Image` docs. (#270) +- Update `libflate` dependency to `2.0.0`. (#279) +- Fix some doc links. (#273) +- Update ggez example to 0.9.3. ## [0.11.1] ### Added diff --git a/Cargo.toml b/Cargo.toml index 66bb93af..2e1c9281 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tiled" -version = "0.11.1" +version = "0.11.3" description = "A rust crate for loading maps created by the Tiled editor" categories = ["game-development"] keywords = ["gamedev", "tiled", "tmx", "map"] @@ -34,12 +34,12 @@ path = "examples/ggez/main.rs" [dependencies] base64 = "0.22.1" xml-rs = "0.8.4" -libflate = "2.1.0" zstd = { version = "0.13.1", optional = true, default-features = false } +flate2 = "1.0.28" [dev-dependencies.sfml] version = "0.20.0" features = ["graphics"] [dev-dependencies.ggez] -version = "0.7" +version = "0.9.3" diff --git a/README.md b/README.md index e1c722bb..1d6a2898 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # rs-tiled ```toml -tiled = "0.11.1" +tiled = "0.11.2" ``` [![Rust](https://github.com/mapeditor/rs-tiled/actions/workflows/rust.yml/badge.svg)](https://github.com/mapeditor/rs-tiled/actions/workflows/rust.yml) diff --git a/examples/ggez/main.rs b/examples/ggez/main.rs index 4a3d3743..f5378929 100644 --- a/examples/ggez/main.rs +++ b/examples/ggez/main.rs @@ -9,9 +9,8 @@ mod res_reader; use ggez::{ event::{self, MouseButton}, graphics::{self, DrawParam}, - input, mint::Point2, - Context, GameResult, + Context, GameResult, *, }; use map::MapHandler; use res_reader::GgezResourceReader; @@ -45,10 +44,8 @@ struct Game { impl Game { fn new(ctx: &mut ggez::Context) -> GameResult { - graphics::set_default_filter(ctx, graphics::FilterMode::Nearest); - // Load the map, using a loader with `GgezResourceReader` for reading from the ggez filesystem - let mut loader = tiled::Loader::with_reader(GgezResourceReader(ctx)); + let mut loader = tiled::Loader::with_reader(GgezResourceReader(&mut ctx.fs)); let map = loader.load_tmx_map("/tiled_base64_external.tmx").unwrap(); let map_handler = MapHandler::new(map, ctx).unwrap(); @@ -72,13 +69,14 @@ impl event::EventHandler for Game { .map .background_color() .unwrap_or([0.1, 0.2, 0.3, 1.0].into()); - graphics::clear(ctx, bg_color); + let mut canvas = graphics::Canvas::from_frame(ctx, bg_color); + canvas.set_sampler(graphics::Sampler::nearest_clamp()); - self.draw_map(ctx)?; + self.draw_map(ctx, &mut canvas)?; - self.draw_fps(ctx)?; + self.draw_fps(ctx, &mut canvas)?; - graphics::present(ctx)?; + canvas.finish(ctx)?; Ok(()) } @@ -89,18 +87,27 @@ impl event::EventHandler for Game { button: event::MouseButton, _x: f32, _y: f32, - ) { + ) -> Result<(), GameError> { // Right click toggles the demo animation effect if button == MouseButton::Right { self.map.example_animate = !self.map.example_animate; self.map.invalidate_batch_cache(); } + + Ok(()) } - fn mouse_motion_event(&mut self, ctx: &mut Context, _x: f32, _y: f32, dx: f32, dy: f32) { + fn mouse_motion_event( + &mut self, + ctx: &mut Context, + _x: f32, + _y: f32, + dx: f32, + dy: f32, + ) -> Result<(), GameError> { // Left or middle click + drag pans the map around - if input::mouse::button_pressed(ctx, event::MouseButton::Left) - || input::mouse::button_pressed(ctx, event::MouseButton::Middle) + if ctx.mouse.button_pressed(event::MouseButton::Left) + || ctx.mouse.button_pressed(event::MouseButton::Middle) { self.pan.0 += dx; self.pan.1 += dy; @@ -108,9 +115,11 @@ impl event::EventHandler for Game { // Need to invalidate for parallax to work self.map.invalidate_batch_cache(); } + + Ok(()) } - fn mouse_wheel_event(&mut self, ctx: &mut Context, _x: f32, y: f32) { + fn mouse_wheel_event(&mut self, ctx: &mut Context, _x: f32, y: f32) -> Result<(), GameError> { // Scroll wheel zooms let old_scale = self.scale; @@ -120,24 +129,26 @@ impl event::EventHandler for Game { let Point2 { x: mouse_x, y: mouse_y, - } = input::mouse::position(ctx); + } = ctx.mouse.position(); self.pan.0 = (self.pan.0 - mouse_x) / old_scale * self.scale + mouse_x; self.pan.1 = (self.pan.1 - mouse_y) / old_scale * self.scale + mouse_y; // Need to invalidate for parallax to work self.map.invalidate_batch_cache(); + + Ok(()) } } impl Game { - fn draw_map(&mut self, ctx: &mut Context) -> GameResult { + fn draw_map(&mut self, ctx: &mut Context, canvas: &mut graphics::Canvas) -> GameResult { // Draw tiles + objects let draw_param = DrawParam::default() .dest([self.pan.0, self.pan.1]) .scale([self.scale, self.scale]); - self.map.draw(ctx, draw_param, self.pan)?; + self.map.draw(ctx, canvas, draw_param, self.pan)?; // Draw bounds @@ -148,25 +159,24 @@ impl Game { rect, graphics::Color::from_rgb_u32(0x888888), )?; - graphics::draw(ctx, &r1, draw_param)?; + canvas.draw(&r1, draw_param); Ok(()) } - fn draw_fps(&self, ctx: &mut Context) -> GameResult { - let fps = ggez::timer::fps(ctx); + fn draw_fps(&self, ctx: &mut Context, canvas: &mut graphics::Canvas) -> GameResult { + let fps = ctx.time.fps(); let text = graphics::Text::new(format!("{:.0} fps", fps)); - let (window_width, _window_height) = graphics::size(ctx); + let (window_width, _window_height) = ctx.gfx.drawable_size(); - graphics::draw( - ctx, + canvas.draw( &text, DrawParam::default() - .dest([window_width - text.width(ctx) - 40.0, 10.0]) + .dest([window_width - text.measure(ctx)?.x - 40.0, 10.0]) .scale([1.25, 1.25]) .color(graphics::Color::WHITE), - )?; + ); Ok(()) } diff --git a/examples/ggez/map.rs b/examples/ggez/map.rs index c3cee5f4..a1c334fe 100644 --- a/examples/ggez/map.rs +++ b/examples/ggez/map.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use ggez::{ - graphics::{self, spritebatch::SpriteBatch, DrawParam}, + graphics::{self, Canvas, DrawParam, InstanceArray}, Context, GameResult, }; use tiled::TileLayer; @@ -9,7 +9,7 @@ use tiled::TileLayer; pub struct MapHandler { map: tiled::Map, tileset_image_cache: HashMap, - batch_cache: Option>>, + batch_cache: Option>>, pub example_animate: bool, } @@ -19,9 +19,7 @@ impl MapHandler { let mut tileset_image_cache = HashMap::new(); for ts in map.tilesets().iter() { if let Some(image) = &ts.image { - let mut img = graphics::Image::new(ctx, &image.source)?; - // Set filter to nearest to get crispy pixel art goodness - img.set_filter(graphics::FilterMode::Nearest); + let img = graphics::Image::from_path(ctx, &image.source)?; tileset_image_cache.insert(ts.name.clone(), img); } @@ -66,7 +64,7 @@ impl MapHandler { .map(|c| ggez::graphics::Color::from_rgba(c.red, c.green, c.blue, c.alpha)) } - /// Clear the tile layer `SpriteBatch` cache. + /// Clear the tile layer `InstanceArray` cache. /// Required if the tile instances change in any way (e.g. The tile positions change) pub fn invalidate_batch_cache(&mut self) { self.batch_cache = None; @@ -75,6 +73,7 @@ impl MapHandler { pub fn draw( &mut self, ctx: &mut Context, + canvas: &mut Canvas, draw_param: DrawParam, parallax_pan: (f32, f32), ) -> GameResult { @@ -90,7 +89,7 @@ impl MapHandler { self.batch_cache = Some(self.generate_map_render(ctx, parallax_pan)); } - let layer_batches: &HashMap> = self.batch_cache.as_ref().unwrap(); + let layer_batches: &HashMap> = self.batch_cache.as_ref().unwrap(); // Draw layers @@ -98,7 +97,7 @@ impl MapHandler { match &l.layer_type() { tiled::LayerType::Objects(ol) => { for o in ol.objects() { - Self::draw_object(&o, ctx, draw_param.clone())?; + Self::draw_object(&o, ctx, canvas, draw_param.clone())?; } } tiled::LayerType::Tiles(_tl) => { @@ -106,7 +105,7 @@ impl MapHandler { // Each tileset in the layer gets a different batch for batch in batches { - graphics::draw(ctx, batch, draw_param)?; + canvas.draw(batch, draw_param); } } _ => {} @@ -116,13 +115,13 @@ impl MapHandler { Ok(()) } - /// Generates a set of `SpriteBatch`es for each tile layer in the map. + /// Generates a set of `InstanceArray`s for each tile layer in the map. fn generate_map_render( &self, ctx: &Context, parallax_pan: (f32, f32), - ) -> HashMap> { - let mut layer_batches: HashMap> = HashMap::new(); + ) -> HashMap> { + let mut layer_batches: HashMap> = HashMap::new(); let tile_layers = self .map @@ -138,7 +137,7 @@ impl MapHandler { for ts in self.map.tilesets().iter() { if let Some(img) = self.tileset_image_cache.get(&ts.name) { // img.clone() here is cheap, as it is just an Arc'ed handle (see docs for `ggez::graphics::Image`) - let batch = SpriteBatch::new(img.clone()); + let batch = InstanceArray::new(ctx, img.clone()); ts_sizes_and_batches .insert(ts.name.clone(), (batch, (img.width(), img.height()))); } @@ -147,7 +146,7 @@ impl MapHandler { let width = d.width(); let height = d.height(); - let secs_since_start = ggez::timer::time_since_start(ctx).as_secs_f32(); + let secs_since_start = ctx.time.time_since_start().as_secs_f32(); // Iterate through every tile in the layer for x in 0..width as i32 { @@ -174,7 +173,7 @@ impl MapHandler { * 20.0; } - batch.add( + batch.push( DrawParam::default() .src(get_tile_rect(ts, tile.id(), ts_size.0, ts_size.1)) .dest([dx, dy]) @@ -209,6 +208,7 @@ impl MapHandler { fn draw_object( object: &tiled::ObjectData, ctx: &mut Context, + canvas: &mut Canvas, draw_param: DrawParam, ) -> GameResult { match &object.shape { @@ -220,7 +220,7 @@ impl MapHandler { bounds, graphics::Color::CYAN, )?; - graphics::draw(ctx, &shape, draw_param)?; + canvas.draw(&shape, draw_param); } tiled::ObjectShape::Ellipse { width, height } => { let shape = graphics::Mesh::new_ellipse( @@ -232,7 +232,7 @@ impl MapHandler { 0.5, graphics::Color::CYAN, )?; - graphics::draw(ctx, &shape, draw_param)?; + canvas.draw(&shape, draw_param); } tiled::ObjectShape::Polyline { points } => { let points: Vec<_> = points @@ -245,7 +245,7 @@ impl MapHandler { &points, graphics::Color::CYAN, )?; - graphics::draw(ctx, &shape, draw_param)?; + canvas.draw(&shape, draw_param); } tiled::ObjectShape::Polygon { points } => { let points: Vec<_> = points @@ -258,7 +258,7 @@ impl MapHandler { &points, graphics::Color::CYAN, )?; - graphics::draw(ctx, &shape, draw_param)?; + canvas.draw(&shape, draw_param); } tiled::ObjectShape::Point(_, _) | tiled::ObjectShape::Text { .. } => { // Left as an exercise for the reader @@ -267,13 +267,12 @@ impl MapHandler { if !object.name.is_empty() { let text = graphics::Text::new(object.name.clone()); - graphics::queue_text( - ctx, + canvas.draw( &text, - [object.x, object.y], - Some(graphics::Color::YELLOW), + DrawParam::new() + .dest([object.x, object.y]) + .color(graphics::Color::YELLOW), ); - graphics::draw_queued_text(ctx, draw_param, None, graphics::FilterMode::Nearest)?; } Ok(()) @@ -283,8 +282,8 @@ impl MapHandler { fn get_tile_rect( tileset: &tiled::Tileset, id: u32, - ts_img_width: u16, - ts_img_height: u16, + ts_img_width: u32, + ts_img_height: u32, ) -> graphics::Rect { let ts_x = id % tileset.columns; let ts_y = id / tileset.columns; diff --git a/examples/ggez/res_reader.rs b/examples/ggez/res_reader.rs index 2b7af552..d4cc3689 100644 --- a/examples/ggez/res_reader.rs +++ b/examples/ggez/res_reader.rs @@ -2,8 +2,7 @@ use ggez::*; /// A resource reader that uses assets from the ggez filesystem. // Need to do newtype to implement ResourceReader for ggez's filesystem -// FIXME: This would greatly improve with separated subcontexts (ggez 0.8.0) -pub struct GgezResourceReader<'ctx>(pub &'ctx mut ggez::Context); +pub struct GgezResourceReader<'ctx>(pub &'ctx mut ggez::filesystem::Filesystem); impl tiled::ResourceReader for GgezResourceReader<'_> { type Resource = filesystem::File; @@ -14,6 +13,6 @@ impl tiled::ResourceReader for GgezResourceReader<'_> { &mut self, path: &std::path::Path, ) -> std::result::Result { - filesystem::open(&self.0, path) + self.0.open(path) } } diff --git a/src/layers/tile/infinite.rs b/src/layers/tile/infinite.rs index 41e82100..9c896851 100644 --- a/src/layers/tile/infinite.rs +++ b/src/layers/tile/infinite.rs @@ -95,7 +95,7 @@ impl InfiniteTileLayerData { } /// Obtains a chunk's data by its position. To obtain the position of the chunk that contains a - /// tile, use [Chunk::tile_to_chunk_pos()]. + /// tile, use [`ChunkData::tile_to_chunk_pos()`]. /// /// In 99.99% of cases you'll want to use [`InfiniteTileLayer::get_chunk()`] instead; Using this method is only /// needed if you *only* require the tile data of the chunk (and no other utilities provided by @@ -136,7 +136,7 @@ impl ChunkData { /// /// If the position given is invalid or the position is empty, this function will return [`None`]. /// - /// If you want to get a [`LayerTile`](`crate::LayerTile`) instead, use [`ChunkWrapper::get_tile()`]. + /// If you want to get a [`LayerTile`](`crate::LayerTile`) instead, use [`Chunk::get_tile()`]. pub fn get_tile_data(&self, x: i32, y: i32) -> Option<&LayerTileData> { if x < Self::WIDTH as i32 && y < Self::HEIGHT as i32 && x >= 0 && y >= 0 { self.tiles[x as usize + y as usize * Self::WIDTH as usize].as_ref() @@ -231,7 +231,7 @@ impl<'map> InfiniteTileLayer<'map> { /// Returns an iterator over different parts of this map called [`Chunk`]s. /// /// These **may not** correspond with the chunks in the TMX file, as the chunk size is - /// implementation defined (see [`Chunk::WIDTH`], [`Chunk::HEIGHT`]). + /// implementation defined (see [`ChunkData::WIDTH`], [`ChunkData::HEIGHT`]). /// /// The iterator item contains the position of the chunk in chunk coordinates along with a /// reference to the actual chunk at that position. @@ -276,7 +276,7 @@ impl<'map> InfiniteTileLayer<'map> { } /// Obtains a chunk by its position. To obtain the position of the chunk that contains a tile, - /// use [Chunk::tile_to_chunk_pos]. + /// use [`ChunkData::tile_to_chunk_pos()`]. #[inline] pub fn get_chunk(&self, x: i32, y: i32) -> Option> { let map: &'map crate::Map = self.map; diff --git a/src/layers/tile/util.rs b/src/layers/tile/util.rs index 486134fb..6e33f60f 100644 --- a/src/layers/tile/util.rs +++ b/src/layers/tile/util.rs @@ -16,10 +16,10 @@ pub(crate) fn parse_data_line( (Some("base64"), None) => parse_base64(parser).map(|v| convert_to_tiles(&v, tilesets)), (Some("base64"), Some("zlib")) => parse_base64(parser) - .and_then(|data| process_decoder(libflate::zlib::Decoder::new(&data[..]))) + .and_then(|data| process_decoder(Ok(flate2::bufread::ZlibDecoder::new(&data[..])))) .map(|v| convert_to_tiles(&v, tilesets)), (Some("base64"), Some("gzip")) => parse_base64(parser) - .and_then(|data| process_decoder(libflate::gzip::Decoder::new(&data[..]))) + .and_then(|data| process_decoder(Ok(flate2::bufread::GzDecoder::new(&data[..])))) .map(|v| convert_to_tiles(&v, tilesets)), #[cfg(feature = "zstd")] (Some("base64"), Some("zstd")) => parse_base64(parser)