// 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 <https://www.gnu.org/licenses/>.
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<T: Hash + PartialEq + Clone + Eq>(a: &mut HashMap<T, u64>, 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<PartType, u64>, m: &Option<SaveModule>) {
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<SignSaveRequest>,
state: Data<AppState>,
) -> JsonAPIResponse<SignSaveResponse> {
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<PartType, u64> = 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<PartType, u64> = 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)
})
}