// Can't use bitStream.Version() here as it is sometimes not set bool CLuaArgument::WriteToBitStream(NetBitStreamInterface& bitStream, CFastHashMap<CLuaArguments*, unsigned long>* pKnownTables) const { SLuaTypeSync type; switch (GetType()) { // Nil type case LUA_TNIL: { type.data.ucType = LUA_TNIL; bitStream.Write(&type); break; } // Boolean type case LUA_TBOOLEAN: { type.data.ucType = LUA_TBOOLEAN; bitStream.Write(&type); // Write the boolean to it bitStream.WriteBit(GetBoolean()); break; } // Table argument case LUA_TTABLE: { ulong* pThingy; if (pKnownTables && (pThingy = MapFind(*pKnownTables, m_pTableData))) { // Self-referencing table type.data.ucType = LUA_TTABLEREF; bitStream.Write(&type); bitStream.WriteCompressed(*pThingy); } else { type.data.ucType = LUA_TTABLE; bitStream.Write(&type); // Write the subtable to the bitstream m_pTableData->WriteToBitStream(bitStream, pKnownTables); } break; } // Number argument? case LUA_TNUMBER: { type.data.ucType = LUA_TNUMBER; bitStream.Write(&type); int iNumber; float fNumber; double dNumber; EDataType dataType = GetDataTypeToUse(GetNumber(), &iNumber, &fNumber, &dNumber); if (dataType == DATA_TYPE_INT) { bitStream.WriteBit(false); bitStream.WriteCompressed(iNumber); } else if (dataType == DATA_TYPE_FLOAT) { bitStream.WriteBit(true); bitStream.WriteBit(false); bitStream.Write(fNumber); } else { bitStream.WriteBit(true); bitStream.WriteBit(true); bitStream.Write(dNumber); } break; } // String argument case LUA_TSTRING: { // Grab the string and its length. Is it short enough to be sendable? const char* szTemp = m_strString.c_str(); size_t sizeTemp = m_strString.length(); unsigned short usLength = static_cast<unsigned short>(sizeTemp); if (sizeTemp == usLength) { // This is a string argument type.data.ucType = LUA_TSTRING; bitStream.Write(&type); // Write its length bitStream.WriteCompressed(usLength); // Write the content too if it's not empty if (usLength > 0) { bitStream.Write(szTemp, usLength); } } else { // This is a long string argument type.data.ucType = LUA_TSTRING_LONG; bitStream.Write(&type); // Write its length uint uiLength = sizeTemp; bitStream.WriteCompressed(uiLength); // Write the content too if it's not empty if (uiLength > 0) { bitStream.AlignWriteToByteBoundary(); bitStream.Write(szTemp, uiLength); } } break; } // Element argument case LUA_TLIGHTUSERDATA: case LUA_TUSERDATA: { // Grab the element from this userdata pointer. Valid and has a synced element ID? CElement* pElement = GetElement(); if (pElement && pElement->GetID() != INVALID_ELEMENT_ID) { // Write its ID type.data.ucType = LUA_TUSERDATA; bitStream.Write(&type); bitStream.Write(pElement->GetID()); } else { // Jax: this just spams the script debugger, it's not really neccesary // LogUnableToPacketize ( "Couldn't packetize argument list, invalid element specified." ); // Write a nil though so other side won't get out of sync type.data.ucType = LUA_TNIL; bitStream.Write(&type); return false; } break; } // Unpacketizable type. default: { // Unpacketizable LogUnableToPacketize("Couldn't packetize argument list, unknown type specified."); // Write a nil though so other side won't get out of sync type.data.ucType = LUA_TNIL; bitStream.Write(&type); return false; } } // Success return true; }
/////////////////////////////////////////////////////////////// // // CLatentSendQueue::DoPulse // // Send next part of the active transfer // /////////////////////////////////////////////////////////////// void CLatentSendQueue::DoPulse ( int iTimeMsBetweenCalls ) { if ( m_TxQueue.empty () ) { m_iBytesOwing = 0; return; } // Check if previous tx has completed if ( m_TxQueue.front ().uiReadPosition == m_TxQueue.front ().bufferRef->GetSize () && m_TxQueue.front ().bSendFinishing ) { m_TxQueue.pop_front (); PostQueueRemove (); if ( m_TxQueue.empty () ) { m_iBytesOwing = 0; return; } } m_uiCurrentRate = Max < uint > ( MIN_SEND_RATE, m_uiCurrentRate ); // How many bytes to send this pulse int iBytesToSendThisPulse = iTimeMsBetweenCalls * m_uiCurrentRate / 1000; // Add bytes owing from last pulse iBytesToSendThisPulse += m_iBytesOwing; // Calc packet size depending on rate uint uiMaxPacketSize = Lerp ( MIN_PACKET_SIZE, UnlerpClamped ( MIN_PACKET_SIZE * 10, m_uiCurrentRate, MAX_PACKET_SIZE * 15 ), MAX_PACKET_SIZE ); // Calc how many packets to do this pulse uint uiNumPackets = iBytesToSendThisPulse / uiMaxPacketSize; // Update carry over m_iBytesOwing = iBytesToSendThisPulse % uiMaxPacketSize; // Process item at front of queue SSendItem& activeTx = m_TxQueue.front (); for ( uint i = 0 ; i < uiNumPackets && !activeTx.bSendFinishing ; i++ ) { // Send next part of data NetBitStreamInterface* pBitStream = DoAllocateNetBitStream ( m_RemoteId, m_usBitStreamVersion ); pBitStream->WriteBits ( &activeTx.uiId, 15 ); // Next bit indicates if it has a special flag if ( activeTx.uiReadPosition == 0 ) { // Head pBitStream->WriteBit ( 1 ); pBitStream->Write ( (uchar)FLAG_HEAD ); pBitStream->Write ( activeTx.usCategory ); pBitStream->Write ( activeTx.bufferRef->GetSize () ); pBitStream->Write ( activeTx.uiRate ); if ( pBitStream->Version () >= 0x31 ) pBitStream->Write ( activeTx.usResourceNetId ); activeTx.bSendStarted = true; } else if ( activeTx.bufferRef->GetSize () == activeTx.uiReadPosition ) { // Tail pBitStream->WriteBit ( 1 ); pBitStream->Write ( (uchar)FLAG_TAIL ); activeTx.bSendFinishing = true; } else { // Body pBitStream->WriteBit ( 0 ); } // Align to next boundary pBitStream->AlignWriteToByteBoundary (); uint uiMaxDataSize = Max < int > ( 10, uiMaxPacketSize - pBitStream->GetNumberOfBytesUsed () ); // Calc how much data to send uint uiDataOffset = activeTx.uiReadPosition; uint uiSizeToSend = Min ( uiMaxDataSize, activeTx.bufferRef->GetSize () - activeTx.uiReadPosition ); activeTx.uiReadPosition += uiSizeToSend; pBitStream->Write ( (ushort)uiSizeToSend ); pBitStream->Write ( activeTx.bufferRef->GetData () + uiDataOffset, uiSizeToSend ); // Send DoSendPacket ( PACKET_ID_LATENT_TRANSFER, m_RemoteId, pBitStream, PACKET_PRIORITY_LOW, PACKET_RELIABILITY_RELIABLE_ORDERED, PACKET_ORDERING_DATA_TRANSFER ); DoDeallocateNetBitStream ( pBitStream ); } }
bool CLuaArgument::WriteToBitStream ( NetBitStreamInterface& bitStream, CFastHashMap < CLuaArguments*, unsigned long > * pKnownTables ) const { SLuaTypeSync type; switch ( GetType () ) { // Nil type case LUA_TNIL: { type.data.ucType = LUA_TNIL; bitStream.Write ( &type ); break; } // Boolean type case LUA_TBOOLEAN: { type.data.ucType = LUA_TBOOLEAN; bitStream.Write ( &type ); // Write the boolean to it bitStream.WriteBit ( GetBoolean () ); break; } // Table argument case LUA_TTABLE: { ulong* pTableId; if ( pKnownTables && ( pTableId = MapFind ( *pKnownTables, m_pTableData ) ) ) { // Self-referencing table type.data.ucType = LUA_TTABLEREF; bitStream.Write ( &type ); bitStream.WriteCompressed ( *pTableId ); } else { type.data.ucType = LUA_TTABLE; bitStream.Write ( &type ); // Write the subtable to the bitstream m_pTableData->WriteToBitStream ( bitStream, pKnownTables ); } break; } // Number argument? case LUA_TNUMBER: { type.data.ucType = LUA_TNUMBER; bitStream.Write ( &type ); if ( bitStream.Version() < 0x59 ) { // Old way int iNumber; if ( !ShouldUseInt( GetNumber(), &iNumber ) ) { bitStream.WriteBit ( true ); bitStream.Write ( static_cast < float > ( GetNumber() ) ); } else { bitStream.WriteBit ( false ); bitStream.WriteCompressed ( iNumber ); } } else { // New way - Maybe use double to better preserve > 32bit numbers int iNumber; float fNumber; double dNumber; EDataType dataType = GetDataTypeToUse( GetNumber(), &iNumber, &fNumber, &dNumber ); if ( dataType == DATA_TYPE_INT ) { bitStream.WriteBit ( false ); bitStream.WriteCompressed ( iNumber ); } else if ( dataType == DATA_TYPE_FLOAT ) { bitStream.WriteBit ( true ); bitStream.WriteBit ( false ); bitStream.Write ( fNumber ); } else { bitStream.WriteBit ( true ); bitStream.WriteBit ( true ); bitStream.Write ( dNumber ); } } break; } // String argument case LUA_TSTRING: { // Grab the string and its length. Is it short enough to be sendable? const char* szTemp = m_strString.c_str (); size_t sizeTemp = m_strString.length (); unsigned short usLength = static_cast < unsigned short > ( sizeTemp ); if ( sizeTemp == usLength ) { // This is a string argument type.data.ucType = LUA_TSTRING; bitStream.Write ( &type ); // Write its length bitStream.WriteCompressed ( usLength ); // Write the content too if it's not empty if ( usLength > 0 ) { bitStream.Write ( szTemp, usLength ); } } else { // This is a long string argument type.data.ucType = LUA_TSTRING_LONG; bitStream.Write ( &type ); // Write its length uint uiLength = sizeTemp; bitStream.WriteCompressed ( uiLength ); // Write the content too if it's not empty if ( uiLength > 0 ) { bitStream.AlignWriteToByteBoundary (); bitStream.Write ( szTemp, uiLength ); } } break; } // Element packet case LUA_TLIGHTUSERDATA: case LUA_TUSERDATA: { // Got a valid element to send? CClientEntity* pElement = GetElement (); if ( pElement ) { // Clientside element? if ( !pElement->IsLocalEntity () ) { type.data.ucType = LUA_TLIGHTUSERDATA; bitStream.Write ( &type ); bitStream.Write ( pElement->GetID () ); } else { // Write a nil though so other side won't get out of sync type.data.ucType = LUA_TNIL; bitStream.Write ( &type ); return false; } } else { // Write a nil though so other side won't get out of sync type.data.ucType = LUA_TNIL; bitStream.Write ( &type ); return false; } break; } // Unpacketizable type. default: { // Unpacketizable LogUnableToPacketize ( "Couldn't packetize argument list, unknown type specified." ); // Write a nil though so other side won't get out of sync type.data.ucType = LUA_TNIL; bitStream.Write ( &type ); return false; } } // Success return true; }
bool CLuaArgument::WriteToBitStream ( NetBitStreamInterface& bitStream, CFastHashMap < CLuaArguments*, unsigned long > * pKnownTables ) const { SLuaTypeSync type; switch ( GetType () ) { // Nil type case LUA_TNIL: { type.data.ucType = LUA_TNIL; bitStream.Write ( &type ); break; } // Boolean type case LUA_TBOOLEAN: { type.data.ucType = LUA_TBOOLEAN; bitStream.Write ( &type ); // Write the boolean to it bitStream.WriteBit ( GetBoolean () ); break; } // Table argument case LUA_TTABLE: { ulong* pThingy; if ( pKnownTables && ( pThingy = MapFind ( *pKnownTables, m_pTableData ) ) ) { // Self-referencing table type.data.ucType = LUA_TTABLEREF; bitStream.Write ( &type ); bitStream.WriteCompressed ( *pThingy ); } else { type.data.ucType = LUA_TTABLE; bitStream.Write ( &type ); // Write the subtable to the bitstream m_pTableData->WriteToBitStream ( bitStream, pKnownTables ); } break; } // Number argument? case LUA_TNUMBER: { type.data.ucType = LUA_TNUMBER; bitStream.Write ( &type ); float fNumber = static_cast < float > ( GetNumber () ); long lNumber = static_cast < long > ( fNumber ); float fNumberInteger = static_cast < float > ( lNumber ); // Check if the number is an integer and can fit a long datatype if ( fabs ( fNumber ) > fabs ( fNumberInteger + 1 ) || fabs ( fNumber - fNumberInteger ) >= FLOAT_EPSILON ) { bitStream.WriteBit ( true ); bitStream.Write ( fNumber ); } else { bitStream.WriteBit ( false ); bitStream.WriteCompressed ( lNumber ); } break; } // String argument case LUA_TSTRING: { // Grab the string and its length. Is it short enough to be sendable? const char* szTemp = m_strString.c_str (); size_t sizeTemp = m_strString.length (); unsigned short usLength = static_cast < unsigned short > ( sizeTemp ); if ( sizeTemp == usLength ) { // This is a string argument type.data.ucType = LUA_TSTRING; bitStream.Write ( &type ); // Write its length bitStream.WriteCompressed ( usLength ); // Write the content too if it's not empty if ( usLength > 0 ) { bitStream.Write ( szTemp, usLength ); } } else if ( sizeTemp > 65535 && bitStream.Version () >= 0x027 && g_pGame->CalculateMinClientRequirement () >= LONG_STRING_MIN_VERSION ) { // This is a long string argument type.data.ucType = LUA_TSTRING_LONG; bitStream.Write ( &type ); // Write its length uint uiLength = sizeTemp; bitStream.WriteCompressed ( uiLength ); // Write the content too if it's not empty if ( uiLength > 0 ) { bitStream.AlignWriteToByteBoundary (); bitStream.Write ( szTemp, uiLength ); } } else { // Too long string LogUnableToPacketize ( "Couldn't packetize argument list. Invalid string specified, limit is 65535 characters." " To use longer strings, set script <min_mta_version> to " LONG_STRING_MIN_VERSION " or higher." ); // Write a nil though so other side won't get out of sync type.data.ucType = LUA_TNIL; bitStream.Write ( &type ); return false; } break; } // Element argument case LUA_TLIGHTUSERDATA: { // Grab the element from this userdata pointer. Valid and has a synced element ID? CElement* pElement = GetElement (); if ( pElement && pElement->GetID () != INVALID_ELEMENT_ID ) { // Write its ID type.data.ucType = LUA_TLIGHTUSERDATA; bitStream.Write ( &type ); bitStream.Write ( pElement->GetID () ); } else { // Jax: this just spams the script debugger, it's not really neccesary // LogUnableToPacketize ( "Couldn't packetize argument list, invalid element specified." ); // Write a nil though so other side won't get out of sync type.data.ucType = LUA_TNIL; bitStream.Write ( &type ); return false; } break; } // Unpacketizable type. default: { // Unpacketizable LogUnableToPacketize ( "Couldn't packetize argument list, unknown type specified." ); // Write a nil though so other side won't get out of sync type.data.ucType = LUA_TNIL; bitStream.Write ( &type ); return false; } } // Success return true; }