312 lines
8.1 KiB
C++
312 lines
8.1 KiB
C++
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
#include "MultiplayerController.h"
|
|
|
|
#include "CoreController.h"
|
|
|
|
#ifdef M_CORE_GBA
|
|
#include <mgba/internal/gba/gba.h>
|
|
#endif
|
|
#ifdef M_CORE_GB
|
|
#include <mgba/internal/gb/gb.h>
|
|
#endif
|
|
|
|
using namespace QGBA;
|
|
|
|
#ifdef M_CORE_GB
|
|
MultiplayerController::Player::Player(CoreController* coreController, GBSIOLockstepNode* node)
|
|
: controller(coreController)
|
|
, gbNode(node)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
#ifdef M_CORE_GBA
|
|
MultiplayerController::Player::Player(CoreController* coreController, GBASIOLockstepNode* node)
|
|
: controller(coreController)
|
|
, gbaNode(node)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
MultiplayerController::MultiplayerController() {
|
|
mLockstepInit(&m_lockstep);
|
|
m_lockstep.context = this;
|
|
m_lockstep.lock = [](mLockstep* lockstep) {
|
|
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
|
controller->m_lock.lock();
|
|
};
|
|
m_lockstep.unlock = [](mLockstep* lockstep) {
|
|
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
|
controller->m_lock.unlock();
|
|
};
|
|
m_lockstep.signal = [](mLockstep* lockstep, unsigned mask) {
|
|
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
|
Player* player = &controller->m_players[0];
|
|
bool woke = false;
|
|
player->waitMask &= ~mask;
|
|
if (!player->waitMask && player->awake < 1) {
|
|
mCoreThreadStopWaiting(player->controller->thread());
|
|
player->awake = 1;
|
|
woke = true;
|
|
}
|
|
return woke;
|
|
};
|
|
m_lockstep.wait = [](mLockstep* lockstep, unsigned mask) {
|
|
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
|
Player* player = &controller->m_players[0];
|
|
bool slept = false;
|
|
player->waitMask |= mask;
|
|
if (player->awake > 0) {
|
|
mCoreThreadWaitFromThread(player->controller->thread());
|
|
player->awake = 0;
|
|
slept = true;
|
|
}
|
|
return slept;
|
|
};
|
|
m_lockstep.addCycles = [](mLockstep* lockstep, int id, int32_t cycles) {
|
|
if (cycles < 0) {
|
|
abort();
|
|
}
|
|
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
|
if (!id) {
|
|
for (int i = 1; i < controller->m_players.count(); ++i) {
|
|
Player* player = &controller->m_players[i];
|
|
player->controller->setSync(false);
|
|
player->cyclesPosted += cycles;
|
|
if (player->awake < 1) {
|
|
switch (player->controller->platform()) {
|
|
#ifdef M_CORE_GBA
|
|
case mPLATFORM_GBA:
|
|
player->gbaNode->nextEvent += player->cyclesPosted;
|
|
break;
|
|
#endif
|
|
#ifdef M_CORE_GB
|
|
case mPLATFORM_GB:
|
|
player->gbNode->nextEvent += player->cyclesPosted;
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
mCoreThreadStopWaiting(player->controller->thread());
|
|
player->awake = 1;
|
|
}
|
|
}
|
|
} else {
|
|
controller->m_players[id].controller->setSync(true);
|
|
controller->m_players[id].cyclesPosted += cycles;
|
|
}
|
|
};
|
|
m_lockstep.useCycles = [](mLockstep* lockstep, int id, int32_t cycles) {
|
|
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
|
Player* player = &controller->m_players[id];
|
|
player->cyclesPosted -= cycles;
|
|
if (player->cyclesPosted <= 0) {
|
|
mCoreThreadWaitFromThread(player->controller->thread());
|
|
player->awake = 0;
|
|
}
|
|
cycles = player->cyclesPosted;
|
|
return cycles;
|
|
};
|
|
m_lockstep.unusedCycles= [](mLockstep* lockstep, int id) {
|
|
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
|
Player* player = &controller->m_players[id];
|
|
auto cycles = player->cyclesPosted;
|
|
return cycles;
|
|
};
|
|
m_lockstep.unload = [](mLockstep* lockstep, int id) {
|
|
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
|
if (id) {
|
|
Player* player = &controller->m_players[id];
|
|
player->controller->setSync(true);
|
|
player->cyclesPosted = 0;
|
|
|
|
// release master GBA if it is waiting for this GBA
|
|
player = &controller->m_players[0];
|
|
player->waitMask &= ~(1 << id);
|
|
if (!player->waitMask && player->awake < 1) {
|
|
mCoreThreadStopWaiting(player->controller->thread());
|
|
player->awake = 1;
|
|
}
|
|
} else {
|
|
for (int i = 1; i < controller->m_players.count(); ++i) {
|
|
Player* player = &controller->m_players[i];
|
|
player->controller->setSync(true);
|
|
switch (player->controller->platform()) {
|
|
#ifdef M_CORE_GBA
|
|
case mPLATFORM_GBA:
|
|
player->cyclesPosted += reinterpret_cast<GBASIOLockstep*>(lockstep)->players[0]->eventDiff;
|
|
break;
|
|
#endif
|
|
#ifdef M_CORE_GB
|
|
case mPLATFORM_GB:
|
|
player->cyclesPosted += reinterpret_cast<GBSIOLockstep*>(lockstep)->players[0]->eventDiff;
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
if (player->awake < 1) {
|
|
switch (player->controller->platform()) {
|
|
#ifdef M_CORE_GBA
|
|
case mPLATFORM_GBA:
|
|
player->gbaNode->nextEvent += player->cyclesPosted;
|
|
break;
|
|
#endif
|
|
#ifdef M_CORE_GB
|
|
case mPLATFORM_GB:
|
|
player->gbNode->nextEvent += player->cyclesPosted;
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
mCoreThreadStopWaiting(player->controller->thread());
|
|
player->awake = 1;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
MultiplayerController::~MultiplayerController() {
|
|
mLockstepDeinit(&m_lockstep);
|
|
}
|
|
|
|
bool MultiplayerController::attachGame(CoreController* controller) {
|
|
if (m_lockstep.attached == MAX_GBAS) {
|
|
return false;
|
|
}
|
|
|
|
if (m_lockstep.attached == 0) {
|
|
switch (controller->platform()) {
|
|
#ifdef M_CORE_GBA
|
|
case mPLATFORM_GBA:
|
|
GBASIOLockstepInit(&m_gbaLockstep);
|
|
break;
|
|
#endif
|
|
#ifdef M_CORE_GB
|
|
case mPLATFORM_GB:
|
|
GBSIOLockstepInit(&m_gbLockstep);
|
|
break;
|
|
#endif
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
mCoreThread* thread = controller->thread();
|
|
if (!thread) {
|
|
return false;
|
|
}
|
|
|
|
switch (controller->platform()) {
|
|
#ifdef M_CORE_GBA
|
|
case mPLATFORM_GBA: {
|
|
GBA* gba = static_cast<GBA*>(thread->core->board);
|
|
|
|
GBASIOLockstepNode* node = new GBASIOLockstepNode;
|
|
GBASIOLockstepNodeCreate(node);
|
|
GBASIOLockstepAttachNode(&m_gbaLockstep, node);
|
|
m_players.append({controller, node});
|
|
|
|
GBASIOSetDriver(&gba->sio, &node->d, SIO_MULTI);
|
|
GBASIOSetDriver(&gba->sio, &node->d, SIO_NORMAL_32);
|
|
|
|
emit gameAttached();
|
|
return true;
|
|
}
|
|
#endif
|
|
#ifdef M_CORE_GB
|
|
case mPLATFORM_GB: {
|
|
GB* gb = static_cast<GB*>(thread->core->board);
|
|
|
|
GBSIOLockstepNode* node = new GBSIOLockstepNode;
|
|
GBSIOLockstepNodeCreate(node);
|
|
GBSIOLockstepAttachNode(&m_gbLockstep, node);
|
|
m_players.append({controller, node});
|
|
|
|
GBSIOSetDriver(&gb->sio, &node->d);
|
|
|
|
emit gameAttached();
|
|
return true;
|
|
}
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void MultiplayerController::detachGame(CoreController* controller) {
|
|
if (m_players.empty()) {
|
|
return;
|
|
}
|
|
mCoreThread* thread = controller->thread();
|
|
if (!thread) {
|
|
return;
|
|
}
|
|
QList<CoreController::Interrupter> interrupters;
|
|
|
|
for (int i = 0; i < m_players.count(); ++i) {
|
|
interrupters.append(m_players[i].controller);
|
|
}
|
|
switch (controller->platform()) {
|
|
#ifdef M_CORE_GBA
|
|
case mPLATFORM_GBA: {
|
|
GBA* gba = static_cast<GBA*>(thread->core->board);
|
|
GBASIOLockstepNode* node = reinterpret_cast<GBASIOLockstepNode*>(gba->sio.drivers.multiplayer);
|
|
GBASIOSetDriver(&gba->sio, nullptr, SIO_MULTI);
|
|
GBASIOSetDriver(&gba->sio, nullptr, SIO_NORMAL_32);
|
|
if (node) {
|
|
GBASIOLockstepDetachNode(&m_gbaLockstep, node);
|
|
delete node;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
#ifdef M_CORE_GB
|
|
case mPLATFORM_GB: {
|
|
GB* gb = static_cast<GB*>(thread->core->board);
|
|
GBSIOLockstepNode* node = reinterpret_cast<GBSIOLockstepNode*>(gb->sio.driver);
|
|
GBSIOSetDriver(&gb->sio, nullptr);
|
|
if (node) {
|
|
GBSIOLockstepDetachNode(&m_gbLockstep, node);
|
|
delete node;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
|
|
for (int i = 0; i < m_players.count(); ++i) {
|
|
if (m_players[i].controller == controller) {
|
|
m_players.removeAt(i);
|
|
break;
|
|
}
|
|
}
|
|
emit gameDetached();
|
|
}
|
|
|
|
int MultiplayerController::playerId(CoreController* controller) {
|
|
for (int i = 0; i < m_players.count(); ++i) {
|
|
if (m_players[i].controller == controller) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int MultiplayerController::attached() {
|
|
int num;
|
|
num = m_lockstep.attached;
|
|
return num;
|
|
}
|