diff --git a/CMakeLists.txt b/CMakeLists.txt index e0b5366..1c316d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ project(minetest) # Also remember to set PROTOCOL_VERSION in clientserver.h when releasing set(VERSION_MAJOR 0) set(VERSION_MINOR 3) -set(VERSION_PATCH dev-20111016) +set(VERSION_PATCH dev-20111021) set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") MESSAGE(STATUS "*** Will build version ${VERSION_STRING} ***") diff --git a/doc/changelog.txt b/doc/changelog.txt index dee648e..b600345 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -3,6 +3,11 @@ Minetest-c55 changelog This should contain all the major changes. For minor stuff, refer to the commit log of the repository. +0.3.dev-20111021: +- Modify dungeon masters to only try to shoot players +- Fix object duplication bug at block load/unload bug +- Improve network layer + 0.3.dev-20111016: - Locked chest - Server user limit setting (max_users) diff --git a/minetest.conf.example b/minetest.conf.example index c8cfbe8..30adc5e 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -39,6 +39,8 @@ #keymap_jump = KEY_SPACE #keymap_sneak = KEY_LSHIFT #keymap_inventory = KEY_KEY_I +# Go down ladder / go down in fly mode / go fast in fast mode +#keymap_special1 = KEY_KEY_E #keymap_chat = KEY_KEY_T #keymap_rangeselect = KEY_KEY_R #keymap_freemove = KEY_KEY_K @@ -46,7 +48,6 @@ #keymap_frametime_graph = KEY_F1 #keymap_screenshot = KEY_F12 # Some (temporary) keys for debugging -#keymap_special1 = KEY_KEY_E #keymap_print_debug_stacks = KEY_KEY_P # The desired FPS @@ -58,8 +59,8 @@ #viewing_range_nodes_max = 300 #viewing_range_nodes_min = 25 # Initial window size -screenW# = 800 -screenH# = 600 +#screenW = 800 +#screenH = 600 # Address to connect to (#blank = start local server) #address = # Enable random user input, for testing @@ -139,11 +140,11 @@ screenH# = 600 # Player and object positions are sent at intervals specified by this #objectdata_interval = 0.2 #active_object_send_range_blocks = 3 -#active_block_range = 5 +#active_block_range = 2 #max_simultaneous_block_sends_per_client = 2 #max_simultaneous_block_sends_server_total = 8 -#max_block_send_distance = 8 -#max_block_generate_distance = 8 +#max_block_send_distance = 7 +#max_block_generate_distance = 5 #time_send_interval = 20 # Length of day/night cycle. 72=20min, 360=4min, 1=24hour #time_speed = 72 diff --git a/src/camera.cpp b/src/camera.cpp index 634a7cc..a323367 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -342,7 +342,7 @@ void Camera::updateViewingRange(f32 frametime_in) <data); @@ -246,7 +246,7 @@ void Client::connect(Address address) { DSTACK(__FUNCTION_NAME); //JMutexAutoLock lock(m_con_mutex); //bulk comment-out - m_con.setTimeoutMs(0); + m_con.SetTimeoutMs(0); m_con.Connect(address); } @@ -563,8 +563,8 @@ void Client::step(float dtime) counter = 0.0; //JMutexAutoLock lock(m_con_mutex); //bulk comment-out // connectedAndInitialized() is true, peer exists. - con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER); - infostream<<"Client: avg_rtt="<avg_rtt<getSceneNodeRenderPass() != scene::ESNRP_SOLID) return; + ScopeProfiler sp(g_profiler, "Rendering of clouds, avg", SPT_AVG); + + int num_faces_to_draw = 6; + if(g_settings->getBool("enable_2d_clouds")) + num_faces_to_draw = 1; + driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); driver->setMaterial(m_material); @@ -144,7 +153,7 @@ void Clouds::render() f32 ry = 8*BS; f32 rz = cloud_size; - for(int i=0;i<6;i++) + for(int i=0; i= 0.0){ + if(rtt < 0.01){ + if(m_max_packets_per_second < 100) + m_max_packets_per_second += 10; + } else if(rtt < 0.2){ + if(m_max_packets_per_second < 100) + m_max_packets_per_second += 2; + } else { + m_max_packets_per_second *= 0.8; + if(m_max_packets_per_second < 10) + m_max_packets_per_second = 10; + } + } + if(rtt < -0.999) {} else if(avg_rtt < 0.0) @@ -485,461 +504,210 @@ void Peer::reportRTT(float rtt) Connection */ -Connection::Connection( - u32 protocol_id, - u32 max_packet_size, - float timeout, - PeerHandler *peerhandler -) +Connection::Connection(u32 protocol_id, u32 max_packet_size, float timeout): + m_protocol_id(protocol_id), + m_max_packet_size(max_packet_size), + m_timeout(timeout), + m_peer_id(0), + m_bc_peerhandler(NULL), + m_bc_receive_timeout(0), + m_indentation(0) { - assert(peerhandler != NULL); + m_socket.setTimeoutMs(5); - m_protocol_id = protocol_id; - m_max_packet_size = max_packet_size; - m_timeout = timeout; - m_peer_id = PEER_ID_INEXISTENT; - //m_waiting_new_peer_id = false; - m_indentation = 0; - m_peerhandler = peerhandler; + Start(); } +Connection::Connection(u32 protocol_id, u32 max_packet_size, float timeout, + PeerHandler *peerhandler): + m_protocol_id(protocol_id), + m_max_packet_size(max_packet_size), + m_timeout(timeout), + m_peer_id(0), + m_bc_peerhandler(peerhandler), + m_bc_receive_timeout(0), + m_indentation(0) +{ + m_socket.setTimeoutMs(5); + + Start(); +} + + Connection::~Connection() { - // Clear peers - core::map::Iterator j; - j = m_peers.getIterator(); - for(; j.atEnd() == false; j++) + stop(); +} + +/* Internal stuff */ + +void * Connection::Thread() +{ + ThreadStarted(); + log_register_thread("Connection"); + + dout_con<<"Connection thread started"< 0.1) + dtime = 0.1; + if(dtime < 0.0) + dtime = 0.0; + + runTimeouts(dtime); + + while(m_command_queue.size() != 0){ + ConnectionCommand c = m_command_queue.pop_front(); + processCommand(c); + } + + send(dtime); + + receive(); + + END_DEBUG_EXCEPTION_HANDLER(derr_con); + } + + return NULL; +} + +void Connection::putEvent(ConnectionEvent &e) +{ + assert(e.type != CONNEVENT_NONE); + m_event_queue.push_back(e); +} + +void Connection::processCommand(ConnectionCommand &c) +{ + switch(c.type){ + case CONNCMD_NONE: + dout_con<::Iterator + j = m_peers.getIterator(); + j.atEnd() == false; j++) { Peer *peer = j.getNode()->getValue(); - delete peer; + peer->m_sendtime_accu += dtime; + peer->m_num_sent = 0; + peer->m_max_num_sent = peer->m_sendtime_accu * + peer->m_max_packets_per_second; } -} - -void Connection::Serve(unsigned short port) -{ - m_socket.Bind(port); - m_peer_id = PEER_ID_SERVER; -} - -void Connection::Connect(Address address) -{ - core::map::Node *node = m_peers.find(PEER_ID_SERVER); - if(node != NULL){ - throw ConnectionException("Already connected to a server"); + Queue postponed_packets; + while(m_outgoing_queue.size() != 0){ + OutgoingPacket packet = m_outgoing_queue.pop_front(); + Peer *peer = getPeerNoEx(packet.peer_id); + if(!peer) + continue; + if(peer->channels[packet.channelnum].outgoing_reliables.size() >= 5){ + postponed_packets.push_back(packet); + } else if(peer->m_num_sent < peer->m_max_num_sent){ + rawSendAsPacket(packet.peer_id, packet.channelnum, + packet.data, packet.reliable); + peer->m_num_sent++; + } else { + postponed_packets.push_back(packet); + } } - - Peer *peer = new Peer(PEER_ID_SERVER, address); - m_peers.insert(peer->id, peer); - m_peerhandler->peerAdded(peer); - - m_socket.Bind(0); - - // Send a dummy packet to server with peer_id = PEER_ID_INEXISTENT - m_peer_id = PEER_ID_INEXISTENT; - SharedBuffer data(0); - Send(PEER_ID_SERVER, 0, data, true); - - //m_waiting_new_peer_id = true; -} - -void Connection::Disconnect() -{ - // Create and send DISCO packet - SharedBuffer data(2); - writeU8(&data[0], TYPE_CONTROL); - writeU8(&data[1], CONTROLTYPE_DISCO); - - // Send to all - core::map::Iterator j; - j = m_peers.getIterator(); - for(; j.atEnd() == false; j++) + while(postponed_packets.size() != 0){ + m_outgoing_queue.push_back(postponed_packets.pop_front()); + } + for(core::map::Iterator + j = m_peers.getIterator(); + j.atEnd() == false; j++) { Peer *peer = j.getNode()->getValue(); - SendAsPacket(peer->id, 0, data, false); + peer->m_sendtime_accu -= (float)peer->m_num_sent / + peer->m_max_packets_per_second; + if(peer->m_sendtime_accu > 10. / peer->m_max_packets_per_second) + peer->m_sendtime_accu = 10. / peer->m_max_packets_per_second; } } -bool Connection::Connected() +// Receive packets from the network and buffers and create ConnectionEvents +void Connection::receive() { - if(m_peers.size() != 1) - return false; - - core::map::Node *node = m_peers.find(PEER_ID_SERVER); - if(node == NULL) - return false; - - if(m_peer_id == PEER_ID_INEXISTENT) - return false; - - return true; -} - -SharedBuffer Channel::ProcessPacket( - SharedBuffer packetdata, - Connection *con, - u16 peer_id, - u8 channelnum, - bool reliable) -{ - IndentationRaiser iraiser(&(con->m_indentation)); - - if(packetdata.getSize() < 1) - throw InvalidIncomingDataException("packetdata.getSize() < 1"); - - u8 type = readU8(&packetdata[0]); - - if(type == TYPE_CONTROL) - { - if(packetdata.getSize() < 2) - throw InvalidIncomingDataException("packetdata.getSize() < 2"); - - u8 controltype = readU8(&packetdata[1]); - - if(controltype == CONTROLTYPE_ACK) - { - if(packetdata.getSize() < 4) - throw InvalidIncomingDataException - ("packetdata.getSize() < 4 (ACK header size)"); - - u16 seqnum = readU16(&packetdata[2]); - con->PrintInfo(); - dout_con<<"Got CONTROLTYPE_ACK: channelnum=" - <<((int)channelnum&0xff)<<", peer_id="<PrintInfo(); - outgoing_reliables.print(); - dout_con<PrintInfo(derr_con); - derr_con<<"WARNING: ACKed packet not " - "in outgoing queue" - <PrintInfo(); - dout_con<<"Got new peer id: "<GetPeerID() != PEER_ID_INEXISTENT) - { - con->PrintInfo(derr_con); - derr_con<<"WARNING: Not changing" - " existing peer id."<SetPeerID(peer_id_new); - } - throw ProcessedSilentlyException("Got a SET_PEER_ID"); - } - else if(controltype == CONTROLTYPE_PING) - { - // Just ignore it, the incoming data already reset - // the timeout counter - con->PrintInfo(); - dout_con<<"PING"<PrintInfo(); - dout_con<<"DISCO: Removing peer "<<(peer_id)<deletePeer(peer_id, false) == false) - { - con->PrintInfo(derr_con); - derr_con<<"DISCO: Peer not found"<PrintInfo(derr_con); - derr_con<<"INVALID TYPE_CONTROL: invalid controltype=" - <<((int)controltype&0xff)<PrintInfo(); - dout_con<<"RETURNING TYPE_ORIGINAL to user" - < payload(packetdata.getSize() - ORIGINAL_HEADER_SIZE); - memcpy(*payload, &packetdata[ORIGINAL_HEADER_SIZE], payload.getSize()); - return payload; - } - else if(type == TYPE_SPLIT) - { - // We have to create a packet again for buffering - // This isn't actually too bad an idea. - BufferedPacket packet = makePacket( - con->GetPeer(peer_id)->address, - packetdata, - con->GetProtocolID(), - peer_id, - channelnum); - // Buffer the packet - SharedBuffer data = incoming_splits.insert(packet, reliable); - if(data.getSize() != 0) - { - con->PrintInfo(); - dout_con<<"RETURNING TYPE_SPLIT: Constructed full data, " - <<"size="<PrintInfo(); - dout_con<<"BUFFERED TYPE_SPLIT"<PrintInfo(); - if(is_future_packet) - dout_con<<"BUFFERING"; - else if(is_old_packet) - dout_con<<"OLD"; - else - dout_con<<"RECUR"; - dout_con<<" TYPE_RELIABLE seqnum="< reply(4); - writeU8(&reply[0], TYPE_CONTROL); - writeU8(&reply[1], CONTROLTYPE_ACK); - writeU16(&reply[2], seqnum); - con->SendAsPacket(peer_id, channelnum, reply, false); - - //if(seqnum_higher(seqnum, next_incoming_seqnum)) - if(is_future_packet) - { - /*con->PrintInfo(); - dout_con<<"Buffering reliable packet (seqnum=" - <GetPeer(peer_id)->address, - packetdata, - con->GetProtocolID(), - peer_id, - channelnum); - try{ - incoming_reliables.insert(packet); - - /*con->PrintInfo(); - dout_con<<"INCOMING: "; - incoming_reliables.print(); - dout_con< payload(packetdata.getSize() - RELIABLE_HEADER_SIZE); - memcpy(*payload, &packetdata[RELIABLE_HEADER_SIZE], payload.getSize()); - - return ProcessPacket(payload, con, peer_id, channelnum, true); - } - else - { - con->PrintInfo(derr_con); - derr_con<<"Got invalid type="<<((int)type&0xff)< Channel::CheckIncomingBuffers(Connection *con, - u16 &peer_id) -{ - u16 firstseqnum = 0; - // Clear old packets from start of buffer - try{ - for(;;){ - firstseqnum = incoming_reliables.getFirstSeqnum(); - if(seqnum_higher(next_incoming_seqnum, firstseqnum)) - incoming_reliables.popFirst(); - else - break; - } - // This happens if all packets are old - }catch(con::NotFoundException) - {} - - if(incoming_reliables.empty() == false) - { - if(firstseqnum == next_incoming_seqnum) - { - BufferedPacket p = incoming_reliables.popFirst(); - - peer_id = readPeerId(*p.data); - u8 channelnum = readChannel(*p.data); - u16 seqnum = readU16(&p.data[BASE_HEADER_SIZE+1]); - - con->PrintInfo(); - dout_con<<"UNBUFFERING TYPE_RELIABLE" - <<" seqnum="< Connection::GetFromBuffers(u16 &peer_id) -{ - core::map::Iterator j; - j = m_peers.getIterator(); - for(; j.atEnd() == false; j++) - { - Peer *peer = j.getNode()->getValue(); - for(u16 i=0; ichannels[i]; - try{ - SharedBuffer resultdata = channel->CheckIncomingBuffers - (this, peer_id); - - return resultdata; - } - catch(NoIncomingDataException &e) - { - } - catch(InvalidIncomingDataException &e) - { - } - catch(ProcessedSilentlyException &e) - { - } - } - } - throw NoIncomingDataException("No relevant data in buffers"); -} - -u32 Connection::Receive(u16 &peer_id, u8 *data, u32 datasize) -{ - /* - Receive a packet from the network - */ - + u32 datasize = 100000; // TODO: We can not know how many layers of header there are. // For now, just assume there are no other than the base headers. u32 packet_maxsize = datasize + BASE_HEADER_SIZE; Buffer packetdata(packet_maxsize); + + bool single_wait_done = false; for(;;) { - try - { - /* - Check if some buffer has relevant data - */ - try{ - SharedBuffer resultdata = GetFromBuffers(peer_id); - - if(datasize < resultdata.getSize()) - throw InvalidIncomingDataException - ("Buffer too small for received data"); - - memcpy(data, *resultdata, resultdata.getSize()); - return resultdata.getSize(); - } - catch(NoIncomingDataException &e) + try{ + /* Check if some buffer has relevant data */ { + u16 peer_id; + SharedBuffer resultdata; + bool got = getFromBuffers(peer_id, resultdata); + if(got){ + ConnectionEvent e; + e.dataReceived(peer_id, resultdata); + putEvent(e); + continue; + } } - - Address sender; + + if(single_wait_done){ + if(m_socket.WaitData(0) == false) + break; + } + + single_wait_done = true; + Address sender; s32 received_size = m_socket.Receive(sender, *packetdata, packet_maxsize); if(received_size < 0) - throw NoIncomingDataException("No incoming data"); + break; if(received_size < BASE_HEADER_SIZE) - throw InvalidIncomingDataException("No full header received"); + continue; if(readU32(&packetdata[0]) != m_protocol_id) - throw InvalidIncomingDataException("Invalid protocol id"); + continue; - peer_id = readPeerId(*packetdata); + u16 peer_id = readPeerId(*packetdata); u8 channelnum = readChannel(*packetdata); if(channelnum > CHANNEL_COUNT-1){ PrintInfo(derr_con); @@ -999,17 +767,23 @@ u32 Connection::Receive(u16 &peer_id, u8 *data, u32 datasize) /* Find an unused peer id */ + bool out_of_ids = false; for(;;) { // Check if exists if(m_peers.find(peer_id_new) == NULL) break; // Check for overflow - if(peer_id_new == 65535) - throw ConnectionException - ("Connection ran out of peer ids"); + if(peer_id_new == 65535){ + out_of_ids = true; + break; + } peer_id_new++; } + if(out_of_ids){ + errorstream<id, peer); - m_peerhandler->peerAdded(peer); + + // Create peer addition event + ConnectionEvent e; + e.peerAdded(peer_id_new, sender); + putEvent(e); // Create CONTROL packet to tell the peer id to the new peer. SharedBuffer reply(4); writeU8(&reply[0], TYPE_CONTROL); writeU8(&reply[1], CONTROLTYPE_SET_PEER_ID); writeU16(&reply[2], peer_id_new); - SendAsPacket(peer_id_new, 0, reply, true); + sendAsPacket(peer_id_new, 0, reply, true); // We're now talking to a valid peer_id peer_id = peer_id_new; @@ -1053,12 +831,7 @@ u32 Connection::Receive(u16 &peer_id, u8 *data, u32 datasize) PrintInfo(derr_con); derr_con<<"Peer "<timeout_counter = 0.0; @@ -1074,8 +847,8 @@ u32 Connection::Receive(u16 &peer_id, u8 *data, u32 datasize) try{ // Process it (the result is some data with no headers made by us) - SharedBuffer resultdata = channel->ProcessPacket - (strippeddata, this, peer_id, channelnum); + SharedBuffer resultdata = processPacket + (channel, strippeddata, peer_id, channelnum, false); PrintInfo(); dout_con<<"ProcessPacket returned data of size " @@ -1085,115 +858,20 @@ u32 Connection::Receive(u16 &peer_id, u8 *data, u32 datasize) throw InvalidIncomingDataException ("Buffer too small for received data"); - memcpy(data, *resultdata, resultdata.getSize()); - return resultdata.getSize(); - } - catch(ProcessedSilentlyException &e) - { - // If there is more data, receive again - if(m_socket.WaitData(0) == true) - continue; - } - throw NoIncomingDataException("No incoming data (2)"); - } // try - catch(InvalidIncomingDataException &e) - { - // If there is more data, receive again - if(m_socket.WaitData(0) == true) + ConnectionEvent e; + e.dataReceived(peer_id, resultdata); + putEvent(e); continue; + }catch(ProcessedSilentlyException &e){ + } + }catch(InvalidIncomingDataException &e){ + } + catch(ProcessedSilentlyException &e){ } } // for } -void Connection::SendToAll(u8 channelnum, SharedBuffer data, bool reliable) -{ - core::map::Iterator j; - j = m_peers.getIterator(); - for(; j.atEnd() == false; j++) - { - Peer *peer = j.getNode()->getValue(); - Send(peer->id, channelnum, data, reliable); - } -} - -void Connection::Send(u16 peer_id, u8 channelnum, - SharedBuffer data, bool reliable) -{ - assert(channelnum < CHANNEL_COUNT); - - Peer *peer = GetPeerNoEx(peer_id); - if(peer == NULL) - return; - Channel *channel = &(peer->channels[channelnum]); - - u32 chunksize_max = m_max_packet_size - BASE_HEADER_SIZE; - if(reliable) - chunksize_max -= RELIABLE_HEADER_SIZE; - - core::list > originals; - originals = makeAutoSplitPacket(data, chunksize_max, - channel->next_outgoing_split_seqnum); - - core::list >::Iterator i; - i = originals.begin(); - for(; i != originals.end(); i++) - { - SharedBuffer original = *i; - - SendAsPacket(peer_id, channelnum, original, reliable); - } -} - -void Connection::SendAsPacket(u16 peer_id, u8 channelnum, - SharedBuffer data, bool reliable) -{ - Peer *peer = GetPeer(peer_id); - Channel *channel = &(peer->channels[channelnum]); - - if(reliable) - { - u16 seqnum = channel->next_outgoing_seqnum; - channel->next_outgoing_seqnum++; - - SharedBuffer reliable = makeReliablePacket(data, seqnum); - - // Add base headers and make a packet - BufferedPacket p = makePacket(peer->address, reliable, - m_protocol_id, m_peer_id, channelnum); - - try{ - // Buffer the packet - channel->outgoing_reliables.insert(p); - } - catch(AlreadyExistsException &e) - { - PrintInfo(derr_con); - derr_con<<"WARNING: Going to send a reliable packet " - "seqnum="<address, data, - m_protocol_id, m_peer_id, channelnum); - - // Send the packet - RawSend(p); - } -} - -void Connection::RawSend(const BufferedPacket &packet) -{ - m_socket.Send(packet.address, *packet.data, packet.data.getSize()); -} - -void Connection::RunTimeouts(float dtime) +void Connection::runTimeouts(float dtime) { core::list timeouted_peers; core::map::Iterator j; @@ -1270,7 +948,7 @@ void Connection::RunTimeouts(float dtime) <<", seqnum="< data(2); writeU8(&data[0], TYPE_CONTROL); writeU8(&data[1], CONTROLTYPE_PING); - SendAsPacket(peer->id, 0, data, true); + rawSendAsPacket(peer->id, 0, data, true); peer->ping_timer = 0.0; } @@ -1309,12 +987,167 @@ nextpeer: } } -Peer* Connection::GetPeer(u16 peer_id) +void Connection::serve(u16 port) +{ + dout_con<::Node *node = m_peers.find(PEER_ID_SERVER); + if(node != NULL){ + throw ConnectionException("Already connected to a server"); + } + + Peer *peer = new Peer(PEER_ID_SERVER, address); + m_peers.insert(peer->id, peer); + + // Create event + ConnectionEvent e; + e.peerAdded(peer->id, peer->address); + putEvent(e); + + m_socket.Bind(0); + + // Send a dummy packet to server with peer_id = PEER_ID_INEXISTENT + m_peer_id = PEER_ID_INEXISTENT; + SharedBuffer data(0); + Send(PEER_ID_SERVER, 0, data, true); +} + +void Connection::disconnect() +{ + dout_con< data(2); + writeU8(&data[0], TYPE_CONTROL); + writeU8(&data[1], CONTROLTYPE_DISCO); + + // Send to all + core::map::Iterator j; + j = m_peers.getIterator(); + for(; j.atEnd() == false; j++) + { + Peer *peer = j.getNode()->getValue(); + rawSendAsPacket(peer->id, 0, data, false); + } +} + +void Connection::sendToAll(u8 channelnum, SharedBuffer data, bool reliable) +{ + core::map::Iterator j; + j = m_peers.getIterator(); + for(; j.atEnd() == false; j++) + { + Peer *peer = j.getNode()->getValue(); + send(peer->id, channelnum, data, reliable); + } +} + +void Connection::send(u16 peer_id, u8 channelnum, + SharedBuffer data, bool reliable) +{ + dout_con<address, data, + m_protocol_id, m_peer_id, channelnum); + + // Send the packet + rawSend(p); + } +} + +void Connection::rawSend(const BufferedPacket &packet) +{ + try{ + m_socket.Send(packet.address, *packet.data, packet.data.getSize()); + } catch(SendFailedException &e){ + derr_con<<"Connection::rawSend(): SendFailedException: " + <::Node *node = m_peers.find(peer_id); if(node == NULL){ - // Peer not found throw PeerNotFoundException("GetPeer: Peer not found (possible timeout)"); } @@ -1324,7 +1157,7 @@ Peer* Connection::GetPeer(u16 peer_id) return node->getValue(); } -Peer* Connection::GetPeerNoEx(u16 peer_id) +Peer* Connection::getPeerNoEx(u16 peer_id) { core::map::Node *node = m_peers.find(peer_id); @@ -1338,7 +1171,7 @@ Peer* Connection::GetPeerNoEx(u16 peer_id) return node->getValue(); } -core::list Connection::GetPeers() +core::list Connection::getPeers() { core::list list; core::map::Iterator j; @@ -1351,23 +1184,474 @@ core::list Connection::GetPeers() return list; } +bool Connection::getFromBuffers(u16 &peer_id, SharedBuffer &dst) +{ + core::map::Iterator j; + j = m_peers.getIterator(); + for(; j.atEnd() == false; j++) + { + Peer *peer = j.getNode()->getValue(); + for(u16 i=0; ichannels[i]; + SharedBuffer resultdata; + bool got = checkIncomingBuffers(channel, peer_id, resultdata); + if(got){ + dst = resultdata; + return true; + } + } + } + return false; +} + +bool Connection::checkIncomingBuffers(Channel *channel, u16 &peer_id, + SharedBuffer &dst) +{ + u16 firstseqnum = 0; + // Clear old packets from start of buffer + try{ + for(;;){ + firstseqnum = channel->incoming_reliables.getFirstSeqnum(); + if(seqnum_higher(channel->next_incoming_seqnum, firstseqnum)) + channel->incoming_reliables.popFirst(); + else + break; + } + // This happens if all packets are old + }catch(con::NotFoundException) + {} + + if(channel->incoming_reliables.empty() == false) + { + if(firstseqnum == channel->next_incoming_seqnum) + { + BufferedPacket p = channel->incoming_reliables.popFirst(); + + peer_id = readPeerId(*p.data); + u8 channelnum = readChannel(*p.data); + u16 seqnum = readU16(&p.data[BASE_HEADER_SIZE+1]); + + PrintInfo(); + dout_con<<"UNBUFFERING TYPE_RELIABLE" + <<" seqnum="<outgoing_reliables.print(); + dout_con< payload(packetdata.getSize() - ORIGINAL_HEADER_SIZE); + memcpy(*payload, &packetdata[ORIGINAL_HEADER_SIZE], payload.getSize()); + return payload; + } + else if(type == TYPE_SPLIT) + { + // We have to create a packet again for buffering + // This isn't actually too bad an idea. + BufferedPacket packet = makePacket( + getPeer(peer_id)->address, + packetdata, + GetProtocolID(), + peer_id, + channelnum); + // Buffer the packet + SharedBuffer data = channel->incoming_splits.insert(packet, reliable); + if(data.getSize() != 0) + { + PrintInfo(); + dout_con<<"RETURNING TYPE_SPLIT: Constructed full data, " + <<"size="<next_incoming_seqnum); + bool is_old_packet = seqnum_higher(channel->next_incoming_seqnum, seqnum); + + PrintInfo(); + if(is_future_packet) + dout_con<<"BUFFERING"; + else if(is_old_packet) + dout_con<<"OLD"; + else + dout_con<<"RECUR"; + dout_con<<" TYPE_RELIABLE seqnum="<incoming_reliables.size() < 100); + + // Send a CONTROLTYPE_ACK + SharedBuffer reply(4); + writeU8(&reply[0], TYPE_CONTROL); + writeU8(&reply[1], CONTROLTYPE_ACK); + writeU16(&reply[2], seqnum); + rawSendAsPacket(peer_id, channelnum, reply, false); + + //if(seqnum_higher(seqnum, channel->next_incoming_seqnum)) + if(is_future_packet) + { + /*PrintInfo(); + dout_con<<"Buffering reliable packet (seqnum=" + <address, + packetdata, + GetProtocolID(), + peer_id, + channelnum); + try{ + channel->incoming_reliables.insert(packet); + + /*PrintInfo(); + dout_con<<"INCOMING: "; + channel->incoming_reliables.print(); + dout_con<next_incoming_seqnum, seqnum)) + else if(is_old_packet) + { + // An old packet, dump it + throw InvalidIncomingDataException("Got an old reliable packet"); + } + + channel->next_incoming_seqnum++; + + // Get out the inside packet and re-process it + SharedBuffer payload(packetdata.getSize() - RELIABLE_HEADER_SIZE); + memcpy(*payload, &packetdata[RELIABLE_HEADER_SIZE], payload.getSize()); + + return processPacket(channel, payload, peer_id, channelnum, true); + } + else + { + PrintInfo(derr_con); + derr_con<<"Got invalid type="<<((int)type&0xff)<deletingPeer(m_peers[peer_id], timeout); + + Peer *peer = m_peers[peer_id]; + + // Create event + ConnectionEvent e; + e.peerRemoved(peer_id, timeout, peer->address); + putEvent(e); + delete m_peers[peer_id]; m_peers.remove(peer_id); return true; } +/* Interface */ + +ConnectionEvent Connection::getEvent() +{ + if(m_event_queue.size() == 0){ + ConnectionEvent e; + e.type = CONNEVENT_NONE; + return e; + } + return m_event_queue.pop_front(); +} + +ConnectionEvent Connection::waitEvent(u32 timeout_ms) +{ + try{ + return m_event_queue.pop_front(timeout_ms); + } catch(ItemNotFoundException &ex){ + ConnectionEvent e; + e.type = CONNEVENT_NONE; + return e; + } +} + +void Connection::putCommand(ConnectionCommand &c) +{ + m_command_queue.push_back(c); +} + +void Connection::Serve(unsigned short port) +{ + ConnectionCommand c; + c.serve(port); + putCommand(c); +} + +void Connection::Connect(Address address) +{ + ConnectionCommand c; + c.connect(address); + putCommand(c); +} + +bool Connection::Connected() +{ + JMutexAutoLock peerlock(m_peers_mutex); + + if(m_peers.size() != 1) + return false; + + core::map::Node *node = m_peers.find(PEER_ID_SERVER); + if(node == NULL) + return false; + + if(m_peer_id == PEER_ID_INEXISTENT) + return false; + + return true; +} + +void Connection::Disconnect() +{ + ConnectionCommand c; + c.disconnect(); + putCommand(c); +} + +u32 Connection::Receive(u16 &peer_id, u8 *data, u32 datasize) +{ + for(;;){ + ConnectionEvent e = waitEvent(m_bc_receive_timeout); + if(e.type != CONNEVENT_NONE) + dout_con<peerAdded(&tmp); + continue; } + case CONNEVENT_PEER_REMOVED: { + Peer tmp(e.peer_id, e.address); + if(m_bc_peerhandler) + m_bc_peerhandler->deletingPeer(&tmp, e.timeout); + continue; } + } + } + throw NoIncomingDataException("No incoming data"); +} + +void Connection::SendToAll(u8 channelnum, SharedBuffer data, bool reliable) +{ + assert(channelnum < CHANNEL_COUNT); + + ConnectionCommand c; + c.sendToAll(channelnum, data, reliable); + putCommand(c); +} + +void Connection::Send(u16 peer_id, u8 channelnum, + SharedBuffer data, bool reliable) +{ + assert(channelnum < CHANNEL_COUNT); + + ConnectionCommand c; + c.send(peer_id, channelnum, data, reliable); + putCommand(c); +} + +void Connection::RunTimeouts(float dtime) +{ + // No-op +} + +Address Connection::GetPeerAddress(u16 peer_id) +{ + JMutexAutoLock peerlock(m_peers_mutex); + return getPeer(peer_id)->address; +} + +float Connection::GetPeerAvgRTT(u16 peer_id) +{ + JMutexAutoLock peerlock(m_peers_mutex); + return getPeer(peer_id)->avg_rtt; +} + +void Connection::DeletePeer(u16 peer_id) +{ + ConnectionCommand c; + c.deletePeer(peer_id); + putCommand(c); +} + void Connection::PrintInfo(std::ostream &out) { - out< ProcessPacket( - SharedBuffer packetdata, - Connection *con, - u16 peer_id, - u8 channelnum, - bool reliable=false); - - // Returns next data from a buffer if possible - // throws a NoIncomingDataException if no data is available - // If found, sets peer_id - SharedBuffer CheckIncomingBuffers(Connection *con, - u16 &peer_id); u16 next_outgoing_seqnum; u16 next_incoming_seqnum; @@ -412,78 +390,237 @@ public: // with the id we have given to it bool has_sent_with_id; + float m_sendtime_accu; + float m_max_packets_per_second; + int m_num_sent; + int m_max_num_sent; + private: }; -class Connection +/* + Connection +*/ + +struct OutgoingPacket +{ + u16 peer_id; + u8 channelnum; + SharedBuffer data; + bool reliable; + + OutgoingPacket(u16 peer_id_, u8 channelnum_, SharedBuffer data_, + bool reliable_): + peer_id(peer_id_), + channelnum(channelnum_), + data(data_), + reliable(reliable_) + { + } +}; + +enum ConnectionEventType{ + CONNEVENT_NONE, + CONNEVENT_DATA_RECEIVED, + CONNEVENT_PEER_ADDED, + CONNEVENT_PEER_REMOVED, +}; + +struct ConnectionEvent +{ + enum ConnectionEventType type; + u16 peer_id; + SharedBuffer data; + bool timeout; + Address address; + + ConnectionEvent(): type(CONNEVENT_NONE) {} + + std::string describe() + { + switch(type){ + case CONNEVENT_NONE: + return "CONNEVENT_NONE"; + case CONNEVENT_DATA_RECEIVED: + return "CONNEVENT_DATA_RECEIVED"; + case CONNEVENT_PEER_ADDED: + return "CONNEVENT_PEER_ADDED"; + case CONNEVENT_PEER_REMOVED: + return "CONNEVENT_PEER_REMOVED"; + } + return "Invalid ConnectionEvent"; + } + + void dataReceived(u16 peer_id_, SharedBuffer data_) + { + type = CONNEVENT_DATA_RECEIVED; + peer_id = peer_id_; + data = data_; + } + void peerAdded(u16 peer_id_, Address address_) + { + type = CONNEVENT_PEER_ADDED; + peer_id = peer_id_; + address = address_; + } + void peerRemoved(u16 peer_id_, bool timeout_, Address address_) + { + type = CONNEVENT_PEER_REMOVED; + peer_id = peer_id_; + timeout = timeout_; + address = address_; + } +}; + +enum ConnectionCommandType{ + CONNCMD_NONE, + CONNCMD_SERVE, + CONNCMD_CONNECT, + CONNCMD_DISCONNECT, + CONNCMD_SEND, + CONNCMD_SEND_TO_ALL, + CONNCMD_DELETE_PEER, +}; + +struct ConnectionCommand +{ + enum ConnectionCommandType type; + u16 port; + Address address; + u16 peer_id; + u8 channelnum; + SharedBuffer data; + bool reliable; + + ConnectionCommand(): type(CONNCMD_NONE) {} + + void serve(u16 port_) + { + type = CONNCMD_SERVE; + port = port_; + } + void connect(Address address_) + { + type = CONNCMD_CONNECT; + address = address_; + } + void disconnect() + { + type = CONNCMD_DISCONNECT; + } + void send(u16 peer_id_, u8 channelnum_, + SharedBuffer data_, bool reliable_) + { + type = CONNCMD_SEND; + peer_id = peer_id_; + channelnum = channelnum_; + data = data_; + reliable = reliable_; + } + void sendToAll(u8 channelnum_, SharedBuffer data_, bool reliable_) + { + type = CONNCMD_SEND_TO_ALL; + channelnum = channelnum_; + data = data_; + reliable = reliable_; + } + void deletePeer(u16 peer_id_) + { + type = CONNCMD_DELETE_PEER; + peer_id = peer_id_; + } +}; + +class Connection: public SimpleThread { public: - Connection( - u32 protocol_id, - u32 max_packet_size, - float timeout, - PeerHandler *peerhandler - ); + Connection(u32 protocol_id, u32 max_packet_size, float timeout); + Connection(u32 protocol_id, u32 max_packet_size, float timeout, + PeerHandler *peerhandler); ~Connection(); - void setTimeoutMs(int timeout){ m_socket.setTimeoutMs(timeout); } - // Start being a server + void * Thread(); + + /* Interface */ + + ConnectionEvent getEvent(); + ConnectionEvent waitEvent(u32 timeout_ms); + void putCommand(ConnectionCommand &c); + + void SetTimeoutMs(int timeout){ m_bc_receive_timeout = timeout; } void Serve(unsigned short port); - // Connect to a server void Connect(Address address); bool Connected(); - void Disconnect(); - - // Sets peer_id - SharedBuffer GetFromBuffers(u16 &peer_id); - - // The peer_id of sender is stored in peer_id - // Return value: I guess this always throws an exception or - // actually gets data - // May call PeerHandler methods u32 Receive(u16 &peer_id, u8 *data, u32 datasize); - - // These will automatically package the data as an original or split void SendToAll(u8 channelnum, SharedBuffer data, bool reliable); void Send(u16 peer_id, u8 channelnum, SharedBuffer data, bool reliable); - // Send data as a packet; it will be wrapped in base header and - // optionally to a reliable packet. - void SendAsPacket(u16 peer_id, u8 channelnum, - SharedBuffer data, bool reliable); - // Sends a raw packet - void RawSend(const BufferedPacket &packet); - - // May call PeerHandler methods - void RunTimeouts(float dtime); - - // Can throw a PeerNotFoundException - Peer* GetPeer(u16 peer_id); - // returns NULL if failed - Peer* GetPeerNoEx(u16 peer_id); - core::list GetPeers(); - - // Calls PeerHandler::deletingPeer - // Returns false if peer was not found - bool deletePeer(u16 peer_id, bool timeout); - - void SetPeerID(u16 id){ m_peer_id = id; } + void RunTimeouts(float dtime); // dummy u16 GetPeerID(){ return m_peer_id; } - u32 GetProtocolID(){ return m_protocol_id; } + Address GetPeerAddress(u16 peer_id); + float GetPeerAvgRTT(u16 peer_id); + void DeletePeer(u16 peer_id); + +private: + void putEvent(ConnectionEvent &e); + void processCommand(ConnectionCommand &c); + void send(float dtime); + void receive(); + void runTimeouts(float dtime); + void serve(u16 port); + void connect(Address address); + void disconnect(); + void sendToAll(u8 channelnum, SharedBuffer data, bool reliable); + void send(u16 peer_id, u8 channelnum, SharedBuffer data, bool reliable); + void sendAsPacket(u16 peer_id, u8 channelnum, + SharedBuffer data, bool reliable); + void rawSendAsPacket(u16 peer_id, u8 channelnum, + SharedBuffer data, bool reliable); + void rawSend(const BufferedPacket &packet); + Peer* getPeer(u16 peer_id); + Peer* getPeerNoEx(u16 peer_id); + core::list getPeers(); + bool getFromBuffers(u16 &peer_id, SharedBuffer &dst); + // Returns next data from a buffer if possible + // If found, returns true; if not, false. + // If found, sets peer_id and dst + bool checkIncomingBuffers(Channel *channel, u16 &peer_id, + SharedBuffer &dst); + /* + Processes a packet with the basic header stripped out. + Parameters: + packetdata: Data in packet (with no base headers) + peer_id: peer id of the sender of the packet in question + channelnum: channel on which the packet was sent + reliable: true if recursing into a reliable packet + */ + SharedBuffer processPacket(Channel *channel, + SharedBuffer packetdata, u16 peer_id, + u8 channelnum, bool reliable); + bool deletePeer(u16 peer_id, bool timeout); + + Queue m_outgoing_queue; + MutexedQueue m_event_queue; + MutexedQueue m_command_queue; + + u32 m_protocol_id; + u32 m_max_packet_size; + float m_timeout; + UDPSocket m_socket; + u16 m_peer_id; + + core::map m_peers; + JMutex m_peers_mutex; - // For debug printing + // Backwards compatibility + PeerHandler *m_bc_peerhandler; + int m_bc_receive_timeout; + + void SetPeerID(u16 id){ m_peer_id = id; } + u32 GetProtocolID(){ return m_protocol_id; } void PrintInfo(std::ostream &out); void PrintInfo(); + std::string getDesc(); u16 m_indentation; - -private: - u32 m_protocol_id; - float m_timeout; - PeerHandler *m_peerhandler; - core::map m_peers; - u16 m_peer_id; - //bool m_waiting_new_peer_id; - u32 m_max_packet_size; - UDPSocket m_socket; }; } // namespace diff --git a/src/content_cao.cpp b/src/content_cao.cpp index d3a1813..6cb2cee 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -208,6 +208,35 @@ void ItemCAO::addToScene(scene::ISceneManager *smgr) // This is needed for changing the texture in the future m_node->setReadOnlyMaterials(true); updateNodePos(); + + /* + Update image of node + */ + + // Create an inventory item to see what is its image + std::istringstream is(m_inventorystring, std::ios_base::binary); + video::ITexture *texture = NULL; + try{ + InventoryItem *item = NULL; + item = InventoryItem::deSerialize(is); + infostream<<__FUNCTION_NAME<<": m_inventorystring=\"" + < item="<getImage(); + delete item; + } + } + catch(SerializationError &e) + { + infostream<<"WARNING: "<<__FUNCTION_NAME + <<": error deSerializing inventorystring \"" + <getMaterial().setTexture(0, texture); } void ItemCAO::removeFromScene() @@ -289,49 +318,6 @@ void ItemCAO::initialize(const std::string &data) } updateNodePos(); - - /* - Update image of node - */ - - if(m_node == NULL) - return; - - scene::IMesh *mesh = m_node->getMesh(); - - if(mesh == NULL) - return; - - scene::IMeshBuffer *buf = mesh->getMeshBuffer(0); - - if(buf == NULL) - return; - - // Create an inventory item to see what is its image - std::istringstream is(m_inventorystring, std::ios_base::binary); - video::ITexture *texture = NULL; - try{ - InventoryItem *item = NULL; - item = InventoryItem::deSerialize(is); - infostream<<__FUNCTION_NAME<<": m_inventorystring=\"" - < item="<getImage(); - delete item; - } - } - catch(SerializationError &e) - { - infostream<<"WARNING: "<<__FUNCTION_NAME - <<": error deSerializing inventorystring \"" - <getMaterial().setTexture(0, texture); - } /* @@ -1007,11 +993,15 @@ void MobV2CAO::updateNodePos() void MobV2CAO::step(float dtime, ClientEnvironment *env) { scene::MyBillboardSceneNode *bill = m_node; + if(!bill) + return; pos_translator.translate(dtime); if(m_sprite_type == "humanoid_1"){ scene::ICameraSceneNode* camera = m_node->getSceneManager()->getActiveCamera(); + if(!camera) + return; v3f cam_to_mob = m_node->getAbsolutePosition() - camera->getAbsolutePosition(); cam_to_mob.normalize(); int col = 0; diff --git a/src/content_inventory.cpp b/src/content_inventory.cpp index 59997ee..413ae85 100644 --- a/src/content_inventory.cpp +++ b/src/content_inventory.cpp @@ -72,8 +72,8 @@ std::string item_craft_get_image_name(const std::string &subname) else if(subname == "firefly") return "firefly.png"; else if(subname == "apple") - return "apple.png"; - else if(subname == "apple_iron") + return "apple.png^[forcesingle"; + else if(subname == "apple_iron") return "apple_iron.png"; else return "cloud.png"; // just something diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 3bfe6ba..43a25f2 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -143,7 +143,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // New-style leaves material video::SMaterial material_leaves1; material_leaves1.setFlag(video::EMF_LIGHTING, false); - //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false); material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false); material_leaves1.setFlag(video::EMF_FOG_ENABLE, true); material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; @@ -229,25 +228,54 @@ void mapblock_mesh_generate_special(MeshMakeData *data, v3s16 p(x,y,z); MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p); - + /* Add torches to mesh */ if(n.getContent() == CONTENT_TORCH) { + v3s16 dir = unpackDir(n.param2); + + const char *texturename = "torch.png"; + if(dir == v3s16(0,-1,0)){ + texturename = "torch_on_floor.png"; + } else if(dir == v3s16(0,1,0)){ + texturename = "torch_on_ceiling.png"; + // For backwards compatibility + } else if(dir == v3s16(0,0,0)){ + texturename = "torch_on_floor.png"; + } else { + texturename = "torch.png"; + } + + AtlasPointer ap = g_texturesource->getTexture(texturename); + + // Set material + video::SMaterial material; + material.setFlag(video::EMF_LIGHTING, false); + material.setFlag(video::EMF_BACK_FACE_CULLING, false); + material.setFlag(video::EMF_BILINEAR_FILTER, false); + material.setFlag(video::EMF_FOG_ENABLE, true); + //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + material.MaterialType + = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + material.setTexture(0, ap.atlas); + video::SColor c(255,255,255,255); // Wall at X+ of node video::S3DVertex vertices[4] = { - video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0), + video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, + ap.x0(), ap.y1()), + video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, + ap.x1(), ap.y1()), + video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, + ap.x1(), ap.y0()), + video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, + ap.x0(), ap.y0()), }; - v3s16 dir = unpackDir(n.param2); - for(s32 i=0; i<4; i++) { if(dir == v3s16(1,0,0)) @@ -266,29 +294,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); } - // Set material - video::SMaterial material; - material.setFlag(video::EMF_LIGHTING, false); - material.setFlag(video::EMF_BACK_FACE_CULLING, false); - material.setFlag(video::EMF_BILINEAR_FILTER, false); - //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - material.MaterialType - = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - - if(dir == v3s16(0,-1,0)) - material.setTexture(0, - g_texturesource->getTextureRaw("torch_on_floor.png")); - else if(dir == v3s16(0,1,0)) - material.setTexture(0, - g_texturesource->getTextureRaw("torch_on_ceiling.png")); - // For backwards compatibility - else if(dir == v3s16(0,0,0)) - material.setTexture(0, - g_texturesource->getTextureRaw("torch_on_floor.png")); - else - material.setTexture(0, - g_texturesource->getTextureRaw("torch.png")); - u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(material, vertices, 4, indices, 6); @@ -298,6 +303,18 @@ void mapblock_mesh_generate_special(MeshMakeData *data, */ else if(n.getContent() == CONTENT_SIGN_WALL) { + AtlasPointer ap = g_texturesource->getTexture("sign_wall.png"); + + // Set material + video::SMaterial material; + material.setFlag(video::EMF_LIGHTING, false); + material.setFlag(video::EMF_BACK_FACE_CULLING, false); + material.setFlag(video::EMF_BILINEAR_FILTER, false); + material.setFlag(video::EMF_FOG_ENABLE, true); + material.MaterialType + = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + material.setTexture(0, ap.atlas); + u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio)); video::SColor c = MapBlock_LightColor(255, l); @@ -305,10 +322,14 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // Wall at X+ of node video::S3DVertex vertices[4] = { - video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0), + video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, + ap.x0(), ap.y1()), + video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, + ap.x1(), ap.y1()), + video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, + ap.x1(), ap.y0()), + video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, + ap.x0(), ap.y0()), }; v3s16 dir = unpackDir(n.param2); @@ -331,19 +352,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); } - // Set material - video::SMaterial material; - material.setFlag(video::EMF_LIGHTING, false); - material.setFlag(video::EMF_BACK_FACE_CULLING, false); - material.setFlag(video::EMF_BILINEAR_FILTER, false); - material.setFlag(video::EMF_FOG_ENABLE, true); - //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - material.MaterialType - = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - - material.setTexture(0, - g_texturesource->getTextureRaw("sign_wall.png")); - u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(material, vertices, 4, indices, 6); @@ -353,17 +361,37 @@ void mapblock_mesh_generate_special(MeshMakeData *data, */ else if(n.getContent() == CONTENT_TELEPORT) { + AtlasPointer ap = g_texturesource->getTexture("teleport.png"); + + // Set material + video::SMaterial material; + material.setFlag(video::EMF_LIGHTING, false); + material.setFlag(video::EMF_BACK_FACE_CULLING, false); + material.setFlag(video::EMF_BILINEAR_FILTER, false); + material.setFlag(video::EMF_FOG_ENABLE, true); + material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + + material.setTexture(0,ap.atlas); + u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio)); video::SColor c = MapBlock_LightColor(255, l); float d = (float)BS/16; // Wall at X+ of node - video::S3DVertex vertices[4] = + /*video::S3DVertex vertices[4] = { video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1), video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1), video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0), video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0), + };*/ + video::S3DVertex vertices[4] = + { + video::S3DVertex(BS/2-d,-BS,-BS, 0,0,0, c, 0,1), + video::S3DVertex(BS/2-d,-BS,BS, 0,0,0, c, 1,1), + video::S3DVertex(BS/2-d,BS,BS, 0,0,0, c, 1,0), + video::S3DVertex(BS/2-d,BS,-BS, 0,0,0, c, 0,0), }; v3s16 dir = unpackDir(n.param2); @@ -386,19 +414,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); } - // Set material - video::SMaterial material; - material.setFlag(video::EMF_LIGHTING, false); - material.setFlag(video::EMF_BACK_FACE_CULLING, false); - material.setFlag(video::EMF_BILINEAR_FILTER, false); - material.setFlag(video::EMF_FOG_ENABLE, true); - //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - material.MaterialType - = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - - material.setTexture(0, - g_texturesource->getTextureRaw("teleport.png")); - u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(material, vertices, 4, indices, 6); @@ -580,10 +595,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex vertices[4] = { - /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, pa_liquid1.x0(), pa_liquid1.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, @@ -665,10 +676,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, { video::S3DVertex vertices[4] = { - /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, pa_liquid1.x0(), pa_liquid1.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, @@ -723,10 +730,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex vertices[4] = { - /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, pa_liquid1.x0(), pa_liquid1.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, @@ -760,10 +763,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, { video::S3DVertex vertices[4] = { - /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/ video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, pa_leaves1.x0(), pa_leaves1.y1()), video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, @@ -982,10 +981,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex vertices[4] = { - /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/ video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, ap.x0(), ap.y1()), video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, @@ -1089,13 +1084,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, - pa_papyrus.x0(), pa_papyrus.y1()), + pa_junglegrass.x0(), pa_junglegrass.y1()), video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, - pa_papyrus.x1(), pa_papyrus.y1()), + pa_junglegrass.x1(), pa_junglegrass.y1()), video::S3DVertex(BS/2,BS/1,0, 0,0,0, c, - pa_papyrus.x1(), pa_papyrus.y0()), + pa_junglegrass.x1(), pa_junglegrass.y0()), video::S3DVertex(-BS/2,BS/1,0, 0,0,0, c, - pa_papyrus.x0(), pa_papyrus.y0()), + pa_junglegrass.x0(), pa_junglegrass.y0()), }; if(j == 0) @@ -1132,9 +1127,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, } else if(n.getContent() == CONTENT_RAIL) { - u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio)); - video::SColor c = MapBlock_LightColor(255, l); - bool is_rail_x [] = { false, false }; /* x-1, x+1 */ bool is_rail_z [] = { false, false }; /* z-1, z+1 */ @@ -1152,18 +1144,25 @@ void mapblock_mesh_generate_special(MeshMakeData *data, if(n_plus_z.getContent() == CONTENT_RAIL) is_rail_z[1] = true; - float d = (float)BS/16; - video::S3DVertex vertices[4] = + int adjacencies = is_rail_x[0] + is_rail_x[1] + is_rail_z[0] + is_rail_z[1]; + + // Assign textures + const char *texturename = "rail.png"; + if(adjacencies < 2) + texturename = "rail.png"; + else if(adjacencies == 2) { - video::S3DVertex(-BS/2,-BS/2+d,-BS/2, 0,0,0, c, - 0, 1), - video::S3DVertex(BS/2,-BS/2+d,-BS/2, 0,0,0, c, - 1, 1), - video::S3DVertex(BS/2,-BS/2+d,BS/2, 0,0,0, c, - 1, 0), - video::S3DVertex(-BS/2,-BS/2+d,BS/2, 0,0,0, c, - 0, 0), - }; + if((is_rail_x[0] && is_rail_x[1]) || (is_rail_z[0] && is_rail_z[1])) + texturename = "rail.png"; + else + texturename = "rail_curved.png"; + } + else if(adjacencies == 3) + texturename = "rail_t_junction.png"; + else if(adjacencies == 4) + texturename = "rail_crossing.png"; + + AtlasPointer ap = g_texturesource->getTexture(texturename); video::SMaterial material_rail; material_rail.setFlag(video::EMF_LIGHTING, false); @@ -1172,23 +1171,23 @@ void mapblock_mesh_generate_special(MeshMakeData *data, material_rail.setFlag(video::EMF_FOG_ENABLE, true); material_rail.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + material_rail.setTexture(0, ap.atlas); - int adjacencies = is_rail_x[0] + is_rail_x[1] + is_rail_z[0] + is_rail_z[1]; + u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio)); + video::SColor c = MapBlock_LightColor(255, l); - // Assign textures - if(adjacencies < 2) - material_rail.setTexture(0, g_texturesource->getTextureRaw("rail.png")); - else if(adjacencies == 2) + float d = (float)BS/16; + video::S3DVertex vertices[4] = { - if((is_rail_x[0] && is_rail_x[1]) || (is_rail_z[0] && is_rail_z[1])) - material_rail.setTexture(0, g_texturesource->getTextureRaw("rail.png")); - else - material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_curved.png")); - } - else if(adjacencies == 3) - material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_t_junction.png")); - else if(adjacencies == 4) - material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_crossing.png")); + video::S3DVertex(-BS/2,-BS/2+d,-BS/2, 0,0,0, c, + ap.x0(), ap.y1()), + video::S3DVertex(BS/2,-BS/2+d,-BS/2, 0,0,0, c, + ap.x1(), ap.y1()), + video::S3DVertex(BS/2,-BS/2+d,BS/2, 0,0,0, c, + ap.x1(), ap.y0()), + video::S3DVertex(-BS/2,-BS/2+d,BS/2, 0,0,0, c, + ap.x0(), ap.y0()), + }; // Rotate textures int angle = 0; @@ -1235,6 +1234,17 @@ void mapblock_mesh_generate_special(MeshMakeData *data, collector.append(material_rail, vertices, 4, indices, 6); } else if (n.getContent() == CONTENT_LADDER) { + AtlasPointer ap = g_texturesource->getTexture("ladder.png"); + + // Set material + video::SMaterial material_ladder; + material_ladder.setFlag(video::EMF_LIGHTING, false); + material_ladder.setFlag(video::EMF_BACK_FACE_CULLING, false); + material_ladder.setFlag(video::EMF_BILINEAR_FILTER, false); + material_ladder.setFlag(video::EMF_FOG_ENABLE, true); + material_ladder.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + material_ladder.setTexture(0, ap.atlas); + u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio)); video::SColor c(255,l,l,l); @@ -1243,10 +1253,14 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // Assume wall is at X+ video::S3DVertex vertices[4] = { - video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0), + video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, + ap.x0(), ap.y1()), + video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, + ap.x1(), ap.y1()), + video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, + ap.x1(), ap.y0()), + video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, + ap.x0(), ap.y0()), }; v3s16 dir = unpackDir(n.param2); @@ -1269,14 +1283,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); } - video::SMaterial material_ladder; - material_ladder.setFlag(video::EMF_LIGHTING, false); - material_ladder.setFlag(video::EMF_BACK_FACE_CULLING, false); - material_ladder.setFlag(video::EMF_BILINEAR_FILTER, false); - material_ladder.setFlag(video::EMF_FOG_ENABLE, true); - material_ladder.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - material_ladder.setTexture(0, g_texturesource->getTextureRaw("ladder.png")); - u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(material_ladder, vertices, 4, indices, 6); diff --git a/src/content_mapnode.cpp b/src/content_mapnode.cpp index daf72a8..d5076bc 100644 --- a/src/content_mapnode.cpp +++ b/src/content_mapnode.cpp @@ -117,6 +117,7 @@ void content_mapnode_init() f->setInventoryTextureCube("stone.png", "stone.png", "stone.png"); f->param_type = CPT_MINERAL; f->is_ground_content = true; + f->often_contains_mineral = true; f->dug_item = std::string("MaterialItem2 ")+itos(CONTENT_COBBLE)+" 1"; setStoneLikeDiggingProperties(f->digging_properties, 1.0); if(invisible_stone) @@ -219,6 +220,7 @@ void content_mapnode_init() i = CONTENT_JUNGLEGRASS; f = &content_features(i); f->setInventoryTexture("junglegrass.png"); + f->used_texturenames["junglegrass.png"] = true; f->light_propagates = true; f->param_type = CPT_LIGHT; //f->is_ground_content = true; @@ -264,6 +266,7 @@ void content_mapnode_init() i = CONTENT_PAPYRUS; f = &content_features(i); f->setInventoryTexture("papyrus.png"); + f->used_texturenames["papyrus.png"] = true; f->light_propagates = true; f->param_type = CPT_LIGHT; f->is_ground_content = true; @@ -306,11 +309,13 @@ void content_mapnode_init() f->solidness = 0; // drawn separately, makes no faces f->air_equivalent = true; // grass grows underneath f->setInventoryTexture("fence.png"); + f->used_texturenames["fence.png"] = true; setWoodLikeDiggingProperties(f->digging_properties, 0.75); i = CONTENT_RAIL; f = &content_features(i); f->setInventoryTexture("rail.png"); + f->used_texturenames["rail.png"] = true; f->light_propagates = true; f->param_type = CPT_LIGHT; f->is_ground_content = true; @@ -323,6 +328,7 @@ void content_mapnode_init() i = CONTENT_LADDER; f = &content_features(i); f->setInventoryTexture("ladder.png"); + f->used_texturenames["ladder.png"] = true; f->light_propagates = true; f->param_type = CPT_LIGHT; f->is_ground_content = true; @@ -465,6 +471,7 @@ void content_mapnode_init() i = CONTENT_LAVA; f = &content_features(i); f->setInventoryTextureCube("lava.png", "lava.png", "lava.png"); + f->used_texturenames["lava.png"] = true; f->param_type = CPT_LIGHT; f->light_propagates = false; f->light_source = LIGHT_MAX-1; @@ -501,6 +508,7 @@ void content_mapnode_init() i = CONTENT_LAVASOURCE; f = &content_features(i); f->setInventoryTextureCube("lava.png", "lava.png", "lava.png"); + f->used_texturenames["ladder.png"] = true; if(new_style_water) { f->solidness = 0; // drawn separately, makes no faces @@ -554,6 +562,10 @@ void content_mapnode_init() i = CONTENT_TORCH; f = &content_features(i); f->setInventoryTexture("torch_on_floor.png"); + f->used_texturenames["torch_on_floor.png"] = true; + f->used_texturenames["torch_on_ceiling.png"] = true; + f->used_texturenames["torch_on_floor.png"] = true; + f->used_texturenames["torch.png"] = true; f->param_type = CPT_LIGHT; f->light_propagates = true; f->sunlight_propagates = true; @@ -568,6 +580,7 @@ void content_mapnode_init() i = CONTENT_SIGN_WALL; f = &content_features(i); f->setInventoryTexture("sign_wall.png"); + f->used_texturenames["sign_wall.png"] = true; f->param_type = CPT_LIGHT; f->light_propagates = true; f->sunlight_propagates = true; @@ -670,6 +683,7 @@ void content_mapnode_init() f->param_type = CPT_LIGHT; f->setAllTextures("sapling.png"); f->setInventoryTexture("sapling.png"); + f->used_texturenames["sapling.png"] = true; f->dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; f->light_propagates = true; f->air_equivalent = false; @@ -692,8 +706,8 @@ void content_mapnode_init() i = CONTENT_TELEPORT; f = &content_features(i); - f->setAllTextures("teleport.png"); - //f->setInventoryTexture("teleport.png"); + //f->setAllTextures("teleport.png"); + f->setInventoryTexture("teleport.png"); f->param_type = CPT_LIGHT; f->light_propagates = true; f->light_source = 3; //? @@ -710,6 +724,7 @@ void content_mapnode_init() i = CONTENT_APPLE; f = &content_features(i); f->setInventoryTexture("apple.png"); + f->used_texturenames["apple.png"] = true; f->param_type = CPT_LIGHT; f->light_propagates = true; f->sunlight_propagates = true; diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 1968b7b..f226371 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "collision.h" #include "environment.h" #include "settings.h" +#include "profiler.h" core::map ServerActiveObject::m_types; @@ -137,6 +138,8 @@ ServerActiveObject* ItemSAO::create(ServerEnvironment *env, u16 id, v3f pos, void ItemSAO::step(float dtime, bool send_recommended) { + ScopeProfiler sp2(g_profiler, "ItemSAO::step avg", SPT_AVG); + assert(m_env); const float interval = 0.2; @@ -291,6 +294,8 @@ ServerActiveObject* RatSAO::create(ServerEnvironment *env, u16 id, v3f pos, void RatSAO::step(float dtime, bool send_recommended) { + ScopeProfiler sp2(g_profiler, "RatSAO::step avg", SPT_AVG); + assert(m_env); if(m_is_active == false) @@ -480,6 +485,8 @@ ServerActiveObject* Oerkki1SAO::create(ServerEnvironment *env, u16 id, v3f pos, void Oerkki1SAO::step(float dtime, bool send_recommended) { + ScopeProfiler sp2(g_profiler, "Oerkki1SAO::step avg", SPT_AVG); + assert(m_env); if(m_is_active == false) @@ -677,11 +684,25 @@ std::string Oerkki1SAO::getStaticData() return os.str(); } -u16 Oerkki1SAO::punch(const std::string &toolname, v3f dir) +u16 Oerkki1SAO::punch(const std::string &toolname, v3f dir, + const std::string &playername) { m_speed_f += dir*12*BS; - u16 amount = 20; + u16 amount = 5; + /* See tool names in inventory.h */ + if(toolname == "WSword") + amount = 10; + if(toolname == "STSword") + amount = 12; + if(toolname == "SteelSword") + amount = 16; + if(toolname == "STAxe") + amount = 7; + if(toolname == "SteelAxe") + amount = 9; + if(toolname == "SteelPick") + amount = 7; doDamage(amount); return 65536/100; } @@ -752,6 +773,8 @@ ServerActiveObject* FireflySAO::create(ServerEnvironment *env, u16 id, v3f pos, void FireflySAO::step(float dtime, bool send_recommended) { + ScopeProfiler sp2(g_profiler, "FireflySAO::step avg", SPT_AVG); + assert(m_env); if(m_is_active == false) @@ -1065,6 +1088,8 @@ static void explodeSquare(Map *map, v3s16 p0, v3s16 size) void MobV2SAO::step(float dtime, bool send_recommended) { + ScopeProfiler sp2(g_profiler, "MobV2SAO::step avg", SPT_AVG); + assert(m_env); Map *map = &m_env->getMap(); @@ -1239,8 +1264,6 @@ void MobV2SAO::step(float dtime, bool send_recommended) m_base_position = pos_f; if((pos_f - next_pos_f).getLength() < 0.1 || arrived){ - verbosestream<<"Mob id="<setDefault("keymap_jump", "KEY_SPACE"); settings->setDefault("keymap_sneak", "KEY_LSHIFT"); settings->setDefault("keymap_inventory", "KEY_KEY_I"); + settings->setDefault("keymap_special1", "KEY_KEY_E"); settings->setDefault("keymap_chat", "KEY_KEY_T"); settings->setDefault("keymap_cmd", "/"); settings->setDefault("keymap_rangeselect", "KEY_KEY_R"); @@ -43,8 +44,9 @@ void set_default_settings(Settings *settings) settings->setDefault("keymap_fastmove", "KEY_KEY_J"); settings->setDefault("keymap_frametime_graph", "KEY_F1"); settings->setDefault("keymap_screenshot", "KEY_F12"); + settings->setDefault("keymap_toggle_profiler", "KEY_F2"); + settings->setDefault("keymap_toggle_force_fog_off", "KEY_F3"); // Some (temporary) keys for debugging - settings->setDefault("keymap_special1", "KEY_KEY_E"); settings->setDefault("keymap_print_debug_stacks", "KEY_KEY_P"); settings->setDefault("wanted_fps", "30"); @@ -75,6 +77,7 @@ void set_default_settings(Settings *settings) settings->setDefault("invisible_stone", "false"); settings->setDefault("screenshot_path", "."); settings->setDefault("view_bobbing_amount", "1.0"); + settings->setDefault("enable_2d_clouds", "false"); // Server stuff // "map-dir" doesn't exist by default. @@ -93,13 +96,13 @@ void set_default_settings(Settings *settings) settings->setDefault("enable_mapgen_debug_info", "false"); settings->setDefault("objectdata_interval", "0.2"); settings->setDefault("active_object_send_range_blocks", "3"); - settings->setDefault("active_block_range", "5"); + settings->setDefault("active_block_range", "2"); //settings->setDefault("max_simultaneous_block_sends_per_client", "1"); // This causes frametime jitter on client side, or does it? settings->setDefault("max_simultaneous_block_sends_per_client", "2"); settings->setDefault("max_simultaneous_block_sends_server_total", "8"); - settings->setDefault("max_block_send_distance", "8"); - settings->setDefault("max_block_generate_distance", "8"); + settings->setDefault("max_block_send_distance", "7"); + settings->setDefault("max_block_generate_distance", "5"); settings->setDefault("time_send_interval", "20"); settings->setDefault("time_speed", "96"); settings->setDefault("server_unload_unused_data_timeout", "60"); diff --git a/src/environment.cpp b/src/environment.cpp index 1cdff30..426544a 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -653,6 +653,92 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime) } } +void ServerEnvironment::clearAllObjects() +{ + infostream<<"ServerEnvironment::clearAllObjects(): " + <<"Removing all active objects"< objects_to_remove; + for(core::map::Iterator + i = m_active_objects.getIterator(); + i.atEnd()==false; i++) + { + ServerActiveObject* obj = i.getNode()->getValue(); + u16 id = i.getNode()->getKey(); + v3f objectpos = obj->getBasePosition(); + // Delete static object if block is loaded + if(obj->m_static_exists){ + MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block); + if(block){ + block->m_static_objects.remove(id); + block->raiseModified(MOD_STATE_WRITE_NEEDED); + obj->m_static_exists = false; + } + } + // If known by some client, don't delete immediately + if(obj->m_known_by_count > 0){ + obj->m_pending_deactivation = true; + obj->m_removed = true; + continue; + } + // Delete active object + delete obj; + // Id to be removed from m_active_objects + objects_to_remove.push_back(id); + } + // Remove references from m_active_objects + for(core::list::Iterator i = objects_to_remove.begin(); + i != objects_to_remove.end(); i++) + { + m_active_objects.remove(*i); + } + + core::list loadable_blocks; + infostream<<"ServerEnvironment::clearAllObjects(): " + <<"Listing all loadable blocks"<listAllLoadableBlocks(loadable_blocks); + infostream<<"ServerEnvironment::clearAllObjects(): " + <<"Done listing all loadable blocks: " + <::Iterator i = loadable_blocks.begin(); + i != loadable_blocks.end(); i++) + { + v3s16 p = *i; + MapBlock *block = m_map->emergeBlock(p, false); + if(!block){ + errorstream<<"ServerEnvironment::clearAllObjects(): " + <<"Failed to emerge block "<m_static_objects.m_stored.size(); + u32 num_active = block->m_static_objects.m_active.size(); + if(num_stored != 0 || num_active != 0){ + block->m_static_objects.m_stored.clear(); + block->m_static_objects.m_active.clear(); + block->raiseModified(MOD_STATE_WRITE_NEEDED); + num_objs_cleared += num_stored + num_active; + num_blocks_cleared++; + } + num_blocks_checked++; + + if(num_blocks_checked % report_interval == 0){ + float percent = 100.0 * (float)num_blocks_checked / + loadable_blocks.size(); + infostream<<"ServerEnvironment::clearAllObjects(): " + <<"Cleared "<::Iterator i = m_players.begin(); i != m_players.end(); i++) { @@ -732,7 +818,7 @@ void ServerEnvironment::step(float dtime) */ if(m_active_blocks_management_interval.step(dtime, 2.0)) { - ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg", SPT_LOWPASS); + ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG); /* Get player block positions */ @@ -809,7 +895,7 @@ void ServerEnvironment::step(float dtime) */ if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0)) { - ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg", SPT_LOWPASS); + ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG); float dtime = 1.0; @@ -848,7 +934,7 @@ void ServerEnvironment::step(float dtime) if(m_active_blocks_test_interval.step(dtime, 10.0)) { - ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg", SPT_LOWPASS); + ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /10s", SPT_AVG); //float dtime = 10.0; for(core::map::Iterator @@ -893,7 +979,18 @@ void ServerEnvironment::step(float dtime) if(block==NULL) continue; active_object_count_wider += - block->m_static_objects.m_active.size(); + block->m_static_objects.m_active.size() + + block->m_static_objects.m_stored.size(); + + /*if(block->m_static_objects.m_stored.size() != 0){ + errorstream<<"ServerEnvironment::step(): " + <getPos())<<" contains " + <m_static_objects.m_stored.size() + <<" stored objects; " + <<"when spawning objects, when counting active " + <<"objects in wide area. relative position: " + <<"("<avg("SEnv: num of objects", m_active_objects.size()); // This helps the objects to send data at the same time bool send_recommended = false; @@ -1092,7 +1191,7 @@ void ServerEnvironment::step(float dtime) */ if(m_object_management_interval.step(dtime, 0.5)) { - ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg", SPT_LOWPASS); + ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG); /* Remove objects that satisfy (m_removed && m_known_by_count==0) */ @@ -1347,21 +1446,24 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, bool set_changed) { assert(object); - if(object->getId() == 0) - { + if(object->getId() == 0){ u16 new_id = getFreeServerActiveObjectId(m_active_objects); if(new_id == 0) { - infostream<<"ServerEnvironment::addActiveObjectRaw(): " + errorstream<<"ServerEnvironment::addActiveObjectRaw(): " <<"no free ids available"<setId(new_id); } + else{ + verbosestream<<"ServerEnvironment::addActiveObjectRaw(): " + <<"supplied with id "<getId()<getId(), m_active_objects) == false) { - infostream<<"ServerEnvironment::addActiveObjectRaw(): " + errorstream<<"ServerEnvironment::addActiveObjectRaw(): " <<"id is not free ("<getId()<<")"<getId(), object); - + + verbosestream<<"ServerEnvironment::addActiveObjectRaw(): " + <<"Added id="<getId()<<"; there are now " + <getBasePosition(); std::string staticdata = object->getStaticData(); @@ -1385,11 +1492,12 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, object->m_static_block = blockpos; if(set_changed) - block->setChangedFlag(); + block->raiseModified(MOD_STATE_WRITE_NEEDED); } else{ - infostream<<"ServerEnv: Could not find a block for " - <<"storing newly added static active object"<getId(); @@ -1429,11 +1537,12 @@ void ServerEnvironment::removeRemovedObjects() */ if(obj->m_static_exists && obj->m_removed) { - MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block); + MapBlock *block = m_map->emergeBlock(obj->m_static_block); if(block) { block->m_static_objects.remove(id); - block->setChangedFlag(); + block->raiseModified(MOD_STATE_WRITE_NEEDED); + obj->m_static_exists = false; } } @@ -1454,6 +1563,40 @@ void ServerEnvironment::removeRemovedObjects() } } +static void print_hexdump(std::ostream &o, const std::string &data) +{ + const int linelength = 16; + for(int l=0; ; l++){ + int i0 = linelength * l; + bool at_end = false; + int thislinelength = linelength; + if(i0 + thislinelength > (int)data.size()){ + thislinelength = data.size() - i0; + at_end = true; + } + for(int di=0; di= 32) + o<m_static_objects.m_stored.size() == 0) return; + verbosestream<<"ServerEnvironment::activateObjects(): " + <<"activating objects of block "<getPos()) + <<" ("<m_static_objects.m_stored.size() + <<" objects)"<m_static_objects.m_stored.size() > 49); + if(large_amount){ + errorstream<<"suspiciously large amount of objects detected: " + <m_static_objects.m_stored.size()<<" in " + <getPos()) + <<"; removing all of them."<m_static_objects.m_stored.clear(); + block->raiseModified(MOD_STATE_WRITE_NEEDED); + return; + } // A list for objects that couldn't be converted to static for some // reason. They will be stored back. core::list new_stored; @@ -1481,12 +1639,20 @@ void ServerEnvironment::activateObjects(MapBlock *block) // If couldn't create object, store static data back. if(obj==NULL) { + errorstream<<"ServerEnvironment::activateObjects(): " + <<"failed to create active object from static object " + <<"in block "<m_pending_deactivation) + continue; + u16 id = i.getNode()->getKey(); v3f objectpos = obj->getBasePosition(); @@ -1540,69 +1715,112 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) if(m_active_blocks.contains(blockpos_o)) continue; + verbosestream<<"ServerEnvironment::deactivateFarObjects(): " + <<"deactivating object id="<m_known_by_count > 0 && !force_delete); + /* Update the static data */ + // Create new static object + std::string staticdata_new = obj->getStaticData(); + StaticObject s_obj(obj->getType(), objectpos, staticdata_new); + + bool stays_in_same_block = false; + bool data_changed = true; + + if(obj->m_static_exists){ + if(obj->m_static_block == blockpos_o) + stays_in_same_block = true; + + MapBlock *block = m_map->emergeBlock(obj->m_static_block, false); + + core::map::Node *n = + block->m_static_objects.m_active.find(id); + if(n){ + StaticObject static_old = n->getValue(); + + if(static_old.data == staticdata_new && + (static_old.pos - objectpos).getLength() < 2*BS) + data_changed = false; + } else { + errorstream<<"ServerEnvironment::deactivateFarObjects(): " + <<"id="<m_static_block)<m_static_exists) { - MapBlock *block = m_map->getBlockNoCreateNoEx - (obj->m_static_block); + MapBlock *block = m_map->emergeBlock(obj->m_static_block, false); if(block) { block->m_static_objects.remove(id); - oldblock = block; + obj->m_static_exists = false; + // Only mark block as modified if data changed considerably + if(!stays_in_same_block || data_changed) + block->raiseModified(MOD_STATE_WRITE_NEEDED); } } - // Create new static object - std::string staticdata = obj->getStaticData(); - StaticObject s_obj(obj->getType(), objectpos, staticdata); + // Add to the block where the object is located in v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS)); // Get or generate the block MapBlock *block = m_map->emergeBlock(blockpos); - /*MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos); - if(block == NULL) - { - // Block not found. Is the old block still ok? - if(oldblock) - block = oldblock; - // Load from disk or generate - else - block = m_map->emergeBlock(blockpos); - }*/ - if(block) { - block->m_static_objects.insert(0, s_obj); - block->setChangedFlag(); - obj->m_static_exists = true; - obj->m_static_block = block->getPos(); + if(block->m_static_objects.m_stored.size() >= 49){ + errorstream<<"ServerEnv: Trying to store id="<getId() + <<" statically but block "<m_static_objects.m_stored.size() + <<" (over 49) objects." + <<" Forcing delete."<m_static_objects.insert(new_id, s_obj); + + // Only mark block as modified if data changed considerably + if(!stays_in_same_block || data_changed) + block->raiseModified(MOD_STATE_WRITE_NEEDED); + + obj->m_static_exists = true; + obj->m_static_block = block->getPos(); + } } else{ - infostream<<"ServerEnv: Could not find or generate " - <<"a block for storing static object"<m_static_exists = false; + errorstream<<"ServerEnv: Could not find or generate " + <<"a block for storing id="<getId() + <<" statically"<m_known_by_count > 0 && force_delete == false) + if(pending_delete) { + verbosestream<<"ServerEnvironment::deactivateFarObjects(): " + <<"object id="<m_pending_deactivation = true; continue; } - /*infostream<<"Server: Stored static data. Deleting object." - <(5, 5+(text_height+5)*1, 795, (5+text_height)*2), false, false, guiroot); - // At the middle of the screen // Object infos are shown in this gui::IGUIStaticText *guitext_info = guienv->addStaticText( @@ -945,6 +949,15 @@ void the_game( //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0)); core::list chat_lines; + // Profiler text + gui::IGUIStaticText *guitext_profiler = guienv->addStaticText( + L"", + core::rect(6, 4+(text_height+5)*3, 400, + (text_height+5)*3 + text_height*35), + false, false); + guitext_profiler->setBackgroundColor(video::SColor(80,0,0,0)); + guitext_profiler->setVisible(false); + /*GUIQuickInventory *quick_inventory = new GUIQuickInventory (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/ /*GUIQuickInventory *quick_inventory = new GUIQuickInventory @@ -998,6 +1011,10 @@ void the_game( bool respawn_menu_active = false; + bool show_profiler = false; + + bool force_fog_off = false; + /* Main loop */ @@ -1129,7 +1146,8 @@ void the_game( object_hit_delay_timer -= dtime; - g_profiler->add("Elapsed time", dtime * 1000); + g_profiler->add("Elapsed time", dtime); + g_profiler->avg("FPS", 1./dtime); /* Log frametime for visualization @@ -1220,15 +1238,24 @@ void the_game( */ float profiler_print_interval = g_settings->getFloat("profiler_print_interval"); - if(profiler_print_interval != 0) + bool print_to_log = true; + if(profiler_print_interval == 0){ + print_to_log = false; + profiler_print_interval = 5; + } + if(m_profiler_interval.step(dtime, profiler_print_interval)) { - if(m_profiler_interval.step(0.030, profiler_print_interval)) - { + if(print_to_log){ infostream<<"Profiler:"<print(infostream); + } + + std::ostringstream os(std::ios_base::binary); + g_profiler->print(os); + guitext_profiler->setText(narrow_to_wide(os.str()).c_str()); + g_profiler->clear(); } - } /* Direct handling of user input @@ -1357,6 +1384,15 @@ void the_game( image->drop(); } } + else if(input->wasKeyDown(getKeySetting("keymap_toggle_profiler"))) + { + show_profiler = !show_profiler; + guitext_profiler->setVisible(show_profiler); + } + else if(input->wasKeyDown(getKeySetting("keymap_toggle_force_fog_off"))) + { + force_fog_off = !force_fog_off; + } // Item selection with mouse wheel { @@ -2043,7 +2079,7 @@ void the_game( Fog */ - if(g_settings->getBool("enable_fog") == true) + if(g_settings->getBool("enable_fog") == true && !force_fog_off) { f32 range; if(farmesh) @@ -2053,10 +2089,11 @@ void the_game( else { range = draw_control.wanted_range*BS + MAP_BLOCKSIZE*BS*1.5; + range *= 0.9; if(draw_control.range_all) range = 100000*BS; - if(range < 50*BS) - range = range * 0.5 + 25*BS; + /*if(range < 50*BS) + range = range * 0.5 + 25*BS;*/ } driver->setFog( @@ -2121,14 +2158,15 @@ void the_game( "(% .1f, % .1f, % .1f)" " (% .3f < btime_jitter < % .3f" ", dtime_jitter = % .1f %%" - ", v_range = %.1f)", + ", v_range = %.1f, RTT = %.3f)", player_position.X/BS, player_position.Y/BS, player_position.Z/BS, busytime_jitter1_min_sample, busytime_jitter1_max_sample, dtime_jitter1_max_fraction * 100.0, - draw_control.wanted_range + draw_control.wanted_range, + client.getRTT() ); guitext2->setText(narrow_to_wide(temptext).c_str()); @@ -2210,7 +2248,8 @@ void the_game( guitext_chat->setRelativePosition(rect); - if(chat_lines.size() == 0) + // Don't show chat if empty or profiler is enabled + if(chat_lines.size() == 0 || show_profiler) guitext_chat->setVisible(false); else guitext_chat->setVisible(true); diff --git a/src/guiInventoryMenu.cpp b/src/guiInventoryMenu.cpp index 463c43d..735c370 100644 --- a/src/guiInventoryMenu.cpp +++ b/src/guiInventoryMenu.cpp @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include "log.h" void drawInventoryItem(video::IVideoDriver *driver, gui::IGUIFont *font, @@ -326,11 +327,11 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event) if(amount >= 0) { v2s32 p(event.MouseInput.X, event.MouseInput.Y); - //dstream<<"Mouse down at p=("<getList(s.listname); if(list_from == NULL) - dstream<<"from list doesn't exist"<getItem(m_selected_item->i) != NULL) { - dstream<<"Handing IACTION_MOVE to manager"<count = amount; a->from_inv = m_selected_item->inventoryname; @@ -408,7 +409,7 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event) { if(!canTakeFocus(event.GUIEvent.Element)) { - dstream<<"GUIInventoryMenu: Not allowing focus change." + infostream<<"GUIInventoryMenu: Not allowing focus change." <current_player == NULL) + return "current_player=NULL"; + else + return std::string("current_player=") + c->current_player->getName(); +} + void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr) { -#if 1 - - /*dstream<<"from_inv="<getList(from_list); InventoryList *list_to = inv_to->getList(to_list); - /*dstream<<"list_from="<getItem(from_i)="<getItem(from_i) - <getItem(to_i)="<getItem(to_i) - <getItem(from_i) == NULL) { - dstream<<__FUNCTION_NAME<<": Operation not allowed " - <<"(the source item doesn't exist)" - < 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) - throw InvalidPositionException("emergeBlock(): pos. over limit"); - - v2s16 p2d(p.X, p.Z); - s16 block_y = p.Y; - /* - This will create or load a sector if not found in memory. - If block exists on disk, it will be loaded. - */ - ServerMapSector *sector; - try{ - sector = createSector(p2d); - //sector = emergeSector(p2d, changed_blocks); - } - catch(InvalidPositionException &e) - { - infostream<<"emergeBlock: createSector() failed: " - <getBlockNoCreateNoEx(block_y); - - // If not found, try loading from disk - if(block == NULL) - { - block = loadBlock(p); - } - - // Handle result - if(block == NULL) - { - does_not_exist = true; - } - else if(block->isDummy() == true) - { - does_not_exist = true; - } - else if(block->getLightingExpired()) - { - lighting_expired = true; - } - else - { - // Valid block - //infostream<<"emergeBlock(): Returning already valid block"<insertBlock(block); - } - // Done. - return block; - } - - //infostream<<"Not found on disk, generating."< making one"< light_sources; - bool black_air_left = false; - bool bottom_invalid = - block->propagateSunlight(light_sources, true, - &black_air_left); - - // If sunlight didn't reach everywhere and part of block is - // above ground, lighting has to be properly updated - //if(black_air_left && some_part_underground) - if(black_air_left) - { - lighting_invalidated_blocks[block->getPos()] = block; - } - - if(bottom_invalid) - { - lighting_invalidated_blocks[block->getPos()] = block; - } - } -#endif - - return block; -} -#endif - s16 ServerMap::findGroundLevel(v2s16 p2d) { #if 0 @@ -2868,6 +2737,12 @@ void ServerMap::verifyDatabase() { throw FileNotGoodException("Cannot prepare write statement"); } + d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL); + if(d != SQLITE_OK) { + infostream<<"WARNING: Database list statment failed to prepare: "<= 0) + return i % mod; + return mod - ((-i) % mod); +} + +v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i) +{ + s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048); + i = (i - x) / 4096; + s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048); + i = (i - y) / 4096; + s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048); + return v3s16(x,y,z); +} + +void ServerMap::listAllLoadableBlocks(core::list &dst) +{ + if(loadFromFolders()){ + errorstream<<"Map::listAllLoadableBlocks(): Result will be missing " + <<"all blocks that are stored in flat files"<setMaterial(buf->getMaterial()); driver->drawMeshBuffer(buf); vertex_count += buf->getVertexCount(); + meshbuffer_count++; + stuff_actually_drawn = true; } } + if(stuff_actually_drawn) + blocks_had_pass_meshbuf++; + else + blocks_without_stuff++; } } // foreach sectorblocks @@ -3941,6 +3890,30 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) } } + std::string prefix = "CM: "; + + // Log only on solid pass because values are the same + if(pass == scene::ESNRP_SOLID){ + g_profiler->avg(prefix+"blocks in range", blocks_in_range); + if(blocks_in_range != 0) + g_profiler->avg(prefix+"blocks in range without mesh (frac)", + (float)blocks_in_range_without_mesh/blocks_in_range); + g_profiler->avg(prefix+"blocks drawn", blocks_drawn); + } + + if(pass == scene::ESNRP_SOLID) + prefix = "CM: solid: "; + else + prefix = "CM: transparent: "; + + g_profiler->avg(prefix+"vertices drawn", vertex_count); + if(blocks_had_pass_meshbuf != 0) + g_profiler->avg(prefix+"meshbuffers per block", + (float)meshbuffer_count / (float)blocks_had_pass_meshbuf); + if(blocks_drawn != 0) + g_profiler->avg(prefix+"empty blocks (frac)", + (float)blocks_without_stuff / blocks_drawn); + m_control.blocks_drawn = blocks_drawn; m_control.blocks_would_have_drawn = blocks_would_have_drawn; diff --git a/src/map.h b/src/map.h index b58ba04..c9bc817 100644 --- a/src/map.h +++ b/src/map.h @@ -157,6 +157,10 @@ public: // Returns NULL if not found MapBlock * getBlockNoCreateNoEx(v3s16 p); + /* Server overrides */ + virtual MapBlock * emergeBlock(v3s16 p, bool allow_generate=true) + { return getBlockNoCreateNoEx(p); } + // Returns InvalidPositionException if not found bool isNodeUnderground(v3s16 p); @@ -379,6 +383,7 @@ public: void verifyDatabase(); // Get an integer suitable for a block static sqlite3_int64 getBlockAsInteger(const v3s16 pos); + static v3s16 getIntegerAsBlock(sqlite3_int64 i); // Returns true if the database file does not exist bool loadFromFolders(); @@ -390,6 +395,8 @@ public: void save(bool only_changed); //void loadAll(); + void listAllLoadableBlocks(core::list &dst); + // Saves map seed and possibly other stuff void saveMapMeta(); void loadMapMeta(); @@ -454,6 +461,7 @@ private: sqlite3 *m_database; sqlite3_stmt *m_database_read; sqlite3_stmt *m_database_write; + sqlite3_stmt *m_database_list; }; /* diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index 2bee572..7ee4998 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "main.h" // For g_settings and g_texturesource #include "content_mapblock.h" #include "settings.h" +#include "profiler.h" void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block) { @@ -527,7 +528,7 @@ void updateFastFaceRow( next_tile); if(next_makes_face == makes_face - && next_p_corrected == p_corrected + && next_p_corrected == p_corrected + translate_dir && next_face_dir_corrected == face_dir_corrected && next_lights[0] == lights[0] && next_lights[1] == lights[1] @@ -537,6 +538,29 @@ void updateFastFaceRow( { next_is_different = false; } + else{ + /*if(makes_face){ + g_profiler->add("Meshgen: diff: next_makes_face != makes_face", + next_makes_face != makes_face ? 1 : 0); + g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir", + (next_p_corrected != p_corrected + translate_dir) ? 1 : 0); + g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr", + next_face_dir_corrected != face_dir_corrected ? 1 : 0); + g_profiler->add("Meshgen: diff: next_lights[] != lights[]", + (next_lights[0] != lights[0] || + next_lights[0] != lights[0] || + next_lights[0] != lights[0] || + next_lights[0] != lights[0]) ? 1 : 0); + g_profiler->add("Meshgen: diff: !(next_tile == tile)", + !(next_tile == tile) ? 1 : 0); + }*/ + } + /*g_profiler->add("Meshgen: Total faces checked", 1); + if(makes_face) + g_profiler->add("Meshgen: Total makes_face checked", 1);*/ + } else { + /*if(makes_face) + g_profiler->add("Meshgen: diff: last position", 1);*/ } continuous_tiles_count++; @@ -568,6 +592,8 @@ void updateFastFaceRow( v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z); // Center point of face (kind of) v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f; + if(continuous_tiles_count != 1) + sp += translate_dir_f; v3f scale(1,1,1); if(translate_dir.X != 0) @@ -586,6 +612,11 @@ void updateFastFaceRow( makeFastFace(tile, lights[0], lights[1], lights[2], lights[3], sp, face_dir_corrected, scale, posRelative_f, dest); + + g_profiler->avg("Meshgen: faces drawn by tiling", 0); + for(int i=1; iavg("Meshgen: faces drawn by tiling", 1); + } } continuous_tiles_count = 0; @@ -707,10 +738,13 @@ scene::SMesh* makeMapBlockMesh(MeshMakeData *data) video::SMaterial material; material.setFlag(video::EMF_LIGHTING, false); + material.setFlag(video::EMF_BACK_FACE_CULLING, true); material.setFlag(video::EMF_BILINEAR_FILTER, false); material.setFlag(video::EMF_FOG_ENABLE, true); //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF); //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE); + material.MaterialType + = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; for(u32 i=0; igetTexture(name); diff --git a/src/mapnode.h b/src/mapnode.h index 5db3379..fb72443 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -124,6 +124,10 @@ struct ContentFeatures AtlasPointer *special_atlas; #endif + // List of all block textures that have been used (value is dummy) + // Exists on server too for cleaner code in content_mapnode.cpp + core::map used_texturenames; + // Type of MapNode::param1 ContentParamType param_type; // True for all ground-like things like stone and mud, false for eg. trees @@ -151,6 +155,10 @@ struct ContentFeatures // If true, node is equivalent to air. Torches are, air is. Water is not. // Is used for example to check whether a mud block can have grass on. bool air_equivalent; + // Whether this content type often contains mineral. + // Used for texture atlas creation. + // Currently only enabled for CONTENT_STONE. + bool often_contains_mineral; // Inventory item string as which the node appears in inventory when dug. // Mineral overrides this. @@ -207,6 +215,7 @@ struct ContentFeatures liquid_type = LIQUID_NONE; wall_mounted = false; air_equivalent = false; + often_contains_mineral = false; dug_item = ""; initial_metadata = NULL; liquid_alternative_flowing = CONTENT_IGNORE; diff --git a/src/profiler.h b/src/profiler.h index 8eaf18d..129118e 100644 --- a/src/profiler.h +++ b/src/profiler.h @@ -41,28 +41,48 @@ public: void add(const std::string &name, float value) { JMutexAutoLock lock(m_mutex); - core::map::Node *n = m_data.find(name); - if(n == NULL) { - m_data[name] = value; + /* No average shall have been used; mark add used as -2 */ + core::map::Node *n = m_avgcounts.find(name); + if(n == NULL) + m_avgcounts[name] = -2; + else{ + if(n->getValue() == -1) + n->setValue(-2); + assert(n->getValue() == -2); + } } - else { - n->setValue(n->getValue() + value); + core::map::Node *n = m_data.find(name); + if(n == NULL) + m_data[name] = value; + else + n->setValue(n->getValue() + value); } } - void lowpass(const std::string &name, float value, float factor) + void avg(const std::string &name, float value) { JMutexAutoLock lock(m_mutex); - core::map::Node *n = m_data.find(name); - if(n == NULL) { - m_data[name] = value; + core::map::Node *n = m_avgcounts.find(name); + if(n == NULL) + m_avgcounts[name] = 1; + else{ + /* No add shall have been used */ + assert(n->getValue() != -2); + if(n->getValue() <= 0) + n->setValue(1); + else + n->setValue(n->getValue() + 1); + } } - else { - n->setValue(n->getValue() * (1.0 - 1.0/factor) + value / factor); + core::map::Node *n = m_data.find(name); + if(n == NULL) + m_data[name] = value; + else + n->setValue(n->getValue() + value); } } @@ -75,6 +95,7 @@ public: { i.getNode()->setValue(0); } + m_avgcounts.clear(); } void print(std::ostream &o) @@ -85,6 +106,12 @@ public: i.atEnd() == false; i++) { std::string name = i.getNode()->getKey(); + int avgcount = 1; + core::map::Node *n = m_avgcounts.find(name); + if(n){ + if(n->getValue() >= 1) + avgcount = n->getValue(); + } o<<" "<getValue(); + o<<(i.getNode()->getValue() / avgcount); o< m_data; + core::map m_avgcounts; }; enum ScopeProfilerType{ SPT_ADD, - SPT_LOWPASS + SPT_AVG }; class ScopeProfiler @@ -138,14 +166,15 @@ public: { if(m_timer) { - u32 duration = m_timer->stop(true); + float duration_ms = m_timer->stop(true); + float duration = duration_ms / 1000.0; if(m_profiler){ switch(m_type){ case SPT_ADD: m_profiler->add(m_name, duration); break; - case SPT_LOWPASS: - m_profiler->lowpass(m_name, duration, 20.0); + case SPT_AVG: + m_profiler->avg(m_name, duration); break; } } diff --git a/src/server.cpp b/src/server.cpp index b96f8bd..c437e0f 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -354,11 +354,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, // Increment timers m_nothing_to_send_pause_timer -= dtime; + m_nearest_unsent_reset_timer += dtime; if(m_nothing_to_send_pause_timer >= 0) { - // Keep this reset - m_nearest_unsent_reset_timer = 0; return; } @@ -410,17 +409,13 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, /*infostream<<"m_nearest_unsent_reset_timer=" < 10.0) + // Reset periodically to workaround for some bugs or stuff + if(m_nearest_unsent_reset_timer > 20.0) { m_nearest_unsent_reset_timer = 0; m_nearest_unsent_d = 0; - /*infostream<<"Resetting m_nearest_unsent_d for " - <getPlayerName(peer_id)<getPlayerName(peer_id)<getS16("max_block_generate_distance"); // Don't loop very much at a time - if(d_max > d_start+1) - d_max = d_start+1; + s16 max_d_increment_at_time = 2; + if(d_max > d_start + max_d_increment_at_time) + d_max = d_start + max_d_increment_at_time; /*if(d_max_gen > d_start+2) d_max_gen = d_start+2;*/ //infostream<<"Starting from "<getPlayerName(peer_id)<m_emerge_queue.addBlock(peer_id, p, flags); server->m_emergethread.trigger(); + + if(nearest_emerged_d == -1) + nearest_emerged_d = d; + } else { + if(nearest_emergefull_d == -1) + nearest_emergefull_d = d; } // get next one. continue; } + if(nearest_sent_d == -1) + nearest_sent_d = d; + /* Add block to send queue */ + /*errorstream<<"sending from d="<getPlayerName(peer_id)< g_settings->getS16("max_block_send_distance")){ + new_nearest_unsent_d = 0; + m_nothing_to_send_pause_timer = 2.0; + /*infostream<<"GetNextBlocks(): d wrapped around for " + <getPlayerName(peer_id) + <<"; setting to 0 and pausing"<= - g_settings->getS16("max_block_send_distance")) - { - // Pause time in seconds - m_nothing_to_send_pause_timer = 1.0; - /*infostream<<"nothing to send to " - <getPlayerName(peer_id) - <<" (d="<address.serializeString())){ - SendAccessDenied(m_con, peer_id, - L"Your ip is banned. Banned name was " - +narrow_to_wide(m_banmanager.getBanName( - peer->address.serializeString()))); - m_con.deletePeer(peer_id, false); - return; - } - - u8 peer_ser_ver = getClient(peer->id)->serialization_version; + u8 peer_ser_ver = getClient(peer_id)->serialization_version; try { @@ -1871,7 +1864,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) return; infostream<<"Server: Got TOSERVER_INIT from " - <id<serialization_version = deployed; - getClient(peer->id)->pending_serialization_version = deployed; + getClient(peer_id)->pending_serialization_version = deployed; if(deployed == SER_FMT_VER_INVALID) { @@ -1906,7 +1899,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]); } - getClient(peer->id)->net_proto_version = net_proto_version; + getClient(peer_id)->net_proto_version = net_proto_version; if(net_proto_version == 0) { @@ -2051,11 +2044,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(command == TOSERVER_INIT2) { infostream<<"Server: Got TOSERVER_INIT2 from " - <id<id)->serialization_version - = getClient(peer->id)->pending_serialization_version; + getClient(peer_id)->serialization_version + = getClient(peer_id)->pending_serialization_version; /* Send some initialization data @@ -2065,8 +2058,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) SendPlayerInfos(); // Send inventory to player - UpdateCrafting(peer->id); - SendInventory(peer->id); + UpdateCrafting(peer_id); + SendInventory(peer_id); // Send player items to all players SendPlayerItems(); @@ -2082,7 +2075,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) { SharedBuffer data = makePacket_TOCLIENT_TIME_OF_DAY( m_env.getTimeOfDay()); - m_con.Send(peer->id, 0, data, true); + m_con.Send(peer_id, 0, data, true); } // Send information about server to player in chat @@ -2103,7 +2096,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } // Warnings about protocol version can be issued here - if(getClient(peer->id)->net_proto_version < PROTOCOL_VERSION) + if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION) { SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER"); } @@ -2445,7 +2438,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) else if(action == 2) { #if 0 - RemoteClient *client = getClient(peer->id); + RemoteClient *client = getClient(peer_id); JMutexAutoLock digmutex(client->m_dig_mutex); client->m_dig_tool_item = -1; #endif @@ -2728,7 +2721,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } // Reset build time counter - getClient(peer->id)->m_time_from_building = 0.0; + getClient(peer_id)->m_time_from_building = 0.0; // Create node data MaterialItem *mitem = (MaterialItem*)item; @@ -3539,11 +3532,10 @@ core::list Server::getPlayerInfo() Player *player = *i; try{ - con::Peer *peer = m_con.GetPeer(player->peer_id); - // Copy info from peer to info struct - info.id = peer->id; - info.address = peer->address; - info.avg_rtt = peer->avg_rtt; + // Copy info from connection to info struct + info.id = player->peer_id; + info.address = m_con.GetPeerAddress(player->peer_id); + info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id); } catch(con::PeerNotFoundException &e) { @@ -4426,6 +4418,11 @@ void Server::notifyPlayer(const char *name, const std::wstring msg) SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg); } +void Server::notifyPlayers(const std::wstring msg) +{ + BroadcastChatMessage(msg); +} + v3f findSpawnPos(ServerMap &map) { //return v3f(50,50,50)*BS; diff --git a/src/server.h b/src/server.h index 45f3c60..cc49b22 100644 --- a/src/server.h +++ b/src/server.h @@ -476,13 +476,14 @@ public: return m_banmanager.getBanDescription(ip_or_name); } - con::Peer* getPeerNoEx(u16 peer_id) + Address getPeerAddress(u16 peer_id) { - return m_con.GetPeerNoEx(peer_id); + return m_con.GetPeerAddress(peer_id); } // Envlock and conlock should be locked when calling this void notifyPlayer(const char *name, const std::wstring msg); + void notifyPlayers(const std::wstring msg); private: diff --git a/src/servercommand.cpp b/src/servercommand.cpp index c1e80c0..016a043 100644 --- a/src/servercommand.cpp +++ b/src/servercommand.cpp @@ -251,19 +251,18 @@ void cmd_banunban(std::wostringstream &os, ServerCommandContext *ctx) return; } - con::Peer *peer = ctx->server->getPeerNoEx(player->peer_id); - if(peer == NULL) - { - dstream<<__FUNCTION_NAME<<": peer was not found"<address.serializeString(); + try{ + Address address = ctx->server->getPeerAddress(player->peer_id); + std::string ip_string = address.serializeString(); ctx->server->setIpBanned(ip_string, player->getName()); os<getName()); actionstream<player->getName()<<" bans " <getName()<<" / "<privs & PRIV_SERVER) ==0) + { + os<player->getName() + <<" clears all objects"<player->getName()); + msg += L")"; + ctx->server->notifyPlayers(msg); + } + + ctx->env->clearAllObjects(); + + actionstream<<"object clearing done"<flags |= SEND_TO_OTHERS; +} + + //j void cmd_clanNew(std::wostringstream &os, ServerCommandContext *ctx) @@ -454,41 +483,25 @@ std::wstring processServerCommand(ServerCommandContext *ctx) os<parms[0] == L"status") - { cmd_status(os, ctx); - } else if(ctx->parms[0] == L"privs") - { cmd_privs(os, ctx); - } else if(ctx->parms[0] == L"grant" || ctx->parms[0] == L"revoke") - { cmd_grantrevoke(os, ctx); - } else if(ctx->parms[0] == L"time") - { cmd_time(os, ctx); - } else if(ctx->parms[0] == L"shutdown") - { cmd_shutdown(os, ctx); - } else if(ctx->parms[0] == L"setting") - { cmd_setting(os, ctx); - } else if(ctx->parms[0] == L"teleport") - { cmd_teleport(os, ctx); - } else if(ctx->parms[0] == L"ban" || ctx->parms[0] == L"unban") - { cmd_banunban(os, ctx); - } else if(ctx->parms[0] == L"me") - { cmd_me(os, ctx); - } + else if(ctx->parms[0] == L"clearobjects") + cmd_clearobjects(os, ctx); else if(ctx->parms[0] == L"clan-new") { cmd_clanNew(os, ctx); @@ -506,9 +519,8 @@ std::wstring processServerCommand(ServerCommandContext *ctx) cmd_clanKick(os, ctx); } else - { os<parms[0]; - } + return os.str(); } diff --git a/src/servermain.cpp b/src/servermain.cpp index f3111c4..73e1963 100644 --- a/src/servermain.cpp +++ b/src/servermain.cpp @@ -117,15 +117,15 @@ u32 getTimeMs() return porting::getTimeMs(); } -class DstreamLogOutput: public ILogOutput +class StderrLogOutput: public ILogOutput { public: /* line: Full line with timestamp, level and thread */ void printLog(const std::string &line) { - dstream<>24; diff --git a/src/socket.h b/src/socket.h index f24947c..74b5fb6 100644 --- a/src/socket.h +++ b/src/socket.h @@ -97,7 +97,7 @@ public: void setPort(unsigned short port); void print(std::ostream *s) const; void print() const; - std::string serializeString(); + std::string serializeString() const; private: unsigned int m_address; unsigned short m_port; diff --git a/src/test.cpp b/src/test.cpp index 81786e0..48a3eb4 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -819,7 +819,10 @@ struct TestConnection /* Test some real connections + + NOTE: This mostly tests the legacy interface. */ + u32 proto_id = 0xad26846a; Handler hand_server("server"); @@ -843,11 +846,30 @@ struct TestConnection sleep_ms(50); + // Client should not have added client yet + assert(hand_client.count == 0); + + try + { + u16 peer_id; + u8 data[100]; + infostream<<"** running client.Receive()"<address; + server.GetPeerAddress(peer_id_client); infostream<<"*** Sending packets in wrong order (2,1,2)" <channels[chn]; + con::Channel *ch = &server.getPeer(peer_id_client)->channels[chn]; u16 sn = ch->next_outgoing_seqnum; ch->next_outgoing_seqnum = sn+1; server.Send(peer_id_client, chn, data2, true); @@ -1004,6 +1026,7 @@ struct TestConnection } assert(got_exception); } +#endif { const int datasize = 30000; SharedBuffer data1(datasize); @@ -1022,12 +1045,25 @@ struct TestConnection server.Send(peer_id_client, 0, data1, true); - sleep_ms(50); + sleep_ms(3000); u8 recvdata[datasize + 1000]; infostream<<"** running client.Receive()"< 5000) + break; + try{ + size = client.Receive(peer_id, recvdata, datasize + 1000); + received = true; + }catch(con::NoIncomingDataException &e){ + } + sleep_ms(10); + } + assert(received); infostream<<"** Client received: peer_id="< #include "log.h" +#include "mapnode.h" // For texture atlas making +#include "mineral.h" // For texture atlas making /* A cache from texture name to texture path @@ -507,52 +509,62 @@ void TextureSource::buildMainAtlas() } /* - A list of stuff to include in the texture atlas. - - It is a single-dimensional texture atlas due to the need to tile - textures. - - It should contain as much of the stuff shown in game as possible, - to minimize texture changes. - - It fills up quickly, so do not add anything that isn't contained - in most MapBlocks. E.g. mese isn't suitable but stone is. + Grab list of stuff to include in the texture atlas from the + main content features */ - core::array sourcelist; + core::map sourcelist; - sourcelist.push_back("stone.png"); - sourcelist.push_back("mud.png"); - sourcelist.push_back("sand.png"); - sourcelist.push_back("grass.png"); - sourcelist.push_back("grass_footsteps.png"); - sourcelist.push_back("tree.png"); - sourcelist.push_back("tree_top.png"); - sourcelist.push_back("water.png"); - sourcelist.push_back("leaves.png"); - sourcelist.push_back("glass.png"); - sourcelist.push_back("mud.png^grass_side.png"); - sourcelist.push_back("cobble.png"); - sourcelist.push_back("mossycobble.png"); - sourcelist.push_back("gravel.png"); - sourcelist.push_back("jungletree.png"); - - sourcelist.push_back("stone.png^mineral_coal.png"); - sourcelist.push_back("stone.png^mineral_iron.png"); + for(u16 j=0; j::Iterator + i = f->used_texturenames.getIterator(); + i.atEnd() == false; i++) + { + std::string name = i.getNode()->getKey(); + sourcelist[name] = true; + + if(f->often_contains_mineral){ + for(int k=1; k::Iterator + i = sourcelist.getIterator(); + i.atEnd() == false; i++) + { + std::string name = i.getNode()->getKey(); + infostream<<"\""< pos_in_atlas(0,0); - pos_in_atlas.Y += padding; + pos_in_atlas.Y = padding; - for(u32 i=0; i::Iterator + i = sourcelist.getIterator(); + i.atEnd() == false; i++) { - std::string name = sourcelist[i]; + std::string name = i.getNode()->getKey(); /*video::IImage *img = driver->createImageFromFile( getTexturePath(name.c_str()).c_str()); @@ -586,20 +598,26 @@ void TextureSource::buildMainAtlas() continue; } - // Stop making atlas if atlas is full + // Wrap columns and stop making atlas if atlas is full if(pos_in_atlas.Y + dim.Height > atlas_dim.Height) { - infostream<<"TextureSource::buildMainAtlas(): " - <<"Atlas is full, not adding more textures." - < (s32)atlas_dim.Width - 256 - padding){ + errorstream<<"TextureSource::buildMainAtlas(): " + <<"Atlas is full, not adding more textures." + < 16) // Limit to 16 (more gives no benefit) + xwise_tiling = 16; for(u32 j=0; jgetPixel(x, src_y); atlas_img->setPixel(x,dst_y,c); } @@ -668,9 +686,11 @@ void TextureSource::buildMainAtlas() /* Second pass: set texture pointer in generated AtlasPointers */ - for(u32 i=0; i::Iterator + i = sourcelist.getIterator(); + i.atEnd() == false; i++) { - std::string name = sourcelist[i]; + std::string name = i.getNode()->getKey(); if(m_name_to_id.find(name) == NULL) continue; u32 id = m_name_to_id[name]; @@ -681,8 +701,12 @@ void TextureSource::buildMainAtlas() /* Write image to file so that it can be inspected */ - /*driver->writeImageToFile(atlas_img, - getTexturePath("main_atlas.png").c_str());*/ + /*std::string atlaspath = porting::path_userdata + + DIR_DELIM + "generated_texture_atlas.png"; + infostream<<"Removing and writing texture atlas for inspection to " + <writeImageToFile(atlas_img, atlaspath.c_str());*/ } video::IImage* generate_image_from_scratch(std::string name, @@ -1092,22 +1116,23 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, core::dimension2d dim = image->getDimension(); baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); + // Blit + image->copyTo(baseimg); + + image->drop(); + for(u32 y=0; ygetPixel(x,y); + video::SColor c = baseimg->getPixel(x,y); u32 r = c.getRed(); u32 g = c.getGreen(); u32 b = c.getBlue(); if(!(r == r1 && g == g1 && b == b1)) continue; c.setAlpha(0); - image->setPixel(x,y,c); + baseimg->setPixel(x,y,c); } - // Blit - image->copyTo(baseimg); - - image->drop(); } } /* @@ -1149,11 +1174,16 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, { core::dimension2d dim = image->getDimension(); baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); + + // Blit + image->copyTo(baseimg); + + image->drop(); for(u32 y=0; ygetPixel(x,y); + video::SColor c = baseimg->getPixel(x,y); u32 r = c.getRed(); u32 g = c.getGreen(); u32 b = c.getBlue(); @@ -1161,12 +1191,8 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, !(r == r2 && g == g2 && b == b2)) continue; c.setAlpha(0); - image->setPixel(x,y,c); + baseimg->setPixel(x,y,c); } - // Blit - image->copyTo(baseimg); - - image->drop(); } } /* diff --git a/src/tile.h b/src/tile.h index 5cebb0e..da23615 100644 --- a/src/tile.h +++ b/src/tile.h @@ -283,9 +283,9 @@ struct TileSpec TileSpec(): texture(0), alpha(255), - material_type(MATERIAL_ALPHA_NONE), + //material_type(MATERIAL_ALPHA_NONE), // Use this so that leaves don't need a separate material - //material_type(MATERIAL_ALPHA_SIMPLE), + material_type(MATERIAL_ALPHA_SIMPLE), material_flags( //0 // <- DEBUG, Use the one below MATERIAL_FLAG_BACKFACE_CULLING diff --git a/src/utility.cpp b/src/utility.cpp index 6ce67cb..0139281 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -236,7 +236,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, // If block is (nearly) touching the camera, don't // bother validating further (that is, render it anyway) - if(d > block_max_radius * 1.5) + if(d > block_max_radius) { // Cosine of the angle between the camera direction // and the block direction (camera_dir is an unit vector)