Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.7.0)
cmake_minimum_required(VERSION 3.16)

project(AttorneyOnline VERSION 2.11.0.0 LANGUAGES CXX C)

Expand All @@ -14,6 +14,13 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
option(AO_BUILD_TESTS "Build test programs" ON)
option(AO_ENABLE_DISCORD_RPC "Enable Discord Rich Presence" ON)

# Override WrapOpenGL to not require deprecated AGL framework on macOS
if(APPLE AND NOT TARGET WrapOpenGL::WrapOpenGL)
add_library(WrapOpenGL::WrapOpenGL INTERFACE IMPORTED)
target_link_libraries(WrapOpenGL::WrapOpenGL INTERFACE "-framework OpenGL")
set(WrapOpenGL_FOUND ON)
endif()

find_package(QT NAMES Qt6)
find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Widgets Concurrent WebSockets UiTools)

Expand Down Expand Up @@ -91,6 +98,8 @@ qt_add_executable(Attorney_Online
src/serverdata.cpp
src/serverdata.h
src/text_file_functions.cpp
src/webcache.cpp
src/webcache.h
src/widgets/aooptionsdialog.cpp
src/widgets/aooptionsdialog.h
src/widgets/direct_connect_dialog.cpp
Expand Down
93 changes: 93 additions & 0 deletions data/ui/options_dialog.ui
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,99 @@ Use this when you have added an asset that takes precedence over another existin
</item>
</layout>
</item>
<item>
<widget class="QFrame" name="webcache_divider">
<property name="frameShape">
<enum>QFrame::HLine</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="webcache_header_lbl">
<property name="text">
<string>Web Cache</string>
</property>
<property name="font">
<font>
<bold>true</bold>
</font>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="webcache_form_layout">
<item row="0" column="0">
<widget class="QLabel" name="webcache_enabled_lbl">
<property name="toolTip">
<string>If enabled, assets not found locally will be automatically downloaded from the server's asset URL and cached for future use.</string>
</property>
<property name="text">
<string>Enable Web Cache:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="webcache_enabled_cb">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="webcache_expiry_lbl">
<property name="toolTip">
<string>How long cached files remain valid before being re-downloaded. Set to a higher value to reduce bandwidth usage.</string>
</property>
<property name="text">
<string>Cache Expiry:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="webcache_expiry_spinbox">
<property name="suffix">
<string> hours</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>720</number>
</property>
<property name="value">
<number>24</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="webcache_size_label_lbl">
<property name="text">
<string>Cache Size:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="webcache_size_label">
<property name="text">
<string>0 MB</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QPushButton" name="webcache_clear">
<property name="toolTip">
<string>Deletes all cached files downloaded from servers.</string>
</property>
<property name="text">
<string>Clear Web Cache</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="logging_tab">
Expand Down
22 changes: 21 additions & 1 deletion src/animationlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "aoapplication.h"
#include "options.h"
#include "webcache.h"

#include <QRectF>
#include <QThreadPool>
Expand Down Expand Up @@ -452,12 +453,23 @@ void CharacterAnimationLayer::loadCharacterEmote(QString character, QString file
}

int index = -1;
int non_placeholder_count = placeholder_fallback ? path_list.size() - 2 : path_list.size();
QString file_path = ao_app->get_image_path(path_list, index);
if (index != -1)
{
m_resolved_emote = prefixed_emote_list[index];
}

// Trigger webcache download if actual character emote not found (fell back to placeholder or not found at all)
if ((index == -1 || index >= non_placeholder_count) && Options::getInstance().webcacheEnabled())
{
static const QStringList image_suffixes{".webp", ".apng", ".gif", ".png"};
for (int i = 0; i < non_placeholder_count; ++i)
{
ao_app->webcache()->resolveOrDownload(path_list[i].toQString(), image_suffixes);
}
}

setFileName(file_path);
setPlayOnce(play_once);
setResizeMode(ao_app->get_scaling(ao_app->get_emote_property(character, fileName, "scaling")));
Expand Down Expand Up @@ -589,7 +601,15 @@ BackgroundAnimationLayer::BackgroundAnimationLayer(AOApplication *ao_app, QWidge

void BackgroundAnimationLayer::loadAndPlayAnimation(QString fileName)
{
QString file_path = ao_app->get_image_suffix(ao_app->get_background_path(fileName));
VPath vpath = ao_app->get_background_path(fileName);
QString file_path = ao_app->get_image_suffix(vpath);

// Trigger webcache download if file not found locally
if (file_path.isEmpty() && Options::getInstance().webcacheEnabled())
{
ao_app->webcache()->resolveOrDownload(vpath.toQString(), {".webp", ".apng", ".gif", ".png"});
}

#ifdef DEBUG_MOVIE
if (file_path.isEmpty())
{
Expand Down
7 changes: 7 additions & 0 deletions src/aoapplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "lobby.h"
#include "networkmanager.h"
#include "options.h"
#include "webcache.h"
#include "widgets/aooptionsdialog.h"

static QtMessageHandler original_message_handler;
Expand All @@ -21,6 +22,7 @@ AOApplication::AOApplication(QObject *parent)
{
net_manager = new NetworkManager(this);
discord = new AttorneyOnline::Discord();
m_webcache = new WebCache(this);

asset_lookup_cache.reserve(2048);

Expand All @@ -36,6 +38,11 @@ AOApplication::~AOApplication()
qInstallMessageHandler(original_message_handler);
}

WebCache *AOApplication::webcache() const
{
return m_webcache;
}

bool AOApplication::is_lobby_constructed()
{
return w_lobby;
Expand Down
6 changes: 6 additions & 0 deletions src/aoapplication.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class NetworkManager;
class Lobby;
class Courtroom;
class Options;
class WebCache;

class VPath : QString
{
Expand Down Expand Up @@ -60,7 +61,12 @@ class AOApplication : public QObject
Lobby *w_lobby = nullptr;
Courtroom *w_courtroom = nullptr;
AttorneyOnline::Discord *discord;
WebCache *webcache() const;

private:
WebCache *m_webcache = nullptr;

public:
QFont default_font;

bool is_lobby_constructed();
Expand Down
15 changes: 13 additions & 2 deletions src/aocharbutton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,18 @@ void AOCharButton::setTaken(bool enabled)

void AOCharButton::setCharacter(QString character)
{
QString image_path = ao_app->get_image_suffix(ao_app->get_character_path(character, "char_icon"), true);
m_character = character;
refreshIcon();
}

QString AOCharButton::character() const
{
return m_character;
}

void AOCharButton::refreshIcon()
{
QString image_path = ao_app->get_image_suffix(ao_app->get_character_path(m_character, "char_icon"), true);

setText(QString());

Expand All @@ -55,7 +66,7 @@ void AOCharButton::setCharacter(QString character)
setStyleSheet("QPushButton { border-image: url(); }"
"QToolTip { background-image: url(); color: #000000; "
"background-color: #ffffff; border: 0px; }");
setText(character);
setText(m_character);
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/aocharbutton.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class AOCharButton : public QPushButton

void setCharacter(QString character);

QString character() const;

void refreshIcon();

void setTaken(bool enabled);

protected:
Expand All @@ -30,6 +34,7 @@ class AOCharButton : public QPushButton

private:
AOApplication *ao_app;
QString m_character;
bool m_taken = false;
AOImage *ui_taken;
AOImage *ui_selector;
Expand Down
11 changes: 10 additions & 1 deletion src/aomusicplayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "file_functions.h"
#include "options.h"
#include "webcache.h"

#include <bass.h>

Expand Down Expand Up @@ -50,7 +51,15 @@ QString AOMusicPlayer::playStream(QString song, int streamId, bool loopEnabled,
{
flags |= BASS_STREAM_PRESCAN | BASS_UNICODE | BASS_ASYNCFILE;

f_path = ao_app->get_real_path(ao_app->get_music_path(song));
VPath vpath = ao_app->get_music_path(song);
f_path = ao_app->get_real_path(vpath);

// Trigger webcache download if file not found locally
if (f_path.isEmpty() && Options::getInstance().webcacheEnabled())
{
ao_app->webcache()->resolveOrDownload(vpath.toQString(), {".opus", ".ogg", ".mp3", ".wav"});
}

newstream = BASS_StreamCreateFile(FALSE, f_path.utf16(), 0, 0, flags);
}

Expand Down
38 changes: 38 additions & 0 deletions src/courtroom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "datatypes.h"
#include "moderation_functions.h"
#include "options.h"
#include "webcache.h"

#include <QtConcurrent/QtConcurrent>

Expand Down Expand Up @@ -137,6 +138,7 @@ Courtroom::Courtroom(AOApplication *p_ao_app)
ui_debug_log->hide();
ui_debug_log->setObjectName("ui_debug_log");
connect(ao_app, &AOApplication::qt_log_message, this, &Courtroom::debug_message_handler);
connect(ao_app->webcache(), &WebCache::fileDownloaded, this, &Courtroom::on_webcache_file_downloaded);

ui_server_chatlog = new AOTextArea(this);
ui_server_chatlog->setReadOnly(true);
Expand Down Expand Up @@ -6723,3 +6725,39 @@ void Courtroom::truncate_label_text(QWidget *p_widget, QString p_identifier)
}
qDebug().nospace() << "Truncated label text from " << label_text_tr << " (" << label_px_width << "px) to " << truncated_label << " (" << truncated_px_width << "px)";
}

void Courtroom::on_webcache_file_downloaded(const QString &relativePath)
{
// Check if this is a character icon and refresh the relevant button
if (relativePath.startsWith("characters/") && relativePath.endsWith("char_icon.png"))
{
// Extract character name from path: "characters/name/char_icon.png" -> "name"
QString charPath = relativePath.mid(11); // Remove "characters/"
int slashPos = charPath.indexOf('/');
if (slashPos > 0)
{
QString charName = charPath.left(slashPos);

// Find and refresh the button for this character (case-insensitive match)
for (AOCharButton *button : std::as_const(ui_char_button_list))
{
if (button->character().toLower() == charName)
{
button->refreshIcon();
break;
}
}

// Also update the tree widget icon
QList<QTreeWidgetItem *> items = ui_char_list->findItems(charName, Qt::MatchFixedString | Qt::MatchRecursive, 0);
for (QTreeWidgetItem *item : items)
{
QString iconPath = ao_app->get_image_suffix(ao_app->get_character_path(item->text(0), "char_icon"), true);
if (!iconPath.isEmpty())
{
item->setIcon(0, QIcon(iconPath));
}
}
}
}
}
2 changes: 2 additions & 0 deletions src/courtroom.h
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,8 @@ private Q_SLOTS:

void chat_tick();

void on_webcache_file_downloaded(const QString &relativePath);

void on_mute_list_clicked(QModelIndex p_index);
void on_pair_list_clicked(QModelIndex p_index);

Expand Down
20 changes: 20 additions & 0 deletions src/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -794,3 +794,23 @@ void Options::setRestoreWindowPositionEnabled(bool state)
{
config.setValue("windows/restore", state);
}

bool Options::webcacheEnabled() const
{
return config.value("webcache_enabled", true).toBool();
}

void Options::setWebcacheEnabled(bool value)
{
config.setValue("webcache_enabled", value);
}

int Options::webcacheExpiryHours() const
{
return config.value("webcache_expiry_hours", 24).toInt();
}

void Options::setWebcacheExpiryHours(int hours)
{
config.setValue("webcache_expiry_hours", hours);
}
Loading
Loading