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