Files
SLikeNet/DependentExtensions/Autopatcher/AutopatcherServer.cpp
2025-11-24 14:19:51 +05:30

885 lines
31 KiB
C++

/*
* Original work: Copyright (c) 2014, Oculus VR, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* RakNet License.txt file in the licenses directory of this source tree. An additional grant
* of patent rights can be found in the RakNet Patents.txt file in the same directory.
*
*
* Modified work: Copyright (c) 2016-2020, SLikeSoft UG (haftungsbeschränkt)
*
* This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style
* license found in the license.txt file in the root directory of this source tree.
*/
/// \file
/// \brief The server plugin for the autopatcher. Must be running for the client to get patches.
#include "AutopatcherServer.h"
#include "slikenet/DirectoryDeltaTransfer.h"
#include "slikenet/FileList.h"
#include "slikenet/StringCompressor.h"
#include "slikenet/peerinterface.h"
#include "slikenet/FileListTransfer.h"
#include "slikenet/FileListTransferCBInterface.h"
#include "slikenet/BitStream.h"
#include "slikenet/MessageIdentifiers.h"
#include "slikenet/AutopatcherRepositoryInterface.h"
#include "slikenet/assert.h"
#include "slikenet/AutopatcherPatchContext.h"
#include <stdio.h>
#include <time.h>
using namespace SLNet;
const static unsigned HASH_LENGTH=4;
void AutopatcherServerLoadNotifier_Printf::OnQueueUpdate(SystemAddress remoteSystem, AutopatcherServerLoadNotifier::RequestType requestType, AutopatcherServerLoadNotifier::QueueOperation queueOperation, AutopatcherServerLoadNotifier::AutopatcherState *autopatcherState)
{
char *operationString;
char *requestTypeString;
char systemAddressString[32];
remoteSystem.ToString(true, systemAddressString, static_cast<size_t>(32));
if (requestType==ASUMC_GET_CHANGELIST)
requestTypeString="GetChangelist";
else
requestTypeString="GetPatch";
if (queueOperation==QO_WAS_ADDED)
operationString="added";
else if (queueOperation==QO_POPPED_ONTO_TO_PROCESSING_THREAD)
operationString="processing";
else // otherwise queueOperation == QO_WAS_ABORTED
operationString="aborted";
printf("%s %s %s. %i queued. %i working.\n", systemAddressString, requestTypeString, operationString, autopatcherState->requestsQueued, autopatcherState->requestsWorking);
}
void AutopatcherServerLoadNotifier_Printf::OnGetChangelistCompleted(
SystemAddress remoteSystem,
AutopatcherServerLoadNotifier::GetChangelistResult getChangelistResult,
AutopatcherServerLoadNotifier::AutopatcherState *autopatcherState)
{
char systemAddressString[32];
remoteSystem.ToString(true, systemAddressString, static_cast<size_t>(32));
char *changelistString;
if (getChangelistResult==GCR_DELETE_FILES)
changelistString="Delete files";
else if (getChangelistResult==GCR_ADD_FILES)
changelistString="Add files";
else if (getChangelistResult==GCR_ADD_AND_DELETE_FILES)
changelistString="Add and delete files";
else if (getChangelistResult==GCR_NOTHING_TO_DO)
changelistString="No files in changelist";
else // otherwise getChangelistResult == GCR_REPOSITORY_ERROR
changelistString="Repository error";
printf("%s GetChangelist complete. %s. %i queued. %i working.\n", systemAddressString, changelistString, autopatcherState->requestsQueued, autopatcherState->requestsWorking);
}
void AutopatcherServerLoadNotifier_Printf::OnGetPatchCompleted(SystemAddress remoteSystem, AutopatcherServerLoadNotifier::PatchResult patchResult, AutopatcherServerLoadNotifier::AutopatcherState *autopatcherState)
{
char systemAddressString[32];
remoteSystem.ToString(true, systemAddressString, static_cast<size_t>(32));
char *patchResultString;
if (patchResult==PR_NO_FILES_NEEDED_PATCHING)
patchResultString="No files needed patching";
else if (patchResult==PR_REPOSITORY_ERROR)
patchResultString="Repository error";
else if (patchResult==PR_DISALLOWED_DOWNLOADING_ORIGINAL_FILES)
patchResultString="Disallowed downloading original files";
else if (patchResult==PR_PATCHES_WERE_SENT)
patchResultString="Files pushed for patching";
else if (patchResult==PR_ABORTED_FROM_INPUT_THREAD)
patchResultString="Aborted from input thread";
else // otherwise patchResult == PR_ABORTED_FROM_DOWNLOAD_THREAD
patchResultString="Aborted from download thread";
printf("%s GetPatch complete. %s. %i queued. %i working.\n", systemAddressString, patchResultString, autopatcherState->requestsQueued, autopatcherState->requestsWorking);
}
AutopatcherServer::AutopatcherServer()
{
fileListTransfer=0;
priority=HIGH_PRIORITY;
orderingChannel=0;
// repository=0;
maxConcurrentUsers=0;
loadNotifier=0;
cache_minTime=0;
cache_maxTime=0;
cacheLoaded=false;
allowDownloadOfOriginalUnmodifiedFiles=true;
}
AutopatcherServer::~AutopatcherServer()
{
Clear();
}
void AutopatcherServer::SetUploadSendParameters(PacketPriority _priority, char _orderingChannel)
{
priority=_priority;
orderingChannel=_orderingChannel;
}
void AutopatcherServer::SetFileListTransferPlugin(FileListTransfer *flt)
{
if (fileListTransfer)
fileListTransfer->RemoveCallback(this);
fileListTransfer=flt;
if (fileListTransfer)
fileListTransfer->AddCallback(this);
}
void AutopatcherServer::StartThreads(int numThreads, int numSQLConnections, AutopatcherRepositoryInterface **sqlConnectionPtrArray)
{
RakAssert(numSQLConnections >= numThreads);
connectionPoolMutex.Lock();
for (int i=0; i < numSQLConnections; i++)
{
// Test the pointers passed, in case the user incorrectly casted an array of a different type
sqlConnectionPtrArray[i]->GetLastError();
connectionPool.Push(sqlConnectionPtrArray[i],_FILE_AND_LINE_);
}
connectionPoolMutex.Unlock();
threadPool.SetThreadDataInterface(this,0);
threadPool.StartThreads(numThreads, 0);
}
void AutopatcherServer::CacheMostRecentPatch(const char *applicationName)
{
if (connectionPool.Size()>0)
{
if (applicationName)
cache_appName=applicationName;
else
cache_appName.Clear();
cache_patchedFiles.Clear();
cache_addedFiles.Clear();
cache_deletedFiles.Clear();
cache_addedOrModifiedFileHashes.Clear();
cache_minTime=0;
cache_maxTime=0;
cacheLoaded = connectionPool[0]->GetMostRecentChangelistWithPatches(cache_appName, &cache_patchedFiles, &cache_addedFiles, &cache_addedOrModifiedFileHashes, &cache_deletedFiles, &cache_minTime, &cache_maxTime);
if (cacheLoaded==false)
{
printf("Warning: Cache not loaded. This is OK if no patch was ever saved.\n");
}
}
}
void AutopatcherServer::OnAttach(void)
{
}
void AutopatcherServer::OnDetach(void)
{
Clear();
}
void AutopatcherServer::Update(void)
{
while (PatchingUserLimitReached()==false && userRequestWaitingQueue.Size()>0)
{
Packet *packet = PopOffWaitingQueue();
switch (packet->data[0])
{
case ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE:
OnGetChangelistSinceDateInt(packet);
break;
// Client sends ID_AUTOPATCHER_GET_PATCH with files that they have different or missing
case ID_AUTOPATCHER_GET_PATCH:
OnGetPatchInt(packet);
break;
}
DeallocPacketUnified(packet);
}
}
PluginReceiveResult AutopatcherServer::OnReceive(Packet *packet)
{
switch (packet->data[0])
{
case ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE:
return OnGetChangelistSinceDate(packet);
case ID_AUTOPATCHER_GET_PATCH:
return OnGetPatch(packet);
}
return RR_CONTINUE_PROCESSING;
}
void AutopatcherServer::OnShutdown(void)
{
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++)
{
if (DecrementPatchingUserCount(threadPool.GetInputAtIndex(i).systemAddress))
CallPatchCompleteCallback(threadPool.GetInputAtIndex(i).systemAddress, AutopatcherServerLoadNotifier::PR_ABORTED_FROM_INPUT_THREAD);
SLNet::OP_DELETE(threadPool.GetInputAtIndex(i).clientList, _FILE_AND_LINE_);
}
threadPool.ClearInput();
for (i=0; i < threadPool.OutputSize(); i++)
{
SLNet::OP_DELETE(threadPool.GetOutputAtIndex(i)->patchList, _FILE_AND_LINE_);
SLNet::OP_DELETE(threadPool.GetOutputAtIndex(i)->deletedFiles, _FILE_AND_LINE_);
SLNet::OP_DELETE(threadPool.GetOutputAtIndex(i)->addedOrModifiedFilesWithHashData, _FILE_AND_LINE_);
}
threadPool.ClearOutput();
while (userRequestWaitingQueue.Size())
DeallocPacketUnified(AbortOffWaitingQueue());
patchingUsers.Clear(true, _FILE_AND_LINE_);
}
void AutopatcherServer::OnStartup(RakPeerInterface *peer)
{
// unused parameters
(void)peer;
}
void AutopatcherServer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason )
{
// unused parameters
(void)rakNetGUID;
(void)lostConnectionReason;
RemoveFromThreadPool(systemAddress);
unsigned int i=0;
patchingUsersMutex.Lock();
while (i < patchingUsers.Size())
{
if (patchingUsers[i]==systemAddress)
patchingUsers.RemoveAtIndexFast(i);
else
i++;
}
patchingUsersMutex.Unlock();
i=0;
while (i < userRequestWaitingQueue.Size())
{
if (userRequestWaitingQueue[i]->systemAddress==systemAddress)
userRequestWaitingQueue.RemoveAtIndex(i);
else
i++;
}
}
void AutopatcherServer::RemoveFromThreadPool(SystemAddress systemAddress)
{
unsigned int i = 0;
threadPool.LockInput();
while (i < threadPool.InputSize())
{
if (threadPool.GetInputAtIndex(i).systemAddress==systemAddress)
{
if (DecrementPatchingUserCount(systemAddress))
CallPatchCompleteCallback(threadPool.GetInputAtIndex(i).systemAddress, AutopatcherServerLoadNotifier::PR_ABORTED_FROM_INPUT_THREAD);
SLNet::OP_DELETE(threadPool.GetInputAtIndex(i).clientList, _FILE_AND_LINE_);
threadPool.RemoveInputAtIndex(i);
}
else
i++;
}
threadPool.UnlockInput();
i=0;
threadPool.LockOutput();
while (i < threadPool.OutputSize())
{
if (threadPool.GetOutputAtIndex(i)->systemAddress==systemAddress)
{
SLNet::OP_DELETE(threadPool.GetOutputAtIndex(i)->patchList, _FILE_AND_LINE_);
SLNet::OP_DELETE(threadPool.GetOutputAtIndex(i)->deletedFiles, _FILE_AND_LINE_);
SLNet::OP_DELETE(threadPool.GetOutputAtIndex(i)->addedOrModifiedFilesWithHashData, _FILE_AND_LINE_);
threadPool.RemoveOutputAtIndex(i);
}
else
i++;
}
threadPool.UnlockOutput();
}
namespace SLNet
{
AutopatcherServer::ResultTypeAndBitstream* GetChangelistSinceDateCB(AutopatcherServer::ThreadData threadData, bool *returnOutput, void* perThreadData)
{
AutopatcherRepositoryInterface *repository = (AutopatcherRepositoryInterface*)perThreadData;
FileList addedOrModifiedFilesWithHashData, deletedFiles;
AutopatcherServer *server = threadData.server;
//AutopatcherServer::ResultTypeAndBitstream *rtab = SLNet::OP_NEW<AutopatcherServer::ResultTypeAndBitstream>( _FILE_AND_LINE_ );
AutopatcherServer::ResultTypeAndBitstream rtab;
rtab.systemAddress=threadData.systemAddress;
// rtab.deletedFiles=SLNet::OP_NEW<FileList>( _FILE_AND_LINE_ );
// rtab.addedFiles=SLNet::OP_NEW<FileList>( _FILE_AND_LINE_ );
rtab.deletedFiles=&deletedFiles;
rtab.addedOrModifiedFilesWithHashData=&addedOrModifiedFilesWithHashData;
// Query the database for a changelist since this date
RakAssert(server);
//if (server->repository->GetChangelistSinceDate(threadData.applicationName.C_String(), rtab.addedFiles, rtab.deletedFiles, threadData.lastUpdateDate.C_String(), currentDate))
if (repository->GetChangelistSinceDate(threadData.applicationName.C_String(), rtab.addedOrModifiedFilesWithHashData, rtab.deletedFiles, threadData.lastUpdateDate))
{
rtab.resultCode=1;
}
else
{
rtab.resultCode=0;
}
rtab.operation=AutopatcherServer::ResultTypeAndBitstream::GET_CHANGELIST_SINCE_DATE;
rtab.currentDate=(double) time(nullptr);
// *returnOutput=true;
// return rtab;
if (rtab.resultCode==1)
{
if (rtab.deletedFiles->fileList.Size())
{
rtab.bitStream1.Write((unsigned char) ID_AUTOPATCHER_DELETION_LIST);
rtab.deletedFiles->Serialize(&rtab.bitStream1);
}
if (rtab.addedOrModifiedFilesWithHashData->fileList.Size())
{
rtab.bitStream2.Write((unsigned char) ID_AUTOPATCHER_CREATION_LIST);
rtab.addedOrModifiedFilesWithHashData->Serialize(&rtab.bitStream2);
rtab.bitStream2.Write(rtab.currentDate);
rtab.bitStream2.WriteCasted<double>(0);
rtab.addedOrModifiedFilesWithHashData->Clear();
}
else
{
rtab.bitStream2.Write((unsigned char) ID_AUTOPATCHER_FINISHED);
rtab.bitStream2.Write(rtab.currentDate);
}
}
else
{
rtab.bitStream2.Write((unsigned char) ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR);
StringCompressor::Instance()->EncodeString(repository->GetLastError(), 256, &rtab.bitStream2);
}
// SLNet::OP_DELETE(rtab.deletedFiles, _FILE_AND_LINE_);
// SLNet::OP_DELETE(rtab.addedFiles, _FILE_AND_LINE_);
*returnOutput=false;
if (server->DecrementPatchingUserCount(rtab.systemAddress))
{
if (rtab.bitStream1.GetNumberOfBitsUsed()>0)
server->SendUnified(&(rtab.bitStream1), server->priority, RELIABLE_ORDERED, server->orderingChannel, rtab.systemAddress, false);
if (rtab.bitStream2.GetNumberOfBitsUsed()>0)
server->SendUnified(&(rtab.bitStream2), server->priority, RELIABLE_ORDERED, server->orderingChannel, rtab.systemAddress, false);
if (server->loadNotifier)
{
AutopatcherServerLoadNotifier::AutopatcherState autopatcherState;
autopatcherState.requestsQueued=server->userRequestWaitingQueue.Size();
autopatcherState.requestsWorking=server->patchingUsers.Size();
AutopatcherServerLoadNotifier::GetChangelistResult getChangelistResult;
if (rtab.resultCode!=1)
getChangelistResult=AutopatcherServerLoadNotifier::GCR_REPOSITORY_ERROR;
else if (rtab.deletedFiles->fileList.Size()==0 && rtab.addedOrModifiedFilesWithHashData->fileList.Size()==0)
getChangelistResult=AutopatcherServerLoadNotifier::GCR_NOTHING_TO_DO;
else if (rtab.deletedFiles->fileList.Size()==0)
getChangelistResult=AutopatcherServerLoadNotifier::GCR_ADD_FILES;
else if (rtab.addedOrModifiedFilesWithHashData->fileList.Size()==0)
getChangelistResult=AutopatcherServerLoadNotifier::GCR_DELETE_FILES;
else
getChangelistResult=AutopatcherServerLoadNotifier::GCR_ADD_AND_DELETE_FILES;
server->loadNotifier->OnGetChangelistCompleted(rtab.systemAddress, getChangelistResult, &autopatcherState);
}
}
return 0;
}
}
PluginReceiveResult AutopatcherServer::OnGetChangelistSinceDate(Packet *packet)
{
SLNet::BitStream inBitStream(packet->data, packet->length, false);
ThreadData threadData;
threadData.clientList=0;
inBitStream.IgnoreBits(8);
inBitStream.ReadCompressed(threadData.applicationName);
inBitStream.Read(threadData.lastUpdateDate);
if (cacheLoaded && threadData.lastUpdateDate!=0 && threadData.applicationName==cache_appName)
{
SLNet::BitStream bitStream1;
SLNet::BitStream bitStream2;
double currentDate=(double) time(nullptr);
if (cache_maxTime!=0 && threadData.lastUpdateDate>cache_maxTime)
{
bitStream2.Write((unsigned char) ID_AUTOPATCHER_FINISHED);
bitStream2.Write(currentDate);
SendUnified(&bitStream2, priority, RELIABLE_ORDERED,orderingChannel, packet->systemAddress, false);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
}
// Check in-memory cache, use if possible rather than accessing database
if (cache_minTime!=0 && threadData.lastUpdateDate>cache_minTime)
{
if (cache_deletedFiles.fileList.Size())
{
bitStream1.Write((unsigned char) ID_AUTOPATCHER_DELETION_LIST);
cache_deletedFiles.Serialize(&bitStream1);
SendUnified(&bitStream1, priority, RELIABLE_ORDERED,orderingChannel, packet->systemAddress, false);
}
if (cache_addedOrModifiedFileHashes.fileList.Size())
{
bitStream2.Write((unsigned char) ID_AUTOPATCHER_CREATION_LIST);
cache_addedOrModifiedFileHashes.Serialize(&bitStream2);
bitStream2.Write(currentDate);
bitStream2.Write(threadData.lastUpdateDate);
}
else
{
bitStream2.Write((unsigned char) ID_AUTOPATCHER_FINISHED);
bitStream2.Write(currentDate);
}
SendUnified(&bitStream2, priority, RELIABLE_ORDERED,orderingChannel, packet->systemAddress, false);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
}
}
if (PatchingUserLimitReached())
{
AddToWaitingQueue(packet);
return RR_STOP_PROCESSING;
}
OnGetChangelistSinceDateInt(packet);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
}
void AutopatcherServer::OnGetChangelistSinceDateInt(Packet *packet)
{
SLNet::BitStream inBitStream(packet->data, packet->length, false);
ThreadData threadData;
threadData.clientList=0;
inBitStream.IgnoreBits(8);
inBitStream.ReadCompressed(threadData.applicationName);
inBitStream.Read(threadData.lastUpdateDate);
if (IncrementPatchingUserCount(packet->systemAddress))
{
CallPacketCallback(packet, AutopatcherServerLoadNotifier::QO_POPPED_ONTO_TO_PROCESSING_THREAD);
threadData.server=this;
threadData.systemAddress=packet->systemAddress;
threadPool.AddInput(GetChangelistSinceDateCB, threadData);
}
}
namespace SLNet {
AutopatcherServer::ResultTypeAndBitstream* GetPatchCB(AutopatcherServer::ThreadData threadData, bool *returnOutput, void* perThreadData)
{
AutopatcherServer *server = threadData.server;
AutopatcherRepositoryInterface *repository = (AutopatcherRepositoryInterface*)perThreadData;
// AutopatcherServer::ResultTypeAndBitstream *rtab = SLNet::OP_NEW<AutopatcherServer::ResultTypeAndBitstream>( _FILE_AND_LINE_ );
AutopatcherServer::ResultTypeAndBitstream rtab;
rtab.systemAddress=threadData.systemAddress;
FileList fileList;
// rtab.patchList=SLNet::OP_NEW<FileList>( _FILE_AND_LINE_ );
rtab.patchList=&fileList;
RakAssert(server);
// RakAssert(server->repository);
// if (server->repository->GetPatches(threadData.applicationName.C_String(), threadData.clientList, rtab.patchList, currentDate))
rtab.resultCode = repository->GetPatches(threadData.applicationName.C_String(), threadData.clientList, server->allowDownloadOfOriginalUnmodifiedFiles, rtab.patchList);
rtab.operation=AutopatcherServer::ResultTypeAndBitstream::GET_PATCH;
rtab.setId=threadData.setId;
rtab.currentDate=(double) time(nullptr);
SLNet::OP_DELETE(threadData.clientList, _FILE_AND_LINE_);
if (rtab.resultCode==1)
{
if (rtab.patchList->fileList.Size())
{
//server->fileListTransfer->Send(rtab.patchList, 0, rtab.systemAddress, rtab.setId, server->priority, server->orderingChannel, false, server->repository);
server->fileListTransfer->Send(rtab.patchList, 0, rtab.systemAddress, rtab.setId, server->priority, server->orderingChannel, repository, repository->GetIncrementalReadChunkSize());
}
else
{
// No files needed to send
if (server->DecrementPatchingUserCount(rtab.systemAddress))
server->CallPatchCompleteCallback(rtab.systemAddress, AutopatcherServerLoadNotifier::PR_NO_FILES_NEEDED_PATCHING);
}
rtab.bitStream1.Write((unsigned char) ID_AUTOPATCHER_FINISHED_INTERNAL);
rtab.bitStream1.Write(rtab.currentDate);
}
else
{
AutopatcherServerLoadNotifier::PatchResult pr;
if (rtab.resultCode==0)
{
rtab.bitStream1.Write((unsigned char) ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR);
StringCompressor::Instance()->EncodeString(repository->GetLastError(), 256, &rtab.bitStream1);
pr = AutopatcherServerLoadNotifier::PR_REPOSITORY_ERROR;
}
else
{
rtab.bitStream1.Write((unsigned char) ID_AUTOPATCHER_CANNOT_DOWNLOAD_ORIGINAL_UNMODIFIED_FILES);
pr = AutopatcherServerLoadNotifier::PR_DISALLOWED_DOWNLOADING_ORIGINAL_FILES;
}
if (server->DecrementPatchingUserCount(rtab.systemAddress))
{
server->CallPatchCompleteCallback(rtab.systemAddress, pr);
}
else
{
*returnOutput=false;
return 0;
}
}
*returnOutput=false;
if (rtab.bitStream1.GetNumberOfBitsUsed()>0)
server->SendUnified(&(rtab.bitStream1), server->priority, RELIABLE_ORDERED, server->orderingChannel, rtab.systemAddress, false);
if (rtab.bitStream2.GetNumberOfBitsUsed()>0)
server->SendUnified(&(rtab.bitStream2), server->priority, RELIABLE_ORDERED, server->orderingChannel, rtab.systemAddress, false);
// 12/1/2010 This doesn't scale well. Changing to allocating a connection object per request
/*
// Wait for repository to finish
// This is so that the same sql connection is not used between two different plugins, which causes thrashing and bad performance
// Plus if fileListTransfer uses multiple threads, this will keep this thread and the fileListTransfer thread from using the same connection at the same time
// PostgreSQL possibly MySQL are not threadsafe for multiple threads on the same connection
int pendingFiles = server->fileListTransfer->GetPendingFilesToAddress(rtab.systemAddress);
while (pendingFiles>0)
{
RakSleep(pendingFiles*10);
pendingFiles = server->fileListTransfer->GetPendingFilesToAddress(rtab.systemAddress);
}
*/
// *returnOutput=true;
// return rtab;
return 0;
}
}
PluginReceiveResult AutopatcherServer::OnGetPatch(Packet *packet)
{
SLNet::BitStream inBitStream(packet->data, packet->length, false);
ThreadData threadData;
inBitStream.IgnoreBits(8);
inBitStream.Read(threadData.setId);
double lastUpdateDate;
inBitStream.Read(lastUpdateDate);
inBitStream.ReadCompressed(threadData.applicationName);
threadData.clientList=0;
// Check in-memory cache, use if possible rather than accessing database
if (threadData.applicationName==cache_appName && lastUpdateDate!=0 && cacheLoaded && cache_minTime!=0 && lastUpdateDate>cache_minTime)
{
threadData.systemAddress=packet->systemAddress;
threadData.server=this;
threadData.clientList= SLNet::OP_NEW<FileList>( _FILE_AND_LINE_ );
if (threadData.clientList->Deserialize(&inBitStream)==false)
{
SLNet::OP_DELETE(threadData.clientList, _FILE_AND_LINE_);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
}
if (threadData.clientList->fileList.Size()==0)
{
RakAssert(0);
SLNet::OP_DELETE(threadData.clientList, _FILE_AND_LINE_);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
}
char *userHash;
SLNet::RakString userFilename;
FileList patchList;
bool cacheUpdateFailed=false;
unsigned int i,j;
// FileList is the list of all files missing or changed as determined by the client
for (i=0; i < threadData.clientList->fileList.Size(); i++)
{
userHash=threadData.clientList->fileList[i].data;
userFilename=threadData.clientList->fileList[i].filename;
if (userHash)
{
// If the user has a hash, check for this file in cache_patchedFiles. If not found, or hash is wrong, use DB
if (threadData.clientList->fileList[i].dataLengthBytes!=HASH_LENGTH)
{
SLNet::OP_DELETE(threadData.clientList, _FILE_AND_LINE_);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
}
for (j=0; j < cache_patchedFiles.fileList.Size(); j++)
{
if (userFilename == cache_patchedFiles.fileList[j].filename)
{
if (memcmp(cache_patchedFiles.fileList[j].data, userHash, HASH_LENGTH)==0)
{
// Send patch
RakAssert(cache_patchedFiles.fileList[j].context.op==PC_HASH_2_WITH_PATCH);
patchList.AddFile(userFilename,userFilename, 0, cache_patchedFiles.fileList[j].dataLengthBytes, cache_patchedFiles.fileList[j].fileLengthBytes, cache_patchedFiles.fileList[j].context, true, false);
}
else
{
// Bad hash
cacheUpdateFailed=true;
}
break;
}
}
if (j==cache_patchedFiles.fileList.Size())
{
// Didn't find the patch even though the client has an older version of the file
cacheUpdateFailed=true;
}
}
else
{
// If the user does not have a hash, check for this file in cache_addedFiles. If not found, use DB
for (j=0; j < cache_addedFiles.fileList.Size(); j++)
{
if (userFilename == cache_addedFiles.fileList[j].filename)
{
// Send added file
patchList.AddFile(userFilename,userFilename, 0, cache_addedFiles.fileList[j].dataLengthBytes, cache_addedFiles.fileList[j].fileLengthBytes, cache_addedFiles.fileList[j].context, true, false);
break;
}
}
if (j==cache_addedFiles.fileList.Size())
{
// Didn't find the file in the cache even though the client asked for it
cacheUpdateFailed=true;
}
}
if (cacheUpdateFailed==true)
{
// Failure to find file in cache
// Will fall to use database
patchList.Clear();
break;
}
}
if (patchList.fileList.Size()>0)
{
if (IncrementPatchingUserCount(packet->systemAddress))
{
fileListTransfer->Send(&patchList, 0, packet->systemAddress, threadData.setId, priority, orderingChannel, this, 262144*4*4);
SLNet::BitStream bitStream1;
bitStream1.Write((unsigned char) ID_AUTOPATCHER_FINISHED_INTERNAL);
double t =(double) time(nullptr);
bitStream1.Write(t);
SendUnified(&bitStream1, priority, RELIABLE_ORDERED, orderingChannel, packet->systemAddress, false);
SLNet::OP_DELETE(threadData.clientList, _FILE_AND_LINE_);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
}
}
}
SLNet::OP_DELETE(threadData.clientList, _FILE_AND_LINE_);
if (PatchingUserLimitReached())
{
AddToWaitingQueue(packet);
return RR_STOP_PROCESSING;
}
OnGetPatchInt(packet);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
}
void AutopatcherServer::OnGetPatchInt(Packet *packet)
{
SLNet::BitStream inBitStream(packet->data, packet->length, false);
ThreadData threadData;
inBitStream.IgnoreBits(8);
inBitStream.Read(threadData.setId);
double lastUpdateDate;
inBitStream.Read(lastUpdateDate);
inBitStream.ReadCompressed(threadData.applicationName);
threadData.systemAddress=packet->systemAddress;
threadData.server=this;
threadData.clientList= SLNet::OP_NEW<FileList>( _FILE_AND_LINE_ );
if (threadData.clientList->Deserialize(&inBitStream)==false)
{
SLNet::OP_DELETE(threadData.clientList, _FILE_AND_LINE_);
return;
}
if (threadData.clientList->fileList.Size()==0)
{
RakAssert(0);
SLNet::OP_DELETE(threadData.clientList, _FILE_AND_LINE_);
return;
}
if (IncrementPatchingUserCount(packet->systemAddress))
CallPacketCallback(packet, AutopatcherServerLoadNotifier::QO_POPPED_ONTO_TO_PROCESSING_THREAD);
threadPool.AddInput(GetPatchCB, threadData);
}
void* AutopatcherServer::PerThreadFactory(void *context)
{
(void)context;
AutopatcherRepositoryInterface* p;
connectionPoolMutex.Lock();
p=connectionPool.Pop();
connectionPoolMutex.Unlock();
return p;
}
void AutopatcherServer::PerThreadDestructor(void* factoryResult, void *context)
{
(void)context;
(void)factoryResult;
}
void AutopatcherServer::OnFilePushesComplete( SystemAddress systemAddress, unsigned short setID )
{
// unused parameters
(void)setID;
if (DecrementPatchingUserCount(systemAddress))
CallPatchCompleteCallback(systemAddress, AutopatcherServerLoadNotifier::PR_PATCHES_WERE_SENT);
}
void AutopatcherServer::OnSendAborted( SystemAddress systemAddress )
{
if (DecrementPatchingUserCount(systemAddress))
CallPatchCompleteCallback(systemAddress, AutopatcherServerLoadNotifier::PR_ABORTED_FROM_DOWNLOAD_THREAD);
}
bool AutopatcherServer::IncrementPatchingUserCount(SystemAddress sa)
{
// A system address may exist more than once in patchingUsers
patchingUsersMutex.Lock();
patchingUsers.Insert(sa, _FILE_AND_LINE_);
patchingUsersMutex.Unlock();
return true;
}
bool AutopatcherServer::DecrementPatchingUserCount(SystemAddress sa)
{
unsigned int i;
patchingUsersMutex.Lock();
for (i=0; i < patchingUsers.Size(); i++)
{
if (patchingUsers[i]==sa)
{
patchingUsers.RemoveAtIndexFast(i);
patchingUsersMutex.Unlock();
return true;
}
}
patchingUsersMutex.Unlock();
return false;
}
bool AutopatcherServer::PatchingUserLimitReached(void) const
{
if (maxConcurrentUsers==0)
return false;
return patchingUsers.Size()>=maxConcurrentUsers;
}
void AutopatcherServer::SetMaxConurrentUsers(unsigned int _maxConcurrentUsers)
{
maxConcurrentUsers=_maxConcurrentUsers;
}
unsigned int AutopatcherServer::GetMaxConurrentUsers(void) const
{
return maxConcurrentUsers;
}
void AutopatcherServer::CallPacketCallback(Packet *packet, AutopatcherServerLoadNotifier::QueueOperation queueOperation)
{
if (loadNotifier)
{
AutopatcherServerLoadNotifier::AutopatcherState autopatcherState;
autopatcherState.requestsQueued=userRequestWaitingQueue.Size();
autopatcherState.requestsWorking=patchingUsers.Size();
AutopatcherServerLoadNotifier::RequestType requestType;
if (packet->data[0]==ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE)
requestType=AutopatcherServerLoadNotifier::ASUMC_GET_CHANGELIST;
else
requestType=AutopatcherServerLoadNotifier::ASUMC_GET_PATCH;
loadNotifier->OnQueueUpdate(packet->systemAddress, requestType, queueOperation, &autopatcherState);
}
}
void AutopatcherServer::CallPatchCompleteCallback(const SystemAddress &systemAddress, AutopatcherServerLoadNotifier::PatchResult patchResult)
{
if (loadNotifier)
{
AutopatcherServerLoadNotifier::AutopatcherState autopatcherState;
autopatcherState.requestsQueued=userRequestWaitingQueue.Size();
autopatcherState.requestsWorking=patchingUsers.Size();
loadNotifier->OnGetPatchCompleted(systemAddress, patchResult, &autopatcherState);
}
}
void AutopatcherServer::AddToWaitingQueue(Packet *packet)
{
userRequestWaitingQueue.Push(packet, _FILE_AND_LINE_);
CallPacketCallback(packet, AutopatcherServerLoadNotifier::QO_WAS_ADDED);
}
Packet *AutopatcherServer::AbortOffWaitingQueue(void)
{
Packet *packet = userRequestWaitingQueue.Pop();
CallPacketCallback(packet,AutopatcherServerLoadNotifier::QO_WAS_ABORTED);
return packet;
}
Packet *AutopatcherServer::PopOffWaitingQueue(void)
{
return userRequestWaitingQueue.Pop();;
}
void AutopatcherServer::SetLoadManagementCallback(AutopatcherServerLoadNotifier *asumc)
{
loadNotifier=asumc;
}
void AutopatcherServer::SetAllowDownloadOfOriginalUnmodifiedFiles(bool allow)
{
allowDownloadOfOriginalUnmodifiedFiles = allow;
}
unsigned int AutopatcherServer::GetFilePart( const char *filename, unsigned int startReadBytes, unsigned int numBytesToRead, void *preallocatedDestination, FileListNodeContext context)
{
// unused parameters
(void)filename;
/*
int offset;
if (context.op==PC_HASH_1_WITH_PATCH)
offset=HASH_LENGTH;
else if (context.op==PC_HASH_2_WITH_PATCH)
offset=HASH_LENGTH*2;
else
offset=0;
int bytesToRead;
if (startReadBytes + numBytesToRead > context.dataLength-offset)
bytesToRead=(context.dataLength-offset)-startReadBytes;
else
bytesToRead=numBytesToRead;
memcpy(preallocatedDestination, ((char*)context.dataPtr)+offset, bytesToRead);
*/
int bytesToRead;
if (startReadBytes + numBytesToRead > context.dataLength)
bytesToRead=(context.dataLength)-startReadBytes;
else
bytesToRead=numBytesToRead;
memcpy(preallocatedDestination, context.dataPtr, bytesToRead);
return bytesToRead;
}