mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-03-30 20:15:35 +08:00
372 lines
13 KiB
C++
372 lines
13 KiB
C++
/* Copyright (C) 2014
|
|
* swift project community / contributors
|
|
*
|
|
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
|
* directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project,
|
|
* including this file, may be copied, modified, propagated, or distributed except according to the terms
|
|
* contained in the LICENSE file.
|
|
*/
|
|
|
|
#include "fs9.h"
|
|
#include "blacksimplugin_freefunctions.h"
|
|
#include "simulator_fs9.h"
|
|
#include "fs9_host.h"
|
|
#include "fs9_client.h"
|
|
#include "multiplayer_packets.h"
|
|
#include "multiplayer_packet_parser.h"
|
|
#include "blacksim/simulatorinfo.h"
|
|
#include "blackmisc/project.h"
|
|
#include <QTimer>
|
|
#include <algorithm>
|
|
|
|
using namespace BlackMisc::Aviation;
|
|
using namespace BlackMisc::PhysicalQuantities;
|
|
using namespace BlackMisc::Geo;
|
|
using namespace BlackSim;
|
|
using namespace BlackSimPlugin::Fs9;
|
|
|
|
namespace BlackSimPlugin
|
|
{
|
|
namespace Fs9
|
|
{
|
|
BlackCore::ISimulator *CSimulatorFs9Factory::create(QObject *parent)
|
|
{
|
|
registerMetadata();
|
|
return new Fs9::CSimulatorFs9(parent);
|
|
}
|
|
|
|
BlackSim::CSimulatorInfo CSimulatorFs9Factory::getSimulatorInfo() const
|
|
{
|
|
return CSimulatorInfo::FS9();
|
|
}
|
|
|
|
CSimulatorFs9::CSimulatorFs9(QObject *parent) :
|
|
ISimulator(parent),
|
|
m_fs9Host(new CFs9Host),
|
|
m_hostThread(this),
|
|
m_lobbyClient(new CLobbyClient(this)),
|
|
m_simulatorInfo(CSimulatorInfo::FS9()),
|
|
m_fsuipc(new FsCommon::CFsuipc())
|
|
{
|
|
// We move the host thread already in the constructor
|
|
m_fs9Host->moveToThread(&m_hostThread);
|
|
connect(&m_hostThread, &QThread::started, m_fs9Host, &CFs9Host::init);
|
|
connect(m_fs9Host, &CFs9Host::customPacketReceived, this, &CSimulatorFs9::ps_processFs9Message);
|
|
connect(m_fs9Host, &CFs9Host::statusChanged, this, &CSimulatorFs9::ps_changeHostStatus);
|
|
connect(&m_hostThread, &QThread::finished, m_fs9Host, &CFs9Host::deleteLater);
|
|
connect(&m_hostThread, &QThread::finished, &m_hostThread, &QThread::deleteLater);
|
|
m_hostThread.start();
|
|
}
|
|
|
|
CSimulatorFs9::~CSimulatorFs9()
|
|
{
|
|
disconnectFrom();
|
|
m_hostThread.quit();
|
|
m_hostThread.wait(1000);
|
|
|
|
QList<QThread *> clientThreads = m_fs9ClientThreads.values();
|
|
for (QThread *clientThread : clientThreads) clientThread->wait();
|
|
}
|
|
|
|
bool CSimulatorFs9::isConnected() const
|
|
{
|
|
return m_fs9Host->isConnected();
|
|
}
|
|
|
|
bool CSimulatorFs9::connectTo()
|
|
{
|
|
m_fsuipc->connect(); // connect FSUIPC too
|
|
|
|
// If we are already hosting, connect FS0 through lobby connection
|
|
if (m_isHosting) m_lobbyClient->connectFs9ToHost(m_fs9Host->getHostAddress());
|
|
// If not, deferre connection until host is setup
|
|
else m_startedLobbyConnection = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
void CSimulatorFs9::asyncConnectTo()
|
|
{
|
|
// Since we are running the host in its own thread, it is async anyway
|
|
connectTo();
|
|
}
|
|
|
|
bool CSimulatorFs9::disconnectFrom()
|
|
{
|
|
disconnectAllClients();
|
|
|
|
// We tell the host to terminate and stop the thread afterwards
|
|
QMetaObject::invokeMethod(m_fs9Host, "stopHosting");
|
|
emit statusChanged(ISimulator::Disconnected);
|
|
m_fsuipc->disconnect();
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CSimulatorFs9::canConnect()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void CSimulatorFs9::addRemoteAircraft(const CCallsign &callsign, const BlackMisc::Aviation::CAircraftSituation &initialSituation)
|
|
{
|
|
if (m_hashFs9Clients.size() >= 20)
|
|
{
|
|
// Only add a maximum number of 20 clients.
|
|
// FIXME: We need a smart method to get the 20 nearest aircrafts. If someone logs in
|
|
// nearby we need to kick out the one with max distance.
|
|
return;
|
|
}
|
|
|
|
// Create a new client thread, set update frequency to 25 ms and start it
|
|
QThread *clientThread = new QThread(this);
|
|
CFs9Client *client = new CFs9Client(callsign.toQString(), CTime(25, CTimeUnit::ms()));
|
|
client->setHostAddress(m_fs9Host->getHostAddress());
|
|
client->setPlayerUserId(m_fs9Host->getPlayerUserId());
|
|
client->moveToThread(clientThread);
|
|
|
|
connect(clientThread, &QThread::started, client, &CFs9Client::init);
|
|
connect(client, &CFs9Client::clientTimedOut, this, &CSimulatorFs9::ps_removeAircraft);
|
|
connect(client, &CFs9Client::statusChanged, this, &CSimulatorFs9::ps_changeClientStatus);
|
|
m_fs9ClientThreads.insert(client, clientThread);
|
|
m_hashFs9Clients.insert(callsign, client);
|
|
clientThread->start();
|
|
|
|
addAircraftSituation(callsign, initialSituation);
|
|
}
|
|
|
|
void CSimulatorFs9::addAircraftSituation(const CCallsign &callsign, const CAircraftSituation &situation)
|
|
{
|
|
// FIXME: should be a Q_ASSERT
|
|
if (!m_hashFs9Clients.contains(callsign))
|
|
{
|
|
// Only add a maximum number of 20 clients.
|
|
// FIXME: We need a smart method to get the 20 nearest aircrafts. If someone logs in
|
|
// nearby we need to kick out the one with max distance.
|
|
return;
|
|
}
|
|
|
|
CFs9Client *client = m_hashFs9Clients.value(callsign);
|
|
if (!client)
|
|
return;
|
|
|
|
client->addAircraftSituation(situation);
|
|
}
|
|
|
|
void CSimulatorFs9::removeRemoteAircraft(const CCallsign &callsign)
|
|
{
|
|
ps_removeAircraft(callsign.toQString());
|
|
}
|
|
|
|
bool CSimulatorFs9::updateOwnSimulatorCockpit(const CAircraft &ownAircraft)
|
|
{
|
|
CComSystem newCom1 = ownAircraft.getCom1System();
|
|
CComSystem newCom2 = ownAircraft.getCom2System();
|
|
CTransponder newTransponder = ownAircraft.getTransponder();
|
|
|
|
bool changed = false;
|
|
if (newCom1 != this->m_ownAircraft.getCom1System())
|
|
{
|
|
if (newCom1.getFrequencyActive() != this->m_ownAircraft.getCom1System().getFrequencyActive())
|
|
{
|
|
|
|
}
|
|
if (newCom1.getFrequencyStandby() != this->m_ownAircraft.getCom1System().getFrequencyStandby())
|
|
{
|
|
|
|
}
|
|
|
|
this->m_ownAircraft.setCom1System(newCom1);
|
|
changed = true;
|
|
}
|
|
|
|
if (newCom2 != this->m_ownAircraft.getCom2System())
|
|
{
|
|
if (newCom2.getFrequencyActive() != this->m_ownAircraft.getCom2System().getFrequencyActive())
|
|
{
|
|
|
|
}
|
|
|
|
if (newCom2.getFrequencyStandby() != this->m_ownAircraft.getCom2System().getFrequencyStandby())
|
|
{
|
|
|
|
}
|
|
|
|
this->m_ownAircraft.setCom2System(newCom2);
|
|
changed = true;
|
|
}
|
|
|
|
if (newTransponder != this->m_ownAircraft.getTransponder())
|
|
{
|
|
if (newTransponder.getTransponderCode() != this->m_ownAircraft.getTransponder().getTransponderCode())
|
|
{
|
|
changed = true;
|
|
}
|
|
this->m_ownAircraft.setTransponder(newTransponder);
|
|
}
|
|
|
|
// bye
|
|
return changed;
|
|
}
|
|
|
|
CSimulatorInfo CSimulatorFs9::getSimulatorInfo() const
|
|
{
|
|
return this->m_simulatorInfo;
|
|
}
|
|
|
|
void CSimulatorFs9::displayStatusMessage(const BlackMisc::CStatusMessage &message) const
|
|
{
|
|
if (message.getSeverity() != BlackMisc::CStatusMessage::SeverityDebug)
|
|
{
|
|
QMetaObject::invokeMethod(m_fs9Host, "sendTextMessage", Q_ARG(QString, message.toQString()));
|
|
}
|
|
}
|
|
|
|
void CSimulatorFs9::displayTextMessage(const BlackMisc::Network::CTextMessage &message) const
|
|
{
|
|
this->displayStatusMessage(message.asStatusMessage(true, true));
|
|
}
|
|
|
|
CAirportList CSimulatorFs9::getAirportsInRange() const
|
|
{
|
|
return this->m_airportsInRange;
|
|
}
|
|
|
|
void CSimulatorFs9::setTimeSynchronization(bool enable, BlackMisc::PhysicalQuantities::CTime offset)
|
|
{
|
|
this->m_syncTime = enable;
|
|
this->m_syncTimeOffset = offset;
|
|
}
|
|
|
|
void CSimulatorFs9::timerEvent(QTimerEvent * /* event */)
|
|
{
|
|
ps_dispatch();
|
|
}
|
|
|
|
void CSimulatorFs9::ps_dispatch()
|
|
{
|
|
if (m_fsuipc) m_fsuipc->process();
|
|
updateOwnAircraftFromSim(m_fsuipc->getOwnAircraft());
|
|
}
|
|
|
|
void CSimulatorFs9::ps_processFs9Message(const QByteArray &message)
|
|
{
|
|
CFs9Sdk::MULTIPLAYER_PACKET_ID messageType = MultiPlayerPacketParser::readType(message);
|
|
|
|
switch (messageType)
|
|
{
|
|
case CFs9Sdk::MULTIPLAYER_PACKET_ID_CHANGE_PLAYER_PLANE:
|
|
{
|
|
MPChangePlayerPlane mpChangePlayerPlane;
|
|
MultiPlayerPacketParser::readMessage(message, mpChangePlayerPlane);
|
|
ps_changeOwnAircraftModel(mpChangePlayerPlane.aircraft_name);
|
|
break;
|
|
}
|
|
case CFs9Sdk::MULTIPLAYER_PACKET_ID_POSITION_VELOCITY:
|
|
{
|
|
MPPositionVelocity mpPositionVelocity;
|
|
MultiPlayerPacketParser::readMessage(message, mpPositionVelocity);
|
|
m_ownAircraft.setSituation(aircraftSituationfromFS9(mpPositionVelocity));
|
|
break;
|
|
}
|
|
case CFs9Sdk::MPCHAT_PACKET_ID_CHAT_TEXT_SEND:
|
|
{
|
|
MPChatText mpChatText;
|
|
MultiPlayerPacketParser::readMessage(message, mpChatText);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CSimulatorFs9::ps_changeOwnAircraftModel(const QString &modelname)
|
|
{
|
|
m_aircraftModel.setQueriedModelString(modelname);
|
|
emit aircraftModelChanged(m_aircraftModel);
|
|
}
|
|
|
|
void CSimulatorFs9::ps_changeHostStatus(BlackSimPlugin::Fs9::CFs9Host::HostStatus status)
|
|
{
|
|
switch (status)
|
|
{
|
|
case CFs9Host::Hosting:
|
|
{
|
|
m_isHosting = true;
|
|
startTimer(50);
|
|
emit statusChanged(Connected);
|
|
if (m_startedLobbyConnection)
|
|
{
|
|
m_lobbyClient->connectFs9ToHost(m_fs9Host->getHostAddress());
|
|
m_startedLobbyConnection = false;
|
|
}
|
|
break;
|
|
}
|
|
case CFs9Host::Terminated:
|
|
{
|
|
m_fs9Host->deleteLater();
|
|
qDebug() << "Quitting thread";
|
|
connect(&m_hostThread, &QThread::finished, &m_hostThread, &QThread::deleteLater);
|
|
m_hostThread.quit();
|
|
m_isHosting = false;
|
|
emit statusChanged(Disconnected);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CSimulatorFs9::ps_changeClientStatus(const QString &callsign, CFs9Client::ClientStatus status)
|
|
{
|
|
switch (status)
|
|
{
|
|
case CFs9Client::Disconnected:
|
|
{
|
|
CFs9Client *client = m_hashFs9Clients.value(callsign);
|
|
Q_ASSERT(m_fs9ClientThreads.contains(client));
|
|
QThread *clientThread = m_fs9ClientThreads.value(client);
|
|
|
|
// Cleanup
|
|
client->deleteLater();
|
|
connect(clientThread, &QThread::finished, clientThread, &QThread::deleteLater);
|
|
clientThread->quit();
|
|
|
|
m_fs9ClientThreads.remove(client);
|
|
m_hashFs9Clients.remove(callsign);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CSimulatorFs9::ps_removeAircraft(const QString &callsign)
|
|
{
|
|
if(!m_hashFs9Clients.contains(callsign)) return;
|
|
|
|
CFs9Client *fs9Client = m_hashFs9Clients.value(callsign);
|
|
|
|
// Send an async disconnect signal. When finished we will clean up
|
|
QMetaObject::invokeMethod(fs9Client, "disconnectFrom");
|
|
}
|
|
|
|
void CSimulatorFs9::updateOwnAircraftFromSim(const CAircraft &ownAircraft)
|
|
{
|
|
m_ownAircraft.setCom1System(ownAircraft.getCom1System());
|
|
m_ownAircraft.setCom2System(ownAircraft.getCom2System());
|
|
m_ownAircraft.setTransponder(ownAircraft.getTransponder());
|
|
}
|
|
|
|
void CSimulatorFs9::disconnectAllClients()
|
|
{
|
|
// Stop all FS9 client threads
|
|
for (auto fs9Client : m_hashFs9Clients.keys())
|
|
{
|
|
removeRemoteAircraft(fs9Client);
|
|
}
|
|
}
|
|
}
|
|
}
|