Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Please make Client remember the Avatar #4735

Open
0lhi opened this issue Feb 6, 2021 · 11 comments
Open

Feature Request: Please make Client remember the Avatar #4735

0lhi opened this issue Feb 6, 2021 · 11 comments
Labels
client feature-request This issue or PR deals with a new feature good first issue Good for first-time contributors

Comments

@0lhi
Copy link

0lhi commented Feb 6, 2021

I'd like to request to make the Client remember the Avatar and automatically upload to every server that it's connecting to.

Right now, users who use the Overlay have the choice between manually uploading the Avatar on every reconnect, or making an Account on every server they use, neither of which is particularly appealing.

@Krzmbrzl
Copy link
Member

Krzmbrzl commented Feb 6, 2021

What Avatar? And how do you upload it? And where does it get displayed? 👀

@Kissaki
Copy link
Member

Kissaki commented Feb 6, 2021

menu Self -> Change Avatar

It’s bound to the user account, or connection if not registered.

@Krzmbrzl
Copy link
Member

Krzmbrzl commented Feb 6, 2021

This Avatar is only used in the Overlay though, correct?

@Krzmbrzl Krzmbrzl added client feature-request This issue or PR deals with a new feature good first issue Good for first-time contributors and removed needs-more-input labels Feb 6, 2021
@Kissaki
Copy link
Member

Kissaki commented Feb 6, 2021

When you mouse hover over users you can see the avatar. When you click the info/comment icon you can see it too.

@gbidkar
Copy link

gbidkar commented Feb 14, 2021

But what if I want a different avatar on different servers? Maybe it should be having a 'main avatar' which is the default one that will be displayed on each server like the original issue describes and a per-server avatar.

@colin-mcl
Copy link

Hi! I am looking to contribute my help to this project and thought that this issue looks around my skill level. I know C++ well but have never used Qt, is this a good place for me to start?

@Krzmbrzl
Copy link
Member

@colin-mcl yes, I think this should be easily doable for you. My guess is that the biggest part of the required changes won't require Qt anyway and the UI side of this seems to be relatively straight forward as well - so probably a good start to get in touch with Qt :)

Do you want me to look up some relevant code parts that should get you started understanding the current logic of the code?

@colin-mcl
Copy link

That would be fantastic, thank you!

@Krzmbrzl
Copy link
Member

Alright - I am unfamiliar with the precise workings of the avatar feature in Mumble (as you see above, I was unaware of this feature prior to this request) but I can pick out some parts, that I think are relevant nonetheless. Just be aware that there might be other parts that are important for this, that I don't list here.

  • The current UI integration happens via the actions qaServerTexture (Self > Change Avatar) and qaServerTextureRemove (Self > Remove Avatar) in MainWindow.cpp
  • Thus, the relevant code that is triggered by clicking any of these actions is
    void MainWindow::on_qaServerTexture_triggered() {
    QPair< QByteArray, QImage > choice = openImageFile();
    if (choice.first.isEmpty())
    return;
    const QImage &img = choice.second;
    if ((img.height() <= 1024) && (img.width() <= 1024))
    Global::get().sh->setUserTexture(Global::get().uiSession, choice.first);
    }

    and
    void MainWindow::on_qaServerTextureRemove_triggered() {
    Global::get().sh->setUserTexture(Global::get().uiSession, QByteArray());
    }
  • As you see, both of these call
    void ServerHandler::setUserTexture(unsigned int uiSession, const QByteArray &qba) {
    QByteArray texture;
    if ((uiVersion >= 0x010202) || qba.isEmpty()) {
    texture = qba;
    } else {
    QByteArray raw = qba;
    QBuffer qb(&raw);
    qb.open(QIODevice::ReadOnly);
    QImageReader qir;
    qir.setDecideFormatFromContent(false);
    QByteArray fmt;
    if (!RichTextImage::isValidImage(qba, fmt)) {
    return;
    }
    qir.setFormat(fmt);
    qir.setDevice(&qb);
    QSize sz = qir.size();
    const int TEX_MAX_WIDTH = 600;
    const int TEX_MAX_HEIGHT = 60;
    const int TEX_RGBA_SIZE = TEX_MAX_WIDTH * TEX_MAX_HEIGHT * 4;
    sz.scale(TEX_MAX_WIDTH, TEX_MAX_HEIGHT, Qt::KeepAspectRatio);
    qir.setScaledSize(sz);
    QImage tex = qir.read();
    if (tex.isNull()) {
    return;
    }
    raw = QByteArray(TEX_RGBA_SIZE, 0);
    QImage img(reinterpret_cast< unsigned char * >(raw.data()), TEX_MAX_WIDTH, TEX_MAX_HEIGHT,
    QImage::Format_ARGB32);
    QPainter imgp(&img);
    imgp.setRenderHint(QPainter::Antialiasing);
    imgp.setRenderHint(QPainter::TextAntialiasing);
    imgp.setCompositionMode(QPainter::CompositionMode_SourceOver);
    imgp.drawImage(0, 0, tex);
    texture = qCompress(QByteArray(reinterpret_cast< const char * >(img.bits()), TEX_RGBA_SIZE));
    }
    MumbleProto::UserState mpus;
    mpus.set_session(uiSession);
    mpus.set_texture(blob(texture));
    sendMessage(mpus);
    if (!texture.isEmpty()) {
    database->setBlob(sha1(texture), texture);
    }
    }
  • The server-side handling happens inside the msgUserState function at
    if (msg.has_texture()) {
    if (iMaxImageMessageLength > 0
    && (msg.texture().length() > static_cast< unsigned int >(iMaxImageMessageLength))) {
    PERM_DENIED_TYPE(TextTooLong);
    return;
    }
    if (uSource != pDstServerUser) {
    if (!hasPermission(uSource, root, ChanACL::ResetUserContent)) {
    PERM_DENIED(uSource, root, ChanACL::ResetUserContent);
    return;
    }
    if (msg.texture().length() > 0) {
    PERM_DENIED_TYPE(TextTooLong);
    return;
    }
    }
    }

    and
    if (msg.has_texture()) {
    QByteArray qba = blob(msg.texture());
    if (pDstServerUser->iId > 0) {
    // For registered users store the texture we just received in the database
    if (!setTexture(pDstServerUser->iId, qba)) {
    return;
    }
    } else {
    // For unregistered users or SuperUser only get the hash
    hashAssign(pDstServerUser->qbaTexture, pDstServerUser->qbaTextureHash, qba);
    }
    // The texture will be sent out later in this function
    bBroadcast = true;
    }

Though, I think you don't need to make any server-side changes in order to implement this feature request 👀

@colin-mcl
Copy link

@Krzmbrzl This is great thank you so much. I think I understand the request flow that occurs when a User sets their avatar based on these code snippets and my own exploration. It seems that the simplest solution is to remember the user's default avatar once it is set by them. I'm a little unclear on how the UserState variables are recorded so I am going to dig through the code and try to get an understanding of that so that I could implement this solution. If I need any more help I will reach out to you!

@Krzmbrzl
Copy link
Member

It seems that the simplest solution is to remember the user's default avatar once it is set by them.

This would then have to be per-server though as we can't assume that a user wants to use the same avatar across different servers.
In addition to that, I think we could add a setting to set up a default avatar, which will be used when connecting to a server we don't have a server-specific avatar stored yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
client feature-request This issue or PR deals with a new feature good first issue Good for first-time contributors
Projects
None yet
Development

No branches or pull requests

5 participants