Skip to content

Instantly share code, notes, and snippets.

@zah
Last active December 16, 2015 23:58
Show Gist options
  • Save zah/5517076 to your computer and use it in GitHub Desktop.
Save zah/5517076 to your computer and use it in GitHub Desktop.
Registering messages
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 =
...
```
@zah
Copy link
Author

zah commented May 4, 2013

# The uglier syntax with do was to provide type-safety (only properly typed
# implementation are possible). I guess that's also achievable in some other
# way if the syntax is like this:
{.messageImplementation.}
proc foo(c: TComponent, x: string, y: int): string =
   ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment