From 3494475df108e3401c6c8463a0aeae0f227fd1fa Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 8 Apr 2020 20:12:58 +0200 Subject: Miscellaneous networking improvements (#9611) fixes #2862 --- src/network/clientopcodes.cpp | 18 +++++++++++++---- src/network/connection.cpp | 35 ++++++++++++++++++++------------- src/network/connection.h | 16 +++++++-------- src/network/connectionthreads.cpp | 41 ++++++++++++++++++++++++++------------- src/network/serveropcodes.cpp | 20 ++++++++++++++----- 5 files changed, 87 insertions(+), 43 deletions(-) (limited to 'src/network') diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp index 431455b76..0f20047c0 100644 --- a/src/network/clientopcodes.cpp +++ b/src/network/clientopcodes.cpp @@ -126,6 +126,16 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] = const static ServerCommandFactory null_command_factory = { "TOSERVER_NULL", 0, false }; +/* + Channels used for Client -> Server communication + 2: Notifications back to the server (e.g. GOTBLOCKS) + 1: Init and Authentication + 0: everything else + + Packet order is only guaranteed inside a channel, so packets that operate on + the same objects are *required* to be in the same channel. +*/ + const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] = { null_command_factory, // 0x00 @@ -143,7 +153,7 @@ const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] = null_command_factory, // 0x0c null_command_factory, // 0x0d null_command_factory, // 0x0e - null_command_factory, // 0x0F + null_command_factory, // 0x0f null_command_factory, // 0x10 { "TOSERVER_INIT2", 1, true }, // 0x11 null_command_factory, // 0x12 @@ -186,7 +196,7 @@ const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] = { "TOSERVER_PLAYERITEM", 0, true }, // 0x37 { "TOSERVER_RESPAWN", 0, true }, // 0x38 { "TOSERVER_INTERACT", 0, true }, // 0x39 - { "TOSERVER_REMOVED_SOUNDS", 1, true }, // 0x3a + { "TOSERVER_REMOVED_SOUNDS", 2, true }, // 0x3a { "TOSERVER_NODEMETA_FIELDS", 0, true }, // 0x3b { "TOSERVER_INVENTORY_FIELDS", 0, true }, // 0x3c null_command_factory, // 0x3d @@ -194,8 +204,8 @@ const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] = null_command_factory, // 0x3f { "TOSERVER_REQUEST_MEDIA", 1, true }, // 0x40 null_command_factory, // 0x41 - { "TOSERVER_BREATH", 0, true }, // 0x42 old TOSERVER_BREATH. Ignored by servers - { "TOSERVER_CLIENT_READY", 0, true }, // 0x43 + null_command_factory, // 0x42 + { "TOSERVER_CLIENT_READY", 1, true }, // 0x43 null_command_factory, // 0x44 null_command_factory, // 0x45 null_command_factory, // 0x46 diff --git a/src/network/connection.cpp b/src/network/connection.cpp index 36124ce3c..15eda7725 100644 --- a/src/network/connection.cpp +++ b/src/network/connection.cpp @@ -924,7 +924,7 @@ UDPPeer::UDPPeer(u16 a_id, Address a_address, Connection* connection) : Peer(a_address,a_id,connection) { for (Channel &channel : channels) - channel.setWindowSize(g_settings->getU16("max_packets_per_iteration")); + channel.setWindowSize(START_RELIABLE_WINDOW_SIZE); } bool UDPPeer::getAddress(MTProtocols type,Address& toset) @@ -975,22 +975,29 @@ void UDPPeer::PutReliableSendCommand(ConnectionCommand &c, if (m_pending_disconnect) return; - if ( channels[c.channelnum].queued_commands.empty() && + Channel &chan = channels[c.channelnum]; + + if (chan.queued_commands.empty() && /* don't queue more packets then window size */ - (channels[c.channelnum].queued_reliables.size() - < (channels[c.channelnum].getWindowSize()/2))) { + (chan.queued_reliables.size() < chan.getWindowSize() / 2)) { LOG(dout_con<getDesc() <<" processing reliable command for peer id: " << c.peer_id <<" data size: " << c.data.getSize() << std::endl); if (!processReliableSendCommand(c,max_packet_size)) { - channels[c.channelnum].queued_commands.push_back(c); + chan.queued_commands.push_back(c); } } else { LOG(dout_con<getDesc() <<" Queueing reliable command for peer id: " << c.peer_id <<" data size: " << c.data.getSize() <= chan.getWindowSize() / 2) { + LOG(derr_con << m_connection->getDesc() + << "Possible packet stall to peer id: " << c.peer_id + << " queued_commands=" << chan.queued_commands.size() + << std::endl); + } } } @@ -1001,6 +1008,8 @@ bool UDPPeer::processReliableSendCommand( if (m_pending_disconnect) return true; + Channel &chan = channels[c.channelnum]; + u32 chunksize_max = max_packet_size - BASE_HEADER_SIZE - RELIABLE_HEADER_SIZE; @@ -1008,13 +1017,13 @@ bool UDPPeer::processReliableSendCommand( sanity_check(c.data.getSize() < MAX_RELIABLE_WINDOW_SIZE*512); std::list> originals; - u16 split_sequence_number = channels[c.channelnum].readNextSplitSeqNum(); + u16 split_sequence_number = chan.readNextSplitSeqNum(); if (c.raw) { originals.emplace_back(c.data); } else { makeAutoSplitPacket(c.data, chunksize_max,split_sequence_number, &originals); - channels[c.channelnum].setNextSplitSeqNum(split_sequence_number); + chan.setNextSplitSeqNum(split_sequence_number); } bool have_sequence_number = true; @@ -1023,7 +1032,7 @@ bool UDPPeer::processReliableSendCommand( volatile u16 initial_sequence_number = 0; for (SharedBuffer &original : originals) { - u16 seqnum = channels[c.channelnum].getOutgoingSequenceNumber(have_sequence_number); + u16 seqnum = chan.getOutgoingSequenceNumber(have_sequence_number); /* oops, we don't have enough sequence numbers to send this packet */ if (!have_sequence_number) @@ -1055,10 +1064,10 @@ bool UDPPeer::processReliableSendCommand( // << " channel: " << (c.channelnum&0xFF) // << " seqnum: " << readU16(&p.data[BASE_HEADER_SIZE+1]) // << std::endl) - channels[c.channelnum].queued_reliables.push(p); + chan.queued_reliables.push(p); pcount++; } - sanity_check(channels[c.channelnum].queued_reliables.size() < 0xFFFF); + sanity_check(chan.queued_reliables.size() < 0xFFFF); return true; } @@ -1073,7 +1082,7 @@ bool UDPPeer::processReliableSendCommand( toadd.pop(); bool successfully_put_back_sequence_number - = channels[c.channelnum].putBackSequenceNumber( + = chan.putBackSequenceNumber( (initial_sequence_number+toadd.size() % (SEQNUM_MAX+1))); FATAL_ERROR_IF(!successfully_put_back_sequence_number, "error"); @@ -1081,7 +1090,7 @@ bool UDPPeer::processReliableSendCommand( // DO NOT REMOVE n_queued! It avoids a deadlock of async locked // 'log_message_mutex' and 'm_list_mutex'. - u32 n_queued = channels[c.channelnum].outgoing_reliables_sent.size(); + u32 n_queued = chan.outgoing_reliables_sent.size(); LOG(dout_con<getDesc() << " Windowsize exceeded on reliable sending " diff --git a/src/network/connection.h b/src/network/connection.h index 0b12bf701..85f021c4c 100644 --- a/src/network/connection.h +++ b/src/network/connection.h @@ -141,7 +141,6 @@ private: === NOTES === A packet is sent through a channel to a peer with a basic header: -TODO: Should we have a receiver_peer_id also? Header (7 bytes): [0] u32 protocol_id [4] session_t sender_peer_id @@ -152,8 +151,7 @@ sender_peer_id: value 1 (PEER_ID_SERVER) is reserved for server these constants are defined in constants.h channel: - The lower the number, the higher the priority is. - Only channels 0, 1 and 2 exist. + Channel numbers have no intrinsic meaning. Currently only 0, 1, 2 exist. */ #define BASE_HEADER_SIZE 7 #define CHANNEL_COUNT 3 @@ -386,12 +384,14 @@ struct ConnectionCommand } }; -/* maximum window size to use, 0xFFFF is theoretical maximum don't think about +/* maximum window size to use, 0xFFFF is theoretical maximum. don't think about * touching it, the less you're away from it the more likely data corruption * will occur */ #define MAX_RELIABLE_WINDOW_SIZE 0x8000 - /* starting value for window size */ +/* starting value for window size */ +#define START_RELIABLE_WINDOW_SIZE 0x400 +/* minimum value for window size */ #define MIN_RELIABLE_WINDOW_SIZE 0x40 class Channel @@ -555,15 +555,15 @@ class Peer { bool isTimedOut(float timeout); - unsigned int m_increment_packets_remaining = 9; - unsigned int m_increment_bytes_remaining = 0; + unsigned int m_increment_packets_remaining = 0; virtual u16 getNextSplitSequenceNumber(u8 channel) { return 0; }; virtual void setNextSplitSequenceNumber(u8 channel, u16 seqnum) {}; virtual SharedBuffer addSplitPacket(u8 channel, const BufferedPacket &toadd, bool reliable) { - fprintf(stderr,"Peer: addSplitPacket called, this is supposed to be never called!\n"); + errorstream << "Peer::addSplitPacket called," + << " this is supposed to be never called!" << std::endl; return SharedBuffer(0); }; diff --git a/src/network/connectionthreads.cpp b/src/network/connectionthreads.cpp index f8b58c025..48a4f51ab 100644 --- a/src/network/connectionthreads.cpp +++ b/src/network/connectionthreads.cpp @@ -73,6 +73,7 @@ ConnectionSendThread::ConnectionSendThread(unsigned int max_packet_size, m_timeout(timeout), m_max_data_packets_per_iteration(g_settings->getU16("max_packets_per_iteration")) { + SANITY_CHECK(m_max_data_packets_per_iteration > 1); } void *ConnectionSendThread::run() @@ -107,8 +108,13 @@ void *ConnectionSendThread::run() curtime = porting::getTimeMs(); float dtime = CALC_DTIME(lasttime, curtime); - /* first do all the reliable stuff */ + /* first resend timed-out packets */ runTimeouts(dtime); + if (m_iteration_packets_avaialble == 0) { + LOG(warningstream << m_connection->getDesc() + << " Packet quota used up after re-sending packets, " + << "max=" << m_max_data_packets_per_iteration << std::endl); + } /* translate commands to packets */ ConnectionCommand c = m_connection->m_command_queue.pop_frontNoEx(0); @@ -121,7 +127,7 @@ void *ConnectionSendThread::run() c = m_connection->m_command_queue.pop_frontNoEx(0); } - /* send non reliable packets */ + /* send queued packets */ sendPackets(dtime); END_DEBUG_EXCEPTION_HANDLER @@ -644,6 +650,9 @@ void ConnectionSendThread::sendPackets(float dtime) std::list pendingDisconnect; std::map pending_unreliable; + const unsigned int peer_packet_quota = m_iteration_packets_avaialble + / MYMAX(peerIds.size(), 1); + for (session_t peerId : peerIds) { PeerHelper peer = m_connection->getPeerNoEx(peerId); //peer may have been removed @@ -653,8 +662,7 @@ void ConnectionSendThread::sendPackets(float dtime) << std::endl); continue; } - peer->m_increment_packets_remaining = - m_iteration_packets_avaialble / m_connection->m_peers.size(); + peer->m_increment_packets_remaining = peer_packet_quota; UDPPeer *udpPeer = dynamic_cast(&peer); @@ -751,23 +759,30 @@ void ConnectionSendThread::sendPackets(float dtime) } /* send acks immediately */ - if (packet.ack) { + if (packet.ack || peer->m_increment_packets_remaining > 0 || stopRequested()) { rawSendAsPacket(packet.peer_id, packet.channelnum, packet.data, packet.reliable); - peer->m_increment_packets_remaining = - MYMIN(0, peer->m_increment_packets_remaining--); - } else if ( - (peer->m_increment_packets_remaining > 0) || - (stopRequested())) { - rawSendAsPacket(packet.peer_id, packet.channelnum, - packet.data, packet.reliable); - peer->m_increment_packets_remaining--; + if (peer->m_increment_packets_remaining > 0) + peer->m_increment_packets_remaining--; } else { m_outgoing_queue.push(packet); pending_unreliable[packet.peer_id] = true; } } + if (peer_packet_quota > 0) { + for (session_t peerId : peerIds) { + PeerHelper peer = m_connection->getPeerNoEx(peerId); + if (!peer) + continue; + if (peer->m_increment_packets_remaining == 0) { + LOG(warningstream << m_connection->getDesc() + << " Packet quota used up for peer_id=" << peerId + << ", was " << peer_packet_quota << " pkts" << std::endl); + } + } + } + for (session_t peerId : pendingDisconnect) { if (!pending_unreliable[peerId]) { m_connection->deletePeer(peerId, false); diff --git a/src/network/serveropcodes.cpp b/src/network/serveropcodes.cpp index cca2e56ea..6ee4ff256 100644 --- a/src/network/serveropcodes.cpp +++ b/src/network/serveropcodes.cpp @@ -111,6 +111,16 @@ const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES] = const static ClientCommandFactory null_command_factory = { "TOCLIENT_NULL", 0, false }; +/* + Channels used for Server -> Client communication + 2: Bulk data (mapblocks, media, ...) + 1: HUD packets + 0: everything else + + Packet order is only guaranteed inside a channel, so packets that operate on + the same objects are *required* to be in the same channel. +*/ + const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] = { null_command_factory, // 0x00 @@ -163,7 +173,7 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_CHAT_MESSAGE", 0, true }, // 0x2F null_command_factory, // 0x30 { "TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD", 0, true }, // 0x31 - { "TOCLIENT_ACTIVE_OBJECT_MESSAGES", 0, true }, // 0x32 Special packet, sent by 0 (rel) and 1 (unrel) channel + { "TOCLIENT_ACTIVE_OBJECT_MESSAGES", 0, true }, // 0x32 (may be sent as unrel over channel 1 too) { "TOCLIENT_HP", 0, true }, // 0x33 { "TOCLIENT_MOVE_PLAYER", 0, true }, // 0x34 { "TOCLIENT_ACCESS_DENIED_LEGACY", 0, true }, // 0x35 @@ -176,7 +186,7 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_ANNOUNCE_MEDIA", 0, true }, // 0x3C { "TOCLIENT_ITEMDEF", 0, true }, // 0x3D null_command_factory, // 0x3E - { "TOCLIENT_PLAY_SOUND", 0, true }, // 0x3f + { "TOCLIENT_PLAY_SOUND", 0, true }, // 0x3f (may be sent as unrel too) { "TOCLIENT_STOP_SOUND", 0, true }, // 0x40 { "TOCLIENT_PRIVILEGES", 0, true }, // 0x41 { "TOCLIENT_INVENTORY_FORMSPEC", 0, true }, // 0x42 @@ -188,9 +198,9 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] = null_command_factory, // 0x48 { "TOCLIENT_HUDADD", 1, true }, // 0x49 { "TOCLIENT_HUDRM", 1, true }, // 0x4a - { "TOCLIENT_HUDCHANGE", 0, true }, // 0x4b - { "TOCLIENT_HUD_SET_FLAGS", 0, true }, // 0x4c - { "TOCLIENT_HUD_SET_PARAM", 0, true }, // 0x4d + { "TOCLIENT_HUDCHANGE", 1, true }, // 0x4b + { "TOCLIENT_HUD_SET_FLAGS", 1, true }, // 0x4c + { "TOCLIENT_HUD_SET_PARAM", 1, true }, // 0x4d { "TOCLIENT_BREATH", 0, true }, // 0x4e { "TOCLIENT_SET_SKY", 0, true }, // 0x4f { "TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO", 0, true }, // 0x50 -- cgit v1.2.3 From de73f989eb1397b1103236031fd91309b294583c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 8 Apr 2020 20:13:23 +0200 Subject: Overall improvements to log messages (#9598) Hide some unnecessarily verbose ones behind --trace or disable them entirely. Remove duplicate ones. Improve their contents in some places. --- src/client/client.cpp | 40 ++++++++++++++++++++++++++----------- src/client/client.h | 19 ++++-------------- src/client/content_cao.cpp | 6 +++--- src/client/fontengine.cpp | 2 +- src/client/game.cpp | 3 +-- src/client/renderingengine.cpp | 20 ++----------------- src/client/sound_openal.cpp | 16 ++++++++------- src/client/tile.cpp | 9 +++++---- src/content/subgames.cpp | 2 +- src/content_sao.cpp | 12 +++++------ src/craftdef.cpp | 4 ++-- src/emerge.cpp | 1 - src/itemdef.cpp | 6 +++--- src/main.cpp | 8 +------- src/map.cpp | 8 ++++---- src/network/clientpackethandler.cpp | 8 ++++---- src/network/connectionthreads.cpp | 3 +-- src/nodedef.cpp | 2 +- src/script/cpp_api/s_env.cpp | 2 +- src/script/lua_api/l_item.cpp | 1 - src/server.cpp | 17 ++++++++-------- src/server/mods.cpp | 9 +++------ 22 files changed, 87 insertions(+), 111 deletions(-) (limited to 'src/network') diff --git a/src/client/client.cpp b/src/client/client.cpp index e15391dde..c9cd24cb3 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -60,6 +60,20 @@ with this program; if not, write to the Free Software Foundation, Inc., extern gui::IGUIEnvironment* guienv; +/* + Utility classes +*/ + +void PacketCounter::print(std::ostream &o) const +{ + for (const auto &it : m_packets) { + auto name = it.first >= TOCLIENT_NUM_MSG_TYPES ? "?" + : toClientCommandTable[it.first].name; + o << "cmd " << it.first << " (" << name << ") count " + << it.second << std::endl; + } +} + /* Client */ @@ -336,12 +350,12 @@ void Client::step(float dtime) { float &counter = m_packetcounter_timer; counter -= dtime; - if(counter <= 0.0) + if(counter <= 0.0f) { - counter = 20.0; + counter = 30.0f; infostream << "Client packetcounter (" << m_packetcounter_timer - << "):"<getFloat("server_map_save_interval"); + int n = 0; for (std::unordered_map::const_iterator it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) { if (it->second->isModified()) { it->second->save(getModStoragePath()); + n++; } } + if (n > 0) + infostream << "Saved " << n << " modified mod storages." << std::endl; } // Write server map @@ -653,8 +670,8 @@ bool Client::loadMedia(const std::string &data, const std::string &filename) }; name = removeStringEnd(filename, image_ext); if (!name.empty()) { - verbosestream<<"Client: Attempting to load image " - <<"file \""<loadSoundData(name, data); - return true; + TRACESTREAM(<< "Client: Attempting to load sound " + << "file \"" << filename << "\"" << std::endl); + return m_sound->loadSoundData(name, data); } const char *model_ext[] = { @@ -714,8 +730,8 @@ bool Client::loadMedia(const std::string &data, const std::string &filename) }; name = removeStringEnd(filename, translate_ext); if (!name.empty()) { - verbosestream << "Client: Loading translation: " - << "\"" << filename << "\"" << std::endl; + TRACESTREAM(<< "Client: Loading translation: " + << "\"" << filename << "\"" << std::endl); g_translations->loadTranslation(data); return true; } diff --git a/src/client/client.h b/src/client/client.h index 1291b944c..eea78d456 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -82,30 +82,19 @@ public: void add(u16 command) { - std::map::iterator n = m_packets.find(command); - if(n == m_packets.end()) - { + auto n = m_packets.find(command); + if (n == m_packets.end()) m_packets[command] = 1; - } else - { n->second++; - } } void clear() { - for (auto &m_packet : m_packets) { - m_packet.second = 0; - } + m_packets.clear(); } - void print(std::ostream &o) - { - for (const auto &m_packet : m_packets) { - o << "cmd "<< m_packet.first <<" count "<< m_packet.second << std::endl; - } - } + void print(std::ostream &o) const; private: // command, count diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index d148df522..8509eccb5 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -576,9 +576,10 @@ void GenericCAO::addToScene(ITextureSource *tsrc) m_visuals_expired = false; - if (!m_prop.is_visible) { + if (!m_prop.is_visible) return; - } + + infostream << "GenericCAO::addToScene(): " << m_prop.visual << std::endl; if (m_enable_shaders) { IShaderSource *shader_source = m_client->getShaderSource(); @@ -593,7 +594,6 @@ void GenericCAO::addToScene(ITextureSource *tsrc) } auto grabMatrixNode = [this] { - infostream << "GenericCAO::addToScene(): " << m_prop.visual << std::endl; m_matrixnode = RenderingEngine::get_scene_manager()-> addDummyTransformationSceneNode(); m_matrixnode->grab(); diff --git a/src/client/fontengine.cpp b/src/client/fontengine.cpp index 2b5841cd8..61d52cc2f 100644 --- a/src/client/fontengine.cpp +++ b/src/client/fontengine.cpp @@ -239,7 +239,7 @@ void FontEngine::updateSkin() FATAL_ERROR_IF(font == NULL, "Could not create/get font"); u32 text_height = font->getDimension(L"Hello, world!").Height; - infostream << "text_height=" << text_height << std::endl; + infostream << "FontEngine: measured text_height=" << text_height << std::endl; } /******************************************************************************/ diff --git a/src/client/game.cpp b/src/client/game.cpp index 0201ded69..437cc7871 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2010,7 +2010,6 @@ void Game::processItemSelection(u16 *new_playeritem) for (u16 i = 0; i <= max_item; i++) { if (wasKeyDown((GameKeyType) (KeyType::SLOT_1 + i))) { *new_playeritem = i; - infostream << "Selected item: " << new_playeritem << std::endl; break; } } @@ -2039,7 +2038,7 @@ void Game::openInventory() if (!player || !player->getCAO()) return; - infostream << "the_game: " << "Launching inventory" << std::endl; + infostream << "Game: Launching inventory" << std::endl; PlayerInventoryFormSource *fs_src = new PlayerInventoryFormSource(client); diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index 6e6509eeb..8b7bbf328 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -226,27 +226,17 @@ bool RenderingEngine::setupTopLevelWindow(const std::string &name) { // FIXME: It would make more sense for there to be a switch of some // sort here that would call the correct toplevel setup methods for - // the environment Minetest is running in but for now not deviating - // from the original pattern. + // the environment Minetest is running in. /* Setting Xorg properties for the top level window */ setupTopLevelXorgWindow(name); - /* Done with Xorg properties */ /* Setting general properties for the top level window */ verbosestream << "Client: Configuring general top level" << " window properties" << std::endl; - bool result = setWindowIcon(); - verbosestream << "Client: Finished configuring general top level" - << " window properties" - << std::endl; - /* Done with general properties */ - - // FIXME: setWindowIcon returns a bool result but it is unused. - // For now continue to return this result. return result; } @@ -262,7 +252,7 @@ void RenderingEngine::setupTopLevelXorgWindow(const std::string &name) return; } - verbosestream << "Client: Configuring Xorg specific top level" + verbosestream << "Client: Configuring X11-specific top level" << " window properties" << std::endl; @@ -309,8 +299,6 @@ void RenderingEngine::setupTopLevelXorgWindow(const std::string &name) Atom NET_WM_PID = XInternAtom(x11_dpl, "_NET_WM_PID", false); pid_t pid = getpid(); - infostream << "Client: PID is '" << static_cast(pid) << "'" - << std::endl; XChangeProperty(x11_dpl, x11_win, NET_WM_PID, XA_CARDINAL, 32, PropModeReplace, @@ -327,10 +315,6 @@ void RenderingEngine::setupTopLevelXorgWindow(const std::string &name) XChangeProperty (x11_dpl, x11_win, WM_CLIENT_LEADER, XA_WINDOW, 32, PropModeReplace, reinterpret_cast(&x11_win), 1); - - verbosestream << "Client: Finished configuring Xorg specific top level" - << " window properties" - << std::endl; #endif } diff --git a/src/client/sound_openal.cpp b/src/client/sound_openal.cpp index 8e696f302..d0f935a7a 100644 --- a/src/client/sound_openal.cpp +++ b/src/client/sound_openal.cpp @@ -165,8 +165,8 @@ SoundBuffer *load_opened_ogg_file(OggVorbis_File *oggFile, << "preparing sound buffer" << std::endl; } - infostream << "Audio file " - << filename_for_logging << " loaded" << std::endl; + //infostream << "Audio file " + // << filename_for_logging << " loaded" << std::endl; // Clean up! ov_clear(oggFile); @@ -498,9 +498,11 @@ public: // Remove stopped sounds void maintain() { - verbosestream<<"OpenALSoundManager::maintain(): " - < del_list; for (const auto &sp : m_sounds_playing) { int id = sp.first; @@ -530,7 +532,7 @@ public: SoundBuffer *buf = load_ogg_from_file(filepath); if (buf) addBuffer(name, buf); - return false; + return !!buf; } bool loadSoundData(const std::string &name, @@ -539,7 +541,7 @@ public: SoundBuffer *buf = load_ogg_from_buffer(filedata, name); if (buf) addBuffer(name, buf); - return false; + return !!buf; } void updateListener(const v3f &pos, const v3f &vel, const v3f &at, const v3f &up) diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 3189ab28c..0fa7a4ae2 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -471,8 +471,8 @@ TextureSource::~TextureSource() driver->removeTexture(t); } - infostream << "~TextureSource() "<< textures_before << "/" - << driver->getTextureCount() << std::endl; + infostream << "~TextureSource() before cleanup: "<< textures_before + << " after: " << driver->getTextureCount() << std::endl; } u32 TextureSource::getTextureId(const std::string &name) @@ -763,6 +763,9 @@ void TextureSource::rebuildImagesAndTextures() video::IVideoDriver *driver = RenderingEngine::get_video_driver(); sanity_check(driver); + infostream << "TextureSource: recreating " << m_textureinfo_cache.size() + << " textures" << std::endl; + // Recreate textures for (TextureInfo &ti : m_textureinfo_cache) { video::IImage *img = generateImage(ti.name); @@ -1270,8 +1273,6 @@ bool TextureSource::generateImagePart(std::string part_of_name, video::IImage *img = generateImage(filename); if (img) { core::dimension2d dim = img->getDimension(); - infostream<<"Size "< pos_base(x, y); video::IImage *img2 = driver->createImage(video::ECF_A8R8G8B8, dim); diff --git a/src/content/subgames.cpp b/src/content/subgames.cpp index bf947cf85..170f54e20 100644 --- a/src/content/subgames.cpp +++ b/src/content/subgames.cpp @@ -253,7 +253,7 @@ std::vector getAvailableWorlds() worldspaths.insert(porting::path_user + DIR_DELIM + "worlds"); infostream << "Searching worlds..." << std::endl; for (const std::string &worldspath : worldspaths) { - infostream << " In " << worldspath << ": " << std::endl; + infostream << " In " << worldspath << ": "; std::vector dirvector = fs::GetDirListing(worldspath); for (const fs::DirListNode &dln : dirvector) { if (!dln.dir) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 75c3eaf37..680bf372a 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -711,13 +711,11 @@ float LuaEntitySAO::getMinimumSavedMovement() std::string LuaEntitySAO::getDescription() { - std::ostringstream os(std::ios::binary); - os<<"LuaEntitySAO at ("; - os<<(m_base_position.X/BS)<<","; - os<<(m_base_position.Y/BS)<<","; - os<<(m_base_position.Z/BS); - os<<")"; - return os.str(); + std::ostringstream oss; + oss << "LuaEntitySAO \"" << m_init_name << "\" "; + auto pos = floatToInt(m_base_position, BS); + oss << "at " << PP(pos); + return oss.str(); } void LuaEntitySAO::setHP(s32 hp, const PlayerHPChangeReason &reason) diff --git a/src/craftdef.cpp b/src/craftdef.cpp index 0181ceb60..210605198 100644 --- a/src/craftdef.cpp +++ b/src/craftdef.cpp @@ -1066,8 +1066,8 @@ public: } virtual void registerCraft(CraftDefinition *def, IGameDef *gamedef) { - verbosestream << "registerCraft: registering craft definition: " - << def->dump() << std::endl; + TRACESTREAM(<< "registerCraft: registering craft definition: " + << def->dump() << std::endl); m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].push_back(def); CraftInput input; diff --git a/src/emerge.cpp b/src/emerge.cpp index fc1da4ee7..4835c3fad 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -136,7 +136,6 @@ EmergeManager::EmergeManager(Server *server) nthreads = Thread::getNumberOfProcessors() - 2; if (nthreads < 1) nthreads = 1; - verbosestream << "Using " << nthreads << " emerge threads." << std::endl; m_qlimit_total = g_settings->getU16("emergequeue_limit_total"); if (!g_settings->getU16NoEx("emergequeue_limit_diskonly", m_qlimit_diskonly)) diff --git a/src/itemdef.cpp b/src/itemdef.cpp index 0d0afeb2b..ba7bd6a0b 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -463,7 +463,7 @@ public: } virtual void registerItem(const ItemDefinition &def) { - verbosestream<<"ItemDefManager: registering \""< "< " << convert_to << std::endl); m_aliases[name] = convert_to; } } diff --git a/src/main.cpp b/src/main.cpp index 1993f7c24..8df2fe7d3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -207,9 +207,6 @@ int main(int argc, char *argv[]) sanity_check(!game_params.world_path.empty()); - infostream << "Using commanded world path [" - << game_params.world_path << "]" << std::endl; - if (game_params.is_dedicated_server) return run_dedicated_server(game_params, cmd_args) ? 0 : 1; @@ -686,8 +683,6 @@ static bool auto_select_world(GameParams *game_params) // No world was specified; try to select it automatically // Get information about available worlds - verbosestream << _("Determining world path") << std::endl; - std::vector worldspecs = getAvailableWorlds(); std::string world_path; @@ -708,7 +703,7 @@ static bool auto_select_world(GameParams *game_params) // This is the ultimate default world path world_path = porting::path_user + DIR_DELIM + "worlds" + DIR_DELIM + "world"; - infostream << "Creating default world at [" + infostream << "Using default world at [" << world_path << "]" << std::endl; } @@ -770,7 +765,6 @@ static bool determine_subgame(GameParams *game_params) assert(game_params->world_path != ""); // Pre-condition - verbosestream << _("Determining gameid/gamespec") << std::endl; // If world doesn't exist if (!game_params->world_path.empty() && !getWorldExists(game_params->world_path)) { diff --git a/src/map.cpp b/src/map.cpp index 40aba067e..eb69955ee 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1827,10 +1827,10 @@ void ServerMap::save(ModifiedState save_level) */ if(save_level == MOD_STATE_CLEAN || block_count != 0) { - infostream<<"ServerMap: Written: " - <id << " has timed out." - << " (source=peer->timeout_counter)" << std::endl; // Add peer to the list timeouted_peers.push_back(peer->id); @@ -292,7 +291,7 @@ void ConnectionSendThread::runTimeouts(float dtime) // Remove timed out peers for (u16 timeouted_peer : timeouted_peers) { - LOG(derr_con << m_connection->getDesc() + LOG(dout_con << m_connection->getDesc() << "RunTimeouts(): Removing peer " << timeouted_peer << std::endl); m_connection->deletePeer(timeouted_peer, true); } diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 977a4533d..b6eca9497 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -1458,7 +1458,7 @@ void NodeDefManager::deSerialize(std::istream &is) m_content_features.resize((u32)(i) + 1); m_content_features[i] = f; addNameIdMapping(i, f.name); - verbosestream << "deserialized " << f.name << std::endl; + TRACESTREAM(<< "NodeDef: deserialized " << f.name << std::endl); getNodeBoxUnion(f.selection_box, f, &m_selection_box_union); fixSelectionBoxIntUnion(); diff --git a/src/script/cpp_api/s_env.cpp b/src/script/cpp_api/s_env.cpp index ab3b5fe46..8da5debaa 100644 --- a/src/script/cpp_api/s_env.cpp +++ b/src/script/cpp_api/s_env.cpp @@ -86,7 +86,7 @@ void ScriptApiEnv::player_event(ServerActiveObject *player, const std::string &t void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env) { SCRIPTAPI_PRECHECKHEADER - verbosestream << "scriptapi_add_environment" << std::endl; + verbosestream << "ScriptApiEnv: Environment initialized" << std::endl; setEnv(env); /* diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index 0c174feca..ff77cba32 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -522,7 +522,6 @@ int ModApiItemMod::l_register_item_raw(lua_State *L) lua_getfield(L, table, "name"); if(lua_isstring(L, -1)){ name = readParam(L, -1); - verbosestream<<"register_item_raw: "<getFloat("server_map_save_interval"); + int n = 0; for (std::unordered_map::const_iterator it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) { if (it->second->isModified()) { it->second->save(getModStoragePath()); + n++; } } + if (n > 0) + infostream << "Saved " << n << " modified mod storages." << std::endl; } } @@ -809,7 +812,6 @@ void Server::AsyncRunStep(bool initial_step) disable_single_change_sending ? 5 : 30); break; case MEET_BLOCK_NODE_METADATA_CHANGED: { - verbosestream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl; prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1); if (!event->is_private_change) { // Don't send the change yet. Collect them to eliminate dupes. @@ -825,7 +827,6 @@ void Server::AsyncRunStep(bool initial_step) break; } case MEET_OTHER: - infostream << "Server: MEET_OTHER" << std::endl; prof.add("MEET_OTHER", 1); for (const v3s16 &modified_block : event->modified_blocks) { m_clients.markBlockposAsNotSent(modified_block); @@ -2535,9 +2536,6 @@ void Server::fillMediaCache() void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code) { - verbosestream << "Server: Announcing files to id(" << peer_id << ")" - << std::endl; - // Make packet NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id); @@ -2560,6 +2558,9 @@ void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_co pkt << g_settings->get("remote_media"); Send(&pkt); + + verbosestream << "Server: Announcing files to id(" << peer_id + << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl; } struct SendableMedia @@ -2938,10 +2939,8 @@ void Server::UpdateCrafting(RemotePlayer *player) if (!clist || clist->getSize() == 0) return; - if (!clist->checkModified()) { - verbosestream << "Skip Server::UpdateCrafting(): list unmodified" << std::endl; + if (!clist->checkModified()) return; - } // Get a preview for crafting ItemStack preview; diff --git a/src/server/mods.cpp b/src/server/mods.cpp index c5616dcd6..c8d8a28e2 100644 --- a/src/server/mods.cpp +++ b/src/server/mods.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "scripting_server.h" #include "content/subgames.h" +#include "porting.h" /** * Manage server mods @@ -66,14 +67,10 @@ void ServerModManager::loadMods(ServerScripting *script) "Only characters [a-z0-9_] are allowed."); } std::string script_path = mod.path + DIR_DELIM + "init.lua"; - infostream << " [" << padStringRight(mod.name, 12) << "] [\"" - << script_path << "\"]" << std::endl; - auto t = std::chrono::steady_clock::now(); + auto t = porting::getTimeMs(); script->loadMod(script_path, mod.name); infostream << "Mod \"" << mod.name << "\" loaded after " - << std::chrono::duration_cast( - std::chrono::steady_clock::now() - t).count() * 0.001f - << " seconds" << std::endl; + << (porting::getTimeMs() - t) << " ms" << std::endl; } // Run a callback when mods are loaded -- cgit v1.2.3 From f648fb76aef96a1da608c64346fc65d4dd44caa8 Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Fri, 10 Apr 2020 19:49:20 +0200 Subject: Drop genericobject.{cpp,h} (#9629) * Drop genericobject.{cpp,h} This file is not for generic object but for ActiveObject message passing. Put ownership of the various commands to the right objects and cleanup the related code. * Protect ServerActiveObject::m_messages_out * typo fix --- build/android/jni/Android.mk | 1 - src/CMakeLists.txt | 1 - src/activeobject.h | 16 ++ src/client/clientenvironment.cpp | 2 +- src/client/content_cao.cpp | 38 +++-- src/client/content_cao.h | 1 + src/content_sao.cpp | 266 ++++++++++++++++++++++++--------- src/content_sao.h | 19 +++ src/genericobject.cpp | 207 ------------------------- src/genericobject.h | 87 ----------- src/network/networkprotocol.h | 8 +- src/server.cpp | 7 +- src/serverenvironment.cpp | 5 +- src/serverobject.cpp | 31 ++++ src/serverobject.h | 16 +- util/travis/clang-format-whitelist.txt | 2 - 16 files changed, 300 insertions(+), 407 deletions(-) delete mode 100644 src/genericobject.cpp delete mode 100644 src/genericobject.h (limited to 'src/network') diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index 72b0daab6..a2f32440a 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -176,7 +176,6 @@ LOCAL_SRC_FILES := \ jni/src/environment.cpp \ jni/src/face_position_cache.cpp \ jni/src/filesys.cpp \ - jni/src/genericobject.cpp \ jni/src/gettext.cpp \ jni/src/gui/guiAnimatedImage.cpp \ jni/src/gui/guiBackgroundImage.cpp \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d579bb965..4b1d6d647 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -384,7 +384,6 @@ set(common_SRCS environment.cpp face_position_cache.cpp filesys.cpp - genericobject.cpp gettext.cpp httpfetch.cpp hud.cpp diff --git a/src/activeobject.h b/src/activeobject.h index 4a2de92cd..c83243f86 100644 --- a/src/activeobject.h +++ b/src/activeobject.h @@ -55,6 +55,22 @@ struct ActiveObjectMessage std::string datastring; }; +enum ActiveObjectCommand { + AO_CMD_SET_PROPERTIES, + AO_CMD_UPDATE_POSITION, + AO_CMD_SET_TEXTURE_MOD, + AO_CMD_SET_SPRITE, + AO_CMD_PUNCHED, + AO_CMD_UPDATE_ARMOR_GROUPS, + AO_CMD_SET_ANIMATION, + AO_CMD_SET_BONE_POSITION, + AO_CMD_ATTACH_TO, + AO_CMD_SET_PHYSICS_OVERRIDE, + AO_CMD_UPDATE_NAMETAG_ATTRIBUTES, + AO_CMD_SPAWN_INFANT, + AO_CMD_SET_ANIMATION_SPEED +}; + /* Parent class for ServerActiveObject and ClientActiveObject */ diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp index 52d133781..6840f2db3 100644 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@ -450,7 +450,7 @@ void ClientEnvironment::addActiveObject(u16 id, u8 type, // Object initialized: if ((obj = getActiveObject(new_id))) { // Final step is to update all children which are already known - // Data provided by GENERIC_CMD_SPAWN_INFANT + // Data provided by AO_CMD_SPAWN_INFANT const auto &children = obj->getAttachmentChildIds(); for (auto c_id : children) { if (auto *o = getActiveObject(c_id)) diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 8509eccb5..798899f9a 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -304,7 +304,6 @@ void TestCAO::processMessage(const std::string &data) GenericCAO */ -#include "genericobject.h" #include "clientobject.h" GenericCAO::GenericCAO(Client *client, ClientEnvironment *env): @@ -1421,14 +1420,23 @@ void GenericCAO::updateAttachments() } } +void GenericCAO::readAOMessageProperties(std::istream &is) +{ + // Reset object properties first + m_prop = ObjectProperties(); + + // Then read the whole new stream + m_prop.deSerialize(is); +} + void GenericCAO::processMessage(const std::string &data) { //infostream<<"GenericCAO: Got message"<physics_override_sneak_glitch = sneak_glitch; player->physics_override_new_move = new_move; } - } else if (cmd == GENERIC_CMD_SET_ANIMATION) { + } else if (cmd == AO_CMD_SET_ANIMATION) { // TODO: change frames send as v2s32 value v2f range = readV2F32(is); if (!m_is_local_player) { @@ -1565,17 +1573,17 @@ void GenericCAO::processMessage(const std::string &data) updateAnimation(); } } - } else if (cmd == GENERIC_CMD_SET_ANIMATION_SPEED) { + } else if (cmd == AO_CMD_SET_ANIMATION_SPEED) { m_animation_speed = readF32(is); updateAnimationSpeed(); - } else if (cmd == GENERIC_CMD_SET_BONE_POSITION) { + } else if (cmd == AO_CMD_SET_BONE_POSITION) { std::string bone = deSerializeString(is); v3f position = readV3F32(is); v3f rotation = readV3F32(is); m_bone_position[bone] = core::vector2d(position, rotation); updateBonePosition(); - } else if (cmd == GENERIC_CMD_ATTACH_TO) { + } else if (cmd == AO_CMD_ATTACH_TO) { u16 parent_id = readS16(is); std::string bone = deSerializeString(is); v3f position = readV3F32(is); @@ -1586,7 +1594,7 @@ void GenericCAO::processMessage(const std::string &data) // localplayer itself can't be attached to localplayer if (!m_is_local_player) m_is_visible = !m_attached_to_local; - } else if (cmd == GENERIC_CMD_PUNCHED) { + } else if (cmd == AO_CMD_PUNCHED) { u16 result_hp = readU16(is); // Use this instead of the send damage to not interfere with prediction @@ -1624,7 +1632,7 @@ void GenericCAO::processMessage(const std::string &data) if (!m_is_player) clearChildAttachments(); } - } else if (cmd == GENERIC_CMD_UPDATE_ARMOR_GROUPS) { + } else if (cmd == AO_CMD_UPDATE_ARMOR_GROUPS) { m_armor_groups.clear(); int armor_groups_size = readU16(is); for(int i=0; inametag_pos = pos; } - } else if (cmd == GENERIC_CMD_SPAWN_INFANT) { + } else if (cmd == AO_CMD_SPAWN_INFANT) { u16 child_id = readU16(is); u8 type = readU8(is); // maybe this will be useful later (void)type; diff --git a/src/client/content_cao.h b/src/client/content_cao.h index 7c29cbf17..c53b81433 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -68,6 +68,7 @@ struct SmoothTranslatorWrappedv3f : SmoothTranslator class GenericCAO : public ClientActiveObject { private: + void readAOMessageProperties(std::istream &is); // Only set at initialization std::string m_name = ""; bool m_is_player = false; diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 680bf372a..be7674f52 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -27,7 +27,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "remoteplayer.h" #include "server.h" #include "scripting_server.h" -#include "genericobject.h" #include "settings.h" #include #include @@ -289,6 +288,120 @@ void UnitSAO::notifyObjectPropertiesModified() m_properties_sent = false; } +std::string UnitSAO::generateUpdateAttachmentCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_ATTACH_TO); + // parameters + writeS16(os, m_attachment_parent_id); + os << serializeString(m_attachment_bone); + writeV3F32(os, m_attachment_position); + writeV3F32(os, m_attachment_rotation); + return os.str(); +} + +std::string UnitSAO::generateUpdateBonePositionCommand(const std::string &bone, + const v3f &position, const v3f &rotation) +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_BONE_POSITION); + // parameters + os << serializeString(bone); + writeV3F32(os, position); + writeV3F32(os, rotation); + return os.str(); +} + + +std::string UnitSAO::generateUpdateAnimationSpeedCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_ANIMATION_SPEED); + // parameters + writeF32(os, m_animation_speed); + return os.str(); +} + +std::string UnitSAO::generateUpdateAnimationCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_ANIMATION); + // parameters + writeV2F32(os, m_animation_range); + writeF32(os, m_animation_speed); + writeF32(os, m_animation_blend); + // these are sent inverted so we get true when the server sends nothing + writeU8(os, !m_animation_loop); + return os.str(); +} + + +std::string UnitSAO::generateUpdateArmorGroupsCommand() const +{ + std::ostringstream os(std::ios::binary); + writeU8(os, AO_CMD_UPDATE_ARMOR_GROUPS); + writeU16(os, m_armor_groups.size()); + for (const auto &armor_group : m_armor_groups) { + os<>::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){ - std::string str = gob_cmd_update_bone_position((*ii).first, + std::string str = generateUpdateBonePositionCommand((*ii).first, (*ii).second.X, (*ii).second.Y); // create message and add to list ActiveObjectMessage aom(getId(), true, str); @@ -538,7 +647,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) if (!m_attachment_sent) { m_attachment_sent = true; - std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation); + std::string str = generateUpdateAttachmentCommand(); // create message and add to list ActiveObjectMessage aom(getId(), true, str); m_messages_out.push(aom); @@ -560,16 +669,14 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) std::ostringstream msg_os(std::ios::binary); msg_os << serializeLongString(getPropertyPacket()); // message 1 - msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2 - msg_os << serializeLongString(gob_cmd_update_animation( - m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3 + msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 + msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 for (std::unordered_map>::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first, + msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size } - msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, - m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4 + msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 int message_count = 4 + m_bone_position.size(); for (std::unordered_set::const_iterator ii = m_attachment_child_ids.begin(); (ii != m_attachment_child_ids.end()); ++ii) { @@ -577,12 +684,11 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) message_count++; // TODO after a protocol bump: only send the object initialization data // to older clients (superfluous since this message exists) - msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(), - obj->getClientInitializationData(protocol_version))); + msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version)); } } - msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier)); + msg_os << serializeLongString(generateSetTextureModCommand()); message_count++; writeU8(os, message_count); @@ -655,10 +761,8 @@ u16 LuaEntitySAO::punch(v3f dir, setHP((s32)getHP() - result.damage, PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher)); - std::string str = gob_cmd_punched(getHP()); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + sendPunchCommand(); } } @@ -750,11 +854,9 @@ v3f LuaEntitySAO::getAcceleration() void LuaEntitySAO::setTextureMod(const std::string &mod) { - std::string str = gob_cmd_set_texture_mod(mod); m_current_texture_modifier = mod; // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, generateSetTextureModCommand()); } std::string LuaEntitySAO::getTextureMod() const @@ -762,18 +864,42 @@ std::string LuaEntitySAO::getTextureMod() const return m_current_texture_modifier; } + +std::string LuaEntitySAO::generateSetTextureModCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_TEXTURE_MOD); + // parameters + os << serializeString(m_current_texture_modifier); + return os.str(); +} + +std::string LuaEntitySAO::generateSetSpriteCommand(v2s16 p, u16 num_frames, + f32 framelength, bool select_horiz_by_yawpitch) +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_SPRITE); + // parameters + writeV2S16(os, p); + writeU16(os, num_frames); + writeF32(os, framelength); + writeU8(os, select_horiz_by_yawpitch); + return os.str(); +} + void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength, bool select_horiz_by_yawpitch) { - std::string str = gob_cmd_set_sprite( + std::string str = generateSetSpriteCommand( p, num_frames, framelength, select_horiz_by_yawpitch ); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, str); } std::string LuaEntitySAO::getName() @@ -783,7 +909,7 @@ std::string LuaEntitySAO::getName() std::string LuaEntitySAO::getPropertyPacket() { - return gob_cmd_set_properties(m_prop); + return generateSetPropertiesCommand(m_prop); } void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end) @@ -802,7 +928,7 @@ void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end) float update_interval = m_env->getSendRecommendedInterval(); - std::string str = gob_cmd_update_position( + std::string str = generateUpdatePositionCommand( m_base_position, m_velocity, m_acceleration, @@ -812,8 +938,7 @@ void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end) update_interval ); // create message and add to list - ActiveObjectMessage aom(getId(), false, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), false, str); } bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const @@ -949,28 +1074,23 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version) std::ostringstream msg_os(std::ios::binary); msg_os << serializeLongString(getPropertyPacket()); // message 1 - msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2 - msg_os << serializeLongString(gob_cmd_update_animation( - m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3 + msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 + msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 for (std::unordered_map>::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first, + msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size } - msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, - m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4 - msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed, - m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak, - m_physics_override_sneak_glitch, m_physics_override_new_move)); // 5 - // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only. - msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6 + msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 + msg_os << serializeLongString(generateUpdatePhysicsOverrideCommand()); // 5 + // (AO_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only. + msg_os << serializeLongString(generateUpdateNametagAttributesCommand(m_prop.nametag_color)); // 6 int message_count = 6 + m_bone_position.size(); for (std::unordered_set::const_iterator ii = m_attachment_child_ids.begin(); ii != m_attachment_child_ids.end(); ++ii) { if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { message_count++; - msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(), - obj->getClientInitializationData(protocol_version))); + msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version)); } } @@ -1116,7 +1236,7 @@ void PlayerSAO::step(float dtime, bool send_recommended) else pos = m_base_position; - std::string str = gob_cmd_update_position( + std::string str = generateUpdatePositionCommand( pos, v3f(0.0f, 0.0f, 0.0f), v3f(0.0f, 0.0f, 0.0f), @@ -1126,61 +1246,63 @@ void PlayerSAO::step(float dtime, bool send_recommended) update_interval ); // create message and add to list - ActiveObjectMessage aom(getId(), false, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), false, str); } if (!m_armor_groups_sent) { m_armor_groups_sent = true; - std::string str = gob_cmd_update_armor_groups( - m_armor_groups); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand()); } if (!m_physics_override_sent) { m_physics_override_sent = true; - std::string str = gob_cmd_update_physics_override(m_physics_override_speed, - m_physics_override_jump, m_physics_override_gravity, - m_physics_override_sneak, m_physics_override_sneak_glitch, - m_physics_override_new_move); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, generateUpdatePhysicsOverrideCommand()); } if (!m_animation_sent) { m_animation_sent = true; - std::string str = gob_cmd_update_animation( - m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, generateUpdateAnimationCommand()); } if (!m_bone_position_sent) { m_bone_position_sent = true; for (std::unordered_map>::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - std::string str = gob_cmd_update_bone_position((*ii).first, + std::string str = generateUpdateBonePositionCommand((*ii).first, (*ii).second.X, (*ii).second.Y); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + m_messages_out.emplace(getId(), true, str); } } if (!m_attachment_sent) { m_attachment_sent = true; - std::string str = gob_cmd_update_attachment(m_attachment_parent_id, - m_attachment_bone, m_attachment_position, m_attachment_rotation); + std::string str = generateUpdateAttachmentCommand(); // create message and add to list ActiveObjectMessage aom(getId(), true, str); m_messages_out.push(aom); } } +std::string PlayerSAO::generateUpdatePhysicsOverrideCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_PHYSICS_OVERRIDE); + // parameters + writeF32(os, m_physics_override_speed); + writeF32(os, m_physics_override_jump); + writeF32(os, m_physics_override_gravity); + // these are sent inverted so we get true when the server sends nothing + writeU8(os, !m_physics_override_sneak); + writeU8(os, !m_physics_override_sneak_glitch); + writeU8(os, !m_physics_override_new_move); + return os.str(); +} + void PlayerSAO::setBasePosition(const v3f &position) { if (m_player && position != m_base_position) @@ -1284,10 +1406,8 @@ u16 PlayerSAO::punch(v3f dir, // No effect if PvP disabled or if immortal if (isImmortal() || !g_settings->getBool("enable_pvp")) { if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - std::string str = gob_cmd_punched(getHP()); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + sendPunchCommand(); return 0; } } @@ -1307,10 +1427,8 @@ u16 PlayerSAO::punch(v3f dir, PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher)); } else { // override client prediction if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - std::string str = gob_cmd_punched(getHP()); // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + sendPunchCommand(); } } @@ -1408,7 +1526,7 @@ void PlayerSAO::unlinkPlayerSessionAndSave() std::string PlayerSAO::getPropertyPacket() { m_prop.is_visible = (true); - return gob_cmd_set_properties(m_prop); + return generateSetPropertiesCommand(m_prop); } void PlayerSAO::setMaxSpeedOverride(const v3f &vel) diff --git a/src/content_sao.h b/src/content_sao.h index e9047daf0..e0304299a 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -67,6 +67,19 @@ public: ServerActiveObject *getParent() const; ObjectProperties* accessObjectProperties(); void notifyObjectPropertiesModified(); + + std::string generateUpdateAttachmentCommand() const; + std::string generateUpdateAnimationSpeedCommand() const; + std::string generateUpdateAnimationCommand() const; + std::string generateUpdateArmorGroupsCommand() const; + static std::string generateUpdatePositionCommand(const v3f &position, const v3f &velocity, + const v3f &acceleration, const v3f &rotation, bool do_interpolate, + bool is_movement_end, f32 update_interval); + std::string generateSetPropertiesCommand(const ObjectProperties &prop) const; + void sendPunchCommand(); + static std::string generateUpdateBonePositionCommand(const std::string &bone, + const v3f &position, const v3f &rotation); + protected: u16 m_hp = 1; @@ -98,6 +111,8 @@ protected: private: void onAttach(int parent_id); void onDetach(int parent_id); + + std::string generatePunchCommand(u16 result_hp) const; }; /* @@ -155,6 +170,9 @@ public: private: std::string getPropertyPacket(); void sendPosition(bool do_interpolate, bool is_movement_end); + std::string generateSetTextureModCommand() const; + static std::string generateSetSpriteCommand(v2s16 p, u16 num_frames, f32 framelength, + bool select_horiz_by_yawpitch); std::string m_init_name; std::string m_init_state; @@ -350,6 +368,7 @@ public: private: std::string getPropertyPacket(); void unlinkPlayerSessionAndSave(); + std::string generateUpdatePhysicsOverrideCommand() const; RemotePlayer *m_player = nullptr; session_t m_peer_id = 0; diff --git a/src/genericobject.cpp b/src/genericobject.cpp deleted file mode 100644 index 49d16001f..000000000 --- a/src/genericobject.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/* -Minetest -Copyright (C) 2013 celeron55, Perttu Ahola - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#include "genericobject.h" -#include -#include "util/serialize.h" - -std::string gob_cmd_set_properties(const ObjectProperties &prop) -{ - std::ostringstream os(std::ios::binary); - writeU8(os, GENERIC_CMD_SET_PROPERTIES); - prop.serialize(os); - return os.str(); -} - -ObjectProperties gob_read_set_properties(std::istream &is) -{ - ObjectProperties prop; - prop.deSerialize(is); - return prop; -} - -std::string gob_cmd_update_position( - v3f position, - v3f velocity, - v3f acceleration, - v3f rotation, - bool do_interpolate, - bool is_movement_end, - f32 update_interval -){ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, GENERIC_CMD_UPDATE_POSITION); - // pos - writeV3F32(os, position); - // velocity - writeV3F32(os, velocity); - // acceleration - writeV3F32(os, acceleration); - // rotation - writeV3F32(os, rotation); - // do_interpolate - writeU8(os, do_interpolate); - // is_end_position (for interpolation) - writeU8(os, is_movement_end); - // update_interval (for interpolation) - writeF32(os, update_interval); - return os.str(); -} - -std::string gob_cmd_set_texture_mod(const std::string &mod) -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, GENERIC_CMD_SET_TEXTURE_MOD); - // parameters - os< - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#pragma once - -#include -#include "irrlichttypes_bloated.h" -#include -#include "itemgroup.h" - -enum GenericCMD { - GENERIC_CMD_SET_PROPERTIES, - GENERIC_CMD_UPDATE_POSITION, - GENERIC_CMD_SET_TEXTURE_MOD, - GENERIC_CMD_SET_SPRITE, - GENERIC_CMD_PUNCHED, - GENERIC_CMD_UPDATE_ARMOR_GROUPS, - GENERIC_CMD_SET_ANIMATION, - GENERIC_CMD_SET_BONE_POSITION, - GENERIC_CMD_ATTACH_TO, - GENERIC_CMD_SET_PHYSICS_OVERRIDE, - GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES, - GENERIC_CMD_SPAWN_INFANT, - GENERIC_CMD_SET_ANIMATION_SPEED -}; - -#include "object_properties.h" -std::string gob_cmd_set_properties(const ObjectProperties &prop); -ObjectProperties gob_read_set_properties(std::istream &is); - -std::string gob_cmd_update_position( - v3f position, - v3f velocity, - v3f acceleration, - v3f rotation, - bool do_interpolate, - bool is_movement_end, - f32 update_interval -); - -std::string gob_cmd_set_texture_mod(const std::string &mod); - -std::string gob_cmd_set_sprite( - v2s16 p, - u16 num_frames, - f32 framelength, - bool select_horiz_by_yawpitch -); - -std::string gob_cmd_punched(u16 result_hp); - -std::string gob_cmd_update_armor_groups(const ItemGroupList &armor_groups); - -std::string gob_cmd_update_physics_override(float physics_override_speed, - float physics_override_jump, float physics_override_gravity, - bool sneak, bool sneak_glitch, bool new_move); - -std::string gob_cmd_update_animation(v2f frames, float frame_speed, float frame_blend, bool frame_loop); - -std::string gob_cmd_update_animation_speed(float frame_speed); - -std::string gob_cmd_update_bone_position(const std::string &bone, v3f position, - v3f rotation); - -std::string gob_cmd_update_attachment(int parent_id, const std::string &bone, - v3f position, v3f rotation); - -std::string gob_cmd_update_nametag_attributes(video::SColor color); - -std::string gob_cmd_update_infant(u16 id, u8 type, - const std::string &client_initialization_data); diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index d3799868b..7223ce05c 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -70,8 +70,8 @@ with this program; if not, write to the Free Software Foundation, Inc., PROTOCOL_VERSION 14: Added transfer of player pressed keys to the server Added new messages for mesh and bone animation, as well as attachments - GENERIC_CMD_SET_ANIMATION - GENERIC_CMD_SET_BONE_POSITION + AO_CMD_SET_ANIMATION + AO_CMD_SET_BONE_POSITION GENERIC_CMD_SET_ATTACHMENT PROTOCOL_VERSION 15: Serialization format changes @@ -87,7 +87,7 @@ with this program; if not, write to the Free Software Foundation, Inc., damageGroups added to ToolCapabilities sound_place added to ItemDefinition PROTOCOL_VERSION 19: - GENERIC_CMD_SET_PHYSICS_OVERRIDE + AO_CMD_SET_PHYSICS_OVERRIDE PROTOCOL_VERSION 20: TOCLIENT_HUDADD TOCLIENT_HUDRM @@ -131,7 +131,7 @@ with this program; if not, write to the Free Software Foundation, Inc., Add TOCLIENT_HELLO for presenting server to client after client presentation Add TOCLIENT_AUTH_ACCEPT to accept connection from client - Rename GENERIC_CMD_SET_ATTACHMENT to GENERIC_CMD_ATTACH_TO + Rename GENERIC_CMD_SET_ATTACHMENT to AO_CMD_ATTACH_TO PROTOCOL_VERSION 26: Add TileDef tileable_horizontal, tileable_vertical flags PROTOCOL_VERSION 27: diff --git a/src/server.cpp b/src/server.cpp index 9eea45b31..062fe0798 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -35,7 +35,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filesys.h" #include "mapblock.h" #include "serverobject.h" -#include "genericobject.h" #include "settings.h" #include "profiler.h" #include "log.h" @@ -721,7 +720,7 @@ void Server::AsyncRunStep(bool initial_step) // Go through every message for (const ActiveObjectMessage &aom : *list) { // Send position updates to players who do not see the attachment - if (aom.datastring[0] == GENERIC_CMD_UPDATE_POSITION) { + if (aom.datastring[0] == AO_CMD_UPDATE_POSITION) { if (sao->getId() == player->getId()) continue; @@ -1819,9 +1818,7 @@ void Server::SendPlayerHP(session_t peer_id) m_script->player_event(playersao,"health_changed"); // Send to other clients - std::string str = gob_cmd_punched(playersao->getHP()); - ActiveObjectMessage aom(playersao->getId(), true, str); - playersao->m_messages_out.push(aom); + playersao->sendPunchCommand(); } void Server::SendPlayerBreath(PlayerSAO *sao) diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 333d32ff5..2d3ee078e 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -1420,10 +1420,7 @@ void ServerEnvironment::step(float dtime) // Step object obj->step(dtime, send_recommended); // Read messages from object - while (!obj->m_messages_out.empty()) { - this->m_active_object_messages.push(obj->m_messages_out.front()); - obj->m_messages_out.pop(); - } + obj->dumpAOMessagesToQueue(m_active_object_messages); }; m_ao_manager.step(dtime, cb_state); } diff --git a/src/serverobject.cpp b/src/serverobject.cpp index 1ed33f66b..119a41b7b 100644 --- a/src/serverobject.cpp +++ b/src/serverobject.cpp @@ -81,3 +81,34 @@ bool ServerActiveObject::setWieldedItem(const ItemStack &item) { return false; } + +std::string ServerActiveObject::generateUpdateInfantCommand(u16 infant_id, u16 protocol_version) +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SPAWN_INFANT); + // parameters + writeU16(os, infant_id); + writeU8(os, getSendType()); + os << serializeLongString(getClientInitializationData(protocol_version)); + return os.str(); +} + +std::string ServerActiveObject::generateUpdateNametagAttributesCommand(const video::SColor &color) const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_UPDATE_NAMETAG_ATTRIBUTES); + // parameters + writeU8(os, 1); // version for forward compatibility + writeARGB8(os, color); + return os.str(); +} + +void ServerActiveObject::dumpAOMessagesToQueue(std::queue &queue) +{ + while (!m_messages_out.empty()) { + queue.push(m_messages_out.front()); + m_messages_out.pop(); + } +} \ No newline at end of file diff --git a/src/serverobject.h b/src/serverobject.h index 48689fcb4..2e013a6b6 100644 --- a/src/serverobject.h +++ b/src/serverobject.h @@ -113,7 +113,7 @@ public: The return value of this is passed to the client-side object when it is created */ - virtual std::string getClientInitializationData(u16 protocol_version){return "";} + virtual std::string getClientInitializationData(u16 protocol_version) {return "";} /* The return value of this is passed to the server-side object @@ -192,6 +192,10 @@ public: m_attached_particle_spawners.erase(id); } + std::string generateUpdateInfantCommand(u16 infant_id, u16 protocol_version); + std::string generateUpdateNametagAttributesCommand(const video::SColor &color) const; + + void dumpAOMessagesToQueue(std::queue &queue); /* Number of players which know about this object. Object won't be @@ -236,11 +240,6 @@ public: */ v3s16 m_static_block = v3s16(1337,1337,1337); - /* - Queue of messages to be sent to the client - */ - std::queue m_messages_out; - protected: virtual void onAttach(int parent_id) {} virtual void onDetach(int parent_id) {} @@ -255,6 +254,11 @@ protected: v3f m_base_position; std::unordered_set m_attached_particle_spawners; + /* + Queue of messages to be sent to the client + */ + std::queue m_messages_out; + private: // Used for creating objects based on type static std::map m_types; diff --git a/util/travis/clang-format-whitelist.txt b/util/travis/clang-format-whitelist.txt index 05b4a96c4..7b2fd8236 100644 --- a/util/travis/clang-format-whitelist.txt +++ b/util/travis/clang-format-whitelist.txt @@ -151,8 +151,6 @@ src/fontengine.h src/game.cpp src/gamedef.h src/game.h -src/genericobject.cpp -src/genericobject.h src/gettext.cpp src/gettext.h src/gui/guiAnimatedImage.cpp -- cgit v1.2.3 From 894a34aef48024a752a1ef151d046955d83858d0 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sat, 11 Apr 2020 09:59:09 +0200 Subject: Move PlayerSAO to dedicated files --- src/clientiface.cpp | 3 +- src/content_sao.cpp | 676 +--------------------------------- src/content_sao.h | 299 --------------- src/database/database-files.cpp | 2 +- src/database/database-postgresql.cpp | 2 +- src/database/database-sqlite3.cpp | 2 +- src/network/serverpackethandler.cpp | 2 +- src/remoteplayer.cpp | 2 +- src/script/common/c_content.cpp | 3 +- src/script/cpp_api/s_base.cpp | 2 +- src/script/cpp_api/s_player.h | 1 + src/script/lua_api/l_env.cpp | 3 +- src/script/lua_api/l_object.cpp | 2 +- src/server.cpp | 2 +- src/server/CMakeLists.txt | 1 + src/server/player_sao.cpp | 695 +++++++++++++++++++++++++++++++++++ src/server/player_sao.h | 325 ++++++++++++++++ src/server/unit_sao.cpp | 4 - src/serverenvironment.cpp | 4 +- 19 files changed, 1038 insertions(+), 992 deletions(-) create mode 100644 src/server/player_sao.cpp create mode 100644 src/server/player_sao.h (limited to 'src/network') diff --git a/src/clientiface.cpp b/src/clientiface.cpp index dceaa64f2..17237f73e 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "clientiface.h" +#include "content_sao.h" #include "network/connection.h" #include "network/serveropcodes.h" #include "remoteplayer.h" @@ -27,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serverenvironment.h" #include "map.h" #include "emerge.h" -#include "content_sao.h" // TODO this is used for cleanup of only +#include "server/player_sao.h" #include "log.h" #include "util/srp.h" #include "face_position_cache.h" diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 0d387b53a..7ec17aa82 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "remoteplayer.h" #include "server.h" #include "scripting_server.h" +#include "server/player_sao.h" #include "settings.h" #include #include @@ -678,678 +679,3 @@ bool LuaEntitySAO::collideWithObjects() const { return m_prop.collideWithObjects; } - -/* - PlayerSAO -*/ - -// No prototype, PlayerSAO does not need to be deserialized - -PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_, - bool is_singleplayer): - UnitSAO(env_, v3f(0,0,0)), - m_player(player_), - m_peer_id(peer_id_), - m_is_singleplayer(is_singleplayer) -{ - SANITY_CHECK(m_peer_id != PEER_ID_INEXISTENT); - - m_prop.hp_max = PLAYER_MAX_HP_DEFAULT; - m_prop.breath_max = PLAYER_MAX_BREATH_DEFAULT; - m_prop.physical = false; - m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f); - m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f); - m_prop.pointable = true; - // Start of default appearance, this should be overwritten by Lua - m_prop.visual = "upright_sprite"; - m_prop.visual_size = v3f(1, 2, 1); - m_prop.textures.clear(); - m_prop.textures.emplace_back("player.png"); - m_prop.textures.emplace_back("player_back.png"); - m_prop.colors.clear(); - m_prop.colors.emplace_back(255, 255, 255, 255); - m_prop.spritediv = v2s16(1,1); - m_prop.eye_height = 1.625f; - // End of default appearance - m_prop.is_visible = true; - m_prop.backface_culling = false; - m_prop.makes_footstep_sound = true; - m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS; - m_hp = m_prop.hp_max; - m_breath = m_prop.breath_max; - // Disable zoom in survival mode using a value of 0 - m_prop.zoom_fov = g_settings->getBool("creative_mode") ? 15.0f : 0.0f; - - if (!g_settings->getBool("enable_damage")) - m_armor_groups["immortal"] = 1; -} - -void PlayerSAO::finalize(RemotePlayer *player, const std::set &privs) -{ - assert(player); - m_player = player; - m_privs = privs; -} - -v3f PlayerSAO::getEyeOffset() const -{ - return v3f(0, BS * m_prop.eye_height, 0); -} - -std::string PlayerSAO::getDescription() -{ - return std::string("player ") + m_player->getName(); -} - -// Called after id has been set and has been inserted in environment -void PlayerSAO::addedToEnvironment(u32 dtime_s) -{ - ServerActiveObject::addedToEnvironment(dtime_s); - ServerActiveObject::setBasePosition(m_base_position); - m_player->setPlayerSAO(this); - m_player->setPeerId(m_peer_id); - m_last_good_position = m_base_position; -} - -// Called before removing from environment -void PlayerSAO::removingFromEnvironment() -{ - ServerActiveObject::removingFromEnvironment(); - if (m_player->getPlayerSAO() == this) { - unlinkPlayerSessionAndSave(); - for (u32 attached_particle_spawner : m_attached_particle_spawners) { - m_env->deleteParticleSpawner(attached_particle_spawner, false); - } - } -} - -std::string PlayerSAO::getClientInitializationData(u16 protocol_version) -{ - std::ostringstream os(std::ios::binary); - - // Protocol >= 15 - writeU8(os, 1); // version - os << serializeString(m_player->getName()); // name - writeU8(os, 1); // is_player - writeS16(os, getId()); // id - writeV3F32(os, m_base_position); - writeV3F32(os, m_rotation); - writeU16(os, getHP()); - - std::ostringstream msg_os(std::ios::binary); - msg_os << serializeLongString(getPropertyPacket()); // message 1 - msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 - msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 - for (std::unordered_map>::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first, - (*ii).second.X, (*ii).second.Y)); // m_bone_position.size - } - msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 - msg_os << serializeLongString(generateUpdatePhysicsOverrideCommand()); // 5 - // (AO_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only. - msg_os << serializeLongString(generateUpdateNametagAttributesCommand(m_prop.nametag_color)); // 6 - int message_count = 6 + m_bone_position.size(); - for (std::unordered_set::const_iterator ii = m_attachment_child_ids.begin(); - ii != m_attachment_child_ids.end(); ++ii) { - if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { - message_count++; - msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version)); - } - } - - writeU8(os, message_count); - os.write(msg_os.str().c_str(), msg_os.str().size()); - - // return result - return os.str(); -} - -void PlayerSAO::getStaticData(std::string * result) const -{ - FATAL_ERROR("Obsolete function"); -} - -void PlayerSAO::step(float dtime, bool send_recommended) -{ - if (!isImmortal() && m_drowning_interval.step(dtime, 2.0f)) { - // Get nose/mouth position, approximate with eye position - v3s16 p = floatToInt(getEyePosition(), BS); - MapNode n = m_env->getMap().getNode(p); - const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); - // If node generates drown - if (c.drowning > 0 && m_hp > 0) { - if (m_breath > 0) - setBreath(m_breath - 1); - - // No more breath, damage player - if (m_breath == 0) { - PlayerHPChangeReason reason(PlayerHPChangeReason::DROWNING); - setHP(m_hp - c.drowning, reason); - m_env->getGameDef()->SendPlayerHPOrDie(this, reason); - } - } - } - - if (m_breathing_interval.step(dtime, 0.5f) && !isImmortal()) { - // Get nose/mouth position, approximate with eye position - v3s16 p = floatToInt(getEyePosition(), BS); - MapNode n = m_env->getMap().getNode(p); - const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); - // If player is alive & not drowning & not in ignore & not immortal, breathe - if (m_breath < m_prop.breath_max && c.drowning == 0 && - n.getContent() != CONTENT_IGNORE && m_hp > 0) - setBreath(m_breath + 1); - } - - if (!isImmortal() && m_node_hurt_interval.step(dtime, 1.0f)) { - u32 damage_per_second = 0; - std::string nodename; - // Lowest and highest damage points are 0.1 within collisionbox - float dam_top = m_prop.collisionbox.MaxEdge.Y - 0.1f; - - // Sequence of damage points, starting 0.1 above feet and progressing - // upwards in 1 node intervals, stopping below top damage point. - for (float dam_height = 0.1f; dam_height < dam_top; dam_height++) { - v3s16 p = floatToInt(m_base_position + - v3f(0.0f, dam_height * BS, 0.0f), BS); - MapNode n = m_env->getMap().getNode(p); - const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); - if (c.damage_per_second > damage_per_second) { - damage_per_second = c.damage_per_second; - nodename = c.name; - } - } - - // Top damage point - v3s16 ptop = floatToInt(m_base_position + - v3f(0.0f, dam_top * BS, 0.0f), BS); - MapNode ntop = m_env->getMap().getNode(ptop); - const ContentFeatures &c = m_env->getGameDef()->ndef()->get(ntop); - if (c.damage_per_second > damage_per_second) { - damage_per_second = c.damage_per_second; - nodename = c.name; - } - - if (damage_per_second != 0 && m_hp > 0) { - s32 newhp = (s32)m_hp - (s32)damage_per_second; - PlayerHPChangeReason reason(PlayerHPChangeReason::NODE_DAMAGE, nodename); - setHP(newhp, reason); - m_env->getGameDef()->SendPlayerHPOrDie(this, reason); - } - } - - if (!m_properties_sent) { - m_properties_sent = true; - std::string str = getPropertyPacket(); - // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); - m_env->getScriptIface()->player_event(this, "properties_changed"); - } - - // If attached, check that our parent is still there. If it isn't, detach. - if (m_attachment_parent_id && !isAttached()) { - m_attachment_parent_id = 0; - m_attachment_bone = ""; - m_attachment_position = v3f(0.0f, 0.0f, 0.0f); - m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f); - setBasePosition(m_last_good_position); - m_env->getGameDef()->SendMovePlayer(m_peer_id); - } - - //dstream<<"PlayerSAO::step: dtime: "<getMaxLagEstimate() * 2.0f; - if(lag_pool_max < LAG_POOL_MIN) - lag_pool_max = LAG_POOL_MIN; - m_dig_pool.setMax(lag_pool_max); - m_move_pool.setMax(lag_pool_max); - - // Increment cheat prevention timers - m_dig_pool.add(dtime); - m_move_pool.add(dtime); - m_time_from_last_teleport += dtime; - m_time_from_last_punch += dtime; - m_nocheat_dig_time += dtime; - m_max_speed_override_time = MYMAX(m_max_speed_override_time - dtime, 0.0f); - - // Each frame, parent position is copied if the object is attached, - // otherwise it's calculated normally. - // If the object gets detached this comes into effect automatically from - // the last known origin. - if (isAttached()) { - v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition(); - m_last_good_position = pos; - setBasePosition(pos); - } - - if (!send_recommended) - return; - - if (m_position_not_sent) { - m_position_not_sent = false; - float update_interval = m_env->getSendRecommendedInterval(); - v3f pos; - // When attached, the position is only sent to clients where the - // parent isn't known - if (isAttached()) - pos = m_last_good_position; - else - pos = m_base_position; - - std::string str = generateUpdatePositionCommand( - pos, - v3f(0.0f, 0.0f, 0.0f), - v3f(0.0f, 0.0f, 0.0f), - m_rotation, - true, - false, - update_interval - ); - // create message and add to list - m_messages_out.emplace(getId(), false, str); - } - - if (!m_armor_groups_sent) { - m_armor_groups_sent = true; - // create message and add to list - m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand()); - } - - if (!m_physics_override_sent) { - m_physics_override_sent = true; - // create message and add to list - m_messages_out.emplace(getId(), true, generateUpdatePhysicsOverrideCommand()); - } - - if (!m_animation_sent) { - m_animation_sent = true; - // create message and add to list - m_messages_out.emplace(getId(), true, generateUpdateAnimationCommand()); - } - - if (!m_bone_position_sent) { - m_bone_position_sent = true; - for (std::unordered_map>::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - std::string str = generateUpdateBonePositionCommand((*ii).first, - (*ii).second.X, (*ii).second.Y); - // create message and add to list - m_messages_out.emplace(getId(), true, str); - } - } - - if (!m_attachment_sent) { - m_attachment_sent = true; - std::string str = generateUpdateAttachmentCommand(); - // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); - } -} - -std::string PlayerSAO::generateUpdatePhysicsOverrideCommand() const -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_SET_PHYSICS_OVERRIDE); - // parameters - writeF32(os, m_physics_override_speed); - writeF32(os, m_physics_override_jump); - writeF32(os, m_physics_override_gravity); - // these are sent inverted so we get true when the server sends nothing - writeU8(os, !m_physics_override_sneak); - writeU8(os, !m_physics_override_sneak_glitch); - writeU8(os, !m_physics_override_new_move); - return os.str(); -} - -void PlayerSAO::setBasePosition(const v3f &position) -{ - if (m_player && position != m_base_position) - m_player->setDirty(true); - - // This needs to be ran for attachments too - ServerActiveObject::setBasePosition(position); - - // Updating is not wanted/required for player migration - if (m_env) { - m_position_not_sent = true; - } -} - -void PlayerSAO::setPos(const v3f &pos) -{ - if(isAttached()) - return; - - // Send mapblock of target location - v3s16 blockpos = v3s16(pos.X / MAP_BLOCKSIZE, pos.Y / MAP_BLOCKSIZE, pos.Z / MAP_BLOCKSIZE); - m_env->getGameDef()->SendBlock(m_peer_id, blockpos); - - setBasePosition(pos); - // Movement caused by this command is always valid - m_last_good_position = pos; - m_move_pool.empty(); - m_time_from_last_teleport = 0.0; - m_env->getGameDef()->SendMovePlayer(m_peer_id); -} - -void PlayerSAO::moveTo(v3f pos, bool continuous) -{ - if(isAttached()) - return; - - setBasePosition(pos); - // Movement caused by this command is always valid - m_last_good_position = pos; - m_move_pool.empty(); - m_time_from_last_teleport = 0.0; - m_env->getGameDef()->SendMovePlayer(m_peer_id); -} - -void PlayerSAO::setPlayerYaw(const float yaw) -{ - v3f rotation(0, yaw, 0); - if (m_player && yaw != m_rotation.Y) - m_player->setDirty(true); - - // Set player model yaw, not look view - UnitSAO::setRotation(rotation); -} - -void PlayerSAO::setFov(const float fov) -{ - if (m_player && fov != m_fov) - m_player->setDirty(true); - - m_fov = fov; -} - -void PlayerSAO::setWantedRange(const s16 range) -{ - if (m_player && range != m_wanted_range) - m_player->setDirty(true); - - m_wanted_range = range; -} - -void PlayerSAO::setPlayerYawAndSend(const float yaw) -{ - setPlayerYaw(yaw); - m_env->getGameDef()->SendMovePlayer(m_peer_id); -} - -void PlayerSAO::setLookPitch(const float pitch) -{ - if (m_player && pitch != m_pitch) - m_player->setDirty(true); - - m_pitch = pitch; -} - -void PlayerSAO::setLookPitchAndSend(const float pitch) -{ - setLookPitch(pitch); - m_env->getGameDef()->SendMovePlayer(m_peer_id); -} - -u16 PlayerSAO::punch(v3f dir, - const ToolCapabilities *toolcap, - ServerActiveObject *puncher, - float time_from_last_punch) -{ - if (!toolcap) - return 0; - - FATAL_ERROR_IF(!puncher, "Punch action called without SAO"); - - // No effect if PvP disabled or if immortal - if (isImmortal() || !g_settings->getBool("enable_pvp")) { - if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - // create message and add to list - sendPunchCommand(); - return 0; - } - } - - s32 old_hp = getHP(); - HitParams hitparams = getHitParams(m_armor_groups, toolcap, - time_from_last_punch); - - PlayerSAO *playersao = m_player->getPlayerSAO(); - - bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao, - puncher, time_from_last_punch, toolcap, dir, - hitparams.hp); - - if (!damage_handled) { - setHP((s32)getHP() - (s32)hitparams.hp, - PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher)); - } else { // override client prediction - if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - // create message and add to list - sendPunchCommand(); - } - } - - actionstream << puncher->getDescription() << " (id=" << puncher->getId() << - ", hp=" << puncher->getHP() << ") punched " << - getDescription() << " (id=" << m_id << ", hp=" << m_hp << - "), damage=" << (old_hp - (s32)getHP()) << - (damage_handled ? " (handled by Lua)" : "") << std::endl; - - return hitparams.wear; -} - -void PlayerSAO::setHP(s32 hp, const PlayerHPChangeReason &reason) -{ - s32 oldhp = m_hp; - - hp = rangelim(hp, 0, m_prop.hp_max); - - if (oldhp != hp) { - s32 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp, reason); - if (hp_change == 0) - return; - - hp = rangelim(oldhp + hp_change, 0, m_prop.hp_max); - } - - if (hp < oldhp && isImmortal()) - return; - - m_hp = hp; - - // Update properties on death - if ((hp == 0) != (oldhp == 0)) - m_properties_sent = false; -} - -void PlayerSAO::setBreath(const u16 breath, bool send) -{ - if (m_player && breath != m_breath) - m_player->setDirty(true); - - m_breath = rangelim(breath, 0, m_prop.breath_max); - - if (send) - m_env->getGameDef()->SendPlayerBreath(this); -} - -Inventory *PlayerSAO::getInventory() const -{ - return m_player ? &m_player->inventory : nullptr; -} - -InventoryLocation PlayerSAO::getInventoryLocation() const -{ - InventoryLocation loc; - loc.setPlayer(m_player->getName()); - return loc; -} - -u16 PlayerSAO::getWieldIndex() const -{ - return m_player->getWieldIndex(); -} - -ItemStack PlayerSAO::getWieldedItem(ItemStack *selected, ItemStack *hand) const -{ - return m_player->getWieldedItem(selected, hand); -} - -bool PlayerSAO::setWieldedItem(const ItemStack &item) -{ - InventoryList *mlist = m_player->inventory.getList(getWieldList()); - if (mlist) { - mlist->changeItem(m_player->getWieldIndex(), item); - return true; - } - return false; -} - -void PlayerSAO::disconnected() -{ - m_peer_id = PEER_ID_INEXISTENT; - m_pending_removal = true; -} - -void PlayerSAO::unlinkPlayerSessionAndSave() -{ - assert(m_player->getPlayerSAO() == this); - m_player->setPeerId(PEER_ID_INEXISTENT); - m_env->savePlayer(m_player); - m_player->setPlayerSAO(NULL); - m_env->removePlayer(m_player); -} - -std::string PlayerSAO::getPropertyPacket() -{ - m_prop.is_visible = (true); - return generateSetPropertiesCommand(m_prop); -} - -void PlayerSAO::setMaxSpeedOverride(const v3f &vel) -{ - if (m_max_speed_override_time == 0.0f) - m_max_speed_override = vel; - else - m_max_speed_override += vel; - if (m_player) { - float accel = MYMIN(m_player->movement_acceleration_default, - m_player->movement_acceleration_air); - m_max_speed_override_time = m_max_speed_override.getLength() / accel / BS; - } -} - -bool PlayerSAO::checkMovementCheat() -{ - if (isAttached() || m_is_singleplayer || - g_settings->getBool("disable_anticheat")) { - m_last_good_position = m_base_position; - return false; - } - - bool cheated = false; - /* - Check player movements - - NOTE: Actually the server should handle player physics like the - client does and compare player's position to what is calculated - on our side. This is required when eg. players fly due to an - explosion. Altough a node-based alternative might be possible - too, and much more lightweight. - */ - - float override_max_H, override_max_V; - if (m_max_speed_override_time > 0.0f) { - override_max_H = MYMAX(fabs(m_max_speed_override.X), fabs(m_max_speed_override.Z)); - override_max_V = fabs(m_max_speed_override.Y); - } else { - override_max_H = override_max_V = 0.0f; - } - - float player_max_walk = 0; // horizontal movement - float player_max_jump = 0; // vertical upwards movement - - if (m_privs.count("fast") != 0) - player_max_walk = m_player->movement_speed_fast; // Fast speed - else - player_max_walk = m_player->movement_speed_walk; // Normal speed - player_max_walk *= m_physics_override_speed; - player_max_walk = MYMAX(player_max_walk, override_max_H); - - player_max_jump = m_player->movement_speed_jump * m_physics_override_jump; - // FIXME: Bouncy nodes cause practically unbound increase in Y speed, - // until this can be verified correctly, tolerate higher jumping speeds - player_max_jump *= 2.0; - player_max_jump = MYMAX(player_max_jump, override_max_V); - - // Don't divide by zero! - if (player_max_walk < 0.0001f) - player_max_walk = 0.0001f; - if (player_max_jump < 0.0001f) - player_max_jump = 0.0001f; - - v3f diff = (m_base_position - m_last_good_position); - float d_vert = diff.Y; - diff.Y = 0; - float d_horiz = diff.getLength(); - float required_time = d_horiz / player_max_walk; - - // FIXME: Checking downwards movement is not easily possible currently, - // the server could calculate speed differences to examine the gravity - if (d_vert > 0) { - // In certain cases (water, ladders) walking speed is applied vertically - float s = MYMAX(player_max_jump, player_max_walk); - required_time = MYMAX(required_time, d_vert / s); - } - - if (m_move_pool.grab(required_time)) { - m_last_good_position = m_base_position; - } else { - const float LAG_POOL_MIN = 5.0; - float lag_pool_max = m_env->getMaxLagEstimate() * 2.0; - lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN); - if (m_time_from_last_teleport > lag_pool_max) { - actionstream << "Player " << m_player->getName() - << " moved too fast; resetting position" - << std::endl; - cheated = true; - } - setBasePosition(m_last_good_position); - } - return cheated; -} - -bool PlayerSAO::getCollisionBox(aabb3f *toset) const -{ - //update collision box - toset->MinEdge = m_prop.collisionbox.MinEdge * BS; - toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS; - - toset->MinEdge += m_base_position; - toset->MaxEdge += m_base_position; - return true; -} - -bool PlayerSAO::getSelectionBox(aabb3f *toset) const -{ - if (!m_prop.is_visible || !m_prop.pointable) { - return false; - } - - toset->MinEdge = m_prop.selectionbox.MinEdge * BS; - toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS; - - return true; -} - -float PlayerSAO::getZoomFOV() const -{ - return m_prop.zoom_fov; -} diff --git a/src/content_sao.h b/src/content_sao.h index 32ab922d6..5387fd108 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -99,302 +99,3 @@ private: std::string m_current_texture_modifier = ""; }; -/* - PlayerSAO needs some internals exposed. -*/ - -class LagPool -{ - float m_pool = 15.0f; - float m_max = 15.0f; -public: - LagPool() = default; - - void setMax(float new_max) - { - m_max = new_max; - if(m_pool > new_max) - m_pool = new_max; - } - - void add(float dtime) - { - m_pool -= dtime; - if(m_pool < 0) - m_pool = 0; - } - - void empty() - { - m_pool = m_max; - } - - bool grab(float dtime) - { - if(dtime <= 0) - return true; - if(m_pool + dtime > m_max) - return false; - m_pool += dtime; - return true; - } -}; - -class RemotePlayer; - -class PlayerSAO : public UnitSAO -{ -public: - PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_, - bool is_singleplayer); - - ActiveObjectType getType() const - { return ACTIVEOBJECT_TYPE_PLAYER; } - ActiveObjectType getSendType() const - { return ACTIVEOBJECT_TYPE_GENERIC; } - std::string getDescription(); - - /* - Active object <-> environment interface - */ - - void addedToEnvironment(u32 dtime_s); - void removingFromEnvironment(); - bool isStaticAllowed() const { return false; } - std::string getClientInitializationData(u16 protocol_version); - void getStaticData(std::string *result) const; - void step(float dtime, bool send_recommended); - void setBasePosition(const v3f &position); - void setPos(const v3f &pos); - void moveTo(v3f pos, bool continuous); - void setPlayerYaw(const float yaw); - // Data should not be sent at player initialization - void setPlayerYawAndSend(const float yaw); - void setLookPitch(const float pitch); - // Data should not be sent at player initialization - void setLookPitchAndSend(const float pitch); - f32 getLookPitch() const { return m_pitch; } - f32 getRadLookPitch() const { return m_pitch * core::DEGTORAD; } - // Deprecated - f32 getRadLookPitchDep() const { return -1.0 * m_pitch * core::DEGTORAD; } - void setFov(const float pitch); - f32 getFov() const { return m_fov; } - void setWantedRange(const s16 range); - s16 getWantedRange() const { return m_wanted_range; } - - /* - Interaction interface - */ - - u16 punch(v3f dir, - const ToolCapabilities *toolcap, - ServerActiveObject *puncher, - float time_from_last_punch); - void rightClick(ServerActiveObject *clicker) {} - void setHP(s32 hp, const PlayerHPChangeReason &reason); - void setHPRaw(u16 hp) { m_hp = hp; } - s16 readDamage(); - u16 getBreath() const { return m_breath; } - void setBreath(const u16 breath, bool send = true); - - /* - Inventory interface - */ - Inventory *getInventory() const; - InventoryLocation getInventoryLocation() const; - void setInventoryModified() {} - std::string getWieldList() const { return "main"; } - u16 getWieldIndex() const; - ItemStack getWieldedItem(ItemStack *selected, ItemStack *hand = nullptr) const; - bool setWieldedItem(const ItemStack &item); - - /* - PlayerSAO-specific - */ - - void disconnected(); - - RemotePlayer *getPlayer() { return m_player; } - session_t getPeerID() const { return m_peer_id; } - - // Cheat prevention - - v3f getLastGoodPosition() const - { - return m_last_good_position; - } - float resetTimeFromLastPunch() - { - float r = m_time_from_last_punch; - m_time_from_last_punch = 0.0; - return r; - } - void noCheatDigStart(const v3s16 &p) - { - m_nocheat_dig_pos = p; - m_nocheat_dig_time = 0; - } - v3s16 getNoCheatDigPos() - { - return m_nocheat_dig_pos; - } - float getNoCheatDigTime() - { - return m_nocheat_dig_time; - } - void noCheatDigEnd() - { - m_nocheat_dig_pos = v3s16(32767, 32767, 32767); - } - LagPool& getDigPool() - { - return m_dig_pool; - } - void setMaxSpeedOverride(const v3f &vel); - // Returns true if cheated - bool checkMovementCheat(); - - // Other - - void updatePrivileges(const std::set &privs, - bool is_singleplayer) - { - m_privs = privs; - m_is_singleplayer = is_singleplayer; - } - - bool getCollisionBox(aabb3f *toset) const; - bool getSelectionBox(aabb3f *toset) const; - bool collideWithObjects() const { return true; } - - void finalize(RemotePlayer *player, const std::set &privs); - - v3f getEyePosition() const { return m_base_position + getEyeOffset(); } - v3f getEyeOffset() const; - float getZoomFOV() const; - - inline Metadata &getMeta() { return m_meta; } - -private: - std::string getPropertyPacket(); - void unlinkPlayerSessionAndSave(); - std::string generateUpdatePhysicsOverrideCommand() const; - - RemotePlayer *m_player = nullptr; - session_t m_peer_id = 0; - - // Cheat prevention - LagPool m_dig_pool; - LagPool m_move_pool; - v3f m_last_good_position; - float m_time_from_last_teleport = 0.0f; - float m_time_from_last_punch = 0.0f; - v3s16 m_nocheat_dig_pos = v3s16(32767, 32767, 32767); - float m_nocheat_dig_time = 0.0f; - float m_max_speed_override_time = 0.0f; - v3f m_max_speed_override = v3f(0.0f, 0.0f, 0.0f); - - // Timers - IntervalLimiter m_breathing_interval; - IntervalLimiter m_drowning_interval; - IntervalLimiter m_node_hurt_interval; - - bool m_position_not_sent = false; - - // Cached privileges for enforcement - std::set m_privs; - bool m_is_singleplayer; - - u16 m_breath = PLAYER_MAX_BREATH_DEFAULT; - f32 m_pitch = 0.0f; - f32 m_fov = 0.0f; - s16 m_wanted_range = 0.0f; - - Metadata m_meta; -public: - float m_physics_override_speed = 1.0f; - float m_physics_override_jump = 1.0f; - float m_physics_override_gravity = 1.0f; - bool m_physics_override_sneak = true; - bool m_physics_override_sneak_glitch = false; - bool m_physics_override_new_move = true; - bool m_physics_override_sent = false; -}; - - -struct PlayerHPChangeReason { - enum Type : u8 { - SET_HP, - PLAYER_PUNCH, - FALL, - NODE_DAMAGE, - DROWNING, - RESPAWN - }; - - Type type = SET_HP; - bool from_mod = false; - int lua_reference = -1; - - // For PLAYER_PUNCH - ServerActiveObject *object = nullptr; - // For NODE_DAMAGE - std::string node; - - inline bool hasLuaReference() const - { - return lua_reference >= 0; - } - - bool setTypeFromString(const std::string &typestr) - { - if (typestr == "set_hp") - type = SET_HP; - else if (typestr == "punch") - type = PLAYER_PUNCH; - else if (typestr == "fall") - type = FALL; - else if (typestr == "node_damage") - type = NODE_DAMAGE; - else if (typestr == "drown") - type = DROWNING; - else if (typestr == "respawn") - type = RESPAWN; - else - return false; - - return true; - } - - std::string getTypeAsString() const - { - switch (type) { - case PlayerHPChangeReason::SET_HP: - return "set_hp"; - case PlayerHPChangeReason::PLAYER_PUNCH: - return "punch"; - case PlayerHPChangeReason::FALL: - return "fall"; - case PlayerHPChangeReason::NODE_DAMAGE: - return "node_damage"; - case PlayerHPChangeReason::DROWNING: - return "drown"; - case PlayerHPChangeReason::RESPAWN: - return "respawn"; - default: - return "?"; - } - } - - PlayerHPChangeReason(Type type): - type(type) - {} - - PlayerHPChangeReason(Type type, ServerActiveObject *object): - type(type), object(object) - {} - - PlayerHPChangeReason(Type type, std::string node): - type(type), node(node) - {} -}; diff --git a/src/database/database-files.cpp b/src/database/database-files.cpp index d09f1c074..d2b0b1543 100644 --- a/src/database/database-files.cpp +++ b/src/database/database-files.cpp @@ -20,11 +20,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "database-files.h" -#include "content_sao.h" #include "remoteplayer.h" #include "settings.h" #include "porting.h" #include "filesys.h" +#include "server/player_sao.h" #include "util/string.h" // !!! WARNING !!! diff --git a/src/database/database-postgresql.cpp b/src/database/database-postgresql.cpp index d7c94ff15..77385e240 100644 --- a/src/database/database-postgresql.cpp +++ b/src/database/database-postgresql.cpp @@ -36,8 +36,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "debug.h" #include "exceptions.h" #include "settings.h" -#include "content_sao.h" #include "remoteplayer.h" +#include "server/player_sao.h" Database_PostgreSQL::Database_PostgreSQL(const std::string &connect_string) : m_connect_string(connect_string) diff --git a/src/database/database-sqlite3.cpp b/src/database/database-sqlite3.cpp index 1bacdfe6c..4560743b9 100644 --- a/src/database/database-sqlite3.cpp +++ b/src/database/database-sqlite3.cpp @@ -33,8 +33,8 @@ SQLite format specification: #include "settings.h" #include "porting.h" #include "util/string.h" -#include "content_sao.h" #include "remoteplayer.h" +#include "server/player_sao.h" #include diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 23bcc867f..b2fdb2a22 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -20,7 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "chatmessage.h" #include "server.h" #include "log.h" -#include "content_sao.h" #include "emerge.h" #include "mapblock.h" #include "modchannels.h" @@ -34,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "network/connection.h" #include "network/networkprotocol.h" #include "network/serveropcodes.h" +#include "server/player_sao.h" #include "util/auth.h" #include "util/base64.h" #include "util/pointedthing.h" diff --git a/src/remoteplayer.cpp b/src/remoteplayer.cpp index 1a8fec68c..7a603d53e 100644 --- a/src/remoteplayer.cpp +++ b/src/remoteplayer.cpp @@ -20,13 +20,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "remoteplayer.h" #include -#include "content_sao.h" #include "filesys.h" #include "gamedef.h" #include "porting.h" // strlcpy #include "server.h" #include "settings.h" #include "convert_json.h" +#include "server/player_sao.h" /* RemotePlayer diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 60f12052f..c8cd7539f 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -21,7 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_types.h" #include "nodedef.h" #include "object_properties.h" -#include "content_sao.h" #include "cpp_api/s_node.h" #include "lua_api/l_object.h" #include "lua_api/l_item.h" @@ -29,10 +28,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include "log.h" #include "tool.h" -#include "server/serveractiveobject.h" #include "porting.h" #include "mapgen/mg_schematic.h" #include "noise.h" +#include "server/player_sao.h" #include "util/pointedthing.h" #include "debug.h" // For FATAL_ERROR #include diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index 16c20eeae..150baf77e 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "cpp_api/s_security.h" #include "lua_api/l_object.h" #include "common/c_converter.h" -#include "server/serveractiveobject.h" +#include "server/player_sao.h" #include "filesys.h" #include "content/mods.h" #include "porting.h" diff --git a/src/script/cpp_api/s_player.h b/src/script/cpp_api/s_player.h index cf24ddc73..7ca3d8f30 100644 --- a/src/script/cpp_api/s_player.h +++ b/src/script/cpp_api/s_player.h @@ -28,6 +28,7 @@ struct InventoryLocation; struct ItemStack; struct ToolCapabilities; struct PlayerHPChangeReason; +class ServerActiveObject; class ScriptApiPlayer : virtual public ScriptApiBase { diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 31e582d3d..438669feb 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #include "lua_api/l_env.h" #include "lua_api/l_internal.h" #include "lua_api/l_nodemeta.h" @@ -25,7 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_vmanip.h" #include "common/c_converter.h" #include "common/c_content.h" -#include #include "scripting_server.h" #include "environment.h" #include "mapblock.h" @@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "pathfinder.h" #include "face_position_cache.h" #include "remoteplayer.h" +#include "server/player_sao.h" #ifndef SERVER #include "client/client.h" #endif diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 1ea144a1c..95c96235e 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -27,12 +27,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_content.h" #include "log.h" #include "tool.h" -#include "server/serveractiveobject.h" #include "content_sao.h" #include "remoteplayer.h" #include "server.h" #include "hud.h" #include "scripting_server.h" +#include "server/player_sao.h" /* ObjectRef diff --git a/src/server.cpp b/src/server.cpp index 529466f6b..85d07fbc4 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -47,7 +47,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapgen/mg_biome.h" #include "content_mapnode.h" #include "content_nodemeta.h" -#include "content_sao.h" #include "content/mods.h" #include "modchannels.h" #include "serverlist.h" @@ -64,6 +63,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "chatmessage.h" #include "chat_interface.h" #include "remoteplayer.h" +#include "server/player_sao.h" class ClientNotFoundException : public BaseException { diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 9fa5ed9fa..26eaed5ac 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -1,6 +1,7 @@ set(server_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/activeobjectmgr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mods.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/player_sao.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serveractiveobject.cpp ${CMAKE_CURRENT_SOURCE_DIR}/unit_sao.cpp PARENT_SCOPE) diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp new file mode 100644 index 000000000..58fcea5fe --- /dev/null +++ b/src/server/player_sao.cpp @@ -0,0 +1,695 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola +Copyright (C) 2013-2020 Minetest core developers & community + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "player_sao.h" +#include "nodedef.h" +#include "remoteplayer.h" +#include "scripting_server.h" +#include "server.h" +#include "serverenvironment.h" + +PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_, + bool is_singleplayer): + UnitSAO(env_, v3f(0,0,0)), + m_player(player_), + m_peer_id(peer_id_), + m_is_singleplayer(is_singleplayer) +{ + SANITY_CHECK(m_peer_id != PEER_ID_INEXISTENT); + + m_prop.hp_max = PLAYER_MAX_HP_DEFAULT; + m_prop.breath_max = PLAYER_MAX_BREATH_DEFAULT; + m_prop.physical = false; + m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f); + m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f); + m_prop.pointable = true; + // Start of default appearance, this should be overwritten by Lua + m_prop.visual = "upright_sprite"; + m_prop.visual_size = v3f(1, 2, 1); + m_prop.textures.clear(); + m_prop.textures.emplace_back("player.png"); + m_prop.textures.emplace_back("player_back.png"); + m_prop.colors.clear(); + m_prop.colors.emplace_back(255, 255, 255, 255); + m_prop.spritediv = v2s16(1,1); + m_prop.eye_height = 1.625f; + // End of default appearance + m_prop.is_visible = true; + m_prop.backface_culling = false; + m_prop.makes_footstep_sound = true; + m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS; + m_hp = m_prop.hp_max; + m_breath = m_prop.breath_max; + // Disable zoom in survival mode using a value of 0 + m_prop.zoom_fov = g_settings->getBool("creative_mode") ? 15.0f : 0.0f; + + if (!g_settings->getBool("enable_damage")) + m_armor_groups["immortal"] = 1; +} + +void PlayerSAO::finalize(RemotePlayer *player, const std::set &privs) +{ + assert(player); + m_player = player; + m_privs = privs; +} + +v3f PlayerSAO::getEyeOffset() const +{ + return v3f(0, BS * m_prop.eye_height, 0); +} + +std::string PlayerSAO::getDescription() +{ + return std::string("player ") + m_player->getName(); +} + +// Called after id has been set and has been inserted in environment +void PlayerSAO::addedToEnvironment(u32 dtime_s) +{ + ServerActiveObject::addedToEnvironment(dtime_s); + ServerActiveObject::setBasePosition(m_base_position); + m_player->setPlayerSAO(this); + m_player->setPeerId(m_peer_id); + m_last_good_position = m_base_position; +} + +// Called before removing from environment +void PlayerSAO::removingFromEnvironment() +{ + ServerActiveObject::removingFromEnvironment(); + if (m_player->getPlayerSAO() == this) { + unlinkPlayerSessionAndSave(); + for (u32 attached_particle_spawner : m_attached_particle_spawners) { + m_env->deleteParticleSpawner(attached_particle_spawner, false); + } + } +} + +std::string PlayerSAO::getClientInitializationData(u16 protocol_version) +{ + std::ostringstream os(std::ios::binary); + + // Protocol >= 15 + writeU8(os, 1); // version + os << serializeString(m_player->getName()); // name + writeU8(os, 1); // is_player + writeS16(os, getId()); // id + writeV3F32(os, m_base_position); + writeV3F32(os, m_rotation); + writeU16(os, getHP()); + + std::ostringstream msg_os(std::ios::binary); + msg_os << serializeLongString(getPropertyPacket()); // message 1 + msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 + msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 + for (std::unordered_map>::const_iterator + ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { + msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first, + (*ii).second.X, (*ii).second.Y)); // m_bone_position.size + } + msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 + msg_os << serializeLongString(generateUpdatePhysicsOverrideCommand()); // 5 + // (AO_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only. + msg_os << serializeLongString(generateUpdateNametagAttributesCommand(m_prop.nametag_color)); // 6 + int message_count = 6 + m_bone_position.size(); + for (std::unordered_set::const_iterator ii = m_attachment_child_ids.begin(); + ii != m_attachment_child_ids.end(); ++ii) { + if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { + message_count++; + msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version)); + } + } + + writeU8(os, message_count); + os.write(msg_os.str().c_str(), msg_os.str().size()); + + // return result + return os.str(); +} + +void PlayerSAO::getStaticData(std::string * result) const +{ + FATAL_ERROR("Obsolete function"); +} + +void PlayerSAO::step(float dtime, bool send_recommended) +{ + if (!isImmortal() && m_drowning_interval.step(dtime, 2.0f)) { + // Get nose/mouth position, approximate with eye position + v3s16 p = floatToInt(getEyePosition(), BS); + MapNode n = m_env->getMap().getNode(p); + const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); + // If node generates drown + if (c.drowning > 0 && m_hp > 0) { + if (m_breath > 0) + setBreath(m_breath - 1); + + // No more breath, damage player + if (m_breath == 0) { + PlayerHPChangeReason reason(PlayerHPChangeReason::DROWNING); + setHP(m_hp - c.drowning, reason); + m_env->getGameDef()->SendPlayerHPOrDie(this, reason); + } + } + } + + if (m_breathing_interval.step(dtime, 0.5f) && !isImmortal()) { + // Get nose/mouth position, approximate with eye position + v3s16 p = floatToInt(getEyePosition(), BS); + MapNode n = m_env->getMap().getNode(p); + const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); + // If player is alive & not drowning & not in ignore & not immortal, breathe + if (m_breath < m_prop.breath_max && c.drowning == 0 && + n.getContent() != CONTENT_IGNORE && m_hp > 0) + setBreath(m_breath + 1); + } + + if (!isImmortal() && m_node_hurt_interval.step(dtime, 1.0f)) { + u32 damage_per_second = 0; + std::string nodename; + // Lowest and highest damage points are 0.1 within collisionbox + float dam_top = m_prop.collisionbox.MaxEdge.Y - 0.1f; + + // Sequence of damage points, starting 0.1 above feet and progressing + // upwards in 1 node intervals, stopping below top damage point. + for (float dam_height = 0.1f; dam_height < dam_top; dam_height++) { + v3s16 p = floatToInt(m_base_position + + v3f(0.0f, dam_height * BS, 0.0f), BS); + MapNode n = m_env->getMap().getNode(p); + const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); + if (c.damage_per_second > damage_per_second) { + damage_per_second = c.damage_per_second; + nodename = c.name; + } + } + + // Top damage point + v3s16 ptop = floatToInt(m_base_position + + v3f(0.0f, dam_top * BS, 0.0f), BS); + MapNode ntop = m_env->getMap().getNode(ptop); + const ContentFeatures &c = m_env->getGameDef()->ndef()->get(ntop); + if (c.damage_per_second > damage_per_second) { + damage_per_second = c.damage_per_second; + nodename = c.name; + } + + if (damage_per_second != 0 && m_hp > 0) { + s32 newhp = (s32)m_hp - (s32)damage_per_second; + PlayerHPChangeReason reason(PlayerHPChangeReason::NODE_DAMAGE, nodename); + setHP(newhp, reason); + m_env->getGameDef()->SendPlayerHPOrDie(this, reason); + } + } + + if (!m_properties_sent) { + m_properties_sent = true; + std::string str = getPropertyPacket(); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push(aom); + m_env->getScriptIface()->player_event(this, "properties_changed"); + } + + // If attached, check that our parent is still there. If it isn't, detach. + if (m_attachment_parent_id && !isAttached()) { + m_attachment_parent_id = 0; + m_attachment_bone = ""; + m_attachment_position = v3f(0.0f, 0.0f, 0.0f); + m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f); + setBasePosition(m_last_good_position); + m_env->getGameDef()->SendMovePlayer(m_peer_id); + } + + //dstream<<"PlayerSAO::step: dtime: "<getMaxLagEstimate() * 2.0f; + if(lag_pool_max < LAG_POOL_MIN) + lag_pool_max = LAG_POOL_MIN; + m_dig_pool.setMax(lag_pool_max); + m_move_pool.setMax(lag_pool_max); + + // Increment cheat prevention timers + m_dig_pool.add(dtime); + m_move_pool.add(dtime); + m_time_from_last_teleport += dtime; + m_time_from_last_punch += dtime; + m_nocheat_dig_time += dtime; + m_max_speed_override_time = MYMAX(m_max_speed_override_time - dtime, 0.0f); + + // Each frame, parent position is copied if the object is attached, + // otherwise it's calculated normally. + // If the object gets detached this comes into effect automatically from + // the last known origin. + if (isAttached()) { + v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition(); + m_last_good_position = pos; + setBasePosition(pos); + } + + if (!send_recommended) + return; + + if (m_position_not_sent) { + m_position_not_sent = false; + float update_interval = m_env->getSendRecommendedInterval(); + v3f pos; + // When attached, the position is only sent to clients where the + // parent isn't known + if (isAttached()) + pos = m_last_good_position; + else + pos = m_base_position; + + std::string str = generateUpdatePositionCommand( + pos, + v3f(0.0f, 0.0f, 0.0f), + v3f(0.0f, 0.0f, 0.0f), + m_rotation, + true, + false, + update_interval + ); + // create message and add to list + m_messages_out.emplace(getId(), false, str); + } + + if (!m_armor_groups_sent) { + m_armor_groups_sent = true; + // create message and add to list + m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand()); + } + + if (!m_physics_override_sent) { + m_physics_override_sent = true; + // create message and add to list + m_messages_out.emplace(getId(), true, generateUpdatePhysicsOverrideCommand()); + } + + if (!m_animation_sent) { + m_animation_sent = true; + // create message and add to list + m_messages_out.emplace(getId(), true, generateUpdateAnimationCommand()); + } + + if (!m_bone_position_sent) { + m_bone_position_sent = true; + for (std::unordered_map>::const_iterator + ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { + std::string str = generateUpdateBonePositionCommand((*ii).first, + (*ii).second.X, (*ii).second.Y); + // create message and add to list + m_messages_out.emplace(getId(), true, str); + } + } + + if (!m_attachment_sent) { + m_attachment_sent = true; + std::string str = generateUpdateAttachmentCommand(); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push(aom); + } +} + +std::string PlayerSAO::generateUpdatePhysicsOverrideCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_PHYSICS_OVERRIDE); + // parameters + writeF32(os, m_physics_override_speed); + writeF32(os, m_physics_override_jump); + writeF32(os, m_physics_override_gravity); + // these are sent inverted so we get true when the server sends nothing + writeU8(os, !m_physics_override_sneak); + writeU8(os, !m_physics_override_sneak_glitch); + writeU8(os, !m_physics_override_new_move); + return os.str(); +} + +void PlayerSAO::setBasePosition(const v3f &position) +{ + if (m_player && position != m_base_position) + m_player->setDirty(true); + + // This needs to be ran for attachments too + ServerActiveObject::setBasePosition(position); + + // Updating is not wanted/required for player migration + if (m_env) { + m_position_not_sent = true; + } +} + +void PlayerSAO::setPos(const v3f &pos) +{ + if(isAttached()) + return; + + // Send mapblock of target location + v3s16 blockpos = v3s16(pos.X / MAP_BLOCKSIZE, pos.Y / MAP_BLOCKSIZE, pos.Z / MAP_BLOCKSIZE); + m_env->getGameDef()->SendBlock(m_peer_id, blockpos); + + setBasePosition(pos); + // Movement caused by this command is always valid + m_last_good_position = pos; + m_move_pool.empty(); + m_time_from_last_teleport = 0.0; + m_env->getGameDef()->SendMovePlayer(m_peer_id); +} + +void PlayerSAO::moveTo(v3f pos, bool continuous) +{ + if(isAttached()) + return; + + setBasePosition(pos); + // Movement caused by this command is always valid + m_last_good_position = pos; + m_move_pool.empty(); + m_time_from_last_teleport = 0.0; + m_env->getGameDef()->SendMovePlayer(m_peer_id); +} + +void PlayerSAO::setPlayerYaw(const float yaw) +{ + v3f rotation(0, yaw, 0); + if (m_player && yaw != m_rotation.Y) + m_player->setDirty(true); + + // Set player model yaw, not look view + UnitSAO::setRotation(rotation); +} + +void PlayerSAO::setFov(const float fov) +{ + if (m_player && fov != m_fov) + m_player->setDirty(true); + + m_fov = fov; +} + +void PlayerSAO::setWantedRange(const s16 range) +{ + if (m_player && range != m_wanted_range) + m_player->setDirty(true); + + m_wanted_range = range; +} + +void PlayerSAO::setPlayerYawAndSend(const float yaw) +{ + setPlayerYaw(yaw); + m_env->getGameDef()->SendMovePlayer(m_peer_id); +} + +void PlayerSAO::setLookPitch(const float pitch) +{ + if (m_player && pitch != m_pitch) + m_player->setDirty(true); + + m_pitch = pitch; +} + +void PlayerSAO::setLookPitchAndSend(const float pitch) +{ + setLookPitch(pitch); + m_env->getGameDef()->SendMovePlayer(m_peer_id); +} + +u16 PlayerSAO::punch(v3f dir, + const ToolCapabilities *toolcap, + ServerActiveObject *puncher, + float time_from_last_punch) +{ + if (!toolcap) + return 0; + + FATAL_ERROR_IF(!puncher, "Punch action called without SAO"); + + // No effect if PvP disabled or if immortal + if (isImmortal() || !g_settings->getBool("enable_pvp")) { + if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + // create message and add to list + sendPunchCommand(); + return 0; + } + } + + s32 old_hp = getHP(); + HitParams hitparams = getHitParams(m_armor_groups, toolcap, + time_from_last_punch); + + PlayerSAO *playersao = m_player->getPlayerSAO(); + + bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao, + puncher, time_from_last_punch, toolcap, dir, + hitparams.hp); + + if (!damage_handled) { + setHP((s32)getHP() - (s32)hitparams.hp, + PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher)); + } else { // override client prediction + if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + // create message and add to list + sendPunchCommand(); + } + } + + actionstream << puncher->getDescription() << " (id=" << puncher->getId() << + ", hp=" << puncher->getHP() << ") punched " << + getDescription() << " (id=" << m_id << ", hp=" << m_hp << + "), damage=" << (old_hp - (s32)getHP()) << + (damage_handled ? " (handled by Lua)" : "") << std::endl; + + return hitparams.wear; +} + +void PlayerSAO::setHP(s32 hp, const PlayerHPChangeReason &reason) +{ + s32 oldhp = m_hp; + + hp = rangelim(hp, 0, m_prop.hp_max); + + if (oldhp != hp) { + s32 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp, reason); + if (hp_change == 0) + return; + + hp = rangelim(oldhp + hp_change, 0, m_prop.hp_max); + } + + if (hp < oldhp && isImmortal()) + return; + + m_hp = hp; + + // Update properties on death + if ((hp == 0) != (oldhp == 0)) + m_properties_sent = false; +} + +void PlayerSAO::setBreath(const u16 breath, bool send) +{ + if (m_player && breath != m_breath) + m_player->setDirty(true); + + m_breath = rangelim(breath, 0, m_prop.breath_max); + + if (send) + m_env->getGameDef()->SendPlayerBreath(this); +} + +Inventory *PlayerSAO::getInventory() const +{ + return m_player ? &m_player->inventory : nullptr; +} + +InventoryLocation PlayerSAO::getInventoryLocation() const +{ + InventoryLocation loc; + loc.setPlayer(m_player->getName()); + return loc; +} + +u16 PlayerSAO::getWieldIndex() const +{ + return m_player->getWieldIndex(); +} + +ItemStack PlayerSAO::getWieldedItem(ItemStack *selected, ItemStack *hand) const +{ + return m_player->getWieldedItem(selected, hand); +} + +bool PlayerSAO::setWieldedItem(const ItemStack &item) +{ + InventoryList *mlist = m_player->inventory.getList(getWieldList()); + if (mlist) { + mlist->changeItem(m_player->getWieldIndex(), item); + return true; + } + return false; +} + +void PlayerSAO::disconnected() +{ + m_peer_id = PEER_ID_INEXISTENT; + m_pending_removal = true; +} + +void PlayerSAO::unlinkPlayerSessionAndSave() +{ + assert(m_player->getPlayerSAO() == this); + m_player->setPeerId(PEER_ID_INEXISTENT); + m_env->savePlayer(m_player); + m_player->setPlayerSAO(NULL); + m_env->removePlayer(m_player); +} + +std::string PlayerSAO::getPropertyPacket() +{ + m_prop.is_visible = (true); + return generateSetPropertiesCommand(m_prop); +} + +void PlayerSAO::setMaxSpeedOverride(const v3f &vel) +{ + if (m_max_speed_override_time == 0.0f) + m_max_speed_override = vel; + else + m_max_speed_override += vel; + if (m_player) { + float accel = MYMIN(m_player->movement_acceleration_default, + m_player->movement_acceleration_air); + m_max_speed_override_time = m_max_speed_override.getLength() / accel / BS; + } +} + +bool PlayerSAO::checkMovementCheat() +{ + if (isAttached() || m_is_singleplayer || + g_settings->getBool("disable_anticheat")) { + m_last_good_position = m_base_position; + return false; + } + + bool cheated = false; + /* + Check player movements + + NOTE: Actually the server should handle player physics like the + client does and compare player's position to what is calculated + on our side. This is required when eg. players fly due to an + explosion. Altough a node-based alternative might be possible + too, and much more lightweight. + */ + + float override_max_H, override_max_V; + if (m_max_speed_override_time > 0.0f) { + override_max_H = MYMAX(fabs(m_max_speed_override.X), fabs(m_max_speed_override.Z)); + override_max_V = fabs(m_max_speed_override.Y); + } else { + override_max_H = override_max_V = 0.0f; + } + + float player_max_walk = 0; // horizontal movement + float player_max_jump = 0; // vertical upwards movement + + if (m_privs.count("fast") != 0) + player_max_walk = m_player->movement_speed_fast; // Fast speed + else + player_max_walk = m_player->movement_speed_walk; // Normal speed + player_max_walk *= m_physics_override_speed; + player_max_walk = MYMAX(player_max_walk, override_max_H); + + player_max_jump = m_player->movement_speed_jump * m_physics_override_jump; + // FIXME: Bouncy nodes cause practically unbound increase in Y speed, + // until this can be verified correctly, tolerate higher jumping speeds + player_max_jump *= 2.0; + player_max_jump = MYMAX(player_max_jump, override_max_V); + + // Don't divide by zero! + if (player_max_walk < 0.0001f) + player_max_walk = 0.0001f; + if (player_max_jump < 0.0001f) + player_max_jump = 0.0001f; + + v3f diff = (m_base_position - m_last_good_position); + float d_vert = diff.Y; + diff.Y = 0; + float d_horiz = diff.getLength(); + float required_time = d_horiz / player_max_walk; + + // FIXME: Checking downwards movement is not easily possible currently, + // the server could calculate speed differences to examine the gravity + if (d_vert > 0) { + // In certain cases (water, ladders) walking speed is applied vertically + float s = MYMAX(player_max_jump, player_max_walk); + required_time = MYMAX(required_time, d_vert / s); + } + + if (m_move_pool.grab(required_time)) { + m_last_good_position = m_base_position; + } else { + const float LAG_POOL_MIN = 5.0; + float lag_pool_max = m_env->getMaxLagEstimate() * 2.0; + lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN); + if (m_time_from_last_teleport > lag_pool_max) { + actionstream << "Player " << m_player->getName() + << " moved too fast; resetting position" + << std::endl; + cheated = true; + } + setBasePosition(m_last_good_position); + } + return cheated; +} + +bool PlayerSAO::getCollisionBox(aabb3f *toset) const +{ + //update collision box + toset->MinEdge = m_prop.collisionbox.MinEdge * BS; + toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS; + + toset->MinEdge += m_base_position; + toset->MaxEdge += m_base_position; + return true; +} + +bool PlayerSAO::getSelectionBox(aabb3f *toset) const +{ + if (!m_prop.is_visible || !m_prop.pointable) { + return false; + } + + toset->MinEdge = m_prop.selectionbox.MinEdge * BS; + toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS; + + return true; +} + +float PlayerSAO::getZoomFOV() const +{ + return m_prop.zoom_fov; +} diff --git a/src/server/player_sao.h b/src/server/player_sao.h new file mode 100644 index 000000000..ce1cb1677 --- /dev/null +++ b/src/server/player_sao.h @@ -0,0 +1,325 @@ + +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola +Copyright (C) 2013-2020 Minetest core developers & community + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "constants.h" +#include "network/networkprotocol.h" +#include "unit_sao.h" +#include "util/numeric.h" + +/* + PlayerSAO needs some internals exposed. +*/ + +class LagPool +{ + float m_pool = 15.0f; + float m_max = 15.0f; +public: + LagPool() = default; + + void setMax(float new_max) + { + m_max = new_max; + if(m_pool > new_max) + m_pool = new_max; + } + + void add(float dtime) + { + m_pool -= dtime; + if(m_pool < 0) + m_pool = 0; + } + + void empty() + { + m_pool = m_max; + } + + bool grab(float dtime) + { + if(dtime <= 0) + return true; + if(m_pool + dtime > m_max) + return false; + m_pool += dtime; + return true; + } +}; + +class RemotePlayer; + +class PlayerSAO : public UnitSAO +{ +public: + PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_, + bool is_singleplayer); + + ActiveObjectType getType() const + { return ACTIVEOBJECT_TYPE_PLAYER; } + ActiveObjectType getSendType() const + { return ACTIVEOBJECT_TYPE_GENERIC; } + std::string getDescription(); + + /* + Active object <-> environment interface + */ + + void addedToEnvironment(u32 dtime_s); + void removingFromEnvironment(); + bool isStaticAllowed() const { return false; } + std::string getClientInitializationData(u16 protocol_version); + void getStaticData(std::string *result) const; + void step(float dtime, bool send_recommended); + void setBasePosition(const v3f &position); + void setPos(const v3f &pos); + void moveTo(v3f pos, bool continuous); + void setPlayerYaw(const float yaw); + // Data should not be sent at player initialization + void setPlayerYawAndSend(const float yaw); + void setLookPitch(const float pitch); + // Data should not be sent at player initialization + void setLookPitchAndSend(const float pitch); + f32 getLookPitch() const { return m_pitch; } + f32 getRadLookPitch() const { return m_pitch * core::DEGTORAD; } + // Deprecated + f32 getRadLookPitchDep() const { return -1.0 * m_pitch * core::DEGTORAD; } + void setFov(const float pitch); + f32 getFov() const { return m_fov; } + void setWantedRange(const s16 range); + s16 getWantedRange() const { return m_wanted_range; } + + /* + Interaction interface + */ + + u16 punch(v3f dir, + const ToolCapabilities *toolcap, + ServerActiveObject *puncher, + float time_from_last_punch); + void rightClick(ServerActiveObject *clicker) {} + void setHP(s32 hp, const PlayerHPChangeReason &reason); + void setHPRaw(u16 hp) { m_hp = hp; } + s16 readDamage(); + u16 getBreath() const { return m_breath; } + void setBreath(const u16 breath, bool send = true); + + /* + Inventory interface + */ + Inventory *getInventory() const; + InventoryLocation getInventoryLocation() const; + void setInventoryModified() {} + std::string getWieldList() const { return "main"; } + u16 getWieldIndex() const; + ItemStack getWieldedItem(ItemStack *selected, ItemStack *hand = nullptr) const; + bool setWieldedItem(const ItemStack &item); + + /* + PlayerSAO-specific + */ + + void disconnected(); + + RemotePlayer *getPlayer() { return m_player; } + session_t getPeerID() const { return m_peer_id; } + + // Cheat prevention + + v3f getLastGoodPosition() const + { + return m_last_good_position; + } + float resetTimeFromLastPunch() + { + float r = m_time_from_last_punch; + m_time_from_last_punch = 0.0; + return r; + } + void noCheatDigStart(const v3s16 &p) + { + m_nocheat_dig_pos = p; + m_nocheat_dig_time = 0; + } + v3s16 getNoCheatDigPos() + { + return m_nocheat_dig_pos; + } + float getNoCheatDigTime() + { + return m_nocheat_dig_time; + } + void noCheatDigEnd() + { + m_nocheat_dig_pos = v3s16(32767, 32767, 32767); + } + LagPool& getDigPool() + { + return m_dig_pool; + } + void setMaxSpeedOverride(const v3f &vel); + // Returns true if cheated + bool checkMovementCheat(); + + // Other + + void updatePrivileges(const std::set &privs, + bool is_singleplayer) + { + m_privs = privs; + m_is_singleplayer = is_singleplayer; + } + + bool getCollisionBox(aabb3f *toset) const; + bool getSelectionBox(aabb3f *toset) const; + bool collideWithObjects() const { return true; } + + void finalize(RemotePlayer *player, const std::set &privs); + + v3f getEyePosition() const { return m_base_position + getEyeOffset(); } + v3f getEyeOffset() const; + float getZoomFOV() const; + + inline Metadata &getMeta() { return m_meta; } + +private: + std::string getPropertyPacket(); + void unlinkPlayerSessionAndSave(); + std::string generateUpdatePhysicsOverrideCommand() const; + + RemotePlayer *m_player = nullptr; + session_t m_peer_id = 0; + + // Cheat prevention + LagPool m_dig_pool; + LagPool m_move_pool; + v3f m_last_good_position; + float m_time_from_last_teleport = 0.0f; + float m_time_from_last_punch = 0.0f; + v3s16 m_nocheat_dig_pos = v3s16(32767, 32767, 32767); + float m_nocheat_dig_time = 0.0f; + float m_max_speed_override_time = 0.0f; + v3f m_max_speed_override = v3f(0.0f, 0.0f, 0.0f); + + // Timers + IntervalLimiter m_breathing_interval; + IntervalLimiter m_drowning_interval; + IntervalLimiter m_node_hurt_interval; + + bool m_position_not_sent = false; + + // Cached privileges for enforcement + std::set m_privs; + bool m_is_singleplayer; + + u16 m_breath = PLAYER_MAX_BREATH_DEFAULT; + f32 m_pitch = 0.0f; + f32 m_fov = 0.0f; + s16 m_wanted_range = 0.0f; + + Metadata m_meta; +public: + float m_physics_override_speed = 1.0f; + float m_physics_override_jump = 1.0f; + float m_physics_override_gravity = 1.0f; + bool m_physics_override_sneak = true; + bool m_physics_override_sneak_glitch = false; + bool m_physics_override_new_move = true; + bool m_physics_override_sent = false; +}; + + +struct PlayerHPChangeReason { + enum Type : u8 { + SET_HP, + PLAYER_PUNCH, + FALL, + NODE_DAMAGE, + DROWNING, + RESPAWN + }; + + Type type = SET_HP; + bool from_mod = false; + int lua_reference = -1; + + // For PLAYER_PUNCH + ServerActiveObject *object = nullptr; + // For NODE_DAMAGE + std::string node; + + inline bool hasLuaReference() const + { + return lua_reference >= 0; + } + + bool setTypeFromString(const std::string &typestr) + { + if (typestr == "set_hp") + type = SET_HP; + else if (typestr == "punch") + type = PLAYER_PUNCH; + else if (typestr == "fall") + type = FALL; + else if (typestr == "node_damage") + type = NODE_DAMAGE; + else if (typestr == "drown") + type = DROWNING; + else if (typestr == "respawn") + type = RESPAWN; + else + return false; + + return true; + } + + std::string getTypeAsString() const + { + switch (type) { + case PlayerHPChangeReason::SET_HP: + return "set_hp"; + case PlayerHPChangeReason::PLAYER_PUNCH: + return "punch"; + case PlayerHPChangeReason::FALL: + return "fall"; + case PlayerHPChangeReason::NODE_DAMAGE: + return "node_damage"; + case PlayerHPChangeReason::DROWNING: + return "drown"; + case PlayerHPChangeReason::RESPAWN: + return "respawn"; + default: + return "?"; + } + } + + PlayerHPChangeReason(Type type): + type(type) + {} + + PlayerHPChangeReason(Type type, ServerActiveObject *object): + type(type), object(object) + {} + + PlayerHPChangeReason(Type type, std::string node): + type(type), node(node) + {} +}; diff --git a/src/server/unit_sao.cpp b/src/server/unit_sao.cpp index b30b7a76b..74b0508b8 100644 --- a/src/server/unit_sao.cpp +++ b/src/server/unit_sao.cpp @@ -22,10 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "scripting_server.h" #include "serverenvironment.h" -/* - UnitSAO - */ - UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos) : ServerActiveObject(env, pos) { // Initialize something to armor groups diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 0ccbd772b..c2ab5c07d 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -17,8 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #include "serverenvironment.h" -#include "content_sao.h" #include "settings.h" #include "log.h" #include "mapblock.h" @@ -44,7 +44,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #if USE_POSTGRESQL #include "database/database-postgresql.h" #endif -#include +#include "server/player_sao.h" #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:" -- cgit v1.2.3 From 0ac999ded725f8efcd26db284161683e37efeecf Mon Sep 17 00:00:00 2001 From: DS Date: Mon, 13 Apr 2020 10:50:07 +0200 Subject: Add scroll_container formspec element (redo) (#9101) New formspec elements: - `scroll_container[,;,;;;]` - `scroll_container_end[]` Other elements can be embedded in this element. Scrollbar must be placed manually. --- build/android/jni/Android.mk | 1 + doc/lua_api.txt | 20 ++++ games/minimal/mods/test/formspec.lua | 69 ++++++++++- src/gui/CMakeLists.txt | 1 + src/gui/guiFormSpecMenu.cpp | 216 +++++++++++++++++++++++++++++------ src/gui/guiFormSpecMenu.h | 5 + src/gui/guiHyperText.cpp | 20 ++-- src/gui/guiHyperText.h | 2 +- src/gui/guiScrollContainer.cpp | 70 ++++++++++++ src/gui/guiScrollContainer.h | 56 +++++++++ src/network/networkprotocol.h | 1 + 11 files changed, 411 insertions(+), 50 deletions(-) create mode 100644 src/gui/guiScrollContainer.cpp create mode 100644 src/gui/guiScrollContainer.h (limited to 'src/network') diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index fdbfba9bc..b67322d79 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -194,6 +194,7 @@ LOCAL_SRC_FILES := \ jni/src/gui/guiPasswordChange.cpp \ jni/src/gui/guiPathSelectMenu.cpp \ jni/src/gui/guiScrollBar.cpp \ + jni/src/gui/guiScrollContainer.cpp \ jni/src/gui/guiSkin.cpp \ jni/src/gui/guiTable.cpp \ jni/src/gui/guiVolumeChange.cpp \ diff --git a/doc/lua_api.txt b/doc/lua_api.txt index dff6c728e..f43987cd8 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2102,6 +2102,26 @@ Elements * End of a container, following elements are no longer relative to this container. +### `scroll_container[,;,;;;]` + +* Start of a scroll_container block. All contained elements will ... + * take the scroll_container coordinate as position origin, + * be additionally moved by the current value of the scrollbar with the name + `scrollbar name` times `scroll factor` along the orientation `orientation` and + * be clipped to the rectangle defined by `X`, `Y`, `W` and `H`. +* `orientation`: possible values are `vertical` and `horizontal`. +* `scroll factor`: optional, defaults to `0.1`. +* Nesting is possible. +* Some elements might work a little different if they are in a scroll_container. +* Note: If you want the scroll_container to actually work, you also need to add a + scrollbar element with the specified name. Furthermore, it is highly recommended + to use a scrollbaroptions element on this scrollbar. + +### `scroll_container_end[]` + +* End of a scroll_container, following elements are no longer bound to this + container. + ### `list[;;,;,;]` * Show an inventory list if it has been sent to the client. Nothing will diff --git a/games/minimal/mods/test/formspec.lua b/games/minimal/mods/test/formspec.lua index 14886aad2..53e92b243 100644 --- a/games/minimal/mods/test/formspec.lua +++ b/games/minimal/mods/test/formspec.lua @@ -1,5 +1,35 @@ local color = minetest.colorize +local hypertext = minetest.formspec_escape([[ + +
Furnace
+ are crafted and used by the player for the purpose of cooking food and smelting various items. + + Solid block + Itself + No + Inactive:No Active:Yes (8) + No + No + Yes + Yes (99) + default:furnace default:furnace_active + Usage + The furnace menu can be accessed by using the furnace. + The furnace has 3 inventories: An input slot, a fuel slot and 4 output slots. The fire in the furnace will automatically start when there is a smeltable item in the input slot and a fuel in the fuel slot. + As long as the fire is on, the furnace will smelt any smeltable item in the input slot, one by one, until it is empty. When the fire goes off, it will smelt the next item until there are no smeltable items and no fuel items left. + The current stage of cooking can be seen by pointing the furnace or by viewing the furnace menu. In the furnace menu, the flame symbol roughly shows the remaining burning time. The arrow symbol shows the progress of the current smelting process. + Renewing + Furnaces can be crafted from e.g. cobblestone, a renewable resource. + Crafting + Sorry no way to display crafting yet in formspec pages. + Fuel + See Smelting for a list of furnace fuels. + Recipes + See the Smelting page. +]]) + + local clip_fs = [[ style_type[label,button,image_button,item_image_button, tabheader,scrollbar,table,animated_image @@ -188,13 +218,48 @@ Number] animated_image[3,4.25;1,1;;test_animation.png;4;0;3] animated_image[5.5,0.5;5,2;;test_animation.png;4;100] animated_image[5.5,2.75;5,2;;test_animation.jpg;4;100] - ]] + ]], + + "formspec_version[3]".. + "size[12,12]".. + "button[8.5,1;1,1;bla;Bla]".. + "box[1,1;8,6;#00aa]".. + "scroll_container[1,1;8,6;scrbar;vertical]".. + "button[0,1;1,1;lorem;Lorem]".. + "button[0,10;1,1;ipsum;Ipsum]".. + "pwdfield[2,2;1,1;lorem2;Lorem]".. + "list[current_player;main;4,4;1,5;]".. + "box[2,5;3,2;#ffff00]".. + "image[1,10;3,2;bubble.png]".. + "image[3,1;bubble.png]".. + "item_image[2,6;3,2;default:mese]".. + "label[2,15;bla Bli\nfoo bar]".. + "item_image_button[2,3;1,1;default:dirt_with_grass;itemimagebutton;ItemImageButton]".. + "tooltip[0,11;3,2;Buz;#f00;#000]".. + "box[0,11;3,2;#00ff00]".. + "hypertext[3,13;3,3;;" .. hypertext .. "]" .. + "container[0,18]".. + "box[1,2;3,2;#0a0a]".. + "scroll_container[1,2;3,2;scrbar2;horizontal;0.06]".. + "button[0,0;6,1;butnest;Nest]".. + "label[10,0.5;nest]".. + "scroll_container_end[]".. + "scrollbar[1,0;3.5,0.3;horizontal;scrbar2;0]".. + "container_end[]".. + "dropdown[0,6;2;hmdrpdwn;apple,bulb;1]".. + "image_button[0,4;2,2;bubble.png;bubblebutton;bbbbtt;false;true;heart.png]".. + "box[1,22.5;4,1;#a00a]".. + "scroll_container_end[]".. + "scrollbaroptions[max=170]".. -- lowest seen pos is: 0.1*170+6=23 (factor*max+height) + "scrollbar[7.5,0;0.3,4;vertical;scrbar;0]".. + "scrollbar[8,0;0.3,4;vertical;scrbarhmmm;0]".. + "dropdown[0,6;2;hmdrpdwnnn;apple,bulb;1]", } local function show_test_formspec(pname, page_id) page_id = page_id or 2 - local fs = pages[page_id] .. "tabheader[0,0;6,0.65;maintabs;Real Coord,Styles,Noclip,MiscEle;" .. page_id .. ";false;false]" + local fs = pages[page_id] .. "tabheader[0,0;8,0.65;maintabs;Real Coord,Styles,Noclip,MiscEle,Scroll Container;" .. page_id .. ";false;false]" minetest.show_formspec(pname, "test:formspec", fs) end diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 110a00595..147f445f4 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -16,6 +16,7 @@ set(gui_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/guiPasswordChange.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiPathSelectMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiScrollBar.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/guiScrollContainer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiSkin.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiTable.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiHyperText.cpp diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index f3bf04275..aac039ad6 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -65,6 +65,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiInventoryList.h" #include "guiItemImage.h" #include "guiScrollBar.h" +#include "guiScrollContainer.h" #include "guiTable.h" #include "intlGUIEditBox.h" #include "guiHyperText.h" @@ -143,6 +144,8 @@ GUIFormSpecMenu::~GUIFormSpecMenu() tooltip_rect_it.first->drop(); for (auto &clickthrough_it : m_clickthrough_elements) clickthrough_it->drop(); + for (auto &scroll_container_it : m_scroll_containers) + scroll_container_it.second->drop(); delete m_selected_item; delete m_form_src; @@ -351,6 +354,102 @@ void GUIFormSpecMenu::parseContainerEnd(parserData* data) } } +void GUIFormSpecMenu::parseScrollContainer(parserData *data, const std::string &element) +{ + std::vector parts = split(element, ';'); + + if (parts.size() < 4 || + (parts.size() > 5 && m_formspec_version <= FORMSPEC_API_VERSION)) { + errorstream << "Invalid scroll_container start element (" << parts.size() + << "): '" << element << "'" << std::endl; + return; + } + + std::vector v_pos = split(parts[0], ','); + std::vector v_geom = split(parts[1], ','); + std::string scrollbar_name = parts[2]; + std::string orientation = parts[3]; + f32 scroll_factor = 0.1f; + if (parts.size() >= 5 && !parts[4].empty()) + scroll_factor = stof(parts[4]); + + MY_CHECKPOS("scroll_container", 0); + MY_CHECKGEOM("scroll_container", 1); + + v2s32 pos = getRealCoordinateBasePos(v_pos); + v2s32 geom = getRealCoordinateGeometry(v_geom); + + if (orientation == "vertical") + scroll_factor *= -imgsize.Y; + else if (orientation == "horizontal") + scroll_factor *= -imgsize.X; + else + warningstream << "GUIFormSpecMenu::parseScrollContainer(): " + << "Invalid scroll_container orientation: " << orientation + << std::endl; + + // old parent (at first: this) + // ^ is parent of clipper + // ^ is parent of mover + // ^ is parent of other elements + + // make clipper + core::rect rect_clipper = core::rect(pos, pos + geom); + + gui::IGUIElement *clipper = new gui::IGUIElement(EGUIET_ELEMENT, Environment, + data->current_parent, 0, rect_clipper); + + // make mover + FieldSpec spec_mover( + "", + L"", + L"", + 258 + m_fields.size() + ); + + core::rect rect_mover = core::rect(0, 0, geom.X, geom.Y); + + GUIScrollContainer *mover = new GUIScrollContainer(Environment, + clipper, spec_mover.fid, rect_mover, orientation, scroll_factor); + + data->current_parent = mover; + + m_scroll_containers.emplace_back(scrollbar_name, mover); + + m_fields.push_back(spec_mover); + + clipper->drop(); + + // remove interferring offset of normal containers + container_stack.push(pos_offset); + pos_offset.X = 0.0f; + pos_offset.Y = 0.0f; +} + +void GUIFormSpecMenu::parseScrollContainerEnd(parserData *data) +{ + if (data->current_parent == this || data->current_parent->getParent() == this || + container_stack.empty()) { + errorstream << "Invalid scroll_container end element, " + << "no matching scroll_container start element" << std::endl; + return; + } + + if (pos_offset.getLengthSQ() != 0.0f) { + // pos_offset is only set by containers and scroll_containers. + // scroll_containers always set it to 0,0 which means that if it is + // not 0,0, it is a normal container that was opened last, not a + // scroll_container + errorstream << "Invalid scroll_container end element, " + << "an inner container was left open" << std::endl; + return; + } + + data->current_parent = data->current_parent->getParent()->getParent(); + pos_offset = container_stack.top(); + container_stack.pop(); +} + void GUIFormSpecMenu::parseList(parserData *data, const std::string &element) { if (m_client == 0) { @@ -443,9 +542,9 @@ void GUIFormSpecMenu::parseList(parserData *data, const std::string &element) pos.X + (geom.X - 1) * slot_spacing.X + imgsize.X, pos.Y + (geom.Y - 1) * slot_spacing.Y + imgsize.Y); - GUIInventoryList *e = new GUIInventoryList(Environment, this, spec.fid, - rect, m_invmgr, loc, listname, geom, start_i, imgsize, slot_spacing, - this, data->inventorylist_options, m_font); + GUIInventoryList *e = new GUIInventoryList(Environment, data->current_parent, + spec.fid, rect, m_invmgr, loc, listname, geom, start_i, imgsize, + slot_spacing, this, data->inventorylist_options, m_font); m_inventorylists.push_back(e); m_fields.push_back(spec); @@ -550,8 +649,8 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element spec.ftype = f_CheckBox; - gui::IGUICheckBox *e = Environment->addCheckBox(fselected, rect, this, - spec.fid, spec.flabel.c_str()); + gui::IGUICheckBox *e = Environment->addCheckBox(fselected, rect, + data->current_parent, spec.fid, spec.flabel.c_str()); auto style = getDefaultStyleForElement("checkbox", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); @@ -610,8 +709,8 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &elemen spec.ftype = f_ScrollBar; spec.send = true; - GUIScrollBar *e = new GUIScrollBar(Environment, this, spec.fid, rect, - is_horizontal, true); + GUIScrollBar *e = new GUIScrollBar(Environment, data->current_parent, + spec.fid, rect, is_horizontal, true); auto style = getDefaultStyleForElement("scrollbar", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); @@ -737,7 +836,8 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) 1 ); core::rect rect(pos, pos + geom); - gui::IGUIImage *e = Environment->addImage(rect, this, spec.fid, 0, true); + gui::IGUIImage *e = Environment->addImage(rect, data->current_parent, + spec.fid, 0, true); e->setImage(texture); e->setScaleImage(true); auto style = getDefaultStyleForElement("image", spec.fname); @@ -774,8 +874,8 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) L"", 258 + m_fields.size() ); - gui::IGUIImage *e = Environment->addImage(texture, pos, true, this, - spec.fid, 0); + gui::IGUIImage *e = Environment->addImage(texture, pos, true, + data->current_parent, spec.fid, 0); auto style = getDefaultStyleForElement("image", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); m_fields.push_back(spec); @@ -886,7 +986,7 @@ void GUIFormSpecMenu::parseItemImage(parserData* data, const std::string &elemen ); spec.ftype = f_ItemImage; - GUIItemImage *e = new GUIItemImage(Environment, this, spec.fid, + GUIItemImage *e = new GUIItemImage(Environment, data->current_parent, spec.fid, core::rect(pos, pos + geom), name, m_font, m_client); auto style = getDefaultStyleForElement("item_image", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); @@ -949,8 +1049,8 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element, if(type == "button_exit") spec.is_exit = true; - GUIButton *e = GUIButton::addButton(Environment, rect, m_tsrc, this, - spec.fid, spec.flabel.c_str()); + GUIButton *e = GUIButton::addButton(Environment, rect, m_tsrc, + data->current_parent, spec.fid, spec.flabel.c_str()); auto style = getStyleForElement(type, name, (type != "button") ? "button" : ""); e->setStyles(style); @@ -1141,7 +1241,8 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element) } //now really show table - GUITable *e = new GUITable(Environment, this, spec.fid, rect, m_tsrc); + GUITable *e = new GUITable(Environment, data->current_parent, spec.fid, + rect, m_tsrc); if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); @@ -1217,7 +1318,8 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element } //now really show list - GUITable *e = new GUITable(Environment, this, spec.fid, rect, m_tsrc); + GUITable *e = new GUITable(Environment, data->current_parent, spec.fid, + rect, m_tsrc); if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); @@ -1293,7 +1395,8 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element spec.send = true; //now really show list - gui::IGUIComboBox *e = Environment->addComboBox(rect, this, spec.fid); + gui::IGUIComboBox *e = Environment->addComboBox(rect, data->current_parent, + spec.fid); if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); @@ -1379,7 +1482,8 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element ); spec.send = true; - gui::IGUIEditBox * e = Environment->addEditBox(0, rect, true, this, spec.fid); + gui::IGUIEditBox *e = Environment->addEditBox(0, rect, true, + data->current_parent, spec.fid); if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); @@ -1390,7 +1494,7 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element rect.UpperLeftCorner.Y -= font_height; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true, - this, 0); + data->current_parent, 0); } e->setPasswordBox(true,L'*'); @@ -1425,7 +1529,7 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec, if (!is_editable && !is_multiline) { // spec field id to 0, this stops submit searching for a value that isn't there gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true, - this, 0); + data->current_parent, 0); return; } @@ -1443,14 +1547,14 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec, if (use_intl_edit_box && g_settings->getBool("freetype")) { e = new gui::intlGUIEditBox(spec.fdefault.c_str(), true, Environment, - this, spec.fid, rect, is_editable, is_multiline); + data->current_parent, spec.fid, rect, is_editable, is_multiline); } else { if (is_multiline) { - e = new GUIEditBoxWithScrollBar(spec.fdefault.c_str(), true, - Environment, this, spec.fid, rect, is_editable, true); + e = new GUIEditBoxWithScrollBar(spec.fdefault.c_str(), true, Environment, + data->current_parent, spec.fid, rect, is_editable, true); } else if (is_editable) { - e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, - spec.fid); + e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, + data->current_parent, spec.fid); e->grab(); } } @@ -1491,7 +1595,7 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec, rect.UpperLeftCorner.Y -= font_height; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; IGUIElement *t = gui::StaticText::add(Environment, spec.flabel.c_str(), - rect, false, true, this, 0); + rect, false, true, data->current_parent, 0); if (t) t->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); @@ -1671,8 +1775,8 @@ void GUIFormSpecMenu::parseHyperText(parserData *data, const std::string &elemen ); spec.ftype = f_HyperText; - GUIHyperText *e = new GUIHyperText(spec.flabel.c_str(), Environment, this, - spec.fid, rect, m_client, m_tsrc); + GUIHyperText *e = new GUIHyperText(spec.flabel.c_str(), Environment, + data->current_parent, spec.fid, rect, m_client, m_tsrc); e->drop(); m_fields.push_back(spec); @@ -1750,7 +1854,8 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) 4 ); gui::IGUIStaticText *e = gui::StaticText::add(Environment, - spec.flabel.c_str(), rect, false, false, this, spec.fid); + spec.flabel.c_str(), rect, false, false, data->current_parent, + spec.fid); e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER); auto style = getDefaultStyleForElement("label", spec.fname); @@ -1830,7 +1935,7 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen 258 + m_fields.size() ); gui::IGUIStaticText *e = gui::StaticText::add(Environment, spec.flabel.c_str(), - rect, false, false, this, spec.fid); + rect, false, false, data->current_parent, spec.fid); e->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); auto style = getDefaultStyleForElement("vertlabel", spec.fname, "label"); @@ -1904,7 +2009,7 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem spec.is_exit = true; GUIButtonImage *e = GUIButtonImage::addButton(Environment, rect, m_tsrc, - this, spec.fid, spec.flabel.c_str()); + data->current_parent, spec.fid, spec.flabel.c_str()); if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); @@ -2010,8 +2115,8 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); - gui::IGUITabControl *e = Environment->addTabControl(rect, this, - show_background, show_border, spec.fid); + gui::IGUITabControl *e = Environment->addTabControl(rect, + data->current_parent, show_background, show_border, spec.fid); e->setAlignment(irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT); e->setTabHeight(geom.Y); @@ -2046,7 +2151,6 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &element) { - if (m_client == 0) { warningstream << "invalid use of item_image_button with m_client==0" << std::endl; @@ -2106,7 +2210,7 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string & ); GUIButtonItemImage *e_btn = GUIButtonItemImage::addButton(Environment, - rect, m_tsrc, this, spec_btn.fid, spec_btn.flabel.c_str(), + rect, m_tsrc, data->current_parent, spec_btn.fid, spec_btn.flabel.c_str(), item_name, m_client); auto style = getStyleForElement("item_image_button", spec_btn.fname, "image_button"); @@ -2164,7 +2268,8 @@ void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element) core::rect rect(pos, pos + geom); - GUIBox *e = new GUIBox(Environment, this, spec.fid, rect, tmp_color); + GUIBox *e = new GUIBox(Environment, data->current_parent, spec.fid, + rect, tmp_color); auto style = getDefaultStyleForElement("box", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); @@ -2316,7 +2421,7 @@ void GUIFormSpecMenu::parseTooltip(parserData* data, const std::string &element) core::rect rect(pos, pos + geom); gui::IGUIElement *e = new gui::IGUIElement(EGUIET_ELEMENT, Environment, - this, fieldspec.fid, rect); + data->current_parent, fieldspec.fid, rect); // the element the rect tooltip is bound to should not block mouse-clicks e->setVisible(false); @@ -2775,6 +2880,16 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element) return; } + if (type == "scroll_container") { + parseScrollContainer(data, description); + return; + } + + if (type == "scroll_container_end") { + parseScrollContainerEnd(data); + return; + } + // Ignore others infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\"" << std::endl; @@ -2831,8 +2946,10 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) tooltip_rect_it.first->drop(); for (auto &clickthrough_it : m_clickthrough_elements) clickthrough_it->drop(); + for (auto &scroll_container_it : m_scroll_containers) + scroll_container_it.second->drop(); - mydata.size= v2s32(100,100); + mydata.size = v2s32(100, 100); mydata.screensize = screensize; mydata.offset = v2f32(0.5f, 0.5f); mydata.anchor = v2f32(0.5f, 0.5f); @@ -2841,6 +2958,9 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) // Base position of contents of form mydata.basepos = getBasePos(); + // the parent for the parsed elements + mydata.current_parent = this; + m_inventorylists.clear(); m_backgrounds.clear(); m_tables.clear(); @@ -2851,6 +2971,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) m_tooltip_rects.clear(); m_inventory_rings.clear(); m_dropdowns.clear(); + m_scroll_containers.clear(); theme_by_name.clear(); theme_by_type.clear(); m_clickthrough_elements.clear(); @@ -3117,11 +3238,24 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) parseElement(&mydata, elements[i]); } - if (!container_stack.empty()) { + if (mydata.current_parent != this) { + errorstream << "Invalid formspec string: scroll_container was never closed!" + << std::endl; + } else if (!container_stack.empty()) { errorstream << "Invalid formspec string: container was never closed!" << std::endl; } + // get the scrollbar elements for scroll_containers + for (const std::pair &c : m_scroll_containers) { + for (const std::pair &b : m_scrollbars) { + if (c.first == b.first.fname) { + c.second->setScrollBar(b.second); + break; + } + } + } + // If there are fields without explicit size[], add a "Proceed" // button and adjust size to fit all the fields. if (mydata.simple_field_count > 0 && !mydata.explicit_size) { @@ -4418,6 +4552,12 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } } + if (event.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED) { + // move scroll_containers + for (const std::pair &c : m_scroll_containers) + c.second->onScrollEvent(event.GUIEvent.Caller); + } + if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) { if (event.GUIEvent.Caller->getID() > 257) { bool close_on_enter = true; diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index dc22e8b54..28088416d 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -39,6 +39,7 @@ class ISimpleTextureSource; class Client; class GUIScrollBar; class TexturePool; +class GUIScrollContainer; typedef enum { f_Button, @@ -310,6 +311,7 @@ protected: std::vector> m_scrollbars; std::vector>> m_dropdowns; std::vector m_clickthrough_elements; + std::vector> m_scroll_containers; GUIInventoryList::ItemSpec *m_selected_item = nullptr; u16 m_selected_amount = 0; @@ -359,6 +361,7 @@ private: std::string focused_fieldname; GUITable::TableOptions table_options; GUITable::TableColumns table_columns; + gui::IGUIElement *current_parent = nullptr; GUIInventoryList::Options inventorylist_options; @@ -391,6 +394,8 @@ private: void parseSize(parserData* data, const std::string &element); void parseContainer(parserData* data, const std::string &element); void parseContainerEnd(parserData* data); + void parseScrollContainer(parserData *data, const std::string &element); + void parseScrollContainerEnd(parserData *data); void parseList(parserData* data, const std::string &element); void parseListRing(parserData* data, const std::string &element); void parseCheckbox(parserData* data, const std::string &element); diff --git a/src/gui/guiHyperText.cpp b/src/gui/guiHyperText.cpp index e107b5a3e..88931cdf9 100644 --- a/src/gui/guiHyperText.cpp +++ b/src/gui/guiHyperText.cpp @@ -917,20 +917,20 @@ void TextDrawer::place(const core::rect &dest_rect) // Draw text in a rectangle with a given offset. Items are actually placed in // relative (to upper left corner) coordinates. -void TextDrawer::draw(const core::rect &dest_rect, +void TextDrawer::draw(const core::rect &clip_rect, const core::position2d &dest_offset) { irr::video::IVideoDriver *driver = m_environment->getVideoDriver(); - core::position2d offset = dest_rect.UpperLeftCorner + dest_offset; + core::position2d offset = dest_offset; offset.Y += m_voffset; if (m_text.background_type == ParsedText::BACKGROUND_COLOR) - driver->draw2DRectangle(m_text.background_color, dest_rect); + driver->draw2DRectangle(m_text.background_color, clip_rect); for (auto &p : m_text.m_paragraphs) { for (auto &el : p.elements) { core::rect rect(el.pos + offset, el.dim); - if (!rect.isRectCollided(dest_rect)) + if (!rect.isRectCollided(clip_rect)) continue; switch (el.type) { @@ -947,7 +947,7 @@ void TextDrawer::draw(const core::rect &dest_rect, if (el.type == ParsedText::ELEMENT_TEXT) el.font->draw(el.text, rect, color, false, true, - &dest_rect); + &clip_rect); if (el.underline && el.drawwidth) { s32 linepos = el.pos.Y + offset.Y + @@ -958,7 +958,7 @@ void TextDrawer::draw(const core::rect &dest_rect, el.pos.X + offset.X + el.drawwidth, linepos + (el.baseline >> 3)); - driver->draw2DRectangle(color, linerect, &dest_rect); + driver->draw2DRectangle(color, linerect, &clip_rect); } } break; @@ -972,7 +972,7 @@ void TextDrawer::draw(const core::rect &dest_rect, irr::core::rect( core::position2d(0, 0), texture->getOriginalSize()), - &dest_rect, 0, true); + &clip_rect, 0, true); } break; case ParsedText::ELEMENT_ITEM: { @@ -982,7 +982,7 @@ void TextDrawer::draw(const core::rect &dest_rect, drawItemStack( m_environment->getVideoDriver(), - g_fontengine->getFont(), item, rect, &dest_rect, + g_fontengine->getFont(), item, rect, &clip_rect, m_client, IT_ROT_OTHER, el.angle, el.rotation ); } break; @@ -1094,6 +1094,7 @@ bool GUIHyperText::OnEvent(const SEvent &event) m_text_scrollpos.Y = -m_vscrollbar->getPos(); m_drawer.draw(m_display_text_rect, m_text_scrollpos); checkHover(event.MouseInput.X, event.MouseInput.Y); + return true; } else if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { ParsedText::Element *element = getElementAt( @@ -1151,7 +1152,8 @@ void GUIHyperText::draw() m_vscrollbar->setPos(0); m_vscrollbar->setVisible(false); } - m_drawer.draw(m_display_text_rect, m_text_scrollpos); + m_drawer.draw(AbsoluteClippingRect, + m_display_text_rect.UpperLeftCorner + m_text_scrollpos); // draw children IGUIElement::draw(); diff --git a/src/gui/guiHyperText.h b/src/gui/guiHyperText.h index c55f8a705..5b936262e 100644 --- a/src/gui/guiHyperText.h +++ b/src/gui/guiHyperText.h @@ -174,7 +174,7 @@ public: void place(const core::rect &dest_rect); inline s32 getHeight() { return m_height; }; - void draw(const core::rect &dest_rect, + void draw(const core::rect &clip_rect, const core::position2d &dest_offset); ParsedText::Element *getElementAt(core::position2d pos); ParsedText::Tag *m_hovertag; diff --git a/src/gui/guiScrollContainer.cpp b/src/gui/guiScrollContainer.cpp new file mode 100644 index 000000000..88cdc7057 --- /dev/null +++ b/src/gui/guiScrollContainer.cpp @@ -0,0 +1,70 @@ +/* +Minetest +Copyright (C) 2020 DS + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "guiScrollContainer.h" + +GUIScrollContainer::GUIScrollContainer(gui::IGUIEnvironment *env, + gui::IGUIElement *parent, s32 id, const core::rect &rectangle, + const std::string &orientation, f32 scrollfactor) : + gui::IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rectangle), + m_scrollbar(nullptr), m_scrollfactor(scrollfactor) +{ + if (orientation == "vertical") + m_orientation = VERTICAL; + else if (orientation == "horizontal") + m_orientation = HORIZONTAL; + else + m_orientation = UNDEFINED; +} + +bool GUIScrollContainer::OnEvent(const SEvent &event) +{ + if (event.EventType == EET_MOUSE_INPUT_EVENT && + event.MouseInput.Event == EMIE_MOUSE_WHEEL && + !event.MouseInput.isLeftPressed() && m_scrollbar) { + Environment->setFocus(m_scrollbar); + bool retval = m_scrollbar->OnEvent(event); + + // a hacky fix for updating the hovering and co. + IGUIElement *hovered_elem = getElementFromPoint(core::position2d( + event.MouseInput.X, event.MouseInput.Y)); + SEvent mov_event = event; + mov_event.MouseInput.Event = EMIE_MOUSE_MOVED; + Environment->postEventFromUser(mov_event); + if (hovered_elem) + hovered_elem->OnEvent(mov_event); + + return retval; + } + + return IGUIElement::OnEvent(event); +} + +void GUIScrollContainer::updateScrolling() +{ + s32 pos = m_scrollbar->getPos(); + core::rect rect = getRelativePosition(); + + if (m_orientation == VERTICAL) + rect.UpperLeftCorner.Y = pos * m_scrollfactor; + else if (m_orientation == HORIZONTAL) + rect.UpperLeftCorner.X = pos * m_scrollfactor; + + setRelativePosition(rect); +} diff --git a/src/gui/guiScrollContainer.h b/src/gui/guiScrollContainer.h new file mode 100644 index 000000000..9eaa880bf --- /dev/null +++ b/src/gui/guiScrollContainer.h @@ -0,0 +1,56 @@ +/* +Minetest +Copyright (C) 2020 DS + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "irrlichttypes_extrabloated.h" +#include "util/string.h" +#include "guiScrollBar.h" + +class GUIScrollContainer : public gui::IGUIElement +{ +public: + GUIScrollContainer(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, + const core::rect &rectangle, const std::string &orientation, + f32 scrollfactor); + + virtual bool OnEvent(const SEvent &event) override; + + inline void onScrollEvent(gui::IGUIElement *caller) + { + if (caller == m_scrollbar) + updateScrolling(); + } + + inline void setScrollBar(GUIScrollBar *scrollbar) { m_scrollbar = scrollbar; } + +private: + enum OrientationEnum + { + VERTICAL, + HORIZONTAL, + UNDEFINED + }; + + GUIScrollBar *m_scrollbar; + OrientationEnum m_orientation; + f32 m_scrollfactor; + + void updateScrolling(); +}; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 7223ce05c..4b7345b15 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -237,6 +237,7 @@ with this program; if not, write to the Free Software Foundation, Inc., Formspec elements are drawn in the order of definition bgcolor[]: use 3 parameters (bgcolor, formspec (now an enum), fbgcolor) box[] and image[] elements enable clipping by default + new element: scroll_container[] */ #define FORMSPEC_API_VERSION 3 -- cgit v1.2.3 From 7b57d3f613bab05914d3f7210064a3282ea37013 Mon Sep 17 00:00:00 2001 From: HybridDog <3192173+HybridDog@users.noreply.github.com> Date: Sat, 18 Apr 2020 17:19:53 +0200 Subject: serverpackethandler: Reduce pkt->getPeerId() invocations and more (#9689) --- src/network/serverpackethandler.cpp | 613 ++++++++++++++++-------------------- src/server.h | 1 - 2 files changed, 272 insertions(+), 342 deletions(-) (limited to 'src/network') diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index b2fdb2a22..c685500ce 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -52,11 +52,12 @@ void Server::handleCommand_Init(NetworkPacket* pkt) if(pkt->getSize() < 1) return; - RemoteClient* client = getClient(pkt->getPeerId(), CS_Created); + session_t peer_id = pkt->getPeerId(); + RemoteClient *client = getClient(peer_id, CS_Created); std::string addr_s; try { - Address address = getPeerAddress(pkt->getPeerId()); + Address address = getPeerAddress(peer_id); addr_s = address.serializeString(); } catch (con::PeerNotFoundException &e) { @@ -66,27 +67,27 @@ void Server::handleCommand_Init(NetworkPacket* pkt) * respond for some time, your server was overloaded or * things like that. */ - infostream << "Server::ProcessData(): Canceling: peer " - << pkt->getPeerId() << " not found" << std::endl; + infostream << "Server::ProcessData(): Canceling: peer " << peer_id << + " not found" << std::endl; return; } // If net_proto_version is set, this client has already been handled if (client->getState() > CS_Created) { - verbosestream << "Server: Ignoring multiple TOSERVER_INITs from " - << addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl; + verbosestream << "Server: Ignoring multiple TOSERVER_INITs from " << + addr_s << " (peer_id=" << peer_id << ")" << std::endl; return; } - verbosestream << "Server: Got TOSERVER_INIT from " << addr_s << " (peer_id=" - << pkt->getPeerId() << ")" << std::endl; + verbosestream << "Server: Got TOSERVER_INIT from " << addr_s << + " (peer_id=" << peer_id << ")" << std::endl; // Do not allow multiple players in simple singleplayer mode. // This isn't a perfect way to do it, but will suffice for now if (m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1) { - infostream << "Server: Not allowing another client (" << addr_s - << ") to connect in simple singleplayer mode" << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SINGLEPLAYER); + infostream << "Server: Not allowing another client (" << addr_s << + ") to connect in simple singleplayer mode" << std::endl; + DenyAccess(peer_id, SERVER_ACCESSDENIED_SINGLEPLAYER); return; } @@ -109,11 +110,11 @@ void Server::handleCommand_Init(NetworkPacket* pkt) depl_serial_v = SER_FMT_VER_INVALID; if (depl_serial_v == SER_FMT_VER_INVALID) { - actionstream << "Server: A mismatched client tried to connect from " - << addr_s << std::endl; - infostream<<"Server: Cannot negotiate serialization version with " - << addr_s << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_VERSION); + actionstream << "Server: A mismatched client tried to connect from " << + addr_s << std::endl; + infostream << "Server: Cannot negotiate serialization version with " << + addr_s << " client_max=" << (int)client_max << std::endl; + DenyAccess(peer_id, SERVER_ACCESSDENIED_WRONG_VERSION); return; } @@ -146,9 +147,9 @@ void Server::handleCommand_Init(NetworkPacket* pkt) net_proto_version != LATEST_PROTOCOL_VERSION) || net_proto_version < SERVER_PROTOCOL_VERSION_MIN || net_proto_version > SERVER_PROTOCOL_VERSION_MAX) { - actionstream << "Server: A mismatched client tried to connect from " - << addr_s << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_VERSION); + actionstream << "Server: A mismatched client tried to connect from " << + addr_s << std::endl; + DenyAccess(peer_id, SERVER_ACCESSDENIED_WRONG_VERSION); return; } @@ -159,56 +160,56 @@ void Server::handleCommand_Init(NetworkPacket* pkt) size_t pns = playerName.size(); if (pns == 0 || pns > PLAYERNAME_SIZE) { - actionstream << "Server: Player with " - << ((pns > PLAYERNAME_SIZE) ? "a too long" : "an empty") - << " name tried to connect from " << addr_s << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_NAME); + actionstream << "Server: Player with " << + ((pns > PLAYERNAME_SIZE) ? "a too long" : "an empty") << + " name tried to connect from " << addr_s << std::endl; + DenyAccess(peer_id, SERVER_ACCESSDENIED_WRONG_NAME); return; } if (!string_allowed(playerName, PLAYERNAME_ALLOWED_CHARS)) { - actionstream << "Server: Player with an invalid name " - << "tried to connect from " << addr_s << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_CHARS_IN_NAME); + actionstream << "Server: Player with an invalid name tried to connect " + "from " << addr_s << std::endl; + DenyAccess(peer_id, SERVER_ACCESSDENIED_WRONG_CHARS_IN_NAME); return; } - m_clients.setPlayerName(pkt->getPeerId(), playername); + m_clients.setPlayerName(peer_id, playername); //TODO (later) case insensitivity std::string legacyPlayerNameCasing = playerName; if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) { - actionstream << "Server: Player with the name \"singleplayer\" " - << "tried to connect from " << addr_s << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_NAME); + actionstream << "Server: Player with the name \"singleplayer\" tried " + "to connect from " << addr_s << std::endl; + DenyAccess(peer_id, SERVER_ACCESSDENIED_WRONG_NAME); return; } { std::string reason; if (m_script->on_prejoinplayer(playername, addr_s, &reason)) { - actionstream << "Server: Player with the name \"" << playerName << "\" " - << "tried to connect from " << addr_s << " " - << "but it was disallowed for the following reason: " - << reason << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING, reason); + actionstream << "Server: Player with the name \"" << playerName << + "\" tried to connect from " << addr_s << + " but it was disallowed for the following reason: " << reason << + std::endl; + DenyAccess(peer_id, SERVER_ACCESSDENIED_CUSTOM_STRING, reason); return; } } - infostream << "Server: New connection: \"" << playerName << "\" from " - << addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl; + infostream << "Server: New connection: \"" << playerName << "\" from " << + addr_s << " (peer_id=" << peer_id << ")" << std::endl; // Enforce user limit. // Don't enforce for users that have some admin right or mod permits it. if (m_clients.isUserLimitReached() && playername != g_settings->get("name") && !m_script->can_bypass_userlimit(playername, addr_s)) { - actionstream << "Server: " << playername << " tried to join from " - << addr_s << ", but there" << " are already max_users=" - << g_settings->getU16("max_users") << " players." << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_TOO_MANY_USERS); + actionstream << "Server: " << playername << " tried to join from " << + addr_s << ", but there are already max_users=" << + g_settings->getU16("max_users") << " players." << std::endl; + DenyAccess(peer_id, SERVER_ACCESSDENIED_TOO_MANY_USERS); return; } @@ -228,20 +229,19 @@ void Server::handleCommand_Init(NetworkPacket* pkt) auth_mechs |= AUTH_MECHANISM_SRP; client->enc_pwd = encpwd; } else { - actionstream << "User " << playername - << " tried to log in, but password field" - << " was invalid (unknown mechcode)." << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL); + actionstream << "User " << playername << " tried to log in, " + "but password field was invalid (unknown mechcode)." << + std::endl; + DenyAccess(peer_id, SERVER_ACCESSDENIED_SERVER_FAIL); return; } } else if (base64_is_valid(encpwd)) { auth_mechs |= AUTH_MECHANISM_LEGACY_PASSWORD; client->enc_pwd = encpwd; } else { - actionstream << "User " << playername - << " tried to log in, but password field" - << " was invalid (invalid base64)." << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL); + actionstream << "User " << playername << " tried to log in, but " + "password field was invalid (invalid base64)." << std::endl; + DenyAccess(peer_id, SERVER_ACCESSDENIED_SERVER_FAIL); return; } } else { @@ -264,8 +264,8 @@ void Server::handleCommand_Init(NetworkPacket* pkt) verbosestream << "Sending TOCLIENT_HELLO with auth method field: " << auth_mechs << std::endl; - NetworkPacket resp_pkt(TOCLIENT_HELLO, 1 + 4 - + legacyPlayerNameCasing.size(), pkt->getPeerId()); + NetworkPacket resp_pkt(TOCLIENT_HELLO, + 1 + 4 + legacyPlayerNameCasing.size(), peer_id); u16 depl_compress_mode = NETPROTO_COMPRESSION_NONE; resp_pkt << depl_serial_v << depl_compress_mode << net_proto_version @@ -276,16 +276,16 @@ void Server::handleCommand_Init(NetworkPacket* pkt) client->allowed_auth_mechs = auth_mechs; client->setDeployedCompressionMode(depl_compress_mode); - m_clients.event(pkt->getPeerId(), CSE_Hello); + m_clients.event(peer_id, CSE_Hello); } void Server::handleCommand_Init2(NetworkPacket* pkt) { - verbosestream << "Server: Got TOSERVER_INIT2 from " - << pkt->getPeerId() << std::endl; + session_t peer_id = pkt->getPeerId(); + verbosestream << "Server: Got TOSERVER_INIT2 from " << peer_id << std::endl; - m_clients.event(pkt->getPeerId(), CSE_GotInit2); - u16 protocol_version = m_clients.getProtocolVersion(pkt->getPeerId()); + m_clients.event(peer_id, CSE_GotInit2); + u16 protocol_version = m_clients.getProtocolVersion(peer_id); std::string lang; if (pkt->getSize() > 0) @@ -295,45 +295,45 @@ void Server::handleCommand_Init2(NetworkPacket* pkt) Send some initialization data */ - infostream << "Server: Sending content to " - << getPlayerName(pkt->getPeerId()) << std::endl; + infostream << "Server: Sending content to " << getPlayerName(peer_id) << + std::endl; // Send item definitions - SendItemDef(pkt->getPeerId(), m_itemdef, protocol_version); + SendItemDef(peer_id, m_itemdef, protocol_version); // Send node definitions - SendNodeDef(pkt->getPeerId(), m_nodedef, protocol_version); + SendNodeDef(peer_id, m_nodedef, protocol_version); - m_clients.event(pkt->getPeerId(), CSE_SetDefinitionsSent); + m_clients.event(peer_id, CSE_SetDefinitionsSent); // Send media announcement - sendMediaAnnouncement(pkt->getPeerId(), lang); + sendMediaAnnouncement(peer_id, lang); - RemoteClient *client = getClient(pkt->getPeerId(), CS_InitDone); + RemoteClient *client = getClient(peer_id, CS_InitDone); // Send active objects { - PlayerSAO *sao = getPlayerSAO(pkt->getPeerId()); + PlayerSAO *sao = getPlayerSAO(peer_id); if (client && sao) SendActiveObjectRemoveAdd(client, sao); } // Send detached inventories - sendDetachedInventories(pkt->getPeerId(), false); + sendDetachedInventories(peer_id, false); // Send player movement settings - SendMovement(pkt->getPeerId()); + SendMovement(peer_id); // Send time of day u16 time = m_env->getTimeOfDay(); float time_speed = g_settings->getFloat("time_speed"); - SendTimeOfDay(pkt->getPeerId(), time, time_speed); + SendTimeOfDay(peer_id, time, time_speed); - SendCSMRestrictionFlags(pkt->getPeerId()); + SendCSMRestrictionFlags(peer_id); // Warnings about protocol version can be issued here if (client->net_proto_version < LATEST_PROTOCOL_VERSION) { - SendChatMessage(pkt->getPeerId(), ChatMessage(CHATMESSAGE_TYPE_SYSTEM, + SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM, L"# Server: WARNING: YOUR CLIENT'S VERSION MAY NOT BE FULLY COMPATIBLE " L"WITH THIS SERVER!")); } @@ -346,8 +346,9 @@ void Server::handleCommand_RequestMedia(NetworkPacket* pkt) *pkt >> numfiles; - infostream << "Sending " << numfiles << " files to " - << getPlayerName(pkt->getPeerId()) << std::endl; + session_t peer_id = pkt->getPeerId(); + infostream << "Sending " << numfiles << " files to " << + getPlayerName(peer_id) << std::endl; verbosestream << "TOSERVER_REQUEST_MEDIA: " << std::endl; for (u16 i = 0; i < numfiles; i++) { @@ -360,7 +361,7 @@ void Server::handleCommand_RequestMedia(NetworkPacket* pkt) << name << std::endl; } - sendRequestedMedia(pkt->getPeerId(), tosend); + sendRequestedMedia(peer_id, tosend); } void Server::handleCommand_ClientReady(NetworkPacket* pkt) @@ -370,18 +371,16 @@ void Server::handleCommand_ClientReady(NetworkPacket* pkt) PlayerSAO* playersao = StageTwoClientInit(peer_id); if (playersao == NULL) { - actionstream - << "TOSERVER_CLIENT_READY stage 2 client init failed for peer_id: " - << peer_id << std::endl; + errorstream << "TOSERVER_CLIENT_READY stage 2 client init failed " + "peer_id=" << peer_id << std::endl; DisconnectPeer(peer_id); return; } if (pkt->getSize() < 8) { - errorstream - << "TOSERVER_CLIENT_READY client sent inconsistent data, disconnecting peer_id: " - << peer_id << std::endl; + errorstream << "TOSERVER_CLIENT_READY client sent inconsistent data, " + "disconnecting peer_id: " << peer_id << std::endl; DisconnectPeer(peer_id); return; } @@ -390,9 +389,8 @@ void Server::handleCommand_ClientReady(NetworkPacket* pkt) std::string full_ver; *pkt >> major_ver >> minor_ver >> patch_ver >> reserved >> full_ver; - m_clients.setClientVersion( - peer_id, major_ver, minor_ver, patch_ver, - full_ver); + m_clients.setClientVersion(peer_id, major_ver, minor_ver, patch_ver, + full_ver); if (pkt->getRemainingBytes() >= 2) *pkt >> playersao->getPlayer()->formspec_version; @@ -414,7 +412,7 @@ void Server::handleCommand_ClientReady(NetworkPacket* pkt) m_script->on_joinplayer(playersao); // Send shutdown timer if shutdown has been scheduled if (m_shutdown_state.isTimerRunning()) { - SendChatMessage(pkt->getPeerId(), m_shutdown_state.getShutdownTimerMessage()); + SendChatMessage(peer_id, m_shutdown_state.getShutdownTimerMessage()); } } @@ -508,21 +506,22 @@ void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao, void Server::handleCommand_PlayerPos(NetworkPacket* pkt) { - RemotePlayer *player = m_env->getPlayer(pkt->getPeerId()); + session_t peer_id = pkt->getPeerId(); + RemotePlayer *player = m_env->getPlayer(peer_id); if (player == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - DisconnectPeer(pkt->getPeerId()); + errorstream << + "Server::ProcessData(): Canceling: No player for peer_id=" << + peer_id << " disconnecting peer!" << std::endl; + DisconnectPeer(peer_id); return; } PlayerSAO *playersao = player->getPlayerSAO(); if (playersao == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player object for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - DisconnectPeer(pkt->getPeerId()); + errorstream << + "Server::ProcessData(): Canceling: No player object for peer_id=" << + peer_id << " disconnecting peer!" << std::endl; + DisconnectPeer(peer_id); return; } @@ -568,22 +567,23 @@ void Server::handleCommand_DeletedBlocks(NetworkPacket* pkt) void Server::handleCommand_InventoryAction(NetworkPacket* pkt) { - RemotePlayer *player = m_env->getPlayer(pkt->getPeerId()); + session_t peer_id = pkt->getPeerId(); + RemotePlayer *player = m_env->getPlayer(peer_id); if (player == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - DisconnectPeer(pkt->getPeerId()); + errorstream << + "Server::ProcessData(): Canceling: No player for peer_id=" << + peer_id << " disconnecting peer!" << std::endl; + DisconnectPeer(peer_id); return; } PlayerSAO *playersao = player->getPlayerSAO(); if (playersao == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player object for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - DisconnectPeer(pkt->getPeerId()); + errorstream << + "Server::ProcessData(): Canceling: No player object for peer_id=" << + peer_id << " disconnecting peer!" << std::endl; + DisconnectPeer(peer_id); return; } @@ -761,12 +761,13 @@ void Server::handleCommand_ChatMessage(NetworkPacket* pkt) message += (wchar_t)tmp_wchar; } - RemotePlayer *player = m_env->getPlayer(pkt->getPeerId()); + session_t peer_id = pkt->getPeerId(); + RemotePlayer *player = m_env->getPlayer(peer_id); if (player == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - DisconnectPeer(pkt->getPeerId()); + errorstream << + "Server::ProcessData(): Canceling: No player for peer_id=" << + peer_id << " disconnecting peer!" << std::endl; + DisconnectPeer(peer_id); return; } @@ -777,8 +778,8 @@ void Server::handleCommand_ChatMessage(NetworkPacket* pkt) std::wstring answer_to_sender = handleChat(name, wname, message, true, player); if (!answer_to_sender.empty()) { // Send the answer to sender - SendChatMessage(pkt->getPeerId(), ChatMessage(CHATMESSAGE_TYPE_NORMAL, - answer_to_sender, wname)); + SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_NORMAL, + answer_to_sender, wname)); } } @@ -788,22 +789,23 @@ void Server::handleCommand_Damage(NetworkPacket* pkt) *pkt >> damage; - RemotePlayer *player = m_env->getPlayer(pkt->getPeerId()); + session_t peer_id = pkt->getPeerId(); + RemotePlayer *player = m_env->getPlayer(peer_id); if (player == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - DisconnectPeer(pkt->getPeerId()); + errorstream << + "Server::ProcessData(): Canceling: No player for peer_id=" << + peer_id << " disconnecting peer!" << std::endl; + DisconnectPeer(peer_id); return; } PlayerSAO *playersao = player->getPlayerSAO(); if (playersao == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player object for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - DisconnectPeer(pkt->getPeerId()); + errorstream << + "Server::ProcessData(): Canceling: No player object for peer_id=" << + peer_id << " disconnecting peer!" << std::endl; + DisconnectPeer(peer_id); return; } @@ -825,105 +827,28 @@ void Server::handleCommand_Damage(NetworkPacket* pkt) } } -void Server::handleCommand_Password(NetworkPacket* pkt) -{ - if (pkt->getSize() != PASSWORD_SIZE * 2) - return; - - std::string oldpwd; - std::string newpwd; - - // Deny for clients using the new protocol - RemoteClient* client = getClient(pkt->getPeerId(), CS_Created); - if (client->net_proto_version >= 25) { - infostream << "Server::handleCommand_Password(): Denying change: " - << " Client protocol version for peer_id=" << pkt->getPeerId() - << " too new!" << std::endl; - return; - } - - for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) { - char c = pkt->getChar(i); - if (c == 0) - break; - oldpwd += c; - } - - for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) { - char c = pkt->getChar(PASSWORD_SIZE + i); - if (c == 0) - break; - newpwd += c; - } - - RemotePlayer *player = m_env->getPlayer(pkt->getPeerId()); - if (player == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - DisconnectPeer(pkt->getPeerId()); - return; - } - - if (!base64_is_valid(newpwd)) { - infostream<<"Server: " << player->getName() << - " supplied invalid password hash" << std::endl; - // Wrong old password supplied!! - SendChatMessage(pkt->getPeerId(), ChatMessage(CHATMESSAGE_TYPE_SYSTEM, - L"Invalid new password hash supplied. Password NOT changed.")); - return; - } - - infostream << "Server: Client requests a password change from " - << "'" << oldpwd << "' to '" << newpwd << "'" << std::endl; - - std::string playername = player->getName(); - - std::string checkpwd; - m_script->getAuth(playername, &checkpwd, NULL); - - if (oldpwd != checkpwd) { - infostream << "Server: invalid old password" << std::endl; - // Wrong old password supplied!! - SendChatMessage(pkt->getPeerId(), ChatMessage(CHATMESSAGE_TYPE_SYSTEM, - L"Invalid old password supplied. Password NOT changed.")); - return; - } - - bool success = m_script->setPassword(playername, newpwd); - if (success) { - actionstream << player->getName() << " changes password" << std::endl; - SendChatMessage(pkt->getPeerId(), ChatMessage(CHATMESSAGE_TYPE_SYSTEM, - L"Password change successful.")); - } else { - actionstream << player->getName() << " tries to change password but " - << "it fails" << std::endl; - SendChatMessage(pkt->getPeerId(), ChatMessage(CHATMESSAGE_TYPE_SYSTEM, - L"Password change failed or unavailable.")); - } -} - void Server::handleCommand_PlayerItem(NetworkPacket* pkt) { if (pkt->getSize() < 2) return; - RemotePlayer *player = m_env->getPlayer(pkt->getPeerId()); + session_t peer_id = pkt->getPeerId(); + RemotePlayer *player = m_env->getPlayer(peer_id); if (player == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - DisconnectPeer(pkt->getPeerId()); + errorstream << + "Server::ProcessData(): Canceling: No player for peer_id=" << + peer_id << " disconnecting peer!" << std::endl; + DisconnectPeer(peer_id); return; } PlayerSAO *playersao = player->getPlayerSAO(); if (playersao == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player object for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - DisconnectPeer(pkt->getPeerId()); + errorstream << + "Server::ProcessData(): Canceling: No player object for peer_id=" << + peer_id << " disconnecting peer!" << std::endl; + DisconnectPeer(peer_id); return; } @@ -936,12 +861,13 @@ void Server::handleCommand_PlayerItem(NetworkPacket* pkt) void Server::handleCommand_Respawn(NetworkPacket* pkt) { - RemotePlayer *player = m_env->getPlayer(pkt->getPeerId()); + session_t peer_id = pkt->getPeerId(); + RemotePlayer *player = m_env->getPlayer(peer_id); if (player == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - DisconnectPeer(pkt->getPeerId()); + errorstream << + "Server::ProcessData(): Canceling: No player for peer_id=" << + peer_id << " disconnecting peer!" << std::endl; + DisconnectPeer(peer_id); return; } @@ -951,7 +877,7 @@ void Server::handleCommand_Respawn(NetworkPacket* pkt) if (!playersao->isDead()) return; - RespawnPlayer(pkt->getPeerId()); + RespawnPlayer(peer_id); actionstream << player->getName() << " respawns at " << PP(playersao->getBasePosition() / BS) << std::endl; @@ -1006,22 +932,23 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) verbosestream << "TOSERVER_INTERACT: action=" << (int)action << ", item=" << item_i << ", pointed=" << pointed.dump() << std::endl; - RemotePlayer *player = m_env->getPlayer(pkt->getPeerId()); + session_t peer_id = pkt->getPeerId(); + RemotePlayer *player = m_env->getPlayer(peer_id); if (player == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - DisconnectPeer(pkt->getPeerId()); + errorstream << + "Server::ProcessData(): Canceling: No player for peer_id=" << + peer_id << " disconnecting peer!" << std::endl; + DisconnectPeer(peer_id); return; } PlayerSAO *playersao = player->getPlayerSAO(); if (playersao == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player object for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - DisconnectPeer(pkt->getPeerId()); + errorstream << + "Server::ProcessData(): Canceling: No player object for peer_id=" << + peer_id << " disconnecting peer!" << std::endl; + DisconnectPeer(peer_id); return; } @@ -1030,7 +957,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) << " tried to interact while dead; ignoring." << std::endl; if (pointed.type == POINTEDTHING_NODE) { // Re-send block to revert change on client-side - RemoteClient *client = getClient(pkt->getPeerId()); + RemoteClient *client = getClient(peer_id); v3s16 blockpos = getNodeBlockPos(pointed.node_undersurface); client->SetBlockNotSent(blockpos); } @@ -1081,7 +1008,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) pointed.dump() << " without 'interact' privilege" << std::endl; // Re-send block to revert change on client-side - RemoteClient *client = getClient(pkt->getPeerId()); + RemoteClient *client = getClient(peer_id); // Digging completed -> under if (action == INTERACT_DIGGING_COMPLETED) { v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS)); @@ -1109,7 +1036,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) if (!checkInteractDistance(player, d, pointed.dump())) { // Re-send block to revert change on client-side - RemoteClient *client = getClient(pkt->getPeerId()); + RemoteClient *client = getClient(peer_id); v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS)); client->SetBlockNotSent(blockpos); return; @@ -1132,11 +1059,10 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) n = m_env->getMap().getNode(p_under, &pos_ok); if (!pos_ok) { - infostream << "Server: Not punching: Node not found." - << " Adding block to emerge queue." - << std::endl; - m_emerge->enqueueBlockEmerge(pkt->getPeerId(), - getNodeBlockPos(p_above), false); + infostream << "Server: Not punching: Node not found. " + "Adding block to emerge queue." << std::endl; + m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), + false); } if (n.getContent() != CONTENT_IGNORE) @@ -1202,11 +1128,10 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) bool pos_ok; MapNode n = m_env->getMap().getNode(p_under, &pos_ok); if (!pos_ok) { - infostream << "Server: Not finishing digging: Node not found." - << " Adding block to emerge queue." - << std::endl; - m_emerge->enqueueBlockEmerge(pkt->getPeerId(), - getNodeBlockPos(p_above), false); + infostream << "Server: Not finishing digging: Node not found. " + "Adding block to emerge queue." << std::endl; + m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), + false); } /* Cheat prevention */ @@ -1282,7 +1207,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) m_script->node_on_dig(p_under, n, playersao); v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS)); - RemoteClient *client = getClient(pkt->getPeerId()); + RemoteClient *client = getClient(peer_id); // Send unusual result (that is, node not being removed) if (m_env->getMap().getNode(p_under).getContent() != CONTENT_AIR) { // Re-send block to revert change on client-side @@ -1304,7 +1229,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) // Reset build time counter if (pointed.type == POINTEDTHING_NODE && selected_item.getDefinition(m_itemdef).type == ITEM_NODE) - getClient(pkt->getPeerId())->m_time_from_building = 0.0; + getClient(peer_id)->m_time_from_building = 0.0; if (pointed.type == POINTEDTHING_OBJECT) { // Right click object @@ -1338,7 +1263,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) // If item has node placement prediction, always send the // blocks to make sure the client knows what exactly happened - RemoteClient *client = getClient(pkt->getPeerId()); + RemoteClient *client = getClient(peer_id); v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS)); v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS)); if (!selected_item.getDefinition(m_itemdef).node_placement_prediction.empty()) { @@ -1441,22 +1366,23 @@ void Server::handleCommand_NodeMetaFields(NetworkPacket* pkt) fields[fieldname] = pkt->readLongString(); } - RemotePlayer *player = m_env->getPlayer(pkt->getPeerId()); + session_t peer_id = pkt->getPeerId(); + RemotePlayer *player = m_env->getPlayer(peer_id); if (player == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - DisconnectPeer(pkt->getPeerId()); + errorstream << + "Server::ProcessData(): Canceling: No player for peer_id=" << + peer_id << " disconnecting peer!" << std::endl; + DisconnectPeer(peer_id); return; } PlayerSAO *playersao = player->getPlayerSAO(); if (playersao == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player object for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - DisconnectPeer(pkt->getPeerId()); + errorstream << + "Server::ProcessData(): Canceling: No player object for peer_id=" << + peer_id << " disconnecting peer!" << std::endl; + DisconnectPeer(peer_id); return; } @@ -1492,22 +1418,23 @@ void Server::handleCommand_InventoryFields(NetworkPacket* pkt) fields[fieldname] = pkt->readLongString(); } - RemotePlayer *player = m_env->getPlayer(pkt->getPeerId()); + session_t peer_id = pkt->getPeerId(); + RemotePlayer *player = m_env->getPlayer(peer_id); if (player == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - DisconnectPeer(pkt->getPeerId()); + errorstream << + "Server::ProcessData(): Canceling: No player for peer_id=" << + peer_id << " disconnecting peer!" << std::endl; + DisconnectPeer(peer_id); return; } PlayerSAO *playersao = player->getPlayerSAO(); if (playersao == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player object for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - DisconnectPeer(pkt->getPeerId()); + errorstream << + "Server::ProcessData(): Canceling: No player object for peer_id=" << + peer_id << " disconnecting peer!" << std::endl; + DisconnectPeer(peer_id); return; } @@ -1517,7 +1444,7 @@ void Server::handleCommand_InventoryFields(NetworkPacket* pkt) } // verify that we displayed the formspec to the user - const auto peer_state_iterator = m_formspec_state_data.find(pkt->getPeerId()); + const auto peer_state_iterator = m_formspec_state_data.find(peer_id); if (peer_state_iterator != m_formspec_state_data.end()) { const std::string &server_formspec_name = peer_state_iterator->second; if (client_formspec_name == server_formspec_name) { @@ -1543,7 +1470,8 @@ void Server::handleCommand_InventoryFields(NetworkPacket* pkt) void Server::handleCommand_FirstSrp(NetworkPacket* pkt) { - RemoteClient* client = getClient(pkt->getPeerId(), CS_Invalid); + session_t peer_id = pkt->getPeerId(); + RemoteClient *client = getClient(peer_id, CS_Invalid); ClientState cstate = client->getState(); std::string playername = client->getName(); @@ -1551,7 +1479,7 @@ void Server::handleCommand_FirstSrp(NetworkPacket* pkt) std::string salt; std::string verification_key; - std::string addr_s = getPeerAddress(pkt->getPeerId()).serializeString(); + std::string addr_s = getPeerAddress(peer_id).serializeString(); u8 is_empty; *pkt >> salt >> verification_key >> is_empty; @@ -1565,7 +1493,7 @@ void Server::handleCommand_FirstSrp(NetworkPacket* pkt) actionstream << "Server: Client from " << addr_s << " tried to set password without being " << "authenticated, or the username being new." << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA); + DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA); return; } @@ -1574,7 +1502,7 @@ void Server::handleCommand_FirstSrp(NetworkPacket* pkt) is_empty == 1) { actionstream << "Server: " << playername << " supplied empty password from " << addr_s << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_EMPTY_PASSWORD); + DenyAccess(peer_id, SERVER_ACCESSDENIED_EMPTY_PASSWORD); return; } @@ -1583,7 +1511,7 @@ void Server::handleCommand_FirstSrp(NetworkPacket* pkt) initial_ver_key = encode_srp_verifier(verification_key, salt); m_script->createAuth(playername, initial_ver_key); - acceptAuth(pkt->getPeerId(), false); + acceptAuth(peer_id, false); } else { if (cstate < CS_SudoMode) { infostream << "Server::ProcessData(): Ignoring TOSERVER_FIRST_SRP from " @@ -1591,48 +1519,48 @@ void Server::handleCommand_FirstSrp(NetworkPacket* pkt) << std::endl; return; } - m_clients.event(pkt->getPeerId(), CSE_SudoLeave); + m_clients.event(peer_id, CSE_SudoLeave); std::string pw_db_field = encode_srp_verifier(verification_key, salt); bool success = m_script->setPassword(playername, pw_db_field); if (success) { actionstream << playername << " changes password" << std::endl; - SendChatMessage(pkt->getPeerId(), ChatMessage(CHATMESSAGE_TYPE_SYSTEM, - L"Password change successful.")); + SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM, + L"Password change successful.")); } else { - actionstream << playername << " tries to change password but " - << "it fails" << std::endl; - SendChatMessage(pkt->getPeerId(), ChatMessage(CHATMESSAGE_TYPE_SYSTEM, - L"Password change failed or unavailable.")); + actionstream << playername << + " tries to change password but it fails" << std::endl; + SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM, + L"Password change failed or unavailable.")); } } } void Server::handleCommand_SrpBytesA(NetworkPacket* pkt) { - RemoteClient* client = getClient(pkt->getPeerId(), CS_Invalid); + session_t peer_id = pkt->getPeerId(); + RemoteClient *client = getClient(peer_id, CS_Invalid); ClientState cstate = client->getState(); bool wantSudo = (cstate == CS_Active); if (!((cstate == CS_HelloSent) || (cstate == CS_Active))) { - actionstream << "Server: got SRP _A packet in wrong state " - << cstate << " from " - << getPeerAddress(pkt->getPeerId()).serializeString() - << ". Ignoring." << std::endl; + actionstream << "Server: got SRP _A packet in wrong state " << cstate << + " from " << getPeerAddress(peer_id).serializeString() << + ". Ignoring." << std::endl; return; } if (client->chosen_mech != AUTH_MECHANISM_NONE) { - actionstream << "Server: got SRP _A packet, while auth" - << "is already going on with mech " << client->chosen_mech - << " from " << getPeerAddress(pkt->getPeerId()).serializeString() - << " (wantSudo=" << wantSudo << "). Ignoring." << std::endl; + actionstream << "Server: got SRP _A packet, while auth is already " + "going on with mech " << client->chosen_mech << " from " << + getPeerAddress(peer_id).serializeString() << + " (wantSudo=" << wantSudo << "). Ignoring." << std::endl; if (wantSudo) { - DenySudoAccess(pkt->getPeerId()); + DenySudoAccess(peer_id); return; } - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA); + DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA); return; } @@ -1649,19 +1577,19 @@ void Server::handleCommand_SrpBytesA(NetworkPacket* pkt) if (wantSudo) { if (!client->isSudoMechAllowed(chosen)) { - actionstream << "Server: Player \"" << client->getName() - << "\" at " << getPeerAddress(pkt->getPeerId()).serializeString() - << " tried to change password using unallowed mech " - << chosen << "." << std::endl; - DenySudoAccess(pkt->getPeerId()); + actionstream << "Server: Player \"" << client->getName() << + "\" at " << getPeerAddress(peer_id).serializeString() << + " tried to change password using unallowed mech " << chosen << + "." << std::endl; + DenySudoAccess(peer_id); return; } } else { if (!client->isMechAllowed(chosen)) { - actionstream << "Server: Client tried to authenticate from " - << getPeerAddress(pkt->getPeerId()).serializeString() - << " using unallowed mech " << chosen << "." << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA); + actionstream << "Server: Client tried to authenticate from " << + getPeerAddress(peer_id).serializeString() << + " using unallowed mech " << chosen << "." << std::endl; + DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA); return; } } @@ -1677,10 +1605,10 @@ void Server::handleCommand_SrpBytesA(NetworkPacket* pkt) &verifier, &salt); } else if (!decode_srp_verifier_and_salt(client->enc_pwd, &verifier, &salt)) { // Non-base64 errors should have been catched in the init handler - actionstream << "Server: User " << client->getName() - << " tried to log in, but srp verifier field" - << " was invalid (most likely invalid base64)." << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL); + actionstream << "Server: User " << client->getName() << + " tried to log in, but srp verifier field was invalid (most likely " + "invalid base64)." << std::endl; + DenyAccess(peer_id, SERVER_ACCESSDENIED_SERVER_FAIL); return; } @@ -1700,22 +1628,23 @@ void Server::handleCommand_SrpBytesA(NetworkPacket* pkt) << " tried to log in, SRP-6a safety check violated in _A handler." << std::endl; if (wantSudo) { - DenySudoAccess(pkt->getPeerId()); + DenySudoAccess(peer_id); return; } - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA); + DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA); return; } - NetworkPacket resp_pkt(TOCLIENT_SRP_BYTES_S_B, 0, pkt->getPeerId()); + NetworkPacket resp_pkt(TOCLIENT_SRP_BYTES_S_B, 0, peer_id); resp_pkt << salt << std::string(bytes_B, len_B); Send(&resp_pkt); } void Server::handleCommand_SrpBytesM(NetworkPacket* pkt) { - RemoteClient* client = getClient(pkt->getPeerId(), CS_Invalid); + session_t peer_id = pkt->getPeerId(); + RemoteClient *client = getClient(peer_id, CS_Invalid); ClientState cstate = client->getState(); bool wantSudo = (cstate == CS_Active); @@ -1723,25 +1652,24 @@ void Server::handleCommand_SrpBytesM(NetworkPacket* pkt) verbosestream << "Server: Received TOCLIENT_SRP_BYTES_M." << std::endl; if (!((cstate == CS_HelloSent) || (cstate == CS_Active))) { - actionstream << "Server: got SRP _M packet in wrong state " - << cstate << " from " - << getPeerAddress(pkt->getPeerId()).serializeString() - << ". Ignoring." << std::endl; + actionstream << "Server: got SRP _M packet in wrong state " << cstate << + " from " << getPeerAddress(peer_id).serializeString() << + ". Ignoring." << std::endl; return; } if (client->chosen_mech != AUTH_MECHANISM_SRP && client->chosen_mech != AUTH_MECHANISM_LEGACY_PASSWORD) { - actionstream << "Server: got SRP _M packet, while auth" - << "is going on with mech " << client->chosen_mech - << " from " << getPeerAddress(pkt->getPeerId()).serializeString() - << " (wantSudo=" << wantSudo << "). Denying." << std::endl; + actionstream << "Server: got SRP _M packet, while auth is going on " + "with mech " << client->chosen_mech << " from " << + getPeerAddress(peer_id).serializeString() << + " (wantSudo=" << wantSudo << "). Denying." << std::endl; if (wantSudo) { - DenySudoAccess(pkt->getPeerId()); + DenySudoAccess(peer_id); return; } - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA); + DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA); return; } @@ -1750,10 +1678,10 @@ void Server::handleCommand_SrpBytesM(NetworkPacket* pkt) if (srp_verifier_get_session_key_length((SRPVerifier *) client->auth_data) != bytes_M.size()) { - actionstream << "Server: User " << client->getName() - << " at " << getPeerAddress(pkt->getPeerId()).serializeString() - << " sent bytes_M with invalid length " << bytes_M.size() << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA); + actionstream << "Server: User " << client->getName() << " at " << + getPeerAddress(peer_id).serializeString() << + " sent bytes_M with invalid length " << bytes_M.size() << std::endl; + DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA); return; } @@ -1764,21 +1692,19 @@ void Server::handleCommand_SrpBytesM(NetworkPacket* pkt) if (!bytes_HAMK) { if (wantSudo) { - actionstream << "Server: User " << client->getName() - << " at " << getPeerAddress(pkt->getPeerId()).serializeString() - << " tried to change their password, but supplied wrong" - << " (SRP) password for authentication." << std::endl; - DenySudoAccess(pkt->getPeerId()); + actionstream << "Server: User " << client->getName() << " at " << + getPeerAddress(peer_id).serializeString() << + " tried to change their password, but supplied wrong (SRP) " + "password for authentication." << std::endl; + DenySudoAccess(peer_id); return; } - std::string ip = getPeerAddress(pkt->getPeerId()).serializeString(); - actionstream << "Server: User " << client->getName() - << " at " << ip - << " supplied wrong password (auth mechanism: SRP)." - << std::endl; + std::string ip = getPeerAddress(peer_id).serializeString(); + actionstream << "Server: User " << client->getName() << " at " << ip << + " supplied wrong password (auth mechanism: SRP)." << std::endl; m_script->on_auth_failure(client->getName(), ip); - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD); + DenyAccess(peer_id, SERVER_ACCESSDENIED_WRONG_PASSWORD); return; } @@ -1788,15 +1714,16 @@ void Server::handleCommand_SrpBytesM(NetworkPacket* pkt) std::string checkpwd; // not used, but needed for passing something if (!m_script->getAuth(playername, &checkpwd, NULL)) { - actionstream << "Server: " << playername << " cannot be authenticated" - << " (auth handler does not work?)" << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL); + actionstream << "Server: " << playername << + " cannot be authenticated (auth handler does not work?)" << + std::endl; + DenyAccess(peer_id, SERVER_ACCESSDENIED_SERVER_FAIL); return; } client->create_player_on_auth_success = false; } - acceptAuth(pkt->getPeerId(), wantSudo); + acceptAuth(peer_id, wantSudo); } /* @@ -1808,20 +1735,21 @@ void Server::handleCommand_ModChannelJoin(NetworkPacket *pkt) std::string channel_name; *pkt >> channel_name; - NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_SIGNAL, 1 + 2 + channel_name.size(), - pkt->getPeerId()); + session_t peer_id = pkt->getPeerId(); + NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_SIGNAL, + 1 + 2 + channel_name.size(), peer_id); // Send signal to client to notify join succeed or not if (g_settings->getBool("enable_mod_channels") && - m_modchannel_mgr->joinChannel(channel_name, pkt->getPeerId())) { + m_modchannel_mgr->joinChannel(channel_name, peer_id)) { resp_pkt << (u8) MODCHANNEL_SIGNAL_JOIN_OK; - infostream << "Peer " << pkt->getPeerId() << " joined channel " << channel_name - << std::endl; + infostream << "Peer " << peer_id << " joined channel " << + channel_name << std::endl; } else { resp_pkt << (u8)MODCHANNEL_SIGNAL_JOIN_FAILURE; - infostream << "Peer " << pkt->getPeerId() << " tried to join channel " - << channel_name << ", but was already registered." << std::endl; + infostream << "Peer " << peer_id << " tried to join channel " << + channel_name << ", but was already registered." << std::endl; } resp_pkt << channel_name; Send(&resp_pkt); @@ -1832,19 +1760,20 @@ void Server::handleCommand_ModChannelLeave(NetworkPacket *pkt) std::string channel_name; *pkt >> channel_name; - NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_SIGNAL, 1 + 2 + channel_name.size(), - pkt->getPeerId()); + session_t peer_id = pkt->getPeerId(); + NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_SIGNAL, + 1 + 2 + channel_name.size(), peer_id); // Send signal to client to notify join succeed or not if (g_settings->getBool("enable_mod_channels") && - m_modchannel_mgr->leaveChannel(channel_name, pkt->getPeerId())) { + m_modchannel_mgr->leaveChannel(channel_name, peer_id)) { resp_pkt << (u8)MODCHANNEL_SIGNAL_LEAVE_OK; - infostream << "Peer " << pkt->getPeerId() << " left channel " << channel_name - << std::endl; + infostream << "Peer " << peer_id << " left channel " << channel_name << + std::endl; } else { resp_pkt << (u8) MODCHANNEL_SIGNAL_LEAVE_FAILURE; - infostream << "Peer " << pkt->getPeerId() << " left channel " << channel_name - << ", but was not registered." << std::endl; + infostream << "Peer " << peer_id << " left channel " << channel_name << + ", but was not registered." << std::endl; } resp_pkt << channel_name; Send(&resp_pkt); @@ -1855,8 +1784,10 @@ void Server::handleCommand_ModChannelMsg(NetworkPacket *pkt) std::string channel_name, channel_msg; *pkt >> channel_name >> channel_msg; - verbosestream << "Mod channel message received from peer " << pkt->getPeerId() - << " on channel " << channel_name << " message: " << channel_msg << std::endl; + session_t peer_id = pkt->getPeerId(); + verbosestream << "Mod channel message received from peer " << peer_id << + " on channel " << channel_name << " message: " << channel_msg << + std::endl; // If mod channels are not enabled, discard message if (!g_settings->getBool("enable_mod_channels")) { @@ -1865,8 +1796,8 @@ void Server::handleCommand_ModChannelMsg(NetworkPacket *pkt) // If channel not registered, signal it and ignore message if (!m_modchannel_mgr->channelRegistered(channel_name)) { - NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_SIGNAL, 1 + 2 + channel_name.size(), - pkt->getPeerId()); + NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_SIGNAL, + 1 + 2 + channel_name.size(), peer_id); resp_pkt << (u8)MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED << channel_name; Send(&resp_pkt); return; @@ -1874,5 +1805,5 @@ void Server::handleCommand_ModChannelMsg(NetworkPacket *pkt) // @TODO: filter, rate limit - broadcastModChannelMessage(channel_name, channel_msg, pkt->getPeerId()); + broadcastModChannelMessage(channel_name, channel_msg, peer_id); } diff --git a/src/server.h b/src/server.h index 680de57b5..eecc2c0f0 100644 --- a/src/server.h +++ b/src/server.h @@ -165,7 +165,6 @@ public: void handleCommand_InventoryAction(NetworkPacket* pkt); void handleCommand_ChatMessage(NetworkPacket* pkt); void handleCommand_Damage(NetworkPacket* pkt); - void handleCommand_Password(NetworkPacket* pkt); void handleCommand_PlayerItem(NetworkPacket* pkt); void handleCommand_Respawn(NetworkPacket* pkt); void handleCommand_Interact(NetworkPacket* pkt); -- cgit v1.2.3 From 8ef239b448c52485cf94d334c1d8b1c6de37d976 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 20 Apr 2020 23:22:00 +0200 Subject: Improve protocol-level receiving code (#9617) --- src/network/connection.cpp | 4 +- src/network/connectionthreads.cpp | 250 +++++++++++++++++++------------------- src/network/connectionthreads.h | 2 +- 3 files changed, 126 insertions(+), 130 deletions(-) (limited to 'src/network') diff --git a/src/network/connection.cpp b/src/network/connection.cpp index 15eda7725..3692e45a9 100644 --- a/src/network/connection.cpp +++ b/src/network/connection.cpp @@ -1173,7 +1173,9 @@ Connection::Connection(u32 protocol_id, u32 max_packet_size, float timeout, m_bc_peerhandler(peerhandler) { - m_udpSocket.setTimeoutMs(5); + /* Amount of time Receive() will wait for data, this is entirely different + * from the connection timeout */ + m_udpSocket.setTimeoutMs(500); m_sendThread->setParent(this); m_receiveThread->setParent(this); diff --git a/src/network/connectionthreads.cpp b/src/network/connectionthreads.cpp index 13d82e06d..1f33d2ded 100644 --- a/src/network/connectionthreads.cpp +++ b/src/network/connectionthreads.cpp @@ -812,6 +812,14 @@ void *ConnectionReceiveThread::run() ThreadIdentifier); PROFILE(ThreadIdentifier << "ConnectionReceive: [" << m_connection->getDesc() << "]"); + // use IPv6 minimum allowed MTU as receive buffer size as this is + // theoretical reliable upper boundary of a udp packet for all IPv6 enabled + // infrastructure + const unsigned int packet_maxsize = 1500; + SharedBuffer packetdata(packet_maxsize); + + bool packet_queued = true; + #ifdef DEBUG_CONNECTION_KBPS u64 curtime = porting::getTimeMs(); u64 lasttime = curtime; @@ -830,7 +838,7 @@ void *ConnectionReceiveThread::run() #endif /* receive packets */ - receive(); + receive(packetdata, packet_queued); #ifdef DEBUG_CONNECTION_KBPS debug_print_timer += dtime; @@ -892,157 +900,142 @@ void *ConnectionReceiveThread::run() } // Receive packets from the network and buffers and create ConnectionEvents -void ConnectionReceiveThread::receive() +void ConnectionReceiveThread::receive(SharedBuffer &packetdata, + bool &packet_queued) { - // use IPv6 minimum allowed MTU as receive buffer size as this is - // theoretical reliable upper boundary of a udp packet for all IPv6 enabled - // infrastructure - unsigned int packet_maxsize = 1500; - SharedBuffer packetdata(packet_maxsize); - - bool packet_queued = true; - - unsigned int loop_count = 0; - - /* first of all read packets from socket */ - /* check for incoming data available */ - while ((loop_count < 10) && - (m_connection->m_udpSocket.WaitData(50))) { - loop_count++; - try { - if (packet_queued) { - bool data_left = true; - session_t peer_id; - SharedBuffer resultdata; - while (data_left) { - try { - data_left = getFromBuffers(peer_id, resultdata); - if (data_left) { - ConnectionEvent e; - e.dataReceived(peer_id, resultdata); - m_connection->putEvent(e); - } - } - catch (ProcessedSilentlyException &e) { - /* try reading again */ + try { + // First, see if there any buffered packets we can process now + if (packet_queued) { + bool data_left = true; + session_t peer_id; + SharedBuffer resultdata; + while (data_left) { + try { + data_left = getFromBuffers(peer_id, resultdata); + if (data_left) { + ConnectionEvent e; + e.dataReceived(peer_id, resultdata); + m_connection->putEvent(e); } } - packet_queued = false; - } - - Address sender; - s32 received_size = m_connection->m_udpSocket.Receive(sender, *packetdata, - packet_maxsize); - - if ((received_size < BASE_HEADER_SIZE) || - (readU32(&packetdata[0]) != m_connection->GetProtocolID())) { - LOG(derr_con << m_connection->getDesc() - << "Receive(): Invalid incoming packet, " - << "size: " << received_size - << ", protocol: " - << ((received_size >= 4) ? readU32(&packetdata[0]) : -1) - << std::endl); - continue; + catch (ProcessedSilentlyException &e) { + /* try reading again */ + } } + packet_queued = false; + } - session_t peer_id = readPeerId(*packetdata); - u8 channelnum = readChannel(*packetdata); + // Call Receive() to wait for incoming data + Address sender; + s32 received_size = m_connection->m_udpSocket.Receive(sender, + *packetdata, packetdata.getSize()); + if (received_size < 0) + return; - if (channelnum > CHANNEL_COUNT - 1) { - LOG(derr_con << m_connection->getDesc() - << "Receive(): Invalid channel " << (u32)channelnum << std::endl); - throw InvalidIncomingDataException("Channel doesn't exist"); - } + if ((received_size < BASE_HEADER_SIZE) || + (readU32(&packetdata[0]) != m_connection->GetProtocolID())) { + LOG(derr_con << m_connection->getDesc() + << "Receive(): Invalid incoming packet, " + << "size: " << received_size + << ", protocol: " + << ((received_size >= 4) ? readU32(&packetdata[0]) : -1) + << std::endl); + return; + } - /* Try to identify peer by sender address (may happen on join) */ - if (peer_id == PEER_ID_INEXISTENT) { - peer_id = m_connection->lookupPeer(sender); - // We do not have to remind the peer of its - // peer id as the CONTROLTYPE_SET_PEER_ID - // command was sent reliably. - } + session_t peer_id = readPeerId(*packetdata); + u8 channelnum = readChannel(*packetdata); - /* The peer was not found in our lists. Add it. */ - if (peer_id == PEER_ID_INEXISTENT) { - peer_id = m_connection->createPeer(sender, MTP_MINETEST_RELIABLE_UDP, 0); - } + if (channelnum > CHANNEL_COUNT - 1) { + LOG(derr_con << m_connection->getDesc() + << "Receive(): Invalid channel " << (u32)channelnum << std::endl); + return; + } - PeerHelper peer = m_connection->getPeerNoEx(peer_id); + /* Try to identify peer by sender address (may happen on join) */ + if (peer_id == PEER_ID_INEXISTENT) { + peer_id = m_connection->lookupPeer(sender); + // We do not have to remind the peer of its + // peer id as the CONTROLTYPE_SET_PEER_ID + // command was sent reliably. + } - if (!peer) { - LOG(dout_con << m_connection->getDesc() - << " got packet from unknown peer_id: " - << peer_id << " Ignoring." << std::endl); - continue; - } + /* The peer was not found in our lists. Add it. */ + if (peer_id == PEER_ID_INEXISTENT) { + peer_id = m_connection->createPeer(sender, MTP_MINETEST_RELIABLE_UDP, 0); + } - // Validate peer address + PeerHelper peer = m_connection->getPeerNoEx(peer_id); + if (!peer) { + LOG(dout_con << m_connection->getDesc() + << " got packet from unknown peer_id: " + << peer_id << " Ignoring." << std::endl); + return; + } - Address peer_address; + // Validate peer address - if (peer->getAddress(MTP_UDP, peer_address)) { - if (peer_address != sender) { - LOG(derr_con << m_connection->getDesc() - << m_connection->getDesc() - << " Peer " << peer_id << " sending from different address." - " Ignoring." << std::endl); - continue; - } - } else { - - bool invalid_address = true; - if (invalid_address) { - LOG(derr_con << m_connection->getDesc() - << m_connection->getDesc() - << " Peer " << peer_id << " unknown." - " Ignoring." << std::endl); - continue; - } + Address peer_address; + if (peer->getAddress(MTP_UDP, peer_address)) { + if (peer_address != sender) { + LOG(derr_con << m_connection->getDesc() + << " Peer " << peer_id << " sending from different address." + " Ignoring." << std::endl); + return; } + } else { + LOG(derr_con << m_connection->getDesc() + << " Peer " << peer_id << " doesn't have an address?!" + " Ignoring." << std::endl); + return; + } - peer->ResetTimeout(); - - Channel *channel = 0; + peer->ResetTimeout(); - if (dynamic_cast(&peer) != 0) { - channel = &(dynamic_cast(&peer)->channels[channelnum]); - } + Channel *channel = nullptr; + if (dynamic_cast(&peer)) { + channel = &dynamic_cast(&peer)->channels[channelnum]; + } else { + LOG(derr_con << m_connection->getDesc() + << "Receive(): peer_id=" << peer_id << " isn't an UDPPeer?!" + " Ignoring." << std::endl); + return; + } - if (channel != 0) { - channel->UpdateBytesReceived(received_size); - } + channel->UpdateBytesReceived(received_size); - // Throw the received packet to channel->processPacket() + // Throw the received packet to channel->processPacket() - // Make a new SharedBuffer from the data without the base headers - SharedBuffer strippeddata(received_size - BASE_HEADER_SIZE); - memcpy(*strippeddata, &packetdata[BASE_HEADER_SIZE], - strippeddata.getSize()); + // Make a new SharedBuffer from the data without the base headers + SharedBuffer strippeddata(received_size - BASE_HEADER_SIZE); + memcpy(*strippeddata, &packetdata[BASE_HEADER_SIZE], + strippeddata.getSize()); - try { - // Process it (the result is some data with no headers made by us) - SharedBuffer resultdata = processPacket - (channel, strippeddata, peer_id, channelnum, false); + try { + // Process it (the result is some data with no headers made by us) + SharedBuffer resultdata = processPacket + (channel, strippeddata, peer_id, channelnum, false); - LOG(dout_con << m_connection->getDesc() - << " ProcessPacket from peer_id: " << peer_id - << ", channel: " << (u32)channelnum << ", returned " - << resultdata.getSize() << " bytes" << std::endl); + LOG(dout_con << m_connection->getDesc() + << " ProcessPacket from peer_id: " << peer_id + << ", channel: " << (u32)channelnum << ", returned " + << resultdata.getSize() << " bytes" << std::endl); - ConnectionEvent e; - e.dataReceived(peer_id, resultdata); - m_connection->putEvent(e); - } - catch (ProcessedSilentlyException &e) { - } - catch (ProcessedQueued &e) { - packet_queued = true; - } - } - catch (InvalidIncomingDataException &e) { + ConnectionEvent e; + e.dataReceived(peer_id, resultdata); + m_connection->putEvent(e); } catch (ProcessedSilentlyException &e) { } + catch (ProcessedQueued &e) { + // we set it to true anyway (see below) + } + + /* Every time we receive a packet it can happen that a previously + * buffered packet is now ready to process. */ + packet_queued = true; + } + catch (InvalidIncomingDataException &e) { } } @@ -1189,7 +1182,8 @@ SharedBuffer ConnectionReceiveThread::handlePacketType_Control(Channel *chan m_connection->TriggerSend(); } catch (NotFoundException &e) { LOG(derr_con << m_connection->getDesc() - << "WARNING: ACKed packet not in outgoing queue" << std::endl); + << "WARNING: ACKed packet not in outgoing queue" + << " seqnum=" << seqnum << std::endl); channel->UpdatePacketTooLateCounter(); } diff --git a/src/network/connectionthreads.h b/src/network/connectionthreads.h index da4ea92f5..612407c3b 100644 --- a/src/network/connectionthreads.h +++ b/src/network/connectionthreads.h @@ -101,7 +101,7 @@ public: } private: - void receive(); + void receive(SharedBuffer &packetdata, bool &packet_queued); // Returns next data from a buffer if possible // If found, returns true; if not, false. -- cgit v1.2.3 From cee3c5e73d7af2a876aa76275234ee76e7cb1bbc Mon Sep 17 00:00:00 2001 From: EvidenceB Kidscode <49488517+EvidenceBKidscode@users.noreply.github.com> Date: Sat, 25 Apr 2020 07:20:00 +0200 Subject: Add server side translations capability (#9733) * Add server side translations capability --- doc/lua_api.txt | 15 ++++++++++++++ src/client/client.cpp | 2 +- src/client/game.cpp | 2 +- src/clientiface.h | 6 ++++++ src/network/serverpackethandler.cpp | 3 +++ src/script/lua_api/l_env.cpp | 16 +++++++++++++++ src/script/lua_api/l_env.h | 3 +++ src/script/lua_api/l_server.cpp | 7 ++++++- src/server.cpp | 22 +++++++++++++++++++- src/server.h | 5 ++++- src/translation.cpp | 13 ++++++++++-- src/translation.h | 5 ++++- src/util/string.cpp | 41 ++++++++++++++++++++++++++++--------- src/util/string.h | 4 ++++ 14 files changed, 126 insertions(+), 18 deletions(-) (limited to 'src/network') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 77f06682f..3ca32649a 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -3176,8 +3176,22 @@ Strings that need to be translated can contain several escapes, preceded by `@`. `minetest.translate`, but is in translation files. * `@n` acts as a literal newline as well. +Server side translations +------------------------ + +On some specific cases, server translation could be useful. For example, filter +a list on labels and send results to client. A method is supplied to achieve +that: + +`minetest.get_translated_string(lang_code, string)`: Translates `string` using +translations for `lang_code` language. It gives the same result as if the string +was translated by the client. +The `lang_code` to use for a given player can be retrieved from +the table returned by `minetest.get_player_information(name)`. +IMPORTANT: This functionality should only be used for sorting, filtering or similar purposes. +You do not need to use this to get translated strings to show up on the client. Perlin noise ============ @@ -4153,6 +4167,7 @@ Utilities connection_uptime = 200, -- seconds since client connected protocol_version = 32, -- protocol version used by client formspec_version = 2, -- supported formspec version + lang_code = "fr" -- Language code used for translation -- following information is available on debug build only!!! -- DO NOT USE IN MODS --ser_vers = 26, -- serialization version used by client diff --git a/src/client/client.cpp b/src/client/client.cpp index 8ee0869cd..941fc203d 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -736,7 +736,7 @@ bool Client::loadMedia(const std::string &data, const std::string &filename) if (!name.empty()) { TRACESTREAM(<< "Client: Loading translation: " << "\"" << filename << "\"" << std::endl); - g_translations->loadTranslation(data); + g_client_translations->loadTranslation(data); return true; } diff --git a/src/client/game.cpp b/src/client/game.cpp index 3429cc57b..610522dc2 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1055,7 +1055,7 @@ bool Game::startup(bool *kill, m_invert_mouse = g_settings->getBool("invert_mouse"); m_first_loop_after_window_activation = true; - g_translations->clear(); + g_client_translations->clear(); if (!init(map_dir, address, port, gamespec)) return false; diff --git a/src/clientiface.h b/src/clientiface.h index bf95df4a8..83fa6fe99 100644 --- a/src/clientiface.h +++ b/src/clientiface.h @@ -339,12 +339,18 @@ public: u8 getMinor() const { return m_version_minor; } u8 getPatch() const { return m_version_patch; } const std::string &getFull() const { return m_full_version; } + + void setLangCode(const std::string &code) { m_lang_code = code; } + const std::string &getLangCode() const { return m_lang_code; } private: // Version is stored in here after INIT before INIT2 u8 m_pending_serialization_version = SER_FMT_VER_INVALID; /* current state of client */ ClientState m_state = CS_Created; + + // Client sent language code + std::string m_lang_code; /* Blocks that have been sent to client. diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index c685500ce..5136eb0ec 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -311,6 +311,9 @@ void Server::handleCommand_Init2(NetworkPacket* pkt) RemoteClient *client = getClient(peer_id, CS_InitDone); + // Keep client language for server translations + client->setLangCode(lang); + // Send active objects { PlayerSAO *sao = getPlayerSAO(peer_id); diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 831464d3b..3fb58b8c8 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -40,6 +40,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "remoteplayer.h" #include "server/luaentity_sao.h" #include "server/player_sao.h" +#include "util/string.h" +#include "translation.h" #ifndef SERVER #include "client/client.h" #endif @@ -1302,6 +1304,19 @@ int ModApiEnvMod::l_forceload_free_block(lua_State *L) return 0; } +// get_translated_string(lang_code, string) +int ModApiEnvMod::l_get_translated_string(lua_State * L) +{ + GET_ENV_PTR; + std::string lang_code = luaL_checkstring(L, 1); + std::string string = luaL_checkstring(L, 2); + getServer(L)->loadTranslationLanguage(lang_code); + string = wide_to_utf8(translate_string(utf8_to_wide(string), + &(*g_server_translations)[lang_code])); + lua_pushstring(L, string.c_str()); + return 1; +} + void ModApiEnvMod::Initialize(lua_State *L, int top) { API_FCT(set_node); @@ -1349,6 +1364,7 @@ void ModApiEnvMod::Initialize(lua_State *L, int top) API_FCT(transforming_liquid_add); API_FCT(forceload_block); API_FCT(forceload_free_block); + API_FCT(get_translated_string); } void ModApiEnvMod::InitializeClient(lua_State *L, int top) diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index ac2f8b588..9050b4306 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -187,6 +187,9 @@ private: // stops forceloading a position static int l_forceload_free_block(lua_State *L); + // Get a string translated server side + static int l_get_translated_string(lua_State * L); + public: static void Initialize(lua_State *L, int top); static void InitializeClient(lua_State *L, int top); diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index 00e849cdf..7137484e8 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -163,6 +163,7 @@ int ModApiServer::l_get_player_information(lua_State *L) u16 prot_vers; u8 ser_vers,major,minor,patch; std::string vers_string; + std::string lang_code; #define ERET(code) \ if (!(code)) { \ @@ -182,7 +183,7 @@ int ModApiServer::l_get_player_information(lua_State *L) &avg_jitter)) ERET(getServer(L)->getClientInfo(player->getPeerId(), &state, &uptime, &ser_vers, - &prot_vers, &major, &minor, &patch, &vers_string)) + &prot_vers, &major, &minor, &patch, &vers_string, &lang_code)) lua_newtable(L); int table = lua_gettop(L); @@ -237,6 +238,10 @@ int ModApiServer::l_get_player_information(lua_State *L) lua_pushnumber(L, player->formspec_version); lua_settable(L, table); + lua_pushstring(L, "lang_code"); + lua_pushstring(L, lang_code.c_str()); + lua_settable(L, table); + #ifndef NDEBUG lua_pushstring(L,"serialization_version"); lua_pushnumber(L, ser_vers); diff --git a/src/server.cpp b/src/server.cpp index c32aa5306..af6d3e40d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -64,6 +64,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "chat_interface.h" #include "remoteplayer.h" #include "server/player_sao.h" +#include "translation.h" class ClientNotFoundException : public BaseException { @@ -1266,7 +1267,8 @@ bool Server::getClientInfo( u8* major, u8* minor, u8* patch, - std::string* vers_string + std::string* vers_string, + std::string* lang_code ) { *state = m_clients.getClientState(peer_id); @@ -1286,6 +1288,7 @@ bool Server::getClientInfo( *minor = client->getMinor(); *patch = client->getPatch(); *vers_string = client->getFull(); + *lang_code = client->getLangCode(); m_clients.unlock(); @@ -3937,3 +3940,20 @@ void Server::broadcastModChannelMessage(const std::string &channel, m_script->on_modchannel_message(channel, sender, message); } } + +void Server::loadTranslationLanguage(const std::string &lang_code) +{ + if (g_server_translations->count(lang_code)) + return; // Already loaded + + std::string suffix = "." + lang_code + ".tr"; + for (const auto &i : m_media) { + if (str_ends_with(i.first, suffix)) { + std::ifstream t(i.second.path); + std::string data((std::istreambuf_iterator(t)), + std::istreambuf_iterator()); + + (*g_server_translations)[lang_code].loadTranslation(data); + } + } +} diff --git a/src/server.h b/src/server.h index eecc2c0f0..b995aba28 100644 --- a/src/server.h +++ b/src/server.h @@ -334,7 +334,7 @@ public: bool getClientConInfo(session_t peer_id, con::rtt_stat_type type, float *retval); bool getClientInfo(session_t peer_id, ClientState *state, u32 *uptime, u8* ser_vers, u16* prot_vers, u8* major, u8* minor, u8* patch, - std::string* vers_string); + std::string* vers_string, std::string* lang_code); void printToConsoleOnly(const std::string &text); @@ -358,6 +358,9 @@ public: // Send block to specific player only bool SendBlock(session_t peer_id, const v3s16 &blockpos); + // Load translations for a language + void loadTranslationLanguage(const std::string &lang_code); + // Bind address Address m_bind_addr; diff --git a/src/translation.cpp b/src/translation.cpp index d17467ce7..8bbaee0a3 100644 --- a/src/translation.cpp +++ b/src/translation.cpp @@ -20,9 +20,18 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "translation.h" #include "log.h" #include "util/string.h" +#include -static Translations main_translations; -Translations *g_translations = &main_translations; + +#ifndef SERVER +// Client translations +Translations client_translations; +Translations *g_client_translations = &client_translations; +#endif + +// Per language server translations +std::unordered_map server_translations; +std::unordered_map *g_server_translations = &server_translations; Translations::~Translations() { diff --git a/src/translation.h b/src/translation.h index 18fc6c38f..71423b15e 100644 --- a/src/translation.h +++ b/src/translation.h @@ -23,7 +23,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include class Translations; -extern Translations *g_translations; +extern std::unordered_map *g_server_translations; +#ifndef SERVER +extern Translations *g_client_translations; +#endif class Translations { diff --git a/src/util/string.cpp b/src/util/string.cpp index 2ee3ec735..6e1db798c 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -693,10 +693,12 @@ void str_replace(std::string &str, char from, char to) * before filling it again. */ -void translate_all(const std::wstring &s, size_t &i, std::wstring &res); +void translate_all(const std::wstring &s, size_t &i, + Translations *translations, std::wstring &res); -void translate_string(const std::wstring &s, const std::wstring &textdomain, - size_t &i, std::wstring &res) { +void translate_string(const std::wstring &s, Translations *translations, + const std::wstring &textdomain, size_t &i, std::wstring &res) +{ std::wostringstream output; std::vector args; int arg_number = 1; @@ -750,7 +752,7 @@ void translate_string(const std::wstring &s, const std::wstring &textdomain, if (arg_number >= 10) { errorstream << "Ignoring too many arguments to translation" << std::endl; std::wstring arg; - translate_all(s, i, arg); + translate_all(s, i, translations, arg); args.push_back(arg); continue; } @@ -758,7 +760,7 @@ void translate_string(const std::wstring &s, const std::wstring &textdomain, output << arg_number; ++arg_number; std::wstring arg; - translate_all(s, i, arg); + translate_all(s, i, translations, arg); args.push_back(arg); } else { // This is an escape sequence *inside* the template string to translate itself. @@ -767,8 +769,13 @@ void translate_string(const std::wstring &s, const std::wstring &textdomain, } } + std::wstring toutput; // Translate the template. - std::wstring toutput = g_translations->getTranslation(textdomain, output.str()); + if (translations != nullptr) + toutput = translations->getTranslation( + textdomain, output.str()); + else + toutput = output.str(); // Put back the arguments in the translated template. std::wostringstream result; @@ -802,7 +809,9 @@ void translate_string(const std::wstring &s, const std::wstring &textdomain, res = result.str(); } -void translate_all(const std::wstring &s, size_t &i, std::wstring &res) { +void translate_all(const std::wstring &s, size_t &i, + Translations *translations, std::wstring &res) +{ std::wostringstream output; while (i < s.length()) { // Not an escape sequence: just add the character. @@ -851,7 +860,7 @@ void translate_all(const std::wstring &s, size_t &i, std::wstring &res) { if (parts.size() > 1) textdomain = parts[1]; std::wstring translated; - translate_string(s, textdomain, i, translated); + translate_string(s, translations, textdomain, i, translated); output << translated; } else { // Another escape sequence, such as colors. Preserve it. @@ -862,9 +871,21 @@ void translate_all(const std::wstring &s, size_t &i, std::wstring &res) { res = output.str(); } -std::wstring translate_string(const std::wstring &s) { +// Translate string server side +std::wstring translate_string(const std::wstring &s, Translations *translations) +{ size_t i = 0; std::wstring res; - translate_all(s, i, res); + translate_all(s, i, translations, res); return res; } + +// Translate string client side +std::wstring translate_string(const std::wstring &s) +{ +#ifdef SERVER + return translate_string(s, nullptr); +#else + return translate_string(s, g_client_translations); +#endif +} diff --git a/src/util/string.h b/src/util/string.h index 0d2a6bdb2..185fb55e2 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -31,6 +31,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include +class Translations; + #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) @@ -650,6 +652,8 @@ std::vector > split(const std::basic_string &s, T delim) return tokens; } +std::wstring translate_string(const std::wstring &s, Translations *translations); + std::wstring translate_string(const std::wstring &s); inline std::wstring unescape_translate(const std::wstring &s) { -- cgit v1.2.3 From ac368af4fe27f61f5a4209cdfe90956ff745993c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 1 May 2020 21:44:28 +0200 Subject: Allow connection info to be missing from minetest.get_player_information() (#9739) fixes #9352 This reverts commit 23c907befea02005e2c0c87fca0131b60aace18a. --- doc/lua_api.txt | 11 +++-- src/network/connection.h | 6 +-- src/script/lua_api/l_server.cpp | 102 ++++++++++++++++++++-------------------- 3 files changed, 61 insertions(+), 58 deletions(-) (limited to 'src/network') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 5e4e18b62..f9107b623 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -4160,17 +4160,18 @@ Utilities { address = "127.0.0.1", -- IP address of client ip_version = 4, -- IPv4 / IPv6 + connection_uptime = 200, -- seconds since client connected + protocol_version = 32, -- protocol version used by client + formspec_version = 2, -- supported formspec version + lang_code = "fr" -- Language code used for translation + -- the following keys can be missing if no stats have been collected yet min_rtt = 0.01, -- minimum round trip time max_rtt = 0.2, -- maximum round trip time avg_rtt = 0.02, -- average round trip time min_jitter = 0.01, -- minimum packet time jitter max_jitter = 0.5, -- maximum packet time jitter avg_jitter = 0.03, -- average packet time jitter - connection_uptime = 200, -- seconds since client connected - protocol_version = 32, -- protocol version used by client - formspec_version = 2, -- supported formspec version - lang_code = "fr" -- Language code used for translation - -- following information is available on debug build only!!! + -- the following information is available in a debug build only!!! -- DO NOT USE IN MODS --ser_vers = 26, -- serialization version used by client --major = 0, -- major version number diff --git a/src/network/connection.h b/src/network/connection.h index 85f021c4c..47b0805ce 100644 --- a/src/network/connection.h +++ b/src/network/connection.h @@ -612,16 +612,16 @@ class Peer { struct rttstats { float jitter_min = FLT_MAX; float jitter_max = 0.0f; - float jitter_avg = -2.0f; + float jitter_avg = -1.0f; float min_rtt = FLT_MAX; float max_rtt = 0.0f; - float avg_rtt = -2.0f; + float avg_rtt = -1.0f; rttstats() = default; }; rttstats m_rtt; - float m_last_rtt = -2.0f; + float m_last_rtt = -1.0f; // current usage count unsigned int m_usage = 0; diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index 7137484e8..b6754938e 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -138,53 +138,54 @@ int ModApiServer::l_get_player_ip(lua_State *L) // get_player_information(name) int ModApiServer::l_get_player_information(lua_State *L) { - NO_MAP_LOCK_REQUIRED; - const char * name = luaL_checkstring(L, 1); - RemotePlayer *player = dynamic_cast(getEnv(L))->getPlayer(name); - if (player == NULL) { + + Server *server = getServer(L); + + const char *name = luaL_checkstring(L, 1); + RemotePlayer *player = server->getEnv().getPlayer(name); + if (!player) { lua_pushnil(L); // no such player return 1; } Address addr; - try - { - addr = getServer(L)->getPeerAddress(player->getPeerId()); - } catch(const con::PeerNotFoundException &) { + try { + addr = server->getPeerAddress(player->getPeerId()); + } catch (const con::PeerNotFoundException &) { dstream << FUNCTION_NAME << ": peer was not found" << std::endl; lua_pushnil(L); // error return 1; } - float min_rtt,max_rtt,avg_rtt,min_jitter,max_jitter,avg_jitter; + float min_rtt, max_rtt, avg_rtt, min_jitter, max_jitter, avg_jitter; ClientState state; u32 uptime; u16 prot_vers; - u8 ser_vers,major,minor,patch; - std::string vers_string; - std::string lang_code; - -#define ERET(code) \ - if (!(code)) { \ - dstream << FUNCTION_NAME << ": peer was not found" << std::endl; \ - lua_pushnil(L); /* error */ \ - return 1; \ + u8 ser_vers, major, minor, patch; + std::string vers_string, lang_code; + + auto getConInfo = [&] (con::rtt_stat_type type, float *value) -> bool { + return server->getClientConInfo(player->getPeerId(), type, value); + }; + + bool have_con_info = + getConInfo(con::MIN_RTT, &min_rtt) && + getConInfo(con::MAX_RTT, &max_rtt) && + getConInfo(con::AVG_RTT, &avg_rtt) && + getConInfo(con::MIN_JITTER, &min_jitter) && + getConInfo(con::MAX_JITTER, &max_jitter) && + getConInfo(con::AVG_JITTER, &avg_jitter); + + bool r = server->getClientInfo(player->getPeerId(), &state, &uptime, + &ser_vers, &prot_vers, &major, &minor, &patch, &vers_string, + &lang_code); + if (!r) { + dstream << FUNCTION_NAME << ": peer was not found" << std::endl; + lua_pushnil(L); // error + return 1; } - ERET(getServer(L)->getClientConInfo(player->getPeerId(), con::MIN_RTT, &min_rtt)) - ERET(getServer(L)->getClientConInfo(player->getPeerId(), con::MAX_RTT, &max_rtt)) - ERET(getServer(L)->getClientConInfo(player->getPeerId(), con::AVG_RTT, &avg_rtt)) - ERET(getServer(L)->getClientConInfo(player->getPeerId(), con::MIN_JITTER, - &min_jitter)) - ERET(getServer(L)->getClientConInfo(player->getPeerId(), con::MAX_JITTER, - &max_jitter)) - ERET(getServer(L)->getClientConInfo(player->getPeerId(), con::AVG_JITTER, - &avg_jitter)) - - ERET(getServer(L)->getClientInfo(player->getPeerId(), &state, &uptime, &ser_vers, - &prot_vers, &major, &minor, &patch, &vers_string, &lang_code)) - lua_newtable(L); int table = lua_gettop(L); @@ -202,29 +203,31 @@ int ModApiServer::l_get_player_information(lua_State *L) } lua_settable(L, table); - lua_pushstring(L,"min_rtt"); - lua_pushnumber(L, min_rtt); - lua_settable(L, table); + if (have_con_info) { // may be missing + lua_pushstring(L, "min_rtt"); + lua_pushnumber(L, min_rtt); + lua_settable(L, table); - lua_pushstring(L,"max_rtt"); - lua_pushnumber(L, max_rtt); - lua_settable(L, table); + lua_pushstring(L, "max_rtt"); + lua_pushnumber(L, max_rtt); + lua_settable(L, table); - lua_pushstring(L,"avg_rtt"); - lua_pushnumber(L, avg_rtt); - lua_settable(L, table); + lua_pushstring(L, "avg_rtt"); + lua_pushnumber(L, avg_rtt); + lua_settable(L, table); - lua_pushstring(L,"min_jitter"); - lua_pushnumber(L, min_jitter); - lua_settable(L, table); + lua_pushstring(L, "min_jitter"); + lua_pushnumber(L, min_jitter); + lua_settable(L, table); - lua_pushstring(L,"max_jitter"); - lua_pushnumber(L, max_jitter); - lua_settable(L, table); + lua_pushstring(L, "max_jitter"); + lua_pushnumber(L, max_jitter); + lua_settable(L, table); - lua_pushstring(L,"avg_jitter"); - lua_pushnumber(L, avg_jitter); - lua_settable(L, table); + lua_pushstring(L, "avg_jitter"); + lua_pushnumber(L, avg_jitter); + lua_settable(L, table); + } lua_pushstring(L,"connection_uptime"); lua_pushnumber(L, uptime); @@ -268,7 +271,6 @@ int ModApiServer::l_get_player_information(lua_State *L) lua_settable(L, table); #endif -#undef ERET return 1; } -- cgit v1.2.3 From e0ea87f1f32273dba2eb5421c2a8c890479ba078 Mon Sep 17 00:00:00 2001 From: ANAND Date: Sat, 2 May 2020 16:22:11 +0530 Subject: set_fov: Add support for time-based transitions (#9705) --- doc/lua_api.txt | 15 ++++--- src/client/camera.cpp | 84 +++++++++++++++++++++++++++++-------- src/client/camera.h | 15 ++++++- src/network/clientpackethandler.cpp | 15 ++++++- src/network/networkprotocol.h | 3 +- src/player.h | 12 ++++-- src/script/lua_api/l_object.cpp | 11 +++-- src/server.cpp | 4 +- 8 files changed, 124 insertions(+), 35 deletions(-) (limited to 'src/network') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index f9107b623..44f62f7a7 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -5998,15 +5998,18 @@ object you are working with still exists. * max: bubbles bar is not shown * See [Object properties] for more information * Is limited to range 0 ... 65535 (2^16 - 1) -* `set_fov(fov, is_multiplier)`: Sets player's FOV +* `set_fov(fov, is_multiplier, transition_time)`: Sets player's FOV * `fov`: FOV value. * `is_multiplier`: Set to `true` if the FOV value is a multiplier. Defaults to `false`. - * Set to 0 to clear FOV override. -* `get_fov()`: - * Returns player's FOV override in degrees, and a boolean depending on whether - the value is a multiplier. - * Returns 0 as first value if player's FOV hasn't been overridden. + * `transition_time`: If defined, enables smooth FOV transition. + Interpreted as the time (in seconds) to reach target FOV. + If set to 0, FOV change is instantaneous. Defaults to 0. + * Set `fov` to 0 to clear FOV override. +* `get_fov()`: Returns the following: + * Server-sent FOV value. Returns 0 if an FOV override doesn't exist. + * Boolean indicating whether the FOV value is a multiplier. + * Time (in seconds) taken for the FOV transition. Set by `set_fov`. * `set_attribute(attribute, value)`: DEPRECATED, use get_meta() instead * Sets an extra attribute with value on player. * `value` must be a string, or a number which will be converted to a diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 69bd82a47..1a5253db4 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -86,6 +86,51 @@ Camera::~Camera() m_wieldmgr->drop(); } +void Camera::notifyFovChange() +{ + LocalPlayer *player = m_client->getEnv().getLocalPlayer(); + assert(player); + + PlayerFovSpec spec = player->getFov(); + + /* + * Update m_old_fov_degrees first - it serves as the starting point of the + * upcoming transition. + * + * If an FOV transition is already active, mark current FOV as the start of + * the new transition. If not, set it to the previous transition's target FOV. + */ + if (m_fov_transition_active) + m_old_fov_degrees = m_curr_fov_degrees; + else + m_old_fov_degrees = m_server_sent_fov ? m_target_fov_degrees : m_cache_fov; + + /* + * Update m_server_sent_fov next - it corresponds to the target FOV of the + * upcoming transition. + * + * Set it to m_cache_fov, if server-sent FOV is 0. Otherwise check if + * server-sent FOV is a multiplier, and multiply it with m_cache_fov instead + * of overriding. + */ + if (spec.fov == 0.0f) { + m_server_sent_fov = false; + m_target_fov_degrees = m_cache_fov; + } else { + m_server_sent_fov = true; + m_target_fov_degrees = spec.is_multiplier ? m_cache_fov * spec.fov : spec.fov; + } + + if (spec.transition_time > 0.0f) + m_fov_transition_active = true; + + // If FOV smooth transition is active, initialize required variables + if (m_fov_transition_active) { + m_transition_time = spec.transition_time; + m_fov_diff = m_target_fov_degrees - m_old_fov_degrees; + } +} + bool Camera::successfullyCreated(std::string &error_message) { if (!m_playernode) { @@ -462,33 +507,38 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r m_camera_position = my_cp; /* - * Apply server-sent FOV. If server doesn't enforce FOV, - * check for zoom and set to zoom FOV. - * Otherwise, default to m_cache_fov + * Apply server-sent FOV, instantaneous or smooth transition. + * If not, check for zoom and set to zoom FOV. + * Otherwise, default to m_cache_fov. */ - - f32 fov_degrees; - PlayerFovSpec fov_spec = player->getFov(); - if (fov_spec.fov > 0.0f) { - // If server-sent FOV is a multiplier, multiply - // it with m_cache_fov instead of overriding - if (fov_spec.is_multiplier) - fov_degrees = m_cache_fov * fov_spec.fov; - else - fov_degrees = fov_spec.fov; + if (m_fov_transition_active) { + // Smooth FOV transition + // Dynamically calculate FOV delta based on frametimes + f32 delta = (frametime / m_transition_time) * m_fov_diff; + m_curr_fov_degrees += delta; + + // Mark transition as complete if target FOV has been reached + if ((m_fov_diff > 0.0f && m_curr_fov_degrees >= m_target_fov_degrees) || + (m_fov_diff < 0.0f && m_curr_fov_degrees <= m_target_fov_degrees)) { + m_fov_transition_active = false; + m_curr_fov_degrees = m_target_fov_degrees; + } + } else if (m_server_sent_fov) { + // Instantaneous FOV change + m_curr_fov_degrees = m_target_fov_degrees; } else if (player->getPlayerControl().zoom && player->getZoomFOV() > 0.001f) { // Player requests zoom, apply zoom FOV - fov_degrees = player->getZoomFOV(); + m_curr_fov_degrees = player->getZoomFOV(); } else { // Set to client's selected FOV - fov_degrees = m_cache_fov; + m_curr_fov_degrees = m_cache_fov; } - fov_degrees = rangelim(fov_degrees, 1.0f, 160.0f); + m_curr_fov_degrees = rangelim(m_curr_fov_degrees, 1.0f, 160.0f); // FOV and aspect ratio const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize(); m_aspect = (f32) window_size.X / (f32) window_size.Y; - m_fov_y = fov_degrees * M_PI / 180.0; + m_fov_y = m_curr_fov_degrees * M_PI / 180.0; // Increase vertical FOV on lower aspect ratios (<16:10) m_fov_y *= MYMAX(1.0, MYMIN(1.4, sqrt(16./10. / m_aspect))); m_fov_x = 2 * atan(m_aspect * tan(0.5 * m_fov_y)); diff --git a/src/client/camera.h b/src/client/camera.h index 6ec37fe10..3a59637bc 100644 --- a/src/client/camera.h +++ b/src/client/camera.h @@ -112,6 +112,9 @@ public: return MYMAX(m_fov_x, m_fov_y); } + // Notify about new server-sent FOV and initialize smooth FOV transition + void notifyFovChange(); + // Checks if the constructor was able to create the scene nodes bool successfullyCreated(std::string &error_message); @@ -186,6 +189,9 @@ private: Client *m_client; + // Default Client FOV (as defined by the "fov" setting) + f32 m_cache_fov; + // Absolute camera position v3f m_camera_position; // Absolute camera direction @@ -193,6 +199,14 @@ private: // Camera offset v3s16 m_camera_offset; + // Server-sent FOV variables + bool m_server_sent_fov = false; + f32 m_curr_fov_degrees, m_old_fov_degrees, m_target_fov_degrees; + + // FOV transition variables + bool m_fov_transition_active = false; + f32 m_fov_diff, m_transition_time; + v2f m_wieldmesh_offset = v2f(55.0f, -35.0f); v2f m_arm_dir; v2f m_cam_vel; @@ -230,7 +244,6 @@ private: f32 m_cache_fall_bobbing_amount; f32 m_cache_view_bobbing_amount; - f32 m_cache_fov; bool m_arm_inertia; std::list m_nametags; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index d19dc3818..6428ed752 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/client.h" #include "util/base64.h" +#include "client/camera.h" #include "chatmessage.h" #include "client/clientmedia.h" #include "log.h" @@ -530,11 +531,21 @@ void Client::handleCommand_Movement(NetworkPacket* pkt) void Client::handleCommand_Fov(NetworkPacket *pkt) { f32 fov; - bool is_multiplier; + bool is_multiplier = false; + f32 transition_time = 0.0f; + *pkt >> fov >> is_multiplier; + // Wrap transition_time extraction within a + // try-catch to preserve backwards compat + try { + *pkt >> transition_time; + } catch (PacketError &e) {}; + LocalPlayer *player = m_env.getLocalPlayer(); - player->setFov({ fov, is_multiplier }); + assert(player); + player->setFov({ fov, is_multiplier, transition_time }); + m_camera->notifyFovChange(); } void Client::handleCommand_HP(NetworkPacket *pkt) diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 4b7345b15..73523ea42 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -384,8 +384,9 @@ enum ToClientCommand /* Sends an FOV override/multiplier to client. - float fov + f32 fov bool is_multiplier + f32 transition_time */ TOCLIENT_DEATHSCREEN = 0x37, diff --git a/src/player.h b/src/player.h index de7f427e9..3bc7762fa 100644 --- a/src/player.h +++ b/src/player.h @@ -35,7 +35,13 @@ with this program; if not, write to the Free Software Foundation, Inc., struct PlayerFovSpec { f32 fov; + + // Whether to multiply the client's FOV or to override it bool is_multiplier; + + // The time to be take to trasition to the new FOV value. + // Transition is instantaneous if omitted. Omitted by default. + f32 transition_time; }; struct PlayerControl @@ -186,12 +192,12 @@ public: void setFov(const PlayerFovSpec &spec) { - m_fov_spec = spec; + m_fov_override_spec = spec; } const PlayerFovSpec &getFov() const { - return m_fov_spec; + return m_fov_override_spec; } u32 keyPressed = 0; @@ -208,7 +214,7 @@ protected: char m_name[PLAYERNAME_SIZE]; v3f m_speed; u16 m_wield_index = 0; - PlayerFovSpec m_fov_spec = { 0.0f, false }; + PlayerFovSpec m_fov_override_spec = { 0.0f, false, 0.0f }; std::vector hud; private: diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 77e1e7dc2..dcaee10b2 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1249,7 +1249,7 @@ int ObjectRef::l_set_look_yaw(lua_State *L) return 1; } -// set_fov(self, degrees[, is_multiplier]) +// set_fov(self, degrees[, is_multiplier, transition_time]) int ObjectRef::l_set_fov(lua_State *L) { NO_MAP_LOCK_REQUIRED; @@ -1258,7 +1258,11 @@ int ObjectRef::l_set_fov(lua_State *L) if (!player) return 0; - player->setFov({ static_cast(luaL_checknumber(L, 2)), readParam(L, 3) }); + player->setFov({ + static_cast(luaL_checknumber(L, 2)), + readParam(L, 3, false), + lua_isnumber(L, 4) ? static_cast(luaL_checknumber(L, 4)) : 0.0f + }); getServer(L)->SendPlayerFov(player->getPeerId()); return 0; @@ -1276,8 +1280,9 @@ int ObjectRef::l_get_fov(lua_State *L) PlayerFovSpec fov_spec = player->getFov(); lua_pushnumber(L, fov_spec.fov); lua_pushboolean(L, fov_spec.is_multiplier); + lua_pushnumber(L, fov_spec.transition_time); - return 2; + return 3; } // set_breath(self, breath) diff --git a/src/server.cpp b/src/server.cpp index a7eb52837..0346d197d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1892,10 +1892,10 @@ void Server::SendMovePlayer(session_t peer_id) void Server::SendPlayerFov(session_t peer_id) { - NetworkPacket pkt(TOCLIENT_FOV, 4 + 1, peer_id); + NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id); PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov(); - pkt << fov_spec.fov << fov_spec.is_multiplier; + pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time; Send(&pkt); } -- cgit v1.2.3 From cad5b987ad4720bd6a49d3604be9e81ea348f799 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Mon, 4 May 2020 20:19:12 +0200 Subject: Sky API: Rename *_tint to fog_*_tint for consistency --- src/client/game.cpp | 12 ++++++------ src/client/sky.cpp | 12 ++++++------ src/network/clientpackethandler.cpp | 8 ++++---- src/network/networkprotocol.h | 6 +++--- src/remoteplayer.cpp | 6 +++--- src/script/lua_api/l_object.cpp | 22 +++++++++++----------- src/server.cpp | 4 ++-- src/skyparams.h | 6 +++--- 8 files changed, 38 insertions(+), 38 deletions(-) (limited to 'src/network') diff --git a/src/client/game.cpp b/src/client/game.cpp index 3bdac786c..d1eb3bba2 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2799,9 +2799,9 @@ void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam) // Update mesh based skybox colours if applicable. sky->setSkyColors(*event->set_sky); sky->setHorizonTint( - event->set_sky->sun_tint, - event->set_sky->moon_tint, - event->set_sky->tint_type + event->set_sky->fog_sun_tint, + event->set_sky->fog_moon_tint, + event->set_sky->fog_tint_type ); } else if (event->set_sky->type == "skybox" && event->set_sky->textures.size() == 6) { @@ -2811,9 +2811,9 @@ void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam) sky->setFallbackBgColor(event->set_sky->bgcolor); // Set sunrise and sunset fog tinting: sky->setHorizonTint( - event->set_sky->sun_tint, - event->set_sky->moon_tint, - event->set_sky->tint_type + event->set_sky->fog_sun_tint, + event->set_sky->fog_moon_tint, + event->set_sky->fog_tint_type ); // Add textures to skybox. for (int i = 0; i < 6; i++) diff --git a/src/client/sky.cpp b/src/client/sky.cpp index 7a7b188ce..ce33b96ae 100644 --- a/src/client/sky.cpp +++ b/src/client/sky.cpp @@ -529,7 +529,7 @@ void Sky::update(float time_of_day, float time_brightness, pointcolor_sun_f.g = pointcolor_light * (float)m_materials[3].EmissiveColor.getGreen() / 255; } else if (!m_default_tint) { - pointcolor_sun_f = m_sky_params.sun_tint; + pointcolor_sun_f = m_sky_params.fog_sun_tint; } else { pointcolor_sun_f.r = pointcolor_light * 1; pointcolor_sun_f.b = pointcolor_light * @@ -548,9 +548,9 @@ void Sky::update(float time_of_day, float time_brightness, ); } else { pointcolor_moon_f = video::SColorf( - (m_sky_params.moon_tint.getRed() / 255) * pointcolor_light, - (m_sky_params.moon_tint.getGreen() / 255) * pointcolor_light, - (m_sky_params.moon_tint.getBlue() / 255) * pointcolor_light, + (m_sky_params.fog_moon_tint.getRed() / 255) * pointcolor_light, + (m_sky_params.fog_moon_tint.getGreen() / 255) * pointcolor_light, + (m_sky_params.fog_moon_tint.getBlue() / 255) * pointcolor_light, 1 ); } @@ -941,8 +941,8 @@ void Sky::setHorizonTint(video::SColor sun_tint, video::SColor moon_tint, std::string use_sun_tint) { // Change sun and moon tinting: - m_sky_params.sun_tint = sun_tint; - m_sky_params.moon_tint = moon_tint; + m_sky_params.fog_sun_tint = sun_tint; + m_sky_params.fog_moon_tint = moon_tint; // Faster than comparing strings every rendering frame if (use_sun_tint == "default") m_default_tint = true; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 6428ed752..8d0225a3d 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1276,9 +1276,9 @@ void Client::handleCommand_HudSetSky(NetworkPacket* pkt) // Fix for "regular" skies, as color isn't kept: if (skybox.type == "regular") { skybox.sky_color = sky_defaults.getSkyColorDefaults(); - skybox.tint_type = "default"; - skybox.moon_tint = video::SColor(255, 255, 255, 255); - skybox.sun_tint = video::SColor(255, 255, 255, 255); + skybox.fog_tint_type = "default"; + skybox.fog_moon_tint = video::SColor(255, 255, 255, 255); + skybox.fog_sun_tint = video::SColor(255, 255, 255, 255); } else { sun.visible = false; @@ -1313,7 +1313,7 @@ void Client::handleCommand_HudSetSky(NetworkPacket* pkt) std::string texture; *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >> - skybox.sun_tint >> skybox.moon_tint >> skybox.tint_type; + skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type; if (skybox.type == "skybox") { *pkt >> texture_count; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 73523ea42..527ebba7c 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -634,9 +634,9 @@ enum ToClientCommand u8[4] night_sky (ARGB) u8[4] night_horizon (ARGB) u8[4] indoors (ARGB) - u8[4] sun_tint (ARGB) - u8[4] moon_tint (ARGB) - std::string tint_type + u8[4] fog_sun_tint (ARGB) + u8[4] fog_moon_tint (ARGB) + std::string fog_tint_type */ TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO = 0x50, diff --git a/src/remoteplayer.cpp b/src/remoteplayer.cpp index 7a603d53e..bef60c792 100644 --- a/src/remoteplayer.cpp +++ b/src/remoteplayer.cpp @@ -74,9 +74,9 @@ RemotePlayer::RemotePlayer(const char *name, IItemDefManager *idef): m_skybox_params.sky_color = sky_defaults.getSkyColorDefaults(); m_skybox_params.type = "regular"; m_skybox_params.clouds = true; - m_skybox_params.sun_tint = video::SColor(255, 244, 125, 29); - m_skybox_params.moon_tint = video::SColorf(0.5, 0.6, 0.8, 1).toSColor(); - m_skybox_params.tint_type = "default"; + m_skybox_params.fog_sun_tint = video::SColor(255, 244, 125, 29); + m_skybox_params.fog_moon_tint = video::SColorf(0.5, 0.6, 0.8, 1).toSColor(); + m_skybox_params.fog_tint_type = "default"; m_sun_params = sky_defaults.getSunDefaults(); m_moon_params = sky_defaults.getMoonDefaults(); diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index dcaee10b2..f71130378 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1782,19 +1782,19 @@ int ObjectRef::l_set_sky(lua_State *L) lua_pop(L, 1); // Prevent flickering clouds at dawn/dusk: - skybox_params.sun_tint = video::SColor(255, 255, 255, 255); + skybox_params.fog_sun_tint = video::SColor(255, 255, 255, 255); lua_getfield(L, -1, "fog_sun_tint"); - read_color(L, -1, &skybox_params.sun_tint); + read_color(L, -1, &skybox_params.fog_sun_tint); lua_pop(L, 1); - skybox_params.moon_tint = video::SColor(255, 255, 255, 255); + skybox_params.fog_moon_tint = video::SColor(255, 255, 255, 255); lua_getfield(L, -1, "fog_moon_tint"); - read_color(L, -1, &skybox_params.moon_tint); + read_color(L, -1, &skybox_params.fog_moon_tint); lua_pop(L, 1); lua_getfield(L, -1, "fog_tint_type"); if (!lua_isnil(L, -1)) - skybox_params.tint_type = luaL_checkstring(L, -1); + skybox_params.fog_tint_type = luaL_checkstring(L, -1); lua_pop(L, 1); // Because we need to leave the "sky_color" table. @@ -1912,12 +1912,12 @@ int ObjectRef::l_get_sky_color(lua_State *L) push_ARGB8(L, skybox_params.sky_color.indoors); lua_setfield(L, -2, "indoors"); } - push_ARGB8(L, skybox_params.sun_tint); - lua_setfield(L, -2, "sun_tint"); - push_ARGB8(L, skybox_params.moon_tint); - lua_setfield(L, -2, "moon_tint"); - lua_pushstring(L, skybox_params.tint_type.c_str()); - lua_setfield(L, -2, "tint_type"); + push_ARGB8(L, skybox_params.fog_sun_tint); + lua_setfield(L, -2, "fog_sun_tint"); + push_ARGB8(L, skybox_params.fog_moon_tint); + lua_setfield(L, -2, "fog_moon_tint"); + lua_pushstring(L, skybox_params.fog_tint_type.c_str()); + lua_setfield(L, -2, "fog_tint_type"); return 1; } diff --git a/src/server.cpp b/src/server.cpp index 0346d197d..05584be2d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1770,8 +1770,8 @@ void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms) pkt << params.clouds; } else { // Handle current clients and future clients pkt << params.bgcolor << params.type - << params.clouds << params.sun_tint - << params.moon_tint << params.tint_type; + << params.clouds << params.fog_sun_tint + << params.fog_moon_tint << params.fog_tint_type; if (params.type == "skybox") { pkt << (u16) params.textures.size(); diff --git a/src/skyparams.h b/src/skyparams.h index 9fdfd89da..c362ef8f3 100644 --- a/src/skyparams.h +++ b/src/skyparams.h @@ -37,9 +37,9 @@ struct SkyboxParams std::vector textures; bool clouds; SkyColor sky_color; - video::SColor sun_tint; - video::SColor moon_tint; - std::string tint_type; + video::SColor fog_sun_tint; + video::SColor fog_moon_tint; + std::string fog_tint_type; }; struct SunParams -- cgit v1.2.3 From d1c6cc72cce7e02dcaca7797b463d071d67d27db Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 5 May 2020 17:05:11 +0200 Subject: Server: Improve some log messages (#9820) --- src/network/serverpackethandler.cpp | 16 +++++++--------- src/server/player_sao.cpp | 6 +++--- 2 files changed, 10 insertions(+), 12 deletions(-) (limited to 'src/network') diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 5136eb0ec..adaa9a965 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -111,8 +111,6 @@ void Server::handleCommand_Init(NetworkPacket* pkt) if (depl_serial_v == SER_FMT_VER_INVALID) { actionstream << "Server: A mismatched client tried to connect from " << - addr_s << std::endl; - infostream << "Server: Cannot negotiate serialization version with " << addr_s << " client_max=" << (int)client_max << std::endl; DenyAccess(peer_id, SERVER_ACCESSDENIED_WRONG_VERSION); return; @@ -902,8 +900,8 @@ bool Server::checkInteractDistance(RemotePlayer *player, const f32 d, const std: actionstream << "Player " << player->getName() << " tried to access " << what << " from too far: " - << "d=" << d <<", max_d=" << max_d - << ". ignoring." << std::endl; + << "d=" << d << ", max_d=" << max_d + << "; ignoring." << std::endl; // Call callbacks m_script->on_cheat(player->getPlayerSAO(), "interacted_too_far"); return false; @@ -956,7 +954,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) } if (playersao->isDead()) { - actionstream << "Server: NoCheat: " << player->getName() + actionstream << "Server: " << player->getName() << " tried to interact while dead; ignoring." << std::endl; if (pointed.type == POINTEDTHING_NODE) { // Re-send block to revert change on client-side @@ -1145,7 +1143,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) playersao->noCheatDigEnd(); // If player didn't start digging this, ignore dig if (nocheat_p != p_under) { - infostream << "Server: NoCheat: " << player->getName() + infostream << "Server: " << player->getName() << " started digging " << PP(nocheat_p) << " and completed digging " << PP(p_under) << "; not digging." << std::endl; @@ -1169,9 +1167,9 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) } // If can't dig, ignore dig if (!params.diggable) { - infostream << "Server: NoCheat: " << player->getName() + infostream << "Server: " << player->getName() << " completed digging " << PP(p_under) - << ", which is not diggable with tool. not digging." + << ", which is not diggable with tool; not digging." << std::endl; is_valid_dig = false; // Call callbacks @@ -1195,7 +1193,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) } // Dig not possible else { - infostream << "Server: NoCheat: " << player->getName() + infostream << "Server: " << player->getName() << " completed digging " << PP(p_under) << "too fast; not digging." << std::endl; is_valid_dig = false; diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 58fcea5fe..a4d0f4ce7 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -656,9 +656,9 @@ bool PlayerSAO::checkMovementCheat() float lag_pool_max = m_env->getMaxLagEstimate() * 2.0; lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN); if (m_time_from_last_teleport > lag_pool_max) { - actionstream << "Player " << m_player->getName() - << " moved too fast; resetting position" - << std::endl; + actionstream << "Server: " << m_player->getName() + << " moved too fast: V=" << d_vert << ", H=" << d_horiz + << "; resetting position." << std::endl; cheated = true; } setBasePosition(m_last_good_position); -- cgit v1.2.3 From 454dbf83a9bf292910c1495a2aa49fd8b960c28f Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Thu, 7 May 2020 22:38:41 +0200 Subject: Server class code cleanups (#9769) * Server::overrideDayNightRatio doesn't require to return bool There is no sense to sending null player, the caller should send a valid object * Server::init: make private & cleanup This function is always called before start() and loads some variables which can be loaded in constructor directly. Make it private and call it directly in start * Split Server inventory responsibility to a dedicated object This splits permit to found various historical issues: * duplicate lookups on player connection * sending inventory to non related player when a player connects * non friendly lookups on detached inventories ownership This reduce the detached inventory complexity and also increased the lookup performance in a quite interesting way for servers with thousands of inventories. --- src/client/game.cpp | 1 - src/main.cpp | 2 - src/network/serverpackethandler.cpp | 11 ++- src/script/lua_api/l_base.cpp | 5 + src/script/lua_api/l_base.h | 2 + src/script/lua_api/l_inventory.cpp | 13 +-- src/script/lua_api/l_object.cpp | 7 +- src/server.cpp | 192 +++++------------------------------- src/server.h | 33 ++----- src/server/CMakeLists.txt | 1 + src/server/serverinventorymgr.cpp | 192 ++++++++++++++++++++++++++++++++++++ src/server/serverinventorymgr.h | 60 +++++++++++ 12 files changed, 312 insertions(+), 207 deletions(-) create mode 100644 src/server/serverinventorymgr.cpp create mode 100644 src/server/serverinventorymgr.h (limited to 'src/network') diff --git a/src/client/game.cpp b/src/client/game.cpp index 1577a37db..4d7a85526 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1302,7 +1302,6 @@ bool Game::createSingleplayerServer(const std::string &map_dir, } server = new Server(map_dir, gamespec, simple_singleplayer_mode, bind_addr, false); - server->init(); server->start(); return true; diff --git a/src/main.cpp b/src/main.cpp index 147f686ed..b3b17c2d1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -887,7 +887,6 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings & // Create server Server server(game_params.world_path, game_params.game_spec, false, bind_addr, true, &iface); - server.init(); g_term_console.setup(&iface, &kill, admin_nick); @@ -922,7 +921,6 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings & // Create server Server server(game_params.world_path, game_params.game_spec, false, bind_addr, true); - server.init(); server.start(); // Run server diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index adaa9a965..39a912827 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "network/networkprotocol.h" #include "network/serveropcodes.h" #include "server/player_sao.h" +#include "server/serverinventorymgr.h" #include "util/auth.h" #include "util/base64.h" #include "util/pointedthing.h" @@ -620,9 +621,9 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) ma->from_inv.applyCurrentPlayer(player->getName()); ma->to_inv.applyCurrentPlayer(player->getName()); - setInventoryModified(ma->from_inv); + m_inventory_mgr->setInventoryModified(ma->from_inv); if (ma->from_inv != ma->to_inv) - setInventoryModified(ma->to_inv); + m_inventory_mgr->setInventoryModified(ma->to_inv); bool from_inv_is_current_player = (ma->from_inv.type == InventoryLocation::PLAYER) && @@ -687,7 +688,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) da->from_inv.applyCurrentPlayer(player->getName()); - setInventoryModified(da->from_inv); + m_inventory_mgr->setInventoryModified(da->from_inv); /* Disable dropping items out of craftpreview @@ -723,7 +724,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) ca->craft_inv.applyCurrentPlayer(player->getName()); - setInventoryModified(ca->craft_inv); + m_inventory_mgr->setInventoryModified(ca->craft_inv); //bool craft_inv_is_current_player = // (ca->craft_inv.type == InventoryLocation::PLAYER) && @@ -739,7 +740,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) } // Do the action - a->apply(this, playersao, this); + a->apply(m_inventory_mgr.get(), playersao, this); // Eat the action delete a; } diff --git a/src/script/lua_api/l_base.cpp b/src/script/lua_api/l_base.cpp index b8658f62b..2bee09436 100644 --- a/src/script/lua_api/l_base.cpp +++ b/src/script/lua_api/l_base.cpp @@ -45,6 +45,11 @@ Server *ModApiBase::getServer(lua_State *L) return getScriptApiBase(L)->getServer(); } +ServerInventoryManager *ModApiBase::getServerInventoryMgr(lua_State *L) +{ + return getScriptApiBase(L)->getServer()->getInventoryMgr(); +} + #ifndef SERVER Client *ModApiBase::getClient(lua_State *L) { diff --git a/src/script/lua_api/l_base.h b/src/script/lua_api/l_base.h index e32647628..65fce8481 100644 --- a/src/script/lua_api/l_base.h +++ b/src/script/lua_api/l_base.h @@ -38,12 +38,14 @@ class GUIEngine; class ScriptApiBase; class Server; class Environment; +class ServerInventoryManager; class ModApiBase : protected LuaHelper { public: static ScriptApiBase* getScriptApiBase(lua_State *L); static Server* getServer(lua_State *L); + static ServerInventoryManager *getServerInventoryMgr(lua_State *L); #ifndef SERVER static Client* getClient(lua_State *L); static GUIEngine* getGuiEngine(lua_State *L); diff --git a/src/script/lua_api/l_inventory.cpp b/src/script/lua_api/l_inventory.cpp index 4c8977898..e41b5cb41 100644 --- a/src/script/lua_api/l_inventory.cpp +++ b/src/script/lua_api/l_inventory.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_converter.h" #include "common/c_content.h" #include "server.h" +#include "server/serverinventorymgr.h" #include "remoteplayer.h" /* @@ -38,7 +39,7 @@ InvRef* InvRef::checkobject(lua_State *L, int narg) Inventory* InvRef::getinv(lua_State *L, InvRef *ref) { - return getServer(L)->getInventory(ref->m_loc); + return getServerInventoryMgr(L)->getInventory(ref->m_loc); } InventoryList* InvRef::getlist(lua_State *L, InvRef *ref, @@ -54,7 +55,7 @@ InventoryList* InvRef::getlist(lua_State *L, InvRef *ref, void InvRef::reportInventoryChange(lua_State *L, InvRef *ref) { // Inform other things that the inventory has changed - getServer(L)->setInventoryModified(ref->m_loc); + getServerInventoryMgr(L)->setInventoryModified(ref->m_loc); } // Exported functions @@ -497,7 +498,7 @@ int ModApiInventory::l_get_inventory(lua_State *L) v3s16 pos = check_v3s16(L, -1); loc.setNodeMeta(pos); - if (getServer(L)->getInventory(loc) != NULL) + if (getServerInventoryMgr(L)->getInventory(loc) != NULL) InvRef::create(L, loc); else lua_pushnil(L); @@ -515,7 +516,7 @@ int ModApiInventory::l_get_inventory(lua_State *L) lua_pop(L, 1); } - if (getServer(L)->getInventory(loc) != NULL) + if (getServerInventoryMgr(L)->getInventory(loc) != NULL) InvRef::create(L, loc); else lua_pushnil(L); @@ -530,7 +531,7 @@ int ModApiInventory::l_create_detached_inventory_raw(lua_State *L) NO_MAP_LOCK_REQUIRED; const char *name = luaL_checkstring(L, 1); std::string player = readParam(L, 2, ""); - if (getServer(L)->createDetachedInventory(name, player) != NULL) { + if (getServerInventoryMgr(L)->createDetachedInventory(name, getServer(L)->idef(), player) != NULL) { InventoryLocation loc; loc.setDetached(name); InvRef::create(L, loc); @@ -545,7 +546,7 @@ int ModApiInventory::l_remove_detached_inventory_raw(lua_State *L) { NO_MAP_LOCK_REQUIRED; const std::string &name = luaL_checkstring(L, 1); - lua_pushboolean(L, getServer(L)->removeDetachedInventory(name)); + lua_pushboolean(L, getServerInventoryMgr(L)->removeDetachedInventory(name)); return 1; } diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index f71130378..0a9f3117b 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "scripting_server.h" #include "server/luaentity_sao.h" #include "server/player_sao.h" +#include "server/serverinventorymgr.h" /* ObjectRef @@ -289,7 +290,7 @@ int ObjectRef::l_get_inventory(lua_State *L) if (co == NULL) return 0; // Do it InventoryLocation loc = co->getInventoryLocation(); - if (getServer(L)->getInventory(loc) != NULL) + if (getServerInventoryMgr(L)->getInventory(loc) != NULL) InvRef::create(L, loc); else lua_pushnil(L); // An object may have no inventory (nil) @@ -2172,9 +2173,7 @@ int ObjectRef::l_override_day_night_ratio(lua_State *L) ratio = readParam(L, 2); } - if (!getServer(L)->overrideDayNightRatio(player, do_override, ratio)) - return 0; - + getServer(L)->overrideDayNightRatio(player, do_override, ratio); lua_pushboolean(L, true); return 1; } diff --git a/src/server.cpp b/src/server.cpp index 05584be2d..16e026ce2 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -64,6 +64,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "chat_interface.h" #include "remoteplayer.h" #include "server/player_sao.h" +#include "server/serverinventorymgr.h" #include "translation.h" class ClientNotFoundException : public BaseException @@ -338,11 +339,6 @@ Server::~Server() infostream << "Server: Deinitializing scripting" << std::endl; delete m_script; - // Delete detached inventories - for (auto &detached_inventory : m_detached_inventories) { - delete detached_inventory.second; - } - while (!m_unsent_map_edit_queue.empty()) { delete m_unsent_map_edit_queue.front(); m_unsent_map_edit_queue.pop(); @@ -388,6 +384,9 @@ void Server::init() m_script = new ServerScripting(this); + // Must be created before mod loading because we have some inventory creation + m_inventory_mgr = std::unique_ptr(new ServerInventoryManager()); + m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME); m_modmgr->loadMods(m_script); @@ -422,6 +421,7 @@ void Server::init() // Initialize Environment m_env = new ServerEnvironment(servermap, m_script, this, m_path_world); + m_inventory_mgr->setEnv(m_env); m_clients.setEnv(m_env); if (!servermap->settings_mgr.makeMapgenParams()) @@ -443,6 +443,8 @@ void Server::init() m_env->loadMeta(); + // Those settings can be overwritten in world.mt, they are + // intended to be cached after environment loading. m_liquid_transform_every = g_settings->getFloat("liquid_update"); m_max_chatmessage_length = g_settings->getU16("chat_message_max_size"); m_csm_restriction_flags = g_settings->getU64("csm_restriction_flags"); @@ -451,6 +453,8 @@ void Server::init() void Server::start() { + init(); + infostream << "Starting server on " << m_bind_addr.serializeString() << "..." << std::endl; @@ -1180,82 +1184,6 @@ void Server::onMapEditEvent(const MapEditEvent &event) m_unsent_map_edit_queue.push(new MapEditEvent(event)); } -Inventory* Server::getInventory(const InventoryLocation &loc) -{ - switch (loc.type) { - case InventoryLocation::UNDEFINED: - case InventoryLocation::CURRENT_PLAYER: - break; - case InventoryLocation::PLAYER: - { - RemotePlayer *player = m_env->getPlayer(loc.name.c_str()); - if(!player) - return NULL; - PlayerSAO *playersao = player->getPlayerSAO(); - if(!playersao) - return NULL; - return playersao->getInventory(); - } - break; - case InventoryLocation::NODEMETA: - { - NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p); - if(!meta) - return NULL; - return meta->getInventory(); - } - break; - case InventoryLocation::DETACHED: - { - if(m_detached_inventories.count(loc.name) == 0) - return NULL; - return m_detached_inventories[loc.name]; - } - break; - default: - sanity_check(false); // abort - break; - } - return NULL; -} - -void Server::setInventoryModified(const InventoryLocation &loc) -{ - switch(loc.type){ - case InventoryLocation::UNDEFINED: - break; - case InventoryLocation::PLAYER: - { - - RemotePlayer *player = m_env->getPlayer(loc.name.c_str()); - - if (!player) - return; - - player->setModified(true); - player->inventory.setModified(true); - // Updates are sent in ServerEnvironment::step() - } - break; - case InventoryLocation::NODEMETA: - { - MapEditEvent event; - event.type = MEET_BLOCK_NODE_METADATA_CHANGED; - event.p = loc.p; - m_env->getMap().dispatchEvent(event); - } - break; - case InventoryLocation::DETACHED: - { - // Updates are sent in ServerEnvironment::step() - } - break; - default: - sanity_check(false); // abort - break; - } -} - void Server::SetBlocksNotSent(std::map& block) { std::vector clients = m_clients.getClientIDs(); @@ -2712,40 +2640,20 @@ void Server::sendRequestedMedia(session_t peer_id, } } -void Server::sendDetachedInventory(const std::string &name, session_t peer_id) +void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id) { - const auto &inv_it = m_detached_inventories.find(name); - const auto &player_it = m_detached_inventories_player.find(name); - - if (player_it == m_detached_inventories_player.end() || - player_it->second.empty()) { - // OK. Send to everyone - } else { - if (!m_env) - return; // Mods are not done loading - - RemotePlayer *p = m_env->getPlayer(player_it->second.c_str()); - if (!p) - return; // Player is offline - - if (peer_id != PEER_ID_INEXISTENT && peer_id != p->getPeerId()) - return; // Caller requested send to a different player, so don't send. - - peer_id = p->getPeerId(); - } - NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id); pkt << name; - if (inv_it == m_detached_inventories.end()) { + if (!inventory) { pkt << false; // Remove inventory } else { pkt << true; // Update inventory // Serialization & NetworkPacket isn't a love story std::ostringstream os(std::ios_base::binary); - inv_it->second->serialize(os); - inv_it->second->setModified(false); + inventory->serialize(os); + inventory->setModified(false); const std::string &os_str = os.str(); pkt << static_cast(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients @@ -2760,16 +2668,17 @@ void Server::sendDetachedInventory(const std::string &name, session_t peer_id) void Server::sendDetachedInventories(session_t peer_id, bool incremental) { - for (const auto &detached_inventory : m_detached_inventories) { - const std::string &name = detached_inventory.first; - if (incremental) { - Inventory *inv = detached_inventory.second; - if (!inv || !inv->checkModified()) - continue; - } - - sendDetachedInventory(name, peer_id); + // Lookup player name, to filter detached inventories just after + std::string peer_name; + if (peer_id != PEER_ID_INEXISTENT) { + peer_name = getClient(peer_id, CS_Created)->getName(); } + + auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) { + sendDetachedInventory(inv, name, peer_id); + }; + + m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb); } /* @@ -3442,15 +3351,12 @@ void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms) SendCloudParams(player->getPeerId(), params); } -bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override, +void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override, float ratio) { - if (!player) - return false; - + sanity_check(player); player->overrideDayNightRatio(do_override, ratio); SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio); - return true; } void Server::notifyPlayers(const std::wstring &msg) @@ -3541,52 +3447,6 @@ void Server::deleteParticleSpawner(const std::string &playername, u32 id) SendDeleteParticleSpawner(peer_id, id); } -Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player) -{ - if(m_detached_inventories.count(name) > 0){ - infostream<<"Server clearing detached inventory \""<second; - m_detached_inventories.erase(inv_it); - - if (!m_env) // Mods are not done loading - return true; - - const auto &player_it = m_detached_inventories_player.find(name); - if (player_it != m_detached_inventories_player.end()) { - RemotePlayer *player = m_env->getPlayer(player_it->second.c_str()); - - if (player && player->getPeerId() != PEER_ID_INEXISTENT) - sendDetachedInventory(name, player->getPeerId()); - - m_detached_inventories_player.erase(player_it); - } else { - // Notify all players about the change - sendDetachedInventory(name, PEER_ID_INEXISTENT); - } - return true; -} - // actions: time-reversed list // Return value: success/failure bool Server::rollbackRevertActions(const std::list &actions, @@ -3607,7 +3467,7 @@ bool Server::rollbackRevertActions(const std::list &actions, for (const RollbackAction &action : actions) { num_tried++; - bool success = action.applyRevert(map, this, this); + bool success = action.applyRevert(map, m_inventory_mgr.get(), this); if(!success){ num_failed++; std::ostringstream os; diff --git a/src/server.h b/src/server.h index 71059dd30..7a1de9370 100644 --- a/src/server.h +++ b/src/server.h @@ -68,6 +68,7 @@ struct MoonParams; struct StarParams; class ServerThread; class ServerModManager; +class ServerInventoryManager; enum ClientDeletionReason { CDR_LEAVE, @@ -116,7 +117,7 @@ struct ServerPlayingSound }; class Server : public con::PeerHandler, public MapEventReceiver, - public InventoryManager, public IGameDef + public IGameDef { public: /* @@ -134,7 +135,6 @@ public: ~Server(); DISABLE_CLASS_COPY(Server); - void init(); void start(); void stop(); // This is mainly a way to pass the time to the server. @@ -196,12 +196,6 @@ public: */ void onMapEditEvent(const MapEditEvent &event); - /* - Shall be called with the environment and the connection locked. - */ - Inventory* getInventory(const InventoryLocation &loc); - void setInventoryModified(const InventoryLocation &loc); - // Connection must be locked when called std::wstring getStatusString(); inline double getUptime() const { return m_uptime_counter->get(); } @@ -253,10 +247,8 @@ public: void deleteParticleSpawner(const std::string &playername, u32 id); - // Creates or resets inventory - Inventory *createDetachedInventory(const std::string &name, - const std::string &player = ""); - bool removeDetachedInventory(const std::string &name); + ServerInventoryManager *getInventoryMgr() const { return m_inventory_mgr.get(); } + void sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id); // Envlock and conlock should be locked when using scriptapi ServerScripting *getScriptIface(){ return m_script; } @@ -318,7 +310,7 @@ public: void setClouds(RemotePlayer *player, const CloudParams ¶ms); - bool overrideDayNightRatio(RemotePlayer *player, bool do_override, float brightness); + void overrideDayNightRatio(RemotePlayer *player, bool do_override, float brightness); /* con::PeerHandler implementation. */ void peerAdded(con::Peer *peer); @@ -389,6 +381,8 @@ private: float m_timer = 0.0f; }; + void init(); + void SendMovement(session_t peer_id); void SendHP(session_t peer_id, u16 hp); void SendBreath(session_t peer_id, u16 breath); @@ -457,8 +451,6 @@ private: void sendRequestedMedia(session_t peer_id, const std::vector &tosend); - void sendDetachedInventory(const std::string &name, session_t peer_id); - // Adds a ParticleSpawner on peer with peer_id (PEER_ID_INEXISTENT == all) void SendAddParticleSpawner(session_t peer_id, u16 protocol_version, u16 amount, float spawntime, @@ -656,14 +648,6 @@ private: s32 m_next_sound_id = 0; // positive values only s32 nextSoundId(); - /* - Detached inventories (behind m_env_mutex) - */ - // key = name - std::map m_detached_inventories; - // value = "" (visible to all players) or player name - std::map m_detached_inventories_player; - std::unordered_map m_mod_storages; float m_mod_storage_save_timer = 10.0f; @@ -674,6 +658,9 @@ private: // ModChannel manager std::unique_ptr m_modchannel_mgr; + // Inventory manager + std::unique_ptr m_inventory_mgr; + // Global server metrics backend std::unique_ptr m_metrics_backend; diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 4d94504f6..0a5a8f3a7 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -4,5 +4,6 @@ set(server_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/mods.cpp ${CMAKE_CURRENT_SOURCE_DIR}/player_sao.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serveractiveobject.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/serverinventorymgr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/unit_sao.cpp PARENT_SCOPE) diff --git a/src/server/serverinventorymgr.cpp b/src/server/serverinventorymgr.cpp new file mode 100644 index 000000000..555e01ec6 --- /dev/null +++ b/src/server/serverinventorymgr.cpp @@ -0,0 +1,192 @@ +/* +Minetest +Copyright (C) 2010-2020 Minetest core development team + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "serverinventorymgr.h" +#include "map.h" +#include "nodemetadata.h" +#include "player_sao.h" +#include "remoteplayer.h" +#include "server.h" +#include "serverenvironment.h" + +ServerInventoryManager::ServerInventoryManager() : InventoryManager() +{ +} + +ServerInventoryManager::~ServerInventoryManager() +{ + // Delete detached inventories + for (auto &detached_inventory : m_detached_inventories) { + delete detached_inventory.second.inventory; + } +} + +Inventory *ServerInventoryManager::getInventory(const InventoryLocation &loc) +{ + switch (loc.type) { + case InventoryLocation::UNDEFINED: + case InventoryLocation::CURRENT_PLAYER: + break; + case InventoryLocation::PLAYER: { + RemotePlayer *player = m_env->getPlayer(loc.name.c_str()); + if (!player) + return NULL; + PlayerSAO *playersao = player->getPlayerSAO(); + if (!playersao) + return NULL; + return playersao->getInventory(); + } break; + case InventoryLocation::NODEMETA: { + NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p); + if (!meta) + return NULL; + return meta->getInventory(); + } break; + case InventoryLocation::DETACHED: { + auto it = m_detached_inventories.find(loc.name); + if (it == m_detached_inventories.end()) + return nullptr; + return it->second.inventory; + } break; + default: + sanity_check(false); // abort + break; + } + return NULL; +} + +void ServerInventoryManager::setInventoryModified(const InventoryLocation &loc) +{ + switch (loc.type) { + case InventoryLocation::UNDEFINED: + break; + case InventoryLocation::PLAYER: { + + RemotePlayer *player = m_env->getPlayer(loc.name.c_str()); + + if (!player) + return; + + player->setModified(true); + player->inventory.setModified(true); + // Updates are sent in ServerEnvironment::step() + } break; + case InventoryLocation::NODEMETA: { + MapEditEvent event; + event.type = MEET_BLOCK_NODE_METADATA_CHANGED; + event.p = loc.p; + m_env->getMap().dispatchEvent(event); + } break; + case InventoryLocation::DETACHED: { + // Updates are sent in ServerEnvironment::step() + } break; + default: + sanity_check(false); // abort + break; + } +} + +Inventory *ServerInventoryManager::createDetachedInventory( + const std::string &name, IItemDefManager *idef, const std::string &player) +{ + if (m_detached_inventories.count(name) > 0) { + infostream << "Server clearing detached inventory \"" << name << "\"" + << std::endl; + delete m_detached_inventories[name].inventory; + } else { + infostream << "Server creating detached inventory \"" << name << "\"" + << std::endl; + } + + Inventory *inv = new Inventory(idef); + sanity_check(inv); + m_detached_inventories[name].inventory = inv; + if (!player.empty()) { + m_detached_inventories[name].owner = player; + + if (!m_env) + return inv; // Mods are not loaded yet, ignore + + RemotePlayer *p = m_env->getPlayer(name.c_str()); + + // if player is connected, send him the inventory + if (p && p->getPeerId() != PEER_ID_INEXISTENT) { + m_env->getGameDef()->sendDetachedInventory( + inv, name, p->getPeerId()); + } + } else { + if (!m_env) + return inv; // Mods are not loaded yet, don't send + + // Inventory is for everybody, broadcast + m_env->getGameDef()->sendDetachedInventory(inv, name, PEER_ID_INEXISTENT); + } + + return inv; +} + +bool ServerInventoryManager::removeDetachedInventory(const std::string &name) +{ + const auto &inv_it = m_detached_inventories.find(name); + if (inv_it == m_detached_inventories.end()) + return false; + + delete inv_it->second.inventory; + const std::string &owner = inv_it->second.owner; + + if (!owner.empty()) { + RemotePlayer *player = m_env->getPlayer(owner.c_str()); + + if (player && player->getPeerId() != PEER_ID_INEXISTENT) + m_env->getGameDef()->sendDetachedInventory( + nullptr, name, player->getPeerId()); + + } else { + // Notify all players about the change + m_env->getGameDef()->sendDetachedInventory( + nullptr, name, PEER_ID_INEXISTENT); + } + + m_detached_inventories.erase(inv_it); + + return true; +} + +void ServerInventoryManager::sendDetachedInventories(const std::string &peer_name, + bool incremental, + std::function apply_cb) +{ + for (const auto &detached_inventory : m_detached_inventories) { + const DetachedInventory &dinv = detached_inventory.second; + if (incremental) { + if (!dinv.inventory || !dinv.inventory->checkModified()) + continue; + } + + // if we are pushing inventories to a specific player + // we should filter to send only the right inventories + if (!peer_name.empty()) { + const std::string &attached_player = dinv.owner; + if (!attached_player.empty() && peer_name != attached_player) + continue; + } + + apply_cb(detached_inventory.first, detached_inventory.second.inventory); + } +} diff --git a/src/server/serverinventorymgr.h b/src/server/serverinventorymgr.h new file mode 100644 index 000000000..d0aac4dae --- /dev/null +++ b/src/server/serverinventorymgr.h @@ -0,0 +1,60 @@ +/* +Minetest +Copyright (C) 2010-2020 Minetest core development team + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "inventorymanager.h" +#include + +class ServerEnvironment; + +class ServerInventoryManager : public InventoryManager +{ +public: + ServerInventoryManager(); + virtual ~ServerInventoryManager(); + + void setEnv(ServerEnvironment *env) + { + assert(!m_env); + m_env = env; + } + + Inventory *getInventory(const InventoryLocation &loc); + void setInventoryModified(const InventoryLocation &loc); + + // Creates or resets inventory + Inventory *createDetachedInventory(const std::string &name, IItemDefManager *idef, + const std::string &player = ""); + bool removeDetachedInventory(const std::string &name); + + void sendDetachedInventories(const std::string &peer_name, bool incremental, + std::function apply_cb); + +private: + struct DetachedInventory + { + Inventory *inventory; + std::string owner; + }; + + ServerEnvironment *m_env = nullptr; + + std::unordered_map m_detached_inventories; +}; \ No newline at end of file -- cgit v1.2.3 From 6e1372bd894d955300c40d69e5c882e9cc7d7523 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Mon, 11 May 2020 21:40:45 +0200 Subject: Add support for statbar “off state” icons (#9462) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for optional “off state” icons for statbars. “off state icons” can be used to denote the lack of something, like missing hearts or bubbles. Add "off state" textures to the builtin statbars. Co-authored-by: SmallJoker --- builtin/game/statbars.lua | 4 ++ doc/lua_api.txt | 16 ++++-- doc/texture_packs.txt | 4 ++ src/client/clientevent.h | 1 + src/client/game.cpp | 7 +++ src/client/hud.cpp | 96 ++++++++++++++++++++++++++++++------ src/client/hud.h | 5 +- src/hud.cpp | 1 + src/hud.h | 2 + src/network/clientpackethandler.cpp | 15 ++---- src/network/networkprotocol.h | 6 ++- src/script/common/c_content.cpp | 8 +++ src/server.cpp | 3 +- textures/base/pack/bubble_gone.png | Bin 0 -> 68 bytes textures/base/pack/heart_gone.png | Bin 0 -> 68 bytes 15 files changed, 132 insertions(+), 36 deletions(-) create mode 100644 textures/base/pack/bubble_gone.png create mode 100644 textures/base/pack/heart_gone.png (limited to 'src/network') diff --git a/builtin/game/statbars.lua b/builtin/game/statbars.lua index 6b5b54428..d192029c5 100644 --- a/builtin/game/statbars.lua +++ b/builtin/game/statbars.lua @@ -5,7 +5,9 @@ local health_bar_definition = { hud_elem_type = "statbar", position = {x = 0.5, y = 1}, text = "heart.png", + text2 = "heart_gone.png", number = core.PLAYER_MAX_HP_DEFAULT, + item = core.PLAYER_MAX_HP_DEFAULT, direction = 0, size = {x = 24, y = 24}, offset = {x = (-10 * 24) - 25, y = -(48 + 24 + 16)}, @@ -15,7 +17,9 @@ local breath_bar_definition = { hud_elem_type = "statbar", position = {x = 0.5, y = 1}, text = "bubble.png", + text2 = "bubble_gone.png", number = core.PLAYER_MAX_BREATH_DEFAULT, + item = core.PLAYER_MAX_BREATH_DEFAULT * 2, direction = 0, size = {x = 24, y = 24}, offset = {x = 25, y= -(48 + 24 + 16)}, diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 961e1ff37..4078e21a1 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1289,9 +1289,9 @@ To account for differing resolutions, the position coordinates are the percentage of the screen, ranging in value from `0` to `1`. The name field is not yet used, but should contain a description of what the -HUD element represents. The direction field is the direction in which something -is drawn. +HUD element represents. +The `direction` field is the direction in which something is drawn. `0` draws from left to right, `1` draws from right to left, `2` draws from top to bottom, and `3` draws from bottom to top. @@ -1355,12 +1355,16 @@ Displays text on the HUD. ### `statbar` -Displays a horizontal bar made up of half-images. +Displays a horizontal bar made up of half-images with an optional background. -* `text`: The name of the texture that is used. +* `text`: The name of the texture to use. +* `text2`: Optional texture name to enable a background / "off state" + texture (useful to visualize the maximal value). Both textures + must have the same size. * `number`: The number of half-textures that are displayed. If odd, will end with a vertically center-split texture. -* `direction` +* `item`: Same as `number` but for the "off state" texture +* `direction`: To which direction the images will extend to * `offset`: offset in pixels from position. * `size`: If used, will force full-image size to this value (override texture pack image size) @@ -7772,6 +7776,8 @@ Used by `Player:hud_add`. Returned by `Player:hud_get`. text = "", + text2 = "", + number = 2, item = 3, diff --git a/doc/texture_packs.txt b/doc/texture_packs.txt index 4e7bc93c4..94151f1a4 100644 --- a/doc/texture_packs.txt +++ b/doc/texture_packs.txt @@ -64,6 +64,8 @@ by texture packs. All existing fallback textures can be found in the directory * `bubble.png`: the bubble texture when the player is drowning (default size: 12×12) +* `bubble_gone.png`: like `bubble.png`, but denotes lack of breath + (transparent by default, same size as bubble.png) * `crack_anylength.png`: node overlay texture when digging @@ -76,6 +78,8 @@ by texture packs. All existing fallback textures can be found in the directory * `heart.png`: used to display the health points of the player (default size: 12×12) +* `heart_gone.png`: like `heart.png`, but denotes lack of health points + (transparent by default, same size as heart.png) * `minimap_mask_round.png`: round minimap mask, white gets replaced by the map * `minimap_mask_square.png`: mask used for the square minimap diff --git a/src/client/clientevent.h b/src/client/clientevent.h index f5689c25b..7f3984b03 100644 --- a/src/client/clientevent.h +++ b/src/client/clientevent.h @@ -136,6 +136,7 @@ struct ClientEvent v3f *world_pos; v2s32 *size; s16 z_index; + std::string *text2; } hudadd; struct { diff --git a/src/client/game.cpp b/src/client/game.cpp index 4d7a85526..422e17d4f 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2672,6 +2672,7 @@ void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam) delete event->hudadd.offset; delete event->hudadd.world_pos; delete event->hudadd.size; + delete event->hudadd.text2; return; } @@ -2689,6 +2690,7 @@ void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam) e->world_pos = *event->hudadd.world_pos; e->size = *event->hudadd.size; e->z_index = event->hudadd.z_index; + e->text2 = *event->hudadd.text2; hud_server_to_client[server_id] = player->addHud(e); delete event->hudadd.pos; @@ -2699,6 +2701,7 @@ void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam) delete event->hudadd.offset; delete event->hudadd.world_pos; delete event->hudadd.size; + delete event->hudadd.text2; } void Game::handleClientEvent_HudRemove(ClientEvent *event, CameraOrientation *cam) @@ -2771,6 +2774,10 @@ void Game::handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *ca case HUD_STAT_Z_INDEX: e->z_index = event->hudchange.data; break; + + case HUD_STAT_TEXT2: + e->text2 = *event->hudchange.sdata; + break; } delete event->hudchange.v3fdata; diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 56763e7e4..f8f712762 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -332,7 +332,8 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) break; } case HUD_ELEM_STATBAR: { v2s32 offs(e->offset.X, e->offset.Y); - drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->number, offs, e->size); + drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->text2, + e->number, e->item, offs, e->size); break; } case HUD_ELEM_INVENTORY: { InventoryList *inv = inventory->getList(e->text); @@ -401,8 +402,9 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) } -void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &texture, - s32 count, v2s32 offset, v2s32 size) +void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, + const std::string &texture, const std::string &bgtexture, + s32 count, s32 maxcount, v2s32 offset, v2s32 size) { const video::SColor color(255, 255, 255, 255); const video::SColor colors[] = {color, color, color, color}; @@ -411,6 +413,11 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &tex if (!stat_texture) return; + video::ITexture *stat_texture_bg = nullptr; + if (!bgtexture.empty()) { + stat_texture_bg = tsrc->getTexture(bgtexture); + } + core::dimension2di srcd(stat_texture->getOriginalSize()); core::dimension2di dstd; if (size == v2s32()) { @@ -430,43 +437,100 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &tex p += offset; v2s32 steppos; - core::rect srchalfrect, dsthalfrect; switch (drawdir) { case HUD_DIR_RIGHT_LEFT: steppos = v2s32(-1, 0); - srchalfrect = core::rect(srcd.Width / 2, 0, srcd.Width, srcd.Height); - dsthalfrect = core::rect(dstd.Width / 2, 0, dstd.Width, dstd.Height); break; case HUD_DIR_TOP_BOTTOM: steppos = v2s32(0, 1); - srchalfrect = core::rect(0, 0, srcd.Width, srcd.Height / 2); - dsthalfrect = core::rect(0, 0, dstd.Width, dstd.Height / 2); break; case HUD_DIR_BOTTOM_TOP: steppos = v2s32(0, -1); - srchalfrect = core::rect(0, srcd.Height / 2, srcd.Width, srcd.Height); - dsthalfrect = core::rect(0, dstd.Height / 2, dstd.Width, dstd.Height); break; default: + // From left to right steppos = v2s32(1, 0); - srchalfrect = core::rect(0, 0, srcd.Width / 2, srcd.Height); - dsthalfrect = core::rect(0, 0, dstd.Width / 2, dstd.Height); + break; + } + + auto calculate_clipping_rect = [] (core::dimension2di src, + v2s32 steppos) -> core::rect { + + // Create basic rectangle + core::rect rect(0, 0, + src.Width - std::abs(steppos.X) * src.Width / 2, + src.Height - std::abs(steppos.Y) * src.Height / 2 + ); + // Move rectangle left or down + if (steppos.X == -1) + rect += v2s32(src.Width / 2, 0); + if (steppos.Y == -1) + rect += v2s32(0, src.Height / 2); + return rect; + }; + // Rectangles for 1/2 the actual value to display + core::rect srchalfrect, dsthalfrect; + // Rectangles for 1/2 the "off state" texture + core::rect srchalfrect2, dsthalfrect2; + + if (count % 2 == 1) { + // Need to draw halves: Calculate rectangles + srchalfrect = calculate_clipping_rect(srcd, steppos); + dsthalfrect = calculate_clipping_rect(dstd, steppos); + srchalfrect2 = calculate_clipping_rect(srcd, steppos * -1); + dsthalfrect2 = calculate_clipping_rect(dstd, steppos * -1); } + steppos.X *= dstd.Width; steppos.Y *= dstd.Height; + // Draw full textures for (s32 i = 0; i < count / 2; i++) { core::rect srcrect(0, 0, srcd.Width, srcd.Height); - core::rect dstrect(0,0, dstd.Width, dstd.Height); + core::rect dstrect(0, 0, dstd.Width, dstd.Height); dstrect += p; - draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true); + draw2DImageFilterScaled(driver, stat_texture, + dstrect, srcrect, NULL, colors, true); p += steppos; } if (count % 2 == 1) { - dsthalfrect += p; - draw2DImageFilterScaled(driver, stat_texture, dsthalfrect, srchalfrect, NULL, colors, true); + // Draw half a texture + draw2DImageFilterScaled(driver, stat_texture, + dsthalfrect + p, srchalfrect, NULL, colors, true); + + if (stat_texture_bg && maxcount > count) { + draw2DImageFilterScaled(driver, stat_texture_bg, + dsthalfrect2 + p, srchalfrect2, + NULL, colors, true); + p += steppos; + } + } + + if (stat_texture_bg && maxcount > count / 2) { + // Draw "off state" textures + s32 start_offset; + if (count % 2 == 1) + start_offset = count / 2 + 1; + else + start_offset = count / 2; + for (s32 i = start_offset; i < maxcount / 2; i++) { + core::rect srcrect(0, 0, srcd.Width, srcd.Height); + core::rect dstrect(0, 0, dstd.Width, dstd.Height); + + dstrect += p; + draw2DImageFilterScaled(driver, stat_texture_bg, + dstrect, srcrect, + NULL, colors, true); + p += steppos; + } + + if (maxcount % 2 == 1) { + draw2DImageFilterScaled(driver, stat_texture_bg, + dsthalfrect + p, srchalfrect, + NULL, colors, true); + } } } diff --git a/src/client/hud.h b/src/client/hud.h index cab115990..6274b1a83 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -82,8 +82,9 @@ public: private: bool calculateScreenPos(const v3s16 &camera_offset, HudElement *e, v2s32 *pos); - void drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &texture, - s32 count, v2s32 offset, v2s32 size = v2s32()); + void drawStatbar(v2s32 pos, u16 corner, u16 drawdir, + const std::string &texture, const std::string& bgtexture, + s32 count, s32 maxcount, v2s32 offset, v2s32 size = v2s32()); void drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount, s32 inv_offset, InventoryList *mainlist, u16 selectitem, diff --git a/src/hud.cpp b/src/hud.cpp index 39625b5fd..3079b5cd8 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -46,6 +46,7 @@ const struct EnumString es_HudElementStat[] = {HUD_STAT_WORLD_POS, "world_pos"}, {HUD_STAT_SIZE, "size"}, {HUD_STAT_Z_INDEX, "z_index"}, + {HUD_STAT_TEXT2, "text2"}, {0, NULL}, }; diff --git a/src/hud.h b/src/hud.h index b0977c6a4..bab420ed2 100644 --- a/src/hud.h +++ b/src/hud.h @@ -77,6 +77,7 @@ enum HudElementStat { HUD_STAT_WORLD_POS, HUD_STAT_SIZE, HUD_STAT_Z_INDEX, + HUD_STAT_TEXT2, }; struct HudElement { @@ -93,6 +94,7 @@ struct HudElement { v3f world_pos; v2s32 size; s16 z_index = 0; + std::string text2; }; extern const EnumString es_HudElementType[]; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 8d0225a3d..7b1b1368c 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1102,22 +1102,16 @@ void Client::handleCommand_HudAdd(NetworkPacket* pkt) v3f world_pos; v2s32 size; s16 z_index = 0; + std::string text2; *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item >> dir >> align >> offset; try { *pkt >> world_pos; - } - catch(SerializationError &e) {}; - - try { *pkt >> size; - } catch(SerializationError &e) {}; - - try { *pkt >> z_index; - } - catch(PacketError &e) {} + *pkt >> text2; + } catch(PacketError &e) {}; ClientEvent *event = new ClientEvent(); event->type = CE_HUDADD; @@ -1135,6 +1129,7 @@ void Client::handleCommand_HudAdd(NetworkPacket* pkt) event->hudadd.world_pos = new v3f(world_pos); event->hudadd.size = new v2s32(size); event->hudadd.z_index = z_index; + event->hudadd.text2 = new std::string(text2); m_client_event_queue.push(event); } @@ -1171,7 +1166,7 @@ void Client::handleCommand_HudChange(NetworkPacket* pkt) if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE || stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET) *pkt >> v2fdata; - else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT) + else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT || stat == HUD_STAT_TEXT2) *pkt >> sdata; else if (stat == HUD_STAT_WORLD_POS) *pkt >> v3fdata; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 527ebba7c..ab924f1db 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -560,10 +560,10 @@ enum ToClientCommand u32 id u8 type v2f1000 pos - u32 len + u16 len u8[len] name v2f1000 scale - u32 len2 + u16 len2 u8[len2] text u32 number u32 item @@ -573,6 +573,8 @@ enum ToClientCommand v3f1000 world_pos v2s32 size s16 z_index + u16 len3 + u8[len3] text2 */ TOCLIENT_HUDRM = 0x4a, diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index dac828316..540b7222f 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -1871,6 +1871,7 @@ void read_hud_element(lua_State *L, HudElement *elem) elem->dir = getintfield_default(L, 2, "direction", 0); elem->z_index = MYMAX(S16_MIN, MYMIN(S16_MAX, getintfield_default(L, 2, "z_index", 0))); + elem->text2 = getstringfield_default(L, 2, "text2", ""); // Deprecated, only for compatibility's sake if (elem->dir == 0) @@ -1939,6 +1940,9 @@ void push_hud_element(lua_State *L, HudElement *elem) lua_pushnumber(L, elem->z_index); lua_setfield(L, -2, "z_index"); + + lua_pushstring(L, elem->text2.c_str()); + lua_setfield(L, -2, "text2"); } HudElementStat read_hud_change(lua_State *L, HudElement *elem, void **value) @@ -2000,6 +2004,10 @@ HudElementStat read_hud_change(lua_State *L, HudElement *elem, void **value) elem->z_index = MYMAX(S16_MIN, MYMIN(S16_MAX, luaL_checknumber(L, 4))); *value = &elem->z_index; break; + case HUD_STAT_TEXT2: + elem->text2 = luaL_checkstring(L, 4); + *value = &elem->text2; + break; } return stat; } diff --git a/src/server.cpp b/src/server.cpp index 16e026ce2..b28c30e1e 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1621,7 +1621,7 @@ void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form) pkt << id << (u8) form->type << form->pos << form->name << form->scale << form->text << form->number << form->item << form->dir << form->align << form->offset << form->world_pos << form->size - << form->z_index; + << form->z_index << form->text2; Send(&pkt); } @@ -1647,6 +1647,7 @@ void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void break; case HUD_STAT_NAME: case HUD_STAT_TEXT: + case HUD_STAT_TEXT2: pkt << *(std::string *) value; break; case HUD_STAT_WORLD_POS: diff --git a/textures/base/pack/bubble_gone.png b/textures/base/pack/bubble_gone.png new file mode 100644 index 000000000..240ca4f8d Binary files /dev/null and b/textures/base/pack/bubble_gone.png differ diff --git a/textures/base/pack/heart_gone.png b/textures/base/pack/heart_gone.png new file mode 100644 index 000000000..240ca4f8d Binary files /dev/null and b/textures/base/pack/heart_gone.png differ -- cgit v1.2.3 From d76785b4c70d834783d2e578086680941257cfa1 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 14 May 2020 21:18:26 +0200 Subject: network: Replace a fatal_error with just error logging --- src/network/connectionthreads.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'src/network') diff --git a/src/network/connectionthreads.cpp b/src/network/connectionthreads.cpp index 1f33d2ded..9a6617a1c 100644 --- a/src/network/connectionthreads.cpp +++ b/src/network/connectionthreads.cpp @@ -336,11 +336,9 @@ bool ConnectionSendThread::rawSendAsPacket(session_t peer_id, u8 channelnum, { PeerHelper peer = m_connection->getPeerNoEx(peer_id); if (!peer) { - LOG(dout_con << m_connection->getDesc() - << " INFO: dropped packet for non existent peer_id: " - << peer_id << std::endl); - FATAL_ERROR_IF(!reliable, - "Trying to send raw packet reliable but no peer found!"); + LOG(errorstream << m_connection->getDesc() + << " dropped " << (reliable ? "reliable " : "") + << "packet for non existent peer_id: " << peer_id << std::endl); return false; } Channel *channel = &(dynamic_cast(&peer)->channels[channelnum]); -- cgit v1.2.3 From a08251a61e5c6f407621d04b2de4da6812410c66 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 16 May 2020 12:03:36 +0200 Subject: Log protocol ver on mismatched client connect too --- src/network/serverpackethandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/network') diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 39a912827..2fa9d4196 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -112,7 +112,7 @@ void Server::handleCommand_Init(NetworkPacket* pkt) if (depl_serial_v == SER_FMT_VER_INVALID) { actionstream << "Server: A mismatched client tried to connect from " << - addr_s << " client_max=" << (int)client_max << std::endl; + addr_s << " ser_fmt_max=" << (int)client_max << std::endl; DenyAccess(peer_id, SERVER_ACCESSDENIED_WRONG_VERSION); return; } @@ -147,7 +147,7 @@ void Server::handleCommand_Init(NetworkPacket* pkt) net_proto_version < SERVER_PROTOCOL_VERSION_MIN || net_proto_version > SERVER_PROTOCOL_VERSION_MAX) { actionstream << "Server: A mismatched client tried to connect from " << - addr_s << std::endl; + addr_s << " proto_max=" << (int)max_net_proto_version << std::endl; DenyAccess(peer_id, SERVER_ACCESSDENIED_WRONG_VERSION); return; } -- cgit v1.2.3 From 1357ea1da25bf01acaf95d5f5419d4f83a84ed61 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 22 May 2020 13:23:25 +0200 Subject: Cleanup of particle & particlespawner structures and code (#9893) --- src/CMakeLists.txt | 1 + src/client/clientevent.h | 45 +---- src/client/particles.cpp | 333 ++++++++++++------------------- src/client/particles.h | 58 ++---- src/network/clientpackethandler.cpp | 136 ++++--------- src/particles.cpp | 53 +++++ src/particles.h | 73 +++++++ src/script/lua_api/l_particles.cpp | 186 ++++++++--------- src/script/lua_api/l_particles_local.cpp | 149 ++++++-------- src/server.cpp | 106 ++++------ src/server.h | 42 +--- 11 files changed, 502 insertions(+), 680 deletions(-) create mode 100644 src/particles.cpp create mode 100644 src/particles.h (limited to 'src/network') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dd68ceec9..3d6d1b0ea 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -427,6 +427,7 @@ set(common_SRCS noise.cpp objdef.cpp object_properties.cpp + particles.cpp pathfinder.cpp player.cpp porting.cpp diff --git a/src/client/clientevent.h b/src/client/clientevent.h index 7f3984b03..9bd31efce 100644 --- a/src/client/clientevent.h +++ b/src/client/clientevent.h @@ -21,8 +21,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "irrlichttypes_bloated.h" -#include "hud.h" -#include "skyparams.h" + +struct ParticleParameters; +struct ParticleSpawnerParameters; +struct SkyboxParams; +struct SunParams; +struct MoonParams; +struct StarParams; enum ClientEventType : u8 { @@ -77,44 +82,12 @@ struct ClientEvent } show_formspec; // struct{ //} textures_updated; + ParticleParameters *spawn_particle; struct { - v3f *pos; - v3f *vel; - v3f *acc; - f32 expirationtime; - f32 size; - bool collisiondetection; - bool collision_removal; - bool object_collision; - bool vertical; - std::string *texture; - struct TileAnimationParams animation; - u8 glow; - } spawn_particle; - struct - { - u16 amount; - f32 spawntime; - v3f *minpos; - v3f *maxpos; - v3f *minvel; - v3f *maxvel; - v3f *minacc; - v3f *maxacc; - f32 minexptime; - f32 maxexptime; - f32 minsize; - f32 maxsize; - bool collisiondetection; - bool collision_removal; - bool object_collision; + ParticleSpawnerParameters *p; u16 attached_id; - bool vertical; - std::string *texture; u64 id; - struct TileAnimationParams animation; - u8 glow; } add_particlespawner; struct { diff --git a/src/client/particles.cpp b/src/client/particles.cpp index a0e4e54eb..c2e751b4f 100644 --- a/src/client/particles.cpp +++ b/src/client/particles.cpp @@ -37,32 +37,31 @@ with this program; if not, write to the Free Software Foundation, Inc., Utility */ -v3f random_v3f(v3f min, v3f max) +static f32 random_f32(f32 min, f32 max) +{ + return rand() / (float)RAND_MAX * (max - min) + min; +} + +static v3f random_v3f(v3f min, v3f max) { return v3f( - rand() / (float)RAND_MAX * (max.X - min.X) + min.X, - rand() / (float)RAND_MAX * (max.Y - min.Y) + min.Y, - rand() / (float)RAND_MAX * (max.Z - min.Z) + min.Z); + random_f32(min.X, max.X), + random_f32(min.Y, max.Y), + random_f32(min.Z, max.Z)); } +/* + Particle +*/ + Particle::Particle( IGameDef *gamedef, LocalPlayer *player, ClientEnvironment *env, - v3f pos, - v3f velocity, - v3f acceleration, - float expirationtime, - float size, - bool collisiondetection, - bool collision_removal, - bool object_collision, - bool vertical, + const ParticleParameters &p, video::ITexture *texture, v2f texpos, v2f texsize, - const struct TileAnimationParams &anim, - u8 glow, video::SColor color ): scene::ISceneNode(RenderingEngine::get_scene_manager()->getRootSceneNode(), @@ -81,33 +80,28 @@ Particle::Particle( m_material.setTexture(0, texture); m_texpos = texpos; m_texsize = texsize; - m_animation = anim; + m_animation = p.animation; // Color m_base_color = color; m_color = color; // Particle related - m_pos = pos; - m_velocity = velocity; - m_acceleration = acceleration; - m_expiration = expirationtime; + m_pos = p.pos; + m_velocity = p.vel; + m_acceleration = p.acc; + m_expiration = p.expirationtime; m_player = player; - m_size = size; - m_collisiondetection = collisiondetection; - m_collision_removal = collision_removal; - m_object_collision = object_collision; - m_vertical = vertical; - m_glow = glow; + m_size = p.size; + m_collisiondetection = p.collisiondetection; + m_collision_removal = p.collision_removal; + m_object_collision = p.object_collision; + m_vertical = p.vertical; + m_glow = p.glow; // Irrlicht stuff - m_collisionbox = aabb3f( - -size / 2, - -size / 2, - -size / 2, - size / 2, - size / 2, - size / 2); + const float c = p.size / 2; + m_collisionbox = aabb3f(-c, -c, -c, c, c, c); this->setAutomaticCulling(scene::EAC_OFF); // Init lighting @@ -255,52 +249,22 @@ void Particle::updateVertices() ParticleSpawner::ParticleSpawner( IGameDef *gamedef, LocalPlayer *player, - u16 amount, - float time, - v3f minpos, v3f maxpos, - v3f minvel, v3f maxvel, - v3f minacc, v3f maxacc, - float minexptime, float maxexptime, - float minsize, float maxsize, - bool collisiondetection, - bool collision_removal, - bool object_collision, + const ParticleSpawnerParameters &p, u16 attached_id, - bool vertical, video::ITexture *texture, - const struct TileAnimationParams &anim, - u8 glow, ParticleManager *p_manager ): - m_particlemanager(p_manager) + m_particlemanager(p_manager), p(p) { m_gamedef = gamedef; m_player = player; - m_amount = amount; - m_spawntime = time; - m_minpos = minpos; - m_maxpos = maxpos; - m_minvel = minvel; - m_maxvel = maxvel; - m_minacc = minacc; - m_maxacc = maxacc; - m_minexptime = minexptime; - m_maxexptime = maxexptime; - m_minsize = minsize; - m_maxsize = maxsize; - m_collisiondetection = collisiondetection; - m_collision_removal = collision_removal; - m_object_collision = object_collision; m_attached_id = attached_id; - m_vertical = vertical; m_texture = texture; m_time = 0; - m_animation = anim; - m_glow = glow; - for (u16 i = 0; i <= m_amount; i++) - { - float spawntime = (float)rand() / (float)RAND_MAX * m_spawntime; + m_spawntimes.reserve(p.amount + 1); + for (u16 i = 0; i <= p.amount; i++) { + float spawntime = rand() / (float)RAND_MAX * p.time; m_spawntimes.push_back(spawntime); } } @@ -309,7 +273,7 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius, const core::matrix4 *attached_absolute_pos_rot_matrix) { v3f ppos = m_player->getPosition() / BS; - v3f pos = random_v3f(m_minpos, m_maxpos); + v3f pos = random_v3f(p.minpos, p.maxpos); // Need to apply this first or the following check // will be wrong for attached spawners @@ -326,41 +290,32 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius, if (pos.getDistanceFrom(ppos) > radius) return; - v3f vel = random_v3f(m_minvel, m_maxvel); - v3f acc = random_v3f(m_minacc, m_maxacc); + // Parameters for the single particle we're about to spawn + ParticleParameters pp; + pp.pos = pos; + + pp.vel = random_v3f(p.minvel, p.maxvel); + pp.acc = random_v3f(p.minacc, p.maxacc); if (attached_absolute_pos_rot_matrix) { // Apply attachment rotation - attached_absolute_pos_rot_matrix->rotateVect(vel); - attached_absolute_pos_rot_matrix->rotateVect(acc); + attached_absolute_pos_rot_matrix->rotateVect(pp.vel); + attached_absolute_pos_rot_matrix->rotateVect(pp.acc); } - float exptime = rand() / (float)RAND_MAX - * (m_maxexptime - m_minexptime) - + m_minexptime; + pp.expirationtime = random_f32(p.minexptime, p.maxexptime); + pp.size = random_f32(p.minsize, p.maxsize); - float size = rand() / (float)RAND_MAX - * (m_maxsize - m_minsize) - + m_minsize; + p.copyCommon(pp); m_particlemanager->addParticle(new Particle( m_gamedef, m_player, env, - pos, - vel, - acc, - exptime, - size, - m_collisiondetection, - m_collision_removal, - m_object_collision, - m_vertical, + pp, m_texture, v2f(0.0, 0.0), - v2f(1.0, 1.0), - m_animation, - m_glow + v2f(1.0, 1.0) )); } @@ -381,12 +336,11 @@ void ParticleSpawner::step(float dtime, ClientEnvironment *env) } } - if (m_spawntime != 0) { + if (p.time != 0) { // Spawner exists for a predefined timespan - for (std::vector::iterator i = m_spawntimes.begin(); - i != m_spawntimes.end();) { - if ((*i) <= m_time && m_amount > 0) { - --m_amount; + for (auto i = m_spawntimes.begin(); i != m_spawntimes.end(); ) { + if ((*i) <= m_time && p.amount > 0) { + --p.amount; // Pretend to, but don't actually spawn a particle if it is // attached to an unloaded object or distant from player. @@ -405,13 +359,16 @@ void ParticleSpawner::step(float dtime, ClientEnvironment *env) if (unloaded) return; - for (int i = 0; i <= m_amount; i++) { + for (int i = 0; i <= p.amount; i++) { if (rand() / (float)RAND_MAX < dtime) spawnParticle(env, radius, attached_absolute_pos_rot_matrix); } } } +/* + ParticleManager +*/ ParticleManager::ParticleManager(ClientEnvironment *env) : m_env(env) @@ -479,99 +436,84 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, { switch (event->type) { case CE_DELETE_PARTICLESPAWNER: { - MutexAutoLock lock(m_spawner_list_lock); - if (m_particle_spawners.find(event->delete_particlespawner.id) != - m_particle_spawners.end()) { - delete m_particle_spawners.find(event->delete_particlespawner.id)->second; - m_particle_spawners.erase(event->delete_particlespawner.id); - } + deleteParticleSpawner(event->delete_particlespawner.id); // no allocated memory in delete event break; } case CE_ADD_PARTICLESPAWNER: { - { - MutexAutoLock lock(m_spawner_list_lock); - if (m_particle_spawners.find(event->add_particlespawner.id) != - m_particle_spawners.end()) { - delete m_particle_spawners.find(event->add_particlespawner.id)->second; - m_particle_spawners.erase(event->add_particlespawner.id); - } - } + deleteParticleSpawner(event->add_particlespawner.id); + + const ParticleSpawnerParameters &p = *event->add_particlespawner.p; video::ITexture *texture = - client->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture)); + client->tsrc()->getTextureForMesh(p.texture); auto toadd = new ParticleSpawner(client, player, - event->add_particlespawner.amount, - event->add_particlespawner.spawntime, - *event->add_particlespawner.minpos, - *event->add_particlespawner.maxpos, - *event->add_particlespawner.minvel, - *event->add_particlespawner.maxvel, - *event->add_particlespawner.minacc, - *event->add_particlespawner.maxacc, - event->add_particlespawner.minexptime, - event->add_particlespawner.maxexptime, - event->add_particlespawner.minsize, - event->add_particlespawner.maxsize, - event->add_particlespawner.collisiondetection, - event->add_particlespawner.collision_removal, - event->add_particlespawner.object_collision, + p, event->add_particlespawner.attached_id, - event->add_particlespawner.vertical, texture, - event->add_particlespawner.animation, - event->add_particlespawner.glow, this); - /* delete allocated content of event */ - delete event->add_particlespawner.minpos; - delete event->add_particlespawner.maxpos; - delete event->add_particlespawner.minvel; - delete event->add_particlespawner.maxvel; - delete event->add_particlespawner.minacc; - delete event->add_particlespawner.texture; - delete event->add_particlespawner.maxacc; - - { - MutexAutoLock lock(m_spawner_list_lock); - m_particle_spawners[event->add_particlespawner.id] = toadd; - } + addParticleSpawner(event->add_particlespawner.id, toadd); + + delete event->add_particlespawner.p; break; } case CE_SPAWN_PARTICLE: { + const ParticleParameters &p = *event->spawn_particle; video::ITexture *texture = - client->tsrc()->getTextureForMesh(*(event->spawn_particle.texture)); + client->tsrc()->getTextureForMesh(p.texture); Particle *toadd = new Particle(client, player, m_env, - *event->spawn_particle.pos, - *event->spawn_particle.vel, - *event->spawn_particle.acc, - event->spawn_particle.expirationtime, - event->spawn_particle.size, - event->spawn_particle.collisiondetection, - event->spawn_particle.collision_removal, - event->spawn_particle.object_collision, - event->spawn_particle.vertical, + p, texture, v2f(0.0, 0.0), - v2f(1.0, 1.0), - event->spawn_particle.animation, - event->spawn_particle.glow); + v2f(1.0, 1.0)); addParticle(toadd); - delete event->spawn_particle.pos; - delete event->spawn_particle.vel; - delete event->spawn_particle.acc; - delete event->spawn_particle.texture; - + delete event->spawn_particle; break; } default: break; } } +bool ParticleManager::getNodeParticleParams(const MapNode &n, + const ContentFeatures &f, ParticleParameters &p, + video::ITexture **texture, v2f &texpos, v2f &texsize, video::SColor *color) +{ + // No particles for "airlike" nodes + if (f.drawtype == NDT_AIRLIKE) + return false; + + // Texture + u8 texid = rand() % 6; + const TileLayer &tile = f.tiles[texid].layers[0]; + p.animation.type = TAT_NONE; + + // Only use first frame of animated texture + if (tile.material_flags & MATERIAL_FLAG_ANIMATION) + *texture = (*tile.frames)[0].texture; + else + *texture = tile.texture; + + float size = (rand() % 8) / 64.0f; + p.size = BS * size; + if (tile.scale) + size /= tile.scale; + texsize = v2f(size * 2.0f, size * 2.0f); + texpos.X = (rand() % 64) / 64.0f - texsize.X; + texpos.Y = (rand() % 64) / 64.0f - texsize.Y; + + if (tile.has_color) + *color = tile.color; + else + n.getColor(f, color); + + return true; +} + // The final burst of particles when a node is finally dug, *not* particles // spawned during the digging of a node. @@ -593,73 +535,41 @@ void ParticleManager::addDiggingParticles(IGameDef *gamedef, void ParticleManager::addNodeParticle(IGameDef *gamedef, LocalPlayer *player, v3s16 pos, const MapNode &n, const ContentFeatures &f) { - // No particles for "airlike" nodes - if (f.drawtype == NDT_AIRLIKE) - return; - - // Texture - u8 texid = myrand_range(0, 5); - const TileLayer &tile = f.tiles[texid].layers[0]; + ParticleParameters p; video::ITexture *texture; - struct TileAnimationParams anim; - anim.type = TAT_NONE; + v2f texpos, texsize; + video::SColor color; - // Only use first frame of animated texture - if (tile.material_flags & MATERIAL_FLAG_ANIMATION) - texture = (*tile.frames)[0].texture; - else - texture = tile.texture; + if (!getNodeParticleParams(n, f, p, &texture, texpos, texsize, &color)) + return; - float size = (rand() % 8) / 64.0f; - float visual_size = BS * size; - if (tile.scale) - size /= tile.scale; - v2f texsize(size * 2.0f, size * 2.0f); - v2f texpos; - texpos.X = (rand() % 64) / 64.0f - texsize.X; - texpos.Y = (rand() % 64) / 64.0f - texsize.Y; + p.expirationtime = (rand() % 100) / 100.0f; // Physics - v3f velocity( + p.vel = v3f( (rand() % 150) / 50.0f - 1.5f, (rand() % 150) / 50.0f, (rand() % 150) / 50.0f - 1.5f ); - v3f acceleration( + p.acc = v3f( 0.0f, -player->movement_gravity * player->physics_override_gravity / BS, 0.0f ); - v3f particlepos = v3f( + p.pos = v3f( (f32)pos.X + (rand() % 100) / 200.0f - 0.25f, (f32)pos.Y + (rand() % 100) / 200.0f - 0.25f, (f32)pos.Z + (rand() % 100) / 200.0f - 0.25f ); - video::SColor color; - if (tile.has_color) - color = tile.color; - else - n.getColor(f, &color); - Particle *toadd = new Particle( gamedef, player, m_env, - particlepos, - velocity, - acceleration, - (rand() % 100) / 100.0f, // expiration time - visual_size, - true, - false, - false, - false, + p, texture, texpos, texsize, - anim, - 0, color); addParticle(toadd); @@ -670,3 +580,20 @@ void ParticleManager::addParticle(Particle *toadd) MutexAutoLock lock(m_particle_list_lock); m_particles.push_back(toadd); } + + +void ParticleManager::addParticleSpawner(u64 id, ParticleSpawner *toadd) +{ + MutexAutoLock lock(m_spawner_list_lock); + m_particle_spawners[id] = toadd; +} + +void ParticleManager::deleteParticleSpawner(u64 id) +{ + MutexAutoLock lock(m_spawner_list_lock); + auto it = m_particle_spawners.find(id); + if (it != m_particle_spawners.end()) { + delete it->second; + m_particle_spawners.erase(it); + } +} diff --git a/src/client/particles.h b/src/client/particles.h index e7b8cbe24..7dda0e1b1 100644 --- a/src/client/particles.h +++ b/src/client/particles.h @@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_extrabloated.h" #include "client/tile.h" #include "localplayer.h" -#include "tileanimation.h" +#include "../particles.h" struct ClientEvent; class ParticleManager; @@ -38,20 +38,10 @@ class Particle : public scene::ISceneNode IGameDef* gamedef, LocalPlayer *player, ClientEnvironment *env, - v3f pos, - v3f velocity, - v3f acceleration, - float expirationtime, - float size, - bool collisiondetection, - bool collision_removal, - bool object_collision, - bool vertical, + const ParticleParameters &p, video::ITexture *texture, v2f texpos, v2f texsize, - const struct TileAnimationParams &anim, - u8 glow, video::SColor color = video::SColor(0xFFFFFFFF) ); ~Particle() = default; @@ -119,20 +109,9 @@ class ParticleSpawner public: ParticleSpawner(IGameDef* gamedef, LocalPlayer *player, - u16 amount, - float time, - v3f minp, v3f maxp, - v3f minvel, v3f maxvel, - v3f minacc, v3f maxacc, - float minexptime, float maxexptime, - float minsize, float maxsize, - bool collisiondetection, - bool collision_removal, - bool object_collision, + const ParticleSpawnerParameters &p, u16 attached_id, - bool vertical, video::ITexture *texture, - const struct TileAnimationParams &anim, u8 glow, ParticleManager* p_manager); ~ParticleSpawner() = default; @@ -140,7 +119,7 @@ public: void step(float dtime, ClientEnvironment *env); bool get_expired () - { return (m_amount <= 0) && m_spawntime != 0; } + { return p.amount <= 0 && p.time != 0; } private: void spawnParticle(ClientEnvironment *env, float radius, @@ -150,27 +129,10 @@ private: float m_time; IGameDef *m_gamedef; LocalPlayer *m_player; - u16 m_amount; - float m_spawntime; - v3f m_minpos; - v3f m_maxpos; - v3f m_minvel; - v3f m_maxvel; - v3f m_minacc; - v3f m_maxacc; - float m_minexptime; - float m_maxexptime; - float m_minsize; - float m_maxsize; + ParticleSpawnerParameters p; video::ITexture *m_texture; std::vector m_spawntimes; - bool m_collisiondetection; - bool m_collision_removal; - bool m_object_collision; - bool m_vertical; u16 m_attached_id; - struct TileAnimationParams m_animation; - u8 m_glow; }; /** @@ -197,8 +159,8 @@ public: /** * This function is only used by client particle spawners * - * We don't need to check the particle spawner list because client ID will n - * ever overlap (u64) + * We don't need to check the particle spawner list because client ID will + * never overlap (u64) * @return new id */ u64 generateSpawnerId() @@ -207,9 +169,15 @@ public: } protected: + static bool getNodeParticleParams(const MapNode &n, const ContentFeatures &f, + ParticleParameters &p, video::ITexture **texture, v2f &texpos, + v2f &texsize, video::SColor *color); + void addParticle(Particle* toadd); private: + void addParticleSpawner(u64 id, ParticleSpawner *toadd); + void deleteParticleSpawner(u64 id); void stepParticles(float dtime); void stepSpawners(float dtime); diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 7b1b1368c..054e60c3c 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -958,114 +958,56 @@ void Client::handleCommand_SpawnParticle(NetworkPacket* pkt) std::string datastring(pkt->getString(0), pkt->getSize()); std::istringstream is(datastring, std::ios_base::binary); - v3f pos = readV3F32(is); - v3f vel = readV3F32(is); - v3f acc = readV3F32(is); - float expirationtime = readF32(is); - float size = readF32(is); - bool collisiondetection = readU8(is); - std::string texture = deSerializeLongString(is); - - bool vertical = false; - bool collision_removal = false; - TileAnimationParams animation; - animation.type = TAT_NONE; - u8 glow = 0; - bool object_collision = false; - try { - vertical = readU8(is); - collision_removal = readU8(is); - animation.deSerialize(is, m_proto_ver); - glow = readU8(is); - object_collision = readU8(is); - } catch (...) {} + ParticleParameters p; + p.deSerialize(is, m_proto_ver); ClientEvent *event = new ClientEvent(); - event->type = CE_SPAWN_PARTICLE; - event->spawn_particle.pos = new v3f (pos); - event->spawn_particle.vel = new v3f (vel); - event->spawn_particle.acc = new v3f (acc); - event->spawn_particle.expirationtime = expirationtime; - event->spawn_particle.size = size; - event->spawn_particle.collisiondetection = collisiondetection; - event->spawn_particle.collision_removal = collision_removal; - event->spawn_particle.object_collision = object_collision; - event->spawn_particle.vertical = vertical; - event->spawn_particle.texture = new std::string(texture); - event->spawn_particle.animation = animation; - event->spawn_particle.glow = glow; + event->type = CE_SPAWN_PARTICLE; + event->spawn_particle = new ParticleParameters(p); m_client_event_queue.push(event); } void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt) { - u16 amount; - float spawntime; - v3f minpos; - v3f maxpos; - v3f minvel; - v3f maxvel; - v3f minacc; - v3f maxacc; - float minexptime; - float maxexptime; - float minsize; - float maxsize; - bool collisiondetection; - u32 server_id; - - *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel - >> minacc >> maxacc >> minexptime >> maxexptime >> minsize - >> maxsize >> collisiondetection; - - std::string texture = pkt->readLongString(); - - *pkt >> server_id; - - bool vertical = false; - bool collision_removal = false; - u16 attached_id = 0; - TileAnimationParams animation; - animation.type = TAT_NONE; - u8 glow = 0; - bool object_collision = false; - try { - *pkt >> vertical; - *pkt >> collision_removal; - *pkt >> attached_id; + std::string datastring(pkt->getString(0), pkt->getSize()); + std::istringstream is(datastring, std::ios_base::binary); - // This is horrible but required (why are there two ways to deserialize pkts?) - std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes()); - std::istringstream is(datastring, std::ios_base::binary); - animation.deSerialize(is, m_proto_ver); - glow = readU8(is); - object_collision = readU8(is); - } catch (...) {} + ParticleSpawnerParameters p; + u32 server_id; + u16 attached_id = 0; + + p.amount = readU16(is); + p.time = readF32(is); + p.minpos = readV3F32(is); + p.maxpos = readV3F32(is); + p.minvel = readV3F32(is); + p.maxvel = readV3F32(is); + p.minacc = readV3F32(is); + p.maxacc = readV3F32(is); + p.minexptime = readF32(is); + p.maxexptime = readF32(is); + p.minsize = readF32(is); + p.maxsize = readF32(is); + p.collisiondetection = readU8(is); + p.texture = deSerializeLongString(is); + + server_id = readU32(is); + + p.vertical = readU8(is); + p.collision_removal = readU8(is); + + attached_id = readU16(is); + + p.animation.deSerialize(is, m_proto_ver); + p.glow = readU8(is); + p.object_collision = readU8(is); auto event = new ClientEvent(); - event->type = CE_ADD_PARTICLESPAWNER; - event->add_particlespawner.amount = amount; - event->add_particlespawner.spawntime = spawntime; - event->add_particlespawner.minpos = new v3f (minpos); - event->add_particlespawner.maxpos = new v3f (maxpos); - event->add_particlespawner.minvel = new v3f (minvel); - event->add_particlespawner.maxvel = new v3f (maxvel); - event->add_particlespawner.minacc = new v3f (minacc); - event->add_particlespawner.maxacc = new v3f (maxacc); - event->add_particlespawner.minexptime = minexptime; - event->add_particlespawner.maxexptime = maxexptime; - event->add_particlespawner.minsize = minsize; - event->add_particlespawner.maxsize = maxsize; - event->add_particlespawner.collisiondetection = collisiondetection; - event->add_particlespawner.collision_removal = collision_removal; - event->add_particlespawner.object_collision = object_collision; - event->add_particlespawner.attached_id = attached_id; - event->add_particlespawner.vertical = vertical; - event->add_particlespawner.texture = new std::string(texture); - event->add_particlespawner.id = server_id; - event->add_particlespawner.animation = animation; - event->add_particlespawner.glow = glow; + event->type = CE_ADD_PARTICLESPAWNER; + event->add_particlespawner.p = new ParticleSpawnerParameters(p); + event->add_particlespawner.attached_id = attached_id; + event->add_particlespawner.id = server_id; m_client_event_queue.push(event); } diff --git a/src/particles.cpp b/src/particles.cpp new file mode 100644 index 000000000..711d189f6 --- /dev/null +++ b/src/particles.cpp @@ -0,0 +1,53 @@ +/* +Minetest +Copyright (C) 2020 sfan5 + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "particles.h" +#include "util/serialize.h" + +void ParticleParameters::serialize(std::ostream &os, u16 protocol_ver) const +{ + writeV3F32(os, pos); + writeV3F32(os, vel); + writeV3F32(os, acc); + writeF32(os, expirationtime); + writeF32(os, size); + writeU8(os, collisiondetection); + os << serializeLongString(texture); + writeU8(os, vertical); + writeU8(os, collision_removal); + animation.serialize(os, 6); /* NOT the protocol ver */ + writeU8(os, glow); + writeU8(os, object_collision); +} + +void ParticleParameters::deSerialize(std::istream &is, u16 protocol_ver) +{ + pos = readV3F32(is); + vel = readV3F32(is); + acc = readV3F32(is); + expirationtime = readF32(is); + size = readF32(is); + collisiondetection = readU8(is); + texture = deSerializeLongString(is); + vertical = readU8(is); + collision_removal = readU8(is); + animation.deSerialize(is, 6); /* NOT the protocol ver */ + glow = readU8(is); + object_collision = readU8(is); +} diff --git a/src/particles.h b/src/particles.h new file mode 100644 index 000000000..659c1249f --- /dev/null +++ b/src/particles.h @@ -0,0 +1,73 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include +#include "irrlichttypes_bloated.h" +#include "tileanimation.h" + +// This file defines the particle-related structures that both the server and +// client need. The ParticleManager and rendering is in client/particles.h + +struct CommonParticleParams { + bool collisiondetection = false; + bool collision_removal = false; + bool object_collision = false; + bool vertical = false; + std::string texture; + struct TileAnimationParams animation; + u8 glow = 0; + + CommonParticleParams() { + animation.type = TAT_NONE; + } + + /* This helper is useful for copying params from + * ParticleSpawnerParameters to ParticleParameters */ + inline void copyCommon(CommonParticleParams &to) const { + to.collisiondetection = collisiondetection; + to.collision_removal = collision_removal; + to.object_collision = object_collision; + to.vertical = vertical; + to.texture = texture; + to.animation = animation; + to.glow = glow; + } +}; + +struct ParticleParameters : CommonParticleParams { + v3f pos; + v3f vel; + v3f acc; + f32 expirationtime = 1; + f32 size = 1; + + void serialize(std::ostream &os, u16 protocol_ver) const; + void deSerialize(std::istream &is, u16 protocol_ver); +}; + +struct ParticleSpawnerParameters : CommonParticleParams { + u16 amount = 1; + v3f minpos, maxpos, minvel, maxvel, minacc, maxacc; + f32 time = 1; + f32 minexptime = 1, maxexptime = 1, minsize = 1, maxsize = 1; + + // For historical reasons no (de-)serialization methods here +}; diff --git a/src/script/lua_api/l_particles.cpp b/src/script/lua_api/l_particles.cpp index 340903ebf..7680aa17b 100644 --- a/src/script/lua_api/l_particles.cpp +++ b/src/script/lua_api/l_particles.cpp @@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_converter.h" #include "common/c_content.h" #include "server.h" -#include "client/particles.h" +#include "particles.h" // add_particle({pos=, velocity=, acceleration=, expirationtime=, // size=, collisiondetection=, collision_removal=, object_collision=, @@ -40,85 +40,81 @@ with this program; if not, write to the Free Software Foundation, Inc., // glow = num int ModApiParticles::l_add_particle(lua_State *L) { - MAP_LOCK_REQUIRED; + NO_MAP_LOCK_REQUIRED; // Get parameters - v3f pos, vel, acc; - float expirationtime, size; - expirationtime = size = 1; - bool collisiondetection, vertical, collision_removal, object_collision; - collisiondetection = vertical = collision_removal = object_collision = false; - struct TileAnimationParams animation; - animation.type = TAT_NONE; - std::string texture; + struct ParticleParameters p; std::string playername; - u8 glow = 0; if (lua_gettop(L) > 1) // deprecated { - log_deprecated(L, "Deprecated add_particle call with individual parameters instead of definition"); - pos = check_v3f(L, 1); - vel = check_v3f(L, 2); - acc = check_v3f(L, 3); - expirationtime = luaL_checknumber(L, 4); - size = luaL_checknumber(L, 5); - collisiondetection = readParam(L, 6); - texture = luaL_checkstring(L, 7); + log_deprecated(L, "Deprecated add_particle call with " + "individual parameters instead of definition"); + p.pos = check_v3f(L, 1); + p.vel = check_v3f(L, 2); + p.acc = check_v3f(L, 3); + p.expirationtime = luaL_checknumber(L, 4); + p.size = luaL_checknumber(L, 5); + p.collisiondetection = readParam(L, 6); + p.texture = luaL_checkstring(L, 7); if (lua_gettop(L) == 8) // only spawn for a single player playername = luaL_checkstring(L, 8); } else if (lua_istable(L, 1)) { lua_getfield(L, 1, "pos"); - pos = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(); + if (lua_istable(L, -1)) + p.pos = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "vel"); if (lua_istable(L, -1)) { - vel = check_v3f(L, -1); + p.vel = check_v3f(L, -1); log_deprecated(L, "The use of vel is deprecated. " "Use velocity instead"); } lua_pop(L, 1); lua_getfield(L, 1, "velocity"); - vel = lua_istable(L, -1) ? check_v3f(L, -1) : vel; + if (lua_istable(L, -1)) + p.vel = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "acc"); if (lua_istable(L, -1)) { - acc = check_v3f(L, -1); + p.acc = check_v3f(L, -1); log_deprecated(L, "The use of acc is deprecated. " "Use acceleration instead"); } lua_pop(L, 1); lua_getfield(L, 1, "acceleration"); - acc = lua_istable(L, -1) ? check_v3f(L, -1) : acc; + if (lua_istable(L, -1)) + p.acc = check_v3f(L, -1); lua_pop(L, 1); - expirationtime = getfloatfield_default(L, 1, "expirationtime", 1); - size = getfloatfield_default(L, 1, "size", 1); - collisiondetection = getboolfield_default(L, 1, - "collisiondetection", collisiondetection); - collision_removal = getboolfield_default(L, 1, - "collision_removal", collision_removal); - object_collision = getboolfield_default(L, 1, - "object_collision", object_collision); - vertical = getboolfield_default(L, 1, "vertical", vertical); + p.expirationtime = getfloatfield_default(L, 1, "expirationtime", + p.expirationtime); + p.size = getfloatfield_default(L, 1, "size", p.size); + p.collisiondetection = getboolfield_default(L, 1, + "collisiondetection", p.collisiondetection); + p.collision_removal = getboolfield_default(L, 1, + "collision_removal", p.collision_removal); + p.object_collision = getboolfield_default(L, 1, + "object_collision", p.object_collision); + p.vertical = getboolfield_default(L, 1, "vertical", p.vertical); lua_getfield(L, 1, "animation"); - animation = read_animation_definition(L, -1); + p.animation = read_animation_definition(L, -1); lua_pop(L, 1); - texture = getstringfield_default(L, 1, "texture", ""); - playername = getstringfield_default(L, 1, "playername", ""); + p.texture = getstringfield_default(L, 1, "texture", p.texture); + p.glow = getintfield_default(L, 1, "glow", p.glow); - glow = getintfield_default(L, 1, "glow", 0); + playername = getstringfield_default(L, 1, "playername", ""); } - getServer(L)->spawnParticle(playername, pos, vel, acc, expirationtime, size, - collisiondetection, collision_removal, object_collision, vertical, - texture, animation, glow); + + getServer(L)->spawnParticle(playername, p); return 1; } @@ -146,84 +142,82 @@ int ModApiParticles::l_add_particle(lua_State *L) // glow = num int ModApiParticles::l_add_particlespawner(lua_State *L) { - MAP_LOCK_REQUIRED; + NO_MAP_LOCK_REQUIRED; // Get parameters - u16 amount = 1; - v3f minpos, maxpos, minvel, maxvel, minacc, maxacc; - float time, minexptime, maxexptime, minsize, maxsize; - time = minexptime = maxexptime = minsize = maxsize = 1; - bool collisiondetection, vertical, collision_removal, object_collision; - collisiondetection = vertical = collision_removal = object_collision = false; - struct TileAnimationParams animation; - animation.type = TAT_NONE; + ParticleSpawnerParameters p; ServerActiveObject *attached = NULL; - std::string texture; std::string playername; - u8 glow = 0; if (lua_gettop(L) > 1) //deprecated { - log_deprecated(L,"Deprecated add_particlespawner call with individual parameters instead of definition"); - amount = luaL_checknumber(L, 1); - time = luaL_checknumber(L, 2); - minpos = check_v3f(L, 3); - maxpos = check_v3f(L, 4); - minvel = check_v3f(L, 5); - maxvel = check_v3f(L, 6); - minacc = check_v3f(L, 7); - maxacc = check_v3f(L, 8); - minexptime = luaL_checknumber(L, 9); - maxexptime = luaL_checknumber(L, 10); - minsize = luaL_checknumber(L, 11); - maxsize = luaL_checknumber(L, 12); - collisiondetection = readParam(L, 13); - texture = luaL_checkstring(L, 14); + log_deprecated(L, "Deprecated add_particlespawner call with " + "individual parameters instead of definition"); + p.amount = luaL_checknumber(L, 1); + p.time = luaL_checknumber(L, 2); + p.minpos = check_v3f(L, 3); + p.maxpos = check_v3f(L, 4); + p.minvel = check_v3f(L, 5); + p.maxvel = check_v3f(L, 6); + p.minacc = check_v3f(L, 7); + p.maxacc = check_v3f(L, 8); + p.minexptime = luaL_checknumber(L, 9); + p.maxexptime = luaL_checknumber(L, 10); + p.minsize = luaL_checknumber(L, 11); + p.maxsize = luaL_checknumber(L, 12); + p.collisiondetection = readParam(L, 13); + p.texture = luaL_checkstring(L, 14); if (lua_gettop(L) == 15) // only spawn for a single player playername = luaL_checkstring(L, 15); } else if (lua_istable(L, 1)) { - amount = getintfield_default(L, 1, "amount", amount); - time = getfloatfield_default(L, 1, "time", time); + p.amount = getintfield_default(L, 1, "amount", p.amount); + p.time = getfloatfield_default(L, 1, "time", p.time); lua_getfield(L, 1, "minpos"); - minpos = lua_istable(L, -1) ? check_v3f(L, -1) : minpos; + if (lua_istable(L, -1)) + p.minpos = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "maxpos"); - maxpos = lua_istable(L, -1) ? check_v3f(L, -1) : maxpos; + if (lua_istable(L, -1)) + p.maxpos = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "minvel"); - minvel = lua_istable(L, -1) ? check_v3f(L, -1) : minvel; + if (lua_istable(L, -1)) + p.minvel = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "maxvel"); - maxvel = lua_istable(L, -1) ? check_v3f(L, -1) : maxvel; + if (lua_istable(L, -1)) + p.maxvel = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "minacc"); - minacc = lua_istable(L, -1) ? check_v3f(L, -1) : minacc; + if (lua_istable(L, -1)) + p.minacc = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "maxacc"); - maxacc = lua_istable(L, -1) ? check_v3f(L, -1) : maxacc; + if (lua_istable(L, -1)) + p.maxacc = check_v3f(L, -1); lua_pop(L, 1); - minexptime = getfloatfield_default(L, 1, "minexptime", minexptime); - maxexptime = getfloatfield_default(L, 1, "maxexptime", maxexptime); - minsize = getfloatfield_default(L, 1, "minsize", minsize); - maxsize = getfloatfield_default(L, 1, "maxsize", maxsize); - collisiondetection = getboolfield_default(L, 1, - "collisiondetection", collisiondetection); - collision_removal = getboolfield_default(L, 1, - "collision_removal", collision_removal); - object_collision = getboolfield_default(L, 1, - "object_collision", object_collision); + p.minexptime = getfloatfield_default(L, 1, "minexptime", p.minexptime); + p.maxexptime = getfloatfield_default(L, 1, "maxexptime", p.maxexptime); + p.minsize = getfloatfield_default(L, 1, "minsize", p.minsize); + p.maxsize = getfloatfield_default(L, 1, "maxsize", p.maxsize); + p.collisiondetection = getboolfield_default(L, 1, + "collisiondetection", p.collisiondetection); + p.collision_removal = getboolfield_default(L, 1, + "collision_removal", p.collision_removal); + p.object_collision = getboolfield_default(L, 1, + "object_collision", p.object_collision); lua_getfield(L, 1, "animation"); - animation = read_animation_definition(L, -1); + p.animation = read_animation_definition(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "attached"); @@ -233,25 +227,13 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) attached = ObjectRef::getobject(ref); } - vertical = getboolfield_default(L, 1, "vertical", vertical); - texture = getstringfield_default(L, 1, "texture", ""); + p.vertical = getboolfield_default(L, 1, "vertical", p.vertical); + p.texture = getstringfield_default(L, 1, "texture", p.texture); playername = getstringfield_default(L, 1, "playername", ""); - glow = getintfield_default(L, 1, "glow", 0); + p.glow = getintfield_default(L, 1, "glow", p.glow); } - u32 id = getServer(L)->addParticleSpawner(amount, time, - minpos, maxpos, - minvel, maxvel, - minacc, maxacc, - minexptime, maxexptime, - minsize, maxsize, - collisiondetection, - collision_removal, - object_collision, - attached, - vertical, - texture, playername, - animation, glow); + u32 id = getServer(L)->addParticleSpawner(p, attached, playername); lua_pushnumber(L, id); return 1; @@ -261,7 +243,7 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) // player (string) is optional int ModApiParticles::l_delete_particlespawner(lua_State *L) { - MAP_LOCK_REQUIRED; + NO_MAP_LOCK_REQUIRED; // Get parameters u32 id = luaL_checknumber(L, 1); diff --git a/src/script/lua_api/l_particles_local.cpp b/src/script/lua_api/l_particles_local.cpp index a9bf55665..9595b2fab 100644 --- a/src/script/lua_api/l_particles_local.cpp +++ b/src/script/lua_api/l_particles_local.cpp @@ -32,56 +32,44 @@ int ModApiParticlesLocal::l_add_particle(lua_State *L) luaL_checktype(L, 1, LUA_TTABLE); // Get parameters - v3f pos, vel, acc; - float expirationtime, size; - bool collisiondetection, vertical, collision_removal; - - struct TileAnimationParams animation; - animation.type = TAT_NONE; - - std::string texture; - - u8 glow; + ParticleParameters p; lua_getfield(L, 1, "pos"); - pos = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.pos = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "velocity"); - vel = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.vel = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "acceleration"); - acc = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.acc = check_v3f(L, -1); lua_pop(L, 1); - expirationtime = getfloatfield_default(L, 1, "expirationtime", 1); - size = getfloatfield_default(L, 1, "size", 1); - collisiondetection = getboolfield_default(L, 1, "collisiondetection", false); - collision_removal = getboolfield_default(L, 1, "collision_removal", false); - vertical = getboolfield_default(L, 1, "vertical", false); + p.expirationtime = getfloatfield_default(L, 1, "expirationtime", + p.expirationtime); + p.size = getfloatfield_default(L, 1, "size", p.size); + p.collisiondetection = getboolfield_default(L, 1, + "collisiondetection", p.collisiondetection); + p.collision_removal = getboolfield_default(L, 1, + "collision_removal", p.collision_removal); + p.object_collision = getboolfield_default(L, 1, + "object_collision", p.object_collision); + p.vertical = getboolfield_default(L, 1, "vertical", p.vertical); lua_getfield(L, 1, "animation"); - animation = read_animation_definition(L, -1); + p.animation = read_animation_definition(L, -1); lua_pop(L, 1); - texture = getstringfield_default(L, 1, "texture", ""); - - glow = getintfield_default(L, 1, "glow", 0); + p.texture = getstringfield_default(L, 1, "texture", p.texture); + p.glow = getintfield_default(L, 1, "glow", p.glow); ClientEvent *event = new ClientEvent(); - event->type = CE_SPAWN_PARTICLE; - event->spawn_particle.pos = new v3f (pos); - event->spawn_particle.vel = new v3f (vel); - event->spawn_particle.acc = new v3f (acc); - event->spawn_particle.expirationtime = expirationtime; - event->spawn_particle.size = size; - event->spawn_particle.collisiondetection = collisiondetection; - event->spawn_particle.collision_removal = collision_removal; - event->spawn_particle.vertical = vertical; - event->spawn_particle.texture = new std::string(texture); - event->spawn_particle.animation = animation; - event->spawn_particle.glow = glow; + event->type = CE_SPAWN_PARTICLE; + event->spawn_particle = new ParticleParameters(p); getClient(L)->pushToEventQueue(event); return 0; @@ -90,94 +78,69 @@ int ModApiParticlesLocal::l_add_particle(lua_State *L) int ModApiParticlesLocal::l_add_particlespawner(lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); - // Get parameters - u16 amount; - v3f minpos, maxpos, minvel, maxvel, minacc, maxacc; - float time, minexptime, maxexptime, minsize, maxsize; - bool collisiondetection, vertical, collision_removal; - struct TileAnimationParams animation; - animation.type = TAT_NONE; - // TODO: Implement this when there is a way to get an objectref. - // ServerActiveObject *attached = NULL; - std::string texture; - u8 glow; + // Get parameters + ParticleSpawnerParameters p; - amount = getintfield_default(L, 1, "amount", 1); - time = getfloatfield_default(L, 1, "time", 1); + p.amount = getintfield_default(L, 1, "amount", p.amount); + p.time = getfloatfield_default(L, 1, "time", p.time); lua_getfield(L, 1, "minpos"); - minpos = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.minpos = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "maxpos"); - maxpos = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.maxpos = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "minvel"); - minvel = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.minvel = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "maxvel"); - maxvel = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.maxvel = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "minacc"); - minacc = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.minacc = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "maxacc"); - maxacc = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.maxacc = check_v3f(L, -1); lua_pop(L, 1); - minexptime = getfloatfield_default(L, 1, "minexptime", 1); - maxexptime = getfloatfield_default(L, 1, "maxexptime", 1); - minsize = getfloatfield_default(L, 1, "minsize", 1); - maxsize = getfloatfield_default(L, 1, "maxsize", 1); - - collisiondetection = getboolfield_default(L, 1, "collisiondetection", false); - collision_removal = getboolfield_default(L, 1, "collision_removal", false); - vertical = getboolfield_default(L, 1, "vertical", false); + p.minexptime = getfloatfield_default(L, 1, "minexptime", p.minexptime); + p.maxexptime = getfloatfield_default(L, 1, "maxexptime", p.maxexptime); + p.minsize = getfloatfield_default(L, 1, "minsize", p.minsize); + p.maxsize = getfloatfield_default(L, 1, "maxsize", p.maxsize); + p.collisiondetection = getboolfield_default(L, 1, + "collisiondetection", p.collisiondetection); + p.collision_removal = getboolfield_default(L, 1, + "collision_removal", p.collision_removal); + p.object_collision = getboolfield_default(L, 1, + "object_collision", p.object_collision); lua_getfield(L, 1, "animation"); - animation = read_animation_definition(L, -1); + p.animation = read_animation_definition(L, -1); lua_pop(L, 1); - // TODO: Implement this when a way to get an objectref on the client is added -// lua_getfield(L, 1, "attached"); -// if (!lua_isnil(L, -1)) { -// ObjectRef *ref = ObjectRef::checkobject(L, -1); -// lua_pop(L, 1); -// attached = ObjectRef::getobject(ref); -// } - - texture = getstringfield_default(L, 1, "texture", ""); - glow = getintfield_default(L, 1, "glow", 0); + p.vertical = getboolfield_default(L, 1, "vertical", p.vertical); + p.texture = getstringfield_default(L, 1, "texture", p.texture); + p.glow = getintfield_default(L, 1, "glow", p.glow); u64 id = getClient(L)->getParticleManager()->generateSpawnerId(); auto event = new ClientEvent(); - event->type = CE_ADD_PARTICLESPAWNER; - event->add_particlespawner.amount = amount; - event->add_particlespawner.spawntime = time; - event->add_particlespawner.minpos = new v3f (minpos); - event->add_particlespawner.maxpos = new v3f (maxpos); - event->add_particlespawner.minvel = new v3f (minvel); - event->add_particlespawner.maxvel = new v3f (maxvel); - event->add_particlespawner.minacc = new v3f (minacc); - event->add_particlespawner.maxacc = new v3f (maxacc); - event->add_particlespawner.minexptime = minexptime; - event->add_particlespawner.maxexptime = maxexptime; - event->add_particlespawner.minsize = minsize; - event->add_particlespawner.maxsize = maxsize; - event->add_particlespawner.collisiondetection = collisiondetection; - event->add_particlespawner.collision_removal = collision_removal; - event->add_particlespawner.attached_id = 0; - event->add_particlespawner.vertical = vertical; - event->add_particlespawner.texture = new std::string(texture); - event->add_particlespawner.id = id; - event->add_particlespawner.animation = animation; - event->add_particlespawner.glow = glow; + event->type = CE_ADD_PARTICLESPAWNER; + event->add_particlespawner.p = new ParticleSpawnerParameters(p); + event->add_particlespawner.attached_id = 0; + event->add_particlespawner.id = id; getClient(L)->pushToEventQueue(event); lua_pushnumber(L, id); diff --git a/src/server.cpp b/src/server.cpp index 92870f972..68b0131d4 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1504,17 +1504,15 @@ void Server::SendShowFormspecMessage(session_t peer_id, const std::string &forms // Spawns a particle on peer with peer_id void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version, - v3f pos, v3f velocity, v3f acceleration, - float expirationtime, float size, bool collisiondetection, - bool collision_removal, bool object_collision, - bool vertical, const std::string &texture, - const struct TileAnimationParams &animation, u8 glow) + const ParticleParameters &p) { static thread_local const float radius = g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS; if (peer_id == PEER_ID_INEXISTENT) { std::vector clients = m_clients.getClientIDs(); + const v3f pos = p.pos * BS; + const float radius_sq = radius * radius; for (const session_t client_id : clients) { RemotePlayer *player = m_env->getPlayer(client_id); @@ -1526,76 +1524,59 @@ void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version, continue; // Do not send to distant clients - if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius) + if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq) continue; - SendSpawnParticle(client_id, player->protocol_version, - pos, velocity, acceleration, - expirationtime, size, collisiondetection, collision_removal, - object_collision, vertical, texture, animation, glow); + SendSpawnParticle(client_id, player->protocol_version, p); } return; } + assert(protocol_version != 0); NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id); - pkt << pos << velocity << acceleration << expirationtime - << size << collisiondetection; - pkt.putLongString(texture); - pkt << vertical; - pkt << collision_removal; - // This is horrible but required (why are there two ways to serialize pkts?) - std::ostringstream os(std::ios_base::binary); - animation.serialize(os, protocol_version); - pkt.putRawString(os.str()); - pkt << glow; - pkt << object_collision; + { + // NetworkPacket and iostreams are incompatible... + std::ostringstream oss(std::ios_base::binary); + p.serialize(oss, protocol_version); + pkt.putRawString(oss.str()); + } Send(&pkt); } // Adds a ParticleSpawner on peer with peer_id void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version, - u16 amount, float spawntime, v3f minpos, v3f maxpos, - v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, - float minsize, float maxsize, bool collisiondetection, bool collision_removal, - bool object_collision, u16 attached_id, bool vertical, const std::string &texture, u32 id, - const struct TileAnimationParams &animation, u8 glow) + const ParticleSpawnerParameters &p, u16 attached_id, u32 id) { if (peer_id == PEER_ID_INEXISTENT) { - // This sucks and should be replaced: std::vector clients = m_clients.getClientIDs(); for (const session_t client_id : clients) { RemotePlayer *player = m_env->getPlayer(client_id); if (!player) continue; SendAddParticleSpawner(client_id, player->protocol_version, - amount, spawntime, minpos, maxpos, - minvel, maxvel, minacc, maxacc, minexptime, maxexptime, - minsize, maxsize, collisiondetection, collision_removal, - object_collision, attached_id, vertical, texture, id, - animation, glow); + p, attached_id, id); } return; } + assert(protocol_version != 0); - NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id); + NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id); - pkt << amount << spawntime << minpos << maxpos << minvel << maxvel - << minacc << maxacc << minexptime << maxexptime << minsize - << maxsize << collisiondetection; + pkt << p.amount << p.time << p.minpos << p.maxpos << p.minvel + << p.maxvel << p.minacc << p.maxacc << p.minexptime << p.maxexptime + << p.minsize << p.maxsize << p.collisiondetection; - pkt.putLongString(texture); + pkt.putLongString(p.texture); - pkt << id << vertical; - pkt << collision_removal; - pkt << attached_id; - // This is horrible but required - std::ostringstream os(std::ios_base::binary); - animation.serialize(os, protocol_version); - pkt.putRawString(os.str()); - pkt << glow; - pkt << object_collision; + pkt << id << p.vertical << p.collision_removal << attached_id; + { + std::ostringstream os(std::ios_base::binary); + p.animation.serialize(os, protocol_version); + pkt.putRawString(os.str()); + } + pkt << p.glow << p.object_collision; Send(&pkt); } @@ -1604,7 +1585,6 @@ void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id) { NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id); - // Ugly error in this packet pkt << id; if (peer_id != PEER_ID_INEXISTENT) @@ -3365,12 +3345,8 @@ void Server::notifyPlayers(const std::wstring &msg) SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg)); } -void Server::spawnParticle(const std::string &playername, v3f pos, - v3f velocity, v3f acceleration, - float expirationtime, float size, bool - collisiondetection, bool collision_removal, bool object_collision, - bool vertical, const std::string &texture, - const struct TileAnimationParams &animation, u8 glow) +void Server::spawnParticle(const std::string &playername, + const ParticleParameters &p) { // m_env will be NULL if the server is initializing if (!m_env) @@ -3386,18 +3362,11 @@ void Server::spawnParticle(const std::string &playername, v3f pos, proto_ver = player->protocol_version; } - SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration, - expirationtime, size, collisiondetection, collision_removal, - object_collision, vertical, texture, animation, glow); + SendSpawnParticle(peer_id, proto_ver, p); } -u32 Server::addParticleSpawner(u16 amount, float spawntime, - v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, - float minexptime, float maxexptime, float minsize, float maxsize, - bool collisiondetection, bool collision_removal, bool object_collision, - ServerActiveObject *attached, bool vertical, const std::string &texture, - const std::string &playername, const struct TileAnimationParams &animation, - u8 glow) +u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p, + ServerActiveObject *attached, const std::string &playername) { // m_env will be NULL if the server is initializing if (!m_env) @@ -3417,16 +3386,11 @@ u32 Server::addParticleSpawner(u16 amount, float spawntime, u32 id; if (attached_id == 0) - id = m_env->addParticleSpawner(spawntime); + id = m_env->addParticleSpawner(p.time); else - id = m_env->addParticleSpawner(spawntime, attached_id); - - SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime, - minpos, maxpos, minvel, maxvel, minacc, maxacc, - minexptime, maxexptime, minsize, maxsize, collisiondetection, - collision_removal, object_collision, attached_id, vertical, - texture, id, animation, glow); + id = m_env->addParticleSpawner(p.time, attached_id); + SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id); return id; } diff --git a/src/server.h b/src/server.h index 7a1de9370..27943cc29 100644 --- a/src/server.h +++ b/src/server.h @@ -27,7 +27,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content/mods.h" #include "inventorymanager.h" #include "content/subgames.h" -#include "tileanimation.h" // struct TileAnimationParams +#include "tileanimation.h" // TileAnimationParams +#include "particles.h" // ParticleParams #include "network/peerhandler.h" #include "network/address.h" #include "util/numeric.h" @@ -226,24 +227,12 @@ public: void notifyPlayer(const char *name, const std::wstring &msg); void notifyPlayers(const std::wstring &msg); + void spawnParticle(const std::string &playername, - v3f pos, v3f velocity, v3f acceleration, - float expirationtime, float size, - bool collisiondetection, bool collision_removal, bool object_collision, - bool vertical, const std::string &texture, - const struct TileAnimationParams &animation, u8 glow); - - u32 addParticleSpawner(u16 amount, float spawntime, - v3f minpos, v3f maxpos, - v3f minvel, v3f maxvel, - v3f minacc, v3f maxacc, - float minexptime, float maxexptime, - float minsize, float maxsize, - bool collisiondetection, bool collision_removal, bool object_collision, - ServerActiveObject *attached, - bool vertical, const std::string &texture, - const std::string &playername, const struct TileAnimationParams &animation, - u8 glow); + const ParticleParameters &p); + + u32 addParticleSpawner(const ParticleSpawnerParameters &p, + ServerActiveObject *attached, const std::string &playername); void deleteParticleSpawner(const std::string &playername, u32 id); @@ -453,26 +442,13 @@ private: // Adds a ParticleSpawner on peer with peer_id (PEER_ID_INEXISTENT == all) void SendAddParticleSpawner(session_t peer_id, u16 protocol_version, - u16 amount, float spawntime, - v3f minpos, v3f maxpos, - v3f minvel, v3f maxvel, - v3f minacc, v3f maxacc, - float minexptime, float maxexptime, - float minsize, float maxsize, - bool collisiondetection, bool collision_removal, bool object_collision, - u16 attached_id, - bool vertical, const std::string &texture, u32 id, - const struct TileAnimationParams &animation, u8 glow); + const ParticleSpawnerParameters &p, u16 attached_id, u32 id); void SendDeleteParticleSpawner(session_t peer_id, u32 id); // Spawns particle on peer with peer_id (PEER_ID_INEXISTENT == all) void SendSpawnParticle(session_t peer_id, u16 protocol_version, - v3f pos, v3f velocity, v3f acceleration, - float expirationtime, float size, - bool collisiondetection, bool collision_removal, bool object_collision, - bool vertical, const std::string &texture, - const struct TileAnimationParams &animation, u8 glow); + const ParticleParameters &p); void SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao); void SendActiveObjectMessages(session_t peer_id, const std::string &datas, -- cgit v1.2.3 From 15ba75e4cf1d1b8ceaa9d8ce33dcfdd7dbe80741 Mon Sep 17 00:00:00 2001 From: sorcerykid Date: Sat, 23 May 2020 06:24:06 -0500 Subject: Add on_authplayer callback and 'last_login' to on_joinplayer (#9574) Replace on_auth_fail callback with more versatile on_authplayer Better clarify account login process in Lua API documentation Change initial timestamp for newly registered accounts to -1 --- builtin/game/auth.lua | 3 +-- builtin/game/chat.lua | 2 +- builtin/game/deprecated.lua | 16 ++++++++++++++ builtin/game/register.lua | 2 +- doc/lua_api.txt | 20 ++++++++++------- src/network/serverpackethandler.cpp | 44 +++++++++++++++++++------------------ src/script/cpp_api/s_player.cpp | 19 +++++++++++----- src/script/cpp_api/s_player.h | 4 ++-- src/script/cpp_api/s_server.cpp | 14 ++++++++---- src/script/cpp_api/s_server.h | 3 ++- 10 files changed, 81 insertions(+), 46 deletions(-) (limited to 'src/network') diff --git a/builtin/game/auth.lua b/builtin/game/auth.lua index 7aedfc82e..fc061666c 100644 --- a/builtin/game/auth.lua +++ b/builtin/game/auth.lua @@ -41,7 +41,6 @@ core.builtin_auth_handler = { return { password = auth_entry.password, privileges = privileges, - -- Is set to nil if unknown last_login = auth_entry.last_login, } end, @@ -53,7 +52,7 @@ core.builtin_auth_handler = { name = name, password = password, privileges = core.string_to_privs(core.settings:get("default_privs")), - last_login = os.time(), + last_login = -1, -- Defer login time calculation until record_login (called by on_joinplayer) }) end, delete_auth = function(name) diff --git a/builtin/game/chat.lua b/builtin/game/chat.lua index b9f84e522..aae811794 100644 --- a/builtin/game/chat.lua +++ b/builtin/game/chat.lua @@ -1068,7 +1068,7 @@ core.register_chatcommand("last-login", { param = name end local pauth = core.get_auth_handler().get_auth(param) - if pauth and pauth.last_login then + if pauth and pauth.last_login and pauth.last_login ~= -1 then -- Time in UTC, ISO 8601 format return true, "Last login time was " .. os.date("!%Y-%m-%dT%H:%M:%SZ", pauth.last_login) diff --git a/builtin/game/deprecated.lua b/builtin/game/deprecated.lua index 73e105eb8..20f0482eb 100644 --- a/builtin/game/deprecated.lua +++ b/builtin/game/deprecated.lua @@ -70,3 +70,19 @@ core.setting_get = setting_proxy("get") core.setting_setbool = setting_proxy("set_bool") core.setting_getbool = setting_proxy("get_bool") core.setting_save = setting_proxy("write") + +-- +-- core.register_on_auth_fail +-- + +function core.register_on_auth_fail(func) + core.log("deprecated", "core.register_on_auth_fail " .. + "is obsolete and should be replaced by " .. + "core.register_on_authplayer instead.") + + core.register_on_authplayer(function (player_name, ip, is_success) + if not is_success then + func(player_name, ip) + end + end) +end diff --git a/builtin/game/register.lua b/builtin/game/register.lua index eb6c2897c..1034d4f2b 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -607,9 +607,9 @@ core.registered_on_item_eats, core.register_on_item_eat = make_registration() core.registered_on_punchplayers, core.register_on_punchplayer = make_registration() core.registered_on_priv_grant, core.register_on_priv_grant = make_registration() core.registered_on_priv_revoke, core.register_on_priv_revoke = make_registration() +core.registered_on_authplayers, core.register_on_authplayer = make_registration() core.registered_can_bypass_userlimit, core.register_can_bypass_userlimit = make_registration() core.registered_on_modchannel_message, core.register_on_modchannel_message = make_registration() -core.registered_on_auth_fail, core.register_on_auth_fail = make_registration() core.registered_on_player_inventory_actions, core.register_on_player_inventory_action = make_registration() core.registered_allow_player_inventory_actions, core.register_allow_player_inventory_action = make_registration() diff --git a/doc/lua_api.txt b/doc/lua_api.txt index bd0cb8acb..26061eccb 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -4374,7 +4374,7 @@ Call these functions only at load time! * Called after generating a piece of world. Modifying nodes inside the area is a bit faster than usually. * `minetest.register_on_newplayer(function(ObjectRef))` - * Called after a new player has been created + * Called when a new player enters the world for the first time * `minetest.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage))` * Called when a player is punched * Note: This callback is invoked even if the punched player is dead. @@ -4415,19 +4415,23 @@ Call these functions only at load time! * Called _before_ repositioning of player occurs * return true in func to disable regular player placement * `minetest.register_on_prejoinplayer(function(name, ip))` - * Called before a player joins the game - * If it returns a string, the player is disconnected with that string as + * Called when a client connects to the server, prior to authentication + * If it returns a string, the client is disconnected with that string as reason. -* `minetest.register_on_joinplayer(function(ObjectRef))` +* `minetest.register_on_joinplayer(function(ObjectRef, last_login))` * Called when a player joins the game + * `last_login`: The timestamp of the previous login, or nil if player is new * `minetest.register_on_leaveplayer(function(ObjectRef, timed_out))` * Called when a player leaves the game * `timed_out`: True for timeout, false for other reasons. +* `minetest.register_on_authplayer(function(name, ip, is_success))` + * Called when a client attempts to log into an account. + * `name`: The name of the account being authenticated. + * `ip`: The IP address of the client + * `is_success`: Whether the client was successfully authenticated + * For newly registered accounts, `is_success` will always be true * `minetest.register_on_auth_fail(function(name, ip))` - * Called when a client attempts to log into an account but supplies the - wrong password. - * `ip`: The IP address of the client. - * `name`: The account the client attempted to log into. + * Deprecated: use `minetest.register_on_authplayer(name, ip, is_success)` instead. * `minetest.register_on_cheat(function(ObjectRef, cheat))` * Called when a player cheats * `cheat`: `{type=}`, where `` is one of: diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 2fa9d4196..fed3b6f85 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -409,9 +409,12 @@ void Server::handleCommand_ClientReady(NetworkPacket* pkt) // (u16) 1 + std::string represents a pseudo vector serialization representation notice_pkt << (u8) PLAYER_LIST_ADD << (u16) 1 << std::string(playersao->getPlayer()->getName()); m_clients.sendToAll(¬ice_pkt); - m_clients.event(peer_id, CSE_SetClientReady); - m_script->on_joinplayer(playersao); + + s64 last_login; + m_script->getAuth(playersao->getPlayer()->getName(), nullptr, nullptr, &last_login); + m_script->on_joinplayer(playersao, last_login); + // Send shutdown timer if shutdown has been scheduled if (m_shutdown_state.isTimerRunning()) { SendChatMessage(peer_id, m_shutdown_state.getShutdownTimerMessage()); @@ -1512,6 +1515,7 @@ void Server::handleCommand_FirstSrp(NetworkPacket* pkt) initial_ver_key = encode_srp_verifier(verification_key, salt); m_script->createAuth(playername, initial_ver_key); + m_script->on_authplayer(playername, addr_s, true); acceptAuth(peer_id, false); } else { @@ -1648,24 +1652,25 @@ void Server::handleCommand_SrpBytesM(NetworkPacket* pkt) session_t peer_id = pkt->getPeerId(); RemoteClient *client = getClient(peer_id, CS_Invalid); ClientState cstate = client->getState(); + std::string addr_s = getPeerAddress(pkt->getPeerId()).serializeString(); + std::string playername = client->getName(); bool wantSudo = (cstate == CS_Active); verbosestream << "Server: Received TOCLIENT_SRP_BYTES_M." << std::endl; if (!((cstate == CS_HelloSent) || (cstate == CS_Active))) { - actionstream << "Server: got SRP _M packet in wrong state " << cstate << - " from " << getPeerAddress(peer_id).serializeString() << - ". Ignoring." << std::endl; + actionstream << "Server: got SRP _M packet in wrong state " + << cstate << " from " << addr_s + << ". Ignoring." << std::endl; return; } if (client->chosen_mech != AUTH_MECHANISM_SRP && client->chosen_mech != AUTH_MECHANISM_LEGACY_PASSWORD) { - actionstream << "Server: got SRP _M packet, while auth is going on " - "with mech " << client->chosen_mech << " from " << - getPeerAddress(peer_id).serializeString() << - " (wantSudo=" << wantSudo << "). Denying." << std::endl; + actionstream << "Server: got SRP _M packet, while auth" + << "is going on with mech " << client->chosen_mech << " from " + << addr_s << " (wantSudo=" << wantSudo << "). Denying." << std::endl; if (wantSudo) { DenySudoAccess(peer_id); return; @@ -1680,9 +1685,8 @@ void Server::handleCommand_SrpBytesM(NetworkPacket* pkt) if (srp_verifier_get_session_key_length((SRPVerifier *) client->auth_data) != bytes_M.size()) { - actionstream << "Server: User " << client->getName() << " at " << - getPeerAddress(peer_id).serializeString() << - " sent bytes_M with invalid length " << bytes_M.size() << std::endl; + actionstream << "Server: User " << playername << " at " << addr_s + << " sent bytes_M with invalid length " << bytes_M.size() << std::endl; DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA); return; } @@ -1694,24 +1698,21 @@ void Server::handleCommand_SrpBytesM(NetworkPacket* pkt) if (!bytes_HAMK) { if (wantSudo) { - actionstream << "Server: User " << client->getName() << " at " << - getPeerAddress(peer_id).serializeString() << - " tried to change their password, but supplied wrong (SRP) " - "password for authentication." << std::endl; + actionstream << "Server: User " << playername << " at " << addr_s + << " tried to change their password, but supplied wrong" + << " (SRP) password for authentication." << std::endl; DenySudoAccess(peer_id); return; } - std::string ip = getPeerAddress(peer_id).serializeString(); - actionstream << "Server: User " << client->getName() << " at " << ip << - " supplied wrong password (auth mechanism: SRP)." << std::endl; - m_script->on_auth_failure(client->getName(), ip); + actionstream << "Server: User " << playername << " at " << addr_s + << " supplied wrong password (auth mechanism: SRP)." << std::endl; + m_script->on_authplayer(playername, addr_s, false); DenyAccess(peer_id, SERVER_ACCESSDENIED_WRONG_PASSWORD); return; } if (client->create_player_on_auth_success) { - std::string playername = client->getName(); m_script->createAuth(playername, client->enc_pwd); std::string checkpwd; // not used, but needed for passing something @@ -1725,6 +1726,7 @@ void Server::handleCommand_SrpBytesM(NetworkPacket* pkt) client->create_player_on_auth_success = false; } + m_script->on_authplayer(playername, addr_s, true); acceptAuth(peer_id, wantSudo); } diff --git a/src/script/cpp_api/s_player.cpp b/src/script/cpp_api/s_player.cpp index df67ea00c..712120c61 100644 --- a/src/script/cpp_api/s_player.cpp +++ b/src/script/cpp_api/s_player.cpp @@ -147,7 +147,7 @@ bool ScriptApiPlayer::can_bypass_userlimit(const std::string &name, const std::s return lua_toboolean(L, -1); } -void ScriptApiPlayer::on_joinplayer(ServerActiveObject *player) +void ScriptApiPlayer::on_joinplayer(ServerActiveObject *player, s64 last_login) { SCRIPTAPI_PRECHECKHEADER @@ -156,7 +156,11 @@ void ScriptApiPlayer::on_joinplayer(ServerActiveObject *player) lua_getfield(L, -1, "registered_on_joinplayers"); // Call callbacks objectrefGetOrCreate(L, player); - runCallbacks(1, RUN_CALLBACKS_MODE_FIRST); + if (last_login != -1) + lua_pushinteger(L, last_login); + else + lua_pushnil(L); + runCallbacks(2, RUN_CALLBACKS_MODE_FIRST); } void ScriptApiPlayer::on_leaveplayer(ServerActiveObject *player, @@ -216,16 +220,19 @@ void ScriptApiPlayer::on_playerReceiveFields(ServerActiveObject *player, runCallbacks(3, RUN_CALLBACKS_MODE_OR_SC); } -void ScriptApiPlayer::on_auth_failure(const std::string &name, const std::string &ip) +void ScriptApiPlayer::on_authplayer(const std::string &name, const std::string &ip, bool is_success) { SCRIPTAPI_PRECHECKHEADER - // Get core.registered_on_auth_failure + // Get core.registered_on_authplayers lua_getglobal(L, "core"); - lua_getfield(L, -1, "registered_on_auth_fail"); + lua_getfield(L, -1, "registered_on_authplayers"); + + // Call callbacks lua_pushstring(L, name.c_str()); lua_pushstring(L, ip.c_str()); - runCallbacks(2, RUN_CALLBACKS_MODE_FIRST); + lua_pushboolean(L, is_success); + runCallbacks(3, RUN_CALLBACKS_MODE_FIRST); } void ScriptApiPlayer::pushMoveArguments( diff --git a/src/script/cpp_api/s_player.h b/src/script/cpp_api/s_player.h index 7ca3d8f30..a337f975b 100644 --- a/src/script/cpp_api/s_player.h +++ b/src/script/cpp_api/s_player.h @@ -41,7 +41,7 @@ public: bool on_prejoinplayer(const std::string &name, const std::string &ip, std::string *reason); bool can_bypass_userlimit(const std::string &name, const std::string &ip); - void on_joinplayer(ServerActiveObject *player); + void on_joinplayer(ServerActiveObject *player, s64 last_login); void on_leaveplayer(ServerActiveObject *player, bool timeout); void on_cheat(ServerActiveObject *player, const std::string &cheat_type); bool on_punchplayer(ServerActiveObject *player, ServerActiveObject *hitter, @@ -51,7 +51,7 @@ public: const PlayerHPChangeReason &reason); void on_playerReceiveFields(ServerActiveObject *player, const std::string &formname, const StringMap &fields); - void on_auth_failure(const std::string &name, const std::string &ip); + void on_authplayer(const std::string &name, const std::string &ip, bool is_success); // Player inventory callbacks // Return number of accepted items to be moved diff --git a/src/script/cpp_api/s_server.cpp b/src/script/cpp_api/s_server.cpp index 1ce2f9d45..96cb28b28 100644 --- a/src/script/cpp_api/s_server.cpp +++ b/src/script/cpp_api/s_server.cpp @@ -23,7 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc., bool ScriptApiServer::getAuth(const std::string &playername, std::string *dst_password, - std::set *dst_privs) + std::set *dst_privs, + s64 *dst_last_login) { SCRIPTAPI_PRECHECKHEADER @@ -43,8 +44,7 @@ bool ScriptApiServer::getAuth(const std::string &playername, luaL_checktype(L, -1, LUA_TTABLE); std::string password; - bool found = getstringfield(L, -1, "password", password); - if (!found) + if (!getstringfield(L, -1, "password", password)) throw LuaError("Authentication handler didn't return password"); if (dst_password) *dst_password = password; @@ -54,7 +54,13 @@ bool ScriptApiServer::getAuth(const std::string &playername, throw LuaError("Authentication handler didn't return privilege table"); if (dst_privs) readPrivileges(-1, *dst_privs); - lua_pop(L, 1); + lua_pop(L, 1); // Remove key from privs table + + s64 last_login; + if(!getintfield(L, -1, "last_login", last_login)) + throw LuaError("Authentication handler didn't return last_login"); + if (dst_last_login) + *dst_last_login = (s64)last_login; return true; } diff --git a/src/script/cpp_api/s_server.h b/src/script/cpp_api/s_server.h index a4cede84d..d8639cba7 100644 --- a/src/script/cpp_api/s_server.h +++ b/src/script/cpp_api/s_server.h @@ -43,7 +43,8 @@ public: /* auth */ bool getAuth(const std::string &playername, std::string *dst_password, - std::set *dst_privs); + std::set *dst_privs, + s64 *dst_last_login = nullptr); void createAuth(const std::string &playername, const std::string &password); bool setPassword(const std::string &playername, -- cgit v1.2.3 From 9d6e7e48d6fb1daff8fedcb2f111164bef61f1e7 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 22 May 2020 14:17:03 +0200 Subject: Implement spawning particles with node texture appearance --- build/android/native/jni/Android.mk | 1 + doc/lua_api.txt | 30 ++++++++++++- src/client/particles.cpp | 75 ++++++++++++++++++++++++-------- src/client/particles.h | 4 +- src/network/clientpackethandler.cpp | 10 +++++ src/particles.cpp | 10 +++++ src/particles.h | 6 +++ src/script/lua_api/l_particles.cpp | 14 ++++++ src/script/lua_api/l_particles_local.cpp | 14 ++++++ src/server.cpp | 1 + 10 files changed, 145 insertions(+), 20 deletions(-) (limited to 'src/network') diff --git a/build/android/native/jni/Android.mk b/build/android/native/jni/Android.mk index a5cb099e6..140947e6a 100644 --- a/build/android/native/jni/Android.mk +++ b/build/android/native/jni/Android.mk @@ -164,6 +164,7 @@ LOCAL_SRC_FILES := \ ../../../src/noise.cpp \ ../../../src/objdef.cpp \ ../../../src/object_properties.cpp \ + ../../../src/particles.cpp \ ../../../src/pathfinder.cpp \ ../../../src/player.cpp \ ../../../src/porting.cpp \ diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 26061eccb..5b3f61c99 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -7835,6 +7835,8 @@ Used by `minetest.add_particle`. size = 1, -- Scales the visual size of the particle texture. + -- If `node` is set, size can be set to 0 to spawn a randomly-sized + -- particle (just like actual node dig particles). collisiondetection = false, -- If true collides with `walkable` nodes and, depending on the @@ -7853,6 +7855,7 @@ Used by `minetest.add_particle`. -- If true faces player using y axis only texture = "image.png", + -- The texture of the particle playername = "singleplayer", -- Optional, if specified spawns particle only on the player's client @@ -7863,6 +7866,17 @@ Used by `minetest.add_particle`. glow = 0 -- Optional, specify particle self-luminescence in darkness. -- Values 0-14. + + node = {name = "ignore", param2 = 0}, + -- Optional, if specified the particle will have the same appearance as + -- node dig particles for the given node. + -- `texture` and `animation` will be ignored if this is set. + + node_tile = 0, + -- Optional, only valid in combination with `node` + -- If set to a valid number 1-6, specifies the tile from which the + -- particle texture is picked. + -- Otherwise, the default behavior is used. (currently: any random tile) } @@ -7892,7 +7906,9 @@ Used by `minetest.add_particlespawner`. maxsize = 1, -- The particles' properties are random values between the min and max -- values. - -- pos, velocity, acceleration, expirationtime, size + -- applies to: pos, velocity, acceleration, expirationtime, size + -- If `node` is set, min and maxsize can be set to 0 to spawn + -- randomly-sized particles (just like actual node dig particles). collisiondetection = false, -- If true collide with `walkable` nodes and, depending on the @@ -7915,6 +7931,7 @@ Used by `minetest.add_particlespawner`. -- If true face player using y axis only texture = "image.png", + -- The texture of the particle playername = "singleplayer", -- Optional, if specified spawns particles only on the player's client @@ -7925,6 +7942,17 @@ Used by `minetest.add_particlespawner`. glow = 0 -- Optional, specify particle self-luminescence in darkness. -- Values 0-14. + + node = {name = "ignore", param2 = 0}, + -- Optional, if specified the particles will have the same appearance as + -- node dig particles for the given node. + -- `texture` and `animation` will be ignored if this is set. + + node_tile = 0, + -- Optional, only valid in combination with `node` + -- If set to a valid number 1-6, specifies the tile from which the + -- particle texture is picked. + -- Otherwise, the default behavior is used. (currently: any random tile) } `HTTPRequest` definition diff --git a/src/client/particles.cpp b/src/client/particles.cpp index c2e751b4f..c78a3e71a 100644 --- a/src/client/particles.cpp +++ b/src/client/particles.cpp @@ -304,18 +304,37 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius, } pp.expirationtime = random_f32(p.minexptime, p.maxexptime); - pp.size = random_f32(p.minsize, p.maxsize); - p.copyCommon(pp); + video::ITexture *texture; + v2f texpos, texsize; + video::SColor color(0xFFFFFFFF); + + if (p.node.getContent() != CONTENT_IGNORE) { + const ContentFeatures &f = + m_particlemanager->m_env->getGameDef()->ndef()->get(p.node); + if (!ParticleManager::getNodeParticleParams(p.node, f, pp, &texture, + texpos, texsize, &color, p.node_tile)) + return; + } else { + texture = m_texture; + texpos = v2f(0.0f, 0.0f); + texsize = v2f(1.0f, 1.0f); + } + + // Allow keeping default random size + if (p.maxsize > 0.0f) + pp.size = random_f32(p.minsize, p.maxsize); + m_particlemanager->addParticle(new Particle( m_gamedef, m_player, env, pp, - m_texture, - v2f(0.0, 0.0), - v2f(1.0, 1.0) + texture, + texpos, + texsize, + color )); } @@ -460,17 +479,35 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, break; } case CE_SPAWN_PARTICLE: { - const ParticleParameters &p = *event->spawn_particle; - video::ITexture *texture = - client->tsrc()->getTextureForMesh(p.texture); + ParticleParameters &p = *event->spawn_particle; - Particle *toadd = new Particle(client, player, m_env, - p, - texture, - v2f(0.0, 0.0), - v2f(1.0, 1.0)); + video::ITexture *texture; + v2f texpos, texsize; + video::SColor color(0xFFFFFFFF); + + f32 oldsize = p.size; + + if (p.node.getContent() != CONTENT_IGNORE) { + const ContentFeatures &f = m_env->getGameDef()->ndef()->get(p.node); + if (!getNodeParticleParams(p.node, f, p, &texture, texpos, + texsize, &color, p.node_tile)) + texture = nullptr; + } else { + texture = client->tsrc()->getTextureForMesh(p.texture); + texpos = v2f(0.0f, 0.0f); + texsize = v2f(1.0f, 1.0f); + } + + // Allow keeping default random size + if (oldsize > 0.0f) + p.size = oldsize; - addParticle(toadd); + if (texture) { + Particle *toadd = new Particle(client, player, m_env, + p, texture, texpos, texsize, color); + + addParticle(toadd); + } delete event->spawn_particle; break; @@ -480,15 +517,19 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, } bool ParticleManager::getNodeParticleParams(const MapNode &n, - const ContentFeatures &f, ParticleParameters &p, - video::ITexture **texture, v2f &texpos, v2f &texsize, video::SColor *color) + const ContentFeatures &f, ParticleParameters &p, video::ITexture **texture, + v2f &texpos, v2f &texsize, video::SColor *color, u8 tilenum) { // No particles for "airlike" nodes if (f.drawtype == NDT_AIRLIKE) return false; // Texture - u8 texid = rand() % 6; + u8 texid; + if (tilenum > 0 && tilenum <= 6) + texid = tilenum - 1; + else + texid = rand() % 6; const TileLayer &tile = f.tiles[texid].layers[0]; p.animation.type = TAT_NONE; diff --git a/src/client/particles.h b/src/client/particles.h index 7dda0e1b1..2011f0262 100644 --- a/src/client/particles.h +++ b/src/client/particles.h @@ -42,7 +42,7 @@ class Particle : public scene::ISceneNode video::ITexture *texture, v2f texpos, v2f texsize, - video::SColor color = video::SColor(0xFFFFFFFF) + video::SColor color ); ~Particle() = default; @@ -171,7 +171,7 @@ public: protected: static bool getNodeParticleParams(const MapNode &n, const ContentFeatures &f, ParticleParameters &p, video::ITexture **texture, v2f &texpos, - v2f &texsize, video::SColor *color); + v2f &texsize, video::SColor *color, u8 tilenum = 0); void addParticle(Particle* toadd); diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 054e60c3c..e000acc92 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1003,6 +1003,16 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt) p.glow = readU8(is); p.object_collision = readU8(is); + // This is kinda awful + do { + u16 tmp_param0 = readU16(is); + if (is.eof()) + break; + p.node.param0 = tmp_param0; + p.node.param2 = readU8(is); + p.node_tile = readU8(is); + } while (0); + auto event = new ClientEvent(); event->type = CE_ADD_PARTICLESPAWNER; event->add_particlespawner.p = new ParticleSpawnerParameters(p); diff --git a/src/particles.cpp b/src/particles.cpp index 711d189f6..fd81238dc 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -34,6 +34,9 @@ void ParticleParameters::serialize(std::ostream &os, u16 protocol_ver) const animation.serialize(os, 6); /* NOT the protocol ver */ writeU8(os, glow); writeU8(os, object_collision); + writeU16(os, node.param0); + writeU8(os, node.param2); + writeU8(os, node_tile); } void ParticleParameters::deSerialize(std::istream &is, u16 protocol_ver) @@ -50,4 +53,11 @@ void ParticleParameters::deSerialize(std::istream &is, u16 protocol_ver) animation.deSerialize(is, 6); /* NOT the protocol ver */ glow = readU8(is); object_collision = readU8(is); + // This is kinda awful + u16 tmp_param0 = readU16(is); + if (is.eof()) + return; + node.param0 = tmp_param0; + node.param2 = readU8(is); + node_tile = readU8(is); } diff --git a/src/particles.h b/src/particles.h index 659c1249f..6f518b771 100644 --- a/src/particles.h +++ b/src/particles.h @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "irrlichttypes_bloated.h" #include "tileanimation.h" +#include "mapnode.h" // This file defines the particle-related structures that both the server and // client need. The ParticleManager and rendering is in client/particles.h @@ -34,9 +35,12 @@ struct CommonParticleParams { std::string texture; struct TileAnimationParams animation; u8 glow = 0; + MapNode node; + u8 node_tile = 0; CommonParticleParams() { animation.type = TAT_NONE; + node.setContent(CONTENT_IGNORE); } /* This helper is useful for copying params from @@ -49,6 +53,8 @@ struct CommonParticleParams { to.texture = texture; to.animation = animation; to.glow = glow; + to.node = node; + to.node_tile = node_tile; } }; diff --git a/src/script/lua_api/l_particles.cpp b/src/script/lua_api/l_particles.cpp index 7680aa17b..a51c4fe20 100644 --- a/src/script/lua_api/l_particles.cpp +++ b/src/script/lua_api/l_particles.cpp @@ -111,6 +111,13 @@ int ModApiParticles::l_add_particle(lua_State *L) p.texture = getstringfield_default(L, 1, "texture", p.texture); p.glow = getintfield_default(L, 1, "glow", p.glow); + lua_getfield(L, 1, "node"); + if (lua_istable(L, -1)) + p.node = readnode(L, -1, getGameDef(L)->ndef()); + lua_pop(L, 1); + + p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile); + playername = getstringfield_default(L, 1, "playername", ""); } @@ -231,6 +238,13 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) p.texture = getstringfield_default(L, 1, "texture", p.texture); playername = getstringfield_default(L, 1, "playername", ""); p.glow = getintfield_default(L, 1, "glow", p.glow); + + lua_getfield(L, 1, "node"); + if (lua_istable(L, -1)) + p.node = readnode(L, -1, getGameDef(L)->ndef()); + lua_pop(L, 1); + + p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile); } u32 id = getServer(L)->addParticleSpawner(p, attached, playername); diff --git a/src/script/lua_api/l_particles_local.cpp b/src/script/lua_api/l_particles_local.cpp index 9595b2fab..cc68b13a5 100644 --- a/src/script/lua_api/l_particles_local.cpp +++ b/src/script/lua_api/l_particles_local.cpp @@ -67,6 +67,13 @@ int ModApiParticlesLocal::l_add_particle(lua_State *L) p.texture = getstringfield_default(L, 1, "texture", p.texture); p.glow = getintfield_default(L, 1, "glow", p.glow); + lua_getfield(L, 1, "node"); + if (lua_istable(L, -1)) + p.node = readnode(L, -1, getGameDef(L)->ndef()); + lua_pop(L, 1); + + p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile); + ClientEvent *event = new ClientEvent(); event->type = CE_SPAWN_PARTICLE; event->spawn_particle = new ParticleParameters(p); @@ -134,6 +141,13 @@ int ModApiParticlesLocal::l_add_particlespawner(lua_State *L) p.texture = getstringfield_default(L, 1, "texture", p.texture); p.glow = getintfield_default(L, 1, "glow", p.glow); + lua_getfield(L, 1, "node"); + if (lua_istable(L, -1)) + p.node = readnode(L, -1, getGameDef(L)->ndef()); + lua_pop(L, 1); + + p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile); + u64 id = getClient(L)->getParticleManager()->generateSpawnerId(); auto event = new ClientEvent(); diff --git a/src/server.cpp b/src/server.cpp index 68b0131d4..d6e545498 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1577,6 +1577,7 @@ void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version, pkt.putRawString(os.str()); } pkt << p.glow << p.object_collision; + pkt << p.node.param0 << p.node.param2 << p.node_tile; Send(&pkt); } -- cgit v1.2.3 From 2424dfe007e451bb02f87884c2b272cf307d6e7c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 13 Jun 2020 19:03:26 +0200 Subject: Server pushing media at runtime (#9961) --- doc/lua_api.txt | 14 +++ src/client/client.cpp | 12 ++- src/client/client.h | 9 +- src/client/clientmedia.cpp | 10 +- src/client/clientmedia.h | 5 + src/client/filecache.cpp | 8 ++ src/client/filecache.h | 1 + src/filesys.cpp | 6 ++ src/network/clientopcodes.cpp | 2 +- src/network/clientpackethandler.cpp | 46 +++++++++ src/network/networkprotocol.h | 9 ++ src/network/serveropcodes.cpp | 2 +- src/script/lua_api/l_server.cpp | 22 +++- src/script/lua_api/l_server.h | 3 + src/server.cpp | 195 +++++++++++++++++++++++------------- src/server.h | 4 + 16 files changed, 263 insertions(+), 85 deletions(-) (limited to 'src/network') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index ed060c4ad..cb968958f 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -5217,6 +5217,20 @@ Server * Returns a code (0: successful, 1: no such player, 2: player is connected) * `minetest.remove_player_auth(name)`: remove player authentication data * Returns boolean indicating success (false if player nonexistant) +* `minetest.dynamic_add_media(filepath)` + * Adds the file at the given path to the media sent to clients by the server + on startup and also pushes this file to already connected clients. + The file must be a supported image, sound or model format. It must not be + modified, deleted, moved or renamed after calling this function. + The list of dynamically added media is not persisted. + * Returns boolean indicating success (duplicate files count as error) + * The media will be ready to use (in e.g. entity textures, sound_play) + immediately after calling this function. + Old clients that lack support for this feature will not see the media + unless they reconnect to the server. + * Since media transferred this way does not use client caching or HTTP + transfers, dynamic media should not be used with big files or performance + will suffer. Bans ---- diff --git a/src/client/client.cpp b/src/client/client.cpp index c03c062c6..34f97a9de 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -670,11 +670,9 @@ void Client::step(float dtime) } } -bool Client::loadMedia(const std::string &data, const std::string &filename) +bool Client::loadMedia(const std::string &data, const std::string &filename, + bool from_media_push) { - // Silly irrlicht's const-incorrectness - Buffer data_rw(data.c_str(), data.size()); - std::string name; const char *image_ext[] = { @@ -690,6 +688,9 @@ bool Client::loadMedia(const std::string &data, const std::string &filename) io::IFileSystem *irrfs = RenderingEngine::get_filesystem(); video::IVideoDriver *vdrv = RenderingEngine::get_video_driver(); + // Silly irrlicht's const-incorrectness + Buffer data_rw(data.c_str(), data.size()); + // Create an irrlicht memory file io::IReadFile *rfile = irrfs->createMemoryReadFile( *data_rw, data_rw.getSize(), "_tempreadfile"); @@ -727,7 +728,6 @@ bool Client::loadMedia(const std::string &data, const std::string &filename) ".x", ".b3d", ".md2", ".obj", NULL }; - name = removeStringEnd(filename, model_ext); if (!name.empty()) { verbosestream<<"Client: Storing model into memory: " @@ -744,6 +744,8 @@ bool Client::loadMedia(const std::string &data, const std::string &filename) }; name = removeStringEnd(filename, translate_ext); if (!name.empty()) { + if (from_media_push) + return false; TRACESTREAM(<< "Client: Loading translation: " << "\"" << filename << "\"" << std::endl); g_client_translations->loadTranslation(data); diff --git a/src/client/client.h b/src/client/client.h index 3b1095ac2..733634db1 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -222,6 +222,7 @@ public: void handleCommand_FormspecPrepend(NetworkPacket *pkt); void handleCommand_CSMRestrictionFlags(NetworkPacket *pkt); void handleCommand_PlayerSpeed(NetworkPacket *pkt); + void handleCommand_MediaPush(NetworkPacket *pkt); void ProcessData(NetworkPacket *pkt); @@ -376,7 +377,8 @@ public: // The following set of functions is used by ClientMediaDownloader // Insert a media file appropriately into the appropriate manager - bool loadMedia(const std::string &data, const std::string &filename); + bool loadMedia(const std::string &data, const std::string &filename, + bool from_media_push = false); // Send a request for conventional media transfer void request_media(const std::vector &file_requests); @@ -488,6 +490,7 @@ private: Camera *m_camera = nullptr; Minimap *m_minimap = nullptr; bool m_minimap_disabled_by_server = false; + // Server serialization version u8 m_server_ser_ver; @@ -529,7 +532,6 @@ private: AuthMechanism m_chosen_auth_mech; void *m_auth_data = nullptr; - bool m_access_denied = false; bool m_access_denied_reconnect = false; std::string m_access_denied_reason = ""; @@ -538,7 +540,10 @@ private: bool m_nodedef_received = false; bool m_activeobjects_received = false; bool m_mods_loaded = false; + ClientMediaDownloader *m_media_downloader; + // Set of media filenames pushed by server at runtime + std::unordered_set m_media_pushed_files; // time_of_day speed approximation for old protocol bool m_time_of_day_set = false; diff --git a/src/client/clientmedia.cpp b/src/client/clientmedia.cpp index 6da99bbbf..8cd3b6bcc 100644 --- a/src/client/clientmedia.cpp +++ b/src/client/clientmedia.cpp @@ -35,6 +35,15 @@ static std::string getMediaCacheDir() return porting::path_cache + DIR_DELIM + "media"; } +bool clientMediaUpdateCache(const std::string &raw_hash, const std::string &filedata) +{ + FileCache media_cache(getMediaCacheDir()); + std::string sha1_hex = hex_encode(raw_hash); + if (!media_cache.exists(sha1_hex)) + return media_cache.update(sha1_hex, filedata); + return true; +} + /* ClientMediaDownloader */ @@ -559,7 +568,6 @@ bool ClientMediaDownloader::checkAndLoad( return true; } - /* Minetest Hashset File Format diff --git a/src/client/clientmedia.h b/src/client/clientmedia.h index 92831082c..5a918535b 100644 --- a/src/client/clientmedia.h +++ b/src/client/clientmedia.h @@ -33,6 +33,11 @@ struct HTTPFetchResult; #define MTHASHSET_FILE_SIGNATURE 0x4d544853 // 'MTHS' #define MTHASHSET_FILE_NAME "index.mth" +// Store file into media cache (unless it exists already) +// Validating the hash is responsibility of the caller +bool clientMediaUpdateCache(const std::string &raw_hash, + const std::string &filedata); + class ClientMediaDownloader { public: diff --git a/src/client/filecache.cpp b/src/client/filecache.cpp index 3d1b302a8..46bbe4059 100644 --- a/src/client/filecache.cpp +++ b/src/client/filecache.cpp @@ -82,8 +82,16 @@ bool FileCache::update(const std::string &name, const std::string &data) std::string path = m_dir + DIR_DELIM + name; return updateByPath(path, data); } + bool FileCache::load(const std::string &name, std::ostream &os) { std::string path = m_dir + DIR_DELIM + name; return loadByPath(path, os); } + +bool FileCache::exists(const std::string &name) +{ + std::string path = m_dir + DIR_DELIM + name; + std::ifstream fis(path.c_str(), std::ios_base::binary); + return fis.good(); +} diff --git a/src/client/filecache.h b/src/client/filecache.h index 96e4c8ba1..ea6afc4b2 100644 --- a/src/client/filecache.h +++ b/src/client/filecache.h @@ -33,6 +33,7 @@ public: bool update(const std::string &name, const std::string &data); bool load(const std::string &name, std::ostream &os); + bool exists(const std::string &name); private: std::string m_dir; diff --git a/src/filesys.cpp b/src/filesys.cpp index f61b39b94..0bc351669 100644 --- a/src/filesys.cpp +++ b/src/filesys.cpp @@ -691,6 +691,12 @@ std::string AbsolutePath(const std::string &path) const char *GetFilenameFromPath(const char *path) { const char *filename = strrchr(path, DIR_DELIM_CHAR); + // Consistent with IsDirDelimiter this function handles '/' too + if (DIR_DELIM_CHAR != '/') { + const char *tmp = strrchr(path, '/'); + if (tmp && tmp > filename) + filename = tmp; + } return filename ? filename + 1 : path; } diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp index 0f20047c0..f812a08a1 100644 --- a/src/network/clientopcodes.cpp +++ b/src/network/clientopcodes.cpp @@ -68,7 +68,7 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_TIME_OF_DAY", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_TimeOfDay }, // 0x29 { "TOCLIENT_CSM_RESTRICTION_FLAGS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_CSMRestrictionFlags }, // 0x2A { "TOCLIENT_PLAYER_SPEED", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_PlayerSpeed }, // 0x2B - null_command_handler, + { "TOCLIENT_MEDIA_PUSH", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_MediaPush }, // 0x2C null_command_handler, null_command_handler, { "TOCLIENT_CHAT_MESSAGE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ChatMessage }, // 0x2F diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index e000acc92..5934eaf8c 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "script/scripting_client.h" #include "util/serialize.h" #include "util/srp.h" +#include "util/sha1.h" #include "tileanimation.h" #include "gettext.h" #include "skyparams.h" @@ -1471,6 +1472,51 @@ void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt) player->addVelocity(added_vel); } +void Client::handleCommand_MediaPush(NetworkPacket *pkt) +{ + std::string raw_hash, filename, filedata; + bool cached; + + *pkt >> raw_hash >> filename >> cached; + filedata = pkt->readLongString(); + + if (raw_hash.size() != 20 || filedata.empty() || filename.empty() || + !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) { + throw PacketError("Illegal filename, data or hash"); + } + + verbosestream << "Server pushes media file \"" << filename << "\" with " + << filedata.size() << " bytes of data (cached=" << cached + << ")" << std::endl; + + if (m_media_pushed_files.count(filename) != 0) { + // Silently ignore for synchronization purposes + return; + } + + // Compute and check checksum of data + std::string computed_hash; + { + SHA1 ctx; + ctx.addBytes(filedata.c_str(), filedata.size()); + unsigned char *buf = ctx.getDigest(); + computed_hash.assign((char*) buf, 20); + free(buf); + } + if (raw_hash != computed_hash) { + verbosestream << "Hash of file data mismatches, ignoring." << std::endl; + return; + } + + // Actually load media + loadMedia(filedata, filename, true); + m_media_pushed_files.insert(filename); + + // Cache file for the next time when this client joins the same server + if (cached) + clientMediaUpdateCache(raw_hash, filedata); +} + /* * Mod channels */ diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index ab924f1db..fd683eac9 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -323,6 +323,15 @@ enum ToClientCommand v3f added_vel */ + TOCLIENT_MEDIA_PUSH = 0x2C, + /* + std::string raw_hash + std::string filename + bool should_be_cached + u32 len + char filedata[len] + */ + // (oops, there is some gap here) TOCLIENT_CHAT_MESSAGE = 0x2F, diff --git a/src/network/serveropcodes.cpp b/src/network/serveropcodes.cpp index 6ee4ff256..2fc3197c2 100644 --- a/src/network/serveropcodes.cpp +++ b/src/network/serveropcodes.cpp @@ -167,7 +167,7 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_TIME_OF_DAY", 0, true }, // 0x29 { "TOCLIENT_CSM_RESTRICTION_FLAGS", 0, true }, // 0x2A { "TOCLIENT_PLAYER_SPEED", 0, true }, // 0x2B - null_command_factory, // 0x2C + { "TOCLIENT_MEDIA_PUSH", 0, true }, // 0x2C (sent over channel 1 too) null_command_factory, // 0x2D null_command_factory, // 0x2E { "TOCLIENT_CHAT_MESSAGE", 0, true }, // 0x2F diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index b6754938e..6f934bb9d 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_converter.h" #include "common/c_content.h" #include "cpp_api/s_base.h" +#include "cpp_api/s_security.h" #include "server.h" #include "environment.h" #include "remoteplayer.h" @@ -412,9 +413,6 @@ int ModApiServer::l_get_modnames(lua_State *L) std::vector modlist; getServer(L)->getModNames(modlist); - // Take unsorted items from mods_unsorted and sort them into - // mods_sorted; not great performance but the number of mods on a - // server will likely be small. std::sort(modlist.begin(), modlist.end()); // Package them up for Lua @@ -474,6 +472,23 @@ int ModApiServer::l_sound_fade(lua_State *L) return 0; } +// dynamic_add_media(filepath) +int ModApiServer::l_dynamic_add_media(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + // Reject adding media before the server has started up + if (!getEnv(L)) + throw LuaError("Dynamic media cannot be added before server has started up"); + + std::string filepath = readParam(L, 1); + CHECK_SECURE_PATH(L, filepath.c_str(), false); + + bool ok = getServer(L)->dynamicAddMedia(filepath); + lua_pushboolean(L, ok); + return 1; +} + // is_singleplayer() int ModApiServer::l_is_singleplayer(lua_State *L) { @@ -538,6 +553,7 @@ void ModApiServer::Initialize(lua_State *L, int top) API_FCT(sound_play); API_FCT(sound_stop); API_FCT(sound_fade); + API_FCT(dynamic_add_media); API_FCT(get_player_information); API_FCT(get_player_privs); diff --git a/src/script/lua_api/l_server.h b/src/script/lua_api/l_server.h index 3aa1785a2..938bfa8ef 100644 --- a/src/script/lua_api/l_server.h +++ b/src/script/lua_api/l_server.h @@ -70,6 +70,9 @@ private: // sound_fade(handle, step, gain) static int l_sound_fade(lua_State *L); + // dynamic_add_media(filepath) + static int l_dynamic_add_media(lua_State *L); + // get_player_privs(name, text) static int l_get_player_privs(lua_State *L); diff --git a/src/server.cpp b/src/server.cpp index 6ecbd7097..fe2bb3840 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2405,9 +2405,87 @@ bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos) return true; } +bool Server::addMediaFile(const std::string &filename, + const std::string &filepath, std::string *filedata_to, + std::string *digest_to) +{ + // If name contains illegal characters, ignore the file + if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) { + infostream << "Server: ignoring illegal file name: \"" + << filename << "\"" << std::endl; + return false; + } + // If name is not in a supported format, ignore it + const char *supported_ext[] = { + ".png", ".jpg", ".bmp", ".tga", + ".pcx", ".ppm", ".psd", ".wal", ".rgb", + ".ogg", + ".x", ".b3d", ".md2", ".obj", + // Custom translation file format + ".tr", + NULL + }; + if (removeStringEnd(filename, supported_ext).empty()) { + infostream << "Server: ignoring unsupported file extension: \"" + << filename << "\"" << std::endl; + return false; + } + // Ok, attempt to load the file and add to cache + + // Read data + std::ifstream fis(filepath.c_str(), std::ios_base::binary); + if (!fis.good()) { + errorstream << "Server::addMediaFile(): Could not open \"" + << filename << "\" for reading" << std::endl; + return false; + } + std::string filedata; + bool bad = false; + for (;;) { + char buf[1024]; + fis.read(buf, sizeof(buf)); + std::streamsize len = fis.gcount(); + filedata.append(buf, len); + if (fis.eof()) + break; + if (!fis.good()) { + bad = true; + break; + } + } + if (bad) { + errorstream << "Server::addMediaFile(): Failed to read \"" + << filename << "\"" << std::endl; + return false; + } else if (filedata.empty()) { + errorstream << "Server::addMediaFile(): Empty file \"" + << filepath << "\"" << std::endl; + return false; + } + + SHA1 sha1; + sha1.addBytes(filedata.c_str(), filedata.length()); + + unsigned char *digest = sha1.getDigest(); + std::string sha1_base64 = base64_encode(digest, 20); + std::string sha1_hex = hex_encode((char*) digest, 20); + if (digest_to) + *digest_to = std::string((char*) digest, 20); + free(digest); + + // Put in list + m_media[filename] = MediaInfo(filepath, sha1_base64); + verbosestream << "Server: " << sha1_hex << " is " << filename + << std::endl; + + if (filedata_to) + *filedata_to = std::move(filedata); + return true; +} + void Server::fillMediaCache() { - infostream<<"Server: Calculating media file checksums"< paths; @@ -2419,80 +2497,15 @@ void Server::fillMediaCache() for (const std::string &mediapath : paths) { std::vector dirlist = fs::GetDirListing(mediapath); for (const fs::DirListNode &dln : dirlist) { - if (dln.dir) // Ignode dirs - continue; - std::string filename = dln.name; - // If name contains illegal characters, ignore the file - if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) { - infostream<<"Server: ignoring illegal file name: \"" - << filename << "\"" << std::endl; - continue; - } - // If name is not in a supported format, ignore it - const char *supported_ext[] = { - ".png", ".jpg", ".bmp", ".tga", - ".pcx", ".ppm", ".psd", ".wal", ".rgb", - ".ogg", - ".x", ".b3d", ".md2", ".obj", - // Custom translation file format - ".tr", - NULL - }; - if (removeStringEnd(filename, supported_ext).empty()){ - infostream << "Server: ignoring unsupported file extension: \"" - << filename << "\"" << std::endl; + if (dln.dir) // Ignore dirs continue; - } - // Ok, attempt to load the file and add to cache - std::string filepath; - filepath.append(mediapath).append(DIR_DELIM).append(filename); - - // Read data - std::ifstream fis(filepath.c_str(), std::ios_base::binary); - if (!fis.good()) { - errorstream << "Server::fillMediaCache(): Could not open \"" - << filename << "\" for reading" << std::endl; - continue; - } - std::ostringstream tmp_os(std::ios_base::binary); - bool bad = false; - for(;;) { - char buf[1024]; - fis.read(buf, 1024); - std::streamsize len = fis.gcount(); - tmp_os.write(buf, len); - if (fis.eof()) - break; - if (!fis.good()) { - bad = true; - break; - } - } - if(bad) { - errorstream<<"Server::fillMediaCache(): Failed to read \"" - << filename << "\"" << std::endl; - continue; - } - if(tmp_os.str().length() == 0) { - errorstream << "Server::fillMediaCache(): Empty file \"" - << filepath << "\"" << std::endl; - continue; - } - - SHA1 sha1; - sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length()); - - unsigned char *digest = sha1.getDigest(); - std::string sha1_base64 = base64_encode(digest, 20); - std::string sha1_hex = hex_encode((char*)digest, 20); - free(digest); - - // Put in list - m_media[filename] = MediaInfo(filepath, sha1_base64); - verbosestream << "Server: " << sha1_hex << " is " << filename - << std::endl; + std::string filepath = mediapath; + filepath.append(DIR_DELIM).append(dln.name); + addMediaFile(dln.name, filepath); } } + + infostream << "Server: " << m_media.size() << " media files collected" << std::endl; } void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code) @@ -3428,6 +3441,44 @@ void Server::deleteParticleSpawner(const std::string &playername, u32 id) SendDeleteParticleSpawner(peer_id, id); } +bool Server::dynamicAddMedia(const std::string &filepath) +{ + std::string filename = fs::GetFilenameFromPath(filepath.c_str()); + if (m_media.find(filename) != m_media.end()) { + errorstream << "Server::dynamicAddMedia(): file \"" << filename + << "\" already exists in media cache" << std::endl; + return false; + } + + // Load the file and add it to our media cache + std::string filedata, raw_hash; + bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash); + if (!ok) + return false; + + // Push file to existing clients + NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0); + pkt << raw_hash << filename << (bool) true; + pkt.putLongString(filedata); + + auto client_ids = m_clients.getClientIDs(CS_DefinitionsSent); + for (session_t client_id : client_ids) { + /* + The network layer only guarantees ordered delivery inside a channel. + Since the very next packet could be one that uses the media, we have + to push the media over ALL channels to ensure it is processed before + it is used. + In practice this means we have to send it twice: + - channel 1 (HUD) + - channel 0 (everything else: e.g. play_sound, object messages) + */ + m_clients.send(client_id, 1, &pkt, true); + m_clients.send(client_id, 0, &pkt, true); + } + + return true; +} + // actions: time-reversed list // Return value: success/failure bool Server::rollbackRevertActions(const std::list &actions, diff --git a/src/server.h b/src/server.h index 27943cc29..f44716531 100644 --- a/src/server.h +++ b/src/server.h @@ -236,6 +236,8 @@ public: void deleteParticleSpawner(const std::string &playername, u32 id); + bool dynamicAddMedia(const std::string &filepath); + ServerInventoryManager *getInventoryMgr() const { return m_inventory_mgr.get(); } void sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id); @@ -435,6 +437,8 @@ private: // Sends blocks to clients (locks env and con on its own) void SendBlocks(float dtime); + bool addMediaFile(const std::string &filename, const std::string &filepath, + std::string *filedata = nullptr, std::string *digest = nullptr); void fillMediaCache(); void sendMediaAnnouncement(session_t peer_id, const std::string &lang_code); void sendRequestedMedia(session_t peer_id, -- cgit v1.2.3 From e7e065f553b430173e9112ad55c7046cfc02f2c5 Mon Sep 17 00:00:00 2001 From: Lars Müller <34514239+appgurueu@users.noreply.github.com> Date: Sat, 13 Jun 2020 22:46:20 +0200 Subject: Exposing the zoom key to Lua API (#9903) Co-authored-by: Raul Ferriz --- doc/lua_api.txt | 7 ++++--- src/client/game.cpp | 5 +++-- src/network/serverpackethandler.cpp | 1 + src/script/lua_api/l_object.cpp | 2 ++ 4 files changed, 10 insertions(+), 5 deletions(-) (limited to 'src/network') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index cb968958f..07e9698e8 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6106,13 +6106,14 @@ object you are working with still exists. * `get_formspec_prepend(formspec)`: returns a formspec string. * `get_player_control()`: returns table with player pressed keys * The table consists of fields with boolean value representing the pressed - keys, the fields are jump, right, left, LMB, RMB, sneak, aux1, down, up. + keys, the fields are jump, right, left, LMB, RMB, sneak, aux1, down, up, zoom. * example: `{jump=false, right=true, left=false, LMB=false, RMB=false, - sneak=true, aux1=false, down=false, up=false}` + sneak=true, aux1=false, down=false, up=false, zoom=false}` + * The `zoom` field is available since 5.3 * `get_player_control_bits()`: returns integer with bit packed player pressed keys. * bit nr/meaning: 0/up, 1/down, 2/left, 3/right, 4/jump, 5/aux1, 6/sneak, - 7/LMB, 8/RMB + 7/LMB, 8/RMB, 9/zoom (zoom available since 5.3) * `set_physics_override(override_table)` * `override_table` is a table with the following fields: * `speed`: multiplier to default walking speed value (default: `1`) diff --git a/src/client/game.cpp b/src/client/game.cpp index 139742cec..069c482ca 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2490,7 +2490,7 @@ void Game::updatePlayerControl(const CameraOrientation &cam) input->joystick.getAxisWithoutDead(JA_FORWARD_MOVE) ); - u32 keypress_bits = + u32 keypress_bits = ( ( (u32)(isKeyDown(KeyType::FORWARD) & 0x1) << 0) | ( (u32)(isKeyDown(KeyType::BACKWARD) & 0x1) << 1) | ( (u32)(isKeyDown(KeyType::LEFT) & 0x1) << 2) | @@ -2499,7 +2499,8 @@ void Game::updatePlayerControl(const CameraOrientation &cam) ( (u32)(isKeyDown(KeyType::SPECIAL1) & 0x1) << 5) | ( (u32)(isKeyDown(KeyType::SNEAK) & 0x1) << 6) | ( (u32)(input->getLeftState() & 0x1) << 7) | - ( (u32)(input->getRightState() & 0x1) << 8 + ( (u32)(input->getRightState() & 0x1) << 8) | + ( (u32)(isKeyDown(KeyType::ZOOM) & 0x1) << 9) ); #ifdef ANDROID diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index fed3b6f85..b3008bb50 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -501,6 +501,7 @@ void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao, player->control.sneak = (keyPressed & 64); player->control.LMB = (keyPressed & 128); player->control.RMB = (keyPressed & 256); + player->control.zoom = (keyPressed & 512); if (playersao->checkMovementCheat()) { // Call callbacks diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 0a9f3117b..e7394133a 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1459,6 +1459,8 @@ int ObjectRef::l_get_player_control(lua_State *L) lua_setfield(L, -2, "LMB"); lua_pushboolean(L, control.RMB); lua_setfield(L, -2, "RMB"); + lua_pushboolean(L, control.zoom); + lua_setfield(L, -2, "zoom"); return 1; } -- cgit v1.2.3 From b1ff04e06da531dc52f6ee91e2967b39743033e6 Mon Sep 17 00:00:00 2001 From: v-rob Date: Fri, 10 Jul 2020 03:11:26 -0700 Subject: Formspec: Make dropdowns optionally return event based on index, not value (#9496) --- doc/lua_api.txt | 15 ++++++++++++--- src/gui/guiFormSpecMenu.cpp | 39 +++++++++++++++++++++++---------------- src/gui/guiFormSpecMenu.h | 1 + src/network/networkprotocol.h | 4 +++- 4 files changed, 39 insertions(+), 20 deletions(-) (limited to 'src/network') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index f2a83eca5..2ac4d6766 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2443,7 +2443,7 @@ Elements * `color` is color specified as a `ColorString`. If the alpha component is left blank, the box will be semitransparent. -### `dropdown[,;;;,, ...,;]` +### `dropdown[,;;;,, ...,;;]` * Show a dropdown field * **Important note**: There are two different operation modes: @@ -2454,8 +2454,12 @@ Elements * Fieldname data is transferred to Lua * Items to be shown in dropdown * Index of currently selected dropdown item +* `index event` (optional, allowed parameter since formspec version 4): Specifies the + event field value for selected items. + * `true`: Selected item index + * `false` (default): Selected item value -### `dropdown[,;,;;,, ...,;]` +### `dropdown[,;,;;,, ...,;;]` * Show a dropdown field * **Important note**: This syntax for dropdowns can only be used with the @@ -2468,6 +2472,10 @@ Elements * Fieldname data is transferred to Lua * Items to be shown in dropdown * Index of currently selected dropdown item +* `index event` (optional, allowed parameter since formspec version 4): Specifies the + event field value for selected items. + * `true`: Selected item index + * `false` (default): Selected item value ### `checkbox[,;;