1.43.0 • Published 9 months ago

@latticexyz/solecs v1.43.0

Weekly downloads
-
License
MIT
Repository
github
Last release
9 months ago

solecs - Solidity Entity Component System

solecs is a framework to build highly composable, extendable and maintainable on-chain Worlds.

It is designed around the Entity Component System architecture pattern. To build some fundamental intuition around ECS, have a look at our MUD ECS introduction.

solecs is seamlessly integrated with the other MUD libraries, but can be used independently.

Features

  • Fully on-chain ECS
  • Powerful queries, including advanced recursive or indirect relationship queries
  • Seamless integration with other MUD libraries and services
  • Simple API

Concepts

World

The World contract is at the core of every on-chain world. Entities, components and systems are registered in the World. Components register updates to their state via the registerComponentValueSet and registerComponentValueRemoved methods, which emit the ComponentValueSet and ComponentValueRemoved events respectively. Clients can reconstruct the entire state (of all components) by listening to these two events, instead of having to add a separate getter or event listener for every type of data. (Have a look at the MUD network package for a TypeScript implementation of contract/client state sync.)

The World is an ownerless and permissionless contract. Anyone can register new components and systems in the world (via the registerComponent and registerSystem methods). Clients decide which components and systems they care about.

interface IWorld {
  function registerComponent(address componentAddr, uint256 id) external;

  function registerSystem(address systemAddr, uint256 id) external;

  function registerComponentValueSet(
    address component,
    uint256 entity,
    bytes calldata data
  ) external;

  function registerComponentValueRemoved(address component, uint256 entity) external;

  function components() external view returns (IUint256Component);

  function systems() external view returns (IUint256Component);

  function getNumEntities() external view returns (uint256);

  function hasEntity(uint256 entity) external view returns (bool);

  function getUniqueEntityId() external view returns (uint256);

  function query(WorldQueryFragment[] calldata worldQueryFragments) external view returns (uint256[] memory);
}

Entities

An entity is just a uint256 id. While any uint256 is a valid id, uint256s from 0 to 2160 are reserved for Ethereum addresses as a convention. This allows every contract and EOA to be a valid entity.

A simple use-case of this convention is being able to represent components and systems (both of which are contracts) as entities (with their entity id being their contract address). This allows them to be stored in "registry components" in the World, which means every component and system is automatically known to the client via the standardized contract/client sync described in the World section. Pretty meta.

Components

Components are a key-value store from entity id to component value. Each component is its own contract. This allows components to be deployed independently from each other.

Components have an owner, who can grant write access to more addresses. (Systems that want to write to a component need to be given write access first.) Everyone has read access.

For convenience the component implementation in solecs also includes a reverse mapping from keccak256(value) to set of entities with this value, but this is not strictly required and might change in a future release.

interface IComponent is IOwnableWritable {
  /** Return the keys and value types of the schema of this component. */
  function getSchema() external pure returns (string[] memory keys, LibTypes.SchemaValue[] memory values);

  function set(uint256 entity, bytes memory value) external;

  function remove(uint256 entity) external;

  function has(uint256 entity) external view returns (bool);

  function getRawValue(uint256 entity) external view returns (bytes memory);

  function getEntities() external view returns (uint256[] memory);

  function getEntitiesWithValue(bytes memory value) external view returns (uint256[] memory);

  function registerIndexer(address indexer) external;
}

To have a common interface, the base component contract stores values as raw bytes. For a typed component that is easier to work with, extend the base component class and add typed getValue, set and getEnttiiesWithValue methods.

Example PositionComponent.sol:

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import { Component } from "solecs/Component.sol";

struct Coord {
  int64 x;
  int64 y;
}

uint256 constant ID = uint256(keccak256("example.component.Position"));

contract PositionComponent is Component {
  constructor(address world) Component(world, ID) {}

  function set(uint256 entity, Coord calldata value) public {
    set(entity, abi.encode(value));
  }

  function getValue(uint256 entity) public view returns (Coord memory) {
    return abi.decode(getRawValue(entity), (Coord));
  }

  function getEntitiesWithValue(Coord calldata value) public view returns (uint256[] memory) {
    return getEntitiesWithValue(abi.encode(value));
  }
}

To allow clients to automatically create a decoder for the abi-encoded raw component value, add an optional getSchema method:

contract PositionComponent is Component {

  ...

  function getSchema() public pure override returns (string[] memory keys, LibTypes.SchemaValue[] memory values) {
    keys[0] = "x";
    values[0] = LibTypes.SchemaValue.INT64;

    keys[1] = "y";
    values[1] = LibTypes.SchemaValue.INT64;
  }
}

Components are registered in the World contract and also register updates to their state in the World contract. (This happens automatically when extending the Component.sol base contract).

Systems

System are contracts with a single execute function.

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import "./IERC173.sol";

interface ISystem is IERC173 {
  function execute(bytes memory arguments) external returns (bytes memory);
}

For convenience adding a typed executeTyped method taking care of abi-encoding the arguments is recommended.

Like components, systems are registered on the World contract to allow clients to automatically sync their contract address.

In the solecs System.sol base contract, the system is initialized with a reference to the World contract and the world's component registry. Systems have read access to every component, but need to be granted write access to components they should modify.

Example MoveSystem.sol:

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import "solecs/System.sol";
import { IWorld } from "solecs/interfaces/IWorld.sol";
import { IUint256Component } from "solecs/interfaces/IUint256Component.sol";
import { IComponent } from "solecs/interfaces/IComponent.sol";
import { getAddressById } from "solecs/utils.sol";

import { PositionComponent, ID as PositionComponentID, Coord } from "./PositionComponent.sol";

uint256 constant ID = uint256(keccak256("ember.system.move"));

contract MoveSystem is System {
  constructor(IUint256Component _components, IWorld _world) System(_components, _world) {}

  function execute(bytes memory arguments) public returns (bytes memory) {
    (uint256 entity, Coord memory targetPosition) = abi.decode(arguments, (uint256, Coord));

    PositionComponent position = PositionComponent(getAddressById(components, PositionComponentID));
    position.set(entity, targetPosition);
  }

  function executeTyped(uint256 entity, Coord memory targetPosition) public returns (bytes memory) {
    return execute(abi.encode(entity, targetPosition));
  }
}

Queries

Queries provide a simple API to get a list of entities matching certain specified criteria. A query consists of a list of query fragments. A query fragment is a struct with three fields:

struct QueryFragment {
  QueryType queryType;
  IComponent component;
  bytes value;
}

Available query fragments are:

  • Has: filter for entities that have the specified component. The value field in the query fragment is ignored.
  • HasValue: filter for entities that have the specified component with the specified value.
  • Not: filter for entities that do not have the specified component. The value field in th query fragment is ignored.
  • NotValue: filter for entities that do not have the specified component with the specified value.
  • ProxyRead: enable the proxy read mode for the rest of the query (more details below).
  • ProxyExpand: enable the proxy expand mode for the rest of the query (more details below).

The query fragments are executed from left to right and are concatenated with a logical AND. For performance reasons, the most restrictive query fragment should be first in the list of query fragments, in order to reduce the number of entities the next query fragment needs to be checked for. If no proxy fragments are used, every entity in the resulting set passes every query fragment. If setting fragments are used, the order of the query fragments influences the result, since settings only apply to fragments after the setting fragment.

Example: Query for all movable entities at position (0,0):

QueryFragment[] memory fragments = new QueryFragment[](2);

// Specify the more restrictive filter first for performance reasons
fragments[0] = QueryFragment(QueryType.HasValue, position, abi.encode(Coord(0,0)));

// The value argument is ignored in Has query fragments
fragments[1] = QueryFragment(QueryType.Has, movable, new bytes(0));

uint256[] memory entities = LibQuery.query(fragments);

Proxy queries

Proxy query fragments provide a way to query for more advanced recursive or indirect relationships.

The ProxyRead query fragment enables the "proxy read mode" for the rest of the query. This means that for all remaining fragments in the query not only the entities themselves are checked, but also their "ancestors" up to the given depth (specified in the value field of the query fragment) on the relationship chain defined by the given component. The component must be a Uint256Component (a component whose raw value decodes to a single uint256 that points to another entity).

Example: Query for all entities that have a position and are (directly or indirectly) owned by an entity with name "Alice":

QueryFragment[] memory fragments = new QueryFragment[](3);

fragments[0] = QueryFragment(QueryType.Has, position, new bytes(0)));
fragments[1] = QueryFragment(QueryType.ProxyRead, ownedBy, abi.encode(0xff)); // Max depth 255
fragments[2] = QueryFragment(QueryType.HasValue, name, abi.encode("Alice"));

uint256[] memory entities = LibQuery.query(fragments);

The ProxyExpand query fragment enables the "proxy expand mode" for the rest of the query. This means that for all remaining fragments in the query not only the matching entities themselves are included in the intermediate set, but also all their "children" down to the given depth (specified in the value field of the query fragment) on the relationship chain define by the given component. The component must be a Uint256Component (a component whose raw value decodes to a single uint256 that points to another entity).

Example: Query for all entities (directly or indirectly) owned by an entity with name "Alice":

QueryFragment[] memory fragments = new QueryFragment[](2);

fragments[0] = QueryFragment(QueryType.ProxyExpand, ownedBy, abi.encode(0xff))); // Max depth 255
fragments[1] = QueryFragment(QueryType.HasValue, name, abi.encode("Alice"));

uint256[] memory entities = LibQuery.query(fragments);

Proxy queries are significantly less performant (more gas intensive) than regular queries and should be used sparingly.

2.0.0-main-0d434816

10 months ago

2.0.0-alpha.1.258

11 months ago

2.0.0-alpha.1.259

11 months ago

2.0.0-main-b38f2df1

10 months ago

2.0.0-main-b0a1a727

10 months ago

2.0.0-next.2

9 months ago

2.0.0-next.3

9 months ago

2.0.0-next.0

10 months ago

2.0.0-next.1

9 months ago

2.0.0-main-e9258dae

10 months ago

2.0.0-main-8dc847d4

10 months ago

2.0.0-main-8d51a034

10 months ago

2.0.0-alpha.1.261

10 months ago

2.0.0-alpha.1.260

11 months ago

2.0.0-alpha.1.263

10 months ago

2.0.0-alpha.1.262

10 months ago

2.0.0-alpha.1.265

10 months ago

2.0.0-alpha.1.264

10 months ago

2.0.0-alpha.1.267

10 months ago

2.0.0-alpha.1.266

10 months ago

2.0.0-alpha.1.268

10 months ago

2.0.0-main-70e4d8eb

10 months ago

2.0.0-main-53522998

10 months ago

2.0.0-main-f03531d9

10 months ago

2.0.0-main-78b3b1de

10 months ago

2.0.0-main-69a96f10

10 months ago

2.0.0-main-e259ef79

10 months ago

2.0.0-main-4e4a3415

10 months ago

2.0.0-main-5e9eb5a7

10 months ago

2.0.0-main-eeb15cc0

10 months ago

2.0.0-main-c963b46c

10 months ago

2.0.0-main-de2245b6

10 months ago

2.0.0-main-168a4cb4

10 months ago

2.0.0-main-6de86f16

10 months ago

2.0.0-main-a7b30c79

10 months ago

2.0.0-main-0c4f9fea

10 months ago

2.0.0-main-353b9aa2

10 months ago

2.0.0-main-db19ea39

10 months ago

2.0.0-main-df85e0c0

10 months ago

2.0.0-main-184d5b52

10 months ago

2.0.0-main-c36ffd13

10 months ago

2.0.0-main-1e2ad78e

10 months ago

2.0.0-main-4bb7e8cb

10 months ago

2.0.0-main-9cdcd02e

10 months ago

2.0.0-main-48909d15

10 months ago

2.0.0-main-708122b7

10 months ago

2.0.0-alpha.1.252

11 months ago

2.0.0-alpha.1.251

11 months ago

2.0.0-alpha.1.254

11 months ago

2.0.0-alpha.1.253

11 months ago

2.0.0-alpha.1.256

11 months ago

2.0.0-alpha.1.255

11 months ago

2.0.0-alpha.1.257

11 months ago

2.0.0-alpha.1.120

12 months ago

2.0.0-alpha.1.99

12 months ago

2.0.0-alpha.1.243

11 months ago

2.0.0-alpha.1.122

12 months ago

2.0.0-alpha.1.97

12 months ago

2.0.0-alpha.1.242

11 months ago

2.0.0-alpha.1.121

12 months ago

2.0.0-alpha.1.98

12 months ago

2.0.0-alpha.1.95

12 months ago

2.0.0-alpha.1.244

11 months ago

2.0.0-alpha.1.96

12 months ago

2.0.0-alpha.1.247

11 months ago

2.0.0-alpha.1.126

12 months ago

2.0.0-alpha.1.125

12 months ago

2.0.0-alpha.1.249

11 months ago

2.0.0-alpha.1.128

12 months ago

2.0.0-alpha.1.248

11 months ago

2.0.0-alpha.1.127

12 months ago

2.0.0-alpha.1.129

12 months ago

2.0.0-alpha.1.250

11 months ago

2.0.0-alpha.1.131

12 months ago

2.0.0-alpha.1.130

12 months ago

2.0.0-alpha.1.133

12 months ago

2.0.0-alpha.1.132

12 months ago

2.0.0-alpha.1.135

12 months ago

2.0.0-alpha.1.134

12 months ago

2.0.0-alpha.1.137

12 months ago

2.0.0-alpha.1.136

12 months ago

2.0.0-alpha.1.139

12 months ago

2.0.0-alpha.1.138

12 months ago

2.0.0-alpha.1.221

11 months ago

2.0.0-alpha.1.100

12 months ago

2.0.0-alpha.1.223

11 months ago

2.0.0-alpha.1.102

12 months ago

2.0.0-alpha.1.222

11 months ago

2.0.0-alpha.1.101

12 months ago

2.0.0-alpha.1.225

11 months ago

2.0.0-alpha.1.104

12 months ago

2.0.0-alpha.1.224

11 months ago

2.0.0-alpha.1.103

12 months ago

2.0.0-alpha.1.227

11 months ago

2.0.0-alpha.1.106

12 months ago

2.0.0-alpha.1.226

11 months ago

2.0.0-alpha.1.105

12 months ago

2.0.0-alpha.1.229

11 months ago

2.0.0-alpha.1.108

12 months ago

2.0.0-alpha.1.228

11 months ago

2.0.0-alpha.1.107

12 months ago

2.0.0-alpha.1.109

12 months ago

2.0.0-alpha.1.230

11 months ago

2.0.0-alpha.1.232

11 months ago

2.0.0-alpha.1.111

12 months ago

2.0.0-alpha.1.231

11 months ago

2.0.0-alpha.1.110

12 months ago

2.0.0-alpha.1.234

11 months ago

2.0.0-alpha.1.113

12 months ago

2.0.0-alpha.1.233

11 months ago

2.0.0-alpha.1.112

12 months ago

2.0.0-alpha.1.236

11 months ago

2.0.0-alpha.1.115

12 months ago

2.0.0-alpha.1.235

11 months ago

2.0.0-alpha.1.114

12 months ago

2.0.0-alpha.1.238

11 months ago

2.0.0-alpha.1.117

12 months ago

2.0.0-alpha.1.237

11 months ago

2.0.0-alpha.1.116

12 months ago

2.0.0-alpha.1.119

12 months ago

2.0.0-alpha.1.239

11 months ago

2.0.0-alpha.1.118

12 months ago

2.0.0-alpha.1.160

12 months ago

2.0.0-alpha.1.162

12 months ago

2.0.0-alpha.1.161

12 months ago

2.0.0-alpha.1.164

12 months ago

2.0.0-alpha.1.163

12 months ago

2.0.0-alpha.1.166

12 months ago

2.0.0-alpha.1.165

12 months ago

2.0.0-alpha.1.168

12 months ago

2.0.0-alpha.1.167

12 months ago

2.0.0-alpha.1.169

12 months ago

2.0.0-alpha.1.171

12 months ago

2.0.0-alpha.1.170

12 months ago

2.0.0-alpha.1.173

12 months ago

2.0.0-alpha.1.68

12 months ago

2.0.0-alpha.1.172

12 months ago

2.0.0-alpha.1.69

12 months ago

2.0.0-alpha.1.175

12 months ago

2.0.0-alpha.1.174

12 months ago

2.0.0-alpha.1.177

12 months ago

2.0.0-alpha.1.176

12 months ago

2.0.0-alpha.1.179

12 months ago

2.0.0-alpha.1.178

12 months ago

2.0.0-alpha.1.71

12 months ago

2.0.0-alpha.1.72

12 months ago

2.0.0-alpha.1.70

12 months ago

2.0.0-alpha.1.140

12 months ago

2.0.0-alpha.1.79

12 months ago

2.0.0-alpha.1.142

12 months ago

2.0.0-alpha.1.77

12 months ago

2.0.0-alpha.1.141

12 months ago

2.0.0-alpha.1.78

12 months ago

2.0.0-alpha.1.144

12 months ago

2.0.0-alpha.1.75

12 months ago

2.0.0-alpha.1.143

12 months ago

2.0.0-alpha.1.76

12 months ago

2.0.0-alpha.1.146

12 months ago

2.0.0-alpha.1.73

12 months ago

2.0.0-alpha.1.145

12 months ago

2.0.0-alpha.1.74

12 months ago

2.0.0-alpha.1.148

12 months ago

2.0.0-alpha.1.82

12 months ago

2.0.0-alpha.1.147

12 months ago

2.0.0-alpha.1.83

12 months ago

2.0.0-alpha.1.80

12 months ago

2.0.0-alpha.1.149

12 months ago

2.0.0-alpha.1.81

12 months ago

2.0.0-alpha.1.151

12 months ago

2.0.0-alpha.1.150

12 months ago

2.0.0-alpha.1.153

12 months ago

2.0.0-alpha.1.88

12 months ago

2.0.0-alpha.1.152

12 months ago

2.0.0-alpha.1.89

12 months ago

2.0.0-alpha.1.155

12 months ago

2.0.0-alpha.1.86

12 months ago

2.0.0-alpha.1.154

12 months ago

2.0.0-alpha.1.87

12 months ago

2.0.0-alpha.1.157

12 months ago

2.0.0-alpha.1.84

12 months ago

2.0.0-alpha.1.156

12 months ago

2.0.0-alpha.1.85

12 months ago

2.0.0-alpha.1.159

12 months ago

2.0.0-alpha.1.93

12 months ago

2.0.0-alpha.1.158

12 months ago

2.0.0-alpha.1.94

12 months ago

2.0.0-alpha.1.91

12 months ago

2.0.0-alpha.1.92

12 months ago

2.0.0-alpha.1.90

12 months ago

2.0.0-alpha.1.180

12 months ago

2.0.0-alpha.1.182

12 months ago

2.0.0-alpha.1.181

12 months ago

2.0.0-alpha.1.184

12 months ago

2.0.0-alpha.1.183

12 months ago

2.0.0-alpha.1.186

12 months ago

2.0.0-alpha.1.185

12 months ago

2.0.0-alpha.1.188

12 months ago

2.0.0-alpha.1.187

12 months ago

2.0.0-alpha.1.189

12 months ago

2.0.0-alpha.1.191

12 months ago

2.0.0-alpha.1.190

12 months ago

2.0.0-alpha.1.193

12 months ago

2.0.0-alpha.1.192

12 months ago

2.0.0-alpha.1.195

12 months ago

2.0.0-alpha.1.194

12 months ago

2.0.0-alpha.1.197

12 months ago

2.0.0-alpha.1.196

12 months ago

2.0.0-alpha.1.199

11 months ago

2.0.0-alpha.1.198

11 months ago

2.0.0-alpha.1.201

11 months ago

2.0.0-alpha.1.200

11 months ago

2.0.0-alpha.1.203

11 months ago

2.0.0-alpha.1.202

11 months ago

2.0.0-alpha.1.205

11 months ago

2.0.0-alpha.1.204

11 months ago

2.0.0-alpha.1.207

11 months ago

2.0.0-alpha.1.206

11 months ago

2.0.0-alpha.1.209

11 months ago

2.0.0-alpha.1.208

11 months ago

2.0.0-alpha.1.210

11 months ago

2.0.0-alpha.1.212

11 months ago

2.0.0-alpha.1.211

11 months ago

2.0.0-alpha.1.214

11 months ago

2.0.0-alpha.1.213

11 months ago

2.0.0-alpha.1.216

11 months ago

2.0.0-alpha.1.215

11 months ago

2.0.0-alpha.1.218

11 months ago

2.0.0-alpha.1.217

11 months ago

2.0.0-alpha.7

1 year ago

2.0.0-alpha.8

1 year ago

2.0.0-alpha.9

1 year ago

2.0.0-alpha.3

1 year ago

2.0.0-alpha.4

1 year ago

2.0.0-alpha.5

1 year ago

1.41.1-alpha.0

1 year ago

2.0.0-alpha.6

1 year ago

2.0.0-alpha.94

1 year ago

2.0.0-alpha.0

1 year ago

2.0.0-alpha.1

1 year ago

2.0.0-alpha.93

1 year ago

2.0.0-alpha.2

1 year ago

2.0.0-alpha.92

1 year ago

1.40.0

1 year ago

2.0.0-alpha.91

1 year ago

2.0.0-alpha.90

1 year ago

2.0.0-alpha.88

1 year ago

2.0.0-alpha.87

1 year ago

2.0.0-alpha.86

1 year ago

2.0.0-alpha.85

1 year ago

2.0.0-alpha.84

1 year ago

2.0.0-alpha.83

1 year ago

2.0.0-alpha.82

1 year ago

2.0.0-alpha.81

1 year ago

2.0.0-alpha.89

1 year ago

2.0.0-alpha.80

1 year ago

2.0.0-alpha.76

1 year ago

2.0.0-alpha.75

1 year ago

2.0.0-alpha.74

1 year ago

2.0.0-alpha.73

1 year ago

2.0.0-alpha.72

1 year ago

1.41.0

1 year ago

2.0.0-alpha.79

1 year ago

2.0.0-alpha.78

1 year ago

2.0.0-alpha.66

1 year ago

2.0.0-alpha.65

1 year ago

2.0.0-alpha.64

1 year ago

2.0.0-alpha.63

1 year ago

2.0.0-alpha.62

1 year ago

2.0.0-alpha.61

1 year ago

2.0.0-alpha.60

1 year ago

2.0.0-alpha.69

1 year ago

2.0.0-alpha.68

1 year ago

2.0.0-alpha.67

1 year ago

2.0.0-alpha.55

1 year ago

2.0.0-alpha.54

1 year ago

2.0.0-alpha.53

1 year ago

2.0.0-alpha.52

1 year ago

2.0.0-alpha.51

1 year ago

2.0.0-alpha.50

1 year ago

1.42.0

1 year ago

2.0.0-alpha.59

1 year ago

2.0.0-alpha.58

1 year ago

2.0.0-alpha.57

1 year ago

2.0.0-alpha.56

1 year ago

2.0.0-alpha.44

1 year ago

2.0.0-alpha.43

1 year ago

2.0.0-alpha.41

1 year ago

2.0.0-alpha.40

1 year ago

2.0.0-alpha.49

1 year ago

2.0.0-alpha.48

1 year ago

2.0.0-alpha.47

1 year ago

2.0.0-alpha.46

1 year ago

2.0.0-alpha.45

1 year ago

2.0.0-alpha.39

1 year ago

2.0.0-alpha.38

1 year ago

2.0.0-alpha.37

1 year ago

2.0.0-alpha.36

1 year ago

1.43.0

1 year ago

1.41.1-alpha.41

1 year ago

1.37.0

1 year ago

1.35.0

1 year ago

1.37.1

1 year ago

1.39.0

1 year ago

1.36.1

1 year ago

1.34.0

1 year ago

1.38.0

1 year ago

1.33.1

1 year ago

1.31.3

1 year ago

1.29.0

1 year ago

1.27.1

1 year ago

1.32.0

1 year ago

1.30.0

1 year ago

1.30.1

1 year ago

1.26.0

2 years ago

1.28.1

1 year ago

1.28.0

1 year ago

1.31.1

1 year ago

1.31.2

1 year ago

1.31.0

1 year ago

1.15.0

2 years ago

1.2.0

2 years ago

1.1.0

2 years ago

1.13.0

2 years ago

1.0.0

2 years ago

1.12.0

2 years ago

1.19.0

2 years ago

1.18.0

2 years ago

1.17.0

2 years ago

1.16.0

2 years ago

1.14.2

2 years ago

1.9.0

2 years ago

1.8.0

2 years ago

1.7.1

2 years ago

1.7.0

2 years ago

1.6.0

2 years ago

1.5.1

2 years ago

1.5.0

2 years ago

1.4.1

2 years ago

1.4.0

2 years ago

1.3.0

2 years ago

0.16.3

2 years ago

0.16.4

2 years ago

1.21.0

2 years ago

1.22.0

2 years ago

1.20.0

2 years ago

1.24.1

2 years ago

1.25.1

2 years ago

1.23.0

2 years ago

1.24.0

2 years ago

1.23.1

2 years ago

1.11.0

2 years ago

1.10.0

2 years ago

0.16.2

2 years ago

0.16.1

2 years ago

0.16.0

2 years ago

0.15.1

2 years ago

0.15.0

2 years ago

0.14.2

2 years ago

0.14.1

2 years ago

0.14.0

2 years ago

0.13.0

2 years ago

0.12.0

2 years ago

0.11.1

2 years ago

0.10.0

2 years ago

0.9.0

2 years ago