X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fserver.cpp;h=8a19306a9a55b7c1aeeaf12274178f5248494acc;hb=f143e269f47fd024f69c3f536b558fbac4828b41;hp=a3baaf1a380b7eb4e32b02013763435c9887d645;hpb=ea6740e9002839f79053cf9d8cd78eb3fcae1b47;p=dragonfireclient.git diff --git a/src/server.cpp b/src/server.cpp index a3baaf1a3..8a19306a9 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 @@ -17,10 +17,6 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -/* -(c) 2010 Perttu Ahola -*/ - #include "server.h" #include "utility.h" #include @@ -32,9 +28,42 @@ 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" +#include "content_mapnode.h" +#include "content_craft.h" +#include "content_nodemeta.h" +#include "mapblock.h" +#include "serverobject.h" #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0) +class MapEditEventIgnorer +{ +public: + MapEditEventIgnorer(bool *flag): + m_flag(flag) + { + if(*m_flag == false) + *m_flag = true; + else + m_flag = NULL; + } + + ~MapEditEventIgnorer() + { + if(m_flag) + { + assert(*m_flag); + *m_flag = false; + } + } + +private: + bool *m_flag; +}; + void * ServerThread::Thread() { ThreadStarted(); @@ -46,7 +75,12 @@ void * ServerThread::Thread() while(getRun()) { try{ - m_server->AsyncRunStep(); + //TimeTaker timer("AsyncRunStep() + Receive()"); + + { + //TimeTaker timer("AsyncRunStep()"); + m_server->AsyncRunStep(); + } //dout_server<<"Running m_server->Receive()"<Receive(); @@ -71,10 +105,10 @@ void * EmergeThread::Thread() DSTACK(__FUNCTION_NAME); - bool debug=false; - BEGIN_DEBUG_EXCEPTION_HANDLER + bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info"); + /* Get block info from queue, emerge them and send them to clients. @@ -90,7 +124,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"<::Iterator i; @@ -120,146 +166,132 @@ void * EmergeThread::Thread() // Check flags u8 flags = i.getNode()->getValue(); if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false) - optional = false; + only_from_disk = false; } } - - /*dstream<<"EmergeThread: p=" - <<"("<m_env_mutex); + block = map.getBlockNoCreateNoEx(p); + if(!block || block->isDummy() || !block->isGenerated()) + { + if(enable_mapgen_debug_info) + dstream<<"EmergeThread: not in memory, loading"<isGenerated() == false) + { + if(enable_mapgen_debug_info) + dstream<<"EmergeThread: generating"<isDummy()) - { - //dstream<<"EmergeThread: Got a dummy block"<m_ignore_map_edit_events); + + // Activate objects and stuff + m_server->m_env.activateBlock(block, 3600); } } + else + { + /*if(block->getLightingExpired()){ + lighting_invalidated_blocks[block->getPos()] = block; + }*/ + } + + // TODO: Some additional checking and lighting updating, + // see emergeBlock } - catch(InvalidPositionException &e) - { - // Block not found. - // This happens when position is over limit. - got_block = false; - } + + {//envlock + JMutexAutoLock envlock(m_server->m_env_mutex); if(got_block) { - if(debug && changed_blocks.size() > 0) - { - dout_server<::Iterator i = changed_blocks.getIterator(); - i.atEnd() == false; i++) - { - MapBlock *block = i.getNode()->getValue(); - v3s16 p = block->getPos(); - dout_server<<"("< 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) { - JMutexAutoLock lock(m_blocks_sent_mutex); - m_nearest_unsent_reset_timer += dtime; + // Keep this reset + m_nearest_unsent_reset_timer = 0; + return; } // Won't send anything if already sending + if(m_blocks_sending.size() >= g_settings.getU16 + ("max_simultaneous_block_sends_per_client")) { - JMutexAutoLock lock(m_blocks_sending_mutex); - - if(m_blocks_sending.size() >= g_settings.getU16 - ("max_simultaneous_block_sends_per_client")) - { - //dstream<<"Not sending any blocks, Queue full."<m_env.getPlayer(peer_id); @@ -334,8 +369,13 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, v3f playerpos = player->getPosition(); v3f playerspeed = player->getSpeed(); + v3f playerspeeddir(0,0,0); + if(playerspeed.getLength() > 1.0*BS) + playerspeeddir = playerspeed / playerspeed.getLength(); + // Predict to next block + v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS; - v3s16 center_nodepos = floatToInt(playerpos); + v3s16 center_nodepos = floatToInt(playerpos_predicted, BS); v3s16 center = getNodeBlockPos(center_nodepos); @@ -346,62 +386,61 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, camera_dir.rotateYZBy(player->getPitch()); camera_dir.rotateXZBy(player->getYaw()); + /*dstream<<"camera_dir=("<getPlayerName(peer_id)< lock(m_time_from_building.getLock()); - m_time_from_building.m_value += dtime; - /*if(m_time_from_building.m_value - < FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)*/ - if(m_time_from_building.m_value < g_settings.getFloat( - "full_block_send_enable_min_time_from_building")) - { - maximum_simultaneous_block_sends - = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS; - } + max_simul_sends_usually + = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS; } - u32 num_blocks_selected; - { - JMutexAutoLock lock(m_blocks_sending_mutex); - num_blocks_selected = m_blocks_sending.size(); - } + /* + Number of blocks sending + number of blocks selected for sending + */ + u32 num_blocks_selected = m_blocks_sending.size(); /* next time d will be continued from the d from which the nearest @@ -412,35 +451,39 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, */ s32 new_nearest_unsent_d = -1; - // Serialization version used - //u8 ser_version = serialization_version; - - //bool has_incomplete_blocks = false; - s16 d_max = g_settings.getS16("max_block_send_distance"); s16 d_max_gen = g_settings.getS16("max_block_generate_distance"); + // Don't loop very much at a time + if(d_max > d_start+1) + d_max = d_start+1; + /*if(d_max_gen > d_start+2) + d_max_gen = d_start+2;*/ + //dstream<<"Starting from "<= max_simul_dynamic) { - JMutexAutoLock lock(m_blocks_sending_mutex); - - // Limit is dynamically lowered when building - if(num_blocks_selected - >= maximum_simultaneous_block_sends_now) - { - /*dstream<<"Not sending more blocks. Queue full. " - < center.Y) - generate = false; - } - else { /*// Limit the generating area vertically to 2/3 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3) @@ -519,42 +547,42 @@ 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 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(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 differs a lot, don't generate + if(fabs(gh - y) > MAP_BLOCKSIZE*2) + generate = false; + // Actually, don't even send it + //continue; + #endif } #endif + //dstream<<"d="<m_env.getMap().getGroundHeight(p2d); - // The sector might not exist yet, thus no heightmap - if(y > GROUNDHEIGHT_VALID_MINVALUE) { - f32 by = p.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2; - if(fabs(by - y) > MAP_BLOCKSIZE + MAP_BLOCKSIZE/3 - && fabs(by - WATER_LEVEL) >= MAP_BLOCKSIZE) - continue; + continue; } } -#endif /* Check if map has this block @@ -606,32 +609,50 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, bool block_is_invalid = false; if(block != NULL) { - /*if(block->isIncomplete()) - { - has_incomplete_blocks = true; - continue; - }*/ + // Reset usage timer, this block will be of use in the future. + block->resetUsageTimer(); + // Block is dummy if data doesn't exist. + // It means it has been not found from disk and not generated if(block->isDummy()) { surely_not_found_on_disk = true; } - + + // Block is valid if lighting is up-to-date and data exists if(block->isValid() == false) { block_is_invalid = true; } + /*if(block->isFullyGenerated() == false) + { + block_is_invalid = true; + }*/ + +#if 0 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; - /*MapChunk *chunk = map->getChunk(chunkpos); - if(chunk == NULL) +#endif + if(block->isGenerated() == false) block_is_invalid = true; - else if(chunk->getIsVolatile() == true) - block_is_invalid = true;*/ +#if 1 + /* + If block is not close, don't send it unless it is near + ground level. + + Block is near ground level if night-time mesh + differs from day-time mesh. + */ + if(d > 3) + { + if(block->dayNightDiffed() == false) + continue; + } +#endif } /* @@ -645,27 +666,27 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, } /* - Record the lowest d from which a a block has been + Record the lowest d from which a block has been found being not sent and possibly to exist */ - if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d) + if(no_blocks_found_for_sending) { - new_nearest_unsent_d = d; + if(generate == true) + new_nearest_unsent_d = d; } + + no_blocks_found_for_sending = false; /* Add inexistent block to emerge queue. */ if(block == NULL || surely_not_found_on_disk || block_is_invalid) { - //dstream<<"asd"< lock - (m_num_blocks_in_emerge_queue.getLock());*/ - //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) + // Allow two blocks in queue per client + if(server->m_emerge_queue.peerItemCount(peer_id) < 2) { //dstream<<"Adding block to emerge queue"<= + g_settings.getS16("max_block_send_distance")) + { + // Pause time in seconds + m_nothing_to_send_pause_timer = 1.0; + /*dstream<<"nothing to send to " + <getPlayerName(peer_id) + <<" (d="<getPosition(); v3f playerspeed = player->getSpeed(); - v3s16 center_nodepos = floatToInt(playerpos); + v3s16 center_nodepos = floatToInt(playerpos, BS); v3s16 center = getNodeBlockPos(center_nodepos); s16 d_max = g_settings.getS16("active_object_range"); @@ -827,7 +876,6 @@ void RemoteClient::SendObjectData( Ignore blocks that haven't been sent to the client */ { - JMutexAutoLock sentlock(m_blocks_sent_mutex); if(m_blocks_sent.find(p) == NULL) continue; } @@ -844,7 +892,7 @@ void RemoteClient::SendObjectData( */ if(stepped_blocks.find(p) == NULL) { - block->stepObjects(dtime, true, server->getDayNightRatio()); + block->stepObjects(dtime, true, server->m_env.getDayNightRatio()); stepped_blocks.insert(p, true); block->setChangedFlag(); } @@ -862,7 +910,9 @@ void RemoteClient::SendObjectData( bos.write((char*)buf, 6); // Write objects - block->serializeObjects(bos, serialization_version); + //block->serializeObjects(bos, serialization_version); // DEPRECATED + // count=0 + writeU16(bos, 0); blockcount++; @@ -921,8 +971,6 @@ void RemoteClient::SendObjectData( void RemoteClient::GotBlock(v3s16 p) { - JMutexAutoLock lock(m_blocks_sending_mutex); - JMutexAutoLock lock2(m_blocks_sent_mutex); if(m_blocks_sending.find(p) != NULL) m_blocks_sending.remove(p); else @@ -936,13 +984,6 @@ void RemoteClient::GotBlock(v3s16 p) void RemoteClient::SentBlock(v3s16 p) { - JMutexAutoLock lock(m_blocks_sending_mutex); - /*if(m_blocks_sending.size() > 15) - { - dstream<<"RemoteClient::SentBlock(): " - <<"m_blocks_sending.size()=" - < &blocks) { - JMutexAutoLock sendinglock(m_blocks_sending_mutex); - JMutexAutoLock sentlock(m_blocks_sent_mutex); - m_nearest_unsent_d = 0; for(core::map::Iterator @@ -990,14 +1025,15 @@ void RemoteClient::SetBlocksNotSent(core::map &blocks) PlayerInfo::PlayerInfo() { name[0] = 0; + avg_rtt = 0; } void PlayerInfo::PrintLine(std::ostream *s) { (*s)<::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + /* + Send shutdown message + */ { - /*// Delete player - // NOTE: These are removed by env destructor - { - u16 peer_id = i.getNode()->getKey(); - JMutexAutoLock envlock(m_env_mutex); - m_env.removePlayer(peer_id); - }*/ + JMutexAutoLock conlock(m_con_mutex); - // Delete client - delete i.getNode()->getValue(); + std::wstring line = L"*** Server shutting down"; + + /* + Send the message to clients + */ + for(core::map::Iterator + i = m_clients.getIterator(); + i.atEnd() == false; i++) + { + // Get client and check that it is valid + RemoteClient *client = i.getNode()->getValue(); + assert(client->peer_id == i.getNode()->getKey()); + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; + + try{ + SendChatMessage(client->peer_id, line); + } + catch(con::PeerNotFoundException &e) + {} + } } -} -void Server::start(unsigned short port) -{ - DSTACK(__FUNCTION_NAME); - // Stop thread if already running - m_thread.stop(); + /* + Save players + */ + dstream<<"Server: Saving players"<::Iterator + i = m_clients.getIterator(); + i.atEnd() == false; i++) + { + /*// Delete player + // NOTE: These are removed by env destructor + { + u16 peer_id = i.getNode()->getKey(); + JMutexAutoLock envlock(m_env_mutex); + m_env.removePlayer(peer_id); + }*/ + + // Delete client + delete i.getNode()->getValue(); + } + } +} + +void Server::start(unsigned short port) +{ + DSTACK(__FUNCTION_NAME); + // Stop thread if already running + m_thread.stop(); // Initialize connection m_con.setTimeoutMs(30); @@ -1091,19 +1189,20 @@ void Server::start(unsigned short port) m_thread.setRun(true); m_thread.Start(); - dout_server<<"Server started on port "< modified_blocks; m_env.getMap().transformLiquids(modified_blocks); #if 0 @@ -1276,121 +1396,401 @@ void Server::AsyncRunStep() { //u16 peer_id = i.getNode()->getKey(); RemoteClient *client = i.getNode()->getValue(); + Player *player = m_env.getPlayer(client->peer_id); + if(player==NULL) + continue; + std::cout<getName()<<"\t"; client->PrintInfo(std::cout); } } } - /* - Update digging + //if(g_settings.getBool("enable_experimental")) + { - NOTE: Some of this could be moved to RemoteClient + /* + Check added and deleted active objects */ -#if 0 { + //dstream<<"Server: Checking added and deleted active objects"<::Iterator i = m_clients.getIterator(); i.atEnd() == false; i++) { RemoteClient *client = i.getNode()->getValue(); Player *player = m_env.getPlayer(client->peer_id); - - JMutexAutoLock digmutex(client->m_dig_mutex); - - if(client->m_dig_tool_item == -1) + if(player==NULL) + { + // This can happen if the client timeouts somehow + /*dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client " + <peer_id + <<" has no associated player"<m_dig_time_remaining -= dtime; - - if(client->m_dig_time_remaining > 0) + } + v3s16 pos = floatToInt(player->getPosition(), BS); + + core::map removed_objects; + core::map added_objects; + m_env.getRemovedActiveObjects(pos, radius, + client->m_known_objects, removed_objects); + m_env.getAddedActiveObjects(pos, radius, + client->m_known_objects, added_objects); + + // Ignore if nothing happened + if(removed_objects.size() == 0 && added_objects.size() == 0) { - client->m_time_from_building.set(0.0); + //dstream<<"INFO: active objects: none changed"<m_dig_position; + char buf[4]; - // Mandatory parameter; actually used for nothing - core::map modified_blocks; + // Handle removed objects + writeU16((u8*)buf, removed_objects.size()); + data_buffer.append(buf, 2); + for(core::map::Iterator + i = removed_objects.getIterator(); + i.atEnd()==false; i++) + { + // Get object + u16 id = i.getNode()->getKey(); + ServerActiveObject* obj = m_env.getActiveObject(id); - u8 material; + // Add to data buffer for sending + writeU16((u8*)buf, i.getNode()->getKey()); + data_buffer.append(buf, 2); + + // Remove from known objects + client->m_known_objects.remove(i.getNode()->getKey()); - try - { - // Get material at position - material = m_env.getMap().getNode(p_under).d; - // If it's not diggable, do nothing - if(content_diggable(material) == false) - { - derr_server<<"Server: Not finishing digging: Node not diggable" - <m_dig_tool_item = -1; - break; - } + if(obj && obj->m_known_by_count > 0) + obj->m_known_by_count--; } - catch(InvalidPositionException &e) + + // Handle added objects + writeU16((u8*)buf, added_objects.size()); + data_buffer.append(buf, 2); + for(core::map::Iterator + i = added_objects.getIterator(); + i.atEnd()==false; i++) { - derr_server<<"Server: Not finishing digging: Node not found" - <m_dig_tool_item = -1; - break; + // Get object + u16 id = i.getNode()->getKey(); + ServerActiveObject* obj = m_env.getActiveObject(id); + + // Get object type + u8 type = ACTIVEOBJECT_TYPE_INVALID; + if(obj == NULL) + dstream<<"WARNING: "<<__FUNCTION_NAME + <<": NULL object"<getType(); + + // Add to data buffer for sending + writeU16((u8*)buf, id); + data_buffer.append(buf, 2); + writeU8((u8*)buf, type); + data_buffer.append(buf, 1); + + 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); + + if(obj) + obj->m_known_by_count++; } - - // Create packet - u32 replysize = 8; - SharedBuffer reply(replysize); - writeU16(&reply[0], TOCLIENT_REMOVENODE); - writeS16(&reply[2], p_under.X); - writeS16(&reply[4], p_under.Y); - writeS16(&reply[6], p_under.Z); + + // Send packet + SharedBuffer reply(2 + data_buffer.size()); + writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD); + memcpy((char*)&reply[2], data_buffer.c_str(), + data_buffer.size()); // Send as reliable - m_con.SendToAll(0, reply, true); - - if(g_settings.getBool("creative_mode") == false) + m_con.Send(client->peer_id, 0, reply, true); + + dstream<<"INFO: Server: Sent object remove/add: " + < 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++) { - // Add to inventory and send inventory - InventoryItem *item = new MaterialItem(material, 1); - player->inventory.addItem("main", item); - SendInventory(player->peer_id); + u16 id = i.getNode()->getKey(); + all_known_objects[id] = true; } + } + + m_env.setKnownActiveObjects(whatever); +#endif - /* - Remove the node - (this takes some time so it is done after the quick stuff) - */ - m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks); + } + + /* + Send object messages + */ + { + JMutexAutoLock envlock(m_env_mutex); + JMutexAutoLock conlock(m_con_mutex); + + ScopeProfiler sp(&g_profiler, "Server: sending object messages"); + + // Key = object id + // Value = data sent by object + core::map* > buffered_messages; + + // Get active object messages from environment + for(;;) + { + ActiveObjectMessage aom = m_env.getActiveObjectMessage(); + if(aom.id == 0) + break; + core::list* message_list = NULL; + core::map* >::Node *n; + n = buffered_messages.find(aom.id); + if(n == NULL) + { + message_list = new core::list; + buffered_messages.insert(aom.id, message_list); + } + else + { + message_list = n->getValue(); + } + message_list->push_back(aom); + } + + // Route data to every client + for(core::map::Iterator + i = m_clients.getIterator(); + i.atEnd()==false; i++) + { + RemoteClient *client = i.getNode()->getValue(); + std::string reliable_data; + std::string unreliable_data; + // Go through all objects in message buffer + for(core::map* >::Iterator + j = buffered_messages.getIterator(); + j.atEnd()==false; j++) + { + // If object is not known by client, skip it + u16 id = j.getNode()->getKey(); + if(client->m_known_objects.find(id) == NULL) + continue; + // Get message list of object + core::list* list = j.getNode()->getValue(); + // Go through every message + for(core::list::Iterator + k = list->begin(); k != list->end(); k++) + { + // Compose the full new data with header + ActiveObjectMessage aom = *k; + std::string new_data; + // Add object id + char buf[2]; + writeU16((u8*)&buf[0], aom.id); + new_data.append(buf, 2); + // Add data + new_data += serializeString(aom.datastring); + // Add data to buffer + if(aom.reliable) + reliable_data += new_data; + else + unreliable_data += new_data; + } + } /* - Update water + reliable_data and unreliable_data are now ready. + Send them. */ - - // Update water pressure around modification - // This also adds it to m_flow_active_nodes if appropriate + if(reliable_data.size() > 0) + { + SharedBuffer reply(2 + reliable_data.size()); + writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES); + memcpy((char*)&reply[2], reliable_data.c_str(), + reliable_data.size()); + // Send as reliable + m_con.Send(client->peer_id, 0, reply, true); + } + if(unreliable_data.size() > 0) + { + SharedBuffer reply(2 + unreliable_data.size()); + writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES); + memcpy((char*)&reply[2], unreliable_data.c_str(), + unreliable_data.size()); + // Send as unreliable + m_con.Send(client->peer_id, 0, reply, false); + } + + /*if(reliable_data.size() > 0 || unreliable_data.size() > 0) + { + dstream<<"INFO: Server: Size of object message data: " + <<"reliable: "<* >::Iterator + i = buffered_messages.getIterator(); + i.atEnd()==false; i++) + { + delete i.getNode()->getValue(); + } + } + + } // enable_experimental + + /* + Send queued-for-sending map edit events. + */ + { + // Don't send too many at a time + //u32 count = 0; + + // Single change sending is disabled if queue size is not small + bool disable_single_change_sending = false; + if(m_unsent_map_edit_queue.size() >= 4) + disable_single_change_sending = true; + + bool got_any_events = false; - MapVoxelManipulator v(&m_env.getMap()); - v.m_disable_water_climb = - g_settings.getBool("disable_water_climb"); + // We'll log the amount of each + Profiler prof; + + while(m_unsent_map_edit_queue.size() != 0) + { + got_any_events = true; + + MapEditEvent* event = m_unsent_map_edit_queue.pop_front(); - VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1)); + // Players far away from the change are stored here. + // Instead of sending the changes, MapBlocks are set not sent + // for them. + core::list far_players; - try + if(event->type == MEET_ADDNODE) + { + //dstream<<"Server: MEET_ADDNODE"<p, event->n, event->already_known_by_peer, + &far_players, 5); + else + sendAddNode(event->p, event->n, event->already_known_by_peer, + &far_players, 30); + } + else if(event->type == MEET_REMOVENODE) + { + //dstream<<"Server: MEET_REMOVENODE"<p, event->already_known_by_peer, + &far_players, 5); + else + sendRemoveNode(event->p, event->already_known_by_peer, + &far_players, 30); + } + else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED) + { + dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<p); + } + else if(event->type == MEET_OTHER) { - v.updateAreaWaterPressure(area, m_flow_active_nodes); + prof.add("MEET_OTHER", 1); + dstream<<"WARNING: Server: MEET_OTHER not implemented" + <type)< 0) + { + // Convert list format to that wanted by SetBlocksNotSent + core::map modified_blocks2; + for(core::map::Iterator + i = event->modified_blocks.getIterator(); + i.atEnd()==false; i++) + { + v3s16 p = i.getNode()->getKey(); + modified_blocks2.insert(p, + m_env.getMap().getBlockNoCreateNoEx(p)); + } + // Set blocks not sent + 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_blocks2); + } + } + + delete event; + + /*// Don't send too many at a time + count++; + if(count >= 1 && m_unsent_map_edit_queue.size() < 100) + break;*/ + } + + if(got_any_events) + { + dstream<<"Server: MapEditEvents:"< 0) + /*if(deleted_count > 0) { dout_server<<"Server: Unloaded "<serialization_version; u8 peer_ser_ver = getClient(peer->id)->serialization_version; try @@ -1523,8 +1940,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<= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2) + { + net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]); + } + + getClient(peer->id)->net_proto_version = net_proto_version; + + if(net_proto_version == 0) + { + SendAccessDenied(m_con, peer_id, + L"Your client is too old. Please upgrade."); return; } @@ -1556,17 +1995,82 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) */ // Get player name - const u32 playername_size = 20; - char playername[playername_size]; - for(u32 i=0; iupdateName((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))); - // Send as reliable - m_con.Send(peer_id, 0, reply, true); - - return; - } - if(command == TOSERVER_INIT2) - { + /* + 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()); + + // 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<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); } - + // Send information about server to player in chat - { - std::wostringstream os(std::ios_base::binary); - os<::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) - { - // Get client and check that it is valid - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - // Get player - Player *player = m_env.getPlayer(client->peer_id); - // Get name of player - std::wstring name = L"unknown"; - if(player != NULL) - name = narrow_to_wide(player->getName()); - // Add name to information string - os<id)->net_proto_version == 0) + { + SendChatMessage(peer_id, L"# Server: NOTE: YOUR CLIENT IS OLD AND DOES NOT WORK PROPERLY WITH THIS SERVER"); + }*/ return; } @@ -1793,6 +2293,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(datasize < 13) return; + if((getPlayerPrivs(player) & PRIV_BUILD) == 0) + return; + /* [0] u16 command [2] u8 button (0=left, 1=right) @@ -1861,6 +2364,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); } @@ -1868,6 +2372,123 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) block->removeObject(id); } } + else if(command == TOSERVER_CLICK_ACTIVEOBJECT) + { + if(datasize < 7) + return; + + if((getPlayerPrivs(player) & PRIV_BUILD) == 0) + return; + + /* + length: 7 + [0] u16 command + [2] u8 button (0=left, 1=right) + [3] u16 id + [5] u16 item + */ + u8 button = readU8(&data[2]); + u16 id = readS16(&data[3]); + u16 item_i = readU16(&data[11]); + + ServerActiveObject *obj = m_env.getActiveObject(id); + + if(obj == NULL) + { + derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found" + <m_removed) + return; + + //TODO: Check that object is reasonably close + + // Left click, pick object up (usually) + if(button == 0) + { + /* + Try creating inventory item + */ + InventoryItem *item = obj->createPickedUpItem(); + + if(item) + { + InventoryList *ilist = player->inventory.getList("main"); + if(ilist != NULL) + { + if(g_settings.getBool("creative_mode") == false) + { + // Skip if inventory has no free space + if(ilist->getUsedSlots() == ilist->getSize()) + { + dout_server<<"Player inventory has no free space"<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(); + } + } + + v3f playerpos = player->getPosition(); + v3f objpos = obj->getBasePosition(); + v3f dir = (objpos - playerpos).normalize(); + + u16 wear = obj->punch(toolname, dir); + + if(titem) + { + bool weared_out = titem->addWear(wear); + if(weared_out) + mlist->deleteItem(item_i); + SendInventory(player->peer_id); + } + } + } + // Right click, do something with object + if(button == 1) + { + // Track hp changes super-crappily + u16 oldhp = player->hp; + + // Do stuff + obj->rightClick(player); + + // Send back stuff + if(player->hp != oldhp) + { + SendPlayerHP(player); + } + } + } else if(command == TOSERVER_GROUND_ACTION) { if(datasize < 17) @@ -1883,6 +2504,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) 0: start digging 1: place block 2: stop digging (all parameters ignored) + 3: digging completed */ u8 action = readU8(&data[2]); v3s16 p_under; @@ -1928,23 +2550,43 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Mandatory parameter; actually used for nothing core::map modified_blocks; - u8 material; + content_t 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) + material = n.getContent(); + // If not yet cancelled + if(cannot_remove_node == false) { - derr_server<<"Server: Not finishing digging: Node not diggable" - <nodeRemovalDisabled() == true) + { + derr_server<<"Server: Not finishing digging: " + <<"Node metadata disables removal" + < reply(replysize); - writeU16(&reply[0], TOCLIENT_REMOVENODE); - writeS16(&reply[2], p_under.X); - writeS16(&reply[4], p_under.Y); - writeS16(&reply[6], p_under.Z); - - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + // Make sure the player is allowed to do it + if((getPlayerPrivs(player) & PRIV_BUILD) == 0) { - // Get client and check that it is valid - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - - // Don't send if it's the same one - if(peer_id == client->peer_id) - continue; + dstream<<"Player "<getName()<<" cannot remove node" + <<" because privileges are "<peer_id, 0, reply, 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 close-by players. + - If other player is close, send REMOVENODE + - Otherwise set blocks not sent + */ + core::list far_players; + sendRemoveNode(p_under, peer_id, &far_players, 30); + /* Update and send inventory */ @@ -2025,56 +2675,58 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } /* - Add digged item to inventory + Add dug item to inventory */ InventoryItem *item = NULL; if(mineral != MINERAL_NONE) item = getDiggedMineralItem(mineral); - + + // If not mineral if(item == NULL) - item = new MaterialItem(material, 1); - - player->inventory.addItem("main", item); + { + std::string &dug_s = content_features(material).dug_item; + if(dug_s != "") + { + std::istringstream is(dug_s, std::ios::binary); + item = InventoryItem::deSerialize(is); + } + } + + if(item != NULL) + { + // Add a item to inventory + player->inventory.addItem("main", item); - /* - Send inventory - */ - SendInventory(player->peer_id); + // Send inventory + UpdateCrafting(player->peer_id); + SendInventory(player->peer_id); + } } /* Remove the node (this takes some time so it is done after the quick stuff) */ - m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks); + { + MapEditEventIgnorer ign(&m_ignore_map_edit_events); -#if 0 + m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks); + } /* - Update water + Set blocks not sent to far players */ - - // Update water pressure around modification - // This also adds it to m_flow_active_nodes if appropriate - - MapVoxelManipulator v(&m_env.getMap()); - v.m_disable_water_climb = - g_settings.getBool("disable_water_climb"); - - VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1)); - - try - { - v.updateAreaWaterPressure(area, m_flow_active_nodes); - } - catch(ProcessingLimitException &e) + for(core::list::Iterator + i = far_players.begin(); + i != far_players.end(); i++) { - dstream<<"Processing limit reached (1)"<SetBlocksNotSent(modified_blocks); } - - v.blitBack(modified_blocks); -#endif } /* @@ -2102,7 +2754,15 @@ 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) + bool no_enough_privs = + ((getPlayerPrivs(player) & PRIV_BUILD)==0); + if(no_enough_privs) + dstream<<"Player "<getName()<<" cannot add node" + <<" because privileges are "<id)->m_time_from_building.set(0.0); + getClient(peer->id)->m_time_from_building = 0.0; // Create node data MaterialItem *mitem = (MaterialItem*)item; MapNode n; - n.d = mitem->getMaterial(); - if(content_features(n.d).wall_mounted) - n.dir = packDir(p_under - p_over); + n.setContent(mitem->getMaterial()); -#if 1 - // Create packet - u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver); - SharedBuffer reply(replysize); - writeU16(&reply[0], TOCLIENT_ADDNODE); - writeS16(&reply[2], p_over.X); - writeS16(&reply[4], p_over.Y); - writeS16(&reply[6], p_over.Z); - n.serialize(&reply[8], peer_ser_ver); - // Send as reliable - m_con.SendToAll(0, reply, true); - - /* - Handle inventory - */ - InventoryList *ilist = player->inventory.getList("main"); - if(g_settings.getBool("creative_mode") == false && ilist) + // Calculate direction for wall mounted stuff + if(content_features(n).wall_mounted) + n.param2 = packDir(p_under - p_over); + + // Calculate the direction for furnaces and chests and stuff + if(content_features(n).param_type == CPT_FACEDIR_SIMPLE) { - // Remove from inventory and send inventory - if(mitem->getCount() == 1) - ilist->deleteItem(item_i); - else - mitem->remove(1); - // Send inventory - SendInventory(peer_id); + v3f playerpos = player->getPosition(); + v3f blockpos = intToFloat(p_over, BS) - playerpos; + blockpos = blockpos.normalize(); + n.param1 = 0; + if (fabs(blockpos.X) > fabs(blockpos.Z)) { + if (blockpos.X < 0) + n.param1 = 3; + else + n.param1 = 1; + } else { + if (blockpos.Z < 0) + n.param1 = 2; + else + n.param1 = 0; + } } - - /* - Add node. - This takes some time so it is done after the quick stuff + /* + Send to all close-by players */ - core::map modified_blocks; - m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks); -#endif -#if 0 + core::list far_players; + sendAddNode(p_over, n, 0, &far_players, 30); + /* Handle inventory */ @@ -2184,126 +2836,84 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) else mitem->remove(1); // Send inventory + UpdateCrafting(peer_id); SendInventory(peer_id); } - + /* Add node. This takes some time so it is done after the quick stuff */ core::map modified_blocks; - m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks); + { + MapEditEventIgnorer ign(&m_ignore_map_edit_events); + m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks); + } /* - Set the modified blocks unsent for all the clients + Set blocks not sent to far players */ - - //JMutexAutoLock lock2(m_con_mutex); - - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(core::list::Iterator + i = far_players.begin(); + i != far_players.end(); i++) { - RemoteClient *client = i.getNode()->getValue(); - - if(modified_blocks.size() > 0) - { - // Remove block from sent history - client->SetBlocksNotSent(modified_blocks); - } + u16 peer_id = *i; + RemoteClient *client = getClient(peer_id); + if(client==NULL) + continue; + client->SetBlocksNotSent(modified_blocks); } -#endif -#if 0 /* - Update water + Calculate special events */ - // Update water pressure around modification - // This also adds it to m_flow_active_nodes if appropriate - - MapVoxelManipulator v(&m_env.getMap()); - v.m_disable_water_climb = - g_settings.getBool("disable_water_climb"); - - VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1)); - - try + /*if(n.d == CONTENT_MESE) { - v.updateAreaWaterPressure(area, m_flow_active_nodes); - } - catch(ProcessingLimitException &e) - { - dstream<<"Processing limit reached (1)"<getPosRelative(); - v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map); - - v3f pos = intToFloat(p_over); - 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) { @@ -2313,16 +2923,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); } } @@ -2354,6 +2982,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) #endif else if(command == TOSERVER_SIGNTEXT) { + if((getPlayerPrivs(player) & PRIV_BUILD) == 0) + return; /* u16 command v3s16 blockpos @@ -2409,6 +3039,54 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) obj->getBlock()->setChangedFlag(); } + else if(command == TOSERVER_SIGNNODETEXT) + { + if((getPlayerPrivs(player) & 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 @@ -2426,6 +3104,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 */ @@ -2434,50 +3116,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") + if(ma->to_inv == "current_player" && + ma->from_inv == "current_player") { - // Do nothing - disable_action = true; - } - // When something is removed from craftresult - if(ma->from_name == "craftresult") - { - 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 { @@ -2510,30 +3209,178 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Get player name of this client std::wstring name = narrow_to_wide(player->getName()); + + // Line to send to players + std::wstring line; + // Whether to send to the player that sent the line + bool send_to_sender = false; + // 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 = getPlayerPrivs(player); + + // Parse commands + std::wstring commandprefix = L"/#"; + if(message.substr(0, commandprefix.size()) == commandprefix) + { + line += L"Server: "; - std::wstring line = std::wstring(L"<")+name+L"> "+message; + message = message.substr(commandprefix.size()); + + WStrfnd f1(message); + f1.next(L" "); // Skip over /#whatever + std::wstring paramstring = f1.next(L""); + + ServerCommandContext *ctx = new ServerCommandContext( + str_split(message, L' '), + paramstring, + 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) + { + line += L"<"; + line += name; + line += L"> "; + line += message; + send_to_others = true; + } + else + { + line += L"Server: You are not allowed to shout"; + send_to_sender = true; + } + } - dstream<<"CHAT: "<::Iterator + i = m_clients.getIterator(); + i.atEnd() == false; i++) + { + // Get client and check that it is valid + RemoteClient *client = i.getNode()->getValue(); + assert(client->peer_id == i.getNode()->getKey()); + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; + + // Filter recipient + bool sender_selected = (peer_id == client->peer_id); + if(sender_selected == true && send_to_sender == false) + continue; + if(sender_selected == false && send_to_others == false) + continue; + + SendChatMessage(client->peer_id, line); + } + } + } + else if(command == TOSERVER_DAMAGE) + { + if(g_settings.getBool("enable_damage")) + { + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + u8 damage = readU8(is); + if(player->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) + { /* - Send the message to all other clients + [0] u16 TOSERVER_PASSWORD + [2] u8[28] old password + [30] u8[28] new password */ - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + + if(datasize != 2+PASSWORD_SIZE*2) + return; + /*char password[PASSWORD_SIZE]; + for(u32 i=0; igetValue(); - assert(client->peer_id == i.getNode()->getKey()); - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; + char c = data[2+i]; + if(c == 0) + break; + oldpwd += c; + } + std::string newpwd; + for(u32 i=0; ipeer_id) - continue; + dstream<<"Server: Client requests a password change from " + <<"'"<getName(); - SendChatMessage(client->peer_id, line); + if(m_authmanager.exists(playername) == false) + { + dstream<<"Server: playername not found in authmanager"< data, bool reliable) +void Server::onMapEditEvent(MapEditEvent *event) { - JMutexAutoLock lock(m_con_mutex); - m_con.Send(peer_id, channelnum, data, reliable); -}*/ + //dstream<<"Server::onMapEditEvent()"<clone(); + m_unsent_map_edit_queue.push_back(e); +} -void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver) +Inventory* Server::getInventory(InventoryContext *c, std::string id) { - DSTACK(__FUNCTION_NAME); - /* - Create a packet with the block in the right format - */ + if(id == "current_player") + { + assert(c->current_player); + return &(c->current_player->inventory); + } - std::ostringstream os(std::ios_base::binary); - block->serialize(os, ver); - std::string s = os.str(); - SharedBuffer blockdata((u8*)s.c_str(), s.size()); + Strfnd fn(id); + std::string id0 = fn.next(":"); - u32 replysize = 8 + blockdata.getSize(); - SharedBuffer 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()); + 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; + } - /* - Send packet - */ - m_con.Send(peer_id, 1, reply, true); + 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() @@ -2629,6 +3516,7 @@ core::list Server::getPlayerInfo() return list; } + void Server::peerAdded(con::Peer *peer) { DSTACK(__FUNCTION_NAME); @@ -2655,6 +3543,45 @@ 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, + const std::wstring &reason) +{ + DSTACK(__FUNCTION_NAME); + std::ostringstream os(std::ios_base::binary); + + writeU16(os, TOCLIENT_ACCESS_DENIED); + os< 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); @@ -2702,6 +3629,7 @@ void Server::SendPlayerInfos() "peer_id="<peer_id<peer_id); + memset((char*)&data[start+2], 0, PLAYERNAME_SIZE); snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName()); start += 2+PLAYERNAME_SIZE; } @@ -2712,290 +3640,12 @@ void Server::SendPlayerInfos() m_con.SendToAll(0, data, true); } -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 @@ -3038,16 +3688,118 @@ void Server::SendChatMessage(u16 peer_id, const std::wstring &message) writeU16(buf, w); os.write((char*)buf, 2); } - - // Make data buffer - std::string s = os.str(); - SharedBuffer data((u8*)s.c_str(), s.size()); - // Send as reliable - m_con.Send(peer_id, 0, data, true); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + m_con.Send(peer_id, 0, data, true); +} + +void Server::BroadcastChatMessage(const std::wstring &message) +{ + for(core::map::Iterator + i = m_clients.getIterator(); + i.atEnd() == false; i++) + { + // Get client and check that it is valid + RemoteClient *client = i.getNode()->getValue(); + assert(client->peer_id == i.getNode()->getKey()); + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; + + SendChatMessage(client->peer_id, message); + } +} + +void Server::SendPlayerHP(Player *player) +{ + 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=("<serialize(os, ver); + std::string s = os.str(); + SharedBuffer blockdata((u8*)s.c_str(), s.size()); + + u32 replysize = 8 + blockdata.getSize(); + SharedBuffer reply(replysize); + 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<<"Server: Sending block ("< queue; 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()); + ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending"); - total_sending += client->SendingCount(); - - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - - client->GetNextBlocks(this, dtime, queue); + 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. @@ -3092,8 +3945,6 @@ void Server::SendBlocks(float dtime) // Lowest is most important. queue.sort(); - JMutexAutoLock conlock(m_con_mutex); - for(u32 i=0; iinventory.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); + } + + // Get result of crafting grid + InventoryItem *result = craft_get_result(items); + if(result) + rlist->addItem(result); + } + + } // if creative_mode == false +} RemoteClient* Server::getClient(u16 peer_id) { @@ -3135,52 +4028,98 @@ RemoteClient* Server::getClient(u16 peer_id) return n->getValue(); } -void setCreativeInventory(Player *player) +std::wstring Server::getStatusString() { - player->resetInventory(); - - // Give some good picks - { - InventoryItem *item = new ToolItem("STPick", 0); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - } + std::wostringstream os(std::ios_base::binary); + os<::Iterator + i = m_clients.getIterator(); + i.atEnd() == false; i++) { - InventoryItem *item = new ToolItem("MesePick", 0); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); + // Get client and check that it is valid + RemoteClient *client = i.getNode()->getValue(); + assert(client->peer_id == i.getNode()->getKey()); + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; + // Get player + Player *player = m_env.getPlayer(client->peer_id); + // Get name of player + std::wstring name = L"unknown"; + if(player != NULL) + name = narrow_to_wide(player->getName()); + // Add name to information string + os<isSavingEnabled() == false) + os<inventory.addItem("main", item); +v3f findSpawnPos(ServerMap &map) +{ + //return v3f(50,50,50)*BS; + + v2s16 nodepos; + s16 groundheight = 0; - // Then others - for(u16 i=0; i Underwater"< WATER_LEVEL + 4) + { + //dstream<<"-> Underwater"<inventory.addItem("main", item); - } - // Sign - { - InventoryItem *item = new MapBlockObjectItem("Sign Example text"); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); + // Found a good place + //dstream<<"Searched through "<inventory_backup = new Inventory(); + *(player->inventory_backup) = player->inventory; + // Set creative inventory + craft_set_creative_inventory(player); } - /* - With new map generator the map is regenerated anyway, - so start at somewhere where you probably don't get underground - */ - player->setPosition(intToFloat(v3s16( - 0, - 64, - 0 - ))); - return player; } @@ -3236,6 +4170,10 @@ Player *Server::emergePlayer(const char *name, const char *password, //player->peer_id = PEER_ID_INEXISTENT; player->peer_id = peer_id; player->updateName(name); + m_authmanager.add(name); + m_authmanager.setPassword(name, password); + m_authmanager.setPrivs(name, + stringToPrivs(g_settings.get("default_privs"))); /* Set player position @@ -3244,80 +4182,9 @@ Player *Server::emergePlayer(const char *name, const char *password, dstream<<"Server: Finding spawn place for player \"" <getName()<<"\""<setPosition(intToFloat(v3s16( - 0, - 64, - 0 - ))); -#endif -#if 0 - f32 groundheight = 0; -#if 0 - // 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); - // Get sector - m_env.getMap().emergeSector(sectorpos); - // Get ground height at point - groundheight = m_env.getMap().getGroundHeight(nodepos, true); - // The sector should have been generated -> groundheight exists - assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE); - // 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, - groundheight + 15, - nodepos.Y - ))); -#endif + v3f pos = findSpawnPos(m_env.getServerMap()); + + player->setPosition(pos); /* Add player to environment @@ -3331,64 +4198,16 @@ Player *Server::emergePlayer(const char *name, const char *password, if(g_settings.getBool("creative_mode")) { - setCreativeInventory(player); + // Warning: double code above + // Backup actual inventory + player->inventory_backup = new Inventory(); + *(player->inventory_backup) = player->inventory; + // Set creative inventory + craft_set_creative_inventory(player); } - else + else if(g_settings.getBool("give_initial_stuff")) { - /*{ - InventoryItem *item = new ToolItem("WPick", 32000); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - }*/ - /*{ - InventoryItem *item = new MaterialItem(CONTENT_MESE, 6); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - } - { - InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - } - { - InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - } - { - InventoryItem *item = new CraftItem("Stick", 4); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - } - { - InventoryItem *item = new ToolItem("WPick", 32000); - void* r = player->inventory.addItem("main", item); - assert(r == NULL); - } - { - InventoryItem *item = new ToolItem("STPick", 32000); - 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 - for(u16 i=0; i<4; i++) - { - InventoryItem *item = new MapBlockObjectItem("Sign Example text"); - bool r = player->inventory.addItem("main", item); - assert(r == true); - }*/ - /*// Give some other stuff - { - InventoryItem *item = new MaterialItem(CONTENT_TREE, 999); - bool r = player->inventory.addItem("main", item); - assert(r == true); - }*/ + craft_give_initial_stuff(player); } return player; @@ -3396,30 +4215,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; { @@ -3513,23 +4325,69 @@ void Server::handlePeerChanges() } } -void dedicated_server_loop(Server &server) +u64 Server::getPlayerPrivs(Player *player) +{ + if(player==NULL) + return 0; + std::string playername = player->getName(); + // Local player gets all privileges regardless of + // what's set on their account. + if(g_settings.get("name") == playername) + { + return PRIV_ALL; + } + else + { + return getPlayerAuthPrivs(playername); + } +} + +void dedicated_server_loop(Server &server, bool &kill) { DSTACK(__FUNCTION_NAME); - std::cout<PrintLine(&std::cout); + i->PrintLine(&dstream); } } sum_old = sum;