Game Development Community

Database Structure

by Demolishun · in Torque Game Engine · 08/24/2007 (7:50 pm) · 1 replies

I am looking into structuring my database for keeping information about missions. That is fine and dandy, but I am thinking about how to allow a second database applied after the first to change all default data the first one provides. It would be akin to a save game file, but it allows the player to "edit" the game.

Should I figure out how to merge my databases before each mission? I am using sqlite so I could create a memory database.
Should I create all the objects and then apply the second databases information? In this case a dead object that has been removed would have to be removed after initially added.

My goal is to allow for editing of most aspects of the game as far as objects placed in the mission. I want the player to be able to change their world and save the changes to a database. My chosen database is sqlite as this seems very efficient for a single file database.

Here is my basic plan:
1. create master database with all default objects and all code functions stored in this database as well, objects will be created dynamically rather than stored in disparate source files
2. allow loading of other databases to provide data for changed objects in missions and new missions
3. rectify differences before a mission is "run" and display the result

Anybody with any experience in doing this with a better structure?

About the author

I love programming, I love programming things that go click, whirr, boom. For organized T3D Links visit: http://demolishun.com/?page_id=67


#1
08/25/2007 (11:50 am)
Oh, and here is how I intend to inject objects into the database:
#ifndef _DYNMEMSTREAM_H_

#define _DYNMEMSTREAM_H_

#ifndef _MEMSTREAM_H_

#include "core/memstream.h"

#endif

class DynMemStream : public MemStream {

	typedef MemStream Parent;

	

	protected:

		U32 m_blockSize;	///< Chunk size for new allocations

		U32 m_writSize;	///< Bytes we have written

	

	public:

		DynMemStream(const U32  in_blockSize,

						const bool in_allowRead  = true,

						const bool in_allowWrite = true);

		~DynMemStream();



		U32 getStreamSize() {return m_writSize;}

		char *getData() {return (char*)m_pBufferBase;}

	protected:

		bool _write(const U32 in_numBytes, const void* in_pBuffer);

};

#endif //_DYNMEMSTREAM_H_

//Includes
#include "platform/platform.h"

#include "streams/dynamicMemStream.h"

DynMemStream::DynMemStream(const U32 in_blockSize,

                     const bool   in_allowRead,

                     const bool   in_allowWrite)

{

   m_blockSize = in_blockSize;

   m_instCaps = 0;

   m_currentPosition = 0;

   m_writSize = 0;

   AssertFatal(in_blockSize > 0,  "Invalid block size");

   AssertFatal(in_allowRead || in_allowWrite, "Either write or read must be allowed");



   char *data = (char*)dMalloc(in_blockSize);

   cm_bufferSize = in_blockSize;

   m_pBufferBase = (void*)data;



   if (in_allowRead)

      m_instCaps |= Stream::StreamRead;

   if (in_allowWrite)

      m_instCaps |= Stream::StreamWrite;



   setStatus(Ok);

}



DynMemStream::~DynMemStream()

{

	dFree(m_pBufferBase);

}



bool DynMemStream::_write(const U32 in_numBytes, const void *in_pBuffer)

{

   AssertFatal(getStatus() != Closed, "Attempted write to a closed stream");



   if (in_numBytes == 0)

      return true;



   AssertFatal(in_pBuffer != NULL, "Invalid input buffer");



   if (hasCapability(StreamWrite) == false) {

      AssertWarn(0, "Writing is disallowed on this stream");

      setStatus(IllegalCall);

      return false;

   }



   bool success     = true;

   if ((m_currentPosition + in_numBytes) > cm_bufferSize) {

      // TODO: could be a bit more accurate...

      U32 newSize = (((m_currentPosition + in_numBytes) - cm_bufferSize) / m_blockSize)+1;

      newSize *= m_blockSize;

      m_pBufferBase = dRealloc((char*)m_pBufferBase, newSize);

      AssertFatal(m_pBufferBase, "Failed to reallocate buffer!");

      cm_bufferSize = newSize;

   }



   // Obtain a current pointer, and do the copy

   void* pCurrent = (void*)((char*)m_pBufferBase + m_currentPosition);

   dMemcpy(pCurrent, in_pBuffer, in_numBytes);



   // Advance the stream position

   m_currentPosition += in_numBytes;

   if (m_currentPosition > m_writSize)

      m_writSize += in_numBytes;



   if (m_currentPosition == cm_bufferSize)

      setStatus(EOS);

   else

      setStatus(Ok);



   return success;

}
This is a class I think James wrote to create dynamic streams.

Now the code that uses this:
// save to string
ConsoleMethod(SimObject, saveToString, const char*, 2, 3, "obj.save(<selectedOnly>)")
{
	static const char *beginMessage = "//--- OBJECT WRITE BEGIN ---";
	8static const char *endMessage = "//--- OBJECT WRITE END ---";
	

        DynMemStream stream(4096, false, true);
	
	// check for flags <selected, ...>
	U32 writeFlags = 0;
	if(argc > 3)
	{
		if(dAtob(argv[3]))
			writeFlags |= SimObject::SelectedOnly;
	}
	
	// Write the begin message
	stream.write(dStrlen(beginMessage), beginMessage);
	stream.write(2, "\r\n");
	
	// Write the object's serialization into the stream
	object->write(stream, 0, writeFlags);
	
	// Write the end message
	stream.write(dStrlen(endMessage), endMessage);
	stream.write(2, "\r\n");

        char *ret = Con::getReturnBuffer(stream.getStreamSize() + 1);

        dStrncpy(ret, stream.getData(), stream.getStreamSize());

        ret[stream.getStreamSize()] = 0;

        return ret;
   
}
// end save to string
This will save an object(s) to a string rather than a file. This was written by someone else as well. I updated these to work with 1.5 engine.

The DynMemStream now uses char instead of U8 because of UTF8 errors cropping up. The saveToString function now uses the DynMemSteam function instead of MemStream.