Merge branch '164-crash-on-patching-and-possibly-right-after-login' into main/gingo...
[ryzomcore.git] / ryzom / client / src / session_browser.cpp
blob7b29f3023f22a74edb64e3c1819697b66f3bafec
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2019 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
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/>.
20 #include "stdpch.h"
21 #include "game_share/utils.h"
22 #include "session_browser.h"
24 #include "game_share/r2_share_itf.h"
26 #ifdef DEBUG_NEW
27 #define new DEBUG_NEW
28 #endif
30 using namespace std;
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)
50 bool _PassThrue;
52 CCallbackClientAdaptor(void *containerClass)
53 : CNelCallbackClientAdaptor(containerClass),
54 _PassThrue(false)
56 _CallbackClient.setPreDispatchCallback(&CCallbackClientAdaptor::cb_preDispatchMessage);
59 CCallbackClient &getCallback()
61 return _CallbackClient;
64 virtual void connect( const NLNET::CInetAddress& /* addr */ )
66 // do not connect now
70 virtual void send(const NLNET::CMessage &buffer, NLNET::TSockId hostid = NLNET::InvalidSockId, bool log = true)
72 CAutoMutex<CUnfairMutex> mutex(_Mutex);
74 if (!_PassThrue)
76 // queue the message for later sending.
77 nldebug("SB: Pushing a buffer into SendQueue (from %u elts)", _SendQueue.size());
78 _SendQueue.push(buffer);
80 else
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());
91 if (adaptor != NULL)
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
124 _CommThread->wait();
126 // ok, we can leave
127 delete _CommThread;
130 /** Set auth info */
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())
166 // wait a little
167 nlSleep(100);
169 if (_TerminateComm)
170 break;
173 disconnected:
174 // disconnected, waiting for message to send
175 while (adaptor->_SendQueue.empty())
177 nlSleep(100);
179 if (_TerminateComm)
180 break;
183 retry_connection:
184 while (!adaptor->getCallback().connected())
186 // connecting...
189 nldebug("SB: Connecting...");
190 adaptor->getCallback().connect(_ServerAddr);
192 catch(...)
194 // connection failed !
195 // erase all queued messages
196 clearSendQueue();
197 // put an event in for the main loop
198 _SignalConnFail = true;
200 if (_TerminateComm)
201 break;
206 CAutoMutex<CUnfairMutex> mutex(adaptor->_Mutex);
207 // authenticate the client
208 adaptor->_PassThrue = true;
209 nldebug("SB: Authenticating");
210 // nico patch
212 if (!connected()) // oddly sometimes it gets disconnected here
214 goto retry_connection;
217 authenticate(_LoginCookie.getUserId(), _LoginCookie);
218 adaptor->_PassThrue = false;
220 sendMessages:
221 // connected, sending messages
222 while (!adaptor->_SendQueue.empty())
224 bool popped = false;
226 CAutoMutex<CUnfairMutex> mutex(adaptor->_Mutex);
227 if (!adaptor->getCallback().connected())
229 nldebug("SB: Disconnecting when authenticating");
230 clearSendQueue(); // TODO: REMOVE?
231 goto disconnected;
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();
239 popped = true;
242 if (popped)
243 nldebug("SB: Popping from SendQueue (now %u elts)", adaptor->_SendQueue.size());
244 else
245 nldebug("SB: Disconnected when sending");
247 if (_TerminateComm)
248 break;
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))
255 nlSleep(100);
257 if (_TerminateComm)
258 break;
261 CAutoMutex<CUnfairMutex> mutex(adaptor->_Mutex);
262 if (!adaptor->_SendQueue.empty())
263 goto sendMessages;
265 if (!adaptor->getCallback().connected())
266 goto disconnected;
271 nldebug("SB: Disconnecting");
272 CAutoMutex<CUnfairMutex> mutex(adaptor->_Mutex);
273 // if (!adaptor->_SendQueue.empty())
274 // timeout, disconnect
275 adaptor->getCallback().disconnect();
278 if (_TerminateComm)
279 break;
281 goto disconnected;
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
295 if (_SignalConnFail)
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()))
321 update();
323 // give up the time slice
324 nlSleep(0);
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
341 return false;
344 bool CSessionBrowser::connected()
346 return getCallbackAdaptor()->connected();
349 void CSessionBrowser::send(const NLNET::CMessage& msg)
351 getCallbackAdaptor()->send(msg);