forked from cataclysmbnteam/Cataclysm-BN
-
Notifications
You must be signed in to change notification settings - Fork 0
/
stats_tracker.h
197 lines (165 loc) · 6.95 KB
/
stats_tracker.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#ifndef CATA_SRC_STATS_TRACKER_H
#define CATA_SRC_STATS_TRACKER_H
#include <memory>
#include <set>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "cata_variant.h"
#include "event.h"
#include "event_bus.h"
#include "hash_utils.h"
#include "string_id.h"
class JsonIn;
class JsonOut;
class event_statistic;
class event_transformation;
enum class monotonically : int;
class score;
class stats_tracker;
// The stats_tracker is intended to keep a summary of events that have occured.
// For each event_type it stores an event_multiset.
// Within the event_tracker, counts are kept. The events are partitioned
// according to their data (an event::data_type object, which is a map of keys
// to values).
// The stats_tracker can be queried in various ways to get summary statistics
// about events that have occured.
class event_multiset
{
public:
using counts_type = std::unordered_map<cata::event::data_type, int, cata::range_hash>;
// Default constructor for deserialization deliberately uses invalid
// type
event_multiset() : type_( event_type::num_event_types ) {}
event_multiset( event_type type ) : type_( type ) {}
void set_type( event_type );
const counts_type &counts() const {
return counts_;
}
// count returns the number of events matching given criteria that have
// occured.
// total returns the sum of some integer-valued field across every
// event satisfying certain criteria.
// maximum and minimum return the max and min respectively of some
// integer-valued field across every event satisfying certain criteria.
// For example, count might return the number of times the avatar has
// taken damage, total might return the total damage taken in all
// those cases, and maximum might return the single largest amount
// of damage taken in a single event across all those cases.
// The criteria takes the form of an event::data_type map specifying
// some values that must be matched in the events of that type. You can
// provide just a subset of the relevant keys from the event_type in
// your criteria.
int count() const;
int count( const cata::event::data_type &criteria ) const;
int total( const std::string &field ) const;
int total( const std::string &field, const cata::event::data_type &criteria ) const;
int minimum( const std::string &field ) const;
int maximum( const std::string &field ) const;
void add( const cata::event & );
void add( const counts_type::value_type & );
void serialize( JsonOut & ) const;
void deserialize( JsonIn & );
private:
event_type type_;
counts_type counts_;
};
class base_watcher
{
public:
base_watcher() = default;
base_watcher( const base_watcher & ) = delete;
base_watcher &operator=( const base_watcher & ) = delete;
protected:
virtual ~base_watcher();
private:
friend class stats_tracker;
void on_subscribe( stats_tracker * );
void on_unsubscribe( stats_tracker * );
stats_tracker *subscribed_to = nullptr;
};
class stat_watcher : public base_watcher
{
public:
virtual void new_value( const cata_variant &, stats_tracker & ) = 0;
};
class event_multiset_watcher : public base_watcher
{
public:
virtual void event_added( const cata::event &, stats_tracker & ) = 0;
virtual void events_reset( const event_multiset &, stats_tracker & ) = 0;
};
template<typename Watcher>
class watcher_set
{
static_assert( std::is_base_of<base_watcher, Watcher>::value,
"Watcher must be derived from base_watcher" );
public:
void insert( Watcher *watcher ) {
watchers_.insert( watcher );
}
bool erase( base_watcher *watcher ) {
return watchers_.erase( watcher );
}
template<typename Class, typename... FnArgs, typename... Args>
void send_to_all( void ( Class::*mem_fn )( FnArgs... ), Args &&... args ) const {
static_assert( std::is_base_of<Class, Watcher>::value,
"Watcher must be derived from Class" );
// Sending an event to a watcher can cause it to be erased, so we
// need to always ensure we have the next iterator prepared in
// advance.
auto current = watchers_.begin();
while( current != watchers_.end() ) {
auto next = current;
++next;
( static_cast<Watcher *>( *current )->*mem_fn )( args... );
current = next;
}
}
private:
std::set<base_watcher *> watchers_;
};
class stats_tracker_state
{
public:
virtual ~stats_tracker_state() = 0;
};
class stats_tracker : public event_subscriber
{
public:
~stats_tracker() override;
event_multiset &get_events( event_type );
event_multiset get_events( const string_id<event_transformation> & );
cata_variant value_of( const string_id<event_statistic> & );
void add_watcher( event_type, event_multiset_watcher * );
void add_watcher( const string_id<event_transformation> &, event_multiset_watcher * );
void add_watcher( const string_id<event_statistic> &, stat_watcher * );
void unwatch( base_watcher * );
void transformed_set_changed( const string_id<event_transformation> &,
const cata::event &new_element );
void transformed_set_changed( const string_id<event_transformation> &,
const event_multiset &new_value );
void stat_value_changed( const string_id<event_statistic> &,
const cata_variant &new_value );
// Return all scores which are valid now and existed at game start
std::vector<const score *> valid_scores() const;
void clear();
void notify( const cata::event & ) override;
void serialize( JsonOut & ) const;
void deserialize( JsonIn & );
private:
void unwatch_all();
std::unordered_map<event_type, event_multiset> data;
std::unordered_map<event_type, watcher_set<event_multiset_watcher>> event_type_watchers;
std::unordered_map<string_id<event_transformation>, watcher_set<event_multiset_watcher>>
event_transformation_watchers;
std::unordered_map<string_id<event_statistic>, watcher_set<stat_watcher>> stat_watchers;
std::unordered_map<string_id<event_transformation>, std::unique_ptr<stats_tracker_state>>
event_transformation_states;
std::unordered_map<string_id<event_statistic>, std::unique_ptr<stats_tracker_state>>
stat_states;
std::unordered_set<string_id<score>> initial_scores;
};
#endif // CATA_SRC_STATS_TRACKER_H