1 #ifndef _IDAllocator_h_
2 #define _IDAllocator_h_
3 
4 #include "../util/Logger.h"
5 #include <unordered_map>
6 #include <vector>
7 #include <random>
8 
9 #include <boost/serialization/access.hpp>
10 
11 /** The IDAllocator coordinates the allocation of new IDs between the server
12     and a number of empires, id consumers.
13 
14     IDs are ints.  The id consumers have disjoint id spaces.  All consumers have
15     a common m_stride and a unique initial offset modulo m_stride. Each id
16     consumer only receives new ids from the set number of numbers x, where
17     (x % m_stride == consumer's initial offset).
18 
19     The IDAllocator is associated with a single empire id.  The constructor
20     creates the server association. Once serialized for another empire_id then
21     it is associated with that new (lesser) empire id, and will no longer have
22     the complete table.
23 
24     TODO:  Replace IDAllocator with using UUID as the id type ID_t instead of int.  This does not
25     require coordination between clients and servers and does not leak any information to clients
26     about other client's allocations.
27 */
28 
29 class IDAllocator {
30 public:
31     using ID_t = int;
32 
33     /** \p client_ids are all the player ids in the game. \p highest_pre_allocated_id is the used for
34         legacy loads to offset the newly allocated id to after the ones from the save game.
35     */
36     IDAllocator(const int server_id,
37                 const std::vector<int>& client_ids,
38                 const ID_t invalid_id,
39                 const ID_t temp_id,
40                 const ID_t highest_pre_allocated_id);
41 
42     /// Return a valid new id.  This is used by both clients and servers.
43     ID_t NewID();
44 
45     /** Return {hard_success, soft_success} where \p hard_success determines if
46         \p id is unused and valid and \p soft_success determines if \p id is in
47         the id space of \p empire_id.  This allows errors that are probably due
48         to legacy loading and order processing to be ignored for now.
49      */
50     std::pair<bool, bool> IsIDValidAndUnused(const ID_t id, const int empire_id);
51 
52     /** UpdateIDAndCheckIfOwned behaves differently on the server and clients.
53 
54         On the server, it determines which client allocated \p id and updates
55         the next allocated id of that client.  It then returns true.  This means
56         that the server has a master list of the maximum id used by each client
57         and can use all ids as valid ids when executing orders.
58 
59         On the client it returns true iff this client allocated \p id and does
60         nothing else. That means that id modulo m_stride == client's offset.*/
61     bool UpdateIDAndCheckIfOwned(const ID_t id);
62 
63     /** ObfuscateBeforeSerialization randomizes which client is using which modulus each turn
64         before IDAllocator is serialized and sent to the clients. */
65     void ObfuscateBeforeSerialization();
66 
67     /** Serialize while stripping out information not known to \p empire_id. */
68     template <typename Archive>
69         void SerializeForEmpire(Archive& ar, const unsigned int version, int empire_id);
70 
71 private:
72     /** Return the empire that should have assigned \p id. */
73     ID_t& AssigningEmpireForID(ID_t id);
74 
75     /// Increment the next assigned id for an empire until it is past checked_id.
76     void IncrementNextAssignedId(const int assigning_empire, const int checked_id);
77 
78     /// Return a string representing the state.
79     std::string StateString() const;
80 
81     ID_t m_invalid_id;
82     ID_t m_temp_id;
83 
84     /// m_stride is used to partition the id space into modulo m_stride sections.
85     ID_t m_stride;
86     /// The zero point or first allocated id.
87     ID_t m_zero;
88 
89     // The server id and the empire id are equal on construction.  Serialization
90     // and deserialization may downgrade empire id to one of the lesser (not
91     // server) ids.
92     int m_server_id;
93     int m_empire_id;
94 
95     // A map from empire id to next used object id;
96     std::unordered_map<int, ID_t> m_empire_id_to_next_assigned_object_id;
97 
98     // An index from id % m_stride to empire ids
99     std::vector<int> m_offset_to_empire_id;
100 
101     /// if less than m_next_assigned_id warn about id exhaustion.
102     ID_t m_warn_threshold;
103     /// Stop assigning ids and start generating errors.
104     ID_t m_exhausted_threshold;
105 
106     /// Random number generator
107     std::mt19937 m_random_generator;
108 };
109 
110 #endif // _IDAllocator_h_
111