OverSim
SimMud.cc
Go to the documentation of this file.
1 //
2 // Copyright (C) 2006 Institut fuer Telematik, Universitaet Karlsruhe (TH)
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 //
18 
24 #include "SimMud.h"
25 #include "ScribeMessage_m.h"
26 #include <limits.h>
27 #include <GlobalStatistics.h>
28 
30 
31 using namespace std;
32 
34 {
35  currentRegionX = currentRegionY = INT_MIN;
36  currentRegionID = OverlayKey::UNSPECIFIED_KEY;
37  playerTimer = new cMessage("playerTimeout");
38 }
39 
41 {
42  cancelAndDelete(playerTimer);
43 }
44 
46 {
47  simtime_t time = globalStatistics->calcMeasuredLifetime(creationTime);
48  if (time < GlobalStatistics::MIN_MEASURED) return;
49 
50  globalStatistics->addStdDev("SimMUD: Lost or too long delayed MoveLists/s",
51  lostMovementLists / time);
52  globalStatistics->addStdDev("SimMUD: Received valid MoveLists/s",
53  receivedMovementLists / time);
54 }
55 
56 void SimMud::initializeApp(int stage)
57 {
58  if( stage != (numInitStages()-1)) {
59  return;
60  }
61 
62  // FIXME: areaDimension is not used consistently between all overlays!
63  fieldSize = par("areaDimension");
64  numSubspaces = par("numSubspaces");
65  regionSize = fieldSize / numSubspaces;
66  playerTimeout = par("playerTimeout");
67 
68  receivedMovementLists = 0;
69  lostMovementLists = 0;
70 
71  maxMoveDelay = par("maxMoveDelay");
72  AOIWidth = par("AOIWidth");
73 
74  WATCH(currentRegionX);
75  WATCH(currentRegionY);
76  WATCH(currentRegionID);
77 // WATCH_MAP( playerMap );
78 }
79 
81 {
82 // RPC_SWITCH_START(msg);
83 // RPC_SWITCH_END( );
84 // return RPC_HANDLED;
85  return false;
86 }
87 
89  cPolymorphic* context,
90  int rpcId, simtime_t rtt )
91 {
92 // RPC_SWITCH_START(msg);
93 // RPC_SWITCH_END( );
94 }
95 
96 void SimMud::handleTimerEvent( cMessage *msg )
97 {
98  if( msg == playerTimer ) {
99  // Check if any player didn't send any updates since last check
100  map<NodeHandle, PlayerInfo>::iterator it;
101  list<NodeHandle> erasePlayers;
102  for( it = playerMap.begin(); it != playerMap.end(); ++it ) {
103  if( it->second.update == false ) {
104  erasePlayers.push_back( it->first );
105  }
106  it->second.update = false;
107  }
108  for( list<NodeHandle>::iterator it = erasePlayers.begin(); it != erasePlayers.end(); ++it) {
109  // Delete all neighbors that didn't update (and inform app)
110  GameAPIListMessage *scMsg = new GameAPIListMessage("NEIGHBOR_UPDATE");
111  scMsg->setCommand(NEIGHBOR_UPDATE);
112  scMsg->setRemoveNeighborArraySize(1);
113  scMsg->setRemoveNeighbor(0, *it);
114  send(scMsg, "to_upperTier");
115 
116  playerMap.erase( *it );
117  }
118  erasePlayers.clear();
119 
120  scheduleAt( simTime() + playerTimeout, msg );
121  }
122 }
123 
124 void SimMud::handleLowerMessage( cMessage *msg )
125 {
126  if (ALMMulticastMessage* mcastMsg =
127  dynamic_cast<ALMMulticastMessage*>(msg) ){
128 
129  cMessage* innerMsg = mcastMsg->decapsulate();
130  SimMudMoveMessage* moveMsg = NULL;
131  if( innerMsg ) {
132  moveMsg = dynamic_cast<SimMudMoveMessage*>(innerMsg);
133  }
134  if( moveMsg ) {
135  handleOtherPlayerMove( moveMsg );
136  }
137  delete innerMsg;
138  delete mcastMsg;
139  }
140 }
141 
143 {
144  // process only ready messages from the tier below
145  if( getThisCompType() - msg->getComp() == 1 ){
146  if( msg->getReady() ) {
147  // TODO/FIXME: use overlay->sendMessageToAllComp(msg, getThisCompType())?
148  msg->setComp(getThisCompType());
149  send(msg, "to_upperTier");
150  // also send AOI size to SimpleGameClient
151  GameAPIResizeAOIMessage* gameMsg = new GameAPIResizeAOIMessage("RESIZE_AOI");
152  gameMsg->setCommand(RESIZE_AOI);
153  gameMsg->setAOIsize(AOIWidth);
154  send(gameMsg, "to_upperTier");
155 
156  if( playerTimeout > 0 ) {
157  cancelEvent( playerTimer );
158  scheduleAt( simTime() + playerTimeout, playerTimer );
159  }
160  }
161  } else {
162  delete msg;
163  }
164 }
165 
166 void SimMud::handleUpperMessage( cMessage *msg )
167 {
168  if( GameAPIPositionMessage* moveMsg =
169  dynamic_cast<GameAPIPositionMessage*>(msg) ) {
170  handleMove( moveMsg );
171  delete msg;
172  }
173 }
174 
176 {
177  if( (int) (msg->getPosition().x/regionSize) != currentRegionX ||
178  (int) (msg->getPosition().y/regionSize) != currentRegionY ) {
179  // New position is outside of current region; change currentRegion
180 
181  // get region ID
182  currentRegionX = (int) (msg->getPosition().x/regionSize);
183  currentRegionY = (int) (msg->getPosition().y/regionSize);
184  std::stringstream regionstr;
185  regionstr << currentRegionX << ":" << currentRegionY;
186  OverlayKey region = OverlayKey::sha1( BinaryValue(regionstr.str() ));
187  currentRegionID = region;
188  }
189 
190  set<OverlayKey> expectedRegions;
191  set<OverlayKey> allowedRegions;
192  int minX = (int) ((msg->getPosition().x - AOIWidth)/regionSize);
193  if( minX < 0 ) minX = 0;
194  int maxX = (int) ((msg->getPosition().x + AOIWidth)/regionSize);
195  if( maxX >= numSubspaces ) maxX = numSubspaces -1;
196  int minY = (int) ((msg->getPosition().y - AOIWidth)/regionSize);
197  if( minY < 0 ) minY = 0;
198  int maxY = (int) ((msg->getPosition().y + AOIWidth)/regionSize);
199  if( maxY >= numSubspaces ) maxY = numSubspaces -1;
200 
201  // FIXME: make parameter: unsubscription size
202  int minUnsubX = (int) ((msg->getPosition().x - 1.5*AOIWidth)/regionSize);
203  if( minUnsubX < 0 ) minUnsubX = 0;
204  int maxUnsubX = (int) ((msg->getPosition().x + 1.5*AOIWidth)/regionSize);
205  if( maxUnsubX >= numSubspaces ) maxUnsubX = numSubspaces -1;
206  int minUnsubY = (int) ((msg->getPosition().y - 1.5*AOIWidth)/regionSize);
207  if( minUnsubY < 0 ) minUnsubY = 0;
208  int maxUnsubY = (int) ((msg->getPosition().y + 1.5+AOIWidth)/regionSize);
209  if( maxUnsubY >= numSubspaces ) maxUnsubY = numSubspaces -1;
210 
211  for( int x = minUnsubX; x <= maxUnsubX; ++x ){
212  for( int y = minUnsubY; y <= maxUnsubY; ++y ){
213  std::stringstream regionstr;
214  regionstr << x << ":" << y;
215  if( x >= minX && x <=maxX && y >= minY && y <= maxY ){
216  expectedRegions.insert( OverlayKey::sha1( BinaryValue(regionstr.str() )));
217  }
218  allowedRegions.insert( OverlayKey::sha1( BinaryValue(regionstr.str() )));
219  }
220  }
221 
222  set<OverlayKey>::iterator subIt = subscribedRegions.begin();
223  while( subIt != subscribedRegions.end() ){
224 
225  expectedRegions.erase( *subIt );
226 
227  // unsubscribe region if to far away
228  if( allowedRegions.find( *subIt ) == allowedRegions.end() ){
229  // Inform other players about region leave
230  SimMudMoveMessage* moveMsg = new SimMudMoveMessage("MOVE/LEAVE_REGION");
231  moveMsg->setSrc( overlay->getThisNode() );
232  moveMsg->setPosX( msg->getPosition().x );
233  moveMsg->setPosY( msg->getPosition().y );
234  moveMsg->setTimestamp( simTime() );
235  moveMsg->setLeaveRegion( true );
236  ALMMulticastMessage* mcastMsg = new ALMMulticastMessage("MOVE/LEAVE_REGION");
237  mcastMsg->setGroupId(*subIt);
238  mcastMsg->encapsulate( moveMsg );
239 
240  send(mcastMsg, "to_lowerTier");
241 
242  // leave old region's multicastGroup
243  ALMLeaveMessage* leaveMsg = new ALMLeaveMessage("LEAVE_REGION_GROUP");
244  leaveMsg->setGroupId(*subIt);
245  send(leaveMsg, "to_lowerTier");
246  // TODO: leave old simMud region
247 
248  // Erase subspace from subscribedList and increase iterator
249  subscribedRegions.erase( subIt++ );
250  } else {
251  ++subIt;
252  }
253  }
254 
255  // if any "near" region is not yet subscribed, subscribe
256  for( set<OverlayKey>::iterator regionIt = expectedRegions.begin(); regionIt != expectedRegions.end(); ++regionIt ){
257  // join region's multicast group
259  subMsg->setGroupId(*regionIt);
260  send( subMsg, "to_lowerTier" );
261 
262  subscribedRegions.insert( *regionIt );
263  // TODO: join simMud region
264  }
265 
266  // publish movement
267  SimMudMoveMessage* moveMsg = new SimMudMoveMessage("MOVE");
268  moveMsg->setSrc( overlay->getThisNode() );
269  moveMsg->setPosX( msg->getPosition().x );
270  moveMsg->setPosY( msg->getPosition().y );
271  moveMsg->setTimestamp( simTime() );
272  moveMsg->setBitLength( SIMMUD_MOVE_L( moveMsg ));
273  ALMMulticastMessage* mcastMsg = new ALMMulticastMessage("MOVE");
274  mcastMsg->setGroupId(currentRegionID);
275  mcastMsg->encapsulate( moveMsg );
276 
277  send(mcastMsg, "to_lowerTier");
278 }
279 
281 {
282  GameAPIListMessage *scMsg = new GameAPIListMessage("NEIGHBOR_UPDATE");
283  scMsg->setCommand(NEIGHBOR_UPDATE);
284 
285  NodeHandle& src = msg->getSrc();
286  RECORD_STATS(
287  if( msg->getTimestamp() < simTime() - maxMoveDelay ){
288  ++lostMovementLists;
289  globalStatistics->addStdDev("SimMUD: LOST MOVE Delay",
290  SIMTIME_DBL(simTime() - msg->getTimestamp()) );
291  } else {
292  ++receivedMovementLists;
293  }
294 
295  if( src != overlay->getThisNode() ){
296  globalStatistics->addStdDev("SimMUD: MOVE Delay",
297  SIMTIME_DBL(simTime() - msg->getTimestamp()) );
298  }
299  );
300 
301  if( msg->getLeaveRegion() ) {
302  // Player leaves region
303  scMsg->setRemoveNeighborArraySize(1);
304  scMsg->setRemoveNeighbor(0, src);
305  playerMap.erase( src );
306 
307  } else {
308  PlayerInfo player;
309  player.pos = Vector2D( msg->getPosX(), msg->getPosY() );
310  player.update = true;
311 
312  // pair< map<NodeHandle, PlayerInfo>::iterator, bool> inserter =
313  playerMap.insert( make_pair(src, player) );
314 
315  /* if( inserter.second ) {
316  // new player
317 
318  } else {
319  // move player
320  }*/
321 
322  // Ordinary move
323  scMsg->setAddNeighborArraySize(1);
325  scMsg->setAddNeighbor(0, src);
326  scMsg->setNeighborPosition(0, Vector2D(msg->getPosX(), msg->getPosY()) );
327  }
328  send(scMsg, "to_upperTier");
329 
330 }
331