]> git.lizzy.rs Git - dragonfireclient.git/blob - src/client/clientmedia.h
Server pushing media at runtime (#9961)
[dragonfireclient.git] / src / client / clientmedia.h
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #pragma once
21
22 #include "irrlichttypes.h"
23 #include "filecache.h"
24 #include <ostream>
25 #include <map>
26 #include <set>
27 #include <vector>
28 #include <unordered_map>
29
30 class Client;
31 struct HTTPFetchResult;
32
33 #define MTHASHSET_FILE_SIGNATURE 0x4d544853 // 'MTHS'
34 #define MTHASHSET_FILE_NAME "index.mth"
35
36 // Store file into media cache (unless it exists already)
37 // Validating the hash is responsibility of the caller
38 bool clientMediaUpdateCache(const std::string &raw_hash,
39         const std::string &filedata);
40
41 class ClientMediaDownloader
42 {
43 public:
44         ClientMediaDownloader();
45         ~ClientMediaDownloader();
46
47         float getProgress() const {
48                 if (m_uncached_count >= 1)
49                         return 1.0f * m_uncached_received_count /
50                                 m_uncached_count;
51
52                 return 0.0f;
53         }
54
55         bool isStarted() const {
56                 return m_initial_step_done;
57         }
58
59         // If this returns true, the downloader is done and can be deleted
60         bool isDone() const {
61                 return m_initial_step_done &&
62                         m_uncached_received_count == m_uncached_count;
63         }
64
65         // Add a file to the list of required file (but don't fetch it yet)
66         void addFile(const std::string &name, const std::string &sha1);
67
68         // Add a remote server to the list; ignored if not built with cURL
69         void addRemoteServer(const std::string &baseurl);
70
71         // Steps the media downloader:
72         // - May load media into client by calling client->loadMedia()
73         // - May check media cache for files
74         // - May add files to media cache
75         // - May start remote transfers by calling httpfetch_async
76         // - May check for completion of current remote transfers
77         // - May start conventional transfers by calling client->request_media()
78         // - May inform server that all media has been loaded
79         //   by calling client->received_media()
80         // After step has been called once, don't call addFile/addRemoteServer.
81         void step(Client *client);
82
83         // Must be called for each file received through TOCLIENT_MEDIA
84         void conventionalTransferDone(
85                         const std::string &name,
86                         const std::string &data,
87                         Client *client);
88
89 private:
90         struct FileStatus {
91                 bool received;
92                 std::string sha1;
93                 s32 current_remote;
94                 std::vector<s32> available_remotes;
95         };
96
97         struct RemoteServerStatus {
98                 std::string baseurl;
99                 s32 active_count;
100         };
101
102         void initialStep(Client *client);
103         void remoteHashSetReceived(const HTTPFetchResult &fetch_result);
104         void remoteMediaReceived(const HTTPFetchResult &fetch_result,
105                         Client *client);
106         s32 selectRemoteServer(FileStatus *filestatus);
107         void startRemoteMediaTransfers();
108         void startConventionalTransfers(Client *client);
109
110         bool checkAndLoad(const std::string &name, const std::string &sha1,
111                         const std::string &data, bool is_from_cache,
112                         Client *client);
113
114         std::string serializeRequiredHashSet();
115         static void deSerializeHashSet(const std::string &data,
116                         std::set<std::string> &result);
117
118         // Maps filename to file status
119         std::map<std::string, FileStatus*> m_files;
120
121         // Array of remote media servers
122         std::vector<RemoteServerStatus*> m_remotes;
123
124         // Filesystem-based media cache
125         FileCache m_media_cache;
126
127         // Has an attempt been made to load media files from the file cache?
128         // Have hash sets been requested from remote servers?
129         bool m_initial_step_done = false;
130
131         // Total number of media files to load
132         s32 m_uncached_count = 0;
133
134         // Number of media files that have been received
135         s32 m_uncached_received_count = 0;
136
137         // Status of remote transfers
138         unsigned long m_httpfetch_caller;
139         unsigned long m_httpfetch_next_id = 0;
140         long m_httpfetch_timeout = 0;
141         s32 m_httpfetch_active = 0;
142         s32 m_httpfetch_active_limit = 0;
143         s32 m_outstanding_hash_sets = 0;
144         std::unordered_map<unsigned long, std::string> m_remote_file_transfers;
145
146         // All files up to this name have either been received from a
147         // remote server or failed on all remote servers, so those files
148         // don't need to be looked at again
149         // (use m_files.upper_bound(m_name_bound) to get an iterator)
150         std::string m_name_bound = "";
151
152 };