]> git.seodisparate.com - LudumDare45_StartWithNothing/commitdiff
save/load: Impl. save/load for wasm
authorStephen Seo <seo.disparate@gmail.com>
Thu, 15 Feb 2024 10:11:56 +0000 (19:11 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Thu, 15 Feb 2024 10:11:56 +0000 (19:11 +0900)
Kind of messy, could use some cleanup

src/lib.rs
src/original_impl.rs
src/wasm_helpers.rs [new file with mode: 0644]
wasm/include/ld45_lib.h
wasm/src/main.c

index ac930c8a788fd5e501d6859ac69e9791ebb8abc6..6ee4f1804ef3b4e7eb39822a907aae3b7c961d93 100644 (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;
index e8d950064919c2cb50400e83bc56060fff5b078e..48f533f4216865795e0aa2518ba6758445d7df90 100644 (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(())
     }
 }
diff --git a/src/wasm_helpers.rs b/src/wasm_helpers.rs
new file mode 100644 (file)
index 0000000..512559a
--- /dev/null
@@ -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)
+}
index cb87442fb12a7da02782675b24a3bc6053ec9873..f0ad3eb3effd2f6df9de49e3dafa3911b9be691a 100644 (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
index 83632972d05e29b6085acb8d64e6e4d076052156..d20261b85eede90b2d11115e8046404677626a1c 100644 (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);
 }