Last active
December 16, 2015 23:58
-
-
Save zah/5517076 to your computer and use it in GitHub Desktop.
Registering messages
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
there are 3 types of messages | |
1) statically dispatched to a certain component: | |
if has(Component): get(Component).doMessage(args) | |
2) dynamically dispatched to a single component | |
let (offset, proc_ptr) = entity->typeinfo->vtable[messageID] | |
proc_ptr(entity->data[offset], args) | |
3) dynamically dispatched to every component that implements them (multicast messages) | |
let runlist = entity->typeinfo->multicast_vtable[messageID] | |
for msg in runlist: msg.proc(entity.data[msg.offset], args) | |
now, the next question is how to populate these vtables | |
this is a bit more complicated to explain, but it's done in 2 steps. | |
First in addComponent we create a VTableFiller function for the component | |
(this function walks over all the dynamically dispatched messages the component implements) | |
in C++, I obtained the list with a special macro looking like this | |
REGISTER_COMPONENT(Foo, MSG(Bar) MSG(Baz)) | |
this compiles into a function that does something like this | |
void VTableFiller(VTableBuilder& builder) { builder.addImplementation(MessageID(Bar), &Foo::Bar); … } | |
the CreateTypeInfo function sets up a VTable builder and executes the VTableFiller for each | |
component type in the current TypeInfo - the end result is a properly populated vtable for | |
this combination of components | |
in Nimrod I would use a set of helper templates/macros for registering messages in the system: | |
``` Nimrod | |
proc messageID(messageName: expr[string]): int = | |
# expr[string] means compile-time string constant | |
# it will be instantiated for each string just like | |
# componentId was instantiated for each type | |
var id {.global.} = addMessage(messageName) | |
result = id | |
{.multicast.} | |
proc foo(x: string, y: int): string | |
# this expands to function that implements the dispatch | |
proc foo(e: Entity, x: string, y: int): string = | |
let runlist = entity.typeinfo.multicast_vtable[messageID("foo")] | |
for msg in runlist: msg.proc_ptr(entity.data[msg.offset], args) | |
# also to a template that allow you to provide implementation | |
# for a given component type | |
template foo(body: proc (c: TComponent, x: string, y: int): string) = | |
getComponentInfo(TComponent).addMessage(messageID("foo"), body) | |
# it's used like this | |
foo do (x: THealth, x: ...): string = | |
... | |
``` |
Author
zah
commented
May 4, 2013
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment