~starkingdoms/starkingdoms

ref: 0493e0227361edcc0ea834ba235fd90430e17f98 starkingdoms/crates/api/src/routes/sign_save.rs -rw-r--r-- 3.4 KiB
0493e022 — core asset system cleanup 8 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// 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)
    })
}