save/load: Impl. save/load for wasm

Kind of messy, could use some cleanup
This commit is contained in:
Stephen Seo 2024-02-15 19:11:56 +09:00
parent a2597a1c5c
commit 1cb833b987
5 changed files with 180 additions and 1 deletions

View file

@ -2,6 +2,7 @@ pub mod agnostic_interface;
mod faux_quicksilver;
mod original_impl;
mod shaders;
mod wasm_helpers;
use agnostic_interface::raylib_impl::RaylibGame;
use faux_quicksilver::Window;

View file

@ -3,6 +3,8 @@ use std::{fs::File, io::Result as IOResult, path::PathBuf, str::FromStr};
use crate::agnostic_interface::CameraInterface;
use crate::faux_quicksilver::{Circle, Color, Rectangle, Transform, Vector, Window};
use rand::prelude::*;
use std::os::raw::{c_int, c_void};
use std::sync::mpsc::{Receiver, TryRecvError};
const WIDTH_F: f32 = 800.0;
const HEIGHT_F: f32 = 600.0;
@ -1899,6 +1901,8 @@ pub struct GameState {
camera: Box<dyn CameraInterface>,
move_to: Vector,
save_load_notification: Option<SaveLoadNotification>,
#[cfg(target_family = "wasm")]
load_recv: Option<Receiver<Vec<u8>>>,
}
impl GameState {
@ -2028,6 +2032,8 @@ impl GameState {
camera,
move_to: Vector::new(400.0, 300.0),
save_load_notification: None,
#[cfg(target_family = "wasm")]
load_recv: None,
})
}
@ -2498,7 +2504,6 @@ impl GameState {
match sl {
SaveLoadNotification::Save { text, timer } => {
*timer -= dt;
println!("save, timer is {}", timer);
if *timer <= 0.0 {
self.save_load_notification = None;
} else if text.is_none() {
@ -2520,6 +2525,57 @@ impl GameState {
fish.update(dt);
}
#[cfg(target_family = "wasm")]
if let Some(rx) = &mut self.load_recv {
let recv_result = rx.try_recv();
if let Ok(v) = recv_result {
if v.is_empty() {
self.save_load_notification = Some(SaveLoadNotification::Load {
text: Some(String::from("Failed to load! (callback failure)")),
timer: SL_NOTIF_TIME,
});
} else {
let des_result = SaveData::deserialize(&v);
if let Ok((save_data, _)) = des_result {
self.planets = save_data.planets;
self.stars = save_data.stars;
self.fishes = save_data.fishes;
self.player = save_data.player;
self.joining_particles = save_data.joining_particles;
self.expl_conv_p_systems.clear();
self.move_to = Vector::new(self.player.x, self.player.y);
self.camera
.set_view_xy(
self.player.x - WIDTH_F / 2.0,
self.player.y - HEIGHT_F / 2.0,
)
.ok();
self.dbl_click_timeout = None;
self.click_time = None;
self.click_release_time = DOUBLE_CLICK_TIME;
self.state = 10;
self.state_dirty = true;
self.save_load_notification = Some(SaveLoadNotification::Load {
text: None,
timer: SL_NOTIF_TIME,
});
} else {
self.save_load_notification = Some(SaveLoadNotification::Load {
text: Some(String::from("Failed to load! (parse issue)")),
timer: SL_NOTIF_TIME,
});
}
}
self.load_recv = None;
} else if recv_result == Err(TryRecvError::Disconnected) {
self.save_load_notification = Some(SaveLoadNotification::Load {
text: Some(String::from("Failed to load! (sender disconnected)")),
timer: SL_NOTIF_TIME,
});
self.load_recv = None;
}
}
Ok(())
}
@ -2703,11 +2759,34 @@ impl GameState {
#[cfg(target_family = "wasm")]
pub fn save(&mut self) -> IOResult<()> {
let save_bytes = SaveData {
planets: self.planets.clone(),
stars: self.stars.clone(),
fishes: self.fishes.clone(),
player: self.player,
joining_particles: self.joining_particles.clone(),
}
.serialize();
crate::wasm_helpers::save_data(&save_bytes)?;
self.save_load_notification = Some(SaveLoadNotification::Save {
text: None,
timer: SL_NOTIF_TIME,
});
Ok(())
}
#[cfg(target_family = "wasm")]
pub fn load(&mut self) -> IOResult<()> {
let receiver = crate::wasm_helpers::load_data()?;
self.load_recv = Some(receiver);
self.save_load_notification = Some(SaveLoadNotification::Save {
text: Some(String::from("Loading...")),
timer: SL_NOTIF_TIME,
});
Ok(())
}
}

61
src/wasm_helpers.rs Normal file
View file

@ -0,0 +1,61 @@
use std::os::raw::*;
use std::sync::mpsc::{channel, Receiver, Sender};
#[cfg(not(target_family = "wasm"))]
pub fn save_data(data: &[u8]) -> std::io::Result<()> {
Err(std::io::Error::other("Unimplemented for native"))
}
#[cfg(not(target_family = "wasm"))]
pub fn load_data() -> std::io::Result<Receiver<Vec<u8>>> {
Err(std::io::Error::other("Unimplemented for native"))
}
#[cfg(target_family = "wasm")]
#[no_mangle]
pub extern "C" fn ld45_load_rust_handler(usr: *mut c_void, data: *const c_void, len: c_int) {
let mut sender_box: Box<Sender<Vec<u8>>> =
unsafe { Box::from_raw(usr as *mut Sender<Vec<u8>>) };
if data.is_null() || len == 0 {
(*sender_box).send(Vec::new()).ok();
drop(sender_box);
println!("callback: Failed to load data!");
return;
}
let v: Vec<u8> =
unsafe { std::slice::from_raw_parts(data as *const u8, len as usize).to_owned() };
(*sender_box).send(v).ok();
println!("callback: Loaded data!");
drop(sender_box);
}
#[cfg(target_family = "wasm")]
extern "C" {
fn ld45_save_async(data: *const c_void, length: c_int);
fn ld45_load_async(usr: *const c_void);
}
#[cfg(target_family = "wasm")]
pub fn save_data(data: &[u8]) -> std::io::Result<()> {
unsafe {
ld45_save_async(data as *const [u8] as *const c_void, data.len() as c_int);
}
Ok(())
}
#[cfg(target_family = "wasm")]
pub fn load_data() -> std::io::Result<Receiver<Vec<u8>>> {
let (tx, rx) = channel();
let mut handler = Box::new(tx);
unsafe {
let mut ptr = Box::into_raw(handler);
ld45_load_async(ptr as *mut c_void);
}
Ok(rx)
}

View file

@ -5,4 +5,8 @@ extern void *ld45_initialize();
extern void ld45_iterate(void *context);
extern void ld45_save_async(void *data, int length);
extern void ld45_load_async(void *usr);
extern void ld45_load_rust_handler(void *usr, void *data, int len);
#endif

View file

@ -5,6 +5,40 @@
#include <stdio.h>
void ld45_saved_result_ok(void *usr) {
puts("Save OK");
}
void ld45_saved_result_err(void *usr) {
puts("Save ERR");
}
void ld45_load_result_ok(void *usr, void *data, int len) {
ld45_load_rust_handler(usr, data, len);
}
void ld45_load_result_err(void *usr) {
ld45_load_rust_handler(usr, NULL, 0);
}
void ld45_save_async(void *data, int length) {
emscripten_idb_async_store("ld45_oneandall_db",
"savedata",
data,
length,
NULL,
ld45_saved_result_ok,
ld45_saved_result_err);
}
void ld45_load_async(void *usr) {
emscripten_idb_async_load("ld45_oneandall_db",
"savedata",
usr,
ld45_load_result_ok,
ld45_load_result_err);
}
void main_loop(void *ud) {
ld45_iterate(ud);
}