Entity Actions memo.

11 views
Skip to first unread message

Jukka Jylänki

unread,
Sep 5, 2010, 7:16:20 AM9/5/10
to realxt...@googlegroups.com
This is a memo of a developer meeting held last Friday, discussing the
concept of Entity Actions to allow more complicated in-world logic be
built in slightly more data-driven fashion.

The idea is to augment the Entity (and Component) classes with the concept
of Actions. An Action is identified with a string, like "Open", or "Talk",
or "ToggleVisibility". The names are handled as case-insensitive. An
action can have any number of parameters, which are also strings.
"Move(up), WalkForward(10), StartGame(level2.lvl, 4 [players] )".

The reason why we are using strings is to keep the interface as simple as
possible. More complicated data passing can be achieved by first filling
in a series of component data and then calling an action that triggers the
event.

Below, I will use the lowercase word "action", as is usual with entities,
and components, even though they refer to the reserved names of the Naali
system. The capital case is used for emphasis.

It sounds more natural to have explicit actions that can be performed on
entities, instead of trying to achieve the same by listening on
edge-triggers on EC data changes. I.e. instead of triggering the locking
of a door by listening in a script when a component's "locked" transitions
from false to true, we can do an explicit "Lock" action, which then
locally sets "locked=true" and e.g. plays an audio clip.

As a lesser note, this also avoids us having to transmit over the wire
lots of data in several situations. E.g. we want to clear a component to
its default values. Instead of synching all ECs individually, we can
execute an action "Reset" on the entity, and locally we know which values
to fill in.

Entities have always been designed to be simple objects. The Entity class
is not subclassable. The Entity class itself will not handle any actions.
Instead, the actions are passed on to the components of that entity.

Why add the concept of Action to the Entity class then, and not to the
individual Component classes? Because this way we do not have to know
about the actual components the entity has, allowing a sorts of "hidden
layer of abstraction". I.e. We can trigger an action "Clicked" on an
Entity from the input system, without requiring the input system to know
which component is to handle the action. Also, it allows a layer of
consistency. We can make sets of Components that behave similarly. E.g.
components Mesh, ParticleSystem, Billboard, Avatar, Light all have in
common that have the notion of actions "Hide", "Show", "ToggleVisibility"
(or perhaps "Visible(false), Visible(true), Visible(toggle)" ?). A piece
of code that needed to know about the components would have to do a chain
of if..elses to see which component was there and which had to be hidden.
(Though, this particular problem can also be solved if you take a
dependency on Ogre, since each visible object on the scene can be
shown/hidden via the SceneNode independent of what's contained in the
node).

Add the following member functions to Entity:
Entity.Exec(string action);
Entity.Exec(string action, string param1);
Entity.Exec(string action, string param1, string param2);
Entity.Exec(string action, string param1, string param2, string param3);
Entity.Exec(string action, vector<string> params);

the overloads are to avoid inconveniency with needing to fill a vector
beforehand. Using C++ ... (ellipsis) is undesirable here.

The Entity will get a signal table of actions. A component can register a
listener to an action of an entity by connecting to a slot of its own.
Something like

void MyComponent::Init()
{
ConnectAction(parentEntity, "WalkForward", this, SLOT(OnWalkForward));
}

This makes the entity actions dynamic and run-time addable/removable, they
do not need to be static compile-time declarations.

By this mechanism, we can also have a component listening on an action of
another entity. This allows us to build in-world a manager entity on a
pool of slave entities that need to be taken care of. (Whether connecting
an EC to actions of other entities is sane and good design, is unclear at
this point, but having this ability of 1:N management sounds interesting,
so at this point no need to try to artificially limit it)

Also, this allows handling actions in Python and JavaScript, they can just
connect to the signals.

For initial access and debugging, implement the console commands
"Exec(entity, action, param1, ..., paramN)" and "SelectedExec(action,
param1, ..., paramN)" which execute an action on an entity. The latter
passes the given action to all currently selected entities.

Requirement: Need to be able to know whether an action succeeded or not,
or if anything even handled the action we sent. These are discussed
separately below.

For debugging, and for more power in the UI editor, the entity action
signal map can be made accessible from the UI. When an Entity is selected,
we can give out a list or a menu of pushbuttons (with fillable param
fields) that show all the actions the entity has available (on the LOCAL
computer, see note below). This allows us to execute the actions from the
UI and to actually know which actions are available.

The signal map also answers to us if anything is handling the action,
since if we execute a nonexistent action, no signal map entry exists, and
we can print out an error.

Letting the handler of an action signal success or failure, or other
pieces of information, is a more complicated question. Success or failure
can be expressed by using exceptions. Implementing "Action Return Values"
would make the actions real function calls, like "Entity.Exec("GetName")".
This topic sounds somewhat interesting, but is for later study since there
are complications, one example below.

If multiple handlers are connected to an action, which one gets to handle
it? Do we implement a similar kind of suppression mechanism as with Naali
events (return "Suppressed"/"NotHandled";)? In which order do the handlers
get to the action? If we have Action Return Values, do we do a "yield
return"-fashioned aggregation if all handlers return a separate value?

We recommend to leave the execution order undefined, and to pass the
action to all handlers without the ability to suppress.

The most important feature of actions is the following.

When we do a Entity.Exec("WalkForward") or Entity.Exec("UnlockDoor"),
where should the action be executed? There are three possible places that
can be immediately identified:
- Local client
- Server
- Peers
And as combinations we get local+server, local+peers(all clients but not
server), server+peers (everyone but me), local+server+peers (everyone).
Not all of these sound immediately sensible even, but we know we need to
be able to do different things at different times. Hence, we need to add
to the Entity.Exec signature an enum that allows to specify where the
action is executed.

This way, server-side execution can be seen as a kind of RPC method
(without return values currently). Since in the Tundra branch our client
and server maintain the exact same scene data structure, we can also
utilize the same scripts, and changing between Client to Server execution
is fast for prototyping.

The server execution works as follows. In a client-side script or native
C++ code that obtains the handle of an Entity, we can call
entity.Exec("Unlock", Server). This will not do anything immediately on
the client, but will cause the action to be serialized through network and
triggered in the scene on the server side. This way the server script can
check and enforce that the caller is actually permissible to do such
thing, and act properly. The client is not able to Unlock the door with
local execution alone.

In the protocol, implement an "Exec" network message that carries the
entity, action and the corresponding parameters. This way most of the
client<->server interaction scripts do not need to implement their own
network messages at the bottom protocol layer for custom messaging
(although, for more power, having also such an option is useful).

How do we know which actions are available on the server if it differs
from the local computer? An idea might be to have a "ExecQuery" message,
but this is not critical the time being. Perhaps we should prefer to make
symmetric client and server -side action registrations to keep it simple.

Security? Who is allowed to do what? When executing over the network, the
handlers need to be able to know the originator of the action, i.e.
whether it was local or from which peer. The individual security checks
are left to the scripts themselves to decide at this point.

Components that can be implemented to utilize entity actions:

ECMovable. For the first test for these actions, implement this component.
The component listens to "WalkForward(start/stop)",
"WalkBackward(start/stop)", "WalkLeft(start/stop)" and
"WalkRight(start/stop)" messages. When receiving these messages, the
component will move the position of the entity in which it belongs
accordingly. Later on we can implement better systems that do movement
prediction and different kinds of movement controllers e.g. with "Jump",
"Fly", or "Swim" actions. No need to override or disable these actions
anymore to control the existing behavior (think of how the fish avatar
movement was in FishWorld).

This component allows us to create moving objects that do not need to be
avatars. Bots that take actions as execution instructions are also
possible, and they do not need to be implemented as virtual client
connections anymore.

ECInputMapper. This component registers an InputContext from the Naali
Input subsystem. As attributes, it has a list of (key, action) pairs,
which translate the given set of keys as actions on the entity the
component is part of. Using this component we can implement an interface
for directly moving an object even though it is not our avatar. A game
logic script can toggle these input mappers between objects, e.g. to
implement an RTS, we'd have the logic: whenever you select a set of
objects, they get inputmappers, and upon deselecting, they are disabled.

The ECScript component has as an attribute the assetref of a .py or .js
script to add to this entity. When loaded, the script can hook into any
actions on the entity it wants. This gets us closer to storing data-driven
logic on the asset server, a topic Toni discussed in a previous mail
regarding the chess game implementation.

ECAreaTrigger, ECVolumeTrigger, ECProximityTrigger. These kinds of
components listen to entities (satisfying some given requirements) that
enter their area, which can trigger an action on the entity the Trigger
component is part of.

For example, when a player reaches the goal in a race, have an area
trigger for the goal area, and a ECParticleSystem script with fireworks
built in it, and an ECScript that listens on the trigger and starts the
particle system when the player reaches the goal.

ECLogic. This component stores an editable state machine for e.g. AI logic
or something similar. Transitions on this component can be triggered using
actions, and upon transitioning, actions are triggered for other scripts
to listen to.

What kind of other examples for using actions can you think of?

jj

Jonne Nauha

unread,
Sep 5, 2010, 9:20:55 AM9/5/10
to realxt...@googlegroups.com
Sounds very interesting. You are starting this work now or next sprint? I guess our opensim servers cant have this server only actions as we don't currently have the components there other than in xml. I guess with these we can just do local that sends the new xml to server, as its done now. I guess this will of disable the permission checks etc. that server could otherwise perform.

I think for us for building this could be handy to make a EC that provides for example pre defined sets of colors (textures) for example to a sofa set. And you can select those from a ui that the ec provides when clicked. Triggering animations to open a window, permission checks if you can move objects or not etc.

Best regards,
Jonne Nauha
realXtend developer

http://www.realxtend.org/
http://www.evocativi.com/


jukka....@ludocraft.com

unread,
Sep 5, 2010, 12:23:25 PM9/5/10
to realxt...@googlegroups.com
Ali is currently working on this and initially it will be all local
execution. The ECScript part of it is very crucial for flexible logic, so
we are hoping to work with Toni to flesh it out, either this or next
sprint. From what we discussed, I understood embedding Javascript to
objects is already done very much in this way, only as part of the
ECDynamicComponent for now.

Another thing we discussed was to start designing the set of "core API"
objects that both JavaScript and Python scripts will be able to access by
default in their namespace (automatically imported in the context when the
ECScript component is created), and naturally the best thing is to have
the same set of objects and functions accessible, using the same
interface. Toni has already been doing this (in Python you can access
functionality like naali.renderer.raycast), so we do not need to go from
scratch.

jj

> Sounds very interesting. You are starting this work now or next sprint? I
> guess our opensim servers cant have this server only actions as we don't
> currently have the components there other than in xml. I guess with these
> we
> can just do local that sends the new xml to server, as its done now. I
> guess
> this will of disable the permission checks etc. that server could
> otherwise
> perform.
>
> I think for us for building this could be handy to make a EC that provides
> for example pre defined sets of colors (textures) for example to a sofa
> set.
> And you can select those from a ui that the ec provides when clicked.
> Triggering animations to open a window, permission checks if you can move
> objects or not etc.
>
> Best regards,
> Jonne Nauha
> realXtend developer
>
> http://www.realxtend.org/
> http://www.evocativi.com/
>
>

> On Sun, Sep 5, 2010 at 2:16 PM, Jukka Jyl�nki

Ryan McDougall

unread,
Sep 9, 2010, 1:02:18 PM9/9/10
to realxt...@googlegroups.com
It's a shame you never attended scaffold discussions, or raised this
idea earlier.

This is why I shied away from this direction myself. Instead of
placing the "intelligence" at the edges, in modules which can define
actions as appropriate, you must define a super-set of actions and
place them within the component definitions. I was afraid to place
"policy" in the core by defining the actions anywhere but in the
modules.

That is to say, you either have some sort of global knowledge about
what components you expect, or some sort of global knowledge about
what sort of actions to expect.

The way I thought of it, the component name *was* the message (in an
Object Oriented sense), and the properties *were* the parameters to
that message.

That said, it makes sense to have an extra layer of abstraction for
reasons stated, and I basically like the idea.

Cheers,

Reply all
Reply to author
Forward
0 new messages