Skip to content

Commit

Permalink
Merge pull request #900 from AmbientRun/tangent-0.1
Browse files Browse the repository at this point in the history
Tangent 0.1
  • Loading branch information
philpax authored Sep 18, 2023
2 parents 3287038 + 2cdcccf commit 8a15505
Show file tree
Hide file tree
Showing 478 changed files with 3,697 additions and 939 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ These PRs are not directly user-facing, but improve the development experience.
- **Docs**: The IDE documentation has been improved, including information on how to set up Emacs for Ambient development (thanks to [@kevzettler](https://github.com/kevzettler) in [#505](https://github.com/AmbientRun/Ambient/pull/505)).
- **Assets**: `ambient assets import` can be used to import assets one by one. This will create or modify the `pipeline.toml` file for you.
- **Packages**: A new fps_controller package to easily get started with a fps or tps camera
- **Camera**: Added `camera::get_active` to get the active camera.
- **Client**: When using the native client, you can now use `--window-x` and `--window-y` to specify the window position, as well as `--window-width` and `--window-height` to specify the window size.

#### Examples

Expand Down Expand Up @@ -153,6 +155,7 @@ These PRs are not directly user-facing, but improve the development experience.
- **Logging**: The logging output levels have been tweaked to better communicate the state of the system at any given time.
- **Debugger**: The debugger has been improved with a resizable sidebar, a scrollable view, and a component filter.
- **Animation**: The animation graph is now executed on the server as well.
- **CLI**: The `ambient new` command now takes several parameters to customize the resulting generation.

### Fixed

Expand Down
5 changes: 4 additions & 1 deletion Cargo.lock

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

24 changes: 19 additions & 5 deletions app/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub mod server;
mod package_path;
pub use package_path::*;

use self::assets::AssetCommand;
use self::{assets::AssetCommand, new_package::NewPackageCli};

#[derive(Parser, Clone)]
#[command(author, version, about, long_about = None)]
Expand All @@ -30,10 +30,8 @@ pub enum Commands {
New {
#[command(flatten)]
package: PackageCli,
#[arg(short, long)]
name: Option<String>,
#[arg(long)]
api_path: Option<String>,
#[command(flatten)]
args: NewPackageCli,
},
/// Builds and runs the package locally
Run {
Expand Down Expand Up @@ -149,6 +147,22 @@ pub struct RunCli {
/// Specify a trusted certificate authority
#[arg(long)]
pub ca: Option<PathBuf>,

/// Window position X override
#[arg(long)]
pub window_x: Option<i32>,

/// Window position Y override
#[arg(long)]
pub window_y: Option<i32>,

/// Window width override
#[arg(long)]
pub window_width: Option<u32>,

/// Window height override
#[arg(long)]
pub window_height: Option<u32>,
}

#[derive(Args, Clone, Debug)]
Expand Down
266 changes: 162 additions & 104 deletions app/src/cli/new_package.rs
Original file line number Diff line number Diff line change
@@ -1,51 +1,74 @@
use std::path::Path;

use ambient_native_std::ambient_version;
use ambient_package::SnakeCaseIdentifier;
use anyhow::Context;
use convert_case::Casing;
use clap::{Args, ValueEnum};
use convert_case::{Case, Casing};

use super::PackagePath;

pub(crate) fn handle(
package_path: &PackagePath,
name: Option<&str>,
api_path: Option<&str>,
) -> anyhow::Result<()> {
#[derive(Args, Clone, Debug)]
pub struct NewPackageCli {
#[arg(short, long)]
name: Option<String>,
#[arg(long)]
api_path: Option<String>,
/// This package is being created in an existing Rust workspace,
/// and does not need to have extra files generated for it.
#[arg(long)]
in_workspace: bool,
#[arg(long, value_enum, default_value_t)]
rust: RustTemplate,
}

#[derive(ValueEnum, Clone, Debug, Default)]
pub enum RustTemplate {
/// No template.
None,
/// An empty client/server.
Empty,
/// An empty client with a camera looking at a quad on the server.
#[default]
Quad,
}

pub(crate) fn handle(package_path: &PackagePath, args: &NewPackageCli) -> anyhow::Result<()> {
let Some(package_path) = &package_path.fs_path else {
anyhow::bail!("Cannot create package in a remote directory.");
};

// Build the identifier.
let package_path = if let Some(name) = name {
package_path.join(name)
} else {
package_path.to_owned()
};
let name = args.name.as_deref();
let api_path = args.api_path.as_deref();
let in_workspace = args.in_workspace;

let name = package_path
.file_name()
.and_then(|s| s.to_str())
.context("Package path has no terminating segment")?;
// Build the identifier.
let name = name.unwrap_or(
package_path
.file_name()
.and_then(|s| s.to_str())
.context("Package path has no terminating segment")?,
);

if package_path.is_dir() && std::fs::read_dir(&package_path)?.next().is_some() {
anyhow::bail!("package path {package_path:?} is not empty");
}

let id = name.to_case(convert_case::Case::Snake);
let id = SnakeCaseIdentifier::new(&id).map_err(|e| anyhow::Error::msg(e.to_string()))?;
let id = ambient_package::PackageId::generate();
let snake_case_name = name.to_case(Case::Snake).replace(":", "");

// Create the folders.
let dot_cargo_path = package_path.join(".cargo");
let dot_vscode_path = package_path.join(".vscode");
let src_path = package_path.join("src");
// Build a list of files to write to disk, then write them all at once.
macro_rules! template_file {
($path:expr) => {
include_str!(concat!("new_package_template/", $path))
};
}

std::fs::create_dir_all(&package_path).context("Failed to create package directory")?;
std::fs::create_dir_all(&dot_cargo_path).context("Failed to create .cargo directory")?;
std::fs::create_dir_all(&dot_vscode_path).context("Failed to create .vscode directory")?;
std::fs::create_dir_all(&src_path).context("Failed to create src directory")?;
macro_rules! template_path_and_file {
($path:expr) => {
(Path::new($path), template_file!($path))
};
}

// Write the files to disk.
let ambient_toml = include_str!("new_package_template/ambient.toml")
.replace("{{id}}", id.as_str())
.replace("{{name}}", name)
Expand All @@ -54,92 +77,127 @@ pub(crate) fn handle(
&format!("{}", ambient_native_std::ambient_version().version),
);

let cargo_toml = {
// Special-case creating an example in guest/rust/examples so that it "Just Works".
let segments = package_path.iter().collect::<Vec<_>>();
let (replacement, in_ambient_examples) = match segments
.windows(3)
.position(|w| w == ["guest", "rust", "examples"])
{
Some(i) => {
let number_of_parents = segments.len() - i - 2;
(
format!(
r#"ambient_api = {{ path = "{}api" }}"#,
"../".repeat(number_of_parents)
),
true,
)
}
None => {
let version = ambient_version();
(
if let Some(api_path) = api_path {
log::info!("Ambient path: {}", api_path);
format!("ambient_api = {{ path = {:?} }}", api_path)
} else if let Some(tag) = version.tag() {
log::info!("Ambient tag: {}", tag);
format!("ambient_api = {{ git = \"https://github.com/AmbientRun/Ambient.git\", tag = \"{}\" }}", tag)
} else if !version.revision.is_empty() {
log::info!("Ambient revision: {}", version.revision);
format!("ambient_api = {{ git = \"https://github.com/AmbientRun/Ambient.git\", rev = \"{}\" }}", version.revision)
} else {
log::info!("Ambient version: {}", version.version);
format!("ambient_api = \"{}\"", version.version)
},
false,
)
}
};
let cargo_toml = build_cargo_toml(package_path, api_path, snake_case_name);

let template_cargo_toml = include_str!("new_package_template/Cargo.toml");
let mut template_cargo_toml = template_cargo_toml.replace("{{id}}", id.as_str()).replace(
"ambient_api = { path = \"../../../../guest/rust/api\" }",
&replacement,
);
let mut files_to_write = vec![
// root
(Path::new("ambient.toml"), ambient_toml.as_str()),
];

if in_ambient_examples {
template_cargo_toml = template_cargo_toml.replace(
r#"version = "0.0.1""#,
"rust-version = {workspace = true}\nversion = {workspace = true}",
)
// Add all common Rust files.
match args.rust {
RustTemplate::None => {}
RustTemplate::Empty | RustTemplate::Quad => {
files_to_write.push((Path::new("Cargo.toml"), cargo_toml.as_str()));

if !in_workspace {
files_to_write.extend_from_slice(&[
template_path_and_file!("rust-toolchain.toml"),
template_path_and_file!(".cargo/config.toml"),
template_path_and_file!(".vscode/settings.json"),
]);
}
}
}

template_cargo_toml
};
// Add specific Rust files.
match args.rust {
RustTemplate::None => {}
RustTemplate::Empty => {
files_to_write.extend_from_slice(&[
template_path_and_file!("src/client.rs"),
(
Path::new("src/server.rs"),
template_file!("src/server_empty.rs"),
),
]);
}
RustTemplate::Quad => {
files_to_write.extend_from_slice(&[
template_path_and_file!("src/client.rs"),
(
Path::new("src/server.rs"),
template_file!("src/server_quad.rs"),
),
]);
}
}

macro_rules! include_template {
($path:expr) => {
(
Path::new($path),
include_str!(concat!("new_package_template/", $path)),
)
};
if !in_workspace {
files_to_write.extend_from_slice(&[
template_path_and_file!(".gitignore"),
template_path_and_file!(".vscode/launch.json"),
]);
}

let files_to_write = &[
// root
(Path::new("ambient.toml"), ambient_toml.as_str()),
(Path::new("Cargo.toml"), cargo_toml.as_str()),
include_template!(".gitignore"),
include_template!("rust-toolchain.toml"),
// .cargo
include_template!(".cargo/config.toml"),
// .vscode
include_template!(".vscode/settings.json"),
include_template!(".vscode/launch.json"),
include_template!(".vscode/extensions.json"),
// src
include_template!("src/client.rs"),
include_template!("src/server.rs"),
];
for (path, contents) in files_to_write {
let path = package_path.join(path);
if let Some(parent) = path.parent() {
if !parent.exists() {
std::fs::create_dir_all(parent)?;
}
}

for (filename, contents) in files_to_write {
std::fs::write(package_path.join(filename), contents)
.with_context(|| format!("Failed to create {filename:?}"))?;
std::fs::write(&path, contents).with_context(|| format!("Failed to create {path:?}"))?;
}

log::info!("Package \"{name}\" with id `{id}` created at {package_path:?}");
log::info!("Package \"{name}\" created at {package_path:?}");

Ok(())
}

fn build_cargo_toml(
package_path: &std::path::PathBuf,
api_path: Option<&str>,
snake_case_name: String,
) -> String {
let segments = package_path.iter().collect::<Vec<_>>();
let (replacement, in_ambient_examples) = match segments
.windows(2)
.position(|w| w == ["guest", "rust"])
{
Some(i) => {
let number_of_parents = segments.len() - i - 2;
(
format!(
r#"ambient_api = {{ path = "{}api" }}"#,
"../".repeat(number_of_parents)
),
true,
)
}
None => {
let version = ambient_version();
(
if let Some(api_path) = api_path {
log::info!("Ambient path: {}", api_path);
format!("ambient_api = {{ path = {:?} }}", api_path)
} else if let Some(tag) = version.tag() {
log::info!("Ambient tag: {}", tag);
format!("ambient_api = {{ git = \"https://github.com/AmbientRun/Ambient.git\", tag = \"{}\" }}", tag)
} else if !version.revision.is_empty() {
log::info!("Ambient revision: {}", version.revision);
format!("ambient_api = {{ git = \"https://github.com/AmbientRun/Ambient.git\", rev = \"{}\" }}", version.revision)
} else {
log::info!("Ambient version: {}", version.version);
format!("ambient_api = \"{}\"", version.version)
},
false,
)
}
};
let template_cargo_toml = include_str!("new_package_template/Cargo.toml");
let mut template_cargo_toml = template_cargo_toml
.replace("{{id}}", &snake_case_name)
.replace(
"ambient_api = { path = \"../../../../guest/rust/api\" }",
&replacement,
);
if in_ambient_examples {
template_cargo_toml = template_cargo_toml.replace(
r#"version = "0.0.1""#,
"rust-version = {workspace = true}\nversion = {workspace = true}",
)
}
template_cargo_toml
}
Loading

0 comments on commit 8a15505

Please sign in to comment.