Browse Source

Roughly save and load to flat file JSON

Apollo 8 months ago
parent
commit
c863c64ac9
8 changed files with 732 additions and 0 deletions
  1. 1 0
      .gitignore
  2. 380 0
      Cargo.lock
  3. 4 0
      Cargo.toml
  4. 73 0
      src/fs_util.rs
  5. 8 0
      src/lib.rs
  6. 16 0
      src/main.rs
  7. 99 0
      src/object.rs
  8. 151 0
      src/types.rs

+ 1 - 0
.gitignore

@@ -1,3 +1,4 @@
 /target
 .vscode/
+data/
 

+ 380 - 0
Cargo.lock

@@ -0,0 +1,380 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "ahash"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
+dependencies = [
+ "cfg-if",
+ "const-random",
+ "getrandom",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "bcrypt"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e65938ed058ef47d92cf8b346cc76ef48984572ade631927e9937b5ffc7662c7"
+dependencies = [
+ "base64",
+ "blowfish",
+ "getrandom",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "bitflags"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+
+[[package]]
+name = "blowfish"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7"
+dependencies = [
+ "byteorder",
+ "cipher",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "cipher"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+dependencies = [
+ "crypto-common",
+ "inout",
+]
+
+[[package]]
+name = "const-random"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
+dependencies = [
+ "const-random-macro",
+]
+
+[[package]]
+name = "const-random-macro"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "tiny-keccak",
+]
+
+[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "inout"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
+[[package]]
+name = "libc"
+version = "0.2.155"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+
+[[package]]
+name = "moonmud"
+version = "0.1.0"
+dependencies = [
+ "bcrypt",
+ "rhai",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rhai"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61797318be89b1a268a018a92a7657096d83f3ecb31418b9e9c16dcbb043b702"
+dependencies = [
+ "ahash",
+ "bitflags",
+ "instant",
+ "num-traits",
+ "once_cell",
+ "rhai_codegen",
+ "smallvec",
+ "smartstring",
+ "thin-vec",
+]
+
+[[package]]
+name = "rhai_codegen"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+
+[[package]]
+name = "serde"
+version = "1.0.204"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.204"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.120"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
+[[package]]
+name = "smartstring"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29"
+dependencies = [
+ "autocfg",
+ "static_assertions",
+ "version_check",
+]
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
+[[package]]
+name = "syn"
+version = "2.0.71"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thin-vec"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b"
+
+[[package]]
+name = "tiny-keccak"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
+dependencies = [
+ "crunchy",
+]
+
+[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "zerocopy"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"

+ 4 - 0
Cargo.toml

@@ -4,3 +4,7 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
+bcrypt = "0.15.1"
+rhai = "1.19.0"
+serde = { version = "1.0.204", features = ["derive"] }
+serde_json = "1.0.120"

+ 73 - 0
src/fs_util.rs

@@ -0,0 +1,73 @@
+use std::{fs, path};
+
+pub fn join(path: &String) -> String {
+    let p = path::PathBuf::from(path);
+    p.to_string_lossy().to_string()
+}
+
+pub fn file_exists(filename: &String) -> bool {
+    let meta = fs::metadata(filename);
+    match meta {
+        Ok(meta) => {
+            meta.is_file() || meta.is_symlink()
+        }
+        Err(_) => false
+    }
+}
+
+pub fn dir_exists(dirname: &String) -> bool {
+    let meta = fs::metadata(dirname);
+    match meta {
+        Ok(meta) => {
+            meta.is_dir()
+        }
+        Err(_) => false
+    }
+}
+
+pub fn read_file(filename: &String) -> String {
+    let content = fs::read_to_string(filename);
+    match content {
+        Ok(content) => {
+            content
+        }
+        Err(_) => String::new()
+    }
+}
+
+pub fn read_dir(dirname: &String, include_dirs: bool) -> Vec<String> {
+    let mut result: Vec<String> = Vec::new();
+    let dir = fs::read_dir(dirname);
+    match dir {
+        Ok(dir) => {
+            for entry in dir {
+                if let Ok(entry) = entry {
+                    let path = entry.path();
+                    if path.is_dir() && include_dirs {
+                        result.push(format!("{}/", path.to_string_lossy().to_string()));
+                    } else if path.is_file() || path.is_symlink() {
+                        result.push(path.to_string_lossy().to_string());
+                    }
+                }
+            }
+            result
+        }
+        Err(_) => Vec::new()
+    }
+}
+
+pub fn write_file(filename: &String, contents: &String) -> bool {
+    let ok = fs::write(filename, contents);
+    match ok {
+        Ok(_) => true,
+        Err(_) => false
+    }
+}
+
+pub fn mkdir(dirname: &String) -> bool {
+    let ok = fs::create_dir_all(dirname);
+    match ok {
+        Ok(_) => true,
+        Err(_) => false
+    }
+}

+ 8 - 0
src/lib.rs

@@ -0,0 +1,8 @@
+
+mod fs_util;
+
+mod types;
+pub use types::{Id, Kind, Inventory, Valid, next_id};
+
+mod object;
+pub use object::Object;

+ 16 - 0
src/main.rs

@@ -1,3 +1,19 @@
+
+use moonmud::*;
+
 fn main() {
     println!("Hello, world!");
+    let mut o: Object = Object::default();
+    o.id = next_id();
+    o.kind = Kind::Player;
+    o.name = "Test Player".to_string();
+    o.set_password(&"12345".to_string());
+    o.health = 10;
+    o.max_health = 10;
+    o.build = 1;
+    o.max_build = 6;
+    println!("{:?}", o);
+    if !o.save() {
+        println!("Err while saving?");
+    }
 }

+ 99 - 0
src/object.rs

@@ -0,0 +1,99 @@
+use serde::{Deserialize, Serialize};
+
+use crate::{Id, Inventory, Kind, Valid};
+
+#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Default)]
+pub struct Object {
+    // Required Fields
+    pub id: Id,
+    pub kind: Kind,
+    pub name: String,
+
+    // Optional Fields
+    pub password: String,
+    pub owner: Id,
+    pub location: Id,
+    pub health: u64,
+    pub max_health: u64,
+    pub magic: u64,
+    pub max_magic: u64,
+    pub build: u64,
+    pub max_build: u64,
+    pub inv: Inventory
+}
+
+impl Valid for Object {
+    fn valid(&self) -> bool {
+        if self.kind == Kind::Player {
+            if self.password.is_empty() {
+                return false;
+            }
+        }
+        self.id.valid() && self.kind.valid() && !self.name.is_empty()
+    }
+}
+
+impl Object {
+    pub fn set_password(&mut self, password: &String) -> bool {
+        let hash = bcrypt::hash(password, bcrypt::DEFAULT_COST);
+        match hash {
+            Ok(hash) => {
+                self.password = hash;
+                true
+            }
+            Err(_) => false
+        }
+    }
+    pub fn verify_password(&self, password: &String) -> bool {
+        let verify = bcrypt::verify(password, self.password.as_str());
+        match verify {
+            Ok(ok) => ok,
+            Err(_) => false
+        }
+    }
+
+    pub fn load_file(filename: &String) -> Option<Object> {
+        let p = crate::fs_util::join(&format!("{}", filename));
+        let cont = crate::fs_util::read_file(&p);
+        if cont.is_empty() {
+            eprintln!("Err reading '{}'", p);
+            return None;
+        }
+        let obj: Result<Object, serde_json::Error> = serde_json::from_str(cont.as_str());
+        match obj {
+            Ok(obj) => Some(obj),
+            Err(_) => {
+                eprintln!("Err parsing '{}'", p);
+                None
+            }
+        }
+    }
+    pub fn save(&mut self) -> bool {
+        if !self.valid() {
+            eprintln!("Object not valid, won't save");
+            return false;
+        }
+        let p = crate::fs_util::join(&format!("data/objects/{}.json", self.id));
+        let dir = crate::fs_util::join(&"data/objects".to_string());
+        if !crate::fs_util::dir_exists(&dir) {
+            eprintln!("Missing data/objects dir, creating... '{}'", dir);
+            if !crate::fs_util::mkdir(&dir) {
+                eprintln!("Err while making data/objects dir, '{}'", dir);
+            }
+        }
+        let cont = serde_json::to_string(self);
+        match cont {
+            Ok(cont) => {
+                if !crate::fs_util::write_file(&p, &cont) {
+                    eprintln!("Err while writing to '{}'", p);
+                    return false;
+                }
+                true
+            }
+            Err(_) => {
+                eprintln!("Err while serializing");
+                false
+            }
+        }
+    }
+}

+ 151 - 0
src/types.rs

@@ -0,0 +1,151 @@
+use std::collections::HashMap;
+
+use serde::{Deserialize, Serialize};
+
+use crate::{fs_util::{dir_exists, join, mkdir, read_dir}, object::Object};
+
+pub trait Valid {
+    fn valid(&self) -> bool;
+}
+
+pub type Id = u64;
+
+impl Valid for Id {
+    fn valid(&self) -> bool {
+        *self != 0
+    }
+}
+
+pub fn next_id() -> Id {
+    let p = join(&"data/objects".to_string());
+    let dir = read_dir(&p, false);
+    if dir.is_empty() {
+        if !dir_exists(&p) {
+            mkdir(&p);
+        }
+        return 1;
+    }
+    let mut last: Id = 1;
+    for entry in dir.iter() {
+        eprintln!("Trying '{}'", entry);
+        let obj = Object::load_file(entry);
+        match obj {
+            Some(obj) => {
+                eprintln!("OK");
+                if last < obj.id {
+                    last = obj.id;
+                }
+            }
+            None => {
+                eprintln!("ERR");
+            }
+        }
+    }
+    last + 1
+}
+
+#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
+pub enum Kind {
+    None,
+
+    Player,
+    Room,
+    Item,
+    Creature,
+    Vehicle
+}
+
+impl Valid for Kind {
+    fn valid(&self) -> bool {
+        !matches!(self, Kind::None)
+    }
+}
+
+impl Default for Kind {
+    fn default() -> Self {
+        Kind::None
+    }
+}
+
+#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Default)]
+pub struct Inventory {
+    data: HashMap<Id, u64>,
+    pub max_slots: u64
+}
+
+impl Valid for Inventory {
+    fn valid(&self) -> bool {
+        self.max_slots != 0
+    }
+}
+
+impl Inventory {
+    pub fn slots_used(&self) -> u64 {
+        self.data.len() as u64
+    }
+    pub fn slots_left(&self) -> u64 {
+        let used = self.slots_used();
+        if used > self.max_slots {
+            return 0;
+        }
+        self.max_slots - used
+    }
+    pub fn has(&self, id: Id) -> bool {
+        let amt = self.data.get(&id);
+        match amt {
+            Some(amt) => *amt != 0,
+            None => false
+        }
+    }
+    pub fn amount(&self, id: Id) -> u64 {
+        let amt = self.data.get(&id);
+        match amt {
+            Some(amt) => *amt,
+            None => 0
+        }
+    }
+    pub fn has_amount(&self, id: Id, amt: u64) -> bool {
+        let amount = self.amount(id);
+        amount >= amt
+    }
+    pub fn add(&mut self, id: Id, amt: u64) -> bool {
+        let amount = self.amount(id);
+        if amount != 0 {
+            self.data.insert(id, amount + amt);
+            return true;
+        }
+        if self.slots_left() == 0 {
+            return false;
+        }
+        self.data.insert(id, amt);
+        true
+    }
+    pub fn add_one(&mut self, id: Id) -> bool {
+        self.add(id, 1)
+    }
+    pub fn take(&mut self, id: Id, amt: u64) -> bool {
+        let amount = self.amount(id);
+        if amount == 0 {
+            return false;
+        }
+        if amount > amt {
+            self.data.insert(id, amount - amt);
+            true
+        } else if amount == amt {
+            self.data.remove(&id);
+            true
+        } else {
+            false
+        }
+    }
+    pub fn take_one(&mut self, id: Id) -> bool {
+        self.take(id, 1)
+    }
+    pub fn take_all(&mut self, id: Id) -> u64 {
+        let amt = self.data.remove(&id);
+        match amt {
+            Some(amt) => amt,
+            None => 0
+        }
+    }
+}