1mod random_level_worker;
2mod random;
3
4use fancy_regex::{Regex, SubCaptureMatches};
5
6use rusqlite::{Connection, Result};
7
8use serde::{Deserialize, Serialize};
9use serde_json;
10
11use snap;
12use snap::raw::{Decoder, Encoder};
13
14use core::time;
15use std::collections::HashMap;
16use std::fs::{self, create_dir, Metadata};
17use std::time::SystemTime;
18
19#[derive(Serialize, Deserialize, Debug)]
23pub struct Data {
24 pub js_level: JSLevel,
25 pub settings: Settings
26}
27
28impl Data {
29 pub fn new (js_level: JSLevel, settings: Settings) -> Self {
30 Data {js_level, settings}
31 }
32}
33
34
35#[derive(Serialize, Deserialize, Debug)]
46pub struct JSLevel {
47 pub worldSeed: i64,
48 pub changedBlocks: HashMap<String,ChangedBlocks>,
49 pub worldSize: i32,
50 pub version: u8
51}
52
53impl JSLevel {
54 pub fn new (worldSeed: i64, changedBlocks: HashMap<String,ChangedBlocks>, worldSize: i32, version: u8) -> Self {
55 JSLevel { worldSeed, changedBlocks, worldSize, version }
56 }
57
58 pub fn default () -> Self {
59 JSLevel { worldSeed: 1, changedBlocks: HashMap::new(), worldSize: 256, version: 1 }
60 }
61}
62
63#[derive(Serialize, Deserialize, Debug)]
73pub struct ChangedBlocks {pub a: u8, pub bt: u8}
74impl ChangedBlocks { pub fn new (a: u8, bt: u8) -> Self {ChangedBlocks { a, bt }}}
75
76#[derive(Serialize, Deserialize, Debug)]
81pub struct Settings {
82 pub music: bool,
83 pub sound: bool,
84 pub invert: bool,
85 pub fps: bool,
86 pub drawDistance: i32,
87 pub forward: String,
88 pub left: String,
89 pub backward: String,
90 pub right: String,
91 pub jump: String,
92 pub build: String,
93 pub chat: String,
94 pub fog: String,
95 pub saveLoc: String,
96 pub loadLoc: String,
97 pub username: String
98}
99
100impl Settings {
101 pub fn new(
102 music: bool,
103 sound: bool,
104 invert: bool,
105 fps: bool,
106 drawDistance: i32,
107 forward: String,
108 left: String,
109 backward: String,
110 right: String,
111 jump: String,
112 build: String,
113 chat: String,
114 fog: String,
115 saveLoc: String,
116 loadLoc: String,
117 username: String
118 ) -> Self {
119 Settings { music, sound, invert, fps, drawDistance, forward, left, backward, right, jump, build, chat, fog, saveLoc, loadLoc, username }
120 }
121
122 pub fn default () -> Self {
123 Settings {
124 music: false,
125 sound: true,
126 invert: false,
127 fps: false,
128 drawDistance: 0,
129 forward: String::from("W"),
130 left: String::from("A"),
131 backward: String::from("S"),
132 right: String::from("D"),
133 jump: String::from("<space>"),
134 build: String::from("B"),
135 chat: String::from("T"),
136 fog: String::from("F"),
137 saveLoc: String::from("<enter>"),
138 loadLoc: String::from("R"),
139 username: String::from("noname")
140 }
141 }
142}
143
144pub struct LocalStorage {
153 key: String,
154 utf16_length: i32,
155 conversion_type: i32,
156 compression_type: i32,
157 last_access_time: i32,
158 value: Vec<u8>
159}
160
161pub fn deserialize_saved_game (json_string: String) -> JSLevel {
166 let level: JSLevel = serde_json::from_str(&json_string).unwrap();
167 return level;
168}
169
170pub fn deserialize_settings (json_string: String) -> Settings {
175 let settings: Settings = serde_json::from_str(&json_string).unwrap();
176 return settings;
177}
178
179pub fn deserialize_data (json_string1: String, json_string2: String) -> Data {
184 let level: JSLevel = serde_json::from_str(&json_string1).unwrap();
185 let settings: Settings = serde_json::from_str(&json_string2).unwrap();
186 return Data { js_level: level, settings: settings}
187}
188
189pub fn serialize_saved_game (level: JSLevel, tile_map: Vec<u8>, opt: u8) -> String {
194
195 let x: i32 = level.worldSize;
197 let y: i32 = 64;
198 let z: i32 = level.worldSize;
199 let tile_map1 = get_tile_map(level.worldSize, level.worldSeed);
200
201 let mut output: String = String::from("{"); output += &format!(r#""worldSeed":{},"#,level.worldSeed.to_string()); output += r#""changedBlocks":"#; output += "{"; let mut t: u8;
211 let mut t1: u8;
212 let mut a: u8; let mut flag: bool = false;
217 for i in 0..y {
218 for j in 0..z {
219 for k in 0..x {
220
221 let mut flag1 = false;
225 let key: String = String::from(format!(r#"p{}_{}_{}"#,k,i,j));
226 let bt: u8 = level.changedBlocks.get(&key).unwrap_or(&ChangedBlocks::new(1,255)).bt;
228 t = tile_map[((i*z*x) + (j*x) + k) as usize];
230 t1 = tile_map1[((i*z*x) + (j*x) + k) as usize];
232 if bt != 255 { t = bt }
233 if t == t1 { a = 0 } else { a = 1 } if (opt == 2 && a == 1) || (opt == 1 && (bt != 255 || a == 1)) || opt == 0 { flag1 = true }
240
241 if flag1 {
242 output += &key;
244
245 output += "{";
247 output += &format!(r#""a":{},"bt":{}"#,a,t);
248 output += "},";
249
250 flag = true;
251 }
252
253 }
254 }
255 }
256
257 if flag {output.pop();} output += "},"; output += &format!{r#""worldSize":{},"#,level.worldSize}; output += &format!{r#""version":{}"#,level.version}; output += "}"; return output;
265
266}
267
268pub fn serialize_settings (settings: Settings) -> String {
273 let mut output: String = String::from("{"); output += &format!{r#""music":{},"#,settings.music};
275 output += &format!{r#""sound":{},"#,settings.sound};
276 output += &format!{r#""invert":{},"#,settings.invert};
277 output += &format!{r#""fps":{},"#,settings.fps};
278 output += &format!{r#""drawDistance":{},"#,settings.drawDistance};
279 output += &format!{r#""forward":"{}","#,settings.forward};
280 output += &format!{r#""left":"{}","#,settings.left};
281 output += &format!{r#""backward":"{}","#,settings.backward};
282 output += &format!{r#""right":"{}","#,settings.right};
283 output += &format!{r#""jump":"{}","#,settings.jump};
284 output += &format!{r#""build":"{}","#,settings.build};
285 output += &format!{r#""chat":"{}","#,settings.chat};
286 output += &format!{r#""fog":"{}","#,settings.fog};
287 output += &format!{r#""saveLoc":"{}","#,settings.saveLoc};
288 output += &format!{r#""loadLoc":"{}","#,settings.loadLoc};
289 output += &format!{r#""username":"{}""#,settings.username};
290 output += "}"; return output;
292}
293
294pub fn serialize_data (data: Data) -> [String; 2] {
299 let tile_map = get_tile_map(data.js_level.worldSize, data.js_level.worldSeed);
300 let level_str: String = serialize_saved_game(data.js_level, tile_map, 1);
301 let settings_str: String = serialize_settings(data.settings);
302 return [level_str, settings_str]
303}
304
305pub fn read_from_db (file_path: String, object: &str) -> Result<String> {
311
312 let conn: Connection = Connection::open(file_path)?;
313
314 let mut stmt = conn.prepare(
315 "SELECT * FROM data where key=?1;"
316 )?;
317
318 let entries = stmt.query_map([object], |row| Ok(
320 LocalStorage {
321 key: row.get(0)?,
322 utf16_length: row.get(1)?,
323 conversion_type: row.get(2)?,
324 compression_type: row.get(3)?,
325 last_access_time: row.get(4)?,
326 value: row.get(5)?,
327 }
328 ))?;
329
330 let mut compressed_object: Vec<u8> = Vec::new();
332 let mut decompressed_length: i32 = 0;
333 for entry in entries {
334 let local: LocalStorage = entry.unwrap();
335 if local.key == object {
336 compressed_object = local.value;
337 decompressed_length = local.utf16_length;
338 break;
339 }
340 }
341
342 let mut decompressed: Vec<u8> = Vec::new();
344 for _ in 0..decompressed_length {
345 decompressed.push(0);
346 }
347
348 Decoder::decompress(&mut Decoder::new(), &compressed_object, &mut decompressed).unwrap();
350
351 let mut characters: Vec<char> = Vec::new();
353 for ch in decompressed {
354 characters.push(ch as char)
355 }
356
357 Ok(characters.iter().collect())
359
360}
361
362pub fn read_saved_game (file_path: String) -> Result<String> {
368 return read_from_db(file_path, "savedGame");
369}
370
371pub fn read_settings (file_path: String) -> Result<String> {
377 return read_from_db(file_path, "settings");
378}
379
380pub fn write_data (file_path: String, json_strings: [String; 2], website: String) -> Result<()> {
389
390 let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_micros() as u64;
391
392 let regex = Regex::new(r#"/|:|\*|\?|"|>|<|\||\\"#).unwrap();
394 let substitution = "+";
395 let dir_name = regex.replace_all(&website, substitution);
396
397 create_dir(file_path.clone() + "/" + &dir_name);
398 create_dir(file_path.clone() + "/" + &dir_name + "/ls");
399
400 let mut metadata: Vec<u8> = Vec::new();
402 metadata.extend_from_slice(×tamp.to_be_bytes()); metadata.push(0); metadata.extend_from_slice(&(0 as i32).to_be_bytes()); metadata.extend_from_slice(&(0 as i32).to_be_bytes()); metadata.extend_from_slice(&(website.len() as u16).to_be_bytes());
409 metadata.extend_from_slice(website.as_bytes());
410 metadata.push(0); fs::write(file_path.clone() + "/" + &dir_name + "/.metadata-v2", metadata);
416
417 let keys: Vec<&str> = vec!["savedGame", "settings"];
418
419 let conn: Connection = Connection::open(file_path.clone() + "/" + &dir_name + "/ls/data.sqlite")?;
420
421 conn.pragma_update(None, "user_version", 80);
422 conn.pragma_update(None, "auto_vacuum", 2);
423 conn.pragma_update(None, "page_size", 1024);
424
425 conn.execute("VACUUM", []);
426
427 conn.execute(
429 "CREATE TABLE if not exists data (
430 key TEXT PRIMARY KEY,
431 utf16_length INTEGER NOT NULL,
432 conversion_type INTEGER NOT NULL,
433 compression_type INTEGER NOT NULL,
434 last_access_time INTEGER NOT NULL DEFAULT 0,
435 value BLOB NOT NULL)",
436 []
437 )?;
438
439 let mut len = 0;
440
441 let mut stmt = conn.prepare("INSERT OR REPLACE INTO data (key, utf16_length, conversion_type, compression_type, value) values (?1, ?2, ?3, ?4, ?5)" )?;
443
444 for i in 0..json_strings.len() {
445 let utf16_length: i32 = json_strings[i].len() as i32;
448
449 len += utf16_length;
450
451 let mut decompressed: Vec<u8> = Vec::new();
453 decompressed.extend_from_slice(json_strings[i].as_bytes());
454
455 let max_comp_length = snap::raw::max_compress_len(decompressed.len());
457 let mut compressed: Vec<u8> = Vec::new();
458 for _ in 0..max_comp_length {
459 compressed.push(0);
460 }
461
462 Encoder::compress(&mut Encoder::new(), &decompressed, & mut compressed).unwrap();
464 let mut b: u8 = 0;
465 while b == 0 {
466 b = compressed.pop().unwrap();
467 }
468 compressed.push(b);
469
470 stmt.execute((keys[i], utf16_length, 1, 1, compressed))?;
471 }
472
473 len += 10;
474 let vacuum_size = fs::metadata(file_path.clone() + "/" + &dir_name + "/ls/data.sqlite").unwrap().len();
475
476 conn.execute(
477 "CREATE TABLE if not exists database (
478 origin TEXT NOT NULL,
479 usage INTEGER NOT NULL DEFAULT 0,
480 last_vacuum_time INTEGER NOT NULL DEFAULT 0,
481 last_analyze_time INTEGER NOT NULL DEFAULT 0,
482 last_vacuum_size INTEGER NOT NULL DEFAULT 0)",
483 [])?;
484
485 stmt = conn.prepare("INSERT OR REPLACE INTO database (origin,usage,last_vacuum_time,last_analyze_time,last_vacuum_size) values (?1, ?2, ?3, ?4, ?5)" )?;
486
487 stmt.execute((&website,len,timestamp,0,vacuum_size))?;
488
489 fs::write(file_path.clone() + "/" + &dir_name + "/ls/usage", "");
490
491 Ok(())
492
493}
494
495
496
497pub fn write_saved_game (file_path: String, json_string: String, website: String) -> Result<()> {
506
507 let settings: String = serialize_settings(Settings::default());
508 write_data(file_path, [json_string,settings], website);
509
510 return Ok(());
511
512}
513
514pub fn write_saved_game_command (file: String, json_string: String) -> String {
521 let open: String = String::from(r#"localStorage.setItem("savedGame", `"#); let close: String = String::from(r#"`)"#); let output: String = String::from(format!{r"{open}{json_string}{close}"});
525
526 if file != "" {fs::write(file, output.clone()).expect("Error when writing to file")} return output;
529
530}
531
532pub fn write_settings_command (file: String, json_string: String) -> String {
539 let open: String = String::from(r#"localStorage.setItem("settings", `"#); let close: String = String::from(r#"`)"#); let output: String = String::from(format!{r"{open}{json_string}{close}"});
543
544 if file != "" {fs::write(file, output.clone()).expect("Error when writing to file")} return output;
547
548}
549
550pub fn write_local_storage_command (file: String, json_strings: [String; 2]) -> String {
558 let open: String = String::from(r#"localStorage.setItem("savedGame", `"#); let close: String = String::from(r#"`)"#); let mut string: String = json_strings[0].clone();
561
562 let mut output: String = String::from(format!{r"{open}{string}{close}"});
563 output += ";";
564
565 string = json_strings[1].clone();
566 output += &format!{r"{open}{string}{close}"};
567
568 if file != "" {fs::write(file, output.clone()).expect("Error when writing to file")} return output;
571
572}
573
574pub fn generate_saved_game_from_seed (seed: i64, tile_map: Vec<u8>) -> JSLevel {
578
579 let world_size: i32 = ((tile_map.len()/64) as f64).sqrt() as i32;
580 let changed_blocks: HashMap<String, ChangedBlocks> = HashMap::new();
581 let level = JSLevel::new(seed, changed_blocks, world_size, 1);
582
583 return deserialize_saved_game(serialize_saved_game(level, tile_map, 2));
584
585}
586
587pub fn get_tile_map (world_size: i32, seed: i64) -> Vec<u8> {
593 let y: i32 = 64;
594 let level: HashMap<usize, u8> = random_level_worker::start_generation(world_size, seed); let mut tile_map: Vec<u8> = Vec::new();
596
597 for i in 0..world_size * y * world_size {
598 tile_map.push(level.get(&(i as usize)).copied().unwrap_or(0)); }
600
601 return tile_map
602}
603
604#[deprecated(since="0.2.0", note="please use `generate_saved_game_from_seed` instead")]
610pub fn serialize_saved_game_from_seed (seed: i64, tile_map: Vec<u8>) -> String {
611
612 let world_size: i32 = ((tile_map.len()/64) as f64).sqrt() as i32;
613 let changed_blocks: HashMap<String, ChangedBlocks> = HashMap::new();
614 let level = JSLevel::new(seed, changed_blocks, world_size, 1);
615
616 return serialize_saved_game(level, tile_map, 2);
617}
618
619