My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
QuickStart  
LibCloak Quick Start Guide
Featured, Phase-Deploy
Updated Aug 25, 2010 by grun...@gmail.com

Introduction

This is the quick start guide for the Cloak Resource Manager. The goal of this document is to get you working with libcloak as quickly as possible.

This library was created because I find it useful, and this document was created in the hope that you will find it useful too.

Quick Overview

This is a brief overview of what the library can do.

libcloak can be used to load resources needed by your program by name. Once these resources are loaded, they may be entered into a cache which is used to speed up loading and object generation times.

Objects are loaded and/or generated by Handlers that you create. Handlers are organized into a file-system-like hierarchy. Their sole purpose is to receive a pathname and an attribute list and then return a pointer to some object.

For example, you may have a few art files you want to use for sprites called: "human1.jpg", "human2.jpg", and "human3.jpg". All of these files are in some folder that your program knows about. You want to write a Handler that will read in and parse these files into an object your program can use.

You decide that these files will be under the "/art" directory in your virtual hierarchy (this does not need to correspond to actual file system organization). So you will load them by requesting "/art/human1.jpg" and so on.

You can write a single handler for all of these by creating one that is bound to "/art/*". That means that if you try to load any path starting with "/art", then it will ask your handler to handle the request (unless a more specific handler can handle it, e.g. you have a handler for "/art/package/item.jpg" and try to load that, then your specific handler will be called instead).

Your handler will be implemented by simply parsing the path it is given and loading based on the file name. So if it is given "/art/human1.jpg" it gets the vector {"art","human1.jpg"}. From this it recovers the filename, "human1.jpg", loads this from the correct place, and converts it into whatever format you want. It then returns that object to the system.

The system will then cache what was returned, and the next load of "/art/human1.jpg" will be loaded from the cache and not the handler. You now have a generic handler that can load any art file from this directory.

The rest of the document is organized as follows:

We will first go over how to set up the system for use, including how to initialize the manager, store prefix paths, create handlers, and start garbage collection. We will then show the different ways that the system can be used to obtain resources from your handlers and the cache.

This system can be incredibly powerful when wielded with some creativity.

Getting Started

The following sections will show how the library is used step by step.

Initialize the Manager

First you need to initialize a Manager.

Here is an example of what this looks like:

#include <cloak/Cloak.h>

// Feel free to use the namespace to make names shorter
// using namespace Cloak;

int main()
{
    Cloak::Manager manager = new Cloak::Manager();
}

That is all you need to do.

Note that the Manager is not a singleton.

You can have as many Managers as you want, each with their own cache. A single Manager should be sufficient (and would be faster than multiple), but we are not adding an artifical constraint on the number of Managers just in case you find a good reason to have several.

The Manager is the main object used to interact with the system. You should keep a reference to the Manager you create wherever it is needed, and it should be deleted when it is no longer necessary.

Note that the Manager owns only a single reference to objects in its cache. You may delete the Manager and all references to previously managed objects will still be valid.

Set up Prefixes

You can now add prefixes to your Manager.

This is completely optional, but it makes it easier for your Handlers to find your resources on disk.

Supposing you have "Cloak::Manager *manager", you can do the following:

// Add a prefix
manager->addPrefix("art", "./resources/art/");

// Use a prefix
string path = manager->getPrefix("art") + "human1.jpg";

// We have path = "./resources/art/human1.jpg"

This makes it easy for your Handlers to find things, especially if you specify where resources will be in a configuration file. All you need to do is parse the file and then set the corresponding prefix.

Making a Handler

We will now go over how to make a handler.

A Handler is derived from the Handler base class.

Here is an example which we will go over:

#include <cloak/Cloak.h>

class MyTestHandler : public Handler
{

public:

    MyTestHandler(string path) : Handler(path)
    {
    }

    virtual Entity handlePath(Manager &manager, PathVector &path, AttrMap &attrs)
    {
        // Initialize a new Image (whatever that is) from your path
	Image *temp = new Image(...);

        // You can return a pointer to _anything_ as an "Entity", so don't worry.
        return temp;   
    };
}

Note first that your Handler must be derived from the Handler base class.

The constructor must take a string and pass that to the base class. You should never need to call this directly.

You must override the virtual function "handlePath" it returns an Entity and receives a reference to the Manager, a PathVector, and an AttrMap as parameters. Entity is a nice name for "void pointer", you can return any pointer from this method. The PathVector you get is a std::vector<string>, you can use it as such to get the parts of the path. The AttrMap is an std::map<string, string>, use it to retrieve attributes that were passed in with a request.

Your handlePath method must take the path and attributes and it must allocate space for some object (with new) and return a pointer to it. Once it is returned, that object no longer belongs to your handler; you should not keep any references to it. If you want to refer to it you should query the Manager for that object to retrieve a shared reference to it.

Once your handler class is defined, you need to add it to the Manager. An example is as follows:

CLOAK_ADDHANDLER("art/*", MyTestHandler, manager);

This says that the class "MyTestHandler" will be used to handle anything beginning in "art/". It adds this binding to the object "manager".

Garbage Collection

This library supports garbage collection of unused resources.

There are 3 currently supported modes of garbage collection:

  • FULL - Collect all unused resources in the cache
  • QUARTER_SPACE - Look at the 25% of entries in the cache that were least recently requested, delete all unused resources in that space
  • QUARTER_SLIDING - Starting with the least recently requested entry, go through each entry in the cache and delete the unused ones, stopping when 25% of the entries have been deleted.

All garbage collection methods require one of these modes to be specified.

Simply running manager->garbageCollect(MODE); will immediately garbage collect using the given mode.

The library also gives the option of automatically spawning a new thread to do garbage collection. Use manager->startGarbageThread(MODE,TIME); to start one. This simply runs "garbageCollect(MODE)" every "TIME" milliseconds in a separate thread. Feel free to stack these, possibly running QUARTER_SPACE every second while running a FULL or QUARTER_SWEEP every minute if that is necessary. The garbage collection is thread safe.

Retrieving Resources

Resources are retrieved from the manager as a CloakRef<Type> object, where "Type" is the type of what you are attempting to retrieve. This is a shared reference to that object, and it can be used as a pointer (dereferenced with '*' and '->'). If you are familiar with Boost, CloakRef is simply a typedef for boost::shared_ptr.

// Assuming we have "CloakRef<int> num;" and "CloakRef<Object> obj;"
std::cout << *num;
*num = 3;

obj->method();

We will now go over the ways to obtain resources as CloakRefs.

Obtain a Non-Mutable Reference

A non-mutable reference is simply a shared pointer to a const object. The object being retrieved may not be modified.

When a non-mutable object is requested, the system first checks if it resides in the cache. If it resides in the cache already, a reference is returned. If not, the corresponding Handler is called and the result it generates is added to the cache and then a reference to that is returned.

A static object is requested using the Cloak::Manager::getEntity method as follows:

CloakRef<const TYPE> ref = manager->getEntity<TYPE>(PATH, ATTRIBUTES);

In this example, TYPE is the type of object expected to be returned, PATH is the entity's path as a string, and ATTRIBUTES are attributes that can be used by the Handler. Attributes are in a string format like "name:value, name2:value2".

Note: Two objects are unique in the eyes of the cache even if they differ by attributes only.

You may now use this returned reference however you wish, but you CANNOT MODIFY IT.

This method is great for obtaining references to art and other media. Each object that needs it can load it individually, but they are all just sharing the same underlying object.

Obtain a Mutable Reference (NOT cached)

A mutable reference is one that can be modified, but is not cached. Retrieving one of these references completely bypasses the cache. It will directly ask the Handler to create a new object that can be modified.

A mutable object is requested using the Cloak::Manager::getMutableEntity method as follows:

CloakRef<TYPE> ref = manager->getMutableEntity<TYPE>(PATH, ATTRIBUTES);

All placeholders have the same explanations as in the previous example.

You may now use this returned reference however you wish, and you may modify it without restriction. The underlying object belongs solely to the calling function at this point, and it will be automatically deleted when it is no longer used by the smart pointer CloakRef.

This method is great for using Cloak as an instance of the "Simple Factory" design pattern. The next method (Mutable Copy), however, is normally preferred.

Obtain a Mutable Copy

A mutable copy is a mutable reference to a copy of a non-mutable object. This works by obtaining a non-mutable reference to some resource, making a copy of that resource, and then returning a reference to that copy.

A mutable object is requested using the Cloak::Manager::getEntityCopy method as follows:

CloakRef<TYPE> ref = manager->getEntityCopy<TYPE>(PATH, ATTRIBUTES);

This method causes the entity to be either read from the cache if it exists or generated and placed in the cache if it doesn't, just like getEntity.

This method uses the object's copy constructor to make the copy. It must be defined and working if the default copy constructor is not sufficient.

This method is great for obtaining a private, mutable copy of a resource while still using the cache for extra speed.

Usage Examples

The main power of this system lies in the ability of Handlers to call the Manager themselves.

There are simple uses of this library as a way to load and keep track of art or GUI images, but there are more complex applications that allow this library to simplify development.

We try to keep the creation of our resources separate from their use, and this system provides a framework for doing that easily.

Procedurally Generated Images

Suppose we are making a "Frogger" type game in which logs will scroll across the screen to block the player.

We have three images "log_right.png", "log_left.png", and "log_middle.png" representing the left end, the right end, and the repeatable center piece of a log respectively.

We will have a Handler that creates log images called "LogHandler", and we will bind it to "/entities/log". We will have an art handler defined similar to the example in this document called "ArtHandler". It will be bound to "/art/" and simply takes the second part of the path and loads that file in our art directory.

We want to create a log using a path of "/entities/log" and attributes of the length, which must be at least 2 (for two end points).

When we call manager->getEntity("/entities/log","length=3");, we want our LogHandler to load "/art/log_right.png", "/art/log_left.png", and "/art/log_middle.png". It will then put these images together into a final image and return that image. When length is greater than 3, it will repeat the middle piece as many times as necessary. The next time we need another of the same log, it will get a const reference to the one in the cache.

We could even add attributes to change the hue of the image based on "type of wood" or something similar, so we have basic wood images and then change the hue at runtime to have the desired effect. The result is also cached so that we do not need to regenerate every time.

For more procedural generation we may create a random map based off of a random seed that is passed in as an attribute.

Deformable Human Models

Human character 3D models in modern games can be customized through the use of sliders in character creation. The same method is used to create a diverse population of people within the game. Cloak can be used to aid in the caching of the first and last steps in this process.

We start with a basic handler that reads in the base 3D models, and caches them (relieving a lot of reloading stress already). A second HumanHandler is used which uses getEntityCopy to retrieve a copy of the base model, and then deforms it based on the attributes before returning it. Now we have the base model cached, as well as the final result.


Sign in to add a comment
Powered by Google Project Hosting