Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions examples/python_example/src/python_example/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from example.imports.app import App, Commands, Query, QueryFor_Mut, QueryFor_With, Schedule_Update, System

class Example(example.Example):
def setup(self):
def setup(self, app: App):
spin_cube = System("spin-cube")
spin_cube.add_query([
QueryFor_Mut("bevy_transform::components::transform::Transform"),
Expand All @@ -20,7 +20,6 @@ def setup(self):
QueryFor_With("python::MyComponent")
])

app = App()
app.add_systems(Schedule_Update(), [my_system, spin_cube])

# Spin speed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,12 @@ def my_system(self, commands: app.Commands, query: app.Query) -> None:
raise NotImplementedError

@abstractmethod
def setup(self) -> None:
def setup(self, app: app.App) -> None:
"""
This function is called once on startup for each WASM component (Not Bevy component).
This method is called once on startup for each WASM component (Not Bevy component).

In this method you should register and configure `system`s via the `app` provided as a
parameter.
"""
raise NotImplementedError

151 changes: 141 additions & 10 deletions examples/python_example/src/python_example/example/imports/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ def add_commands(self) -> None:
raise NotImplementedError
def add_query(self, query: List[QueryFor]) -> None:
"""
Adds a single system-param
add-single: func(query: list<query-for>);
Adds a query system-param
"""
raise NotImplementedError
Expand Down Expand Up @@ -128,20 +130,82 @@ def __exit__(self, exc_type: type[BaseException] | None, exc_value: BaseExceptio
class App:
"""
A mod, similar to bevy::App

This is an interface through which mods may interact with the bevy app.
"""

def __init__(self) -> None:
def add_systems(self, schedule: Schedule, systems: List[System]) -> None:
"""
Construct an new App: an interface through which mods may interact with the bevy world.

Each mod may only do this once inside its setup function call. Attempting to do this
twice or outside setup will trap.
Adds systems to the mod
"""
raise NotImplementedError
def __enter__(self) -> Self:
"""Returns self"""
return self

def __exit__(self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None) -> bool | None:
"""
Release this resource.
"""
raise NotImplementedError

def add_systems(self, schedule: Schedule, systems: List[System]) -> None:

class Entity:
"""
An identifier for an entity.
"""

def __enter__(self) -> Self:
"""Returns self"""
return self

def __exit__(self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None) -> bool | None:
"""
Adds systems to the mod
Release this resource.
"""
raise NotImplementedError


class EntityCommands:
"""
A list of commands that will be run to modify an `entity`.
"""

def id(self) -> Entity:
"""
Returns the identifier for this entity
"""
raise NotImplementedError
def insert(self, bundle: List[Tuple[str, str]]) -> None:
"""
Adds a `bundle` of components to the entity.

This will overwrite any previous value(s) of the same component type.
See `insert_if_new` to keep the old value instead.
"""
raise NotImplementedError
def remove(self, bundle: List[str]) -> None:
"""
Adds a `bundle` of components to the entity without overwriting.

This is the same as `insert`, but in case of duplicate components
will leave the old values instead of replacing them with new ones.
insert-if-new: func(bundle: bundle);
Removes a Bundle of components from the entity if it exists.
"""
raise NotImplementedError
def despawn(self) -> None:
"""
Despawns the entity.

This will emit a warning if the entity does not exist.
"""
raise NotImplementedError
def try_despawn(self) -> None:
"""
Despawns the entity.

Unlike `despawn`, this will not emit a warning if the entity does not exist.
"""
raise NotImplementedError
def __enter__(self) -> Self:
Expand All @@ -157,10 +221,35 @@ def __exit__(self, exc_type: type[BaseException] | None, exc_value: BaseExceptio

class Commands:
"""
A commands system param
A `command` queue system param to perform structural changes to the world.

Since each command requires exclusive access to the world,
all queued commands are automatically applied in sequence.

Each command can be used to modify the world in arbitrary ways:
- spawning or despawning entities
- inserting components on new or existing entities
- etc.
"""

def spawn(self, components: List[Tuple[str, str]]) -> None:
def spawn_empty(self) -> EntityCommands:
"""
Spawns a new empty `entity` and returns its corresponding `entity-commands`.
"""
raise NotImplementedError
def spawn(self, bundle: List[Tuple[str, str]]) -> EntityCommands:
"""
Spawns a new `entity` with the given components
and returns the entity's corresponding `entity-commands`.
"""
raise NotImplementedError
def entity(self, entity: Entity) -> EntityCommands:
"""
Returns the `entity-commands` for the given `entity`.

This method does not guarantee that commands queued by the returned `entity-commands`
will be successful, since the entity could be despawned before they are executed.
"""
raise NotImplementedError
def __enter__(self) -> Self:
"""Returns self"""
Expand Down Expand Up @@ -198,12 +287,54 @@ def __exit__(self, exc_type: type[BaseException] | None, exc_value: BaseExceptio
raise NotImplementedError


class QueryResult:
"""
A query system param
"""

def entity(self) -> Entity:
"""
Returns the entity id for the query
"""
raise NotImplementedError
def component(self, index: int) -> Component:
"""
Gets the component at the specified index.

So for example, if your system was registered as:

```
let system = System::new("example");
system.add_
app.add_system(system);
"""
raise NotImplementedError
def __enter__(self) -> Self:
"""Returns self"""
return self

def __exit__(self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None) -> bool | None:
"""
Release this resource.
"""
raise NotImplementedError


class Query:
"""
A single system param.

This is identical to query but will only match a single entity.

If this query doesn't match just one entity, the system will be skipped.
resource single {
/// Evaluates and returns the query result
get: func() -> query-result;
}
A query system param
"""

def iter(self) -> Optional[List[Component]]:
def iter(self) -> Optional[QueryResult]:
"""
Evaluates and returns the next query results
"""
Expand Down
22 changes: 11 additions & 11 deletions examples/simple/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,19 @@ use serde::{Deserialize, Serialize};
struct GuestComponent;

impl Guest for GuestComponent {
fn setup() {
// Define an example system with commands that runs on startup
fn setup(app: App) {
// Define an example system with commands that run on startup
let spawn_entities = System::new("spawn-entities");
spawn_entities.add_commands();
app.add_systems(&Schedule::ModStartup, &[&spawn_entities]);

// Define another new system that queries for entities with a Transform and a Marker component
let spin_cube = System::new("spin-cube");
spin_cube.add_query(&[
QueryFor::Mut("bevy_transform::components::transform::Transform".to_string()),
QueryFor::With("host_example::MyMarker".to_string()),
]);

// Register the systems to run in the Update schedule
let app = App::new();
app.add_systems(&Schedule::ModStartup, vec![spawn_entities]);
app.add_systems(&Schedule::Update, vec![spin_cube]);
app.add_systems(&Schedule::Update, &[&spin_cube]);
}

fn spawn_entities(commands: Commands) {
Expand All @@ -57,15 +54,18 @@ impl Guest for GuestComponent {
}

fn spin_cube(query: Query) {
while let Some(components) = query.iter() {
// Get and deserialize the first component
let mut transform: Transform = from_json(&components[0].get());
while let Some(results) = query.iter() {
// Get the first component
let component = results.component(0);

// Deserialize the first component
let mut transform: Transform = from_json(&component.get());

// Spin the cube
transform.rotate(Quat::from_rotation_y(0.025));

// Set the new component value
components[0].set(&to_json(&transform));
component.set(&to_json(&transform));
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ run-host-example:

# Requires `poetry` to run
build-example-python:
cd examples/python_example/src/python_example && poetry run componentize-py --wit-path ../../wit/ --world example componentize app -o ../../../host_example/assets/mods/python.wasm
cd examples/python_example && poetry run componentize-py --wit-path wit --world example componentize app -o ../host_example/assets/mods/python.wasm

# Create the bindings for the python example
example-bindings-python:
cd examples/python_example && poetry run componentize-py --wit-path wit/ --world example bindings src/python_example
cd examples/python_example && rm -rf ./src/example && poetry run componentize-py --wit-path wit/ --world example bindings src/python_example

# For the fetching to take effect you must delete the deps folder manually
example-fetch-deps example:
Expand Down
Loading