Trading must be fun

1 comments

Yesterday I was discussing Ragnarok Online trading system with a friend of mine and some interesting thoughts were found.

What I was thinking about - there is no fun involved with trading in RO, therefore it is not considered as a part of gameplay, rather a trivial function.
The trading profession in RO called "merchant" has only that Vending skill involved in trading, which allows players to sell items to other players in automated mode - merchant chooses which items he want to sell, puts them on shop and after that his character considered "on vend" - he is not allowed to move or do anything and he has that sign over his head - the shop name. Any person at place would click the sign, view the merchandise and maybe buy something. The actual "buy" happens automatically - the person takes the item from list and his money amount is automatically decreased by item price. Also in RO the merchant must stay in a game for days, weeks or even months - otherwise if he logs off the shop automatically closes and therefore he wouldn't sell anything. On private servers this feature is overriden by helper function @autotrade which allows players to log off and still have their merchants "on vend". However, the merchant profession becomes not enjoying and the two reasons why one would like to have a merchant are: to advance to next profession which gives more possibilites in PvM or PvP, though they are not so interesting in trading too.
The second reason is simply you would like to have a method of selling your items automatically even without your presence. That is not a bad point, but is it all about trading?
What I was thinking back there, there is almost no continuous learning thing in RO trading system, it burns-out really quick.
Like when you create your first-time merchant and start trading you have absolutely no idea of the supply and demand on market. After playing some time you'll discover that some loot is actually valued by some professions and you'll start collecting it. As the second step you would like to examine the market to find out the prices for the loot you were collecting so you can sell it without overcharging or discounting much. That's where it ends, after this the last learning step will be acquiring the list of the "demanded" stuff and their prices. In RO this list is very short. At this time the merchant profession ends to be interesting in terms of gameplay and serves only as a tool for selling items, which is the case why often players in RO create secondary "merchant" character, but not a primary one.

The things could be improved by some points, some thoughts on this:
- Setting trading level caps: each town has own trading cap, like Alberta is a 10 - 20 lvl trading zone, Al De Baran is a 35-50 lvl trading zone and so on. Trading cap means that you are not allowed to perform any activities which involve trading, though you can buy stuff from vendors. Also, the stuff sold is divided between the zones, so the player always has to examine the market.
- Providing useful tools for market analyzing etc.
- Advancing further: creating industry. Starting own business, hiring workers, managing finances, playing on stock market - each one of these is a broad field to play with.
- Socialize: stimulate player-on-player social trade play - negotiating the prices and other stuff

Resource files management IV

0 comments

Previous posts:
Resource files management. Part I
Resource files management. Part II
Resource files management. Part III

Okay, so far we've been writing helper classes and now we're getting to the main stuff: ResourceFile class.
ResourceFile is pretty simple, let's take a look over its interface:

class ResourceFile
{
protected:
    ResFileHeader mFileHeader;       // Resource file header structure
    std::vector<Lump*> mLumpList;    // List holding every lump
                                     // of the resource file

    std::string mFileName;           // Local storage of the
                                     // resource file's name
    OpenMode mFileMode;              // Current file access mode
    std::fstream mFileStream;        // Primary stream for reading data
    std::fstream mSaveStream;        // Helper stream for saving data

    bool LoadLumpList();             // Loads each lump (info) from file
    bool SaveLumpList();             // Saves each lump (info) to file
    void UnloadLumpList();           // Clears mLumpList
    bool SaveLumps();                // Saves each lump (data) to file

public:
    ResourceFile() {}
    ResourceFile(const std::string& FileName, OpenMode FileMode)
        { this->Open(FileName, FileMode); }
    ~ResourceFile();

    // Open existing resource file and read data or create new file
    bool Open (const std::string& FileName, OpenMode FileMode);

    // Save data to resource file
    bool Save (const std::string& AltFileName = std::string());

    // Close resource file and clear data from memory
    void Close();

    // Add new lump to resource file from external file
    bool CreateLumpFromFile (const std::string& Name,
                             const std::string& FileName);

    // Add new lump to resource file using data from memory
    bool CreateLump (const std::string& Name, void* Data = NULL);

    // Delete selected lump from resource file
    bool DeleteLump (const std::string& Name);

    // Load data for selected lump
    bool LoadLump   (const std::string& Name);

    // Unload data for selected lump
    bool UnloadLump (const std::string& Name);

    // Accessor functions
    std::vector<Lump*>* GetLumpList();
    Lump* GetLump(const std::string& Name);
};


That's it - simple resource file class. Now you can take it and use in your projects, though you may want to tweak it more - add compression, encryption, internal directory hierarchies and so on, there is much to do more.
But now I'll just stop on this simpliest implementation of resource file. There is several things I would like to mention.
First of all as you may notice resource file reads or writes lumps in two separate passes. Let's look at opening resource file logic:
At the beginning ResourceFile checks if we want to open existing file or create a new file. If we are creating new file, then it just opens a new file for writing, but if we want to open an existing file, then the ResourceFile class opens that file and starts reading useful information from it.
First it reads mFileHeader, from which it gets information about the file, more specifically how much lumps are stored in file and where is the lump information list. Having this information the ResourceFile class starts creating N lumps according to information stored in ResFileHeader and reads information of each lump from lump information list in file.
After performing these operations we are having something like this, assuming there are two lumps in a file:

ResourceFile -
    mLumpList[0] -
        mLumpInfo -
            Size     = 215
            Position = 25
            Name     = "Grass Texture"
        mData = NULL
    mLumpList[1] -
        mLumpInfo -
            Size     = 198
            Position = 240
            Name     = "Sand Texture"
        mData = NULL


As you can see, after having this information we can dynamically load the data for any lump we want in our lump list, we just specify the name of the texture, ResourceFile finds it in a file according to the stored information and loads its data to the memory. This is how LoadLump works.

Now the second thing is about saving files. The saving procedure is implemented this way:

1. Write mFileHeader
2. Write all lumps data
3. Get current position in file and update mFileHeader.LumpListOffset
4. Write all lumps information
5. Return to the beginning of the file and rewrite mFileHeader


Here you can see that we need to write resource file header twice. Well that is because I'm pretty lazy now for writing better methods. The reason we need to write file header twice is because ResourceFile doesn't knows where is the lump info list is located in file before it actually writes it. Therefore when we get to the position where the lump info list will be written, we update the mFileHeader with this information and proceed. After all information is written we get back to the beginning of the file and rewrite the header. Of course this can be battled if you write updating of LumpListOffset each time you are adding or removing lump to mLumpList, i.e. add lump's size to the LumpListOffset if you're adding a lump or subtract the size if you're removing it. Also, if you're creating a new file you'll need to set the initial size of LumpListOffset to be equal to size of the ResFileHeader, because the LumpListOffset is equal to ResFileHeader size + sum of each lump size. This is not needed when you're reading existing file as initial value of LumpListOffset is loaded from file.

Now I must admit that the previous saving sequence is not real. The full sequence looks like:

1. Create temporary file
2. Write mFileHeader
3. Write all lumps data:

    for each lump
    if lump's data loaded in memory
    3.1 Write data from memory to file
    else
    3.2 Copy data from existing file to temporary file

4. Get current position in file and update mFileHeader.LumpListOffset
5. Write all lumps information
6. Return to the beginning of the file and rewrite mFileHeader
7. Close both files
8. Remove original file if we're overwriting it or remove alternative-name file (Save As)
9. Rename the temporary file to original or alternate name


As you can see, there is much going at the background. Though, this is not the best solution (imagine doubling 8 GB resource file), but it is easier to implement.

Phew, that was long one.
What to do next? You can go further and create GUI editor of resource files. Well, my friend made one already, it is Qt based.

Source included:
ResourceFile.h
ResourceFile.cpp

Please, feel free to contact me if you're having problems compiling this source.

Resource files management III

0 comments

Resource files management. Part I
Resource files management. Part II

In previous post I wrote about implementation of data structures of the resource file.
Today's topic will be serialization of resource files.

File types

Ok, to retrieve our resources first of all we'll need to define basic methods of reading and writing with files.
In C++ it could be done simply using file streams. Refresh your memory on std::fstream methods if necessary.
Next, the format of file must be chosen. There are two choices to go from here: text/binary format.
When using textual format the data in file is represented as human-readable text.

myresource.dat:
[LumpCount] = 3
[LumpListOffset] = 215
...

(Note: .dat filename extension is not a requirement, you can use anything you like, however you should choose unique extension that is not used by other programs)

Whereas, binary files contain non-readable characters and from human point of view its just a mess.
However, data in binary files is always packed and so it takes less storage than text files. On the other hand, binary files are platform-dependent, i.e. int variable on x64 platform will take twice more than the same int on x86 platform. Text files don't suffer from this issue - text is always the same on any other platform.
And lastly you should know that files you will store in resource file probably would be of binary format, therefore if you are up to using text files you should think about how to write binary data to text file.
That's said I'm sticking up to binary format and below are the simple writing/reading methods of data to/from binary file.

Serialization

According to wikipedia:

Serialization is the process of converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer, or transmitted across a network connection link to be "resurrected" later in the same or another computer environment. When the resulting series of bits is reread according to the serialization format, it can be used to create a semantically identical clone of the original object.

What it says is, serialization is a tecnhique of writing data of objects in program to another source (in our case it is a file), so that it is possible anytime to read back the data stored in file and load it into the objects.
Unfortunately, C++ do not provide native support for serialization. Therefore, we'll need to write own serialization methods to write our data to file.

I've done simple functions of writing/reading basic types and std::strings with a file, you may write additional functions that suite yourself (std::vector etc.)
Here is the source: Serialization.h
The code is pretty self-explanatory, though if you have questions, just ask. Next, I'll write about how to use this serialization stuff.

Okay, last time we discussed the data structures of resource file and I've dropped functionality of Lump and ResourceFile classes on purpose for the sake of clarity. Now, I'll define methods of Lump class (ResourceFile is left for the next post).

Lump interface is pretty simple:

class Lump
{
protected:
    LumpInfo  mLumpInfo;
    void*     mData;

public:
    Lump();
    ~Lump();

    bool LoadData (std::fstream&amp;);        // Read data from file
    bool SaveData (std::fstream&amp;);        // Write data to file
    void UnloadData();                    // Clear data

    void LoadInfo (std::fstream&amp;);        // Read information from file
    void SaveInfo (std::fstream&amp;);        // Write information to file

    // accessors and mutators
    ...
};


That's all. Lump class implements information reading and writing methods that simply call serialization write() and read() methods for each data member of mLumpInfo:
void Lump::SaveInfo (std::fstream&amp; fs)
{
    Serialization::Binary::Write(fs, mLumpInfo.Size);
    Serialization::Binary::Write(fs, mLumpInfo.Position);
    Serialization::Binary::Write(fs, mLumpInfo.Name);
}

void Lump::LoadInfo (std::fstream&amp; fs)
{
    Serialization::Binary::Read(fs, mLumpInfo.Size);
    Serialization::Binary::Read(fs, mLumpInfo.Position);
    Serialization::Binary::Read(fs, mLumpInfo.Name);
}


Also Lump can dynamically load and unload lump content from file to memory. Here is the full source:
Lump.h
Lump.cpp

Feel free to ask any questions you have. Next time I'll write about ResourceFile class.

Resource files management. Part IV

Resource files management II

0 comments

Resource files management. Part I

Last time I've described resource files and their data structure. Let's continue with management of resource files.

Lump and ResourceFile objects
Resource files are implemented as objects and may be included in other programs to utilize resource files. I'm using C++, though the provided concept may be written in any other programming language.

In the previous post we have learned that resource file consists of header, lump's data and lump's information. Therefore, we'll need two classes: Lump and ResourceFile and two additional data structures: ResFileHeader, LumpInfo:

// Structure defining the header of our resource files.
struct ResFileHeader
{
    unsigned long Version;                  // Resource file version
    unsigned long LumpCount;                // Number of lumps in res.file
    unsigned long LumpListOffset;           // Position of the first

                                            // Lump info list element
};


// Structure of each element in the lump info list.
// Stores information which can be used to retrieve the contents of a lump.
struct LumpInfo
{
    unsigned long Size;                     // Size of the lump, in bytes
    unsigned long Position;                 // Position in the res.file
    std::string   Name;                     // Name of the lump
};


// Class representing single resource stored in a resource file.
// Stores lump information and lump contents
class Lump
{
protected:
    LumpInfo mLumpInfo;                     // Our lump information
    void*    mData;                         // Lump's contents
...
};


// Class representing single resource file.
// Stores resource file header and list of lumps
class ResourceFile
{
protected:
    ResFileHeader mFileHeader;
    std::vector<Lump*> mLumpList;
...
};


That's it. We'll discuss functionality of two classes later, right now we know the structure of our resource file. Once again, from abstract view the structure of resource file looks like:
[Resource File]
-   [ResFileHeader]
-   [Lump]
    -   [LumpInfo]
    -   [Data]
-   [Lump]
    -   [LumpInfo]
    -   [Data]
    ...


whereas in practice LumpInfo is separated from Lump:
[Resource File]
-   [ResFileHeader]
-   [Lump]
    -   [Data]
-   [Lump]
    -   [Data]
    ...    
-   [LumpInfo]
-   [LumpInfo]
    ...


Next time I'll write about basic functionality that Lump and ResourceFile implement.

Resource file management. Part III
Resource file management. Part IV

Resource files management

0 comments


First of all, a little bit of theory before I present actual implementation. Let's find out what is a resource file and why we would want to use them.

Resource files
Ok, we all know that video games consist of various components like sound effects, music, 3d models, textures, fonts, scripts etc. Each piece of these is a File.
When you build your game, you add these components, file by file and when the game is built you end up with a whole bunch of files of variable types. Of course, if you make a little planning, you would sort these files into categories by creating subfolders and it could look like:

C:\MyGame\
C:\MyGame\mygame.exe
C:\MyGame\Music\music.ogg
C:\MyGame\Music\music2.ogg
C:\MyGame\Textures\brickwall.png
...


However, you may notice that it is hard to maintain, because you need to know filenames and paths for each object you would like to load into game. It would be way better if we could reduce the amount of files. And we can!
Let's think about it in more common terms. Imagine yourself an employee working at the office. Your job involves signing documents. And here is the question, where do you store your papers?

  1. You could just throw them all over the desk.
  2. You could store them in your drawer sorted in paper-cases.
  3. All documents could be stored in a storage-room with an additional manager looking over it, whose duty is to keep these documents safe and bring documents on demand.
The first two cases means you will need to know about particular location of specific document, so in second case you will need to know in which drawer and paper-case you should look for a document, whereas first case implies that you will need to find the document in the pile of papers and the only clue is the name of a document.
And the last case is a close representation of what we would like to have in our game. All documents (files) are stored in a single storage-room (container) with an additional manager (program) looking over it. A container is a single file, called package, archive, resource file.
The simpliest example I could think of is ZIP archive. ZIP file may contain numerous files inside as well as it may compress them so the container's size will be smaller. Also, you could set the password for a ZIP archive to ensure that nobody except you has access to that archive.

Resource file structure
So, what makes file a resource file? Generally, resource file consists of system information, called header and files that we packed, called data lumps.
Header stores information used by program to perform manipulations with resource file, like retrieving, writing or deleting data. Also it may contain other information like resource file version, additional flags, checksum, etc.
Lumps are data structures which consist of stored data, i.e. file, and corresponding information like unique name of lump, position of data in the resource file, size of data. The information about lumps can be stored apart from data, so the manager program can read the information about the lumps in the resource file. Overall resource file structure looks like:

[HEADER]

// Lump data list
[Data of Lump 1]
[Data of Lump 2]
[Data of Lump 3]
...
[Data of Lump N]

// Lump information list
[Information of Lump 1]
[Information of Lump 2]
[Information of Lump 3]
...
[Information of Lump N]


Notice that information list comes after the data list, this is needed for a simplier management of resource file as you will see later.

Header consists of two basic fields: [Lump Count] and [Lump Information Offset].
The [Lump Count] field tells us how many lumps this resource file has and the [Lump Information offset] field contains position of the beginning of lump information list. This information is crucial for our manager that works with resource files, all other information may be considered optional and may be included or not in the header.

Lump information record consists of three basic fields: [Lump Name], [Lump Offset] and [Lump Size].
[Lump Name] is an unique name which is used by manager to find lumps.
[Lump Offset] is a position of lump data and [Lump Size] is a size of data in bytes.

Well, that's all about the resource file. Coming up next: resource file manager and complete realization of simple resource file management in C++

Resource files management. Part II
Resource files management. Part III
Resource files management. Part IV

Outer links: http://www.gamedev.net/reference/articles/article902.asp