openclonk/src/network/C4Network2Res.h

451 lines
13 KiB
C++

/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
* Copyright (c) 2013-2016, The OpenClonk Team and contributors
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
*
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
* See accompanying file "TRADEMARK" for details.
*
* To redistribute this file separately, substitute the full license texts
* for the above references.
*/
// network resource: data needed for the game (scenario, plr files, definitions...)
#ifndef INC_C4Network2Res
#define INC_C4Network2Res
#include "lib/StdAdaptors.h"
#include "platform/StdSync.h"
#include "lib/SHA1.h"
#include <atomic>
const uint32_t C4NetResChunkSize = 10U * 1024U;
const int32_t C4NetResDiscoverTimeout = 10, // (s)
C4NetResDiscoverInterval = 1, // (s)
C4NetResStatusInterval = 1, // (s)
C4NetResMaxLoad = 5,
C4NetResLoadTimeout = 60, // (s)
C4NetResDeleteTime = 60, // (s)
C4NetResMaxBigicon = 20; // maximum size, in KB, of bigicon
const int32_t C4NetResIDAnonymous = -2;
enum C4Network2ResType
{
NRT_Null=0,
NRT_Scenario,
NRT_Dynamic,
NRT_Player,
NRT_Definitions,
NRT_System,
NRT_Material
};
const StdEnumEntry<C4Network2ResType> C4Network2ResType_EnumMap[] =
{
{ "Scenario", NRT_Scenario },
{ "Dynamic", NRT_Dynamic },
{ "Player", NRT_Player },
{ "Definitions", NRT_Definitions },
{ "System", NRT_System },
{ "Material", NRT_Material },
};
// damn circular dependencies
#include "network/C4PacketBase.h"
#include "network/C4Network2IO.h"
class C4Network2ResList;
class C4Network2ResChunk;
// classes
class C4Network2ResCore : public C4PacketBase
{
public:
C4Network2ResCore();
protected:
C4Network2ResType eType{NRT_Null};
int32_t iID{-1}, iDerID{-1};
StdCopyStrBuf FileName;
bool fLoadable{false};
uint32_t iFileSize, iFileCRC, iContentsCRC;
uint8_t fHasFileSHA{false};
uint8_t FileSHA[SHA_DIGEST_LENGTH];
uint32_t iChunkSize;
public:
C4Network2ResType getType() const { return eType; }
bool isNull() const { return eType == NRT_Null; }
int32_t getID() const { return iID; }
int32_t getDerID() const { return iDerID; }
bool isLoadable() const { return fLoadable; }
uint32_t getFileSize() const { return iFileSize; }
uint32_t getFileCRC() const { return iFileCRC; }
uint32_t getContentsCRC()const { return iContentsCRC; }
bool hasFileSHA() const { return !!fHasFileSHA; }
const uint8_t*getFileSHA() const { return FileSHA; }
const char * getFileName() const { return FileName.getData(); }
uint32_t getChunkSize() const { return iChunkSize; }
uint32_t getChunkCnt() const { return iFileSize && iChunkSize ? (iFileSize - 1) / iChunkSize + 1 : 0; }
void Set(C4Network2ResType eType, int32_t iResID, const char *strFileName, uint32_t iContentsCRC);
void SetID(int32_t inID) { iID = inID; }
void SetDerived(int32_t inDerID) { iDerID = inDerID; }
void SetLoadable(uint32_t iSize, uint32_t iCRC);
void SetFileSHA(BYTE *pSHA) { memcpy(FileSHA, pSHA, SHA_DIGEST_LENGTH); fHasFileSHA = true; }
void Clear();
void CompileFunc(StdCompiler *pComp) override;
};
class C4Network2ResLoad
{
friend class C4Network2Res;
public:
C4Network2ResLoad(int32_t iChunk, int32_t iByClient);
~C4Network2ResLoad();
protected:
// chunk download data
int32_t iChunk;
time_t Timestamp;
int32_t iByClient;
// list (C4Network2Res)
C4Network2ResLoad *pNext;
public:
int32_t getChunk() const { return iChunk; }
int32_t getByClient() const { return iByClient; }
C4Network2ResLoad *Next() const { return pNext; }
bool CheckTimeout();
};
class C4Network2ResChunkData : public C4PacketBase
{
public:
C4Network2ResChunkData();
C4Network2ResChunkData(const C4Network2ResChunkData &Data2);
~C4Network2ResChunkData() override;
C4Network2ResChunkData &operator =(const C4Network2ResChunkData &Data2);
protected:
int32_t iChunkCnt{0}, iPresentChunkCnt{0};
// present chunk ranges
struct ChunkRange { int32_t Start, Length; ChunkRange *Next; };
ChunkRange *pChunkRanges{nullptr};
int32_t iChunkRangeCnt{0};
public:
int32_t getChunkCnt() const { return iChunkCnt; }
int32_t getPresentChunkCnt() const { return iPresentChunkCnt; }
int32_t getPresentPercent() const { return iPresentChunkCnt * 100 / iChunkCnt; }
bool isComplete() const { return iPresentChunkCnt == iChunkCnt; }
void SetIncomplete(int32_t iChunkCnt);
void SetComplete(int32_t iChunkCnt);
void AddChunk(int32_t iChunk);
void AddChunkRange(int32_t iStart, int32_t iLength);
void Merge(const C4Network2ResChunkData &Data2);
void Clear();
int32_t GetChunkToRetrieve(const C4Network2ResChunkData &Available, int32_t iLoadingCnt, int32_t *pLoading) const;
protected:
// helpers
bool MergeRanges(ChunkRange *pRange);
void GetNegative(C4Network2ResChunkData &Target) const;
int32_t getPresentChunk(int32_t iNr) const;
public:
void CompileFunc(StdCompiler *pComp) override;
};
class C4Network2Res
{
friend class C4Network2ResList;
friend class C4Network2ResChunk;
public:
// helper for reference-holding
class Ref
{
public:
Ref() = default;
Ref(C4Network2Res *pRes) : pRes(pRes) { if (pRes) pRes->AddRef(); }
Ref(const Ref &rCopy) : pRes(rCopy.pRes) { if (pRes) pRes->AddRef(); }
~Ref() { Clear(); }
Ref &operator = (C4Network2Res *pnRes) { Set(pnRes); return *this; }
Ref &operator = (const Ref &rCopy) { Set(rCopy.pRes); return *this; }
private:
C4Network2Res *pRes{nullptr};
public:
operator C4Network2Res *() const { return pRes; }
bool operator ! () const { return !pRes; }
C4Network2Res * operator ->() const { return pRes; }
void Clear() { if (pRes) pRes->DelRef(); pRes = nullptr; }
void Set(C4Network2Res *pnRes) { if (pRes == pnRes) return; Clear(); pRes = pnRes; if (pRes) pRes->AddRef(); }
};
C4Network2Res(C4Network2ResList *pnParent);
~C4Network2Res();
protected:
// core, chunk data
C4Network2ResCore Core;
C4Network2ResChunkData Chunks; // (only valid while loading)
bool fDirty;
// local file data
CStdCSec FileCSec;
char szFile[_MAX_PATH + 1], szStandalone[_MAX_PATH + 1];
bool fTempFile, fStandaloneFailed;
// references
std::atomic_long iRefCnt;
bool fRemoved;
// being load?
int32_t iLastReqTime;
// loading
bool fLoading;
struct ClientChunks { C4Network2ResChunkData Chunks; int32_t ClientID; ClientChunks *Next; }
*pCChunks;
time_t iDiscoverStartTime;
C4Network2ResLoad *pLoads;
int32_t iLoadCnt;
// list (C4Network2ResList)
C4Network2Res *pNext;
C4Network2ResList *pParent;
public:
C4Network2ResType getType() const { return Core.getType(); }
const C4Network2ResCore &getCore() const { return Core; }
bool isDirty() const { return fDirty; }
bool isAnonymous() const { return getResID() == C4NetResIDAnonymous; }
int32_t getResID() const { return Core.getID(); }
int32_t getResClient() const { return Core.getID() >> 16; }
const char *getFile() const { return szFile; }
CStdCSec *getFileCSec() { return &FileCSec; }
int32_t getLastReqTime()const { return iLastReqTime; }
bool isRemoved() const { return fRemoved; }
bool isLoading() const { return fLoading; }
bool isComplete() const { return !fLoading; }
int32_t getPresentPercent() const { return fLoading ? Chunks.getPresentPercent() : 100; }
bool isTempFile() const { return fTempFile; }
bool SetByFile(const char *strFilePath, bool fTemp, C4Network2ResType eType, int32_t iResID, const char *szResName = nullptr, bool fSilent = false);
bool SetByGroup(C4Group *pGrp, bool fTemp, C4Network2ResType eType, int32_t iResID, const char *szResName = nullptr, bool fSilent = false);
bool SetByCore(const C4Network2ResCore &nCore, bool fSilent = false, const char *szAsFilename = nullptr, int32_t iRecursion=0);
bool SetLoad(const C4Network2ResCore &nCore);
bool SetDerived(const char *strName, const char *strFilePath, bool fTemp, C4Network2ResType eType, int32_t iDResID);
void ChangeID(int32_t inID);
bool IsBinaryCompatible();
bool GetStandalone(char *pTo, int32_t iMaxL, bool fSetOfficial, bool fAllowUnloadable = false, bool fSilent = false);
bool CalculateSHA();
bool SaveBackFile();
C4Network2Res::Ref Derive();
bool FinishDerive();
bool FinishDerive(const C4Network2ResCore &nCore);
bool SendStatus(C4Network2IOConnection *pTo = nullptr);
bool SendChunk(uint32_t iChunk, int32_t iToClient);
// references
void AddRef(); void DelRef();
// events
void OnDiscover(C4Network2IOConnection *pBy);
void OnStatus(const C4Network2ResChunkData &rChunkData, C4Network2IOConnection *pBy);
void OnChunk(const C4Network2ResChunk &rChunk);
bool DoLoad();
bool NeedsDiscover();
C4Group *OpenAsGrp() const;
void Remove();
void Clear();
protected:
int32_t OpenFileRead(); int32_t OpenFileWrite();
void StartNewLoads();
bool StartLoad(int32_t iFromClient, const C4Network2ResChunkData &Chunks);
void EndLoad();
void ClearLoad();
void RemoveLoad(C4Network2ResLoad *pLoad);
void RemoveCChunks(ClientChunks *pChunks);
bool OptimizeStandalone(bool fSilent);
};
class C4Network2ResChunk : public C4PacketBase
{
public:
C4Network2ResChunk();
~C4Network2ResChunk() override;
protected:
int32_t iResID;
uint32_t iChunk;
StdBuf Data;
public:
int32_t getResID() const { return iResID; }
uint32_t getChunkNr() const { return iChunk; }
bool Set(C4Network2Res *pRes, uint32_t iChunk);
bool AddTo(C4Network2Res *pRes, C4Network2IO *pIO) const;
void CompileFunc(StdCompiler *pComp) override;
};
class C4Network2ResList : protected CStdCSecExCallback // run by network thread
{
friend class C4Network2Res;
friend class C4Network2;
public:
C4Network2ResList();
~C4Network2ResList() override;
protected:
C4Network2Res *pFirst{nullptr};
CStdCSecEx ResListCSec;
CStdCSec ResListAddCSec;
int32_t iClientID{-1}, iNextResID;
CStdCSec ResIDCSec;
// timings
int32_t iLastDiscover{0}, iLastStatus{0};
// object used for network i/o
C4Network2IO *pIO{nullptr};
public:
// initialization
bool Init(int32_t iClientID, C4Network2IO *pIOClass); // by main thread
void SetLocalID(int32_t iClientID); // by both
protected:
int32_t nextResID(); // by main thread
C4Network2Res *getRes(int32_t iResID); // by both
C4Network2Res *getRes(const char *szFile, bool fLocalOnly); // by both
public:
// returns referenced resource ptrs
C4Network2Res::Ref getRefRes(int32_t iResID); // by both
C4Network2Res::Ref getRefRes(const char *szFile, bool fLocalOnly = false); // by both
C4Network2Res::Ref getRefNextRes(int32_t iResID); // by both
void Add(C4Network2Res *pRes); // by both
C4Network2Res::Ref AddByFile(const char *strFilePath, bool fTemp, C4Network2ResType eType, int32_t iResID = -1, const char *szResName = nullptr, bool fAllowUnloadable = false); // by both
C4Network2Res::Ref AddByGroup(C4Group *pGrp, bool fTemp, C4Network2ResType eType, int32_t iResID = -1, const char *szResName = nullptr, bool fAllowUnloadable = false); // by both
C4Network2Res::Ref AddByCore(const C4Network2ResCore &Core, bool fLoad = true); // by main thread
C4Network2Res::Ref AddLoad(const C4Network2ResCore &Core); // by main thread
void RemoveAtClient(int32_t iClientID); // by main thread
void Clear(); // by main thread
bool SendDiscover(C4Network2IOConnection *pTo = nullptr); // by both
void OnClientConnect(C4Network2IOConnection *pConn); // by main thread
// interface for C4Network2IO
void HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn);
void OnTimer();
// CStdCSecExCallback
void OnShareFree(CStdCSecEx *pCSec) override;
// for C4Network2Res
C4Network2IO *getIOClass() { return pIO; }
protected:
void OnResComplete(C4Network2Res *pRes);
// misc
bool CreateNetworkFolder();
bool FindTempResFileName(const char *szFilename, char *pTarget);
};
// * Packets *
class C4PacketResStatus : public C4PacketBase
{
public:
C4PacketResStatus();
C4PacketResStatus(int32_t iResID, const C4Network2ResChunkData &nChunks);
protected:
int32_t iResID;
C4Network2ResChunkData Chunks;
public:
int32_t getResID() const { return iResID; }
const C4Network2ResChunkData &getChunks() const { return Chunks; }
void CompileFunc(StdCompiler *pComp) override;
};
class C4PacketResDiscover : public C4PacketBase
{
public:
C4PacketResDiscover();
protected:
int32_t iDisIDs[16], iDisIDCnt{0};
public:
int32_t getDisIDCnt() const { return iDisIDCnt; }
int32_t getDisID(int32_t i) const { return iDisIDs[i]; }
bool isIDPresent(int32_t iID) const;
bool AddDisID(int32_t iID);
void CompileFunc(StdCompiler *pComp) override;
};
class C4PacketResRequest : public C4PacketBase
{
public:
C4PacketResRequest(int32_t iID = -1, int32_t iChunk = -1);
protected:
int32_t iReqID, iReqChunk;
public:
int32_t getReqID() const { return iReqID; }
int32_t getReqChunk() const { return iReqChunk; }
void CompileFunc(StdCompiler *pComp) override;
};
#endif // INC_C4Network2Res