123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347 |
- /*
- * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
- * its licensors.
- *
- * For complete copyright and license terms please see the LICENSE at the root of this
- * distribution (the "License"). All use of this software is governed by the License,
- * or, if provided, by the license below or the license accompanying this file. Do not
- * remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *
- */
- // Original file Copyright Crytek GMBH or its affiliates, used under license.
- #include "CrySystem_precompiled.h"
- #include "NotificationNetwork.h"
- #include <ISystem.h>
- #include <AzCore/Socket/AzSocket.h>
- #include <AzCore/Utils/Utils.h>
- #undef LockDebug
- //#define LockDebug(str1,str2) {string strMessage;strMessage.Format(str1,str2);if (m_clients.size()) OutputDebugString(strMessage.c_str());}
- #define LockDebug(str1, str2)
- //
- extern bool RCON_IsRemoteAllowedToConnect(const AZ::AzSock::AzSocketAddress& connectee);
- using namespace NotificationNetwork;
- //
- #include <CryPath.h>
- class CQueryNotification
- : public INotificationNetworkListener
- {
- // INotificationNetworkListener
- public:
- virtual void OnNotificationNetworkReceive([[maybe_unused]] const void* pBuffer, [[maybe_unused]] size_t length)
- {
- INotificationNetwork* pNotificationNetwork =
- gEnv->pSystem->GetINotificationNetwork();
- if (!pNotificationNetwork)
- {
- return;
- }
- const char* path = nullptr; // Don't call GetGameFolder here, it returns a full absolute path and we just really want the game name
- AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath();
- if (projectPath.empty())
- {
- return;
- }
- pNotificationNetwork->Send("SystemInfo", projectPath.c_str(), projectPath.size());
- }
- } g_queryNotification;
- AZSOCKET CConnectionBase::CreateSocket()
- {
- AZSOCKET sock = AZ::AzSock::Socket();
- if (!AZ::AzSock::IsAzSocketValid(sock))
- {
- CryLog("CNotificationNetworkClient::Create: Failed to create socket.");
- return AZ_SOCKET_INVALID;
- }
- if (AZ::AzSock::SetSocketOption(sock, AZ::AzSock::AzSocketOption::REUSEADDR, true))
- {
- AZ::AzSock::CloseSocket(sock);
- CryLog("CNotificationNetworkClient::Create: Failed to set SO_REUSEADDR option.");
- return AZ_SOCKET_INVALID;
- }
- #if defined (WIN32) || defined(WIN64) //MS Platforms
- if (AZ::AzSock::SetSocketBlockingMode(sock, false))
- {
- AZ::AzSock::CloseSocket(sock);
- CryLog("CNotificationNetworkClient::Connect: Failed to set socket to asynchronous operation.");
- return AZ_SOCKET_INVALID;
- }
- #endif
- // TCP_NODELAY required for win32 because of high latency connection otherwise
- #if defined(WIN32)
- if (AZ::AzSock::EnableTCPNoDelay(sock, true))
- {
- AZ::AzSock::CloseSocket(sock);
- CryLog("CNotificationNetworkClient::Create: Failed to set TCP_NODELAY option.");
- return AZ_SOCKET_INVALID;
- }
- #endif
- return sock;
- }
- bool CConnectionBase::Connect(const char* address, uint16 port)
- {
- AZ::AzSock::AzSocketAddress socketAddress;
- socketAddress.SetAddress(address, port);
- int result = AZ::AzSock::Connect(m_socket, socketAddress);
- if (AZ::AzSock::SocketErrorOccured(result))
- {
- AZ::AzSock::AzSockError err = AZ::AzSock::AzSockError(result);
- if (err == AZ::AzSock::AzSockError::eASE_EWOULDBLOCK_CONN)
- {
- return true;
- }
- if (err == AZ::AzSock::AzSockError::eASE_EISCONN)
- {
- if (!m_boIsConnected)
- {
- m_boIsConnected = true;
- m_boIsFailedToConnect = false;
- OnConnect(true);
- }
- return true;
- }
- if (err == AZ::AzSock::AzSockError::eASE_EALREADY)
- {
- // It will happen, in case of DNS problems, or if the console is not
- // reachable or turned off.
- //CryLog("CNotificationNetworkClient::Connect: Failed to connect. Reason: already conencted.");
- return true;
- }
- AZ::AzSock::CloseSocket(m_socket);
- m_socket = AZ_SOCKET_INVALID;
- CryLog("CNotificationNetworkClient::Connect: Failed to connect. Reason: %d ", result);
- return false;
- }
- return true;
- }
- /*
- CChannel
- */
- bool CChannel::IsNameValid(const char* name)
- {
- if (!name)
- {
- return false;
- }
- if (!*name)
- {
- return false;
- }
- if (::strlen(name) > NN_CHANNEL_NAME_LENGTH_MAX)
- {
- return false;
- }
- return true;
- }
- //
- CChannel::CChannel()
- {
- }
- CChannel::CChannel(const char* name)
- {
- if (!name)
- {
- return;
- }
- if (!*name)
- {
- return;
- }
- size_t length = MIN(::strlen(name), NN_CHANNEL_NAME_LENGTH_MAX);
- ::memset(m_name, 0, NN_CHANNEL_NAME_LENGTH_MAX);
- ::memcpy(m_name, name, length);
- }
- CChannel::~CChannel()
- {
- }
- //
- void CChannel::WriteToPacketHeader(void* pPacket) const
- {
- ::memcpy((uint8*)pPacket + NN_PACKET_HEADER_OFFSET_CHANNEL,
- m_name, NN_CHANNEL_NAME_LENGTH_MAX);
- }
- void CChannel::ReadFromPacketHeader(void* pPacket)
- {
- ::memcpy(m_name, (uint8*)pPacket + NN_PACKET_HEADER_OFFSET_CHANNEL,
- NN_CHANNEL_NAME_LENGTH_MAX);
- }
- //
- bool CChannel::operator ==(const CChannel& channel) const
- {
- return ::strncmp(m_name, channel.m_name, NN_CHANNEL_NAME_LENGTH_MAX) == 0;
- }
- bool CChannel::operator !=(const CChannel& channel) const
- {
- return ::strncmp(m_name, channel.m_name, NN_CHANNEL_NAME_LENGTH_MAX) != 0;
- }
- /*
- CListeners
- */
- CListeners::CListeners()
- {
- m_pNotificationWrite = &m_notifications[0];
- m_pNotificationRead = &m_notifications[1];
- }
- CListeners::~CListeners()
- {
- while (!m_pNotificationRead->empty())
- {
- SBuffer buffer = m_pNotificationRead->front();
- m_pNotificationRead->pop();
- delete[] buffer.pData;
- }
- while (!m_pNotificationWrite->empty())
- {
- SBuffer buffer = m_pNotificationWrite->front();
- m_pNotificationWrite->pop();
- delete[] buffer.pData;
- }
- }
- //
- size_t CListeners::Count(const CChannel& channel)
- {
- size_t count = 0;
- for (size_t i = 0; i < m_listeners.size(); ++i)
- {
- if (m_listeners[i].second != channel)
- {
- continue;
- }
- ++count;
- }
- return count;
- }
- CChannel* CListeners::Channel(INotificationNetworkListener* pListener)
- {
- for (size_t i = 0; i < m_listeners.size(); ++i)
- {
- if (m_listeners[i].first != pListener)
- {
- continue;
- }
- return &m_listeners[i].second;
- }
- return nullptr;
- }
- bool CListeners::Bind(const CChannel& channel, INotificationNetworkListener* pListener)
- {
- for (size_t i = 0; i < m_listeners.size(); ++i)
- {
- if (m_listeners[i].first == pListener)
- {
- m_listeners[i].second = channel;
- return true;
- }
- }
- m_listeners.push_back(std::pair<INotificationNetworkListener*, CChannel>());
- m_listeners.back().first = pListener;
- m_listeners.back().second = channel;
- return true;
- }
- bool CListeners::Remove(INotificationNetworkListener* pListener)
- {
- for (size_t i = 0; i < m_listeners.size(); ++i)
- {
- if (m_listeners[i].first != pListener)
- {
- continue;
- }
- m_listeners[i] = m_listeners.back();
- m_listeners.pop_back();
- return true;
- }
- return false;
- }
- void CListeners::NotificationPush(const SBuffer& buffer)
- {
- // TODO: Use auto lock.
- m_notificationCriticalSection.Lock();
- m_pNotificationWrite->push(buffer);
- m_notificationCriticalSection.Unlock();
- }
- void CListeners::NotificationsProcess()
- {
- m_notificationCriticalSection.Lock();
- std::swap(m_pNotificationWrite, m_pNotificationRead);
- m_notificationCriticalSection.Unlock();
- while (!m_pNotificationRead->empty())
- {
- SBuffer buffer = m_pNotificationRead->front();
- m_pNotificationRead->pop();
- for (size_t i = 0; i < m_listeners.size(); ++i)
- {
- if (m_listeners[i].second != buffer.channel)
- {
- continue;
- }
- m_listeners[i].first->OnNotificationNetworkReceive(
- buffer.pData, buffer.length);
- }
- delete[] buffer.pData;
- }
- }
- /*
- CConnectionBase
- */
- CConnectionBase::CConnectionBase(CNotificationNetwork* pNotificationNetwork)
- {
- m_pNotificationNetwork = pNotificationNetwork;
- m_port = 0;
- m_socket = AZ_SOCKET_INVALID;
- m_buffer.pData = nullptr;
- m_buffer.length = 0;
- m_dataLeft = 0;
- m_boIsConnected = false;
- m_boIsFailedToConnect = false;
- }
- CConnectionBase::~CConnectionBase()
- {
- if (m_buffer.pData)
- {
- delete[] m_buffer.pData;
- }
- if (m_socket != AZ_SOCKET_INVALID)
- {
- CloseSocket_Internal();
- }
- }
- //
- void CConnectionBase::SetAddress(const char* address, uint16 port)
- {
- size_t length = MIN(::strlen(address), 15);
- ::memset(m_address, 0, sizeof(m_address));
- ::memcpy(m_address, address, length);
- m_port = port;
- }
- bool CConnectionBase::Validate()
- {
- if (m_socket != AZ_SOCKET_INVALID)
- {
- if (!m_port)
- {
- AZ::AzSock::AzSocketAddress socketAddress;
- int result = AZ::AzSock::GetSockName(m_socket, socketAddress);
- if (AZ::AzSock::SocketErrorOccured(result))
- {
- return false;
- }
- }
- return Select_Internal();
- }
- if (!m_port) // If port is not set we don't want to try to reconnect.
- {
- return false;
- }
- m_socket = CreateSocket();
- // If the create sockect will fail, it is likely that we will never be able to connect,
- // we might want to signal that.
- Connect(m_address, m_port);
- return false;
- }
- bool CConnectionBase::Send(const void* pBuffer, size_t length)
- {
- if (!Validate())
- {
- return false;
- }
- size_t sent = 0;
- while (sent < length)
- {
- int r = AZ::AzSock::Send(m_socket, (const char*)pBuffer + sent, length - sent, 0);
- if (AZ::AzSock::SocketErrorOccured(r))
- {
- AZ::AzSock::AzSockError nCurrentError = AZ::AzSock::AzSockError(r);
- if (nCurrentError == AZ::AzSock::AzSockError::eASE_ENOTCONN)
- {
- r = 0;
- break;
- }
- else if (nCurrentError == AZ::AzSock::AzSockError::eASE_EWOULDBLOCK)
- {
- r = 0;
- }
- else
- {
- CryLog("CNotificationNetworkClient::Send: Failed to send package. Reason: %s", AZ::AzSock::GetStringForError(r));
- CloseSocket_Internal();
- return false;
- }
- }
- sent += r;
- }
- return true;
- }
- bool CConnectionBase::SendMessage(EMessage eMessage, const CChannel& channel, uint32 data)
- {
- char header[NN_PACKET_HEADER_LENGTH];
- ::memset(header, 0, NN_PACKET_HEADER_LENGTH);
- *(uint32*)&header[NN_PACKET_HEADER_OFFSET_MESSAGE] = AZ::AzSock::HostToNetLong(eMessage);
- *(uint32*)&header[NN_PACKET_HEADER_OFFSET_DATA_LENGTH] = AZ::AzSock::HostToNetLong(data);
- channel.WriteToPacketHeader(header);
- if (!Send(header, NN_PACKET_HEADER_LENGTH))
- {
- return false;
- }
- return true;
- }
- bool CConnectionBase::Select_Internal()
- {
- if (m_socket == AZ_SOCKET_INVALID)
- {
- return false;
- }
- AZFD_SET stExceptions;
- AZFD_SET stWriteSockets;
- FD_ZERO(&stExceptions);
- FD_SET(m_socket, &stExceptions);
- FD_ZERO(&stWriteSockets);
- FD_SET(m_socket, &stWriteSockets);
- AZTIMEVAL timeOut = { 0, 0 };
- int r = AZ::AzSock::Select(m_socket, nullptr, &stWriteSockets, &stExceptions, &timeOut);
- if (AZ::AzSock::SocketErrorOccured(r))
- {
- CryLog("CNotificationNetworkClient:: Failed to select socket. Reason: %s", AZ::AzSock::GetStringForError(r));
- CloseSocket_Internal();
- m_boIsFailedToConnect = true;
- return false;
- }
- else if (!r)
- {
- return m_boIsConnected;
- }
- if (FD_ISSET(m_socket, &stExceptions))
- {
- CloseSocket_Internal();
- m_boIsFailedToConnect = true;
- OnConnect(m_boIsConnected); // Handles failed attempt to connect.
- return false;
- }
- else if (FD_ISSET(m_socket, &stWriteSockets)) // In Windows a socket can be in both lists.
- {
- if (!m_boIsConnected)
- {
- m_boIsConnected = true;
- m_boIsFailedToConnect = false;
- OnConnect(m_boIsConnected); // Handles successful attempt to connect.
- }
- return true;
- }
- return false;
- }
- void CConnectionBase::CloseSocket_Internal()
- {
- AZ::AzSock::CloseSocket(m_socket);
- m_socket = AZ_SOCKET_INVALID;
- if (m_boIsConnected)
- {
- OnDisconnect();
- }
- m_boIsConnected = false;
- }
- bool CConnectionBase::SendNotification(const CChannel& channel, const void* pBuffer, size_t length)
- {
- if (!SendMessage(eMessage_DataTransfer, channel, length))
- {
- return false;
- }
- if (!length)
- {
- return true;
- }
- if (!Send(pBuffer, length))
- {
- return false;
- }
- return true;
- }
- bool CConnectionBase::ReceiveMessage(CListeners& listeners)
- {
- if (!Validate())
- {
- return false;
- }
- if (!m_dataLeft)
- {
- m_dataLeft = NN_PACKET_HEADER_LENGTH;
- }
- int r = AZ::AzSock::Recv(m_socket, (char*)&m_bufferHeader[NN_PACKET_HEADER_LENGTH - m_dataLeft], m_dataLeft, 0);
- if (!r)
- {
- // Connection terminated.
- m_dataLeft = 0;
- CloseSocket_Internal();
- return false;
- }
- if (AZ::AzSock::SocketErrorOccured(r))
- {
- m_dataLeft = 0;
- CryLog("CNotificationNetworkClient::ReceiveMessage: Failed to receive package. Reason: %s", AZ::AzSock::GetStringForError(r));
- CloseSocket_Internal();
- return false;
- }
- if (m_dataLeft -= r)
- {
- return true;
- }
- // The whole message was received, process it...
- EMessage eMessage = (EMessage)AZ::AzSock::NetToHostLong(
- *(uint32*)&m_bufferHeader[NN_PACKET_HEADER_OFFSET_MESSAGE]);
- const CChannel& channel = *(CChannel*)&m_bufferHeader[NN_PACKET_HEADER_OFFSET_CHANNEL];
- if (eMessage == eMessage_DataTransfer)
- {
- m_dataLeft = AZ::AzSock::NetToHostLong(*(uint32*)&m_bufferHeader[NN_PACKET_HEADER_OFFSET_DATA_LENGTH]);
- if (!m_dataLeft)
- {
- SBuffer buffer;
- buffer.channel = channel;
- buffer.pData = nullptr;
- buffer.length = 0;
- listeners.NotificationPush(buffer);
- return true;
- }
- m_buffer.pData = new uint8[m_buffer.length = m_dataLeft];
- if (!m_buffer.pData)
- {
- CryLog("CNotificationNetwork::CConnection::Receive: Failed to allocate buffer.\n");
- m_dataLeft = 0;
- CloseSocket_Internal();
- return false;
- }
- m_buffer.channel.ReadFromPacketHeader(m_bufferHeader);
- return +1;
- }
- if (!OnMessage(eMessage, channel))
- {
- CryLog("NotificationNetwork::CConnectionBase::ReceiveMessage: "
- "Unknown message received, terminating Connection...\n");
- m_dataLeft = 0;
- CloseSocket_Internal();
- return false;
- }
- return true;
- }
- bool CConnectionBase::ReceiveNotification(CListeners& listeners)
- {
- int r = AZ::AzSock::Recv(m_socket, (char*)&m_buffer.pData[m_buffer.length - m_dataLeft], m_dataLeft, 0);
- if (!r)
- {
- CryLog("CNotificationNetworkClient::ReceiveNotification: Failed to receive package. Reason: Connection terminated.");
- // Connection terminated.
- m_dataLeft = 0;
- CloseSocket_Internal();
- return false;
- }
- if (AZ::AzSock::SocketErrorOccured(r))
- {
- m_dataLeft = 0;
- CryLog("CNotificationNetworkClient::ReceiveNotification: Failed to receive package. Reason: %s", AZ::AzSock::GetStringForError(r));
- CloseSocket_Internal();
- return false;
- }
- if (m_dataLeft -= r)
- {
- return true;
- }
- listeners.NotificationPush(m_buffer);
- m_buffer.pData = nullptr;
- m_buffer.length = 0;
- m_dataLeft = 0;
- return true;
- }
- bool CConnectionBase::Receive(CListeners& listeners)
- {
- if (m_buffer.pData)
- {
- return ReceiveNotification(listeners);
- }
- return ReceiveMessage(listeners);
- }
- bool CConnectionBase::GetIsConnectedFlag()
- {
- return Select_Internal() || m_boIsConnected;
- }
- bool CConnectionBase::GetIsFailedToConnectFlag() const
- {
- return m_boIsFailedToConnect;
- }
- /*
- CClient
- */
- CClient* CClient::Create(CNotificationNetwork* pNotificationNetwork, const char* address, uint16 port)
- {
- CClient* pClient = new CClient(pNotificationNetwork);
- AZSOCKET sock = pClient->CreateSocket();
- // In the current implementation, this is REALLY UNLIKELY to happen.
- if (sock == AZ_SOCKET_INVALID)
- {
- delete pClient;
- return nullptr;
- }
- //
- pClient->SetSocket(sock);
- pClient->Connect(address, port);
- pClient->SetAddress(address, port);
- pClient->SetSocket(sock);
- return pClient;
- }
- CClient* CClient::Create(CNotificationNetwork* pNotificationNetwork)
- {
- CClient* pClient = new CClient(pNotificationNetwork);
- return pClient;
- }
- //
- CClient::CClient(CNotificationNetwork* pNotificationNetwork)
- : CConnectionBase(pNotificationNetwork)
- {
- }
- CClient::~CClient()
- {
- GetNotificationNetwork()->ReleaseClients(this);
- }
- //
- void CClient::Update()
- {
- m_listeners.NotificationsProcess();
- }
- // CConnectionBase
- bool CClient::OnConnect(bool boConnected)
- {
- if (boConnected)
- {
- for (size_t i = 0; i < m_listeners.Count(); ++i)
- {
- if (!SendMessage(eMessage_ChannelRegister, m_listeners.Channel(i), 0))
- {
- return false;
- }
- }
- }
- CryAutoLock<CryCriticalSection> lock(m_stConnectionCallbacksLock);
- for (size_t nCount = 0; nCount < m_cNotificationNetworkConnectionCallbacks.size(); ++nCount)
- {
- m_cNotificationNetworkConnectionCallbacks[nCount]->OnConnect(this, boConnected);
- }
- return boConnected;
- }
- bool CClient::OnDisconnect()
- {
- CryAutoLock<CryCriticalSection> lock(m_stConnectionCallbacksLock);
- for (size_t nCount = 0; nCount < m_cNotificationNetworkConnectionCallbacks.size(); ++nCount)
- {
- m_cNotificationNetworkConnectionCallbacks[nCount]->OnDisconnected(this);
- }
- return true;
- }
- bool CClient::OnMessage([[maybe_unused]] EMessage eMessage, [[maybe_unused]] const CChannel& channel)
- {
- return false;
- }
- // INotificationNetworkClient
- bool CClient::Connect(const char* address, uint16 port)
- {
- bool bReturnValue(false);
- if (m_socket == AZ_SOCKET_INVALID)
- {
- m_socket = CreateSocket();
- }
- bReturnValue = CConnectionBase::Connect(address, port);
- if (bReturnValue)
- {
- SetAddress(address, port);
- }
- return bReturnValue;
- }
- bool CClient::ListenerBind(const char* channelName, INotificationNetworkListener* pListener)
- {
- if (!CChannel::IsNameValid(channelName))
- {
- return false;
- }
- if (!m_listeners.Bind(CChannel(channelName), pListener))
- {
- return false;
- }
- if (!SendMessage(eMessage_ChannelRegister, CChannel(channelName), 0))
- {
- return false;
- }
- return true;
- }
- bool CClient::ListenerRemove(INotificationNetworkListener* pListener)
- {
- CChannel* pChannel = m_listeners.Channel(pListener);
- if (!pChannel)
- {
- return false;
- }
- if (!m_listeners.Remove(pListener))
- {
- return false;
- }
- if (!SendMessage(eMessage_ChannelUnregister, *pChannel, 0))
- {
- return false;
- }
- return true;
- }
- bool CClient::Send(const char* channelName, const void* pBuffer, size_t length)
- {
- CRY_ASSERT(CChannel::IsNameValid(channelName));
- // CRY_ASSERT_MESSAGE(channelLength <= NN_CHANNEL_NAME_LENGTH_MAX,
- // "Channel name \"%s\" was passed to a Notification Network method, the name cannot be longer than %d chars.",
- // channel, NN_CHANNEL_NAME_LENGTH_MAX);
- if (!CChannel::IsNameValid(channelName))
- {
- return false;
- }
- if (!SendNotification(CChannel(channelName), pBuffer, length))
- {
- return false;
- }
- return true;
- }
- bool CClient::RegisterCallbackListener(INotificationNetworkConnectionCallback* pConnectionCallback)
- {
- CryAutoLock<CryCriticalSection> lock(m_stConnectionCallbacksLock);
- return stl::push_back_unique(m_cNotificationNetworkConnectionCallbacks, pConnectionCallback);
- }
- bool CClient::UnregisterCallbackListener(INotificationNetworkConnectionCallback* pConnectionCallback)
- {
- CryAutoLock<CryCriticalSection> lock(m_stConnectionCallbacksLock);
- return stl::find_and_erase(m_cNotificationNetworkConnectionCallbacks, pConnectionCallback);
- }
- /*
- CNotificationNetwork::CConnection
- */
- CNotificationNetwork::CConnection::CConnection(CNotificationNetwork* pNotificationNetwork, AZSOCKET sock)
- : CConnectionBase(pNotificationNetwork)
- {
- SetSocket(sock);
- m_listeningChannels.reserve(8);
- }
- CNotificationNetwork::CConnection::~CConnection()
- {
- }
- //
- bool CNotificationNetwork::CConnection::IsListening(const CChannel& channel)
- {
- for (size_t i = 0; i < m_listeningChannels.size(); ++i)
- {
- if (m_listeningChannels[i] == channel)
- {
- return true;
- }
- }
- return false;
- }
- // CConnectionBase
- bool CNotificationNetwork::CConnection::OnMessage(EMessage eMessage, const CChannel& channel)
- {
- switch (eMessage)
- {
- case eMessage_ChannelRegister:
- for (size_t i = 0; i < m_listeningChannels.size(); ++i)
- {
- if (m_listeningChannels[i] == channel)
- {
- return true;
- }
- }
- m_listeningChannels.push_back(channel);
- return true;
- case eMessage_ChannelUnregister:
- for (size_t i = 0; i < m_listeningChannels.size(); ++i)
- {
- if (m_listeningChannels[i] != channel)
- {
- continue;
- }
- m_listeningChannels[i] = m_listeningChannels.back();
- m_listeningChannels.pop_back();
- return true;
- }
- return true;
- }
- return false;
- }
- /*
- CNotificationNetwork::CThread
- */
- CNotificationNetwork::CThread::CThread()
- {
- m_pNotificationNetwork = nullptr;
- m_bRun = true;
- }
- CNotificationNetwork::CThread::~CThread()
- {
- }
- //
- bool CNotificationNetwork::CThread::Begin(CNotificationNetwork* pNotificationNetwork)
- {
- m_pNotificationNetwork = pNotificationNetwork;
- Start(-1, (char*)NN_THREAD_NAME);
- return true;
- }
- void CNotificationNetwork::CThread::End()
- {
- m_bRun = false;
- // WaitForThread();
- // TODO: Should properly close!
- }
- // CryRunnable
- void CNotificationNetwork::CThread::Run()
- {
- CryThreadSetName(threadID(THREADID_NULL), NN_THREAD_NAME);
- while (m_bRun)
- {
- m_pNotificationNetwork->ProcessSockets();
- }
- }
- /*
- CNotificationNetwork
- */
- CNotificationNetwork* CNotificationNetwork::Create()
- {
- AZ::AzSock::Startup();
- AZSOCKET sock = AZ::AzSock::Socket();
- if (!AZ::AzSock::IsAzSocketValid(sock))
- {
- CryLog("CNotificationNetwork::Create: Failed to create socket.\n");
- return nullptr;
- }
- // Disable nagling of small blocks to fight high latency connection
- int result = AZ::AzSock::EnableTCPNoDelay(sock, true);
- if (AZ::AzSock::SocketErrorOccured(result))
- {
- AZ::AzSock::CloseSocket(sock);
- CryLog("CNotificationNetworkClient::Create: Failed to set TCP_NODELAY option.");
- return nullptr;
- }
- result = AZ::AzSock::SetSocketBlockingMode(sock, false);
- if (AZ::AzSock::SocketErrorOccured(result))
- {
- AZ::AzSock::CloseSocket(sock);
- CryLog("CNotificationNetworkClient::Connect: Failed to set socket to asynchronous operation.");
- return nullptr;
- }
- // Editor uses a different port to avoid conflicts when running both editor and game on same PC
- // But allows the lua remote debugger to connect to the editor
- unsigned short port = gEnv && gEnv->IsEditor() ? 9433 : 9432;
- AZ::AzSock::AzSocketAddress addr;
- addr.SetAddrPort(port);
- result = AZ::AzSock::Bind(sock, addr);
- if (AZ::AzSock::SocketErrorOccured(result))
- {
- CryLog("CNotificationNetwork::Create: Failed to bind socket.\n");
- AZ::AzSock::CloseSocket(sock);
- return nullptr;
- }
- result = AZ::AzSock::Listen(sock, 8);
- if (AZ::AzSock::SocketErrorOccured(result))
- {
- CryLog("CNotificationNetwork::Create: Failed to listen.\n");
- AZ::AzSock::CloseSocket(sock);
- return nullptr;
- }
- CNotificationNetwork* pNotificationNetwork = new CNotificationNetwork();
- pNotificationNetwork->m_socket = sock;
- pNotificationNetwork->m_thread.Begin(pNotificationNetwork);
- return pNotificationNetwork;
- }
- //
- CNotificationNetwork::CNotificationNetwork()
- {
- m_socket = AZ_SOCKET_INVALID;
- m_connections.reserve(4);
- m_listeners.Bind("Query", &g_queryNotification);
- }
- CNotificationNetwork::~CNotificationNetwork()
- {
- m_thread.End();
- m_thread.Stop();
- m_thread.WaitForThread();
- while (!m_connections.empty())
- {
- delete m_connections.back();
- m_connections.pop_back();
- }
- if (m_socket != AZ_SOCKET_INVALID)
- {
- AZ::AzSock::CloseSocket(m_socket);
- m_socket = AZ_SOCKET_INVALID;
- }
- AZ::AzSock::Cleanup();
- }
- //
- void CNotificationNetwork::ReleaseClients(CClient* pClient)
- {
- // TODO: Use CryAutoLock
- LockDebug("Lock %s\n", "CNotificationNetwork::ReleaseClients()");
- m_clientsCriticalSection.Lock();
- for (size_t i = 0; i < m_clients.size(); ++i)
- {
- if (m_clients[i] != pClient)
- {
- continue;
- }
- m_clients[i] = m_clients.back();
- m_clients.pop_back();
- break;
- }
- m_clientsCriticalSection.Unlock();
- LockDebug("Unlock %s\n", "CNotificationNetwork::ReleaseClients()");
- }
- void CNotificationNetwork::ProcessSockets()
- {
- fd_set read;
- FD_ZERO(&read);
- AZSOCKET socketMax = 0;
- if (m_socket != AZ_SOCKET_INVALID)
- {
- FD_SET(m_socket, &read);
- socketMax = m_socket;
- }
- for (size_t i = 0; i < m_connections.size(); ++i)
- {
- if (m_connections[i]->Validate())
- {
- AZSOCKET sock = m_connections[i]->GetSocket();
- FD_SET(sock, &read);
- if (socketMax < sock)
- {
- socketMax = sock;
- }
- continue;
- }
- // The Connection is invalid, remove it.
- CConnection* pConnection = m_connections[i];
- m_connections[i] = m_connections.back();
- m_connections.pop_back();
- delete pConnection;
- // Invalidate the loop increment since we just removed a Connection and
- // in the process potentially replaced its slot with an unprocessed one.
- --i;
- CryLog("Notification Network Connection terminated, current total: %d\n",
- (int)m_connections.size());
- }
- LockDebug("Lock %s\n", "CNotificationNetwork::ProcessSockets()");
- m_clientsCriticalSection.Lock();
- for (size_t i = 0; i < m_clients.size(); ++i)
- {
- if (!m_clients[i]->Validate())
- {
- continue;
- }
- AZSOCKET sock = m_clients[i]->GetSocket();
- FD_SET(sock, &read);
- if (socketMax < sock)
- {
- socketMax = sock;
- }
- }
- m_clientsCriticalSection.Unlock();
- LockDebug("Unlock %s\n", "CNotificationNetwork::ProcessSockets()");
- AZTIMEVAL timeOut = { 1, 0 };
- int r = AZ::AzSock::Select(socketMax, &read, nullptr, nullptr, &timeOut);
- if (r == 0)
- {
- return;
- }
- // When we have no sockets, the select statement will fail and not
- // block for even 1 second, as it should...
- if (AZ::AzSock::SocketErrorOccured(r))
- {
- // So we force the sleep here for now.
- Sleep(1000);
- return;
- }
- for (size_t i = 0; i < m_connections.size(); ++i)
- {
- if (!FD_ISSET(m_connections[i]->GetSocket(), &read))
- {
- continue;
- }
- m_connections[i]->Receive(m_listeners);
- }
- LockDebug("Lock 2 %s\n", "CNotificationNetwork::ProcessSockets()");
- m_clientsCriticalSection.Lock();
- for (size_t i = 0; i < m_clients.size(); ++i)
- {
- if (!FD_ISSET(m_clients[i]->GetSocket(), &read))
- {
- continue;
- }
- m_clients[i]->Receive();
- }
- m_clientsCriticalSection.Unlock();
- LockDebug("Unlock 2 %s\n", "CNotificationNetwork::ProcessSockets()");
- if (m_socket == AZ_SOCKET_INVALID)
- {
- return;
- }
- if (!FD_ISSET(m_socket, &read))
- {
- return;
- }
- AZ::AzSock::AzSocketAddress addr;
- AZSOCKET sock = AZ::AzSock::Accept(m_socket, addr);
- if (!AZ::AzSock::IsAzSocketValid(sock))
- {
- return;
- }
- if (!RCON_IsRemoteAllowedToConnect(addr))
- {
- AZ::AzSock::CloseSocket(sock);
- return;
- }
- m_connections.push_back(new CConnection(this, sock));
- CryLog("Notification Network accepted new Connection, current total: %d\n",
- (int)m_connections.size());
- }
- // INotificationNetwork
- INotificationNetworkClient* CNotificationNetwork::CreateClient()
- {
- CClient* pClient = CClient::Create(this);
- LockDebug("Lock %s\n", "CNotificationNetwork::CreateClient()");
- m_clientsCriticalSection.Lock();
- m_clients.push_back(pClient);
- m_clientsCriticalSection.Unlock();
- LockDebug("Unlock %s\n", "CNotificationNetwork::CreateClient()");
- return pClient;
- }
- INotificationNetworkClient* CNotificationNetwork::Connect(const char* address, uint16 port)
- {
- CClient* pClient = CClient::Create(this, address, port);
- if (!pClient)
- {
- return nullptr;
- }
- LockDebug("Lock %s\n", "CNotificationNetwork::Connect()");
- m_clientsCriticalSection.Lock();
- m_clients.push_back(pClient);
- m_clientsCriticalSection.Unlock();
- LockDebug("Unlock %s\n", "CNotificationNetwork::Connect()");
- return pClient;
- }
- size_t CNotificationNetwork::GetConnectionCount(const char* channelName)
- {
- if (!channelName)
- {
- return m_connections.size();
- }
- if (!CChannel::IsNameValid(channelName))
- {
- return 0;
- }
- CChannel channel(channelName);
- size_t count = 0;
- for (size_t i = 0; i < m_connections.size(); ++i)
- {
- if (!m_connections[i]->IsListening(channel))
- {
- continue;
- }
- ++count;
- }
- return count;
- }
- bool CNotificationNetwork::ListenerBind(const char* channelName, INotificationNetworkListener* pListener)
- {
- if (!CChannel::IsNameValid(channelName))
- {
- return false;
- }
- return m_listeners.Bind(CChannel(channelName), pListener);
- }
- bool CNotificationNetwork::ListenerRemove(INotificationNetworkListener* pListener)
- {
- return m_listeners.Remove(pListener);
- }
- void CNotificationNetwork::Update()
- {
- m_listeners.NotificationsProcess();
- LockDebug("Lock %s\n", "CNotificationNetwork::Update()");
- m_clientsCriticalSection.Lock();
- for (size_t i = 0; i < m_clients.size(); ++i)
- {
- m_clients[i]->Update();
- }
- m_clientsCriticalSection.Unlock();
- LockDebug("Unlock %s\n", "CNotificationNetwork::Update()");
- }
- uint32 CNotificationNetwork::Send(const char* channelName, const void* pBuffer, size_t length)
- {
- if (!CChannel::IsNameValid(channelName))
- {
- return 0;
- }
- CChannel channel(channelName);
- // TODO: There should be a mutex lock here to ensure thread safety.
- uint32 count = 0;
- for (size_t i = 0; i < m_connections.size(); ++i)
- {
- if (!m_connections[i]->IsListening(channel))
- {
- continue;
- }
- if (m_connections[i]->SendNotification(channel, pBuffer, length))
- {
- ++count;
- }
- }
- return count;
- }
|