X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fserver.cpp;h=75b47e498f50af5ead900118257f10e9236b1172;hb=c5e583e059bc31c0dc57496b23f5c97db1f5a3e3;hp=76fa23a932307f78bd35f887e86c822dfd9f4840;hpb=9778347c7f118f6b74153f1d2c8070920352eb68;p=dragonfireclient.git diff --git a/src/server.cpp b/src/server.cpp index 76fa23a93..75b47e498 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1,6 +1,6 @@ /* Minetest-c55 -Copyright (C) 2010 celeron55, Perttu Ahola +Copyright (C) 2010-2011 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -28,6 +28,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "voxel.h" #include "materials.h" #include "mineral.h" +#include "config.h" +#include "servercommand.h" +#include "filesys.h" #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0) @@ -72,7 +75,7 @@ void * EmergeThread::Thread() DSTACK(__FUNCTION_NAME); - bool debug=false; + //bool debug=false; BEGIN_DEBUG_EXCEPTION_HANDLER @@ -91,7 +94,19 @@ void * EmergeThread::Thread() SharedPtr q(qptr); v3s16 &p = q->pos; - + v2s16 p2d(p.X,p.Z); + + /* + Do not generate over-limit + */ + if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE + || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE + || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE + || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE + || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE + || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE) + continue; + //derr_server<<"EmergeThread::Thread(): running"< modified_blocks; - {//envlock - - //TimeTaker envlockwaittimer("block emerge envlock wait time"); + bool only_from_disk = false; - // 0-50ms - JMutexAutoLock envlock(m_server->m_env_mutex); + if(optional) + only_from_disk = true; - //envlockwaittimer.stop(); + v2s16 chunkpos = map.sector_to_chunk(p2d); - //TimeTaker timer("block emerge (while env locked)"); - - try{ - bool only_from_disk = false; - - if(optional) - only_from_disk = true; + bool generate_chunk = false; + if(only_from_disk == false) + { + JMutexAutoLock envlock(m_server->m_env_mutex); + if(map.chunkNonVolatile(chunkpos) == false) + generate_chunk = true; + } + if(generate_chunk) + { + ChunkMakeData data; - // First check if the block already exists - //block = map.getBlockNoCreate(p); - - if(block == NULL) { - //dstream<<"Calling emergeBlock"<m_env_mutex); + map.initChunkMake(data, chunkpos); } - // If it is a dummy, block was not found on disk - if(block->isDummy()) - { - //dstream<<"EmergeThread: Got a dummy block"<m_env_mutex); + map.finishChunkMake(data, changed_blocks); } } - catch(InvalidPositionException &e) - { - // Block not found. - // This happens when position is over limit. - got_block = false; - } - - if(got_block) + + /* + Fetch block from map or generate a single block + */ { - if(debug && changed_blocks.size() > 0) + JMutexAutoLock envlock(m_server->m_env_mutex); + + // Load sector if it isn't loaded + if(map.getSectorNoGenerateNoEx(p2d) == NULL) + map.loadSectorFull(p2d); + + block = map.getBlockNoCreateNoEx(p); + if(!block || block->isDummy()) { - dout_server<::Iterator i = changed_blocks.getIterator(); - i.atEnd() == false; i++) + if(only_from_disk) + { + got_block = false; + } + else { - MapBlock *block = i.getNode()->getValue(); - v3s16 p = block->getPos(); - dout_server<<"("<getLightingExpired()){ + lighting_invalidated_blocks[block->getPos()] = block; } - dout_server<m_env_mutex); + + if(got_block) + { /* Collect a list of blocks that have been modified in addition to the fetched one. */ - - // Add all the "changed blocks" to modified_blocks + + if(lighting_invalidated_blocks.size() > 0) + { + /*dstream<<"lighting "<::Iterator i = changed_blocks.getIterator(); i.atEnd() == false; i++) { MapBlock *block = i.getNode()->getValue(); modified_blocks.insert(block->getPos(), block); } - - /*dstream<<"lighting "<= 0) + return; // Won't send anything if already sending if(m_blocks_sending.size() >= g_settings.getU16 @@ -310,8 +347,6 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, /* Get the starting value of the block finder radius. */ - s16 last_nearest_unsent_d; - s16 d_start; if(m_last_center != center) { @@ -328,14 +363,14 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, //dstream<<"Resetting m_nearest_unsent_d"<= maximum_simultaneous_block_sends_now) - { - /*dstream<<"Not sending more blocks. Queue full. " - <= max_simul_dynamic) goto queue_full; - } - + + // Don't send blocks that are currently being transferred if(m_blocks_sending.find(p) != NULL) continue; @@ -448,37 +481,35 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, continue; } -#if 0 +#if 1 /* If block is far away, don't generate it unless it is - near ground level - - NOTE: We can't know the ground level this way with the - new generator. + near ground level. */ - if(d > 4) + if(d >= 4) { - v2s16 p2d(p.X, p.Z); - MapSector *sector = NULL; - try - { - sector = server->m_env.getMap().getSectorNoGenerate(p2d); - } - catch(InvalidPositionException &e) - { - } - - if(sector != NULL) - { - // Get center ground height in nodes - f32 gh = sector->getGroundHeight( - v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2)); - // Block center y in nodes - f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2); - // If differs a lot, don't generate - if(fabs(gh - y) > MAP_BLOCKSIZE*2) - generate = false; - } + #if 1 + // Block center y in nodes + f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2); + // Don't generate if it's very high or very low + if(y < -64 || y > 64) + generate = false; + #endif + #if 0 + v2s16 p2d_nodes_center( + MAP_BLOCKSIZE*p.X, + MAP_BLOCKSIZE*p.Z); + + // Get ground height in nodes + s16 gh = server->m_env.getServerMap().findGroundLevel( + p2d_nodes_center); + + // If differs a lot, don't generate + if(fabs(gh - y) > MAP_BLOCKSIZE*2) + generate = false; + // Actually, don't even send it + //continue; + #endif } #endif @@ -518,11 +549,30 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, block_is_invalid = true; } + /*if(block->isFullyGenerated() == false) + { + block_is_invalid = true; + }*/ + v2s16 p2d(p.X, p.Z); ServerMap *map = (ServerMap*)(&server->m_env.getMap()); v2s16 chunkpos = map->sector_to_chunk(p2d); if(map->chunkNonVolatile(chunkpos) == false) block_is_invalid = true; +#if 1 + /* + If block is not close, don't send it unless it is near + ground level. + + Block is not near ground level if night-time mesh + doesn't differ from day-time mesh. + */ + if(d > 3) + { + if(block->dayNightDiffed() == false) + continue; + } +#endif } /* @@ -541,7 +591,8 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, */ if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d) { - new_nearest_unsent_d = d; + if(generate == true) + new_nearest_unsent_d = d; } /* @@ -551,7 +602,8 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, { //TODO: Get value from somewhere // Allow only one block in emerge queue - if(server->m_emerge_queue.peerItemCount(peer_id) < 1) + //if(server->m_emerge_queue.peerItemCount(peer_id) < 1) + if(server->m_emerge_queue.peerItemCount(peer_id) < 2) { //dstream<<"Adding block to emerge queue"<= 3) + { + // Pause time in seconds + m_nothing_to_send_pause_timer = 2.0; + } + } + else + { + m_nothing_to_send_counter = 0; + } + + /*timer_result = timer.stop(true); + if(timer_result != 0) + dstream<<"GetNextBlocks duration: "<stepObjects(dtime, true, server->getDayNightRatio()); + block->stepObjects(dtime, true, server->m_env.getDayNightRatio()); stepped_blocks.insert(p, true); block->setChangedFlag(); } @@ -859,6 +930,7 @@ void RemoteClient::SetBlocksNotSent(core::map &blocks) PlayerInfo::PlayerInfo() { name[0] = 0; + avg_rtt = 0; } void PlayerInfo::PrintLine(std::ostream *s) @@ -897,7 +969,6 @@ Server::Server( m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this), m_thread(this), m_emergethread(this), - m_time_of_day(9000), m_time_counter(0), m_time_of_day_send_timer(0), m_uptime(0), @@ -916,15 +987,26 @@ Server::Server( m_con_mutex.Init(); m_step_dtime_mutex.Init(); m_step_dtime = 0.0; - + + // Register us to receive map edit events m_env.getMap().addEventReceiver(this); + // If file exists, load environment metadata + if(fs::PathExists(m_mapsavedir+"/env_meta.txt")) + { + dstream<<"Server: Loading environment metadata"<serialization_version == SER_FMT_VER_INVALID) continue; - SendChatMessage(client->peer_id, line); + try{ + SendChatMessage(client->peer_id, line); + } + catch(con::PeerNotFoundException &e) + {} } } /* Save players */ + dstream<<"Server: Saving players"<PrintInfo(std::cout); } } } - if(g_settings.getBool("enable_experimental")) + //if(g_settings.getBool("enable_experimental")) { /* Check added and deleted active objects */ { + //dstream<<"Server: Checking added and deleted active objects"<getValue(); Player *player = m_env.getPlayer(client->peer_id); if(player==NULL) + { + dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<peer_id + <<" has no associated player"<getPosition(), BS); core::map removed_objects; @@ -1226,7 +1327,10 @@ void Server::AsyncRunStep() // Ignore if nothing happened if(removed_objects.size() == 0 && added_objects.size() == 0) + { + //dstream<<"INFO: active objects: none changed"<getClientInitializationData())); + + if(obj) + data_buffer.append(serializeLongString( + obj->getClientInitializationData())); + else + data_buffer.append(serializeLongString("")); // Add to known objects client->m_known_objects.insert(i.getNode()->getKey(), false); @@ -1302,6 +1409,33 @@ void Server::AsyncRunStep() < all_known_objects; + + for(core::map::Iterator + i = m_clients.getIterator(); + i.atEnd() == false; i++) + { + RemoteClient *client = i.getNode()->getValue(); + // Go through all known objects of client + for(core::map::Iterator + i = client->m_known_objects.getIterator(); + i.atEnd()==false; i++) + { + u16 id = i.getNode()->getKey(); + all_known_objects[id] = true; + } + } + + m_env.setKnownActiveObjects(whatever); +#endif + } /* @@ -1469,6 +1603,34 @@ void Server::AsyncRunStep() } } + /* + Step node metadata + */ + { + //TimeTaker timer("Step node metadata"); + + JMutexAutoLock envlock(m_env_mutex); + JMutexAutoLock conlock(m_con_mutex); + + core::map changed_blocks; + m_env.getMap().nodeMetadataStep(dtime, changed_blocks); + + for(core::map::Iterator + i = changed_blocks.getIterator(); + i.atEnd() == false; i++) + { + MapBlock *block = i.getNode()->getValue(); + + for(core::map::Iterator + i = m_clients.getIterator(); + i.atEnd()==false; i++) + { + RemoteClient *client = i.getNode()->getValue(); + client->SetBlockNotSent(block->getPos()); + } + } + } + /* Trigger emergethread (it somehow gets to a non-triggered but bysy state sometimes) @@ -1510,6 +1672,9 @@ void Server::AsyncRunStep() // Save players m_env.serializePlayers(m_mapsavedir); + + // Save environment metadata + m_env.saveMeta(m_mapsavedir); } } } @@ -1590,8 +1755,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // [0] u16 TOSERVER_INIT // [2] u8 SER_FMT_VER_HIGHEST // [3] u8[20] player_name + // [23] u8[28] password <--- can be sent without this, from old versions - if(datasize < 3) + if(datasize < 2+1+PLAYERNAME_SIZE) return; derr_server<getPassword(),password)) + { + derr_server<updateName((const char*)&data[3]); }*/ - - // Now answer with a TOCLIENT_INIT - SharedBuffer reply(2+1+6); - writeU16(&reply[0], TOCLIENT_INIT); - writeU8(&reply[2], deployed); - writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS)); - // Send as reliable - m_con.Send(peer_id, 0, reply, true); + /* + Answer with a TOCLIENT_INIT + */ + { + SharedBuffer reply(2+1+6+8); + writeU16(&reply[0], TOCLIENT_INIT); + writeU8(&reply[2], deployed); + writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS)); + //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed()); + writeU64(&reply[2+1+6], 0); // no seed + + // Send as reliable + m_con.Send(peer_id, 0, reply, true); + } + + /* + Send complete position information + */ + SendMovePlayer(player); return; } + if(command == TOSERVER_INIT2) { derr_server<id); SendInventory(peer->id); + + // Send HP + { + Player *player = m_env.getPlayer(peer_id); + SendPlayerHP(player); + } // Send time of day { SharedBuffer data = makePacket_TOCLIENT_TIME_OF_DAY( - m_time_of_day.get()); + m_env.getTimeOfDay()); m_con.Send(peer->id, 0, data, true); } @@ -1832,6 +2041,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(datasize < 13) return; + if((player->privs & PRIV_BUILD) == 0) + return; + /* [0] u16 command [2] u8 button (0=left, 1=right) @@ -1900,6 +2112,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Add to inventory and send inventory ilist->addItem(item); + UpdateCrafting(player->peer_id); SendInventory(player->peer_id); } @@ -1907,53 +2120,149 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) block->removeObject(id); } } - else if(command == TOSERVER_GROUND_ACTION) + else if(command == TOSERVER_CLICK_ACTIVEOBJECT) { - if(datasize < 17) + if(datasize < 7) + return; + + if((player->privs & PRIV_BUILD) == 0) return; + /* - length: 17 + length: 7 [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 + [2] u8 button (0=left, 1=right) + [3] u16 id + [5] u16 item */ - u8 action = readU8(&data[2]); - v3s16 p_under; - p_under.X = readS16(&data[3]); - p_under.Y = readS16(&data[5]); - p_under.Z = readS16(&data[7]); - v3s16 p_over; - p_over.X = readS16(&data[9]); - p_over.Y = readS16(&data[11]); - p_over.Z = readS16(&data[13]); - u16 item_i = readU16(&data[15]); + u8 button = readU8(&data[2]); + u16 id = readS16(&data[3]); + u16 item_i = readU16(&data[11]); + + ServerActiveObject *obj = m_env.getActiveObject(id); - //TODO: Check that target is reasonably close - - /* - 0: start digging - */ - if(action == 0) + if(obj == NULL) { - /* - NOTE: This can be used in the future to check if - somebody is cheating, by checking the timing. - */ - } // action == 0 + derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found" + <inventory.getList("main"); + if(g_settings.getBool("creative_mode") == false && ilist != NULL) + { + + // Skip if inventory has no free space + if(ilist->getUsedSlots() == ilist->getSize()) + { + dout_server<<"Player inventory has no free space"<m_removed) + return; + + /* + Create the inventory item + */ + InventoryItem *item = obj->createPickedUpItem(); + + if(item) + { + // Add to inventory and send inventory + ilist->addItem(item); + UpdateCrafting(player->peer_id); + SendInventory(player->peer_id); + + // Remove object from environment + obj->m_removed = true; + } + else + { + /* + Item cannot be picked up. Punch it instead. + */ + + ToolItem *titem = NULL; + std::string toolname = ""; + + InventoryList *mlist = player->inventory.getList("main"); + if(mlist != NULL) + { + InventoryItem *item = mlist->getItem(item_i); + if(item && (std::string)item->getName() == "ToolItem") + { + titem = (ToolItem*)item; + toolname = titem->getToolName(); + } + } + + u16 wear = obj->punch(toolname); + + if(titem) + { + bool weared_out = titem->addWear(wear); + if(weared_out) + mlist->deleteItem(item_i); + SendInventory(player->peer_id); + } + } + } + } + } + else if(command == TOSERVER_GROUND_ACTION) + { + if(datasize < 17) + 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 action = readU8(&data[2]); + v3s16 p_under; + p_under.X = readS16(&data[3]); + p_under.Y = readS16(&data[5]); + p_under.Z = readS16(&data[7]); + v3s16 p_over; + p_over.X = readS16(&data[9]); + p_over.Y = readS16(&data[11]); + p_over.Z = readS16(&data[13]); + u16 item_i = readU16(&data[15]); + + //TODO: Check that target is reasonably close + + /* + 0: start digging + */ + if(action == 0) + { + /* + NOTE: This can be used in the future to check if + somebody is cheating, by checking the timing. + */ + } // action == 0 + + /* + 2: stop digging + */ + else if(action == 2) + { +#if 0 RemoteClient *client = getClient(peer->id); JMutexAutoLock digmutex(client->m_dig_mutex); client->m_dig_tool_item = -1; @@ -1968,34 +2277,43 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Mandatory parameter; actually used for nothing core::map modified_blocks; - u8 material; + u8 material = CONTENT_IGNORE; u8 mineral = MINERAL_NONE; + bool cannot_remove_node = false; + try { MapNode n = m_env.getMap().getNode(p_under); + // Get mineral + mineral = n.getMineral(); // Get material at position material = n.d; - // If it's not diggable, do nothing - if(content_diggable(material) == false) + // If not yet cancelled + if(cannot_remove_node == false) { - derr_server<<"Server: Not finishing digging: Node not diggable" - <SetBlockNotSent(blockpos); - - return; + // If it's not diggable, do nothing + if(content_diggable(material) == false) + { + derr_server<<"Server: Not finishing digging: " + <<"Node not diggable" + <nodeRemovalDisabled() == true) + { + derr_server<<"Server: Not finishing digging: " + <<"Node metadata disables removal" + <privs & PRIV_BUILD) == 0) + cannot_remove_node = true; + + /* + If node can't be removed, set block to be re-sent to + client and quit. + */ + if(cannot_remove_node) + { + derr_server<<"Server: Not finishing digging."<SetBlockNotSent(blockpos); + return; } /* - Send the removal to all other clients + Send the removal to all other clients. + - If other player is close, send REMOVENODE + - Otherwise set blocks not sent */ - sendRemoveNode(p_over, peer_id); + core::list far_players; + sendRemoveNode(p_under, peer_id, &far_players, 100); /* Update and send inventory @@ -2076,6 +2422,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) player->inventory.addItem("main", item); // Send inventory + UpdateCrafting(player->peer_id); SendInventory(player->peer_id); } } @@ -2087,6 +2434,20 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) m_ignore_map_edit_events = true; m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks); m_ignore_map_edit_events = false; + + /* + Set blocks not sent to far players + */ + for(core::list::Iterator + i = far_players.begin(); + i != far_players.end(); i++) + { + u16 peer_id = *i; + RemoteClient *client = getClient(peer_id); + if(client==NULL) + continue; + client->SetBlocksNotSent(modified_blocks); + } } /* @@ -2114,7 +2475,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) try{ // Don't add a node if this is not a free space MapNode n2 = m_env.getMap().getNode(p_over); - if(content_buildable_to(n2.d) == false) + if(content_buildable_to(n2.d) == false + || (player->privs & PRIV_BUILD) ==0) { // Client probably has wrong data. // Set block not sent, so that client will get @@ -2151,7 +2513,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) /* Send to all players */ - sendAddNode(p_over, n, 0); + core::list far_players; + sendAddNode(p_over, n, 0, &far_players, 100); /* Handle inventory @@ -2165,6 +2528,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) else mitem->remove(1); // Send inventory + UpdateCrafting(peer_id); SendInventory(peer_id); } @@ -2178,6 +2542,20 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks); m_ignore_map_edit_events = false; + /* + Set blocks not sent to far players + */ + for(core::list::Iterator + i = far_players.begin(); + i != far_players.end(); i++) + { + u16 peer_id = *i; + RemoteClient *client = getClient(peer_id); + if(client==NULL) + continue; + client->SetBlocksNotSent(modified_blocks); + } + /* Calculate special events */ @@ -2194,68 +2572,39 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) }*/ } /* - Handle other items + Place other item (not a block) */ else { v3s16 blockpos = getNodeBlockPos(p_over); - - MapBlock *block = NULL; - try - { - block = m_env.getMap().getBlockNoCreate(blockpos); - } - catch(InvalidPositionException &e) + + /* + Check that the block is loaded so that the item + can properly be added to the static list too + */ + MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos); + if(block==NULL) { derr_server<<"Error while placing object: " "block not found"<getPosRelative(); - v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS); - - v3f pos = intToFloat(p_over, BS); - pos -= block_pos_f_on_map; + dout_server<<"Placing a miscellaneous item on map" + <getName()) - { - MapBlockObjectItem *oitem = (MapBlockObjectItem*)item; + // Calculate a position for it + v3f pos = intToFloat(p_over, BS); + //pos.Y -= BS*0.45; + pos.Y -= BS*0.25; // let it drop a bit + // Randomize a bit + pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0; + pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0; - /*dout_server<<"Trying to place a MapBlockObjectItem: " - "inventorystring=\"" - <getInventoryString() - <<"\""<createObject - (pos, player->getYaw(), player->getPitch()); - } /* - Handle other items + Create the object */ - else - { - dout_server<<"Placing a miscellaneous item on map" - <serialize(os); - dout_server<<"Item string is \""<setItemString(os.str()); - obj = iobj; - } + ServerActiveObject *obj = item->createSAO(&m_env, 0, pos); if(obj == NULL) { @@ -2265,16 +2614,34 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } else { - block->addObject(obj); - + // Add the object to the environment + m_env.addActiveObject(obj); + dout_server<<"Placed object"<inventory.getList("main"); - if(g_settings.getBool("creative_mode") == false && ilist) + if(g_settings.getBool("creative_mode") == false) { - // Remove from inventory and send inventory - ilist->deleteItem(item_i); + // Delete the right amount of items from the slot + u16 dropcount = item->getDropCount(); + + // Delete item if all gone + if(item->getCount() <= dropcount) + { + if(item->getCount() < dropcount) + dstream<<"WARNING: Server: dropped more items" + <<" than the slot contains"<inventory.getList("main"); + if(ilist) + // Remove from inventory and send inventory + ilist->deleteItem(item_i); + } + // Else decrement it + else + item->remove(dropcount); + // Send inventory + UpdateCrafting(peer_id); SendInventory(peer_id); } } @@ -2306,6 +2673,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) #endif else if(command == TOSERVER_SIGNTEXT) { + if((player->privs & PRIV_BUILD) == 0) + return; /* u16 command v3s16 blockpos @@ -2361,6 +2730,54 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) obj->getBlock()->setChangedFlag(); } + else if(command == TOSERVER_SIGNNODETEXT) + { + if((player->privs & PRIV_BUILD) == 0) + return; + /* + u16 command + v3s16 p + u16 textlen + textdata + */ + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + u8 buf[6]; + // Read stuff + is.read((char*)buf, 6); + v3s16 p = readV3S16(buf); + is.read((char*)buf, 2); + u16 textlen = readU16(buf); + std::string text; + for(u16 i=0; itypeId() != CONTENT_SIGN_WALL) + return; + SignNodeMetadata *signmeta = (SignNodeMetadata*)meta; + signmeta->setText(text); + + v3s16 blockpos = getNodeBlockPos(p); + MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos); + if(block) + { + block->setChangedFlag(); + } + + for(core::map::Iterator + i = m_clients.getIterator(); + i.atEnd()==false; i++) + { + RemoteClient *client = i.getNode()->getValue(); + client->SetBlockNotSent(blockpos); + } + } else if(command == TOSERVER_INVENTORY_ACTION) { /*// Ignore inventory changes if in creative mode @@ -2378,6 +2795,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) InventoryAction *a = InventoryAction::deSerialize(is); if(a != NULL) { + // Create context + InventoryContext c; + c.current_player = player; + /* Handle craftresult specially if not in creative mode */ @@ -2386,50 +2807,67 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) && g_settings.getBool("creative_mode") == false) { IMoveAction *ma = (IMoveAction*)a; - // Don't allow moving anything to craftresult - if(ma->to_name == "craftresult") - { - // Do nothing - disable_action = true; - } - // When something is removed from craftresult - if(ma->from_name == "craftresult") + if(ma->to_inv == "current_player" && + ma->from_inv == "current_player") { - disable_action = true; - // Remove stuff from craft + InventoryList *rlist = player->inventory.getList("craftresult"); + assert(rlist); InventoryList *clist = player->inventory.getList("craft"); - if(clist) + assert(clist); + InventoryList *mlist = player->inventory.getList("main"); + assert(mlist); + /* + Craftresult is no longer preview if something + is moved into it + */ + if(ma->to_list == "craftresult" + && ma->from_list != "craftresult") { - u16 count = ma->count; - if(count == 0) - count = 1; - clist->decrementMaterials(count); + // If it currently is a preview, remove + // its contents + if(player->craftresult_is_preview) + { + rlist->deleteItem(0); + } + player->craftresult_is_preview = false; } - // Do action - // Feed action to player inventory - a->apply(&player->inventory); - // Eat it - delete a; - // If something appeared in craftresult, throw it - // in the main list - InventoryList *rlist = player->inventory.getList("craftresult"); - InventoryList *mlist = player->inventory.getList("main"); - if(rlist && mlist && rlist->getUsedSlots() == 1) + /* + Crafting takes place if this condition is true. + */ + if(player->craftresult_is_preview && + ma->from_list == "craftresult") + { + player->craftresult_is_preview = false; + clist->decrementMaterials(1); + } + /* + If the craftresult is placed on itself, move it to + main inventory instead of doing the action + */ + if(ma->to_list == "craftresult" + && ma->from_list == "craftresult") { + disable_action = true; + InventoryItem *item1 = rlist->changeItem(0, NULL); mlist->addItem(item1); } } } + if(disable_action == false) { // Feed action to player inventory - a->apply(&player->inventory); - // Eat it + a->apply(&c, this); + // Eat the action delete a; } - // Send inventory - SendInventory(player->peer_id); + else + { + // Send inventory + UpdateCrafting(player->peer_id); + SendInventory(player->peer_id); + } } else { @@ -2470,6 +2908,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Whether to send to other players bool send_to_others = false; + // Local player gets all privileges regardless of + // what's set on their account. + u64 privs = player->privs; + if(g_settings.get("name") == player->getName()) + privs = PRIV_ALL; + // Parse commands std::wstring commandprefix = L"/#"; if(message.substr(0, commandprefix.size()) == commandprefix) @@ -2477,73 +2921,36 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) line += L"Server: "; message = message.substr(commandprefix.size()); - // Get player name as narrow string - std::string name_s = player->getName(); - // Convert message to narrow string - std::string message_s = wide_to_narrow(message); - // Operator is the single name defined in config. - std::string operator_name = g_settings.get("name"); - bool is_operator = (operator_name != "" && - wide_to_narrow(name) == operator_name); - bool valid_command = false; - if(message_s == "help") - { - line += L"-!- Available commands: "; - line += L"status "; - if(is_operator) - { - line += L"shutdown setting "; - } - else - { - } - send_to_sender = true; - valid_command = true; - } - else if(message_s == "status") - { - line = getStatusString(); - send_to_sender = true; - valid_command = true; - } - else if(is_operator) + + ServerCommandContext *ctx = new ServerCommandContext( + str_split(message, L' '), + this, + &m_env, + player, + privs); + + line += processServerCommand(ctx); + send_to_sender = ctx->flags & 1; + send_to_others = ctx->flags & 2; + delete ctx; + + } + else + { + if(privs & PRIV_SHOUT) { - if(message_s == "shutdown") - { - dstream< "; + line += message; + send_to_others = true; } - - if(valid_command == false) + else { - line += L"-!- Invalid command: " + message; + line += L"Server: You are not allowed to shout"; send_to_sender = true; } } - else - { - line += L"<"; - /*if(is_operator) - line += L"@";*/ - line += name; - line += L"> "; - line += message; - send_to_others = true; - } if(line != L"") { @@ -2573,16 +2980,71 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } } } - else - { - derr_server<<"WARNING: Server::ProcessData(): Ignoring " - "unknown command "<hp > damage) + { + player->hp -= damage; + } + else + { + player->hp = 0; + + dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies" + <setPosition(pos); + player->hp = 20; + SendMovePlayer(player); + SendPlayerHP(player); + + //TODO: Throw items around + } + } + + SendPlayerHP(player); + } + else if(command == TOSERVER_PASSWORD) + { + /* + [0] u16 TOSERVER_PASSWORD + [2] u8[28] old password + [30] u8[28] new password + */ + + if(datasize != 2+PASSWORD_SIZE*2) + return; + char password[PASSWORD_SIZE]; + for(u32 i=0; igetPassword(),password)) + { + // Wrong old password supplied!! + SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed."); + return; + } + for(u32 i=0; iupdatePassword(password); + SendChatMessage(peer_id, L"Password change successful"); + } + else + { + derr_server<<"WARNING: Server::ProcessData(): Ignoring " + "unknown command "<current_player); + return &(c->current_player->inventory); + } + + Strfnd fn(id); + std::string id0 = fn.next(":"); + + if(id0 == "nodemeta") + { + v3s16 p; + p.X = stoi(fn.next(",")); + p.Y = stoi(fn.next(",")); + p.Z = stoi(fn.next(",")); + NodeMetadata *meta = m_env.getMap().getNodeMetadata(p); + if(meta) + return meta->getInventory(); + dstream<<"nodemeta at ("<current_player); + // Send inventory + UpdateCrafting(c->current_player->peer_id); + SendInventory(c->current_player->peer_id); + return; + } + + Strfnd fn(id); + std::string id0 = fn.next(":"); + + if(id0 == "nodemeta") + { + v3s16 p; + p.X = stoi(fn.next(",")); + p.Y = stoi(fn.next(",")); + p.Z = stoi(fn.next(",")); + v3s16 blockpos = getNodeBlockPos(p); + + NodeMetadata *meta = m_env.getMap().getNodeMetadata(p); + if(meta) + meta->inventoryModified(); + + for(core::map::Iterator + i = m_clients.getIterator(); + i.atEnd()==false; i++) + { + RemoteClient *client = i.getNode()->getValue(); + client->SetBlockNotSent(blockpos); + } + + return; + } + + dstream<<__FUNCTION_NAME<<": unknown id "< Server::getPlayerInfo() { DSTACK(__FUNCTION_NAME); @@ -2666,6 +3196,43 @@ void Server::deletingPeer(con::Peer *peer, bool timeout) m_peer_change_queue.push_back(c); } +/* + Static send methods +*/ + +void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp) +{ + DSTACK(__FUNCTION_NAME); + std::ostringstream os(std::ios_base::binary); + + writeU16(os, TOCLIENT_HP); + writeU8(os, hp); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + con.Send(peer_id, 0, data, true); +} + +void Server::SendAccessDenied(con::Connection &con, u16 peer_id) +{ + DSTACK(__FUNCTION_NAME); + std::ostringstream os(std::ios_base::binary); + + writeU16(os, TOCLIENT_ACCESS_DENIED); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + con.Send(peer_id, 0, data, true); +} + +/* + Non-static send methods +*/ + void Server::SendObjectData(float dtime) { DSTACK(__FUNCTION_NAME); @@ -2724,294 +3291,12 @@ void Server::SendPlayerInfos() m_con.SendToAll(0, data, true); } -/* - Craft checking system -*/ - -enum ItemSpecType -{ - ITEM_NONE, - ITEM_MATERIAL, - ITEM_CRAFT, - ITEM_TOOL, - ITEM_MBO -}; - -struct ItemSpec -{ - ItemSpec(): - type(ITEM_NONE) - { - } - ItemSpec(enum ItemSpecType a_type, std::string a_name): - type(a_type), - name(a_name), - num(65535) - { - } - ItemSpec(enum ItemSpecType a_type, u16 a_num): - type(a_type), - name(""), - num(a_num) - { - } - enum ItemSpecType type; - // Only other one of these is used - std::string name; - u16 num; -}; - -/* - items: a pointer to an array of 9 pointers to items - specs: a pointer to an array of 9 ItemSpecs -*/ -bool checkItemCombination(InventoryItem **items, ItemSpec *specs) -{ - u16 items_min_x = 100; - u16 items_max_x = 100; - u16 items_min_y = 100; - u16 items_max_y = 100; - for(u16 y=0; y<3; y++) - for(u16 x=0; x<3; x++) - { - if(items[y*3 + x] == NULL) - continue; - if(items_min_x == 100 || x < items_min_x) - items_min_x = x; - if(items_min_y == 100 || y < items_min_y) - items_min_y = y; - if(items_max_x == 100 || x > items_max_x) - items_max_x = x; - if(items_max_y == 100 || y > items_max_y) - items_max_y = y; - } - // No items at all, just return false - if(items_min_x == 100) - return false; - - u16 items_w = items_max_x - items_min_x + 1; - u16 items_h = items_max_y - items_min_y + 1; - - u16 specs_min_x = 100; - u16 specs_max_x = 100; - u16 specs_min_y = 100; - u16 specs_max_y = 100; - for(u16 y=0; y<3; y++) - for(u16 x=0; x<3; x++) - { - if(specs[y*3 + x].type == ITEM_NONE) - continue; - if(specs_min_x == 100 || x < specs_min_x) - specs_min_x = x; - if(specs_min_y == 100 || y < specs_min_y) - specs_min_y = y; - if(specs_max_x == 100 || x > specs_max_x) - specs_max_x = x; - if(specs_max_y == 100 || y > specs_max_y) - specs_max_y = y; - } - // No specs at all, just return false - if(specs_min_x == 100) - return false; - - u16 specs_w = specs_max_x - specs_min_x + 1; - u16 specs_h = specs_max_y - specs_min_y + 1; - - // Different sizes - if(items_w != specs_w || items_h != specs_h) - return false; - - for(u16 y=0; ygetName(); - - if(spec.type == ITEM_MATERIAL) - { - if(itemname != "MaterialItem") - return false; - MaterialItem *mitem = (MaterialItem*)item; - if(mitem->getMaterial() != spec.num) - return false; - } - else if(spec.type == ITEM_CRAFT) - { - if(itemname != "CraftItem") - return false; - CraftItem *mitem = (CraftItem*)item; - if(mitem->getSubName() != spec.name) - return false; - } - else if(spec.type == ITEM_TOOL) - { - // Not supported yet - assert(0); - } - else if(spec.type == ITEM_MBO) - { - // Not supported yet - assert(0); - } - else - { - // Not supported yet - assert(0); - } - } - - return true; -} - void Server::SendInventory(u16 peer_id) { DSTACK(__FUNCTION_NAME); Player* player = m_env.getPlayer(peer_id); - - /* - Calculate crafting stuff - */ - if(g_settings.getBool("creative_mode") == false) - { - InventoryList *clist = player->inventory.getList("craft"); - InventoryList *rlist = player->inventory.getList("craftresult"); - if(rlist) - { - rlist->clearItems(); - } - if(clist && rlist) - { - InventoryItem *items[9]; - for(u16 i=0; i<9; i++) - { - items[i] = clist->getItem(i); - } - - bool found = false; - - // Wood - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new MaterialItem(CONTENT_WOOD, 4)); - found = true; - } - } - - // Stick - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new CraftItem("Stick", 4)); - found = true; - } - } - - // Sign - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new MapBlockObjectItem("Sign")); - found = true; - } - } - - // Torch - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal"); - specs[3] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new MaterialItem(CONTENT_TORCH, 4)); - found = true; - } - } - - // Wooden pick - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); - specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); - specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new ToolItem("WPick", 0)); - found = true; - } - } - - // Stone pick - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE); - specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE); - specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE); - specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); - specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new ToolItem("STPick", 0)); - found = true; - } - } - - // Mese pick - if(!found) - { - ItemSpec specs[9]; - specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE); - specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE); - specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE); - specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); - specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); - if(checkItemCombination(items, specs)) - { - rlist->addItem(new ToolItem("MesePick", 0)); - found = true; - } - } - } - } // if creative_mode == false + assert(player); /* Serialize it @@ -3078,12 +3363,47 @@ void Server::BroadcastChatMessage(const std::wstring &message) } } -void Server::sendRemoveNode(v3s16 p, u16 ignore_id) +void Server::SendPlayerHP(Player *player) { - JMutexAutoLock conlock(m_con_mutex); - - // Create packet - u32 replysize = 8; + SendHP(m_con, player->peer_id, player->hp); +} + +void Server::SendMovePlayer(Player *player) +{ + DSTACK(__FUNCTION_NAME); + std::ostringstream os(std::ios_base::binary); + + writeU16(os, TOCLIENT_MOVE_PLAYER); + writeV3F1000(os, player->getPosition()); + writeF1000(os, player->getPitch()); + writeF1000(os, player->getYaw()); + + { + v3f pos = player->getPosition(); + f32 pitch = player->getPitch(); + f32 yaw = player->getYaw(); + dstream<<"Server sending TOCLIENT_MOVE_PLAYER" + <<" pos=("< reply(replysize); - v3s16 p = block->getPos(); writeU16(&reply[0], TOCLIENT_BLOCKDATA); writeS16(&reply[2], p.X); writeS16(&reply[4], p.Y); writeS16(&reply[6], p.Z); memcpy(&reply[8], *blockdata, blockdata.getSize()); - /*dstream<<"Sending block ("< queue; - s32 total_sending = 0; + s32 total_sending = 0; + + for(core::map::Iterator + i = m_clients.getIterator(); + i.atEnd() == false; i++) + { + RemoteClient *client = i.getNode()->getValue(); + assert(client->peer_id == i.getNode()->getKey()); + + total_sending += client->SendingCount(); + + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; + + client->GetNextBlocks(this, dtime, queue); + } + + // Sort. + // Lowest priority number comes first. + // Lowest is most important. + queue.sort(); + + for(u32 i=0; i= g_settings.getS32 + ("max_simultaneous_block_sends_server_total")) + break; + + PrioritySortedBlockTransfer q = queue[i]; + + MapBlock *block = NULL; + try + { + block = m_env.getMap().getBlockNoCreate(q.pos); + } + catch(InvalidPositionException &e) + { + continue; + } + + RemoteClient *client = getClient(q.peer_id); + + SendBlockNoLock(q.peer_id, block, client->serialization_version); + + client->SentBlock(q.pos); + + total_sending++; + } +} + +/* + Something random +*/ + +void Server::UpdateCrafting(u16 peer_id) +{ + DSTACK(__FUNCTION_NAME); + + Player* player = m_env.getPlayer(peer_id); + assert(player); + + /* + Calculate crafting stuff + */ + if(g_settings.getBool("creative_mode") == false) + { + InventoryList *clist = player->inventory.getList("craft"); + InventoryList *rlist = player->inventory.getList("craftresult"); + + if(rlist->getUsedSlots() == 0) + player->craftresult_is_preview = true; + + if(rlist && player->craftresult_is_preview) + { + rlist->clearItems(); + } + if(clist && rlist && player->craftresult_is_preview) + { + InventoryItem *items[9]; + for(u16 i=0; i<9; i++) + { + items[i] = clist->getItem(i); + } + + bool found = false; + + // Wood + if(!found) + { + ItemSpec specs[9]; + specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new MaterialItem(CONTENT_WOOD, 4)); + found = true; + } + } + + // Stick + if(!found) + { + ItemSpec specs[9]; + specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new CraftItem("Stick", 4)); + found = true; + } + } + + // Fence + if(!found) + { + ItemSpec specs[9]; + specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); + specs[5] = ItemSpec(ITEM_CRAFT, "Stick"); + specs[6] = ItemSpec(ITEM_CRAFT, "Stick"); + specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); + specs[8] = ItemSpec(ITEM_CRAFT, "Stick"); + specs[9] = ItemSpec(ITEM_CRAFT, "Stick"); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new MaterialItem(CONTENT_FENCE, 2)); + found = true; + } + } + + // Sign + if(!found) + { + ItemSpec specs[9]; + specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); + if(checkItemCombination(items, specs)) + { + //rlist->addItem(new MapBlockObjectItem("Sign")); + rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1)); + found = true; + } + } + + // Torch + if(!found) + { + ItemSpec specs[9]; + specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal"); + specs[3] = ItemSpec(ITEM_CRAFT, "Stick"); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new MaterialItem(CONTENT_TORCH, 4)); + found = true; + } + } + + // Wooden pick + if(!found) + { + ItemSpec specs[9]; + specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); + specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new ToolItem("WPick", 0)); + found = true; + } + } + + // Stone pick + if(!found) + { + ItemSpec specs[9]; + specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); + specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); + specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); + specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); + specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new ToolItem("STPick", 0)); + found = true; + } + } + + // Steel pick + if(!found) + { + ItemSpec specs[9]; + specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); + specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new ToolItem("SteelPick", 0)); + found = true; + } + } + + // Mese pick + if(!found) + { + ItemSpec specs[9]; + specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE); + specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE); + specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE); + specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); + specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new ToolItem("MesePick", 0)); + found = true; + } + } + + // Wooden shovel + if(!found) + { + ItemSpec specs[9]; + specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); + specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new ToolItem("WShovel", 0)); + found = true; + } + } + + // Stone shovel + if(!found) + { + ItemSpec specs[9]; + specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); + specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); + specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new ToolItem("STShovel", 0)); + found = true; + } + } + + // Steel shovel + if(!found) + { + ItemSpec specs[9]; + specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); + specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new ToolItem("SteelShovel", 0)); + found = true; + } + } - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) - { - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); + // Wooden axe + if(!found) + { + ItemSpec specs[9]; + specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); + specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new ToolItem("WAxe", 0)); + found = true; + } + } - total_sending += client->SendingCount(); - - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - - client->GetNextBlocks(this, dtime, queue); - } + // Stone axe + if(!found) + { + ItemSpec specs[9]; + specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); + specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); + specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); + specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); + specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new ToolItem("STAxe", 0)); + found = true; + } + } - // Sort. - // Lowest priority number comes first. - // Lowest is most important. - queue.sort(); + // Steel axe + if(!found) + { + ItemSpec specs[9]; + specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + specs[4] = ItemSpec(ITEM_CRAFT, "Stick"); + specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new ToolItem("SteelAxe", 0)); + found = true; + } + } - for(u32 i=0; i= g_settings.getS32 - ("max_simultaneous_block_sends_server_total")) - break; - - PrioritySortedBlockTransfer q = queue[i]; + // Wooden sword + if(!found) + { + ItemSpec specs[9]; + specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new ToolItem("WSword", 0)); + found = true; + } + } - MapBlock *block = NULL; - try - { - block = m_env.getMap().getBlockNoCreate(q.pos); - } - catch(InvalidPositionException &e) - { - continue; - } + // Stone sword + if(!found) + { + ItemSpec specs[9]; + specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); + specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); + specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new ToolItem("STSword", 0)); + found = true; + } + } - RemoteClient *client = getClient(q.peer_id); + // Steel sword + if(!found) + { + ItemSpec specs[9]; + specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new ToolItem("SteelSword", 0)); + found = true; + } + } - SendBlockNoLock(q.peer_id, block, client->serialization_version); + // Chest + if(!found) + { + ItemSpec specs[9]; + specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new MaterialItem(CONTENT_CHEST, 1)); + found = true; + } + } - client->SentBlock(q.pos); + // Furnace + if(!found) + { + ItemSpec specs[9]; + specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); + specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); + specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); + specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); + specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); + specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); + specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); + specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1)); + found = true; + } + } - total_sending++; - } + // Steel block + if(!found) + { + ItemSpec specs[9]; + specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new MaterialItem(CONTENT_STEEL, 1)); + found = true; + } + } + } + + } // if creative_mode == false } - RemoteClient* Server::getClient(u16 peer_id) { DSTACK(__FUNCTION_NAME); @@ -3247,8 +3992,10 @@ std::wstring Server::getStatusString() { std::wostringstream os(std::ios_base::binary); os<::Iterator @@ -3280,14 +4027,24 @@ void setCreativeInventory(Player *player) { player->resetInventory(); - // Give some good picks + // Give some good tools { - InventoryItem *item = new ToolItem("STPick", 0); + InventoryItem *item = new ToolItem("MesePick", 0); void* r = player->inventory.addItem("main", item); assert(r == NULL); } { - InventoryItem *item = new ToolItem("MesePick", 0); + InventoryItem *item = new ToolItem("SteelPick", 0); + void* r = player->inventory.addItem("main", item); + assert(r == NULL); + } + { + InventoryItem *item = new ToolItem("SteelAxe", 0); + void* r = player->inventory.addItem("main", item); + assert(r == NULL); + } + { + InventoryItem *item = new ToolItem("SteelShovel", 0); void* r = player->inventory.addItem("main", item); assert(r == NULL); } @@ -3295,6 +4052,40 @@ void setCreativeInventory(Player *player) /* Give materials */ + + // CONTENT_IGNORE-terminated list + u8 material_items[] = { + CONTENT_TORCH, + CONTENT_COBBLE, + CONTENT_MUD, + CONTENT_STONE, + CONTENT_SAND, + CONTENT_TREE, + CONTENT_LEAVES, + CONTENT_GLASS, + CONTENT_FENCE, + CONTENT_MESE, + CONTENT_WATERSOURCE, + CONTENT_CLOUD, + CONTENT_CHEST, + CONTENT_FURNACE, + CONTENT_SIGN_WALL, + CONTENT_IGNORE + }; + + u8 *mip = material_items; + for(u16 i=0; iinventory.addItem("main", item); + + mip++; + } + +#if 0 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE); // add torch first @@ -3312,16 +4103,65 @@ void setCreativeInventory(Player *player) InventoryItem *item = new MaterialItem(i, 1); player->inventory.addItem("main", item); } - // Sign +#endif + + /*// Sign { InventoryItem *item = new MapBlockObjectItem("Sign Example text"); void* r = player->inventory.addItem("main", item); assert(r == NULL); + }*/ +} + +v3f findSpawnPos(ServerMap &map) +{ + //return v3f(50,50,50)*BS; + + v2s16 nodepos; + s16 groundheight = 0; + + // Try to find a good place a few times + for(s32 i=0; i<1000; i++) + { + s32 range = 1 + i; + // We're going to try to throw the player to this position + nodepos = v2s16(-range + (myrand()%(range*2)), + -range + (myrand()%(range*2))); + v2s16 sectorpos = getNodeSectorPos(nodepos); + // Get sector (NOTE: Don't get because it's slow) + //m_env.getMap().emergeSector(sectorpos); + // Get ground height at point (fallbacks to heightmap function) + groundheight = map.findGroundLevel(nodepos); + // Don't go underwater + if(groundheight < WATER_LEVEL) + { + //dstream<<"-> Underwater"< WATER_LEVEL + 4) + { + //dstream<<"-> Underwater"<peer_id = PEER_ID_INEXISTENT; player->peer_id = peer_id; player->updateName(name); + player->updatePassword(password); + + if(g_settings.exists("default_privs")) + player->privs = g_settings.getU64("default_privs"); /* Set player position @@ -3375,87 +4219,9 @@ Player *Server::emergePlayer(const char *name, const char *password, dstream<<"Server: Finding spawn place for player \"" <getName()<<"\""<setPosition(intToFloat(v3s16( - 0, - 45, //64, - 0 - ), BS)); -#endif -#if 1 - s16 groundheight = 0; -#if 1 - // Try to find a good place a few times - for(s32 i=0; i<500; i++) - { - s32 range = 1 + i; - // We're going to try to throw the player to this position - nodepos = v2s16(-range + (myrand()%(range*2)), - -range + (myrand()%(range*2))); - v2s16 sectorpos = getNodeSectorPos(nodepos); - /* - Ignore position if it is near a chunk edge. - Otherwise it would cause excessive loading time at - initial generation - */ - { - if(m_env.getServerMap().sector_to_chunk(sectorpos+v2s16(1,1)) - != m_env.getServerMap().sector_to_chunk(sectorpos+v2s16(-1,-1))) - continue; - } - // Get sector - m_env.getMap().emergeSector(sectorpos); - // Get ground height at point - groundheight = m_env.getServerMap().findGroundLevel(nodepos); - // Don't go underwater - if(groundheight < WATER_LEVEL) - { - //dstream<<"-> Underwater"<emergeBlock(blockpos); - // Don't go inside ground - try{ - /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y); - v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/ - v3s16 footpos = nodepos3d + v3s16(0,0,0); - v3s16 headpos = nodepos3d + v3s16(0,1,0); - if(m_env.getMap().getNode(footpos).d != CONTENT_AIR - || m_env.getMap().getNode(headpos).d != CONTENT_AIR) - { - dstream<<"-> Inside ground"< Invalid position"<setPosition(intToFloat(v3s16( - nodepos.X, - groundheight + 1, - nodepos.Y - ), BS)); -#endif + player->setPosition(pos); /* Add player to environment @@ -3471,13 +4237,33 @@ Player *Server::emergePlayer(const char *name, const char *password, { setCreativeInventory(player); } - else + else if(g_settings.getBool("give_initial_stuff")) { - /*{ - InventoryItem *item = new ToolItem("WPick", 32000); + { + InventoryItem *item = new ToolItem("SteelPick", 0); void* r = player->inventory.addItem("main", item); assert(r == NULL); - }*/ + } + { + InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99); + void* r = player->inventory.addItem("main", item); + assert(r == NULL); + } + { + InventoryItem *item = new ToolItem("SteelAxe", 0); + void* r = player->inventory.addItem("main", item); + assert(r == NULL); + } + { + InventoryItem *item = new ToolItem("SteelShovel", 0); + void* r = player->inventory.addItem("main", item); + assert(r == NULL); + } + { + InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99); + void* r = player->inventory.addItem("main", item); + assert(r == NULL); + } /*{ InventoryItem *item = new MaterialItem(CONTENT_MESE, 6); void* r = player->inventory.addItem("main", item); @@ -3508,13 +4294,7 @@ Player *Server::emergePlayer(const char *name, const char *password, void* r = player->inventory.addItem("main", item); assert(r == NULL); }*/ - /*// Give some lights - { - InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999); - bool r = player->inventory.addItem("main", item); - assert(r == true); - } - // and some signs + /*// and some signs for(u16 i=0; i<4; i++) { InventoryItem *item = new MapBlockObjectItem("Sign Example text"); @@ -3534,30 +4314,6 @@ Player *Server::emergePlayer(const char *name, const char *password, } // create new player } -#if 0 -void Server::UpdateBlockWaterPressure(MapBlock *block, - core::map &modified_blocks) -{ - MapVoxelManipulator v(&m_env.getMap()); - v.m_disable_water_climb = - g_settings.getBool("disable_water_climb"); - - VoxelArea area(block->getPosRelative(), - block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1)); - - try - { - v.updateAreaWaterPressure(area, m_flow_active_nodes); - } - catch(ProcessingLimitException &e) - { - dstream<<"Processing limit reached (1)"<getValue(); + // Handle objects + for(core::map::Iterator + i = client->m_known_objects.getIterator(); + i.atEnd()==false; i++) + { + // Get object + u16 id = i.getNode()->getKey(); + ServerActiveObject* obj = m_env.getActiveObject(id); + + if(obj && obj->m_known_by_count > 0) + obj->m_known_by_count--; + } + // Collect information about leaving in chat std::wstring message; {