unit_map.cpp

Go to the documentation of this file.
00001 /* $Id: unit_map.cpp 52533 2012-01-07 02:35:17Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2006 - 2009 by Rusty Russell <rusty@rustcorp.com.au>
00004    Copyright (C) 2010 - 2012 by Guillaume Melquiond <guillaume.melquiond@gmail.com>
00005    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2 of the License, or
00010    (at your option) any later version.
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY.
00013 
00014    See the COPYING file for more details.
00015 */
00016 
00017 /** @file */
00018 
00019 #include "unit_id.hpp"
00020 #include "foreach.hpp"
00021 #include "log.hpp"
00022 #include "unit.hpp"
00023 
00024 #include <functional>
00025 #include "unit_map.hpp"
00026 
00027 static lg::log_domain log_engine("engine");
00028 #define ERR_NG LOG_STREAM(err, log_engine)
00029 #define WRN_NG LOG_STREAM(warn, log_engine)
00030 #define LOG_NG LOG_STREAM(info, log_engine)
00031 #define DBG_NG LOG_STREAM(debug, log_engine)
00032 
00033 unit_map::unit_map()
00034     : umap_()
00035     , lmap_()
00036     , ilist_()
00037     , the_end_()
00038 {
00039     init_end();
00040 }
00041 
00042 unit_map::unit_map(const unit_map& that)
00043     : umap_()
00044     , lmap_()
00045     , ilist_()
00046     , the_end_()
00047 {
00048     init_end();
00049     for (const_unit_iterator i = that.begin(); i != that.end(); ++i) {
00050         add(i->get_location(), *i);
00051     }
00052 }
00053 
00054 unit_map &unit_map::operator=(const unit_map &that) {
00055     unit_map temp(that);
00056     swap(temp);
00057     return *this;
00058 }
00059 
00060 void unit_map::swap(unit_map &o) {
00061     assert(num_iters()==0 && o.num_iters() == 0);
00062 
00063     std::swap(umap_, o.umap_);
00064     std::swap(lmap_, o.lmap_);
00065     std::swap(ilist_, o.ilist_);
00066     std::swap(the_end_, o.the_end_);
00067 }
00068 
00069 unit_map::~unit_map() {
00070     clear(true);
00071 }
00072 
00073 unit_map::t_ilist::iterator unit_map::begin_core() const {
00074     self_check();
00075     t_ilist::iterator i = ilist_.begin();
00076     while (i != the_end_ && (i->unit == NULL)) { ++i; }
00077     return i;
00078 }
00079 
00080 std::pair<unit_map::unit_iterator, bool> unit_map::add(const map_location &l, const unit &u) {
00081     self_check();
00082     unit *p = new unit(u);
00083     p->set_location(l);
00084     std::pair<unit_map::unit_iterator, bool> res( insert(p) );
00085     if(res.second == false) { delete p; }
00086     return res;
00087 }
00088 
00089 std::pair<unit_map::unit_iterator, bool> unit_map::move(const map_location &src, const map_location &dst) {
00090     self_check();
00091     DBG_NG << "Unit map: Moving unit from " << src << " to " << dst << "\n";
00092 
00093     //Find the unit at the src location
00094     t_lmap::iterator i = lmap_.find(src);
00095     if(i == lmap_.end()) { return std::make_pair(make_unit_iterator(the_end_), false);}
00096 
00097     t_ilist::iterator lit(i->second);
00098 
00099     if(src == dst){ return std::make_pair(make_unit_iterator(lit),true);}
00100 
00101     //Fail if there is no unit to move
00102     unit *p = lit->unit;
00103     if(p == NULL){ return std::make_pair(make_unit_iterator(lit), false);}
00104 
00105     p->set_location(dst);
00106 
00107     ///@todo upgrade to quick_erase when boost 1.42 supported by wesnoth
00108     lmap_.erase(i);
00109 
00110     std::pair<t_lmap::iterator,bool> res = lmap_.insert(std::make_pair(dst, lit));
00111 
00112     //Fail and don't move if the destination is already occupied
00113     if(res.second == false) {
00114         p->set_location(src);
00115         lmap_.insert(std::make_pair(src, lit));
00116         return std::make_pair(make_unit_iterator(lit), false);
00117     }
00118 
00119     self_check();
00120 
00121     return std::make_pair(make_unit_iterator(lit), true);
00122 }
00123 
00124 
00125 /** Inserts the unit pointed to by @a p into the unit_map.
00126 
00127 It needs to succeed on the insertion to the umap and to the lmap
00128 otherwise all operations are reverted.
00129 1. Insert a unit_pod into the list
00130 2. Try insertion into the umap and remove the list item on failure
00131 3. Try insertion in the lmap and remove the umap and the list item on failure
00132 
00133 The one oddity is that to facilitate non-invalidating iterators the list
00134 sometimes has NULL pointers which should be used when they correspond
00135 to uids previously used.
00136  */
00137 std::pair<unit_map::unit_iterator, bool> unit_map::insert(unit *p) {
00138     self_check();
00139     assert(p);
00140 
00141     size_t unit_id = p->underlying_id();
00142     const map_location &loc = p->get_location();
00143 
00144     if (!loc.valid()) {
00145         ERR_NG << "Trying to add " << p->name()
00146             << " - " << p->id() << " at an invalid location; Discarding.\n";
00147         return std::make_pair(make_unit_iterator(the_end_), false);
00148     }
00149 
00150     unit_pod upod;
00151     upod.unit = p;
00152     upod.deleted_uid = unit_id;
00153     ilist_.push_front(upod);
00154     t_ilist::iterator lit(ilist_.begin());
00155 
00156     DBG_NG << "Adding unit " << p->underlying_id() << " - " << p->id()
00157         << " to location: (" << loc << ")\n";
00158 
00159     std::pair<t_umap::iterator, bool> uinsert = umap_.insert(std::make_pair(unit_id, lit ));
00160 
00161     if (! uinsert.second) {
00162         //If the UID is empty reinsert the unit in the same list element
00163         if ( uinsert.first->second->unit == NULL) {
00164             ilist_.pop_front();
00165             lit = uinsert.first->second;
00166             lit->unit = p;
00167             assert(lit->ref_count != 0);
00168         } else {
00169             unit *q = uinsert.first->second->unit;
00170             ERR_NG << "Trying to add " << p->name()
00171                    << " - " << p->id() << " - " << p->underlying_id()
00172                    << " ("  << loc << ") over " << q->name()
00173                    << " - " << q->id() << " - " << q->underlying_id()
00174                    << " ("  << q->get_location()
00175                    << "). The new unit will be assigned underlying_id="
00176                    << (1 + n_unit::id_manager::instance().get_save_id())
00177                    << " to prevent duplicate id conflicts.\n";
00178 
00179             p->clone(false);
00180             uinsert = umap_.insert(std::make_pair(p->underlying_id(), lit ));
00181             int guard(0);
00182             while (!uinsert.second && (++guard < 1e6) ) {
00183                 if(guard % 10 == 9){
00184                     ERR_NG << "\n\nPlease Report this error to https://gna.org/bugs/index.php?18591 "
00185                         "\nIn addition to the standard details of operating system and wesnoth version "
00186                         "and how it happened, please answer the following questions "
00187                         "\n 1. Were you playing mutli-player?"
00188                         "\n 2. Did you start/restart/reload the game/scenario?"
00189                         "\nThank you for your help in fixing this bug.\n";
00190                 }
00191                 p->clone(false);
00192                 uinsert = umap_.insert(std::make_pair(p->underlying_id(), lit )); }
00193             if (!uinsert.second) {
00194                 throw "One million collisions in unit_map"; }
00195         }
00196     }
00197 
00198     std::pair<t_lmap::iterator,bool> linsert = lmap_.insert(std::make_pair(loc, lit ));
00199 
00200     //Fail if the location is occupied
00201     if(! linsert.second) {
00202         if(lit->ref_count == 0) {
00203             //Undo a virgin insertion
00204             ilist_.pop_front();
00205             ///@todo replace with quick_erase(i) when wesnoth supports  boost 1.42 min version
00206             umap_.erase(uinsert.first);
00207         } else {
00208             //undo a reinsertion
00209             uinsert.first->second->unit = NULL;
00210         }
00211         DBG_NG << "Trying to add " << p->name()
00212                << " - " << p->id() << " at location ("<<loc <<"); Occupied  by "
00213                <<(linsert.first->second)->unit->name()<< " - " << linsert.first->second->unit->id() <<"\n";
00214 
00215         return std::make_pair(make_unit_iterator(the_end_), false);
00216     }
00217 
00218     self_check();
00219     return std::make_pair( make_unit_iterator( lit ), true);
00220 }
00221 
00222 std::pair<unit_map::unit_iterator, bool> unit_map::replace(const map_location &l, const unit &u) {
00223     self_check();
00224     //when 'l' is the reference to map_location that is part
00225     //of that unit iterator which is to be deleted by erase,
00226     // 'l' is invalidated by erase, too. Thus, 'add(l,u)' fails.
00227     // So, we need to make a copy of that map_location.
00228     map_location loc = l;
00229     erase(loc);
00230     return add(loc, u);
00231 }
00232 
00233 size_t unit_map::num_iters() const  {
00234     ///Add up number of extant iterators
00235     size_t num_iters(0);
00236     t_ilist::const_iterator ii(ilist_.begin());
00237     for( ; ii != the_end_ ; ++ii){
00238         if(ii->ref_count < 0) {
00239             //Somewhere, someone generated 2^31 iterators to this unit
00240             bool a_reference_counter_overflowed(false);
00241             assert(a_reference_counter_overflowed); }
00242         num_iters += ii->ref_count; }
00243 
00244     return num_iters;
00245 }
00246 
00247 void unit_map::clear(bool force) {
00248     assert(force  || (num_iters() == 0));
00249 
00250     for (t_ilist::iterator i = ilist_.begin(); i != the_end_; ++i) {
00251         if (is_valid(i)) {
00252             DBG_NG << "Delete unit " << i->unit->underlying_id() << "\n";
00253             delete i->unit;
00254         }
00255     }
00256 
00257     lmap_.clear();
00258     umap_.clear();
00259     ilist_.clear();
00260 }
00261 
00262 unit *unit_map::extract(const map_location &loc) {
00263     self_check();
00264     t_lmap::iterator i = lmap_.find(loc);
00265     if (i == lmap_.end()) { return NULL; }
00266 
00267     t_ilist::iterator lit(i->second);
00268 
00269     unit *u = lit->unit;
00270     size_t uid( u->underlying_id() );
00271 
00272     DBG_NG << "Extract unit " << uid << " - " << u->id()
00273             << " from location: (" << loc << ")\n";
00274 
00275     if(lit->ref_count == 0){
00276         assert(lit != the_end_);
00277         if(umap_.erase(uid) != 1){
00278             error_recovery_externally_changed_uid(lit); }
00279         ilist_.erase( lit );
00280     } else {
00281         //Soft extraction keeps the old lit item if any iterators reference it
00282         lit->unit = NULL;
00283         lit->deleted_uid = uid;
00284         assert( uid != 0);
00285     }
00286 
00287     lmap_.erase(i);
00288     self_check();
00289 
00290     return u;
00291 }
00292 
00293 /** error_recovery_externally_changed_uid is called when an attempt is made to
00294     delete a umap_ element, which fails because the underlying_id() has been changed externally.
00295     It searches the entire umap_ to for a matching ilist_ element to erase.
00296  */
00297 void unit_map::error_recovery_externally_changed_uid(t_ilist::iterator const & lit) const {
00298     std::string name, id ;
00299     size_t uid;
00300     if(lit->unit != NULL){
00301         unit const * u(lit->unit);
00302         name = u->name();
00303         id = u->id();
00304         uid = u->underlying_id();
00305     } else {
00306         name = "unknown";
00307         id = "unknown";
00308         uid = lit->deleted_uid;
00309     }
00310     t_umap::iterator uit(umap_.begin());
00311     for(; uit != umap_.end(); ++uit){
00312         if(uit->second == lit){ break; }
00313     }
00314     std::stringstream oldidss;
00315     if (uit != umap_.end()) {
00316         size_t olduid =  uit->first ;
00317         umap_.erase(uit);
00318         oldidss << olduid;
00319     } else {
00320         oldidss << "unknown";
00321     }
00322 
00323     ERR_NG << "Extracting " << name << " - " << id
00324            << " from unit map. Underlying ID changed from "<< oldidss.str()  << " to " << uid
00325             << " between insertion and extraction, requiring an exhaustive search of umap_.\n";
00326 }
00327 
00328 
00329 size_t unit_map::erase(const map_location &loc) {
00330     self_check();
00331     unit *u = extract(loc);
00332     if (!u) return 0;
00333     delete u;
00334     return 1;
00335 }
00336 
00337 unit_map::unit_iterator unit_map::find(size_t id) {
00338     self_check();
00339     t_umap::iterator i(umap_.find(id));
00340     if((i != umap_.end()) && i->second->unit==NULL){ i = umap_.end() ;}
00341     return make_unit_iterator<t_umap::iterator>( i ); }
00342 
00343 unit_map::unit_iterator unit_map::find(const map_location &loc) {
00344     self_check();
00345     return make_unit_iterator<t_lmap::iterator>(lmap_.find(loc) ); }
00346 
00347 unit_map::unit_iterator unit_map::find_leader(int side) {
00348     unit_map::iterator i = begin(), i_end = end();
00349     for (; i != i_end; ++i) {
00350         if (static_cast<int>(i->side()) == side && i->can_recruit())
00351             return i;
00352     }
00353     return i_end;
00354 }
00355 
00356 unit_map::unit_iterator unit_map::find_first_leader(int side) {
00357     unit_map::iterator i = begin(), i_end = end();
00358     unit_map::iterator first_leader = end();
00359 
00360     for (; i != i_end; ++i) {
00361         if (static_cast<int>(i->side()) == side && i->can_recruit()){
00362             if ((first_leader == end()) || (i->underlying_id() < first_leader->underlying_id()) )
00363                 first_leader = i;
00364         }
00365     }
00366     return first_leader;
00367 }
00368 
00369 std::vector<unit_map::unit_iterator> unit_map::find_leaders(int side) {
00370     unit_map::unit_iterator i = begin(), i_end = end();
00371     std::vector<unit_map::unit_iterator> leaders;
00372     for(;i != i_end; ++i){
00373         if(static_cast<int>(i->side()) == side && i->can_recruit()){
00374             leaders.push_back(i);
00375         }
00376     }
00377     return leaders;
00378 }
00379 std::vector<unit_map::const_unit_iterator> unit_map::find_leaders(int side)const{
00380     const std::vector<unit_map::unit_iterator> &leaders = const_cast<unit_map*>(this)->find_leaders(side);
00381     std::vector<unit_map::const_unit_iterator> const_leaders;
00382     std::copy(leaders.begin(), leaders.end(), std::back_inserter(const_leaders));
00383     return const_leaders;
00384 }
00385 
00386 #ifdef DEBUG
00387 
00388 bool unit_map::self_check() const {
00389     bool found_the_end(false), good(true);
00390     t_ilist::const_iterator lit(ilist_.begin());
00391     for(; lit != ilist_.end(); ++lit){
00392         if(lit == the_end_){ found_the_end = true; continue; }
00393         if(lit->ref_count < 0){
00394             good=false;
00395             ERR_NG << "unit_map list element ref_count <0 is " << lit->ref_count<<"\n"; }
00396         if(lit->unit != NULL){
00397             lit->unit->id(); //crash if bad pointer
00398         } else {
00399             if(lit->ref_count <= 0){
00400                 good=false;
00401                 ERR_NG << "unit_map list element ref_count <=0 is " << lit->ref_count<<", when unit deleted.\n"; }
00402             if(lit->deleted_uid <= 0 ){
00403                 good=false;
00404                 ERR_NG << "unit_map list element deleted_uid <=0 is " << lit->deleted_uid<<"\n"; }
00405         }
00406     }
00407 
00408     if(!found_the_end){
00409         good=false;
00410         ERR_NG << "unit_map list the_end_ is missing. " <<"\n";}
00411 
00412     t_umap::const_iterator uit(umap_.begin());
00413     for(; uit != umap_.end(); ++uit){
00414         if(uit->first <= 0){
00415             good=false;
00416             ERR_NG << "unit_map umap uid <=0 is " << uit->first <<"\n"; }
00417         if(uit->second == the_end_ ){
00418             good=false;
00419             ERR_NG << "unit_map umap element == the_end_ "<<"\n"; }
00420         if(uit->second->unit == NULL && uit->second->ref_count == 0 ){
00421             good=false;
00422             ERR_NG << "unit_map umap unit==NULL when refcount == 0 uid="<<uit->second->deleted_uid<<"\n";
00423         }
00424         if(uit->second->unit && uit->second->unit->underlying_id() != uit->first){
00425             good=false;
00426             ERR_NG << "unit_map umap uid("<<uit->first<<") != underlying_id()["<< uit->second->unit->underlying_id()<< "]\n"; }
00427     }
00428     t_lmap::const_iterator locit(lmap_.begin());
00429     for(; locit != lmap_.end(); ++locit){
00430         if(locit->second == the_end_ ){
00431             good=false;
00432             ERR_NG << "unit_map lmap element == the_end_ "<<"\n"; }
00433         if(locit->first != locit->second->unit->get_location()){
00434             good=false;
00435             ERR_NG << "unit_map lmap location != unit->get_location() " <<"\n"; }
00436     }
00437     //assert(good);
00438     return good;
00439 }
00440 
00441 #endif
00442 
00443 bool unit_map::has_unit(const unit * const u)
00444 {
00445     assert(u);
00446 
00447     foreach(const unit_pod& item, ilist_) {
00448         if(item.unit == u) {
00449             return true;
00450         }
00451     }
00452     return false;
00453 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:14 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs