Ember Entity Creator

From WorldForgeWiki
Jump to: navigation, search

This page contains development documentation on successfully finished GSoC-2008 project Entity Creator. It was coded in a branch in separate git repository. Now, it is fully merged in main repo.

Check the screencast which shows Entity Creator in work.

Process of creating entity:

  1. Parser is parsing specifications using standard Atlas methods.
  2. Lua script for each placeholder is called with values from GUI adapters. Result is transformed into XML structure and replace placeholder.
  3. Data is transferred to server to create entity.

Entity recipe

All information that entity creator uses to create entity is gathered together in one entity recipe file (*.entityrecipe). It has following structure:

<entityrecipes>
  <entityrecipe name="test">
    <author>... Author name ...</author>
    <description>
      ... Brief recipe description ...
    </description>
    <entity>
      ... Entity Specification in semi Atlas-XML format ...
    </entity>
    <adapters>
      ... Description of GUI adapters ...
    </adapters>
    <bindings>
      ... Bindings between scripts and GUI adapters ...
    </bindings>
    <script><![CDATA[
      ... Lua script ...
    ]]></script>
  </entityrecipe>
</entityrecipes>

Semi Atlas-XML entity specification

It is the semi atlas-xml structure that have placeholders like $val. DTD is described in documentation. Also, Atlas has additional restrictions on elements content. That is, for example, in <int> element it is possible to store only integer values and so on. In our case, we want to store placeholders in form $val so it cannot be valid Atlas structure and because of this Atlas-XML parser cannot be used.

Attribute type specifies Eris type of entity.

Placeholder should be the only child in element. That is, it should be like <float name="foo">$val</float>. This element will be replaced with resulting value from GUI adapter. Attribute name is maps will be preserved.

<entity type="foobar">
  <atlas>
    <map>
      <int name="foo">13</int>
      <float name="meep">1.5</float>
      <string name="bar">$val</string>
      <list name="args">
        <int>1</int>
        <int>2</int>
        <float>3.0</float>
      </list>
    </map>
  </atlas>
</entity>

GUI adapters description

This part of recipe describe properties of GUI adapters. Attribute type contains type ID of an adapter. Following types are supported:

  • string — just a string, allows list of suggestion within child <item> elements but value is not limited to them,
  • number,
  • size,
  • position — position in 3D space,
  • position2d — position in 2D space.
<adapters>
  <adapter name="value_of_a" type="string">
    <item value="1">A is big</item>
    <item value="2">A is small</item>
  </adapter>
  <adapter name="value_of_b" type="number" />
  <adapter name="value_of_c" type="position3d" />
</adapters>

GUI adapters bindings

This part of recipe describe bindings of GUI adapters to correspondent placeholders. Attribute func contains a name of Lua function that should be called. If this attribute is missing than it will bind an adapter directly to the placeholder, i. e. it will copy adapter value without passing it through function.

<bindings>
  <bind name="foo">
    <adapter name="value_of_a" />
  </bind>
  <bind name="meep" func="calc_meep">
    <adapter name="value_of_b" />
    <adapter name="value_of_c" />
  </bind>
  <bind name="bar" func="calc_bar">
    <adapter name="value_of_a" />
    <adapter name="value_of_c" />
  </bind>
</bindings>

Lua scripts

(this info should be added to pages describing Lua bindings in Ember when those would be written)

Reading params

Lua functions used in entity recipes receive bound adapters values as Atlas.Message.Element objects in order they are listed in bindings section of recipe. Current Atlas bindings support following types of Elements:

  • string. Standard functions param:isString() and param:asString().
  • float and integer. Lua doesn't distinct between floats and integers, so, for better interoperability better to use general “number” functions param:isNum() and param:asNum().
  • list. This is the complex type. Standard functions param:isList() and param:asList() is available to check is it a list and convert it to Atlas.Message.ListType. To read it, Lua-style iterator over Atlas.Message.ListType is implemented:
for k,v in param:asList():pairs() do
  -- k is integer index
  -- v is Atlas.Message.Element on that index
end
  • map operations is not implemented yet. Though it should be almost the same as EmberOgre.GUIAdaptersStore.

Returning results

Lua functions used in entity recipes should return objects of type Atlas.Message.Element. Current Atlas bindings support creation of following types of Elements:

  • string. It is created simply: Atlas.Message.Element("abc").
  • float. Also simple: Atlas.Message.Element(123). Note that Lua doesn’t distinct between integers and floars, so even if passed number is integer resulting element type would be float.
  • list. This is the complex type. Script need to create Atlas.Message.ListType first and fill it with push_back operation that allows to add elements into list. It allows to add Atlas.Message.Elements to the end of list. Also, it is overloaded to instant addition of strings or floats without creating intermediate object:
local list = Atlas.Message.ListType()
list:push_back(Atlas.Message.Element(123))
list:push_back("abc")
list:push_back(123)

When script thinks that its list is fully formed, it needs to create Atlas.Message.Element from it and return it:

return Atlas.Message.Element(list)
  • map operations is not implemented yet. Though it should be almost the same as EmberOgre.GUIAdaptersStore.

Additional library

Along with all standard Lua library, following functions are available in entity recipe scripts:

  • math.randelem — returns random element from table
  • math.randn(mean, stddev) — returns normally distributed random number with correspondent mean and std.dev.

Example

Complete example of real entity recipe file.

<entityrecipes>
  <entityrecipe name="boulder">
    <author>Alexey Torkhov</author>
    <description>
      This is boulder recipe
    </description>
    <entity type="boulder">
      <atlas>
        <map>
          <list name="bbox">$bbox</list>
          <float name="mass">$mass</float>
          <string name="style">$style</string>
        </map>
      </atlas>
    </entity>
    <adapters>
      <adapter name="mass" type="number" title="Mass"/>
      <adapter name="massdev" type="number" title="Mass std. dev."/>
      <adapter name="style" type="string" title="Style" allowrandom="yes">
        <item value="a">Boulder A</item>
        <item value="b">Boulder B</item>
        <item value="c">Boulder C</item>
        <item value="d">Boulder D</item>
      </adapter>
    </adapters>
    <bindings>
      <bind name="bbox" func="boulder.calcSize">
        <adapter name="mass" />
        <adapter name="massdev" />
      </bind>
      <bind name="mass">
        <adapter name="mass" />
      </bind>
      <bind name="style">
        <adapter name="style" />
      </bind>
    </bindings>
    <script><![CDATA[
      boulder = {}
      function boulder.calcSize(a, b)
        local density = 2500  * 0.52 -- our boulder is not cubic, 0.52 is rough volume of its size 1

        local massmean = a:asNum()
        local massdev = b:asNum()
        local mass = math.abs(math.randn(massmean, massdev))
        local volume = mass / density
        local len = volume^(1/3)

        local bbox = Atlas.Message.ListType()
        bbox:push_back(-len/2)
        bbox:push_back(-len/2)
        bbox:push_back(0)
        bbox:push_back(len/2)
        bbox:push_back(len/2)
        bbox:push_back(len)
        return Atlas.Message.Element(bbox)
      end
    ]]></script>
  </entityrecipe>
</entityrecipes>