Giter Club home page Giter Club logo

editor-core's Introduction

Amethyst Game Engine

Build Status Crates.io docs page MIT/Apache Join us on Discord Community forum Reddit Code coverage Lines of Code

Inactively Maintained!

Inactively Maintained

The Amethyst Game Engine has halted its development. Read this post about it: https://amethyst.rs/posts/amethyst--starting-fresh

For 0.15.3 and older, the following Rust version has to be used for compiles to work.

rustup override set 1.47

What is Amethyst?

Amethyst is a data-driven and data-oriented game engine aiming to be fast and as configurable as possible.

Principles

These principles are what makes Amethyst unique and competitive in the world of game engines:

  • Massively parallel architecture.
  • Powered by a correct Entity Component System model.
  • Rapid prototyping with RON files for prefabs and an abstract scripting API.
  • Strong focus on encouraging reusability and clean interfaces.

Why Amethyst?

Extreme Multithreading

Amethyst is based over a very powerful parallel ECS called Specs. This allows games built with Amethyst to maximize the available processing power to run as smoothly and as quickly as possible, without the headaches of multi-threaded programming.

Clean

By design, the Amethyst engine encourages you to write clean and reusable code for your behaviours and data structures. This allows engine users to easily share useful components, thus reducing development time and cost.

Using the ECS architecture, the code of games can be cleanly divided between data and behaviour, making it easy to understand what is going on, even if the game is running on a massive 64-core processor.

Community

  • Discord - Announcements, help, useful information, general discussion.

Features

Please visit the features page for a list of features Amethyst provides.

Navigation

Usage

While the engine can be hard to use at times, we made a lot of documentation that will teach you everything you need to use Amethyst comfortably.

If you don't understand a part of the documentation, please let us know. Join us on Discord or open an issue; we are always happy to help!

Getting started

Before you begin

This repository uses Git LFS for some files used in examples. If you intend to run the examples, make sure you have LFS installed in your system before you clone. You can download it and read the installation instructions at Git LFS home page.

Examples

To compile any of the examples run:

$ cargo run -p name_of_example

All available examples are listed under the examples directory.

For a full-blown "Hello World" tutorial check out the Getting Started chapter in the book.

Showcase games

Our official showcase games demonstrate larger, continuously developed game projects made with Amethyst:

For more examples see Games Made With Amethyst topic on the community forum for some good sources of inspiration.

Dependencies

If you are compiling on Linux, make sure to install the dependencies below.

Arch Linux

pacman -Syu grep gcc pkgconf openssl alsa-lib cmake make python3 freetype2 awk libxcb

Debian/Ubuntu

apt install gcc pkg-config openssl libasound2-dev cmake build-essential python3 libfreetype6-dev libexpat1-dev libxcb-composite0-dev libssl-dev libx11-dev libfontconfig1-dev

Fedora

dnf install pkgconfig gcc openssl-devel alsa-lib-devel cmake make gcc-c++ freetype-devel expat-devel libxcb-devel libX11-devel

openSUSE

zypper install gcc pkg-config libopenssl-devel alsa-devel cmake gcc-c++ python3 freetype2-devel libexpat-devel libxcb-devel

Nix/NixOS

In your project's root folder, create a file shell.nix with the following contents:

let
  mozilla = import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz);
  nixpkgs = import <nixpkgs> { overlays = [ mozilla ]; };
in

  with nixpkgs;

  mkShell {
    buildInputs = [
      alsaLib
      cmake
      freetype
      latest.rustChannels.stable.rust
      expat
      openssl
      pkgconfig
      python3
      vulkan-validation-layers
      xlibs.libX11
    ];

    APPEND_LIBRARY_PATH = lib.makeLibraryPath [
      vulkan-loader
      xlibs.libXcursor
      xlibs.libXi
      xlibs.libXrandr
    ];

    shellHook = ''
      export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$APPEND_LIBRARY_PATH"
    '';
  }

Other

See your distribution-specific installation process for the equivalent dependencies.

Please note that you need to have a functional graphics driver installed. If you get a panic about the renderer unable to create the context when trying to run an example, a faulty driver installation could be the issue.

Building Documentation

You can build the book locally with:

cargo install mdbook
mdbook build book

If you're actively editing the book, it's easiest to run:

mdbook serve book

and navigate to http://localhost:3000. The text itself can be found in book/html/index.html. For more information, please see the mdBook project.

To generate the API documentation locally, do:

$ cargo doc

The API reference can be found in target/doc/amethyst/index.html.

Questions/Help

Amethyst supports only the latest stable release of Rust. Use the nightly and beta channels with this project at your own risk.

If you have a question, please check out the FAQ before asking. Chances are, the solution to your problem is already present there. If you still need help, feel free to ask on our Discord server.

Other places you might want to check out are r/rust_gamedev and the #rust-gamedev IRC.

Contributing

Note: Any interaction with the Amethyst project is subject to our Code of Conduct.

Amethyst is a community-based project that welcomes contributions from anyone. If you're interested in helping out, please read the contribution guidelines before getting started.

We have a good first issue category that groups all issues or feature requests that can be made without having an extensive knowledge of Rust or Amethyst. Working on those issues is a good, if not the best, way to learn.

If you think you are not ready to code yet, you can still contribute by reviewing code written by other members of the community. Code reviews ensure that code merged into Amethyst is of the highest quality as possible. Pull requests that are available for reviews can be found here.

If for some reason we don't have any open PRs in need of a review nor any good first issues (that would be a good thing), feel free to consult our issue tracker.

Backers

Thank you to all our backers! ๐Ÿ™ Become a backer

Sponsors

Amethyst is supported by:

License

Amethyst is free and open source software distributed under the terms of both the MIT License and the Apache License 2.0.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

editor-core's People

Contributors

erlend-sh avatar flappybug avatar khionu avatar mh84 avatar mvesterli avatar randompoison avatar velfi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

editor-core's Issues

trait bound not satisfied

I'm trying this tool:

// devtool setup
    let components = type_set![];
    let resources = type_set![];
    let editor_bundle = SyncEditorBundle::new()
        .sync_default_types()
        .sync_components(&components)
        .sync_resources(&resources);

let game_data = GameDataBuilder::default()
        .with(PrefabLoaderSystem::<MyPrefabData>::default(), "", &[])
        .with_bundle(AnimationBundle::<AnimationId, Transform>::new(
            "head_animation_control_system",
            "head_sampler_interpolation_system",
        ))?.with_bundle(TransformBundle::new().with_dep(&["head_sampler_interpolation_system"]))?
        .with_bundle(FPSCounterBundle::default())?
        .with_bundle(InputBundle::<String, String>::new())?
        .with_bundle(UiBundle::<String, String>::new())?
        .with(Processor::<Source>::new(), "source_processor", &[])
        .with(UiEventHandlerSystem::new(), "ui_event_handler", &[])
        .with_basic_renderer(display_config_path, DrawShaded::<PosNormTex>::new(), true)?


        // here from example โ†“
        .with_bundle(editor_bundle)?;
    let mut game = Application::new(resources, EnterScene::default(), game_data)?;
    game.run();
    Ok(())

But got:

error[E0277]: the trait bound `amethyst_editor_sync::SyncEditorBundle<impl amethyst_editor_sync::ReadComponentSet, impl amethyst_editor_sync::ReadComponentSet+amethyst_editor_sync::WriteComponentSet, impl amethyst_editor_sync::ReadResourceSet, impl amethyst_editor_sync::ReadResourceSet+amethyst_editor_sync::WriteResourceSet>: amethyst::amethyst_core::SystemBundle<'_, '_>` is not satisfied
   --> src/main.rs:216:10
    |
216 |         .with_bundle(editor_bundle)?;
    |          ^^^^^^^^^^^ the trait `amethyst::amethyst_core::SystemBundle<'_, '_>` is not implemented for `amethyst_editor_sync::SyncEditorBundle<impl amethyst_editor_sync::ReadComponentSet, impl amethyst_editor_sync::ReadComponentSet+amethyst_editor_sync::WriteComponentSet, impl amethyst_editor_sync::ReadResourceSet, impl amethyst_editor_sync::ReadResourceSet+amethyst_editor_sync::WriteResourceSet>`

error[E0277]: the trait bound `amethyst_editor_sync::TypeSet<()>: std::convert::AsRef<std::path::Path>` is not satisfied
   --> src/main.rs:217:20
    |
217 |     let mut game = Application::new(resources, EnterScene::default(), game_data)?;
    |                    ^^^^^^^^^^^^^^^^ the trait `std::convert::AsRef<std::path::Path>` is not implemented for `amethyst_editor_sync::TypeSet<()>`
    |
    = note: required by `<amethyst::CoreApplication<'a, T, E, R>>::new`

error: aborting due to 2 previous errors

I'm in rustc 1.30.1 (1433507eb 2018-11-07), amethyst = "0.9.0" and amethyst-editor-sync = "0.3.0"

Send log output to editor

It would be useful if users could also send log files to their editor, that way the editor can provide a more interactive view for searching and filtering log output.

This should be setup as a logger implementation for the log crate. It should export log lines in an easily parsed format (e.g. JSON structured), and should send logs using the same connection that the editor uses for other data. It should not attempt to filter log output from within the game process, instead allowing all filtering and processing of log data to happen within the editor process.

Panic when resource is missing

Originally reported by @Jojolepro in randomPoison/amethyst-editor#24. The issue is that we use ReadExpect in SyncResourceSystem so that we don't need to enforce the Default bound needed by Read. A better solution that @Jojolepro pointed out is to use Option<Read>, which will give us control over how we handle missing resources.

Update to Amethyst 0.10

People trying to use this crate with the latest version of Amethyst (currently 0.10) are getting compiler errors due to version mismatches between the version of Amethyst that they're using and the version that amethyst-editor-sync is depending on. We need to update to use the latest version and publish a new version of amethyst-editor-sync.

Panic on Linux when no editor running

Original bug report from @Jojolepro:

Using amethyst-editor-sync in a game when the editor is closed leads to a crash.
thread '' panicked at 'Failed to send message: Os { code: 111, kind: ConnectionRefused, message: "Connection refused" }

I should note that I don't see this issue on Windows, and it's a very weird issue considering we're using UDP, which doesn't actually have a concept of connections ๐Ÿค” This might be because we're using UdpSocket::connect.

Note that while this might be easy to solve for UDP, we also want to switch to TCP in order to fix #12, so we'd end up running into the same issue with TCP anyway.

Support editing components

This issue references work that is in ticket #26. It isn't fully blocked by that ticket, but it might be easier to do once #26 is finished.

Following the examples set for #25 and #26 we should also add support for editing components. The following things need to be done to enable such support:

  • Add a new variant to IncomingMessage called ComponentUpdate that contains the ID for the component type, the entity the component is attached to (as a SerializableEntity), and the data for the component (as a serde_json::Value).
  • Rename SyncComponentSystem to ReadComponentSystem and move it into it's own read_component module.
  • Make a new System WriteComponentSystem mirroring WriteResourceSystem and put it in a new write_component module.
  • Rename ComponentSet to ReadComponentSet.
  • Add a new trait WriteComponentSet in the type_set module mirroring the WriteResourceSet trait (which should be created for #26).
  • Rename SyncEditorSystem::deserializer_map to resource_deserializer_map and update SyncEditorBundle accordingly.
  • Add a member component_deserializer_map to SyncEditorSystem that mirrors resource_deserializer_map. It should take the Component ID (i.e. the name used when registering the component) and map it to a Sender<(Entity, serde_json::Value)>. The corresponding WriteComponentSystem should have the Receiver end of the channel.
  • Update SyncComponentBundle::sync_component and sync_components to require that the registered types be Serialize + DeserializeOwned (or ReadComponentSet + WriteComponentSet in the case of sync_components.
  • Add methods read_component and read_components to SyncComponentsBundle that only require the types to be Serialize/ReadComponentSet.

Note that these steps assume that #26 has been completed. If this is being done before #26, you can still follow all the steps here, however you may need read #26 to understand the details for how to support read-only components (i.e. needing both a ReadComponentSet and a WriteComponentSet trait).

I'm happy to answer any questions or offer mentorship for anyone who wants to take on this task ๐Ÿ™‚

Identify read-only components to the editor

This ticket is blocked until #26 and #27 are complete.

As defined in #26 and #27, we support syncing Components/Resources that only implement Serialize, but in a read-only fashion that can't accept edits from the editor. In order to allow editors to provide a better user-experience for read-only components, we should provide information to the editor about which types have edit support vs which ones are read-only.

Ideally, it would be best to provide this sort of metadata once when the connection is established, however we don't currently have any sense of a "connection" between the game and editor. As such, there's no reasonable way to ensure that the game will know to send the information to the editor when the editor first connects. For simplicity, then, we should send this information with every state update that the game sends out.

We should update the SerializedComponent and SerializedResource structs to include some indication of whether or not the types supports editing. A boolean read_only field would be sufficient, but it might be nicer to have an EditMode enum that has variants for ReadOnly and ReadWrite. The approach to take is up to whomever takes on this task.

Logging is broken

One of the things that wasn't finished in #40 when I merged it was updating the setup for the logger. In SyncEditorBundle::new, a sender/receiver pair is created for sending log output, but the receiver half is immediately dropped. As a result, the first time a message is logged after hooking up the editor logger, we immediately get an error and a panic. I need to finish implementing the logger setup for the new bundle/systems so that we can send log output to the editor.

Remove components at runtime

This issue is currently blocked by #27.

Allow the editor to request that an existing component be removed from an entity at runtime.

To do this, add a variant to IncomingMessage called RemoveComponent that specifies the ID of the component type (i.e. the user-provided name of the component) and the entity from which the component should be removed. It probably makes the most sense to add the logic for actually removing the component to WriteComponentSystem, since it already requests write access to the appropriate component storage.

I'm happy to answer any questions or provide mentorship for anyone who wants to take on this task ๐Ÿ™‚

Intermediate types in serialization

In order to properly support serialization for types that contain an Entity, we need a way to create an intermediate type that converts entities and other non-stable data into a serialization-safe form. The idea is that any type that can be directly serialized has itself as the intermediate type, but types that need preprocessing before being serialized can specify a different type to use for serialization, and can be converted to and from that intermediate representation. This expands on the ideas present in saveload, but should provide a more flexible approach that doesn't require all types to be known at compile time.

Background and Motivation

The initial motivation for this idea is that Entity is not Serialize. Not allowing entities to be serialized directly is a deliberate design decision by the specs developers, and one that I think is entirely reasonable. Entities are meant to be ephemeral, and neither specs nor Amethyst make any guarantees about consistency in the actual entity IDs generated at runtime; That is to say, the same logic object in your game world may have a different entity ID each time you run your game. For cases like networking and prefab saving/loading, it's not practical to try to serialize the entity ID directly, since the entity ID won't necessarily be the same between client and server (in the case of networking).

For the purposes of a read-only editor, it's actually fine for us to serialize the entity directly. Since the editor only needs to know the entity values in order to display components by entity, and it will never try to reuse an entity ID across sessions, there's nothing that can break as a result of using entity IDs directly. In fact, for an editor, it's desirable to be able to see the actual entity IDs being used within an active session of a game for debugging purposes.

However, even with a read-only editor we quickly run into limitations resulting from Entity not being Serialize. The biggest of such issues is that we can't serialize components that have an Entity as a member (and therefore we can't view such components in the editor). As a short-term workaround for this we provide the SerializableEntity type, but that solution is very specific to this crate and only serves to obfuscate the larger problem of finding stable IDs to use in place of entity IDs for serialization.

Current Solution: saveload

The currently recommended solution is is the saveload module that specs provides. saveload provides functionality specifying stable "marker" values to be used instead of entity IDs when serializing a group of components. This core concept of allowing the user (or some external system) to provide stable marker values that are specific to the current context works well, however the specific implementation for saveload has some drawbacks that make it unsuitable in my estimation:

  • Only addresses Entity values for component grouping but still doesn't support replacing Entity values that are used within a component. This means that you still can't use saveload to serialize a component that has an Entity as one of its members.
  • All component types must be known at compile time because the implementation is provided over a tuple of component types, e.g. you must specify something like (Transform, Light, MyFoo, MyBar) and only those specified component types will be serialized.
  • Component data is grouped around entities rather than all components of a given type being serialized into a flat array. This setup is useful for things that are meant to be human-facing since grouping an entity's components together is easier to read, but it's an inherently inefficient solution for serializing and storing large amounts of component data.

The proposed solution builds on the approach taken by saveload, but attempts to solve some of the ergonomic and functional issues that it has.

Proposed Solution

Rather than directly using the Serialize impl on a component, we should introduce an intermediate trait SerializeIntermediate which is able to produce a serialization-safe intermediate value:

pub trait SerializeIntermediate {
    type Intermediate: Serialize;

    fn to(&self) -> Self::Intermediate;
    fn from(from: Self::Intermediate) -> Self;
}

Any type that can't be directly serialized (e.g. because it has an Entity member) could instead specify an alternate type that can be used, and the internal serialization mechanism would first convert each instance to the intermediate representation before actually serializing the data.

This solution can work for deserialization as well: As long as the component knows how to convert from the intermediate representation back into the concrete type, then we can handle deserialization by deserializing into the intermediate representation first, then converting back to the main type.

Example: Parent Component

The Parent component is a commonly-used component that can't be serialized (and therefore can't be viewed in the editor) today. Using SerializeIntermediate, we can instead convert to a second type that uses a stable marker to describe the hierarchy:

pub struct ParentIntermediate {
    pub entity: EntityMarker,
}

impl SerializeIntermediate for Parent {
    type Intermediate = ParentIntermediate;

    fn to(&self) -> Self::Intermediate {
        let marker = ...; // Lookup marker corresponding to entity.
        ParentIntermediate {
            entity: marker,
        }
    }

    fn from(from: Self::Intermediate) -> Self {
        let entity = ...; // Lookup entity corresponding to the marker.
        Parent { entity }
    }
}

Note that this example does not demonstrate a proper mechanism for how marker values would be generated or mapped to/from entities. See the next section for discussion on how this can be handled.

Generating Intermediate Values

The question remains of how to handle generating marker values and map them to/from entity IDs. I don't think this is something that should be handled by this crate or the serialization library, since different use cases call for different approaches (i.e. prefabs use the index within the file to determine the entity, game networking may require the server to generate unique IDs and send them to the client, the editor may want to use entity IDs directly, etc.).

Instead, I think it would be enough to allow the SerializeIntermediate trait to provide a SystemData type that can be passed in as a parameter to the conversion functions. This would allow the implementation for each type to perform any case-specific conversion that it cares to do. I suspect that this approach is not going to handle all the necessary use cases, but it simple enough to get us started.

The adjusted SerializeIntermediate would be as follows:

pub trait SerializeIntermediate {
    type Intermediate: Serialize;
    type Data: SystemData;

    fn to(&self, data: Self::Data) -> Self::Intermediate;
    fn from(from: Self::Intermediate, data: Self::Data) -> Self;
}

Lower update rate for sending serialized state

Currently we serialize and send the entire game state to the editor every singe frame. This potentially puts an unnecessary load on the editor, which has to process and re-render the entire game state 60 times per second. In practice we only need to update the editor maybe 5-10 times per second in order to avoid any visual lag for the end user. As such, reducing the rate at which we send the data to the editor could potentially fix some performance issues, specifically randomPoison/amethyst-editor#26.

Note that while most of the game data should only be sent infrequently, we still need to send log data every frame.

Test README examples with rust-skeptic

It's pretty easy to forget to update the code examples in README.md when the API changes, which means the first usage example new users see might not even be correct. In order to catch such issues as quickly as possible, we should setup rust-skeptic. Doing so will ensure that code examples in the README will get tested as part of cargo test (and therefor as part of CI builds) just like any other doc test.

Improved UDP socket binding

Currently we require that the application logic manually create and bind a UDP socket and add it to the world as a resource. This gives the user control over what port the socket is bound to, but ultimately exposes UDP as an implementation detail that we don't want to finalize on.

Instead, we should have the EditorSyncSystem create its own UDP socket, and add a way to configure which port it binds to. By default it should let the system decide what port it binds to (by binding to 0.0.0.0:0), but allow the user to specify a different port/address to bind to. It might also be good to allow the user to override the default port via an environment variable.

Automatic registration of engine types

The engine provides a lot of component and resource types and it quickly becomes a pain to manually remember every one that you need to register when using the editor with your game. It would be better if this crate provided a way to automatically register all the engine-provided types.

For convenience this functionality should be enabled by default, but there should be a way to disable it in case there is any use case where the user wouldn't want to register all the engine types.

Here's a list of types we should start with:

  • AmbientColor
  • Light
  • Named
  • Shape
  • RotationControl
  • Camera
  • Transform
  • GlobalTransform

Macro for registering types for syncing

Originally requested by @Jojolepro in randomPoison/amethyst-editor#28.

A macro for registering components/resources for synchronization would be helpful in cutting down boilerplate when registering a large number of types. Such a macro should define a way to register both components and resources, and should only require that the type be specified for each. It should automatically generate the name by stringifying the type name used for registration (the stringify macro can be used for this). It should generate code that just calls sync_component and sync_resource, returning a new SyncEditorBundle.

A strawman syntax could be:

let editor_bundle = sync_editor! {
    components:
        Foo,
        Bar,
        Baz,

    resources:
        Abba,
        Baab,
        Flub,
};

Setup Travis CI

It would be good to have automated testing for commits and pull requests so that we can have some basic verification for contributions. Setting up Travis should be fairly quick to do, and we shouldn't need to do much in the way of testing on multiple platforms since we should be avoiding platform-specific logic in this crate.

Add new components to entities at runtime

This is currently blocked by #27.

Add a way for editors to request that a new component be added to an existing entity at runtime. The editor should specify the ID for the component type (i.e. the user-provided name for the type) and the entity it should be attached to. The game should then create the corresponding component in the game world.

Since the editor currently knows nothing about the type definitions for components, it won't know how to create the data for a new component when adding one. Instead, we'll leave it to the game process to use Default or similar user-provided logic to create the new component directly within the game.

To do this, add a new variant to IncomingMessage called AddComponent that specifies the ID for the component type and the entity the component should be added to. We'll also likely have to add Default as a bound for WriteComponentSet.

Note that it may prove insufficient to use Default for some component types, since they may require data from resources or other components when being initialized. If this proves to be the case, we can discuss adding a new trait such as ComponentDefault that allows users to access world state when creating default values for a component.

Remove patch for amethyst dependency

Currently our Cargo.toml includes a patch section for amethyst that pulls in the development version of Amethyst, rather than using the stable 0.8 release. I believe I did this originally because I copied in the pong example from the development branch, but I don't think the crate itself actually needs any functionality that's only on the development branch currently. In order to prepare for a 0.1 release of the library, we should redo the pong example to be compatible with the stable release of Amethyst, and thereby make sure that the crate as a whole is compatible with the official Amethyst release.

If it turns out that there is anything in the main crate that depends on unreleased features in Amethyst, please bring that up here so that we can discuss solutions as necessary ๐Ÿ™‚

Pass logs through to Amethyst logger

Currently EditorLogger only passes log output to the editor, but doesn't log anything to the local console. It would be good to also be able to pass log output through the regular Amethyst logger in order to be able to still see log output when the editor isn't running. This is especially important currently since no editor front end handles displaying log output well.

Amethyst exposes the Logger type as the concrete logger implementation, as well as the LoggerConfig for configuring the logger. EditorLogger should be updated to take a LoggerConfig as an optional parameter, and should forward any log output to an internal Amethyst Logger if configured to do so. This can likely take the form of an additional method EditorLogger::forward_logs(config: LoggerConfig).

Setup bundle for easier, more configurable initialization

#3 made setup for editor support easier by allowing users to register their component and resource types with EditorSyncSystem directly, removing the need manually register a separate system for each component/resource type. Unfortunately this change removed the ability to run serialization in parallel for each data type being serialized, so it would be good if we could take an alternate approach that allowed us to still register a separate system for each type while keeping the ergonomic improvements that #3 added.

Instead of registering the types directly with EditorSyncSystem, we should instead setup a bundle builder. Users would build the bundle similar to how they currently register their types with EditorSyncSystem, and then the bundle can register all the systems with GameDataBuilder. This approach would still be ergonomic for the user, but would allow us to regain the ability to serialize the game state in parallel.

Notes

One ergonomic drawback is that users will also need to specify a system name for each type that they register. The signature for GameDataBuilder::with requires a &str be used as the system name, which means we can't have the bundle generate a unique name for each system at runtime (which would be the preferred solution). As a first pass we can have users manually specify a name for each system, but later we should make a PR to amethyst to use a Cow<'a, str> for system names so that it can support runtime-generated String values for names.

Tracking issue for serializing more engine types

Originally brought up by @Jojolepro in randomPoison/amethyst-editor#29.

The following components provided by Amethyst do not implement Serialize:

  • UiTransform
  • MeshData
  • UiText
  • Removal<T>
  • UiButton
  • FlyControlTag
  • Parent

And the following resources provided by Amethyst do not implement Serialize:

  • HideCursor

I'm making a PR to Amethyst adding missing Serialize impls to any types that only contain simple data. The following types are more complex and will need special handling:

  • MeshData contains potentially large amounts of data, and therefor should not be serialized and sent every frame for performance purposes. I'm also not clear what exactly MeshData is used for? I'm more familiar with using MeshHandle for attaching meshes to entities. Is MeshData used for dynamically-generated meshes? We'll have to discuss it in more detail to determine how it should be handled, both in terms of serialization and how we should display it in the editor.
  • UiText contains a good chunk of plain data that could be shown directly in the editor, but it also contains some private data that can't be (or at least shouldn't be) serialized (specifically the cached font handle, cached glyphs, and cached brush ID). There are potentially a few ways to solve this:
    • Only implement Serialize and have it only serialize the data we care about. This is okay for now, but we do eventually want to be able to modify this data in the editor, which will require a Deserialize impl.
    • Split the cached data into a separate UiTextCache component.
    • Wait for #7 to be implemented so that we can use intermediate serialization to split off the data we don't want to serialize.
  • Parent contains an Entity, which means it cannot be serialized directly. It is going to need #7 to be implemented in order to properly support serialization of Entity values.

Use UUIDs to identify types

Right now we use the user-provided names for Resources/Components to identify them when deserializing update data sent from the editor. While this is an okay solution in the short term, we'll need a more stable way of identifying types in order to support prefab editing in the future. To do this, we can use serde-dyn, which provides the trait TypeUuid.

TODO: Note that we'll have to define our own TypeUuid trait because of the orphan rule.

TODO: Note that we should put this behind a feature until we're ready to make it mandatory.

Register read-only resources

#25 added support for editing Resources, but in doing so added the requirement that registered Resource types be DeserializeOwned. While most Resources types will likely be able to #[derive(Deserialize)], it would be good to still support displaying Resources that only implement Serialize.

To do so, we should add a new method SyncEditorBundle::read_resource for registering a read-only Resource, as well as a corresponding SyncEditorBundle::read_resources method for registering a set of read-only resources generated with type_set!.

To do this, we'll likely have to split the ResourceSet trait into two traits ReadResourceSet and WriteResourceSet. ReadResourceSet would only need to enforce the Serialize bound, and WriteResourceSet would only need to enforce the DeserializeOwned bound.

Split out specs support

The core functionality of this crate, synchronizing component and resource data with an editor, isn't actually specific to Amethyst. Most of the functionality is still useful to projects that use specs directly, with only SyncEditorBundle using an Amethyst-specific API in order to make setup more user-friendly. It would be possible to expose an core API that only relied on specs so that a broader portion of the community could take advantage of this functionality.

That said, there is Amethyst-specific functionality that we would like to add in the future (e.g. default registration of Amethyst components and resources, showing the state machine stack, showing state event history, etc.). As such, it would likely make sense to split this project into two crates: One that provides specs-compatible functionality, and then continue to build the amethyst-editor-sync crate on top of the specs-only crate.

Create and destroy entities at runtime

Add support for allowing the editor to request that a new entity be added to the game world.

  • Add a new variant to IncomingMessage called CreateEntities for requesting that an entity be created. The variant should carry a field specifying how many entities should be created.
  • Add a new field to outgoing messages "created_entities" that is a list of any entities that were just created at the request of the editor.
  • The list of new entities should be sent as soon as the entities are created, ideally on the same frame.
  • The list of new entities should only be sent once.

Similarly, to support destroying entities at runtime we'll need to add a variant to IncomingMessage called DestroyEntities that takes a list of entities to be destroyed. The list of destroyed entities does not need to be sent back to the editor, though.

Panic when syncing many entities

If the synchronization message is greater than supported by the socket, it fails to send and therefore panics.
On windows 10 this limit is 64kB, which is ~2500 empty entities and significantly less when components are involved.

This code causes the error on my machine.

extern crate amethyst;
extern crate amethyst_editor_sync;

use amethyst::prelude::*;
use amethyst_editor_sync::*;

struct TestState;

impl<'a, 'b> SimpleState<'a, 'b> for TestState {
    fn on_start(&mut self, data: StateData<GameData>) {
        for _ in 0..2_500 {
            data.world.create_entity().build();
        }
    }
}

fn main() -> amethyst::Result<()> {
    let editor_sync_bundle = SyncEditorBundle::new();
    let game_data = GameDataBuilder::default()
        .with_bundle(editor_sync_bundle)?;
    let mut game = Application::build(".", TestState)?
        .build(game_data)?;
    game.run();
    Ok(())
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.