User Guide
Quick Reference
Modding
Development
User Guide
Quick Reference
Modding
Development
In Doomsday 2, resources are collected into packages. This article describes the Doomsday 2 package format and how you should go about creating your own packages.
A Doomsday 2 package can either be a folder or a ZIP archive. Regardless of the type, the file name must have a .pack extension. The package is also required to contain metadata that describes the contents of the package.
Below is an example of what a package might look like as a folder structure. This package is from one of the Doomsday test apps, and it contains 3D model and image files.
File structure:
net.dengine.test.glsandbox.pack/ info.dei models/ boblampclean.md5anim boblampclean.md5mesh guard1_body.png guard1_face.png guard1_helmet.png iron_grill.png marine.md2 Marine_green.pcx round_grill.png testpic.png
The info.dei file has the following contents:
title: GLSandbox Test version: 1.0 license: GPL 3+ tags: test
Naming conventions. Here is a brief overview of the conventions used in package file names. These conventions are described in more detail elsewhere on this page.
Package identifier. The name of the package file/folder has a special meaning: it is used as the package's globally unique identifier. Being unique, the identifier can be used as-is in savegames or multiplayer games to record and share information about which packages are required or in use. Also, it is possible to compile a single repository of all available packages, and build a package manager interface for browsing and acquiring packages, without fear of identifier conflicts or other ambiguities. Note that the identifier is case insensitive (upper and lower case letters are not treated differently), however using lower case letters is recommended for consistency.
We have chosen to use reverse domain name notation in package identifers. In other words, the package identifier also identifies the origin or creator of the package. In the case of the above example, the origin is net.dengine, i.e., the Doomsday project's web site. From a technical standpoint, this notation has the significant advantage of supporting hierarchies, providing a natural way to identify subpackages. Another advantage is that the notation is compatible with alphabetic sorting, because the most general component is at the beginning of the identifier.
A package identifier is required to have at least two components. For example: skyjake.example is a valid identifier, but example is not. (The corresponding files might be named skyjake.example.pack and example.pack.)
Alternative versions of packages. Consider the package file name: net.dengine.test.glsandbox.pack. What if you have two versions of this package that need to co-exist in the same folder on a hard drive (or in an online repository)? It is possible to add a free-form suffix to the file name; for example:
net.dengine.test.glsandbox_1.2beta.pack ^^^^^^^
Anything following the left-most underscore (_) found in the package file name is excluded from the package identifier. Doomsday attempts to parse a version number from the part following the underscore.
Folders and ZIPs. A package may be either a regular folder or a ZIP archive. Likewise, subpackages contained within a package may be either regular folders or ZIP archives. At runtime, Doomsday accesses all ZIP archives as regular folders. Therefore, you can use whichever format is most suitable for your needs.
Every package is required to have metadata. In practice, the metadata is a Doomsday Script Record containing a set of required variables and any amount of other information.
The Info file. The root folder of the package must contain a file named info.dei or Info. This is the primary place where the package's metadata is defined. If this file is missing, the package will not be recognized as a package by Doomsday. The file uses the ScriptedInfo syntax to specify a set of variables and functions that will be stored with the package at runtime.
Required metadata. The following four variables must be defined in the metadata for a package to be loadable by Doomsday.
Variable | Description |
---|---|
title | Human-readable name of the package. Normally this is the only name shown to the user. |
version | Version number of the package. Must be in the format x, x.y, or x.y.z, with x, y and z being numbers. |
license | Terms under which the package is being made available: determines who can legally distribute and/or modify the package contents (see package licensing). |
tags | Keywords/tags that apply to the package, as a white-space separated list of words. This is used for organizing larger collections of packages and for finding packages (see package tags). |
__init__.ds script. Additionally, a package may contain a script named __init__.ds. If present, this script is executed immediately after the Info file has been parsed, and the script will run with the package metadata as the global namespace; i.e., any variables defined in Info are available as global variables in the script. This way one can more conveniently add pure DS content like functions into the package metadata.
Globally available DS modules. A package may contain Doomsday Script modules that are made available for importing from all DS scripts. This is done by adding the importPath
variable to the package metadata, for instance:
importPath <modules>
The import path must be specified as an array, which is why the angle brackets are used here (see Info syntax). The end result is that the folder modules inside the package is added to the global DS import path, and its contents will be checked when someone tries to import a module in Doomsday Script. (Many of the net.dengine packages use this, for instance see net.dengine.stdlib.)
Load/unload scripts. When a package is loaded, Doomsday checks if the function onLoad()
exists in the package metadata and automatically calls it. Similarly, the function onUnload()
gets called right before the package is unloaded. This allows one to perform any relevant actions via Doomsday Script at these times.
A subpackage is a package that is contained inside another package's root folder. For instance, this allows creating a larger collection of packages that can be distributed as a single file. From Doomsday's point of view, though, subpackages are treated just like any other package — they are not automatically loaded when the containing package is loaded. This makes subpackages mostly a convenience for the packager.
Let's examine the client's packages (omitting most of the contained files):
net.dengine.client.pack/ Info defaultstyle.pack/ modules/ renderer.pack/ lensflares.pack/ testmodel.pack/
Here we have .pack folders inside the main net.dengine.client package. defaultstyle.pack is a subpackage whose identifier becomes net.dengine.client.defaultstyle: the containing package's identifier is used as a prefix.
The rule about Info remains: a package is only loadable if it has metadata. However, subpackaging still works without metadata. In the above example, you could omit the Info inside the root folder, and still be able to load net.dengine.client.defaultstyle.
It is allowed to place packages in other folders inside a package, however those will have to be named using their full identifiers as they are not treated as subpackages.
A package may require other packages to be loaded before it can be used. The required dependencies of a package are specified using the requires
metadata variable. Doomsday will ensure that each listed required package is successfully loaded before loading the package itself.
requires <veirdo.hexen.common>
Packages may also be configured to be optional. The recommends
variable lists the optional packages that are loaded by default, and the extras
variable lists the packages that are not loaded by default.
recommends <org.example.small> extras <org.example.large>
Below is a link to an example that demonstrates setting up dependencies between packages:
Packages may contain any kind of files and any amount of Doomsday Script. To let Doomsday know that the package contains something it can use (an asset), it has to defined in the metadata of the package. For example, an asset could be a 3D model that the renderer would use to represent a thing of a particular type.
Package assets are a generic mechanism in the sense that it builds on the Doomsday 2 file system and Doomsday Script, and leaves the interpretation of the asset definitions to whichever subsystem uses the asset.
All the assets provided by a package are defined in the package metadata. For instance, a 3D model asset definition could look like this:
asset model.thing.possessed { path = "boblampclean.md5mesh" }
The Info block type asset
must be used when defining an asset. The asset identifier (model.thing.possessed
) is used to declare what kind of asset is being defined. See Asset identifiers for more details.
Packages can contain DED definitions that are only in effect when the package is loaded.
The defsPath
metadata variable specifies which folder contains the DED files of the package. The path is relative to the root folder of the package. For example:
defsPath = "defs"
This would look for DED files in the folder /defs inside the package. Only this folder is checked — any DED files in subfolders are not automatically read. You can use the Include
directive in DED files to read further DEDs from subfolders.
Packages can provide an image file specifically meant for representing the package in GUIs. Simply place an image file called icon.jpg or icon.png in the root of a .pack package. The maximum allowed size for the image is 512×512 pixels. There is no other limitation for the resolution and aspect ratio (doesn’t have to be square; can be small). If you use a PNG image, it is allowed to have an alpha channel for a non-rectangular shape.
The package icon is not declared or affected by the package metadata in any way — only the presence of the icon.jpg/icon.png file in the package root is checked. This is primarily for efficiency: the UI may have hundreds of packages to manage, so fetching their icons needs to be as fast as possible.
A package may have an alias, i.e., an identifier that is different from the one that is determined based on its file name. When such a package is loaded, the package appears under the /packs folder also under the alias in addition to its real identifier.
For instance, the shaders and images for lens flares are stored in the net.dengine.client.renderer.lensflares package. In its metadata, an alias has been defined:
alias: feature.lensflares
However, since the lens flare renderer accesses these resources using the identifer feature.lensflares, any other loaded package with this alias could be used instead, allowing the creation of altenative packages for the same purpose.
For more details about the feature packages used by Doomsday, see feature_packages.
Here are a few notes that you should bear in mind when creating your own packages:
(2.0 →) The -file
option can be used to load packages (together with -game
) or to register them for use at runtime in game profiles (without -game
).
The -pkg
option can be used for loading packages at launch (together with -game
).
While it is unimportant to understand exactly how Doomsday manages packages at runtime, one detail should be noted. All the packages that are currently loaded (i.e., in use) appear inside the special /packs folder in the Doomsday 2 file system. This means that if a package needs to access to the contents of another package, it can do so via the path /packs/(identifier)/.
Assets appear under /packs, too. For example, given the asset above, the symbolic link /packs/asset.model.thing.possessed would be created, pointing to the package containing the asset. The model renderer would then be notified of the availability of this asset due to the existence of this link, and where to access the files and scripts associated with the asset. This guarantees that there is no ambiguity about which package offers which asset, even if many packages have variants of the asset — only the one linked under /packs will be used.
The Doomsday 2 package system was first introduced in 1.15, however it is not yet used for all resources.
-pkg
option