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 if (m_players.empty()) {
237 return;
238 }
239 mCoreThread* thread = controller->thread();
240 if (!thread) {
241 return;
242 }
243 QList<CoreController::Interrupter> interrupters;
244
245 for (int i = 0; i < m_players.count(); ++i) {
246 interrupters.append(m_players[i].controller);
247 }
248 switch (controller->platform()) {
249#ifdef M_CORE_GBA
250 case PLATFORM_GBA: {
251 GBA* gba = static_cast<GBA*>(thread->core->board);
252 GBASIOLockstepNode* node = reinterpret_cast<GBASIOLockstepNode*>(gba->sio.drivers.multiplayer);
253 GBASIOSetDriver(&gba->sio, nullptr, SIO_MULTI);
254 if (node) {
255 GBASIOLockstepDetachNode(&m_gbaLockstep, node);
256 delete node;
257 }
258 break;
259 }
260#endif
261#ifdef M_CORE_GB
262 case PLATFORM_GB: {
263 GB* gb = static_cast<GB*>(thread->core->board);
264 GBSIOLockstepNode* node = reinterpret_cast<GBSIOLockstepNode*>(gb->sio.driver);
265 GBSIOSetDriver(&gb->sio, nullptr);
266 if (node) {
267 GBSIOLockstepDetachNode(&m_gbLockstep, node);
268 delete node;
269 }
270 break;
271 }
272#endif
273 default:
274 break;
275 }
276
277 for (int i = 0; i < m_players.count(); ++i) {
278 if (m_players[i].controller == controller) {
279 m_players.removeAt(i);
280 break;
281 }
282 }
283 emit gameDetached();
284}
285
286int MultiplayerController::playerId(CoreController* controller) {
287 for (int i = 0; i < m_players.count(); ++i) {
288 if (m_players[i].controller == controller) {
289 return i;
290 }
291 }
292 return -1;
293}
294
295int MultiplayerController::attached() {
296 int num;
297 num = m_lockstep.attached;
298 return num;
299}