Skip to content

Commit

Permalink
tidy up
Browse files Browse the repository at this point in the history
  • Loading branch information
fluxxu committed Sep 21, 2022
1 parent 72c919f commit e226119
Show file tree
Hide file tree
Showing 9 changed files with 409 additions and 305 deletions.
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ members = [
"crates/observer-fs",
"crates/observer-archiver",
"crates/kinesis",
"crates/replay",

"crates/controller",
"crates/node",
Expand Down
1 change: 1 addition & 0 deletions binaries/flo-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ flo-kinesis = { path = "../../crates/kinesis" }
flo-w3replay = { path = "../../crates/w3replay" }
flo-util = { path = "../../crates/util" }
flo-types = { path = "../../crates/types" }
flo-replay = { path = "../../crates/replay" }

anyhow = "1"
tonic = "0.6"
Expand Down
281 changes: 14 additions & 267 deletions binaries/flo-cli/src/observer.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
use flo_net::w3gs::W3GSPacketTypeId;
use flo_observer::record::GameRecordData;
use flo_observer_archiver::{ArchiverOptions, Fetcher};
use flo_observer_fs::GameDataArchiveReader;
use flo_w3gs::player::{PlayerProfileMessage, PlayerSkinsMessage, PlayerUnknown5Message};
use flo_w3replay::{
GameInfo, PlayerChatMessage, PlayerInfo, PlayerLeft, ProtoBufPayload, RacePref, ReplayEncoder,
SlotInfo, TimeSlot, TimeSlotAck,
};
use flo_replay::{generate_replay, GenerateReplayOptions};
use s2_grpc_utils::S2ProtoUnpack;
use structopt::StructOpt;

Expand Down Expand Up @@ -47,9 +40,6 @@ impl Command {
client.serve().await;
}
Command::GenerateReplay { game_id } => {
use flo_types::game::SlotStatus;
use flo_w3replay::Record;

tracing::info!("fetching game information...");

let ctrl = get_grpc_client().await;
Expand All @@ -63,41 +53,6 @@ impl Command {

let game = flo_types::observer::GameInfo::unpack(game).unwrap();

let occupied_slots: Vec<(usize, _)> = game
.slots
.iter()
.enumerate()
.filter(|(_, slot)| slot.settings.status == SlotStatus::Occupied)
.collect();

let (first_player_info, first_player_name) = occupied_slots
.get(0)
.and_then(|(i, slot)| {
let name = slot.player.as_ref().map(|p| p.name.clone())?;
Some((PlayerInfo::new(index_to_player_id(*i), &name), name))
})
.unwrap();
let first_player_id = first_player_info.id;

let game_info = GameInfo::new(
first_player_info,
&game.name,
flo_w3gs::game::GameSettings::new(
Default::default(),
flo_w3gs::game::GameSettingsMap {
path: game.map.path.clone(),
width: 0,
height: 0,
sha1: {
let mut value = [0_u8; 20];
value.copy_from_slice(&game.map.sha1[..]);
value
},
checksum: 0xFFFFFFFF,
},
),
);

tracing::info!("game name = {}, version = {}", game.name, game.game_version);

tracing::info!("fetching game archive...");
Expand All @@ -110,232 +65,24 @@ impl Command {
};

let fetcher = Fetcher::new(opts).unwrap();
let bytes = fetcher.fetch(game_id).await.unwrap();
let rdr = GameDataArchiveReader::open_bytes(&bytes).await.unwrap();
let archive_records = rdr.records().collect_vec().await.unwrap();
tracing::info!(
"archive: size: {}, records: {}",
bytes.len(),
archive_records.len()
);

fn index_to_player_id(index: usize) -> u8 {
return (index + 1) as u8;
}

const FLO_OB_SLOT: usize = 23;

let flo_ob_slot_occupied = occupied_slots
.iter()
.find(|(idx, _)| *idx == FLO_OB_SLOT)
.is_some();

let mut player_infos = vec![];
let mut player_skins = vec![];
let mut player_profiles = vec![];

let mut is_first_player = true;
for (i, slot) in game.slots.iter().enumerate() {
if let Some(ref p) = slot.player {
let player_id = index_to_player_id(i);
if is_first_player {
is_first_player = false;
} else {
player_infos.push(PlayerInfo::new(player_id, p.name.as_str()));
}
player_skins.push(ProtoBufPayload::new(PlayerSkinsMessage {
player_id: player_id as _,
..Default::default()
}));
player_profiles.push(ProtoBufPayload::new(PlayerProfileMessage {
player_id: player_id as _,
battle_tag: p.name.clone(),
portrait: "p042".to_string(),
..Default::default()
}));
}
}

if !flo_ob_slot_occupied {
let ob_player_id = index_to_player_id(FLO_OB_SLOT);
player_infos.push(PlayerInfo::new(ob_player_id, "FLO"));
player_profiles.push(ProtoBufPayload::new(PlayerProfileMessage::new(
ob_player_id,
"FLO",
)));
}

let mut records = vec![];

// 0 GameInfo
records.push(Record::GameInfo(game_info));

// 1 PlayerInfo
for item in player_infos {
records.push(Record::PlayerInfo(flo_w3replay::PlayerInfoRecord {
player_info: item,
unknown: 0,
}));
}

// 2 ProtoBuf PlayerSkins
for item in player_skins {
records.push(Record::ProtoBuf(item));
}

// 3 ProtoBuf PlayerProfile
for item in player_profiles {
records.push(Record::ProtoBuf(item));
}
let archive = fetcher.fetch(game_id).await.unwrap();
tracing::info!("archive: size: {}", archive.len());

// special messages
{
let (my_id, my_name) = if flo_ob_slot_occupied {
(first_player_id, first_player_name.clone())
} else {
(index_to_player_id(FLO_OB_SLOT), "FLO".to_string())
};

// ProtoBuf PlayerSkinsMessage

records.push(Record::ProtoBuf(ProtoBufPayload::new(PlayerSkinsMessage {
player_id: my_id as _,
..Default::default()
})));

// ProtoBuf PlayerUnknown5
records.push(Record::ProtoBuf(ProtoBufPayload::new(
PlayerUnknown5Message {
player_id: my_id as _,
unknown_1: 1,
},
)));

let ack_player_profile_count =
if flo_ob_slot_occupied { 0 } else { 1 } + occupied_slots.len() - 1;
let msg = PlayerProfileMessage::new(my_id, &my_name);
for _ in 0..ack_player_profile_count {
records.push(Record::ProtoBuf(ProtoBufPayload::new(msg.clone())))
}
}

// 4 SlotInfo
{
let mut b = SlotInfo::build();
let mut slot_info = b
.random_seed(game.random_seed)
.num_slots(24)
.num_players(
occupied_slots
.iter()
.filter(|(_, slot)| slot.settings.team != 24 && slot.player.is_some())
.count(),
)
.build();

for (i, player_slot) in &occupied_slots {
use flo_w3gs::slot::SlotStatus;
let slot = slot_info.slot_mut(*i).expect("always has 24 slots");

if player_slot.player.is_some() {
slot.player_id = index_to_player_id(*i);
slot.slot_status = SlotStatus::Occupied;
slot.race = player_slot.settings.race.into();
slot.color = player_slot.settings.color as u8;
slot.team = player_slot.settings.team as u8;
slot.handicap = player_slot.settings.handicap as u8;
slot.download_status = 100;
} else {
slot.computer = true;
slot.computer_type = player_slot.settings.computer.into();
slot.slot_status = SlotStatus::Occupied;
slot.race = player_slot.settings.race.into();
slot.color = player_slot.settings.color as u8;
slot.team = player_slot.settings.team as u8;
slot.handicap = player_slot.settings.handicap as u8;
slot.download_status = 100;
}
}

if !flo_ob_slot_occupied {
use flo_w3gs::slot::SlotStatus;
let slot = slot_info
.slot_mut(FLO_OB_SLOT)
.expect("always has 24 slots");

slot.player_id = index_to_player_id(FLO_OB_SLOT);
slot.slot_status = SlotStatus::Occupied;
slot.race = RacePref::RANDOM;
slot.color = 0;
slot.team = 24;
}

records.push(Record::SlotInfo(slot_info));
}

// 5 CountDownStart
records.push(Record::CountDownStart(Default::default()));

// 6 CountDownEnd
records.push(Record::CountDownEnd(Default::default()));

// 7 GameStart
records.push(Record::GameStart(Default::default()));

// archive records
for r in archive_records {
match r {
GameRecordData::W3GS(p) => match p.type_id() {
W3GSPacketTypeId::PlayerLeft => {
let payload: flo_w3gs::protocol::leave::PlayerLeft = p.decode_simple().unwrap();
records.push(Record::PlayerLeft(PlayerLeft {
reason: payload.reason,
player_id: payload.player_id,
result: 13, // ?
unknown: 2,
}))
}
W3GSPacketTypeId::ChatFromHost => {
let payload: flo_w3gs::protocol::chat::ChatFromHost = p.decode_simple().unwrap();
if payload
.0
.to_players
.contains(&index_to_player_id(FLO_OB_SLOT))
{
records.push(Record::ChatMessage(PlayerChatMessage {
player_id: payload.from_player(),
message: payload.0.message,
}));
}
}
W3GSPacketTypeId::IncomingAction => {
let payload: flo_w3gs::protocol::action::IncomingAction =
p.decode_payload().unwrap();
records.push(Record::TimeSlot(TimeSlot {
time_increment_ms: payload.0.time_increment_ms,
actions: payload.0.actions,
}))
}
_ => {}
},
GameRecordData::StartLag(_) => {}
GameRecordData::StopLag(_) => {}
GameRecordData::GameEnd => {}
GameRecordData::TickChecksum { checksum, .. } => {
records.push(Record::TimeSlotAck(TimeSlotAck::new(checksum)))
}
GameRecordData::RTTStats(_) => {}
}
}

tracing::info!("total replay records: {}", records.len());
tracing::info!("encoding replay...");

let file = std::fs::File::create(format!("{}.w3g", game_id)).unwrap();
let w = std::io::BufWriter::new(file);
let mut encoder = ReplayEncoder::new(&game.game_version, 0x8000, w).unwrap();
encoder.encode_records(records.iter()).unwrap();
encoder.finish().unwrap();

generate_replay(
GenerateReplayOptions {
game,
archive,
include_chats: true,
},
w,
)
.await
.unwrap();

tracing::info!("done");
}
Expand Down
2 changes: 1 addition & 1 deletion crates/observer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pub mod error;
mod kinesis;
pub mod record;
pub mod error;
pub mod token;

use once_cell::sync::Lazy;
Expand Down
15 changes: 15 additions & 0 deletions crates/replay/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "flo-replay"
version = "0.1.0"
edition = "2021"

[dependencies]
flo-net = { path = "../net" }
flo-w3gs = { path = "../w3gs" }
flo-types = { path = "../types" }
flo-w3replay = { path = "../w3replay" }
flo-observer = { path = "../observer" }
flo-observer-fs = { path = "../observer-fs" }
bytes = "1.1.0"
thiserror = "1.0"
tracing = "0.1"
15 changes: 15 additions & 0 deletions crates/replay/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use thiserror::Error;

#[derive(Error, Debug)]
pub enum Error {
#[error("flo observer slot occupied")]
FloObserverSlotOccupied,
#[error("w3gs: {0}")]
W3GS(#[from] flo_w3gs::error::Error),
#[error("observer fs: {0}")]
ObserverFs(#[from] flo_observer_fs::error::Error),
#[error("w3replay: {0}")]
W3Replay(#[from] flo_w3replay::error::Error),
}

pub type Result<T, E = Error> = std::result::Result<T, E>;
Loading

0 comments on commit e226119

Please sign in to comment.