1 /* 2 * OpenClonk, http://www.openclonk.org 3 * 4 * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/ 5 * Copyright (c) 2013-2016, The OpenClonk Team and contributors 6 * 7 * Distributed under the terms of the ISC license; see accompanying file 8 * "COPYING" for details. 9 * 10 * "Clonk" is a registered trademark of Matthes Bender, used with permission. 11 * See accompanying file "TRADEMARK" for details. 12 * 13 * To redistribute this file separately, substitute the full license texts 14 * for the above references. 15 */ 16 // network resource: data needed for the game (scenario, plr files, definitions...) 17 18 #ifndef INC_C4Network2Res 19 #define INC_C4Network2Res 20 21 #include "lib/StdAdaptors.h" 22 #include "platform/StdSync.h" 23 24 #include "lib/SHA1.h" 25 26 #include <atomic> 27 28 const uint32_t C4NetResChunkSize = 10U * 1024U; 29 30 const int32_t C4NetResDiscoverTimeout = 10, // (s) 31 C4NetResDiscoverInterval = 1, // (s) 32 C4NetResStatusInterval = 1, // (s) 33 C4NetResMaxLoad = 5, 34 C4NetResLoadTimeout = 60, // (s) 35 C4NetResDeleteTime = 60, // (s) 36 C4NetResMaxBigicon = 20; // maximum size, in KB, of bigicon 37 38 const int32_t C4NetResIDAnonymous = -2; 39 40 enum C4Network2ResType 41 { 42 NRT_Null=0, 43 NRT_Scenario, 44 NRT_Dynamic, 45 NRT_Player, 46 NRT_Definitions, 47 NRT_System, 48 NRT_Material 49 }; 50 51 const StdEnumEntry<C4Network2ResType> C4Network2ResType_EnumMap[] = 52 { 53 { "Scenario", NRT_Scenario }, 54 { "Dynamic", NRT_Dynamic }, 55 { "Player", NRT_Player }, 56 { "Definitions", NRT_Definitions }, 57 { "System", NRT_System }, 58 { "Material", NRT_Material }, 59 }; 60 61 // damn circular dependencies 62 #include "network/C4PacketBase.h" 63 #include "network/C4Network2IO.h" 64 class C4Network2ResList; 65 class C4Network2ResChunk; 66 67 // classes 68 class C4Network2ResCore : public C4PacketBase 69 { 70 public: 71 C4Network2ResCore(); 72 73 protected: 74 C4Network2ResType eType{NRT_Null}; 75 int32_t iID{-1}, iDerID{-1}; 76 StdCopyStrBuf FileName; 77 bool fLoadable{false}; 78 uint32_t iFileSize, iFileCRC, iContentsCRC; 79 uint8_t fHasFileSHA{false}; 80 uint8_t FileSHA[SHA_DIGEST_LENGTH]; 81 uint32_t iChunkSize; 82 83 public: getType()84 C4Network2ResType getType() const { return eType; } isNull()85 bool isNull() const { return eType == NRT_Null; } getID()86 int32_t getID() const { return iID; } getDerID()87 int32_t getDerID() const { return iDerID; } isLoadable()88 bool isLoadable() const { return fLoadable; } getFileSize()89 uint32_t getFileSize() const { return iFileSize; } getFileCRC()90 uint32_t getFileCRC() const { return iFileCRC; } getContentsCRC()91 uint32_t getContentsCRC()const { return iContentsCRC; } hasFileSHA()92 bool hasFileSHA() const { return !!fHasFileSHA; } getFileSHA()93 const uint8_t*getFileSHA() const { return FileSHA; } getFileName()94 const char * getFileName() const { return FileName.getData(); } getChunkSize()95 uint32_t getChunkSize() const { return iChunkSize; } getChunkCnt()96 uint32_t getChunkCnt() const { return iFileSize && iChunkSize ? (iFileSize - 1) / iChunkSize + 1 : 0; } 97 98 void Set(C4Network2ResType eType, int32_t iResID, const char *strFileName, uint32_t iContentsCRC); SetID(int32_t inID)99 void SetID(int32_t inID) { iID = inID; } SetDerived(int32_t inDerID)100 void SetDerived(int32_t inDerID) { iDerID = inDerID; } 101 void SetLoadable(uint32_t iSize, uint32_t iCRC); SetFileSHA(BYTE * pSHA)102 void SetFileSHA(BYTE *pSHA) { memcpy(FileSHA, pSHA, SHA_DIGEST_LENGTH); fHasFileSHA = true; } 103 void Clear(); 104 105 void CompileFunc(StdCompiler *pComp) override; 106 107 }; 108 109 class C4Network2ResLoad 110 { 111 friend class C4Network2Res; 112 public: 113 C4Network2ResLoad(int32_t iChunk, int32_t iByClient); 114 ~C4Network2ResLoad(); 115 116 protected: 117 // chunk download data 118 int32_t iChunk; 119 time_t Timestamp; 120 int32_t iByClient; 121 122 // list (C4Network2Res) 123 C4Network2ResLoad *pNext; 124 125 public: getChunk()126 int32_t getChunk() const { return iChunk; } getByClient()127 int32_t getByClient() const { return iByClient; } 128 Next()129 C4Network2ResLoad *Next() const { return pNext; } 130 131 bool CheckTimeout(); 132 133 }; 134 135 class C4Network2ResChunkData : public C4PacketBase 136 { 137 public: 138 C4Network2ResChunkData(); 139 C4Network2ResChunkData(const C4Network2ResChunkData &Data2); 140 ~C4Network2ResChunkData() override; 141 142 C4Network2ResChunkData &operator =(const C4Network2ResChunkData &Data2); 143 protected: 144 int32_t iChunkCnt{0}, iPresentChunkCnt{0}; 145 146 // present chunk ranges 147 struct ChunkRange { int32_t Start, Length; ChunkRange *Next; }; 148 ChunkRange *pChunkRanges{nullptr}; 149 int32_t iChunkRangeCnt{0}; 150 151 public: getChunkCnt()152 int32_t getChunkCnt() const { return iChunkCnt; } getPresentChunkCnt()153 int32_t getPresentChunkCnt() const { return iPresentChunkCnt; } getPresentPercent()154 int32_t getPresentPercent() const { return iPresentChunkCnt * 100 / iChunkCnt; } isComplete()155 bool isComplete() const { return iPresentChunkCnt == iChunkCnt; } 156 157 void SetIncomplete(int32_t iChunkCnt); 158 void SetComplete(int32_t iChunkCnt); 159 160 void AddChunk(int32_t iChunk); 161 void AddChunkRange(int32_t iStart, int32_t iLength); 162 void Merge(const C4Network2ResChunkData &Data2); 163 164 void Clear(); 165 166 int32_t GetChunkToRetrieve(const C4Network2ResChunkData &Available, int32_t iLoadingCnt, int32_t *pLoading) const; 167 168 protected: 169 // helpers 170 bool MergeRanges(ChunkRange *pRange); 171 void GetNegative(C4Network2ResChunkData &Target) const; 172 int32_t getPresentChunk(int32_t iNr) const; 173 174 public: 175 void CompileFunc(StdCompiler *pComp) override; 176 }; 177 178 class C4Network2Res 179 { 180 friend class C4Network2ResList; 181 friend class C4Network2ResChunk; 182 public: 183 184 // helper for reference-holding 185 class Ref 186 { 187 public: 188 Ref() = default; Ref(C4Network2Res * pRes)189 Ref(C4Network2Res *pRes) : pRes(pRes) { if (pRes) pRes->AddRef(); } Ref(const Ref & rCopy)190 Ref(const Ref &rCopy) : pRes(rCopy.pRes) { if (pRes) pRes->AddRef(); } ~Ref()191 ~Ref() { Clear(); } 192 Ref &operator = (C4Network2Res *pnRes) { Set(pnRes); return *this; } 193 Ref &operator = (const Ref &rCopy) { Set(rCopy.pRes); return *this; } 194 private: 195 C4Network2Res *pRes{nullptr}; 196 public: 197 operator C4Network2Res *() const { return pRes; } 198 bool operator ! () const { return !pRes; } 199 C4Network2Res * operator ->() const { return pRes; } Clear()200 void Clear() { if (pRes) pRes->DelRef(); pRes = nullptr; } Set(C4Network2Res * pnRes)201 void Set(C4Network2Res *pnRes) { if (pRes == pnRes) return; Clear(); pRes = pnRes; if (pRes) pRes->AddRef(); } 202 }; 203 204 C4Network2Res(C4Network2ResList *pnParent); 205 ~C4Network2Res(); 206 207 protected: 208 // core, chunk data 209 C4Network2ResCore Core; 210 C4Network2ResChunkData Chunks; // (only valid while loading) 211 bool fDirty; 212 213 // local file data 214 CStdCSec FileCSec; 215 char szFile[_MAX_PATH + 1], szStandalone[_MAX_PATH + 1]; 216 bool fTempFile, fStandaloneFailed; 217 218 // references 219 std::atomic_long iRefCnt; 220 bool fRemoved; 221 222 // being load? 223 int32_t iLastReqTime; 224 225 // loading 226 bool fLoading; 227 struct ClientChunks { C4Network2ResChunkData Chunks; int32_t ClientID; ClientChunks *Next; } 228 *pCChunks; 229 time_t iDiscoverStartTime; 230 C4Network2ResLoad *pLoads; 231 int32_t iLoadCnt; 232 233 // list (C4Network2ResList) 234 C4Network2Res *pNext; 235 C4Network2ResList *pParent; 236 237 public: getType()238 C4Network2ResType getType() const { return Core.getType(); } getCore()239 const C4Network2ResCore &getCore() const { return Core; } isDirty()240 bool isDirty() const { return fDirty; } isAnonymous()241 bool isAnonymous() const { return getResID() == C4NetResIDAnonymous; } getResID()242 int32_t getResID() const { return Core.getID(); } getResClient()243 int32_t getResClient() const { return Core.getID() >> 16; } getFile()244 const char *getFile() const { return szFile; } getFileCSec()245 CStdCSec *getFileCSec() { return &FileCSec; } getLastReqTime()246 int32_t getLastReqTime()const { return iLastReqTime; } isRemoved()247 bool isRemoved() const { return fRemoved; } isLoading()248 bool isLoading() const { return fLoading; } isComplete()249 bool isComplete() const { return !fLoading; } getPresentPercent()250 int32_t getPresentPercent() const { return fLoading ? Chunks.getPresentPercent() : 100; } isTempFile()251 bool isTempFile() const { return fTempFile; } 252 253 bool SetByFile(const char *strFilePath, bool fTemp, C4Network2ResType eType, int32_t iResID, const char *szResName = nullptr, bool fSilent = false); 254 bool SetByGroup(C4Group *pGrp, bool fTemp, C4Network2ResType eType, int32_t iResID, const char *szResName = nullptr, bool fSilent = false); 255 bool SetByCore(const C4Network2ResCore &nCore, bool fSilent = false, const char *szAsFilename = nullptr, int32_t iRecursion=0); 256 bool SetLoad(const C4Network2ResCore &nCore); 257 258 bool SetDerived(const char *strName, const char *strFilePath, bool fTemp, C4Network2ResType eType, int32_t iDResID); 259 260 void ChangeID(int32_t inID); 261 262 bool IsBinaryCompatible(); 263 bool GetStandalone(char *pTo, int32_t iMaxL, bool fSetOfficial, bool fAllowUnloadable = false, bool fSilent = false); 264 bool CalculateSHA(); 265 266 bool SaveBackFile(); 267 C4Network2Res::Ref Derive(); 268 bool FinishDerive(); 269 bool FinishDerive(const C4Network2ResCore &nCore); 270 271 bool SendStatus(C4Network2IOConnection *pTo = nullptr); 272 bool SendChunk(uint32_t iChunk, int32_t iToClient); 273 274 // references 275 void AddRef(); void DelRef(); 276 277 // events 278 void OnDiscover(C4Network2IOConnection *pBy); 279 void OnStatus(const C4Network2ResChunkData &rChunkData, C4Network2IOConnection *pBy); 280 void OnChunk(const C4Network2ResChunk &rChunk); 281 bool DoLoad(); 282 283 bool NeedsDiscover(); 284 285 C4Group *OpenAsGrp() const; 286 287 void Remove(); 288 void Clear(); 289 290 protected: 291 int32_t OpenFileRead(); int32_t OpenFileWrite(); 292 293 void StartNewLoads(); 294 bool StartLoad(int32_t iFromClient, const C4Network2ResChunkData &Chunks); 295 void EndLoad(); 296 void ClearLoad(); 297 298 void RemoveLoad(C4Network2ResLoad *pLoad); 299 void RemoveCChunks(ClientChunks *pChunks); 300 301 bool OptimizeStandalone(bool fSilent); 302 303 }; 304 305 class C4Network2ResChunk : public C4PacketBase 306 { 307 public: 308 C4Network2ResChunk(); 309 ~C4Network2ResChunk() override; 310 311 protected: 312 int32_t iResID; 313 uint32_t iChunk; 314 StdBuf Data; 315 316 public: getResID()317 int32_t getResID() const { return iResID; } getChunkNr()318 uint32_t getChunkNr() const { return iChunk; } 319 320 bool Set(C4Network2Res *pRes, uint32_t iChunk); 321 bool AddTo(C4Network2Res *pRes, C4Network2IO *pIO) const; 322 323 void CompileFunc(StdCompiler *pComp) override; 324 }; 325 326 class C4Network2ResList : protected CStdCSecExCallback // run by network thread 327 { 328 friend class C4Network2Res; 329 friend class C4Network2; 330 public: 331 C4Network2ResList(); 332 ~C4Network2ResList() override; 333 334 protected: 335 336 C4Network2Res *pFirst{nullptr}; 337 CStdCSecEx ResListCSec; 338 CStdCSec ResListAddCSec; 339 340 int32_t iClientID{-1}, iNextResID; 341 CStdCSec ResIDCSec; 342 343 // timings 344 int32_t iLastDiscover{0}, iLastStatus{0}; 345 346 // object used for network i/o 347 C4Network2IO *pIO{nullptr}; 348 349 public: 350 351 // initialization 352 bool Init(int32_t iClientID, C4Network2IO *pIOClass); // by main thread 353 void SetLocalID(int32_t iClientID); // by both 354 355 protected: 356 int32_t nextResID(); // by main thread 357 358 C4Network2Res *getRes(int32_t iResID); // by both 359 C4Network2Res *getRes(const char *szFile, bool fLocalOnly); // by both 360 361 public: 362 // returns referenced resource ptrs 363 C4Network2Res::Ref getRefRes(int32_t iResID); // by both 364 C4Network2Res::Ref getRefRes(const char *szFile, bool fLocalOnly = false); // by both 365 C4Network2Res::Ref getRefNextRes(int32_t iResID); // by both 366 367 void Add(C4Network2Res *pRes); // by both 368 C4Network2Res::Ref AddByFile(const char *strFilePath, bool fTemp, C4Network2ResType eType, int32_t iResID = -1, const char *szResName = nullptr, bool fAllowUnloadable = false); // by both 369 C4Network2Res::Ref AddByGroup(C4Group *pGrp, bool fTemp, C4Network2ResType eType, int32_t iResID = -1, const char *szResName = nullptr, bool fAllowUnloadable = false); // by both 370 C4Network2Res::Ref AddByCore(const C4Network2ResCore &Core, bool fLoad = true); // by main thread 371 C4Network2Res::Ref AddLoad(const C4Network2ResCore &Core); // by main thread 372 373 void RemoveAtClient(int32_t iClientID); // by main thread 374 void Clear(); // by main thread 375 376 bool SendDiscover(C4Network2IOConnection *pTo = nullptr); // by both 377 void OnClientConnect(C4Network2IOConnection *pConn); // by main thread 378 379 // interface for C4Network2IO 380 void HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn); 381 void OnTimer(); 382 383 // CStdCSecExCallback 384 void OnShareFree(CStdCSecEx *pCSec) override; 385 386 // for C4Network2Res getIOClass()387 C4Network2IO *getIOClass() { return pIO; } 388 389 protected: 390 void OnResComplete(C4Network2Res *pRes); 391 392 // misc 393 bool CreateNetworkFolder(); 394 bool FindTempResFileName(const char *szFilename, char *pTarget); 395 396 }; 397 398 // * Packets * 399 400 class C4PacketResStatus : public C4PacketBase 401 { 402 public: 403 C4PacketResStatus(); 404 C4PacketResStatus(int32_t iResID, const C4Network2ResChunkData &nChunks); 405 406 protected: 407 int32_t iResID; 408 C4Network2ResChunkData Chunks; 409 410 public: getResID()411 int32_t getResID() const { return iResID; } getChunks()412 const C4Network2ResChunkData &getChunks() const { return Chunks; } 413 414 void CompileFunc(StdCompiler *pComp) override; 415 }; 416 417 class C4PacketResDiscover : public C4PacketBase 418 { 419 public: 420 C4PacketResDiscover(); 421 422 protected: 423 int32_t iDisIDs[16], iDisIDCnt{0}; 424 425 public: getDisIDCnt()426 int32_t getDisIDCnt() const { return iDisIDCnt; } getDisID(int32_t i)427 int32_t getDisID(int32_t i) const { return iDisIDs[i]; } 428 bool isIDPresent(int32_t iID) const; 429 430 bool AddDisID(int32_t iID); 431 432 void CompileFunc(StdCompiler *pComp) override; 433 }; 434 435 class C4PacketResRequest : public C4PacketBase 436 { 437 public: 438 C4PacketResRequest(int32_t iID = -1, int32_t iChunk = -1); 439 440 protected: 441 int32_t iReqID, iReqChunk; 442 443 public: getReqID()444 int32_t getReqID() const { return iReqID; } getReqChunk()445 int32_t getReqChunk() const { return iReqChunk; } 446 447 void CompileFunc(StdCompiler *pComp) override; 448 }; 449 450 #endif // INC_C4Network2Res 451