Skip to content

Commit

Permalink
Allow expressions in @:priority tags.
Browse files Browse the repository at this point in the history
Also, rewrite `AdvancedFunctionalityTest.testPriority()` to no longer rely on a map (`fixedPriorityUpdateListeners`) having a stable order.
  • Loading branch information
player-03 committed Jun 24, 2024
1 parent c70fdc8 commit bb1af72
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 20 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,8 @@ class Main {
}
```

The argument to `@:priority` can be any integer expression. For instance, `@:priority(OtherSystem.DEFAULT_PRIORITY + 1)` will make the listener run before `OtherSystem`. The expression will be evaluated during the constructor, meaning it can access the constructor's `priority` argument.

### Update length

As Glenn Fielder explains in his article ["Fix Your Timestep!"](https://www.gafferongames.com/post/fix_your_timestep/), games and physics simulations can be very sensitive to the length of each update. `@:update` listeners in Echoes are no exception.
Expand Down
32 changes: 19 additions & 13 deletions src/echoes/macro/SystemBuilder.hx
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,16 @@ class SystemBuilder {
return null;
}

private static function getPriority(meta:Metadata):Null<Int> {
private static function getPriority(meta:Metadata, knownPriorities:Map<String, Expr>):String {
final entry:MetadataEntry = getMeta(meta, PRIORITY_META);
switch(entry) {
case null:
case _.params => [_.expr => EConst(CInt(v))]:
return Std.parseInt(v);
case _.params => [expr]:
final key:String = new Printer().printExpr(expr);
if(!knownPriorities.exists(key)) {
knownPriorities[key] = expr;
}
return key;
default:
}
return null;
Expand Down Expand Up @@ -195,9 +199,11 @@ class SystemBuilder {
//Listener function priorities
//============================

final updateListeners:Array<ListenerFunction> = fields.map(ListenerFunction.fromField.bind(_, UPDATE_META)).filter(notNull);
final addListeners:Array<ListenerFunction> = fields.map(ListenerFunction.fromField.bind(_, ADD_META)).filter(notNull);
final removeListeners:Array<ListenerFunction> = fields.map(ListenerFunction.fromField.bind(_, REMOVE_META)).filter(notNull);
final knownPriorities:Map<String, Expr> = new Map();

final updateListeners:Array<ListenerFunction> = fields.map(ListenerFunction.fromField.bind(_, UPDATE_META, knownPriorities)).filter(notNull);
final addListeners:Array<ListenerFunction> = fields.map(ListenerFunction.fromField.bind(_, ADD_META, knownPriorities)).filter(notNull);
final removeListeners:Array<ListenerFunction> = fields.map(ListenerFunction.fromField.bind(_, REMOVE_META, knownPriorities)).filter(notNull);
for(listener in addListeners.concat(removeListeners)) {
if(listener.wrapperFunction == null) {
Context.error("An @:add or @:remove listener must take at least one component. (Optional arguments don't count.)", listener.pos);
Expand All @@ -208,7 +214,7 @@ class SystemBuilder {
* Update listeners that have `@:priority` tags. Each group of these
* will be used to create a `ChildSystem`.
*/
final fixedPriorityUpdateListeners:Map<Int, Array<ListenerFunction>> = new Map();
final fixedPriorityUpdateListeners:Map<String, Array<ListenerFunction>> = new Map();
for(listener in updateListeners) {
if(listener.priority != null) {
if(!fixedPriorityUpdateListeners.exists(listener.priority)) {
Expand All @@ -219,11 +225,11 @@ class SystemBuilder {
}
}

final defaultPriority:Null<Int> = getPriority(classType.meta.get());
final defaultPriority:Null<String> = getPriority(classType.meta.get(), knownPriorities);
if(defaultPriority != null) {
fields.pushFields(macro class DefaultPriority {
private override function __getDefaultPriority__():Int {
return $v{ defaultPriority };
return ${ knownPriorities.get(defaultPriority) };
}
});
}
Expand All @@ -235,7 +241,7 @@ class SystemBuilder {
final body:Array<Expr> = [for(listener in listeners) listener.callDuringUpdate()];
body.unshift(macro __dt__ = dt);

macro __addListenersWithPriority__($v{ priority }, function(dt:Float) $b{ body });
macro __addListenersWithPriority__(${ knownPriorities[priority] }, function(dt:Float) $b{ body });
}];
initializeChildren.push(macro if(parent != null) {
for(child in __children__) {
Expand Down Expand Up @@ -425,7 +431,7 @@ class SystemBuilder {
name:String,
args:Array<FunctionArg>,
pos:Position,
priority:Null<Int>,
priority:Null<String>,
?components:Array<ComplexType>,
?optionalComponents:Array<ComplexType>,
?viewName:String,
Expand All @@ -434,7 +440,7 @@ class SystemBuilder {

@:forward
abstract ListenerFunction(ListenerFunctionData) from ListenerFunctionData {
public static function fromField(field:Field, listenerType:String):ListenerFunction {
public static function fromField(field:Field, listenerType:String, knownPriorities:Map<String, Expr>):ListenerFunction {
switch(field.kind) {
case FFun(func):
if(SystemBuilder.getMeta(field.meta, listenerType) == null) {
Expand All @@ -445,7 +451,7 @@ abstract ListenerFunction(ListenerFunctionData) from ListenerFunctionData {
name: field.name,
args: func.args,
pos: field.pos,
priority: SystemBuilder.getPriority(field.meta)
priority: SystemBuilder.getPriority(field.meta, knownPriorities)
};
default:
return null;
Expand Down
14 changes: 8 additions & 6 deletions test/AdvancedFunctionalityTest.hx
Original file line number Diff line number Diff line change
Expand Up @@ -191,14 +191,16 @@ class AdvancedFunctionalityTest extends Test {
//Next, add a system with children.
final parent:UpdateOrderSystem = new UpdateOrderSystem();
Assert.equals(0, parent.priority);
Assert.equals(1, parent.__children__[0].priority);
Assert.equals(-1, parent.__children__[1].priority);
final positiveChild:System = Lambda.find(parent.__children__, child -> child.priority == 1);
Assert.notNull(positiveChild);
final negativeChild:System = Lambda.find(parent.__children__, child -> child.priority == -1);
Assert.notNull(negativeChild);

list.add(parent);
assertListContents([
high, parent.__children__[0], //1
high, positiveChild, //1
middle, parent, //0
low, parent.__children__[1] //-1
low, negativeChild //-1
]);

final updateOrder:Array<String> = [];
Expand All @@ -214,8 +216,8 @@ class AdvancedFunctionalityTest extends Test {
low.priority = -1;
assertListContents([
parent, //2
high, parent.__children__[0], //1
parent.__children__[1], middle, low //-1
high, positiveChild, //1
negativeChild, middle, low //-1
]);

updateOrder.resize(0);
Expand Down
4 changes: 3 additions & 1 deletion test/Systems.hx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ class TimeCountSystem extends System implements IMethodCounter {
}

class UpdateOrderSystem extends System {
@:update @:priority(-1) private function post_update(order:Array<String>):Void {
private static final CONSTANT:Int = 5;

@:update @:priority(CONSTANT - 6) private function post_update(order:Array<String>):Void {
order.push("post_update");
}

Expand Down

0 comments on commit bb1af72

Please sign in to comment.