mirror of https://github.com/mgba-emu/mgba.git
Qt: Improve handling of multiplayer syncing (fixes #2720)
This commit is contained in:
parent
6f08b740f9
commit
1b684ae2e3
1
CHANGES
1
CHANGES
|
@ -13,6 +13,7 @@ Other fixes:
|
||||||
- Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679)
|
- Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679)
|
||||||
- Qt: Fix scanning specific e-Reader dotcodes (fixes mgba.io/i/2693)
|
- Qt: Fix scanning specific e-Reader dotcodes (fixes mgba.io/i/2693)
|
||||||
- Qt: Don't re-enable sync if GBA link modes aren't the same (fixes mgba.io/i/2044)
|
- Qt: Don't re-enable sync if GBA link modes aren't the same (fixes mgba.io/i/2044)
|
||||||
|
- Qt: Improve handling of multiplayer syncing (fixes mgba.io/i/2720)
|
||||||
- Res: Fix species name location in Ruby/Sapphire revs 1/2 (fixes mgba.io/i/2685)
|
- Res: Fix species name location in Ruby/Sapphire revs 1/2 (fixes mgba.io/i/2685)
|
||||||
- VFS: Fix minizip write returning 0 on success instead of size
|
- VFS: Fix minizip write returning 0 on success instead of size
|
||||||
Misc:
|
Misc:
|
||||||
|
|
|
@ -14,24 +14,46 @@
|
||||||
#include <mgba/internal/gb/gb.h>
|
#include <mgba/internal/gb/gb.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
|
||||||
#ifdef M_CORE_GB
|
#ifdef M_CORE_GB
|
||||||
MultiplayerController::Player::Player(CoreController* coreController, GBSIOLockstepNode* node)
|
MultiplayerController::Player::Player(CoreController* coreController, GBSIOLockstepNode* gbNode)
|
||||||
: controller(coreController)
|
: controller(coreController)
|
||||||
, gbNode(node)
|
|
||||||
{
|
{
|
||||||
|
node.gb = gbNode;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef M_CORE_GBA
|
#ifdef M_CORE_GBA
|
||||||
MultiplayerController::Player::Player(CoreController* coreController, GBASIOLockstepNode* node)
|
MultiplayerController::Player::Player(CoreController* coreController, GBASIOLockstepNode* gbaNode)
|
||||||
: controller(coreController)
|
: controller(coreController)
|
||||||
, gbaNode(node)
|
|
||||||
{
|
{
|
||||||
|
node.gba = gbaNode;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
int MultiplayerController::Player::id() const {
|
||||||
|
switch (controller->platform()) {
|
||||||
|
#ifdef M_CORE_GBA
|
||||||
|
case mPLATFORM_GBA:
|
||||||
|
return node.gba->id;
|
||||||
|
#endif
|
||||||
|
#ifdef M_CORE_GB
|
||||||
|
case mPLATFORM_GB:
|
||||||
|
return node.gb->id;
|
||||||
|
#endif
|
||||||
|
case mPLATFORM_NONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MultiplayerController::Player::operator<(const MultiplayerController::Player& other) const {
|
||||||
|
return id() < other.id();
|
||||||
|
}
|
||||||
|
|
||||||
MultiplayerController::MultiplayerController() {
|
MultiplayerController::MultiplayerController() {
|
||||||
mLockstepInit(&m_lockstep);
|
mLockstepInit(&m_lockstep);
|
||||||
m_lockstep.context = this;
|
m_lockstep.context = this;
|
||||||
|
@ -65,6 +87,7 @@ MultiplayerController::MultiplayerController() {
|
||||||
player->awake = 0;
|
player->awake = 0;
|
||||||
slept = true;
|
slept = true;
|
||||||
}
|
}
|
||||||
|
player->controller->setSync(true);
|
||||||
return slept;
|
return slept;
|
||||||
};
|
};
|
||||||
m_lockstep.addCycles = [](mLockstep* lockstep, int id, int32_t cycles) {
|
m_lockstep.addCycles = [](mLockstep* lockstep, int id, int32_t cycles) {
|
||||||
|
@ -72,38 +95,51 @@ MultiplayerController::MultiplayerController() {
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
||||||
if (!id) {
|
Player* player = controller->player(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()) {
|
switch (player->controller->platform()) {
|
||||||
#ifdef M_CORE_GBA
|
#ifdef M_CORE_GBA
|
||||||
case mPLATFORM_GBA:
|
case mPLATFORM_GBA:
|
||||||
player->gbaNode->nextEvent += player->cyclesPosted;
|
if (!id) {
|
||||||
|
for (int i = 1; i < controller->m_players.count(); ++i) {
|
||||||
|
player = controller->player(i);
|
||||||
|
player->controller->setSync(false);
|
||||||
|
player->cyclesPosted += cycles;
|
||||||
|
if (player->awake < 1) {
|
||||||
|
player->node.gba->nextEvent += player->cyclesPosted;
|
||||||
|
}
|
||||||
|
mCoreThreadStopWaiting(player->controller->thread());
|
||||||
|
player->awake = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
player->controller->setSync(true);
|
||||||
|
player->cyclesPosted += cycles;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
#ifdef M_CORE_GB
|
#ifdef M_CORE_GB
|
||||||
case mPLATFORM_GB:
|
case mPLATFORM_GB:
|
||||||
player->gbNode->nextEvent += player->cyclesPosted;
|
if (!id) {
|
||||||
|
player = controller->player(1);
|
||||||
|
player->controller->setSync(false);
|
||||||
|
player->cyclesPosted += cycles;
|
||||||
|
if (player->awake < 1) {
|
||||||
|
player->node.gb->nextEvent += player->cyclesPosted;
|
||||||
|
}
|
||||||
|
mCoreThreadStopWaiting(player->controller->thread());
|
||||||
|
player->awake = 1;
|
||||||
|
} else {
|
||||||
|
player->controller->setSync(true);
|
||||||
|
player->cyclesPosted += cycles;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
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) {
|
m_lockstep.useCycles = [](mLockstep* lockstep, int id, int32_t cycles) {
|
||||||
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
||||||
Player* player = &controller->m_players[id];
|
Player* player = controller->player(id);
|
||||||
player->cyclesPosted -= cycles;
|
player->cyclesPosted -= cycles;
|
||||||
if (player->cyclesPosted <= 0) {
|
if (player->cyclesPosted <= 0) {
|
||||||
mCoreThreadWaitFromThread(player->controller->thread());
|
mCoreThreadWaitFromThread(player->controller->thread());
|
||||||
|
@ -112,21 +148,21 @@ MultiplayerController::MultiplayerController() {
|
||||||
cycles = player->cyclesPosted;
|
cycles = player->cyclesPosted;
|
||||||
return cycles;
|
return cycles;
|
||||||
};
|
};
|
||||||
m_lockstep.unusedCycles= [](mLockstep* lockstep, int id) {
|
m_lockstep.unusedCycles = [](mLockstep* lockstep, int id) {
|
||||||
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
||||||
Player* player = &controller->m_players[id];
|
Player* player = controller->player(id);
|
||||||
auto cycles = player->cyclesPosted;
|
auto cycles = player->cyclesPosted;
|
||||||
return cycles;
|
return cycles;
|
||||||
};
|
};
|
||||||
m_lockstep.unload = [](mLockstep* lockstep, int id) {
|
m_lockstep.unload = [](mLockstep* lockstep, int id) {
|
||||||
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
||||||
if (id) {
|
if (id) {
|
||||||
Player* player = &controller->m_players[id];
|
Player* player = controller->player(id);
|
||||||
player->controller->setSync(true);
|
player->controller->setSync(true);
|
||||||
player->cyclesPosted = 0;
|
player->cyclesPosted = 0;
|
||||||
|
|
||||||
// release master GBA if it is waiting for this GBA
|
// release master GBA if it is waiting for this GBA
|
||||||
player = &controller->m_players[0];
|
player = controller->player(0);
|
||||||
player->waitMask &= ~(1 << id);
|
player->waitMask &= ~(1 << id);
|
||||||
if (!player->waitMask && player->awake < 1) {
|
if (!player->waitMask && player->awake < 1) {
|
||||||
mCoreThreadStopWaiting(player->controller->thread());
|
mCoreThreadStopWaiting(player->controller->thread());
|
||||||
|
@ -134,7 +170,7 @@ MultiplayerController::MultiplayerController() {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (int i = 1; i < controller->m_players.count(); ++i) {
|
for (int i = 1; i < controller->m_players.count(); ++i) {
|
||||||
Player* player = &controller->m_players[i];
|
Player* player = controller->player(i);
|
||||||
player->controller->setSync(true);
|
player->controller->setSync(true);
|
||||||
switch (player->controller->platform()) {
|
switch (player->controller->platform()) {
|
||||||
#ifdef M_CORE_GBA
|
#ifdef M_CORE_GBA
|
||||||
|
@ -154,12 +190,12 @@ MultiplayerController::MultiplayerController() {
|
||||||
switch (player->controller->platform()) {
|
switch (player->controller->platform()) {
|
||||||
#ifdef M_CORE_GBA
|
#ifdef M_CORE_GBA
|
||||||
case mPLATFORM_GBA:
|
case mPLATFORM_GBA:
|
||||||
player->gbaNode->nextEvent += player->cyclesPosted;
|
player->node.gba->nextEvent += player->cyclesPosted;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
#ifdef M_CORE_GB
|
#ifdef M_CORE_GB
|
||||||
case mPLATFORM_GB:
|
case mPLATFORM_GB:
|
||||||
player->gbNode->nextEvent += player->cyclesPosted;
|
player->node.gb->nextEvent += player->cyclesPosted;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
|
@ -309,3 +345,29 @@ int MultiplayerController::attached() {
|
||||||
num = m_lockstep.attached;
|
num = m_lockstep.attached;
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MultiplayerController::Player* MultiplayerController::player(int id) {
|
||||||
|
Player* player = &m_players[id];
|
||||||
|
switch (player->controller->platform()) {
|
||||||
|
#ifdef M_CORE_GBA
|
||||||
|
case mPLATFORM_GBA:
|
||||||
|
if (player->node.gba->id != id) {
|
||||||
|
std::sort(m_players.begin(), m_players.end());
|
||||||
|
player = &m_players[id];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#ifdef M_CORE_GB
|
||||||
|
case mPLATFORM_GB:
|
||||||
|
if (player->node.gb->id != id) {
|
||||||
|
std::swap(m_players[0], m_players[1]);
|
||||||
|
player = &m_players[id];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case mPLATFORM_NONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QList>
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
#include <mgba/core/lockstep.h>
|
#include <mgba/core/lockstep.h>
|
||||||
#ifdef M_CORE_GBA
|
#ifdef M_CORE_GBA
|
||||||
|
@ -44,6 +44,10 @@ signals:
|
||||||
void gameDetached();
|
void gameDetached();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
union Node {
|
||||||
|
GBSIOLockstepNode* gb;
|
||||||
|
GBASIOLockstepNode* gba;
|
||||||
|
};
|
||||||
struct Player {
|
struct Player {
|
||||||
#ifdef M_CORE_GB
|
#ifdef M_CORE_GB
|
||||||
Player(CoreController* controller, GBSIOLockstepNode* node);
|
Player(CoreController* controller, GBSIOLockstepNode* node);
|
||||||
|
@ -52,13 +56,18 @@ private:
|
||||||
Player(CoreController* controller, GBASIOLockstepNode* node);
|
Player(CoreController* controller, GBASIOLockstepNode* node);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
int id() const;
|
||||||
|
bool operator<(const Player&) const;
|
||||||
|
|
||||||
CoreController* controller;
|
CoreController* controller;
|
||||||
GBSIOLockstepNode* gbNode = nullptr;
|
Node node = {nullptr};
|
||||||
GBASIOLockstepNode* gbaNode = nullptr;
|
|
||||||
int awake = 1;
|
int awake = 1;
|
||||||
int32_t cyclesPosted = 0;
|
int32_t cyclesPosted = 0;
|
||||||
unsigned waitMask = 0;
|
unsigned waitMask = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Player* player(int id);
|
||||||
|
|
||||||
union {
|
union {
|
||||||
mLockstep m_lockstep;
|
mLockstep m_lockstep;
|
||||||
#ifdef M_CORE_GB
|
#ifdef M_CORE_GB
|
||||||
|
@ -68,7 +77,7 @@ private:
|
||||||
GBASIOLockstep m_gbaLockstep;
|
GBASIOLockstep m_gbaLockstep;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
QList<Player> m_players;
|
QVector<Player> m_players;
|
||||||
QMutex m_lock;
|
QMutex m_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue