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 "HW/SI_DeviceGCController.h"
19 #include "HW/EXI_DeviceIPL.h"
24 #include "NetWindow.h"
28 // this will be removed soon
29 #define TEMP_FIXED_BUFFER 20
33 CON_ERR_SERVER_FULL
= 1,
35 CON_ERR_VERSION_MISMATCH
38 NetPlay
* NetClass_ptr
= NULL
;
39 extern CFrame
* main_frame
;
41 DEFINE_EVENT_TYPE(wxEVT_THREAD
)
43 THREAD_RETURN
NetPlayThreadFunc(void* arg
)
45 ((NetPlay
*)arg
)->Entry();
51 ::NetClass_ptr
= NULL
;
54 NetPlayServer::~NetPlayServer()
59 m_thread
->WaitForDeath();
64 NetPlayClient::~NetPlayClient()
69 m_thread
->WaitForDeath();
74 NetPlayServer::Player::Player()
76 memset(pad_map
, -1, sizeof(pad_map
));
79 NetPlayClient::Player::Player()
81 memset(pad_map
, -1, sizeof(pad_map
));
84 NetPlayServer::NetPlayServer(const u16 port
, const std::string
& name
, NetPlayDiag
* const npd
, const std::string
& game
)
87 m_selected_game
= game
;
89 if (m_socket
.Listen(port
))
93 player
.revision
= NETPLAY_DOLPHIN_VER
;
94 player
.socket
= m_socket
;
97 // map local pad 1 to game pad 1
98 player
.pad_map
[0] = 0;
100 // add self to player list
101 m_players
[m_socket
] = player
;
102 //PanicAlert("Listening");
108 m_selector
.Add(m_socket
);
109 m_thread
= new Common::Thread(NetPlayThreadFunc
, this);
112 is_connected
= false;
115 NetPlayClient::NetPlayClient(const std::string
& address
, const u16 port
, const std::string
& name
, NetPlayDiag
* const npd
)
118 is_connected
= false;
120 // why is false successful? documentation says true is
121 if (0 == m_socket
.Connect(port
, address
))
123 // send connect message
125 spac
<< NETPLAY_VERSION
;
126 spac
<< NETPLAY_DOLPHIN_VER
;
131 // TODO: make this not hang
132 m_socket
.Receive(rpac
);
141 case CON_ERR_SERVER_FULL
:
142 PanicAlert("The server is full!");
144 case CON_ERR_VERSION_MISMATCH
:
145 PanicAlert("The NetPlay versions are incompatible!");
147 case CON_ERR_GAME_RUNNING
:
148 PanicAlert("The game is currently running!");
158 player
.name
= "Player";
160 player
.revision
= NETPLAY_DOLPHIN_VER
;
162 // add self to player list
163 m_players
[m_pid
] = player
;
167 //PanicAlert("Connection successful: assigned player id: %d", m_pid);
170 m_selector
.Add(m_socket
);
171 m_thread
= new Common::Thread(NetPlayThreadFunc
, this);
175 PanicAlert("Failed to Connect!");
179 void NetPlayServer::Entry()
183 const unsigned int num
= m_selector
.Wait(0.01f
);
185 for (unsigned int i
=0; i
<num
; ++i
)
187 sf::SocketTCP ready_socket
= m_selector
.GetSocketReady(i
);
190 if (ready_socket
== m_socket
)
192 sf::SocketTCP accept_socket
;
193 m_socket
.Accept(accept_socket
);
195 m_crit
.other
.Enter();
196 unsigned int error
= OnConnect(accept_socket
);
197 m_crit
.other
.Leave();
202 spac
<< (MessageId
)error
;
203 // don't need to lock, this client isn't in the client map
204 accept_socket
.Send(spac
);
206 // TODO: not sure if client gets the message if i close right away
207 accept_socket
.Close();
214 switch (ready_socket
.Receive(rpac
))
216 case sf::Socket::Done
:
217 // if a bad packet is recieved, disconnect the client
218 if (0 == OnData(rpac
, ready_socket
))
221 case sf::Socket::Disconnected
:
222 m_crit
.other
.Enter();
223 OnDisconnect(ready_socket
);
224 m_crit
.other
.Leave();
231 // close listening socket and client sockets
233 std::map
<sf::SocketTCP
, Player
>::reverse_iterator
234 i
= m_players
.rbegin(),
235 e
= m_players
.rend();
237 i
->second
.socket
.Close();
243 unsigned int NetPlayServer::OnConnect(sf::SocketTCP
& socket
)
246 // TODO: make this not hang / check if good packet
247 socket
.Receive(rpac
);
251 // dolphin netplay version
252 if (npver
!= NETPLAY_VERSION
)
253 return CON_ERR_VERSION_MISMATCH
;
255 // game is currently running
257 return CON_ERR_GAME_RUNNING
;
260 if (m_players
.size() >= 255)
261 return CON_ERR_SERVER_FULL
;
264 player
.socket
= socket
;
265 rpac
>> player
.revision
;
268 // give new client first available id
270 std::map
<sf::SocketTCP
, Player
>::const_iterator
273 for (u8 p
= 1; 0 == player
.pid
; ++p
)
275 for (i
= m_players
.begin(); ; ++i
)
282 if (p
== i
->second
.pid
)
287 // TODO: this is crappy
288 // try to automatically assign new user a pad
290 bool is_mapped
[4] = {false,false,false,false};
292 for ( unsigned int m
= 0; m
<4; ++m
)
294 for (i
= m_players
.begin(); i
!=e
; ++i
)
296 if (i
->second
.pad_map
[m
] >= 0)
297 is_mapped
[i
->second
.pad_map
[m
]] = true;
301 for ( unsigned int m
= 0; m
<4; ++m
)
302 if (false == is_mapped
[m
])
304 player
.pad_map
[0] = m
;
313 // send join message to already connected clients
315 spac
<< (MessageId
)NP_MSG_PLAYER_JOIN
;
316 spac
<< player
.pid
<< player
.name
<< player
.revision
;
319 // send new client success message with their id
321 spac
<< (MessageId
)0;
325 // send user their pad mapping
327 spac
<< (MessageId
)NP_MSG_PAD_MAPPING
;
329 for (unsigned int pm
= 0; pm
<4; ++pm
)
330 spac
<< player
.pad_map
[pm
];
333 // send new client the selected game
335 spac
<< (MessageId
)NP_MSG_CHANGE_GAME
;
336 spac
<< m_selected_game
;
339 // sync values with new client
340 for (i
= m_players
.begin(); i
!=e
; ++i
)
343 spac
<< (MessageId
)NP_MSG_PLAYER_JOIN
;
344 spac
<< i
->second
.pid
<< i
->second
.name
<< i
->second
.revision
;
348 spac
<< (MessageId
)NP_MSG_PAD_MAPPING
;
349 spac
<< i
->second
.pid
;
350 for (unsigned int pm
= 0; pm
<4; ++pm
)
351 spac
<< i
->second
.pad_map
[pm
];
358 // add client to the player list
359 m_crit
.players
.Enter();
360 m_players
[socket
] = player
;
361 m_crit
.players
.Leave();
363 // add client to selector/ used for receiving
364 m_selector
.Add(socket
);
371 unsigned int NetPlayServer::OnDisconnect(sf::SocketTCP
& socket
)
374 PanicAlert("Disconnect while game is running. Game will likely hang!");
377 spac
<< (MessageId
)NP_MSG_PLAYER_LEAVE
;
378 spac
<< m_players
[socket
].pid
;
380 m_selector
.Remove(socket
);
382 m_crit
.players
.Enter();
383 m_players
.erase(m_players
.find(socket
));
384 m_crit
.players
.Leave();
395 void NetPlay::UpdateGUI()
399 wxCommandEvent
evt(wxEVT_THREAD
, 1);
400 m_dialog
->AddPendingEvent(evt
);
404 void NetPlayServer::SendToClients(sf::Packet
& packet
, const u8 skip_pid
)
406 std::map
<sf::SocketTCP
, Player
>::iterator
407 i
= m_players
.begin(),
410 if (i
->second
.pid
&& (i
->second
.pid
!= skip_pid
))
411 i
->second
.socket
.Send(packet
);
414 unsigned int NetPlayServer::OnData(sf::Packet
& packet
, sf::SocketTCP
& socket
)
419 // don't need lock because this is the only thread that modifies the players
420 // only need locks for writes to m_players in this thread
421 Player
& player
= m_players
[socket
];
425 case NP_MSG_CHAT_MESSAGE
:
430 // send msg to other clients
432 spac
<< (MessageId
)NP_MSG_CHAT_MESSAGE
;
437 SendToClients(spac
, player
.pid
);
441 std::ostringstream ss
;
442 ss
<< player
.name
<< '[' << (char)(player
.pid
+'0') << "]: " << msg
;
444 m_dialog
->chat_msgs
.Push(ss
.str());
450 case NP_MSG_PAD_DATA
:
454 packet
>> map
>> np
.nHi
>> np
.nLo
;
456 // check if client's pad indeed maps in game
457 if (map
>= 0 && map
< 4)
458 map
= player
.pad_map
[map
];
462 // if not, they are hacking, so disconnect them
463 // this could happen right after a pad map change, but that isn't implimented yet
468 m_crit
.buffer
.Enter();
469 m_pad_buffer
[map
].push(np
);
470 m_crit
.buffer
.Leave();
474 spac
<< (MessageId
)NP_MSG_PAD_DATA
;
475 spac
<< map
; // in game mapping
476 spac
<< np
.nHi
<< np
.nLo
;
479 SendToClients(spac
, player
.pid
);
485 //PanicAlert("Unknown message received with id : %d", mid);
486 // unknown message, kick the client
494 unsigned int NetPlayClient::OnData(sf::Packet
& packet
)
501 case NP_MSG_PLAYER_JOIN
:
504 packet
>> player
.pid
;
505 packet
>> player
.name
;
506 packet
>> player
.revision
;
508 m_crit
.players
.Enter();
509 m_players
[player
.pid
] = player
;
510 m_crit
.players
.Leave();
516 case NP_MSG_PLAYER_LEAVE
:
521 m_crit
.players
.Enter();
522 m_players
.erase(m_players
.find(pid
));
523 m_crit
.players
.Leave();
529 case NP_MSG_CHAT_MESSAGE
:
536 // don't need lock to read in this thread
537 const Player
& player
= m_players
[pid
];
540 std::ostringstream ss
;
541 ss
<< player
.name
<< '[' << (char)(pid
+'0') << "]: " << msg
;
543 m_dialog
->chat_msgs
.Push(ss
.str());
548 case NP_MSG_PAD_MAPPING
:
553 m_crit
.players
.Enter();
554 Player
& player
= m_players
[pid
];
556 for (unsigned int i
=0; i
<4; ++i
)
557 packet
>> player
.pad_map
[i
];
558 m_crit
.players
.Leave();
564 case NP_MSG_PAD_DATA
:
568 packet
>> map
>> np
.nHi
>> np
.nLo
;
571 m_crit
.buffer
.Enter();
572 m_pad_buffer
[map
].push(np
);
573 m_crit
.buffer
.Leave();
577 case NP_MSG_CHANGE_GAME
:
579 m_crit
.other
.Enter();
580 packet
>> m_selected_game
;
581 m_crit
.other
.Leave();
584 wxCommandEvent
evt(wxEVT_THREAD
, 45);
585 evt
.SetString(wxString(m_selected_game
.c_str(), *wxConvCurrent
));
586 m_dialog
->AddPendingEvent(evt
);
590 case NP_MSG_START_GAME
:
594 m_dialog
->OnStart(evt
);
599 PanicAlert("Unknown message received with id : %d", mid
);
607 void NetPlayClient::Entry()
611 if (m_selector
.Wait(0.01f
))
614 switch (m_socket
.Receive(rpac
))
616 case sf::Socket::Done
:
620 case sf::Socket::Disconnected
:
621 PanicAlert("Lost connection to server! If the game is running, it will likely hang!");
633 template <typename P
>
634 static inline std::string
PlayerToString(const P
& p
)
636 std::ostringstream ss
;
637 ss
<< p
.name
<< '[' << (char)(p
.pid
+'0') << "] : " << p
.revision
<< " |";
638 for (unsigned int i
=0; i
<4; ++i
)
639 ss
<< (p
.pad_map
[i
]>=0 ? (char)(p
.pad_map
[i
]+'1') : '-');
644 void NetPlayClient::GetPlayerList(std::string
&list
)
646 m_crit
.players
.Enter();
648 std::ostringstream ss
;
650 std::map
<u8
, Player
>::const_iterator
651 i
= m_players
.begin(),
654 ss
<< PlayerToString(i
->second
) << '\n';
658 m_crit
.players
.Leave();
661 void NetPlayServer::GetPlayerList(std::string
&list
)
663 m_crit
.players
.Enter();
665 std::ostringstream ss
;
667 std::map
<sf::SocketTCP
, Player
>::const_iterator
668 i
= m_players
.begin(),
671 ss
<< PlayerToString(i
->second
) << '\n';
675 m_crit
.players
.Leave();
678 void NetPlayServer::SendChatMessage(const std::string
& msg
)
681 spac
<< (MessageId
)NP_MSG_CHAT_MESSAGE
;
682 spac
<< (PlayerId
)0; // server id always 0
690 void NetPlayClient::SendChatMessage(const std::string
& msg
)
693 spac
<< (MessageId
)NP_MSG_CHAT_MESSAGE
;
704 //memset(&sp, 0, sizeof(sp));
705 //memset(&sp.stickX, 0x80, 4);
706 //*this = NetPad(&sp);
708 // i'd rather do something like this
713 NetPad::NetPad(const SPADStatus
* const pad_status
)
715 nHi
= (u32
)((u8
)pad_status
->stickY
);
716 nHi
|= (u32
)((u8
)pad_status
->stickX
<< 8);
717 nHi
|= (u32
)((u16
)pad_status
->button
<< 16);
719 nLo
= (u8
)pad_status
->triggerRight
;
720 nLo
|= (u32
)((u8
)pad_status
->triggerLeft
<< 8);
721 nLo
|= (u32
)((u8
)pad_status
->substickY
<< 16);
722 nLo
|= (u32
)((u8
)pad_status
->substickX
<< 24);
725 bool NetPlayServer::GetNetPads(const u8 pad_nb
, const SPADStatus
* const pad_status
, NetPad
* const netvalues
)
727 m_crit
.players
.Enter();
729 // in game mapping for this local pad, kinda silly method
730 PadMapping in_game_num
= m_players
[m_socket
].pad_map
[pad_nb
];
732 // check if this local pad maps in game
733 if (in_game_num
>= 0)
735 NetPad
np(pad_status
);
738 m_crit
.buffer
.Enter();
739 m_pad_buffer
[in_game_num
].push(np
);
740 m_crit
.buffer
.Leave();
744 spac
<< (MessageId
)NP_MSG_PAD_DATA
;
745 spac
<< in_game_num
; // in game pad num
746 spac
<< np
.nHi
<< np
.nLo
;
753 // get padstate from buffer and send to game
754 m_crit
.buffer
.Enter();
755 while (0 == m_pad_buffer
[pad_nb
].size())
757 m_crit
.buffer
.Leave();
758 // wait for receiving thread to push some data
759 Common::SleepCurrentThread(10);
760 m_crit
.buffer
.Enter();
762 *netvalues
= m_pad_buffer
[pad_nb
].front();
763 m_pad_buffer
[pad_nb
].pop();
764 m_crit
.buffer
.Leave();
766 m_crit
.players
.Leave();
771 bool NetPlayServer::SetSelectedGame(const std::string
&game
)
779 bool NetPlayClient::GetNetPads(const u8 pad_nb
, const SPADStatus
* const pad_status
, NetPad
* const netvalues
)
781 m_crit
.players
.Enter();
783 // in game mapping for this local pad
784 PadMapping in_game_num
= m_players
[m_pid
].pad_map
[pad_nb
];
786 // does this local pad map in game?
787 if (in_game_num
>= 0)
789 NetPad
np(pad_status
);
792 m_crit
.buffer
.Enter();
793 m_pad_buffer
[in_game_num
].push(np
);
794 m_crit
.buffer
.Leave();
798 spac
<< (MessageId
)NP_MSG_PAD_DATA
;
799 spac
<< (PadMapping
)pad_nb
; // local pad num
800 spac
<< np
.nHi
<< np
.nLo
;
807 // get padstate from buffer and send to game
808 m_crit
.buffer
.Enter();
809 while (0 == m_pad_buffer
[pad_nb
].size())
811 m_crit
.buffer
.Leave();
812 // wait for receiving thread to push some data
813 Common::SleepCurrentThread(10);
814 m_crit
.buffer
.Enter();
816 *netvalues
= m_pad_buffer
[pad_nb
].front();
817 m_pad_buffer
[pad_nb
].pop();
818 m_crit
.buffer
.Leave();
820 m_crit
.players
.Leave();
824 bool NetPlayClient::SetSelectedGame(const std::string
&game
)
832 bool NetPlayServer::StartGame(const std::string
&path
)
834 m_crit
.other
.Enter();
838 PanicAlert("Game is already running!");
843 ::NetClass_ptr
= this;
845 m_crit
.buffer
.Enter();
848 for (unsigned int i
=0; i
<TEMP_FIXED_BUFFER
; ++i
)
849 for (unsigned int p
=0; p
<4; ++p
)
850 m_pad_buffer
[p
].push(np
);
851 m_crit
.buffer
.Leave();
853 // tell clients to start game
855 spac
<< (MessageId
)NP_MSG_START_GAME
;
862 ::main_frame
->BootGame(path
);
863 //BootManager::BootCore(path);
865 m_crit
.other
.Leave();
870 bool NetPlayClient::StartGame(const std::string
&path
)
872 m_crit
.other
.Enter();
875 ::NetClass_ptr
= this;
877 m_crit
.buffer
.Enter();
880 for (unsigned int i
=0; i
<TEMP_FIXED_BUFFER
; ++i
)
881 for (unsigned int p
=0; p
<4; ++p
)
882 m_pad_buffer
[p
].push(np
);
883 m_crit
.buffer
.Leave();
886 ::main_frame
->BootGame(path
);
887 //BootManager::BootCore(path);
889 m_crit
.other
.Leave();
894 // Actual Core function which is called on every frame
895 int CSIDevice_GCController::GetNetInput(u8 numPAD
, SPADStatus PadStatus
, u32
*PADStatus
)
898 // TODO: NetClass_ptr could go null here, need a lock
899 return NetClass_ptr
->GetNetPads(numPAD
, &PadStatus
, (NetPad
*)PADStatus
) ? 1 : 0;
904 // so all players' games get the same time
905 u32
CEXIIPL::GetNetGCTime()
908 // TODO: NetClass_ptr could go null here, need a lock
909 return 1272737767; // watev