Lightly reworked NetworkManager, ...

* Lightly reworked `NetworkManager`
* Added new modules to handle various connection types.
  * TCP
  * WebSocket
* Added general string splitter alias based on Qt version.
* Replaced `lobby_constructed` and `courtroom_constructed`
* Refactored and partially reimplemented the following classes:
  * `AOBlipPlayer`
  * `AOEmotePreview`
  * `AOMusicPlayer`
  * `AOSfxPlayer`
  * `AOTextArea`
This commit is contained in:
TrickyLeifa 2024-05-17 16:39:30 +02:00
parent 39e4354b1d
commit 1ef96383c8
34 changed files with 945 additions and 666 deletions

View File

@ -77,6 +77,12 @@ add_executable(Attorney_Online
src/lobby.cpp src/lobby.cpp
src/lobby.h src/lobby.h
src/main.cpp src/main.cpp
src/net/netconnection.cpp
src/net/netconnection.h
src/net/nettcpconnection.cpp
src/net/nettcpconnection.h
src/net/netwebsocketconnection.cpp
src/net/netwebsocketconnection.h
src/networkmanager.cpp src/networkmanager.cpp
src/networkmanager.h src/networkmanager.h
src/options.cpp src/options.cpp

View File

@ -40,16 +40,20 @@ AOApplication::~AOApplication()
qInstallMessageHandler(original_message_handler); qInstallMessageHandler(original_message_handler);
} }
bool AOApplication::is_lobby_constructed()
{
return w_lobby;
}
void AOApplication::construct_lobby() void AOApplication::construct_lobby()
{ {
if (lobby_constructed) if (is_lobby_constructed())
{ {
qWarning() << "lobby was attempted constructed when it already exists"; qWarning() << "lobby was attempted constructed when it already exists";
return; return;
} }
w_lobby = new Lobby(this, net_manager); w_lobby = new Lobby(this, net_manager);
lobby_constructed = true;
QRect geometry = QGuiApplication::primaryScreen()->geometry(); QRect geometry = QGuiApplication::primaryScreen()->geometry();
int x = (geometry.width() - w_lobby->width()) / 2; int x = (geometry.width() - w_lobby->width()) / 2;
@ -71,7 +75,7 @@ void AOApplication::construct_lobby()
void AOApplication::destruct_lobby() void AOApplication::destruct_lobby()
{ {
if (!lobby_constructed) if (!is_lobby_constructed())
{ {
qWarning() << "lobby was attempted destructed when it did not exist"; qWarning() << "lobby was attempted destructed when it did not exist";
return; return;
@ -79,19 +83,22 @@ void AOApplication::destruct_lobby()
delete w_lobby; delete w_lobby;
w_lobby = nullptr; w_lobby = nullptr;
lobby_constructed = false; }
bool AOApplication::is_courtroom_constructed()
{
return w_courtroom;
} }
void AOApplication::construct_courtroom() void AOApplication::construct_courtroom()
{ {
if (courtroom_constructed) if (is_courtroom_constructed())
{ {
qWarning() << "courtroom was attempted constructed when it already exists"; qWarning() << "courtroom was attempted constructed when it already exists";
return; return;
} }
w_courtroom = new Courtroom(this); w_courtroom = new Courtroom(this);
courtroom_constructed = true;
QRect geometry = QGuiApplication::primaryScreen()->geometry(); QRect geometry = QGuiApplication::primaryScreen()->geometry();
int x = (geometry.width() - w_courtroom->width()) / 2; int x = (geometry.width() - w_courtroom->width()) / 2;
@ -110,7 +117,7 @@ void AOApplication::construct_courtroom()
void AOApplication::destruct_courtroom() void AOApplication::destruct_courtroom()
{ {
if (!courtroom_constructed) if (!is_courtroom_constructed())
{ {
qWarning() << "courtroom was attempted destructed when it did not exist"; qWarning() << "courtroom was attempted destructed when it did not exist";
return; return;
@ -118,7 +125,6 @@ void AOApplication::destruct_courtroom()
delete w_courtroom; delete w_courtroom;
w_courtroom = nullptr; w_courtroom = nullptr;
courtroom_constructed = false;
} }
QString AOApplication::get_version_string() QString AOApplication::get_version_string()
@ -128,9 +134,12 @@ QString AOApplication::get_version_string()
void AOApplication::server_disconnected() void AOApplication::server_disconnected()
{ {
if (courtroom_constructed) if (is_courtroom_constructed())
{ {
call_notice(tr("Disconnected from server.")); if (w_courtroom->isVisible())
{
call_notice(tr("Disconnected from server."));
}
construct_lobby(); construct_lobby();
destruct_courtroom(); destruct_courtroom();
} }
@ -145,12 +154,12 @@ void AOApplication::loading_cancelled()
void AOApplication::call_settings_menu() void AOApplication::call_settings_menu()
{ {
AOOptionsDialog *l_dialog = new AOOptionsDialog(this); AOOptionsDialog *l_dialog = new AOOptionsDialog(this);
if (courtroom_constructed) if (is_courtroom_constructed())
{ {
connect(l_dialog, &AOOptionsDialog::reloadThemeRequest, w_courtroom, &Courtroom::on_reload_theme_clicked); connect(l_dialog, &AOOptionsDialog::reloadThemeRequest, w_courtroom, &Courtroom::on_reload_theme_clicked);
} }
if (lobby_constructed) if (is_lobby_constructed())
{} {}
l_dialog->exec(); l_dialog->exec();
delete l_dialog; delete l_dialog;

View File

@ -61,18 +61,17 @@ public:
~AOApplication(); ~AOApplication();
NetworkManager *net_manager; NetworkManager *net_manager;
Lobby *w_lobby; Lobby *w_lobby = nullptr;
Courtroom *w_courtroom; Courtroom *w_courtroom = nullptr;
AttorneyOnline::Discord *discord; AttorneyOnline::Discord *discord;
QFont default_font; QFont default_font;
bool lobby_constructed = false; bool is_lobby_constructed();
bool courtroom_constructed = false;
void construct_lobby(); void construct_lobby();
void destruct_lobby(); void destruct_lobby();
bool is_courtroom_constructed();
void construct_courtroom(); void construct_courtroom();
void destruct_courtroom(); void destruct_courtroom();

View File

@ -1,64 +1,54 @@
#include "aoblipplayer.h" #include "aoblipplayer.h"
AOBlipPlayer::AOBlipPlayer(AOApplication *p_ao_app) AOBlipPlayer::AOBlipPlayer(AOApplication *ao_app)
: ao_app(p_ao_app) : ao_app(ao_app)
{} {}
void AOBlipPlayer::set_blips(QString p_sfx) void AOBlipPlayer::setVolume(int value)
{ {
QString f_path = ao_app->get_sfx_suffix(ao_app->get_sounds_path(p_sfx)); m_volume = value;
updateInternalVolume();
}
for (int n_stream = 0; n_stream < 5; ++n_stream) void AOBlipPlayer::setMuted(bool enabled)
{
m_muted = enabled;
updateInternalVolume();
}
void AOBlipPlayer::setBlip(QString blip)
{
QString path = ao_app->get_sfx_suffix(ao_app->get_sounds_path(blip));
for (int i = 0; i < STREAM_COUNT; ++i)
{ {
BASS_StreamFree(m_stream_list[n_stream]); BASS_StreamFree(m_stream[i]);
if (f_path.endsWith(".opus")) if (path.endsWith(".opus"))
{ {
m_stream_list[n_stream] = BASS_OPUS_StreamCreateFile(FALSE, f_path.utf16(), 0, 0, BASS_UNICODE | BASS_ASYNCFILE); m_stream[i] = BASS_OPUS_StreamCreateFile(FALSE, path.utf16(), 0, 0, BASS_UNICODE | BASS_ASYNCFILE);
} }
else else
{ {
m_stream_list[n_stream] = BASS_StreamCreateFile(FALSE, f_path.utf16(), 0, 0, BASS_UNICODE | BASS_ASYNCFILE); m_stream[i] = BASS_StreamCreateFile(FALSE, path.utf16(), 0, 0, BASS_UNICODE | BASS_ASYNCFILE);
} }
} }
set_volume_internal(m_volume); updateInternalVolume();
} }
void AOBlipPlayer::blip_tick() void AOBlipPlayer::playBlip()
{ {
int f_cycle = m_cycle++; HSTREAM stream = m_stream[m_cycle];
BASS_ChannelSetDevice(stream, BASS_GetDevice());
BASS_ChannelPlay(stream, false);
m_cycle = ++m_cycle % STREAM_COUNT;
}
if (m_cycle == 5) void AOBlipPlayer::updateInternalVolume()
{
float volume = m_muted ? 0.0f : (m_volume * 0.01);
for (int i = 0; i < STREAM_COUNT; ++i)
{ {
m_cycle = 0; BASS_ChannelSetAttribute(m_stream[i], BASS_ATTRIB_VOL, volume);
}
HSTREAM f_stream = m_stream_list[f_cycle];
BASS_ChannelSetDevice(f_stream, BASS_GetDevice());
BASS_ChannelPlay(f_stream, false);
}
void AOBlipPlayer::set_muted(bool toggle)
{
m_muted = toggle;
set_volume_internal(m_volume);
}
void AOBlipPlayer::set_volume(int p_value)
{
m_volume = static_cast<qreal>(p_value) / 100;
set_volume_internal(m_volume);
}
void AOBlipPlayer::set_volume_internal(qreal p_value)
{
// If muted, volume will always be 0
float volume = static_cast<float>(p_value) * !m_muted;
for (int n_stream = 0; n_stream < 5; ++n_stream)
{
BASS_ChannelSetAttribute(m_stream_list[n_stream], BASS_ATTRIB_VOL, volume);
} }
} }

View File

@ -13,19 +13,24 @@
class AOBlipPlayer class AOBlipPlayer
{ {
public: public:
AOBlipPlayer(AOApplication *p_ao_app); static constexpr int STREAM_COUNT = 5;
void set_blips(QString p_sfx); AOBlipPlayer(AOApplication *ao_app);
void blip_tick();
void set_volume(int p_volume); void setVolume(int value);
void set_muted(bool toggle); void setMuted(bool enabled);
void setBlip(QString blip);
void playBlip();
private: private:
AOApplication *ao_app; AOApplication *ao_app;
qreal m_volume = 0.0;
int m_cycle = 0;
bool m_muted = false;
HSTREAM m_stream_list[5];
void set_volume_internal(qreal p_volume); int m_volume = 0;
bool m_muted = false;
HSTREAM m_stream[STREAM_COUNT]{};
int m_cycle = 0;
void updateInternalVolume();
}; };

View File

@ -1,44 +1,45 @@
#include "aoemotepreview.h" #include "aoemotepreview.h"
AOEmotePreview::AOEmotePreview(AOApplication *p_ao_app, QWidget *parent) AOEmotePreview::AOEmotePreview(AOApplication *ao_app, QWidget *parent)
: QWidget(parent) : QWidget(parent)
, ao_app(p_ao_app) , ao_app(ao_app)
{ {
setWindowFlag(Qt::Tool);
setWindowFlag(Qt::WindowMinMaxButtonsHint, false);
ui_viewport = new QWidget(this); ui_viewport = new QWidget(this);
ui_vp_player_char = new CharLayer(ao_app, ui_viewport); ui_vp_player_char = new CharLayer(ao_app, ui_viewport);
ui_vp_player_char->setObjectName("ui_vp_player_char"); ui_vp_player_char->setObjectName("ui_vp_player_char");
ui_vp_player_char->masked = false; ui_vp_player_char->masked = false;
ui_size_label = new QLabel(this); ui_size_label = new QLabel(this);
ui_size_label->setObjectName("ui_size_label"); ui_size_label->setObjectName("ui_size_label");
setWindowFlag(Qt::WindowMinMaxButtonsHint, false);
setWindowFlag(Qt::Tool);
this->resize(256, 192);
} }
void AOEmotePreview::set_widgets() void AOEmotePreview::updateViewportGeometry()
{ {
ui_viewport->resize(this->width(), this->height()); ui_viewport->resize(size());
ui_vp_player_char->move_and_center(0, 0); ui_vp_player_char->move_and_center(0, 0);
ui_vp_player_char->combo_resize(ui_viewport->width(), ui_viewport->height()); ui_vp_player_char->combo_resize(ui_viewport->width(), ui_viewport->height());
ui_size_label->setText(QString::number(this->width()) + "x" + QString::number(this->height())); ui_size_label->setText(QString::number(width()) + "x" + QString::number(height()));
} }
void AOEmotePreview::play(QString emote, QString char_name, bool flipped, int self_offset, int self_offset_v) void AOEmotePreview::display(QString character, QString emote, bool flipped, int xOffset, int yOffset)
{ {
m_character = character;
m_emote = emote;
ui_vp_player_char->stop(); ui_vp_player_char->stop();
ui_vp_player_char->set_flipped(flipped); ui_vp_player_char->set_flipped(flipped);
ui_vp_player_char->move_and_center(ui_viewport->width() * self_offset / 100, ui_viewport->height() * self_offset_v / 100); ui_vp_player_char->move_and_center(ui_viewport->width() * xOffset / 100, ui_viewport->height() * yOffset / 100);
ui_vp_player_char->load_image(emote, char_name, 0, false); ui_vp_player_char->load_image(emote, character, 0, false);
ui_vp_player_char->set_play_once(false); ui_vp_player_char->set_play_once(false);
m_emote = emote; setWindowTitle(character + ": " + emote);
m_char = char_name;
setWindowTitle(char_name + ": " + emote);
} }
void AOEmotePreview::resizeEvent(QResizeEvent *) void AOEmotePreview::resizeEvent(QResizeEvent *event)
{ {
set_widgets(); QWidget::resizeEvent(event);
ui_vp_player_char->load_image(m_emote, m_char, 0, false); updateViewportGeometry();
ui_vp_player_char->load_image(m_emote, m_character, 0, false);
} }

View File

@ -8,19 +8,20 @@ class AOEmotePreview : public QWidget
Q_OBJECT Q_OBJECT
public: public:
AOEmotePreview(AOApplication *p_ao_app, QWidget *parent = nullptr); AOEmotePreview(AOApplication *ao_app, QWidget *parent = nullptr);
void set_widgets(); void display(QString character, QString emote, bool flipped = false, int xOffset = 0, int yOffset = 0);
void play(QString emote, QString char_name, bool flipped = false, int self_offset = 0, int self_offset_v = 0);
void updateViewportGeometry();
protected: protected:
void resizeEvent(QResizeEvent *); void resizeEvent(QResizeEvent *event);
private: private:
AOApplication *ao_app; AOApplication *ao_app;
QString m_character;
QString m_emote; QString m_emote;
QString m_char;
QWidget *ui_viewport; QWidget *ui_viewport;
BackgroundLayer *ui_vp_background; BackgroundLayer *ui_vp_background;

View File

@ -24,7 +24,7 @@ void AOEvidenceDisplay::show_evidence(int p_index, QString p_evidence_image, boo
m_last_evidence_index = p_index; m_last_evidence_index = p_index;
m_sfx_player->set_volume(p_volume); m_sfx_player->setVolume(p_volume);
QString gif_name; QString gif_name;
QString icon_identifier; QString icon_identifier;
@ -56,7 +56,7 @@ void AOEvidenceDisplay::show_evidence(int p_index, QString p_evidence_image, boo
m_evidence_movie->max_duration = 1000; m_evidence_movie->max_duration = 1000;
m_evidence_movie->set_play_once(true); m_evidence_movie->set_play_once(true);
m_evidence_movie->load_image(gif_name, ""); m_evidence_movie->load_image(gif_name, "");
m_sfx_player->play(ao_app->get_court_sfx("evidence_present")); m_sfx_player->findAndPlaySfx(ao_app->get_court_sfx("evidence_present"));
} }
void AOEvidenceDisplay::reset() void AOEvidenceDisplay::reset()

View File

@ -9,48 +9,52 @@
#include <QFuture> #include <QFuture>
#include <QWidget> #include <QWidget>
AOMusicPlayer::AOMusicPlayer(AOApplication *p_ao_app) AOMusicPlayer::AOMusicPlayer(AOApplication *ao_app)
: ao_app(p_ao_app) : ao_app(ao_app)
{} {}
AOMusicPlayer::~AOMusicPlayer() AOMusicPlayer::~AOMusicPlayer()
{ {
for (int n_stream = 0; n_stream < CHANNEL_COUNT; ++n_stream) for (int n_stream = 0; n_stream < STREAM_COUNT; ++n_stream)
{ {
BASS_ChannelStop(m_stream_list[n_stream]); BASS_ChannelStop(m_stream_list[n_stream]);
} }
} }
QString AOMusicPlayer::play(QString p_song, int channel, bool loop, int effect_flags) QString AOMusicPlayer::playStream(QString song, int streamId, bool loopEnabled, int effectFlags)
{ {
channel = channel % CHANNEL_COUNT; if (!ensureValidStreamId(streamId))
if (channel < 0) // wtf?
{ {
return "[ERROR] Invalid Channel"; return "[ERROR] Invalid Channel";
} }
unsigned int flags = BASS_STREAM_PRESCAN | BASS_STREAM_AUTOFREE | BASS_UNICODE | BASS_ASYNCFILE;
unsigned int streaming_flags = BASS_STREAM_AUTOFREE; quint32 flags = BASS_STREAM_AUTOFREE;
if (loop) if (loopEnabled)
{ {
flags |= BASS_SAMPLE_LOOP; flags |= BASS_SAMPLE_LOOP;
streaming_flags |= BASS_SAMPLE_LOOP;
} }
QString f_path = p_song;
QString f_path = song;
DWORD newstream; DWORD newstream;
if (f_path.startsWith("http")) if (f_path.startsWith("http"))
{ {
if (!Options::getInstance().streamingEnabled()) if (!Options::getInstance().streamingEnabled())
{ {
BASS_ChannelStop(m_stream_list[channel]); BASS_ChannelStop(m_stream_list[streamId]);
return QObject::tr("[MISSING] Streaming disabled."); return QObject::tr("[MISSING] Streaming disabled.");
} }
QUrl l_url = QUrl(f_path); QUrl l_url = QUrl(f_path);
newstream = BASS_StreamCreateURL(l_url.toEncoded().toStdString().c_str(), 0, streaming_flags, nullptr, 0); newstream = BASS_StreamCreateURL(l_url.toEncoded().toStdString().c_str(), 0, flags, nullptr, 0);
} }
else else
{ {
f_path = ao_app->get_real_path(ao_app->get_music_path(p_song)); flags |= BASS_STREAM_PRESCAN | BASS_UNICODE | BASS_ASYNCFILE;
if (f_path.endsWith(".mo3") || f_path.endsWith(".xm") || f_path.endsWith(".mod") || f_path.endsWith(".s3m") || f_path.endsWith(".it") || f_path.endsWith(".mtm") || f_path.endsWith(".umx"))
f_path = ao_app->get_real_path(ao_app->get_music_path(song));
QString extension = f_path.split('.').last();
static const QStringList VALID_EXTENSION_LIST{"mo3", "xm", "mod", "s3m", "it", "mtm", "umx"};
if (VALID_EXTENSION_LIST.contains(extension, Qt::CaseInsensitive))
{ {
newstream = BASS_MusicLoad(FALSE, f_path.utf16(), 0, 0, flags, 1); newstream = BASS_MusicLoad(FALSE, f_path.utf16(), 0, 0, flags, 1);
} }
@ -60,18 +64,17 @@ QString AOMusicPlayer::play(QString p_song, int channel, bool loop, int effect_f
} }
} }
int error_code = BASS_ErrorGetCode(); int error = BASS_ErrorGetCode();
if (Options::getInstance().audioOutputDevice() != "default") if (Options::getInstance().audioOutputDevice() != "default")
{ {
BASS_ChannelSetDevice(m_stream_list[channel], BASS_GetDevice()); BASS_ChannelSetDevice(m_stream_list[streamId], BASS_GetDevice());
} }
QString d_path = f_path + ".txt"; m_loop_start[streamId] = 0;
m_loop_end[streamId] = 0;
m_loop_start[channel] = 0; QString d_path = f_path + ".txt";
m_loop_end[channel] = 0; if (loopEnabled && file_exists(d_path)) // Contains loop/etc. information file
if (loop && file_exists(d_path)) // Contains loop/etc. information file
{ {
QStringList lines = ao_app->read_file(d_path).split("\n"); QStringList lines = ao_app->read_file(d_path).split("\n");
bool seconds_mode = false; bool seconds_mode = false;
@ -90,6 +93,7 @@ QString AOMusicPlayer::play(QString p_song, int channel, bool loop, int effect_f
seconds_mode = true; // Use new epic behavior seconds_mode = true; // Use new epic behavior
continue; continue;
} }
continue; continue;
} }
@ -114,25 +118,25 @@ QString AOMusicPlayer::play(QString p_song, int channel, bool loop, int effect_f
} }
if (arg == "loop_start") if (arg == "loop_start")
{ {
m_loop_start[channel] = bytes; m_loop_start[streamId] = bytes;
} }
else if (arg == "loop_length") else if (arg == "loop_length")
{ {
m_loop_end[channel] = m_loop_start[channel] + bytes; m_loop_end[streamId] = m_loop_start[streamId] + bytes;
} }
else if (arg == "loop_end") else if (arg == "loop_end")
{ {
m_loop_end[channel] = bytes; m_loop_end[streamId] = bytes;
} }
} }
qDebug() << "Found data file for song" << p_song << "length" << BASS_ChannelGetLength(newstream, BASS_POS_BYTE) << "loop start" << m_loop_start[channel] << "loop end" << m_loop_end[channel]; qDebug() << "Found data file for song" << song << "length" << BASS_ChannelGetLength(newstream, BASS_POS_BYTE) << "loop start" << m_loop_start[streamId] << "loop end" << m_loop_end[streamId];
} }
if (BASS_ChannelIsActive(m_stream_list[channel]) == BASS_ACTIVE_PLAYING) if (BASS_ChannelIsActive(m_stream_list[streamId]) == BASS_ACTIVE_PLAYING)
{ {
DWORD oldstream = m_stream_list[channel]; DWORD oldstream = m_stream_list[streamId];
if (effect_flags & SYNC_POS) if (effectFlags & SYNC_POS)
{ {
BASS_ChannelLock(oldstream, true); BASS_ChannelLock(oldstream, true);
// Sync it with the new sample // Sync it with the new sample
@ -140,7 +144,7 @@ QString AOMusicPlayer::play(QString p_song, int channel, bool loop, int effect_f
BASS_ChannelLock(oldstream, false); BASS_ChannelLock(oldstream, false);
} }
if ((effect_flags & FADE_OUT) && m_volume[channel] > 0) if ((effectFlags & FADE_OUT) && m_volume[streamId] > 0)
{ {
// Fade out the other sample and stop it (due to -1) // Fade out the other sample and stop it (due to -1)
BASS_ChannelSlideAttribute(oldstream, BASS_ATTRIB_VOL | BASS_SLIDE_LOG, -1, 4000); BASS_ChannelSlideAttribute(oldstream, BASS_ATTRIB_VOL | BASS_SLIDE_LOG, -1, 4000);
@ -152,47 +156,47 @@ QString AOMusicPlayer::play(QString p_song, int channel, bool loop, int effect_f
} }
else else
{ {
BASS_ChannelStop(m_stream_list[channel]); BASS_ChannelStop(m_stream_list[streamId]);
} }
m_stream_list[channel] = newstream; m_stream_list[streamId] = newstream;
BASS_ChannelPlay(newstream, false); BASS_ChannelPlay(newstream, false);
if (effect_flags & FADE_IN) if (effectFlags & FADE_IN)
{ {
// Fade in our sample // Fade in our sample
BASS_ChannelSetAttribute(newstream, BASS_ATTRIB_VOL, 0); BASS_ChannelSetAttribute(newstream, BASS_ATTRIB_VOL, 0);
BASS_ChannelSlideAttribute(newstream, BASS_ATTRIB_VOL, static_cast<float>(m_volume[channel] / 100.0f), 1000); BASS_ChannelSlideAttribute(newstream, BASS_ATTRIB_VOL, static_cast<float>(m_volume[streamId] / 100.0f), 1000);
} }
else else
{ {
this->set_volume(m_volume[channel], channel); this->setStreamVolume(m_volume[streamId], streamId);
} }
BASS_ChannelSetSync(newstream, BASS_SYNC_DEV_FAIL, 0, ao_app->BASSreset, 0); BASS_ChannelSetSync(newstream, BASS_SYNC_DEV_FAIL, 0, ao_app->BASSreset, 0);
this->set_looping(loop, channel); // Have to do this here due to any this->setStreamLooping(loopEnabled, streamId); // Have to do this here due to any
// crossfading-related changes, etc. // crossfading-related changes, etc.
bool is_stop = (p_song == "~stop.mp3"); bool is_stop = (song == "~stop.mp3");
QString p_song_clear = QUrl(p_song).fileName(); QString p_song_clear = QUrl(song).fileName();
p_song_clear = p_song_clear.left(p_song_clear.lastIndexOf('.')); p_song_clear = p_song_clear.left(p_song_clear.lastIndexOf('.'));
if (is_stop && channel == 0) if (is_stop && streamId == 0)
{ // don't send text on channels besides 0 { // don't send text on channels besides 0
return QObject::tr("None"); return QObject::tr("None");
} }
if (error_code == BASS_ERROR_HANDLE) if (error == BASS_ERROR_HANDLE)
{ // Cheap hack to see if file missing { // Cheap hack to see if file missing
return QObject::tr("[MISSING] %1").arg(p_song_clear); return QObject::tr("[MISSING] %1").arg(p_song_clear);
} }
if (p_song.startsWith("http") && channel == 0) if (song.startsWith("http") && streamId == 0)
{ {
return QObject::tr("[STREAM] %1").arg(p_song_clear); return QObject::tr("[STREAM] %1").arg(p_song_clear);
} }
if (channel == 0) if (streamId == 0)
{ {
return p_song_clear; return p_song_clear;
} }
@ -200,36 +204,37 @@ QString AOMusicPlayer::play(QString p_song, int channel, bool loop, int effect_f
return ""; return "";
} }
void AOMusicPlayer::stop(int channel) void AOMusicPlayer::setMuted(bool enabled)
{ {
BASS_ChannelStop(m_stream_list[channel]); m_muted = enabled;
}
void AOMusicPlayer::set_muted(bool toggle)
{
m_muted = toggle;
// Update all volume based on the mute setting // Update all volume based on the mute setting
for (int n_stream = 0; n_stream < CHANNEL_COUNT; ++n_stream) for (int n_stream = 0; n_stream < STREAM_COUNT; ++n_stream)
{ {
set_volume(m_volume[n_stream], n_stream); setStreamVolume(m_volume[n_stream], n_stream);
} }
} }
void AOMusicPlayer::set_volume(int p_value, int channel) void AOMusicPlayer::setStreamVolume(int value, int streamId)
{ {
m_volume[channel] = p_value; if (!ensureValidStreamId(streamId))
// If muted, volume will always be 0
float volume = (m_volume[channel] / 100.0f) * !m_muted;
if (channel < 0)
{ {
for (int n_stream = 0; n_stream < CHANNEL_COUNT; ++n_stream) qWarning().noquote() << QObject::tr("Invalid stream ID '%2'").arg(streamId);
return;
}
m_volume[streamId] = value;
// If muted, volume will always be 0
float volume = (m_volume[streamId] / 100.0f) * !m_muted;
if (streamId < 0)
{
for (int n_stream = 0; n_stream < STREAM_COUNT; ++n_stream)
{ {
BASS_ChannelSetAttribute(m_stream_list[n_stream], BASS_ATTRIB_VOL, volume); BASS_ChannelSetAttribute(m_stream_list[n_stream], BASS_ATTRIB_VOL, volume);
} }
} }
else else
{ {
BASS_ChannelSetAttribute(m_stream_list[channel], BASS_ATTRIB_VOL, volume); BASS_ChannelSetAttribute(m_stream_list[streamId], BASS_ATTRIB_VOL, volume);
} }
} }
@ -243,37 +248,48 @@ void CALLBACK loopProc(HSYNC handle, DWORD channel, DWORD data, void *user)
BASS_ChannelLock(channel, false); BASS_ChannelLock(channel, false);
} }
void AOMusicPlayer::set_looping(bool loop_song, int channel) void AOMusicPlayer::setStreamLooping(bool enabled, int streamId)
{ {
if (!loop_song) if (!ensureValidStreamId(streamId))
{ {
if (BASS_ChannelFlags(m_stream_list[channel], 0, 0) & BASS_SAMPLE_LOOP) qWarning().noquote() << QObject::tr("Invalid stream ID '%2'").arg(streamId);
{
BASS_ChannelFlags(m_stream_list[channel], 0,
BASS_SAMPLE_LOOP); // remove the LOOP flag
}
BASS_ChannelRemoveSync(m_stream_list[channel], m_loop_sync[channel]);
m_loop_sync[channel] = 0;
return; return;
} }
BASS_ChannelFlags(m_stream_list[channel], BASS_SAMPLE_LOOP, if (!enabled)
BASS_SAMPLE_LOOP); // set the LOOP flag
if (m_loop_sync[channel] != 0)
{ {
BASS_ChannelRemoveSync(m_stream_list[channel], if (BASS_ChannelFlags(m_stream_list[streamId], 0, 0) & BASS_SAMPLE_LOOP)
m_loop_sync[channel]); // remove the sync {
m_loop_sync[channel] = 0; BASS_ChannelFlags(m_stream_list[streamId], 0,
BASS_SAMPLE_LOOP); // remove the LOOP flag
}
BASS_ChannelRemoveSync(m_stream_list[streamId], m_loop_sync[streamId]);
m_loop_sync[streamId] = 0;
return;
} }
if (m_loop_start[channel] < m_loop_end[channel]) BASS_ChannelFlags(m_stream_list[streamId], BASS_SAMPLE_LOOP,
BASS_SAMPLE_LOOP); // set the LOOP flag
if (m_loop_sync[streamId] != 0)
{
BASS_ChannelRemoveSync(m_stream_list[streamId],
m_loop_sync[streamId]); // remove the sync
m_loop_sync[streamId] = 0;
}
if (m_loop_start[streamId] < m_loop_end[streamId])
{ {
// Loop when the endpoint is reached. // Loop when the endpoint is reached.
m_loop_sync[channel] = BASS_ChannelSetSync(m_stream_list[channel], BASS_SYNC_POS | BASS_SYNC_MIXTIME, m_loop_end[channel], loopProc, &m_loop_start[channel]); m_loop_sync[streamId] = BASS_ChannelSetSync(m_stream_list[streamId], BASS_SYNC_POS | BASS_SYNC_MIXTIME, m_loop_end[streamId], loopProc, &m_loop_start[streamId]);
} }
else else
{ {
// Loop when the end of the file is reached. // Loop when the end of the file is reached.
m_loop_sync[channel] = BASS_ChannelSetSync(m_stream_list[channel], BASS_SYNC_END | BASS_SYNC_MIXTIME, 0, loopProc, &m_loop_start[channel]); m_loop_sync[streamId] = BASS_ChannelSetSync(m_stream_list[streamId], BASS_SYNC_END | BASS_SYNC_MIXTIME, 0, loopProc, &m_loop_start[streamId]);
} }
} }
bool AOMusicPlayer::ensureValidStreamId(int streamId)
{
return (streamId >= 0 && streamId < STREAM_COUNT);
}

View File

@ -7,38 +7,32 @@
class AOMusicPlayer class AOMusicPlayer
{ {
public: public:
// Channel 0 = music // 0 = music
// Channel 1 = ambience // 1 = ambience
static constexpr int CHANNEL_COUNT = 2; static constexpr int STREAM_COUNT = 2;
AOMusicPlayer(AOApplication *p_ao_app); explicit AOMusicPlayer(AOApplication *ao_app);
virtual ~AOMusicPlayer(); virtual ~AOMusicPlayer();
void set_volume(int p_value, int channel = -1); void setMuted(bool enabled);
void set_looping(bool loop_song, int channel = 0);
void set_muted(bool toggle);
QFutureWatcher<QString> music_watcher; QString playStream(QString song, int streamId, bool loopEnabled, int effectFlags);
public Q_SLOTS: void setStreamVolume(int value, int streamId);
QString play(QString p_song, int channel = 0, bool loop = false, int effect_flags = 0); void setStreamLooping(bool enabled, int streamId);
void stop(int channel = 0);
QFutureWatcher<QString> m_watcher;
private: private:
AOApplication *ao_app; AOApplication *ao_app;
bool m_muted = false; bool m_muted = false;
int m_volume[CHANNEL_COUNT] = {0, 0};
HSTREAM m_stream_list[CHANNEL_COUNT];
HSYNC m_loop_sync[CHANNEL_COUNT];
/** int m_volume[STREAM_COUNT]{};
* @brief The starting sample of the AB-Loop. HSTREAM m_stream_list[STREAM_COUNT]{};
*/ HSYNC m_loop_sync[STREAM_COUNT]{};
unsigned int m_loop_start[CHANNEL_COUNT] = {0, 0}; quint32 m_loop_start[STREAM_COUNT]{};
quint32 m_loop_end[STREAM_COUNT]{};
/** bool ensureValidStreamId(int streamId);
* @brief The end sample of the AB-Loop.
*/
unsigned int m_loop_end[CHANNEL_COUNT] = {0, 0};
}; };

View File

@ -10,6 +10,9 @@ QString AOPacket::decode(QString data)
return data.replace("<num>", "#").replace("<percent>", "%").replace("<dollar>", "$").replace("<and>", "&"); return data.replace("<num>", "#").replace("<percent>", "%").replace("<dollar>", "$").replace("<and>", "&");
} }
AOPacket::AOPacket()
{}
AOPacket::AOPacket(QString header) AOPacket::AOPacket(QString header)
: m_header(header) : m_header(header)
{} {}

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <QMetaType>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
@ -9,6 +10,7 @@ public:
static QString encode(QString data); static QString encode(QString data);
static QString decode(QString data); static QString decode(QString data);
AOPacket();
AOPacket(QString header); AOPacket(QString header);
AOPacket(QString header, QStringList content); AOPacket(QString header, QStringList content);
@ -21,3 +23,4 @@ private:
QString m_header; QString m_header;
QStringList m_content; QStringList m_content;
}; };
Q_DECLARE_METATYPE(AOPacket)

View File

@ -2,111 +2,135 @@
#include "file_functions.h" #include "file_functions.h"
AOSfxPlayer::AOSfxPlayer(AOApplication *p_ao_app) AOSfxPlayer::AOSfxPlayer(AOApplication *ao_app)
: ao_app(p_ao_app) : ao_app(ao_app)
{} {}
void AOSfxPlayer::clear() int AOSfxPlayer::volume()
{
for (int n_stream = 0; n_stream < CHANNEL_COUNT; ++n_stream)
{
BASS_ChannelStop(m_stream_list[n_stream]);
}
set_volume_internal(m_volume);
}
void AOSfxPlayer::loop_clear()
{
for (int n_stream = 0; n_stream < CHANNEL_COUNT; ++n_stream)
{
if ((BASS_ChannelFlags(m_stream_list[n_stream], 0, 0) & BASS_SAMPLE_LOOP))
{
BASS_ChannelStop(m_stream_list[n_stream]);
}
}
set_volume_internal(m_volume);
}
void AOSfxPlayer::play(QString p_sfx, QString p_character, QString p_misc)
{
for (int i = 0; i < CHANNEL_COUNT; ++i)
{
if (BASS_ChannelIsActive(m_stream_list[i]) == BASS_ACTIVE_PLAYING)
{
m_channel = (i + 1) % CHANNEL_COUNT;
}
else
{
m_channel = i;
break;
}
}
QString path = ao_app->get_sfx(p_sfx, p_misc, p_character);
if (path.endsWith(".opus"))
{
m_stream_list[m_channel] = BASS_OPUS_StreamCreateFile(FALSE, path.utf16(), 0, 0, BASS_STREAM_AUTOFREE | BASS_UNICODE | BASS_ASYNCFILE);
}
else
{
m_stream_list[m_channel] = BASS_StreamCreateFile(FALSE, path.utf16(), 0, 0, BASS_STREAM_AUTOFREE | BASS_UNICODE | BASS_ASYNCFILE);
}
set_volume_internal(m_volume);
BASS_ChannelSetDevice(m_stream_list[m_channel], BASS_GetDevice());
BASS_ChannelPlay(m_stream_list[m_channel], false);
BASS_ChannelSetSync(m_stream_list[m_channel], BASS_SYNC_DEV_FAIL, 0, ao_app->BASSreset, 0);
}
void AOSfxPlayer::stop(int channel)
{
if (channel == -1)
{
channel = m_channel;
}
BASS_ChannelStop(m_stream_list[channel]);
}
void AOSfxPlayer::set_muted(bool toggle)
{
m_muted = toggle;
// Update the audio volume
set_volume_internal(m_volume);
}
int AOSfxPlayer::get_volume()
{ {
return m_volume * 100; return m_volume * 100;
} }
void AOSfxPlayer::set_volume(qreal p_value) void AOSfxPlayer::setVolume(int value)
{ {
m_volume = p_value * 0.01; m_volume = value;
set_volume_internal(m_volume); updateInternalVolume();
} }
void AOSfxPlayer::set_volume_internal(qreal p_value) void AOSfxPlayer::play(QString path)
{ {
// If muted, volume will always be 0 for (int i = 0; i < STREAM_COUNT; ++i)
float volume = static_cast<float>(p_value) * !m_muted;
for (int n_stream = 0; n_stream < CHANNEL_COUNT; ++n_stream)
{ {
BASS_ChannelSetAttribute(m_stream_list[n_stream], BASS_ATTRIB_VOL, volume); if (BASS_ChannelIsActive(m_stream[i]) == BASS_ACTIVE_PLAYING)
{
m_current_stream_id = (i + 1) % STREAM_COUNT;
}
else
{
m_current_stream_id = i;
break;
}
}
if (path.endsWith(".opus"))
{
m_stream[m_current_stream_id] = BASS_OPUS_StreamCreateFile(FALSE, path.utf16(), 0, 0, BASS_STREAM_AUTOFREE | BASS_UNICODE | BASS_ASYNCFILE);
}
else
{
m_stream[m_current_stream_id] = BASS_StreamCreateFile(FALSE, path.utf16(), 0, 0, BASS_STREAM_AUTOFREE | BASS_UNICODE | BASS_ASYNCFILE);
}
updateInternalVolume();
BASS_ChannelSetDevice(m_stream[m_current_stream_id], BASS_GetDevice());
BASS_ChannelPlay(m_stream[m_current_stream_id], false);
BASS_ChannelSetSync(m_stream[m_current_stream_id], BASS_SYNC_DEV_FAIL, 0, ao_app->BASSreset, 0);
}
void AOSfxPlayer::findAndPlaySfx(QString sfx)
{
// TODO replace this with proper pathing tools
findAndPlayCharacterShout(sfx, QString(), QString());
}
void AOSfxPlayer::findAndPlayCharacterSfx(QString sfx, QString character)
{
// TODO replace this with proper pathing tools
findAndPlayCharacterShout(sfx, character, QString());
}
void AOSfxPlayer::findAndPlayCharacterShout(QString shout, QString character, QString group)
{
QString file_path = ao_app->get_sfx(shout, group, character);
if (file_exists(file_path))
{
play(file_path);
} }
} }
void AOSfxPlayer::set_looping(bool toggle, int channel) void AOSfxPlayer::stopAll()
{ {
if (channel == -1) for (int i = 0; i < STREAM_COUNT; ++i)
{ {
channel = m_channel; stop(i);
} }
}
void AOSfxPlayer::stopAllLoopingStream()
{
for (int i = 0; i < STREAM_COUNT; ++i)
{
if (BASS_ChannelFlags(m_stream[i], 0, 0) & BASS_SAMPLE_LOOP)
{
stop(i);
}
}
}
void AOSfxPlayer::stop(int streamId)
{
streamId = maybeFetchCurrentStreamId(streamId);
if (!ensureValidStreamId(streamId))
{
qWarning().noquote() << QObject::tr("Failed to stop stream; invalid stream ID '%1'").arg(streamId);
return;
}
BASS_ChannelStop(m_stream[streamId]);
}
void AOSfxPlayer::setMuted(bool toggle)
{
m_muted = toggle;
// Update the audio volume
updateInternalVolume();
}
void AOSfxPlayer::updateInternalVolume()
{
float volume = m_muted ? 0.0f : m_volume;
for (int i = 0; i < STREAM_COUNT; ++i)
{
BASS_ChannelSetAttribute(m_stream[i], BASS_ATTRIB_VOL, volume);
}
}
void AOSfxPlayer::setLooping(bool toggle, int streamId)
{
streamId = maybeFetchCurrentStreamId(streamId);
if (!ensureValidStreamId(streamId))
{
qWarning().noquote() << QObject::tr("Failed to setup stream loop; invalid stream ID '%1'").arg(streamId);
return;
}
m_looping = toggle; m_looping = toggle;
if (BASS_ChannelFlags(m_stream_list[channel], 0, 0) & BASS_SAMPLE_LOOP) if (BASS_ChannelFlags(m_stream[streamId], 0, 0) & BASS_SAMPLE_LOOP)
{ {
if (m_looping == false) if (m_looping == false)
{ {
BASS_ChannelFlags(m_stream_list[channel], 0, BASS_ChannelFlags(m_stream[streamId], 0,
BASS_SAMPLE_LOOP); // remove the LOOP flag BASS_SAMPLE_LOOP); // remove the LOOP flag
} }
} }
@ -114,8 +138,18 @@ void AOSfxPlayer::set_looping(bool toggle, int channel)
{ {
if (m_looping == true) if (m_looping == true)
{ {
BASS_ChannelFlags(m_stream_list[channel], BASS_SAMPLE_LOOP, BASS_ChannelFlags(m_stream[streamId], BASS_SAMPLE_LOOP,
BASS_SAMPLE_LOOP); // set the LOOP flag BASS_SAMPLE_LOOP); // set the LOOP flag
} }
} }
} }
int AOSfxPlayer::maybeFetchCurrentStreamId(int streamId)
{
return streamId == -1 ? m_current_stream_id : streamId;
}
bool AOSfxPlayer::ensureValidStreamId(int streamId)
{
return streamId >= 0 && streamId < STREAM_COUNT;
}

View File

@ -11,28 +11,35 @@
class AOSfxPlayer class AOSfxPlayer
{ {
public: public:
static constexpr int CHANNEL_COUNT = 5; static constexpr int STREAM_COUNT = 5;
AOSfxPlayer(AOApplication *p_ao_app); AOSfxPlayer(AOApplication *ao_app);
int get_volume(); int volume();
void setVolume(int value);
void clear(); void play(QString path);
void loop_clear(); void stop(int streamId = -1);
void play(QString p_sfx, QString p_char = QString(), QString shout = QString()); void stopAll();
void stop(int channel = -1); void stopAllLoopingStream();
void set_volume(qreal p_volume);
void set_looping(bool toggle, int channel = -1); void findAndPlaySfx(QString sfx);
void set_muted(bool toggle); void findAndPlayCharacterSfx(QString sfx, QString character);
void findAndPlayCharacterShout(QString shout, QString character, QString group);
void setMuted(bool toggle);
void setLooping(bool toggle, int streamId = -1);
private: private:
AOApplication *ao_app; AOApplication *ao_app;
qreal m_volume = 0.0; int m_volume = 0;
bool m_looping = true;
bool m_muted = false; bool m_muted = false;
int m_channel = 0; bool m_looping = true;
HSTREAM m_stream_list[CHANNEL_COUNT]{}; HSTREAM m_stream[STREAM_COUNT]{};
int m_current_stream_id = 0;
void set_volume_internal(qreal p_volume); int maybeFetchCurrentStreamId(int streamId);
bool ensureValidStreamId(int streamId);
void updateInternalVolume();
}; };

View File

@ -1,16 +1,16 @@
#include "aotextarea.h" #include "aotextarea.h"
AOTextArea::AOTextArea(QWidget *p_parent) AOTextArea::AOTextArea(QWidget *parent)
: AOTextArea(5000, p_parent) : AOTextArea(5000, parent)
{} {}
AOTextArea::AOTextArea(int p_log_length, QWidget *p_parent) AOTextArea::AOTextArea(int maximumLogLenth, QWidget *parent)
: QTextBrowser(p_parent) : QTextBrowser(parent)
{ {
this->document()->setMaximumBlockCount(p_log_length); document()->setMaximumBlockCount(maximumLogLenth);
} }
void AOTextArea::append_chatmessage(QString p_name, QString p_message, QString p_name_colour, QString p_color) void AOTextArea::addMessage(QString name, QString message, QString nameColor, QString messageColor)
{ {
const QTextCursor old_cursor = this->textCursor(); const QTextCursor old_cursor = this->textCursor();
const int old_scrollbar_value = this->verticalScrollBar()->value(); const int old_scrollbar_value = this->verticalScrollBar()->value();
@ -19,19 +19,19 @@ void AOTextArea::append_chatmessage(QString p_name, QString p_message, QString p
this->moveCursor(QTextCursor::End); this->moveCursor(QTextCursor::End);
this->append(""); this->append("");
if (!p_name.isEmpty()) if (!name.isEmpty())
{ {
this->insertHtml("<b><font color=" + p_name_colour + ">" + p_name.toHtmlEscaped() + "</font></b>:&nbsp;"); this->insertHtml("<b><font color=" + nameColor + ">" + name.toHtmlEscaped() + "</font></b>:&nbsp;");
// cheap workarounds ahoy // cheap workarounds ahoy
p_message += " "; message += " ";
} }
QString result = p_message.toHtmlEscaped().replace("\n", "<br>").replace(url_parser_regex, "<a href='\\1'>\\1</a>"); QString result = message.toHtmlEscaped().replace("\n", "<br>").replace(url_parser_regex, "<a href='\\1'>\\1</a>");
if (!p_color.isEmpty()) if (!messageColor.isEmpty())
{ {
result = "<font color=" + p_color + ">" + result + "</font>"; result = "<font color=" + messageColor + ">" + result + "</font>";
} }
this->insertHtml(result); this->insertHtml(result);

View File

@ -11,10 +11,10 @@ class AOTextArea : public QTextBrowser
Q_OBJECT Q_OBJECT
public: public:
AOTextArea(QWidget *p_parent = nullptr); AOTextArea(QWidget *parent = nullptr);
AOTextArea(int p_log_length, QWidget *p_parent = nullptr); AOTextArea(int maximumLogLenth, QWidget *parent = nullptr);
void append_chatmessage(QString p_name, QString p_message, QString p_name_colour, QString p_color = QString()); void addMessage(QString name, QString message, QString nameColor, QString messageColor = QString());
private: private:
const QRegularExpression url_parser_regex = QRegularExpression("\\b(https?://\\S+\\.\\S+)\\b"); const QRegularExpression url_parser_regex = QRegularExpression("\\b(https?://\\S+\\.\\S+)\\b");

View File

@ -338,7 +338,7 @@ void Courtroom::character_loading_finished()
// This part here serves as a way of showing to the player that the game is // This part here serves as a way of showing to the player that the game is
// still running, it is just loading the pictures of the characters. // still running, it is just loading the pictures of the characters.
if (ao_app->lobby_constructed) if (ao_app->is_lobby_constructed())
{ {
ao_app->generated_chars++; ao_app->generated_chars++;
} }

View File

@ -28,20 +28,20 @@ Courtroom::Courtroom(AOApplication *p_ao_app)
sfx_delay_timer->setSingleShot(true); sfx_delay_timer->setSingleShot(true);
music_player = new AOMusicPlayer(ao_app); music_player = new AOMusicPlayer(ao_app);
music_player->set_muted(true); music_player->setMuted(true);
connect(&music_player->music_watcher, &QFutureWatcher<QString>::finished, this, &Courtroom::update_ui_music_name, Qt::QueuedConnection); connect(&music_player->m_watcher, &QFutureWatcher<QString>::finished, this, &Courtroom::update_ui_music_name, Qt::QueuedConnection);
sfx_player = new AOSfxPlayer(ao_app); sfx_player = new AOSfxPlayer(ao_app);
sfx_player->set_muted(true); sfx_player->setMuted(true);
objection_player = new AOSfxPlayer(ao_app); objection_player = new AOSfxPlayer(ao_app);
objection_player->set_muted(true); objection_player->setMuted(true);
blip_player = new AOBlipPlayer(ao_app); blip_player = new AOBlipPlayer(ao_app);
blip_player->set_muted(true); blip_player->setMuted(true);
modcall_player = new AOSfxPlayer(ao_app); modcall_player = new AOSfxPlayer(ao_app);
modcall_player->set_volume(50); modcall_player->setVolume(50);
ui_background = new AOImage(ao_app, this); ui_background = new AOImage(ao_app, this);
ui_background->setObjectName("ui_background"); ui_background->setObjectName("ui_background");
@ -552,15 +552,15 @@ void Courtroom::update_audio_volume()
remaining_percent = 0; remaining_percent = 0;
} }
music_player->set_volume(ui_music_slider->value() * remaining_percent, 0); // set music music_player->setStreamVolume(ui_music_slider->value() * remaining_percent, 0); // set music
// Set the ambience and other misc. music layers // Set the ambience and other misc. music layers
for (int i = 1; i < music_player->CHANNEL_COUNT; ++i) for (int i = 1; i < music_player->STREAM_COUNT; ++i)
{ {
music_player->set_volume(ui_sfx_slider->value() * remaining_percent, i); music_player->setStreamVolume(ui_sfx_slider->value() * remaining_percent, i);
} }
sfx_player->set_volume(ui_sfx_slider->value() * remaining_percent); sfx_player->setVolume(ui_sfx_slider->value() * remaining_percent);
objection_player->set_volume(ui_sfx_slider->value() * remaining_percent); objection_player->setVolume(ui_sfx_slider->value() * remaining_percent);
blip_player->set_volume(ui_blip_slider->value() * remaining_percent); blip_player->setVolume(ui_blip_slider->value() * remaining_percent);
} }
void Courtroom::append_char(CharacterSlot p_char) void Courtroom::append_char(CharacterSlot p_char)
@ -1688,10 +1688,10 @@ void Courtroom::enter_courtroom()
} }
// Unmute everything // Unmute everything
music_player->set_muted(false); music_player->setMuted(false);
objection_player->set_muted(false); objection_player->setMuted(false);
sfx_player->set_muted(false); sfx_player->setMuted(false);
blip_player->set_muted(false); blip_player->setMuted(false);
// Update the audio sliders // Update the audio sliders
update_audio_volume(); update_audio_volume();
@ -1866,7 +1866,7 @@ void Courtroom::debug_message_handler(QtMsgType type, const QMessageLogContext &
Q_UNUSED(context); Q_UNUSED(context);
const QMap<QtMsgType, QString> colors = {{QtDebugMsg, "debug"}, {QtInfoMsg, "info"}, {QtWarningMsg, "warn"}, {QtCriticalMsg, "critical"}, {QtFatalMsg, "fatal"}}; const QMap<QtMsgType, QString> colors = {{QtDebugMsg, "debug"}, {QtInfoMsg, "info"}, {QtWarningMsg, "warn"}, {QtCriticalMsg, "critical"}, {QtFatalMsg, "fatal"}};
const QString color_id = QString("debug_log_%1_color").arg(colors.value(type, "info")); const QString color_id = QString("debug_log_%1_color").arg(colors.value(type, "info"));
ui_debug_log->append_chatmessage(colors.value(type, "info"), msg, QString(), ao_app->get_color(color_id, "courtroom_fonts.ini").name()); ui_debug_log->addMessage(colors.value(type, "info"), msg, QString(), ao_app->get_color(color_id, "courtroom_fonts.ini").name());
} }
void Courtroom::append_server_chatmessage(QString p_name, QString p_message, QString p_color) void Courtroom::append_server_chatmessage(QString p_name, QString p_message, QString p_color)
@ -1887,7 +1887,7 @@ void Courtroom::append_server_chatmessage(QString p_name, QString p_message, QSt
on_authentication_state_received(1); on_authentication_state_received(1);
} }
ui_server_chatlog->append_chatmessage(p_name, p_message, color); ui_server_chatlog->addMessage(p_name, p_message, color);
if (Options::getInstance().logToTextFileEnabled() && !ao_app->log_filename.isEmpty()) if (Options::getInstance().logToTextFileEnabled() && !ao_app->log_filename.isEmpty())
{ {
@ -2666,33 +2666,33 @@ bool Courtroom::handle_objection()
{ {
case 1: case 1:
filename = "holdit_bubble"; filename = "holdit_bubble";
objection_player->play("holdit", m_chatmessage[CHAR_NAME], ao_app->get_chat(m_chatmessage[CHAR_NAME])); objection_player->findAndPlayCharacterShout("holdit", m_chatmessage[CHAR_NAME], ao_app->get_chat(m_chatmessage[CHAR_NAME]));
break; break;
case 2: case 2:
filename = "objection_bubble"; filename = "objection_bubble";
objection_player->play("objection", m_chatmessage[CHAR_NAME], ao_app->get_chat(m_chatmessage[CHAR_NAME])); objection_player->findAndPlayCharacterShout("objection", m_chatmessage[CHAR_NAME], ao_app->get_chat(m_chatmessage[CHAR_NAME]));
break; break;
case 3: case 3:
filename = "takethat_bubble"; filename = "takethat_bubble";
objection_player->play("takethat", m_chatmessage[CHAR_NAME], ao_app->get_chat(m_chatmessage[CHAR_NAME])); objection_player->findAndPlayCharacterShout("takethat", m_chatmessage[CHAR_NAME], ao_app->get_chat(m_chatmessage[CHAR_NAME]));
break; break;
// case 4 is AO2 only // case 4 is AO2 only
case 4: case 4:
if (custom_objection != "") if (custom_objection != "")
{ {
filename = "custom_objections/" + custom_objection.left(custom_objection.lastIndexOf(".")); filename = "custom_objections/" + custom_objection.left(custom_objection.lastIndexOf("."));
objection_player->play(filename, m_chatmessage[CHAR_NAME], ao_app->get_chat(m_chatmessage[CHAR_NAME])); objection_player->findAndPlayCharacterShout(filename, m_chatmessage[CHAR_NAME], ao_app->get_chat(m_chatmessage[CHAR_NAME]));
} }
else else
{ {
filename = "custom"; filename = "custom";
objection_player->play("custom", m_chatmessage[CHAR_NAME], ao_app->get_chat(m_chatmessage[CHAR_NAME])); objection_player->findAndPlayCharacterShout("custom", m_chatmessage[CHAR_NAME], ao_app->get_chat(m_chatmessage[CHAR_NAME]));
} }
break; break;
m_chatmessage[EMOTE_MOD] = QChar(PREANIM); m_chatmessage[EMOTE_MOD] = QChar(PREANIM);
} }
ui_vp_objection->load_image(filename, m_chatmessage[CHAR_NAME], ao_app->get_chat(m_chatmessage[CHAR_NAME])); ui_vp_objection->load_image(filename, m_chatmessage[CHAR_NAME], ao_app->get_chat(m_chatmessage[CHAR_NAME]));
sfx_player->clear(); // Objection played! Cut all sfx. sfx_player->stopAll(); // Objection played! Cut all sfx.
ui_vp_player_char->set_play_once(true); ui_vp_player_char->set_play_once(true);
return true; return true;
} }
@ -2710,7 +2710,7 @@ void Courtroom::display_character()
ui_vp_player_char->stop(); ui_vp_player_char->stop();
ui_vp_effect->stop(); ui_vp_effect->stop();
// Clear all looping sfx to prevent obnoxiousness // Clear all looping sfx to prevent obnoxiousness
sfx_player->loop_clear(); sfx_player->stopAllLoopingStream();
// Hide the message and chatbox and handle the emotes // Hide the message and chatbox and handle the emotes
ui_vp_message->hide(); ui_vp_message->hide();
ui_vp_chatbox->setVisible(chatbox_always_show); ui_vp_chatbox->setVisible(chatbox_always_show);
@ -2991,7 +2991,7 @@ void Courtroom::do_effect(QString fx_path, QString fx_sound, QString p_char, QSt
if (fx_sound != "") if (fx_sound != "")
{ {
sfx_player->play(fx_sound); sfx_player->findAndPlaySfx(fx_sound);
} }
// Only check if effects are disabled after playing the sound if it exists // Only check if effects are disabled after playing the sound if it exists
@ -3073,7 +3073,7 @@ void Courtroom::do_effect(QString fx_path, QString fx_sound, QString p_char, QSt
void Courtroom::play_char_sfx(QString sfx_name) void Courtroom::play_char_sfx(QString sfx_name)
{ {
sfx_player->play(sfx_name); sfx_player->findAndPlaySfx(sfx_name);
} }
void Courtroom::initialize_chatbox() void Courtroom::initialize_chatbox()
@ -3216,7 +3216,7 @@ void Courtroom::handle_callwords()
if (f_message.contains(word, Qt::CaseInsensitive)) if (f_message.contains(word, Qt::CaseInsensitive))
{ {
// Play the call word sfx on the modcall_player sound container // Play the call word sfx on the modcall_player sound container
modcall_player->play(ao_app->get_court_sfx("word_call")); modcall_player->findAndPlaySfx(ao_app->get_court_sfx("word_call"));
// Make the window flash // Make the window flash
ao_app->alert(this); ao_app->alert(this);
// Break the loop so we don't spam sound effects // Break the loop so we don't spam sound effects
@ -3236,7 +3236,7 @@ void Courtroom::display_evidence_image()
// QString f_evi_name = local_evidence_list.at(f_evi_id - 1).name; // QString f_evi_name = local_evidence_list.at(f_evi_id - 1).name;
// def jud and hlp should display the evidence icon on the RIGHT side // def jud and hlp should display the evidence icon on the RIGHT side
bool is_left_side = !(side == "def" || side == "hlp" || side == "jud" || side == "jur"); bool is_left_side = !(side == "def" || side == "hlp" || side == "jud" || side == "jur");
ui_vp_evidence_display->show_evidence(f_evi_id, f_image, is_left_side, sfx_player->get_volume()); ui_vp_evidence_display->show_evidence(f_evi_id, f_image, is_left_side, sfx_player->volume());
} }
} }
@ -3930,7 +3930,7 @@ void Courtroom::start_chat_ticking()
else if (m_chatmessage[REALIZATION] == "1") else if (m_chatmessage[REALIZATION] == "1")
{ {
this->do_flash(); this->do_flash();
sfx_player->play(ao_app->get_custom_realization(m_chatmessage[CHAR_NAME])); sfx_player->findAndPlaySfx(ao_app->get_custom_realization(m_chatmessage[CHAR_NAME]));
} }
int emote_mod = m_chatmessage[EMOTE_MOD].toInt(); // text meme bonanza int emote_mod = m_chatmessage[EMOTE_MOD].toInt(); // text meme bonanza
if ((emote_mod == IDLE || emote_mod == ZOOM) && m_chatmessage[SCREENSHAKE] == "1") if ((emote_mod == IDLE || emote_mod == ZOOM) && m_chatmessage[SCREENSHAKE] == "1")
@ -4009,7 +4009,7 @@ void Courtroom::start_chat_ticking()
{ {
f_blips = ao_app->get_blips(m_chatmessage[BLIPNAME]); f_blips = ao_app->get_blips(m_chatmessage[BLIPNAME]);
} }
blip_player->set_blips(f_blips); blip_player->setBlip(f_blips);
// means text is currently ticking // means text is currently ticking
text_state = 1; text_state = 1;
@ -4276,7 +4276,7 @@ void Courtroom::chat_tick()
// ignoring white space unless blank_blip is enabled. // ignoring white space unless blank_blip is enabled.
if (!formatting_char && (f_character != ' ' || blank_blip)) if (!formatting_char && (f_character != ' ' || blank_blip))
{ {
blip_player->blip_tick(); blip_player->playBlip();
++blip_ticker; ++blip_ticker;
} }
} }
@ -4335,10 +4335,10 @@ void Courtroom::play_sfx()
return; return;
} }
sfx_player->play(sfx_name); sfx_player->findAndPlaySfx(sfx_name);
if (Options::getInstance().loopingSfx()) if (Options::getInstance().loopingSfx())
{ {
sfx_player->set_looping(ao_app->get_sfx_looping(current_char, current_emote) == "1"); sfx_player->setLooping(ao_app->get_sfx_looping(current_char, current_emote) == "1");
} }
} }
@ -4500,22 +4500,19 @@ void Courtroom::handle_song(QStringList *p_contents)
{ {
// Current song UI only displays the song playing, not other channels. // Current song UI only displays the song playing, not other channels.
// Any other music playing is irrelevant. // Any other music playing is irrelevant.
if (music_player->music_watcher.isRunning()) if (music_player->m_watcher.isRunning())
{ {
music_player->music_watcher.cancel(); music_player->m_watcher.cancel();
} }
ui_music_name->setText(tr("[LOADING] %1").arg(f_song_clear)); ui_music_name->setText(tr("[LOADING] %1").arg(f_song_clear));
} }
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
music_player->music_watcher.setFuture(QtConcurrent::run(music_player, &AOMusicPlayer::play, f_song, channel, looping, effect_flags)); music_player->m_watcher.setFuture(QtConcurrent::run([=, this]() -> QString { return music_player->playStream(f_song, channel, looping, effect_flags); }));
#else
music_player->music_watcher.setFuture(QtConcurrent::run(&AOMusicPlayer::play, music_player, f_song, channel, looping, effect_flags));
#endif
} }
void Courtroom::update_ui_music_name() void Courtroom::update_ui_music_name()
{ {
QString result = music_player->music_watcher.result(); QString result = music_player->m_watcher.result();
if (result.isEmpty()) if (result.isEmpty())
{ {
return; return;
@ -4590,7 +4587,7 @@ void Courtroom::handle_wtce(QString p_wtce, int variant)
filename = p_wtce; filename = p_wtce;
} }
} }
sfx_player->play(sfx_name); sfx_player->findAndPlaySfx(sfx_name);
ui_vp_wtce->load_image(filename, "", bg_misc); ui_vp_wtce->load_image(filename, "", bg_misc);
ui_vp_wtce->set_play_once(true); ui_vp_wtce->set_play_once(true);
} }
@ -4648,7 +4645,7 @@ void Courtroom::set_hp_bar(int p_bar, int p_state)
if (!sfx_name.isEmpty()) if (!sfx_name.isEmpty())
{ {
sfx_player->play(sfx_name); sfx_player->findAndPlaySfx(sfx_name);
} }
} }
@ -4678,7 +4675,7 @@ void Courtroom::mod_called(QString p_ip)
ui_server_chatlog->append(p_ip); ui_server_chatlog->append(p_ip);
if (!ui_guard->isChecked()) if (!ui_guard->isChecked())
{ {
modcall_player->play(ao_app->get_court_sfx("mod_call")); modcall_player->findAndPlaySfx(ao_app->get_court_sfx("mod_call"));
ao_app->alert(this); ao_app->alert(this);
} }
} }
@ -4703,11 +4700,7 @@ void Courtroom::on_ooc_return_pressed()
if (ooc_message.startsWith("/load_case")) if (ooc_message.startsWith("/load_case"))
{ {
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) QStringList command = ooc_message.split(" ", AOSplitBehaviorFlags::SkipEmptyParts);
QStringList command = ooc_message.split(" ", QString::SkipEmptyParts);
#else
QStringList command = ooc_message.split(" ", Qt::SkipEmptyParts);
#endif
QDir casefolder(get_base_path() + "/cases"); QDir casefolder(get_base_path() + "/cases");
if (!casefolder.exists()) if (!casefolder.exists())
{ {
@ -4803,11 +4796,7 @@ void Courtroom::on_ooc_return_pressed()
} }
else if (ooc_message.startsWith("/save_case")) else if (ooc_message.startsWith("/save_case"))
{ {
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) QStringList command = ooc_message.split(" ", AOSplitBehaviorFlags::SkipEmptyParts);
QStringList command = ooc_message.split(" ", QString::SkipEmptyParts);
#else
QStringList command = ooc_message.split(" ", Qt::SkipEmptyParts);
#endif
QDir casefolder(get_base_path() + "cases"); QDir casefolder(get_base_path() + "cases");
if (!casefolder.exists()) if (!casefolder.exists())
{ {
@ -5265,7 +5254,7 @@ void Courtroom::on_sfx_context_menu_requested(const QPoint &pos)
void Courtroom::on_sfx_play_clicked() void Courtroom::on_sfx_play_clicked()
{ {
sfx_player->play(get_char_sfx(), get_current_char()); sfx_player->findAndPlayCharacterSfx(get_char_sfx(), get_current_char());
} }
void Courtroom::on_sfx_edit_requested() void Courtroom::on_sfx_edit_requested()
@ -6063,25 +6052,25 @@ void Courtroom::on_text_color_changed(int p_color)
void Courtroom::on_music_slider_moved(int p_value) void Courtroom::on_music_slider_moved(int p_value)
{ {
music_player->set_volume(p_value, 0); // Set volume on music layer music_player->setStreamVolume(p_value, 0); // Set volume on music layer
ui_ic_chat_message->setFocus(); ui_ic_chat_message->setFocus();
} }
void Courtroom::on_sfx_slider_moved(int p_value) void Courtroom::on_sfx_slider_moved(int p_value)
{ {
sfx_player->set_volume(p_value); sfx_player->setVolume(p_value);
// Set the ambience and other misc. music layers // Set the ambience and other misc. music layers
for (int i = 1; i < music_player->CHANNEL_COUNT; ++i) for (int i = 1; i < music_player->STREAM_COUNT; ++i)
{ {
music_player->set_volume(p_value, i); music_player->setStreamVolume(p_value, i);
} }
objection_player->set_volume(p_value); objection_player->setVolume(p_value);
ui_ic_chat_message->setFocus(); ui_ic_chat_message->setFocus();
} }
void Courtroom::on_blip_slider_moved(int p_value) void Courtroom::on_blip_slider_moved(int p_value)
{ {
blip_player->set_volume(p_value); blip_player->setVolume(p_value);
ui_ic_chat_message->setFocus(); ui_ic_chat_message->setFocus();
} }
@ -6150,8 +6139,8 @@ void Courtroom::on_guilty_clicked()
void Courtroom::on_change_character_clicked() void Courtroom::on_change_character_clicked()
{ {
sfx_player->set_muted(true); sfx_player->setMuted(true);
blip_player->set_muted(true); blip_player->setMuted(true);
set_char_select(); set_char_select();

View File

@ -3,6 +3,12 @@
#include <QMap> #include <QMap>
#include <QString> #include <QString>
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
using AOSplitBehaviorFlags = QString::SplitBehaviorFlags;
#else
using AOSplitBehaviorFlags = Qt::SplitBehaviorFlags;
#endif
enum ServerConnectionType enum ServerConnectionType
{ {
TcpServerConnection, TcpServerConnection,
@ -18,6 +24,8 @@ struct ServerInfo
QString ip; QString ip;
int port; int port;
ServerConnectionType socket_type; ServerConnectionType socket_type;
inline QString toString() { return QString("%1 (<%2>%3:%4)").arg(name, SERVER_CONNECTION_TYPE_STRING_MAP.key(socket_type), ip, QString::number(port)); }
}; };
struct CharacterSlot struct CharacterSlot

View File

@ -1,5 +1,7 @@
#include "demoserver.h" #include "demoserver.h"
#include "datatypes.h"
DemoServer::DemoServer(QObject *parent) DemoServer::DemoServer(QObject *parent)
: QObject(parent) : QObject(parent)
{ {
@ -93,12 +95,7 @@ void DemoServer::recv_data()
{ {
QString in_data = QString::fromUtf8(client_sock->readAll()); QString in_data = QString::fromUtf8(client_sock->readAll());
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) const QStringList packet_list = in_data.split("%", AOSplitBehaviorFlags::SkipEmptyParts);
const QStringList packet_list = in_data.split("%", QString::SkipEmptyParts);
#else
const QStringList packet_list = in_data.split("%", Qt::SkipEmptyParts);
#endif
for (const QString &packet : packet_list) for (const QString &packet : packet_list)
{ {
QStringList f_contents; QStringList f_contents;

View File

@ -22,6 +22,7 @@ void Courtroom::initialize_emotes()
emote_preview = new AOEmotePreview(ao_app, this); emote_preview = new AOEmotePreview(ao_app, this);
emote_preview->setObjectName("ui_emote_preview"); emote_preview->setObjectName("ui_emote_preview");
emote_preview->resize(256, 192);
connect(ui_emote_left, &AOButton::clicked, this, &Courtroom::on_emote_left_clicked); connect(ui_emote_left, &AOButton::clicked, this, &Courtroom::on_emote_left_clicked);
connect(ui_emote_right, &AOButton::clicked, this, &Courtroom::on_emote_right_clicked); connect(ui_emote_right, &AOButton::clicked, this, &Courtroom::on_emote_right_clicked);
@ -262,7 +263,7 @@ void Courtroom::show_emote_menu(const QPoint &pos)
emote_menu->setDefaultAction(emote_menu->addAction("Preview Selected", this, [this] { emote_menu->setDefaultAction(emote_menu->addAction("Preview Selected", this, [this] {
emote_preview->show(); emote_preview->show();
emote_preview->raise(); emote_preview->raise();
emote_preview->set_widgets(); emote_preview->updateViewportGeometry();
update_emote_preview(); update_emote_preview();
})); }));
QString prefix; QString prefix;
@ -291,8 +292,8 @@ void Courtroom::preview_emote(QString f_emote)
{ {
emote_preview->show(); emote_preview->show();
emote_preview->raise(); emote_preview->raise();
emote_preview->set_widgets(); emote_preview->updateViewportGeometry();
emote_preview->play(f_emote, current_char, ui_flip->isChecked(), ui_pair_offset_spinbox->value(), ui_pair_vert_offset_spinbox->value()); emote_preview->display(current_char, f_emote, ui_flip->isChecked(), ui_pair_offset_spinbox->value(), ui_pair_vert_offset_spinbox->value());
} }
void Courtroom::on_emote_left_clicked() void Courtroom::on_emote_left_clicked()

View File

@ -17,6 +17,8 @@ int main(int argc, char *argv[])
{ {
qSetMessagePattern("%{type}: %{if-category}%{category}: %{endif}%{message}"); qSetMessagePattern("%{type}: %{if-category}%{category}: %{endif}%{message}");
qRegisterMetaType<AOPacket>();
AOApplication main_app(argc, argv); AOApplication main_app(argc, argv);
#ifdef ANDROID #ifdef ANDROID

View File

@ -0,0 +1,5 @@
#include "netconnection.h"
NetConnection::NetConnection(QObject *parent)
: QObject(parent)
{}

28
src/net/netconnection.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include "aopacket.h"
#include "datatypes.h"
#include <QObject>
class NetConnection : public QObject
{
Q_OBJECT
public:
explicit NetConnection(QObject *parent = nullptr);
virtual bool isConnected() = 0;
virtual void connectToServer(ServerInfo &server) = 0;
virtual void disconnectFromServer() = 0;
virtual void sendPacket(AOPacket packet) = 0;
Q_SIGNALS:
void connectedToServer();
void disconnectedFromServer();
void errorOccurred(QString error);
void receivedPacket(AOPacket packet);
};

View File

@ -0,0 +1,102 @@
#include "nettcpconnection.h"
NetTcpConnection::NetTcpConnection(QObject *parent)
: NetConnection(parent)
, m_socket(new QTcpSocket(this))
, m_last_state(QAbstractSocket::UnconnectedState)
{
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &NetTcpConnection::onErrorOccurred);
#else
connect(m_socket, &QTcpSocket::errorOccurred, this, &NetTcpConnection::onErrorOccurred);
#endif
connect(m_socket, &QTcpSocket::stateChanged, this, &NetTcpConnection::onStateChanged);
connect(m_socket, &QTcpSocket::readyRead, this, &NetTcpConnection::onReadyRead);
}
NetTcpConnection::~NetTcpConnection()
{
m_socket->disconnect(this);
disconnectFromServer();
}
bool NetTcpConnection::isConnected()
{
return m_last_state == QAbstractSocket::ConnectedState;
}
void NetTcpConnection::connectToServer(ServerInfo &server)
{
disconnectedFromServer();
m_socket->connectToHost(server.ip, server.port);
}
void NetTcpConnection::disconnectFromServer()
{
m_socket->abort();
}
void NetTcpConnection::sendPacket(AOPacket packet)
{
if (!isConnected())
{
qWarning().noquote() << QObject::tr("Cannot send packet, not connected to server");
return;
}
m_socket->write(packet.toString(true).toUtf8());
}
void NetTcpConnection::onErrorOccurred()
{
Q_EMIT errorOccurred(m_socket->errorString());
}
void NetTcpConnection::onStateChanged(QAbstractSocket::SocketState state)
{
m_last_state = state;
switch (state)
{
default:
break;
case QAbstractSocket::ConnectedState:
m_cached_data.clear();
Q_EMIT connectedToServer();
break;
case QAbstractSocket::UnconnectedState:
Q_EMIT disconnectFromServer();
break;
}
}
void NetTcpConnection::onReadyRead()
{
m_cached_data += QString::fromUtf8(m_socket->readAll());
if (!m_cached_data.endsWith('%'))
{
return;
}
QStringList raw_packet_list = m_cached_data.split('%', AOSplitBehaviorFlags::SkipEmptyParts);
m_cached_data.clear();
for (QString raw_packet : raw_packet_list)
{
if (!raw_packet.endsWith('#'))
{
Q_EMIT errorOccurred(QObject::tr("Malformed packet received %1").arg(raw_packet));
continue;
}
raw_packet.chop(1);
QStringList raw_content = raw_packet.split('#');
const QString header = raw_content.takeFirst();
for (QString &data : raw_content)
{
data = AOPacket::decode(data);
}
Q_EMIT receivedPacket(AOPacket(header, raw_content));
}
}

View File

@ -0,0 +1,31 @@
#pragma once
#include "aopacket.h"
#include "datatypes.h"
#include "netconnection.h"
#include <QTcpSocket>
class NetTcpConnection : public NetConnection
{
public:
NetTcpConnection(QObject *parent = nullptr);
virtual ~NetTcpConnection();
bool isConnected() override;
void connectToServer(ServerInfo &server) override;
void disconnectFromServer() override;
void sendPacket(AOPacket packet) override;
private:
QTcpSocket *m_socket;
QAbstractSocket::SocketState m_last_state;
QString m_cached_data;
private Q_SLOTS:
void onErrorOccurred();
void onStateChanged(QAbstractSocket::SocketState state);
void onReadyRead();
};

View File

@ -0,0 +1,97 @@
#include "netwebsocketconnection.h"
#include "networkmanager.h"
#include <QNetworkRequest>
#include <QUrl>
NetWebSocketConnection::NetWebSocketConnection(NetworkManager *networkManager)
: NetConnection(networkManager)
, m_network_manager(networkManager)
, m_socket(new QWebSocket(QString(), QWebSocketProtocol::VersionLatest, this))
, m_last_state(QAbstractSocket::UnconnectedState)
{
connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error), this, &NetWebSocketConnection::onError);
connect(m_socket, &QWebSocket::stateChanged, this, &NetWebSocketConnection::onStateChanged);
connect(m_socket, &QWebSocket::textMessageReceived, this, &NetWebSocketConnection::onTextMessageReceived);
}
NetWebSocketConnection::~NetWebSocketConnection()
{
m_socket->disconnect(this);
disconnectFromServer();
}
bool NetWebSocketConnection::isConnected()
{
return m_last_state == QAbstractSocket::ConnectedState;
}
void NetWebSocketConnection::connectToServer(ServerInfo &server)
{
disconnectFromServer();
QUrl url;
url.setScheme("ws");
url.setHost(server.ip);
url.setPort(server.port);
QNetworkRequest req(url);
req.setHeader(QNetworkRequest::UserAgentHeader, m_network_manager->get_user_agent());
m_socket->open(req);
}
void NetWebSocketConnection::disconnectFromServer()
{
if (isConnected())
{
m_socket->close(QWebSocketProtocol::CloseCodeGoingAway);
}
}
void NetWebSocketConnection::sendPacket(AOPacket packet)
{
m_socket->sendTextMessage(packet.toString(true));
}
void NetWebSocketConnection::onError()
{
Q_EMIT errorOccurred(m_socket->errorString());
}
void NetWebSocketConnection::onStateChanged(QAbstractSocket::SocketState state)
{
m_last_state = state;
switch (state)
{
default:
break;
case QAbstractSocket::ConnectedState:
Q_EMIT connectedToServer();
break;
case QAbstractSocket::UnconnectedState:
Q_EMIT disconnectFromServer();
break;
}
}
void NetWebSocketConnection::onTextMessageReceived(QString message)
{
if (!message.endsWith("#%"))
{
return;
}
message.chop(2);
QStringList raw_content = message.split('#');
const QString header = raw_content.takeFirst();
for (QString &data : raw_content)
{
data = AOPacket::decode(data);
}
Q_EMIT receivedPacket(AOPacket(header, raw_content));
}

View File

@ -0,0 +1,32 @@
#pragma once
#include "netconnection.h"
class NetworkManager;
#include <QWebSocket>
class NetWebSocketConnection : public NetConnection
{
public:
NetWebSocketConnection(NetworkManager *networkManager);
virtual ~NetWebSocketConnection();
bool isConnected() override;
void connectToServer(ServerInfo &server) override;
void disconnectFromServer() override;
void sendPacket(AOPacket packet) override;
private:
NetworkManager *m_network_manager;
QWebSocket *m_socket;
QAbstractSocket::SocketState m_last_state;
private Q_SLOTS:
void onError();
void onStateChanged(QAbstractSocket::SocketState state);
void onTextMessageReceived(QString message);
};

View File

@ -3,6 +3,8 @@
#include "datatypes.h" #include "datatypes.h"
#include "debug_functions.h" #include "debug_functions.h"
#include "lobby.h" #include "lobby.h"
#include "net/nettcpconnection.h"
#include "net/netwebsocketconnection.h"
#include "options.h" #include "options.h"
#include <QAbstractSocket> #include <QAbstractSocket>
@ -77,13 +79,18 @@ void NetworkManager::ms_request_finished(QNetworkReply *reply)
} }
ao_app->set_server_list(server_list); ao_app->set_server_list(server_list);
if (ao_app->lobby_constructed) if (ao_app->is_lobby_constructed())
{ {
ao_app->w_lobby->list_servers(); ao_app->w_lobby->list_servers();
} }
reply->deleteLater(); reply->deleteLater();
} }
QString NetworkManager::get_user_agent() const
{
return QStringLiteral("AttorneyOnline/%1 (Desktop)").arg(ao_app->get_version_string());
}
void NetworkManager::send_heartbeat() void NetworkManager::send_heartbeat()
{ {
// Ping the server periodically to tell the MS that you've been playing // Ping the server periodically to tell the MS that you've been playing
@ -137,57 +144,62 @@ void NetworkManager::request_document(MSDocumentType document_type, const std::f
}); });
} }
void NetworkManager::connect_to_server(ServerInfo p_server) void NetworkManager::connect_to_server(ServerInfo server)
{ {
disconnect_from_server(); disconnect_from_server();
qInfo().nospace().noquote() << "connecting to " << p_server.ip << ":" << p_server.port; qInfo().noquote() << QObject::tr("Connecting to %1").arg(server.toString());
switch (server.socket_type)
switch (p_server.socket_type)
{ {
default: default:
p_server.socket_type = TcpServerConnection; server.socket_type = TcpServerConnection;
[[fallthrough]]; [[fallthrough]];
case TcpServerConnection: case TcpServerConnection:
qInfo() << "using TCP backend"; qInfo() << "Using TCP backend.";
server_socket.tcp = new QTcpSocket(this); m_connection = new NetTcpConnection(this);
connect(server_socket.tcp, &QAbstractSocket::connected, this, [] { qDebug() << "established connection to server"; });
connect(server_socket.tcp, &QIODevice::readyRead, this, [this] { handle_server_packet(QString::fromUtf8(server_socket.tcp->readAll())); });
connect(server_socket.tcp, &QAbstractSocket::disconnected, ao_app, &AOApplication::server_disconnected);
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(server_socket.tcp, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, [this] {
#else
connect(server_socket.tcp, &QAbstractSocket::errorOccurred, this, [this] {
#endif
qCritical() << "TCP socket error:" << server_socket.tcp->errorString();
});
server_socket.tcp->connectToHost(p_server.ip, p_server.port);
break; break;
case WebSocketServerConnection: case WebSocketServerConnection:
qInfo() << "using WebSockets backend"; qInfo() << "Using WebSockets backend.";
server_socket.ws = new QWebSocket(QString(), QWebSocketProtocol::VersionLatest, this); m_connection = new NetWebSocketConnection(this);
connect(server_socket.ws, &QWebSocket::connected, this, [] { qDebug() << "established connection to server"; });
connect(server_socket.ws, &QWebSocket::textMessageReceived, this, &NetworkManager::handle_server_packet);
connect(server_socket.ws, &QWebSocket::disconnected, ao_app, &AOApplication::server_disconnected);
connect(server_socket.ws, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error), this, [this] { qCritical() << "WebSockets error:" << server_socket.ws->errorString(); });
QUrl url;
url.setScheme("ws");
url.setHost(p_server.ip);
url.setPort(p_server.port);
QNetworkRequest req(url);
req.setHeader(QNetworkRequest::UserAgentHeader, get_user_agent());
server_socket.ws->open(req);
break; break;
} }
connected = true; connect(m_connection, &NetConnection::connectedToServer, this, [] { qInfo() << "Established connection to server."; });
active_connection_type = p_server.socket_type; connect(m_connection, &NetConnection::disconnectedFromServer, ao_app, &AOApplication::server_disconnected);
connect(m_connection, &NetConnection::errorOccurred, this, [](QString error) { qCritical() << "Connection error:" << error; });
connect(m_connection, &NetConnection::receivedPacket, this, &NetworkManager::handle_server_packet);
m_connection->connectToServer(server);
}
void NetworkManager::disconnect_from_server()
{
if (m_connection)
{
m_connection->disconnectFromServer();
m_connection->deleteLater();
m_connection = nullptr;
}
}
void NetworkManager::ship_server_packet(AOPacket packet)
{
if (!m_connection)
{
qCritical() << "Failed to ship packet; no connection.";
return;
}
if (!m_connection->isConnected())
{
qCritical() << "Failed to ship packet; not connected.";
return;
}
qInfo().noquote() << "Sending packet:" << packet.toString();
m_connection->sendPacket(packet);
} }
void NetworkManager::join_to_server() void NetworkManager::join_to_server()
@ -195,98 +207,8 @@ void NetworkManager::join_to_server()
ship_server_packet(AOPacket("askchaa").toString()); ship_server_packet(AOPacket("askchaa").toString());
} }
void NetworkManager::disconnect_from_server() void NetworkManager::handle_server_packet(AOPacket packet)
{ {
if (!connected) qInfo().noquote() << "Received packet:" << packet.toString();
{ ao_app->server_packet_received(packet);
return;
}
switch (active_connection_type)
{
case TcpServerConnection:
server_socket.tcp->close();
server_socket.tcp->deleteLater();
break;
case WebSocketServerConnection:
server_socket.ws->close(QWebSocketProtocol::CloseCodeGoingAway);
server_socket.ws->deleteLater();
break;
}
connected = false;
}
void NetworkManager::ship_server_packet(AOPacket p_packet)
{
QString message = p_packet.toString(true);
switch (active_connection_type)
{
case TcpServerConnection:
server_socket.tcp->write(message.toUtf8());
break;
case WebSocketServerConnection:
server_socket.ws->sendTextMessage(message);
break;
}
}
void NetworkManager::handle_server_packet(const QString &p_data)
{
QString in_data = p_data;
if (!p_data.endsWith("%"))
{
partial_packet = true;
temp_packet += in_data;
return;
}
else
{
if (partial_packet)
{
in_data = temp_packet + in_data;
temp_packet = "";
partial_packet = false;
}
}
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
const QStringList packet_list = in_data.split("%", QString::SkipEmptyParts);
#else
const QStringList packet_list = in_data.split("%", Qt::SkipEmptyParts);
#endif
for (const QString &packet : packet_list)
{
QStringList f_contents;
// Packet should *always* end with #
if (packet.endsWith("#"))
{
f_contents = packet.chopped(1).split("#");
}
// But, if it somehow doesn't, we should still be able to handle it
else
{
f_contents = packet.split("#");
}
// Empty packets are suspicious!
if (f_contents.isEmpty())
{
qWarning() << "WARNING: Empty packet received from server, skipping...";
continue;
}
// Take the first arg as the command
QString command = f_contents.takeFirst();
for (QString &data : f_contents)
{
data = AOPacket::decode(data);
}
// The rest is contents of the packet
AOPacket f_packet(command, f_contents);
// Ship it to the server!
ao_app->server_packet_received(f_packet);
}
} }

View File

@ -2,6 +2,7 @@
#include "aoapplication.h" #include "aoapplication.h"
#include "aopacket.h" #include "aopacket.h"
#include "net/netconnection.h"
#include <QDnsLookup> #include <QDnsLookup>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
@ -28,11 +29,13 @@ public:
void connect_to_server(ServerInfo p_server); void connect_to_server(ServerInfo p_server);
void disconnect_from_server(); void disconnect_from_server();
QString get_user_agent() const;
public Q_SLOTS: public Q_SLOTS:
void get_server_list(); void get_server_list();
void ship_server_packet(AOPacket p_packet); void ship_server_packet(AOPacket packet);
void join_to_server(); void join_to_server();
void handle_server_packet(const QString &p_data); void handle_server_packet(AOPacket packet);
void request_document(MSDocumentType document_type, const std::function<void(QString)> &cb); void request_document(MSDocumentType document_type, const std::function<void(QString)> &cb);
void send_heartbeat(); void send_heartbeat();
@ -47,13 +50,7 @@ private:
AOApplication *ao_app; AOApplication *ao_app;
QNetworkAccessManager *http; QNetworkAccessManager *http;
union NetConnection *m_connection = nullptr;
{
QWebSocket *ws;
QTcpSocket *tcp;
} server_socket;
ServerConnectionType active_connection_type;
bool connected = false;
QTimer *heartbeat_timer; QTimer *heartbeat_timer;
@ -62,10 +59,5 @@ private:
const int heartbeat_interval = 60 * 5 * 1000; const int heartbeat_interval = 60 * 5 * 1000;
bool partial_packet = false;
QString temp_packet;
unsigned int s_decryptor = 5; unsigned int s_decryptor = 5;
QString get_user_agent() const { return QStringLiteral("AttorneyOnline/%1 (Desktop)").arg(ao_app->get_version_string()); }
}; };

View File

@ -24,14 +24,10 @@ void AOApplication::append_to_demofile(QString packet_string)
} }
} }
void AOApplication::server_packet_received(AOPacket p_packet) void AOApplication::server_packet_received(AOPacket packet)
{ {
QStringList f_contents_encoded = p_packet.content(); QString header = packet.header();
QString f_packet_encoded = p_packet.toString(); QStringList content = packet.content();
QString header = p_packet.header();
QStringList f_contents = p_packet.content();
QString f_packet = p_packet.toString();
bool log_to_demo = true; bool log_to_demo = true;
@ -44,7 +40,7 @@ void AOApplication::server_packet_received(AOPacket p_packet)
if (header == "decryptor") if (header == "decryptor")
{ {
if (f_contents.size() == 0) if (content.size() == 0)
{ {
return; return;
} }
@ -76,13 +72,13 @@ void AOApplication::server_packet_received(AOPacket p_packet)
} }
else if (header == "ID") else if (header == "ID")
{ {
if (f_contents.size() < 2) if (content.size() < 2)
{ {
return; return;
} }
client_id = f_contents.at(0).toInt(); client_id = content.at(0).toInt();
server_software = f_contents.at(1); server_software = content.at(1);
net_manager->server_connected(true); net_manager->server_connected(true);
@ -91,18 +87,18 @@ void AOApplication::server_packet_received(AOPacket p_packet)
} }
else if (header == "CT") else if (header == "CT")
{ {
if (!courtroom_constructed || f_contents.size() < 2) if (!is_courtroom_constructed() || content.size() < 2)
{ {
return; return;
} }
if (f_contents.size() == 3) if (content.size() == 3)
{ {
w_courtroom->append_server_chatmessage(f_contents.at(0), f_contents.at(1), f_contents.at(2)); w_courtroom->append_server_chatmessage(content.at(0), content.at(1), content.at(2));
} }
else else
{ {
w_courtroom->append_server_chatmessage(f_contents.at(0), f_contents.at(1), "0"); w_courtroom->append_server_chatmessage(content.at(0), content.at(1), "0");
} }
} }
else if (header == "FL") else if (header == "FL")
@ -125,104 +121,102 @@ void AOApplication::server_packet_received(AOPacket p_packet)
custom_blips_supported = false; custom_blips_supported = false;
log_to_demo = false; log_to_demo = false;
if (f_packet.contains("yellowtext", Qt::CaseInsensitive)) if (content.contains("yellowtext", Qt::CaseInsensitive))
{ {
yellow_text_supported = true; yellow_text_supported = true;
} }
if (f_packet.contains("prezoom", Qt::CaseInsensitive)) if (content.contains("prezoom", Qt::CaseInsensitive))
{ {
prezoom_supported = true; prezoom_supported = true;
} }
if (f_packet.contains("flipping", Qt::CaseInsensitive)) if (content.contains("flipping", Qt::CaseInsensitive))
{ {
flipping_supported = true; flipping_supported = true;
} }
if (f_packet.contains("customobjections", Qt::CaseInsensitive)) if (content.contains("customobjections", Qt::CaseInsensitive))
{ {
custom_objection_supported = true; custom_objection_supported = true;
} }
if (f_packet.contains("deskmod", Qt::CaseInsensitive)) if (content.contains("deskmod", Qt::CaseInsensitive))
{ {
desk_mod_supported = true; desk_mod_supported = true;
} }
if (f_packet.contains("evidence", Qt::CaseInsensitive)) if (content.contains("evidence", Qt::CaseInsensitive))
{ {
evidence_supported = true; evidence_supported = true;
} }
if (f_packet.contains("cccc_ic_support", Qt::CaseInsensitive)) if (content.contains("cccc_ic_support", Qt::CaseInsensitive))
{ {
cccc_ic_supported = true; cccc_ic_supported = true;
} }
if (f_packet.contains("arup", Qt::CaseInsensitive)) if (content.contains("arup", Qt::CaseInsensitive))
{ {
arup_supported = true; arup_supported = true;
} }
if (f_packet.contains("casing_alerts", Qt::CaseInsensitive)) if (content.contains("casing_alerts", Qt::CaseInsensitive))
{ {
casing_alerts_supported = true; casing_alerts_supported = true;
} }
if (f_packet.contains("modcall_reason", Qt::CaseInsensitive)) if (content.contains("modcall_reason", Qt::CaseInsensitive))
{ {
modcall_reason_supported = true; modcall_reason_supported = true;
} }
if (f_packet.contains("looping_sfx", Qt::CaseInsensitive)) if (content.contains("looping_sfx", Qt::CaseInsensitive))
{ {
looping_sfx_supported = true; looping_sfx_supported = true;
} }
if (f_packet.contains("additive", Qt::CaseInsensitive)) if (content.contains("additive", Qt::CaseInsensitive))
{ {
additive_supported = true; additive_supported = true;
} }
if (f_packet.contains("effects", Qt::CaseInsensitive)) if (content.contains("effects", Qt::CaseInsensitive))
{ {
effects_supported = true; effects_supported = true;
} }
if (f_packet.contains("y_offset", Qt::CaseInsensitive)) if (content.contains("y_offset", Qt::CaseInsensitive))
{ {
y_offset_supported = true; y_offset_supported = true;
} }
if (f_packet.contains("expanded_desk_mods", Qt::CaseInsensitive)) if (content.contains("expanded_desk_mods", Qt::CaseInsensitive))
{ {
expanded_desk_mods_supported = true; expanded_desk_mods_supported = true;
} }
if (f_packet.contains("auth_packet", Qt::CaseInsensitive)) if (content.contains("auth_packet", Qt::CaseInsensitive))
{ {
auth_packet_supported = true; auth_packet_supported = true;
} }
if (f_packet.contains("custom_blips", Qt::CaseInsensitive)) if (content.contains("custom_blips", Qt::CaseInsensitive))
{ {
custom_blips_supported = true; custom_blips_supported = true;
} }
log_to_demo = false; log_to_demo = false;
} }
else if (header == "PN") else if (header == "PN")
{ {
if (!lobby_constructed || f_contents.size() < 2) if (!is_lobby_constructed() || content.size() < 2)
{ {
return; return;
} }
w_lobby->set_player_count(f_contents.at(0).toInt(), f_contents.at(1).toInt()); w_lobby->set_player_count(content.at(0).toInt(), content.at(1).toInt());
if (f_contents.size() >= 3) if (content.size() >= 3)
{ {
w_lobby->set_server_description(f_contents.at(2)); w_lobby->set_server_description(content.at(2));
} }
log_to_demo = false; log_to_demo = false;
} }
else if (header == "SI") else if (header == "SI")
{ {
if (!lobby_constructed || f_contents.size() != 3) if (!is_lobby_constructed() || content.size() != 3)
{ {
return; return;
} }
char_list_size = f_contents.at(0).toInt(); char_list_size = content.at(0).toInt();
evidence_list_size = f_contents.at(1).toInt(); evidence_list_size = content.at(1).toInt();
music_list_size = f_contents.at(2).toInt(); music_list_size = content.at(2).toInt();
if (char_list_size < 0 || evidence_list_size < 0 || music_list_size < 0) if (char_list_size < 0 || evidence_list_size < 0 || music_list_size < 0)
{ {
@ -272,7 +266,7 @@ void AOApplication::server_packet_received(AOPacket p_packet)
break; break;
} }
if (courtroom_constructed) if (is_courtroom_constructed())
{ {
w_courtroom->set_window_title(window_title); w_courtroom->set_window_title(window_title);
} }
@ -301,14 +295,14 @@ void AOApplication::server_packet_received(AOPacket p_packet)
} }
else if (header == "CharsCheck") else if (header == "CharsCheck")
{ {
if (!courtroom_constructed) if (!is_courtroom_constructed())
{ {
return; return;
} }
for (int n_char = 0; n_char < f_contents.size(); ++n_char) for (int n_char = 0; n_char < content.size(); ++n_char)
{ {
if (f_contents.at(n_char) == "-1") if (content.at(n_char) == "-1")
{ {
w_courtroom->set_taken(n_char, true); w_courtroom->set_taken(n_char, true);
} }
@ -322,14 +316,14 @@ void AOApplication::server_packet_received(AOPacket p_packet)
else if (header == "SC") else if (header == "SC")
{ {
if (!courtroom_constructed) if (!is_courtroom_constructed())
{ {
return; return;
} }
w_courtroom->clear_chars(); w_courtroom->clear_chars();
for (int n_element = 0; n_element < f_contents.size(); ++n_element) for (int n_element = 0; n_element < content.size(); ++n_element)
{ {
QStringList sub_elements = f_contents.at(n_element).split("&"); QStringList sub_elements = content.at(n_element).split("&");
for (QString &sub_element : sub_elements) for (QString &sub_element : sub_elements)
{ {
sub_element = AOPacket::decode(sub_element); sub_element = AOPacket::decode(sub_element);
@ -359,7 +353,7 @@ void AOApplication::server_packet_received(AOPacket p_packet)
} }
else if (header == "SM") else if (header == "SM")
{ {
if (!courtroom_constructed || courtroom_loaded) if (!is_courtroom_constructed() || courtroom_loaded)
{ {
return; return;
} }
@ -367,25 +361,25 @@ void AOApplication::server_packet_received(AOPacket p_packet)
bool musics_time = false; bool musics_time = false;
int areas = 0; int areas = 0;
for (int n_element = 0; n_element < f_contents.size(); ++n_element) for (int n_element = 0; n_element < content.size(); ++n_element)
{ {
++loaded_music; ++loaded_music;
if (musics_time) if (musics_time)
{ {
w_courtroom->append_music(f_contents.at(n_element)); w_courtroom->append_music(content.at(n_element));
} }
else else
{ {
if (f_contents.at(n_element).endsWith(".wav") || f_contents.at(n_element).endsWith(".mp3") || f_contents.at(n_element).endsWith(".mp4") || f_contents.at(n_element).endsWith(".ogg") || f_contents.at(n_element).endsWith(".opus")) if (content.at(n_element).endsWith(".wav") || content.at(n_element).endsWith(".mp3") || content.at(n_element).endsWith(".mp4") || content.at(n_element).endsWith(".ogg") || content.at(n_element).endsWith(".opus"))
{ {
musics_time = true; musics_time = true;
w_courtroom->fix_last_area(); w_courtroom->fix_last_area();
w_courtroom->append_music(f_contents.at(n_element)); w_courtroom->append_music(content.at(n_element));
areas--; areas--;
} }
else else
{ {
w_courtroom->append_area(f_contents.at(n_element)); w_courtroom->append_area(content.at(n_element));
areas++; areas++;
} }
} }
@ -401,16 +395,16 @@ void AOApplication::server_packet_received(AOPacket p_packet)
} }
else if (header == "FM") // Fetch music ONLY else if (header == "FM") // Fetch music ONLY
{ {
if (!courtroom_constructed) if (!is_courtroom_constructed())
{ {
return; return;
} }
w_courtroom->clear_music(); w_courtroom->clear_music();
for (int n_element = 0; n_element < f_contents.size(); ++n_element) for (int n_element = 0; n_element < content.size(); ++n_element)
{ {
w_courtroom->append_music(f_contents.at(n_element)); w_courtroom->append_music(content.at(n_element));
} }
w_courtroom->list_music(); w_courtroom->list_music();
@ -418,7 +412,7 @@ void AOApplication::server_packet_received(AOPacket p_packet)
} }
else if (header == "FA") // Fetch areas ONLY else if (header == "FA") // Fetch areas ONLY
{ {
if (!courtroom_constructed) if (!is_courtroom_constructed())
{ {
return; return;
} }
@ -426,9 +420,9 @@ void AOApplication::server_packet_received(AOPacket p_packet)
w_courtroom->clear_areas(); w_courtroom->clear_areas();
w_courtroom->arup_clear(); w_courtroom->arup_clear();
for (int n_element = 0; n_element < f_contents.size(); ++n_element) for (int n_element = 0; n_element < content.size(); ++n_element)
{ {
w_courtroom->append_area(f_contents.at(n_element)); w_courtroom->append_area(content.at(n_element));
w_courtroom->arup_append(0, "Unknown", "Unknown", "Unknown"); w_courtroom->arup_append(0, "Unknown", "Unknown", "Unknown");
} }
@ -437,7 +431,7 @@ void AOApplication::server_packet_received(AOPacket p_packet)
} }
else if (header == "DONE") else if (header == "DONE")
{ {
if (!courtroom_constructed) if (!is_courtroom_constructed())
{ {
return; return;
} }
@ -452,99 +446,99 @@ void AOApplication::server_packet_received(AOPacket p_packet)
} }
else if (header == "BN") else if (header == "BN")
{ {
if (!courtroom_constructed || f_contents.isEmpty()) if (!is_courtroom_constructed() || content.isEmpty())
{ {
return; return;
} }
if (f_contents.size() >= 2) if (content.size() >= 2)
{ {
// We have a pos included in the background packet! // We have a pos included in the background packet!
if (!f_contents.at(1).isEmpty()) if (!content.at(1).isEmpty())
{ {
// Not touching it when its empty. // Not touching it when its empty.
w_courtroom->set_side(f_contents.at(1)); w_courtroom->set_side(content.at(1));
} }
} }
w_courtroom->set_background(f_contents.at(0), f_contents.size() >= 2); w_courtroom->set_background(content.at(0), content.size() >= 2);
} }
else if (header == "SP") else if (header == "SP")
{ {
if (!courtroom_constructed || f_contents.isEmpty()) if (!is_courtroom_constructed() || content.isEmpty())
{ {
return; return;
} }
// We were sent a "set position" packet // We were sent a "set position" packet
w_courtroom->set_side(f_contents.at(0)); w_courtroom->set_side(content.at(0));
} }
else if (header == "SD") // Send pos dropdown else if (header == "SD") // Send pos dropdown
{ {
if (!courtroom_constructed || f_contents.isEmpty()) if (!is_courtroom_constructed() || content.isEmpty())
{ {
return; return;
} }
w_courtroom->set_pos_dropdown(f_contents.at(0).split("*")); w_courtroom->set_pos_dropdown(content.at(0).split("*"));
} }
// server accepting char request(CC) packet // server accepting char request(CC) packet
else if (header == "PV") else if (header == "PV")
{ {
if (!courtroom_constructed || f_contents.size() < 3) if (!is_courtroom_constructed() || content.size() < 3)
{ {
return; return;
} }
// For some reason, args 0 and 1 are not used (from tsu3 they're client ID and a string "CID") // For some reason, args 0 and 1 are not used (from tsu3 they're client ID and a string "CID")
w_courtroom->enter_courtroom(); w_courtroom->enter_courtroom();
w_courtroom->set_courtroom_size(); w_courtroom->set_courtroom_size();
w_courtroom->update_character(f_contents.at(2).toInt()); w_courtroom->update_character(content.at(2).toInt());
} }
else if (header == "MS") else if (header == "MS")
{ {
if (courtroom_constructed && courtroom_loaded) if (is_courtroom_constructed() && courtroom_loaded)
{ {
w_courtroom->chatmessage_enqueue(p_packet.content()); w_courtroom->chatmessage_enqueue(packet.content());
} }
} }
else if (header == "MC") else if (header == "MC")
{ {
if (courtroom_constructed && courtroom_loaded) if (is_courtroom_constructed() && courtroom_loaded)
{ {
w_courtroom->handle_song(&p_packet.content()); w_courtroom->handle_song(&packet.content());
} }
} }
else if (header == "RT") else if (header == "RT")
{ {
if (f_contents.isEmpty()) if (content.isEmpty())
{ {
return; return;
} }
if (courtroom_constructed) if (is_courtroom_constructed())
{ {
if (f_contents.size() == 1) if (content.size() == 1)
{ {
w_courtroom->handle_wtce(f_contents.at(0), 0); w_courtroom->handle_wtce(content.at(0), 0);
} }
else if (f_contents.size() >= 2) else if (content.size() >= 2)
{ {
w_courtroom->handle_wtce(f_contents.at(0), f_contents.at(1).toInt()); w_courtroom->handle_wtce(content.at(0), content.at(1).toInt());
} }
} }
} }
else if (header == "HP") else if (header == "HP")
{ {
if (courtroom_constructed && f_contents.size() >= 2) if (is_courtroom_constructed() && content.size() >= 2)
{ {
w_courtroom->set_hp_bar(f_contents.at(0).toInt(), f_contents.at(1).toInt()); w_courtroom->set_hp_bar(content.at(0).toInt(), content.at(1).toInt());
} }
} }
else if (header == "LE") else if (header == "LE")
{ {
if (courtroom_constructed) if (is_courtroom_constructed())
{ {
QVector<EvidenceItem> f_evi_list; QVector<EvidenceItem> f_evi_list;
for (QString f_string : f_contents_encoded) for (QString f_string : packet.content())
{ {
QStringList sub_contents = f_string.split("&"); QStringList sub_contents = f_string.split("&");
if (sub_contents.size() < 3) if (sub_contents.size() < 3)
@ -572,12 +566,12 @@ void AOApplication::server_packet_received(AOPacket p_packet)
} }
else if (header == "ARUP") else if (header == "ARUP")
{ {
if (courtroom_constructed && !f_contents.isEmpty()) if (is_courtroom_constructed() && !content.isEmpty())
{ {
int arup_type = f_contents.at(0).toInt(); int arup_type = content.at(0).toInt();
for (int n_element = 1; n_element < f_contents.size(); n_element++) for (int n_element = 1; n_element < content.size(); n_element++)
{ {
w_courtroom->arup_modify(arup_type, n_element - 1, f_contents.at(n_element)); w_courtroom->arup_modify(arup_type, n_element - 1, content.at(n_element));
} }
w_courtroom->list_areas(); w_courtroom->list_areas();
} }
@ -585,41 +579,41 @@ void AOApplication::server_packet_received(AOPacket p_packet)
} }
else if (header == "IL") else if (header == "IL")
{ {
if (courtroom_constructed && !f_contents.isEmpty()) if (is_courtroom_constructed() && !content.isEmpty())
{ {
w_courtroom->set_ip_list(f_contents.at(0)); w_courtroom->set_ip_list(content.at(0));
} }
log_to_demo = false; log_to_demo = false;
} }
else if (header == "MU") else if (header == "MU")
{ {
if (courtroom_constructed && !f_contents.isEmpty()) if (is_courtroom_constructed() && !content.isEmpty())
{ {
w_courtroom->set_mute(true, f_contents.at(0).toInt()); w_courtroom->set_mute(true, content.at(0).toInt());
} }
log_to_demo = false; log_to_demo = false;
} }
else if (header == "UM") else if (header == "UM")
{ {
if (courtroom_constructed && !f_contents.isEmpty()) if (is_courtroom_constructed() && !content.isEmpty())
{ {
w_courtroom->set_mute(false, f_contents.at(0).toInt()); w_courtroom->set_mute(false, content.at(0).toInt());
log_to_demo = false; log_to_demo = false;
} }
} }
else if (header == "BB") else if (header == "BB")
{ {
if (courtroom_constructed && !f_contents.isEmpty()) if (is_courtroom_constructed() && !content.isEmpty())
{ {
call_notice(f_contents.at(0)); call_notice(content.at(0));
} }
log_to_demo = false; log_to_demo = false;
} }
else if (header == "KK") else if (header == "KK")
{ {
if (courtroom_constructed && !f_contents.isEmpty()) if (is_courtroom_constructed() && !content.isEmpty())
{ {
call_notice(tr("You have been kicked from the server.\nReason: %1").arg(f_contents.at(0))); call_notice(tr("You have been kicked from the server.\nReason: %1").arg(content.at(0)));
construct_lobby(); construct_lobby();
destruct_courtroom(); destruct_courtroom();
} }
@ -627,9 +621,9 @@ void AOApplication::server_packet_received(AOPacket p_packet)
} }
else if (header == "KB") else if (header == "KB")
{ {
if (courtroom_constructed && !f_contents.isEmpty()) if (is_courtroom_constructed() && !content.isEmpty())
{ {
call_notice(tr("You have been banned from the server.\nReason: %1").arg(f_contents.at(0))); call_notice(tr("You have been banned from the server.\nReason: %1").arg(content.at(0)));
construct_lobby(); construct_lobby();
destruct_courtroom(); destruct_courtroom();
} }
@ -637,46 +631,46 @@ void AOApplication::server_packet_received(AOPacket p_packet)
} }
else if (header == "BD") else if (header == "BD")
{ {
if (f_contents.isEmpty()) if (content.isEmpty())
{ {
return; return;
} }
call_notice(tr("You are banned on this server.\nReason: %1").arg(f_contents.at(0))); call_notice(tr("You are banned on this server.\nReason: %1").arg(content.at(0)));
log_to_demo = false; log_to_demo = false;
} }
else if (header == "ZZ") else if (header == "ZZ")
{ {
if (courtroom_constructed && !f_contents.isEmpty()) if (is_courtroom_constructed() && !content.isEmpty())
{ {
w_courtroom->mod_called(f_contents.at(0)); w_courtroom->mod_called(content.at(0));
} }
} }
else if (header == "TI") else if (header == "TI")
{ // Timer packet { // Timer packet
if (!courtroom_constructed || f_contents.size() < 2) if (!is_courtroom_constructed() || content.size() < 2)
{ {
return; return;
} }
// Timer ID is reserved as argument 0 // Timer ID is reserved as argument 0
int id = f_contents.at(0).toInt(); int id = content.at(0).toInt();
// Type 0 = start/resume/sync timer at time // Type 0 = start/resume/sync timer at time
// Type 1 = pause timer at time // Type 1 = pause timer at time
// Type 2 = show timer // Type 2 = show timer
// Type 3 = hide timer // Type 3 = hide timer
int type = f_contents.at(1).toInt(); int type = content.at(1).toInt();
if (type == 0 || type == 1) if (type == 0 || type == 1)
{ {
if (f_contents.size() < 3) if (content.size() < 3)
{ {
return; return;
} }
// The time as displayed on the clock, in milliseconds. // The time as displayed on the clock, in milliseconds.
// If the number received is negative, stop the timer. // If the number received is negative, stop the timer.
qint64 timer_value = f_contents.at(2).toLongLong(); qint64 timer_value = content.at(2).toLongLong();
if (timer_value > 0) if (timer_value > 0)
{ {
if (type == 0) if (type == 0)
@ -706,7 +700,7 @@ void AOApplication::server_packet_received(AOPacket p_packet)
} }
else if (header == "CHECK") else if (header == "CHECK")
{ {
if (!courtroom_constructed) if (!is_courtroom_constructed())
{ {
return; return;
} }
@ -722,12 +716,12 @@ void AOApplication::server_packet_received(AOPacket p_packet)
// Subtheme packet // Subtheme packet
else if (header == "ST") else if (header == "ST")
{ {
if (!courtroom_constructed || f_contents.isEmpty()) if (!is_courtroom_constructed() || content.isEmpty())
{ {
return; return;
} }
// Subtheme reserved as argument 0 // Subtheme reserved as argument 0
subtheme = f_contents.at(0); subtheme = content.at(0);
// Check if we have subthemes set to "server" // Check if we have subthemes set to "server"
if (Options::getInstance().settingsSubTheme().toLower() != "server") if (Options::getInstance().settingsSubTheme().toLower() != "server")
@ -737,7 +731,7 @@ void AOApplication::server_packet_received(AOPacket p_packet)
} }
// Reload theme request // Reload theme request
if (f_contents.size() > 1 && f_contents.at(1) == "1") if (content.size() > 1 && content.at(1) == "1")
{ {
Options::getInstance().setServerSubTheme(subtheme); Options::getInstance().setServerSubTheme(subtheme);
w_courtroom->on_reload_theme_clicked(); w_courtroom->on_reload_theme_clicked();
@ -746,15 +740,15 @@ void AOApplication::server_packet_received(AOPacket p_packet)
// Auth packet // Auth packet
else if (header == "AUTH") else if (header == "AUTH")
{ {
if (!courtroom_constructed || !auth_packet_supported || f_contents.isEmpty()) if (!is_courtroom_constructed() || !auth_packet_supported || content.isEmpty())
{ {
return; return;
} }
bool ok; bool ok;
int authenticated = f_contents.at(0).toInt(&ok); int authenticated = content.at(0).toInt(&ok);
if (!ok) if (!ok)
{ {
qWarning() << "Malformed AUTH packet! Contents:" << f_contents.at(0); qWarning() << "Malformed AUTH packet! Contents:" << content.at(0);
} }
w_courtroom->on_authentication_state_received(authenticated); w_courtroom->on_authentication_state_received(authenticated);
@ -762,12 +756,12 @@ void AOApplication::server_packet_received(AOPacket p_packet)
} }
else if (header == "JD") else if (header == "JD")
{ {
if (!courtroom_constructed || f_contents.isEmpty()) if (!is_courtroom_constructed() || content.isEmpty())
{ {
return; return;
} }
bool ok; bool ok;
Courtroom::JudgeState state = static_cast<Courtroom::JudgeState>(f_contents.at(0).toInt(&ok)); Courtroom::JudgeState state = static_cast<Courtroom::JudgeState>(content.at(0).toInt(&ok));
if (!ok) if (!ok)
{ {
return; // ignore malformed packet return; // ignore malformed packet
@ -786,11 +780,11 @@ void AOApplication::server_packet_received(AOPacket p_packet)
// AssetURL Packet // AssetURL Packet
else if (header == "ASS") else if (header == "ASS")
{ {
if (f_contents.size() > 1 || f_contents.isEmpty()) if (content.size() > 1 || content.isEmpty())
{ // This can never be more than one link. { // This can never be more than one link.
return; return;
} }
QUrl t_asset_url = QUrl::fromPercentEncoding(f_contents.at(0).toUtf8()); QUrl t_asset_url = QUrl::fromPercentEncoding(content.at(0).toUtf8());
if (t_asset_url.isValid()) if (t_asset_url.isValid())
{ {
asset_url = t_asset_url.toString(); asset_url = t_asset_url.toString();
@ -799,7 +793,7 @@ void AOApplication::server_packet_received(AOPacket p_packet)
if (log_to_demo) if (log_to_demo)
{ {
append_to_demofile(f_packet_encoded); append_to_demofile(packet.toString(true));
} }
} }

View File

@ -66,7 +66,7 @@ VPath AOApplication::get_music_path(QString p_song)
VPath AOApplication::get_background_path(QString p_file) VPath AOApplication::get_background_path(QString p_file)
{ {
if (courtroom_constructed) if (is_courtroom_constructed())
{ {
return VPath("background/" + w_courtroom->get_current_background() + "/" + p_file); return VPath("background/" + w_courtroom->get_current_background() + "/" + p_file);
} }

View File

@ -447,13 +447,15 @@ QString AOApplication::get_showname(QString p_char, int p_emote)
QString f_result = read_char_ini(p_char, "showname", "Options"); QString f_result = read_char_ini(p_char, "showname", "Options");
QString f_needed = read_char_ini(p_char, "needs_showname", "Options"); QString f_needed = read_char_ini(p_char, "needs_showname", "Options");
if (p_emote != -1) { if (p_emote != -1)
int override_idx = {
read_char_ini(p_char, QString::number(p_emote + 1), "OptionsN").toInt(); int override_idx = read_char_ini(p_char, QString::number(p_emote + 1), "OptionsN").toInt();
if (override_idx > 0) { if (override_idx > 0)
{
QString override_key = "Options" + QString::number(override_idx); QString override_key = "Options" + QString::number(override_idx);
QString temp_f_result = read_char_ini(p_char, "showname", override_key); QString temp_f_result = read_char_ini(p_char, "showname", override_key);
if (!temp_f_result.isEmpty()) { if (!temp_f_result.isEmpty())
{
f_result = temp_f_result; f_result = temp_f_result;
} }
} }
@ -485,30 +487,38 @@ QString AOApplication::get_blipname(QString p_char, int p_emote)
{ {
QString f_result = read_char_ini(p_char, "blips", "Options"); QString f_result = read_char_ini(p_char, "blips", "Options");
if (p_emote != -1) { if (p_emote != -1)
int override_idx = {
read_char_ini(p_char, QString::number(p_emote + 1), "OptionsN").toInt(); int override_idx = read_char_ini(p_char, QString::number(p_emote + 1), "OptionsN").toInt();
if (override_idx > 0) { if (override_idx > 0)
{
QString override_key = "Options" + QString::number(override_idx); QString override_key = "Options" + QString::number(override_idx);
QString temp_f_result = read_char_ini(p_char, "blips", override_key); QString temp_f_result = read_char_ini(p_char, "blips", override_key);
if (!temp_f_result.isEmpty()) { if (!temp_f_result.isEmpty())
{
f_result = temp_f_result; f_result = temp_f_result;
} }
} }
} }
if (f_result == "") { if (f_result == "")
f_result = {
read_char_ini(p_char, "gender", "Options"); // not very PC, FanatSors f_result = read_char_ini(p_char, "gender", "Options"); // not very PC, FanatSors
if (f_result == "") f_result = "male"; if (f_result == "")
{
f_result = "male";
}
} }
return f_result; return f_result;
} }
QString AOApplication::get_blips(QString p_blipname) QString AOApplication::get_blips(QString p_blipname)
{ {
if (!file_exists(get_sfx_suffix(get_sounds_path(p_blipname)))) { if (!file_exists(get_sfx_suffix(get_sounds_path(p_blipname))))
{
if (file_exists(get_sfx_suffix(get_sounds_path("../blips/" + p_blipname)))) if (file_exists(get_sfx_suffix(get_sounds_path("../blips/" + p_blipname))))
{
return "../blips/" + p_blipname; // Return the cool kids variant return "../blips/" + p_blipname; // Return the cool kids variant
}
return "sfx-blip" + p_blipname; // Return legacy variant return "sfx-blip" + p_blipname; // Return legacy variant
} }

View File

@ -4,6 +4,7 @@
#include "gui_utils.h" #include "gui_utils.h"
#include "options.h" #include "options.h"
#include <QDebug>
#include <QFile> #include <QFile>
#include <QUiLoader> #include <QUiLoader>
#include <QVBoxLayout> #include <QVBoxLayout>