123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560 |
- ////////////////////////////////////////////////////////////////////////////////
- //
- // 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
- //
- // netclient.h
- // Project: RSPiX
- //
- // History:
- // 08/30/97 MJR Started.
- //
- // 11/25/97 JMI Changed m_error to m_msgError so we could store a whole
- // error message instead of just the error type.
- //
- ////////////////////////////////////////////////////////////////////////////////
- #ifndef NETCLIENT_H
- #define NETCLIENT_H
- #include "RSPiX.h"
- #include "socket.h"
- #include "net.h"
- #include "netmsgr.h"
- #include "NetInput.h"
- #include "ORANGE/CDT/fqueue.h"
- #include "IdBank.h"
- #include "Average.h"
- #define NUM_TIMES 8; // Number of frame times to remember
- #define TIME_MASK 0x7; // Mask for frame sequence number to index the array of frame times
- ////////////////////////////////////////////////////////////////////////////////
- //
- // CNetClient impliments the client side of the client-server architecture.
- //
- ////////////////////////////////////////////////////////////////////////////////
- class CNetClient
- {
- //------------------------------------------------------------------------------
- // Classes
- //------------------------------------------------------------------------------
- protected:
- ////////////////////////////////////////////////////////////////////////////////
- //
- // CClient is the server's representation of a client
- //
- // The general state is indicated by m_state, while the connection state is
- // indicated by m_msgr.State(). The two states will generally be "in agreement"
- // with one another, but since connecting and disconnecting are asynchronous
- // processes, it may seem like the connection status "lags behind" m_state,
- // which changes immediately.
- //
- ////////////////////////////////////////////////////////////////////////////////
- class CPeer
- {
- //------------------------------------------------------------------------------
- // Types, enums, etc.
- //------------------------------------------------------------------------------
- public:
- // Player states.
- typedef enum
- {
- Unused, // Becomes "Joined" when server sends JOINED msg
- Joined, // Becomes "Unused" if server sends DROPPED before game starts
- // Becomes "Dropped" if server sends DROPPED after game starts
- Dropped // Becomes "Unused" when new game starts
- } State;
- enum
- {
- NumAvgItems = 5
- };
- //------------------------------------------------------------------------------
- // Variables
- //------------------------------------------------------------------------------
- public:
- State m_state; // State
- RSocket::Address m_address; // Address
- char m_acName[Net::MaxPlayerNameSize];// Name
- unsigned char m_ucColor; // Color number
- unsigned char m_ucTeam; // Team number
- short m_sBandwidth; // Net::Bandwidth
- CNetInput m_netinput; // Sliding window of inputs
- Net::SEQ m_seqLastActive; // Last active sequence (only if dropped)
- bool m_bInactive; // True after last active sequence was used
- // Net::SEQ m_seqWhatHeNeeds; // What input seq he needs from me
- // Net::SEQ m_seqWhatINeed; // What input seq I need from him
- // long m_lNextSendTime; // When to next send inputs to him
- long m_lLastReceiveTime; // When we last got data from him *SPA
- // FQueue<long, NumAvgItems> m_qPings; // Queue of ping times for running average
- // long m_lRunnigAvgPing; // Running average
- U16 m_idDude; // Dude's ID
- //------------------------------------------------------------------------------
- // Functions
- //------------------------------------------------------------------------------
- public:
- ////////////////////////////////////////////////////////////////////////////////
- // Constructor
- ////////////////////////////////////////////////////////////////////////////////
- CPeer()
- {
- Reset();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Destructor
- ////////////////////////////////////////////////////////////////////////////////
- ~CPeer()
- {
- Reset();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Reset to post-construction state
- ////////////////////////////////////////////////////////////////////////////////
- void Reset(void)
- {
- m_state = Unused;
- m_address.Reset();
- m_acName[0] = 0;
- m_ucColor = 0;
- m_ucTeam = 0;
- m_sBandwidth = Net::FirstBandwidth;
- m_netinput.Reset();
- m_seqLastActive = 0;
- m_bInactive = false;
- // m_seqWhatHeNeeds = 0;
- // m_seqWhatINeed = 0;
- // m_lNextSendTime = 0;
- m_lLastReceiveTime = 0; // *SPA
- // m_qPings.Reset();
- // m_lRunnigAvgPing = 0;
- m_idDude = CIdBank::IdNil;
- }
- };
- //------------------------------------------------------------------------------
- // Types, enums, etc.
- //------------------------------------------------------------------------------
- public:
- // Client states
- typedef enum
- {
- Nothing,
- WaitForConnect,
- WaitForLoginResponse,
- WaitForJoinResponse,
- Joined
- } State;
- //------------------------------------------------------------------------------
- // Variables
- //------------------------------------------------------------------------------
- protected:
- CNetMsgr m_msgr; // Messenger to server
- RSocket m_socketPeers; // Socket used to communicate with peers
- RSocket::Address m_addressServer; // Server's address (with base port)
- RSocket::Address m_addressServerListen; // Server's address (listen port)
- RSocket::BLOCK_CALLBACK m_callback;
- State m_state; // My state
- NetMsg m_msgError; // Error type
- NetMsg::Status m_status; // Status type
- long m_lTimeOut; // Timer used to detect time-outs
- Net::ID m_id; // My id
- Net::ID m_idServer; // Server's client's ID
- short m_sNumJoined; // Number of joined players
- bool m_bGameStarted; // Whether game has started
- bool m_bPlaying; // true means playing, false means stopped
- bool m_bUseHaltFrame; // Whether to use m_seqStopFrame
- bool m_bReachedHaltFrame; // Whether we've reached the halt frame
- Net::SEQ m_seqHaltFrame; // Stop when we reach this frame seq
- Net::SEQ m_seqInput; // My input sequence
- Net::SEQ m_seqFrame; // My frame sequence
- Net::SEQ m_seqMaxAhead; // Max ahead for input versus frame
- Net::SEQ m_seqInputNotYetSent; // Input seq that we did NOT send yet
- CNetInput m_netinput; // My input buffer
- long m_lFrameTime; // Current frame time
- long m_lNextLocalInputTime; // When to get next local input
- bool m_bNextRealmPending; // Whether next realm is pending
- CPeer m_aPeers[Net::MaxNumIDs]; // Array of peers
- /** SPA **/
- long m_lStartTime; // The start from which time to calculate the frame delta
- long m_alAvgFrameTimes[8]; // Array to hold the last several average frame times
- /** SPA **/
- /** 12/16/97 AJC **/
- U16 m_u16PackageID; // Unique number for every package sent
- /** 12/16/97 AJC **/
- bool m_bSendNextFrame; // 12/30/97 *SPA True if we have all the info to render the current frame (m_seqFrame)
- long m_lMaxWaitTime;
- //------------------------------------------------------------------------------
- // Functions
- //------------------------------------------------------------------------------
- public:
- ////////////////////////////////////////////////////////////////////////////////
- // Constructor
- ////////////////////////////////////////////////////////////////////////////////
- CNetClient()
- {
- Reset();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Destructor
- ////////////////////////////////////////////////////////////////////////////////
- ~CNetClient()
- {
- Reset();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Reset
- ////////////////////////////////////////////////////////////////////////////////
- void Reset(void)
- {
- m_msgr.Reset();
- m_socketPeers.Reset();
- m_addressServer.Reset();
- m_addressServerListen.Reset();
- m_callback = 0;
- m_state = Nothing;
- m_msgError.msg.err.ucType = NetMsg::ERR;
- m_msgError.msg.err.error = NetMsg::NoError;
- m_msgError.msg.err.ulParam = 0;
- m_status = NetMsg::NoStatus;
- m_lTimeOut = 0;
- m_id = Net::InvalidID;
- m_idServer = Net::InvalidID;
- m_sNumJoined = 0;
- m_bGameStarted = false;
- m_bPlaying = false;
- m_bUseHaltFrame = false;
- m_bReachedHaltFrame = false;
- m_seqHaltFrame = 0;
- m_seqInput = 0;
- m_seqFrame = 0;
- m_seqMaxAhead = 0;
- m_seqInputNotYetSent = 0;
- m_netinput.Reset();
- m_lFrameTime = 0;
- m_lNextLocalInputTime = 0;
- m_bNextRealmPending = false;
- for (U8 id = 0; id < Net::MaxNumIDs; id++)
- m_aPeers[id].Reset();
- /** 12/15/97 SPA **/
- m_lStartTime = 0;
- for (short i = 0; i < 8; i++)
- m_alAvgFrameTimes[i] = 100;
- /** 12/15/97 SPA **/
- /** 12/16/97 AJC **/
- m_u16PackageID = 0;
- /** 12/16/97 AJC **/
- m_bSendNextFrame = false; // 12/30/97 *SPA
- m_lMaxWaitTime = 0;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Startup
- ////////////////////////////////////////////////////////////////////////////////
- void Startup(
- RSocket::BLOCK_CALLBACK callback); // In: Blocking callback
- ////////////////////////////////////////////////////////////////////////////////
- // Shutdown
- ////////////////////////////////////////////////////////////////////////////////
- void Shutdown(void);
- ////////////////////////////////////////////////////////////////////////////////
- // Start the process of joining the game on the specified host. The port in
- // in the host's address is assumed to be the so-called "base port".
- //
- // If this function returns an error, it indicates that the join process has
- // failed, most likely because the currently selected protocol is not supported.
- // Even if this happens, it is still safe to call Update() and GetMsg() as if
- // no error occurred, realizing, of course, that the join process will not
- // succeed and GetMsg() will shortly return an error to that effect.
- ////////////////////////////////////////////////////////////////////////////////
- short StartJoinProcess( // Returns 0 if successfull, non-zero otherwise
- RSocket::Address* paddressHost, // In: Host's address
- char* pszName, // In: Joiner's name
- unsigned char ucColor, // In: Joiner's color
- unsigned char ucTeam, // In: Joiner's team
- short sBandwidth); // In: Joiner's Net::Bandwidth
- ////////////////////////////////////////////////////////////////////////////////
- // Update (must be called regularly)
- ////////////////////////////////////////////////////////////////////////////////
- void Update(void);
- ////////////////////////////////////////////////////////////////////////////////
- // Get next available message from server
- ////////////////////////////////////////////////////////////////////////////////
- void GetMsg(
- NetMsg* pmsg); // Out: Message is returned here
- ////////////////////////////////////////////////////////////////////////////////
- // Send message to server
- ////////////////////////////////////////////////////////////////////////////////
- void SendMsg(
- NetMsg* pmsg, // In: Message to send
- bool bSendNow = true); // In: Whether to send now or wait until Update()
- ////////////////////////////////////////////////////////////////////////////////
- // Send chat message (text is sent with player's name as a prefix)
- ////////////////////////////////////////////////////////////////////////////////
- void SendChat(
- const char* pszText); // In: Text to send
- ////////////////////////////////////////////////////////////////////////////////
- // Send text message (text is send as is)
- ////////////////////////////////////////////////////////////////////////////////
- void SendText(
- const char* pszText); // In: Text to send
- ////////////////////////////////////////////////////////////////////////////////
- // Send realm status
- ////////////////////////////////////////////////////////////////////////////////
- void SendRealmStatus(
- bool bReady);
- ////////////////////////////////////////////////////////////////////////////////
- // Drop self
- ////////////////////////////////////////////////////////////////////////////////
- void Drop(void);
- ////////////////////////////////////////////////////////////////////////////////
- // Determine if there is more data waiting to be sent. If there is data to
- // to be sent AND there is a send error, then that data can't be sent, so we
- // return false to indicate "no more data".
- ////////////////////////////////////////////////////////////////////////////////
- bool IsMoreToSend(void)
- {
- return m_msgr.IsMoreToSend();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Get assigned ID
- ////////////////////////////////////////////////////////////////////////////////
- Net::ID GetID(void) // Returns ID
- {
- return m_id;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Get "servers net ID", which is really the ID of the server's client
- ////////////////////////////////////////////////////////////////////////////////
- Net::ID GetServerID(void) // Returns ID
- {
- return m_idServer;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Get the current number of players
- ////////////////////////////////////////////////////////////////////////////////
- short GetNumPlayers(void)
- {
- return m_sNumJoined;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Get the specified player's name
- ////////////////////////////////////////////////////////////////////////////////
- const char* GetPlayerName(
- Net::ID id)
- {
- ASSERT(id != Net::InvalidID);
- ASSERT(id < Net::MaxNumIDs);
- return m_aPeers[id].m_acName;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Get the specified player's color
- ////////////////////////////////////////////////////////////////////////////////
- unsigned char GetPlayerColor(
- Net::ID id)
- {
- ASSERT(id != Net::InvalidID);
- ASSERT(id < Net::MaxNumIDs);
- return m_aPeers[id].m_ucColor;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Determine whether ths specified player needs a dude
- ////////////////////////////////////////////////////////////////////////////////
- bool DoesPlayerNeedDude(
- Net::ID id)
- {
- return (m_aPeers[id].m_state == CPeer::Joined) || (m_aPeers[id].m_state == CPeer::Dropped);
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Get or set specified player's dude ID (not to be confused with a Net::ID)
- ////////////////////////////////////////////////////////////////////////////////
- U16 GetPlayerDudeID(
- Net::ID id)
- {
- ASSERT(id != Net::InvalidID);
- ASSERT(id < Net::MaxNumIDs);
- return m_aPeers[id].m_idDude;
- }
- void SetPlayerDudeID(
- Net::ID id,
- U16 idDude)
- {
- ASSERT(id != Net::InvalidID);
- ASSERT(id < Net::MaxNumIDs);
- m_aPeers[id].m_idDude = idDude;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Determine whether we're playing or not
- ////////////////////////////////////////////////////////////////////////////////
- bool IsPlaying(void)
- {
- return m_bPlaying;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Determine whether another frame can be done. If so, the necessary peer
- // inputs are returned in the supplied array and the result is true.
- // Otherwise, the result is false, and a frame cannot be done.
- ////////////////////////////////////////////////////////////////////////////////
- bool CanDoFrame( // Returns true if frame can be done, false otherwise
- UINPUT aInputs[], // Out: Total of Net::MaxNumIDs inputs returned here
- short* psFrameTime); // Out the current frames elapsed time
- ////////////////////////////////////////////////////////////////////////////////
- // Check whether local input is required
- ////////////////////////////////////////////////////////////////////////////////
- bool IsLocalInputNeeded(void);
- ////////////////////////////////////////////////////////////////////////////////
- // Set local input.
- // Call this if and only if IsLocalInputNeeded() returns true!
- ////////////////////////////////////////////////////////////////////////////////
- void SetLocalInput(
- UINPUT input);
- ////////////////////////////////////////////////////////////////////////////////
- // Get the input seq that has NOT been sent yet to any other player
- ////////////////////////////////////////////////////////////////////////////////
- Net::SEQ GetInputSeqNotYetSent(void)
- {
- return m_seqInputNotYetSent;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // This is normally used to handle a HALT_REALM message when we get one from
- // the server.
- //
- // It is also used the local server, if there is one, to directly inform the
- // local client (that would be us) about the halt frame. This is a necessary
- // breach of client/server separation that is more fully explained elsewhere.
- // (It may indicate a design flaw, but I'm at a loss for a better solution.)
- ////////////////////////////////////////////////////////////////////////////////
- void SetHaltFrame(
- Net::SEQ seqHalt);
- ////////////////////////////////////////////////////////////////////////////////
- // This is used to see if any peers have not sent data for too long of a period
- //
- // It returns the id of the first peer it finds that has exceeded the time limit
- // so that the server can drop him. It should only be called if this client is
- // attached to the server. If more than one peer has exceeded the time limit,
- // it will be handled on a subsequent pass. By then the first late peer will
- // have been marked as dropped
- // Returns -1 if no peer has exceeded time limit *SPA
- ////////////////////////////////////////////////////////////////////////////////
- Net::ID CheckForLostPeer(void);
- protected:
- ////////////////////////////////////////////////////////////////////////////////
- // Receive messages from peers
- ////////////////////////////////////////////////////////////////////////////////
- void ReceiveFromPeers(void);
- ////////////////////////////////////////////////////////////////////////////////
- // Send messages to peers
- ////////////////////////////////////////////////////////////////////////////////
- void SendToPeers(void);
- ////////////////////////////////////////////////////////////////////////////////
- // Send message to single peer
- ////////////////////////////////////////////////////////////////////////////////
- void SendToPeer(Net::ID id, Net::SEQ seqStart, bool bSeqReq);
- };
- #endif //NETCLIENT_H
- ////////////////////////////////////////////////////////////////////////////////
- // EOF
- ////////////////////////////////////////////////////////////////////////////////
|