Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
947 lines
24 KiB
C++
947 lines
24 KiB
C++
//===========================================================================//
|
|
// File: Dictionary.cpp
|
|
//---------------------------------------------------------------------------//
|
|
// Date Who Modification //
|
|
// -------- --- ---------------------------------------------------------- //
|
|
// 01/06/1999 JSE Inital coding
|
|
//---------------------------------------------------------------------------//
|
|
// Copyright (C) 1998, Microsoft Corp. //
|
|
// All Rights reserved worldwide //
|
|
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
|
//===========================================================================//
|
|
|
|
|
|
#include "MW4Headers.hpp"
|
|
|
|
#include "Dictionary.hpp"
|
|
#include "MWEntityManager.hpp"
|
|
#include "NetUpdateManager.hpp"
|
|
#include "MWApplication.hpp"
|
|
#include "Adept\NetStatCollector.hpp"
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
Scalar OneCycle = 2.0f;
|
|
|
|
Scalar HerzPreSec[DictionaryPage::UpdateCycleCount] =
|
|
{
|
|
0.5f, //double of the hrz since we are in a 2 second cycle..
|
|
1.0f,
|
|
2.0f,
|
|
3.0f,
|
|
4.0f,
|
|
6.0f,
|
|
8.0f,
|
|
9.0f,
|
|
12.0f,
|
|
16.0f
|
|
};
|
|
|
|
|
|
int Translation_Table[DictionaryPage::UpdateCycleCount] =
|
|
{
|
|
DictionaryPage::HalfhzType,
|
|
DictionaryPage::OneHzType,
|
|
DictionaryPage::TwoHzType,
|
|
DictionaryPage::ThreeHzType,
|
|
DictionaryPage::FourHzType,
|
|
DictionaryPage::SixHzType,
|
|
DictionaryPage::EightHzType,
|
|
DictionaryPage::NineHzType,
|
|
DictionaryPage::TwelveHzType,
|
|
DictionaryPage::SixteenHzType
|
|
};
|
|
|
|
|
|
|
|
//##########################################################################
|
|
//########################### DictionaryParagraph
|
|
//##########################################################################
|
|
|
|
DictionaryParagraph::DictionaryParagraph():
|
|
Plug(DefaultData),
|
|
entitySlot(NULL)
|
|
{
|
|
activeUpdateCycle = 0x0;
|
|
inactiveUpdateCycle = 0x0;
|
|
active = true;
|
|
activeCycle = -1;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
DictionaryParagraph::DictionaryParagraph(Entity *entity, int message_type, int active_update_cycle, int inactive_update_cycle):
|
|
Plug(DefaultData),
|
|
entitySlot(NULL)
|
|
{
|
|
Check_Object(entity);
|
|
entitySlot.Add(entity);
|
|
|
|
NetVerify(message_type > UpdateManager::BaseUpdateID);
|
|
NetVerify(message_type < UpdateManager::UpdateCount);
|
|
|
|
messageType = message_type;
|
|
activeUpdateCycle = active_update_cycle;
|
|
inactiveUpdateCycle = inactive_update_cycle;
|
|
active = true;
|
|
activeCycle = 0;
|
|
|
|
entityVariableMessageSize = -1;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
DictionaryParagraph::~DictionaryParagraph()
|
|
{
|
|
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
void DictionaryParagraph::Encode(Stuff::DynamicMemoryStream* memory_stream, int connection, int debug_level, int bandwidth)
|
|
{
|
|
Check_Object(memory_stream);
|
|
|
|
Entity *entity = entitySlot.GetCurrent();
|
|
Check_Object(entity);
|
|
|
|
ReplicatorID rep_id = entity->GetReplicatorID();
|
|
|
|
|
|
|
|
UpdateEntry *update = &UpdateManager::UpdateEntries[messageType];
|
|
|
|
(update->m_encode)(entity, memory_stream, connection, debug_level, bandwidth);
|
|
|
|
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
void DictionaryParagraph::Decode(Stuff::MemoryStream* memory_stream, Time till, Scalar packet_latency, int connection, int debug_level, int bandwidth)
|
|
{
|
|
Check_Object(memory_stream);
|
|
|
|
Entity *entity = entitySlot.GetCurrent();
|
|
|
|
if (entity != NULL)
|
|
{
|
|
Check_Object(entity);
|
|
|
|
ReplicatorID rep_id = entity->GetReplicatorID();
|
|
|
|
UpdateEntry *update = &UpdateManager::UpdateEntries[messageType];
|
|
|
|
int rate_type = BitDepthManager::GetUpdateRate(update->m_updateID, bandwidth).m_ActiveRate;
|
|
Scalar rate = DictionaryPage::CalculateSeconds(rate_type);
|
|
|
|
(*update->m_decode)(entity, memory_stream, till, packet_latency, connection, debug_level, bandwidth, rate);
|
|
}
|
|
else
|
|
{
|
|
UpdateEntry *update = &UpdateManager::UpdateEntries[messageType];
|
|
(*update->m_skip)(memory_stream, entityVariableMessageSize, till, connection, debug_level, bandwidth);
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
|
|
bool DictionaryParagraph::ReadyToUpdate(int cycles_to_update)
|
|
{
|
|
if (active)
|
|
{
|
|
if (activeUpdateCycle & cycles_to_update)
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (inactiveUpdateCycle & cycles_to_update)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
//##########################################################################
|
|
//########################### DictionaryPage
|
|
//##########################################################################
|
|
|
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
DictionaryPage::DictionaryPage(int page_number):
|
|
Plug(DefaultData),
|
|
dictionaryParagraphs(NULL)
|
|
{
|
|
|
|
pageNumber = page_number;
|
|
paragraphCount = 0;
|
|
usedUpdateCycle = 0x0;
|
|
lastExecuted = -2.0f;
|
|
timeFromLastSlowestUpdate = 0.0f;
|
|
lastPacketNumber = 0;
|
|
|
|
for (int i = 0; i < UpdateCycleCount; ++i)
|
|
{
|
|
currentCycleCount[i] = 0;
|
|
}
|
|
|
|
|
|
if (pageNumber >= MaximumPages)
|
|
STOP(("DictionaryPage::DictionaryPage:TOO MANY PAGES"));
|
|
|
|
NetVerify(pageNumber > -1);
|
|
NetVerify(pageNumber < MaximumPages);
|
|
|
|
|
|
Time current_time = gos_GetElapsedTime();
|
|
for (i = 0; i < 8; ++i)
|
|
{
|
|
activeCycles[i] = current_time;
|
|
current_time += 0.25;
|
|
}
|
|
currentActiveCycle = 0;
|
|
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
DictionaryPage::~DictionaryPage()
|
|
{
|
|
Stuff::ChainIteratorOf<DictionaryParagraph*> iterator(&dictionaryParagraphs);
|
|
Stuff::Plug *plug;
|
|
while ((plug = iterator.GetCurrent()) != NULL)
|
|
{
|
|
iterator.Remove();
|
|
Check_Object(plug);
|
|
delete plug;
|
|
}
|
|
}
|
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
void DictionaryPage::Encode(Stuff::DynamicMemoryStream* memory_stream, int connection, int debug_level, int bandwidth)
|
|
{
|
|
Check_Object(memory_stream);
|
|
Stuff::ChainIteratorOf<DictionaryParagraph*> iterator(&dictionaryParagraphs);
|
|
|
|
|
|
int paragraph_count = 0;
|
|
|
|
|
|
++lastPacketNumber;
|
|
|
|
if (lastPacketNumber == 255)
|
|
lastPacketNumber = 0;
|
|
|
|
|
|
MWEntityManager *manager = Cast_Object(MWEntityManager*, EntityManager::GetInstance());
|
|
NetVerify(manager->serverController != NULL);
|
|
Scalar current_time = (float)(gos_GetElapsedTime(1) * 1000.0);
|
|
|
|
memory_stream->WriteBits(&lastPacketNumber, 8);
|
|
memory_stream->WriteBits(&manager->serverController->lastPingDelta[connection], 32);
|
|
memory_stream->WriteBits(¤t_time, 32);
|
|
|
|
//SPEWALWAYS(("jerryeds", "S-SEND - %d : t2-t1 : %f, t3 : %f", lastPacketNumber, manager->serverController->lastPingDelta[connection], current_time));
|
|
|
|
|
|
//SPEW(("jerryeds", "ENCODE: packet %d", lastPacketNumber));
|
|
//SPEW(("jerryeds", "ENCODE: t1 %d", manager->serverController->lastPingDelta[connection]));
|
|
//SPEW(("jerryeds", "ENCODE: t2 %d", current_time));
|
|
|
|
|
|
|
|
if (debug_level > 1)
|
|
{
|
|
BYTE magic_number = 0xAA;
|
|
memory_stream->WriteBits(&magic_number, 8);
|
|
//SPEW(("jerryeds", "ENCODE: MAGIC"));
|
|
|
|
Network::GetInstance()->netStatCollector[Network::OutgoingHeaderBitSizeStat].AddToStatistic(connection,8);
|
|
}
|
|
|
|
|
|
DictionaryParagraph *paragraph;
|
|
|
|
while ((paragraph = iterator.ReadAndNext()) != NULL)
|
|
{
|
|
|
|
if (paragraph->ReadyToUpdate(cyclesToUpdate))
|
|
{
|
|
|
|
memory_stream->WriteBit(true);
|
|
paragraph->Encode(memory_stream, connection, debug_level, bandwidth);
|
|
}
|
|
else
|
|
{
|
|
memory_stream->WriteBit(false);
|
|
}
|
|
|
|
if (debug_level > 1)
|
|
{
|
|
memory_stream->WriteBits(¶graph_count, 8);
|
|
Network::GetInstance()->netStatCollector[Network::OutgoingHeaderBitSizeStat].AddToStatistic(connection,8);
|
|
|
|
}
|
|
|
|
++paragraph_count;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
if (debug_level > 0)
|
|
{
|
|
BYTE magic_number = 0xAA;
|
|
memory_stream->WriteBits(&magic_number, 8);
|
|
Network::GetInstance()->netStatCollector[Network::OutgoingHeaderBitSizeStat].AddToStatistic(connection,8);
|
|
|
|
}
|
|
|
|
Network::GetInstance()->netStatCollector[Network::OutgoingHeaderBitSizeStat].AddToStatistic(connection,paragraph_count + 8);
|
|
Network::GetInstance()->netStatCollector[Network::OutgoingHeaderBitSizeStat].AddToStatistic(connection,64);
|
|
|
|
|
|
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
void DictionaryPage::Decode(Stuff::MemoryStream* memory_stream, Time till, int connection, int debug_level, int bandwidth)
|
|
{
|
|
Check_Object(memory_stream);
|
|
|
|
// throw out old ones
|
|
|
|
BYTE packet_number;
|
|
Scalar t2_minus_t1;
|
|
Scalar t3;
|
|
|
|
memory_stream->ReadBits(&packet_number, 8);
|
|
memory_stream->ReadBits(&t2_minus_t1, 32);
|
|
memory_stream->ReadBits(&t3, 32);
|
|
|
|
//SPEW(("jerryeds", "DECODE: packet %d", packet_number));
|
|
//SPEW(("jerryeds", "DECODE: t1 %d", t2_minus_t1));
|
|
//SPEW(("jerryeds", "DECODE: t2 %d", t3));
|
|
|
|
|
|
if (debug_level > 1)
|
|
{
|
|
BYTE magic_number = 0;
|
|
memory_stream->ReadBits(&magic_number, 8);
|
|
Network::GetInstance()->netStatCollector[Network::IncomingHeaderBitSizeStat].AddToStatistic(connection,8);
|
|
//SPEW(("jerryeds", "DECODE: MAGIC"));
|
|
|
|
if(magic_number != 0xAA)
|
|
{
|
|
STOP(("UPDATE HEADER PAGE READ ERROR: %d",pageNumber));
|
|
}
|
|
}
|
|
|
|
|
|
//SPEWALWAYS(("jerryeds", "DICTIONARY LATENCY : %f", packet_latency));
|
|
|
|
if (MWEntityManager::IsNumberBehind(packet_number, lastPacketNumber, 40, 255))
|
|
{
|
|
#if defined(LAB_ONLY)
|
|
SPEWALWAYS(("jerryeds", "LATE DICTIONARY PACKET THROWN OUT"));
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
MWEntityManager *manager = Cast_Object(MWEntityManager*, EntityManager::GetInstance());
|
|
Scalar t4 = (float)(gos_GetElapsedTime(1) * 1000.0);
|
|
manager->clientController->lastPingDelta = t4 - t3;
|
|
MWApplication::GetInstance()->servedConnectionData[connection].effectivePing = t2_minus_t1 + (t4 - t3);
|
|
|
|
//SPEWALWAYS(("jerryeds", "C-REC - %d : t2-t1 : %f, t4-t3 %f : t3 : %f t4 : %f", packet_number, t2_minus_t1, t4 - t3, t3, t4));
|
|
|
|
|
|
Scalar packet_latency = MWApplication::GetInstance()->servedConnectionData[connection].effectivePing * 0.5f;
|
|
Min_Clamp(packet_latency, 1.0f);
|
|
packet_latency *= 0.001f;
|
|
|
|
|
|
//SPEWALWAYS(("jerryeds", "latency : %f\n", packet_latency));
|
|
|
|
lastPacketNumber = packet_number;
|
|
lastExecuted = till;
|
|
|
|
Stuff::ChainIteratorOf<DictionaryParagraph*> iterator(&dictionaryParagraphs);
|
|
|
|
int paragraph_count = 0;
|
|
|
|
|
|
DictionaryParagraph *paragraph;
|
|
while ((paragraph = iterator.ReadAndNext()) != NULL)
|
|
{
|
|
bool noskip_bit;
|
|
memory_stream->ReadBit(noskip_bit);
|
|
if (noskip_bit)
|
|
paragraph->Decode(memory_stream, till, packet_latency, connection, debug_level, bandwidth);
|
|
|
|
|
|
if (debug_level > 1)
|
|
{
|
|
int debug_paragraph_count = 0;
|
|
memory_stream->ReadBits(&debug_paragraph_count, 8);
|
|
|
|
Network::GetInstance()->netStatCollector[Network::IncomingHeaderBitSizeStat].AddToStatistic(connection,8);
|
|
|
|
if (debug_paragraph_count != paragraph_count)
|
|
{
|
|
Entity *entity = paragraph->entitySlot.GetCurrent();
|
|
|
|
if (entity != NULL && entity->instanceName)
|
|
{
|
|
STOP(("UPDATE PARAGRAPH READ ERROR: %s : Page %d, Paragraph %d, Type %d", (char*)entity->instanceName, pageNumber, paragraph_count, paragraph->messageType));
|
|
}
|
|
else
|
|
{
|
|
STOP(("UPDATE PARAGRAPH READ ERROR: NO ENTITY : Page %d, Paragraph %d, Type %d", pageNumber, paragraph_count, paragraph->messageType));
|
|
}
|
|
}
|
|
}
|
|
|
|
++paragraph_count;
|
|
|
|
}
|
|
|
|
|
|
if (debug_level > 0)
|
|
{
|
|
BYTE magic_number;
|
|
memory_stream->ReadBits(&magic_number, 8);
|
|
Network::GetInstance()->netStatCollector[Network::IncomingHeaderBitSizeStat].AddToStatistic(connection,8);
|
|
|
|
if(magic_number != 0xAA)
|
|
{
|
|
STOP(("UPDATE PAGE READ ERROR: %d",pageNumber));
|
|
}
|
|
}
|
|
|
|
Network::GetInstance()->netStatCollector[Network::IncomingHeaderBitSizeStat].AddToStatistic(connection,paragraph_count+8);
|
|
Network::GetInstance()->netStatCollector[Network::IncomingHeaderBitSizeStat].AddToStatistic(connection,64);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Scalar DictionaryPage::CalculateSeconds(int type)
|
|
{
|
|
Verify(type >= 0);
|
|
Verify(type <= UpdateCycleCount);
|
|
return 1.0f / HerzPreSec[type];
|
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
int DictionaryPage::CalculateCycles(Time till)
|
|
{
|
|
|
|
Scalar time_slice = static_cast<Stuff::Scalar>(till - lastExecuted);
|
|
lastExecuted = till;
|
|
|
|
//increment the time from the last full cycle.
|
|
//The full cycle is OneCycle, which is also the
|
|
//slowest cycle.
|
|
timeFromLastSlowestUpdate += time_slice;
|
|
|
|
|
|
// calculate the carryover if there is any.
|
|
Scalar carryover = 0;
|
|
int carryover_count = 0;
|
|
|
|
if (timeFromLastSlowestUpdate > OneCycle)
|
|
{
|
|
// how many cycles it carried over
|
|
carryover_count = (int)(timeFromLastSlowestUpdate / OneCycle);
|
|
// how far past the last cycle it carried over
|
|
carryover = timeFromLastSlowestUpdate - (carryover_count * OneCycle);
|
|
}
|
|
|
|
// calculate the cycle counts
|
|
int cycle_triggers = 0x0;
|
|
for (int i = 0; i < UpdateCycleCount; ++i)
|
|
{
|
|
// this is how many times this cycle should have triggered
|
|
int cycles_triggered = (int)(timeFromLastSlowestUpdate*HerzPreSec[i]);
|
|
|
|
|
|
// if we haven't triggered that many times than trigger one
|
|
if (cycles_triggered > currentCycleCount[i])
|
|
{
|
|
|
|
|
|
// add the cycle to the bit mask
|
|
cycle_triggers |= (0x1<<i);
|
|
|
|
// we only want to update once so
|
|
// rather than increment we just go ahead
|
|
// and equal up to how many we should have
|
|
// updated.
|
|
currentCycleCount[i] = cycles_triggered;
|
|
}
|
|
|
|
// subtract the carryover if there is any
|
|
// if not the carryover_count will be zero and
|
|
// nothing will happen.
|
|
currentCycleCount[i] -= (int)(carryover_count * HerzPreSec[i] * OneCycle);
|
|
|
|
}
|
|
|
|
|
|
if (carryover_count)
|
|
{
|
|
timeFromLastSlowestUpdate = carryover;
|
|
}
|
|
|
|
|
|
return cycle_triggers;
|
|
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
bool DictionaryPage::MaintainCycles(Time till, int connection_id)
|
|
{
|
|
|
|
Time largest_time = activeCycles[0];
|
|
|
|
bool flip[8];
|
|
|
|
bool req_update = false;
|
|
|
|
Point3D cull_location;
|
|
cull_location = MWApplication::GetInstance()->servedConnectionData[connection_id].vehiclePosition;
|
|
|
|
|
|
// reset the counters
|
|
for (int i = 0; i < 8; ++i)
|
|
{
|
|
flip[i] = false;
|
|
|
|
if (till - activeCycles[i] > 2.0)
|
|
{
|
|
flip[i] = true;
|
|
activeCycles[i] += 2.0;
|
|
// deactivate any cylces that are on this cycle..
|
|
|
|
|
|
// if this is the largest time to flip this frame
|
|
// than this is the currentactivecycle.
|
|
if (largest_time < activeCycles[i])
|
|
{
|
|
largest_time = activeCycles[i];
|
|
currentActiveCycle = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// maintain the paragraphs active states
|
|
DictionaryParagraph *paragraph;
|
|
Stuff::ChainIteratorOf<DictionaryParagraph*> iterator(&dictionaryParagraphs);
|
|
while ((paragraph = iterator.ReadAndNext()) != NULL)
|
|
{
|
|
if (paragraph->active && flip[paragraph->activeCycle])
|
|
{
|
|
paragraph->active = false;
|
|
paragraph->activeCycle = -1;
|
|
}
|
|
|
|
UpdateEntry *update = &UpdateManager::UpdateEntries[paragraph->messageType];
|
|
if ((update->m_maintainActiveFlag)(paragraph->entitySlot.GetCurrent(), cull_location))
|
|
{
|
|
paragraph->active = true;
|
|
paragraph->activeCycle = currentActiveCycle;
|
|
}
|
|
|
|
if (paragraph->ReadyToUpdate(cyclesToUpdate))
|
|
{
|
|
req_update = true;
|
|
}
|
|
|
|
}
|
|
|
|
return req_update;
|
|
|
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
bool
|
|
DictionaryPage::RequireUpdate(Time new_time, int connection_id)
|
|
{
|
|
|
|
cyclesToUpdate = CalculateCycles(new_time);
|
|
bool require_update = MaintainCycles(new_time, connection_id);
|
|
|
|
return require_update;
|
|
}
|
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
DictionaryParagraph *
|
|
DictionaryPage::MakeDictionaryParagraph(Entity *entity, int message_type, UpdateRate update_cycle)
|
|
{
|
|
|
|
usedUpdateCycle |= Translation_Table[update_cycle.m_ActiveRate];
|
|
usedUpdateCycle |= Translation_Table[update_cycle.m_InactiveRate];
|
|
|
|
DictionaryParagraph *paragraph = new DictionaryParagraph(entity, message_type, Translation_Table[update_cycle.m_ActiveRate], Translation_Table[update_cycle.m_InactiveRate]);
|
|
Check_Object(paragraph);
|
|
|
|
dictionaryParagraphs.Add(paragraph);
|
|
|
|
++paragraphCount;
|
|
|
|
//NetVerify(paragraphCount < 64);
|
|
|
|
return paragraph;
|
|
|
|
}
|
|
|
|
|
|
//##########################################################################
|
|
//########################### Dictionary
|
|
//##########################################################################
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
Dictionary::Dictionary(int version_number, int connection, int debug_level, int bandwidth):
|
|
Plug(DefaultData),
|
|
dictionaryPages(NULL, true)
|
|
{
|
|
versionNumber = version_number;
|
|
connectionID = connection;
|
|
|
|
debugDictionaryLevel = debug_level;
|
|
dictionaryBandwidth = bandwidth;
|
|
|
|
|
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Dictionary::Dictionary(int version_number, int connection):
|
|
Plug(DefaultData),
|
|
dictionaryPages(NULL, true)
|
|
{
|
|
versionNumber = version_number;
|
|
connectionID = connection;
|
|
|
|
debugDictionaryLevel = -1;
|
|
dictionaryBandwidth = -1;
|
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Dictionary::~Dictionary()
|
|
{
|
|
Stuff::SortedChainIteratorOf<DictionaryPage*, int> iterator(&dictionaryPages);
|
|
Stuff::Plug *plug;
|
|
while ((plug = iterator.GetCurrent()) != NULL)
|
|
{
|
|
iterator.Remove();
|
|
Check_Object(plug);
|
|
delete plug;
|
|
}
|
|
}
|
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
void
|
|
Dictionary::Encode(Time till)
|
|
{
|
|
|
|
Stuff::SortedChainIteratorOf<DictionaryPage*, int> iterator(&dictionaryPages);
|
|
DictionaryPage *page;
|
|
while ((page = iterator.ReadAndNext()) != NULL)
|
|
{
|
|
|
|
if (page->RequireUpdate(till, connectionID))
|
|
{
|
|
|
|
DynamicMemoryStream memory_stream(MWEntityManager::Packet_Buffer, DefaultPacketBufferSize);
|
|
// make stream
|
|
|
|
// write dictionary format
|
|
memory_stream.WriteBits(&versionNumber, 8);
|
|
|
|
//SPEW(("jerryeds", "ENCODE: version %d", versionNumber));
|
|
|
|
|
|
// write page data
|
|
page->Encode(&memory_stream, connectionID, debugDictionaryLevel, dictionaryBandwidth);
|
|
|
|
|
|
memory_stream.WriteByteAlign();
|
|
memory_stream.Rewind();
|
|
|
|
NetVerify(memory_stream.GetBufferBytesUsed() > 0);
|
|
|
|
// send page
|
|
Network::GetInstance()->SendNetMessage(
|
|
connectionID, MWEntityManager::DictionaryPage0MessageID + page->pageNumber, &memory_stream, false);
|
|
|
|
Network::GetInstance()->netStatCollector[Network::OutgoingHeaderBitSizeStat].AddToStatistic(connectionID,4);
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
void
|
|
Dictionary::Decode(int message_type, Stuff::MemoryStream* memory_stream, Time till)
|
|
{
|
|
Check_Object(memory_stream);
|
|
|
|
int version = 0;
|
|
memory_stream->ReadBits(&version, 8);
|
|
|
|
//SPEW(("jerryeds", "DECODE: version %d", version));
|
|
|
|
|
|
Network::GetInstance()->netStatCollector[Network::IncomingHeaderBitSizeStat].AddToStatistic(connectionID,4);
|
|
|
|
if (version != versionNumber)
|
|
return;
|
|
|
|
|
|
Stuff::SortedChainIteratorOf<DictionaryPage*, int> iterator(&dictionaryPages);
|
|
|
|
int page_index = message_type - MWEntityManager::DictionaryPage0MessageID;
|
|
|
|
DictionaryPage *page = iterator.Find(page_index);
|
|
|
|
if (page == NULL)
|
|
STOP(("Dictionary::Decode : Page in dictionary not found"));
|
|
|
|
Check_Object(page);
|
|
|
|
page->Decode(memory_stream, till, Connection::Local->GetID(), debugDictionaryLevel, dictionaryBandwidth);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
DictionaryPage *
|
|
Dictionary::MakeDictionaryPage(int page_number)
|
|
{
|
|
NetVerify(page_number > -1);
|
|
|
|
NetVerify(page_number < MaximumPages);
|
|
|
|
DictionaryPage *page = new DictionaryPage(page_number);
|
|
Check_Object(page);
|
|
|
|
dictionaryPages.AddValue(page, page_number);
|
|
|
|
return page;
|
|
}
|
|
|
|
|
|
//##########################################################################
|
|
//########################### DictionaryManager
|
|
//##########################################################################
|
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
DictionaryManager::DictionaryManager(int connection):
|
|
Plug(DefaultData),
|
|
dictionaryIndex(NULL, true)
|
|
{
|
|
activeDictionaryIndex = -1;
|
|
activeDictionary = NULL;
|
|
connectionID = connection;
|
|
|
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
DictionaryManager::~DictionaryManager()
|
|
{
|
|
Stuff::SortedChainIteratorOf<Dictionary*, int> iterator(&dictionaryIndex);
|
|
Stuff::Plug *plug;
|
|
while ((plug = iterator.GetCurrent()) != NULL)
|
|
{
|
|
iterator.Remove();
|
|
Check_Object(plug);
|
|
delete plug;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
Dictionary *
|
|
DictionaryManager::MakeNewDictionary(int version_number, int debug_level, int bandwidth)
|
|
{
|
|
|
|
// server keeps old copies but the client remove them
|
|
|
|
SortedChainIteratorOf<Dictionary*, int> iterator(&dictionaryIndex);
|
|
|
|
if (!Application::GetInstance()->serverFlag)
|
|
{
|
|
// clients remove all old dictionaries
|
|
Stuff::SortedChainIteratorOf<Dictionary*, int> iterator(&dictionaryIndex);
|
|
Stuff::Plug *plug;
|
|
while ((plug = iterator.GetCurrent()) != NULL)
|
|
{
|
|
iterator.Remove();
|
|
Check_Object(plug);
|
|
delete plug;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// we are the server, we only care about the last one and the new one...
|
|
// remove everything but the old one and the new one...
|
|
|
|
Stuff::SortedChainIteratorOf<Dictionary*, int> iterator(&dictionaryIndex);
|
|
Stuff::Plug *plug;
|
|
while ((plug = iterator.GetCurrent()) != NULL)
|
|
{
|
|
if (iterator.GetValue() != activeDictionaryIndex)
|
|
{
|
|
iterator.Remove();
|
|
Check_Object(plug);
|
|
delete plug;
|
|
}
|
|
else
|
|
{
|
|
iterator.Next();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
activeDictionaryIndex = version_number;
|
|
|
|
activeDictionary = new Dictionary(activeDictionaryIndex, connectionID, debug_level, bandwidth);
|
|
Check_Object(activeDictionary);
|
|
|
|
Dictionary* test = dictionaryIndex.Find(activeDictionaryIndex);
|
|
if (test != NULL)
|
|
{
|
|
Unregister_Object(test);
|
|
delete (test);
|
|
}
|
|
|
|
dictionaryIndex.AddValue(activeDictionary, activeDictionaryIndex);
|
|
|
|
return activeDictionary;
|
|
|
|
}
|
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
int
|
|
DictionaryManager::GetNextDictionaryID()
|
|
{
|
|
int next_dictionary = activeDictionaryIndex;
|
|
|
|
if (next_dictionary == 254)
|
|
{
|
|
next_dictionary = 0;
|
|
|
|
}
|
|
else
|
|
{
|
|
++next_dictionary;
|
|
}
|
|
|
|
return next_dictionary;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
Dictionary *
|
|
DictionaryManager::GetCurrentDictionary()
|
|
{
|
|
NetVerify(activeDictionaryIndex >= 0);
|
|
Check_Object(activeDictionary);
|
|
|
|
return activeDictionary;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
Dictionary *
|
|
DictionaryManager::GetDictionary(int version)
|
|
{
|
|
// if it is already here we need to remove it...
|
|
SortedChainIteratorOf<Dictionary*, int> iterator(&dictionaryIndex);
|
|
|
|
Dictionary *dictionary = iterator.Find(version);
|
|
return dictionary;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
void
|
|
DictionaryManager::Encode(Time till)
|
|
{
|
|
|
|
activeDictionary->Encode(till);
|
|
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
void
|
|
DictionaryManager::Decode(int message_type, Stuff::MemoryStream* memory_stream)
|
|
{
|
|
|
|
|
|
NetVerify(message_type >= MWEntityManager::DictionaryPage0MessageID && message_type <= MWEntityManager::DictionaryPageMaxMessageID);
|
|
|
|
if (activeDictionary == NULL)
|
|
return;
|
|
|
|
// change till to the packet latency...
|
|
activeDictionary->Decode(message_type, memory_stream, 0.0f);
|
|
|
|
}
|
|
|