M Cargo.lock => Cargo.lock +16 -15
@@ 281,13 281,13 @@ checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae"
[[package]]
name = "async-trait"
-version = "0.1.68"
+version = "0.1.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
+checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.22",
+ "syn 2.0.28",
]
[[package]]
@@ 698,7 698,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.22",
+ "syn 2.0.28",
]
[[package]]
@@ 906,7 906,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.22",
+ "syn 2.0.28",
]
[[package]]
@@ 1604,6 1604,7 @@ name = "little_town"
version = "0.2.3"
dependencies = [
"async-std",
+ "async-trait",
"directories-next",
"futures",
"libp2p",
@@ 2111,7 2112,7 @@ checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.22",
+ "syn 2.0.28",
]
[[package]]
@@ 2352,9 2353,9 @@ dependencies = [
[[package]]
name = "quote"
-version = "1.0.28"
+version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
+checksum = "5907a1b7c277254a8b15170f6e7c97cfa60ee7872a3217663bb81151e48184bb"
dependencies = [
"proc-macro2",
]
@@ 2787,9 2788,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.22"
+version = "2.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616"
+checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
dependencies = [
"proc-macro2",
"quote",
@@ 2846,7 2847,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.22",
+ "syn 2.0.28",
]
[[package]]
@@ 2936,7 2937,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.22",
+ "syn 2.0.28",
]
[[package]]
@@ 3138,7 3139,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
- "syn 2.0.22",
+ "syn 2.0.28",
"wasm-bindgen-shared",
]
@@ 3172,7 3173,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.22",
+ "syn 2.0.28",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ 3420,5 3421,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.22",
+ "syn 2.0.28",
]
M Cargo.toml => Cargo.toml +1 -0
@@ 29,3 29,4 @@ futures = { version = "0.3.26", optional = true }
async-std = { version = "1.12.0", optional = true }
quad-rand = { version = "0.2.1", optional = true }
quad-snd = "0.2.7"
+async-trait = "0.1.73"
M README.md => README.md +4 -4
@@ 97,9 97,9 @@ or you don't want to use the default features,
you can follow this guide to build the binary yourself.
### Dependencies
-- [rustup](https://www.rust-lang.org/tools/install), `cargo` and `rust` nightly
-- `alsa-lib-dev` (or `libasound` depending on your distro) on linux
-- `protoc` on linux, with `multiplayer` feature enabled
+- [rustup](https://www.rust-lang.org/tools/install), `cargo` and `rust`
+- `alsa-lib-dev` (or `libasound` depending on your distro) on Linux
+- `protoc` on Linux, with `multiplayer` feature enabled
### Steps
1. Clone this repo:
```bash
@@ 129,6 129,6 @@ See below or a detailed list:
The Background music has been obtained from [pixabay](https://pixabay.com/music/solo-piano-raining-ambient-calm-piano-music-loop-111521/).
The song is called *Raining - Ambient Calm Piano Music*,
-and has been comosed by *HarumachiMusic*.
+and has been composed by *HarumachiMusic*.
According to the [pixabey license](https://pixabay.com/service/license/),
it may be used for personal and commercial use.
M src/game/mod.rs => src/game/mod.rs +3 -0
@@ 4,6 4,8 @@ pub mod inventory;
pub mod types;
pub mod world;
+use async_trait::async_trait;
+
use crate::screens::Screen;
use crate::textures::AssetStore;
@@ 12,6 14,7 @@ use crate::textures::AssetStore;
/// and has a handler for the event loop
/// NOTE: draw and `ev_loop` are tehcnically the same,
/// but the serve different purposes
+#[async_trait]
pub trait GameComponent {
/// Callback used to redraw the component
async fn draw(&self, _assets: &AssetStore) {}
M src/game/world.rs => src/game/world.rs +19 -18
@@ 101,13 101,14 @@ impl World {
let mut builder = Vec::new();
for entry in rd.by_ref().flatten() {
- if let Ok(ftype) = entry.file_type() && ftype.is_file() {
- if let Some(name) = entry.file_name().to_str() {
- builder.push(name.to_string());
+ if let Ok(ftype) = entry.file_type() {
+ if ftype.is_file() {
+ if let Some(name) = entry.file_name().to_str() {
+ builder.push(name.to_string());
+ }
}
}
}
-
return builder;
}
}
@@ 117,24 118,24 @@ impl World {
/// tries loading the world from a file
pub fn from_disk(name: &str) -> Option<Self> {
let existing_maps = Self::get_list();
- if let Some(dir) = get_data_dir() && existing_maps.contains(&name.to_string()) {
- if let Ok(file) = File::open(Path::new(&dir).join(name)) {
- let mut buf_reader = BufReader::new(file);
- let mut contents = String::new();
- if buf_reader.read_to_string(&mut contents).is_ok() {
- match Self::deserialize_json(&contents) {
- Ok(bscr) => return Some(bscr),
- Err(e) => println!("{e:?}")
+ if let Some(dir) = get_data_dir() {
+ if existing_maps.contains(&name.to_string()) {
+ if let Ok(file) = File::open(Path::new(&dir).join(name)) {
+ let mut buf_reader = BufReader::new(file);
+ let mut contents = String::new();
+ if buf_reader.read_to_string(&mut contents).is_ok() {
+ match Self::deserialize_json(&contents) {
+ Ok(bscr) => return Some(bscr),
+ Err(e) => {
+ println!("{e:?}");
+ return None;
+ }
+ }
}
- } else {
- println!("Faield to read file");
}
- } else {
- println!("Failed to open file");
}
- } else {
- println!("World not found");
}
+ println!("Unable to open world");
None
}
M src/main.rs => src/main.rs +0 -6
@@ 1,9 1,3 @@
-#![feature(int_roundings)]
-#![feature(let_chains)]
-#![feature(async_fn_in_trait)]
-#![feature(associated_type_defaults)]
-#![feature(inherent_associated_types)]
-
use macroquad::prelude::*;
mod screens;
M src/screens/build.rs => src/screens/build.rs +14 -4
@@ 8,6 8,7 @@ use crate::game::{
};
use crate::textures::AssetStore;
use crate::ui::{ButtonEvent, CenteredText, ItemFrame, TextButton, Widget};
+use async_trait::async_trait;
use macroquad::prelude::*;
#[cfg(feature = "multiplayer")]
@@ 95,7 96,9 @@ impl InventoryWidgets {
let title = inv.category.get_name();
self.widget_categories.set_text(title);
let list = inv.category.get_blocks();
- let page_count = list.len().div_ceil(9);
+ let page_count = (list.len() + 9 - 1) / 9;
+ // NOTE: once div_ceil is stable, we can use
+ // let page_count = list.len().div_ceil(9);
self.widget_page_indicator
.set_text(&format!("{}/{}", self.page + 1, page_count));
self.widget_categories.set_text(title);
@@ 106,7 109,9 @@ impl InventoryWidgets {
fn load_page(&mut self, page: usize, inv: &Inventory) {
self.page = page;
let list = inv.category.get_blocks();
- let max_page = list.len().div_ceil(9);
+ let max_page = (list.len() + 9 - 1) / 9;
+ // NOTE: once div_ceil is stable, we can use
+ //let max_page = list.len().div_ceil(9);
for (i, slot) in self.widgets_slots.iter_mut().enumerate() {
if let Some(block) = list.get(i + page * 9) {
let amount = inv.contents.get(block).copied();
@@ 125,7 130,9 @@ impl InventoryWidgets {
if page > 0 {
page -= 1;
} else {
- page = list.len().div_ceil(9) - 1;
+ page = (list.len() + 9 - 1) / 9 - 1;
+ // NOTE: once div_ceil is stable, we can use
+ // page = list.len().div_ceil(9) - 1;
}
self.load_page(page, inv);
}
@@ 133,7 140,9 @@ impl InventoryWidgets {
fn page_up(&mut self, inv: &Inventory) {
let list = inv.category.get_blocks();
let mut page = self.page;
- let max_page = list.len().div_ceil(9);
+ let max_page = (list.len() + 9 - 1) / 9;
+ // NOTE: once div_ceil is stable, we can use
+ //let max_page = list.len().div_ceil(9);
if page + 1 >= max_page {
page = 0;
} else {
@@ 216,6 225,7 @@ impl BuildScreen {
}
}
}
+#[async_trait]
impl GameComponent for BuildScreen {
async fn draw(&self, assets: &AssetStore) {
if let Some(world) = &self.world {
M src/screens/map_creator.rs => src/screens/map_creator.rs +2 -0
@@ 3,6 3,7 @@ use super::map_select::SelectScreen;
use super::Screen;
use crate::game::{world::World, GameComponent, GameEvent};
use crate::textures::AssetStore;
+use async_trait::async_trait;
use macroquad::prelude::*;
use crate::ui::{ButtonEvent, SelectableText, TextButton, Widget};
@@ 33,6 34,7 @@ impl MapCreatorScreen {
}
}
}
+#[async_trait]
impl GameComponent for MapCreatorScreen {
async fn draw(&self, assets: &AssetStore) {
{
M src/screens/map_select.rs => src/screens/map_select.rs +11 -4
@@ 2,6 2,7 @@ use super::{build::BuildScreen, map_creator::MapCreatorScreen, Screen};
use crate::game::{world::World, GameComponent, GameEvent};
use crate::textures::AssetStore;
use crate::ui::{ButtonEvent, SelectableText, TextButton, Widget};
+use async_trait::async_trait;
use macroquad::prelude::*;
/// The map select screen
@@ 55,6 56,7 @@ impl SelectScreen {
}
}
}
+#[async_trait]
impl GameComponent for SelectScreen {
async fn draw(&self, assets: &AssetStore) {
{
@@ 124,10 126,15 @@ impl GameComponent for SelectScreen {
for slot in &mut self.level_select_widgets {
slot.set_selected(false);
if let ButtonEvent::LeftClick = slot.ev_loop() {
- if let Some(sel) = &self.selected && sel == slot.get_text() {
- // already selected
- // deselect
- self.selected = None;
+ if let Some(sel) = &self.selected {
+ if sel == slot.get_text() {
+ // already selected
+ // deselect
+ self.selected = None;
+ } else {
+ // user selected this item
+ self.selected = Some(slot.get_text().to_string());
+ }
} else {
// user selected this item
self.selected = Some(slot.get_text().to_string());
M src/screens/mod.rs => src/screens/mod.rs +2 -0
@@ 5,6 5,7 @@ mod welcome;
use crate::game::{GameComponent, GameEvent};
use crate::textures::AssetStore;
+use async_trait::async_trait;
use build::BuildScreen;
use map_creator::MapCreatorScreen;
use map_select::SelectScreen;
@@ 22,6 23,7 @@ pub enum Screen {
/// Map creation screen
Create(MapCreatorScreen),
}
+#[async_trait]
impl GameComponent for Screen {
async fn draw(&self, assets: &AssetStore) {
match self {
M src/screens/welcome.rs => src/screens/welcome.rs +2 -0
@@ 5,6 5,7 @@ use super::Screen;
use crate::game::{GameComponent, GameEvent};
use crate::textures::AssetStore;
use crate::ui::{ButtonEvent, CenteredText, TextButton, Widget};
+use async_trait::async_trait;
use macroquad::prelude::*;
/// The welcome screen
@@ 32,6 33,7 @@ impl WelcomeScreen {
}
}
}
+#[async_trait]
impl GameComponent for WelcomeScreen {
async fn draw(&self, assets: &AssetStore) {
{
M src/ui/centered_text.rs => src/ui/centered_text.rs +3 -0
@@ 1,5 1,6 @@
use super::Widget;
use crate::textures::AssetStore;
+use async_trait::async_trait;
use macroquad::prelude::*;
/// UI Widget
@@ 65,6 66,8 @@ impl CenteredText {
self.text = text.to_string();
}
}
+
+#[async_trait]
impl Widget for CenteredText {
type Event = bool;
async fn draw(&self, _assets: &AssetStore) {
M src/ui/item_frame.rs => src/ui/item_frame.rs +2 -0
@@ 2,6 2,7 @@ use super::{ButtonEvent, CenteredText, Widget};
use crate::game::blocks::Block;
use crate::game::types::Direction;
use crate::textures::AssetStore;
+use async_trait::async_trait;
use macroquad::prelude::*;
pub struct ItemFrame {
@@ 103,6 104,7 @@ impl ItemFrame {
self.direction = direction.clone();
}
}
+#[async_trait]
impl Widget for ItemFrame {
type Event = ButtonEvent;
fn get_dimensions(&self) -> (f32, f32) {
M src/ui/mod.rs => src/ui/mod.rs +2 -0
@@ 1,4 1,5 @@
use crate::textures::AssetStore;
+use async_trait::async_trait;
use macroquad::prelude::*;
mod centered_text;
@@ 23,6 24,7 @@ pub enum ButtonEvent {
}
/// A UI item that can be rendered
+#[async_trait]
pub trait Widget {
/// Message types returned from the event loop
type Event;
M src/ui/selectable_text.rs => src/ui/selectable_text.rs +2 -0
@@ 1,5 1,6 @@
use super::{ButtonEvent, Widget};
use crate::textures::AssetStore;
+use async_trait::async_trait;
use macroquad::prelude::*;
/// A widget similar to a button,
@@ 93,6 94,7 @@ impl SelectableText {
&self.text
}
}
+#[async_trait]
impl Widget for SelectableText {
type Event = ButtonEvent;
fn get_dimensions(&self) -> (f32, f32) {
M src/ui/text_button.rs => src/ui/text_button.rs +2 -0
@@ 1,5 1,6 @@
use super::{ButtonEvent, Widget};
use crate::textures::AssetStore;
+use async_trait::async_trait;
use macroquad::prelude::*;
/// UI Widget
@@ 65,6 66,7 @@ impl TextButton {
self.text = text.to_string();
}
}
+#[async_trait]
impl Widget for TextButton {
type Event = ButtonEvent;
fn get_dimensions(&self) -> (f32, f32) {