1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2015 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
28 using namespace NLMISC
;
29 using namespace NLNET
;
31 extern std::map
<TYPE_NAME_STRING_ID
, std::string
> StringMap
;
32 extern std::set
<TYPE_NAME_STRING_ID
> StringAsked
;
34 // ***************************************************************************
36 // Must be a pointer to control when to start listening socket and when stop it
37 extern CCallbackServer
*Server
;
39 uint32 StillToAdd
= 0;
40 uint32 StillToRemove
= 0;
43 uint32 CurrentlyInVision
= 0;
45 uint32 SentMiscProp
= 0;
48 NLMISC_VARIABLE(uint32
, StillToAdd
, "");
49 NLMISC_VARIABLE(uint32
, StillToRemove
, "");
50 NLMISC_VARIABLE(uint32
, Added
, "");
51 NLMISC_VARIABLE(uint32
, Removed
, "");
52 NLMISC_VARIABLE(uint32
, CurrentlyInVision
, "");
53 NLMISC_VARIABLE(uint32
, SentPos
, "");
54 NLMISC_VARIABLE(uint32
, SentMiscProp
, "");
55 NLMISC_VARIABLE(uint32
, SentStr
, "");
57 // ***************************************************************************
59 vector
<NLMISC::CSmartPtr
<CMonitorClient
> > Clients
;
61 // ***************************************************************************
63 CMonitorClient::CMonitorClient(TSockId sock
)
68 AllowedUploadBandwidth
= 2048; // 2kB per second for a start
75 MiscPropWeight
= 0.5f
;
76 Authentificated
= false;
80 // ***************************************************************************
82 CMonitorClient::~CMonitorClient()
86 // ***************************************************************************
88 void CMonitorClient::setWindow(float xmin
, float ymin
, float xmax
, float ymax
)
98 _WindowTopLeft
= CVector(xmin
, ymin
, 0);
99 _WindowBottomRight
= CVector(xmax
, ymax
, 0);
102 // ***************************************************************************
104 void CMonitorClient::add (const TDataSetRow
&entity
)
106 uint32 index
= entity
.getIndex();
108 if (index
>= Entites
.size())
109 Entites
.resize(index
+1);
112 if ( (Entites
[index
].Flags
& CEntityEntry::Present
) != 0)
115 // if pending in removal, just remove from PendingRemove list
116 if ( (Entites
[index
].Flags
& CEntityEntry::Pending
) != 0)
118 vector
<uint32
>::iterator it
= find(PendingRemove
.begin(), PendingRemove
.end(), entity
.getIndex());
119 if (it
!= PendingRemove
.end())
120 PendingRemove
.erase(it
);
121 Entites
[index
].Flags
&= (~CEntityEntry::Pending
);
122 Entites
[index
].Flags
|= CEntityEntry::Present
;
126 // set entity as present
127 Entites
[index
].Flags
|= (CEntityEntry::Present
| CEntityEntry::DirtyAll
| CEntityEntry::Pending
);
129 // add to pending to addition entities
130 PendingAdd
.push_back(index
);
133 // ***************************************************************************
135 void CMonitorClient::remove (const TDataSetRow
&entity
)
137 uint32 index
= entity
.getIndex();
139 nlassert( index
< Entites
.size() );
141 // don't remove twice
142 if ( (Entites
[index
].Flags
& CEntityEntry::Present
) == 0)
145 // if pending in addition, just remove from PendingAdd list
146 if ( (Entites
[index
].Flags
& CEntityEntry::Pending
) != 0)
148 vector
<uint32
>::iterator it
= find(PendingAdd
.begin(), PendingAdd
.end(), entity
.getIndex());
149 if (it
!= PendingAdd
.end())
150 PendingAdd
.erase(it
);
151 Entites
[index
].Flags
&= (~(CEntityEntry::Pending
| CEntityEntry::Present
));
155 // set entity as not present
156 Entites
[index
].Flags
&= (~CEntityEntry::Present
);
157 Entites
[index
].Flags
|= CEntityEntry::Pending
;
159 PendingRemove
.push_back(index
);
162 // ***************************************************************************
164 void CMonitorClient::resetVision()
168 const CVector
&topleft
= getTopLeft();
169 const CVector
&bottomright
= getBottomRight();
171 for (i
=0; i
<GlobalEntites
.size(); ++i
)
173 if ((GlobalEntites
[i
].Flags
& CGlobalEntityEntry::Present
) != 0)
175 TDataSetRow entityIndex
= TDataSetRow::createFromRawIndex (i
);
176 CMirrorPropValueRO
<TYPE_POSX
> valueX( TheDataset
, entityIndex
, DSPropertyPOSX
);
177 CMirrorPropValueRO
<TYPE_POSY
> valueY( TheDataset
, entityIndex
, DSPropertyPOSY
);
179 CVector
pos((float)valueX
/ 1000.f
, (float)valueY
/ 1000.f
, 0.0f
);
181 // is entity in client window
182 //nlassert(entityIndex.getIndex() < client.Entites.size());
183 bool wasPresent
= (i
< Entites
.size() && (Entites
[i
].Flags
& CEntityEntry::Present
) != 0);
184 if (pos
.x
> topleft
.x
&& pos
.x
< bottomright
.x
&& pos
.y
> topleft
.y
&& pos
.y
< bottomright
.y
)
192 Entites
[i
].Flags
|= CEntityEntry::DirtyAll
;
197 remove( entityIndex
);
203 // ***************************************************************************
205 void CMonitorClient::update ()
211 CurrentlyInVision
= 0;
215 // compute bandwidth spaces allowed for this cycle
216 float cycleBandw
= (float)AllowedUploadBandwidth
/ 10.0f
;
217 float totalRatio
= AddWeight
+ RemoveWeight
+ PosWeight
+ StrWeight
+ MiscPropWeight
;
218 uint maxAddSize
= (uint
)(cycleBandw
*AddWeight
/totalRatio
);
219 uint maxRmvSize
= (uint
)(cycleBandw
*RemoveWeight
/totalRatio
);
220 uint maxPosSize
= (uint
)(cycleBandw
*PosWeight
/totalRatio
);
221 uint maxStrSize
= (uint
)(cycleBandw
*StrWeight
/totalRatio
);
222 uint maxMiscPropSize
= (uint
)(cycleBandw
*MiscPropWeight
/totalRatio
);
224 const uint addSize
= 4+4+8+4+4+4;
225 const uint rmvSize
= 4;
226 const uint posSize
= 4+4+4+4;
227 const uint miscPropSize
= 4+4+4+1+1;
229 CConfigFile::CVar
*var
= IService::getInstance()->ConfigFile
.getVarPtr ("UpdatePerTick");
230 // here we suppose that position is changing often, whereas other less important
231 // properties are updated less frequently
233 uint miscPropToSend
= 0;
236 if (var
&& (var
->Type
== CConfigFile::CVar::T_INT
))
237 poscount
= var
->asInt();
239 uint startOffset
= StartOffset
;
240 if (startOffset
>= InVision
.size())
243 uint entity
= startOffset
;
244 while (poscount
> 0 && !InVision
.empty())
247 uint32 entityRawIndex
= InVision
[entity
];
248 if ((Entites
[entityRawIndex
].Flags
& CEntityEntry::PosDirty
) != 0)
250 if ((Entites
[entityRawIndex
].Flags
& CEntityEntry::Pending
) == 0)
255 if ((Entites
[entityRawIndex
].Flags
& CEntityEntry::MiscPropDirty
) != 0 && (Entites
[entityRawIndex
].Flags
& CEntityEntry::Pending
) == 0)
259 if (entity
>= InVision
.size())
261 if (entity
== startOffset
)
266 uint addTotalSize
= (uint
)PendingAdd
.size()*addSize
;
267 uint rmvTotalSize
= (uint
)PendingRemove
.size()*rmvSize
;
268 uint posTotalSize
= posToSend
*posSize
;
269 uint miscPropTotalSize
= miscPropToSend
*miscPropSize
;
270 uint strTotalSize
= 0;
273 vector
< pair
<TYPE_NAME_STRING_ID
, string
*> > strToSend
;
274 for (i
=0; i
<Str
.size() && strTotalSize
< (uint
)cycleBandw
; ++i
)
276 TYPE_NAME_STRING_ID id
= Str
[i
];
277 std::map
<TYPE_NAME_STRING_ID
, std::string
>::iterator ite
= StringMap
.find (id
);
278 nlassert (ite
!= StringMap
.end());
279 strToSend
.push_back(std::pair
<TYPE_NAME_STRING_ID
, string
*>(id
, &((*ite
).second
)));
280 strTotalSize
+= 4+4+(uint
)(*ite
).second
.size();
283 bool restrictAdd
= (addTotalSize
> maxAddSize
);
284 bool restrictRmv
= (rmvTotalSize
> maxRmvSize
);
285 bool restrictPos
= (posTotalSize
> maxPosSize
);
286 bool restrictStr
= (strTotalSize
> maxStrSize
);
287 bool restrictMiscProp
= (miscPropTotalSize
> maxMiscPropSize
);
289 float remainingBandw
= cycleBandw
;
291 if (!restrictAdd
) { totalRatio
-= AddWeight
; remainingBandw
-= addTotalSize
; }
292 if (!restrictRmv
) { totalRatio
-= RemoveWeight
; remainingBandw
-= rmvTotalSize
; }
293 if (!restrictPos
) { totalRatio
-= PosWeight
; remainingBandw
-= posTotalSize
; }
294 if (!restrictStr
) { totalRatio
-= StrWeight
; remainingBandw
-= strTotalSize
; }
295 if (!restrictMiscProp
) { totalRatio
-= MiscPropWeight
; remainingBandw
-= miscPropTotalSize
; }
297 if (restrictAdd
) addTotalSize
= (uint
)(remainingBandw
*AddWeight
/totalRatio
);
298 if (restrictRmv
) rmvTotalSize
= (uint
)(remainingBandw
*RemoveWeight
/totalRatio
);
299 if (restrictPos
) posTotalSize
= (uint
)(remainingBandw
*PosWeight
/totalRatio
);
300 if (restrictStr
) strTotalSize
= (uint
)(remainingBandw
*StrWeight
/totalRatio
);
301 if (restrictMiscProp
) miscPropTotalSize
= (uint
)(remainingBandw
*MiscPropWeight
/totalRatio
);
304 // update pending lists
305 uint pendingAddCount
= (addTotalSize
/addSize
);
306 while (!PendingAdd
.empty() && pendingAddCount
> 0)
308 uint32 entityRawIndex
= PendingAdd
.back();
309 PendingAdd
.pop_back();
310 TDataSetRow entity
= TDataSetRow::createFromRawIndex (entityRawIndex
);
311 CMirrorPropValueRO
<TYPE_NAME_STRING_ID
> stringId( TheDataset
, entity
, DSPropertyNAME_STRING_ID
);
312 CMirrorPropValueRO
<TYPE_SHEET
> sheetId( TheDataset
, entity
, DSPropertySHEET
);
314 addData
.Id
= entity
.getIndex();
315 addData
.StringId
= stringId
;
316 addData
.EntityId
= TheDataset
.getEntityId (entity
);
317 addData
.SheetId
= sheetId
;
318 Add
.push_back (addData
);
320 Entites
[entityRawIndex
].Flags
&= (~CEntityEntry::Pending
);
322 InVision
.push_back(entity
.getIndex());
328 uint pendingRemoveCount
= (rmvTotalSize
/rmvSize
);
329 while (!PendingRemove
.empty() && pendingRemoveCount
> 0)
331 uint32 entityRawIndex
= PendingRemove
.back();
333 vector
<uint32
>::iterator it
= find(InVision
.begin(), InVision
.end(), entityRawIndex
);
334 if (it
!= InVision
.end())
337 PendingRemove
.pop_back();
338 Rmv
.push_back (entityRawIndex
);
340 Entites
[entityRawIndex
].Flags
&= (~CEntityEntry::Pending
);
342 --pendingRemoveCount
;
346 StillToAdd
= (uint32
)PendingAdd
.size();
347 StillToRemove
= (uint32
)PendingRemove
.size();
348 CurrentlyInVision
= (uint32
)InVision
.size();
350 if (StartOffset
>= InVision
.size())
353 uint entity
= StartOffset
;
354 uint pendingPosCount
= (posTotalSize
/posSize
);
355 uint pendingMiscPropCount
= (miscPropTotalSize
/posSize
);
356 while ((pendingPosCount
> 0 || pendingMiscPropCount
> 0) && !InVision
.empty())
359 uint32 entityRawIndex
= InVision
[entity
];
360 if ((Entites
[entityRawIndex
].Flags
& CEntityEntry::PosDirty
) != 0 && (Entites
[entityRawIndex
].Flags
& CEntityEntry::Pending
) == 0)
362 TDataSetRow entityIndex
= TDataSetRow::createFromRawIndex (entityRawIndex
);
363 CMirrorPropValueRO
<TYPE_POSX
> valueX( TheDataset
, entityIndex
, DSPropertyPOSX
);
364 CMirrorPropValueRO
<TYPE_POSY
> valueY( TheDataset
, entityIndex
, DSPropertyPOSY
);
365 CMirrorPropValueRO
<TYPE_ORIENTATION
> valueT( TheDataset
, entityIndex
, DSPropertyORIENTATION
);
367 CMonitorClient::CPosData posData
;
368 posData
.X
= (float)valueX
/ 1000.f
;
369 posData
.Y
= (float)valueY
/ 1000.f
;
370 posData
.Id
= entityRawIndex
;
371 posData
.Tetha
= valueT
;
372 Pos
.push_back (posData
);
374 Entites
[entityRawIndex
].Flags
&= (~CEntityEntry::PosDirty
);
379 if ((Entites
[entityRawIndex
].Flags
& CEntityEntry::MiscPropDirty
) != 0 && (Entites
[entityRawIndex
].Flags
& CEntityEntry::Pending
) == 0)
381 TDataSetRow entityIndex
= TDataSetRow::createFromRawIndex (entityRawIndex
);
382 CMirrorPropValueRO
<TYPE_CURRENT_HIT_POINTS
> valueCurrentHP( TheDataset
, entityIndex
, DSPropertyCURRENT_HIT_POINTS
);
383 CMirrorPropValueRO
<TYPE_MAX_HIT_POINTS
> valueMaxHP( TheDataset
, entityIndex
, DSPropertyMAX_HIT_POINTS
);
384 CMirrorPropValueRO
<TYPE_MODE
> valueMode( TheDataset
, entityIndex
, DSPropertyMODE
);
385 CMirrorPropValueRO
<TYPE_BEHAVIOUR
> valueBehaviour( TheDataset
, entityIndex
, DSPropertyBEHAVIOUR
);
387 CMonitorClient::CMiscPropData miscPropData
;
388 miscPropData
.Id
= entityRawIndex
;
389 miscPropData
.CurrentHP
= valueCurrentHP
;
390 miscPropData
.MaxHP
= valueMaxHP
;
391 miscPropData
.Mode
= (uint8
) valueMode
;
392 miscPropData
.Behaviour
= (uint8
) valueBehaviour
;
393 MiscProp
.push_back (miscPropData
);
395 Entites
[entityRawIndex
].Flags
&= (~CEntityEntry::MiscPropDirty
);
397 --pendingMiscPropCount
;
402 if (entity
>= InVision
.size())
404 if (entity
== StartOffset
)
408 StartOffset
= entity
;
410 // Send a ADD message
414 msgout
.setType("ADD");
415 // version 1 : added sheet ID of the entity
416 msgout
.serialVersion(1);
417 uint32 count
= (uint32
)Add
.size();
418 msgout
.serial(count
);
420 for (i
=0; i
<count
; i
++)
422 msgout
.serial (Add
[i
].Id
);
423 msgout
.serial (Add
[i
].StringId
);
424 msgout
.serial (Add
[i
].EntityId
);
425 msgout
.serial (Add
[i
].SheetId
);
427 Entites
[Add
[i
].Id
].Flags
|= CEntityEntry::Present
;
429 Server
->send(msgout
, getSock());
433 // Send a RMV message
437 msgout
.setType("RMV");
438 msgout
.serialVersion(0);
439 uint32 count
= (uint32
)Rmv
.size();
440 msgout
.serial(count
);
442 for (i
=0; i
<count
; i
++)
444 msgout
.serial (Rmv
[i
]);
445 Entites
[Rmv
[i
]].Flags
&= ~CEntityEntry::Present
;
447 Server
->send(msgout
, getSock());
451 // Send a POS message
455 msgout
.setType("POS");
456 msgout
.serialVersion(0);
457 uint32 count
= (uint32
)Pos
.size();
458 msgout
.serial(count
);
460 for (i
=0; i
<count
; i
++)
462 msgout
.serial (Pos
[i
].Id
);
463 msgout
.serial (Pos
[i
].X
);
464 msgout
.serial (Pos
[i
].Y
);
465 msgout
.serial (Pos
[i
].Tetha
);
467 Server
->send(msgout
, getSock());
471 // Send a MISC_PROP message
472 if (!MiscProp
.empty())
475 msgout
.setType("MISC_PROP");
476 msgout
.serialVersion(0);
477 uint32 count
= (uint32
)MiscProp
.size();
478 msgout
.serial(count
);
480 for (i
=0; i
<count
; i
++)
482 msgout
.serial (MiscProp
[i
].Id
);
483 msgout
.serial (MiscProp
[i
].CurrentHP
);
484 msgout
.serial (MiscProp
[i
].MaxHP
);
485 msgout
.serial (MiscProp
[i
].Mode
);
486 msgout
.serial (MiscProp
[i
].Behaviour
);
488 Server
->send(msgout
, getSock());
494 if (!Str
.empty() && strTotalSize
> 0)
497 msgout
.setType("STR");
498 msgout
.serialVersion(0);
500 uint sentStrSize
= 0;
502 for (i
=0; i
<strToSend
.size() && sentStrSize
< strTotalSize
; ++i
)
503 sentStrSize
+= 4+4+(uint
)(strToSend
[i
].second
)->size();
506 msgout
.serial(count
);
507 for (i
=0; i
<count
; i
++)
509 msgout
.serial(strToSend
[i
].first
);
510 msgout
.serial(*(strToSend
[i
].second
));
513 Str
.erase(Str
.begin(), Str
.begin()+count
);
515 Server
->send(msgout
, getSock());
520 // ***************************************************************************