all repos — mgba @ 2f643d79445b3cfb4d84ea6167d2c0bf005b855c

mGBA Game Boy Advance Emulator

src/platform/qt/MultiplayerController.cpp (view raw)

  1/* Copyright (c) 2013-2015 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include "MultiplayerController.h"
  7
  8#include "CoreController.h"
  9
 10#ifdef M_CORE_GBA
 11#include <mgba/internal/gba/gba.h>
 12#endif
 13#ifdef M_CORE_GB
 14#include <mgba/internal/gb/gb.h>
 15#endif
 16
 17using namespace QGBA;
 18
 19MultiplayerController::Player::Player(CoreController* coreController, GBSIOLockstepNode* node)
 20	: controller(coreController)
 21	, gbNode(node)
 22{
 23}
 24
 25#ifdef M_CORE_GBA
 26MultiplayerController::Player::Player(CoreController* coreController, GBASIOLockstepNode* node)
 27	: controller(coreController)
 28	, gbaNode(node)
 29{
 30}
 31#endif
 32
 33MultiplayerController::MultiplayerController() {
 34	mLockstepInit(&m_lockstep);
 35	m_lockstep.context = this;
 36	m_lockstep.lock = [](mLockstep* lockstep) {
 37		MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
 38		controller->m_lock.lock();
 39	};
 40	m_lockstep.unlock = [](mLockstep* lockstep) {
 41		MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
 42		controller->m_lock.unlock();
 43	};
 44	m_lockstep.signal = [](mLockstep* lockstep, unsigned mask) {
 45		MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
 46		Player* player = &controller->m_players[0];
 47		bool woke = false;
 48		player->waitMask &= ~mask;
 49		if (!player->waitMask && player->awake < 1) {
 50			mCoreThreadStopWaiting(player->controller->thread());
 51			player->awake = 1;
 52			woke = true;
 53		}
 54		return woke;
 55	};
 56	m_lockstep.wait = [](mLockstep* lockstep, unsigned mask) {
 57		MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
 58		Player* player = &controller->m_players[0];
 59		bool slept = false;
 60		player->waitMask |= mask;
 61		if (player->awake > 0) {
 62			mCoreThreadWaitFromThread(player->controller->thread());
 63			player->awake = 0;
 64			slept = true;
 65		}
 66		return slept;
 67	};
 68	m_lockstep.addCycles = [](mLockstep* lockstep, int id, int32_t cycles) {
 69		if (cycles < 0) {
 70			abort();
 71		}
 72		MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
 73		if (!id) {
 74			for (int i = 1; i < controller->m_players.count(); ++i) {
 75				Player* player = &controller->m_players[i];
 76#ifdef M_CORE_GBA
 77				if (player->controller->platform() == PLATFORM_GBA && player->gbaNode->d.p->mode != controller->m_players[0].gbaNode->d.p->mode) {
 78					player->controller->setSync(true);
 79					continue;
 80				}
 81#endif
 82				player->controller->setSync(false);
 83				player->cyclesPosted += cycles;
 84				if (player->awake < 1) {
 85					switch (player->controller->platform()) {
 86#ifdef M_CORE_GBA
 87					case PLATFORM_GBA:
 88						player->gbaNode->nextEvent += player->cyclesPosted;
 89						break;
 90#endif
 91#ifdef M_CORE_GB
 92					case PLATFORM_GB:
 93						player->gbNode->nextEvent += player->cyclesPosted;
 94						break;
 95#endif
 96					default:
 97						break;
 98					}
 99					mCoreThreadStopWaiting(player->controller->thread());
100					player->awake = 1;
101				}
102			}
103		} else {
104			controller->m_players[id].controller->setSync(true);
105			controller->m_players[id].cyclesPosted += cycles;
106		}
107	};
108	m_lockstep.useCycles = [](mLockstep* lockstep, int id, int32_t cycles) {
109		MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
110		Player* player = &controller->m_players[id];
111		player->cyclesPosted -= cycles;
112		if (player->cyclesPosted <= 0) {
113			mCoreThreadWaitFromThread(player->controller->thread());
114			player->awake = 0;
115		}
116		cycles = player->cyclesPosted;
117		return cycles;
118	};
119	m_lockstep.unusedCycles= [](mLockstep* lockstep, int id) {
120		MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
121		Player* player = &controller->m_players[id];
122		auto cycles = player->cyclesPosted;
123		return cycles;
124	};
125	m_lockstep.unload = [](mLockstep* lockstep, int id) {
126		MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
127		if (id) {
128			Player* player = &controller->m_players[id];
129			player->controller->setSync(true);
130			player->cyclesPosted = 0;
131
132			// release master GBA if it is waiting for this GBA
133			player = &controller->m_players[0];
134			player->waitMask &= ~(1 << id);
135			if (!player->waitMask && player->awake < 1) {
136				mCoreThreadStopWaiting(player->controller->thread());
137				player->awake = 1;
138			}
139		} else {
140			for (int i = 1; i < controller->m_players.count(); ++i) {
141				Player* player = &controller->m_players[i];
142				player->controller->setSync(true);
143				switch (player->controller->platform()) {
144#ifdef M_CORE_GBA
145				case PLATFORM_GBA:
146					player->cyclesPosted += reinterpret_cast<GBASIOLockstep*>(lockstep)->players[0]->eventDiff;
147					break;
148#endif
149#ifdef M_CORE_GB
150				case PLATFORM_GB:
151					player->cyclesPosted += reinterpret_cast<GBSIOLockstep*>(lockstep)->players[0]->eventDiff;
152					break;
153#endif
154				default:
155					break;
156				}
157				if (player->awake < 1) {
158					switch (player->controller->platform()) {
159#ifdef M_CORE_GBA
160					case PLATFORM_GBA:
161						player->gbaNode->nextEvent += player->cyclesPosted;
162						break;
163#endif
164#ifdef M_CORE_GB
165					case PLATFORM_GB:
166						player->gbNode->nextEvent += player->cyclesPosted;
167						break;
168#endif
169					default:
170						break;
171					}
172					mCoreThreadStopWaiting(player->controller->thread());
173					player->awake = 1;
174				}
175			}
176		}
177	};
178}
179
180MultiplayerController::~MultiplayerController() {
181	mLockstepDeinit(&m_lockstep);
182}
183
184bool MultiplayerController::attachGame(CoreController* controller) {
185	if (m_lockstep.attached == MAX_GBAS) {
186		return false;
187	}
188
189	if (m_lockstep.attached == 0) {
190		switch (controller->platform()) {
191#ifdef M_CORE_GBA
192		case PLATFORM_GBA:
193			GBASIOLockstepInit(&m_gbaLockstep);
194			break;
195#endif
196#ifdef M_CORE_GB
197		case PLATFORM_GB:
198			GBSIOLockstepInit(&m_gbLockstep);
199			break;
200#endif
201		default:
202			return false;
203		}
204	}
205
206	mCoreThread* thread = controller->thread();
207	if (!thread) {
208		return false;
209	}
210
211	switch (controller->platform()) {
212#ifdef M_CORE_GBA
213	case PLATFORM_GBA: {
214		GBA* gba = static_cast<GBA*>(thread->core->board);
215
216		GBASIOLockstepNode* node = new GBASIOLockstepNode;
217		GBASIOLockstepNodeCreate(node);
218		GBASIOLockstepAttachNode(&m_gbaLockstep, node);
219		m_players.append({controller, node});
220
221		GBASIOSetDriver(&gba->sio, &node->d, SIO_MULTI);
222
223		emit gameAttached();
224		return true;
225	}
226#endif
227#ifdef M_CORE_GB
228	case PLATFORM_GB: {
229		GB* gb = static_cast<GB*>(thread->core->board);
230
231		GBSIOLockstepNode* node = new GBSIOLockstepNode;
232		GBSIOLockstepNodeCreate(node);
233		GBSIOLockstepAttachNode(&m_gbLockstep, node);
234		m_players.append({controller, node});
235
236		GBSIOSetDriver(&gb->sio, &node->d);
237
238		emit gameAttached();
239		return true;
240	}
241#endif
242	default:
243		break;
244	}
245
246	return false;
247}
248
249void MultiplayerController::detachGame(CoreController* controller) {
250	if (m_players.empty()) {
251		return;
252	}
253	mCoreThread* thread = controller->thread();
254	if (!thread) {
255		return;
256	}
257	QList<CoreController::Interrupter> interrupters;
258
259	for (int i = 0; i < m_players.count(); ++i) {
260		interrupters.append(m_players[i].controller);
261	}
262	switch (controller->platform()) {
263#ifdef M_CORE_GBA
264	case PLATFORM_GBA: {
265		GBA* gba = static_cast<GBA*>(thread->core->board);
266		GBASIOLockstepNode* node = reinterpret_cast<GBASIOLockstepNode*>(gba->sio.drivers.multiplayer);
267		GBASIOSetDriver(&gba->sio, nullptr, SIO_MULTI);
268		if (node) {
269			GBASIOLockstepDetachNode(&m_gbaLockstep, node);
270			delete node;
271		}
272		break;
273	}
274#endif
275#ifdef M_CORE_GB
276	case PLATFORM_GB: {
277		GB* gb = static_cast<GB*>(thread->core->board);
278		GBSIOLockstepNode* node = reinterpret_cast<GBSIOLockstepNode*>(gb->sio.driver);
279		GBSIOSetDriver(&gb->sio, nullptr);
280		if (node) {
281			GBSIOLockstepDetachNode(&m_gbLockstep, node);
282			delete node;
283		}
284		break;
285	}
286#endif
287	default:
288		break;
289	}
290
291	for (int i = 0; i < m_players.count(); ++i) {
292		if (m_players[i].controller == controller) {
293			m_players.removeAt(i);
294			break;
295		}
296	}
297	emit gameDetached();
298}
299
300int MultiplayerController::playerId(CoreController* controller) {
301	for (int i = 0; i < m_players.count(); ++i) {
302		if (m_players[i].controller == controller) {
303			return i;
304		}
305	}
306	return -1;
307}
308
309int MultiplayerController::attached() {
310	int num;
311	num = m_lockstep.attached;
312	return num;
313}