1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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/>.
21 #include "nel/misc/debug.h"
23 #include "packet_history.h"
24 #include "property_history.h"
25 #include "game_share/action.h"
27 #include "frontend_service.h"
28 #include "vision_array.h"
30 using namespace CLFECOMMON
;
35 CPacketHistory::CPacketHistory()
40 void CPacketHistory::clear()
42 uint32 num
= (uint32
)_ClientsHistories
.size();
43 _ClientsHistories
.clear();
44 _ClientsHistories
.resize(num
);
47 void CPacketHistory::setMaximumClient(uint maxClient
)
49 _ClientsHistories
.resize(maxClient
);
55 void CPacketHistory::store(TClientId clientId, uint packetNumber, CLFECOMMON::CAction *action)
57 // nlassert(clientId < _ClientsHistories.size() && _ClientsHistories[clientId].EntryUsed);
59 // get the packet queue of the client
60 TPacketQueue &queue = _ClientsHistories[clientId].Queue;
62 nlassert(queue.empty() || packetNumber >= queue.back().Number);
64 if (queue.empty() || packetNumber>queue.back().Number)
66 queue.push_back(CPacketEntry());
67 queue.back().Number = packetNumber;
70 queue.back().Packet.push_back(CMessageEntry(action->Slot, action->PropertyCode));
74 void CPacketHistory::storeDisassociation(TClientId clientId
, CLFECOMMON::TCLEntityId slot
, uint packetNumber
)
76 // get the packet queue of the client
77 TPacketQueue
&queue
= _ClientsHistories
[clientId
].Queue
;
79 nlassert(queue
.empty() || packetNumber
>= queue
.back().Number
);
81 if (queue
.empty() || packetNumber
>queue
.back().Number
)
83 queue
.push_back(CPacketEntry());
84 queue
.back().Number
= packetNumber
;
87 queue
.back().Packet
.push_back(CMessageEntry(slot
, PROPERTY_DISASSOCIATION
));
93 void CPacketHistory::ack(TClientId clientId
, uint32 packet
, uint32 bits
, uint ackBitWidth
)
95 //nlassert(clientId < _ClientsHistories.size() && _ClientsHistories[clientId].EntryUsed);
97 TPacketQueue
&queue
= _ClientsHistories
[clientId
].Queue
;
102 uint32 firstAck
= (sint32
)(queue
.front().Number
);
104 // nothing to ack ? just leave...
105 if (packet
< firstAck
)
108 uint totalUnreceived
= packet
-firstAck
;
109 uint totalRecoverable
= std::min(totalUnreceived
, ackBitWidth
);
110 uint firstRecoverable
= packet
-totalRecoverable
;
113 // negAck all unrecoverable acks
114 while (!queue
.empty() && queue
.front().Number
<firstRecoverable
)
116 uint thisPacket
= queue
.front().Number
;
117 CPacket
&apacket
= queue
.front().Packet
;
118 for (j
=0; j
<apacket
.size(); ++j
)
119 negativeAck(clientId
, apacket
[j
].EntityId
, apacket
[j
].PropIndex
, thisPacket
);
123 // ack all recoverable acks depending on the bit
124 while (!queue
.empty() && queue
.front().Number
<packet
)
126 uint thisPacket
= queue
.front().Number
;
127 uint bitNum
= packet
-queue
.front().Number
-1;
129 CPacket
&apacket
= queue
.front().Packet
;
130 if (bits
& (1<<bitNum
))
131 for (j
=0; j
<apacket
.size(); ++j
)
132 positiveAck(clientId
, apacket
[j
].EntityId
, apacket
[j
].PropIndex
, thisPacket
);
134 for (j
=0; j
<apacket
.size(); ++j
)
135 negativeAck(clientId
, apacket
[j
].EntityId
, apacket
[j
].PropIndex
, thisPacket
);
140 // ack current packet
141 if (queue
.empty() || queue
.front().Number
>packet
)
144 nlassert(!queue
.empty() && queue
.front().Number
== packet
);
146 CPacket
&apacket
= queue
.front().Packet
;
147 for (j
=0; j
<apacket
.size(); ++j
)
148 positiveAck(clientId
, apacket
[j
].EntityId
, apacket
[j
].PropIndex
, packet
);
152 void CPacketHistory::ack(TClientId clientId
, uint32 packet
, bool ackvalue
)
154 TPacketQueue
&queue
= _ClientsHistories
[clientId
].Queue
;
156 while (!queue
.empty() && queue
.front().Number
<packet
)
158 nlwarning("FEPKHIST: removed a packet awaiting ack (client %d, packet %d, packet %d being acked)", clientId
, queue
.front().Number
, packet
);
162 if (!queue
.empty() && queue
.front().Number
==packet
)
164 uint thisPacket
= queue
.front().Number
;
166 CPacket
&apacket
= queue
.front().Packet
;
168 for (j
=0; j
<apacket
.size(); ++j
)
169 positiveAck(clientId
, apacket
[j
].EntityId
, apacket
[j
].PropIndex
, thisPacket
);
171 for (j
=0; j
<apacket
.size(); ++j
)
172 negativeAck(clientId
, apacket
[j
].EntityId
, apacket
[j
].PropIndex
, thisPacket
);
178 void CPacketHistory::positiveAck(TClientId clientId
, TCLEntityId entityId
, TPropIndex propindex
, uint32 packet
)
180 /*TPropState& propstate = CFrontEndService::instance()->PrioSub.VisionArray.propState( clientId, entityId, propindex );
181 if ( propstate.UpdateStatus == Updating )
182 propstate.UpdateStatus = Unchanged;*/
184 //if (_PropertyHistory->isContinuousProperty(propertyId))
185 // _PropertyHistory->ackProperty(clientId, entityId, packet, propertyId);
188 void CPacketHistory::negativeAck(TClientId clientId
, TCLEntityId entityId
, TPropIndex propindex
, uint32 packet
)
190 TPairState
& pairState
= CFrontEndService::instance()->PrioSub
.VisionArray
.getPairState( clientId
, entityId
);
192 // Empty the history for each property (to force the sending of the current value)
193 if ( _PropertyHistory
->resetValue( clientId
, entityId
, propindex
, (uint8
)pairState
.AssociationChangeBits
) )
195 //if ( pairState.AssociationChangeBits & 0x80 == 0 )
198 LOG_PACKET_LOST( "%u: Reverting prio for C%hu S%hu (packet %u)", CTickEventHandler::getGameCycle(), clientId
, (uint16
)entityId
, packet
);
199 pairState
.revertPrio();
201 // Set "negative-acked" bit
202 //pairState.AssociationChangeBits |= 0x80;
205 else // association has changed
207 if ( pairState
.associationSuppressed() )
209 // Set high priority for whole slot (all properties at once)
210 LOG_PACKET_LOST( "%u: Reverting prio for disassociation C%hu S%hu (packet %u)", CTickEventHandler::getGameCycle(), clientId
, (uint16
)entityId
, packet
);
212 //pairState.revertPrio();
213 // Set "negative-acked" bit to resend at least the disassociation
214 //pairState.AssociationChangeBits |= 0x80;
215 pairState
.PrevAssociationBits
= pairState
.AssociationChangeBits
- 1; // force them different
217 CFrontEndService::instance()->PrioSub
.Prioritizer
.pushDissociationToResend( clientId
, entityId
);
224 void CPacketHistory::addClient(TClientId clientId
)
226 //nlassert(!_ClientsHistories[clientId].EntryUsed);
228 _ClientsHistories
[clientId
].EntryUsed
= true;
229 _ClientsHistories
[clientId
].Queue
.clear();
232 void CPacketHistory::removeClient(TClientId clientId
)
234 //nlassert(_ClientsHistories[clientId].EntryUsed);
236 _ClientsHistories
[clientId
].EntryUsed
= false;
237 _ClientsHistories
[clientId
].Queue
.clear();
240 void CPacketHistory::resetClient(TClientId clientId
)
242 //nlassert(_ClientsHistories[clientId].EntryUsed);
243 _ClientsHistories
[clientId
].Queue
.clear();
246 bool CPacketHistory::isValidClient(TClientId clientId
)
248 return (clientId
< _ClientsHistories
.size() && _ClientsHistories
[clientId
].EntryUsed
);