// Browser integration via our own miniquad JS plugin (www/dc_plugin.js): // persistent key/value storage (localStorage), a cache that is written to storage when the // page is hidden or closed, file download, file upload and HTTP requests. // Native builds use simple fallbacks. // Result of an HTTP request: status 0 means the service could not be reached pub struct HttpResult { pub status: i32, pub body: Vec, } #[cfg(target_arch = "wasm32")] mod imp { extern "C" { fn dc_storage_get(key: *const u8, key_len: u32) -> i32; fn dc_storage_take(dst: *mut u8); fn dc_storage_set(key: *const u8, key_len: u32, val: *const u8, val_len: u32); fn dc_cache_set(key: *const u8, key_len: u32, val: *const u8, val_len: u32); fn dc_download(name: *const u8, name_len: u32, data: *const u8, data_len: u32); fn dc_open_file(); fn dc_file_poll() -> i32; fn dc_file_take(dst: *mut u8); fn dc_http(m: *const u8, ml: u32, u: *const u8, ul: u32, b: *const u8, bl: u32) -> u32; fn dc_http_poll(id: u32) -> i32; fn dc_http_len(id: u32) -> u32; fn dc_http_take(id: u32, dst: *mut u8); } // starts a request; poll the returned id with http_poll() pub fn http(method: &str, url: &str, body: &[u8]) -> u32 { unsafe { dc_http(method.as_ptr(), method.len() as u32, url.as_ptr(), url.len() as u32, body.as_ptr(), body.len() as u32) } } pub fn http_poll(id: u32) -> Option { unsafe { let status = dc_http_poll(id); if status == -2 { return None; } let mut body = vec![0u8; dc_http_len(id) as usize]; dc_http_take(id, body.as_mut_ptr()); Some(super::HttpResult { status, body }) } } pub fn storage_get(key: &str) -> Option { unsafe { let n = dc_storage_get(key.as_ptr(), key.len() as u32); if n < 0 { return None; } let mut buf = vec![0u8; n as usize]; dc_storage_take(buf.as_mut_ptr()); String::from_utf8(buf).ok() } } pub fn storage_set(key: &str, val: &str) { unsafe { dc_storage_set(key.as_ptr(), key.len() as u32, val.as_ptr(), val.len() as u32) } } // value is written to storage by JS when the page gets hidden or closed pub fn cache_on_leave(key: &str, val: &str) { unsafe { dc_cache_set(key.as_ptr(), key.len() as u32, val.as_ptr(), val.len() as u32) } } pub fn download(name: &str, data: &[u8]) { unsafe { dc_download(name.as_ptr(), name.len() as u32, data.as_ptr(), data.len() as u32) } } // opens the browser file picker; poll with take_file() pub fn open_file() { unsafe { dc_open_file() } } pub fn take_file() -> Option> { unsafe { let n = dc_file_poll(); if n < 0 { return None; } let mut buf = vec![0u8; n as usize]; dc_file_take(buf.as_mut_ptr()); Some(buf) } } } #[cfg(not(target_arch = "wasm32"))] mod imp { use std::cell::RefCell; use std::collections::HashMap; thread_local! { static STORE: RefCell> = RefCell::new(HashMap::new()); } pub fn storage_get(key: &str) -> Option { STORE.with(|s| s.borrow().get(key).cloned()) } pub fn storage_set(key: &str, val: &str) { STORE.with(|s| s.borrow_mut().insert(key.to_string(), val.to_string())); } pub fn cache_on_leave(key: &str, val: &str) { storage_set(key, val); } pub fn download(name: &str, data: &[u8]) { let _ = std::fs::write(name, data); } pub fn open_file() {} pub fn take_file() -> Option> { None } // no savegame service in native builds: every request fails like an unreachable server pub fn http(_method: &str, _url: &str, _body: &[u8]) -> u32 { 0 } pub fn http_poll(_id: u32) -> Option { Some(super::HttpResult { status: 0, body: Vec::new() }) } } pub use imp::*; // Lets the JS plugin loader know this crate uses the "dungeoncraft" plugin #[no_mangle] pub extern "C" fn dungeoncraft_crate_version() -> u32 { 1 }