Pol  Revision:cb584c9
savedata.cpp
Go to the documentation of this file.
1 
8 #include "savedata.h"
9 
10 #include <cerrno>
11 #include <exception>
12 #include <fstream>
13 
14 #include "../clib/clib_endian.h"
15 #include "../clib/fileutil.h"
16 #include "../clib/iohelp.h"
17 #include "../clib/logfacility.h"
18 #include "../clib/rawtypes.h"
19 #include "../clib/streamsaver.h"
20 #include "../clib/strutil.h"
21 #include "../clib/timer.h"
22 #include "../plib/systemstate.h"
23 #include "globals/object_storage.h"
24 #include "globals/uvars.h"
25 #include "item/item.h"
26 #include "item/itemdesc.h"
27 #include "objecthash.h"
28 #include "storage.h"
29 #include "uobject.h"
30 
31 namespace Pol
32 {
33 namespace Core
34 {
35 // incremental saves. yay.
36 // incrementals are saved in two files: the index and the data.
37 // the index looks like this:
38 // incr-index.1.txt:
39 // modified
40 // {
41 // 0x40002002
42 // 0x40002006
43 // ...
44 // }
45 // deleted
46 // {
47 // 0x40002003
48 // ...
49 // }
50 //
51 // the data just like normal data, except for storage area root items
52 // which are preceeded by:
53 // StorageArea [name]
54 // {
55 // }
56 // each such storage area element is always followed by a single item,
57 // which is the root item.
58 //
59 // all data goes into this one file.
60 
62 {
63  // these will all be processed again in write_dirty_data - but
64  // they'll be clean then. So we'll have to fudge the counters a little.
65 
66  for ( Storage::AreaCont::const_iterator area_itr = gamestate.storage.areas.begin();
67  area_itr != gamestate.storage.areas.end(); ++area_itr )
68  {
69  const StorageArea* area = ( *area_itr ).second;
70 
71  for ( StorageArea::Cont::const_iterator item_itr = area->_items.begin();
72  item_itr != area->_items.end(); ++item_itr )
73  {
74  const Items::Item* item = ( *item_itr ).second;
75  if ( !item->dirty() )
76  {
77  continue;
78  }
79 
81  --objStorageManager.clean_objects; // because this will be counted again
82 
83  if ( !item->orphan() )
84  {
85  // write the storage area header, and the item (but not any contents)
86  sw_data() << "StorageArea " << area->_name << pf_endl << "{" << pf_endl << "}" << pf_endl
87  << pf_endl;
88 
89  item->printSelfOn( sw_data );
90  objStorageManager.modified_serials.push_back( item->serial );
91  }
92  else
93  {
95  }
96  item->clear_dirty();
97  }
98  }
99 }
100 
101 
103  bool& has_nonsaved_owner )
104 {
105  const UObject* owner = obj->owner();
106  if ( owner )
107  {
108  auto id = Items::find_itemdesc( owner->objtype_ );
109  if ( !id.save_on_exit || owner->orphan() || !owner->saveonexit() )
110  {
111  has_nonsaved_owner = true;
112  return;
113  }
114 
115  write_object_dirty_owners( sw_data, owner, has_nonsaved_owner );
116  if ( has_nonsaved_owner )
117  return;
118 
119  if ( owner->dirty() )
120  {
122  // this will get counted again as we iterate through the objecthash
124 
125  owner->printSelfOn( sw_data );
126  objStorageManager.modified_serials.push_back( owner->serial );
127  owner->clear_dirty();
128  }
129  }
130 }
131 
133 {
134  // iterate over the object hash, writing dirty elements.
135  // the only tricky bit here is we want to write dirty containers first.
136  // this includes Characters.
137  ObjectHash::hs::const_iterator citr = objStorageManager.objecthash.begin(),
139  for ( ; citr != end; ++citr )
140  {
141  const UObjectRef& ref = ( *citr ).second;
142  const UObject* obj = ref.get();
143 
144  auto id = Items::find_itemdesc( obj->objtype_ );
145  if ( !id.save_on_exit )
146  continue;
147 
148  if ( !obj->dirty() )
149  {
151  continue;
152  }
153 
154  bool has_nonsaved_owner = false;
155  write_object_dirty_owners( sw_data, obj, has_nonsaved_owner );
156  if ( has_nonsaved_owner )
157  continue;
158 
160  if ( !obj->orphan() )
161  {
162  obj->printSelfOn( sw_data );
163  objStorageManager.modified_serials.push_back( obj->serial );
164  }
165  else
166  {
168  }
169  obj->clear_dirty();
170  }
171 
172  ObjectHash::ds::const_iterator deleted_citr = objStorageManager.objecthash.dirty_deleted_begin(),
174  for ( ; deleted_citr != deleted_end; ++deleted_citr )
175  {
176  u32 serial = ( *deleted_citr );
177  objStorageManager.deleted_serials.push_back( serial );
178  }
180 }
181 
182 void write_index( std::ostream& ofs )
183 {
184  ofs << "Modified" << pf_endl << "{" << pf_endl;
185  for ( unsigned i = 0; i < objStorageManager.modified_serials.size(); ++i )
186  {
187  ofs << "\t0x" << std::hex << objStorageManager.modified_serials[i] << std::dec << pf_endl;
188  }
189  ofs << "}" << pf_endl << pf_endl;
190 
191  ofs << "Deleted" << pf_endl << "{" << pf_endl;
192  for ( unsigned i = 0; i < objStorageManager.deleted_serials.size(); ++i )
193  {
194  ofs << "\t0x" << std::hex << objStorageManager.deleted_serials[i] << std::dec << pf_endl;
195  }
196  ofs << "}" << pf_endl << pf_endl;
197 }
198 
199 bool commit_incremental( const std::string& basename )
200 {
201  std::string datfile = Plib::systemstate.config.world_data_path + basename + ".txt";
202  std::string ndtfile = Plib::systemstate.config.world_data_path + basename + ".ndt";
203 
204  bool any = false;
205 
206  if ( Clib::FileExists( datfile ) )
207  {
208  any = true;
209  if ( unlink( datfile.c_str() ) )
210  {
211  int err = errno;
212  POLLOG_ERROR.Format( "Unable to delete {}: {} ({})\n" ) << datfile << strerror( err ) << err;
213  }
214  }
215  if ( Clib::FileExists( ndtfile ) )
216  {
217  any = true;
218  if ( rename( ndtfile.c_str(), datfile.c_str() ) )
219  {
220  int err = errno;
221  POLLOG_ERROR.Format( "Unable to rename {} to {}: {} ({})\n" )
222  << ndtfile << datfile << strerror( err ) << err;
223  }
224  }
225 
226  return any;
227 }
228 
229 int save_incremental( unsigned int& dirty, unsigned int& clean, long long& elapsed_ms )
230 {
231  if ( !should_write_data() )
232  {
233  dirty = clean = 0;
234  elapsed_ms = 0;
235  return -1;
236  }
237 
239  throw std::runtime_error(
240  "Incremental saves are disabled until the next full save, due to a previous incremental "
241  "save failure (dirty flags are inconsistent)" );
242 
243  try
244  {
245  Tools::Timer<> timer;
247 
250 
251  std::ofstream ofs_data;
252  std::ofstream ofs_index;
253 
254  ofs_data.exceptions( std::ios_base::failbit | std::ios_base::badbit );
255  ofs_index.exceptions( std::ios_base::failbit | std::ios_base::badbit );
256 
257  unsigned save_index = objStorageManager.incremental_save_count + 1;
258  std::string data_basename = "incr-data-" + Clib::decint( save_index );
259  std::string index_basename = "incr-index-" + Clib::decint( save_index );
260  std::string data_pathname = Plib::systemstate.config.world_data_path + data_basename + ".ndt";
261  std::string index_pathname = Plib::systemstate.config.world_data_path + index_basename + ".ndt";
262  Clib::open_file( ofs_data, data_pathname, std::ios::out );
263  Clib::open_file( ofs_index, index_pathname, std::ios::out );
264  Clib::OFStreamWriter sw_data( &ofs_data );
265  write_system_data( sw_data );
266  write_global_properties( sw_data );
267 
268  // TODO:
269  // guilds
270  // resources
271  // datastore
272 
273  write_dirty_storage( sw_data );
274  write_dirty_data( sw_data );
275 
276  write_index( ofs_index );
277 
278  ofs_data.close();
279  ofs_index.close();
280 
283 
284  commit_incremental( data_basename );
285  commit_incremental( index_basename );
287 
288  timer.stop();
291  elapsed_ms = timer.ellapsed();
292  }
293  catch ( std::exception& ex )
294  {
295  POLLOG_ERROR.Format( "Exception during incremental save: {}\n" ) << ex.what();
297  throw;
298  }
299  return 0;
300 }
301 
303 {
304  for ( unsigned save_index = 1;; ++save_index )
305  {
306  std::string data_basename = "incr-data-" + Clib::decint( save_index );
307  std::string index_basename = "incr-index-" + Clib::decint( save_index );
308 
309  bool res1 = commit( data_basename );
310  bool res2 = commit( index_basename );
311  if ( res1 || res2 )
312  {
313  continue;
314  }
315  else
316  {
317  break;
318  }
319  }
320 }
321 }
322 }
void write_dirty_storage(Clib::StreamWriter &sw_data)
Definition: savedata.cpp:61
std::string _name
Definition: storage.h:49
SystemState systemstate
Definition: systemstate.cpp:12
Core::PolConfig config
Definition: systemstate.h:43
std::string world_data_path
Definition: polcfg.h:28
void write_system_data(Clib::StreamWriter &sw)
Definition: uimport.cpp:816
void open_file(std::fstream &ofs, std::string &filename, std::ios::openmode mode)
Definition: iohelp.cpp:17
#define cfBEu32(x)
Definition: clib_endian.h:43
T * get() const
Definition: refptr.h:176
std::string decint(unsigned short v)
Definition: strutil.cpp:64
hs::const_iterator begin() const
Definition: objecthash.cpp:239
void write_dirty_data(Clib::StreamWriter &sw_data)
Definition: savedata.cpp:132
virtual UObject * owner()
Definition: uobject.cpp:214
#define POLLOG_ERROR
Definition: logfacility.h:207
const ItemDesc & find_itemdesc(unsigned int objtype)
Definition: itemdesc.cpp:933
bool dirty() const
Definition: uobject.cpp:141
unsigned int u32
Definition: rawtypes.h:27
ds::const_iterator dirty_deleted_end() const
Definition: objecthash.cpp:254
AreaCont areas
Definition: storage.h:77
virtual void printSelfOn(Clib::StreamWriter &sw) const
Definition: uobject.cpp:327
bool orphan() const
Definition: baseobject.h:119
std::vector< u32 > deleted_serials
void commit_incremental_saves()
Definition: savedata.cpp:302
bool commit_incremental(const std::string &basename)
Definition: savedata.cpp:199
std::vector< u32 > modified_serials
const u32 objtype_
Definition: uobject.h:249
GameState gamestate
Definition: uvars.cpp:74
bool should_write_data()
Definition: uimport.cpp:1015
bool saveonexit() const
Definition: uobject.cpp:385
void write_global_properties(Clib::StreamWriter &sw)
Definition: uimport.cpp:808
int save_incremental(unsigned int &dirty, unsigned int &clean, long long &elapsed_ms)
Definition: savedata.cpp:229
void clear_dirty() const
Definition: uobject.cpp:146
ObjectStorageManager objStorageManager
long long ellapsed() const
Definition: timer.cpp:46
hs::const_iterator end() const
Definition: objecthash.cpp:244
ds::const_iterator dirty_deleted_begin() const
Definition: objecthash.cpp:249
bool FileExists(const char *filename)
Definition: fileutil.cpp:118
void write_index(std::ostream &ofs)
Definition: savedata.cpp:182
void write_object_dirty_owners(Clib::StreamWriter &sw_data, const UObject *obj, bool &has_nonsaved_owner)
Definition: savedata.cpp:102
bool commit(const std::string &basename)
Definition: uimport.cpp:968
#define pf_endl
Definition: proplist.cpp:25
Storage storage
Definition: uvars.h:156
Definition: berror.cpp:12