Coder Social home page Coder Social logo

msakuta / factorishwasm Goto Github PK

View Code? Open in Web Editor NEW
6.0 6.0 3.0 10.29 MB

A port of FactorishJS to Wasm/Rust (and a bit of HTML5+JavaScript).

License: MIT License

HTML 0.22% JavaScript 9.43% Rust 83.28% CSS 1.05% Vue 6.02%
game html5 rust wasm

factorishwasm's Introduction

factorishwasm's People

Contributors

msakuta avatar sweetim avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

factorishwasm's Issues

Should we early return or ignore errors from dynamically dispatched methods?

This is a code design discussion, but I put it as an issue not to forget.

There is a pattern in this repository source code that occurs repeatedly. That is, calling a method on all Structure trait objects in sequence. One example is on_construction event handler method.

            let mut structures = std::mem::take(&mut self.structures);
            for i in 0..structures.len() {
                let (notify_structure, others) = StructureDynIter::new(&mut structures, i)?;
                if let Some(s) = notify_structure.dynamic.as_deref_mut() {
                    match s.on_construction(
                        StructureId { id: i as u32, gen },
                        structure.as_mut(),
                        &others,
                        false,
                    ) {
                        Ok(()) => (),
                        Err(s) => {
                            drop(others);
                            self.structures = structures;
                            return Err(s);
                        }
                    }
                }
            }
            self.structures = structures;

It is unclear whether we should ignore with warnings or early return if an error is returned from on_construction(). It depends on whether the error is recoverable, but we have no actual errors from the functions yet (it is declared with Result to allow yet to exist trait implementor to return an error). Each structure is somewhat independent, so it should be possible to just ignore and carry on to the next structure, unless the error indicates some invariants are violated. If an invariant is violated, it may not be safe to continue. But if such an unrecoverable error happend, the code should panic. But if unrecoverable errors are reported with panic so that all the returned errors can be ignored, why would we return an error in the first place?

It turned out, the ecs branch uses early return with ? operator a lot inside on_construction or any other dynamically dispatched methods. It is usually not a hard error, it just indicates some operations are irrelevant to that structure, but we use ? operator just to make the code cleaner. In this use case, we don't want to return early.

Options

Ignore and show warnings on console

Showing warnings on console is not a beautiful solution, because usual players won't see it and only developers would be interested, but it doesn't have to be in production code.

Early return and skip later Structures

In usual Rust, panic would be more appropriate in case of unrecoverable error, but WebAssembly is not great to work with panics. Returning errors from Rust will be translated to JS exception, which is appropriate for this kind of error.

The implementor of the trait should handle recoverable errors by itself and should not return Error in such case.

Define error type as enum of recoverable and unrecoverable error

It would allow using ? operator in ecs branch ergonomically, but the caller code would not be beautiful.

Coastline shapes have defects

This is known problem and left for some time.

image

Currently we have a tile image with all possible combinations of adjacent 8 water tiles. However, the total number of all combinations are huge (2^8=256), so we ignore some combination and just put ground image. Namely, we have 32 combinations in the image.

image

In order to correctly render the coast lines, we need 256 image patterns, or we can divide the tiles into 4 pieces and define combinations in each of them. We need to draw more primitives, but it would reduce amount of image greatly.

WebGL rendering

When in fullscreen with HD resolution with fully zoomed out in Chrome, it takes over 20ms to render every frame.

image

It is too slow to draw in 60FPS. Currently we use 50ms timer to render a frame, but slower computers would not even make it in time.

It gets worse in Firefox. For some reason Firefox canvas renderer is several times slower and has artifacts between tiles.

The main reason why it is so slow is that we use HTML canvas to render the backgound tiles. When it comes to render repeating images, canvas API has pretty bad performance, especially when the API is called from inside Wasm.
WebGL should be able to render repeating patterns pretty quickly, and also has capability to make variations efficiently in GPU with GLSL.

Generational ids for DropItems

Implementing in branch item-id.

We have successfully implemented generational ids to improve performance. However, DropItems are still plain vector. So why not converting it to generational ids too?

I benchmarked the performance with about 400 DropItems on transport belts.

image

Current:

image

With generational ids:

image

Very interestingly, the performance after "improvement" by generational id is actually worse than before.

My guess is that there is a significant overhead in creating iterator in SplitSlice::dyn_iter_id even though the returning iterator is statically dispatched. Because every DropItem has to check every other DropItem for collision detection in every frame, this calculation is not trivial.

    pub(crate) fn dyn_iter_id(&self) -> impl Iterator<Item = (GenId, &T)> + '_ {
        self.left
            .iter()
            .enumerate()
            .map(move |(i, val)| (GenId::new((i + self.left_start) as u32, val.gen), val))
            .chain(
                self.right
                    .iter()
                    .enumerate()
                    .map(move |(i, val)| (GenId::new((i + self.right_start) as u32, val.gen), val)),
            )
            .filter_map(|(i, s)| Some((i, s.item.as_ref()?)))
    }

Generational ids are good fit for Structures, because they often refer to other objects, but DropItems will never refer to other items except collision checking, so the benefit of generational id is actually not much. Another advantage of generational ids is that deletion is very fast, but DropItems don't get deleted that often (at least in the benchmark).

Clearly, we need some kind of spatial index to improve performance of collision checking, no matter generational ids are used or not.

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.