// StarKingdoms.IO, a browser game about drifting through space // Copyright (C) 2023 ghostly_zsh, TerraMaster85, core // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . use crate::response::JsonAPIResponse; use crate::AppState; use actix_web::{ http::StatusCode, post, web::{Data, Json}, }; use log::error; use serde::{Deserialize, Serialize}; use starkingdoms_common::{ __noverify__unpack_savefile, pack_savefile, unpack_savefile, PartType, SaveModule, }; use std::collections::HashMap; use std::hash::Hash; #[derive(Deserialize)] pub struct SignSaveRequest { pub old_save: String, pub new_partial: String, } #[derive(Serialize, Debug)] pub struct SignSaveResponse { pub signed_save: String, } fn inc(a: &mut HashMap, t: &T, by: u64) { if let Some(r) = a.get_mut(t) { *r += by; } else { a.insert(t.clone(), by); } } fn recursive_inc(a: &mut HashMap, m: &Option) { if let Some(m) = m { inc(a, &m.part_type, 1); for child in &m.children { recursive_inc(a, child); } } } #[post("/sign_save")] pub async fn sign_save_req( req: Json, state: Data, ) -> JsonAPIResponse { let old_save = handle_error!(unpack_savefile(&state.key_raw, req.old_save.clone())); let new_save = handle_error!(__noverify__unpack_savefile(req.new_partial.clone())); // ensure part counts are the same let mut part_counts_old: HashMap = HashMap::new(); for unused_modules in &old_save.unused_modules { inc( &mut part_counts_old, &unused_modules.0, unused_modules.1 as u64, ); } for child in &old_save.children { recursive_inc(&mut part_counts_old, child); } let mut part_counts_new: HashMap = HashMap::new(); for unused_modules in &new_save.unused_modules { inc( &mut part_counts_new, &unused_modules.0, unused_modules.1 as u64, ); } for child in &new_save.children { recursive_inc(&mut part_counts_new, child); } if part_counts_old != part_counts_new { error!( "save validation failed: {:?} -> {:?}", part_counts_old, part_counts_new ); error!("old file: {:?}", old_save); error!("new file: {:?}", new_save); err!( StatusCode::BAD_REQUEST, make_err!( "ERR_SAVE_VALIDATION_FAILED", "save validation failed: part counts do not match" ) ) } // resign the savefile and return it to the user ok!(SignSaveResponse { signed_save: pack_savefile(&state.key_raw, new_save) }) }