Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/network/src/lib/network/synchronizeable.cc @ 8244

Last change on this file since 8244 was 8244, checked in by rennerc, 18 years ago

fixed bug with synchronizeable vars after vars with non const length

File size: 15.0 KB
Line 
1/*
2   orxonox - the future of 3D-vertical-scrollers
3
4   Copyright (C) 2004 orx
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2, or (at your option)
9   any later version.
10
11
12### File Specific:
13   main-programmer: Silvan Nellen
14   co-programmer: Benjamin Wuest
15*/
16
17#define DEBUG_MODULE_NETWORK
18
19#include "shared_network_data.h"
20#include "network_stream.h"
21#include "netdefs.h"
22#include "network_log.h"
23#include "network_game_manager.h"
24
25#include "state.h"
26
27#include <cassert>
28
29#include "synchronizeable.h"
30
31
32
33/**
34 *  default constructor
35 */
36Synchronizeable::Synchronizeable()
37{
38  this->setClassID(CL_SYNCHRONIZEABLE, "Synchronizeable");
39  this->owner = 0;
40  this->hostID = SharedNetworkData::getInstance()->getHostID();
41  this->setIsServer(this->hostID == 0);
42  this->uniqueID = NET_UID_UNASSIGNED;
43  this->networkStream = NULL;
44  this->bSynchronize = false;
45 
46  if( State::isOnline())
47  {
48    NetworkStream* nd = SharedNetworkData::getInstance()->getDefaultSyncStream();
49    assert(nd != NULL);
50    nd->connectSynchronizeable(*this);
51    this->setUniqueID(SharedNetworkData::getInstance()->getNewUniqueID());
52  }
53
54  /* make sure loadClassId is first synced var because this is read by networkStream */
55  assert( syncVarList.size() == 0 );
56  mLeafClassId = this->registerVarId( new SynchronizeableInt( (int*)&this->getLeafClassID(), (int*)&this->getLeafClassID(), "leafClassId" ) );
57   
58  this->registerVar( new SynchronizeableInt( &this->owner, &this->owner, "owner" ) );
59  this->registerVar( new SynchronizeableString( &this->objectName, &this->objectName, "objectName" ) );
60}
61
62
63
64/**
65 *  default destructor deletes all unneded stuff
66 */
67Synchronizeable::~Synchronizeable()
68{
69  if ( this->networkStream )
70    this->networkStream->disconnectSynchronizeable(*this);
71 
72  if ( this->isServer() && this->beSynchronized() && this->getUniqueID() > 0 )
73    NetworkGameManager::getInstance()->removeSynchronizeable( this->getUniqueID() );
74}
75
76/**
77 * Sets the server flag to a given value
78 * @param isServer: the boolean value which the server flag is to set to
79 */
80void Synchronizeable::setIsServer(bool isServer)
81{
82  if( isServer )
83    this->state = this->state | STATE_SERVER;
84  else
85    this->state = this->state & (~STATE_SERVER);
86}
87
88
89/**
90 * Determines if the server flag is set
91 * @return true, if the server flag is true, false else
92 */
93bool Synchronizeable::isServer()
94{
95  return (this->state & STATE_SERVER) >0;
96}
97
98
99
100int Synchronizeable::getStateDiff( int userId, byte* data, int maxLength, int stateId, int fromStateId, int priorityTH )
101{
102  //make sure this user has his history
103  if ( sentStates.size() <= userId )
104    sentStates.resize( userId+1 );
105
106  //calculate needed memory
107  int neededSize = 0;
108
109  for ( SyncVarList::iterator it = syncVarList.begin(); it != syncVarList.end(); it++ )
110  {
111    //PRINTF(0)("SIZE = %d %s\n", (*it)->getSize(), (*it)->getName().c_str());
112    neededSize += (*it)->getSize();
113  }
114
115  if ( !( neededSize <= maxLength ) )
116  {
117    PRINTF(0)( "%d > %d\n", neededSize, maxLength );
118    assert(false);
119  }
120
121  //remove older states from history than fromStateId
122  StateHistory::iterator it = sentStates[userId].begin();
123
124  while ( it != sentStates[userId].end() && (*it)->stateId < fromStateId )
125    it++;
126
127  if ( it != sentStates[userId].begin() )
128  {
129    for ( StateHistory::iterator it2 = sentStates[userId].begin(); it2 != it; it2++ )
130    {
131      if ( (*it2)->data != NULL )
132      {
133        delete [] (*it2)->data;
134        (*it2)->data = NULL;
135      }
136    }
137    sentStates[userId].erase( sentStates[userId].begin(), it );
138  }
139
140  //find state to create diff from
141  StateHistoryEntry * stateFrom = NULL;
142
143  it = sentStates[userId].begin();
144  while ( it != sentStates[userId].end() && (*it)->stateId != fromStateId )
145    it++;
146 
147//  if ( getLeafClassID() == CL_SPACE_SHIP )
148//  {
149//    PRINTF(0)("getStateDiff:SpaceShip from: %d stateId: %d\n", (it == sentStates[userId].end())?-1:fromStateId, stateId);
150//  }
151
152  if ( it == sentStates[userId].end() )
153  {
154    StateHistoryEntry * initialEntry = new StateHistoryEntry();
155
156    initialEntry->stateId = fromStateId;
157    initialEntry->dataLength = 0;
158    initialEntry->data = NULL;
159
160    stateFrom = initialEntry;
161  }
162  else
163    stateFrom = (*it);
164
165  StateHistoryEntry * stateTo = new StateHistoryEntry();
166
167  stateTo->stateId = stateId;
168  stateTo->dataLength = neededSize;
169  stateTo->data = new byte[ neededSize ];
170
171  std::list<int>::iterator sizeIter = stateFrom->sizeList.begin();
172
173  int i = 0;
174  int n;
175 
176  bool hasPermission;
177  bool sizeChanged = false;
178
179  // now do the actual synchronization: kick all variables to write into a common buffer
180  for ( SyncVarList::iterator it = syncVarList.begin(); it != syncVarList.end(); it++ )
181  {
182    hasPermission = (
183            this->isServer() && (*it)->checkPermission( PERMISSION_SERVER ) ||
184            this->owner == this->hostID && (*it)->checkPermission( PERMISSION_OWNER ) ||
185            this->isServer() && this->owner != userId && (*it)->checkPermission( PERMISSION_OWNER ) ||
186            (*it)->checkPermission( PERMISSION_ALL ) 
187                    );
188    if ( sizeIter != stateFrom->sizeList.end() || *sizeIter != (*it)->getSize() ) 
189      sizeChanged = true;
190   
191    if ( ( hasPermission && (*it)->getPriority() >= priorityTH ) || sizeChanged )
192    {
193      n = (*it)->writeToBuf( stateTo->data+i, stateTo->dataLength - i );
194      //NETPRINTF(0)("getvar %s %d\n", (*it)->getName().c_str(), n);
195      stateTo->sizeList.push_back( n );
196      //(*it)->debug();
197      i += n;
198    }
199    else
200    {
201      for ( int j = 0; j<(*sizeIter); j++ )
202      {
203        assert( i < stateFrom->dataLength );
204        stateTo->data[i] = stateFrom->data[i];
205        i++;
206      }
207      //NETPRINTF(0)("getvar %s %d\n", (*it)->getName().c_str(), *sizeIter);
208      stateTo->sizeList.push_back( (*sizeIter) );
209    }
210
211    if ( sizeIter != stateFrom->sizeList.end() )
212      sizeIter++;
213  }
214
215  sentStates[userId].push_back( stateTo );
216 
217  if ( i != neededSize )
218  {
219    PRINTF(0)("strange error: (%s) %d != %d\n", this->getClassName(), i, neededSize);
220    assert(false);
221  }
222
223  //write diff to data
224  for ( i = 0; i<neededSize; i++ )
225  {
226    if ( i < stateFrom->dataLength )
227      data[i] = stateTo->data[i] - stateFrom->data[i];
228    else
229      data[i] = stateTo->data[i];
230  }
231
232  return neededSize;
233}
234
235/**
236 * sets a new state out of a diff created on another host
237 * @param userId hostId of user who send me that diff
238 * @param data pointer to diff
239 * @param length length of diff
240 * @param stateId id of current state
241 * @param fromStateId id of the base state id
242 * @return number bytes read
243 * @todo check for permissions
244 */
245int Synchronizeable::setStateDiff( int userId, byte* data, int length, int stateId, int fromStateId )
246{
247  //make sure this user has his history
248  if ( recvStates.size() <= userId )
249    recvStates.resize( userId+1 );
250
251  //create new state
252  StateHistoryEntry * stateTo = new StateHistoryEntry();
253  stateTo->stateId = stateId;
254  stateTo->dataLength = length;
255  stateTo->data = new byte[ length ];
256
257
258  //find state to apply diff to
259  StateHistoryEntry * stateFrom = NULL;
260
261  StateHistory::iterator it = recvStates[userId].begin();
262  while ( it != recvStates[userId].end() && (*it)->stateId != fromStateId )
263    it++;
264
265 
266//  if ( getLeafClassID() == CL_SPACE_SHIP )
267//  {
268//    PRINTF(0)("setStateDiff:SpaceShip from: %d stateId: %d\n", (it == recvStates[userId].end())?-1:fromStateId, stateId);
269//  }
270
271  if ( it == recvStates[userId].end() )
272  {
273    StateHistoryEntry * initialEntry = new StateHistoryEntry();
274
275    initialEntry->stateId = fromStateId;
276    initialEntry->dataLength = 0;
277    initialEntry->data = NULL;
278
279    stateFrom = initialEntry;
280  }
281  else
282    stateFrom = (*it);
283 
284  //apply diff
285  for ( int i = 0; i<length; i++ )
286  {
287    if ( i < stateFrom->dataLength )
288      stateTo->data[i] = stateFrom->data[i] + data[i];
289    else
290      stateTo->data[i] = data[i];
291   
292  }
293 
294  //add state to state history
295  recvStates[userId].push_back( stateTo );
296 
297  int i = 0;
298  int n = 0;
299  std::list<int> changes;
300 
301  for ( SyncVarList::iterator it = syncVarList.begin(); it != syncVarList.end(); it++ )
302  {
303    if (
304        (*it)->checkPermission( PERMISSION_SERVER ) && networkStream->isUserServer( userId ) ||
305        (*it)->checkPermission( PERMISSION_OWNER ) && this->owner == userId ||
306        networkStream->isUserServer( userId ) && this->owner != getHostID() && (*it)->checkPermission( PERMISSION_OWNER ) ||
307        (*it)->checkPermission( PERMISSION_ALL ) 
308       )
309    {
310      n = (*it)->readFromBuf( stateTo->data + i, stateTo->dataLength - i );
311      i += n;
312      //NETPRINTF(0)("%s::setvar %s %d\n", getClassName(), (*it)->getName().c_str(), n);
313      //(*it)->debug();
314      if ( (*it)->getHasChanged() )
315      {
316        changes.push_back( (*it)->getVarId() );
317      }
318    }
319    else
320    {
321//      PRINTF(0)("DONT SET VAR BECAUSE OF PERMISSION: %s %d %d %d %d %d %d\n", (*it)->getName().c_str(), (*it)->checkPermission( PERMISSION_SERVER ), (*it)->checkPermission( PERMISSION_OWNER ), (*it)->checkPermission( PERMISSION_ALL ), networkStream->isUserServer( userId ), this->owner, userId );
322      n = (*it)->getSizeFromBuf( stateTo->data + i, stateTo->dataLength - i );
323      //NETPRINTF(0)("%s::setvar %s %d\n", getClassName(), (*it)->getName().c_str(), n);
324      //(*it)->debug();
325      i += n;
326    }
327  }
328
329  this->varChangeHandler( changes );
330 
331  return i;
332}
333
334 /**
335 * override this function to be notified on change
336 * of your registred variables.
337 * @param id id's which have changed
338 */
339void Synchronizeable::varChangeHandler( std::list<int> & id )
340{
341}
342
343/**
344 * registers a varable to be synchronized over network
345 * @param var see src/lib/network/synchronizeable_var/ for available classes
346 */
347void Synchronizeable::registerVar( SynchronizeableVar * var )
348{
349  //PRINTF(0)("ADDING VAR: %s\n", var->getName().c_str());
350  syncVarList.push_back( var );
351}
352
353/**
354 * registers a varable to be synchronized over network
355 * return value is passed to varChangeHandler on change
356 * @param var see src/lib/network/synchronizeable_var/ for available classes
357 * @return handle passed to varChangeHandler on changes
358 */
359int Synchronizeable::registerVarId( SynchronizeableVar * var )
360{
361  //PRINTF(0)("ADDING VAR: %s\n", var->getName().c_str());
362  syncVarList.push_back( var );
363  var->setWatched( true );
364  var->setVarId( syncVarList.size()-1 );
365  return syncVarList.size()-1;
366}
367
368/**
369 * removed user's states from memory
370 * @param userId user to clean
371 */
372void Synchronizeable::cleanUpUser( int userId )
373{
374  if ( recvStates.size() > userId )
375  {
376    for ( std::list<StateHistoryEntry*>::iterator it = recvStates[userId].begin(); it != recvStates[userId].end(); it++ )
377    {
378      if ( (*it)->data )
379        delete [] (*it)->data;
380      (*it)->data = NULL;
381   
382      delete *it;
383    }
384    recvStates[userId].clear();
385  }
386 
387  if ( sentStates.size() > userId )
388  {
389   
390    for ( std::list<StateHistoryEntry*>::iterator it = sentStates[userId].begin(); it != sentStates[userId].end(); it++ )
391    {
392      if ( (*it)->data )
393        delete [] (*it)->data;
394      (*it)->data = NULL;
395   
396      delete *it;
397    }
398    sentStates[userId].clear();
399  }
400}
401
402/**
403 * this function is called after recieving a state.
404 * @param userId
405 * @param stateId
406 * @param fromStateId
407 */
408void Synchronizeable::handleRecvState( int userId, int stateId, int fromStateId )
409{
410   //make sure this user has his history
411  if ( recvStates.size() <= userId )
412    recvStates.resize( userId+1 );
413 
414  //remove old states
415  StateHistory::iterator it = recvStates[userId].begin();
416
417#if 0
418  while ( it != recvStates[userId].end() && (*it)->stateId < fromStateId )
419    it++;
420
421  if ( it != recvStates[userId].begin() )
422  {
423    for ( StateHistory::iterator it2 = recvStates[userId].begin(); it2 != it; it2++ )
424    {
425      if ( (*it2)->data != NULL )
426      {
427        delete [] (*it2)->data;
428        (*it2)->data = NULL;
429      }
430    }
431    recvStates[userId].erase( recvStates[userId].begin(), it );
432  }
433#endif
434
435  for ( it = recvStates[userId].begin(); it != recvStates[userId].end();  )
436  {
437    if ( (*it)->stateId < fromStateId )
438    {
439      StateHistory::iterator delIt = it;
440      it ++;
441     
442      if ( (*delIt)->data )
443        delete [] (*delIt)->data;
444      recvStates[userId].erase( delIt );
445     
446      continue;
447    }
448    it++;
449  }
450 
451  StateHistory::iterator fromState = recvStates[userId].end();
452  StateHistory::iterator toState = recvStates[userId].end();
453 
454  for ( it = recvStates[userId].begin(); it != recvStates[userId].end(); it++ )
455  {
456    if ( (*it)->stateId == stateId )
457      toState = it;
458    if ( (*it)->stateId == fromStateId )
459      fromState = it;
460   
461    if ( fromState != recvStates[userId].end() && toState != recvStates[userId].end() )
462      break;
463  }
464 
465  // setStateDiff was not called and i know fromStateId
466  if ( fromState != recvStates[userId].end() && toState == recvStates[userId].end() )
467  {
468    StateHistoryEntry * entry = new StateHistoryEntry;
469   
470    entry->dataLength = (*fromState)->dataLength;
471    if ( entry->dataLength > 0 )
472    {
473      entry->data = new byte[entry->dataLength];
474         
475      assert( (*fromState)->data );
476      memcpy( entry->data, (*fromState)->data, entry->dataLength );
477    }
478    else
479      entry->data = NULL;
480   
481    entry->sizeList = (*fromState)->sizeList;
482    entry->stateId = stateId;
483   
484    recvStates[userId].push_back(entry);
485  }
486}
487
488/**
489 * this function is called after sending a state
490 * @param userId
491 * @param stateId
492 * @param fromStateId
493 */
494void Synchronizeable::handleSentState( int userId, int stateId, int fromStateId )
495{
496   //make sure this user has his history
497  if ( sentStates.size() <= userId )
498    sentStates.resize( userId+1 );
499
500   //remove old states
501  StateHistory::iterator it = sentStates[userId].begin();
502
503  for ( it = sentStates[userId].begin(); it != sentStates[userId].end();  )
504  {
505    if ( (*it)->stateId < fromStateId )
506    {
507      StateHistory::iterator delIt = it;
508      it ++;
509     
510      if ( (*delIt)->data )
511        delete [] (*delIt)->data;
512      sentStates[userId].erase( delIt );
513     
514      continue;
515    }
516    it++;
517  }
518
519 
520  StateHistory::iterator fromState = sentStates[userId].end();
521  StateHistory::iterator toState = sentStates[userId].end();
522 
523  for ( it = sentStates[userId].begin(); it != sentStates[userId].end(); it++ )
524  {
525    if ( (*it)->stateId == stateId )
526      toState = it;
527    if ( (*it)->stateId == fromStateId )
528      fromState = it;
529   
530    if ( fromState != sentStates[userId].end() && toState != sentStates[userId].end() )
531      break;
532  }
533
534 
535  // getStateDiff was not called and i know fromStateId
536  if ( fromState != sentStates[userId].end() && toState == sentStates[userId].end() )
537  {
538    StateHistoryEntry * entry = new StateHistoryEntry;
539   
540    entry->dataLength = (*fromState)->dataLength;
541    if ( entry->dataLength > 0 )
542    {
543      entry->data = new byte[entry->dataLength];
544     
545      assert( (*fromState)->data );
546      memcpy( entry->data, (*fromState)->data, entry->dataLength );
547    }
548    else
549      entry->data = NULL;
550   
551    entry->sizeList = (*fromState)->sizeList;
552    entry->stateId = stateId;
553   
554    sentStates[userId].push_back(entry);
555  }
556 
557}
558
559
560
Note: See TracBrowser for help on using the repository browser.