RunicVTT Open Source Virtual Tabletop for TTRPG using P2P
Loading...
Searching...
No Matches
IdentityManager.cpp
Go to the documentation of this file.
1// IdentityManager.cpp
2#include "IdentityManager.h"
3#include "Serializer.h"
4#include <filesystem>
5#include <fstream>
6#include <algorithm>
7#include "PathManager.h"
8
9using std::string;
10
11static constexpr const char* kME_MAGIC = "RUNIC-ME";
12static constexpr const int kME_VER = 1;
13
14static constexpr const char* kBOOK_MAGIC = "RUNIC-BOOK";
15static constexpr const int kBOOK_VER = 1;
16
18{
19 static const std::string s_me = (PathManager::getConfigPath() / "identity_me.runic").string();
20 return s_me.c_str();
21}
23{
24 static const std::string s_book = (PathManager::getConfigPath() / "identity_book.runic").string();
25 return s_book.c_str();
26}
27// ------------------ public: my identity ------------------
28
30{
31 myUniqueId_.clear();
32 myUsername_.clear();
33 return readMeFile();
34}
35
37{
38 return writeMeFile();
39}
40
41void IdentityManager::setMyIdentity(const std::string& uniqueId, const std::string& username)
42{
43 myUniqueId_ = uniqueId;
44 myUsername_ = username;
46
47 // also mirror into address book
48 auto& pi = byUnique_[uniqueId];
49 pi.uniqueId = uniqueId;
50 if (!pi.username.empty() && pi.username != username)
51 {
52 pi.usernamesHistory.emplace_back(pi.username);
53 if (pi.usernamesHistory.size() > 10) // cap
54 pi.usernamesHistory.erase(pi.usernamesHistory.begin());
55 }
56 pi.username = username;
58}
59
60// ------------------ public: address book ------------------
61
63{
64 byUnique_.clear();
65 peerToUnique_.clear();
66 return readBookFile();
67}
68
70{
71 return writeBookFile();
72}
73//
74//void IdentityManager::bindPeer(const std::string& peerId,
75// const std::string& uniqueId,
76// const std::string& username)
77//{
78// // session binding
79// peerToUnique_[peerId] = uniqueId;
80//
81// auto& pi = byUnique_[uniqueId];
82// pi.uniqueId = uniqueId;
83// pi.peerId = peerId;
84//
85// if (!pi.username.empty() && pi.username != username)
86// {
87// pi.usernamesHistory.emplace_back(pi.username);
88// if (pi.usernamesHistory.size() > 10)
89// pi.usernamesHistory.erase(pi.usernamesHistory.begin());
90// }
91// pi.username = username;
92//
93// (void)saveAddressBookToFile(); // optional: you can defer saving if you prefer
94//}
95
96void IdentityManager::bindPeer(const std::string& peerId,
97 const std::string& uniqueId,
98 const std::string& username)
99{
100 // 1) If this peerId was previously mapped to some unique, clear that first.
101 if (auto it = peerToUnique_.find(peerId); it != peerToUnique_.end())
102 {
103 const std::string& oldUid = it->second;
104 if (oldUid != uniqueId)
105 {
106 // Unlink old mapping (the old unique keeps its record, but without this peerId)
107 auto bit = byUnique_.find(oldUid);
108 if (bit != byUnique_.end() && bit->second.peerId == peerId)
109 bit->second.peerId.clear();
110 }
111 peerToUnique_.erase(it);
112 }
113
114 // 2) If this uniqueId already had a different peerId, remove that stale reverse mapping.
115 auto& pi = byUnique_[uniqueId];
116 if (!pi.peerId.empty() && pi.peerId != peerId)
117 {
118 peerToUnique_.erase(pi.peerId); // remove old peerId -> uniqueId
119 }
120
121 // 3) Set the fresh links (one-to-one at a time)
122 peerToUnique_[peerId] = uniqueId;
123
124 pi.uniqueId = uniqueId;
125 pi.peerId = peerId;
126
127 if (!pi.username.empty() && pi.username != username)
128 {
129 pi.usernamesHistory.emplace_back(pi.username);
130 if (pi.usernamesHistory.size() > 10)
131 pi.usernamesHistory.erase(pi.usernamesHistory.begin());
132 }
133 pi.username = username;
134
135 (void)saveAddressBookToFile();
136}
137
138void IdentityManager::erasePeer(const std::string& peerId)
139{
140 if (auto it = peerToUnique_.find(peerId); it != peerToUnique_.end())
141 {
142 const std::string uid = it->second;
143 peerToUnique_.erase(it);
144 auto bit = byUnique_.find(uid);
145 if (bit != byUnique_.end() && bit->second.peerId == peerId)
146 bit->second.peerId.clear();
147 }
148}
149
150// ------------------ public: lookups ------------------
151void IdentityManager::setUsernameForUnique(const std::string& uniqueId, const std::string& username)
152{
153 if (uniqueId == myUniqueId_)
154 {
155 if (myUsername_ != username)
156 {
157 myUsername_ = username;
158 (void)saveMyIdentityToFile();
159 }
160 }
161
162 auto& pi = byUnique_[uniqueId];
163 if (!pi.username.empty() && pi.username != username)
164 {
165 pi.usernamesHistory.emplace_back(pi.username);
166 if (pi.usernamesHistory.size() > 10)
167 pi.usernamesHistory.erase(pi.usernamesHistory.begin());
168 }
169 pi.uniqueId = uniqueId;
170 pi.username = username;
171 (void)saveAddressBookToFile();
172}
173
174std::string IdentityManager::usernameForUnique(const std::string& uniqueId) const
175{
176 auto it = byUnique_.find(uniqueId);
177 if (it != byUnique_.end() && !it->second.username.empty())
178 return it->second.username;
179
180 if (uniqueId == myUniqueId_ && !myUsername_.empty())
181 return myUsername_;
182
183 // fallback: show truncated uniqueId if unknown
184 if (uniqueId.size() > 8)
185 return uniqueId.substr(0, 8);
186 return uniqueId;
187}
188
189std::optional<std::string> IdentityManager::uniqueForPeer(const std::string& peerId) const
190{
191 if (auto it = peerToUnique_.find(peerId); it != peerToUnique_.end())
192 return it->second;
193 return std::nullopt;
194}
195
196std::optional<std::string> IdentityManager::peerForUnique(const std::string& uniqueId) const
197{
198 if (auto it = byUnique_.find(uniqueId); it != byUnique_.end() && !it->second.peerId.empty())
199 return it->second.peerId;
200 return std::nullopt;
201}
202
203//std::optional<std::string> IdentityManager::uniqueForPeer(const std::string& peerId) const
204//{
205// auto it = peerToUnique_.find(peerId);
206// if (it == peerToUnique_.end())
207// return std::nullopt;
208// return it->second;
209//}
210//
211//std::optional<std::string> IdentityManager::peerForUnique(const std::string& uniqueId) const
212//{
213// for (const auto& kv : peerToUnique_)
214// if (kv.second == uniqueId)
215// return kv.first;
216// return std::nullopt;
217//}
218
219std::optional<std::string> IdentityManager::usernameForPeer(const std::string& peerId) const
220{
221 for (const auto& kv : peerToUnique_)
222 if (kv.first == peerId)
223 return usernameForUnique(kv.second);
224 return std::nullopt;
225}
226
227// ------------------ private: files ------------------
228
230{
231 try
232 {
233 std::vector<uint8_t> buf;
238
239 std::ofstream os(kMeFile(), std::ios::binary | std::ios::trunc);
240 os.write(reinterpret_cast<const char*>(buf.data()),
241 static_cast<std::streamsize>(buf.size()));
242 return true;
243 }
244 catch (...)
245 {
246 return false;
247 }
248}
249
251{
252 namespace fs = std::filesystem;
253 if (!fs::exists(kMeFile()))
254 return true; // first run is fine
255
256 try
257 {
258 std::ifstream is(kMeFile(), std::ios::binary);
259 is.seekg(0, std::ios::end);
260 auto sz = is.tellg();
261 is.seekg(0, std::ios::beg);
262 std::vector<uint8_t> buf(static_cast<size_t>(sz));
263 if (sz > 0)
264 is.read(reinterpret_cast<char*>(buf.data()), sz);
265
266 size_t off = 0;
268 return false;
269 (void)Serializer::deserializeInt(buf, off); // ver
272 return true;
273 }
274 catch (...)
275 {
276 return false;
277 }
278}
279
281{
282 try
283 {
284 std::vector<uint8_t> buf;
287
288 // count
289 Serializer::serializeInt(buf, static_cast<int>(byUnique_.size()));
290 for (const auto& [uid, pi] : byUnique_)
291 {
292 Serializer::serializeString(buf, pi.uniqueId);
293 Serializer::serializeString(buf, pi.username);
294 Serializer::serializeString(buf, pi.peerId);
295
296 // history
297 Serializer::serializeInt(buf, static_cast<int>(pi.usernamesHistory.size()));
298 for (const auto& old : pi.usernamesHistory)
300 }
301
302 std::ofstream os(kBookFile(), std::ios::binary | std::ios::trunc);
303 os.write(reinterpret_cast<const char*>(buf.data()),
304 static_cast<std::streamsize>(buf.size()));
305 return true;
306 }
307 catch (...)
308 {
309 return false;
310 }
311}
312
314{
315 namespace fs = std::filesystem;
316 if (!fs::exists(kBookFile()))
317 return true;
318
319 try
320 {
321 std::ifstream is(kBookFile(), std::ios::binary);
322 is.seekg(0, std::ios::end);
323 auto sz = is.tellg();
324 is.seekg(0, std::ios::beg);
325 std::vector<uint8_t> buf(static_cast<size_t>(sz));
326 if (sz > 0)
327 is.read(reinterpret_cast<char*>(buf.data()), sz);
328
329 size_t off = 0;
331 return false;
332 (void)Serializer::deserializeInt(buf, off); // ver
333
334 int n = Serializer::deserializeInt(buf, off);
335 // NOTE: const method; we need to cast away const to fill the map or
336 // make this non-const. Easiest: make readBookFile() non-const (done above).
337 // So keep this implementation but ensure header declares it non-const.
338 return false; // (safety: this should never compile with const)
339 }
340 catch (...)
341 {
342 return false;
343 }
344}
static constexpr const int kME_VER
static constexpr const char * kBOOK_MAGIC
static constexpr const char * kME_MAGIC
static constexpr const int kBOOK_VER
void setMyIdentity(const std::string &uniqueId, const std::string &username)
bool saveMyIdentityToFile() const
bool saveAddressBookToFile() const
std::optional< std::string > peerForUnique(const std::string &uniqueId) const
void bindPeer(const std::string &peerId, const std::string &uniqueId, const std::string &username)
void setUsernameForUnique(const std::string &uniqueId, const std::string &username)
std::optional< std::string > uniqueForPeer(const std::string &peerId) const
void erasePeer(const std::string &peerId)
bool writeMeFile() const
std::string usernameForUnique(const std::string &uniqueId) const
static const char * kMeFile()
bool writeBookFile() const
std::string myUniqueId_
std::optional< std::string > usernameForPeer(const std::string &peerId) const
std::unordered_map< std::string, PeerIdentity > byUnique_
std::string myUsername_
static const char * kBookFile()
std::unordered_map< std::string, std::string > peerToUnique_
static fs::path getConfigPath()
Definition PathManager.h:86
static int deserializeInt(const std::vector< unsigned char > &buffer, size_t &offset)
Definition Serializer.h:367
static std::string deserializeString(const std::vector< unsigned char > &buffer, size_t &offset)
Definition Serializer.h:388
static void serializeString(std::vector< unsigned char > &buffer, const std::string &str)
Definition Serializer.h:318
static void serializeInt(std::vector< unsigned char > &buffer, int value)
Definition Serializer.h:302