123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- ////////////////////////////////////////////////////////////////////////////////
- //
- // Copyright 2016 RWS Inc, All Rights Reserved
- //
- // This program is free software; you can redistribute it and/or modify
- // it under the terms of version 2 of the GNU General Public License as published by
- // the Free Software Foundation
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License along
- // with this program; if not, write to the Free Software Foundation, Inc.,
- // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- //
- // NetMsgr.cpp
- // Project: Net Messenger
- //
- // History:
- // 05/24/97 MJR Final assault begins.
- //
- // 05/25/97 JMI Filled out CNetMsgr::InfoMsg declaration.
- //
- // MJR Made some changes to union, structs, etc.
- //
- // 05/26/97 MJR Moved some functions from header to here.
- //
- // 06/11/97 JMI Added FINISH_REALM and FINISHED_REALM net messages.
- //
- // 06/14/97 MJR Removed LOAD_REALM and FINISHED_REALM messages and
- // modified START_GAME message.
- //
- // 08/13/97 MJR Changed from ASSERT() to TRACE() when message len is
- // incorrect.
- //
- // 08/14/97 MJR Changed from TRACE() when message len is incorrect to
- // generating an actual error. This is safer in case this
- // ever occurs in a real game situation.
- //
- // Also fixed bug where the message byte would be ungotten
- // twice if the message hadn't been fully received yet.
- //
- // 08/15/97 MJR Cleaned up usage of CBufQ along with some other potential
- // (actual???) bugs.
- //
- // 08/18/97 MJR Added "ChangeReq" and "Changed" messages.
- //
- // 09/01/97 MJR Lots of changes as part of overall network overhaul.
- //
- // 09/02/97 MJR Tested and tuned alot, and fixed a bunch of bugs. Now
- // appears to be very stable.
- //
- // 09/07/97 MJR Added "Proceed" message.
- //
- // 06/02/98 JMI The handling of variable length messages was assuming the
- // message size was 2 (a short) but it was really a long.
- // Fixed.
- //
- ////////////////////////////////////////////////////////////////////////////////
- #include "RSPiX.h"
- #include "netmsgr.h"
- #include "NetDlg.h"
- ////////////////////////////////////////////////////////////////////////////////
- // This macro creates the horrendous amount of crap needed to declare the
- // various pieces of information about a particular message struct.
- //
- // For each struct, we get it's size and pointers to it's Read() and Write()
- // functions. The usage of these function pointers is very similar to C++
- // virtual functions, but for various reasons, we went with structs instead
- // of classes, so we have to do our own table of function pointers.
- ////////////////////////////////////////////////////////////////////////////////
- #define INFO(x) NetMsg::x::Size, \
- NetMsg::x::Read, \
- NetMsg::x::Write
- ////////////////////////////////////////////////////////////////////////////////
- // Initialize array of information about each message struct.
- ////////////////////////////////////////////////////////////////////////////////
- CNetMsgr::InfoMsg CNetMsgr::ms_aInfoMsg[NetMsg::NumMessages] =
- {
- { NetMsg::NOTHING , INFO(Nothing) },
- { NetMsg::STAT , INFO(Stat) },
- { NetMsg::ERR , INFO(Err) },
- { NetMsg::LOGIN , INFO(Login) },
- { NetMsg::LOGIN_ACCEPT , INFO(LoginAccept) },
- { NetMsg::LOGIN_DENY , INFO(LoginDeny) },
- { NetMsg::LOGOUT , INFO(Logout) },
- { NetMsg::JOIN_REQ , INFO(JoinReq) },
- { NetMsg::JOIN_ACCEPT , INFO(JoinAccept) },
- { NetMsg::JOIN_DENY , INFO(JoinDeny) },
- { NetMsg::JOINED , INFO(Joined) },
- { NetMsg::CHANGE_REQ , INFO(ChangeReq) },
- { NetMsg::CHANGED , INFO(Changed) },
- { NetMsg::DROP_REQ , INFO(DropReq) },
- { NetMsg::DROPPED , INFO(Dropped) },
- { NetMsg::DROP_ACK , INFO(DropAck) },
- { NetMsg::INPUT_REQ , INFO(InputReq) },
- { NetMsg::INPUT_DATA , (size_t) (INFO(InputData)) },
- { NetMsg::INPUT_MARK , INFO(InputMark) },
- { NetMsg::CHAT_REQ , INFO(ChatReq) },
- { NetMsg::CHAT , INFO(Chat) },
- { NetMsg::SETUP_GAME , INFO(SetupGame) },
- { NetMsg::START_GAME , INFO(StartGame) },
- { NetMsg::ABORT_GAME , INFO(AbortGame) },
- { NetMsg::READY_REALM , INFO(ReadyRealm) },
- { NetMsg::BAD_REALM , INFO(BadRealm) },
- { NetMsg::START_REALM , INFO(StartRealm) },
- { NetMsg::HALT_REALM , INFO(HaltRealm) },
- { NetMsg::NEXT_REALM , INFO(NextRealm) },
- { NetMsg::PROGRESS_REALM , INFO(ProgressRealm) },
- { NetMsg::PROCEED , INFO(Proceed) },
- { NetMsg::PING , INFO(Ping) },
- { NetMsg::RAND , INFO(Rand) }
- };
- ////////////////////////////////////////////////////////////////////////////////
- // Update (must be called regularly)
- ////////////////////////////////////////////////////////////////////////////////
- void CNetMsgr::Update(void)
- {
- switch (m_state)
- {
- case Disconnected:
- // Nothing to do
- break;
- case Connecting:
- {
- short serr = m_socket.Connect(&m_address);
- if (serr == 0)
- {
- m_state = Connected;
- m_lMsgRecvTime = rspGetMilliseconds();
- m_lMsgSentTime = rspGetMilliseconds();
- }
- else if (serr != RSocket::errWouldBlock)
- {
- TRACE("CNetMsgr::Connect(): Attempt to connect failed!\n");
- // It may seem harsh, but resetting is just fine in this
- // situation because it cleans everything up, and there's
- // nothing to be "nice" about -- we're not connected to anyone!
- Reset();
- }
- }
- break;
- case Connected:
- ReceiveData();
- SendData();
- break;
- case Disconnecting:
- // When disconnecting, we only send data (we don't bother receiving it).
- // If there's no more data to send, or there's a send error, then disconnect.
- SendData();
- if (!IsMoreToSend())
- Disconnect();
- break;
- default:
- TRACE("CNetMsgr::Update(): Unknown state!\n");
- break;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Get message
- ////////////////////////////////////////////////////////////////////////////////
- bool CNetMsgr::GetMsg( // True if message was available, false otherwise
- NetMsg* pmsg) // Out: Message is returned here
- {
- bool bGotOne = false;
- // If there is no error condition
- if (m_error == NetMsg::NoError)
- {
- // See how much data (if any) is available
- long lGetable = m_bufIn.CheckGetable();
- if (lGetable >= 1)
- {
- // Peek at first byte of data, which ought to be the message type
- unsigned char ucMsg;
- if (m_bufIn.Get(&ucMsg) == 1)
- {
- // Make sure it's a valid message type
- if ((ucMsg >= 0) && (ucMsg < NetMsg::NumMessages))
- {
- // Get the expected message length. A value of -1 indicates a
- // variable-sized message, in which case the next 2 bytes (if
- // they are available) would indicate the message size.
- long lMsgSize = ms_aInfoMsg[ucMsg].size;
- if (lMsgSize == -1)
- {
- // Check if at least enough is available (beyond the ucMsg byte we got)
- if (lGetable >= sizeof(ucMsg) + sizeof(lMsgSize) )
- {
- // Get the message size. We assume this will always succeed because
- // we were just told that enough was available.
- m_bufIn.Get(&lMsgSize);
- // Undo the get of lMsgSize.
- short sInc;
- for (sInc = 0; sInc < sizeof(lMsgSize); sInc++)
- {
- m_bufIn.UnGet();
- }
- }
- else
- {
- // Set fake message size so we'll realize message is not available
- lMsgSize = 0x7fffffff;
- }
- }
- // Undo the get of ucMsg byte.
- m_bufIn.UnGet();
- // If entire message is available, then we can get it
- if (lGetable >= lMsgSize)
- {
- // Make sure the read func is the right one . . .
- ASSERT(ms_aInfoMsg[ucMsg].ucType == ucMsg);
- // Read the message
- (ms_aInfoMsg[ucMsg].funcRead)(pmsg, &m_bufIn);
- // Verify that the correct number of bytes were read
- long lNewGetable = m_bufIn.CheckGetable();
- if ((lGetable - lNewGetable) == lMsgSize)
- {
- // Update most-recent receive time
- m_lMsgRecvTime = rspGetMilliseconds();
- // Indicate that we got a message
- bGotOne = true;
- /* // 12/7/97 AJC
- #ifdef WIN32
- if (g_GameSettings.m_bLogNetTime)
- {
- if (ucMsg == NetMsg::INPUT_DATA)
- WriteTimeStamp("CNetMsgr::GetMsg()",
- (char*)pmsg->msg.inputData.id ,
- ucMsg,
- 0,
- 0,
- true);
- else if (ucMsg != NetMsg::NOTHING)
- WriteTimeStamp("CNetMsgr::GetMsg()",
- NULL,
- ucMsg,
- 0,
- 0,
- true);
- }
- #endif
- // 12/7/97 AJC
- */
- }
- else
- {
- m_error = NetMsg::ReceiveError;
- TRACE("CNetMsgr::GetMsg(): Msg len should be %ld but was %ld !\n", lMsgSize, lGetable - lNewGetable);
- }
- }
- }
- else
- {
- // An invalid message type means we're in deep shit. There's no real way to
- // recover because we can't tell where messages start and end.
- m_error = NetMsg::ReceiveError;
- TRACE("CNetMsgr::GetMsg(): Invalid message type: %hd !\n", (short)ucMsg);
- }
- }
- }
- }
- // If there's an error condition then we need to generate an error message. Note that
- // the error condition might have existed before we got into this function, in which
- // case we would not have tried to get a message above. If there was no error on the
- // way in, and there is one now, then it occured while trying to get a message above.
- // Either way, we catch it here.
- if (m_error != NetMsg::NoError)
- {
- pmsg->msg.err.ucType = NetMsg::ERR;
- pmsg->msg.err.error = m_error;
- // Got one.
- bGotOne = true;
- // Clear error now that user has been notified.
- m_error = NetMsg::NoError;
- /* // 12/7/97 AJC
- #ifdef WIN32
- if (g_GameSettings.m_bLogNetTime)
- {
- WriteTimeStamp("CNetMsgr::GetMsg()",
- NULL,
- NetMsg::ERR,
- 0,
- 0,
- true);
- }
- #endif
- // 12/7/97 AJC
- */
- }
- // If we didn't get a message, then generate a "nothing" message
- if (!bGotOne)
- pmsg->msg.nothing.ucType = NetMsg::NOTHING;
- //==============================================================================
- // MUST BE AT END OF FUNCTION!!!
- //
- // If it's a send error and we're disconnecting, then disconnect now.
- //
- // We do this at the end of this function because if we disconnect, we don't
- // want anyone to accidentally change any member variables afterwards!
- //==============================================================================
- if (m_state == Disconnecting)
- {
- if (pmsg->msg.nothing.ucType == NetMsg::ERR)
- {
- if (IsSendError(pmsg->msg.err.error))
- Disconnect();
- }
- }
- return bGotOne;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Send message
- ////////////////////////////////////////////////////////////////////////////////
- void CNetMsgr::SendMsg(
- NetMsg* pmsg, // In: Message to send
- bool bSendNow /*= true*/) // In: Whether to send now or wait until Update()
- {
- ASSERT(m_state == Connected);
- // Only send messages if we're connected
- if (m_state == Connected)
- {
- // Get msg type.
- U8 ucMsg = pmsg->msg.nothing.ucType;
- // Make sure it's a valid message type
- if ((ucMsg >= 0) && (ucMsg < NetMsg::NumMessages))
- {
- // Determine message size. A size of -1 indicates a variable-sized message,
- // in which case the actual size is stored within the message itself.
- long lMsgSize = ms_aInfoMsg[ucMsg].size;
- if (lMsgSize == -1)
- lMsgSize = pmsg->msg.nothing.lSize;
- // Check available space in the queue, and if it's enough for the message, go ahead
- long lPutable = m_bufOut.CheckPutable();
- if (lPutable >= lMsgSize)
- {
- // Make sure the write func is the right one . . .
- ASSERT(ms_aInfoMsg[ucMsg].ucType == ucMsg);
- // Write the message . . .
- (ms_aInfoMsg[ucMsg].funcWrite)(pmsg, &m_bufOut);
- // Verify that the correct number of bytes were written
- long lNewPutable = m_bufOut.CheckPutable();
- if ((lPutable - lNewPutable) == lMsgSize)
- {
- // Update time last message was sent (hmmmm....not really! This merely indicates
- // that the message was queued up, but not that it was actually sent! Perhaps
- // this needs to be changed at some point to be more accurate. It would have to
- // be moved to wherever we actually transmit the data to the socket. Of course,
- // at that level, it's hard to tell if an entire message was sent, but still, it
- // would be more accurate than this.
- m_lMsgSentTime = rspGetMilliseconds();
- }
- else
- {
- m_error = NetMsg::SendError;
- TRACE("CNetMsgr::SendMsg(): Msg len should be %ld but was %ld !\n", lMsgSize, lPutable - lNewPutable);
- }
- }
- else
- {
- m_error = NetMsg::OutQFullError;
- TRACE("CNetMsgr::SendMsg(): Output queue is full!\n");
- }
- }
- else
- {
- // This should obviously never occur as it would indicate a humongous internal problem,
- // like the program has blown itself up or something.
- m_error = NetMsg::SendError;
- TRACE("CNetMsgr::SendMsg(): Attempting to write invalid message type: %hd !\n", (short)ucMsg);
- ASSERT(0);
- }
-
- // If "send now" flag is set, do it
- if (bSendNow)
- {
- /* // 12/7/97 AJC
- #ifdef WIN32
- if (g_GameSettings.m_bLogNetTime)
- {
- if (ucMsg == NetMsg::INPUT_DATA)
- WriteTimeStamp("CNetMsgr::SendMsg()",
- (char*)pmsg->msg.inputData.id ,
- ucMsg,
- 0,
- 0,
- false);
- else if (ucMsg != NetMsg::NOTHING)
- WriteTimeStamp("CNetMsgr::SendMsg()",
- NULL,
- ucMsg,
- 0,
- 0,
- false);
- }
- #endif
- // 12/7/97 AJC
- */
- SendData();
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Receive data (copy data from socket into input buffer)
- ////////////////////////////////////////////////////////////////////////////////
- void CNetMsgr::ReceiveData(void)
- {
- // If there's bytes waiting to be received and no errors, then get them.
- // This was originally a while() loop but that was too dangerous because a
- // constant stream of data would have kept us in the loop forever! Instead
- // we now loop twice. The reason is that our buffer is implimented in such
- // a way that if the largest block we can write to it is from the current
- // position to the end of the buffer. However, there might be additional
- // space at the beginning of the buffer -- all we have to do is "wrap around"
- // to the beginning. Doing two gets does exactly that.
- for (short sGet = 0; sGet < 2; sGet++)
- {
- if (m_socket.CheckReceivableBytes() && (m_error == NetMsg::NoError))
- {
- // Call watchdog to let it know we're still going (we're in a loop!)
- NetBlockingWatchdog();
- // No bytes received yet
- long lReceivedBytes = 0;
- // Lock the buffer so we can write directly into it
- U8* pu8Put;
- long lMaxPuttableBytes;
- m_bufIn.LockPutPtr(&pu8Put, &lMaxPuttableBytes);
- // Make sure there's room in the buffer
- if (lMaxPuttableBytes > 0)
- {
- // Receive up to the specified number of bytes
- short serr = m_socket.Receive(pu8Put, lMaxPuttableBytes, &lReceivedBytes);
- if ((serr != 0) && (serr != RSocket::errWouldBlock))
- {
- m_error = NetMsg::ReceiveError;
- TRACE("CNetMsgr::Update(): Receive error!\n");
- }
- }
- else
- {
- TRACE("CNetMsgr::Update(): Warning! Input queue is full!\n");
- }
- // Release pointer, telling it how many bytes we actually added to it
- m_bufIn.ReleasePutPtr(lReceivedBytes);
- }
- else
- break;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Send data (copy data output buffer to socket)
- ////////////////////////////////////////////////////////////////////////////////
- void CNetMsgr::SendData(void)
- {
- // If we have data to send and there's no errors, then try to send it. It's
- // safe to loop because we can't get stuck -- there's only so much data to send!
- while (!m_bufOut.IsEmpty() && (m_error == NetMsg::NoError))
- {
- // We don't check this in the while() because it may be somewhat slow,
- // depending on the underlying implimentation. Instead, we only check
- // it after we've already determined that we actually have data to send.
- if (m_socket.CanSendWithoutBlocking())
- {
- // Call watchdog to let it know we're still going (we're in a loop!)
- NetBlockingWatchdog();
- // No bytes sent yet
- long lSentBytes = 0;
- // Lock the buffer so we can read directly from it
- U8* pu8Get;
- long lMaxGettableBytes;
- m_bufOut.LockGetPtr(&pu8Get, &lMaxGettableBytes);
- // Make sure we can get something from buffer (this is not really
- // necessary since we already check IsEmpty() above, but what the hell...
- if (lMaxGettableBytes > 0)
- {
- // Receive up to the specified number of bytes
- short serr = m_socket.Send(pu8Get, lMaxGettableBytes, &lSentBytes);
- if ((serr != 0) && (serr != RSocket::errWouldBlock))
- {
- m_error = NetMsg::SendError;
- TRACE("CNetMsgr::Update(): Send error!\n");
- }
- }
- else
- {
- // If for some unbelievable reason this test fails even though the
- // previous test said there was data, then we're in deep shit, but
- // let's avoid an infinite loop in any case...
- TRACE("CNetMsgr::Update(): Internal inconsistancy detected!!!\n");
- ASSERT(0);
- break;
- }
- // Release pointer, telling it how many bytes we actually got from it
- m_bufOut.ReleaseGetPtr(lSentBytes);
- }
- else
- {
- // If we can't send without blocking, then break out of the loop
- break;
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // EOF
- ////////////////////////////////////////////////////////////////////////////////
|