1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2019 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "game_share/utils.h"
22 #include "session_browser.h"
24 #include "game_share/r2_share_itf.h"
31 using namespace NLMISC
;
32 using namespace NLNET
;
33 using namespace RSMGR
;
35 /// The time the client stay connected to the session browser without requests
36 const uint32 KeepConnectedTime
= 3;
38 // a callback adaptor that queue the sent message for later sending.
39 class CCallbackClientAdaptor
: public CNelCallbackClientAdaptor
41 friend class CSessionBrowser
;
43 // a queue of message to be sent by the comm thread
44 std::queue
<CMessage
> _SendQueue
;
46 // The mutex used by the session browser thread
47 NLMISC::CUnfairMutex _Mutex
;
49 // A flag to let some message passthrue without enqueuing (for authenticate)
52 CCallbackClientAdaptor(void *containerClass
)
53 : CNelCallbackClientAdaptor(containerClass
),
56 _CallbackClient
.setPreDispatchCallback(&CCallbackClientAdaptor::cb_preDispatchMessage
);
59 CCallbackClient
&getCallback()
61 return _CallbackClient
;
64 virtual void connect( const NLNET::CInetAddress
& /* addr */ )
70 virtual void send(const NLNET::CMessage
&buffer
, NLNET::TSockId hostid
= NLNET::InvalidSockId
, bool log
= true)
72 CAutoMutex
<CUnfairMutex
> mutex(_Mutex
);
76 // queue the message for later sending.
77 nldebug("SB: Pushing a buffer into SendQueue (from %u elts)", _SendQueue
.size());
78 _SendQueue
.push(buffer
);
82 // protect socket because update() can be called in the main thread while send() can be called with passthru=true in the session browser thread (by authenticate() for example)
83 _CallbackClient
.send(buffer
, hostid
, log
);
87 // called by the callback client before each message dispatching
88 static void cb_preDispatchMessage(CMessage
&msgin
, TSockId from
, CCallbackNetBase
&netbase
)
90 CNelCallbackClientAdaptor
*adaptor
= static_cast<CNelCallbackClientAdaptor
*>(netbase
.getUserData());
93 CSessionBrowser
*sb
= static_cast<CSessionBrowser
*>(adaptor
->getContainerClass());
95 sb
->on_preDispatchMessage(msgin
, from
, netbase
);
104 ///////////////////////////////////////////////////////////////////////////
105 ///////////////////////////////////////////////////////////////////////////
107 CSessionBrowser::CSessionBrowser()
108 : CSessionBrowserServerWebClientItf(new CCallbackClientAdaptor(this)),
109 _TerminateComm(false),
110 _SignalConnFail(false),
111 _NBReceivedMessage(0),
112 _WaitingForMessage(false)
114 // start the comm thread
115 _CommThread
= IThread::create(this);
116 _CommThread
->start();
119 CSessionBrowser::~CSessionBrowser()
121 // signal the comm thread to terminate
122 _TerminateComm
= true;
123 // wait the termination of the thread
131 void CSessionBrowser::setAuthInfo(const NLNET::CLoginCookie
&cookie
)
133 _LoginCookie
= cookie
;
136 CCallbackClientAdaptor
*CSessionBrowser::getCallbackAdaptor()
138 return static_cast<CCallbackClientAdaptor
*>(_CallbackClient
.get());
141 void CSessionBrowser::connectItf(NLNET::CInetAddress address
)
143 // call the interface connectItf
144 CSessionBrowserServerWebClientItf::connectItf(address
);
145 _ServerAddr
= address
;
148 void CSessionBrowser::clearSendQueue()
150 CCallbackClientAdaptor
*adaptor
= getCallbackAdaptor();
151 nldebug("SB: Clearing SendQueue");
152 CAutoMutex
<CUnfairMutex
> mutex(adaptor
->_Mutex
);
153 while (!adaptor
->_SendQueue
.empty())
154 adaptor
->_SendQueue
.pop();
157 // the comm thread entry point
158 void CSessionBrowser::run()
160 CCallbackClientAdaptor
*adaptor
= getCallbackAdaptor();
161 while(!_TerminateComm
)
163 // no server address available
164 while (!_ServerAddr
.isValid())
174 // disconnected, waiting for message to send
175 while (adaptor
->_SendQueue
.empty())
184 while (!adaptor
->getCallback().connected())
189 nldebug("SB: Connecting...");
190 adaptor
->getCallback().connect(_ServerAddr
);
194 // connection failed !
195 // erase all queued messages
197 // put an event in for the main loop
198 _SignalConnFail
= true;
206 CAutoMutex
<CUnfairMutex
> mutex(adaptor
->_Mutex
);
207 // authenticate the client
208 adaptor
->_PassThrue
= true;
209 nldebug("SB: Authenticating");
212 if (!connected()) // oddly sometimes it gets disconnected here
214 goto retry_connection
;
217 authenticate(_LoginCookie
.getUserId(), _LoginCookie
);
218 adaptor
->_PassThrue
= false;
221 // connected, sending messages
222 while (!adaptor
->_SendQueue
.empty())
226 CAutoMutex
<CUnfairMutex
> mutex(adaptor
->_Mutex
);
227 if (!adaptor
->getCallback().connected())
229 nldebug("SB: Disconnecting when authenticating");
230 clearSendQueue(); // TODO: REMOVE?
234 nldebug("SB: Sending next buffer of SendQueue");
235 adaptor
->getCallback().send(adaptor
->_SendQueue
.front());
236 //if (adaptor->getCallback().connected()) // don't loose msg if it has not been sent (TODO: ADD?)
238 adaptor
->_SendQueue
.pop();
243 nldebug("SB: Popping from SendQueue (now %u elts)", adaptor
->_SendQueue
.size());
245 nldebug("SB: Disconnected when sending");
251 // idle, waiting new message to send/receive or time out to disconnect
252 time_t startWait
= CTime::getSecondsSince1970();
253 while (_WaitingForMessage
|| (adaptor
->_SendQueue
.empty() && CTime::getSecondsSince1970() - startWait
< KeepConnectedTime
))
261 CAutoMutex
<CUnfairMutex
> mutex(adaptor
->_Mutex
);
262 if (!adaptor
->_SendQueue
.empty())
265 if (!adaptor
->getCallback().connected())
271 nldebug("SB: Disconnecting");
272 CAutoMutex
<CUnfairMutex
> mutex(adaptor
->_Mutex
);
273 // if (!adaptor->_SendQueue.empty())
274 // timeout, disconnect
275 adaptor
->getCallback().disconnect();
285 void CSessionBrowser::on_preDispatchMessage(CMessage
&msgin
, TSockId
/* from */, CCallbackNetBase
&/* netbase */)
287 ++_NBReceivedMessage
;
288 _LastReceivedMessageNames
.push_back(msgin
.getName());
292 void CSessionBrowser::update()
294 // check flag to callback a connection failure event
297 on_connectionFailed();
298 _SignalConnFail
= false;
301 //nldebug("SB: Updating module");
302 CAutoMutex
<CUnfairMutex
> mutex(getCallbackAdaptor()->_Mutex
);
303 // call the underlying interface update
304 CSessionBrowserServerWebClientItf::update();
307 bool CSessionBrowser::waitOneMessage(const std::string
&msgName
)
309 nldebug("SB: Waiting for message");
310 _WaitingForMessage
= true;
311 // cleanup the list of received messages
312 _LastReceivedMessageNames
.clear();
314 CCallbackClientAdaptor
*adaptor
= getCallbackAdaptor();
317 // loop while no message received, still connected or having message to send
318 while (_LastReceivedMessageNames
.empty()
319 && (adaptor
->getCallback().connected() || !adaptor
->_SendQueue
.empty()))
323 // give up the time slice
328 _WaitingForMessage
= false;
330 if (!_LastReceivedMessageNames
.empty())
332 // check the first received message match the requested one
333 bool receivedExpected
=
334 (_LastReceivedMessageNames
.front() == msgName
) ||
335 (_LastReceivedMessageNames
.front() == "JSSRE" && (msgName
== "JSSR")); // harcoded: JSSRE may replace JSSR in some responses
336 STOP_IF(!receivedExpected
, "BIG BAD BUG - Expected SBS message '"+msgName
+"' but received '"+_LastReceivedMessageNames
.front()+"'");
337 return receivedExpected
;
340 // sorry, wait failed
344 bool CSessionBrowser::connected()
346 return getCallbackAdaptor()->connected();
349 void CSessionBrowser::send(const NLNET::CMessage
& msg
)
351 getCallbackAdaptor()->send(msg
);