all repos — mgba @ f6755a6e1b7b0cf2b944cd8ca842746f11d6bf82

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