The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
map.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2009 by Rusty Russell <[email protected]>
3  Copyright (C) 2010 - 2016 by Guillaume Melquiond <[email protected]>
4  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /** @file */
17 
18 #include "units/id.hpp"
19 #include "log.hpp"
20 #include "units/unit.hpp"
21 
22 #include <functional>
23 #include "units/map.hpp"
24 
25 static lg::log_domain log_engine("engine");
26 #define ERR_NG LOG_STREAM(err, log_engine)
27 #define WRN_NG LOG_STREAM(warn, log_engine)
28 #define LOG_NG LOG_STREAM(info, log_engine)
29 #define DBG_NG LOG_STREAM(debug, log_engine)
30 
32  : umap_()
33  , lmap_()
34 {
35 }
36 
38  : umap_()
39  , lmap_()
40 {
41  for (const_unit_iterator i = that.begin(); i != that.end(); ++i) {
42  add(i->get_location(), *i);
43  }
44 }
45 
47  unit_map temp(that);
48  swap(temp);
49  return *this;
50 }
51 
53  assert(num_iters()==0 && o.num_iters() == 0);
54 
55  std::swap(umap_, o.umap_);
56  std::swap(lmap_, o.lmap_);
57 }
58 
60  clear(true);
61 }
62 
64  self_check();
65  t_umap::iterator i = umap_.begin();
66  while (i != umap_.end() && (!i->second.unit)) { ++i; }
67  return i;
68 }
69 
70 std::pair<unit_map::unit_iterator, bool> unit_map::add(const map_location &l, const unit &u) {
71  self_check();
72  unit_ptr p = unit_ptr (new unit(u)); //TODO: should this instead take a shared pointer to a unit, rather than make a copy?
73  p->set_location(l);
74  std::pair<unit_map::unit_iterator, bool> res( insert(p) );
75  if(res.second == false) { p.reset(); }
76  return res;
77 }
78 
79 std::pair<unit_map::unit_iterator, bool> unit_map::move(const map_location &src, const map_location &dst) {
80  self_check();
81  DBG_NG << "Unit map: Moving unit from " << src << " to " << dst << "\n";
82 
83  //Find the unit at the src location
84  t_lmap::iterator i = lmap_.find(src);
85  if(i == lmap_.end()) { return std::make_pair(make_unit_iterator(i), false);}
86 
87  t_umap::iterator uit(i->second);
88 
89  if(src == dst){ return std::make_pair(make_unit_iterator(uit), true);}
90 
91  //Fail if there is no unit to move
92  unit_ptr p = uit->second.unit;
93  if(!p){ return std::make_pair(make_unit_iterator(uit), false);}
94 
95  p->set_location(dst);
96 
97  lmap_.erase(i);
98 
99  std::pair<t_lmap::iterator,bool> res = lmap_.insert(std::make_pair(dst, uit));
100 
101  //Fail and don't move if the destination is already occupied
102  if(res.second == false) {
103  p->set_location(src);
104  lmap_.insert(std::make_pair(src, uit));
105  return std::make_pair(make_unit_iterator(uit), false);
106  }
107 
108  self_check();
109 
110  return std::make_pair(make_unit_iterator(uit), true);
111 }
112 
113 
114 /** Inserts the unit pointed to by @a p into the unit_map.
115 
116 It needs to succeed on the insertion to the umap and to the lmap
117 otherwise all operations are reverted.
118 1. Construct a unit_pod
119 2. Try insertion into the umap
120 3. Try insertion in the lmap and remove the umap entry on failure
121 
122 The one oddity is that to facilitate non-invalidating iterators the list
123 sometimes has nullptr pointers which should be used when they correspond
124 to uids previously used.
125  */
126 std::pair<unit_map::unit_iterator, bool> unit_map::insert(unit_ptr p) {
127  self_check();
128  assert(p);
129 
130  size_t unit_id = p->underlying_id();
131  const map_location &loc = p->get_location();
132 
133  if (!loc.valid()) {
134  ERR_NG << "Trying to add " << p->name()
135  << " - " << p->id() << " at an invalid location; Discarding.\n";
136  return std::make_pair(make_unit_iterator(umap_.end()), false);
137  }
138 
139  unit_pod upod;
140  upod.unit = p ;
141 
142  DBG_NG << "Adding unit " << p->underlying_id() << " - " << p->id()
143  << " to location: (" << loc << ")\n";
144 
145  std::pair<t_umap::iterator, bool> uinsert = umap_.insert(std::make_pair(unit_id, upod ));
146 
147  if (! uinsert.second) {
148  //If the pod is empty reinsert the unit in the same list element
149  if (!uinsert.first->second.unit) {
150  unit_pod &opod = uinsert.first->second;
151  opod.unit = p ;
152  assert(opod.ref_count != 0);
153  } else {
154  unit_ptr q = uinsert.first->second.unit;
155  ERR_NG << "Trying to add " << p->name()
156  << " - " << p->id() << " - " << p->underlying_id()
157  << " (" << loc << ") over " << q->name()
158  << " - " << q->id() << " - " << q->underlying_id()
159  << " (" << q->get_location()
160  << ").";
161 
162  p->clone(false);
163  ERR_NG << "The new unit was assigned underlying_id="
164  << p->underlying_id()
165  << " to prevent duplicate id conflicts.\n";
166 
167  uinsert = umap_.insert(std::make_pair(p->underlying_id(), upod ));
168  int guard(0);
169  while (!uinsert.second && (++guard < 1e6) ) {
170  if(guard % 10 == 9){
171  ERR_NG << "\n\nPlease Report this error to https://gna.org/bugs/index.php?18591 "
172  "\nIn addition to the standard details of operating system and wesnoth version "
173  "and how it happened, please answer the following questions "
174  "\n 1. Were you playing multi-player?"
175  "\n 2. Did you start/restart/reload the game/scenario?"
176  "\nThank you for your help in fixing this bug.\n";
177  }
178  p->clone(false);
179  uinsert = umap_.insert(std::make_pair(p->underlying_id(), upod )); }
180  if (!uinsert.second) {
181  throw game::error("One million collisions in unit_map"); }
182  }
183  }
184 
185  std::pair<t_lmap::iterator,bool> linsert = lmap_.insert(std::make_pair(loc, uinsert.first ));
186 
187  //Fail if the location is occupied
188  if(! linsert.second) {
189  if(upod.ref_count == 0) {
190  //Undo a virgin insertion
191  umap_.erase(uinsert.first);
192  } else {
193  //undo a reinsertion
194  uinsert.first->second.unit.reset();
195  }
196  DBG_NG << "Trying to add " << p->name()
197  << " - " << p->id() << " at location ("<<loc <<"); Occupied by "
198  <<(linsert.first->second->second).unit->name()<< " - " << linsert.first->second->second.unit->id() <<"\n";
199 
200  return std::make_pair(make_unit_iterator(umap_.end()), false);
201  }
202 
203  self_check();
204  return std::make_pair( make_unit_iterator( uinsert.first ), true);
205 }
206 
207 std::pair<unit_map::unit_iterator, bool> unit_map::replace(const map_location &l, const unit &u) {
208  self_check();
209  //when 'l' is the reference to map_location that is part
210  //of that unit iterator which is to be deleted by erase,
211  // 'l' is invalidated by erase, too. Thus, 'add(l,u)' fails.
212  // So, we need to make a copy of that map_location.
213  map_location loc = l;
214  erase(loc);
215  return add(loc, u);
216 }
217 
218 size_t unit_map::num_iters() const {
219  ///Add up number of extant iterators
220  size_t num_iters(0);
221  t_umap::const_iterator ui = umap_.begin();
222  t_umap::const_iterator uend = umap_.end();
223  for( ; ui != uend ; ++ui){
224  if(ui->second.ref_count < 0) {
225  //Somewhere, someone generated 2^31 iterators to this unit
226  bool a_reference_counter_overflowed(false);
227  assert(a_reference_counter_overflowed);
228  }
229  num_iters += ui->second.ref_count;
230  }
231 
232  return num_iters;
233 }
234 
235 void unit_map::clear(bool force) {
236  assert(force || (num_iters() == 0));
237 
238  for (t_umap::iterator i = umap_.begin(); i != umap_.end(); ++i) {
239  if (is_valid(i)) {
240  DBG_NG << "Delete unit " << i->second.unit->underlying_id() << "\n";
241  i->second.unit.reset();
242  }
243  }
244 
245  lmap_.clear();
246  umap_.clear();
247 }
248 
250  self_check();
251  t_lmap::iterator i = lmap_.find(loc);
252  if (i == lmap_.end()) { return unit_ptr(); }
253 
254  t_umap::iterator uit(i->second);
255 
256  unit_ptr u = uit->second.unit;
257  size_t uid( u->underlying_id() );
258 
259  DBG_NG << "Extract unit " << uid << " - " << u->id()
260  << " from location: (" << loc << ")\n";
261 
262  assert(uit->first == uit->second.unit->underlying_id());
263  if(uit->second.ref_count == 0){
264  umap_.erase(uit);
265  } else {
266  //Soft extraction keeps the old lit item if any iterators reference it
267  uit->second.unit.reset();
268  }
269 
270  lmap_.erase(i);
271  self_check();
272 
273  return u;
274 }
275 
276 
277 size_t unit_map::erase(const map_location &loc) {
278  self_check();
279  unit_ptr u = extract(loc);
280  if (!u) return 0;
281  u.reset();
282  return 1;
283 }
284 
286  self_check();
287  t_umap::iterator i(umap_.find(id));
288  if((i != umap_.end()) && !i->second.unit){ i = umap_.end() ;}
289  return make_unit_iterator<t_umap::iterator>( i );
290 }
291 
293  self_check();
294  return make_unit_iterator<t_lmap::iterator>(lmap_.find(loc) );
295 }
296 
298  unit_map::iterator i = begin(), i_end = end();
299  for (; i != i_end; ++i) {
300  if (i->side() == side && i->can_recruit())
301  return i;
302  }
303  return i_end;
304 }
305 
307  unit_map::iterator i = begin(), i_end = end();
308  unit_map::iterator first_leader = end();
309 
310  for (; i != i_end; ++i) {
311  if (i->side() == side && i->can_recruit()){
312  if ((first_leader == end()) || (i->underlying_id() < first_leader->underlying_id()) )
313  first_leader = i;
314  }
315  }
316  return first_leader;
317 }
318 
319 std::vector<unit_map::unit_iterator> unit_map::find_leaders(int side) {
320  unit_map::unit_iterator i = begin(), i_end = end();
321  std::vector<unit_map::unit_iterator> leaders;
322  for(;i != i_end; ++i){
323  if(i->side() == side && i->can_recruit()){
324  leaders.push_back(i);
325  }
326  }
327  return leaders;
328 }
329 std::vector<unit_map::const_unit_iterator> unit_map::find_leaders(int side)const{
330  const std::vector<unit_map::unit_iterator> &leaders = const_cast<unit_map*>(this)->find_leaders(side);
331  std::vector<unit_map::const_unit_iterator> const_leaders (leaders.begin(), leaders.end());
332  return const_leaders;
333 }
334 
335 #ifdef DEBUG_UNIT_MAP
336 
337 bool unit_map::self_check() const {
338  bool good(true);
339 
340  t_umap::const_iterator uit(umap_.begin());
341  for(; uit != umap_.end(); ++uit){
342  if(uit->second.ref_count < 0){
343  good=false;
344  ERR_NG << "unit_map pod ref_count <0 is " << uit->second.ref_count<< std::endl;
345  }
346  if(uit->second.unit){
347  uit->second.unit->id(); //crash if bad pointer
348  }
349  if(uit->first <= 0){
350  good=false;
351  ERR_NG << "unit_map umap uid <=0 is " << uit->first << std::endl;
352  }
353  if(!uit->second.unit && uit->second.ref_count == 0 ){
354  good=false;
355  ERR_NG << "unit_map umap unit==nullptr when refcount == 0" << std::endl;
356  }
357  if(uit->second.unit && uit->second.unit->underlying_id() != uit->first){
358  good=false;
359  ERR_NG << "unit_map umap uid("<<uit->first<<") != underlying_id()["<< uit->second.unit->underlying_id()<< "]" << std::endl;
360  }
361  }
362  t_lmap::const_iterator locit(lmap_.begin());
363  for(; locit != lmap_.end(); ++locit){
364  if(locit->second == umap_.end() ){
365  good=false;
366  ERR_NG << "unit_map lmap element == umap_.end() "<< std::endl;
367  }
368  if(locit->first != locit->second->second.unit->get_location()){
369  good=false;
370  ERR_NG << "unit_map lmap location != unit->get_location() " << std::endl;
371  }
372  }
373  //assert(good);
374  return good;
375 }
376 
377 #endif
378 
379 bool unit_map::has_unit(const unit * const u) const
380 {
381  assert(u);
382 
383  for(const t_umap::value_type& item : umap_) {
384  if(item.second.unit.get() == u) {
385  return true;
386  }
387  }
388  return false;
389 }
std::vector< unit_iterator > find_leaders(int side)
Definition: map.cpp:319
unit_iterator end()
Definition: map.hpp:311
Definition: unit.hpp:95
#define ERR_NG
Definition: map.cpp:26
const t_string & name() const
The unit name for display.
Definition: unit.hpp:158
unit_map & operator=(const unit_map &that)
Definition: map.cpp:46
unit_iterator find_leader(int side)
Definition: map.cpp:297
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:29
t_lmap lmap_
location -> umap::iterator.
Definition: map.hpp:431
unit_iterator begin()
Definition: map.hpp:308
bool self_check() const
Checks invariants. For debugging purposes only. Doesn't do anything in non-debug mode.
Definition: map.hpp:380
n_ref_counter::t_ref_counter< signed int > ref_count
Definition: map.hpp:102
#define DBG_NG
Definition: map.cpp:29
GLenum src
Definition: glew.h:2392
GLdouble l
Definition: glew.h:6966
GLdouble GLdouble GLdouble GLdouble q
Definition: glew.h:1382
t_umap umap_
underlying_id -> unit_pod.
Definition: map.hpp:426
bool valid() const
Definition: location.hpp:69
std::pair< unit_iterator, bool > insert(unit_ptr p)
Adds the unit to the map.
Definition: map.cpp:126
GLenum GLenum dst
Definition: glew.h:2392
void clear(bool force=false)
Definition: map.cpp:235
bool is_valid(const t_umap::const_iterator &i) const
Definition: map.hpp:401
GLfloat GLfloat p
Definition: glew.h:12766
std::pair< unit_iterator, bool > replace(const map_location &l, const unit &u)
Works like unit_map::add; but l is emptied first, if needed.
Definition: map.cpp:207
std::pair< unit_iterator, bool > add(const map_location &l, const unit &u)
Adds a copy of unit u at location l of the map.
Definition: map.cpp:70
Encapsulates the map of the game.
Definition: location.hpp:38
void swap(unit_map &o)
Definition: map.cpp:52
unit_ptr unit
Definition: map.hpp:101
GLuint res
Definition: glew.h:9258
t_umap::iterator begin_core() const
Definition: map.cpp:63
unit_iterator find_first_leader(int side)
Definition: map.cpp:306
size_t i
Definition: function.cpp:1057
bool has_unit(const unit *const u) const
Is the unit in the map?
Definition: map.cpp:379
std::pair< unit_iterator, bool > move(const map_location &src, const map_location &dst)
Moves a unit from location src to location dst.
Definition: map.cpp:79
unit_map::unit_iterator make_unit_iterator(X const &i)
Definition: map.hpp:412
size_t erase(const map_location &l)
Erases the unit at location l, if any.
Definition: map.cpp:277
The pointer to the unit and a reference counter to record the number of extant iterators pointing to ...
Definition: map.hpp:93
boost::intrusive_ptr< unit > unit_ptr
Definition: ptr.hpp:29
void swap(game_board &one, game_board &other)
Definition: game_board.cpp:56
unit_ptr extract(const map_location &loc)
Extracts a unit from the map.
Definition: map.cpp:249
Standard logging facilities (interface).
unit_map()
Definition: map.cpp:31
static lg::log_domain log_engine("engine")
Container associating units to locations.
Definition: map.hpp:90
~unit_map()
Definition: map.cpp:59
unit_iterator find(size_t id)
Definition: map.cpp:285
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
size_t num_iters() const
Definition: map.cpp:218