123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- ////////////////////////////////////////////////////////////////////////////////
- //
- // 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
- //
- // netinput.h
- // Project: RSPiX
- //
- // History:
- // 09/02/97 MJR Started.
- //
- ////////////////////////////////////////////////////////////////////////////////
- #ifndef NETINPUT_H
- #define NETINPUT_H
- #include "input.h"
- ////////////////////////////////////////////////////////////////////////////////
- //
- // CNetInput handle the buffering of inputs received from other players.
- //
- // The Net::SEQ values are unsigned, and last I checked were 16-bit values,
- // but this same logic would work for 32-bit, too.
- //
- // The sequence values will increment steadly until they wrap-around. We assume
- // that the amount of time that it will take for them to wrap-around is greater
- // than the amount of time a network packet can live for and still manage to get
- // delivered. With a 16-bit value at 30 frames per second, this allow for over
- // 30 minutes -- I doubt a network packet can survive that long.
- //
- // This class impliments a "sliding window" of input values. The window must
- // be at least 3 * Net::MaxAheadSeq in size. (A detailed explanation can be
- // found in where that value is defined).
- //
- // Assuming Net::SEQ ranges from 0 to 65535, the window looks like this:
- //
- // 0-------------[ooonnnnnn]-----------------------------65535
- // ^
- // F
- //
- // The dashes represent values we don't care about -- any values that are
- // NOT within the window are ignored.
- //
- // The "F" represents the current frame number, which determines the overall
- // position of the window. This is the LOCAL PLAYER'S frame number, not the
- // frame number of the player whose data we are dealing with!
- //
- // As the frame number increases, the window moves to the right, eventually
- // wrapping around and starting over at the left side.
- //
- // The window contains 3 sets of Net::MaxAheadSeq values. The first set is
- // to the left of the current frame number, and is represented by o's. These
- // are "old" values we must keep around in case this player drops from the game
- // and we are the only player that received (and used!) his input values. In
- // that case, we will be asked to supply these values to the other players.
- //
- // The second two sets are represented by n's, and are new values we might
- // have received from this player, but that we haven't yet used.
- //
- // Inputs may arrive out of order, which means we might get a few values,
- // then get a few more values further along in the window but leaving a "hole"
- // of unknown values in between. We assume that we'll eventually get those
- // values, too, but in the meantime, we can't move our frame number past that
- // hole.
- //
- // In order to properly detect which values we did and did not get, we start
- // out with the entire window filled with invalid values, and whenever we move
- // the window, we mark any "newly uncovered" values as invalid, too. As we
- // receive inputs, we put them in the appropriate spots in the array,
- // overwriting the invalid values in the process. Whenever an attempt is made
- // to move the frame forward, we check if the value for that frame is valid.
- // If so, we can move forward. If not, it means we haven't gotten that value
- // yet, so we can't move forward yet.
- //
- //
- // All that cool theory aside, the array is actually implimented in a somewhat
- // different manner.
- //
- // By making it a power of 2 in size (making sure it's at least as
- // large as 3 * Net::MaxAheadSeq), everything gets harder to think about, but
- // very efficient. Normally, in order to impliment a sliding window buffer,
- // you need to slide around the values contained in the buffer as it moves
- // forward. By making it a power of 2 in size, we can use simple bit-masks
- // to get all the indices into to the buffer to automatically wrap-around as
- // needed, and suddenly we don't need to move the values anymore -- instead we
- // are changing how we look at the buffer. We are merely moving what we think
- // of as the start and end of the buffer around, rather than moving everything
- // within the buffer. Kind of hard to explain, but it DOES work!
- //
- // Let's assume Net::SEQ is only 4-bits, so it ranges from 0 to 15. Now let's
- // say Net::MaxAheadSeq is 1, so our buffer needs to 3 times that, but instead
- // we go with 4 entries, which is the next power-of-2 after 3. The diagram
- // below shows how the various input sequences will map into our buffer. In
- // order to arrive at the index into our buffer, we simply take the sequence
- // number and mark out all the bits but the bottom 2, which yields a number
- // between 0 and 3, and happens to be the correct index into our buffer. The
- // diagram shows how our buffer "slides along" without moving the actual
- // contents -- instead, it's all just a matter of how you think of it.
- //
- // 0 1 2 3 4 5 6 7 8 9 A B C D E F
- // - - - - - - - - - - - - - - - -
- // 0 1 2 3
- // 1 2 3 0
- // 2 3 0 1
- // 3 0 1 2
- // 0 1 2 3
- //
- ////////////////////////////////////////////////////////////////////////////////
- class CNetInput
- {
- //------------------------------------------------------------------------------
- // Types, enums, etc.
- //------------------------------------------------------------------------------
- public:
- enum
- {
- // Maximum total entries (see elsewhere for in-depth explanation)
- MaxTotalEntries = 3 * Net::MaxAheadSeq,
- // Maxumum new entries (see elsewhere for in-depth explanation)
- MaxNewEntries = 2 * Net::MaxAheadSeq,
- // Maximum old entries (see elsewhere for in-depth explanation)
- MaxOldEntries = 1 * Net::MaxAheadSeq,
- // The size must be a power of two, and the mask must correspond to it.
- // Remember that this must be at LEAST as large as MaxTotalEntries!
- Size = 256,
- Mask = Size - 1,
- // Invalid input value
- Invalid = 0xffffffff
- };
- //------------------------------------------------------------------------------
- // Variables
- //------------------------------------------------------------------------------
- protected:
- UINPUT m_aInputs[Size]; // Inputs
- U8 m_aFrameTimes[Size]; // Game time for the frames *SPA
- Net::SEQ m_seqFrame; // Local player's current frame number
- Net::SEQ m_seqOldest; // Oldest sequence we have
- //------------------------------------------------------------------------------
- // Functions
- //------------------------------------------------------------------------------
- public:
- ////////////////////////////////////////////////////////////////////////////////
- // Constructor
- ////////////////////////////////////////////////////////////////////////////////
- CNetInput()
- {
- ASSERT(Size >= MaxTotalEntries);
- Reset();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Destructor
- ////////////////////////////////////////////////////////////////////////////////
- ~CNetInput()
- {
- Reset();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Reset to post-construction state
- ////////////////////////////////////////////////////////////////////////////////
- void Reset(void)
- {
- short i = 0;
- // Clear the entire window to "invalid" values
- for (i = 0; i < Size; i++)
- m_aInputs[i] = Invalid;
- // Clear the entire window to initail values *SPA !!Eventually should input from prefs!!
- for (i = 0; i < Size; i++)
- m_aFrameTimes[i] = 100;
- // Start the frame at 0. The oldest value always lags by a fixed distance.
- m_seqFrame = 0;
- m_seqOldest = m_seqFrame - MaxOldEntries;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Move the frame forward
- // IT IS ASSUMED THAT THE CALLER WILL NOT BE STUPID!!!
- // Don't ever move the frame forward unless the input for the current frame
- // is valid, as reported by GetInput()!
- ////////////////////////////////////////////////////////////////////////////////
- void IncFrame(void)
- {
- // Make sure this makes sense!
- ASSERT(m_aInputs[m_seqFrame & Mask] != Invalid);
- // Invalidate oldest sequence
- m_aInputs[m_seqOldest & Mask] = Invalid;
- // Move forward
- m_seqFrame++;
- m_seqOldest++;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Put a new input value.
- // If the specified seq is outside of the "new value window", it is ignored.
- ////////////////////////////////////////////////////////////////////////////////
- void Put(
- Net::SEQ seq,
- UINPUT input)
- {
- // If the seq falls within the "new values" window, we use it. Note that
- // the "new values" window starts at the current frame.
- // The excessive casting is to make sure the compiler does this all as
- // unsigned math and comparisons.
- if ((Net::SEQ)(seq - m_seqFrame) < (Net::SEQ)MaxNewEntries)
- m_aInputs[seq & Mask] = input;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Put a new frame time value. *SPA
- // If the specified seq is outside of the "new value window", it is ignored.
- ////////////////////////////////////////////////////////////////////////////////
- void PutFrameTime(
- Net::SEQ seq,
- U8 frameTime)
- {
- // If the seq falls within the "new values" window, we use it. Note that
- // the "new values" window starts at the current frame.
- // The excessive casting is to make sure the compiler does this all as
- // unsigned math and comparisons.
- if ((Net::SEQ)(seq - m_seqFrame) < (Net::SEQ)MaxNewEntries)
- {
- m_aFrameTimes[seq & Mask] = frameTime;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Find first invalid value starting at the specified seq and continuing to
- // the end of the "new" window.
- ////////////////////////////////////////////////////////////////////////////////
- Net::SEQ FindFirstInvalid(
- Net::SEQ seq)
- {
- Net::SEQ offset = (Net::SEQ)(seq - m_seqFrame);
- while (offset < (Net::SEQ)MaxNewEntries)
- {
- if (m_aInputs[seq & Mask] == Invalid)
- break;
- seq++;
- offset++;
- }
- return seq;
- }
- // This alternative version starts at the current frame
- Net::SEQ FindFirstInvalid(void)
- {
- return FindFirstInvalid(m_seqFrame);
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Get the specified input value.
- // A return value of CNetInput::Invalid means that input was not available.
- ////////////////////////////////////////////////////////////////////////////////
- UINPUT Get(
- Net::SEQ seq)
- {
- // If the seq falls within the total window, we get it. Note that this
- // window starts at the oldest value, which is always Net::MaxAheadSeq less
- // than the current frame.
- // The excessive casting is to make sure the compiler does this all as
- // unsigned math and comparisons.
- if ((Net::SEQ)(seq - m_seqOldest) < (Net::SEQ)MaxTotalEntries)
- return m_aInputs[seq & Mask];
- else
- return Invalid;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Get the specified frame time value. *SPA
- // A return value of CNetInput::Invalid means that input was not available.
- ////////////////////////////////////////////////////////////////////////////////
- U8 GetFrameTime(
- Net::SEQ seq)
- {
- // If the seq falls within the total window, we get it. Note that this
- // window starts at the oldest value, which is always Net::MaxAheadSeq less
- // than the current frame.
- // The excessive casting is to make sure the compiler does this all as
- // unsigned math and comparisons.
- if ((Net::SEQ)(seq - m_seqOldest) < (Net::SEQ)MaxTotalEntries)
- return m_aFrameTimes[seq & Mask];
- else
- return Invalid;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Get current frame seq
- ////////////////////////////////////////////////////////////////////////////////
- Net::SEQ GetFrameSeq(void)
- {
- return m_seqFrame;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Get oldest frame seq
- ////////////////////////////////////////////////////////////////////////////////
- Net::SEQ GetOldestSeq(void)
- {
- return m_seqOldest;
- }
- };
- #endif //NETINPUT_H
- ////////////////////////////////////////////////////////////////////////////////
- // EOF
- ////////////////////////////////////////////////////////////////////////////////
|