mirror of
https://github.com/simple64/simple64.git
synced 2026-07-24 23:54:00 +02:00
351 lines
12 KiB
C++
351 lines
12 KiB
C++
#include "joinroom.h"
|
|
#include "waitroom.h"
|
|
#include "../mainwindow.h"
|
|
#include "../interface/core_commands.h"
|
|
#include "../version.h"
|
|
#include "netplay_common.h"
|
|
#include <QGridLayout>
|
|
#include <QLabel>
|
|
#include <QJsonObject>
|
|
#include <QJsonDocument>
|
|
#include <QHeaderView>
|
|
#include <QMessageBox>
|
|
#include <QFileDialog>
|
|
#include <QInputDialog>
|
|
#include <QLabel>
|
|
|
|
JoinRoom::JoinRoom(QWidget *parent)
|
|
: QDialog(parent)
|
|
{
|
|
setMinimumWidth(1000);
|
|
setMinimumHeight(500);
|
|
QGridLayout *layout = new QGridLayout(this);
|
|
layout->setColumnMinimumWidth(1, 500);
|
|
|
|
QRegularExpression rx("[a-zA-Z0-9]+");
|
|
QValidator *validator = new QRegularExpressionValidator(rx, this);
|
|
|
|
playerNameEdit = new QLineEdit(this);
|
|
playerNameEdit->setValidator(validator);
|
|
playerNameEdit->setMaxLength(30);
|
|
if (w->getSettings()->contains("netplay_name"))
|
|
playerNameEdit->setText(w->getSettings()->value("netplay_name").toString());
|
|
playerNameEdit->setPlaceholderText("Player Name");
|
|
layout->addWidget(playerNameEdit, 0, 0);
|
|
|
|
pingLabel = new QLabel("Ping: (Calculating)", this);
|
|
layout->addWidget(pingLabel, 0, 2);
|
|
|
|
serverChooser = new QComboBox(this);
|
|
serverChooser->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
|
connect(serverChooser, &QComboBox::currentIndexChanged, this, &JoinRoom::serverChanged);
|
|
|
|
layout->addWidget(serverChooser, 0, 3);
|
|
|
|
refreshButton = new QPushButton(this);
|
|
refreshButton->setText("Refresh");
|
|
layout->addWidget(refreshButton, 0, 4);
|
|
connect(refreshButton, &QPushButton::released, this, &JoinRoom::refresh);
|
|
|
|
listWidget = new QTableWidget(this);
|
|
listWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
resetList();
|
|
|
|
layout->addWidget(listWidget, 1, 0, 1, 5);
|
|
|
|
passwordEdit = new QLineEdit(this);
|
|
passwordEdit->setPlaceholderText("Password (if required)");
|
|
layout->addWidget(passwordEdit, 2, 0);
|
|
|
|
joinButton = new QPushButton(this);
|
|
connect(joinButton, &QPushButton::released, this, &JoinRoom::joinGame);
|
|
joinButton->setText("Join Game");
|
|
layout->addWidget(joinButton, 2, 2, 1, 2);
|
|
|
|
setLayout(layout);
|
|
|
|
connect(&manager, &QNetworkAccessManager::finished, this,
|
|
&JoinRoom::downloadFinished);
|
|
|
|
connect(this, &JoinRoom::finished, this, &JoinRoom::onFinished);
|
|
|
|
QNetworkRequest request(QUrl(QStringLiteral("https://m64p.s3.amazonaws.com/servers.json")));
|
|
manager.get(request);
|
|
|
|
broadcastSocket.bind(QHostAddress(QHostAddress::AnyIPv4), 0);
|
|
connect(&broadcastSocket, &QUdpSocket::readyRead, this, &JoinRoom::processBroadcast);
|
|
QByteArray multirequest;
|
|
multirequest.append(1);
|
|
broadcastSocket.writeDatagram(multirequest, QHostAddress::Broadcast, 45000);
|
|
|
|
launched = 0;
|
|
}
|
|
|
|
void JoinRoom::processBroadcast()
|
|
{
|
|
while (broadcastSocket.hasPendingDatagrams())
|
|
{
|
|
QNetworkDatagram datagram = broadcastSocket.receiveDatagram();
|
|
QByteArray incomingData = datagram.data();
|
|
QJsonDocument json_doc = QJsonDocument::fromJson(incomingData);
|
|
QJsonObject json = json_doc.object();
|
|
QStringList servers = json.keys();
|
|
for (int i = 0; i < servers.size(); ++i)
|
|
serverChooser->addItem(servers.at(i), json.value(servers.at(i)).toString());
|
|
}
|
|
}
|
|
|
|
void JoinRoom::onFinished(int)
|
|
{
|
|
broadcastSocket.close();
|
|
(*CoreDoCommand)(M64CMD_ROM_CLOSE, 0, NULL);
|
|
if (!launched && webSocket)
|
|
{
|
|
webSocket->close();
|
|
webSocket->deleteLater();
|
|
}
|
|
}
|
|
|
|
void JoinRoom::resetList()
|
|
{
|
|
row = 0;
|
|
rooms.clear();
|
|
listWidget->clear();
|
|
listWidget->setColumnCount(5);
|
|
listWidget->setRowCount(row);
|
|
QStringList headers;
|
|
headers.append("Room Name");
|
|
headers.append("Game Name");
|
|
headers.append("Game MD5");
|
|
headers.append("Password Protected");
|
|
headers.append("Cheats Enabled");
|
|
listWidget->setHorizontalHeaderLabels(headers);
|
|
listWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
|
}
|
|
|
|
void JoinRoom::downloadFinished(QNetworkReply *reply)
|
|
{
|
|
if (!reply->error())
|
|
{
|
|
QJsonDocument json_doc = QJsonDocument::fromJson(reply->readAll());
|
|
QJsonObject json = json_doc.object();
|
|
QStringList servers = json.keys();
|
|
for (int i = 0; i < servers.size(); ++i)
|
|
serverChooser->addItem(servers.at(i), json.value(servers.at(i)).toString());
|
|
serverChooser->addItem(QString("Custom"), QString("Custom"));
|
|
}
|
|
|
|
reply->deleteLater();
|
|
}
|
|
|
|
void JoinRoom::refresh()
|
|
{
|
|
serverChanged(serverChooser->currentIndex());
|
|
}
|
|
|
|
void JoinRoom::joinGame()
|
|
{
|
|
QMessageBox msgBox;
|
|
if (webSocket && webSocket->state() != QAbstractSocket::ConnectedState)
|
|
{
|
|
msgBox.setText("Could not connect to server");
|
|
msgBox.exec();
|
|
return;
|
|
}
|
|
if (listWidget->currentRow() < 0)
|
|
{
|
|
msgBox.setText("You haven't selected a game to join");
|
|
msgBox.exec();
|
|
return;
|
|
}
|
|
|
|
filename = QFileDialog::getOpenFileName(this,
|
|
tr("Open ROM"), w->getSettings()->value("ROMdir").toString(), tr("ROM Files (*.n64 *.N64 *.z64 *.Z64 *.v64 *.V64 *.rom *.ROM *.zip *.ZIP *.7z)"));
|
|
if (!filename.isNull())
|
|
{
|
|
if (loadROM(filename) == M64ERR_SUCCESS)
|
|
{
|
|
int room_port = rooms.at(listWidget->currentRow()).value("port").toInt();
|
|
m64p_rom_settings rom_settings;
|
|
(*CoreDoCommand)(M64CMD_ROM_GET_SETTINGS, sizeof(rom_settings), &rom_settings);
|
|
|
|
joinButton->setEnabled(false);
|
|
QJsonObject json;
|
|
QJsonObject room;
|
|
json.insert("type", "request_join_room");
|
|
json.insert("player_name", playerNameEdit->text());
|
|
json.insert("client_sha", QStringLiteral(GUI_VERSION));
|
|
room.insert("password", passwordEdit->text());
|
|
room.insert("MD5", QString(rom_settings.MD5));
|
|
room.insert("port", room_port);
|
|
json.insert("room", room);
|
|
|
|
QJsonDocument json_doc(json);
|
|
webSocket->sendTextMessage(json_doc.toJson());
|
|
}
|
|
else
|
|
{
|
|
msgBox.setText("Could not open ROM");
|
|
msgBox.exec();
|
|
}
|
|
}
|
|
}
|
|
|
|
void JoinRoom::serverChanged(int index)
|
|
{
|
|
QString serverName = serverChooser->itemData(index).toString();
|
|
if (webSocket)
|
|
{
|
|
webSocket->close();
|
|
webSocket->deleteLater();
|
|
webSocket = NULL;
|
|
}
|
|
|
|
if (serverName == "Custom")
|
|
{
|
|
bool ok;
|
|
customServerAddress = QInputDialog::getText(this, "Custom Netplay Server", "IP Address / Host:", QLineEdit::Normal, "", &ok);
|
|
|
|
if (!ok || customServerAddress.isEmpty())
|
|
{
|
|
customServerAddress.clear();
|
|
resetList();
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
customServerAddress.prepend("ws://");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
customServerAddress.clear();
|
|
}
|
|
|
|
pingLabel->setText("Ping: (Calculating)");
|
|
|
|
resetList();
|
|
webSocket = new QWebSocket();
|
|
connect(webSocket, &QWebSocket::connected, this, &JoinRoom::onConnected);
|
|
connectionTimer = new QTimer(this);
|
|
connectionTimer->setSingleShot(true);
|
|
connectionTimer->start(5000);
|
|
connect(webSocket, &QWebSocket::disconnected, connectionTimer, &QTimer::stop);
|
|
connect(webSocket, &QObject::destroyed, connectionTimer, &QTimer::stop);
|
|
|
|
connect(webSocket, &QWebSocket::textMessageReceived, this, &JoinRoom::processTextMessage);
|
|
|
|
QTimer *pingTimer = new QTimer(this);
|
|
connect(webSocket, &QWebSocket::pong, this, &JoinRoom::updatePing);
|
|
connect(pingTimer, &QTimer::timeout, this, &JoinRoom::sendPing);
|
|
connect(webSocket, &QWebSocket::disconnected, pingTimer, &QTimer::stop);
|
|
connect(webSocket, &QObject::destroyed, pingTimer, &QTimer::stop);
|
|
pingTimer->start(2500);
|
|
webSocket->ping();
|
|
|
|
QString serverUrlStr = customServerAddress.isEmpty() ? serverChooser->currentData().toString() : customServerAddress;
|
|
QUrl serverUrl = QUrl(serverUrlStr);
|
|
if (!customServerAddress.isEmpty() && serverUrl.port() < 0)
|
|
// Be forgiving of custom server addresses that forget the port
|
|
serverUrl.setPort(45000);
|
|
|
|
webSocket->open(serverUrl);
|
|
}
|
|
|
|
void JoinRoom::onConnected()
|
|
{
|
|
connectionTimer->stop();
|
|
|
|
QJsonObject json;
|
|
json.insert("type", "request_get_rooms");
|
|
json.insert("netplay_version", NETPLAY_VER);
|
|
addAuthData(webSocket, &json);
|
|
|
|
QJsonDocument json_doc(json);
|
|
webSocket->sendTextMessage(json_doc.toJson());
|
|
}
|
|
|
|
void JoinRoom::processTextMessage(QString message)
|
|
{
|
|
QJsonDocument json_doc = QJsonDocument::fromJson(message.toUtf8());
|
|
QJsonObject json = json_doc.object();
|
|
QMessageBox msgBox;
|
|
msgBox.setTextFormat(Qt::RichText);
|
|
msgBox.setTextInteractionFlags(Qt::TextBrowserInteraction);
|
|
|
|
if (json.value("type").toString() == "reply_get_rooms")
|
|
{
|
|
if (json.value("accept").toInt() == 0)
|
|
{
|
|
json.remove("type");
|
|
QJsonArray room_list = json.value("rooms").toArray();
|
|
for (int i = 0; i < room_list.size(); ++i)
|
|
{
|
|
rooms << room_list.at(i).toObject();
|
|
|
|
listWidget->insertRow(row);
|
|
QTableWidgetItem *newItem = new QTableWidgetItem(room_list.at(i).toObject().value("room_name").toString());
|
|
newItem->setFlags(newItem->flags() & ~Qt::ItemIsEditable);
|
|
listWidget->setItem(row, 0, newItem);
|
|
newItem = new QTableWidgetItem(room_list.at(i).toObject().value("game_name").toString());
|
|
newItem->setFlags(newItem->flags() & ~Qt::ItemIsEditable);
|
|
listWidget->setItem(row, 1, newItem);
|
|
newItem = new QTableWidgetItem(room_list.at(i).toObject().value("MD5").toString());
|
|
newItem->setFlags(newItem->flags() & ~Qt::ItemIsEditable);
|
|
listWidget->setItem(row, 2, newItem);
|
|
|
|
QString protectedValue = "No";
|
|
if (room_list.at(i).toObject().value("protected").toBool())
|
|
protectedValue = "Yes";
|
|
newItem = new QTableWidgetItem(protectedValue);
|
|
newItem->setFlags(newItem->flags() & ~Qt::ItemIsEditable);
|
|
listWidget->setItem(row, 3, newItem);
|
|
|
|
QString cheatsValue = "No";
|
|
if (!room_list.at(i).toObject().value("features").toObject().value("cheats").toString().isEmpty())
|
|
cheatsValue = "Yes";
|
|
newItem = new QTableWidgetItem(cheatsValue);
|
|
newItem->setFlags(newItem->flags() & ~Qt::ItemIsEditable);
|
|
listWidget->setItem(row, 4, newItem);
|
|
|
|
++row;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
msgBox.setText(json.value("message").toString());
|
|
msgBox.exec();
|
|
}
|
|
}
|
|
else if (json.value("type").toString() == "reply_join_room")
|
|
{
|
|
if (json.value("accept").toInt() == 0)
|
|
{
|
|
json.remove("type");
|
|
json.remove("accept");
|
|
launched = 1;
|
|
WaitRoom *waitRoom = new WaitRoom(filename, json, webSocket, parentWidget());
|
|
waitRoom->show();
|
|
accept();
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
(*CoreDoCommand)(M64CMD_ROM_CLOSE, 0, NULL);
|
|
msgBox.setText(json.value("message").toString());
|
|
msgBox.exec();
|
|
}
|
|
joinButton->setEnabled(true);
|
|
}
|
|
}
|
|
|
|
void JoinRoom::updatePing(quint64 elapsedTime, const QByteArray &)
|
|
{
|
|
pingLabel->setText("Ping: " + QString::number(elapsedTime) + " ms");
|
|
}
|
|
|
|
void JoinRoom::sendPing()
|
|
{
|
|
webSocket->ping();
|
|
}
|