Skip to content

Commit 26e99fe

Browse files
JasonAtClockworkgefjonbfopsjdetter
authored
Added the Unreal SDK work for codegen, testing, and the plugin (#3223)
# Description of Changes Closes #3219 This adds the Unreal SDK, the new Unreal test cases, updates the test runner to handle Unreal, codegen updates for Unreal, and a QuickStart Chat. # API and ABI breaking changes No breaking changes. # Expected complexity level and risk 2 - This impacts the subcommand generate.rs to include unrealcpp and crates/testing to expand for Unreal # Testing - [x] Run the new Unreal tests - [x] Run any previous automation testing - with all the changes to generate/testing I'm uncertain if there is an impact - [x] Review the new CLI generate documentation changes --------- Co-authored-by: Phoebe Goldman <[email protected]> Co-authored-by: Zeke Foppa <[email protected]> Co-authored-by: Zeke Foppa <[email protected]> Co-authored-by: John Detter <[email protected]>
1 parent c83f55f commit 26e99fe

File tree

662 files changed

+77654
-57
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

662 files changed

+77654
-57
lines changed

.github/workflows/ci.yml

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,10 @@ jobs:
103103
run: |
104104
sudo mkdir /stdb
105105
sudo chmod 777 /stdb
106-
106+
107107
- name: Run cargo test
108-
run: cargo test --all
108+
#Note: Unreal tests will be run separately
109+
run: cargo test --all -- --skip unreal
109110

110111
- name: Check that the test outputs are up-to-date
111112
run: bash tools/check-diff.sh
@@ -243,6 +244,90 @@ jobs:
243244
cargo run --features github-token-auth --target ${{ matrix.target }} -p spacetimedb-update -- self-install --root-dir="${ROOT_DIR}" --yes
244245
"${ROOT_DIR}"/spacetime --root-dir="${ROOT_DIR}" help
245246
247+
unreal_engine_tests:
248+
name: Unreal Engine Tests
249+
# This can't go on e.g. ubuntu-latest because that runner runs out of disk space. ChatGPT suggested that the general solution tends to be to use
250+
# a custom runner.
251+
runs-on: spacetimedb-runner
252+
container:
253+
image: ghcr.io/epicgames/unreal-engine:dev-5.6
254+
credentials:
255+
# Note(bfops): I don't think that `github.actor` needs to match the user that the token is for, because I'm using a token for my account and
256+
# it seems to be totally happy.
257+
# However, the token needs to be for a user that has access to the EpicGames org (see
258+
# https://dev.epicgames.com/documentation/en-us/unreal-engine/downloading-source-code-in-unreal-engine?application_version=5.6)
259+
username: ${{ github.actor }}
260+
password: ${{ secrets.GHCR_TOKEN }}
261+
# Run as root because otherwise we get permission denied for various directories inside the container. I tried doing dances to allow it to run
262+
# without this (reassigning env vars and stuff), but was unable to get it to work and it felt like an uphill battle.
263+
options: --user 0:0
264+
steps:
265+
# Uncomment this before merging so that it will run properly if run manually through the GH actions flow. It was playing weird with rolled back
266+
# commits though.
267+
# - name: Find Git ref
268+
# env:
269+
# GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
270+
# shell: bash
271+
# run: |
272+
# PR_NUMBER="${{ github.event.inputs.pr_number || null }}"
273+
# if test -n "${PR_NUMBER}"; then
274+
# GIT_REF="$( gh pr view --repo clockworklabs/SpacetimeDB $PR_NUMBER --json headRefName --jq .headRefName )"
275+
# else
276+
# GIT_REF="${{ github.ref }}"
277+
# fi
278+
# echo "GIT_REF=${GIT_REF}" >>"$GITHUB_ENV"
279+
- name: Checkout sources
280+
uses: actions/checkout@v4
281+
with:
282+
ref: ${{ env.GIT_REF }}
283+
- uses: dsherret/rust-toolchain-file@v1
284+
- name: Run Unreal Engine tests
285+
working-directory: sdks/unreal
286+
env:
287+
UE_ROOT_PATH: /home/ue4/UnrealEngine
288+
run: |
289+
290+
apt-get update
291+
apt-get install -y acl curl ca-certificates
292+
293+
REPO="$GITHUB_WORKSPACE"
294+
# Let ue4 read/write the workspace & tool caches without changing ownership
295+
for p in "$REPO" "${RUNNER_TEMP:-/__t}" "${RUNNER_TOOL_CACHE:-/__t}"; do
296+
[ -d "$p" ] && setfacl -R -m u:ue4:rwX -m d:u:ue4:rwX "$p" || true
297+
done
298+
299+
# Rust tool caches live under the runner tool cache so they persist
300+
export CARGO_HOME="${RUNNER_TOOL_CACHE:-/__t}/cargo"
301+
export RUSTUP_HOME="${RUNNER_TOOL_CACHE:-/__t}/rustup"
302+
mkdir -p "$CARGO_HOME" "$RUSTUP_HOME"
303+
chown -R ue4:ue4 "$CARGO_HOME" "$RUSTUP_HOME"
304+
305+
# Make sure the UE build script is executable (and parents traversable)
306+
UE_DIR="${UE_ROOT_PATH:-/home/ue4/UnrealEngine}"
307+
chmod a+rx "$UE_DIR" "$UE_DIR/Engine" "$UE_DIR/Engine/Build" "$UE_DIR/Engine/Build/BatchFiles/Linux" || true
308+
chmod a+rx "$UE_DIR/Engine/Build/BatchFiles/Linux/Build.sh" || true
309+
310+
# Run the build & tests as ue4 (who owns the UE tree)
311+
sudo -E -H -u ue4 env \
312+
HOME=/home/ue4 \
313+
XDG_CONFIG_HOME=/home/ue4/.config \
314+
CARGO_HOME="$CARGO_HOME" \
315+
RUSTUP_HOME="$RUSTUP_HOME" \
316+
PATH="$CARGO_HOME/bin:$PATH" \
317+
bash -lc '
318+
set -euxo pipefail
319+
# Install rustup for ue4 if needed (uses the shared caches)
320+
if ! command -v cargo >/dev/null 2>&1; then
321+
curl -sSf https://sh.rustup.rs | sh -s -- -y
322+
fi
323+
rustup show >/dev/null
324+
git config --global --add safe.directory "$GITHUB_WORKSPACE" || true
325+
326+
cd "$GITHUB_WORKSPACE/sdks/unreal"
327+
cargo --version
328+
cargo test
329+
'
330+
246331
cli_docs:
247332
name: Check CLI docs
248333
permissions: read-all

Cargo.lock

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ members = [
2626
"crates/sats",
2727
"crates/schema",
2828
"sdks/rust",
29+
"sdks/unreal",
2930
"crates/snapshot",
3031
"crates/sqltest",
3132
"crates/sql-parser",

crates/cli/src/subcommands/generate.rs

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use clap::parser::ValueSource;
55
use clap::Arg;
66
use clap::ArgAction::Set;
77
use fs_err as fs;
8-
use spacetimedb_codegen::{generate, Csharp, Lang, OutputFile, Rust, TypeScript, AUTO_GENERATED_PREFIX};
8+
use spacetimedb_codegen::{generate, Csharp, Lang, OutputFile, Rust, TypeScript, UnrealCpp, AUTO_GENERATED_PREFIX};
99
use spacetimedb_lib::de::serde::DeserializeWrapper;
1010
use spacetimedb_lib::{sats, RawModuleDef};
1111
use spacetimedb_schema;
@@ -25,7 +25,7 @@ use std::io::Read;
2525
pub fn cli() -> clap::Command {
2626
clap::Command::new("generate")
2727
.about("Generate client files for a spacetime module.")
28-
.override_usage("spacetime generate --lang <LANG> --out-dir <DIR> [--project-path <DIR> | --bin-path <PATH>]")
28+
.override_usage("spacetime generate --lang <LANG> --out-dir <DIR> [--project-path <DIR> | --bin-path <PATH> | --module-name <MODULE_NAME> | --uproject-dir <DIR>]")
2929
.arg(
3030
Arg::new("wasm_file")
3131
.value_parser(clap::value_parser!(PathBuf))
@@ -57,17 +57,32 @@ pub fn cli() -> clap::Command {
5757
.arg(
5858
Arg::new("out_dir")
5959
.value_parser(clap::value_parser!(PathBuf))
60-
.required(true)
6160
.long("out-dir")
6261
.short('o')
63-
.help("The system path (absolute or relative) to the generate output directory"),
62+
.help("The system path (absolute or relative) to the generate output directory")
63+
.required_if_eq("lang", "rust")
64+
.required_if_eq("lang", "csharp")
65+
.required_if_eq("lang", "typescript"),
66+
)
67+
.arg(
68+
Arg::new("uproject_dir")
69+
.value_parser(clap::value_parser!(PathBuf))
70+
.long("uproject-dir")
71+
.help("Path to the Unreal project directory, replaces --out-dir for Unreal generation (only used with --lang unrealcpp)")
72+
.required_if_eq("lang", "unrealcpp")
6473
)
6574
.arg(
6675
Arg::new("namespace")
6776
.default_value("SpacetimeDB.Types")
6877
.long("namespace")
6978
.help("The namespace that should be used"),
7079
)
80+
.arg(
81+
Arg::new("module_name")
82+
.long("module-name")
83+
.help("The module name that should be used for DLL export macros (required for lang unrealcpp)")
84+
.required_if_eq("lang", "unrealcpp")
85+
)
7186
.arg(
7287
Arg::new("lang")
7388
.required(true)
@@ -86,6 +101,11 @@ pub fn cli() -> clap::Command {
86101
)
87102
.arg(common_args::yes())
88103
.after_help("Run `spacetime help publish` for more detailed information.")
104+
.group(
105+
clap::ArgGroup::new("output_dir")
106+
.args(["out_dir", "uproject_dir"])
107+
.required(true)
108+
)
89109
}
90110

91111
pub async fn exec(config: Config, args: &clap::ArgMatches) -> anyhow::Result<()> {
@@ -101,16 +121,21 @@ pub async fn exec_ex(
101121
let project_path = args.get_one::<PathBuf>("project_path").unwrap();
102122
let wasm_file = args.get_one::<PathBuf>("wasm_file").cloned();
103123
let json_module = args.get_many::<PathBuf>("json_module");
104-
let out_dir = args.get_one::<PathBuf>("out_dir").unwrap();
105124
let lang = *args.get_one::<Language>("lang").unwrap();
106125
let namespace = args.get_one::<String>("namespace").unwrap();
126+
let module_name = args.get_one::<String>("module_name");
107127
let force = args.get_flag("force");
108128
let build_options = args.get_one::<String>("build_options").unwrap();
109129

110130
if args.value_source("namespace") == Some(ValueSource::CommandLine) && lang != Language::Csharp {
111131
return Err(anyhow::anyhow!("--namespace is only supported with --lang csharp"));
112132
}
113133

134+
let out_dir = args
135+
.get_one::<PathBuf>("out_dir")
136+
.or_else(|| args.get_one::<PathBuf>("uproject_dir"))
137+
.unwrap();
138+
114139
let module: ModuleDef = if let Some(mut json_module) = json_module {
115140
let DeserializeWrapper::<RawModuleDef>(module) = if let Some(path) = json_module.next() {
116141
serde_json::from_slice(&fs::read(path)?)?
@@ -136,11 +161,19 @@ pub async fn exec_ex(
136161
let mut paths = BTreeSet::new();
137162

138163
let csharp_lang;
164+
let unreal_cpp_lang;
139165
let gen_lang = match lang {
140166
Language::Csharp => {
141167
csharp_lang = Csharp { namespace };
142168
&csharp_lang as &dyn Lang
143169
}
170+
Language::UnrealCpp => {
171+
unreal_cpp_lang = UnrealCpp {
172+
module_name: module_name.as_ref().unwrap(),
173+
uproject_dir: out_dir,
174+
};
175+
&unreal_cpp_lang as &dyn Lang
176+
}
144177
Language::Rust => &Rust,
145178
Language::TypeScript => &TypeScript,
146179
};
@@ -156,9 +189,15 @@ pub async fn exec_ex(
156189
paths.insert(path);
157190
}
158191

192+
// For Unreal, we want to clean up just the module directory, not the entire uproject directory tree.
193+
let cleanup_root = match lang {
194+
Language::UnrealCpp => out_dir.join("Source").join(module_name.as_ref().unwrap()),
195+
_ => out_dir.clone(),
196+
};
197+
159198
// TODO: We should probably just delete all generated files before we generate any, rather than selectively deleting some afterward.
160199
let mut auto_generated_buf: [u8; AUTO_GENERATED_PREFIX.len()] = [0; AUTO_GENERATED_PREFIX.len()];
161-
let files_to_delete = walkdir::WalkDir::new(out_dir)
200+
let files_to_delete = walkdir::WalkDir::new(&cleanup_root)
162201
.into_iter()
163202
.map(|entry_result| {
164203
let entry = entry_result?;
@@ -213,17 +252,19 @@ pub enum Language {
213252
Csharp,
214253
TypeScript,
215254
Rust,
255+
UnrealCpp,
216256
}
217257

218258
impl clap::ValueEnum for Language {
219259
fn value_variants<'a>() -> &'a [Self] {
220-
&[Self::Csharp, Self::TypeScript, Self::Rust]
260+
&[Self::Csharp, Self::TypeScript, Self::Rust, Self::UnrealCpp]
221261
}
222262
fn to_possible_value(&self) -> Option<PossibleValue> {
223263
Some(match self {
224264
Self::Csharp => clap::builder::PossibleValue::new("csharp").aliases(["c#", "cs"]),
225265
Self::TypeScript => clap::builder::PossibleValue::new("typescript").aliases(["ts", "TS"]),
226266
Self::Rust => clap::builder::PossibleValue::new("rust").aliases(["rs", "RS"]),
267+
Self::UnrealCpp => PossibleValue::new("unrealcpp").aliases(["uecpp", "ue5cpp", "unreal"]),
227268
})
228269
}
229270
}
@@ -236,6 +277,9 @@ impl Language {
236277
Language::TypeScript => {
237278
// TODO: implement formatting.
238279
}
280+
Language::UnrealCpp => {
281+
// TODO: implement formatting.
282+
}
239283
}
240284

241285
Ok(())

0 commit comments

Comments
 (0)