| 1 |
----------------------------- |
| 2 |
THE MANA WORLD PACKAGE SYSTEM |
| 3 |
----------------------------- |
| 4 |
|
| 5 |
1. INTRODUCTION |
| 6 |
2. LOCATION OF DATA |
| 7 |
3. CONTENTS OF DATA PACKAGE |
| 8 |
4. TYPES OF DATA |
| 9 |
5. INITIALIZING PACKAGE MANAGEMENT |
| 10 |
6. LOADING A REQUESTED RESOURCE |
| 11 |
7. RESOURCE MANAGEMENT DETAILS |
| 12 |
|
| 13 |
|
| 14 |
1. INTRODUCTION |
| 15 |
|
| 16 |
The Mana World is expected to grow continuously with updates to the game world |
| 17 |
occurring relatively frequently. More often so than for example new releases |
| 18 |
of the game client. To make sure players don't have to update their data |
| 19 |
manually all the time, by for example downloading the latest from the website, |
| 20 |
the TMW client should be able to automatically obtain new data packages from |
| 21 |
the server. |
| 22 |
|
| 23 |
Note: To reduce the load on the server (which isn't expected to have huge |
| 24 |
free uploading resources), the idea is that the server will only send a |
| 25 |
torrent file to the client and that the file is subsequently downloaded from |
| 26 |
several locations that have volunteered to spread TMW data files. Ultimately |
| 27 |
a simple option on the client will even allow players to contribute their |
| 28 |
excess bandwidth to help other players get the updates faster. |
| 29 |
|
| 30 |
|
| 31 |
2. LOCATION OF DATA |
| 32 |
|
| 33 |
There are two locations where TMW can look for game data. The install data |
| 34 |
directory and the data directory in the user's home directory. The latter one |
| 35 |
doesn't have to be used for Windows users, but is required for dynamic updates |
| 36 |
for UNIX users, who generally won't have write permissions to the install |
| 37 |
data directory. So for UNIX the two locations are: |
| 38 |
|
| 39 |
/usr/local/share/manaworld/data/* |
| 40 |
|
| 41 |
~/.manaworld/data/* |
| 42 |
|
| 43 |
While for Windows all the data will be located at: |
| 44 |
|
| 45 |
C:\Program Files\The Mana World\data\* |
| 46 |
|
| 47 |
In the UNIX case it doesn't matter in which order the data directories are |
| 48 |
examined. |
| 49 |
|
| 50 |
|
| 51 |
3. CONTENTS OF DATA PACKAGE |
| 52 |
|
| 53 |
The contents of the data packages are strictly categorized and all packages |
| 54 |
share a single root, similar to the paths on a UNIX system. The name of the |
| 55 |
package is irrelevant. An example of the contents is given by: |
| 56 |
|
| 57 |
/graphics/sprites/forest/pinetree.png |
| 58 |
/graphics/sprites/furniture/bed.png |
| 59 |
/graphics/tiles/dark_forest.png |
| 60 |
/graphics/tiles/city.png |
| 61 |
/music/eagles_are_watching.xm |
| 62 |
/music/silent_rose.xm |
| 63 |
/sound/battle/sword1.ogg |
| 64 |
/sound/battle/sword2.ogg |
| 65 |
/maps/deep_desert.tmx |
| 66 |
/maps/desert_town.tmx |
| 67 |
/tilesets/dark_forest.tsx |
| 68 |
/tilesets/city.tsx |
| 69 |
/scripts/Portal.rb |
| 70 |
/scripts/PawnShop.rb |
| 71 |
/scripts/Fountain.rb |
| 72 |
|
| 73 |
|
| 74 |
4. TYPES OF DATA |
| 75 |
|
| 76 |
png - The preferred format for images |
| 77 |
xm - The preferred format for music (or other kinds of module formats) |
| 78 |
ogg - The preferred format for sound effects |
| 79 |
tmx - The map format (to be implemented) |
| 80 |
tsx - The tile set format (to be implemented) |
| 81 |
rb - A Ruby script file (application to be discussed) |
| 82 |
|
| 83 |
|
| 84 |
5. INITIALIZING PACKAGE MANAGEMENT |
| 85 |
|
| 86 |
When TMW starts it will scan its data directories for both packages (archives) |
| 87 |
and directories. When a directory is found with the same name as a package, the |
| 88 |
directory is the preferred location to load data from as it is assumed to be |
| 89 |
more up to date. |
| 90 |
|
| 91 |
Each package will have an ID and a file listing associated with it. Having made |
| 92 |
a list of all packages they are processed in the order of their IDs. A mapping |
| 93 |
is made from file to package, as follows: |
| 94 |
|
| 95 |
/music/eagles_are_watching.xm -> /usr/local/share/manaworld/data/musicpack |
| 96 |
/music/silent_rose.xm -> /usr/local/share/manaworld/data/musicpack |
| 97 |
/sound/battle/sword1.ogg -> ~/.manaworld/data/patch1 |
| 98 |
/sound/battle/sword2.ogg -> ~/.manaworld/data/patch1 |
| 99 |
... |
| 100 |
|
| 101 |
Because the packages are loaded in the order of their IDs, it is made sure that |
| 102 |
each file will always point to the package in which is was last updated. The |
| 103 |
package IDs make sure that there is an absolute ordering of the packages. |
| 104 |
|
| 105 |
To allow the client to get rid of old packages, a package can declare an |
| 106 |
arbitrary amount of packages with a lower ID than itself as obsolete. These |
| 107 |
packages will then be ignored by the client, and optionally they can be |
| 108 |
automatically deleted. |
| 109 |
|
| 110 |
|
| 111 |
6. LOADING A REQUESTED RESOURCE |
| 112 |
|
| 113 |
When the game starts and during the game, resources will continuously be asked |
| 114 |
for. A resource manager will take care that each resource is only loaded once. |
| 115 |
It also makes sure that the resources are loaded from the right package using |
| 116 |
the constructed mapping. |
| 117 |
|
| 118 |
As noted above, the resource manager makes sure directories are preferred |
| 119 |
to package files when resources are loaded. The presence of directories is |
| 120 |
only expected in the case of developers that will relatively frequently update |
| 121 |
the data while working on the next package to be released. |
| 122 |
|
| 123 |
|
| 124 |
7. RESOURCE MANAGEMENT DETAILS |
| 125 |
|
| 126 |
The resource management technique is critical to the overall success of the |
| 127 |
package management system as a whole. Resources are loaded at runtime as they |
| 128 |
are needed, and unloaded as they become unused. In order to ensure the |
| 129 |
autonomous functioning of this process reference counting is the agreed upon |
| 130 |
technique for managing loaded resources in TMW. |
| 131 |
|
| 132 |
For those unfamiliar with the practice of reference counting, it involves |
| 133 |
every resource object having a variable containing the number of references to |
| 134 |
the object. When a reference is added the function addRef() is called and when |
| 135 |
it is removed the function release() is called. When the reference count |
| 136 |
reaches zero the object will automatically delete itself, thus handling the |
| 137 |
cleanup of resources. |
| 138 |
|
| 139 |
Reference counting will form the core of the resource management system. Each |
| 140 |
resource object will have the functionality of a reference counted object. The |
| 141 |
resource manager will hold ResourceEntry objects. The resource entry object |
| 142 |
contains a pointer to the resource as well as the location of the path of the |
| 143 |
file the resource was loaded from. This would look something like: |
| 144 |
|
| 145 |
/** |
| 146 |
* A generic reference counted resource object. |
| 147 |
*/ |
| 148 |
class Resource { |
| 149 |
public: |
| 150 |
/** |
| 151 |
* Loads the resource from the specified path. |
| 152 |
* @param filePath The path to the file to be loaded. |
| 153 |
* @return <code>true</code> if loaded <code>false</code> otherwise. |
| 154 |
*/ |
| 155 |
virtual bool Load(std::string filePath) = 0; |
| 156 |
... |
| 157 |
/** |
| 158 |
* Increments the reference counted of this object. |
| 159 |
*/ |
| 160 |
void addRef() { ++referenceCount; } |
| 161 |
|
| 162 |
/** |
| 163 |
* Decrements the reference count and deletes the object |
| 164 |
* if no references are left. |
| 165 |
* @return <code>true</code> if the object was deleted |
| 166 |
* <code>false</code> otherwise. |
| 167 |
*/ |
| 168 |
void release() { |
| 169 |
--referenceCount; |
| 170 |
|
| 171 |
if (!referenceCount) |
| 172 |
{ |
| 173 |
delete this; |
| 174 |
return true; |
| 175 |
} |
| 176 |
|
| 177 |
return false; |
| 178 |
} |
| 179 |
private: |
| 180 |
unsigned int referenceCount; |
| 181 |
}; |
| 182 |
... |
| 183 |
/** |
| 184 |
* A resource entry descriptor. |
| 185 |
*/ |
| 186 |
struct ResourceEntry { |
| 187 |
Resource* resource; |
| 188 |
std::string filePath; |
| 189 |
}; |
| 190 |
... |
| 191 |
|
| 192 |
The resource manager would then hold a mapping containing the resource entry as |
| 193 |
well as the string defining its resource identification path. The resource |
| 194 |
manager would thus look something like this: |
| 195 |
|
| 196 |
/** |
| 197 |
* A class for loading and managing resources. |
| 198 |
*/ |
| 199 |
class ResourceManager { |
| 200 |
public: |
| 201 |
... |
| 202 |
private: |
| 203 |
std::map<std::string, ResourceEntry> resources; |
| 204 |
}; |
| 205 |
... |
| 206 |
|
| 207 |
This will allow the game to load resources with little awareness of the actual |
| 208 |
path from which they were loaded. The resource manager will also act as a |
| 209 |
resource object factory. A factory object is an object that creates an |
| 210 |
instance of an object derived from a common base class. In this case it will |
| 211 |
create Resource objects. This would make the ResourceManager object look like |
| 212 |
this: |
| 213 |
|
| 214 |
/** |
| 215 |
* A class for loading and managing resources. |
| 216 |
*/ |
| 217 |
class ResourceManager { |
| 218 |
public: |
| 219 |
enum E_RESOURCE_TYPE |
| 220 |
{ |
| 221 |
MAP, |
| 222 |
MUSIC, |
| 223 |
IMAGE, |
| 224 |
SCRIPT, |
| 225 |
TILESET, |
| 226 |
SOUND_EFFECT |
| 227 |
}; |
| 228 |
|
| 229 |
/** |
| 230 |
* Creates a resource and adds it to the resource map. |
| 231 |
* The idPath is converted into the appropriate path |
| 232 |
* for the current operating system and the resource |
| 233 |
* is loaded. |
| 234 |
* @param type The type of resource to load. |
| 235 |
* @param idPath The resource identifier path. |
| 236 |
* @return A valid resource or <code>NULL</code> if |
| 237 |
* the resource could not be loaded. |
| 238 |
*/ |
| 239 |
Resource* Create(const E_RESOURCE_TYPE& type, |
| 240 |
std::string idPath); |
| 241 |
... |
| 242 |
private: |
| 243 |
std::map<std::string, ResourceEntry> resources; |
| 244 |
}; |
| 245 |
... |
| 246 |
|
| 247 |
Loading a resource would then look something like: |
| 248 |
|
| 249 |
Image* img = (Image*) ResourceManager.Create(ResourceManager::IMAGE, |
| 250 |
"/graphics/tiles/dark_forest.png"); |