NWN Tools - ANT Tasks

Version 0.1

ANT is a build tool by the fine folks at Apache and can be located here. It provides an easy way to specify build targets and tasks for doing general development as well as cutting releases or otherwise managing projects.

Because ANT is my build tool of choice, I implemented several NWN specific custom ANT tasks to make my NWN content life a little easier to deal with. These are:

At the bottom, I include a full example of how I build the ZDialog utilities into several different forms including the sample module.

Installation and Task Definition

To install, just put the included .jar files into the ant/lib directory. All of the jars in the nwn-ant/jars directory should be copied there. ANT will automatically pick these up when it is run.

In order to use any of these tasks within your ANT environment, your build.xml file will need to define them as tasks. Here is an example of these task definitions:

<taskdef name="nwnc" classname="org.progeeks.nwn.ant.CompileTask" />

<taskdef name="xmltogff" classname="org.progeeks.nwn.ant.XmlToGffTask" />

<taskdef name="erfpacker" classname="org.progeeks.nwn.ant.ErfPackerTask" />

If the above definitions are used then the tasks can be refered to as <nwnc>, <xmltogff>, and <erfpacker> respectively. The examples in the following sections assume that these tasks were defined with the names as above. Adjust as necessary if you customize the names.

NWN Compiler

Compiles a set of NWScript source files using the PRC script compiler.

Options

Different options can be specified to control which source files are compiled and where the compiled binaries end up. In addition, the path to the NWN script compiler can be specified.

Attribute Description Required
srcdir Base location of the .nss source files. Included source files will be resolved relative to this path. Yes
destdir Destination for the compiled binaries. Relative source paths will be expanded under this directory such that srcdir/foo/script.nss will end up in destdir/foo/script.ncs. Yes
compiler Specifies the path (including executable name) to the NWN script compiler. Currently this task only supports the PRC compiler since it parses the compiler output to generate appropriate console messages. Plus, the Bioware compiler is severely limited on options.
By default, this task will look for the compiler at "/NeverwinterNights/NWN/tools/nwnnsscomp.exe". If the compiler is not found there then an attempt is made to execute the compiler on the system path.
No

Parameters specified as nested elements

The NWN Compiler task is an implicit FileSet and supports all attributes of <fileset> (dir becomes srcdir).

Examples

  <nwnc srcdir="mysrc" destdir="build" >
    <include name="*.nss" />
  </nwnc>

Compiles all .nss files in the mysrc directory and stores the .ncs file into build using the default compiler location or an nwnnsscomp executable on the system path.

  <nwnc srcdir="mysrc" destdir="build" compiler="C:\Download\PRC\nwnnsscomp.exe" >
    <include name="*.nss" />
  </nwnc>

Compiles all .nss files in the mysrc directory and stores the .ncs file into build using the compiler executable found at: C:\Download\PRC\nwnnsscomp.exe Note: in these cases it would be best to use a property to define the location but that's a topic for another discussion.

  <nwnc srcdir="${src}" destdir="${build}" >
    <include name="*.nss" />
  </nwnc>

Compiles all .nss files in the directory contained in the "src" property and stores the .ncs file into the directory contained in the "build" property.

  <nwnc srcdir="mysrc/modules" destdir="build/modules" >
    <include name="**/*.nss" />
  </nwnc>

Compiles all .nss files in the mysrc/modules directory (including sub-directories) and stores the .ncs files into the build directory (including sub-directories). For example:

mysrc/modules/MyModule/*.nss -> build/modules/MyModule/*.ncs
mysrc/modules/MyOtherModule/*.nss -> build/modules/MyOtherModule/*.ncs
and so on.

XML to GFF

Compiles a set of GFF XML source files into binary GFF files. The XML source files would come from the GffToXml or ModToXml tools. The combination of those utilities and this ant task make it convenient for storing module contents in a source control system.

Options

Different options can be specified to control which XML files are compiled and where the compiled binaries end up.

Attribute Description Required
srcdir Base location of the .xml source files. Included source files will be resolved relative to this path. Yes
destdir Destination for the compiled binary GFF files. Relative source paths will be expanded under this directory such that srcdir/foo/area001.are.xml will end up in destdir/foo/area001.are Yes

Parameters specified as nested elements

The xmltogff task is an implicit FileSet and supports all attributes of <fileset> (dir becomes srcdir).

Examples

  <xmltogff srcdir="${src}/zdialog" destdir="${build}/zdialog" >
      <include name="*.xml" />
  </xmltogff>

Converts all .xml files in the ${src}/zdialog directory into various types of GFF files stored in the ${build}/zdialog directory.

  <xmltogff srcdir="${src}/modules" destdir="${build}/modules" >
      <include name="**/*.xml" />
  </xmltogff>

Converts all .xml files in the ${src}/zdialog directory (including sub-directories) into various types of GFF files stored in the ${build}/zdialog directory (with expanded sub-directories based on the source files).

mysrc/modules/MyModule/area001.are.xml -> build/modules/MyModule/area001.are
mysrc/modules/MyModule/doorpalcus.itp.xml -> build/modules/MyModule/doorpalcus.itp
...and so on...
mysrc/modules/MyOtherModule/MainArea.are.xml -> build/modules/MyOtherModule/MainArea.are
...and so on...

ERF Packer

Packs a set of files into an ERF structured file. ERF is the format of .erf, .hak, and .mod files.

Options

Different options can be specified to control which files are included in the resulting ERF file as well as the path of that ERF file.

Note: HAK files can be created with no contents and it's the only empty ERF format that this task supports. This is often an advanced way to make server-only HAK files since you can create an empty client version of the HAK for users to download that is only a few hunder bytes. A useful multiplayer setup that can be used to better organize the server side content and make the day-to-day module maintenance easier.

Attribute Description Required
basedir The base directory for any packed files. Yes for all files except HAK files. HAKs can be empty.
erffile The .mod, .erf, or .hak file that will be generated. The erfpacker task uses the extension to determine the file type. Yes
description A one-line description that will be embedded into the generated ERF file. Realistically, one should always use the nested "description" elements instead to provide multi-line descriptions. In fact, HAK files will be messed up if you use this one-line version.
Note: For module files, if a description is not specified then it is retrieved from the module's module.ifo file.
No
minGameVersion String specifying the minimum game version required to run/use the generated ERF file. Currently defaults to "1.64". No
expansionPacks An encoded set of flags representing the required expansion packs to run/use the generated ERF file.
ExpansionValue
Shadows of Undrentide1
Hordes of the Underdark2
Both3
In general, single expansion values can be added together to form combiniations... just in case Bioware comes out with any future expansion it would be 4 the next would be 8 and so on.
Defaults to "3".
No

Parameters specified as nested elements

The erfpacker task is an implicit FileSet and supports all attributes of <fileset> (dir becomes basedir).

In addition, the erfpacker task supports nested "description" elements that will be used to form a multiline description. For .HAK files, the lines take on specific meaning. The first line is title of the HAK and the second line is the URL displayed for the HAK. All other lines are combined to form the actual description. See examples below.

Examples

  <erfpacker basedir="${build}" erffile="MyStuff.erf"
                minGameVersion="1.62" expansionPacks="2" >
    <description line="This if my ERF file.  There are many like it" />
    <description line="but this one is mine.  It contains a bunch of" />
    <description line="stuff you can import into your module." />
  </erfpacker>

Generates the MyStuff.erf file containing all of the contents in the ${build} directory.

  <erfpacker basedir="${build}" erffile="MyStuff.erf"
                minGameVersion="1.62" expansionPacks="2" >
    <description line="This if my ERF file.  There are many like it" />
    <description line="but this one is mine.  It contains a bunch of" />
    <description line="stuff you can import into your module." />

    <include name="*.nss" />
    <include name="*.ncs" />
  </erfpacker>

Generates the MyStuff.erf file containing only the .nss and .ncs files in the ${build} directory.

  <erfpacker basedir="${build}" erffile="MyStuff.erf"
                minGameVersion="1.62" expansionPacks="2" >
    <description line="This if my ERF file.  There are many like it" />
    <description line="but this one is mine.  It contains a bunch of" />
    <description line="stuff you can import into your module." />

    <include name="**/*.nss" />
    <include name="**/*.ncs" />
  </erfpacker>

Generates the MyStuff.erf file containing only the .nss and .ncs files in the ${build} directory including any sub-directories.

  <erfpacker basedir="${build}" erffile="MyStuff.hak"
                minGameVersion="1.62" expansionPacks="2" >
    <description line="A bunch of stuff in a HAK file." />
    <description line="http://nwntools.sf.net/" />
    <description line="Stuff you can attach to your module to add new content." />
  </erfpacker>

Generates the MyStuff.hak file containing the contents of the ${build} directory and using the specified description. The first line is the title of the HAK. The second line is the URL displayed for the HAK and the third line (and any subsequent lines) is the description.

Combined Example

Following are portions of the build.xml file that I use to generate various targets for my ZDialog utilities. I generated a .erf file for importing into modules. I also generate client and server .HAK files for those (like me) who like to keep their script and resource lists uncluttered while developing. Third, I generate a sample module.

This is basically a reduced version of the build file in the nwn-content module in the NWN Tools CVS.


<project name="NWN Content Development" default="build" basedir=".">

    <!-- Load some build.properties files so that users can customize
        certain properties to their environment.  ANT only uses the first
        value encountered so any properties defined in these files will
        override later settings.
        See "build.properties.sample" in the top level directory for all
        property values you must customize for successful building!!!        -->
    <property file="build.properties"/>
    <property file="${user.home}/build.properties"/>

    <!-- set global property defaults for this build -->
    <property name="src"           value="src"/>
    <property name="build"         value="build"/>
    <property name="dist"          value="dist"/>


    <target name="init">
        <!-- define some custom tasks used later-->
        <taskdef name="nwnc" classname="org.progeeks.nwn.ant.CompileTask" />

        <taskdef name="xmltogff" classname="org.progeeks.nwn.ant.XmlToGffTask" />

        <taskdef name="erfpacker" classname="org.progeeks.nwn.ant.ErfPackerTask" />

        <!-- Create the time stamp... and an additional formatted build time property -->
        <tstamp>
            <!-- Because I like to include the build time in my generated ERF file descriptions. -->
            <format property="build.time" pattern="MM/dd/yyyy hh:mm aa" />
        </tstamp>

        <!-- Create the build directory structure used by compile -->
        <mkdir dir="${build}"/>
        <mkdir dir="${build}/client"/>
        <mkdir dir="${build}/server"/>
    </target>

    <target name="zdlg_sample.mod" description="Creates the ZDialog sample module." depends="init" >

        <!-- Compile the NWN sources. -->
        <nwnc srcdir="${src}/modules/zdlg_sample" destdir="${build}/modules/zdlg_sample" >
            <include name="*.nss" />
        </nwnc>

        <!-- We want the module to have the source script files too. -->
        <copy todir="${build}/modules/zdlg_sample" >
            <fileset dir="$(src}/zdialog" includes="*.nss" />
        </copy>

        <!-- Convert all of the module's XML files into binary form. -->
        <xmltogff srcdir="${src}/modules/zdlg_sample" destdir="${build}/modules/zdlg_sample" >
            <include name="**/*.xml" />
        </xmltogff>

        <!-- Generate the .mod file using the description from the module.ifo file. -->
        <erfpacker basedir="${build}/modules/zdlg_sample" erffile="${build}/zdlg_sample.mod" >
        </erfpacker>
    </target>

    <target name="zdialog.erf" description="Builds the ZDialog.erf file." depends="init" >

        <nwnc srcdir="${src}/zdialog" destdir="${build}/zdialog" >
            <include name="*.nss" />
        </nwnc>

        <!-- Since we're making a developer-bundle we'll go ahead and make
             sure the scripts are copied too. --<
        <copy todir="${build}/zdialog" >
            <fileset dir="${src}/zdialog" includes="*.nss" />
        </copy>

        <!-- This ERF will contain some conversation files in addition to scripts
             and we keep those in XML form in the src directories. -->
        <xmltogff srcdir="${src}/zdialog" destdir="${build}/zdialog" >
            <include name="**/*.xml" />
        </xmltogff>

        <erfpacker basedir="${build}/zdialog" erffile="${build}/zdialog.erf"
                   minGameVersion="1.65" expansionPacks="3" >
            <description line="Z-Dialog Conversation System" />
            <description line="A set of scripts and some associated conversation files" />
            <description line="for programmable conversations." />
            <description line="Packaged: ${build.time}" />
        </erfpacker>

    </target>

    <target name="zdialog.haks"
               description="Builds the client and server HAK files for the ZDialog utils." >

        <!-- Put together the real .HAK file that will contain the actual content
            to be used by the module developer.  This is the one that the .mod file
            actually uses when running on the server. -->
        <erfpacker basedir="${build}/zdialog" erffile="${build}/server/zdlg.hak" >
            <description line="Z-Dialog Conversation System" />
            <description line="http://nwntools.sf.net/" />
            <description line="A HAK version of the Z-Dialog system for those module" />
            <description line="developers that don't like their script list cluttered." />
            <description line="Packaged: ${build.time}" />
        </erfpacker>

        <!-- Create another empty .HAK file that can be downloaded by users to play
            modules that use the real .HAK file generated above.  This allows the user
            to download an extremely small .HAK file while we can load up the server-side
            version with tons of content and update it at will without requiring a new download.
            Note: this only works for server-side custom content. -->
        <erfpacker erffile="${build}/client/zdlg.hak" >
            <description line="Z-Dialog Conversation System" />
            <description line="http://nwntools.sf.net/" />
            <description line="An empty client-only HAK so that the server can use the" />
            <description line="full HAK file." />
            <description line="Packaged: ${build.time}" />
        </erfpacker>

    </target>

    <target name="build" description="Builds everything."
                depends="zdlg_sample.mod,zdialog.erf,zdialog.haks" >
    </target>

    <target name="clean">
        <!-- Delete the ${build} directory tree. -->
        <delete dir="${build}"/>
    </target>

</project>