[Opentnl-cvs] tnl/tnl Makefile,NONE,1.1 assert.cpp,NONE,1.1 asymmetricKey.cpp,NONE,1.1 bitStream.cpp
Brought to you by:
mark_frohnmayer,
s_alanet
Update of /cvsroot/opentnl/tnl/tnl In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31063/tnl Added Files: Makefile assert.cpp asymmetricKey.cpp bitStream.cpp byteBuffer.cpp certificate.cpp clientPuzzle.cpp connectionStringTable.cpp dataChunker.cpp eventConnection.cpp ghostConnection.cpp huffmanStringProcessor.cpp log.cpp netBase.cpp netConnection.cpp netInterface.cpp netObject.cpp netStringTable.cpp platform.cpp random.cpp rpc.cpp symmetricCipher.cpp tnl.changelog.txt tnl.dsp tnl.h tnl.overview.txt tnl.suo tnl.vcproj tnlAssert.h tnlAsymmetricKey.h tnlBitSet.h tnlBitStream.h tnlByteBuffer.h tnlCertificate.h tnlClientPuzzle.h tnlConnectionStringTable.h tnlDataChunker.h tnlEndian.h tnlEventConnection.h tnlGhostConnection.h tnlHuffmanStringProcessor.h tnlLog.h tnlNetBase.h tnlNetConnection.h tnlNetEvent.h tnlNetInterface.h tnlNetObject.h tnlNetStringTable.h tnlNonce.h tnlPlatform.h tnlRPC.h tnlRandom.h tnlSymmetricCipher.h tnlTypes.h tnlUDP.h tnlVector.h udp.cpp vector.cpp Log Message: Initial commit. --- NEW FILE: tnlBitSet.h --- //----------------------------------------------------------------------------------- // // Torque Network Library // Copyright (C) 2004 GarageGames.com, Inc. // For more information see http://www.opentnl.org // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // For use in products that are not compatible with the terms of the GNU // General Public License, alternative licensing options are available // from GarageGames.com. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //------------------------------------------------------------------------------------ #ifndef _TNL_BITSET_H_ #define _TNL_BITSET_H_ //Includes #ifndef _TNL_TYPES_H_ #include "tnlTypes.h" #endif namespace TNL { /// Represents a word of bits. /// /// A helper class to perform operations on a set of 32 bits. class BitSet32 { private: U32 mBits; public: /// Default constructor initializes this bit set to all zeros. BitSet32() { mBits = 0; } /// Copy constructor. BitSet32(const BitSet32& in_rCopy) { mBits = in_rCopy.mBits; } /// Construct from an input U32. BitSet32(const U32 in_mask) { mBits = in_mask; } /// @name Accessors /// @{ /// Returns the U32 representation of the bit set. operator U32() const { return mBits; } /// Returns the U32 representation of the bit set. U32 getMask() const { return mBits; } /// @} /// @name Mutators /// /// Most of these methods take a word (ie, a BitSet32) of bits /// to operate with. /// @{ /// Sets all the bits in the bit set to 1 void set() { mBits = 0xFFFFFFFFUL; } /// Sets all the bits in the bit set that are set in m. void set(const U32 m) { mBits |= m; } /// For each bit set in s, sets or clears that bit in this, depending on whether b is true or false. void set(BitSet32 s, bool b) { mBits = (mBits&~(s.mBits))|(b?s.mBits:0); } /// Clears all the bits in the bit set to 0. void clear() { mBits = 0; } /// Clears all the bits in the bit set that are set in m. void clear(const U32 m) { mBits &= ~m; } /// Flips all the bits in the bit set that are set in m. void toggle(const U32 m) { mBits ^= m; } /// Test if the passed bits are set. bool test(const U32 m) const { return (mBits & m) != 0; } /// Test if the passed bits and only the passed bits are set. bool testStrict(const U32 m) const { return (mBits & m) == m; } BitSet32& operator =(const U32 m) { mBits = m; return *this; } BitSet32& operator|=(const U32 m) { mBits |= m; return *this; } BitSet32& operator&=(const U32 m) { mBits &= m; return *this; } BitSet32& operator^=(const U32 m) { mBits ^= m; return *this; } BitSet32 operator|(const U32 m) const { return BitSet32(mBits | m); } BitSet32 operator&(const U32 m) const { return BitSet32(mBits & m); } BitSet32 operator^(const U32 m) const { return BitSet32(mBits ^ m); } /// @} }; }; #endif //_TNL_BITSET_H_ --- NEW FILE: tnlClientPuzzle.h --- //----------------------------------------------------------------------------------- // // Torque Network Library // Copyright (C) 2004 GarageGames.com, Inc. // For more information see http://www.opentnl.org // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // For use in products that are not compatible with the terms of the GNU // General Public License, alternative licensing options are available // from GarageGames.com. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //------------------------------------------------------------------------------------ #ifndef _TNL_CLIENTPUZZLE_H_ #define _TNL_CLIENTPUZZLE_H_ #include "tnlDataChunker.h" #include "tnlNonce.h" // JMQ: work around X.h header file #if defined(TNL_OS_LINUX) && defined(Success) #undef Success #endif namespace TNL { /// The ClientPuzzleManager class issues, solves and validates client /// puzzles for connection authentication. class ClientPuzzleManager { public: private: /// NonceTable manages the list of client nonces for which clients /// have constructed valid puzzle solutions for the current server /// nonce. There are 2 nonce tables in the ClientPuzzleManager - /// one for the current nonce and one for the previous nonce. class NonceTable { private: struct Entry { Nonce mNonce; Entry *mHashNext; }; enum { MinHashTableSize = 127, MaxHashTableSize = 387, }; Entry **mHashTable; U32 mHashTableSize; DataChunker mChunker; public: /// NonceTable constructor NonceTable() { reset(); } /// Resets and clears the nonce table void reset(); /// checks if the given nonce is already in the table and adds it /// if it is not. Returns true if the nonce was not in the table /// when the function was called. bool checkAdd(Nonce &theNonce); }; enum { PuzzleRefreshTime = 30000, ///< Refresh the server puzzle every 30 seconds }; U32 mCurrentDifficulty; U32 mLastUpdateTime; U32 mLastTickTime; Nonce mCurrentNonce; Nonce mLastNonce; NonceTable *mCurrentNonceTable; NonceTable *mLastNonceTable; static bool checkOneSolution(U32 solution, Nonce &clientNonce, Nonce &serverNonce, U32 puzzleDifficulty, U32 clientIdentity); public: ClientPuzzleManager(); ~ClientPuzzleManager(); /// Checks to see if a new nonce needs to be created, and if so /// generates one and tosses out the current list of accepted nonces void tick(U32 currentTime); /// Error codes that can be returned by checkSolution enum ErrorCode { Success, InvalidSolution, InvalidServerNonce, InvalidClientNonce, InvalidPuzzleDifficulty, ErrorCodeCount, }; /// Difficulty levels of puzzles enum { InitialPuzzleDifficulty = 17, ///< Initial puzzle difficulty is set so clients do approx 2-3x the shared secret generation of the server MaxPuzzleDifficulty = 26, ///< Maximum puzzle diffuclty is approx 1 minute to solve on ~2004 hardware. MaxSolutionComputeFragment = 30, ///< Number of milliseconds spent computing a solution per call to solvePuzzle. }; /// Checks a puzzle solution submitted by a client to see if it is a valid solution for the current or previous puzzle nonces ErrorCode checkSolution(U32 solution, Nonce &clientNonce, Nonce &serverNonce, U32 puzzleDifficulty, U32 clientIdentity); /// Computes a puzzle solution value for the given puzzle difficulty and server nonce. If the execution time of this function /// exceeds 30 milliseconds, it will return the current trail solution in the solution variable and a return value of false. static bool solvePuzzle(U32 *solution, Nonce &clientNonce, Nonce &serverNonce, U32 puzzleDifficulty, U32 clientIdentity); /// Returns the current server nonce Nonce getCurrentNonce() { return mCurrentNonce; } /// Returns the current client puzzle difficulty U32 getCurrentDifficulty() { return mCurrentDifficulty; } }; }; #endif --- NEW FILE: tnl.suo --- (This appears to be a binary file; contents omitted.) --- NEW FILE: tnlBitStream.h --- //----------------------------------------------------------------------------------- // // Torque Network Library // Copyright (C) 2004 GarageGames.com, Inc. // For more information see http://www.opentnl.org // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // For use in products that are not compatible with the terms of the GNU // General Public License, alternative licensing options are available // from GarageGames.com. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //------------------------------------------------------------------------------------ #ifndef _TNL_BITSTREAM_H_ #define _TNL_BITSTREAM_H_ #ifndef _TNL_BYTEBUFFER_H_ #include "tnlByteBuffer.h" #endif namespace TNL { class SymmetricCipher; /// Point3F is used by BitStream for transmitting 3D points and vectors. /// /// Users of the TNL should provide conversion operators to and from /// this TNL-native type. struct Point3F { F32 x; ///< the X coordinate F32 y; ///< the Y coordinate F32 z; ///< the Z coordinate }; /// Helper macro used in BitStream declaration. /// /// @note DeclareTemplatizedReadWrite macro declares a read and write function /// for the specified type. This is a macro because MSVC6 has seriously /// broken template functionality. Thanks MS! #define DeclareTemplatizedReadWrite(T) \ inline bool write(T value) { T temp = convertHostToLEndian(value); return write(sizeof(T), &temp); } \ inline bool read(T *value) { T temp; bool success = read(sizeof(T), &temp); *value = convertLEndianToHost(temp); return success;} /// BitStream provides a bit-level stream interface to a data buffer. class BitStream : public ByteBuffer { protected: enum { ResizePad = 1500, }; U32 bitNum; ///< The current bit position for reading/writing in the bit stream. bool error; ///< Flag set if a user operation attempts to read or write past the max read/write sizes. bool mCompressRelative; ///< Flag set if the bit stream should compress points relative to a compression point. Point3F mCompressPoint; ///< Reference point for relative point compression. U32 maxReadBitNum; ///< Last valid read bit position. U32 maxWriteBitNum; ///< Last valid write bit position. /// String buffer holds the last string written into the stream for substring compression. char mStringBuffer[256]; bool resizeBits(U32 numBitsNeeded); public: /// @name Constructors /// /// Note that the BitStream essentially wraps an existing buffer, so to use a bitstream you must /// have an existing buffer for it to work with! /// /// @{ /// Default to maximum write size being the size of the buffer. BitStream(U8 *bufPtr, U32 bufSize) : ByteBuffer(bufPtr, bufSize) { setMaxSizes(bufSize, bufSize); reset(); } /// Optionally, specify a maximum write size. BitStream(U8 *bufPtr, U32 bufSize, U32 maxWriteSize) : ByteBuffer(bufPtr, bufSize) { setMaxSizes(bufSize, maxWriteSize); reset(); } /// Creates a resizable BitStream BitStream() : ByteBuffer() { setMaxSizes( getBufferSize(), getBufferSize() ); reset(); } /// @} /// Sets the maximum read and write sizes for the BitStream. void setMaxSizes(U32 maxReadSize, U32 maxWriteSize = 0); /// resets the read/write position to 0 and clears any error state. void reset(); /// Returns a pointer to the next byte in the BitStream from the current bit position U8* getBytePtr(); /// Returns the current position in the stream rounded up to the next byte. U32 getBytePosition() const; /// Returns the current bit position in the stream U32 getBitPosition() const; /// Sets the position in the stream to the first bit of byte newPosition. void setBytePosition(const U32 newPosition); /// Sets the position in the stream to newBitPosition. void setBitPosition(const U32 newBitPosition); /// Advances the position in the stream by numBits. void advanceBitPosition(const S32 numBits); /// Writes an unsigned integer value between 0 and 2^(bitCount - 1) into the stream. void writeInt(U32 value, U8 bitCount); /// Reads an unsigned integer value between 0 and 2^(bitCount - 1) from the stream. U32 readInt(U8 bitCount); /// Writes an unsigned integer value between 0 and 2^(bitCount -1) into the stream at the specified position, without changing the current write position. void writeIntAt(U32 value, U8 bitCount, U32 bitPosition); /// Writes a signed integer value between -2^(bitCount-1) and 2^(bitCount-1) - 1. void writeSignedInt(S32 value, U8 bitCount); /// Reads a signed integer value between -2^(bitCount-1) and 2^(bitCount-1) - 1. S32 readSignedInt(U8 bitCount); /// Writes an unsigned integer value in the range rangeStart to rangeEnd inclusive. void writeRangedU32(U32 value, U32 rangeStart, U32 rangeEnd); /// Reads an unsigned integer value in the range rangeStart to rangeEnd inclusive. U32 readRangedU32(U32 rangeStart, U32 rangeEnd); /// Writes a float from 0 to 1 inclusive, using bitCount bits of precision. void writeFloat(F32 f, U8 bitCount); /// Reads a float from 0 to 1 inclusive, using bitCount bits of precision. F32 readFloat(U8 bitCount); /// Writes a signed float from -1 to 1 inclusive, using bitCount bits of precision. void writeSignedFloat(F32 f, U8 bitCount); /// Reads a signed float from -1 to 1 inclusive, using bitCount bits of precision. F32 readSignedFloat(U8 bitCount); /// Writes an object's class ID, given its class type and class group. void writeClassId(U32 classId, U32 classType, U32 classGroup); /// Reads a class ID for an object, given a class type and class group. Returns -1 if the class type is out of range U32 readClassId(U32 classType, U32 classGroup); /// Writes a normalized vector into the stream, using bitCount bits for the precision of angles phi and theta. void writeNormalVector(const Point3F &vec, U8 bitCount); /// Reads a normalized vector from the stream, using bitCount bits for the precision of angles phi and theta. void readNormalVector(Point3F *vec, U8 bitCount); /// Uses the same method as in writeNormalVector to reduce the precision of a normal vector /// to determine what will be read from the stream. static Point3F dumbDownNormal(const Point3F& vec, U8 bitCount); /// Writes a normalized vector by writing a z value and theta angle. void writeNormalVector(const Point3F& vec, U8 angleBitCount, U8 zBitCount); /// Reads a normalized vector by reading a z value and theta angle. void readNormalVector(Point3F *vec, U8 angleBitCount, U8 zBitCount); /// Sets a reference point for subsequent compressed point writing. void setPointCompression(const Point3F &p); /// Disables compression of point. void clearPointCompression(); /// Writes a point into the stream, to a precision denoted by scale. void writePointCompressed(const Point3F &p, F32 scale); /// Reads a compressed point from the stream, to a precision denoted by scale. void readPointCompressed(Point3F *p, F32 scale); /// Writes bitCount bits into the stream from bitPtr. bool writeBits(U32 bitCount, const void *bitPtr); /// Reads bitCount bits from the stream into bitPtr. bool readBits(U32 bitCount, void *bitPtr); /// Writes a ByteBuffer into the stream. The ByteBuffer can be no larger than 1024 bytes in size. bool write(const ByteBuffer *theBuffer); /// Reads a ByteBuffer in from the stream. bool read(ByteBuffer *theBuffer); /// Writes a single boolean flag (bit) into the stream, and returns the boolean that was written. /// /// This is set up so you can do... /// /// @code /// if(stream->writeFlag(foo == bar)) /// { /// ... write other stuff ... /// } /// @endcode bool writeFlag(bool val); /// Reads a single bit from the stream. /// /// This is set up so you can do... /// /// @code /// if(stream->readFlag()) /// { /// ... read other stuff ... /// } /// @endcode bool readFlag(); /// Writes a huffman compressed string into the stream. void writeString(const char *stringBuf, U8 maxLen=255); /// Reads a huffman compressed string from the stream. void readString(char stringBuf[256]); /// Writes byte data into the stream. bool write(const U32 in_numBytes, const void* in_pBuffer); /// Reads byte data from the stream. bool read(const U32 in_numBytes, void* out_pBuffer); /// @name Various types that the BitStream can read and write... /// @{ /// DeclareTemplatizedReadWrite(U8); DeclareTemplatizedReadWrite(S8); DeclareTemplatizedReadWrite(U16); DeclareTemplatizedReadWrite(S16); DeclareTemplatizedReadWrite(U32); DeclareTemplatizedReadWrite(S32); DeclareTemplatizedReadWrite(F32); DeclareTemplatizedReadWrite(F64); /// @} /// Sets the bit at position bitCount to the value of set bool setBit(U32 bitCount, bool set); /// Tests the value of the bit at position bitCount. bool testBit(U32 bitCount); /// Returns whether the BitStream writing has exceeded the write target size. bool isFull() { return bitNum > (getBufferSize() << 3); } /// Returns whether the stream has generated an error condition due to reading or writing past the end of the buffer. bool isValid() { return !error; } /// Hashes the BitStream, writing the hash digest into the end of the buffer, and then encrypts with the given cipher void hashAndEncrypt(U32 hashDigestSize, U32 encryptStartOffset, SymmetricCipher *theCipher); /// Decrypts the BitStream, then checks the hash digest at the end of the buffer to validate the contents bool decryptAndCheckHash(U32 hashDigestSize, U32 decryptStartOffset, SymmetricCipher *theCipher); }; //------------------------------------------------------------------------------ /// PacketStream provides a network interface to the BitStream for easy construction of data packets. class PacketStream : public BitStream { U8 buffer[MaxPacketDataSize]; ///< internal buffer for packet data, sized to the maximum UDP packet size. public: /// Constructor assigns the internal buffer to the BitStream. PacketStream(U32 targetPacketSize = MaxPacketDataSize) : BitStream(buffer, targetPacketSize, MaxPacketDataSize) {} /// Sends this packet to the specified address through the specified socket. NetError sendto(Socket &outgoingSocket, const Address &theAddress); /// Reads a packet into the stream from the specified socket. NetError recvfrom(Socket &incomingSocket, Address *recvAddress); }; //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ inline U32 BitStream::getBytePosition() const { return (bitNum + 7) >> 3; } inline U32 BitStream::getBitPosition() const { return bitNum; } inline void BitStream::setBytePosition(const U32 newPosition) { bitNum = newPosition << 3; } inline void BitStream::setBitPosition(const U32 newBitPosition) { bitNum = newBitPosition; } inline void BitStream::advanceBitPosition(const S32 numBits) { setBitPosition(getBitPosition() + numBits); } inline bool BitStream::write(const U32 in_numBytes, const void* in_pBuffer) { return writeBits(in_numBytes << 3, in_pBuffer); } inline bool BitStream::read(const U32 in_numBytes, void* out_pBuffer) { return readBits(in_numBytes << 3, out_pBuffer); } inline bool BitStream::readFlag() { if(bitNum > maxReadBitNum) { error = true; TNLAssert(false, "Out of range read"); return false; } S32 mask = 1 << (bitNum & 0x7); bool ret = (*(getBuffer() + (bitNum >> 3)) & mask) != 0; bitNum++; return ret; } inline void BitStream::writeIntAt(U32 value, U8 bitCount, U32 bitPosition) { U32 curPos = getBitPosition(); setBitPosition(bitPosition); writeInt(value, bitCount); setBitPosition(curPos); } inline void BitStream::writeRangedU32(U32 value, U32 rangeStart, U32 rangeEnd) { TNLAssert(value >= rangeStart && value <= rangeEnd, "Out of bounds value!"); TNLAssert(rangeEnd >= rangeStart, "error, end of range less than start"); U32 rangeSize = rangeEnd - rangeStart + 1; U32 rangeBits = getNextBinLog2(rangeSize); writeInt(S32(value - rangeStart), S32(rangeBits)); } inline U32 BitStream::readRangedU32(U32 rangeStart, U32 rangeEnd) { TNLAssert(rangeEnd >= rangeStart, "error, end of range less than start"); U32 rangeSize = rangeEnd - rangeStart + 1; U32 rangeBits = getNextBinLog2(rangeSize); U32 val = U32(readInt(S32(rangeBits))); return val + rangeStart; } }; #endif //_TNL_BITSTREAM_H_ --- NEW FILE: Makefile --- # TNL Makefile # (c) 2003 GarageGames # # This makefile is for gcc-based projects, at the moment. # # Configuration # CC=gcc -g -DTNL_DEBUG -I../libtomcrypt # -O2 OBJECTS=\ assert.o\ asymmetricKey.o\ bitStream.o\ byteBuffer.o\ clientPuzzle.o\ connectionStringTable.o\ dataChunker.o\ eventConnection.o\ ghostConnection.o\ huffmanStringProcessor.o\ log.o\ netBase.o\ netConnection.o\ netInterface.o\ netObject.o\ netStringTable.o\ platform.o\ random.o\ rpc.o\ symmetricCipher.o\ udp.o\ vector.o\ CFLAGS= .cpp.o : $(CC) -c $(CFLAGS) $< default: $(OBJECTS) @echo Building libtnl.a ... ar rcv libtnl.a $(OBJECTS) clean: rm -f $(OBJECTS) libtnl.a --- NEW FILE: eventConnection.cpp --- //----------------------------------------------------------------------------------- // // Torque Network Library // Copyright (C) 2004 GarageGames.com, Inc. // For more information see http://www.opentnl.org // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // For use in products that are not compatible with the terms of the GNU // General Public License, alternative licensing options are available // from GarageGames.com. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //------------------------------------------------------------------------------------ #include "tnl.h" #include "tnlEventConnection.h" #include "tnlBitStream.h" #include "tnlLog.h" #include "tnlNetInterface.h" namespace TNL { ClassChunker<EventConnection::EventNote> EventConnection::mEventNoteChunker; EventConnection::EventConnection() { // event management data: mNotifyEventList = NULL; mSendEventQueueHead = NULL; mSendEventQueueTail = NULL; mUnorderedSendEventQueueHead = NULL; mUnorderedSendEventQueueTail = NULL; mWaitSeqEvents = NULL; mNextSendEventSeq = FirstValidSendEventSeq; mNextRecvEventSeq = FirstValidSendEventSeq; mLastAckedEventSeq = -1; mStringTable = NULL; mEventClassCount = 0; mEventClassBitSize = 0; } EventConnection::~EventConnection() { while(mNotifyEventList) { EventNote *temp = mNotifyEventList; mNotifyEventList = temp->mNextEvent; temp->mEvent->notifyDelivered(this, true); mEventNoteChunker.free(temp); } while(mUnorderedSendEventQueueHead) { EventNote *temp = mUnorderedSendEventQueueHead; mUnorderedSendEventQueueHead = temp->mNextEvent; temp->mEvent->notifyDelivered(this, true); mEventNoteChunker.free(temp); } while(mSendEventQueueHead) { EventNote *temp = mSendEventQueueHead; mSendEventQueueHead = temp->mNextEvent; temp->mEvent->notifyDelivered(this, true); mEventNoteChunker.free(temp); } delete mStringTable; } void EventConnection::writeConnectRequest(BitStream *stream) { Parent::writeConnectRequest(stream); stream->write(NetClassRep::getNetClassCount(getNetClassGroup(), NetClassTypeEvent)); } bool EventConnection::readConnectRequest(BitStream *stream, const char **errorString) { if(!Parent::readConnectRequest(stream, errorString)) return false; U32 classCount; stream->read(&classCount); U32 myCount = NetClassRep::getNetClassCount(getNetClassGroup(), NetClassTypeEvent); if(myCount <= classCount) mEventClassCount = myCount; else { mEventClassCount = classCount; if(!NetClassRep::isVersionBorderCount(getNetClassGroup(), NetClassTypeEvent, mEventClassCount)) return false; } mEventClassBitSize = getNextBinLog2(mEventClassCount); return true; } void EventConnection::writeConnectAccept(BitStream *stream) { Parent::writeConnectAccept(stream); stream->write(mEventClassCount); } bool EventConnection::readConnectAccept(BitStream *stream, const char **errorString) { if(!Parent::readConnectAccept(stream, errorString)) return false; stream->read(&mEventClassCount); if(!NetClassRep::isVersionBorderCount(getNetClassGroup(), NetClassTypeEvent, mEventClassCount)) return false; mEventClassBitSize = getNextBinLog2(mEventClassCount); return true; } void EventConnection::setTranslatesStrings() { if(!mStringTable) mStringTable = new ConnectionStringTable(this); } void EventConnection::packetDropped(PacketNotify *pnotify) { Parent::packetDropped(pnotify); EventPacketNotify *notify = static_cast<EventPacketNotify *>(pnotify); if(mStringTable) mStringTable->packetDropped(¬ify->stringList); EventNote *walk = notify->eventList; EventNote **insertList = &mSendEventQueueHead; EventNote *temp; while(walk) { switch(walk->mEvent->mGuaranteeType) { case NetEvent::GuaranteedOrdered: // It was a guaranteed ordered packet, reinsert it back into // mSendEventQueueHead in the right place (based on seq numbers) TNLLogMessageV(LogEventConnection, ("EventConnection %s: DroppedGuaranteed - %d", getNetAddressString(), walk->mSeqCount)); while(*insertList && (*insertList)->mSeqCount < walk->mSeqCount) insertList = &((*insertList)->mNextEvent); temp = walk->mNextEvent; walk->mNextEvent = *insertList; if(!walk->mNextEvent) mSendEventQueueTail = walk; *insertList = walk; insertList = &(walk->mNextEvent); walk = temp; break; case NetEvent::Guaranteed: // It was a guaranteed packet, put it at the top of // mUnorderedSendEventQueueHead. temp = walk->mNextEvent; walk->mNextEvent = mUnorderedSendEventQueueHead; mUnorderedSendEventQueueHead = walk; if(!walk->mNextEvent) mUnorderedSendEventQueueTail = walk; walk = temp; break; case NetEvent::Unguaranteed: // Or else it was an unguaranteed packet, notify that // it was _not_ delivered and blast it. walk->mEvent->notifyDelivered(this, false); temp = walk->mNextEvent; mEventNoteChunker.free(walk); walk = temp; } } } void EventConnection::packetReceived(PacketNotify *pnotify) { Parent::packetReceived(pnotify); EventPacketNotify *notify = static_cast<EventPacketNotify *>(pnotify); if(mStringTable) mStringTable->packetReceived(¬ify->stringList); EventNote *walk = notify->eventList; EventNote **noteList = &mNotifyEventList; while(walk) { EventNote *next = walk->mNextEvent; if(walk->mEvent->mGuaranteeType != NetEvent::GuaranteedOrdered) { walk->mEvent->notifyDelivered(this, true); mEventNoteChunker.free(walk); walk = next; } else { while(*noteList && (*noteList)->mSeqCount < walk->mSeqCount) noteList = &((*noteList)->mNextEvent); walk->mNextEvent = *noteList; *noteList = walk; noteList = &walk->mNextEvent; walk = next; } } while(mNotifyEventList && mNotifyEventList->mSeqCount == mLastAckedEventSeq + 1) { mLastAckedEventSeq++; EventNote *next = mNotifyEventList->mNextEvent; TNLLogMessageV(LogEventConnection, ("EventConnection %s: NotifyDelivered - %d", getNetAddressString(), mNotifyEventList->mSeqCount)); mNotifyEventList->mEvent->notifyDelivered(this, true); mEventNoteChunker.free(mNotifyEventList); mNotifyEventList = next; } } void EventConnection::writePacket(BitStream *bstream, PacketNotify *pnotify) { Parent::writePacket(bstream, pnotify); EventPacketNotify *notify = static_cast<EventPacketNotify *>(pnotify); if(mConnectionParameters.mDebugObjectSizes) bstream->writeInt(DebugChecksum, 32); EventNote *packQueueHead = NULL, *packQueueTail = NULL; while(mUnorderedSendEventQueueHead) { if(bstream->isFull()) break; // dequeue the first event EventNote *ev = mUnorderedSendEventQueueHead; mUnorderedSendEventQueueHead = ev->mNextEvent; bstream->writeFlag(true); S32 start = bstream->getBitPosition(); if(mConnectionParameters.mDebugObjectSizes) bstream->advanceBitPosition(BitStreamPosBitSize); S32 classId = ev->mEvent->getClassId(getNetClassGroup()); bstream->writeInt(classId, mEventClassBitSize); ev->mEvent->pack(this, bstream); TNLLogMessageV(LogEventConnection, ("EventConnection %s: WroteEvent %s - %d bits", getNetAddressString(), ev->mEvent->getDebugName(), bstream->getBitPosition() - start)); if(mConnectionParameters.mDebugObjectSizes) bstream->writeIntAt(bstream->getBitPosition(), BitStreamPosBitSize, start); // add this event onto the packet queue ev->mNextEvent = NULL; if(!packQueueHead) packQueueHead = ev; else packQueueTail->mNextEvent = ev; packQueueTail = ev; } bstream->writeFlag(false); S32 prevSeq = -2; while(mSendEventQueueHead) { if(bstream->isFull()) break; // if the event window is full, stop processing if(mSendEventQueueHead->mSeqCount > mLastAckedEventSeq + 126) break; // dequeue the first event EventNote *ev = mSendEventQueueHead; mSendEventQueueHead = ev->mNextEvent; //Con::printf("EVT %d: SEND - %d", getId(), ev->mSeqCount); bstream->writeFlag(true); ev->mNextEvent = NULL; if(!packQueueHead) packQueueHead = ev; else packQueueTail->mNextEvent = ev; packQueueTail = ev; if(!bstream->writeFlag(ev->mSeqCount == prevSeq + 1)) bstream->writeInt(ev->mSeqCount, 7); prevSeq = ev->mSeqCount; S32 start = bstream->getBitPosition(); if(mConnectionParameters.mDebugObjectSizes) bstream->advanceBitPosition(BitStreamPosBitSize); S32 classId = ev->mEvent->getClassId(getNetClassGroup()); bstream->writeInt(classId, mEventClassBitSize); ev->mEvent->pack(this, bstream); TNLLogMessageV(LogEventConnection, ("EventConnection %s: WroteEvent %s - %d bits", getNetAddressString(), ev->mEvent->getDebugName(), bstream->getBitPosition() - start)); if(mConnectionParameters.mDebugObjectSizes) bstream->writeIntAt(bstream->getBitPosition(), BitStreamPosBitSize, start); } for(EventNote *ev = packQueueHead; ev; ev = ev->mNextEvent) ev->mEvent->notifySent(this); notify->eventList = packQueueHead; bstream->writeFlag(0); } void EventConnection::readPacket(BitStream *bstream) { Parent::readPacket(bstream); if(mConnectionParameters.mDebugObjectSizes) { U32 sum = bstream->readInt(32); TNLAssert(sum == DebugChecksum, "Invalid checksum."); } S32 prevSeq = -2; EventNote **waitInsert = &mWaitSeqEvents; bool unguaranteedPhase = true; while(true) { bool bit = bstream->readFlag(); if(unguaranteedPhase && !bit) { unguaranteedPhase = false; bit = bstream->readFlag(); } if(!unguaranteedPhase && !bit) break; S32 seq = -1; if(!unguaranteedPhase) // get the sequence { if(bstream->readFlag()) seq = (prevSeq + 1) & 0x7f; else seq = bstream->readInt(7); prevSeq = seq; } U32 endingPosition; if(mConnectionParameters.mDebugObjectSizes) endingPosition = bstream->readInt(BitStreamPosBitSize); U32 classId = bstream->readInt(mEventClassBitSize); if(classId >= mEventClassCount) { setLastError("Invalid packet."); return; } NetEvent *evt = (NetEvent *) Object::create(getNetClassGroup(), NetClassTypeEvent, classId); if(!evt) { setLastError("Invalid packet."); return; } // check if the direction this event moves is a valid direction. if( (evt->getEventDirection() == NetEvent::DirUnset) || (evt->getEventDirection() == NetEvent::DirServerToClient && isConnectionToClient()) || (evt->getEventDirection() == NetEvent::DirClientToServer && isConnectionToServer()) ) { setLastError("Invalid Packet."); return; } evt->unpack(this, bstream); if(mErrorBuffer[0]) return; if(mConnectionParameters.mDebugObjectSizes) { TNLAssert(endingPosition == bstream->getBitPosition(), avar("unpack did not match pack for event of class %s.", evt->getClassName()) ); } if(unguaranteedPhase) { evt->process(this); delete evt; if(mErrorBuffer[0]) return; continue; } seq |= (mNextRecvEventSeq & ~0x7F); if(seq < mNextRecvEventSeq) seq += 128; EventNote *note = mEventNoteChunker.alloc(); note->mEvent = evt; note->mSeqCount = seq; TNLLogMessageV(LogEventConnection, ("EventConnection %s: RecvdGuaranteed %d", getNetAddressString(), seq)); while(*waitInsert && (*waitInsert)->mSeqCount < seq) waitInsert = &((*waitInsert)->mNextEvent); note->mNextEvent = *waitInsert; *waitInsert = note; waitInsert = &(note->mNextEvent); } while(mWaitSeqEvents && mWaitSeqEvents->mSeqCount == mNextRecvEventSeq) { mNextRecvEventSeq++; EventNote *temp = mWaitSeqEvents; mWaitSeqEvents = temp->mNextEvent; TNLLogMessageV(LogEventConnection, ("EventConnection %s: ProcessGuaranteed %d", getNetAddressString(), temp->mSeqCount)); temp->mEvent->process(this); mEventNoteChunker.free(temp); if(mErrorBuffer[0]) return; } } bool EventConnection::postNetEvent(NetEvent *theEvent) { theEvent->notifyPosted(this); EventNote *event = mEventNoteChunker.alloc(); event->mEvent = theEvent; event->mNextEvent = NULL; if(event->mEvent->mGuaranteeType == NetEvent::GuaranteedOrdered) { event->mSeqCount = mNextSendEventSeq++; if(!mSendEventQueueHead) mSendEventQueueHead = event; else mSendEventQueueTail->mNextEvent = event; mSendEventQueueTail = event; } else { event->mSeqCount = InvalidSendEventSeq; if(!mUnorderedSendEventQueueHead) mUnorderedSendEventQueueHead = event; else mUnorderedSendEventQueueTail->mNextEvent = event; mUnorderedSendEventQueueTail = event; } return true; } void EventConnection::packStringTableEntry(BitStream *stream, StringTableEntryRef h) { if(!mStringTable) stream->writeString(h.getString()); else mStringTable->writeStringTableEntry(stream, h, &((EventPacketNotify *) getCurrentWritePacketNotify())->stringList); } StringTableEntry EventConnection::unpackStringTableEntry(BitStream *stream) { if(!mStringTable) { char buf[256]; stream->readString(buf); return StringTableEntry(buf); } else return mStringTable->readStringTableEntry(stream); } bool EventConnection::isDataToTransmit() { return mUnorderedSendEventQueueHead || mSendEventQueueHead || Parent::isDataToTransmit(); } }; --- NEW FILE: tnlLog.h --- //----------------------------------------------------------------------------------- // // Torque Network Library // Copyright (C) 2004 GarageGames.com, Inc. // For more information see http://www.opentnl.org // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // For use in products that are not compatible with the terms of the GNU // General Public License, alternative licensing options are available // from GarageGames.com. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //------------------------------------------------------------------------------------ #ifndef _TNL_LOG_H_ #define _TNL_LOG_H_ #include "tnlBitSet.h" #include "tnl.h" namespace TNL { /// Global interface to the TNL logging system. /// /// The TNL logging system is consumer based. This is just a /// global stub that routes the log string to all the consumers. /// /// You should <b>really</b> be using the TNLLogMessage() and /// TNLLogMessageV() macros instead of calling this. /// /// @see LogConsumer extern void logprintf(const char *format, ...); /// LogConsumer is the base class for the message logging system in TNL. /// /// TNL by default doesn't log messages anywhere, but users of the library /// can instantiate subclasses that override the logString method. /// Any instantiated subclass of LogConsumer will receive all general /// logprintf's, as well as any TNLLogMessages that are enabled via /// the TNLLogEnable macro. class LogConsumer { LogConsumer *mNextConsumer; ///< Next LogConsumer in the global linked list of log consumers. LogConsumer *mPrevConsumer; ///< Previous LogConsumer in the global linked list of log consumers. static LogConsumer *mLinkedList; ///< Head of the global linked list of log consumers. public: /// Constructor adds this LogConsumer to the global linked list. LogConsumer(); /// Destructor removes this LogConsumer from the global linked list, and updates the log flags. virtual ~LogConsumer(); /// Returns the head of the linked list of all log consumers. static LogConsumer *getLinkedList() { return mLinkedList; } /// Returns the next LogConsumer in the linked list. LogConsumer *getNext() { return mNextConsumer; } /// Writes a string to this instance of LogConsumer. /// /// By default the string is sent to the Platform::outputDebugString function. Subclasses /// might log to a file, a remote service, or even a message box. virtual void logString(const char *string); }; struct LogType { LogType *next; static LogType *linkedList; bool isEnabled; const char *typeName; static LogType *current; #ifdef TNL_ENABLE_LOGGING static void setCurrent(LogType *theType) { current = theType; } static LogType *find(const char *name); #endif }; #ifdef TNL_ENABLE_LOGGING struct LogTypeRef { LogType *theLogType; LogTypeRef(const char *name) { theLogType = LogType::find(name); } }; /// LogConnectionProtocol, /// LogNetConnection, /// LogEventConnection, /// LogGhostConnection, /// LogNetInterface, /// LogPlatform, /// Logs a message of the specified type to the currently active LogConsumers. #define TNLLogMessage(logType, message) { static TNL::LogTypeRef theType(#logType); if(theType.theLogType->isEnabled) { TNL::LogType::setCurrent(theType.theLogType); logprintf("%s", message); TNL::LogType::setCurrent(NULL); } } /// Logs a printf-style variable argument message of the specified type to the currently active LogConsumers. #define TNLLogMessageV(logType, message) { static TNL::LogTypeRef theType(#logType); if(theType.theLogType->isEnabled) { TNL::LogType::setCurrent(theType.theLogType); logprintf message; TNL::LogType::setCurrent(NULL); } } #define TNLLogEnable(logType, enabled) { static TNL::LogTypeRef theType(#logType); theType.theLogType->isEnabled = enabled; } #define TNLLogBlock(logType, code) { static TNL::LogTypeRef theType(#logType); if(theType.theLogType->isEnabled) { TNL::LogType::setCurrent(theType.theLogType); { code } TNL::LogType::setCurrent(NULL); } } #else #define TNLLogMessage(logType, message) { } #define TNLLogMessageV(logType, message) { } #define TNLLogEnable(logType, enabled) { } #define TNLLogBlock(logType, code) { } #endif }; #endif --- NEW FILE: netInterface.cpp --- //----------------------------------------------------------------------------------- // // Torque Network Library // Copyright (C) 2004 GarageGames.com, Inc. // For more information see http://www.opentnl.org // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // For use in products that are not compatible with the terms of the GNU // General Public License, alternative licensing options are available // from GarageGames.com. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. [...1223 lines suppressed...] if(nonce != theParams.mNonce || serverNonce != theParams.mServerNonce) return; U32 decryptPos = stream->getBytePosition(); stream->setBytePosition(decryptPos); if(theParams.mUsingCrypto) { SymmetricCipher theCipher(theParams.mSharedSecret); if(!stream->decryptAndCheckHash(NetConnection::MessageSignatureBytes, decryptPos, &theCipher)) return; } stream->readString(reason); conn->setConnectionState(NetConnection::Disconnected); conn->onDisconnect(reason); removeConnection(conn); } }; --- NEW FILE: clientPuzzle.cpp --- //----------------------------------------------------------------------------------- // // Torque Network Library // Copyright (C) 2004 GarageGames.com, Inc. // For more information see http://www.opentnl.org // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // For use in products that are not compatible with the terms of the GNU // General Public License, alternative licensing options are available // from GarageGames.com. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //------------------------------------------------------------------------------------ #include "tnl.h" #include "tnlClientPuzzle.h" #include "tnlRandom.h" #include <mycrypt.h> namespace TNL { void ClientPuzzleManager::NonceTable::reset() { mChunker.freeBlocks(); mHashTableSize = Random::readI(MinHashTableSize, MaxHashTableSize) * 2 + 1; mHashTable = (Entry **) mChunker.alloc(sizeof(Entry *) * mHashTableSize); for(U32 i = 0; i < mHashTableSize; i++) mHashTable[i] = NULL; } bool ClientPuzzleManager::NonceTable::checkAdd(Nonce &theNonce) { U32 nonce1 = readU32FromBuffer(theNonce.data); U32 nonce2 = readU32FromBuffer(theNonce.data + 4); U64 fullNonce = (U64(nonce1) << 32) | nonce2; U32 hashIndex = U32(fullNonce % mHashTableSize); for(Entry *walk = mHashTable[hashIndex]; walk; walk = walk->mHashNext) if(walk->mNonce == theNonce) return false; Entry *newEntry = (Entry *) mChunker.alloc(sizeof(Entry)); newEntry->mNonce = theNonce; newEntry->mHashNext = mHashTable[hashIndex]; mHashTable[hashIndex] = newEntry; return true; } ClientPuzzleManager::ClientPuzzleManager() { mCurrentDifficulty = InitialPuzzleDifficulty; mLastUpdateTime = 0; mLastTickTime = 0; Random::read(mCurrentNonce.data, Nonce::NonceSize); Random::read(mLastNonce.data, Nonce::NonceSize); mCurrentNonceTable = new NonceTable; mLastNonceTable = new NonceTable; } ClientPuzzleManager::~ClientPuzzleManager() { delete mCurrentNonceTable; delete mLastNonceTable; } void ClientPuzzleManager::tick(U32 currentTime) { if(!mLastTickTime) mLastTickTime = currentTime; // use delta of last tick time and current time to manage puzzle // difficulty. // not yet implemented. // see if it's time to refresh the current puzzle: U32 timeDelta = currentTime - mLastUpdateTime; if(timeDelta > PuzzleRefreshTime) { mLastUpdateTime = currentTime; mLastNonce = mCurrentNonce; NonceTable *tempTable = mLastNonceTable; mLastNonceTable = mCurrentNonceTable; mCurrentNonceTable = tempTable; mLastNonce = mCurrentNonce; mCurrentNonceTable->reset(); Random::read(mCurrentNonce.data, Nonce::NonceSize); } } bool ClientPuzzleManager::checkOneSolution(U32 solution, Nonce &clientNonce, Nonce &serverNonce, U32 puzzleDifficulty, U32 clientIdentity) { U8 buffer[8]; writeU32ToBuffer(solution, buffer); writeU32ToBuffer(clientIdentity, buffer + 4); hash_state hashState; U8 hash[32]; sha256_init(&hashState); sha256_process(&hashState, buffer, sizeof(buffer)); sha256_process(&hashState, clientNonce.data, Nonce::NonceSize); sha256_process(&hashState, serverNonce.data, Nonce::NonceSize); sha256_done(&hashState, hash); U32 index = 0; while(puzzleDifficulty > 8) { if(hash[index]) return false; index++; puzzleDifficulty -= 8; } U8 mask = 0xFF << (8 - puzzleDifficulty); return (mask & hash[index]) == 0; } ClientPuzzleManager::ErrorCode ClientPuzzleManager::checkSolution(U32 solution, Nonce &clientNonce, Nonce &serverNonce, U32 puzzleDifficulty, U32 clientIdentity) { if(puzzleDifficulty != mCurrentDifficulty) return InvalidPuzzleDifficulty; NonceTable *theTable = NULL; if(serverNonce == mCurrentNonce) theTable = mCurrentNonceTable; else if(serverNonce == mLastNonce) theTable = mLastNonceTable; if(!theTable) return InvalidServerNonce; if(!checkOneSolution(solution, clientNonce, serverNonce, puzzleDifficulty, clientIdentity)) return InvalidSolution; if(!theTable->checkAdd(clientNonce)) return InvalidClientNonce; return Success; } bool ClientPuzzleManager::solvePuzzle(U32 *solution, Nonce &clientNonce, Nonce &serverNonce, U32 puzzleDifficulty, U32 clientIdentity) { U32 startTime = Platform::getRealMilliseconds(); U32 startValue = *solution; // Until we're done... for(;;) { // We do 100,000 attempts... U32 nextValue = startValue + 100000; for(;startValue < nextValue; startValue++) { if(checkOneSolution(startValue, clientNonce, serverNonce, puzzleDifficulty, clientIdentity)) { *solution = startValue; return true; } } // Then we check to see if we're out of time... if(Platform::getRealMilliseconds() - startTime > MaxSolutionComputeFragment) { *solution = startValue; return false; } } } }; --- NEW FILE: tnlDataChunker.h --- //----------------------------------------------------------------------------------- // // Torque Network Library // Copyright (C) 2004 GarageGames.com, Inc. // For more information see http://www.opentnl.org // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // For use in products that are not compatible with the terms of the GNU // General Public License, alternative licensing options are available // from GarageGames.com. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //------------------------------------------------------------------------------------ #ifndef _TNL_DATACHUNKER_H_ #define _TNL_DATACHUNKER_H_ //---------------------------------------------------------------------------- namespace TNL { //---------------------------------------------------------------------------- /// Implements a chunked data allocater. /// /// Calling new/malloc all the time is a time consuming operation. Therefore, /// we provide the DataChunker, which allocates memory in blocks of /// chunkSize (by default 16k, see ChunkSize, though it can be set in /// the constructor), then doles it out as requested, in chunks of up to /// chunkSize in size. /// /// It will assert if you try to get more than ChunkSize bytes at a time, /// and it deals with the logic of allocating new blocks and giving out /// word-aligned chunks. /// /// Note that new/free/realloc WILL NOT WORK on memory gotten from the /// DataChunker. This also only grows (you can call freeBlocks to deallocate /// and reset things). class DataChunker { public: enum { ChunkSize = 16376 ///< Default size of each DataBlock page in the DataChunker }; private: /// DataBlock representation for each page of the DataChunker struct DataBlock { DataBlock *next; ///< linked list pointer to the next DataBlock for this chunker U8 *data; ///< allocated pointer for the base of this page S32 curIndex; ///< current allocation point within this DataBlock DataBlock(S32 size); ~DataBlock(); }; DataBlock *curBlock; ///< current page we're allocating data from. If the ///< data size request is greater than the memory space currently ///< available in the current page, a new page will be allocated. S32 chunkSize; ///< The size allocated for each page in the DataChunker public: void *alloc(S32 size); ///< allocate a pointer to memory of size bytes from the DataChunker void freeBlocks(); ///< free all pages currently allocated in the DataChunker DataChunker(S32 size=ChunkSize); ///< Construct a DataChunker with a page size of size bytes. ~DataChunker(); /// Swaps the memory allocated in one data chunker for another. This can be used to implement /// packing of memory stored in a DataChunker. void swap(DataChunker &d) { DataBlock *temp = d.curBlock; d.curBlock = curBlock; curBlock = temp; } }; //---------------------------------------------------------------------------- /// Templatized data chunker class with proper construction and destruction of its elements. /// /// DataChunker just allocates space. This subclass actually constructs/destructs the /// elements. This class is appropriate for more complex classes. template<class T> class ClassChunker: private DataChunker { S32 numAllocated; ///< number of elements currently allocated through this ClassChunker S32 elementSize; ///< the size of each element, or the size of a pointer, whichever is greater T *freeListHead; ///< a pointer to a linked list of freed elements for reuse public: ClassChunker(S32 size = DataChunker::ChunkSize) : DataChunker(size) { numAllocated = 0; elementSize = getMax(U32(sizeof(T)), U32(sizeof(T *))); freeListHead = NULL; } /// Allocates and properly constructs in place a new element. T *alloc() { numAllocated++; if(freeListHead == NULL) return constructInPlace(reinterpret_cast<T*>(DataChunker::alloc(elementSize))); T* ret = freeListHead; freeListHead = *(reinterpret_cast<T**>(freeListHead)); return constructInPlace(ret); } /// Properly destructs and freesan element allocated with the alloc method. void free(T* elem) { destructInPlace(elem); numAllocated--; *(reinterpret_cast<T**>(elem)) = freeListHead; freeListHead = elem; } }; }; #endif --- NEW FILE: tnlPlatform.h --- //----------------------------------------------------------------------------------- // // Torque Network Library // Copyright (C) 2004 GarageGames.com, Inc. // For more information see http://www.opentnl.org // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // For use in products that are not compatible with the terms of the GNU // General Public License, alternative licensing options are available // from GarageGames.com. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //------------------------------------------------------------------------------------ #ifndef _TNL_PLATFORM_H_ #define _TNL_PLATFORM_H_ #ifndef _TNL_TYPES_H_ #include "tnlTypes.h" #endif namespace TNL { /// Platform specific functionality is gathered here to enable easier porting. /// /// If you are embedding TNL in a complex application, you may want to replace /// these with hooks to your own code - for instance the alerts really should /// bring up an actual message box; currently they just emit a log item. namespace Platform { /// Prints a string to the OS specific debug log. void outputDebugString(const char *string); /// Stops the program in the debugger, if it is being debugged. void debugBreak(); /// Forces the program to exit immediately. /// /// @note This is probably a bit strong for a networking library. -- BJG void forceQuit(); /// Brings up a dialog window with a message and an "OK" button void AlertOK(const char *windowTitle, const char *message); /// Brings up a dialog window with the message, and "OK" and "Cancel" buttons bool AlertOKCancel(const char *windowTitle, const char *message); /// Brings up a dialog window with the message, and "Retry" and "Cancel" buttons bool AlertRetry(const char *windowTitle, const char *message); /// Elapsed time in milliseconds. /// /// Usually since last reboot, but it varies from platform to platform. It is /// guaranteed to always increase, however, up to the limit of a U32 - about /// 7 weeks worth of time. If you are developing a server you want to run for longer /// than that, prepared to see wraparounds. U32 getRealMilliseconds(); /// Returns a high-precision time value, in a platform-specific time value S64 getHighPrecisionTimerValue(); /// Converts a high precision timer delta into milliseconds F64 getHighPrecisionMilliseconds(S64 timerDelta); /// Put the process to sleep for the specified millisecond interva. void sleep(U32 msCount); /// checks the status of the memory allocation heap bool checkHeap(); }; #define TIME_BLOCK(name,block) { S64 st = Platform::getHighPrecisionTimerValue(); {block} S64 delta = Platform::getHighPrecisionTimerValue() - st; F64 ms = Platform::getHighPrecisionMilliseconds(delta); logprintf("Timer: %s Elapsed: %g ms", #name, ms); } #if defined (TNL_SUPPORTS_VC_INLINE_X86_ASM) #define TNL_DEBUGBREAK() { __asm { int 3 }; } #elif defined(TNL_SUPPORTS_GCC_INLINE_X86_ASM) #define TNL_DEBUGBREAK() { asm ( "int $3"); } #else /// Macro to do in-line debug breaks, used for asserts. Does inline assembly where appropriate #define TNL_DEBUGBREAK() Platform::debugBreak(); #endif #define TNL_CHECK_HEAP() { bool status = TNL::Platform::checkHeap(); if(!status) TNL_DEBUGBREAK(); } extern bool atob(const char *str); ///< String to boolean conversion. /// Printf into string with a buffer size. /// /// This will print into the specified string until the buffer size is reached. extern int dSprintf(char *buffer, U32 bufferSize, const char *format, ...); /// Vsprintf with buffer size argument. /// /// This will print into the specified string until the ... [truncated message content] |