Skip to content

Commit

Permalink
Merge pull request #64 from louis-e/api-error-handling
Browse files Browse the repository at this point in the history
Graceful handling of API request errors
  • Loading branch information
louis-e authored Sep 21, 2024
2 parents 1056b89 + 755583d commit 0d48f60
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 12 deletions.
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

# Arnis [![CI Build Status](https://github.com/louis-e/arnis/actions/workflows/ci-build.yml/badge.svg)](https://github.com/louis-e/arnis/actions)

This open source project written in Rust generates any chosen location from the real world in Minecraft with a high level of detail.
This open source project written in Rust generates any chosen location from the real world in Minecraft Java Edition with a high level of detail.

## :desktop_computer: Example
<img width="700" height="400" src="https://github.com/louis-e/arnis/blob/main/gitassets/mc.gif?raw=true">
![Minecraft Preview](https://github.com/louis-e/arnis/blob/main/gitassets/mc.gif?raw=true)

By leveraging geospatial data from OpenStreetMap and utilizing the powerful capabilities of Rust, Arnis provides an efficient and robust solution for creating complex and accurate Minecraft worlds that reflect real-world geography and architecture.

Expand All @@ -33,7 +33,7 @@ Get the [latest release](https://github.com/louis-e/arnis/releases/) or [compile
### How to find your bbox coordinates
Use http://bboxfinder.com/ to draw a rectangle of your wanted area. Then copy the four box coordinates as shown below and use them as the input for the --bbox parameter.
![How to find area](https://github.com/louis-e/arnis/blob/main/gitassets/bbox-finder.png?raw=true)
The world will always be generated starting from the coordinates 0 0 0.
The world will always be generated starting from the coordinates 0 0 0. Try starting with a small area since large areas take a lot of computing power and time to process.

Manually generate a new Minecraft world (preferably a flat world) before running the script.
The --bbox parameter specifies the bounding box coordinates in the format: min_lng,min_lat,max_lng,max_lat.
Expand All @@ -52,6 +52,8 @@ The project is named after the smallest city in Germany, Arnis[^2]. The city's s
## :memo: ToDo and Known Bugs
Feel free to choose an item from the To-Do or Known Bugs list, or bring your own idea to the table. Bug reports shall be raised as a Github issue. Contributions are highly welcome and appreciated!
- [ ] Design and implement a GUI
- [ ] Memory optimization
- [ ] Fix Github Action Workflow for releasing Linux & MacOS Binary
- [ ] Evaluate and implement multithreaded region saving
- [ ] Better code documentation
- [ ] Implement house roof types
Expand All @@ -64,6 +66,7 @@ Feel free to choose an item from the To-Do or Known Bugs list, or bring your own
- [ ] Add interior to buildings
- [ ] Evaluate and implement elevation
- [ ] Generate a few big cities using high performance hardware and make them available to download
- [ ] Implement memory mapped storing of chunks to reduce memory usage
- [x] Fix faulty empty chunks ([https://github.com/owengage/fastnbt/issues/120](https://github.com/owengage/fastnbt/issues/120)) (workaround found)

## :trophy: Open Source
Expand Down
57 changes: 48 additions & 9 deletions src/retrieve_data.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,40 @@
use colored::Colorize;
use rand::seq::SliceRandom;
use reqwest::blocking::Client;
use reqwest::blocking::ClientBuilder;
use serde_json::Value;
use std::fs::File;
use std::io::{self, BufReader, Write};
use std::process::Command;
use rand::seq::SliceRandom;
use std::time::Duration;

/// Function to download data using the `reqwest` crate
/// Function to download data using reqwest
fn download_with_reqwest(url: &str, query: &str) -> Result<String, Box<dyn std::error::Error>> {
let client: Client = Client::new();
let response: String = client.get(url).query(&[("data", query)]).send()?.text()?;
Ok(response)
let client: Client = ClientBuilder::new()
.timeout(Duration::from_secs(1800))
.build()?;

let response = client.get(url)
.query(&[("data", query)])
.send();

match response {
Ok(resp) => {
if resp.status().is_success() {
Ok(resp.text()?)
} else {
Err(format!("Error! Received response code: {}", resp.status()).into())
}
},
Err(e) => {
if e.is_timeout() {
eprintln!("{}", "Error! Request timed out. Try selecting a smaller area.".red().bold());
} else {
eprintln!("{}", format!("Error! {}", e).red().bold());
}
std::process::exit(1);
}
}
}

/// Function to download data using `curl`
Expand All @@ -19,7 +43,7 @@ fn download_with_curl(url: &str, query: &str) -> io::Result<String> {
.arg("-s") // Add silent mode to suppress output
.arg(format!("{}?data={}", url, query))
.output()?;

if !output.status.success() {
Err(io::Error::new(io::ErrorKind::Other, "Curl command failed"))
} else {
Expand All @@ -33,7 +57,7 @@ fn download_with_wget(url: &str, query: &str) -> io::Result<String> {
.arg("-qO-") // Use `-qO-` to output the result directly to stdout
.arg(format!("{}?data={}", url, query))
.output()?;

if !output.status.success() {
Err(io::Error::new(io::ErrorKind::Other, "Wget command failed"))
} else {
Expand Down Expand Up @@ -62,7 +86,7 @@ pub fn fetch_data(

// Generate Overpass API query for bounding box
let query: String = format!(
r#"[out:json][bbox:{},{},{},{}];
r#"[out:json][timeout:1800][bbox:{},{},{},{}];
(
nwr["building"];
nwr["highway"];
Expand Down Expand Up @@ -104,7 +128,22 @@ pub fn fetch_data(
let data: Value = serde_json::from_str(&response)?;

if data["elements"].as_array().map_or(0, |elements: &Vec<Value>| elements.len()) == 0 {
println!("Error! No data available");
if let Some(remark) = data["remark"].as_str() {
// Check if the remark mentions memory or other runtime errors
if remark.contains("runtime error") && remark.contains("out of memory") {
eprintln!("{}", "Error! The query ran out of memory on the Overpass API server. Try using a smaller area.".red().bold());
} else {
// Handle other Overpass API errors if present in the remark field
eprintln!("{}", format!("Error! API returned: {}", remark).red().bold());
}
} else {
// General case for when there are no elements and no specific remark
eprintln!("{}", "Error! No data available.".red().bold());
}

if debug {
println!("Additional debug information: {}", data);
}
std::process::exit(1);
}

Expand Down

0 comments on commit 0d48f60

Please sign in to comment.