Portals in STAGE

by Damien

Along the way, I'll describe agent, observer, container, and a bit about events.

First, some background. (I'm going to call the player's in-game character an "avatar".)

An avatar has a bit of code called an "agent" that connects the network pipe into the server core. The agent also has the responsibility of remembering the entities that are visible to the player and dispatching appearance/disappearance operations when entities come into view or go out of view. Since the agent knows which entities are visible, it also handles LOOK operations. The agent is an "observer". An observer receives broadcasted events, or events directed to it.

Every entity currently has a "container". Sometimes this container is unused, such as in the case of "an apple". When it is used, however, it shows what other entities an entity contains. For example, Room655 might contain Avatar21, Avatar44, chest66, and door22.
The container can be observed (by an agent), and broadcasted events are directed to these observers.

An avatar is always in a container. Therefore, in the above example, a call to avatar21.parent() would give the EntityId of Room655. An avatar always broadcasts events to its container. Therefore, if my avatar moves, a move event is broadcast to the container. The container then broadcasts this event to any observers.

Again from our example

  1. Avatar21 => move to 2,2,2
  2. the rim that processes movement receives this command, executes it, and broadcasts Sight(Avatar21, moves to 2,2,2) to Room655.
  3. Room655 has two observers (Avatar21 and Avatar44)
  4. The agent for Avatar21 receives the broadcast and directs it through the network to the client.
  5. The agent for Avatar44 receives the broadcast, but dumps it because the source of the sight event happens to be out of range.

The container also has functionality to return a set of entities that are in view. Currently this is accomplished using a hard-coded range box, but this will be much more flexible in the future.
room655.container->findAllInView(range) would give {Avatar44, Avatar22, door22, Room655}, assuming the chest is outside of the range box.

Now, to portals.

In the examples above, all of the events have been confined to a single container. If door22 is open (which leads to Room323), events still remain confined to the container.

A Portal allows messages and events to pass between containers in STAGE. A portal does not allow an entity to move between containers; only sights, sounds, etc. The most general portal is one-way, and passes all events.

The changes required to add portals are quite trivial. Broadcasts are now sent to any portal that allows events to escape the container (a FromPortal). findAllInView will traverse to any container that allows events to escape that container into this one. This would be a ToPortal in this container. Effectively, the two cases are "pushing" an event through the portal, and "pulling" the event through the portal.

When a portal is added, it is necessary to fire appearance events through to the observers. Similarly, when a portal is removed, disappearance events must be fired.

A two-way portal is constructed by combining two portals:
portal1 A=>B
portal2 B

Now, when door22 is opened, it adds the appropriate portals to the rooms it adjoins, and when Avatar21 moves, these movement events are broadcast to Room655, and also through the portal to Room323. Observers in both rooms will receive the event.

All of the above is implemented and working in STAGE.

Portals can be specialized to prevent certain events from passing through them. A thin, closed door might allow sound to pass, but not sight. A double glazed window might allow sight, but not sound.


Currently a container/portal does not filter events. A movement in the kitchen goes through the door to the living room, then out the door to the street, then in the door to the library, then down the stairs to the dungeon, ..... then through the rift to the demon plane..., etc. The agent that eventually receives the event will probably discard it as being out of range, but is a bit silly to propagate the event this far.

One solution is to put a maximum range on an event, but this could be too limiting. Another solution would be to give the portal a physical presence that resides in the octtree, and could then be included in searches/intersection, etc... But thats a whole 'nother story...