diff --git a/docs/getting-started/creating-a-place.md b/docs/getting-started/creating-a-place.md index 1ff300ed..fb2b493f 100644 --- a/docs/getting-started/creating-a-place.md +++ b/docs/getting-started/creating-a-place.md @@ -60,5 +60,5 @@ You'll need an existing place on Roblox.com as well as the `.ROBLOSECURITY` cook Generating and uploading your place file is as simple as: ```sh -rojo upload --place_id [PLACE ID] --cookie "[SECURITY COOKIE]" +rojo upload --asset_id [PLACE ID] --cookie "[SECURITY COOKIE]" ``` \ No newline at end of file diff --git a/server/src/bin.rs b/server/src/bin.rs index 87520d5a..d57230d2 100644 --- a/server/src/bin.rs +++ b/server/src/bin.rs @@ -48,10 +48,11 @@ fn main() { ) (@subcommand upload => - (about: "Generates a place file out of the project and uploads it to Roblox.") + (about: "Generates a place or model file out of the project and uploads it to Roblox.") (@arg PROJECT: "Path to the project to upload. Defaults to the current directory.") + (@arg kind: --kind +takes_value "The kind of asset to generate, 'place', or 'model'. Defaults to place.") (@arg cookie: --cookie +takes_value +required "Security cookie to authenticate with.") - (@arg place_id: --place_id +takes_value +required "Place ID to upload to.") + (@arg asset_id: --asset_id +takes_value +required "Asset ID to upload to.") ) ); @@ -134,10 +135,11 @@ fn main() { None => std::env::current_dir().unwrap(), }; + let kind = sub_matches.value_of("kind"); let security_cookie = sub_matches.value_of("cookie").unwrap(); - let place_id: u64 = { - let arg = sub_matches.value_of("place_id").unwrap(); + let asset_id: u64 = { + let arg = sub_matches.value_of("asset_id").unwrap(); match arg.parse() { Ok(v) => v, @@ -151,7 +153,8 @@ fn main() { let options = commands::UploadOptions { fuzzy_project_path, security_cookie: security_cookie.to_string(), - place_id, + asset_id, + kind, }; match commands::upload(&options) { diff --git a/server/src/commands/upload.rs b/server/src/commands/upload.rs index 7ed1e649..1685275e 100644 --- a/server/src/commands/upload.rs +++ b/server/src/commands/upload.rs @@ -5,7 +5,7 @@ use std::{ use failure::Fail; -use reqwest::header::{USER_AGENT, CONTENT_TYPE, COOKIE}; +use reqwest::header::{ACCEPT, USER_AGENT, CONTENT_TYPE, COOKIE}; use crate::{ rbx_session::construct_oneoff_tree, @@ -18,6 +18,9 @@ pub enum UploadError { #[fail(display = "Roblox API Error: {}", _0)] RobloxApiError(String), + #[fail(display = "Invalid asset kind: {}", _0)] + InvalidKind(String), + #[fail(display = "Project load error: {}", _0)] ProjectLoadError(#[fail(cause)] ProjectLoadFuzzyError), @@ -47,14 +50,14 @@ impl From for UploadError { } #[derive(Debug)] -pub struct UploadOptions { +pub struct UploadOptions<'a> { pub fuzzy_project_path: PathBuf, pub security_cookie: String, - pub place_id: u64, + pub asset_id: u64, + pub kind: Option<&'a str>, } pub fn upload(options: &UploadOptions) -> Result<(), UploadError> { - // TODO: Support uploading models too // TODO: Switch to uploading binary format? info!("Looking for project at {}", options.fuzzy_project_path.display()); @@ -69,11 +72,20 @@ pub fn upload(options: &UploadOptions) -> Result<(), UploadError> { let tree = construct_oneoff_tree(&project, &imfs); let root_id = tree.get_root_id(); - let top_level_ids = tree.get_instance(root_id).unwrap().get_children_ids(); let mut contents = Vec::new(); - rbx_xml::encode(&tree, top_level_ids, &mut contents); - let url = format!("https://data.roblox.com/Data/Upload.ashx?json=1&type=Place&assetid={}", options.place_id); + match options.kind { + Some("place") | None => { + let top_level_ids = tree.get_instance(root_id).unwrap().get_children_ids(); + rbx_xml::encode(&tree, top_level_ids, &mut contents); + }, + Some("model") => { + rbx_xml::encode(&tree, &[root_id], &mut contents); + }, + Some(invalid) => return Err(UploadError::InvalidKind(invalid.to_owned())), + } + + let url = format!("https://data.roblox.com/Data/Upload.ashx?assetid={}", options.asset_id); let client = reqwest::Client::new(); let mut response = client.post(&url) @@ -81,6 +93,7 @@ pub fn upload(options: &UploadOptions) -> Result<(), UploadError> { .header(USER_AGENT, "Roblox/WinInet") .header("Requester", "Client") .header(CONTENT_TYPE, "application/xml") + .header(ACCEPT, "application/json") .body(contents) .send()?;