void CClientEntity::_GetEntitiesFromRoot ( unsigned int uiTypeHash, std::map < CClientEntity*, int >& mapResults ) { t_mapEntitiesFromRoot::iterator find = ms_mapEntitiesFromRoot.find ( uiTypeHash ); if ( find != ms_mapEntitiesFromRoot.end () ) { const std::list < CClientEntity* >& listEntities = find->second; CClientEntity* pEntity; unsigned int uiIndex = 0; for ( std::list < CClientEntity* >::const_reverse_iterator i = listEntities.rbegin (); i != listEntities.rend (); ++i ) { pEntity = *i; assert ( pEntity ); ElementID ID = pEntity->GetID (); assert ( ID != INVALID_ELEMENT_ID ); assert ( pEntity == CElementIDs::GetElement ( ID ) ); if ( pEntity->IsBeingDeleted () ) OutputDebugString ( SString ( "Client: 0x%08x %s is flagged as IsBeingDeleted() but is still in GetEntitiesFromRoot\n", pEntity, pEntity->GetTypeName () ) ); assert ( mapResults.find ( pEntity ) == mapResults.end () ); mapResults [ pEntity ] = 1; } } }
/////////////////////////////////////////////////////////////// // // CGameEntityXRefManagerImpl::OnGameEntityDestruct // // Called when GTA is destroying a game entity // /////////////////////////////////////////////////////////////// void CGameEntityXRefManagerImpl::OnGameEntityDestruct ( CEntitySAInterface* pEntitySAInterface ) { if ( MapContains ( m_InterfaceToClientMap, pEntitySAInterface ) ) { BYTE* pInterface = (BYTE*)pEntitySAInterface; DWORD InterfaceVtbl = *(DWORD*)pInterface; ushort InterfaceModelId = *(ushort*)(pInterface + 34); CClientEntity* pClientEntity = MapFindRef ( m_InterfaceToClientMap, pEntitySAInterface ); bool bClientEntityValid = MapContains ( m_ValidClientEntityMap, pClientEntity ); SString strClientEntityInfo; if ( bClientEntityValid ) { CEntity* pGameEntity = NULL; if ( CClientPed* pPed = DynamicCast < CClientPed > ( pClientEntity ) ) pGameEntity = pPed->GetGameEntity (); if ( CClientVehicle* pVehicle = DynamicCast < CClientVehicle > ( pClientEntity ) ) pGameEntity = pVehicle->GetGameEntity (); if ( CClientObject* pObject = DynamicCast < CClientObject > ( pClientEntity ) ) pGameEntity = pObject->GetGameEntity (); if ( CClientProjectile* pProjectile = DynamicCast < CClientProjectile > ( pClientEntity ) ) pGameEntity = pProjectile->GetGameEntity (); if ( CClientPickup* pPickup = DynamicCast < CClientPickup > ( pClientEntity ) ) pGameEntity = pPickup->GetGameObject (); CEntity* pMappedGameEntity = MapFindRef ( m_ClientToGameMap, pClientEntity ); CClientEntity* pMappedClientEntity = MapFindRef ( m_GameToClientMap, pGameEntity ); strClientEntityInfo = SString ( "%s Id:%x GameEntity:%08x MappedGameEntity:%08x MappedClientEntity:%08x" , pClientEntity->GetClassName () , pClientEntity->GetID () , (int)pGameEntity , (int)pMappedGameEntity , (int)pMappedClientEntity ); } SString strMessage ( "EntitySAInterface:%08x Vtbl:%08x ModelId:%d ClientEntity:%08x [%s]" , (int)pEntitySAInterface , InterfaceVtbl , InterfaceModelId , (int)pClientEntity , *strClientEntityInfo ); g_pCore->LogEvent ( 8542, "XRefManager", "GameEntity Mismatch", strMessage ); AddReportLog ( 8542, strMessage ); dassert ( 0 ); } }
bool CLuaArgument::WriteToBitStream ( NetBitStreamInterface& bitStream, std::map < 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: { if ( pKnownTables && pKnownTables->find ( m_pTableData ) != pKnownTables->end () ) { // Self-referencing table type.data.ucType = LUA_TTABLEREF; bitStream.Write ( &type ); bitStream.WriteCompressed ( pKnownTables->find ( m_pTableData )->second ); } 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 = strlen ( szTemp ); 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 ( const_cast < char* > ( szTemp ), usLength ); } } else { // Too long string LogUnableToPacketize ( "Couldn't packetize argument list. Invalid string specified, limit is 65535 characters." ); // Write a nil though so other side won't get out of sync bitStream.Write ( (unsigned char) LUA_TNIL ); return false; } break; } // Element packet case LUA_TLIGHTUSERDATA: { // 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.WriteCompressed ( static_cast < ElementID > ( 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; }
char * CLuaArgument::WriteToString ( char * szBuffer, int length ) { switch ( GetType () ) { case LUA_TNIL: { snprintf ( szBuffer, length, "0" ); return szBuffer; } case LUA_TBOOLEAN: { if ( GetBoolean () ) snprintf ( szBuffer, length, "true" ); else snprintf ( szBuffer, length, "false" ); return szBuffer; } case LUA_TTABLE: { g_pClientGame->GetScriptDebugging()->LogError ( NULL, "Cannot convert table to string (do not use tables as keys in tables if you want to send them over http/JSON)." ); return NULL; } case LUA_TNUMBER: { int iNumber; if ( ShouldUseInt( GetNumber(), &iNumber ) ) { snprintf ( szBuffer, length, "%d", iNumber ); return szBuffer; } else { snprintf ( szBuffer, length, "%f", static_cast < float > ( GetNumber() ) ); return szBuffer; } break; } case LUA_TSTRING: { const char* szTemp = GetString (); unsigned short usLength = static_cast < unsigned short > ( strlen ( szTemp ) ); if ( strlen ( szTemp ) == usLength ) { snprintf ( szBuffer, length, "%s", szTemp ); return szBuffer; } else { g_pClientGame->GetScriptDebugging()->LogError ( NULL, "String is too long. Limit is 65535 characters." ); } break; } case LUA_TLIGHTUSERDATA: case LUA_TUSERDATA: { CClientEntity* pElement = GetElement (); CResource* pResource = reinterpret_cast < CResource* > ( GetUserData() ); if ( pElement ) { snprintf ( szBuffer, length, "#E#%d", (int)pElement->GetID().Value() ); return szBuffer; } else if ( VERIFY_RESOURCE(pResource) ) { snprintf ( szBuffer, length, "#R#%s", pResource->GetName()/*.c_str ()*/ ); return szBuffer; } else { g_pClientGame->GetScriptDebugging()->LogError ( NULL, "Couldn't convert element to string, only valid elements can be sent." ); return NULL; } break; } default: { g_pClientGame->GetScriptDebugging()->LogError ( NULL, "Couldn't convert argument to string, unsupported data type. Use String, Number, Boolean or Element." ); return NULL; } } return NULL; }
json_object * CLuaArgument::WriteToJSONObject ( bool bSerialize, CFastHashMap < CLuaArguments*, unsigned long > * pKnownTables ) { switch ( GetType () ) { case LUA_TNIL: { return json_object_new_int(0); } case LUA_TBOOLEAN: { return json_object_new_boolean(GetBoolean ()); } case LUA_TTABLE: { ulong* pTableId; if ( pKnownTables && ( pTableId = MapFind ( *pKnownTables, m_pTableData ) ) ) { // Self-referencing table char szTableID[10]; snprintf ( szTableID, sizeof(szTableID), "^T^%lu", *pTableId ); return json_object_new_string ( szTableID ); } else { return m_pTableData->WriteTableToJSONObject ( bSerialize, pKnownTables ); } } case LUA_TNUMBER: { int iNumber; if ( ShouldUseInt( GetNumber(), &iNumber ) ) { return json_object_new_int( iNumber ); } else { return json_object_new_double( static_cast < float > ( GetNumber() ) ); } break; } case LUA_TSTRING: { const char* szTemp = GetString (); unsigned short usLength = static_cast < unsigned short > ( strlen ( szTemp ) ); if ( strlen ( szTemp ) == usLength ) { return json_object_new_string_len ( (char *)szTemp, usLength ); } else { g_pClientGame->GetScriptDebugging()->LogError ( NULL, "Couldn't convert argument list to JSON. Invalid string specified, limit is 65535 characters." ); } break; } case LUA_TUSERDATA: case LUA_TLIGHTUSERDATA: { CClientEntity* pElement = GetElement (); CResource* pResource = g_pClientGame->GetResourceManager ()->GetResourceFromScriptID ( reinterpret_cast < unsigned long > ( GetUserData () ) ); // Elements are dynamic, so storing them is potentially unsafe if ( pElement && bSerialize ) { char szElementID[10] = {0}; snprintf ( szElementID, 9, "^E^%d", (int)pElement->GetID().Value() ); return json_object_new_string ( szElementID ); } else if ( VERIFY_RESOURCE(pResource) ) { char szElementID[MAX_RESOURCE_NAME_LENGTH+4] = {0}; snprintf ( szElementID, MAX_RESOURCE_NAME_LENGTH+3, "^R^%s", pResource->GetName()/*.c_str ()*/ ); return json_object_new_string ( szElementID ); } else { if ( pElement ) // eg toJSON() with valid element g_pClientGame->GetScriptDebugging()->LogError ( NULL, "Couldn't convert userdata argument to JSON, elements not allowed for this function." ); else if ( !bSerialize ) // eg toJSON() with invalid element g_pClientGame->GetScriptDebugging()->LogError ( NULL, "Couldn't convert userdata argument to JSON, only valid resources can be included for this function." ); else g_pClientGame->GetScriptDebugging()->LogError ( NULL, "Couldn't convert userdata argument to JSON, only valid elements or resources can be included." ); return NULL; } break; } default: { g_pClientGame->GetScriptDebugging()->LogError ( NULL, "Couldn't convert argument list to JSON, unsupported data type. Use Table, Nil, String, Number, Boolean, Resource or Element." ); return NULL; } } return NULL; }
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; }
json_object * CLuaArgument::WriteToJSONObject ( bool bSerialize, std::map < CLuaArguments*, unsigned long > * pKnownTables ) { switch ( GetType () ) { case LUA_TNIL: { return json_object_new_int(0); } case LUA_TBOOLEAN: { return json_object_new_boolean(GetBoolean ()); } case LUA_TTABLE: { if ( pKnownTables && pKnownTables->find ( m_pTableData ) != pKnownTables->end () ) { char szTableID[10]; snprintf ( szTableID, sizeof(szTableID), "^T^%lu", pKnownTables->find ( m_pTableData )->second ); return json_object_new_string ( szTableID ); } else { return m_pTableData->WriteTableToJSONObject ( bSerialize, pKnownTables ); } } case LUA_TNUMBER: { float fNum = static_cast < float > ( GetNumber () ); int iNum = static_cast < int > ( GetNumber () ); if ( iNum == fNum ) { return json_object_new_int(iNum); } else { return json_object_new_double(fNum); } break; } case LUA_TSTRING: { const char* szTemp = GetString (); unsigned short usLength = static_cast < unsigned short > ( strlen ( szTemp ) ); if ( strlen ( szTemp ) == usLength ) { return json_object_new_string_len ( (char *)szTemp, usLength ); } else { g_pClientGame->GetScriptDebugging()->LogError ( NULL, "Couldn't convert argument list to JSON. Invalid string specified, limit is 65535 characters." ); } break; } case LUA_TLIGHTUSERDATA: { CClientEntity* pElement = GetElement (); CResource* pResource = reinterpret_cast < CResource* > ( GetLightUserData() ); // Elements are dynamic, so storing them is potentially unsafe if ( pElement && bSerialize ) { char szElementID[10] = {0}; snprintf ( szElementID, 9, "^E^%d", (int)pElement->GetID().Value() ); return json_object_new_string ( szElementID ); } else if ( VERIFY_RESOURCE(pResource) ) { char szElementID[MAX_RESOURCE_NAME_LENGTH+4] = {0}; snprintf ( szElementID, MAX_RESOURCE_NAME_LENGTH+3, "^R^%s", pResource->GetName()/*.c_str ()*/ ); return json_object_new_string ( szElementID ); } else { g_pClientGame->GetScriptDebugging()->LogError ( NULL, "Couldn't convert argument list to JSON, only valid elements can be sent." ); return NULL; } break; } default: { g_pClientGame->GetScriptDebugging()->LogError ( NULL, "Couldn't convert argument list to JSON, unsupported data type. Use Table, Nil, String, Number, Boolean, Resource or Element." ); return NULL; } } return NULL; }