Creating modules for the Unnamed 3D Engine

By Adam Chlipala


The engine is designed to be extremely modular. Everything in the way of user extension is performed by compiled, cross-platform virtual machine bytecode modules. There are no flat file formats involved from the start; any that you want to use are read by VM modules. The "world" is basically just a collection of entities with various attributes. When the server starts, it runs the module server.xc, which is responsible for initializing all settings, including adding all entities to be present at the start, however it sees fit. Similarly, the client uses client.xc. Various events occur throughout execution and they are sent to one of these modules or to an entity's module, all of which have their main function declared as:

void main(int Event, int Arg1, int Arg2, int Arg3)

or a compatible declaration. A unique integer code for the event is passed first, along with up to 3 event-specific integer arguments, is passed to the program to be used like argc/argv are for conventional C programs. The module is called anew for each event and is not kept running. Additional string arguments are used by some events and can be retrieved with GetStringArg() or GetStringArg2().


client.xc

All client events stream output to the server if connected.

CLIENT_INIT
Triggered when the client program is first started.

CLIENT_MOVE
Triggered when the client has altered the avatar's position, velocity, or other related property. This event should stream calls to the server to synchronize the changed values.

CLIENT_COMMAND
Triggered when the user types a line into the console text entry blank and presses return. This event should determine if the text contains a /command (the module may actually use another way of determining what is a command than checking for a leading slash if desired) and stream a call to Cmd() if so.

  • StringArg: The string entered.
  • CLIENT_CLICK
    Triggered when the user clicks on an entity. This should stream a call to Click() to the server.

  • Arg1: The ID# of the entity clicked.
  • Arg2: The ID# of the entity component clicked.
  • CLIENT_OLOGON
    Should stream InformAvatar() to let the server know to which other connection this client belongs.


    server.xc

    EVENT_INIT
    Triggered when the server program first starts. Should initialize all world settings.

    EVENT_SHUTDOWN
    Triggered just before closing the server program.

    EVENT_LOGON
    Triggered when a client connects requesting an input stream (server to client). Should call SetUser() if the logon is accepted or Quit() otherwise. Should associate the user with an avatar, possibly after creating a new one.

  • StringArg: Username that the client sent
  • StringArg2: Password that the client sent
  • EVENT_OLOGON
    Triggered when a client connects requesting an output stream (client to server). Should call SetOUser() if the logon is accepted or Quit() otherwise. Should associate the user with an avatar, possibly after creating a new one.

  • StringArg: Username that the client sent
  • StringArg2: Password that the client sent
  • EVENT_LOGOFF
    Triggered when a client's main input stream disconnects.

  • Arg1: ID# of the client's avatar
  • EVENT_REMOTE_INIT
    Triggered streaming output to the client right after a successfull logon. Should initialize all world settings on the client side.

    EVENT_PRE_SYNC, EVENT_POST_SYNC
    Triggered streaming output to the client before and after a period where critical information is being transferred. Generally uses SetFreeze() to freeze physics during this time.

    EVENT_CREATE
    Triggered streaming output to the client to create a locally existent entity on the client side with a call to AddEntity() specifying the ID# and synchronize all appropriate entity properties.

  • Arg1: The ID# of the entity to create
  • EVENT_POST_LOGON
    Triggered streaming output to the client after a succesfull input stream logon. Should set the client's avatar.

    EVENT_WRITE_TEXT
    Should stream a call to WriteText() with the given message.

  • StringArg: The message to stream
  • EVENT_SYNC
    Should stream updates on the status of the specified entity.

  • Arg1: ID# of the entity whose status to update
  • EVENT_CLOSE
    Triggered to synchronize the removal of an entity by streaming a RemoveEntity() call to each client. It is triggered once streaming to each affected client.

  • Arg1: ID# of the removed entity
  • EVENT_MODEL
    Should stream a call to ModelEvent() with the given arguments.

  • Arg1: ID# of the entity
  • Arg2: Event to trigger
  • Arg3: Value for event's Arg2
  • EVENT_QUIT
    Should stream a call to Quit() with the appropriate message.

  • Arg1: ID# of the active user
  • StringArg1: Argument that was specified in the previous non-streamed call to Quit()


    Render modules

    All render events are run without streaming. Arg1 is always the ID# of the entity for whom the event is being called. EVENT_INIT and EVENT_CLOSE are called when the entity is created and removed, respectively. On the server side, EVENT_SYNC is called, streaming output to the client, when changeable entity state information should be set. All other events will be generated as results of the use of callback functions.


    Control modules

    All control events are run without streaming. Arg1 is always the ID# of the entity for whom the event is being called. EVENT_INIT and EVENT_CLOSE are called when the entity is created and removed, respectively. EVENT_SYNC is called, streaming output to the client, when changeable entity state information should be set. All other events will be generated as results of the use of callback functions.


    Host functions

    The following sections describe the host functions available to modules, organized by use. A vector is an array of 3 GLfloats and a matrix is a 4x4 array of GLfloats. The standard ANSI 9899-1990 libc functions are also available, though a handful of them knowingly malfunction. In particular, don't expect *printf/*scanf functions to work with floating point values.


    Event information

    GetStringArg
    __host__ GetStringArg(char *str);
    Copies the primary string argument for the current event to str, or clears str if no such argument applies.

    GetStringArg2
    __host__ GetStringArg2(char *str);
    Copies the secondary string argument for the current event to str, or clears str if no such argument applies.

    GetArgs
    __host__ GetArgs(__string__ char *str);
    Stores the argument string associated with the active entity in str.

    SetArgs
    __host__ SetArgs(__string__ char *str);
    Sets str as the active entity's argument string.


    Entity management

    AddEntity
    __host__ __output__ AddEntity(__string__ char *render_mod, __string__ char *control_mod, __string__ char *arg, int id);
    This function creates a new entity and returns the entity ID# on success or 0 on failure. render_mod and control_mod give the relative paths to the .xc-file modules to control the entity (server side only) or draw the entity and handle other client-side things, respectively. The arg can be used to pass additional information that these modules can retrieve with
    GetArg(). id should be -1 when creating a new entity, and it should be the ID# of an entity when this call is being streamed from the server to the client and the ID# must be the same in all places.

    RemoveEntity
    __host__ __output__ void RemoveEntity(void);
    This function removes the currently
    selected entity.

    OpenEnt
    __host__ __output__ OpenEnt(int id, int mode);
    This function attempts to open an entity in the specified mode. It returns 0 on success and nonzero on failure. Mode can be ACC_PUBLIC (reading "visible" properties), ACC_READ (reading all properties), or ACC_WRITE (complete access). At a given time, an entity can be opened in a read mode by any number of module instances, or it can be opened for write by one. If the module has enough access to open the entity but another module has opened it in a mode that precludes access, OpenEnt() blocks until the entity is closed by the other instance(s). Some server-level actions, i.e. those meant to be performed by "admin"-owned entities, require that you open and
    select entity #0, "the Server," for write.

    CloseEnt
    __host__ __output__ void CloseEnt(int id);
    This function closes an entity opened with
    OpenEnt().

    SelectEnt
    __host__ __output__ SelectEnt(int id);
    This function sets the active entity affected by many of the other host functions. It returns 0 if you have the entity with the given id open or nonzero if you have not opened the entity or another error occurs.

    The following functions are available, declared without __output__ so that they may be run locally even in streamed execution. They function identically to their similarly named counterparts.

    __host__ LocalOpenEnt(int, int);
    __host__ LocalCloseEnt(int);
    __host__ LocalSelectEnt(int);
    __host__ LocalSelectComponent(int);


    Entity properties

    GetEntID
    __host__ GetEntID(void);
    Returns the ID# of the selected entity, or -1 if no entity is selected.

    GetRenderMod
    __host__ void GetRenderMod(__string__ char *str);
    Stores the relative path of the render mod for the current entity in str, or clears str if no entity is selected or the current entity has no render mod.

    GetMod
    __host__ void GetMod(__string__ char *str);
    Stores the relative path of the control mod for the current entity in str, or clears str if no entity is selected or the current entity has no control mod, which is always true on the client side.

    GetName
    __host__ void GetName(__string__ char *str);
    Stores the name of the current entity in str, or clears str if no entity is selected or the current entity has no name.

    SetName
    __host__ __output__ SetName(__string__ char *str);
    Sets the name of the selected entity.

    SelectComponent
    __host__ __output__ SelectComponent(int id);
    Selects the active component of the current entity, returning 0 if the id is valid or nonzero otherwise. Entities are made up of hierarchies of objects with both polygonal definitions for physics and collision detection and, on the client side only, lists of OpenGL commands to execute to draw the object. The polygonal data is hopefully much more simplistic than the drawn object and optimized for fast collision testing. The ID used by this function is initially returned by
    ColBegin() or ColNode(). Pass 0 for id to select the entity's base object.

    SetTrans
    __host__ __output__ void SetTrans(__sync__ vector pos);
    Sets the relative translation for the active component and entity.

    GetTrans
    __host__ void GetTrans(vector pos);
    Stores the relative translation for the active component and entity in pos.

    SetVel
    __host__ __output__ void SetVel(__sync__ vector vel);
    Sets the velocity of the active component and entity.

    GetVel
    __host__ void GetVel(vector vel);
    Stores the velocity of the active component and entity in vel.

    AllocCmem
    __host__ AllocCmem(int size);
    Sets the amount of persistent memory space to be used by the active entity's control module for data to be stored between events to size bytes. The bytes in the allocated area are indexed 0 to size-1. No Cmem is allocated by default. If the size of a Cmem area is increased, the old data is guaranteed to remain intact. Returns 0 on success, nonzero otherwise.

    GetCmem
    __host__ GetCmem(void *ptr, int loc, int len);
    Copies len bytes from the allocated Cmem area starting at index loc to ptr. Returns 0 on success, nonzero otherwise.

    SetCmem
    __host__ SetCmem(void*, int, int);
    Copies len bytes to the allocated Cmem area starting at index loc from ptr. Returns 0 on success, nonzero otherwise.

    AllocRmem
    __host__ AllocRmem(int size);
    Sets the amount of persistent memory space to be used by the active entity's render module for data to be stored between events to size bytes. The bytes in the allocated area are indexed 0 to size-1. No Rmem is allocated by default. If the size of a Rmem area is increased, the old data is guaranteed to remain intact. Returns 0 on success, nonzero otherwise.

    GetRmem
    __host__ GetRmem(void *ptr, int loc, int len);
    Copies len bytes from the allocated Rmem area starting at index loc to ptr. Returns 0 on success, nonzero otherwise.

    SetRmem
    __host__ SetRmem(void*, int, int);
    Copies len bytes to the allocated Rmem area starting at index loc from ptr. Returns 0 on success, nonzero otherwise.

    GetMass
    __host__ float GetMass(void);
    Returns the mass of the active entity. The mass is used in various physics calculations for non-stationary entities.

    SetMass
    __host__ __output__ void SetMass(float mass);
    Sets the mass of the active entity.

    GetElasticity
    __host__ float GetElasticity(void);
    Returns the elasticity of the active entity. When two non-stationary entities collide, the post-collision velocities are multiplied by the sum of their two elasticities. Thus, elasticties should generally be between 0 and 1, with higher values for "bouncier" entities.

    SetElasticity
    __host__ __output__ void SetElasticity(float elast);
    Sets the elasticity of the active entity.

    GetWalk
    __host__ float GetWalk(void);
    Gets the walk speed for the active entity. Entities with nonzero walk speeds try to move along their forward-facing vector multiplied by the walk value. This feature may be replaced by functionality that lets modules implement their own walking.

    SetWalk
    __host__ __output__ void SetWalk(float walk);
    Sets the walk speed for the active entity.

    GetRot
    __host__ GetRot(matrix m);
    Stores the rotation matrix of the active component and entity in m.

    SetRot
    __host__ __output__ void SetRot(__sync__ matrix m);
    Sets the rotation matrix of the active component and entity.

    GetRotVel
    __host__ GetRotVel(matrix m);
    Stores the rotational velocity matrix of the active component and entity in m.

    SetRotVel
    __host__ __output__ void SetRotVel(__sync__ matrix m);
    Sets the rotational velocity matrix of the active component and entity.

    GetRV
    __host__ GetRV(vector rv);
    Sets the array of degree rotational velocity values about each of the active entity's three axes: to its left, upward, and forward, respectively. When an entity rotates because of an RV change with a nonzero walk speed, its movement direction is altered accordingly.

    SetRV
    __host__ __output__ void SetRV(__sync__ vector rv);
    Gets the active entity's RV array.

    GetActive
    __host__ GetActive(void);
    Returns the Active flag of the active entity. Entities do not appear in the world until both their Active and RenderActive flags are set.

    SetActive
    __host__ __output__ void SetActive(int val); Sets the active entity's Active flag if val is nonzero and unsets it otherwise.

    GetRenderActive
    __host__ GetRenderActive(void);
    Returns the RenderActive flag of the active entity.

    SetRenderActive
    __host__ __output__ void SetRenderActive(int val); Sets the active entity's RenderActive flag if val is nonzero and unsets it otherwise.

    GetStationary
    __host__ GetRenderActive(void);
    Returns the Stationary flag of the active entity. Forces do not affect entities with nonzero Stationary values, and collisions with them are handled differently to allow objects to slide along them, such as down inclines.

    SetStationary
    __host__ __output__ void SetStationary(int val); Sets the active entity's Stationary flag. The value should be 0, for normal entities; STAT_MOVE, for entities that move but are unaffected by gravity and other forces; STAT_ABSORB, for entities that absorb momentum headed into them and allow other entities to slide along them; or STAT_DEFLECT, for entities that reflect others like billiar balls hitting the side of a table.

    GetGlobal
    __host__ GetGlobal(void);
    Gets the active entity's Global flag. All clients receive updates on the statuses of Global entities, as opposed to how they usually only receive information on entities located in their and adjacent VZones, rectangular blocks of space of configurable sizes.

    SetGlobal
    __host__ __output__ void SetGlobal(int val);
    Sets the active entity's Global flag.

    GetOwner
    __host__ GetOwner(void);
    Returns the user ID# of the active entity's owner.

    SetOwner __host__ __output__ SetOwner(int uid);
    Sets the owner of the active entity to be the user with the given ID#. Returns 0 on success and nonzero on failure.

    CalcZone
    __host__ CalcZone(void);
    Recalculates in which VZone the active entity is.

    RequestUpdate
    __host__ __output__ void RequestUpdate(void);
    Requests that the server send updates on the active entity's status to clients that can "see" it.


    Entity component creation

    ColNode
    __host__ ColNode(void);
    Creates a component with the active component as its parent. The new component will not have any polygon data associated with it and exists just to group child objects. The new component's ID is returned, or 0 on failure.

    ColBegin
    __host__ ColBegin(void);
    Marks the beginning of specification of polygon data for a new component with the active component as its parent. The new component's ID is returned, or 0 on failure.

    ColTri
    __host__ ColTri(GLfloat x1, GLfloat y1, GLfloat z1, GLfloat x2, GLfloat y2, GLfloat z2, GLfloat x3, GLfloat y3, GLfloat z3);
    Adds a triangle with vertices at points 1, 2, and 3 to the component definition currently being given. Returns 0 on success and nonzero on failure.

    ColEnd
    __host__ ColEnd(void);
    Ends the definition of a component. Returns 0 on success and nonzero on failure.


    OpenGL rendering specification

    The following standard OpenGL functions are available. The appropriate constants are all defined when 3d.h is included. Calling these functions adds the specified OpenGL call to the end of the list of calls used to draw the active component.

    __host__ __output__ void glBegin(GLenum);
    __host__ __output__ void glEnd(void);
    __host__ __output__ void glVertex3f(GLfloat, GLfloat, GLfloat);
    __host__ __output__ void glColor3f(GLfloat, GLfloat, GLfloat);
    __host__ __output__ void glEnable(GLenum);
    __host__ __output__ void glDisable(Glenum);
    __host__ __output__ void glTexCoord2f(GLfloat, GLfloat);
    __host__ __output__ void glNormal3f(GLfloat, GLfloat, GLfloat);
    __host__ __output__ void glMaterialfv(GLenum, GLenum, GLfloat*);
    __host__ __output__ void glMaterialf(GLenum, GLenum, GLfloat);

    glClear
    __host__ __output__ void glClear(int);
    This function has the same syntax as the standard OpenGL function, but the argument is ignored and the list of rendering calls for the active component is cleared.

    SetTexture
    __host__ __output__ void SetTexture(__string__ char *path);
    This function adds a texture change to the list of rendering calls for the active component. If the texture file (JPEG only in the current version and with the usual restriction that each of its dimensions must be a multiple of 2) exists at the given relative path, then it is loaded and applied. If not, it is added to the download queue and appears on download completion.


    World manipulation

    ShutDown
    __host__ ShutDown(void);
    Shuts down the server. Returns nonzero if the calling module instance does not have object #0 selected for write.

    AddGravity
    __host__ __output__ AddGravity(float x, float y, float z);
    Adds a universal force to the world that accelerates each non-stationary entity with the given vector components on each tick. Entity #0, the "Server," must be selected for write to use this function. Returns 0 on success and nonzero on failure.

    GetFreeze
    __host__ GetFreeze(void);
    Returns the boolean freeze status of the world. When true, no entities move. This is primarily present for debugging purposes.

    SetFreeze
    __host__ __output__ void SetFreeze(int val);
    Sets the freeze status of the world.

    AllocSmem
    __host__ AllocSmem(int size);
    Change the amount of memory available primarily for use by server.xc or client.xc to store between-event data. Initially, no memory is allocated for this purpose. The allocated memory is bytes indexed from 0 to size-1. Returns 0 on success and nonzero on failure.

    GetSmem
    __host__ GetSmem(void *ptr, int loc, int len);
    Copies len bytes from Smem starting at index loc to ptr. Returns 0 on success and nonzero on failure.

    SetSmem
    __host__ SetSmem(void *ptr, int loc, int len);
    Copies len bytes to Smem index loc from ptr. Returns 0 on success and nonzero on failure.

    SetWorldInfo
    __host__ __output__ void SetWorldInfo(float x, float y, float z, float vzone_size);
    Sets the x, y, and z dimensions of the world to the specified values. Coordinates range from -x/2 to x/2, -y/2 to y/2, and -z/2 to z/2. Also sets the size of a VZone to a cube of side vzone_size.

    GetWorldX, GetWorldY, GetWorldZ, GetVZoneSize
    __host__ float GetWorldX(void);
    __host__ float GetWorldY(void);
    __host__ float GetWorldZ(void);
    __host__ float GetVZoneSize(void);
    These functions retrieve the settings passed to SetWorldInfo.


    Commands

    BindCmd
    __host__ BindCmd(__string__ char *cmd, int event);
    Binds the command cmd to the active entity. When a client types a /command into his console, the server will check all entities in the client's avatar's and adjacent zones, as well as Global entities, for all that have that command bound, then trigger event in each entity's control module with the command line following the command stored in StringArg. Arg2 contains the ID# of the user's avatar and Arg3 the user's ID#.

    Cmd
    __host__ __input__ void Cmd(__string__ char *cmd, __string__ char *args);
    Streamed to the server by a client to indicate that the given cmd has been entered with the given args.

    SimCmd
    __host__ SimCmd(__string__ char *cmd, __string__ char *args);
    Simulates the effect of the active entity's being user #0's avatar and typing the given command and arguments.

    BindClick
    __host__ BindClick(int event);
    Binds a click event to the active entity. When a client clicks on it, the specified event is triggered in the entity's control module, with the user ID of the clicker as Arg2 and the ID# of the clicked component in Arg3.

    Click
    __host__ __input__ void Click(int entity_id, int component_id);
    Streamed to the server by a client to indicate a click on the given entity's component.


    Text output

    LogText
    __host__ LogText(char *msg);
    Writes msg to the local log.

    WriteText
    __host__ __output__ void WriteText(__string__ char *msg);
    Writes msg to either the local or remote log, depending on if output is streamed.

    WriteMsg
    __host__ void WriteMsg(__string__ char *msg); Writes msg to the log of the client controlling the active entity.

    ZoneText
    __host__ ZoneText(__string__ char *msg); Writes msg to the logs of all clients that can "see" the active entity, which is all clients for Global entities.


    3D math

    MultVectorMatrix
    __host__ MultVectorMatrix(vector u, vector v, matrix m);
    u = v * m

    MultMatrix
    __host__ MultMatrix(matrix m, matrix n, matrix o);
    m = n * o

    RotationMatrix
    __host__ RotationMatrix(matrix m, float x, float y, float z, float a);
    Stores a rotation matrix of a degrees about the axis (x, y, z) in m.


    Client/server setup

    Connect
    __host__ Connect(char *host, char *uname, char *pass);
    Client only. Connect to the specified host with the given username and password.

    TimeRef
    __host__ __output__ void TimeRef(time_t tr);
    Streamed client to server to tell the server what the value of time() is on client side to decide when the client needs to redownload updated files.

    Quit
    __host__ __output__ void Quit(__string__ char *msg);
    On server side, triggers the streaming of a Quit call to the active client. On the client side, disconnects, displaying msg.

    GetAvatar
    __host__ GetAvatar(void);
    On client side, returns the ID# of the current avatar, or 0 if none is set. On server side, returns the ID# of the active client's avatar, or 0 if none is set.

    SetAvatar, SetOAvatar
    __host__ __output__ void SetAvatar(int id);
    __host__ __output__ void SetOAvatar(int id);

    On client side, sets the avatar to the entity with the given ID#. On server side, sets the avatar of the active client. SetOAvatar() should be used with additional connections belonging to the same client when previous connections have already been assigned to the same avatar.

    InformAvatar
    __host__ __output__ void InformAvatar(int id);
    Streamed client to server to let the server know what avatar the user has been previously assigned when making additional connections beyond the first.
    Sync
    __host__ __output__ __input__ Sync(void);
    Creates a pause that continues execution when the machine to which code is being streamed has acknowledged the Sync() request.

    ToggleViewMode
    __host__ ToggleViewMode(void);
    Client only. Change between full screen and windowed display modes.


    User management

    SetUser, SetOUser
    __host__ SetUser(int uid);
    __host__ SetOUser(int);

    Sets the user ID# associated with the active connection. Use SetOUser() for additional connections belonging to the same client being assigned to a user.

    The rest of the functions in this section are server only.

    AddUser
    __host__ AddUser(char *name, char *pass, int acc);
    Adds a user with the given name, password, and access level. Returns the user ID on success or -1 on failure.

    GetUserID
    __host__ GetUserID(char *name);
    Returns the ID of the user with the given name or -1 if not found.

    DelUser __host__ DelUser(int uid);
    Deletes the specified user. Returns 0 on success and nonzero on failure.

    GetUname
    __host__ GetUname(int uid, char *str);
    Stores the specified user's name in str.

    SetUname
    __host__ SetUname(int uid, char *str);
    Sets the specified user's name to str.

    GetPass
    __host__ GetPass(int uid, char *str);
    Stores the specified user's password in str.

    SetPass
    __host__ SetPass(int uid, char *str);
    Sets the specified user's password to str.

    GetAcc
    __host__ GetAcc(int uid);
    Returns the specified user's access level.

    SetAcc
    __host__ SetAcc(int uid, int acc);
    Sets the specified user's access level to acc.

    GetUserAvatar
    __host__ GetUserAvatar(int uid);
    Returns the specified user's avatar's ID# or 0 if the user does not exist or has no avatar.

    NextUser
    __host__ NextUser(int uid);
    Returns the lowest user ID# greater than the valid ID# uid that is in use, or -1 if none is found. Pass -1 to NextUser() to get the lowest used ID#.


    Module callbacks

    RequireFile
    __host__ RequireFile(__string__ char *path, int event, int arg2, int arg3);
    If a file exists at the specified relative path, then the function returns 0. If not, then the function returns nonzero, the file is added to the download queue, and the active entity's render module is triggered with the given event and arguments when that download is complete.

    CallRenderModule
    __host__ CallRenderModule(char *path, int event, int arg2, int arg3);
    Calls the module with the specified relative path with event, arg2, and arg3. Returns 0 on success and nonzero on failure.

    ModelEvent
    __host__ __output__ void ModelEvent(int event, int arg2);
    Call event with arg2 in the active entity's render module.

    Timer
    __host__ Timer(int tm, int event, int arg2);
    Sets the active entity's control module to be triggered with event and arg2 after tm ticks.

    RenderTimer
    __host__ RenderTimer(int tm, int event, int arg2);
    Sets the active entity's render module to be triggered with event and arg2 after tm ticks.


    Copyright Adam Chlipala 2000