aoe2rec_js/
lib.rs

1mod utils;
2
3use aoe2rec::Savegame;
4use bytes::Bytes;
5
6use js_sys::Uint8Array;
7use wasm_bindgen::prelude::*;
8
9#[wasm_bindgen]
10extern "C" {
11    fn alert(s: &str);
12    #[wasm_bindgen(js_namespace = console)]
13    fn log(s: &str);
14}
15
16#[wasm_bindgen]
17pub fn parse_rec(buffer: js_sys::ArrayBuffer) -> JsValue {
18    utils::set_panic_hook();
19
20    let input_rec = Uint8Array::new(&buffer).to_vec();
21    let rec = Savegame::from_bytes(Bytes::from(input_rec)).unwrap();
22    serde_wasm_bindgen::to_value(&rec).unwrap()
23}
24
25#[wasm_bindgen]
26pub fn parse_rec_summary(buffer: js_sys::ArrayBuffer) -> SavegameSummary {
27    utils::set_panic_hook();
28
29    let input_rec = Uint8Array::new(&buffer).to_vec();
30    let rec = Savegame::from_bytes(Bytes::from(input_rec)).unwrap();
31    SavegameSummary::from(rec.get_summary())
32}
33
34#[wasm_bindgen(getter_with_clone)]
35pub struct SavegameSummary {
36    pub header: SavegameHeader,
37    pub teams: Vec<Team>,
38    pub duration: u32,
39}
40
41impl From<aoe2rec::summary::SavegameSummary<'_>> for SavegameSummary {
42    fn from(summary: aoe2rec::summary::SavegameSummary) -> SavegameSummary {
43        SavegameSummary {
44            header: SavegameHeader {
45                game_string: summary.header.game.to_owned().into(),
46                version_major: summary.header.version_major,
47                version_minor: summary.header.version_minor,
48                build: summary.header.build,
49                timestamp: summary.header.timestamp,
50                game_settings: GameSettings::from(&summary),
51                replay: Replay::from(&summary),
52            },
53            teams: summary.teams.iter().map(|team| team.into()).collect(),
54            duration: summary.duration,
55        }
56    }
57}
58
59#[wasm_bindgen(getter_with_clone)]
60#[derive(Clone)]
61pub struct SavegameHeader {
62    pub game_string: String,
63    pub version_major: u16,
64    pub version_minor: u16,
65    pub build: u32,
66    pub timestamp: i32,
67    pub game_settings: GameSettings,
68    pub replay: Replay,
69}
70
71#[wasm_bindgen(getter_with_clone)]
72#[derive(Clone)]
73pub struct GameSettings {
74    pub dataset_ref: u32,
75    pub difficulty: u32,
76    pub selected_map_id: u32,
77    pub resolved_map_id: u32,
78    pub reveal_map: u32,
79    pub victory_type_id: u32,
80    pub starting_resources_id: u32,
81    pub starting_age_id: u32,
82    pub ending_age_id: u32,
83    pub game_type: u32,
84    pub speed: f32,
85    pub treaty_length: u32,
86    pub population_limit: u32,
87    pub n_players: u32,
88    pub victory_amount: i32,
89    pub trade_enabled: bool,
90    pub team_bonus_disabled: bool,
91    pub random_positions: bool,
92    pub all_techs: bool,
93    pub num_starting_units: u8,
94    pub lock_teams: bool,
95    pub lock_speed: bool,
96    pub multiplayer: bool,
97    pub cheats: bool,
98    pub record_game: bool,
99    pub shared_exploration: bool,
100    pub team_positions: bool,
101    pub sub_game_mode: u32,
102    pub battle_royale_time: u32,
103    pub handicap: bool,
104    pub fog_of_war: bool,
105    pub ranked: bool,
106    pub allow_specs: bool,
107    pub lobby_visibility: u32,
108    pub hidden_civs: bool,
109    pub matchmaking: bool,
110    pub spec_delay: u32,
111    pub scenario_civ: bool,
112    pub rms_strings: Vec<String>,
113    pub lobby_name: String,
114    pub modded_dataset: String,
115}
116
117impl From<&aoe2rec::summary::SavegameSummary<'_>> for GameSettings {
118    fn from(summary: &aoe2rec::summary::SavegameSummary) -> GameSettings {
119        let gs = summary.header.game_settings;
120        let map_strings: Vec<String> = gs
121            .rms_strings
122            .iter()
123            .map(|map_str| map_str.clone().into())
124            .collect();
125        GameSettings {
126            dataset_ref: gs.dataset_ref,
127            difficulty: gs.difficulty,
128            selected_map_id: gs.selected_map_id,
129            resolved_map_id: gs.resolved_map_id,
130            reveal_map: gs.reveal_map,
131            victory_type_id: gs.victory_type_id,
132            starting_resources_id: gs.starting_resources_id,
133            starting_age_id: gs.starting_age_id,
134            ending_age_id: gs.ending_age_id,
135            game_type: gs.game_type,
136            speed: gs.speed,
137            treaty_length: gs.treaty_length,
138            population_limit: gs.population_limit,
139            n_players: gs.n_players,
140            victory_amount: gs.victory_amount,
141            trade_enabled: gs.trade_enabled.into(),
142            team_bonus_disabled: gs.team_bonus_disabled.into(),
143            random_positions: gs.random_positions.into(),
144            all_techs: gs.all_techs.into(),
145            num_starting_units: gs.num_starting_units,
146            lock_teams: gs.lock_teams.into(),
147            lock_speed: gs.lock_speed.into(),
148            multiplayer: gs.multiplayer.into(),
149            cheats: gs.cheats.into(),
150            record_game: gs.record_game.into(),
151            shared_exploration: gs.shared_exploration.into(),
152            team_positions: gs.team_positions.into(),
153            sub_game_mode: gs.sub_game_mode,
154            battle_royale_time: gs.battle_royale_time,
155            handicap: gs.handicap.into(),
156            fog_of_war: gs.fog_of_war.into(),
157            ranked: gs.ranked.into(),
158            allow_specs: gs.allow_specs.into(),
159            lobby_visibility: gs.lobby_visibility,
160            hidden_civs: gs.hidden_civs.into(),
161            matchmaking: gs.matchmaking.into(),
162            spec_delay: gs.spec_delay,
163            scenario_civ: gs.scenario_civ.into(),
164            rms_strings: map_strings,
165            lobby_name: gs.lobby_name.clone().into(),
166            modded_dataset: gs.modded_dataset.clone().into(),
167        }
168    }
169}
170
171#[wasm_bindgen(getter_with_clone)]
172#[derive(Clone)]
173pub struct Replay {
174    pub old_time: u32,
175    pub world_time: u32,
176    pub old_world_time: u32,
177    pub game_speed_id: u32,
178    pub world_time_delta_seconds: u32,
179    pub timer: f32,
180    pub game_speed: f32,
181    pub temp_pause: bool,
182    pub random_seed: u32,
183    pub random_seed_2: u32,
184    pub rec_player: u16,
185    pub num_players: u8,
186    pub instant_build: bool,
187    pub cheats_enabled: bool,
188    pub game_mode: u16,
189}
190
191impl From<&aoe2rec::summary::SavegameSummary<'_>> for Replay {
192    fn from(summary: &aoe2rec::summary::SavegameSummary) -> Replay {
193        let r = summary.header.replay;
194        Replay {
195            old_time: r.old_time,
196            world_time: r.world_time,
197            old_world_time: r.old_world_time,
198            game_speed_id: r.game_speed_id,
199            world_time_delta_seconds: r.world_time_delta_seconds,
200            timer: r.timer,
201            game_speed: r.game_speed,
202            temp_pause: r.temp_pause.into(),
203            random_seed: r.random_seed,
204            random_seed_2: r.random_seed_2,
205            rec_player: r.rec_player,
206            num_players: r.num_players,
207            instant_build: r.instant_build.into(),
208            cheats_enabled: r.cheats_enabled.into(),
209            game_mode: r.game_mode,
210        }
211    }
212}
213
214#[wasm_bindgen(getter_with_clone)]
215#[derive(Clone)]
216pub struct Team {
217    pub winner: bool,
218    pub players: Vec<Player>,
219}
220
221impl From<&aoe2rec::summary::GameTeam<'_>> for Team {
222    fn from(team: &aoe2rec::summary::GameTeam<'_>) -> Self {
223        Self {
224            winner: team.players.iter().any(|player| !player.resigned),
225            players: team.players.iter().map(|player| player.into()).collect(),
226        }
227    }
228}
229
230#[wasm_bindgen(getter_with_clone)]
231#[derive(Clone)]
232pub struct Player {
233    pub name: String,
234    pub color_id: i32,
235    pub selected_color: u8,
236    pub selected_team_id: u8,
237    pub resolved_team_id: u8,
238    pub civ_id: u32,
239    pub custom_civ_ids: Vec<u32>,
240    pub player_type: u32,
241    pub profile_id: i32,
242    pub player_number: i32,
243    pub prefer_random: bool,
244    pub resigned: bool,
245}
246
247impl From<&aoe2rec::summary::TeamPlayer<'_>> for Player {
248    fn from(player: &aoe2rec::summary::TeamPlayer<'_>) -> Self {
249        Self {
250            name: player.info.name.clone().into(),
251            color_id: player.info.color_id,
252            selected_color: player.info.selected_color,
253            selected_team_id: player.info.selected_team_id,
254            resolved_team_id: player.info.resolved_team_id,
255            civ_id: player.info.civ_id,
256            custom_civ_ids: player.info.custom_civ_ids.clone(),
257            player_type: player.info.player_type,
258            profile_id: player.info.profile_id,
259            player_number: player.info.player_number,
260            prefer_random: player.info.prefer_random.into(),
261            resigned: player.resigned,
262        }
263    }
264}