Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace addSystem() and removeSystem(). #12

Merged
merged 2 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 15 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class EchoesExample {
Echoes.init();

//Create and activate an instance of our system.
Echoes.addSystem(new RenderSystem()); //Details below.
new RenderSystem().activate(); //Details below.

//Create an entity with the components our system will use. Entities are
//activated automatically unless `false` is passed.
Expand Down Expand Up @@ -114,8 +114,8 @@ class EchoesExample {

//Adding `physicsSystems` first means that entire group will run before
//`RenderSystem`, even if more systems are added later.
Echoes.addSystem(physicsSystems);
Echoes.addSystem(new RenderSystem()); //Details in previous example.
physicsSystems.activate();
new RenderSystem().activate(); //Details in previous example.

//Create entities: one tree and two rabbits. The rabbits will race
//towards the tree.
Expand Down Expand Up @@ -303,9 +303,9 @@ class Main {

//Run all `enterFrame` systems first, then all `midFrame` systems, then
//all `exitFrame` systems.
Echoes.addSystem(enterFrame);
Echoes.addSystem(midFrame);
Echoes.addSystem(exitFrame);
enterFrame.activate();
midFrame.activate();
exitFrame.activate();

//Even if `exitFrame` systems are defined first, they'll run last.
exitFrame.add(new ExitFrameSystem());
Expand Down Expand Up @@ -333,11 +333,11 @@ class Main {
var enterFrame:SystemList = new SystemList();
enterFrame.add(new EnterFrameSystem());
enterFrame.add(new EnterFrameSystem2());
Echoes.addSystem(enterFrame);
enterFrame.activate();

var midFrame:SystemList = new SystemList();
midFrame.add(new MidFrameSystem());
Echoes.addSystem(midFrame);
midFrame.activate();

//Set up `physics` as part of `midFrame`.
var physics:SystemList = new SystemList();
Expand All @@ -355,7 +355,7 @@ class Main {
var exitFrame:SystemList = new SystemList();
exitFrame.add(new ExitFrameSystem());
exitFrame.add(new ExitFrameSystem2());
Echoes.addSystem(exitFrame);
exitFrame.activate();
}
}
```
Expand Down Expand Up @@ -430,8 +430,8 @@ class Main {

//Because `AverageSystem` and `list` both have priority 0, they run in
//the order they're added.
Echoes.addSystem(new AverageSystem());
Echoes.addSystem(list);
new AverageSystem().activate();
list.activate();

//No matter how high a system's priority, if it's added to `list` it
//will run during `list`, and will come after `AverageSystem`.
Expand Down Expand Up @@ -576,11 +576,13 @@ Echoes offers a few ways to customize compilation.

### Since v1.0.0-rc.5

- `Echoes.addSystem()`, `Echoes.hasSystem()`, and `Echoes.removeSystem()` have been replaced by `system.activate()`, `system.active`, and `system.deactivate()`, respectively.
- `Entity.getComponents()` now returns a list of `ComponentStorage` instances, instead of a map. If you prefer the old format, you can perform an implicit cast: `var map:Map<String, Dynamic> = Entity.getComponents()`.
- Systems no longer receive `@:remove` events when deactivated. For instance, a system removed by `Echoes.removeSystem()` won't receive a bunch of events.
- `View.entities` is now an `Array` rather than a `List`. You can still iterate over it as before, but you'll have to call `contains()` rather than `has()` if you want to check existence.
- `Echoes.activeEntities` and `View.entities` may be re-ordered when entities or their components are removed. You can set `-D echoes_stable_order` to preserve the order, potentially at the cost of speed.
- `@:remove` listeners are no longer allowed to add back the component that's currently being removed. They may still add other components as normal.
- `SystemList.exists()` now searches recursively, returning true for grandchildren as well as direct children.

### Since v1.0.0-rc.3

Expand Down Expand Up @@ -630,6 +632,8 @@ Find | Replace with | Notes
`Echoes.entities` | `Echoes.activeEntities`
`Echoes.views` | `Echoes.activeViews`
`Echoes.systems` | `Echoes.activeSystems`
`Echoes.addSystem(system)` | `system.activate()` | You might have used a different variable name than `system`.
`Echoes.removeSystem(system)` | `system.deactivate()` | Ditto.
`AbstractView` | `ViewBase` | Import `echoes.View`.
`ISystem` | `System` | Change "`implements`" to "`extends`," if applicable.
`ICleanableComponentContainer` | `ComponentStorage`
Expand Down
38 changes: 23 additions & 15 deletions src/echoes/Echoes.hx
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,38 @@ class Echoes {

@:allow(echoes.ViewBase)
private static final _activeViews:Array<ViewBase> = [];
/**
* All currently-active views.
*/
public static var activeViews(get, never):ReadOnlyArray<ViewBase>;
private static inline function get_activeViews():ReadOnlyArray<ViewBase> return _activeViews;

/**
* All currently-active systems. Unlike `activeEntities` and `activeViews`,
* this is not a flat array, but rather the root node of a tree: it may
* contain `SystemList`s containing `SystemList`s. All active systems will
* be somewhere in this tree.
*
* Adding a system to this list (whether directly, via `addSystem()`, or by
* adding a list containing that system) activates that system.
*
* Removing a system from this list (whether directly, via `removeSystem()`,
* or by removing a list containing that system) deactivates that system.
*
* To search the full tree, use `activeSystems.find()`.
*/
public static var activeSystems(default, null):SystemList = {
var activeSystems:SystemList = new SystemList();
activeSystems.__activate__();
activeSystems.clock.maxTime = 1;
activeSystems;
};

/**
* The clock used to update `activeSystems`. This starts with all the usual
* defaults, except `maxTime` is set to 1 second. Any changes you make to
* this clock will be preserved, even after `Echoes.reset()`.
*/
public static var clock(get, never):Clock;
private static inline function get_clock():Clock {
return activeSystems.clock;
Expand Down Expand Up @@ -141,21 +164,6 @@ class Echoes {
init(0);
}

//System management
//=================

public static inline function addSystem(system:System):Void {
activeSystems.add(system);
}

public static inline function removeSystem(system:System):Void {
activeSystems.remove(system);
}

public static inline function hasSystem(system:System):Bool {
return activeSystems.exists(system);
}

//Singleton getters
//=================

Expand Down
16 changes: 16 additions & 0 deletions src/echoes/System.hx
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,22 @@ class System {
//Everything else is handled by macro.
}

/**
* Adds this to `activeSystems`, activating it.
*
* Note: you can also activate this by adding it to an active `SystemList`.
*/
public inline function activate():Void {
Echoes.activeSystems.add(this);
}

/**
* Removes this from `activeSystems`, deactivating it.
*/
public inline function deactivate():Void {
parent.remove(this);
}

/**
* @see `SystemList.find()`
*/
Expand Down
10 changes: 7 additions & 3 deletions src/echoes/SystemList.hx
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,15 @@ class SystemList extends System {
}

/**
* Returns whether this list directly contains the given system.
* @see `find()` if you need to recursively search child lists.
* Returns whether this list directly or indirectly contains `system`. Use
* `system.parent` instead if you only want its direct parent.
*/
public inline function exists(system:System):Bool {
return system.parent == this;
var parent:SystemList = system.parent;
while(parent != null && parent != this) {
parent = parent.parent;
}
return parent == this;
}

/**
Expand Down
12 changes: 6 additions & 6 deletions test/AdvancedFunctionalityTest.hx
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ class AdvancedFunctionalityTest extends Test {
}

private function testEntityTemplates():Void {
Echoes.addSystem(new NameSystem());
Echoes.addSystem(new AppearanceSystem());
new NameSystem().activate();
new AppearanceSystem().activate();

var entity:Entity = new Entity();
entity.add(("John":Name));
Expand Down Expand Up @@ -112,7 +112,7 @@ class AdvancedFunctionalityTest extends Test {

private function testGenerics():Void {
var system:GenericSystem<String, Int> = new GenericSystem<String, Int>();
Echoes.addSystem(system);
system.activate();

var entity:Entity = new Entity();
entity.add("STRING");
Expand All @@ -133,7 +133,7 @@ class AdvancedFunctionalityTest extends Test {
}

var system = new GenericSystem<Alias<Name>, String>();
Echoes.addSystem(system);
system.activate();

entity.add(("NAME":Alias<Name>));
switch(system.record) {
Expand Down Expand Up @@ -311,13 +311,13 @@ class AdvancedFunctionalityTest extends Test {
Assert.equals(0, viewOfColor.entities.length);

//Adding/removing the system should activate/deactivate the linked view.
Echoes.addSystem(nameSystem);
nameSystem.activate();
Assert.isTrue(viewOfColor.active);
Assert.equals(2, viewOfColor.entities.length);
Assert.isTrue(viewOfColor.entities.contains(colorName));
Assert.isTrue(viewOfColor.entities.contains(colorShape));

Echoes.removeSystem(nameSystem);
nameSystem.deactivate();
Assert.isFalse(viewOfColor.active);
Assert.equals(0, viewOfColor.entities.length);
}
Expand Down
10 changes: 5 additions & 5 deletions test/BasicFunctionalityTest.hx
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ class BasicFunctionalityTest extends Test {
Assert.isFalse(inactive.active);
Assert.equals(0, Echoes.activeEntities.length);

Echoes.addSystem(new AppearanceSystem());
new AppearanceSystem().activate();
assertTimesCalled(0, "AppearanceSystem.colorAdded");

//Add some components the system looks for.
Expand All @@ -159,7 +159,7 @@ class BasicFunctionalityTest extends Test {
var appearanceSystem:AppearanceSystem = new AppearanceSystem();
Assert.equals(0, Echoes.activeSystems.length);

Echoes.addSystem(appearanceSystem);
appearanceSystem.activate();
Assert.equals(1, Echoes.activeSystems.length);
assertTimesCalled(0, "AppearanceSystem.colorAdded");

Expand Down Expand Up @@ -193,7 +193,7 @@ class BasicFunctionalityTest extends Test {
redLine.add(("redLine":Name));
assertTimesCalled(0, "NameSystem.nameAdded", "NameSystem isn't active but its method was still called.");

Echoes.addSystem(nameSystem);
nameSystem.activate();
assertTimesCalled(2, "NameSystem.nameAdded");
assertTimesCalled(0, "NameSystem.nameRemoved");

Expand Down Expand Up @@ -223,7 +223,7 @@ class BasicFunctionalityTest extends Test {
assertTimesCalled(2, "NameSystem.nameRemoved");

//Deactivate a system.
Echoes.removeSystem(nameSystem);
nameSystem.deactivate();
assertTimesCalled(2, "NameSystem.nameRemoved");

//Destroy the remaining entity.
Expand All @@ -238,7 +238,7 @@ class BasicFunctionalityTest extends Test {
private function testUpdateEvents():Void {
//Create a `TimeCountSystem` and use a custom `Clock`.
var systems:SystemList = new SystemList(new OneSecondClock());
Echoes.addSystem(systems);
systems.activate();

var timeCountSystem:TimeCountSystem = new TimeCountSystem();
Assert.equals(0.0, timeCountSystem.totalTime);
Expand Down
12 changes: 6 additions & 6 deletions test/EdgeCaseTest.hx
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ class EdgeCaseTest extends Test {
//Tests may be run in any order, but not in parallel.

private function testChildSystems():Void {
Echoes.addSystem(new NameSubsystem());
new NameSubsystem().activate();

var entity:Entity = new Entity();
entity.add(("Name":Name));
assertTimesCalled(0, "NameSystem.nameAdded");
assertTimesCalled(1, "NameSubsystem.nameAdded");

Echoes.addSystem(new NameSystem());
new NameSystem().activate();
assertTimesCalled(1, "NameSystem.nameAdded");
assertTimesCalled(0, "NameSystem.nameRemoved");
assertTimesCalled(0, "NameSubsystem.nameRemoved");
Expand All @@ -45,7 +45,7 @@ class EdgeCaseTest extends Test {
}

private function testComponentsExist():Void {
Echoes.addSystem(new ComponentsExistSystem());
new ComponentsExistSystem().activate();

var entity:Entity = new Entity();
entity.add(("name":Name));
Expand Down Expand Up @@ -136,7 +136,7 @@ class EdgeCaseTest extends Test {
}

private function testRedundantOperations():Void {
Echoes.addSystem(new AppearanceSystem());
new AppearanceSystem().activate();

var entity:Entity = new Entity(false);

Expand Down Expand Up @@ -171,7 +171,7 @@ class EdgeCaseTest extends Test {
assertTimesCalled(1, "AppearanceSystem.colorRemoved");

//Replace a `Name` with itself.
Echoes.addSystem(new NameSystem());
new NameSystem().activate();
entity.add(("name":Name));
assertTimesCalled(1, "NameSystem.nameAdded");
assertTimesCalled(0, "NameSystem.nameRemoved");
Expand All @@ -189,7 +189,7 @@ class EdgeCaseTest extends Test {
var entity:Entity = new Entity();

//Activate the system first so that it can process events first.
Echoes.addSystem(new RecursiveEventSystem());
new RecursiveEventSystem().activate();

//Certain events should stop propagating after `RecursiveEventSystem`
//gets to them.
Expand Down