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