void Client::groundAction(u8 action, v3s16 nodepos_undersurface, v3s16 nodepos_oversurface, u16 item) { if(connectedAndInitialized() == false){ infostream<<"Client::groundAction() " "cancelled (not connected)" <<std::endl; return; } /* length: 17 [0] u16 command [2] u8 action [3] v3s16 nodepos_undersurface [9] v3s16 nodepos_abovesurface [15] u16 item actions: 0: start digging 1: place block 2: stop digging (all parameters ignored) 3: digging completed */ u8 datasize = 2 + 1 + 6 + 6 + 2; SharedBuffer<u8> data(datasize); writeU16(&data[0], TOSERVER_GROUND_ACTION); writeU8(&data[2], action); writeV3S16(&data[3], nodepos_undersurface); writeV3S16(&data[9], nodepos_oversurface); writeU16(&data[15], item); Send(0, data, true); }
void AreaStore::serialize(std::ostream &os) const { writeU8(os, 0); // Serialisation version // TODO: Compression? writeU16(os, areas_map.size()); for (const auto &it : areas_map) { const Area &a = it.second; writeV3S16(os, a.minedge); writeV3S16(os, a.maxedge); writeU16(os, a.data.size()); os.write(a.data.data(), a.data.size()); } }
void AreaStore::serialize(std::ostream &os) const { writeU8(os, 0); // Serialisation version // TODO: Compression? writeU16(os, areas_map.size()); for (AreaMap::const_iterator it = areas_map.begin(); it != areas_map.end(); ++it) { const Area &a = it->second; writeV3S16(os, a.minedge); writeV3S16(os, a.maxedge); writeU16(os, a.data.size()); os.write(a.data.data(), a.data.size()); } }
/* Minetest Schematic File Format All values are stored in big-endian byte order. [u32] signature: 'MTSM' [u16] version: 3 [u16] size X [u16] size Y [u16] size Z For each Y: [u8] slice probability value [Name-ID table] Name ID Mapping Table [u16] name-id count For each name-id mapping: [u16] name length [u8[]] name ZLib deflated { For each node in schematic: (for z, y, x) [u16] content For each node in schematic: [u8] probability of occurance (param1) For each node in schematic: [u8] param2 } Version changes: 1 - Initial version 2 - Fixed messy never/always place; 0 probability is now never, 0xFF is always 3 - Added y-slice probabilities; this allows for variable height structures */ void DecoSchematic::saveSchematicFile(INodeDefManager *ndef) { std::ostringstream ss(std::ios_base::binary); writeU32(ss, MTSCHEM_FILE_SIGNATURE); // signature writeU16(ss, MTSCHEM_FILE_VER_HIGHEST_WRITE); // version writeV3S16(ss, size); // schematic size for (int y = 0; y != size.Y; y++) // Y slice probabilities writeU8(ss, slice_probs[y]); std::vector<content_t> usednodes; int nodecount = size.X * size.Y * size.Z; build_nnlist_and_update_ids(schematic, nodecount, &usednodes); u16 numids = usednodes.size(); writeU16(ss, numids); // name count for (int i = 0; i != numids; i++) ss << serializeString(ndef->get(usednodes[i]).name); // node names // compressed bulk node data MapNode::serializeBulk(ss, SER_FMT_VER_HIGHEST_WRITE, schematic, nodecount, 2, 2, true); fs::safeWriteToFile(filename, ss.str()); }
void PointedThing::serialize(std::ostream &os) const { writeU8(os, 0); // version writeU8(os, (u8)type); switch (type) { case POINTEDTHING_NOTHING: break; case POINTEDTHING_NODE: writeV3S16(os, node_undersurface); writeV3S16(os, node_abovesurface); break; case POINTEDTHING_OBJECT: writeS16(os, object_id); break; } }
void Client::sendSignNodeText(v3s16 p, std::string text) { /* u16 command v3s16 p u16 textlen textdata */ std::ostringstream os(std::ios_base::binary); u8 buf[12]; // Write command writeU16(buf, TOSERVER_SIGNNODETEXT); os.write((char*)buf, 2); // Write p writeV3S16(buf, p); os.write((char*)buf, 6); u16 textlen = text.size(); // Write text length writeS16(buf, textlen); os.write((char*)buf, 2); // Write text os.write((char*)text.c_str(), textlen); // Make data buffer std::string s = os.str(); SharedBuffer<u8> data((u8*)s.c_str(), s.size()); // Send as reliable Send(0, data, true); }
void PointedThing::serialize(std::ostream &os) const { writeU8(os, 0); // version writeU8(os, (u8)type); if(type == POINTEDTHING_NOTHING) { // nothing } else if(type == POINTEDTHING_NODE) { writeV3S16(os, node_undersurface); writeV3S16(os, node_abovesurface); } else if(type == POINTEDTHING_OBJECT) { writeS16(os, object_id); } }
void TestSerialization::testStreamWrite() { std::ostringstream os(std::ios_base::binary); std::string data; writeU8(os, 0x11); writeU16(os, 0x2233); writeU32(os, 0x44556677); writeU64(os, 0x8899AABBCCDDEEFF); writeS8(os, -128); writeS16(os, 30000); writeS32(os, -6); writeS64(os, -43); writeF1000(os, 53.53467f); writeF1000(os, -300000.32f); writeF1000(os, F1000_MIN); writeF1000(os, F1000_MAX); os << serializeString("foobar!"); data = os.str(); UASSERT(data.size() < sizeof(test_serialized_data)); UASSERT(!memcmp(&data[0], test_serialized_data, data.size())); writeV2S16(os, v2s16(500, 500)); writeV3S16(os, v3s16(4207, 604, -30)); writeV2S32(os, v2s32(1920, 1080)); writeV3S32(os, v3s32(-400, 6400054, 290549855)); writeV2F1000(os, v2f(500.65661f, 350.34567f)); os << serializeWideString(L"\x02~woof~\x5455"); writeV3F1000(os, v3f(500, 10024.2f, -192.54f)); writeARGB8(os, video::SColor(255, 128, 50, 128)); os << serializeLongString("some longer string here"); writeU16(os, 0xF00D); data = os.str(); UASSERT(data.size() == sizeof(test_serialized_data)); UASSERT(!memcmp(&data[0], test_serialized_data, sizeof(test_serialized_data))); }
/* Minetest Schematic File Format All values are stored in big-endian byte order. [u32] signature: 'MTSM' [u16] version: 1 [u16] size X [u16] size Y [u16] size Z [Name-ID table] Name ID Mapping Table [u16] name-id count For each name-id mapping: [u16] name length [u8[]] name ZLib deflated { For each node in schematic: (for z, y, x) [u16] content For each node in schematic: [u8] probability of occurance (param1) For each node in schematic: [u8] param2 } */ void DecoSchematic::saveSchematicFile(INodeDefManager *ndef) { std::ofstream os(filename.c_str(), std::ios_base::binary); writeU32(os, MTSCHEM_FILE_SIGNATURE); // signature writeU16(os, 1); // version writeV3S16(os, size); // schematic size std::vector<content_t> usednodes; int nodecount = size.X * size.Y * size.Z; build_nnlist_and_update_ids(schematic, nodecount, &usednodes); u16 numids = usednodes.size(); writeU16(os, numids); // name count for (int i = 0; i != numids; i++) os << serializeString(ndef->get(usednodes[i]).name); // node names // compressed bulk node data MapNode::serializeBulk(os, SER_FMT_VER_HIGHEST, schematic, nodecount, 2, 2, true); }
bool Schematic::serializeToMts(std::ostream *os, const std::vector<std::string> &names) { std::ostream &ss = *os; writeU32(ss, MTSCHEM_FILE_SIGNATURE); // signature writeU16(ss, MTSCHEM_FILE_VER_HIGHEST_WRITE); // version writeV3S16(ss, size); // schematic size for (int y = 0; y != size.Y; y++) // Y slice probabilities writeU8(ss, slice_probs[y]); writeU16(ss, names.size()); // name count for (size_t i = 0; i != names.size(); i++) ss << serializeString(names[i]); // node names // compressed bulk node data MapNode::serializeBulk(ss, SER_FMT_VER_HIGHEST_WRITE, schemdata, size.X * size.Y * size.Z, 2, 2, true); return true; }
/* 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; } }
void Client::step(float dtime) { DSTACK(__FUNCTION_NAME); // Limit a bit if(dtime > 2.0) dtime = 2.0; if(m_ignore_damage_timer > dtime) m_ignore_damage_timer -= dtime; else m_ignore_damage_timer = 0.0; //infostream<<"Client steps "<<dtime<<std::endl; { //TimeTaker timer("ReceiveAll()", m_device); // 0ms ReceiveAll(); } { //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device); // 0ms //JMutexAutoLock lock(m_con_mutex); //bulk comment-out m_con.RunTimeouts(dtime); } /* Packet counter */ { float &counter = m_packetcounter_timer; counter -= dtime; if(counter <= 0.0) { counter = 20.0; infostream<<"Client packetcounter (20s):"<<std::endl; m_packetcounter.print(infostream); m_packetcounter.clear(); } } // Get connection status bool connected = connectedAndInitialized(); #if 0 { /* Delete unused sectors NOTE: This jams the game for a while because deleting sectors clear caches */ float &counter = m_delete_unused_sectors_timer; counter -= dtime; if(counter <= 0.0) { // 3 minute interval //counter = 180.0; counter = 60.0; //JMutexAutoLock lock(m_env_mutex); //bulk comment-out core::list<v3s16> deleted_blocks; float delete_unused_sectors_timeout = g_settings->getFloat("client_delete_unused_sectors_timeout"); // Delete sector blocks /*u32 num = m_env.getMap().unloadUnusedData (delete_unused_sectors_timeout, true, &deleted_blocks);*/ // Delete whole sectors m_env.getMap().unloadUnusedData (delete_unused_sectors_timeout, &deleted_blocks); if(deleted_blocks.size() > 0) { /*infostream<<"Client: Deleted blocks of "<<num <<" unused sectors"<<std::endl;*/ /*infostream<<"Client: Deleted "<<num <<" unused sectors"<<std::endl;*/ /* Send info to server */ // Env is locked so con can be locked. //JMutexAutoLock lock(m_con_mutex); //bulk comment-out core::list<v3s16>::Iterator i = deleted_blocks.begin(); core::list<v3s16> sendlist; for(;;) { if(sendlist.size() == 255 || i == deleted_blocks.end()) { if(sendlist.size() == 0) break; /* [0] u16 command [2] u8 count [3] v3s16 pos_0 [3+6] v3s16 pos_1 ... */ u32 replysize = 2+1+6*sendlist.size(); SharedBuffer<u8> reply(replysize); writeU16(&reply[0], TOSERVER_DELETEDBLOCKS); reply[2] = sendlist.size(); u32 k = 0; for(core::list<v3s16>::Iterator j = sendlist.begin(); j != sendlist.end(); j++) { writeV3S16(&reply[2+1+6*k], *j); k++; } m_con.Send(PEER_ID_SERVER, 1, reply, true); if(i == deleted_blocks.end()) break; sendlist.clear(); } sendlist.push_back(*i); i++; } } } } #endif if(connected == false) { float &counter = m_connection_reinit_timer; counter -= dtime; if(counter <= 0.0) { counter = 2.0; //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out Player *myplayer = m_env.getLocalPlayer(); assert(myplayer != NULL); // Send TOSERVER_INIT // [0] u16 TOSERVER_INIT // [2] u8 SER_FMT_VER_HIGHEST // [3] u8[20] player_name // [23] u8[28] password (new in some version) // [51] u16 client network protocol version (new in some version) SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2); writeU16(&data[0], TOSERVER_INIT); writeU8(&data[2], SER_FMT_VER_HIGHEST); memset((char*)&data[3], 0, PLAYERNAME_SIZE); snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName()); /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\"" <<std::endl;*/ memset((char*)&data[23], 0, PASSWORD_SIZE); snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str()); // This should be incremented in each version writeU16(&data[51], 3); // Send as unreliable Send(0, data, false); } // Not connected, return return; } /* Do stuff if connected */ /* Run Map's timers and unload unused data */ const float map_timer_and_unload_dtime = 5.25; if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime)) { ScopeProfiler sp(g_profiler, "Client: map timer and unload"); core::list<v3s16> deleted_blocks; m_env.getMap().timerUpdate(map_timer_and_unload_dtime, g_settings->getFloat("client_unload_unused_data_timeout"), &deleted_blocks); /*if(deleted_blocks.size() > 0) infostream<<"Client: Unloaded "<<deleted_blocks.size() <<" unused blocks"<<std::endl;*/ /* Send info to server NOTE: This loop is intentionally iterated the way it is. */ core::list<v3s16>::Iterator i = deleted_blocks.begin(); core::list<v3s16> sendlist; for(;;) { if(sendlist.size() == 255 || i == deleted_blocks.end()) { if(sendlist.size() == 0) break; /* [0] u16 command [2] u8 count [3] v3s16 pos_0 [3+6] v3s16 pos_1 ... */ u32 replysize = 2+1+6*sendlist.size(); SharedBuffer<u8> reply(replysize); writeU16(&reply[0], TOSERVER_DELETEDBLOCKS); reply[2] = sendlist.size(); u32 k = 0; for(core::list<v3s16>::Iterator j = sendlist.begin(); j != sendlist.end(); j++) { writeV3S16(&reply[2+1+6*k], *j); k++; } m_con.Send(PEER_ID_SERVER, 1, reply, true); if(i == deleted_blocks.end()) break; sendlist.clear(); } sendlist.push_back(*i); i++; } } /* Handle environment */ { // 0ms //JMutexAutoLock lock(m_env_mutex); //bulk comment-out // Control local player (0ms) LocalPlayer *player = m_env.getLocalPlayer(); assert(player != NULL); player->applyControl(dtime); //TimeTaker envtimer("env step", m_device); // Step environment m_env.step(dtime); /* Get events */ for(;;) { ClientEnvEvent event = m_env.getClientEvent(); if(event.type == CEE_NONE) { break; } else if(event.type == CEE_PLAYER_DAMAGE) { if(m_ignore_damage_timer <= 0) { u8 damage = event.player_damage.amount; sendDamage(damage); // Add to ClientEvent queue ClientEvent event; event.type = CE_PLAYER_DAMAGE; event.player_damage.amount = damage; m_client_event_queue.push_back(event); } } } } /* Print some info */ { float &counter = m_avg_rtt_timer; counter += dtime; if(counter >= 10) { counter = 0.0; //JMutexAutoLock lock(m_con_mutex); //bulk comment-out // connectedAndInitialized() is true, peer exists. float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER); infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl; } } /* Send player position to server */ { float &counter = m_playerpos_send_timer; counter += dtime; if(counter >= 0.2) { counter = 0.0; sendPlayerPos(); } } /* Replace updated meshes */ { //JMutexAutoLock lock(m_env_mutex); //bulk comment-out //TimeTaker timer("** Processing mesh update result queue"); // 0ms /*infostream<<"Mesh update result queue size is " <<m_mesh_update_thread.m_queue_out.size() <<std::endl;*/ while(m_mesh_update_thread.m_queue_out.size() > 0) { MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front(); MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p); if(block) { block->replaceMesh(r.mesh); } if(r.ack_block_to_server) { /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y <<","<<r.p.Z<<")"<<std::endl;*/ /* 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], r.p); // Send as reliable m_con.Send(PEER_ID_SERVER, 1, reply, true); } } } }