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
use memofs::Vfs;
use reqwest::{
header::{ACCEPT, CONTENT_TYPE, COOKIE, USER_AGENT},
StatusCode,
};
use thiserror::Error;
use crate::{auth_cookie::get_auth_cookie, cli::UploadCommand, serve_session::ServeSession};
#[derive(Debug, Error)]
enum Error {
#[error("Rojo could not find your Roblox auth cookie. Please pass one via --cookie.")]
NeedAuthCookie,
#[error("The Roblox API returned an unexpected error: {body}")]
RobloxApi { body: String },
}
pub fn upload(options: UploadCommand) -> Result<(), anyhow::Error> {
let cookie = options
.cookie
.clone()
.or_else(get_auth_cookie)
.ok_or(Error::NeedAuthCookie)?;
let vfs = Vfs::new_default();
let session = ServeSession::new(vfs, &options.absolute_project())?;
let tree = session.tree();
let inner_tree = tree.inner();
let root = inner_tree.root();
let encode_ids = match root.class.as_str() {
"DataModel" => root.children().to_vec(),
_ => vec![root.referent()],
};
let mut buffer = Vec::new();
log::trace!("Encoding binary model");
rbx_binary::to_writer_default(&mut buffer, tree.inner(), &encode_ids)?;
do_upload(buffer, options.asset_id, &cookie)
}
fn do_upload(buffer: Vec<u8>, asset_id: u64, cookie: &str) -> anyhow::Result<()> {
let url = format!(
"https://data.roblox.com/Data/Upload.ashx?assetid={}",
asset_id
);
let client = reqwest::Client::new();
let build_request = move || {
client
.post(&url)
.header(COOKIE, format!(".ROBLOSECURITY={}", cookie))
.header(USER_AGENT, "Roblox/WinInet")
.header(CONTENT_TYPE, "application/xml")
.header(ACCEPT, "application/json")
.body(buffer.clone())
};
log::debug!("Uploading to Roblox...");
let mut response = build_request().send()?;
if response.status() == StatusCode::FORBIDDEN {
if let Some(csrf_token) = response.headers().get("X-CSRF-Token") {
log::debug!("Received CSRF challenge, retrying with token...");
response = build_request().header("X-CSRF-Token", csrf_token).send()?;
}
}
let status = response.status();
if !status.is_success() {
return Err(Error::RobloxApi {
body: response.text()?,
}
.into());
}
Ok(())
}