/************************************************************************************
 *
* ***********************************************************************************/
bool ScopeDome::SetupParms()
{
    targetAz = 0;

    readU32(GetImpPerTurn, stepsPerTurn);
    LOGF_INFO("Steps per turn read as %d", stepsPerTurn);
    StepsPerRevolutionN[0].value = stepsPerTurn;
    StepsPerRevolutionNP.s       = IPS_OK;
    IDSetNumber(&StepsPerRevolutionNP, nullptr);

    readS32(GetHomeSensorPosition, homePosition);
    LOGF_INFO("Home position read as %d", homePosition);

    if (UpdatePosition())
        IDSetNumber(&DomeAbsPosNP, nullptr);

    if (UpdateShutterStatus())
        IDSetSwitch(&DomeShutterSP, nullptr);

    UpdateSensorStatus();
    UpdateRelayStatus();

    if (InitPark())
    {
        // If loading parking data is successful, we just set the default parking
        // values.
        SetAxis1ParkDefault(0);
    }
    else
    {
        // Otherwise, we set all parking data to default in case no parking data is
        // found.
        SetAxis1Park(0);
        SetAxis1ParkDefault(0);
    }

    uint8_t calibrationNeeded = false;
    readU8(IsFullSystemCalReq, calibrationNeeded);
    CalibrationNeededS[0].s = calibrationNeeded ? ISS_ON : ISS_OFF;
    CalibrationNeededSP.s   = IPS_OK;
    IDSetSwitch(&CalibrationNeededSP, nullptr);

    uint16_t fwVersion;
    readU16(GetVersionFirmware, fwVersion);
    FirmwareVersionsN[0].value = fwVersion / 100.0;

    uint8_t fwVersionRotary;
    readU8(GetVersionFirmwareRotary, fwVersionRotary);
    FirmwareVersionsN[1].value = (fwVersionRotary + 9) / 10.0;
    FirmwareVersionsNP.s       = IPS_OK;
    IDSetNumber(&FirmwareVersionsNP, nullptr);
    return true;
}
U32		PInternalQuitApplicationPkt::deserialize( const S8 * buf, S32 offset ) 
{
	U32 cur_pos = PPacketBase::deserialize(buf, offset );

	const S8 * buff = buf + offset;

	m_iReaseaon = readS32( buff+ cur_pos );

	cur_pos += sizeof(S32);

	return cur_pos;

}
void TestSerialization::testStreamRead()
{
	std::string datastr(
		(const char *)test_serialized_data,
		sizeof(test_serialized_data));
	std::istringstream is(datastr, std::ios_base::binary);

	UASSERT(readU8(is) == 0x11);
	UASSERT(readU16(is) == 0x2233);
	UASSERT(readU32(is) == 0x44556677);
	UASSERT(readU64(is) == 0x8899AABBCCDDEEFF);

	UASSERT(readS8(is) == -128);
	UASSERT(readS16(is) == 30000);
	UASSERT(readS32(is) == -6);
	UASSERT(readS64(is) == -43);

	UASSERT(readF1000(is) == 53.534f);
	UASSERT(readF1000(is) == -300000.32f);
	UASSERT(readF1000(is) == F1000_MIN);
	UASSERT(readF1000(is) == F1000_MAX);

	UASSERT(deSerializeString(is) == "foobar!");

	UASSERT(readV2S16(is) == v2s16(500, 500));
	UASSERT(readV3S16(is) == v3s16(4207, 604, -30));
	UASSERT(readV2S32(is) == v2s32(1920, 1080));
	UASSERT(readV3S32(is) == v3s32(-400, 6400054, 290549855));
	UASSERT(readV2F1000(is) == v2f(500.656f, 350.345f));

	UASSERT(deSerializeWideString(is) == L"\x02~woof~\x5455");

	UASSERT(readV3F1000(is) == v3f(500, 10024.2f, -192.54f));
	UASSERT(readARGB8(is) == video::SColor(255, 128, 50, 128));

	UASSERT(deSerializeLongString(is) == "some longer string here");

	UASSERT(is.rdbuf()->in_avail() == 2);
	UASSERT(readU16(is) == 0xF00D);
	UASSERT(is.rdbuf()->in_avail() == 0);
}
Exemple #4
0
/************************************************************************************
 *
* ***********************************************************************************/
void ScopeDome::TimerHit()
{
    if (!isConnected())
        return; //  No need to reset timer if we are not connected anymore

    readU16(GetStatus, currentStatus);
    // LOGF_INFO("Status: %x", currentStatus);
    UpdatePosition();

    UpdateShutterStatus();
    IDSetSwitch(&DomeShutterSP, nullptr);

    UpdateRelayStatus();

    if (status == DOME_HOMING)
    {
        if ((currentStatus & 8) == 0 && getInputState(IN_HOME))
        {
            // Found home
            status   = DOME_READY;
            targetAz = DomeHomePositionN[0].value;

            // Reset counter
            writeCmd(ResetCounter);

            FindHomeSP.s   = IPS_OK;
            DomeAbsPosNP.s = IPS_OK;
            IDSetSwitch(&FindHomeSP, nullptr);
        }
        IDSetNumber(&DomeAbsPosNP, nullptr);
    }
    else if (status == DOME_DEROTATING)
    {
        if ((currentStatus & 2) == 0)
        {
            readS32(GetCounterExt, currentRotation);
            LOGF_INFO("Current rotation is %d", currentRotation);
            if (abs(currentRotation) < 100)
            {
                // Close enough
                status         = DOME_READY;
                DerotateSP.s   = IPS_OK;
                DomeAbsPosNP.s = IPS_OK;
                IDSetSwitch(&DerotateSP, nullptr);
            }
            else
            {
                if (currentRotation < 0)
                {
                    writeU16(CCWRotation, compensateInertia(-currentRotation));
                }
                else
                {
                    writeU16(CWRotation, compensateInertia(currentRotation));
                }
            }
        }
        IDSetNumber(&DomeAbsPosNP, nullptr);
    }
    else if (DomeAbsPosNP.s == IPS_BUSY)
    {
        if ((currentStatus & 2) == 0)
        {
            // Rotation idle, are we close enough?
            double azDiff = targetAz - DomeAbsPosN[0].value;
            if (azDiff > 180)
            {
                azDiff -= 360;
            }
            if (azDiff < -180)
            {
                azDiff += 360;
            }
            if (fabs(azDiff) <= DomeParamN[0].value)
            {
                DomeAbsPosN[0].value = targetAz;
                DomeAbsPosNP.s       = IPS_OK;
                LOG_INFO("Dome reached requested azimuth angle.");

                if (getDomeState() == DOME_PARKING)
                {
                    if (ParkShutterS[0].s == ISS_ON && getInputState(IN_CLOSED1) == ISS_OFF)
                    {
                        ControlShutter(SHUTTER_CLOSE);
                    }
                    else
                    {
                        SetParked(true);
                    }
                }
                else if (getDomeState() == DOME_UNPARKING)
                    SetParked(false);
                else
                    setDomeState(DOME_SYNCED);
            }
            else
            {
                // Refine azimuth
                MoveAbs(targetAz);
            }
        }

        IDSetNumber(&DomeAbsPosNP, nullptr);
    }
    else
        IDSetNumber(&DomeAbsPosNP, nullptr);

    // Read temperatures only every 10th time
    static int tmpCounter = 0;
    if (--tmpCounter <= 0)
    {
        UpdateSensorStatus();
        tmpCounter = 10;
    }

    SetTimer(POLLMS);
}
Exemple #5
0
/* Initialise the map structure */
GAMEMAP *mapLoad(char *filename)
{
	char		path[PATH_MAX];
	GAMEMAP		*map = (GAMEMAP *)malloc(sizeof(*map));
	uint32_t	i, j, gwVersion;
	char		aFileType[4];
	bool		littleEndian = true;
	PHYSFS_file	*fp = NULL;
	bool		counted[MAX_PLAYERS];
	uint16_t	pType;

	// this cries out for a class based design
	#define readU8(v) ( littleEndian ? PHYSFS_readULE8(fp, v) : PHYSFS_readUBE8(fp, v) )
	#define readU16(v) ( littleEndian ? PHYSFS_readULE16(fp, v) : PHYSFS_readUBE16(fp, v) )
	#define readU32(v) ( littleEndian ? PHYSFS_readULE32(fp, v) : PHYSFS_readUBE32(fp, v) )
	#define readS8(v) ( littleEndian ? PHYSFS_readSLE8(fp, v) : PHYSFS_readSBE8(fp, v) )
	#define readS16(v) ( littleEndian ? PHYSFS_readSLE16(fp, v) : PHYSFS_readSBE16(fp, v) )
	#define readS32(v) ( littleEndian ? PHYSFS_readSLE32(fp, v) : PHYSFS_readSBE32(fp, v) )
	
	/* === Load map data === */

	strcpy(path, filename);
	strcat(path, "/game.map");
	fp = PHYSFS_openRead(path);
	map->mGateways = NULL;
	map->mMapTiles = NULL;

	if (!fp)
	{
		debug(LOG_ERROR, "Could not open %s", path);
		map->mapVersion = 0;
		map->width = UINT32_MAX;
		map->height = UINT32_MAX;
		map->mMapTiles = NULL;
		goto mapfailure;
	}
	else if (PHYSFS_read(fp, aFileType, 4, 1) != 1
		|| !readU32(&map->mapVersion)
		|| !readU32(&map->width)
		|| !readU32(&map->height)
		|| aFileType[0] != 'm'
		|| aFileType[1] != 'a'
		|| aFileType[2] != 'p')
	{
		debug(LOG_ERROR, "Bad header in %s", path);
		goto failure;
	}
	else if (map->mapVersion <= 9)
	{
		debug(LOG_ERROR, "%s: Unsupported save format version %u", path, map->mapVersion);
		goto failure;
	}
	else if (map->mapVersion > 36)
	{
		debug(LOG_ERROR, "%s: Undefined save format version %u", path, map->mapVersion);
		goto failure;
	}
	else if (map->width * map->height > MAP_MAXAREA)
	{
		debug(LOG_ERROR, "Map %s too large : %d %d", path, map->width, map->height);
		goto failure;
	}

	/* Allocate the memory for the map */
	map->mMapTiles = (MAPTILE *)calloc(map->width * map->height, sizeof(*map->mMapTiles));
	if (!map->mMapTiles)
	{
		debug(LOG_ERROR, "Out of memory");
		goto failure;
	}
	
	/* Load in the map data */
	for (i = 0; i < map->width * map->height; i++)
	{
		uint16_t	texture;
		uint8_t		height;

		if (!readU16(&texture) || !readU8(&height))
		{
			debug(LOG_ERROR, "%s: Error during savegame load", path);
			goto failure;
		}

		map->mMapTiles[i].texture = static_cast<TerrainType>(texture);
		map->mMapTiles[i].height = height;
		for (j = 0; j < MAX_PLAYERS; j++)
		{
			map->mMapTiles[i].tileVisBits = (uint8_t)(map->mMapTiles[i].tileVisBits &~ (uint8_t)(1 << j));
		}
	}

	if (!readU32(&gwVersion) || !readU32(&map->numGateways) || gwVersion != 1)
	{
		debug(LOG_ERROR, "Bad gateway in %s", path);
		goto failure;
	}

	map->mGateways = (GATEWAY *)calloc(map->numGateways, sizeof(*map->mGateways));
	for (i = 0; i < map->numGateways; i++)
	{
		if (!readU8(&map->mGateways[i].x1) || !readU8(&map->mGateways[i].y1)
			|| !readU8(&map->mGateways[i].x2) || !readU8(&map->mGateways[i].y2))
		{
			debug(LOG_ERROR, "%s: Failed to read gateway info", path);
			goto failure;
		}
	}
	PHYSFS_close(fp);
mapfailure:

	/* === Load game data === */

	strcpy(path, filename);
	strcat(path, ".gam");
	fp = PHYSFS_openRead(path);
	if (!fp)
	{
		debug(LOG_ERROR, "Game file %s not found", path);
		goto failure;
	}
	else if (PHYSFS_read(fp, aFileType, 4, 1) != 1
		|| aFileType[0] != 'g'
		|| aFileType[1] != 'a'
		|| aFileType[2] != 'm'
		|| aFileType[3] != 'e'
		|| !readU32(&map->gameVersion))
	{
		debug(LOG_ERROR, "Bad header in %s", path);
		goto failure;
	}
	if (map->gameVersion > 35)	// big-endian
	{
		littleEndian = false;
	}
	if (!readU32(&map->gameTime)
		|| !readU32(&map->gameType)
		|| !readS32(&map->scrollMinX)
		|| !readS32(&map->scrollMinY)
		|| !readU32(&map->scrollMaxX)
		|| !readU32(&map->scrollMaxY)
		|| PHYSFS_read(fp, map->levelName, 20, 1) != 1)
	{
		debug(LOG_ERROR, "Bad data in %s", filename);
		goto failure;
	}
	for (i = 0; i < 8; i++)
	{
		if (map->gameVersion >= 10)
		{
			uint32_t dummy;	// extracted power, not used

			if (!readU32(&map->power[i]) || !readU32(&dummy))
			{
				debug(LOG_ERROR, "Bad power data in %s", filename);
				goto failure;
			}
		}
		else
		{
			map->power[i] = 0;	// TODO... is there a default?
		}
	}
	PHYSFS_close(fp);


	/* === Load feature data === */

	littleEndian = true;
	strcpy(path, filename);
	strcat(path, "/feat.bjo");
	fp = PHYSFS_openRead(path);
	if (!fp)
	{
		debug(LOG_ERROR, "Feature file %s not found", path);
		map->featVersion = 0;
		map->numFeatures = 0;
		map->mLndObjects[IMD_FEATURE] = NULL;
		goto featfailure;
	}
	else if (PHYSFS_read(fp, aFileType, 4, 1) != 1
		|| aFileType[0] != 'f'
		|| aFileType[1] != 'e'
		|| aFileType[2] != 'a'
		|| aFileType[3] != 't'
		|| !readU32(&map->featVersion)
		|| !readU32(&map->numFeatures))
	{
		debug(LOG_ERROR, "Bad features header in %s", path);
		goto failure;
	}
	map->mLndObjects[IMD_FEATURE] = (LND_OBJECT *)malloc(sizeof(*map->mLndObjects[IMD_FEATURE]) * map->numFeatures);
	for(i = 0; i < map->numFeatures; i++)
	{
		LND_OBJECT *psObj = &map->mLndObjects[IMD_FEATURE][i];
		int nameLength = 60;
		uint32_t dummy;
		uint8_t visibility[8];

		if (map->featVersion <= 19)
		{
			nameLength = 40;
		}
		if (PHYSFS_read(fp, psObj->name, nameLength, 1) != 1
			|| !readU32(&psObj->id)
			|| !readU32(&psObj->x) || !readU32(&psObj->y) || !readU32(&psObj->z)
			|| !readU32(&psObj->direction)
			|| !readU32(&psObj->player)
			|| !readU32(&dummy) // BOOL inFire
			|| !readU32(&dummy) // burnStart
			|| !readU32(&dummy)) // burnDamage
		{
			debug(LOG_ERROR, "Failed to read feature from %s", path);
			goto failure;
		}
		psObj->player = 0;	// work around invalid feature owner
		if (map->featVersion >= 14 && PHYSFS_read(fp, &visibility, 1, 8) != 8)
		{
			debug(LOG_ERROR, "Failed to read feature visibility from %s", path);
			goto failure;
		}
		psObj->type = 0;	// IMD LND type for feature
		// Sanity check data
		if (psObj->x >= map->width * TILE_WIDTH || psObj->y >= map->height * TILE_HEIGHT)
		{
			debug(LOG_ERROR, "Bad feature coordinate %u(%u, %u)", psObj->id, psObj->x, psObj->y);
			goto failure;
		}
	}
	PHYSFS_close(fp);
featfailure:


	/* === Load terrain data === */

	littleEndian = true;
	strcpy(path, filename);
	strcat(path, "/ttypes.ttp");
	fp = PHYSFS_openRead(path);
	if (!fp)
	{
		map->terrainVersion = 0;
		goto terrainfailure;
	}
	else if (PHYSFS_read(fp, aFileType, 4, 1) != 1
		|| aFileType[0] != 't'
		|| aFileType[1] != 't'
		|| aFileType[2] != 'y'
		|| aFileType[3] != 'p'
		|| !readU32(&map->terrainVersion)
		|| !readU32(&map->numTerrainTypes))
	{
		debug(LOG_ERROR, "Bad features header in %s", path);
		goto failure;
	}
	
	if (map->numTerrainTypes >= MAX_TILE_TEXTURES)
	{
		// Workaround for fugly map editor bug, since we can't fix the map editor
		map->numTerrainTypes = MAX_TILE_TEXTURES - 1;
	}

	// reset the terrain table
	memset(terrainTypes, 0, sizeof(terrainTypes));

	for (i = 0; i < map->numTerrainTypes; i++)
	{
		readU16(&pType);
		
		if (pType > TER_MAX)
		{
			debug(LOG_ERROR, "loadTerrainTypeMap: terrain type out of range");
			goto terrainfailure;
		}

		terrainTypes[i] = (uint8_t)pType;
	}

	if (terrainTypes[0] == 1 && terrainTypes[1] == 0 && terrainTypes[2] == 2)
	{
		map->tileset = TILESET_ARIZONA;
	}
	else if (terrainTypes[0] == 2 && terrainTypes[1] == 2 && terrainTypes[2] == 2)
	{
		map->tileset = TILESET_URBAN;
	}
	else if (terrainTypes[0] == 0 && terrainTypes[1] == 0 && terrainTypes[2] == 2)
	{
		map->tileset = TILESET_ROCKIES;
	}
	else
	{
		debug(LOG_ERROR, "Unknown terrain signature in %s: %u %u %u", path,
			  terrainTypes[0], terrainTypes[1], terrainTypes[2]);
		goto failure;
	}
	
	PHYSFS_close(fp);
terrainfailure:

	/* === Load structure data === */

	map->mLndObjects[IMD_STRUCTURE] = NULL;
	map->numStructures = 0;
	littleEndian = true;
	strcpy(path, filename);
	strcat(path, "/struct.bjo");
	map->mLndObjects[IMD_STRUCTURE] = NULL;
	fp = PHYSFS_openRead(path);
	if (fp)
	{
		if (PHYSFS_read(fp, aFileType, 4, 1) != 1
			|| aFileType[0] != 's'
			|| aFileType[1] != 't'
			|| aFileType[2] != 'r'
			|| aFileType[3] != 'u'
			|| !readU32(&map->structVersion)
			|| !readU32(&map->numStructures))
		{
			debug(LOG_ERROR, "Bad structure header in %s", path);
			goto failure;
		}
		map->mLndObjects[IMD_STRUCTURE] = (LND_OBJECT *)malloc(sizeof(*map->mLndObjects[IMD_STRUCTURE]) * map->numStructures);
		for (i = 0; i < map->numStructures; i++)
		{
			LND_OBJECT *psObj = &map->mLndObjects[IMD_STRUCTURE][i];
			int nameLength = 60;
			uint32_t dummy;
			uint8_t visibility[8], dummy8;
			int16_t dummyS16;
			int32_t dummyS32;
			char researchName[60];

			if (map->structVersion <= 19)
			{
				nameLength = 40;
			}
			if (PHYSFS_read(fp, psObj->name, nameLength, 1) != 1
				|| !readU32(&psObj->id)
				|| !readU32(&psObj->x) || !readU32(&psObj->y) || !readU32(&psObj->z)
				|| !readU32(&psObj->direction)
				|| !readU32(&psObj->player)
				|| !readU32(&dummy) // BOOL inFire
				|| !readU32(&dummy) // burnStart
				|| !readU32(&dummy) // burnDamage
				|| !readU8(&dummy8)	// status - causes structure padding
				|| !readU8(&dummy8)	// structure padding
				|| !readU8(&dummy8)	// structure padding
				|| !readU8(&dummy8) // structure padding
				|| !readS32(&dummyS32) // currentBuildPts - aligned on 4 byte boundary
				|| !readU32(&dummy) // body
				|| !readU32(&dummy) // armour
				|| !readU32(&dummy) // resistance
				|| !readU32(&dummy) // dummy1
				|| !readU32(&dummy) // subjectInc
				|| !readU32(&dummy) // timeStarted
				|| !readU32(&dummy) // output
				|| !readU32(&dummy) // capacity
				|| !readU32(&dummy)) // quantity
			{
				debug(LOG_ERROR, "Failed to read structure from %s", path);
				goto failure;
			}
			if (map->structVersion >= 12
				&& (!readU32(&dummy)	// factoryInc
					|| !readU8(&dummy8) // loopsPerformed - causes structure padding
					|| !readU8(&dummy8) // structure padding
					|| !readU8(&dummy8) // structure padding
					|| !readU8(&dummy8) // structure padding
					|| !readU32(&dummy) // powerAccrued - aligned on 4 byte boundary
					|| !readU32(&dummy) // dummy2
					|| !readU32(&dummy) // droidTimeStarted
					|| !readU32(&dummy) // timeToBuild
					|| !readU32(&dummy))) // timeStartHold
			{
				debug(LOG_ERROR, "Failed to read structure v12 part from %s", path);
				goto failure;
			}
			if (map->structVersion >= 14 && PHYSFS_read(fp, &visibility, 1, 8) != 8)
			{
				debug(LOG_ERROR, "Failed to read structure visibility from %s", path);
				goto failure;
			}
			if (map->structVersion >= 15 && PHYSFS_read(fp, researchName, nameLength, 1) != 1)
			{
				// If version < 20, then this causes no padding, but the short below
				// will still cause two bytes padding; however, if version >= 20, we
				// will cause 4 bytes padding, but the short below will eat 2 of them,
				// leaving us again with only two bytes padding before the next word.
				debug(LOG_ERROR, "Failed to read structure v15 part from %s", path);
				goto failure;
			}
			if (map->structVersion >= 17 && !readS16(&dummyS16))
			{
				debug(LOG_ERROR, "Failed to read structure v17 part from %s", path);
				goto failure;
			}
			if (map->structVersion >= 15 && !readS16(&dummyS16))	// structure padding
			{
				debug(LOG_ERROR, "Failed to read 16 bits of structure padding from %s", path);
				goto failure;
			}
			if (map->structVersion >= 21 && !readU32(&dummy))
			{
				debug(LOG_ERROR, "Failed to read structure v21 part from %s", path);
				goto failure;
			}
			psObj->type = IMD_STRUCTURE;
			// Sanity check data
			if (psObj->player > MAX_PLAYERS)
			{
				debug(LOG_ERROR, "Bad structure owner %u for structure %d id=%u", psObj->player, i, psObj->id);
				goto failure;
			}
			if (psObj->x >= map->width * TILE_WIDTH || psObj->y >= map->height * TILE_HEIGHT)
			{
				debug(LOG_ERROR, "Bad structure %d coordinate %u(%u, %u)", i, psObj->id, psObj->x, psObj->y);
				goto failure;
			}
		}
		PHYSFS_close(fp);
	}


	/* === Load droid data === */

	map->mLndObjects[IMD_DROID] = NULL;
	map->numDroids = 0;
	littleEndian = true;
	strcpy(path, filename);
	strcat(path, "/dinit.bjo");
	map->mLndObjects[IMD_DROID] = NULL;
	fp = PHYSFS_openRead(path);
	if (fp)
	{
		if (PHYSFS_read(fp, aFileType, 4, 1) != 1
			|| aFileType[0] != 'd'
			|| aFileType[1] != 'i'
			|| aFileType[2] != 'n'
			|| aFileType[3] != 't'
			|| !readU32(&map->droidVersion)
			|| !readU32(&map->numDroids))
		{
			debug(LOG_ERROR, "Bad droid header in %s", path);
			goto failure;
		}
		map->mLndObjects[IMD_DROID] = (LND_OBJECT *)malloc(sizeof(*map->mLndObjects[IMD_DROID]) * map->numDroids);
		for (i = 0; i < map->numDroids; i++)
		{
			LND_OBJECT *psObj = &map->mLndObjects[IMD_DROID][i];
			int nameLength = 60;
			uint32_t dummy;

			if (map->droidVersion <= 19)
			{
				nameLength = 40;
			}
			if (PHYSFS_read(fp, psObj->name, nameLength, 1) != 1
				|| !readU32(&psObj->id)
				|| !readU32(&psObj->x) || !readU32(&psObj->y) || !readU32(&psObj->z)
				|| !readU32(&psObj->direction)
				|| !readU32(&psObj->player)
				|| !readU32(&dummy) // BOOL inFire
				|| !readU32(&dummy) // burnStart
				|| !readU32(&dummy)) // burnDamage
			{
				debug(LOG_ERROR, "Failed to read droid from %s", path);
				goto failure;
			}
			psObj->type = IMD_DROID;
			// Sanity check data
			if (psObj->x >= map->width * TILE_WIDTH || psObj->y >= map->height * TILE_HEIGHT)
			{
				debug(LOG_ERROR, "Bad droid coordinate %u(%u, %u)", psObj->id, psObj->x, psObj->y);
				goto failure;
			}
		}
		PHYSFS_close(fp);
	}

	// Count players by looking for the obligatory construction droids
	map->numPlayers = 0;
	memset(counted, 0, sizeof(counted));
	for(i = 0; i < map->numDroids; i++)
	{
		LND_OBJECT *psObj = &map->mLndObjects[IMD_DROID][i];

		if (counted[psObj->player] == false && (strcmp(psObj->name, "ConstructorDroid") == 0 || strcmp(psObj->name, "ConstructionDroid") == 0))
		{
			counted[psObj->player] = true;
			map->numPlayers++;
		}
	}

	return map;

failure:
	mapFree(map);
	if (fp)
	{
		PHYSFS_close(fp);
	}
	return NULL;
}
/*
	sender_peer_id given to this shall be quaranteed to be a valid peer
*/
void Client::ProcessData(NetworkPacket *pkt) {
	DSTACK(__FUNCTION_NAME);

	ScopeProfiler sp(g_profiler, "Client::ProcessData");

	auto datasize = pkt->getSize();
	auto sender_peer_id = pkt->getPeerId();

	int command;
	MsgpackPacket packet;
	msgpack::unpacked msg;

	if (!con::parse_msgpack_packet(pkt->getString(0), datasize, &packet, &command, &msg)) {
		// invalid packet
		return;
	}

	//infostream<<"Client: received command="<<command<<std::endl;
	m_packetcounter.add((u16)command);

	/*
		If this check is removed, be sure to change the queue
		system to know the ids
	*/
	if(sender_peer_id != PEER_ID_SERVER)
	{
		infostream<<"Client::ProcessData(): Discarding data not "
				"coming from server: peer_id="<<sender_peer_id
				<<std::endl;
		return;
	}

	u8 ser_version = m_server_ser_ver;

	//infostream<<"Client received command="<<(int)command<<std::endl;

	if(command == TOCLIENT_INIT)
	{
		u8 deployed;
		packet[TOCLIENT_INIT_DEPLOYED].convert(&deployed);

		infostream<<"Client: TOCLIENT_INIT received with "
				"deployed="<<((int)deployed&0xff)<<std::endl;

		if(!ser_ver_supported(deployed))
		{
			infostream<<"Client: TOCLIENT_INIT: Server sent "
					<<"unsupported ser_fmt_ver"<<std::endl;
			return;
		}

		m_server_ser_ver = deployed;

		// Set player position
		Player *player = m_env.getLocalPlayer();
		if(!player)
			return;

		packet[TOCLIENT_INIT_SEED].convert(&m_map_seed);
		infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;

		packet[TOCLIENT_INIT_STEP].convert(&m_recommended_send_interval);
		infostream<<"Client: received recommended send interval "
				<<m_recommended_send_interval<<std::endl;

		// TOCLIENT_INIT_POS

		if (m_localserver) {
			Settings settings;
			packet[TOCLIENT_INIT_MAP_PARAMS].convert(&settings);
			m_localserver->getEmergeManager()->params.load(settings);
		}

		// Reply to server
		MSGPACK_PACKET_INIT(TOSERVER_INIT2, 0);
		m_con.Send(PEER_ID_SERVER, 1, buffer, true);

		m_state = LC_Init;

		return;
	}

	if(command == TOCLIENT_ACCESS_DENIED_LEGACY)
	{
		// The server didn't like our password. Note, this needs
		// to be processed even if the serialisation format has
		// not been agreed yet, the same as TOCLIENT_INIT.
		m_access_denied = true;
		packet[TOCLIENT_ACCESS_DENIED_CUSTOM_STRING].convert(&m_access_denied_reason);
		return;
	}

	if(ser_version == SER_FMT_VER_INVALID)
	{
		infostream<<"Client: Server serialization"
				" format invalid or not initialized."
				" Skipping incoming command="<<command<<std::endl;
		return;
	}

	/*
	  Handle runtime commands
	*/
	// there's no sane reason why we shouldn't have a player and
	// almost everyone needs a player reference
	Player *player = m_env.getLocalPlayer();
	if(!player)
		return;

	if(command == TOCLIENT_REMOVENODE)
	{
		v3s16 p = packet[TOCLIENT_REMOVENODE_POS].as<v3s16>();
		removeNode(p, 2); //use light from top node
	}
	else if(command == TOCLIENT_ADDNODE)
	{
		v3s16 p = packet[TOCLIENT_ADDNODE_POS].as<v3s16>();
		MapNode n = packet[TOCLIENT_ADDNODE_NODE].as<MapNode>();
		bool remove_metadata = packet[TOCLIENT_ADDNODE_REMOVE_METADATA].as<bool>();

		addNode(p, n, remove_metadata, 2); //fast add
	}
	else if(command == TOCLIENT_BLOCKDATA)
	{
		v3s16 p = packet[TOCLIENT_BLOCKDATA_POS].as<v3s16>();
		s8 step = 1;
		packet[TOCLIENT_BLOCKDATA_STEP].convert(&step);
		if (step == 1) {

		std::istringstream istr(packet[TOCLIENT_BLOCKDATA_DATA].as<std::string>(), std::ios_base::binary);

		MapBlock *block;

		block = m_env.getMap().getBlockNoCreateNoEx(p);
		bool new_block = !block;
		if (new_block)
			block = new MapBlock(&m_env.getMap(), p, this);

		block->deSerialize(istr, ser_version, false);
		s32 h; // for convert to atomic
		packet[TOCLIENT_BLOCKDATA_HEAT].convert(&h);
		block->heat = h;
		packet[TOCLIENT_BLOCKDATA_HUMIDITY].convert(&h);
		block->humidity = h;


		if (packet.count(TOCLIENT_BLOCKDATA_CONTENT_ONLY))
			block->content_only = packet[TOCLIENT_BLOCKDATA_CONTENT_ONLY].as<content_t>();


		if (m_localserver != NULL) {
			m_localserver->getMap().saveBlock(block);
		}

		if (new_block)
			if (!m_env.getMap().insertBlock(block))
				delete block;

		/*
			//Add it to mesh update queue and set it to be acknowledged after update.
		*/
		//infostream<<"Adding mesh update task for received block "<<p<<std::endl;
		updateMeshTimestampWithEdge(p);
		if (block->content_only != CONTENT_IGNORE && block->content_only != CONTENT_AIR) {
			if (getNodeBlockPos(floatToInt(m_env.getLocalPlayer()->getPosition(), BS)).getDistanceFrom(p) <= 1)
				addUpdateMeshTaskWithEdge(p);
		}

/*
#if !defined(NDEBUG)
		if (m_env.getClientMap().m_block_boundary.size() > 150)
			m_env.getClientMap().m_block_boundary.clear();
		m_env.getClientMap().m_block_boundary[p] = block;
#endif
*/

		}//step

	}
	else if(command == TOCLIENT_INVENTORY)
	{
		std::string datastring = packet[TOCLIENT_INVENTORY_DATA].as<std::string>();
		std::istringstream is(datastring, std::ios_base::binary);
		Player *player = m_env.getLocalPlayer();
		if(!player)
			return;

		player->inventory.deSerialize(is);

		m_inventory_updated = true;

		delete m_inventory_from_server;
		m_inventory_from_server = new Inventory(player->inventory);
		m_inventory_from_server_age = 0.0;
	}
	else if(command == TOCLIENT_TIME_OF_DAY)
	{
		u16 time_of_day = packet[TOCLIENT_TIME_OF_DAY_TIME].as<u16>();
		time_of_day = time_of_day % 24000;
		f32 time_speed = packet[TOCLIENT_TIME_OF_DAY_TIME_SPEED].as<f32>();

		// Update environment
		m_env.setTimeOfDay(time_of_day);
		m_env.setTimeOfDaySpeed(time_speed);
		m_time_of_day_set = true;

		u32 dr = m_env.getDayNightRatio();
		verbosestream<<"Client: time_of_day="<<time_of_day
				<<" time_speed="<<time_speed
				<<" dr="<<dr<<std::endl;
	}
	else if(command == TOCLIENT_CHAT_MESSAGE)
	{
		std::string message = packet[TOCLIENT_CHAT_MESSAGE_DATA].as<std::string>();
		m_chat_queue.push(message);
	}
	else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
	{
		std::vector<u16> removed_objects;
		packet[TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD_REMOVE].convert(&removed_objects);
		for (size_t i = 0; i < removed_objects.size(); ++i)
			m_env.removeActiveObject(removed_objects[i]);

		std::vector<ActiveObjectAddData> added_objects;
		packet[TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD_ADD].convert(&added_objects);
		for (size_t i = 0; i < added_objects.size(); ++i)
			m_env.addActiveObject(added_objects[i].id, added_objects[i].type, added_objects[i].data);
	}
	else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
	{
		ActiveObjectMessages messages;
		packet[TOCLIENT_ACTIVE_OBJECT_MESSAGES_MESSAGES].convert(&messages);
		for (size_t i = 0; i < messages.size(); ++i)
			m_env.processActiveObjectMessage(messages[i].first, messages[i].second);
	}
	else if(command == TOCLIENT_MOVEMENT)
	{
		Player *player = m_env.getLocalPlayer();
		packet[TOCLIENT_MOVEMENT_ACCELERATION_DEFAULT].convert(&player->movement_acceleration_default);
		packet[TOCLIENT_MOVEMENT_ACCELERATION_AIR].convert(&player->movement_acceleration_air);
		packet[TOCLIENT_MOVEMENT_ACCELERATION_FAST].convert(&player->movement_acceleration_fast);
		packet[TOCLIENT_MOVEMENT_SPEED_WALK].convert(&player->movement_speed_walk);
		packet[TOCLIENT_MOVEMENT_SPEED_CROUCH].convert(&player->movement_speed_crouch);
		packet[TOCLIENT_MOVEMENT_SPEED_FAST].convert(&player->movement_speed_fast);
		packet[TOCLIENT_MOVEMENT_SPEED_CLIMB].convert(&player->movement_speed_climb);
		packet[TOCLIENT_MOVEMENT_SPEED_JUMP].convert(&player->movement_speed_jump);
		packet[TOCLIENT_MOVEMENT_LIQUID_FLUIDITY].convert(&player->movement_liquid_fluidity);
		packet[TOCLIENT_MOVEMENT_LIQUID_FLUIDITY_SMOOTH].convert(&player->movement_liquid_fluidity_smooth);
		packet[TOCLIENT_MOVEMENT_LIQUID_SINK].convert(&player->movement_liquid_sink);
		packet[TOCLIENT_MOVEMENT_GRAVITY].convert(&player->movement_gravity);
	}
	else if(command == TOCLIENT_HP)
	{
		Player *player = m_env.getLocalPlayer();
		if(!player)
			return;

		u8 oldhp = player->hp;
		u8 hp = packet[TOCLIENT_HP_HP].as<u8>();
		player->hp = hp;

		if(hp < oldhp)
		{
			// Add to ClientEvent queue
			ClientEvent event;
			event.type = CE_PLAYER_DAMAGE;
			event.player_damage.amount = oldhp - hp;
			m_client_event_queue.push(event);
		}
	}
	else if(command == TOCLIENT_BREATH)
	{
		Player *player = m_env.getLocalPlayer();
		player->setBreath(packet[TOCLIENT_BREATH_BREATH].as<u16>()) ;
	}
	else if(command == TOCLIENT_MOVE_PLAYER)
	{
		Player *player = m_env.getLocalPlayer();
		if(!player)
			return;

		v3f pos = packet[TOCLIENT_MOVE_PLAYER_POS].as<v3f>();
		f32 pitch = packet[TOCLIENT_MOVE_PLAYER_PITCH].as<f32>();
		f32 yaw = packet[TOCLIENT_MOVE_PLAYER_YAW].as<f32>();
		player->setPosition(pos);

		infostream<<"Client got TOCLIENT_MOVE_PLAYER"
				<<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
				<<" pitch="<<pitch
				<<" yaw="<<yaw
				<<std::endl;

		/*
			Add to ClientEvent queue.
			This has to be sent to the main program because otherwise
			it would just force the pitch and yaw values to whatever
			the camera points to.
		*/
		ClientEvent event;
		event.type = CE_PLAYER_FORCE_MOVE;
		event.player_force_move.pitch = pitch;
		event.player_force_move.yaw = yaw;
		m_client_event_queue.push(event);

		// Ignore damage for a few seconds, so that the player doesn't
		// get damage from falling on ground
		m_ignore_damage_timer = 3.0;
	}
	else if(command == TOCLIENT_DEATHSCREEN)
	{
		bool set_camera_point_target = packet[TOCLIENT_DEATHSCREEN_SET_CAMERA].as<bool>();
		v3f camera_point_target = packet[TOCLIENT_DEATHSCREEN_CAMERA_POINT].as<v3f>();

		ClientEvent event;
		event.type = CE_DEATHSCREEN;
		event.deathscreen.set_camera_point_target = set_camera_point_target;
		event.deathscreen.camera_point_target_x = camera_point_target.X;
		event.deathscreen.camera_point_target_y = camera_point_target.Y;
		event.deathscreen.camera_point_target_z = camera_point_target.Z;
		m_client_event_queue.push(event);
	}
	else if(command == TOCLIENT_ANNOUNCE_MEDIA)
	{
		if (m_media_downloader == NULL ||
				m_media_downloader->isStarted()) {
			const char *problem = m_media_downloader ?
				"we already saw another announcement" :
				"all media has been received already";
			errorstream<<"Client: Received media announcement but "
				<<problem<<"!"
				<<std::endl;
			return;
		}

		// Mesh update thread must be stopped while
		// updating content definitions
		//assert(!m_mesh_update_thread.IsRunning());

		MediaAnnounceList announce_list;
		packet[TOCLIENT_ANNOUNCE_MEDIA_LIST].convert(&announce_list);
		for (size_t i = 0; i < announce_list.size(); ++i)
			m_media_downloader->addFile(announce_list[i].first, base64_decode(announce_list[i].second));

		std::vector<std::string> remote_media;
		std::string remote_media_string = packet[TOCLIENT_ANNOUNCE_MEDIA_REMOTE_SERVER].as<std::string>();
		Strfnd sf(remote_media_string);
		while(!sf.atend()) {
			std::string baseurl = trim(sf.next(","));
			if(baseurl != "")
				m_media_downloader->addRemoteServer(baseurl);
		}

		m_media_downloader->step(this);
	}
	else if(command == TOCLIENT_MEDIA)
	{
		MediaData media_data;
		packet[TOCLIENT_MEDIA_MEDIA].convert(&media_data);

		// Mesh update thread must be stopped while
		// updating content definitions
		//assert(!m_mesh_update_thread.IsRunning());

		for(size_t i = 0; i < media_data.size(); ++i)
			m_media_downloader->conventionalTransferDone(
					media_data[i].first, media_data[i].second, this);
	}
	else if(command == TOCLIENT_NODEDEF)
	{
		infostream<<"Client: Received node definitions: packet size: "
				<<datasize<<std::endl;

		// Mesh update thread must be stopped while
		// updating content definitions
		//assert(!m_mesh_update_thread.IsRunning());

		packet[TOCLIENT_NODEDEF_DEFINITIONS].convert(m_nodedef);
		m_nodedef_received = true;
	}
	else if(command == TOCLIENT_ITEMDEF)
	{
		infostream<<"Client: Received item definitions: packet size: "
				<<datasize<<std::endl;

		// Mesh update thread must be stopped while
		// updating content definitions
		//assert(!m_mesh_update_thread.IsRunning());

		packet[TOCLIENT_ITEMDEF_DEFINITIONS].convert(m_itemdef);
		m_itemdef_received = true;
	}
	else if(command == TOCLIENT_PLAY_SOUND)
	{
		s32 server_id = packet[TOCLIENT_PLAY_SOUND_ID].as<s32>();
		std::string name = packet[TOCLIENT_PLAY_SOUND_NAME].as<std::string>();
		float gain = packet[TOCLIENT_PLAY_SOUND_GAIN].as<f32>();
		int type = packet[TOCLIENT_PLAY_SOUND_TYPE].as<u8>(); // 0=local, 1=positional, 2=object
		v3f pos = packet[TOCLIENT_PLAY_SOUND_POS].as<v3f>();
		u16 object_id = packet[TOCLIENT_PLAY_SOUND_OBJECT_ID].as<u16>();
		bool loop = packet[TOCLIENT_PLAY_SOUND_LOOP].as<bool>();
		// Start playing
		int client_id = -1;
		switch(type){
		case 0: // local
			client_id = m_sound->playSound(name, loop, gain);
			break;
		case 1: // positional
			client_id = m_sound->playSoundAt(name, loop, gain, pos);
			break;
		case 2: { // object
			ClientActiveObject *cao = m_env.getActiveObject(object_id);
			if(cao)
				pos = cao->getPosition();
			client_id = m_sound->playSoundAt(name, loop, gain, pos);
			// TODO: Set up sound to move with object
			break; }
		default:
			break;
		}
		if(client_id != -1){
			m_sounds_server_to_client[server_id] = client_id;
			m_sounds_client_to_server[client_id] = server_id;
			if(object_id != 0)
				m_sounds_to_objects[client_id] = object_id;
		}
	}
	else if(command == TOCLIENT_STOP_SOUND)
	{
		s32 server_id = packet[TOCLIENT_STOP_SOUND_ID].as<s32>();
		std::map<s32, int>::iterator i =
				m_sounds_server_to_client.find(server_id);
		if(i != m_sounds_server_to_client.end()){
			int client_id = i->second;
			m_sound->stopSound(client_id);
		}
	}
	else if(command == TOCLIENT_PRIVILEGES)
	{
		packet[TOCLIENT_PRIVILEGES_PRIVILEGES].convert(&m_privileges);
	}
	else if(command == TOCLIENT_INVENTORY_FORMSPEC)
	{
		// Store formspec in LocalPlayer
		player->inventory_formspec = packet[TOCLIENT_INVENTORY_FORMSPEC_DATA].as<std::string>();
	}
	else if(command == TOCLIENT_DETACHED_INVENTORY)
	{
		std::string name = packet[TOCLIENT_DETACHED_INVENTORY_NAME].as<std::string>();
		std::string datastring = packet[TOCLIENT_DETACHED_INVENTORY_DATA].as<std::string>();
		std::istringstream is(datastring, std::ios_base::binary);

		infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;

		Inventory *inv = NULL;
		if(m_detached_inventories.count(name) > 0)
			inv = m_detached_inventories[name];
		else{
			inv = new Inventory(m_itemdef);
			m_detached_inventories[name] = inv;
		}
		inv->deSerialize(is);
	}
	else if(command == TOCLIENT_SHOW_FORMSPEC)
	{
		std::string formspec = packet[TOCLIENT_SHOW_FORMSPEC_DATA].as<std::string>();
		std::string formname = packet[TOCLIENT_SHOW_FORMSPEC_NAME].as<std::string>();

		ClientEvent event;
		event.type = CE_SHOW_FORMSPEC;
		// pointer is required as event is a struct only!
		// adding a std:string to a struct isn't possible
		event.show_formspec.formspec = new std::string(formspec);
		event.show_formspec.formname = new std::string(formname);
		m_client_event_queue.push(event);
	}
	else if(command == TOCLIENT_SPAWN_PARTICLE)
	{
		v3f pos = packet[TOCLIENT_SPAWN_PARTICLE_POS].as<v3f>();
		v3f vel = packet[TOCLIENT_SPAWN_PARTICLE_VELOCITY].as<v3f>();
		v3f acc = packet[TOCLIENT_SPAWN_PARTICLE_ACCELERATION].as<v3f>();
		float expirationtime = packet[TOCLIENT_SPAWN_PARTICLE_EXPIRATIONTIME].as<float>();
		float size = packet[TOCLIENT_SPAWN_PARTICLE_SIZE].as<float>();
		bool collisiondetection = packet[TOCLIENT_SPAWN_PARTICLE_COLLISIONDETECTION].as<bool>();
		std::string texture = packet[TOCLIENT_SPAWN_PARTICLE_TEXTURE].as<std::string>();
		bool vertical = packet[TOCLIENT_SPAWN_PARTICLE_VERTICAL].as<bool>();

		ClientEvent event;
		event.type = CE_SPAWN_PARTICLE;
		event.spawn_particle.pos = new v3f (pos);
		event.spawn_particle.vel = new v3f (vel);
		event.spawn_particle.acc = new v3f (acc);

		event.spawn_particle.expirationtime = expirationtime;
		event.spawn_particle.size = size;
		event.spawn_particle.collisiondetection =
				collisiondetection;
		event.spawn_particle.vertical = vertical;
		event.spawn_particle.texture = new std::string(texture);

		m_client_event_queue.push(event);
	}
	else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
	{
		u16 amount;
		float spawntime, minexptime, maxexptime, minsize, maxsize;
		v3f minpos, maxpos, minvel, maxvel, minacc, maxacc;
		bool collisiondetection, vertical;
		u32 id;
		std::string texture;

		packet[TOCLIENT_ADD_PARTICLESPAWNER_AMOUNT].convert(&amount);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_SPAWNTIME].convert(&spawntime);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_MINPOS].convert(&minpos);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_MAXPOS].convert(&maxpos);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_MINVEL].convert(&minvel);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_MAXVEL].convert(&maxvel);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_MINACC].convert(&minacc);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_MAXACC].convert(&maxacc);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_MINEXPTIME].convert(&minexptime);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_MAXEXPTIME].convert(&maxexptime);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_MINSIZE].convert(&minsize);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_MAXSIZE].convert(&maxsize);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_COLLISIONDETECTION].convert(&collisiondetection);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_TEXTURE].convert(&texture);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_VERTICAL].convert(&vertical);
		packet[TOCLIENT_ADD_PARTICLESPAWNER_ID].convert(&id);

		ClientEvent event;
		event.type = CE_ADD_PARTICLESPAWNER;
		event.add_particlespawner.amount = amount;
		event.add_particlespawner.spawntime = spawntime;

		event.add_particlespawner.minpos = new v3f (minpos);
		event.add_particlespawner.maxpos = new v3f (maxpos);
		event.add_particlespawner.minvel = new v3f (minvel);
		event.add_particlespawner.maxvel = new v3f (maxvel);
		event.add_particlespawner.minacc = new v3f (minacc);
		event.add_particlespawner.maxacc = new v3f (maxacc);

		event.add_particlespawner.minexptime = minexptime;
		event.add_particlespawner.maxexptime = maxexptime;
		event.add_particlespawner.minsize = minsize;
		event.add_particlespawner.maxsize = maxsize;
		event.add_particlespawner.collisiondetection = collisiondetection;
		event.add_particlespawner.vertical = vertical;
		event.add_particlespawner.texture = new std::string(texture);
		event.add_particlespawner.id = id;

		m_client_event_queue.push(event);
	}
	else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
	{
		u32 id = packet[TOCLIENT_DELETE_PARTICLESPAWNER_ID].as<u32>();

		ClientEvent event;
		event.type = CE_DELETE_PARTICLESPAWNER;
		event.delete_particlespawner.id = id;

		m_client_event_queue.push(event);
	}
	else if(command == TOCLIENT_HUDADD)
	{
		//std::string datastring((char *)&data[2], datasize - 2);
		//std::istringstream is(datastring, std::ios_base::binary);

		u32 id, number, item, dir;
		u8 type;
		v2f pos, scale, align, offset;
		std::string name, text;
		v3f world_pos;
		v2s32 size;

		packet[TOCLIENT_HUDADD_ID].convert(&id);
		packet[TOCLIENT_HUDADD_TYPE].convert(&type);
		packet[TOCLIENT_HUDADD_POS].convert(&pos);
		packet[TOCLIENT_HUDADD_NAME].convert(&name);
		packet[TOCLIENT_HUDADD_SCALE].convert(&scale);
		packet[TOCLIENT_HUDADD_TEXT].convert(&text);
		packet[TOCLIENT_HUDADD_NUMBER].convert(&number);
		packet[TOCLIENT_HUDADD_ITEM].convert(&item);
		packet[TOCLIENT_HUDADD_DIR].convert(&dir);
		packet[TOCLIENT_HUDADD_ALIGN].convert(&align);
		packet[TOCLIENT_HUDADD_OFFSET].convert(&offset);
		packet[TOCLIENT_HUDADD_WORLD_POS].convert(&world_pos);
		packet[TOCLIENT_HUDADD_SIZE].convert(&size);

		ClientEvent event;
		event.type = CE_HUDADD;
		event.hudadd.id     = id;
		event.hudadd.type   = type;
		event.hudadd.pos    = new v2f(pos);
		event.hudadd.name   = new std::string(name);
		event.hudadd.scale  = new v2f(scale);
		event.hudadd.text   = new std::string(text);
		event.hudadd.number = number;
		event.hudadd.item   = item;
		event.hudadd.dir    = dir;
		event.hudadd.align  = new v2f(align);
		event.hudadd.offset = new v2f(offset);
		event.hudadd.world_pos = new v3f(world_pos);
		event.hudadd.size      = new v2s32(size);
		m_client_event_queue.push(event);
	}
	else if(command == TOCLIENT_HUDRM)
	{
		u32 id = packet[TOCLIENT_HUDRM_ID].as<u32>();

		ClientEvent event;
		event.type = CE_HUDRM;
		event.hudrm.id = id;
		m_client_event_queue.push(event);
	}
	else if(command == TOCLIENT_HUDCHANGE)
	{
		std::string sdata;
		v2f v2fdata;
		v3f v3fdata;
		v2s32 v2s32data;
		u32 intdata = 0;

		u32 id = packet[TOCLIENT_HUDCHANGE_ID].as<u32>();
		u8 stat = packet[TOCLIENT_HUDCHANGE_STAT].as<int>();

		if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
				stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
			packet[TOCLIENT_HUDCHANGE_V2F].convert(&v2fdata);
		else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
			packet[TOCLIENT_HUDCHANGE_STRING].convert(&sdata);
		else if (stat == HUD_STAT_WORLD_POS)
			packet[TOCLIENT_HUDCHANGE_V3F].convert(&v3fdata);
		else if (stat == HUD_STAT_SIZE)
			packet[TOCLIENT_HUDCHANGE_V2S32].convert(&v2s32data);
		else
			packet[TOCLIENT_HUDCHANGE_U32].convert(&intdata);

		ClientEvent event;
		event.type = CE_HUDCHANGE;
		event.hudchange.id      = id;
		event.hudchange.stat    = (HudElementStat)stat;
		event.hudchange.v2fdata = new v2f(v2fdata);
		event.hudchange.v3fdata = new v3f(v3fdata);
		event.hudchange.sdata   = new std::string(sdata);
		event.hudchange.data    = intdata;
		event.hudchange.v2s32data = new v2s32(v2s32data);
		m_client_event_queue.push(event);
	}
	else if(command == TOCLIENT_HUD_SET_FLAGS)
	{
		Player *player = m_env.getLocalPlayer();
		if(!player)
			return;

		u32 flags = packet[TOCLIENT_HUD_SET_FLAGS_FLAGS].as<u32>();
		u32 mask = packet[TOCLIENT_HUD_SET_FLAGS_MASK].as<u32>();

		player->hud_flags &= ~mask;
		player->hud_flags |= flags;
	}
	else if(command == TOCLIENT_HUD_SET_PARAM)
	{
		u16 param = packet[TOCLIENT_HUD_SET_PARAM_ID].as<u16>();
		std::string value = packet[TOCLIENT_HUD_SET_PARAM_VALUE].as<std::string>();

		if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
			s32 hotbar_itemcount = readS32((u8*) value.c_str());
			if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
				player->hud_hotbar_itemcount = hotbar_itemcount;
		} else if (param == HUD_PARAM_HOTBAR_IMAGE) {
			((LocalPlayer *) player)->hotbar_image = value;
		} else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
			((LocalPlayer *) player)->hotbar_selected_image = value;
		}
	}
/*
	else if(command == TOCLIENT_ANIMATIONS)
	{
		LocalPlayer *player = m_env.getLocalPlayer();
		packet[TOCLIENT_ANIMATIONS_DEFAULT_START].convert(&player->animation_default_start);
		packet[TOCLIENT_ANIMATIONS_DEFAULT_STOP].convert(&player->animation_default_stop);
		packet[TOCLIENT_ANIMATIONS_WALK_START].convert(&player->animation_walk_start);
		packet[TOCLIENT_ANIMATIONS_WALK_STOP].convert(&player->animation_walk_stop);
		packet[TOCLIENT_ANIMATIONS_DIG_START].convert(&player->animation_dig_start);
		packet[TOCLIENT_ANIMATIONS_DIG_STOP].convert(&player->animation_dig_stop);
		packet[TOCLIENT_ANIMATIONS_WD_START].convert(&player->animation_wd_start);
		packet[TOCLIENT_ANIMATIONS_WD_STOP].convert(&player->animation_wd_stop);
	}
*/
	else if(command == TOCLIENT_SET_SKY)
	{
		video::SColor *bgcolor = new video::SColor(packet[TOCLIENT_SET_SKY_COLOR].as<video::SColor>());
		std::string *type = new std::string(packet[TOCLIENT_SET_SKY_TYPE].as<std::string>());
		std::vector<std::string> *params = new std::vector<std::string>;
		packet[TOCLIENT_SET_SKY_PARAMS].convert(params);

		ClientEvent event;
		event.type            = CE_SET_SKY;
		event.set_sky.bgcolor = bgcolor;
		event.set_sky.type    = type;
		event.set_sky.params  = params;
		m_client_event_queue.push(event);
	}
	else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO)
	{
		bool do_override;
		float day_night_ratio_f;
		packet[TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO_DO].convert(&do_override);
		packet[TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO_VALUE].convert(&day_night_ratio_f);

		ClientEvent event;
		event.type                                 = CE_OVERRIDE_DAY_NIGHT_RATIO;
		event.override_day_night_ratio.do_override = do_override;
		event.override_day_night_ratio.ratio_f     = day_night_ratio_f;
		m_client_event_queue.push(event);
	}
	else if(command == TOCLIENT_LOCAL_PLAYER_ANIMATIONS)
	{
		LocalPlayer *player = m_env.getLocalPlayer();
		if(!player)
			return;

		packet[TOCLIENT_LOCAL_PLAYER_ANIMATIONS_IDLE].convert(&player->local_animations[0]);
		packet[TOCLIENT_LOCAL_PLAYER_ANIMATIONS_WALK].convert(&player->local_animations[1]);
		packet[TOCLIENT_LOCAL_PLAYER_ANIMATIONS_DIG].convert(&player->local_animations[2]);
		packet[TOCLIENT_LOCAL_PLAYER_ANIMATIONS_WALKDIG].convert(&player->local_animations[3]);
		packet[TOCLIENT_LOCAL_PLAYER_ANIMATIONS_FRAME_SPEED].convert(&player->local_animation_speed);
	}
	else if(command == TOCLIENT_EYE_OFFSET)
	{
		LocalPlayer *player = m_env.getLocalPlayer();
		if(!player)
			return;

		packet[TOCLIENT_EYE_OFFSET_FIRST].convert(&player->eye_offset_first);
		packet[TOCLIENT_EYE_OFFSET_THIRD].convert(&player->eye_offset_third);
	}
	else
	{
		infostream<<"Client: Ignoring unknown command "
				<<command<<std::endl;
	}
}
void pnVaultNode::read(const unsigned char* buffer, size_t size) {
    if (size < sizeof(uint64_t)) {
        plDebug::Error("Invalid node data");
        return;
    }
    fDirtyMask = 0;
    fDirtySize = 0;
    fFieldMask = readU64(buffer, size);
    fCachedSize = size;

    for (size_t bit=0; bit<kNumFields; bit++) {
        if ((fFieldMask & (1ULL<<bit)) == 0)
            continue;

        switch (bit) {
        case kNodeIdx:
            fNodeIdx = readU32(buffer, size);
            break;
        case kCreateTime:
            fCreateTime = readU32(buffer, size);
            break;
        case kModifyTime:
            fModifyTime = readU32(buffer, size);
            break;
        case kCreateAgeName:
            fCreateAgeName = readString(buffer, size);
            break;
        case kCreateAgeUuid:
            fCreateAgeUuid = readUuid(buffer, size);
            break;
        case kCreatorUuid:
            fCreatorUuid = readUuid(buffer, size);
            break;
        case kCreatorIdx:
            fCreatorIdx = readU32(buffer, size);
            break;
        case kNodeType:
            fNodeType = readU32(buffer, size);
            break;
        case kInt32_1:
        case kInt32_2:
        case kInt32_3:
        case kInt32_4:
            fInt32[bit - kInt32_1] = readS32(buffer, size);
            break;
        case kUint32_1:
        case kUint32_2:
        case kUint32_3:
        case kUint32_4:
            fUint32[bit - kUint32_1] = readU32(buffer, size);
            break;
        case kUuid_1:
        case kUuid_2:
        case kUuid_3:
        case kUuid_4:
            fUuid[bit - kUuid_1] = readUuid(buffer, size);
            break;
        case kString64_1:
        case kString64_2:
        case kString64_3:
        case kString64_4:
        case kString64_5:
        case kString64_6:
            fString64[bit - kString64_1] = readString(buffer, size);
            break;
        case kIString64_1:
        case kIString64_2:
            fIString64[bit - kIString64_1] = readString(buffer, size);
            break;
        case kText_1:
        case kText_2:
            fText[bit - kText_1] = readString(buffer, size);
            break;
        case kBlob_1:
        case kBlob_2:
            {
                size_t len = readU32(buffer, size);
                fBlob[bit - kBlob_1].setData(len, buffer);
                buffer += len;
                size -= len;
            }
            break;
        }
    }

    if (size != 0)
        plDebug::Warning("Incomplete read of node %d", fNodeIdx);
}
/*
	sender_peer_id given to this shall be quaranteed to be a valid peer
*/
void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
{
	DSTACK(__FUNCTION_NAME);

	// Ignore packets that don't even fit a command
	if(datasize < 2)
	{
		m_packetcounter.add(60000);
		return;
	}

	ToClientCommand command = (ToClientCommand)readU16(&data[0]);

	//infostream<<"Client: received command="<<command<<std::endl;
	m_packetcounter.add((u16)command);

	/*
		If this check is removed, be sure to change the queue
		system to know the ids
	*/
	if(sender_peer_id != PEER_ID_SERVER)
	{
		infostream<<"Client::ProcessData(): Discarding data not "
				"coming from server: peer_id="<<sender_peer_id
				<<std::endl;
		return;
	}

	u8 ser_version = m_server_ser_ver;

	//infostream<<"Client received command="<<(int)command<<std::endl;

	if(command == TOCLIENT_INIT)
	{
		if(datasize < 3)
			return;

		u8 deployed = data[2];

		infostream<<"Client: TOCLIENT_INIT received with "
				"deployed="<<((int)deployed&0xff)<<std::endl;

		if(deployed < SER_FMT_VER_LOWEST
				|| deployed > SER_FMT_VER_HIGHEST)
		{
			infostream<<"Client: TOCLIENT_INIT: Server sent "
					<<"unsupported ser_fmt_ver"<<std::endl;
			return;
		}

		m_server_ser_ver = deployed;

		// Get player position
		v3s16 playerpos_s16(0, BS*2+BS*20, 0);
		if(datasize >= 2+1+6)
			playerpos_s16 = readV3S16(&data[2+1]);
		v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);

		{ //envlock
			//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out

			// Set player position
			Player *player = m_env.getLocalPlayer();
			assert(player != NULL);
			player->setPosition(playerpos_f);
		}

		if(datasize >= 2+1+6+8)
		{
			// Get map seed
			m_map_seed = readU64(&data[2+1+6]);
			infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
		}

		// Reply to server
		u32 replysize = 2;
		SharedBuffer<u8> reply(replysize);
		writeU16(&reply[0], TOSERVER_INIT2);
		// Send as reliable
		m_con.Send(PEER_ID_SERVER, 1, reply, true);

		return;
	}

	if(command == TOCLIENT_ACCESS_DENIED)
	{
		// The server didn't like our password. Note, this needs
		// to be processed even if the serialisation format has
		// not been agreed yet, the same as TOCLIENT_INIT.
		m_access_denied = true;
		m_access_denied_reason = L"Unknown";
		if(datasize >= 4)
		{
			std::string datastring((char*)&data[2], datasize-2);
			std::istringstream is(datastring, std::ios_base::binary);
			m_access_denied_reason = deSerializeWideString(is);
		}
		return;
	}

	if(ser_version == SER_FMT_VER_INVALID)
	{
		infostream<<"Client: Server serialization"
				" format invalid or not initialized."
				" Skipping incoming command="<<command<<std::endl;
		return;
	}

	// Just here to avoid putting the two if's together when
	// making some copypasta
	{}

	if(command == TOCLIENT_REMOVENODE)
	{
		if(datasize < 8)
			return;
		v3s16 p;
		p.X = readS16(&data[2]);
		p.Y = readS16(&data[4]);
		p.Z = readS16(&data[6]);

		//TimeTaker t1("TOCLIENT_REMOVENODE");

		// This will clear the cracking animation after digging
		((ClientMap&)m_env.getMap()).clearTempMod(p);

		removeNode(p);
	}
	else if(command == TOCLIENT_ADDNODE)
	{
		if(datasize < 8 + MapNode::serializedLength(ser_version))
			return;

		v3s16 p;
		p.X = readS16(&data[2]);
		p.Y = readS16(&data[4]);
		p.Z = readS16(&data[6]);

		//TimeTaker t1("TOCLIENT_ADDNODE");

		MapNode n;
		n.deSerialize(&data[8], ser_version);

		addNode(p, n);
	}
	else if(command == TOCLIENT_BLOCKDATA)
	{
		// Ignore too small packet
		if(datasize < 8)
			return;

		v3s16 p;
		p.X = readS16(&data[2]);
		p.Y = readS16(&data[4]);
		p.Z = readS16(&data[6]);

		/*infostream<<"Client: Thread: BLOCKDATA for ("
				<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
		/*infostream<<"Client: Thread: BLOCKDATA for ("
				<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/

		std::string datastring((char*)&data[8], datasize-8);
		std::istringstream istr(datastring, std::ios_base::binary);

		MapSector *sector;
		MapBlock *block;

		v2s16 p2d(p.X, p.Z);
		sector = m_env.getMap().emergeSector(p2d);

		assert(sector->getPos() == p2d);

		//TimeTaker timer("MapBlock deSerialize");
		// 0ms

		block = sector->getBlockNoCreateNoEx(p.Y);
		if(block)
		{
			/*
				Update an existing block
			*/
			//infostream<<"Updating"<<std::endl;
			block->deSerialize(istr, ser_version);
		}
		else
		{
			/*
				Create a new block
			*/
			//infostream<<"Creating new"<<std::endl;
			block = new MapBlock(&m_env.getMap(), p);
			block->deSerialize(istr, ser_version);
			sector->insertBlock(block);

			//DEBUG
			/*NodeMod mod;
			mod.type = NODEMOD_CHANGECONTENT;
			mod.param = CONTENT_MESE;
			block->setTempMod(v3s16(8,10,8), mod);
			block->setTempMod(v3s16(8,9,8), mod);
			block->setTempMod(v3s16(8,8,8), mod);
			block->setTempMod(v3s16(8,7,8), mod);
			block->setTempMod(v3s16(8,6,8), mod);*/
		}

#if 0
		/*
			Acknowledge block
		*/
		/*
			[0] u16 command
			[2] u8 count
			[3] v3s16 pos_0
			[3+6] v3s16 pos_1
			...
		*/
		u32 replysize = 2+1+6;
		SharedBuffer<u8> reply(replysize);
		writeU16(&reply[0], TOSERVER_GOTBLOCKS);
		reply[2] = 1;
		writeV3S16(&reply[3], p);
		// Send as reliable
		m_con.Send(PEER_ID_SERVER, 1, reply, true);
#endif

		/*
			Update Mesh of this block and blocks at x-, y- and z-.
			Environment should not be locked as it interlocks with the
			main thread, from which is will want to retrieve textures.
		*/

		//m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
		/*
			Add it to mesh update queue and set it to be acknowledged after update.
		*/
		//infostream<<"Adding mesh update task for received block"<<std::endl;
		addUpdateMeshTaskWithEdge(p, true);
	}
	else if(command == TOCLIENT_PLAYERPOS)
	{
		infostream<<"Received deprecated TOCLIENT_PLAYERPOS"
				<<std::endl;
		/*u16 our_peer_id;
		{
			//JMutexAutoLock lock(m_con_mutex); //bulk comment-out
			our_peer_id = m_con.GetPeerID();
		}
		// Cancel if we don't have a peer id
		if(our_peer_id == PEER_ID_INEXISTENT){
			infostream<<"TOCLIENT_PLAYERPOS cancelled: "
					"we have no peer id"
					<<std::endl;
			return;
		}*/

		{ //envlock
			//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out

			u32 player_size = 2+12+12+4+4;

			u32 player_count = (datasize-2) / player_size;
			u32 start = 2;
			for(u32 i=0; i<player_count; i++)
			{
				u16 peer_id = readU16(&data[start]);

				Player *player = m_env.getPlayer(peer_id);

				// Skip if player doesn't exist
				if(player == NULL)
				{
					start += player_size;
					continue;
				}

				// Skip if player is local player
				if(player->isLocal())
				{
					start += player_size;
					continue;
				}

				v3s32 ps = readV3S32(&data[start+2]);
				v3s32 ss = readV3S32(&data[start+2+12]);
				s32 pitch_i = readS32(&data[start+2+12+12]);
				s32 yaw_i = readS32(&data[start+2+12+12+4]);
				/*infostream<<"Client: got "
						<<"pitch_i="<<pitch_i
						<<" yaw_i="<<yaw_i<<std::endl;*/
				f32 pitch = (f32)pitch_i / 100.0;
				f32 yaw = (f32)yaw_i / 100.0;
				v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
				v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
				player->setPosition(position);
				player->setSpeed(speed);
				player->setPitch(pitch);
				player->setYaw(yaw);

				/*infostream<<"Client: player "<<peer_id
						<<" pitch="<<pitch
						<<" yaw="<<yaw<<std::endl;*/

				start += player_size;
			}
		} //envlock
	}
	else if(command == TOCLIENT_PLAYERINFO)
	{
		u16 our_peer_id;
		{
			//JMutexAutoLock lock(m_con_mutex); //bulk comment-out
			our_peer_id = m_con.GetPeerID();
		}
		// Cancel if we don't have a peer id
		if(our_peer_id == PEER_ID_INEXISTENT){
			infostream<<"TOCLIENT_PLAYERINFO cancelled: "
					"we have no peer id"
					<<std::endl;
			return;
		}

		//infostream<<"Client: Server reports players:"<<std::endl;

		{ //envlock
			//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out

			u32 item_size = 2+PLAYERNAME_SIZE;
			u32 player_count = (datasize-2) / item_size;
			u32 start = 2;
			// peer_ids
			core::list<u16> players_alive;
			for(u32 i=0; i<player_count; i++)
			{
				// Make sure the name ends in '\0'
				data[start+2+20-1] = 0;

				u16 peer_id = readU16(&data[start]);

				players_alive.push_back(peer_id);

				/*infostream<<"peer_id="<<peer_id
						<<" name="<<((char*)&data[start+2])<<std::endl;*/

				// Don't update the info of the local player
				if(peer_id == our_peer_id)
				{
					start += item_size;
					continue;
				}

				Player *player = m_env.getPlayer(peer_id);

				// Create a player if it doesn't exist
				if(player == NULL)
				{
					player = new RemotePlayer(
							m_device->getSceneManager()->getRootSceneNode(),
							m_device,
							-1);
					player->peer_id = peer_id;
					m_env.addPlayer(player);
					infostream<<"Client: Adding new player "
							<<peer_id<<std::endl;
				}

				player->updateName((char*)&data[start+2]);

				start += item_size;
			}

			/*
				Remove those players from the environment that
				weren't listed by the server.
			*/
			//infostream<<"Removing dead players"<<std::endl;
			core::list<Player*> players = m_env.getPlayers();
			core::list<Player*>::Iterator ip;
			for(ip=players.begin(); ip!=players.end(); ip++)
			{
				// Ingore local player
				if((*ip)->isLocal())
					continue;

				// Warn about a special case
				if((*ip)->peer_id == 0)
				{
					infostream<<"Client: Removing "
							"dead player with id=0"<<std::endl;
				}

				bool is_alive = false;
				core::list<u16>::Iterator i;
				for(i=players_alive.begin(); i!=players_alive.end(); i++)
				{
					if((*ip)->peer_id == *i)
					{
						is_alive = true;
						break;
					}
				}
				/*infostream<<"peer_id="<<((*ip)->peer_id)
						<<" is_alive="<<is_alive<<std::endl;*/
				if(is_alive)
					continue;
				infostream<<"Removing dead player "<<(*ip)->peer_id
						<<std::endl;
				m_env.removePlayer((*ip)->peer_id);
			}
		} //envlock
	}
	else if(command == TOCLIENT_SECTORMETA)
	{
		infostream<<"Client received DEPRECATED TOCLIENT_SECTORMETA"<<std::endl;
#if 0
		/*
			[0] u16 command
			[2] u8 sector count
			[3...] v2s16 pos + sector metadata
		*/
		if(datasize < 3)
			return;

		//infostream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;

		{ //envlock
			//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out

			std::string datastring((char*)&data[2], datasize-2);
			std::istringstream is(datastring, std::ios_base::binary);

			u8 buf[4];

			is.read((char*)buf, 1);
			u16 sector_count = readU8(buf);

			//infostream<<"sector_count="<<sector_count<<std::endl;

			for(u16 i=0; i<sector_count; i++)
			{
				// Read position
				is.read((char*)buf, 4);
				v2s16 pos = readV2S16(buf);
				/*infostream<<"Client: deserializing sector at "
						<<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
				// Create sector
				assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
				((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
			}
		} //envlock
#endif
	}
	else if(command == TOCLIENT_INVENTORY)
	{
		if(datasize < 3)
			return;

		//TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);

		{ //envlock
			//TimeTaker t2("mutex locking", m_device);
			//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
			//t2.stop();

			//TimeTaker t3("istringstream init", m_device);
			std::string datastring((char*)&data[2], datasize-2);
			std::istringstream is(datastring, std::ios_base::binary);
			//t3.stop();

			//m_env.printPlayers(infostream);

			//TimeTaker t4("player get", m_device);
			Player *player = m_env.getLocalPlayer();
			assert(player != NULL);
			//t4.stop();

			//TimeTaker t1("inventory.deSerialize()", m_device);
			player->inventory.deSerialize(is);
			//t1.stop();

			m_inventory_updated = true;

			//infostream<<"Client got player inventory:"<<std::endl;
			//player->inventory.print(infostream);
		}
	}
	//DEBUG
	else if(command == TOCLIENT_OBJECTDATA)
	{
		// Strip command word and create a stringstream
		std::string datastring((char*)&data[2], datasize-2);
		std::istringstream is(datastring, std::ios_base::binary);

		u8 buf[12];

		/*
			Read players
		*/

		is.read((char*)buf, 2);
		u16 playercount = readU16(buf);

		for(u16 i=0; i<playercount; i++)
		{
			is.read((char*)buf, 2);
			u16 peer_id = readU16(buf);
			is.read((char*)buf, 12);
			v3s32 p_i = readV3S32(buf);
			is.read((char*)buf, 12);
			v3s32 s_i = readV3S32(buf);
			is.read((char*)buf, 4);
			s32 pitch_i = readS32(buf);
			is.read((char*)buf, 4);
			s32 yaw_i = readS32(buf);

			Player *player = m_env.getPlayer(peer_id);

			// Skip if player doesn't exist
			if(player == NULL)
			{
				continue;
			}

			// Skip if player is local player
			if(player->isLocal())
			{
				continue;
			}

			f32 pitch = (f32)pitch_i / 100.0;
			f32 yaw = (f32)yaw_i / 100.0;
			v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
			v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);

			player->setPosition(position);
			player->setSpeed(speed);
			player->setPitch(pitch);
			player->setYaw(yaw);
		}

		/*
			Read block objects
			NOTE: Deprecated stuff
		*/

		// Read active block count
		u16 blockcount = readU16(is);
		if(blockcount != 0){
			infostream<<"TOCLIENT_OBJECTDATA: blockcount != 0 "
					"not supported"<<std::endl;
			return;
		}
	}
	else if(command == TOCLIENT_TIME_OF_DAY)
	{
		if(datasize < 4)
			return;

		u16 time_of_day = readU16(&data[2]);
		time_of_day = time_of_day % 24000;
		//infostream<<"Client: time_of_day="<<time_of_day<<std::endl;

		/*
			time_of_day:
			0 = midnight
			12000 = midday
		*/
		{
			m_env.setTimeOfDay(time_of_day);

			u32 dr = m_env.getDayNightRatio();

			infostream<<"Client: time_of_day="<<time_of_day
					<<", dr="<<dr
					<<std::endl;
		}

	}
	else if(command == TOCLIENT_CHAT_MESSAGE)
	{
		/*
			u16 command
			u16 length
			wstring message
		*/
		u8 buf[6];
		std::string datastring((char*)&data[2], datasize-2);
		std::istringstream is(datastring, std::ios_base::binary);

		// Read stuff
		is.read((char*)buf, 2);
		u16 len = readU16(buf);

		std::wstring message;
		for(u16 i=0; i<len; i++)
		{
			is.read((char*)buf, 2);
			message += (wchar_t)readU16(buf);
		}

		/*infostream<<"Client received chat message: "
				<<wide_to_narrow(message)<<std::endl;*/

		m_chat_queue.push_back(message);
	}
	else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
	{
		//if(g_settings->getBool("enable_experimental"))
		{
			/*
				u16 command
				u16 count of removed objects
				for all removed objects {
					u16 id
				}
				u16 count of added objects
				for all added objects {
					u16 id
					u8 type
					u32 initialization data length
					string initialization data
				}
			*/

			char buf[6];
			// Get all data except the command number
			std::string datastring((char*)&data[2], datasize-2);
			// Throw them in an istringstream
			std::istringstream is(datastring, std::ios_base::binary);

			// Read stuff

			// Read removed objects
			is.read(buf, 2);
			u16 removed_count = readU16((u8*)buf);
			for(u16 i=0; i<removed_count; i++)
			{
				is.read(buf, 2);
				u16 id = readU16((u8*)buf);
				// Remove it
				{
					//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
					m_env.removeActiveObject(id);
				}
			}

			// Read added objects
			is.read(buf, 2);
			u16 added_count = readU16((u8*)buf);
			for(u16 i=0; i<added_count; i++)
			{
				is.read(buf, 2);
				u16 id = readU16((u8*)buf);
				is.read(buf, 1);
				u8 type = readU8((u8*)buf);
				std::string data = deSerializeLongString(is);
				// Add it
				{
					//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
					m_env.addActiveObject(id, type, data);
				}
			}
		}
	}
	else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
	{
		//if(g_settings->getBool("enable_experimental"))
		{
			/*
				u16 command
				for all objects
				{
					u16 id
					u16 message length
					string message
				}
			*/
			char buf[6];
			// Get all data except the command number
			std::string datastring((char*)&data[2], datasize-2);
			// Throw them in an istringstream
			std::istringstream is(datastring, std::ios_base::binary);

			while(is.eof() == false)
			{
				// Read stuff
				is.read(buf, 2);
				u16 id = readU16((u8*)buf);
				if(is.eof())
					break;
				is.read(buf, 2);
				u16 message_size = readU16((u8*)buf);
				std::string message;
				message.reserve(message_size);
				for(u16 i=0; i<message_size; i++)
				{
					is.read(buf, 1);
					message.append(buf, 1);
				}
				// Pass on to the environment
				{
					//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
					m_env.processActiveObjectMessage(id, message);
				}
			}
		}
	}
	else if(command == TOCLIENT_HP)
	{
		std::string datastring((char*)&data[2], datasize-2);
		std::istringstream is(datastring, std::ios_base::binary);
		Player *player = m_env.getLocalPlayer();
		assert(player != NULL);
		u8 hp = readU8(is);
		player->hp = hp;
	}
	else if(command == TOCLIENT_MOVE_PLAYER)
	{
		std::string datastring((char*)&data[2], datasize-2);
		std::istringstream is(datastring, std::ios_base::binary);
		Player *player = m_env.getLocalPlayer();
		assert(player != NULL);
		v3f pos = readV3F1000(is);
		f32 pitch = readF1000(is);
		f32 yaw = readF1000(is);
		player->setPosition(pos);
		/*player->setPitch(pitch);
		player->setYaw(yaw);*/

		infostream<<"Client got TOCLIENT_MOVE_PLAYER"
				<<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
				<<" pitch="<<pitch
				<<" yaw="<<yaw
				<<std::endl;

		/*
			Add to ClientEvent queue.
			This has to be sent to the main program because otherwise
			it would just force the pitch and yaw values to whatever
			the camera points to.
		*/
		ClientEvent event;
		event.type = CE_PLAYER_FORCE_MOVE;
		event.player_force_move.pitch = pitch;
		event.player_force_move.yaw = yaw;
		m_client_event_queue.push_back(event);

		// Ignore damage for a few seconds, so that the player doesn't
		// get damage from falling on ground
		m_ignore_damage_timer = 3.0;
	}
	else if(command == TOCLIENT_PLAYERITEM)
	{
		std::string datastring((char*)&data[2], datasize-2);
		std::istringstream is(datastring, std::ios_base::binary);

		u16 count = readU16(is);

		for (u16 i = 0; i < count; ++i) {
			u16 peer_id = readU16(is);
			Player *player = m_env.getPlayer(peer_id);

			if (player == NULL)
			{
				infostream<<"Client: ignoring player item "
					<< deSerializeString(is)
					<< " for non-existing peer id " << peer_id
					<< std::endl;
				continue;
			} else if (player->isLocal()) {
				infostream<<"Client: ignoring player item "
					<< deSerializeString(is)
					<< " for local player" << std::endl;
				continue;
			} else {
				InventoryList *inv = player->inventory.getList("main");
				std::string itemstring(deSerializeString(is));
				if (itemstring.empty()) {
					inv->deleteItem(0);
					infostream
						<<"Client: empty player item for peer "
						<< peer_id << std::endl;
				} else {
					std::istringstream iss(itemstring);
					delete inv->changeItem(0, InventoryItem::deSerialize(iss));
					infostream<<"Client: player item for peer " << peer_id << ": ";
					player->getWieldItem()->serialize(infostream);
					infostream<<std::endl;
				}
			}
		}
	}
	else if(command == TOCLIENT_DEATHSCREEN)
	{
		std::string datastring((char*)&data[2], datasize-2);
		std::istringstream is(datastring, std::ios_base::binary);

		bool set_camera_point_target = readU8(is);
		v3f camera_point_target = readV3F1000(is);

		ClientEvent event;
		event.type = CE_DEATHSCREEN;
		event.deathscreen.set_camera_point_target = set_camera_point_target;
		event.deathscreen.camera_point_target_x = camera_point_target.X;
		event.deathscreen.camera_point_target_y = camera_point_target.Y;
		event.deathscreen.camera_point_target_z = camera_point_target.Z;
		m_client_event_queue.push_back(event);
	}
	else
	{
		infostream<<"Client: Ignoring unknown command "
				<<command<<std::endl;
	}
}