From: Adam Date: Mon, 20 Jan 2014 09:55:53 +0000 (-0500) Subject: m_spanningtree Rewrite /map X-Git-Url: https://git.netwichtig.de/gitweb/?a=commitdiff_plain;h=407f10664a5f8ead87967ca52fd6fcb87d09189a;p=user%2Fhenk%2Fcode%2Finspircd.git m_spanningtree Rewrite /map --- diff --git a/src/modules/m_spanningtree/commands.h b/src/modules/m_spanningtree/commands.h index 97322ed99..0c1ea8e49 100644 --- a/src/modules/m_spanningtree/commands.h +++ b/src/modules/m_spanningtree/commands.h @@ -42,14 +42,6 @@ class CommandRSQuit : public Command class CommandMap : public Command { - /** Show MAP output to a user (recursive) - */ - void ShowMap(TreeServer* Current, User* user, int depth, int &line, char* names, int &maxnamew, char* stats); - - /** Returns oper-specific MAP information - */ - std::string MapOperInfo(TreeServer* Current); - public: CommandMap(Module* Creator); CmdResult Handle(const std::vector& parameters, User* user); diff --git a/src/modules/m_spanningtree/override_map.cpp b/src/modules/m_spanningtree/override_map.cpp index a9b68eb9b..216fd4d66 100644 --- a/src/modules/m_spanningtree/override_map.cpp +++ b/src/modules/m_spanningtree/override_map.cpp @@ -1,6 +1,7 @@ /* * InspIRCd -- Internet Relay Chat Daemon * + * Copyright (C) 2014 Adam * Copyright (C) 2009 Daniel De Graaf * Copyright (C) 2007-2008 Craig Edwards * Copyright (C) 2008 Robin Burchell @@ -32,75 +33,136 @@ CommandMap::CommandMap(Module* Creator) Penalty = 2; } -std::string CommandMap::MapOperInfo(TreeServer* Current) +static inline bool IsHidden(User* user, TreeServer* server) { - time_t secs_up = ServerInstance->Time() - Current->age; - return " [Up: " + ModuleSpanningTree::TimeToStr(secs_up) + (Current->rtt == 0 ? "]" : " Lag: " + ConvToStr(Current->rtt) + "ms]"); + if (!user->IsOper()) + { + if (server->Hidden) + return true; + if (Utils->HideULines && server->IsULine()) + return true; + } + + return false; } -void CommandMap::ShowMap(TreeServer* Current, User* user, int depth, int &line, char* names, int &maxnamew, char* stats) +// Calculate the map depth the servers go, and the longest server name +static void GetDepthAndLen(TreeServer* current, unsigned int depth, unsigned int& max_depth, unsigned int& max_len) { - ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "ShowMap depth %d on line %d", depth, line); - float percent; + if (depth > max_depth) + max_depth = depth; + if (current->GetName().length() > max_len) + max_len = current->GetName().length(); - if (ServerInstance->Users->clientlist->size() == 0) + const TreeServer::ChildServers& servers = current->GetChildren(); + for (TreeServer::ChildServers::const_iterator i = servers.begin(); i != servers.end(); ++i) + { + TreeServer* child = *i; + GetDepthAndLen(child, depth + 1, max_depth, max_len); + } +} + +static std::vector GetMap(User* user, TreeServer* current, unsigned int max_len, unsigned int depth) +{ + float percent = 0; + + if (!ServerInstance->Users->clientlist->empty()) { // If there are no users, WHO THE HELL DID THE /MAP?!?!?! - percent = 0; + percent = current->UserCount * 100.0 / ServerInstance->Users->clientlist->size(); } - else + + std::string buffer = current->GetName(); + if (user->IsOper()) { - percent = Current->UserCount * 100.0 / ServerInstance->Users->clientlist->size(); + buffer += " (" + current->GetID() + ")"; } - const std::string operdata = user->IsOper() ? MapOperInfo(Current) : ""; + // Pad with spaces until its at max len, max_len must always be >= my names length + buffer.append(max_len - current->GetName().length(), ' '); - char* myname = names + 100 * line; - char* mystat = stats + 50 * line; - memset(myname, ' ', depth); - int w = depth; + char buf[16]; + snprintf(buf, sizeof(buf), "%5d [%5.2f%%]", current->UserCount, percent); + buffer += buf; if (user->IsOper()) { - w += snprintf(myname + depth, 99 - depth, "%s (%s)", Current->GetName().c_str(), Current->GetID().c_str()); - } - else - { - w += snprintf(myname + depth, 99 - depth, "%s", Current->GetName().c_str()); + time_t secs_up = ServerInstance->Time() - current->age; + buffer += " [Up: " + ModuleSpanningTree::TimeToStr(secs_up) + (current->rtt == 0 ? "]" : " Lag: " + ConvToStr(current->rtt) + "ms]"); } - memset(myname + w, ' ', 100 - w); - if (w > maxnamew) - maxnamew = w; - snprintf(mystat, 49, "%5d [%5.2f%%]%s", Current->UserCount, percent, operdata.c_str()); - - line++; - if (user->IsOper() || !Utils->FlatLinks) - depth = depth + 2; + std::vector map; + map.push_back(buffer); - const TreeServer::ChildServers& servers = Current->GetChildren(); + const TreeServer::ChildServers& servers = current->GetChildren(); for (TreeServer::ChildServers::const_iterator i = servers.begin(); i != servers.end(); ++i) { TreeServer* child = *i; - if (!user->IsOper()) { - if (child->Hidden) - continue; - if ((Utils->HideULines) && (child->IsULine())) - continue; + + if (IsHidden(user, child)) + continue; + + bool last = true; + for (TreeServer::ChildServers::const_iterator j = i + 1; last && j != servers.end(); ++j) + if (!IsHidden(user, *j)) + last = false; + + unsigned int next_len; + + if (user->IsOper() || !Utils->FlatLinks) + { + // This child is indented by us, so remove the depth from the max length to align the users properly + next_len = max_len - 2; + } + else + { + // This user can not see depth, so max_len remains constant + next_len = max_len; + } + + // Build the map for this child + std::vector child_map = GetMap(user, child, next_len, depth + 1); + + for (std::vector::const_iterator j = child_map.begin(); j != child_map.end(); ++j) + { + const char* prefix; + + if (user->IsOper() || !Utils->FlatLinks) + { + // If this server is not the root child + if (j != child_map.begin()) + { + // If this child is not my last child, then add | + // to be able to "link" the next server in my list to me, and to indent this childs servers + if (!last) + prefix = "| "; + // Otherwise this is my last child, so just use a space as theres nothing else linked to me below this + else + prefix = " "; + } + // If we get here, this server must be the root child + else + { + // If this is the last child, it gets a `- + if (last) + prefix = "`-"; + // Otherwise this isn't the last child, so it gets |- + else + prefix = "|-"; + } + } + else + // User can't see depth, so use no prefix + prefix = ""; + + // Add line to the map + map.push_back(prefix + *j); } - ShowMap(child, user, depth, line, names, maxnamew, stats); } -} + return map; +} -// Ok, prepare to be confused. -// After much mulling over how to approach this, it struck me that -// the 'usual' way of doing a /MAP isnt the best way. Instead of -// keeping track of a ton of ascii characters, and line by line -// under recursion working out where to place them using multiplications -// and divisons, we instead render the map onto a backplane of characters -// (a character matrix), then draw the branches as a series of "L" shapes -// from the nodes. This is not only friendlier on CPU it uses less stack. CmdResult CommandMap::Handle(const std::vector& parameters, User* user) { if (parameters.size() > 0) @@ -117,71 +179,37 @@ CmdResult CommandMap::Handle(const std::vector& parameters, User* u return CMD_SUCCESS; } - // These arrays represent a virtual screen which we will - // "scratch" draw to, as the console device of an irc - // client does not provide for a proper terminal. - int totusers = ServerInstance->Users->clientlist->size(); - int totservers = Utils->serverlist.size(); - int maxnamew = 0; - int line = 0; - char* names = new char[totservers * 100]; - char* stats = new char[totservers * 50]; - - // The only recursive bit is called here. - ShowMap(Utils->TreeRoot,user,0,line,names,maxnamew,stats); - - // Process each line one by one. - for (int l = 1; l < line; l++) - { - char* myname = names + 100 * l; - // scan across the line looking for the start of the - // servername (the recursive part of the algorithm has placed - // the servers at indented positions depending on what they - // are related to) - int first_nonspace = 0; - - while (myname[first_nonspace] == ' ') - { - first_nonspace++; - } - - first_nonspace--; - - // Draw the `- (corner) section: this may be overwritten by - // another L shape passing along the same vertical pane, becoming - // a |- (branch) section instead. + // Max depth and max server name length + unsigned int max_depth = 0; + unsigned int max_len = 0; + GetDepthAndLen(Utils->TreeRoot, 0, max_depth, max_len); - myname[first_nonspace] = '-'; - myname[first_nonspace-1] = '`'; - int l2 = l - 1; - - // Draw upwards until we hit the parent server, causing possibly - // other corners (`-) to become branches (|-) - while ((names[l2 * 100 + first_nonspace-1] == ' ') || (names[l2 * 100 + first_nonspace-1] == '`')) - { - names[l2 * 100 + first_nonspace-1] = '|'; - l2--; - } + unsigned int max; + if (user->IsOper() || !Utils->FlatLinks) + { + // Each level of the map is indented by 2 characters, making the max possible line (max_depth * 2) + max_len + max = (max_depth * 2) + max_len; } - - float avg_users = totusers * 1.0 / line; - - for (int t = 0; t < line; t++) + else { - // terminate the string at maxnamew characters - names[100 * t + maxnamew] = '\0'; - user->SendText(":%s %03d %s :%s %s", ServerInstance->Config->ServerName.c_str(), - RPL_MAP, user->nick.c_str(), names + 100 * t, stats + 50 * t); + // This user can't see any depth + max = max_len; } - user->SendText(":%s %03d %s :%d server%s and %d user%s, average %.2f users per server", + + std::vector map = GetMap(user, Utils->TreeRoot, max, 0); + for (std::vector::const_iterator i = map.begin(); i != map.end(); ++i) + user->SendText(":%s %03d %s :%s", ServerInstance->Config->ServerName.c_str(), + RPL_MAP, user->nick.c_str(), i->c_str()); + + size_t totusers = ServerInstance->Users->clientlist->size(); + float avg_users = (float) totusers / Utils->serverlist.size(); + + user->SendText(":%s %03d %s :%u server%s and %u user%s, average %.2f users per server", ServerInstance->Config->ServerName.c_str(), RPL_MAPUSERS, user->nick.c_str(), - line, (line > 1 ? "s" : ""), totusers, (totusers > 1 ? "s" : ""), avg_users); + (unsigned int)Utils->serverlist.size(), (Utils->serverlist.size() > 1 ? "s" : ""), (unsigned int)totusers, (totusers > 1 ? "s" : ""), avg_users); user->SendText(":%s %03d %s :End of /MAP", ServerInstance->Config->ServerName.c_str(), RPL_ENDMAP, user->nick.c_str()); - delete[] names; - delete[] stats; - return CMD_SUCCESS; }