Torque3D Documentation / _generateds / hifiMoveList.cpp

hifiMoveList.cpp

Engine/source/T3D/gameBase/hifi/hifiMoveList.cpp

More...

Public Defines

Detailed Description

Public Defines

MAX_MOVE_PACKET_SENDS() 4

Public Variables

const U32 DefaultMaxMoveSizeList 
const F32 DefaultMoveListSizeSlack 
const F32 DefaultSmoothMoveAvg 
const U32 DefaultTargetMoveListSize 
  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "T3D/gameBase/hifi/hifiMoveList.h"
 25#include "T3D/gameBase/hifi/hifiGameProcess.h"
 26#include "T3D/gameBase/gameConnection.h"
 27#include "core/stream/bitStream.h"
 28
 29#define MAX_MOVE_PACKET_SENDS 4
 30
 31const U32 DefaultTargetMoveListSize = 3;
 32const U32 DefaultMaxMoveSizeList = 5;
 33const F32 DefaultSmoothMoveAvg = 0.15f;
 34const F32 DefaultMoveListSizeSlack = 1.0f;
 35
 36HifiMoveList::HifiMoveList()
 37{
 38   mLastSentMove = 0;
 39   mAvgMoveQueueSize = DefaultTargetMoveListSize ;
 40   mTargetMoveListSize = DefaultTargetMoveListSize;
 41   mMaxMoveListSize = DefaultMaxMoveSizeList;
 42   mSmoothMoveAvg = DefaultSmoothMoveAvg;
 43   mMoveListSizeSlack = DefaultMoveListSizeSlack;
 44   mTotalServerTicks = ServerTicksUninitialized;
 45   mSuppressMove = false;
 46}
 47
 48void HifiMoveList::updateClientServerTickDiff(S32 & tickDiff)
 49{
 50   if (mLastMoveAck==0)
 51      tickDiff=0;
 52
 53   // Make adjustments to move list to account for tick mis-matches between client and server.
 54   if (tickDiff>0)
 55   {
 56      // Server ticked more than client.  Adjust for this by reseting all hifi objects
 57      // to a later position in the tick cache (see ageTickCache below) and at the same
 58      // time pulling back some moves we thought we had made (so that time on client
 59      // doesn't change).
 60      S32 dropTicks = tickDiff;
 61      while (dropTicks)
 62      {         
 63#ifdef TORQUE_DEBUG_NET_MOVES
 64         Con::printf("dropping move%s",mLastClientMove>mFirstMoveIndex ? "" : " but none there");
 65#endif
 66         if (mLastClientMove>mFirstMoveIndex)
 67            mLastClientMove--;
 68         else
 69            tickDiff--;
 70         dropTicks--;
 71      }
 72      AssertFatal(mLastClientMove >= mFirstMoveIndex, "Bad move request");
 73      AssertFatal(mLastClientMove - mFirstMoveIndex <= mMoveVec.size(), "Desynched first and last move.");
 74   }
 75   else
 76   {
 77      // Client ticked more than server.  Adjust for this by taking extra moves
 78      // (either adding back moves that were dropped above, or taking new ones).
 79      for (S32 i=0; i<-tickDiff; i++)
 80      {
 81         if (mMoveVec.size() > mLastClientMove - mFirstMoveIndex)
 82         {
 83#ifdef TORQUE_DEBUG_NET_MOVES
 84            Con::printf("add back move");
 85#endif
 86            mLastClientMove++;
 87         }
 88         else
 89         {
 90#ifdef TORQUE_DEBUG_NET_MOVES
 91            Con::printf("add back move -- create one");
 92#endif
 93            collectMove();
 94            mLastClientMove++;
 95         }
 96      }
 97   }
 98
 99   // drop moves that are not made yet (because we rolled them back) and not yet sent   
100   U32 len = getMax(mLastClientMove-mFirstMoveIndex,mLastSentMove-mFirstMoveIndex);
101   mMoveVec.setSize(len);
102
103#ifdef TORQUE_DEBUG_NET_MOVES
104   Con::printf("move list size: %i, last move: %i, last sent: %i",mMoveVec.size(),mLastClientMove-mFirstMoveIndex,mLastSentMove-mFirstMoveIndex);
105#endif      
106}
107
108S32 HifiMoveList::getServerTicks(U32 serverTickNum)
109{
110   S32 serverTicks=0;
111   if (serverTicksInitialized())
112   {
113      // handle tick wrapping...
114      const S32 MaxTickCount = (1<<TotalTicksBits);
115      const S32 HalfMaxTickCount = MaxTickCount>>1;
116      U32 prevTickNum = mTotalServerTicks & TotalTicksMask;
117      serverTicks = serverTickNum-prevTickNum;
118      if (serverTicks>HalfMaxTickCount)
119         serverTicks -= MaxTickCount;
120      else if (-serverTicks>HalfMaxTickCount)
121         serverTicks += MaxTickCount;
122      AssertFatal(serverTicks>=0,"Server can't tick backwards!!!");
123      if (serverTicks<0)
124         serverTicks=0;
125   }
126   mTotalServerTicks = serverTickNum;
127   return serverTicks;
128}
129
130void HifiMoveList::markControlDirty()
131{
132   mLastClientMove = mLastMoveAck;
133
134   // save state for future update
135   GameBase *obj = mConnection->getControlObject();
136   AssertFatal(obj,"ClientProcessList::markControlDirty: no control object");
137   obj->setGhostUpdated(true);
138   obj->getTickCache().beginCacheList();
139   TickCacheEntry * tce = obj->getTickCache().incCacheList();
140   BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize);
141   obj->writePacketData( mConnection, &bs );
142}
143
144void HifiMoveList::resetMoveList()
145{
146   mMoveVec.clear();
147   mLastMoveAck = 0;
148   mLastClientMove = 0;
149   mFirstMoveIndex = 0;
150   mLastSentMove = 0;   
151}
152
153U32 HifiMoveList::getMoves(Move** movePtr,U32* numMoves)
154{
155   if (mConnection->isConnectionToServer())
156      // give back moves starting at the last client move...
157      return Parent::getMoves(movePtr,numMoves);
158
159   if (mSuppressMove || mMoveVec.size()==0)
160   {
161      *numMoves=0;
162      *movePtr=<a href="/coding/file/types_8lint_8h/#types_8lint_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a>;
163   }
164   else
165   {
166      *numMoves=1;
167      *movePtr=<a href="/coding/class/classmovelist/#classmovelist_1aaf5b973eb5c053cd4fd08feca2a3974d">mMoveVec</a>.begin();
168   }
169
170   return *numMoves;
171}
172
173void HifiMoveList::advanceMove()
174{ 
175   S32 numMoves = mMoveVec.size();
176   mAvgMoveQueueSize *= (1.0f-mSmoothMoveAvg);
177   mAvgMoveQueueSize += mSmoothMoveAvg * F32(numMoves);
178
179#ifdef TORQUE_DEBUG_NET_MOVES
180   Con::printf("moves remaining: %i, running avg: %f",numMoves,mAvgMoveQueueSize);
181#endif
182
183   if (mAvgMoveQueueSize<mTargetMoveListSize-mMoveListSizeSlack && numMoves<mTargetMoveListSize && numMoves)
184   {
185      numMoves=0;
186      mAvgMoveQueueSize = (F32)getMax(S32(mAvgMoveQueueSize + mMoveListSizeSlack + 0.5f),numMoves);
187
188#ifdef TORQUE_DEBUG_NET_MOVES
189      Con::printf("too few moves on server, padding with null move");
190#endif
191   }
192   if (numMoves)
193      numMoves=1;
194
195   if ( mMoveVec.size()>mMaxMoveListSize || (mAvgMoveQueueSize>mTargetMoveListSize+mMoveListSizeSlack && mMoveVec.size()>mTargetMoveListSize) )
196   {
197      U32 drop = mMoveVec.size()-mTargetMoveListSize;
198      clearMoves(drop);
199      mAvgMoveQueueSize = (F32)mTargetMoveListSize;
200
201#ifdef TORQUE_DEBUG_NET_MOVES
202      Con::printf("too many moves on server, dropping moves (%i)",drop);
203#endif
204   }
205
206   mSuppressMove = numMoves == 0;
207   
208   // now clear move
209   if (areMovesPending())
210      clearMoves(1);
211}
212
213void HifiMoveList::clientWriteMovePacket(BitStream *bstream)
214{
215   if (!serverTicksInitialized())
216      resetMoveList();
217
218   AssertFatal(mLastMoveAck == mFirstMoveIndex, "Invalid move index.");
219
220   // enforce limit on number of moves sent
221   if (mLastSentMove<mFirstMoveIndex)
222      mLastSentMove=<a href="/coding/class/classmovelist/#classmovelist_1aba3d83664ed2b543f3873f1a14915ed5">mFirstMoveIndex</a>;
223   U32 count = mLastSentMove-mFirstMoveIndex;
224
225   Move * move = mMoveVec.address();
226   U32 start = mLastMoveAck;
227   U32 offset;
228   for(offset = 0; offset < count; offset++)
229      if(move[offset].sendCount < MAX_MOVE_PACKET_SENDS)
230         break;
231   if(offset == count && count != 0)
232      offset--;
233
234   start += offset;
235   count -= offset;
236
237   if (count > MaxMoveCount)
238      count = MaxMoveCount;
239   bstream->writeInt(start,32);
240   bstream->writeInt(count,MoveCountBits);
241   Move * prevMove = NULL;
242   for (int i = 0; i < count; i++)
243   {
244      move[offset + i].sendCount++;
245      move[offset + i].pack(bstream,prevMove);
246      bstream->writeInt(move[offset + i].checksum,Move::ChecksumBits);
247      prevMove = &move[offset+i];
248   }
249}
250
251void HifiMoveList::serverReadMovePacket(BitStream *bstream)
252{
253   // Server side packet read.
254   U32 start = bstream->readInt(32);
255   U32 count = bstream->readInt(MoveCountBits);
256
257   Move * prevMove = NULL;
258   Move prevMoveHolder;
259
260   // Skip forward (must be starting up), or over the moves
261   // we already have.
262   int skip = mLastMoveAck - start;
263   if (skip < 0) 
264   {
265      mLastMoveAck = start;
266   }
267   else 
268   {
269      if (skip > count)
270         skip = count;
271      for (int i = 0; i < skip; i++)
272      {
273         prevMoveHolder.unpack(bstream,prevMove);
274         prevMoveHolder.checksum = bstream->readInt(Move::ChecksumBits);
275         prevMove = &prevMoveHolder;
276         S32 idx = mMoveVec.size()-skip+i;
277         if (idx>=0)
278         {
279#ifdef TORQUE_DEBUG_NET_MOVES
280            if (mMoveVec[idx].checksum != prevMoveHolder.checksum)
281               Con::printf("updated checksum on move %i from %i to %i",mMoveVec[idx].id,mMoveVec[idx].checksum,prevMoveHolder.checksum);
282#endif
283            mMoveVec[idx].checksum = prevMoveHolder.checksum;
284         }
285      }
286      start += skip;
287      count = count - skip;
288   }
289
290   // Put the rest on the move list.
291   int index = mMoveVec.size();
292   mMoveVec.increment(count);
293   while (index < mMoveVec.size())
294   {
295      mMoveVec[index].unpack(bstream,prevMove);
296      mMoveVec[index].checksum = bstream->readInt(Move::ChecksumBits);
297      prevMove = &mMoveVec[index];
298      mMoveVec[index].id = start++;
299      index ++;
300   }
301
302   mLastMoveAck += count;
303
304   if (mMoveVec.size()>mMaxMoveListSize)
305   {
306      U32 drop = mMoveVec.size()-mTargetMoveListSize;
307      clearMoves(drop);
308      mAvgMoveQueueSize = (F32)mTargetMoveListSize;
309
310#ifdef TORQUE_DEBUG_NET_MOVES
311      Con::printf("too many moves on server, dropping moves (%i)",drop);
312#endif
313   }
314}
315
316void HifiMoveList::serverWriteMovePacket(BitStream * bstream)
317{
318#ifdef TORQUE_DEBUG_NET_MOVES
319   Con::printf("ack %i minus %i",mLastMoveAck,mMoveVec.size());
320#endif
321
322   // acknowledge only those moves that have been ticked
323   bstream->writeInt(mLastMoveAck - mMoveVec.size(),32);
324
325   // send over the current tick count on the server...
326   bstream->writeInt(ServerProcessList::get()->getTotalTicks() & TotalTicksMask, TotalTicksBits);
327}
328
329void HifiMoveList::clientReadMovePacket(BitStream * bstream)
330{
331   if (!serverTicksInitialized())
332      resetMoveList();
333
334#ifdef TORQUE_DEBUG_NET_MOVES
335   Con::printf("pre move ack: %i", mLastMoveAck);
336#endif
337
338   mLastMoveAck = bstream->readInt(32);
339
340#ifdef TORQUE_DEBUG_NET_MOVES
341   Con::printf("post move ack %i, first move %i, last move %i", mLastMoveAck, mFirstMoveIndex, mLastClientMove);
342#endif
343
344   // This is how many times we've ticked since last ack -- before adjustments below
345   S32 ourTicks = mLastMoveAck - mFirstMoveIndex;
346
347   if (mLastMoveAck < mFirstMoveIndex)
348      mLastMoveAck = mFirstMoveIndex;
349
350   if(mLastMoveAck > mLastClientMove)
351   {
352      ourTicks -= mLastMoveAck-mLastClientMove;
353      mLastClientMove = mLastMoveAck;
354   }
355   while(mFirstMoveIndex < mLastMoveAck)
356   {
357      if (mMoveVec.size())
358      {
359         mMoveVec.pop_front();
360         mFirstMoveIndex++;
361      }
362      else
363      {
364         AssertWarn(1, "Popping off too many moves!");
365         mFirstMoveIndex = mLastMoveAck;
366      }
367   }
368
369   // get server ticks using total number of ticks on server to date...
370   U32 serverTickNum = bstream->readInt(TotalTicksBits);
371   S32 serverTicks = getServerTicks(serverTickNum);
372   S32 tickDiff = serverTicks - ourTicks;
373
374#ifdef TORQUE_DEBUG_NET_MOVES
375   Con::printf("server ticks: %i, client ticks: %i, diff: %i%s", serverTicks, ourTicks, tickDiff, !tickDiff ? "" : " (ticks mis-match)");
376#endif
377
378
379   // Apply the first (of two) client-side synchronization mechanisms.  Key is that
380   // we need to both synchronize client/server move streams (so first move in list is made
381   // at same "time" on both client and server) and maintain the "time" at which the most
382   // recent move was made on the server.  In both cases, "time" is the number of ticks
383   // it took to get to that move.
384   updateClientServerTickDiff(tickDiff);
385
386   // Apply the second (and final) client-side synchronization mechanism.  The tickDiff adjustments above 
387   // make sure time is preserved on client.  But that assumes that a future (or previous) update will adjust
388   // time in the other direction, so that we don't get too far behind or ahead of the server.  The updateMoveSync
389   // mechanism tracks us over time to make sure we eventually return to be in sync, and makes adjustments
390   // if we don't after a certain time period (number of updates).  Unlike the tickDiff mechanism, when
391   // the updateMoveSync acts time is not preserved on the client.
392   HifiClientProcessList * processList = dynamic_cast<HifiClientProcessList*>(ClientProcessList::get());
393   if (processList)
394   {
395      processList->updateMoveSync(mLastSentMove-mLastClientMove);
396
397      // set catchup parameters...
398      U32 totalCatchup = mLastClientMove - mFirstMoveIndex;
399
400      processList->ageTickCache(ourTicks + (tickDiff>0 ? tickDiff : 0), totalCatchup+1);
401      processList->forceHifiReset(tickDiff!=0);
402      processList->setCatchup(totalCatchup);
403   }
404}
405
406void HifiMoveList::ghostPreRead(NetObject * nobj, bool newGhost)
407{
408   GameBase* obj = dynamic_cast<GameBase*>(nobj);
409   if ( obj && ( obj->getTypeMask() & GameBaseHiFiObjectType ) && !newGhost )
410   {
411      // set next cache entry to start
412      obj->getTickCache().beginCacheList();
413
414      // reset to old state because we are about to unpack (and then tick forward)
415      TickCacheEntry * tce = obj->getTickCache().incCacheList(false);
416      if (tce)
417      {
418         BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize);
419         obj->readPacketData(mConnection, &bs);
420      }
421   }
422}
423
424void HifiMoveList::ghostReadExtra(NetObject * nobj, BitStream * bstream, bool newGhost)
425{
426   // Receive additional per ghost information.
427   // Get pending moves for ghosts that have them and add the moves to
428   // the tick cache.
429   GameBase* obj = dynamic_cast<GameBase*>(nobj);
430   if ( obj && ( obj->getTypeMask() & GameBaseHiFiObjectType ) )
431   {
432      // mark ghost so that it updates correctly
433      obj->setGhostUpdated(true);
434      obj->setNewGhost(newGhost);
435
436      // set next cache entry to start
437      obj->getTickCache().beginCacheList();
438
439      // save state for future update
440      TickCacheEntry * tce = obj->getTickCache().incCacheList();
441      BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize);
442      obj->writePacketData(mConnection, &bs);
443   }
444}
445