/// \file
/// \brief The server plugin for the autopatcher. Must be running for the client to get patches.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.rakkarsoft.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#include "AutopatcherServer.h"
#include "DirectoryDeltaTransfer.h"
#include "FileList.h"
#include "StringCompressor.h"
#include "RakPeerInterface.h"
#include "FileListTransfer.h"
#include "FileListTransferCBInterface.h"
#include "BitStream.h"
#include "PacketEnumerations.h"
#include "AutopatcherRepositoryInterface.h"
#include "RakAssert.h"
#ifdef _MSC_VER
#pragma warning( push )
#endif
AutopatcherServer::AutopatcherServer()
{
rakPeer=0;
fileListTransfer=0;
priority=HIGH_PRIORITY;
orderingChannel=0;
}
AutopatcherServer::~AutopatcherServer()
{
Clear();
}
void AutopatcherServer::SetUploadSendParameters(PacketPriority _priority, char _orderingChannel)
{
priority=_priority;
orderingChannel=_orderingChannel;
}
void AutopatcherServer::SetFileListTransferPlugin(FileListTransfer *flt)
{
fileListTransfer=flt;
}
void AutopatcherServer::SetAutopatcherRepositoryInterface(AutopatcherRepositoryInterface *ari)
{
repository=ari;
}
void AutopatcherServer::OnAttach(RakPeerInterface *peer)
{
rakPeer=peer;
threadPool.StartThreads(1, 0);
}
void AutopatcherServer::OnDetach(RakPeerInterface *peer)
{
Clear();
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void AutopatcherServer::Update(RakPeerInterface *peer)
{
ResultTypeAndBitstream* rtab;
while (threadPool.HasOutputFast() && threadPool.HasOutput())
{
rtab = threadPool.GetOutput();
if (rtab->bitStream1.GetNumberOfBitsUsed()>0)
rakPeer->Send(&(rtab->bitStream1), priority, RELIABLE_ORDERED, orderingChannel, rtab->playerId, false);
if (rtab->bitStream2.GetNumberOfBitsUsed()>0)
rakPeer->Send(&(rtab->bitStream2), priority, RELIABLE_ORDERED, orderingChannel, rtab->playerId, false);
delete rtab;
}
}
PluginReceiveResult AutopatcherServer::OnReceive(RakPeerInterface *peer, Packet *packet)
{
switch (packet->data[0])
{
case ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE:
OnGetChangelistSinceDate(peer, packet);
return RR_STOP_PROCESSING;
case ID_AUTOPATCHER_GET_PATCH:
OnGetPatch(peer, packet);
return RR_STOP_PROCESSING;
case ID_DISCONNECTION_NOTIFICATION:
case ID_CONNECTION_LOST:
RemoveFromThreadPool(packet->playerId);
break;
}
return RR_CONTINUE_PROCESSING;
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void AutopatcherServer::OnDisconnect(RakPeerInterface *peer)
{
Clear();
}
void AutopatcherServer::Clear(void)
{
// Clear the waiting input and output from the thread pool.
unsigned i;
threadPool.StopThreads();
for (i=0; i < threadPool.InputSize(); i++)
rakPeer->DeallocatePacket(threadPool.GetInputAtIndex(i).packet);
threadPool.ClearInput();
for (i=0; i < threadPool.OutputSize(); i++)
delete threadPool.GetOutputAtIndex(i);
threadPool.ClearOutput();
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void AutopatcherServer::OnInitialize(RakPeerInterface *peer)
{
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void AutopatcherServer::OnCloseConnection(RakPeerInterface *peer, PlayerID playerId)
{
RemoveFromThreadPool(playerId);
}
void AutopatcherServer::RemoveFromThreadPool(PlayerID playerId)
{
unsigned i;
i=0;
threadPool.LockInput();
while (i < threadPool.InputSize())
{
if (threadPool.GetInputAtIndex(i).packet->playerId==playerId)
{
rakPeer->DeallocatePacket(threadPool.GetInputAtIndex(i).packet);
threadPool.RemoveInputAtIndex(i);
}
else
i++;
}
threadPool.UnlockInput();
i=0;
threadPool.LockOutput();
while (i < threadPool.OutputSize())
{
if (threadPool.GetOutputAtIndex(i)->playerId==playerId)
{
delete threadPool.GetOutputAtIndex(i);
threadPool.RemoveOutputAtIndex(i);
}
else
i++;
}
threadPool.UnlockOutput();
}
AutopatcherServer::ResultTypeAndBitstream* GetChangelistSinceDateCB(AutopatcherServer::PatcherAndPacket pap, bool *returnOutput)
{
Packet *packet = pap.packet;
AutopatcherServer *server = pap.server;
RakNet::BitStream inBitStream(packet->data, packet->length, false);
inBitStream.IgnoreBits(8);
char applicationName[512];
char lastUpdateDate[64];
stringCompressor->DecodeString(applicationName, 512, &inBitStream);
if (stringCompressor->DecodeString(lastUpdateDate, 512, &inBitStream)==false)
{
server->rakPeer->DeallocatePacket(packet);
*returnOutput=false;
return 0; // Invalid bitstream
}
FileList addedFiles, deletedFiles;
char currentDate[64];
AutopatcherServer::ResultTypeAndBitstream *rtab = new AutopatcherServer::ResultTypeAndBitstream;
rtab->playerId=pap.packet->playerId;
// Query the database for a changelist since this date
if (server->repository->GetChangelistSinceDate(applicationName, &addedFiles, &deletedFiles, lastUpdateDate, currentDate))
{
if (deletedFiles.fileList.Size())
{
rtab->bitStream1.Write((unsigned char) ID_AUTOPATCHER_DELETION_LIST);
deletedFiles.Serialize(&rtab->bitStream1);
}
if (addedFiles.fileList.Size())
{
rtab->bitStream2.Write((unsigned char) ID_AUTOPATCHER_CREATION_LIST);
addedFiles.Serialize(&rtab->bitStream2);
stringCompressor->EncodeString(currentDate,64,&rtab->bitStream2);
addedFiles.Clear();
}
else
{
rtab->bitStream2.Write((unsigned char) ID_AUTOPATCHER_FINISHED);
stringCompressor->EncodeString(currentDate,64,&rtab->bitStream2);
}
}
else
{
rtab->bitStream2.Write((unsigned char) ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR);
stringCompressor->EncodeString(server->repository->GetLastError(), 256, &rtab->bitStream2);
}
*returnOutput=true;
server->rakPeer->DeallocatePacket(packet);
return rtab;
}
void AutopatcherServer::OnGetChangelistSinceDate(RakPeerInterface *peer, Packet *packet)
{
PatcherAndPacket pap;
pap.packet=packet;
pap.server=this;
threadPool.AddInput(GetChangelistSinceDateCB, pap);
}
AutopatcherServer::ResultTypeAndBitstream* GetPatchCB(AutopatcherServer::PatcherAndPacket pap, bool *returnOutput)
{
Packet *packet = pap.packet;
AutopatcherServer *server = pap.server;
RakNet::BitStream inBitStream(packet->data, packet->length, false);
FileList clientList, patchList;
unsigned short setId;
char applicationName[512];
inBitStream.IgnoreBits(8);
inBitStream.Read(setId);
stringCompressor->DecodeString(applicationName, 512, &inBitStream);
if (clientList.Deserialize(&inBitStream)==false)
{
*returnOutput=false;
server->rakPeer->DeallocatePacket(packet);
return 0;
}
if (clientList.fileList.Size()==0)
{
RakAssert(0);
*returnOutput=false;
server->rakPeer->DeallocatePacket(packet);
return 0;
}
AutopatcherServer::ResultTypeAndBitstream *rtab = new AutopatcherServer::ResultTypeAndBitstream;
rtab->playerId=pap.packet->playerId;
if (server->repository->GetPatches(applicationName, &clientList, &patchList))
{
if (patchList.fileList.Size())
server->fileListTransfer->Send(&patchList, server->rakPeer, packet->playerId, setId, server->priority, server->orderingChannel, true);
rtab->bitStream1.Write((unsigned char) ID_AUTOPATCHER_FINISHED);
}
else
{
rtab->bitStream1.Write((unsigned char) ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR);
stringCompressor->EncodeString(server->repository->GetLastError(), 256, &rtab->bitStream1);
}
*returnOutput=true;
server->rakPeer->DeallocatePacket(packet);
return rtab;
}
void AutopatcherServer::OnGetPatch(RakPeerInterface *peer, Packet *packet)
{
PatcherAndPacket pap;
pap.packet=packet;
pap.server=this;
threadPool.AddInput(GetPatchCB, pap);
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif
syntax highlighted by Code2HTML, v. 0.9.1