GithubHelp home page GithubHelp logo

traffloat / traffloat Goto Github PK

View Code? Open in Web Editor NEW
19.0 2.0 0.0 2.21 MB

A cooperative web game of 3D logistics and space city building.

Home Page: https://traffloat.github.io/

License: GNU Affero General Public License v3.0

Rust 95.09% Fluent 4.91%
rust game tower-defense factory-game webassembly web-game webgl 3d-game hacktoberfest

traffloat's Introduction

Traffloat

GitHub GitHub

Simple issues Medium issues Complex issues

API issues Bug issues Documentation issues Feature issues Optimization issues Tooling issues

A 3D traffic, factory, city building, tower defense web game.

Play the game | Rust API docs | Discuss | Report bug

What is this game about?

This game happens in a self-sustaining space colony. The player constructs buildings and corridors in the colony to produce and transfer different resources.

This game is about logistics. Most resources are created from other types with factories. Insufifciency or excess of resources will bottleneck the game.

This game is about traffic. Resources are transferred in different forms (cargo, liquid, gas, electricity, human). Optimize transportation routes to maximize production rate.

This game is about city building. Inhabitants are produced in the colony to boost productivity. Build schools to improve inhabitant skills and unlock new technologies.

This game is 3D. Explore new transportation mechanisms in a genuinely 3D network of buildings. Become a leading architect for 3D cities.

This game is cooperative. Players can join the colonies of other players, or create new colonies and establish trades with existing colonies.

This game involves tower defense. Colonies are attacked by waves of asteroids in the space, which can be propelled or dissolved into raw resources.

Compilation

This project uses Rust nightly-2021-11-17. The toolchain version is automatically installed if you installed Rust with rustup.

This project requites the wasm target for the Rust toolchain. Install it by running the following command inside a traffloat clone:

rustup target add wasm32-unknown-unknown

This project uses just to manage script commands, and trunk to manage WebAssembly and site building. To install these tools:

cargo install trunk just

See the justfile for common commands, in particular:

# Compile the scenario files
just client-scenarios-dev
# Compile the client for production
just client-build

To compile for development mode, use just client-build-dev instead. There is also just client-watch, which compiles the client and start a dev server, and recompiles if files have been modified.

While development build claims to produce "optimized" output, LTO (link time optimization) in release build can further improve performance and reduce file size at the cost of longer compile time. The release build also triggers wasm-opt with optimization level 4, which takes a long time to execute (more than 15 minutes of CPU time).

Contribution

The game is composed of multiple crates:

  • codegen/codegen-raw: Defines procedural macros used in the game.
  • types: Defines standard data types of vectors, units and gamerule definition.
  • types: Defines scenario schema.
  • common: Implements game world simulation.
  • client: Implements a web game client.
  • tfsave-builder: CLI tool to compile scenarios from human-readable TOML files.

Create a thread in Discussions if you would like to contribute and don't know where to start.

traffloat's People

Contributors

dependabot-preview[bot] avatar dependabot[bot] avatar endermanbugzjfc avatar sof3 avatar

Stargazers

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

Watchers

 avatar  avatar

traffloat's Issues

Create settings menu

Options for rendering.

  • Rendering perspectives (e.g. #177)
  • Reticle rendering
  • Debug information rendering (only available when compiled with that flag)

GameDefinition seems empty

It appears that GameDefinition::default() was used to populate GameDefinition before client::initial initializes it.

Generalize reactions and node features

In particular, some node features like pumps need to consume inputs just like reactions. Consider turning these into special features of a reaction instead of features of a node.

Inhabitant motion

  • For tasked inhabitants, store target and pathfinding in a component
  • Upon arrival at a node, check if next path is still valid, recompute pathfinding if needed
  • For idle inhabitants, select random node to go to
  • For each edge entry, ensure number of inhabitants at starting zone
  • For each edge exit, check target node capacity. Stay in edge of jammed.
  • For each entry, exit or jammed exit, recompute velocity of all moving inhabitants in the path.

Style type

let style = style! {
    "background-color": "white",
    "border": "black solid",
};

let style2 = style! {
    "padding": "1px 2px",
    ..style,
};

let style3 = style! {
    "color": "red",
    ..style,
    ..style2,
};

html! {
    <div style=style3>
        <span style=style!(..style3, "color": "blue") />
    </div>
}

Compute electricity reachability

Need to take cable throughput into consideration

Don't really need to care about resistance, since this also happens IRL

Need to find maximum flow for a flow graph, possibly a modification of EK algorithm

TSVT to TSV(B) converter

Currently, vanilla.tsvt is shipped as a text file.

While this is useful for git diff, it is inefficient to transport across the wire. It also creates inconsistency as both TSVT and TSVB are save formats.

It would be a better idea to provide a tsvconv binary that converts between the two formats, and only allow TSVB (without the need to specify the b in the file extension anymore) for the web client.

Construction mechanism

  • Introduce construction hotkey
  • Click on node to choose construction base
  • Menu to select construction target
  • Click air to select construction location
  • Render inactivated target
  • Render menu for construction process
  • Build corridors
  • Edit corridors by cross section

Merge happiness with other skills

Lots of code have to handle both happiness and skills separately. It appears that happiness can be generalized as a kind of skill.

Implement union shapes for buildings

Currently, only sphere, cylinder and cube are implemented. It should not be too difficult to extend the mechanism to accept a union of shapes as the building shape.

  • Modify definition schema
  • Update rendering
  • Update raytracing
  • Update sunlight casting

View-based ECS

This proposes a major change in the ECS API design.

Motivation

#[codegen::system(Visualize)]
#[read_component(node::Id)]
#[read_component(node::Name)]
#[read_component(edge::Id)]
#[read_component(edge::Size)]
#[read_component(edge::Design)]
#[read_component(Position)]
#[read_component(Shape)]
#[read_component(units::Portion<units::Hitpoint>)]
#[read_component(cargo::StorageList)]
#[read_component(cargo::StorageCapacity)]
#[read_component(cargo::StorageSize)]
#[read_component(liquid::Storage)]
#[read_component(liquid::StorageList)]
#[read_component(liquid::StorageCapacity)]
#[read_component(liquid::StorageSize)]
#[read_component(gas::StorageList)]
#[read_component(gas::StorageCapacity)]
#[read_component(gas::StorageSize)]
#[read_component(defense::Core)]
#[read_component(population::Housing)]
#[read_component(vehicle::RailPump)]
#[read_component(liquid::Pump)]
#[read_component(gas::Pump)]

nodes: <(
Entity,
&node::Id,
&node::Name,
&Position,
&Shape,
&units::Portion<units::Hitpoint>,
&cargo::StorageList,
&cargo::StorageCapacity,
&liquid::StorageList,
&gas::StorageList,
&gas::StorageCapacity,
)>::query()
.iter(world)
.map(
|(
entity,
&id,
name,
&position,
shape,
&hitpoint,
cargo,
&cargo_capacity,
liquid,
gas,
&gas_capacity,
)| {

The huge amount of component reads makes the code too verbose. It is easy to forget some component reads too.

Guide-level explanation

Consider this original system:

fn create_entity(
    buf: &mut CommandBuffer,
    drag: Drag, boost: Boost, stuff: Stuff,
    position: Position, velocity: Velocity, size: Size, more: More, unused: Unused,
) {
    let child = buf.push((
        drag, boost, stuff,
    ));
    buf.push((
        position, velocity, size, ChildRef::new(child), more, unused,
    ))
}

#[codegen::system(Simulation)]
#[write_component(Position)]
#[read_component(Velocity)]
#[read_component(Size)]
#[read_component(ChildRef)]
#[read_component(Drag)]
fn simulate(world: &mut SubWorld) {
    let query = <(&mut Position, &Velocity, &Size, &ChildRef)>::query();
    let (mut query_world, entry_world) = query;
    for (pos, vel, size, child) in query.iter_mut(&mut query_world) {
        *pos += compute_delta(vel, size, child, &entry_world);
    }
}

fn compute_delta(vel: &Velocity, size: &Size, child: &ChildRef, world: &SubWorld) -> Vector3 {
    let entity = world.entry_ref(child.entity).unwrap();
    let drag: &Drag = entity.get_component::<Drag>().unwrap();
    let boost: &Boost = entity.get_component::<Boost>().unwrap();
    *vel * *size - *drag * *boost
}

We want to write the code like this instead:

#[derive(codegen::ComponentSet)]
pub struct Parent {
    pub position: Position,
    pub velocity: Velocity,
}
#[derive(legion::ComponentSet)]
pub struct ParentMeta {
    pub size: Size,
    pub more: More,
    pub unused: Unused,
}
#[derive(legion::ComponentSet)]
pub struct Child {
    pub drag: Drag,
    pub boost: Boost,
}

fn create_entity(buf: &mut CommandBuffer, parent: Parent, meta: ParentMeta, child: Child, stuff: Stuff) -> Entity {
    let child: Entity = buf.push_sets((child,))); // push_sets(x: Set<Set<Comp>>) = push(union(x))
    let child_ref = ChildRef::new(child);
    buf.push_sets_comps((parent, meta), (child_ref,)) // push_sets_comps(x: Set<Set<Comp>>, y: Set<Comp>) = push(union(x) union y)
}

#[derive(codegen::ComponentSetView)]
#[view(Parent)] // verify that fields are from Parent
struct SimulationParentView<'t> {
    position: &'t mut Position,
    velocity: &'t Velocity,
}
// autogenerate static assertions to verify that `*SimulationParentView.position` and `Parent.position` have the same type

#[derive(codegen::ComponentSetView)]
struct SimulationView<'t> {
    #[view(subview)] // recurse into a subview
    parent: SimulationParentView<'t>,
    #[view(type Parent)] // verify from Parent like above
    size: &'t Size,
    #[view(~)] // skip verification, this is a standalone component
    child: &'t Child,
}

#[codegen::system]
pub fn simulate(mut query: ViewQuery<SimulationView>, entry: ViewEntry<DeltaChildView<'_>>) {
    for view in query.iter_mut() {
       *view.position += compute_delta(view.project(), entry);
    }
}

#[derive(codegen::ComponentSetView)]
struct DeltaParentView<'t> {
    #[view(type Parent)]
    velocity: &'t Velocity,
    #[view(type Parent)]
    size: &'t Size,
    #[view(~)]
    child: &'t Child,
}

#[derive(codegen::ComponentSetView)]
#[view(type Child)]
struct DeltaChildView<'t> {
    drag: &'t Drag,
    boost: &'t Boost,
}

fn compute_delta(parent: impl Project<DeltaParentView<'_>>, entry: ViewEntry<DeltaChildView<'_>>) -> Vector3 {
    let child = entry.for_entity(*parent.child);
    *parent.vel * *parent.size - *child.drag * *child.boost
}

Future enhancement

If typeof and named type arguments are implemented in Rust, we can have a macro to autogenerate view macros.

Inhabitant system

  • Store parent node/edge of each inhabitant
  • Store subnode position of each inhabitant as a point relative to node center
  • Store subedge position of each inhabitant as a function in the form p(t) = p0 + v (t - t0)
  • Render inhabitants
  • Skip inhabitants when zoom is sufficiently large
  • Store inventory for each inhabitant

Support cylinder-shaped nodes

  • Implement shape::Unit::contains
  • #151
  • Implement shape::Unit::bb_under
  • Adapt cylinder position/normal buffer for node rendering
  • Add cylinder texture type
    • curve.svg: f(x, y) = (cos 2x pi, sin 2x pi, y)
    • top.svg and bottom.svg: f(x, y) = (x, y, 0 or 1), area beyond unit circle unused
  • Check other places hardcoded with shape::Cube

Unit test game systems

Most systems are untested right now. In particular, we need unit tests to ensure that #[read_component] and #[write_component] are correctly added. It is so easy to forget this.

Construction tasks

  • Register delivery tasks when construction is requested
  • Register construction tasks when materials arrive

Implement corridor editor for ducts

    • Add a component to store ducts
    • Implement an SVG-based interface to display ducts
    • Add button in edge preview to open duct viewer.
  • Add entities based on duct design
    • Rails
    • Pipes
    • Cables
    • Gas diffusion

Cache uniform locations

Do not keep calling getUniformLocation on the webgl context.

  • Cache uniforms locations
  • Cache attribute locations

Use a better file extension

.tsv means "tab-separataed values" (similar to csv). This extension is too common and needs to be changed.

Cargo system

  • Store cargo count of each type in each node
  • Implement data structure to search and count cargo
  • Implement perspective that renders cargo availability

Game saves

  • Save data
  • Load data
  • Make editing schema easier, don't use numeric IDs (see also #157)

Discussed in #155

Originally posted by SOF3 July 27, 2021

Things to store

  • Game definition
  • Game time
  • Node state
  • Edge state

Serialization formats

  • Binary-based format for transferring, e.g. MsgPack
  • Text-based format for source control, e.g. YAML

Cargo system

Discussed in #143

Originally posted by SOF3 February 3, 2021

  • Store cargo count of each type in each node
  • Implement data structure to search and count cargo
  • Implement perspective that renders cargo availability

TypeId can use integer keys

traffloat_types::def::xxx::TypeId are currently just wrappers around ArcStr. It would improve performance and allow atomic updates if the references are changed to usize instead. This is currently already possible since the GameDefinition fields are using indexmap, which allows sequential indexing.

Node preview is not real-time

Node/edge preview only displays the information of the node/edge when the cursor starts targeting it. This can be fixed by disabling the if-same-node-id-skip-event check.

Bump edition to 2021

Edition 2021 is scheduled to release with 1.56, which is the currently locked nightly. It would be good to start testing for issues.

Task system

  • Implement data structure to store task list with priority and location indices
  • Store task state for each inhabitant
  • Inhabitants poll task list by radius and happiness level

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.