0.4 comes with a few big changes, this chapter aims to facilitate the transition.
Imports
Let's start small, prelude and internal no longer exist, you just have to replace all shipyard::prelude
and shipyard::internal
by shipyard
.
Systems
Following an issue opened by @cart, systems will become functions instead of an instance of the System
trait.
To make this work, borrowing is now done with the actual types you get when you borrow a storage instead of using references.
In 0.3:
struct MySystem;
impl<'sys> System<'sys> for MySystem {
type Data = (
EntitiesMut,
&mut usize,
);
fn run((mut entities, mut usizes): <Self::Data as SystemData<'sys>>::View) {}
}
// or with the macro
#[system(MySystem)]
fn run(mut entities: &mut Entities, mut usizes: &mut usize) {}
In 0.4:
fn my_system(mut entities: EntitiesViewMut, mut usizes: ViewMut<usize>) {}
This change also affects run
and borrow
.
World::run_system
is no longer needed and you can run systems with run
directly.
world.run(my_system);
// and closures still work
world.run(|mut entities: EntitiesViewMut, mut usizes: ViewMut<usize>| {});
run
has the same return type as the system or closure and it doesn't require any tuple most of the time.
Here's the complete list:
0.3 | 0.4 |
---|---|
AllStorages / &mut AllStorages | AllStoragesViewMut |
Entities / &Entities | EntitiesView |
EntitiesMut / &mut Entities | EntitiesViewMut |
&T | View<T> |
&mut T | ViewMut<T> |
ThreadPool / &ThreadPool | ThreadPoolView |
Unique<&T> | UniqueView<T> |
Unique<&mut T> | UniqueViewMut<T> |
NonSend<&T> | NonSend<View<T>> |
NonSend<&mut T> | NonSend<ViewMut<T>> |
Unique<NonSend<&T>> | NonSend<UniqueView<T>> |
Unique<NonSend<&mut T>> | NonSend<UniqueViewMut<T>> |
FakeBorrow<T> | FakeBorrow<T> |
NonSync
and NonSendSync
follow the same pattern as NonSend
.
Macro
The system proc macro doesn't exist anymore. With the new system design the advantage it provides are not great enough to justify it.
Workloads
The ugly
Workloads are the only one suffering a downgrade. You'll have to give all systems twice to the function plus a few things.
In 0.3:
world.add_workload<(Sys1, Sys2), _>("Workload1");
In 0.4:
world
.add_workload("Workload1")
.with_system((
|world: &World| world.try_run(sys1),
sys1
))
.with_system((
|world: &World| world.try_run(sys2),
sys2
))
.build();
// with a macro
world
.add_workload("Workload1")
.with_system(system!(sys1))
.with_system(system!(sys2))
.build();
⚠️ The two arguments are wrapped in a tuple.
This repetition will disappear someday but I don't expect it to be soon.
You don't have to use a closure, any function with &World
as argument and returning Result<(), shipyard::error::Run>
are valid.
It's very important to pass the same function twice, the workload might always error if this isn't the case.
The good
Workloads don't come with only a downgrade. You can now return errors from systems inside workloads.
#[derive(Debug)]
struct TerribleError;
impl Display for TerribleError {
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
Debug::fmt(self, fmt)
}
}
impl Error for TerribleError {}
fn my_sys(mut entities: EntitiesViewMut) -> Result<(), TerribleError> {
Err(TerribleError)
}
fn main() {
use shipyard::error::{Run, RunWorkload};
let world = World::new();
world
.add_workload("May fail")
.with_system((
|world: &World| world.try_run(my_sys)?.map_err(Run::from_custom),
my_sys,
))
.build();
match world.try_run_default().map_err(RunWorkload::custom_error) {
Err(Some(error)) => {
assert!(error.is::<TerribleError>());
}
_ => {}
}
}
The error has to be anonymized so you'll get back a Box<dyn Error + Send>
with std and a Box<dyn Any + Send>
with no_std.
Workloads stop at the first error encountered, just like 0.3.
You can also use the try_system!
macro the same way as system!
.
world
.add_workload("May fail")
.with_system(try_system!(my_sys))
.build();
It'll generate the same code as above.
Iterator
You can now use std::iter::Iterator
and for loop
with views without having to call into_iter
.
All iteration code from 0.3 will still work.
fn my_sys((mut usizes, u32s): (ViewMut<usize>, View<u32>)) {
for (i, &j) in (&mut usizes, &u32s).iter() {
*i += j as usize;
}
}
Get
The GetComponent
trait has been renamed Get
.
What follows is only true for 0.4. 0.5 went back to get
returning a Result
.
Get::get
has been renamed try_get
and a new get
method has been added to unwrap errors.
If you used get
followed by unwrap
, you can simply remove the unwrap
.
If you used another error handling method you'll have to replace get
by try_get
.