Skip to content
This repository has been archived by the owner on Apr 18, 2022. It is now read-only.

refactor: Make game data pluggable #691

Merged
merged 4 commits into from
May 14, 2018
Merged

Conversation

Rhuagh
Copy link
Member

@Rhuagh Rhuagh commented May 1, 2018

Remove Dispatcher from Application, and add a pluggable generic type.
Add a direct replacement for the old game data, featuring only a Dispatcher.
Make the states take a new StateData type, containing references to World and pluggable game data.
Move responsibility for doing system dispatch to the states.

Opening this early to get feedback.

Resolves #576


This change is Reviewable

@Rhuagh Rhuagh added type: improvement An improvement or change to an existing feature. diff: hard Achievable, but may require efforts from multiple experienced developers. pri: important Something other teams are relying on, or a low-level, critical piece of functionality. status: working labels May 1, 2018
@AnneKitsune
Copy link
Contributor

I'll need a more complex example to really get the feel of it.
Maybe an example with:
-Global systems
renderer,input,ui,assets
-StateLoading
waits for assets to be all loaded, shows a ui specific to this state (loading ui), which get removed automatically when changing state (because when you switch you lose this World)
-StateGame
simple ui showing the loaded asset

The prototype looks good ;)

@Rhuagh
Copy link
Member Author

Rhuagh commented May 2, 2018

The World is not replaced, it's just the Dispatcher that's been replaced with a generic type.

@Rhuagh
Copy link
Member Author

Rhuagh commented May 2, 2018

I was thinking of maybe adding a loading state to the renderable example, since some of those assets take a while to load in debug mode.

@Rhuagh
Copy link
Member Author

Rhuagh commented May 2, 2018

This is basically my stab at #576.

Because we use World for a lot of things internally in Application and a lot of the core, my proposal is basically to keep the core World in place, and simply abstract away the Dispatcher, and any "extra" data that the user might want to keep centrally. The naming of the different types could use improvements, but the core concept is solid I believe.

Copy link
Member

@Xaeroxe Xaeroxe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was pleasantly surprised with how this turned out.

image

LGTM!

@Xaeroxe
Copy link
Member

Xaeroxe commented May 3, 2018

I think it might be a good idea to add a chapter to the book called Advanced Game Architecture that shows how to use multiple dispatchers in a custom GameData structure.

@Rhuagh
Copy link
Member Author

Rhuagh commented May 3, 2018

I was planning on creating an example atleast once I get some feedback on the basic design :)

@AnneKitsune
Copy link
Contributor

I'll re-review it soon.

Copy link
Member

@torkleyy torkleyy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it, thanks for your work! A chapter would really be helpful, I'm not sure I understand how all the *Data types interact exactly.

controls.entry(entity).unwrap().or_insert_with(AnimationControlSet::default)
controls
.entry(entity)
.unwrap()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not unwrap but return the error.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a known (and reported issue), yes, will fix that in a separate PR as it changes the API.

@@ -0,0 +1,240 @@
use std::sync::Arc;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing top-level docs

Copy link
Contributor

@AnneKitsune AnneKitsune left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks good, but since this is a really big breaking change, I have a few questions.

Main one:
Is it possible to have a GameData that has:
global_dispatch
state_specific_dispatch
where the state_specific_dispatch is created during a State start() method?

Basically, sometimes you want to have systems run only for some states (like in this example), but you want the state to conditionally set the systems. Also, creating it in the start (or similar) method makes it easy to find the relation between State and state_specific_dispatch.

I don't want to have
GameData
-global_dispatch
-state1_dispatch
-state2_dispatch
-state3_dispatch
-state4_dispatch
Especially since sometimes creating a System might need data loaded by a previous state. For example, a System specific to GameMode1State would require a Handle loaded by LoadingState.

(entities, mut locals, parents, anchors, stretches, screen_dim, hierarchy): Self::SystemData,
){
fn run(&mut self, data: Self::SystemData) {
let (entities, mut locals, parents, anchors, stretches, screen_dim, hierarchy) = data;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting. The previous code complained about too long row every time I ran rustfmt, and I got tired of it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@ -0,0 +1,5 @@
# Renderable Example
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/Renderable/Game Data/

use amethyst::{DataInit, Error, Result};
use rayon::ThreadPool;

pub struct GameData<'a, 'b> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got really really confused, because this custom GameData has the same name has amethyst::GameData. I couldn't figure out why this one had two dispatchers and the other one had one.

Please rename it to CustomGameData.

@@ -0,0 +1,583 @@
//! Demonstrates how to load renderable objects, along with several lighting
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I doubt it!

//! Demonstrates how to load renderable objects, along with several lighting
//! methods.
//!
//! TODO: Rewrite for new renderer.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmmmmmmm :P


mod game_data;

struct DemoState {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think having all the lighting stuff makes the example way more complicated than how it needs to be. Its harder to really spot what GameData does when there is so much unrelated code. A simple rotating cube with a fixed camera is all you would need I think.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The renderable example is the only one where loading is slow enough to actually warrant a Loading state, that's why I chose it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could keep the .obj loading, which is the slow part, but remove the lights. If not then its fine.

data.data.update(&data.world, true);
match self.progress.as_ref().unwrap().complete() {
Completion::Failed => {
println!("Failed loading assets");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

printerr! or error!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't see a need to pull in an extra crate for an example.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

printerr! then?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

printerr? Not a part of core rust from what I know ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya sorry its "eprintln!" actually :P

.with_pass(DrawShaded::<PosNormTex>::new())
.with_pass(DrawUi::new()),
);
let game_data = GameDataBuilder::default()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Games don't only have a base and a running state. There should be a way to provide multiple systems that are specific to each of the states. Those systems should be added during the state start function, to allow for better logic locality.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ofc not, but I'm not about to write a whole game in a single example, and this example is for demonstrating a custom game data implementation, not teach people how to structure a game code wise.

(
fullscreen: false,
multisampling: 0,
title: "Renderable Example",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renderable again

@Rhuagh
Copy link
Member Author

Rhuagh commented May 4, 2018

It's a generic type without bounds, you can put anything you like in it. Data would be in the World though, so there's no need to "share" that. Personally, I would put state specific dispatchers in the state, and not in a central location.

@AnneKitsune
Copy link
Contributor

So GameData is global? If so I'm not sure what you can do with it that you couldn't do using Resource.

@Xaeroxe
Copy link
Member

Xaeroxe commented May 4, 2018

@Jojolepro The main thrust of the PR is to permit people to access data in a way that isn't via specs. So this would be for people who don't even want a Resource.

@AnneKitsune
Copy link
Contributor

  1. What would be the use case for that?
  2. Is it worth the added complexity?

@Xaeroxe
Copy link
Member

Xaeroxe commented May 4, 2018

@Rhuagh mentioned #576 in a comment on this PR (Maybe we should mention it in the opening PR description)

If we go to that issue some helpful context is present. Specifically this quote:

As more use cases for Amethyst begin to explore the virtues and limitations of
the current data architecture some hurdles are beginning to present themselves for
both optimization and flexibility. The current ECS design forces a specific way of
thinking that works for a lot of designs but requires use of anti-pattern solutions
in others. When examining how one would implement a dense voxel map idiomatically in
Amethyst general consensus is that each voxel should be an entity within
the system. While very simple this is not an optimal structure for chunked densely
packed Voxel data accessed by range query. Thus Voxel information would be
relegated to an outside resource with array data and a second ecs world containing
Voxel definitions. The access pattern of this second ecs is random and differs
greatly from the sequential access pattern of the primary ecs iterator.

@AnneKitsune
Copy link
Contributor

AnneKitsune commented May 4, 2018

And how does Resource not solve this? My current plan for a voxel world involves having it as a resource and having Systems use that resource to create entities (optimised) that will end up getting rendered.

For me, this change is replicating what specs Resources is doing already.

@Xaeroxe
Copy link
Member

Xaeroxe commented May 4, 2018

Yeah a Resource could be used for this. That means that to me there's one remaining major reason to merge this PR, and that is to facilitate types that aren't Any + Send + Sync.

@Rhuagh
Copy link
Member Author

Rhuagh commented May 4, 2018

You can't store systems in a resource.

@Xaeroxe
Copy link
Member

Xaeroxe commented May 4, 2018

@Rhuagh
Copy link
Member Author

Rhuagh commented May 7, 2018

Re-review if you can please.

Copy link
Contributor

@AnneKitsune AnneKitsune left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still not a fan of all the lighting stuff, but that's fine ;)

Thanks!

@torkleyy
Copy link
Member

torkleyy commented May 8, 2018

When examining how one would implement a dense voxel map idiomatically in
Amethyst general consensus is that each voxel should be an entity within
the system.

Didn't we have that discussion before?

@torkleyy
Copy link
Member

torkleyy commented May 8, 2018

Reviewed 3 of 8 files at r1, 5 of 10 files at r2, 19 of 21 files at r3, 3 of 3 files at r4, 1 of 2 files at r6, 13 of 20 files at r7.
Review status: all files reviewed at latest revision, 10 unresolved discussions.


Comments from Reviewable

Copy link
Member

@torkleyy torkleyy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@AnneKitsune
Copy link
Contributor

AnneKitsune commented May 9, 2018

I thought cfg!(target_os = "ios") didn't work?
anyway
Waiting for label change + build checks to merge. @Rhuagh

@Rhuagh
Copy link
Member Author

Rhuagh commented May 9, 2018

I still need to add a chapter to the book, and I'm currently focusing on getting specs out.

@Rhuagh
Copy link
Member Author

Rhuagh commented May 9, 2018

Rebased, squashed, book chapter added. @Xaeroxe feel free to take over.

@Rhuagh
Copy link
Member Author

Rhuagh commented May 10, 2018

Ready for final review @Jojolepro @torkleyy @Xaeroxe

@AnneKitsune
Copy link
Contributor

outside most of the day, I'll review tonight if I can, or tomorow

Copy link
Member

@Xaeroxe Xaeroxe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! bors r+

@Xaeroxe
Copy link
Member

Xaeroxe commented May 14, 2018

err actually nevermind, I think this might fail on CI due to some new examples.

@Rhuagh
Copy link
Member Author

Rhuagh commented May 14, 2018

bors r=Xaeroxe,torkleyy,jojolepro

bors bot added a commit that referenced this pull request May 14, 2018
691: refactor: Make game data pluggable r=Xaeroxe,torkleyy,jojolepro a=Rhuagh

Remove Dispatcher from Application, and add a pluggable generic type.
Add a direct replacement for the old game data, featuring only a Dispatcher.
Make the states take a new StateData type, containing references to World and pluggable game data.
Move responsibility for doing system dispatch to the states.

Opening this early to get feedback.

Resolves #576 

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/amethyst/amethyst/691)
<!-- Reviewable:end -->


Co-authored-by: Simon Rönnberg <seamonr@gmail.com>
@bors
Copy link
Contributor

bors bot commented May 14, 2018

Build failed

@Rhuagh
Copy link
Member Author

Rhuagh commented May 14, 2018

bors r=Xaeroxe,torkleyy,jojolepro

bors bot added a commit that referenced this pull request May 14, 2018
691: refactor: Make game data pluggable r=Xaeroxe,torkleyy,jojolepro a=Rhuagh

Remove Dispatcher from Application, and add a pluggable generic type.
Add a direct replacement for the old game data, featuring only a Dispatcher.
Make the states take a new StateData type, containing references to World and pluggable game data.
Move responsibility for doing system dispatch to the states.

Opening this early to get feedback.

Resolves #576 

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/amethyst/amethyst/691)
<!-- Reviewable:end -->


Co-authored-by: Simon Rönnberg <seamonr@gmail.com>
@bors
Copy link
Contributor

bors bot commented May 14, 2018

@bors bors bot merged commit 6d3f65a into amethyst:develop May 14, 2018
@Rhuagh Rhuagh deleted the refactor/game-data branch May 14, 2018 23:05
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
type: improvement An improvement or change to an existing feature.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants