miniplex

A ๐Ÿ‘ฉโ€๐Ÿ’ป developer-friendly entity management system for ๐Ÿ•น games and similarly demanding applications, based on ๐Ÿ›  ECS architecture.

MIT License

Downloads
15.2K
Stars
764
Committers
10

Bot releases are visible (Hide)

miniplex - @miniplex/[email protected]

Published by github-actions[bot] almost 2 years ago

Patch Changes

  • 42134bc: Hooray, it's the first beta release of Miniplex 2.0! While the new documentation website is still deeply work in progress, I'd like to provide you with a summary of the changes so you can start giving this thing a go in your projects.

    Focus:

    Miniplex 2.0 is a complete rewrite of the library, but while it does bring some breaking changes, it will still allow you to do everything that you've been doing with 1.0. When upgrading a 1.0 project to 2.0, most changes you will need to do are related to things having been renamed.

    The headline changes in 2.0:

    • A lot more relaxed and lightweight! Where Miniplex 1.0 would immediately crash your entire application when, for example, adding a component to an entity that already has the component, Miniplex 2.0 will simply no-op and continue.
    • Much more flexible! Miniplex 2.0 finally lets you create archetypes of entities that do not have a specific component, and goes even further than that; you can now create predicate-based archetypes using any kind of function.
    • Better type support! If you're using TypeScript, you will be happy to hear that type support has been significantly improved, with much better narrowed types for created archetypes, and support for both predicates with type guards as well as type generics.
    • The React API has been significantly simplified.

    Installing:

    Miniplex 2.0 is still in beta, so you will need to install it using the beta tag:

    npm add miniplex@beta
    yarn add miniplex@beta
    pnpm add miniplex@beta
    

    Core:

    • world.createEntity has been renamed and simplified to just world.add (which now returns the correct type for the entity, too), and world.destroyEntity to world.remove. addComponent and removeComponent have not been changed.

      const entity = world.add({ position: { x: 0, y: 0 } })
      world.addComponent(entity, { velocity: { x: 0, y: 0 } })
      world.remove(entity)
      
    • There is a new world.update function that you may use to update an entity and make sure it is reindexed across the various archetypes. It provides a number of different overloads to provide some flexibility in how you update the entity.

      world.update(entity, { position: { x: 1, y: 1 } })
      world.update(entity, "position", { x: 1, y: 1 })
      world.update(entity, () => ({ position: { x: 1, y: 1 } }))
      world.update(entity, (e) => (e.position = { x: 1, y: 1 }))
      

      Please keep in mind that in Miniplex, you'll typically mutate your entities anyway, so going through this function is not strictly necessary.

    • The Tag type and constant have been removed. For tag-like components, simply use true (which Tag was just an alias for.)

    • Entities added to a world no longer receive a __miniplex component. This component has always been an internal implementation detail, but you might have used it in the past to get a unique identifier for an entity. This can now be done through world.id(entity), with ID lookups being available through world.entity(id).

    • Archetypes can now be iterated over directly. Example:

      const moving = world.archetype("position", "velocity")
      
      for (const { position, velocity } of moving) {
        position.x += velocity.x
        position.y += velocity.y
      }
      

      You can use this to neatly fetch the first entity from an archetype that you only expect to have a single entity in it:

      const [player] = world.archetype("player")
      
    • The queuing functionality that was built into the World class has been removed. If you've relied on this in the past, miniplex now exports a queue object that you can use instead. Example:

      import { queue } from "miniplex"
      
      queue(() => {
        // Do something
      })
      
      /* Later */
      queue.flush()
      

      Please note that this is being provided to make upgrading to 2.0 a little easier, and will likely be removed in a future version.

    • world.archetype can now take a predicate! You can use this as an escape hatch for creating any kind of archetype based on the conditions you specify. Example:

      const almostDead = world.archetype((entity) => entity.health < 10)
      

      Please note that his requires entities with the health component to be updated through the world.update function in order to keep the archetype up to date.

    • You can use with and without as an alternative API for creating archetypes. Example:

      const moving = world.with("position", "velocity")
      const alive = world.without("dead")
      
    • You can use where to create a predicate-based iterator. This allows you to quickly filter a set of entities without creating new archetypes or other objects. Example:

      for (const entity of world.where((entity) => entity.health < 10)) {
        // Do something
      }
      
    • All of these can be nested!

      world
        .with("position", "velocity")
        .without("dead")
        .where((entity) => entity.health < 10)
      
    • Entities fetched from an archetype will have much improved types, but you can also specify a type to narrow to via these functions' generics:

      const player = world.archetype<Player>("player")
      
    • Miniplex provides the new Strict and With types which you can use to compose types from your entity main type:

      type Entity = {
        position: { x: number; y: number }
        velocity: { x: number; y: number }
      }
      
      type Player = Strict<With<Entity, "position" | "velocity">>
      
      const player = world.archetype<Player>("player")
      

    React:

    • The React package's main import and initialization has been changed:

      import { World } from "miniplex"
      import { createReactAPI } from "miniplex/react" // !
      
      /* It now expects a world as its argument, so you need to create one first: */
      const world = new World()
      const ECS = createReactAPI(world)
      
    • The <Archetype> component now supports the with and without properties:

      <Archetype with={["position", "velocity"]} without="dead">
        {/* ... */}
      </Archetype>
      
    • If you already have a reference to an archetype, you can pass it to the newly improved <Entities> component to automatically render all entities contained within it, and have them automatically update when the archetype changes:

      <Entities in={archetype}>{/* ... */}</Entities>
      

      If you ever want to list a simple array of entities, you can use the same component (but it will not automatically update if the array contents change):

      <Entities in={[entity1, entity2]}>{/* ... */}</Entities>
      
    • <ManagedEntities> has been removed. You were probably not using it. If you were, you can replicate the same behavior using a combination of the <Entities> or <Archetype> components and a useEffect hook.

    • The useEntity hook has been renamed to useCurrentEntity.

    • The world-scoped useArchetype hook has been removed, and superceded by the new global useEntities hook:

      /* Before */
      const entities = useArchetype("position", "velocity")
      
      /* Now */
      const entities = useEntities(world.with("position", "velocity"))
      

    Feedback and Questions?

    This is the first beta of a big new release for this library, and since it is a complete rewrite, there are bound to be some bugs and rough edges.

  • Updated dependencies [42134bc]

miniplex - @miniplex/[email protected]

Published by github-actions[bot] almost 2 years ago

Patch Changes

  • 42134bc: Hooray, it's the first beta release of Miniplex 2.0! While the new documentation website is still deeply work in progress, I'd like to provide you with a summary of the changes so you can start giving this thing a go in your projects.

    Focus:

    Miniplex 2.0 is a complete rewrite of the library, but while it does bring some breaking changes, it will still allow you to do everything that you've been doing with 1.0. When upgrading a 1.0 project to 2.0, most changes you will need to do are related to things having been renamed.

    The headline changes in 2.0:

    • A lot more relaxed and lightweight! Where Miniplex 1.0 would immediately crash your entire application when, for example, adding a component to an entity that already has the component, Miniplex 2.0 will simply no-op and continue.
    • Much more flexible! Miniplex 2.0 finally lets you create archetypes of entities that do not have a specific component, and goes even further than that; you can now create predicate-based archetypes using any kind of function.
    • Better type support! If you're using TypeScript, you will be happy to hear that type support has been significantly improved, with much better narrowed types for created archetypes, and support for both predicates with type guards as well as type generics.
    • The React API has been significantly simplified.

    Installing:

    Miniplex 2.0 is still in beta, so you will need to install it using the beta tag:

    npm add miniplex@beta
    yarn add miniplex@beta
    pnpm add miniplex@beta
    

    Core:

    • world.createEntity has been renamed and simplified to just world.add (which now returns the correct type for the entity, too), and world.destroyEntity to world.remove. addComponent and removeComponent have not been changed.

      const entity = world.add({ position: { x: 0, y: 0 } })
      world.addComponent(entity, { velocity: { x: 0, y: 0 } })
      world.remove(entity)
      
    • There is a new world.update function that you may use to update an entity and make sure it is reindexed across the various archetypes. It provides a number of different overloads to provide some flexibility in how you update the entity.

      world.update(entity, { position: { x: 1, y: 1 } })
      world.update(entity, "position", { x: 1, y: 1 })
      world.update(entity, () => ({ position: { x: 1, y: 1 } }))
      world.update(entity, (e) => (e.position = { x: 1, y: 1 }))
      

      Please keep in mind that in Miniplex, you'll typically mutate your entities anyway, so going through this function is not strictly necessary.

    • The Tag type and constant have been removed. For tag-like components, simply use true (which Tag was just an alias for.)

    • Entities added to a world no longer receive a __miniplex component. This component has always been an internal implementation detail, but you might have used it in the past to get a unique identifier for an entity. This can now be done through world.id(entity), with ID lookups being available through world.entity(id).

    • Archetypes can now be iterated over directly. Example:

      const moving = world.archetype("position", "velocity")
      
      for (const { position, velocity } of moving) {
        position.x += velocity.x
        position.y += velocity.y
      }
      

      You can use this to neatly fetch the first entity from an archetype that you only expect to have a single entity in it:

      const [player] = world.archetype("player")
      
    • The queuing functionality that was built into the World class has been removed. If you've relied on this in the past, miniplex now exports a queue object that you can use instead. Example:

      import { queue } from "miniplex"
      
      queue(() => {
        // Do something
      })
      
      /* Later */
      queue.flush()
      

      Please note that this is being provided to make upgrading to 2.0 a little easier, and will likely be removed in a future version.

    • world.archetype can now take a predicate! You can use this as an escape hatch for creating any kind of archetype based on the conditions you specify. Example:

      const almostDead = world.archetype((entity) => entity.health < 10)
      

      Please note that his requires entities with the health component to be updated through the world.update function in order to keep the archetype up to date.

    • You can use with and without as an alternative API for creating archetypes. Example:

      const moving = world.with("position", "velocity")
      const alive = world.without("dead")
      
    • You can use where to create a predicate-based iterator. This allows you to quickly filter a set of entities without creating new archetypes or other objects. Example:

      for (const entity of world.where((entity) => entity.health < 10)) {
        // Do something
      }
      
    • All of these can be nested!

      world
        .with("position", "velocity")
        .without("dead")
        .where((entity) => entity.health < 10)
      
    • Entities fetched from an archetype will have much improved types, but you can also specify a type to narrow to via these functions' generics:

      const player = world.archetype<Player>("player")
      
    • Miniplex provides the new Strict and With types which you can use to compose types from your entity main type:

      type Entity = {
        position: { x: number; y: number }
        velocity: { x: number; y: number }
      }
      
      type Player = Strict<With<Entity, "position" | "velocity">>
      
      const player = world.archetype<Player>("player")
      

    React:

    • The React package's main import and initialization has been changed:

      import { World } from "miniplex"
      import { createReactAPI } from "miniplex/react" // !
      
      /* It now expects a world as its argument, so you need to create one first: */
      const world = new World()
      const ECS = createReactAPI(world)
      
    • The <Archetype> component now supports the with and without properties:

      <Archetype with={["position", "velocity"]} without="dead">
        {/* ... */}
      </Archetype>
      
    • If you already have a reference to an archetype, you can pass it to the newly improved <Entities> component to automatically render all entities contained within it, and have them automatically update when the archetype changes:

      <Entities in={archetype}>{/* ... */}</Entities>
      

      If you ever want to list a simple array of entities, you can use the same component (but it will not automatically update if the array contents change):

      <Entities in={[entity1, entity2]}>{/* ... */}</Entities>
      
    • <ManagedEntities> has been removed. You were probably not using it. If you were, you can replicate the same behavior using a combination of the <Entities> or <Archetype> components and a useEffect hook.

    • The useEntity hook has been renamed to useCurrentEntity.

    • The world-scoped useArchetype hook has been removed, and superceded by the new global useEntities hook:

      /* Before */
      const entities = useArchetype("position", "velocity")
      
      /* Now */
      const entities = useEntities(world.with("position", "velocity"))
      

    Feedback and Questions?

    This is the first beta of a big new release for this library, and since it is a complete rewrite, there are bound to be some bugs and rough edges.

miniplex - [email protected]

Published by github-actions[bot] almost 2 years ago

Patch Changes

  • 42134bc: Hooray, it's the first beta release of Miniplex 2.0! While the new documentation website is still deeply work in progress, I'd like to provide you with a summary of the changes so you can start giving this thing a go in your projects.

    Focus:

    Miniplex 2.0 is a complete rewrite of the library, but while it does bring some breaking changes, it will still allow you to do everything that you've been doing with 1.0. When upgrading a 1.0 project to 2.0, most changes you will need to do are related to things having been renamed.

    The headline changes in 2.0:

    • A lot more relaxed and lightweight! Where Miniplex 1.0 would immediately crash your entire application when, for example, adding a component to an entity that already has the component, Miniplex 2.0 will simply no-op and continue.
    • Much more flexible! Miniplex 2.0 finally lets you create archetypes of entities that do not have a specific component, and goes even further than that; you can now create predicate-based archetypes using any kind of function.
    • Better type support! If you're using TypeScript, you will be happy to hear that type support has been significantly improved, with much better narrowed types for created archetypes, and support for both predicates with type guards as well as type generics.
    • The React API has been significantly simplified.

    Installing:

    Miniplex 2.0 is still in beta, so you will need to install it using the beta tag:

    npm add miniplex@beta
    yarn add miniplex@beta
    pnpm add miniplex@beta
    

    Core:

    • world.createEntity has been renamed and simplified to just world.add (which now returns the correct type for the entity, too), and world.destroyEntity to world.remove. addComponent and removeComponent have not been changed.

      const entity = world.add({ position: { x: 0, y: 0 } })
      world.addComponent(entity, { velocity: { x: 0, y: 0 } })
      world.remove(entity)
      
    • There is a new world.update function that you may use to update an entity and make sure it is reindexed across the various archetypes. It provides a number of different overloads to provide some flexibility in how you update the entity.

      world.update(entity, { position: { x: 1, y: 1 } })
      world.update(entity, "position", { x: 1, y: 1 })
      world.update(entity, () => ({ position: { x: 1, y: 1 } }))
      world.update(entity, (e) => (e.position = { x: 1, y: 1 }))
      

      Please keep in mind that in Miniplex, you'll typically mutate your entities anyway, so going through this function is not strictly necessary.

    • The Tag type and constant have been removed. For tag-like components, simply use true (which Tag was just an alias for.)

    • Entities added to a world no longer receive a __miniplex component. This component has always been an internal implementation detail, but you might have used it in the past to get a unique identifier for an entity. This can now be done through world.id(entity), with ID lookups being available through world.entity(id).

    • Archetypes can now be iterated over directly. Example:

      const moving = world.archetype("position", "velocity")
      
      for (const { position, velocity } of moving) {
        position.x += velocity.x
        position.y += velocity.y
      }
      

      You can use this to neatly fetch the first entity from an archetype that you only expect to have a single entity in it:

      const [player] = world.archetype("player")
      
    • The queuing functionality that was built into the World class has been removed. If you've relied on this in the past, miniplex now exports a queue object that you can use instead. Example:

      import { queue } from "miniplex"
      
      queue(() => {
        // Do something
      })
      
      /* Later */
      queue.flush()
      

      Please note that this is being provided to make upgrading to 2.0 a little easier, and will likely be removed in a future version.

    • world.archetype can now take a predicate! You can use this as an escape hatch for creating any kind of archetype based on the conditions you specify. Example:

      const almostDead = world.archetype((entity) => entity.health < 10)
      

      Please note that his requires entities with the health component to be updated through the world.update function in order to keep the archetype up to date.

    • You can use with and without as an alternative API for creating archetypes. Example:

      const moving = world.with("position", "velocity")
      const alive = world.without("dead")
      
    • You can use where to create a predicate-based iterator. This allows you to quickly filter a set of entities without creating new archetypes or other objects. Example:

      for (const entity of world.where((entity) => entity.health < 10)) {
        // Do something
      }
      
    • All of these can be nested!

      world
        .with("position", "velocity")
        .without("dead")
        .where((entity) => entity.health < 10)
      
    • Entities fetched from an archetype will have much improved types, but you can also specify a type to narrow to via these functions' generics:

      const player = world.archetype<Player>("player")
      
    • Miniplex provides the new Strict and With types which you can use to compose types from your entity main type:

      type Entity = {
        position: { x: number; y: number }
        velocity: { x: number; y: number }
      }
      
      type Player = Strict<With<Entity, "position" | "velocity">>
      
      const player = world.archetype<Player>("player")
      

    React:

    • The React package's main import and initialization has been changed:

      import { World } from "miniplex"
      import { createReactAPI } from "miniplex/react" // !
      
      /* It now expects a world as its argument, so you need to create one first: */
      const world = new World()
      const ECS = createReactAPI(world)
      
    • The <Archetype> component now supports the with and without properties:

      <Archetype with={["position", "velocity"]} without="dead">
        {/* ... */}
      </Archetype>
      
    • If you already have a reference to an archetype, you can pass it to the newly improved <Entities> component to automatically render all entities contained within it, and have them automatically update when the archetype changes:

      <Entities in={archetype}>{/* ... */}</Entities>
      

      If you ever want to list a simple array of entities, you can use the same component (but it will not automatically update if the array contents change):

      <Entities in={[entity1, entity2]}>{/* ... */}</Entities>
      
    • <ManagedEntities> has been removed. You were probably not using it. If you were, you can replicate the same behavior using a combination of the <Entities> or <Archetype> components and a useEffect hook.

    • The useEntity hook has been renamed to useCurrentEntity.

    • The world-scoped useArchetype hook has been removed, and superceded by the new global useEntities hook:

      /* Before */
      const entities = useArchetype("position", "velocity")
      
      /* Now */
      const entities = useEntities(world.with("position", "velocity"))
      

    Feedback and Questions?

    This is the first beta of a big new release for this library, and since it is a complete rewrite, there are bound to be some bugs and rough edges.

  • Updated dependencies [42134bc]

miniplex - [email protected]

Published by github-actions[bot] almost 2 years ago

Patch Changes

  • Updated dependencies [b01cf43]
  • Updated dependencies [6f44ee7]
  • Updated dependencies [26cedc6]
  • Updated dependencies [2bcec9b]
miniplex - @miniplex/[email protected]

Published by github-actions[bot] almost 2 years ago

Patch Changes

  • 2bcec9b: Breaking Change: Removed the as prop. Please use children instead:

    /* A function component whose props signature matches the entity type */
    const User = ({ name }: { name: string }) => <div>{name}</div>
    
    /* Pass it directly into the `children` prop */
    <Entity in={users} children={User} />
    

    As a reminder, this sort of children prop support isn't new; Miniplex has always supported this form:

    <Entity in={users}>{(user) => <div>{user.name}</div>}</Entity>
    

    Passing a children prop is just another way to pass children to a React component.

miniplex - @miniplex/[email protected]

Published by github-actions[bot] almost 2 years ago

Patch Changes

  • b01cf43: You can now call update() on any derived bucket to make it reindex all of the entities containeed in the source bucket it's derived from.

  • 6f44ee7: The former world.update function has been renamed to world.evaluate.

  • 26cedc6: Added an update function to the World class that allows the user to update an entity. This is intended to complement the other two mutating functions (addComponent, removeComponent), simply to allow for the use of syntactic sugar (component constructor functions, entity factories, etc.) in a streamlined fashion:

    /* With an object with changes */
    world.update(entity, { age: entity.age + 1 })
    
    /* With a function returning an object with changes */
    const increaseAge = ({age}) => ({ age: age + 1 }
    world.update(entity, increaseAge)
    
    /* With a function that mutates the entity directly: */
    const mutatingIncreaseAge = (entity) => entity.age++
    world.update(entity, mutatingIncreaseAge)
    

    The main job of the function is to re-index the updated entity against known derived buckets, and since in Miniplex you'll typically mutate entities directly, it can even be called with only an entity. All of the above are essentially equivalent to:

    entity.age++
    world.update(entity)
    
miniplex - @miniplex/[email protected]

Published by github-actions[bot] almost 2 years ago

Patch Changes

  • c40587e: Fixed a bug where <Component>, with the capturing ref form, would remove the ECS component a little too eagerly.
miniplex - @miniplex/[email protected]

Published by github-actions[bot] almost 2 years ago

Patch Changes

  • e0684cd: Removed IEntityIterator<T>, just use Iterable<T> instead.
miniplex - [email protected]

Published by github-actions[bot] almost 2 years ago

Patch Changes

miniplex - @miniplex/[email protected]

Published by github-actions[bot] almost 2 years ago

Patch Changes

  • 531f4ae: Breaking Change: The friendlier, cozier 1.0 API is back. You now create archetypes once again through world.archetype:

    /* Component name form */
    world.archetype("name")
    world.archetype("name", "age")
    
    /* Query form (allows for without checks) */
    world.archetype({ with: ["age"], without: ["height"] })
    

    These can now be nested:

    world.archetype("name").archetype("age")
    

    archetype also takes a function predicate:

    world.archetype("age").archetype((e) => e.age > 18)
    

    Warning This will only be evaluated whenever the entity is added to the archetype from its source, and every time components are added to or removed from it, but not when any of the actual component values change.

    where produces a short-lived iterator that allows a system to only operate on a subset of entities, without creating a new archetype, which in some situations will be much more efficient than creating value-based archetypes and keeping them updated:

    const withAge = world.archetype("age")
    
    for (const entity of withAge.where((e) => e.age > 18)) {
      /* Do something with entity */
    }
    
  • c6abd0b: Added .with(...components) and .without(...components) functions to all entity buckets.

    /* Equivalent */
    world.with("foo")
    world.archetype("foo")
    
    /* Equivalent */
    world.without("foo")
    world.archetype({ without: ["foo"] })
    
  • Updated dependencies [531f4ae]

miniplex - [email protected]

Published by github-actions[bot] almost 2 years ago

Patch Changes

  • Updated dependencies [531f4ae]
  • Updated dependencies [531f4ae]
  • Updated dependencies [c6abd0b]
  • Updated dependencies [531f4ae]
  • Updated dependencies [78745ab]
miniplex - @miniplex/[email protected]

Published by github-actions[bot] almost 2 years ago

Patch Changes

  • 531f4ae: @miniplex/bucket has been simplified to only contain the Bucket class (and the most simple version of it, too.)
miniplex - @miniplex/[email protected]

Published by github-actions[bot] almost 2 years ago

Patch Changes

  • 531f4ae: createReactAPI no longer returns useEntities. Please use the globally exported useEntities instead.

  • 531f4ae: The <Archetype> component is back:

    <Archetype with="isEnemy" without={("dead", "paused")} as={Enemy} />
    
  • 78745ab: The value prop of <Component> has been changed (back) to data, to match the 1.0 API.

miniplex - [email protected]

Published by github-actions[bot] almost 2 years ago

Patch Changes

  • 94054ed: Now exporting queue, a simple queueing mechanism that is being provided for convenience, and to make upgrading from Miniplex 1.0 (which provided its own
    queueing mechanism) a little easier.

    import { queue } from "miniplex"
    
    /* Queue some work */
    queue(() => console.log("Hello, world!"))
    
    /* Call flush to execute all queued work */
    queue.flush()
    

    Note queue will likely be marked as deprecated in a future version, and eventually removed. It's simply an instance of a queue provided by the @hmans/queue package, which you can also just use directly.

  • Updated dependencies [a293a9c]

miniplex - @miniplex/[email protected]

Published by github-actions[bot] almost 2 years ago

Patch Changes

  • a293a9c: The object returned by createReactAPI now contains the world originally passed into it, as world.
miniplex - @miniplex/[email protected]

Published by github-actions[bot] almost 2 years ago

Patch Changes

  • 682caf4: Renamed the WithComponent<E, P> helper type to With<E, P>. Also added the Strictly<T> type that removes all non-required properties from a given type. These can be combined to create a type that is a strict version of a specificy type of entity:

    type Player = With<Entity, "isPlayer" | "transform" | "health">
    
    const players = world.where<Strictly<Player>>(archetype("isPlayer"))
    
  • 8ff926c: Experimental new tagged predicate factory.

miniplex - [email protected]

Published by github-actions[bot] almost 2 years ago

Patch Changes

  • Updated dependencies [682caf4]
  • Updated dependencies [8ff926c]
miniplex - @miniplex/[email protected]

Published by github-actions[bot] almost 2 years ago

Patch Changes

  • 5bf4733: Removed IEntity - amazingly, we no longer need it at all!
  • 2efcd9e: isArchetype(entity, query), hasComponents(entity, ...c), hasAnyComponents(entity, ...c) and hasNoComponents(entity, ...c) helpers.
  • Updated dependencies [6eee056]
miniplex - @miniplex/[email protected]

Published by github-actions[bot] almost 2 years ago

Patch Changes

  • 6eee056: Extracted the simple, non-queryable core of the Bucket class into a new SimpleBucket class.
miniplex - [email protected]

Published by github-actions[bot] almost 2 years ago

Patch Changes