1 // Copyright (C) 2003 Dolphin Project.
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0.
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official SVN repository and contact information can be found at
16 // http://code.google.com/p/dolphin-emu/
18 #include "NetSockets.h"
19 #include "NetWindow.h"
20 #include "HW/SI_DeviceGCController.h"
22 NetPlay
*NetClass_ptr
= NULL
;
24 void NetPlay::IsGameFound(unsigned char * ptr
, std::string m_selected
)
28 m_selectedGame
= m_selected
;
30 if (m_games
.find(m_selected
) != std::string::npos
)
38 void NetPlay::OnNetEvent(wxCommandEvent
& event
)
40 switch (event
.GetId())
44 AppendText(_(" Server is Full !\n*You have been Disconnected.\n\n"));
52 AppendText(_("ERROR : Network Error !\n*You have been Disconnected.\n\n"));
58 AppendText( wxString::Format(wxT("ERROR : Network Error !\n")
59 wxT("*Player : %s has been dropped from the game.\n\n"),
60 (const char *)event
.GetString().mb_str()) );
64 case HOST_DISCONNECTED
:
66 // Event sent from Client's thread, it means that the thread
67 // has been killed and so we tell the GUI thread.
68 AppendText(_("*Connection to Host lost.\n*You have been Disconnected.\n\n"));
81 m_NetModel
= event
.GetInt();
86 m_clients_ready
= true;
88 // Tell clients everyone is ready...
93 case CLIENTS_NOTREADY
:
95 m_clients_ready
= false;
99 UpdateNetWindow(false);
102 AppendText(event
.GetString());
105 UpdateNetWindow(true, event
.GetString());
110 void ServerSide::IsEveryoneReady()
114 for (int i
=0; i
< m_numplayers
; i
++)
115 if (m_client
[i
].ready
)
118 if (nb_ready
== m_numplayers
)
119 Event
->SendEvent(CLIENTS_READY
);
121 Event
->SendEvent(CLIENTS_NOTREADY
);
124 // Actual Core function which is called on every frame
125 int CSIDevice_GCController::GetNetInput(u8 numPAD
, SPADStatus PadStatus
, u32
*PADStatus
)
127 if (NetClass_ptr
!= NULL
)
128 return NetClass_ptr
->GetNetPads(numPAD
, PadStatus
, PADStatus
) ? 1 : 0;
133 void NetPlay::LoadGame()
135 // Two implementations, one "p2p" implementation which sends to peer
136 // and receive from peer 2 players max. and another which uses server model
137 // and always sends to the server which then send it back to all the clients
138 // -> P2P model is faster, but is limited to 2 players
139 // -> Server model is slower, but supports up to 4 players
141 if (m_isHosting
== 1)
144 unsigned char value
= 0x50;
147 m_sock_server
->Write(0, 0, 0, ping
);
148 float fping
= (ping
[0]+ping
[1]+ping
[2])/(float)m_numClients
;
150 // Tell client everyone is ready
151 for (int i
=0; i
< m_numClients
; i
++)
152 m_sock_server
->Write(i
, (const char*)&value
, 1);
154 // Sleep a bit to start the game at more or less the same time than the peer
155 wxMilliSleep(fping
/2);
157 m_Logging
->AppendText(wxString::Format(wxT("** Everyone is ready... Loading Game ! **\n")
158 wxT("** Ping to client(s) is : %f ms\n"), fping
));
161 m_Logging
->AppendText(_("** Everyone is ready... Loading Game ! **\n"));
163 // TODO : Throttle should be on by default, to avoid stuttering
164 //soundStream->GetMixer()->SetThrottle(true);
170 std::string tmp
= m_games
.substr(0, m_games
.find(m_selectedGame
));
172 for (int i
=0; i
< (int)tmp
.size(); i
++)
173 if (tmp
.c_str()[i
] == '\n')
179 m_data_received
= false;
182 // Find corresponding game path
183 for (int i
=0; i
< (int)m_paths
.size(); i
++)
185 if (m_paths
.c_str()[i
] == '\n')
188 if (line_n
== line_p
) {
189 // Game path found, get its string
190 int str_pos
= line_p
> 0 ? i
+1 : i
;
191 int str_end
= (int)m_paths
.find('\n', str_pos
);
192 // Boot the selected game
193 BootManager::BootCore(m_paths
.substr(str_pos
, str_end
- str_pos
));
199 bool NetPlay::GetNetPads(u8 padnb
, SPADStatus PadStatus
, u32
*netValues
)
201 if (m_numClients
< 1)
203 m_Logging
->AppendText(_("** WARNING : Ping too high (>2000ms) or connection lost ! \n** WARNING : Stopping Netplay... \n"));
208 // Store current pad status in netValues[]
209 netValues
[0] = (u32
)((u8
)PadStatus
.stickY
);
210 netValues
[0] |= (u32
)((u8
)PadStatus
.stickX
<< 8);
211 netValues
[0] |= (u32
)((u16
)PadStatus
.button
<< 16);
212 netValues
[0] |= 0x00800000;
213 netValues
[1] = (u8
)PadStatus
.triggerRight
;
214 netValues
[1] |= (u32
)((u8
)PadStatus
.triggerLeft
<< 8);
215 netValues
[1] |= (u32
)((u8
)PadStatus
.substickY
<< 16);
216 netValues
[1] |= (u32
)((u8
)PadStatus
.substickX
<< 24);
218 if (m_NetModel
== 0) // Use 2 players Model
222 // Update the timer and increment total frame number
227 // We make sure everyone's pad is enabled
228 for (int i
= 0; i
< m_numClients
+1; i
++)
229 SerialInterface::ChangeDevice(SI_GC_CONTROLLER
, i
);
231 // Better disable unused ports
232 for (int i
= m_numClients
+1; i
< 4; i
++)
233 SerialInterface::ChangeDevice(SI_NONE
, i
);
236 if (m_timer
.GetTimeDifference() > 1000)
241 sprintf(sent
, "Sent Values: 0x%08x : 0x%08x \n", netValues
[0], netValues
[1]);
242 m_Logging
->AppendText(wxString::FromAscii(sent
));
244 unsigned char player
= 0;
247 unsigned char init_value
= 0xA1;
249 if (m_isHosting
== 1) {
251 m_sock_server
->Write(0, (const char*)&init_value
, 1);
252 m_sock_server
->Write(0, (const char*)netValues
, 8);
256 m_sock_client
->Write((const char*)&init_value
, 1);
257 m_sock_client
->Write((const char*)netValues
, 8);
263 padsValues
[0] = m_frame
;
264 padsValues
[1] = netValues
[0];
265 padsValues
[2] = netValues
[1];
267 if (m_isHosting
== 1) {
269 m_sock_server
->WriteUDP(0, (const char*)padsValues
, 12);
273 m_sock_client
->WriteUDP((const char*)padsValues
, 12);
278 if (!m_data_received
)
281 m_pads
[player
].nHi
[m_loopframe
] = netValues
[0];
282 m_pads
[player
].nLow
[m_loopframe
] = netValues
[1];
284 // Try to read from peer...
285 if (m_isHosting
== 1)
286 m_data_received
= m_sock_server
->isNewPadData(0, false);
288 m_data_received
= m_sock_client
->isNewPadData(0, false);
292 // Set our practical frame delay
293 m_frameDelay
= m_loopframe
;
296 // First Data has been received !
297 m_Logging
->AppendText(_("** Data received from Peer. Starting Sync !"));
298 m_Logging
->AppendText(wxString::Format(wxT(" Frame Delay : %d **\n"), m_frameDelay
));
301 if (m_loopframe
> 126)
303 m_Logging
->AppendText(_("** WARNING : Ping too high (>2000ms) or connection lost ! \n** WARNING : Stopping Netplay... \n"));
314 // We have successfully received the data, now use it...
315 // If we received data, we can update our pads on each frame, here's the behaviour :
316 // we received our init number, so we should receive our pad values on each frames
317 // with a frame delay of 'm_frameDelay' frames from the peer. So here, we just wait
318 // for the pad status. note : if the peer can't keep up, sending the values
319 // (i.e : framerate is too low) we have to wait for it thus slowing down emulation
321 // Save current pad values, it will be used in 'm_frameDelay' frames :D
322 int saveslot
= (m_loopframe
- 1 < 0 ? m_frameDelay
: m_loopframe
- 1);
325 m_pads
[player
].nHi
[saveslot
] = netValues
[0];
326 m_pads
[player
].nLow
[saveslot
] = netValues
[1];
328 // Read the socket for pad values
329 if (m_isHosting
== 1)
330 m_sock_server
->isNewPadData(recvedValues
, true);
332 m_sock_client
->isNewPadData(recvedValues
, true);
336 // Store received peer values
337 m_pads
[1].nHi
[m_loopframe
] = recvedValues
[0];
338 m_pads
[1].nLow
[m_loopframe
] = recvedValues
[1];
340 // Apply synced pad values
341 netValues
[0] = m_pads
[0].nHi
[m_loopframe
];
342 netValues
[1] = m_pads
[0].nLow
[m_loopframe
];
346 // Apply received pad values
347 netValues
[0] = recvedValues
[0];
348 netValues
[1] = recvedValues
[1];
354 sprintf(usedval
, "Player 1 Values: 0x%08x : 0x%08x \n", netValues
[0], netValues
[1]);
355 m_Logging
->AppendText(wxString::FromAscii(usedval
));
363 netValues
[0] = m_pads
[1].nHi
[m_loopframe
];
364 netValues
[1] = m_pads
[1].nLow
[m_loopframe
];
366 // Reset the loop to avoid reading unused values
367 if (m_loopframe
== m_frameDelay
)
376 sprintf(usedval
, "Player 2 Values: 0x%08x : 0x%08x \n", netValues
[0], netValues
[1]);
377 m_Logging
->AppendText(wxString::FromAscii(usedval
));
392 void NetPlay::ChangeSelectedGame(std::string game
)
394 wxCriticalSectionLocker
lock(m_critical
);
395 if (m_isHosting
== 0)
397 m_selectedGame
= game
;
401 if (game
!= m_selectedGame
)
403 unsigned char value
= 0x35;
404 int game_size
= (int)game
.size();
406 // Send command then Game String
407 for (int i
=0; i
< m_numClients
; i
++)
409 m_sock_server
->Write(i
, (const char*)&value
, 1); // 0x35 -> Change game
411 m_sock_server
->Write(i
, (const char*)&game_size
, 4);
412 m_sock_server
->Write(i
, game
.c_str(), game_size
+ 1);
415 m_selectedGame
= game
;
416 UpdateNetWindow(false);
417 m_Logging
->AppendText(wxString::Format( wxT(" *Game has been changed to : %s \r\n "), wxString(game
.c_str(), wxConvUTF8
).c_str()));
418 NOTICE_LOG(NETPLAY
,"Game has been changed to : %s \n",game
.c_str());
422 void NetPlay::OnQuit(wxCloseEvent
& WXUNUSED(event
))
427 // Destroy the Window
430 // Then Kill the threads
431 if (m_isHosting
== 0)
432 m_sock_client
->Delete();
433 else if (m_isHosting
== 1) {
434 m_sock_server
->Delete();
439 void NetPlay::OnDisconnect(wxCommandEvent
& WXUNUSED(event
))
445 bool ClientSide::isNewPadData(u32
*netValues
, bool current
, bool isVersus
)
452 m_CriticalSection
.Enter();
453 if (m_data_received
&& isVersus
)
455 netValues
[0] = m_netvalues
[0][0];
456 netValues
[1] = m_netvalues
[0][1];
457 m_data_received
= false;
459 m_CriticalSection
.Leave();
462 m_CriticalSection
.Leave();
471 wxCriticalSectionLocker
lock(m_CriticalSection
);
473 return m_data_received
;
479 m_CriticalSection
.Enter();
483 if (m_netvalues
[0][1] != 0)
485 netValues
[0] = m_netvalues
[0][1];
486 netValues
[1] = m_netvalues
[0][2];
492 u32 frame_saved
= m_netvalues
[0][0];
493 bool pass
= RecvT(m_socketUDP
, (char*)&m_netvalues
[0], 12, recv_size
, 5);
495 if (m_netvalues
[0][0] < frame_saved
+1)
497 if (m_netvalues
[0][0] > frame_saved
+1 || !pass
)
498 PanicAlert("Network ERROR !");
500 netValues
[0] = m_netvalues
[0][1];
501 netValues
[1] = m_netvalues
[0][2];
507 m_netvalues
[0][1] = 0;
508 m_CriticalSection
.Leave();
513 return RecvT(m_socketUDP
, (char*)&m_netvalues
[0], 12, recv_size
, 1/1000);
519 bool ServerSide::isNewPadData(u32
*netValues
, bool current
, int client
)
526 m_CriticalSection
.Enter();
529 netValues
[0] = m_netvalues
[client
][0];
530 netValues
[1] = m_netvalues
[client
][1];
531 m_data_received
= false;
533 m_CriticalSection
.Leave();
536 m_CriticalSection
.Leave();
545 wxCriticalSectionLocker
lock(m_CriticalSection
);
547 return m_data_received
;
553 m_CriticalSection
.Enter();
555 if (m_netvalues
[0][1] != 0)
557 netValues
[0] = m_netvalues
[client
][1];
558 netValues
[1] = m_netvalues
[client
][2];
564 u32 frame_saved
= m_netvalues
[client
][0];
565 bool pass
= RecvT(m_socketUDP
, (char*)&m_netvalues
[client
], 12, recv_size
, 5);
567 if (m_netvalues
[client
][0] < frame_saved
+1)
569 if (m_netvalues
[client
][0] > frame_saved
+1 || !pass
)
570 PanicAlert("Network ERROR !");
572 netValues
[0] = m_netvalues
[client
][1];
573 netValues
[1] = m_netvalues
[client
][2];
578 m_netvalues
[client
][1] = 0;
579 m_CriticalSection
.Leave();
584 return RecvT(m_socketUDP
, (char*)&m_netvalues
[client
], 12, recv_size
, 1/1000);