1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2008 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
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 Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "collision_service.h"
18 #include <nel/3d/u_instance_group.h>
24 using namespace SBSERVICE
;
25 using namespace NLMISC
;
26 using namespace NLNET
;
27 using namespace NLPACS
;
31 //////////////////////////////////////////////////////////////////////////////
35 //////////////////////////////////////////////////////////////////////////////
37 CCollisionService::CClientMap
CCollisionService::_Clients
;
38 NLPACS::URetrieverBank
*CCollisionService::_RetrieverBank
;
39 NLPACS::UGlobalRetriever
*CCollisionService::_GlobalRetriever
;
40 NLPACS::UMoveContainer
*CCollisionService::_MoveContainer
;
41 CCollisionService::CMovePrimitiveVector
CCollisionService::_StaticMovePrimitives
;
42 TTime
CCollisionService::_LastTime
, CCollisionService::_NewTime
, CCollisionService::_DiffTime
;
43 double CCollisionService::_DiffTimeSeconds
;
44 float CCollisionService::_DiffTimeFloat
;
47 //////////////////////////////////////////////////////////////////////////////
49 /// BASIC FUNCTIONS ///
51 //////////////////////////////////////////////////////////////////////////////
53 void CCollisionService::commandStart()
58 void CCollisionService::init()
61 CUnifiedNetwork::getInstance()->setServiceDownCallback("*", cbDown
);
64 CPath::addSearchPath(ConfigFile
.getVar("DataPath").asString(), true, false);
66 // init the global retriever, the retriever bank, and the move container
67 _RetrieverBank
= URetrieverBank::createRetrieverBank(ConfigFile
.getVar("RetrieverBankName").asString().c_str());
68 _GlobalRetriever
= UGlobalRetriever::createGlobalRetriever(ConfigFile
.getVar("GlobalRetrieverName").asString().c_str(), _RetrieverBank
);
69 _MoveContainer
= UMoveContainer::createMoveContainer(_GlobalRetriever
, 100, 100, 6.0);
71 // some silly snowballs specific code to load static instance groups, redo
72 CConfigFile::CVar igv
= ConfigFile
.getVar("InstanceGroups");
73 for (uint i
= 0; i
< igv
.size(); ++i
)
75 NL3D::UInstanceGroup
*ig
= NL3D::UInstanceGroup::createInstanceGroup(igv
.asString(i
));
76 if (ig
== NULL
) nlwarning("Instance group '%s' not found", igv
.asString(i
).c_str());
79 for (uint i
= 0; i
< ig
->getNumInstance(); ++i
)
81 UMovePrimitive
*primitive
= _MoveContainer
->addCollisionablePrimitive(0, 1);
82 primitive
->setPrimitiveType(UMovePrimitive::_2DOrientedCylinder
);
83 primitive
->setReactionType(UMovePrimitive::DoNothing
);
84 primitive
->setTriggerType(UMovePrimitive::NotATrigger
);
85 primitive
->setCollisionMask(2);
86 primitive
->setOcclusionMask(1);
87 primitive
->setObstacle(true);
89 string name
= ig
->getShapeName(i
);
91 if (strlwr(name
) == "pi_po_igloo_a") rad
= 4.5f
;
92 else if (strlwr(name
) == "pi_po_snowman_a") rad
= 1.0f
;
93 else if (strlwr(name
) == "pi_po_pinetree_a") rad
= 2.0f
;
94 else if (strlwr(name
) == "pi_po_tree_a") rad
= 2.0f
;
95 else if (strlwr(name
) == "pi_po_pingoo_stat_a") rad
= 1.0f
;
96 else if (strlwr(name
) == "pi_po_gnu_stat_a") rad
= 1.0f
;
100 nlwarning ("Instance name '%s' doesn't have a good radius for collision", name
.c_str());
103 primitive
->setRadius(rad
);
104 primitive
->setHeight(6.0f
);
106 primitive
->insertInWorldImage(0);
107 CVector pos
= ig
->getInstancePos(i
);
108 primitive
->setGlobalPosition(CVectorD(pos
.x
, pos
.y
, pos
.z
- 1.5f
), 0);
109 _StaticMovePrimitives
.push_back(primitive
);
115 _NewTime
= CTime::getLocalTime();
118 bool CCollisionService::update()
120 _LastTime
= _NewTime
;
121 _NewTime
= CTime::getLocalTime();
122 _DiffTime
= _NewTime
- _LastTime
;
123 _DiffTimeSeconds
= (double)_DiffTime
/ 1000.0;
124 _DiffTimeFloat
= (float)_DiffTimeSeconds
;
126 for (CClientMap::iterator it
= _Clients
.begin(); it
!= _Clients
.end(); it
++)
127 for (CEntityMap::iterator it2
= it
->second
.begin(); it2
!= it
->second
.end(); it2
++)
129 CEntity
&entity
= it2
->second
;
133 // nldebug("entity.Moving");
134 CVector movement
= entity
.NewClientPosition
- entity
.OldClientPosition
;
135 entity
.Distance
= movement
.norm();
136 if (entity
.Distance
== 0.0f
)
138 entity
.Moving
= false;
139 entity
.Retry
= false;
141 CVectorD speed
= CVectorD(movement
) / _DiffTimeSeconds
;
142 entity
.MovePrimitive
->move(speed
, 0);
144 // else nldebug("!entity.Moving");
147 // apparently this thingy does the collision checks
148 _MoveContainer
->evalCollision(_DiffTimeSeconds
, 0);
150 // check all wrong stuff (use some different way maybe)
151 for (CClientMap::iterator it
= _Clients
.begin(); it
!= _Clients
.end(); it
++)
152 for (CEntityMap::iterator it2
= it
->second
.begin(); it2
!= it
->second
.end(); it2
++)
154 CEntity
&entity
= it2
->second
;
158 UGlobalPosition globalPosition
;
159 entity
.MovePrimitive
->getGlobalPosition(globalPosition
, 0);
160 CVector serverPosition
= _GlobalRetriever
->getGlobalPosition(globalPosition
);
161 //nlinfo("clientPosition: %f %f %f", entity.NewClientPosition.x, entity.NewClientPosition.y, entity.NewClientPosition.z);
162 //nlinfo("serverPosition: %f %f %f", serverPosition.x, serverPosition.y, serverPosition.z);
164 // Allow the difference between the external client position and the
165 // local server position to be up to half of the traveled distance
166 // plus the entity's height or radius, twice (using Retry).
167 float allowedDifference
= entity
.Distance
* 0.5f
;
169 if (abs(entity
.NewClientPosition
.z
- serverPosition
.z
) > 1.0f
+ allowedDifference
)
175 // fake server position has same z as client position if ok
176 serverPosition
.z
= entity
.NewClientPosition
.z
;
177 if ((serverPosition
- entity
.NewClientPosition
).norm() > entity
.MovePrimitive
->getRadius() + allowedDifference
)
181 if (move
) // Entity moved incorrectly.
183 if (entity
.Retry
) // If second try.
185 nldebug("entity.Retry");
186 msgPosition(it2
->first
, serverPosition
);
188 else nldebug("!entity.Retry");
189 entity
.Retry
= !entity
.Retry
; // Else the entity gets one more chance.
192 // The difference to the new position must be from local server position.
193 entity
.OldClientPosition
= serverPosition
;
201 void CCollisionService::release()
207 //////////////////////////////////////////////////////////////////////////////
209 /// OTHER FUNCTIONS ///
211 //////////////////////////////////////////////////////////////////////////////
213 void CCollisionService::sendMessage(CMessage
&msgout
)
215 CUnifiedNetwork
*instance
= CUnifiedNetwork::getInstance();
216 for (CClientMap::iterator it
= _Clients
.begin(); it
!= _Clients
.end(); it
++)
217 CUnifiedNetwork::getInstance()->send(TServiceId(it
->first
), msgout
);
221 //////////////////////////////////////////////////////////////////////////////
223 /// MESSAGE SENDERS ///
225 //////////////////////////////////////////////////////////////////////////////
227 /****************************************************************************
228 * Function: msgUpdate
229 * Send CLS_UPDATE message to registered services
232 ****************************************************************************/
233 void CCollisionService::msgUpdate()
235 if (!_Clients
.size()) return;
237 static CMessage
msgout("CLS_UPDATE");
239 //nldebug("Sent CLS_UPDATE to %u services", _Clients.size());
242 /****************************************************************************
243 * Function: msgPosition
244 * Send CLS_POSITION message to registered services
248 * - position: new position
249 ****************************************************************************/
250 void CCollisionService::msgPosition(uint32 id
, CVector position
)
252 if (!_Clients
.size()) return;
254 CMessage
msgout("CLS_POSITION");
255 msgout
.serial(id
, position
);
257 nldebug("Sent CLS_POSITION %u %f %f %f to %u services", id
, position
.x
, position
.y
, position
.z
, _Clients
.size());
261 //////////////////////////////////////////////////////////////////////////////
263 /// MESSAGE CALLBACKS ///
265 //////////////////////////////////////////////////////////////////////////////
267 /****************************************************************************
269 * Receives an "ADD" message.
270 ****************************************************************************/
271 void CCollisionService::cbAdd(CMessage
&msgin
, const std::string
&serviceName
, TServiceId sid
)
273 // Read incoming message
277 msgin
.serial(id
, position
, radius
);
278 nldebug("Received ADD %u %f %f %f %f", id
, position
.x
, position
.y
, position
.z
, radius
);
280 // Do something with it
281 CEntity
&entity
= _Clients
[sid
.get()][id
] = CEntity();
282 entity
.OldClientPosition
= position
;
283 entity
.NewClientPosition
= position
;
284 entity
.MovePrimitive
= _MoveContainer
->addCollisionablePrimitive(0, 1);
285 entity
.MovePrimitive
->setPrimitiveType(UMovePrimitive::_2DOrientedCylinder
);
286 entity
.MovePrimitive
->setReactionType(UMovePrimitive::Slide
);
287 entity
.MovePrimitive
->setTriggerType(UMovePrimitive::NotATrigger
);
288 entity
.MovePrimitive
->setCollisionMask(3);
289 entity
.MovePrimitive
->setOcclusionMask(2);
290 entity
.MovePrimitive
->setObstacle(true);
291 entity
.MovePrimitive
->setRadius(radius
);
292 entity
.MovePrimitive
->setHeight(1.8f
);
293 entity
.MovePrimitive
->insertInWorldImage(0);
294 entity
.MovePrimitive
->setGlobalPosition(position
, 0);
297 /****************************************************************************
299 * Receives a "MOVE" message.
300 ****************************************************************************/
301 void CCollisionService::cbMove(CMessage
&msgin
, const std::string
&serviceName
, TServiceId sid
)
303 // Read incoming message
306 msgin
.serial(id
, position
);
307 // nldebug("Received MOVE %u %f %f %f", id, position.x, position.y, position.z);
309 // Do something with it
310 CEntity
&entity
= _Clients
[sid
.get()][id
];
311 entity
.NewClientPosition
= position
;
312 if (entity
.OldClientPosition
!= position
)
313 entity
.Moving
= true;
316 /****************************************************************************
318 * Receives a "REMOVE" message.
319 ****************************************************************************/
320 void CCollisionService::cbRemove(CMessage
&msgin
, const std::string
&serviceName
, TServiceId sid
)
322 // Read incoming message
325 nldebug("Received REMOVE %u", id
);
327 // Do something with it
328 if (_Clients
[sid
.get()].find(id
) == _Clients
[sid
.get()].end())
330 nlwarning("Unknown entity %u", id
);
333 CEntity
&entity
= _Clients
[sid
.get()][id
];
334 _MoveContainer
->removePrimitive(entity
.MovePrimitive
);
335 _Clients
[sid
.get()].erase(id
);
338 /****************************************************************************
339 * Function: cbRegister
340 * Receives a "REGISTER" message.
341 ****************************************************************************/
342 void CCollisionService::cbRegister(CMessage
&msgin
, const std::string
&serviceName
, TServiceId sid
)
344 // Read incoming message
345 nldebug("Received REGISTER %s %s", serviceName
.c_str(), sid
.toString().c_str());
347 // Do something with it
348 _Clients
[sid
.get()] = CEntityMap();
352 //////////////////////////////////////////////////////////////////////////////
354 /// NETWORK CALLBACKS ///
356 //////////////////////////////////////////////////////////////////////////////
358 /****************************************************************************
360 * Called when a registered service goes down
361 ****************************************************************************/
362 void CCollisionService::cbDown(const string
&serviceName
, TServiceId sid
, void *arg
)
364 CClientMap::iterator it
= _Clients
.find(sid
.get());
365 if (it
!= _Clients
.end())
367 // remove all entities
368 for (CEntityMap::iterator it2
= it
->second
.begin(); it2
!= it
->second
.end(); it2
++)
369 _MoveContainer
->removePrimitive(it2
->second
.MovePrimitive
);
376 //////////////////////////////////////////////////////////////////////////////
378 /// SERVICE CONFIGURATION ///
380 //////////////////////////////////////////////////////////////////////////////
382 /****************************************************************************
385 * It define the functions to call when receiving a specific message
386 ****************************************************************************/
387 TUnifiedCallbackItem CallbackArray
[] =
389 { "REGISTER", CCollisionService::cbRegister
},
390 { "ADD", CCollisionService::cbAdd
},
391 { "MOVE", CCollisionService::cbMove
},
392 { "REMOVE", CCollisionService::cbRemove
},
395 /****************************************************************************
396 * SNOWBALLS COLLISION SERVICE MAIN Function
398 * This call create a main function for the world_service:
400 * - based on the service class CCollisionService inherited from IService
401 * - having the short name "CLS"
402 * - having the long name "collision_service"
403 * - listening on an automatically allocated port (0) by the naming service
404 * - and callback actions set to "CallbackArray"
406 ****************************************************************************/
407 NLNET_SERVICE_MAIN(CCollisionService
, "CLS", "collision_service", 0, CallbackArray
, "", "")