Synchronize.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. /******************************************************************************/
  4. /******************************************************************************
  5. World Objects are synchronized in following way:
  6. -Assuming that there will be a lot of objects in all worlds
  7. -Taking that in mind, synchronizing will not exchange all object Version's
  8. -Instead it will exchange AreaVer.obj_ver (version of all objects in an area)
  9. -if local ver is different than server, then request all objects in that area from the server
  10. -if after syncing all objects are identical in local as from the server (including number of objects) then set local AreaVer.obj_ver to the one from server
  11. -in other case {if(syncing changed anything)randomize local AreaVer.obj_ver} and {send different objects from that area to the server}
  12. -modifying objects on the client side in world editor will make them sent individually (not its area and all objects located there, however if many objects were modified in one area, then try groupping and sending those modified objects)
  13. This approach is the best I could think of, alternatives are:
  14. -1. exchange versions for all objects in all worlds separately (this would be lots of ID's and Version's each time client is connected to Server)
  15. -2. modifying objects would send all objects from that area to the Server with the new AreaVer.obj_ver to somehow prevent (upon reconnecting) Server sending objects from modified areas since last time which we already have (however this would result in too big bandwidth each time an object is modified)
  16. -Using above approach I use least bandwitdh (the only downside is AreaVer.obj_ver is modified on the client only at connecting to Server after receiving Server data)
  17. /******************************************************************************/
  18. SynchronizerClass Synchronizer;
  19. /******************************************************************************/
  20. /******************************************************************************/
  21. int SynchronizerClass::CompareDepth(C ElmDepth &a, C ElmDepth &b)
  22. {
  23. if(int c=Compare(a.depth , b.depth ))return c;
  24. return Compare(a.elm->name, b.elm->name);
  25. }
  26. int SynchronizerClass::ElmFile::size()C {return elm.size()+data.size();}
  27. void SynchronizerClass::ElmFile::process()
  28. {
  29. if(compress)
  30. {
  31. data.pos(0); File temp; Compress(data, temp.writeMem(), compress_fast ? ServerNetworkCompression : ClientNetworkCompression, compress_fast ? ServerNetworkCompressionLevel : ClientNetworkCompressionLevel); Swap(data, temp);
  32. }
  33. }
  34. void SynchronizerClass::ElmFile::store(File &f)
  35. {
  36. process();
  37. elm.pos(0); elm.copy(f); data.pos(0); data.copy(f);
  38. }
  39. bool SynchronizerClass::WorldSync::empty()C {return !set_area.elms() && !set_obj.elms();}
  40. int SynchronizerClass::WorldSync::elms()C {return set_area.elms() + set_obj.elms();}
  41. void SynchronizerClass::WorldSync::getServerVer(C UID &world_id) {if(!server_ver)server_ver=Server.world_vers.find(world_id);}
  42. bool SynchronizerClass::Create(WorldSync &world_sync, C UID &world_id, ptr)
  43. {
  44. world_sync.getServerVer(world_id);
  45. world_sync.local_ver=Proj.worldVerRequire(world_id);
  46. return world_sync.server_ver || world_sync.local_ver;
  47. }
  48. bool SynchronizerClass::MiniMapSync::empty()C {return !set_image.elms();}
  49. int SynchronizerClass::MiniMapSync::elms()C {return set_image.elms();}
  50. void SynchronizerClass::MiniMapSync::getServerVer(C UID &mini_map_id) {if(!server_ver)server_ver=Server.mini_map_vers.find(mini_map_id);}
  51. bool SynchronizerClass::Create(MiniMapSync &mini_map_sync, C UID &mini_map_id, ptr)
  52. {
  53. mini_map_sync.getServerVer(mini_map_id);
  54. mini_map_sync.local_ver=Proj.miniMapVerRequire(mini_map_id);
  55. return mini_map_sync.server_ver || mini_map_sync.local_ver;
  56. }
  57. SynchronizerClass::~SynchronizerClass() {clearSync(); thread.del();}
  58. bool SynchronizerClass::CompressFunc(Thread &thread) {return Synchronizer.compressFunc();}
  59. bool SynchronizerClass::compressFunc()
  60. {
  61. if(set_elm_full_file.elms() && Server.smallBuf()) // full elements first
  62. {
  63. SyncLockerEx locker(lock); if(set_elm_full_file.elms())
  64. {
  65. ElmFile elm; Swap(elm, set_elm_full_file.first()); set_elm_full_file.remove(0, true); compressing=true;
  66. {
  67. locker.off();
  68. File cmd; elm.store(cmd.writeMem());
  69. cmd.pos(0); locker.on(); Swap(cmd, cmds.New()); compressing=false;
  70. }
  71. }
  72. }else
  73. if(set_elm_long_file.elms() && Server.smallBuf()) // then long elemenets
  74. {
  75. SyncLockerEx locker(lock); if(set_elm_long_file.elms())
  76. {
  77. ElmFile elm; Swap(elm, set_elm_long_file.first()); set_elm_long_file.remove(0, true); compressing=true;
  78. {
  79. locker.off();
  80. File cmd; elm.store(cmd.writeMem());
  81. cmd.pos(0); locker.on(); Swap(cmd, cmds.New()); compressing=false;
  82. }
  83. }
  84. }else
  85. if(set_tex.elms() && Server.smallBuf()) // last textures
  86. {
  87. SyncLockerEx locker(lock); if(set_tex.elms())
  88. {
  89. UID tex_id=set_tex[0]; set_tex.remove(0, true); File tex_data; if(tex_data.readTry(tex_path+EncodeFileName(tex_id))) // texture can be read on secondary thread because each texture file is written only once (name stands for texture hash, so it will never be overwritten with different data)
  90. {
  91. compressing=true; locker.off();
  92. File cmd; if(ClientWriteSetTexture(cmd.writeMem(), tex_id, tex_data))
  93. {
  94. cmd.pos(0); locker.on(); Swap(cmd, cmds.New()); compressing=false;
  95. }
  96. }
  97. }
  98. }else
  99. {
  100. Time.wait(1);
  101. }
  102. return true;
  103. }
  104. int SynchronizerClass::elmFileSize() {int size=0; SyncLocker locker(lock); REPA(set_elm_full_file)size+=set_elm_full_file[i].size(); REPA(set_elm_long_file)size+=set_elm_long_file[i].size(); return size;}
  105. int SynchronizerClass::queuedElms()
  106. {
  107. int n=set_elm_full_file.elms() + set_elm_long_file.elms() + set_elm_full.elms() + set_elm_long.elms() + set_elm_short.elms() + set_tex.elms() + cmds.elms() + compressing;
  108. REPA( world_sync)n+= world_sync.lockedData(i).elms();
  109. REPA(mini_map_sync)n+=mini_map_sync.lockedData(i).elms();
  110. return n;
  111. }
  112. void SynchronizerClass::clearSync()
  113. {
  114. thread.stop();
  115. if(lock.created()) // this can be called after destructor
  116. {
  117. SyncLocker locker(lock);
  118. set_elm_full .clear(); set_elm_full_file.clear();
  119. set_elm_long .clear(); set_elm_long_file.clear();
  120. set_elm_short.clear();
  121. set_tex .clear();
  122. cmds .clear();
  123. delayed_world_sync.del();
  124. world_sync.del();
  125. mini_map_sync.del();
  126. }
  127. }
  128. bool SynchronizerClass::getCmds(Memc<File> &cmds)
  129. {
  130. cmds.clear();
  131. if(T.cmds.elms())
  132. {
  133. SyncLocker locker(lock);
  134. Swap(cmds, T.cmds);
  135. }
  136. return cmds.elms()>0;
  137. }
  138. void SynchronizerClass::setArea(C UID &world_id, C VecI2 &area_xy)
  139. {
  140. if(Server.canWrite() && world_id.valid())if(WorldSync *ws=world_sync.get(world_id))ws->set_area.binaryInclude(area_xy, Compare);
  141. }
  142. void SynchronizerClass::setObjs(C UID &world_id, Memc<UID> &obj_ids)
  143. {
  144. if(Server.canWrite() && world_id.valid() && obj_ids.elms())if(WorldSync *ws=world_sync.get(world_id))REPA(obj_ids)ws->set_obj.binaryInclude(obj_ids[i], Compare);
  145. }
  146. void SynchronizerClass::setMiniMapImage(C UID &mini_map_id, C VecI2 &image_xy)
  147. {
  148. if(Server.canWrite() && mini_map_id.valid())if(MiniMapSync *mms=mini_map_sync.get(mini_map_id))mms->set_image.binaryInclude(image_xy, Compare);
  149. }
  150. void SynchronizerClass::delayedSetArea(C UID &world_id, C VecI2 &area_xy) // !! must be multi-threaded SAFE !!
  151. {
  152. if(Server.canWrite() && world_id.valid())
  153. {
  154. MapLock ml(delayed_world_sync); // use lock because this may be called on multiple threads by 'Area.setServer'
  155. if(WorldSync *ws=delayed_world_sync.get(world_id))ws->set_area.binaryInclude(area_xy, Compare);
  156. }
  157. }
  158. void SynchronizerClass::delayedSetObj(C UID &world_id, C MemPtr<UID> &obj_id) // !! must be multi-threaded SAFE !!
  159. {
  160. if(Server.canWrite() && world_id.valid())
  161. {
  162. MapLock ml(delayed_world_sync); // use lock because this may be called on multiple threads by 'Area.setChangedObj'
  163. if(WorldSync *ws=delayed_world_sync.get(world_id))
  164. REPA(obj_id)if(obj_id[i].valid())ws->set_obj.binaryInclude(obj_id[i], Compare);
  165. }
  166. }
  167. void SynchronizerClass::erasing(C UID &elm_id)
  168. {
  169. delayed_world_sync.removeKey(elm_id);
  170. world_sync.removeKey(elm_id);
  171. mini_map_sync.removeKey(elm_id);
  172. set_elm_full .exclude (elm_id, true);
  173. set_elm_long .exclude (elm_id, true);
  174. set_elm_short.exclude (elm_id, true);
  175. }
  176. void SynchronizerClass::erasingTex(C UID &tex_id)
  177. {
  178. SyncLocker locker(lock);
  179. set_tex.exclude(tex_id, true);
  180. }
  181. void SynchronizerClass::setElmFull(C UID &elm_id) {if(elm_id.valid()){/*SyncLocker locker(lock);*/ set_elm_full .include(elm_id);}}
  182. void SynchronizerClass::setElmLong(C UID &elm_id) {if(elm_id.valid()){/*SyncLocker locker(lock);*/ set_elm_long .include(elm_id);}}
  183. void SynchronizerClass::setElmShort(C UID &elm_id) {if(elm_id.valid()){/*SyncLocker locker(lock);*/ if(!set_elm_long.has(elm_id))set_elm_short.include(elm_id);}}
  184. void SynchronizerClass::setTex(C UID &tex_id) {if(tex_id.valid()){ SyncLocker locker(lock); set_tex .include(tex_id);}}
  185. void SynchronizerClass::createLocal(Project &local, Memx<Elm> &server) // process recursively to create parents first
  186. {
  187. FREPA(server)
  188. {
  189. Elm &s=server[i],
  190. &l=local.getElm(s.id);
  191. if( !l.type) // just added
  192. {
  193. // skip 'name', 'name_time' and 'data' because it was not sent yet
  194. if(1)l.name="_Downloading..";
  195. l.type=s.type;
  196. l.flag=s.flag;
  197. l. parent_id =s. parent_id;
  198. l. parent_time=s. parent_time;
  199. l. removed_time=s. removed_time;
  200. l.no_publish_time=s.no_publish_time;
  201. l.opened(false); // keep new downloaded elements as closed
  202. }
  203. }
  204. }
  205. void SynchronizerClass::sync(Project &local, Project &server)
  206. {
  207. if(!Proj.testElmsNum())return;
  208. Proj.setListCurSel();
  209. {
  210. SyncLocker locker(lock);
  211. game_path=Proj.game_path;
  212. edit_path=Proj.edit_path;
  213. tex_path=Proj. tex_path;
  214. }
  215. createLocal(local, server.elms);
  216. Server.texs=server.texs;
  217. Proj.setHierarchy(); // set hierarchy needed for various checks
  218. // compare versions
  219. Memc<UID > get_names, get_textures, set_textures, get_short, get_long, set_long;
  220. Memc<Elm* > set_names, set_parents, set_removed, set_no_publish;
  221. Memc<ElmDepth> set_full;
  222. FREPA(local.elms)
  223. {
  224. Elm &l=local.elms[i], *s=server.findElm(l.id);
  225. if( !s)set_full.New().elm=&l;else // if server doesn't have the element then send it fully
  226. {
  227. if(l.name_time<s->name_time)get_names.add(l.id);else
  228. if(l.name_time>s->name_time)set_names.add(&l);
  229. if(l.parent_time<s->parent_time)l.setParent(s->parent_id, s->parent_time);else
  230. if(l.parent_time>s->parent_time)set_parents.add(&l);
  231. if(l.removed_time<s->removed_time)l.setRemoved(s->removed(), s->removed_time);else
  232. if(l.removed_time>s->removed_time)set_removed.add(&l);
  233. if(l.no_publish_time<s->no_publish_time)l.setNoPublish(s->noPublish(), s->no_publish_time);else
  234. if(l.no_publish_time>s->no_publish_time)set_no_publish.add(&l);
  235. // data
  236. if(s->type==l.type) // just in case
  237. if(!ElmManualSync(l.type))
  238. {
  239. if(s->data && (!l.data || s->data->ver!=l.data->ver)) // server has data and (local doesn't or has different version) -> get data
  240. {
  241. if(l.initialized())get_short.add(l.id); // if we have some data, then download the small version, to see what we need to synchronize, as perhaps some parts are already OK
  242. else get_long .add(l.id); // if we have nothing , then download everything
  243. }else
  244. if(l.data && !s->data) // local has data but server doesn't
  245. {
  246. set_long.add(l.id);
  247. }
  248. }
  249. }
  250. }
  251. // textures
  252. Memc<UID> local_texs; local.getTextures(local_texs);
  253. REPA(server.texs)if(!local .texs.binaryHas(server.texs[i], Compare))get_textures.add(server.texs[i]);
  254. REPA( local_texs)if(!server.texs.binaryHas( local_texs[i], Compare))set_textures.add( local_texs[i]);
  255. // sync
  256. Memc<UID> temp;
  257. Server.getElmNames ( get_names );
  258. Server.getElmShort ( get_short );
  259. Server.getElmLong ( get_long );
  260. Server.getTextures ( get_textures );
  261. FREPA(set_names )Server.renameElm (*set_names [i]);
  262. FREPA(set_parents )Server.setElmParent(*set_parents[i]);
  263. FREPA(set_removed ){Elm &elm=*set_removed [i]; temp(0)=elm.id; Server. removeElms(temp, elm. removed(), elm. removed_time);}
  264. FREPA(set_no_publish){Elm &elm=*set_no_publish[i]; temp(0)=elm.id; Server.noPublishElms(temp, elm.noPublish(), elm.no_publish_time);}
  265. // send elms
  266. REPAO(set_full).depth=Proj.depth(set_full[i].elm); set_full.sort(CompareDepth); // sort by depth so root elements are sent first
  267. FREPA(set_full)Server.setElmFull(set_full[i].elm->id);
  268. // set data (after sending elements)
  269. FREPA(set_long)Server.setElmLong(set_long[i]);
  270. // send textures (after sending elements)
  271. FREPA(set_textures)Server.setTex(set_textures[i]);
  272. Proj.setList();
  273. if(!thread.active())thread.create(CompressFunc, this);
  274. FREPA(Proj.elms)if(Proj.elms[i].type==ELM_WORLD )Server.getWorldVer (Proj.elms[i].id);
  275. FREPA(Proj.elms)if(Proj.elms[i].type==ELM_MINI_MAP)Server.getMiniMapVer(Proj.elms[i].id);
  276. }
  277. void SynchronizerClass::syncWorld(C UID &world_id)
  278. {
  279. if(world_id.valid())
  280. if(WorldSync *ws=world_sync.get(world_id))
  281. {
  282. Memc<AreaSync> area_get;
  283. Memc<UID > waypoints_get, lakes_get, rivers_get;
  284. // check local to send
  285. if(Server.canWrite() && ws->local_ver) // only if can write
  286. {
  287. REPA(ws->local_ver->areas) // areas
  288. {
  289. VecI2 area_xy= ws-> local_ver->areas.lockedKey (i);
  290. AreaVer & local_area= ws-> local_ver->areas.lockedData(i),
  291. *server_area=(ws->server_ver ? ws->server_ver->areas.find(area_xy) : null);
  292. if(uint area_sync_flag=local_area.compare(server_area))
  293. {
  294. if( area_sync_flag&AREA_SYNC_HM )ws->set_area.binaryInclude(area_xy, Compare); // don't send objects in this way
  295. if((area_sync_flag&AREA_SYNC_OBJ) && !server_area)area_get.New().set(AREA_SYNC_OBJ, area_xy); // if objects need to be synced and this will not be processed below again "!server_area"
  296. }
  297. }
  298. REPA(ws->local_ver->waypoints) // waypoints
  299. {
  300. C UID &waypoint_id =ws->local_ver->waypoints.lockedKey (i);
  301. C Version &waypoint_ver=ws->local_ver->waypoints.lockedData(i);
  302. if(!ws->server_ver || !ws->server_ver->waypoints.find(waypoint_id)) // if was not found on the server at all
  303. {
  304. EditWaypoint waypoint; if(waypoint.load(Proj.editWaypointPath(world_id, waypoint_id)))Server.setWaypoint(world_id, waypoint_id, waypoint_ver, waypoint);
  305. }
  306. }
  307. REPA(ws->local_ver->lakes) // lakes
  308. {
  309. C UID &lake_id =ws->local_ver->lakes.lockedKey (i);
  310. C Version &lake_ver=ws->local_ver->lakes.lockedData(i).ver;
  311. if(!ws->server_ver || !ws->server_ver->lakes.find(lake_id)) // if was not found on the server at all
  312. {
  313. Lake lake; if(lake.load(Proj.editLakePath(world_id, lake_id)))Server.setLake(world_id, lake_id, lake_ver, lake);
  314. }
  315. }
  316. REPA(ws->local_ver->rivers) // rivers
  317. {
  318. C UID &river_id =ws->local_ver->rivers.lockedKey (i);
  319. C Version &river_ver=ws->local_ver->rivers.lockedData(i).ver;
  320. if(!ws->server_ver || !ws->server_ver->rivers.find(river_id)) // if was not found on the server at all
  321. {
  322. River river; if(river.load(Proj.editRiverPath(world_id, river_id)))Server.setRiver(world_id, river_id, river_ver, river);
  323. }
  324. }
  325. }
  326. // check server to receive
  327. if(ws->server_ver)
  328. {
  329. REPA(ws->server_ver->areas) // areas
  330. {
  331. VecI2 area_xy= ws->server_ver->areas.lockedKey (i);
  332. AreaVer &server_area= ws->server_ver->areas.lockedData(i),
  333. * local_area=(ws-> local_ver ? ws->local_ver->areas.find(area_xy) : null);
  334. if(uint area_sync_flag=server_area.compare(local_area))
  335. {
  336. if(area_sync_flag&AREA_SYNC_REMOVED) // if we need to delete the heightmap, then no need to receive data from server but just delete it
  337. {
  338. Proj.hmDel(world_id, area_xy, &server_area.hm_removed_time);
  339. FlagDisable(area_sync_flag, AREA_SYNC_REMOVED);
  340. }
  341. if(area_sync_flag)area_get.New().set(area_sync_flag, area_xy);
  342. }
  343. }
  344. REPA(ws->server_ver->waypoints) // waypoints
  345. {
  346. C UID &waypoint_id =ws->server_ver->waypoints.lockedKey (i);
  347. C Version &waypoint_ver=ws->server_ver->waypoints.lockedData(i);
  348. Version * local_ver=(ws->local_ver ? ws->local_ver->waypoints.find(waypoint_id) : null);
  349. if(!local_ver || waypoint_ver!=*local_ver)waypoints_get.add(waypoint_id); // if not found in local, or is of different version, then receive from server and sync
  350. }
  351. REPA(ws->server_ver->lakes) // lakes
  352. {
  353. C UID & lake_id =ws->server_ver->lakes.lockedKey (i);
  354. C Version & lake_ver=ws->server_ver->lakes.lockedData(i).ver;
  355. WaterVer *local_ver=(ws->local_ver ? ws->local_ver->lakes.find(lake_id) : null);
  356. if(!local_ver || lake_ver!=local_ver->ver)lakes_get.add(lake_id); // if not found in local, or is of different version, then receive from server and sync
  357. }
  358. REPA(ws->server_ver->rivers) // rivers
  359. {
  360. C UID &river_id =ws->server_ver->rivers.lockedKey (i);
  361. C Version &river_ver=ws->server_ver->rivers.lockedData(i).ver;
  362. WaterVer *local_ver=(ws->local_ver ? ws->local_ver->rivers.find(river_id) : null);
  363. if(!local_ver || river_ver!=local_ver->ver)rivers_get.add(river_id); // if not found in local, or is of different version, then receive from server and sync
  364. }
  365. }
  366. Server.getWorldAreas (world_id, area_get);
  367. Server.getWorldWaypoints(world_id, waypoints_get);
  368. Server.getWorldLakes (world_id, lakes_get);
  369. Server.getWorldRivers (world_id, rivers_get);
  370. }
  371. }
  372. void SynchronizerClass::syncMiniMap(C UID &mini_map_id)
  373. {
  374. if(mini_map_id.valid())
  375. if(MiniMapSync *mms=mini_map_sync.get(mini_map_id))
  376. {
  377. // sync settings
  378. if(mms->server_ver)Proj.syncMiniMapSettings(mini_map_id, mms->server_ver->settings, mms->server_ver->time); // try to replace from server
  379. if(mms-> local_ver)
  380. if(!mms->server_ver || mms->local_ver->time>mms->server_ver->time)Server.setMiniMapSettings(mini_map_id, mms->local_ver->settings, mms->local_ver->time); // send to server
  381. // sync images
  382. Memc<VecI2> get_image;
  383. // check local to send
  384. if(Server.canWrite() && mms->local_ver) // only if can write
  385. if(!mms->server_ver || mms->local_ver->time>=mms->server_ver->time) // send only if the time of the images is same/newer than on the server (check for same in case some images did not finish sending yet)
  386. FREPA(mms->local_ver->images)
  387. if(!mms->server_ver || mms->local_ver->time>mms->server_ver->time || !mms->server_ver->images.binaryHas(mms->local_ver->images[i], Compare)) // if server ver doesn't exist, or is old, or is the same time but doesn't have the image yet
  388. mms->set_image.binaryInclude(mms->local_ver->images[i], Compare); // mark to be sent
  389. // check server to receive
  390. if(mms->server_ver)
  391. if(!mms->local_ver || mms->server_ver->time>=mms->local_ver->time) // receive only if the time of the images is same/newer than on the client (check for same in case some images did not finish sending yet)
  392. FREPA(mms->server_ver->images)
  393. if(!mms->local_ver || mms->server_ver->time>mms->local_ver->time || !mms->local_ver->images.binaryHas(mms->server_ver->images[i], Compare)) // if local ver doesn't exist, or is old, or is the same time but doesn't have the image yet
  394. get_image.binaryInclude(mms->server_ver->images[i], Compare); // mark to be received
  395. Server.getMiniMapImages(mini_map_id, get_image);
  396. }
  397. }
  398. void SynchronizerClass::update()
  399. {
  400. // move 'delayed_world_sync' to 'world_sync'
  401. if(!delayed_world_sync.elms())last_delayed_time=Time.realTime();else // if there are no elements then set last time to current time so after adding an element it won't be sent right away
  402. if(Time.realTime()-last_delayed_time>=SendAreasDelay) // if enough time has passed
  403. {
  404. last_delayed_time=Time.realTime();
  405. MapLock ml(delayed_world_sync);
  406. FREPA(delayed_world_sync)
  407. {
  408. UID world_id=delayed_world_sync.lockedKey(i);
  409. if(WorldSync *ws=world_sync.get(world_id))
  410. {
  411. WorldSync &dws=delayed_world_sync.lockedData(i);
  412. FREPA(dws.set_area)ws->set_area.binaryInclude(dws.set_area[i], Compare);
  413. FREPA(dws.set_obj )ws->set_obj .binaryInclude(dws.set_obj [i], Compare);
  414. }
  415. }
  416. delayed_world_sync.del();
  417. }
  418. // if we have any elements to send
  419. if(set_elm_full.elms() || set_elm_long.elms() || set_elm_short.elms() || world_sync.elms() || mini_map_sync.elms())
  420. {
  421. const uint time=Time.curTimeMs(), delay=16; // 1000ms/60fps
  422. // set elm short
  423. if(Server.smallBuf())for(; set_elm_short.elms()>0; )
  424. {
  425. UID elm_id=set_elm_short[0]; Proj.flushElm(elm_id); set_elm_short.remove(0, true); // flush before sending to make sure that file has latest data, do this before removing from element to send, in case flush will include that element in the send-to list
  426. if(Elm *elm=Proj.findElm(elm_id))
  427. {
  428. ClientSendSetElmShort(Server, *elm, Proj);
  429. if(Time.curTimeMs()-time>=delay || !Server.smallBuf())goto skip;
  430. }
  431. }
  432. // set elm full
  433. if(elmFileSize()<=ServerSendBufSize)for(; set_elm_full.elms()>0; )
  434. {
  435. UID elm_id=set_elm_full[0]; Proj.flushElm(elm_id); set_elm_full.remove(0, true); // flush before sending to make sure that file has latest data, do this before removing from element to send, in case flush will include that element in the send-to list
  436. if(Elm *elm=Proj.findElm(elm_id))
  437. {
  438. ElmFile elm_file; ClientWriteSetElmFull(elm_file.elm.writeMem(), elm_file.data.writeMem(), *elm, Proj, elm_file.compress);
  439. {SyncLocker locker(lock); Swap(elm_file, set_elm_full_file.New());}
  440. if(Time.curTimeMs()-time>=delay || elmFileSize()>ServerSendBufSize)goto skip;
  441. }
  442. }
  443. // set elm long
  444. if(elmFileSize()<=ServerSendBufSize)for(; set_elm_long.elms()>0; )
  445. {
  446. UID elm_id=set_elm_long[0]; Proj.flushElm(elm_id); set_elm_long.remove(0, true); // flush before sending to make sure that file has latest data, do this before removing from element to send, in case flush will include that element in the send-to list
  447. if(Elm *elm=Proj.findElm(elm_id))
  448. {
  449. ElmFile elm_file; ClientWriteSetElmLong(elm_file.elm.writeMem(), elm_file.data.writeMem(), *elm, Proj, elm_file.compress);
  450. {SyncLocker locker(lock); Swap(elm_file, set_elm_long_file.New());}
  451. if(Time.curTimeMs()-time>=delay || elmFileSize()>ServerSendBufSize)goto skip;
  452. }
  453. }
  454. // world sync
  455. if(elmFileSize()<=ServerSendBufSize)for(int i=0; i<world_sync.elms(); )
  456. {
  457. UID world_id=world_sync.lockedKey (i);
  458. WorldSync &ws=world_sync.lockedData(i);
  459. // iterate all areas to send, before sending check which parts of the area actually need to be sent
  460. REPAD(j, ws.set_area)
  461. {
  462. VecI2 area_xy=ws.set_area[j]; ws.set_area.remove(j, true);
  463. if(ws.local_ver)
  464. if(AreaVer *local_area=ws.local_ver->areas.find(area_xy))
  465. if(!local_area->oldHm())ws.set_area.NewAt(j)=area_xy;else // send only if the data is old to make sure that new changes will set new timestamp (if data is not old then put it back to send it later)
  466. {
  467. ws.getServerVer(world_id); // try getting 'server_ver' if it's not set yet (this is needed in case the server ver is received after starting syncing)
  468. AreaVer *server_area=(ws.server_ver ? ws.server_ver->areas.find(area_xy) : null);
  469. if(uint area_sync_flag=(local_area->compare(server_area)&AREA_SYNC_HM)) // if we have anything to send, don't send objects here
  470. {
  471. if(!server_area && ws.server_ver)server_area=ws.server_ver->areas.get(area_xy);
  472. if( server_area)server_area->sync(*local_area); // we're sending new data so let's update the 'server_area' to assume that after sending the data it receives them
  473. Heightmap temp, *hm=null;
  474. if(area_sync_flag&(AREA_SYNC_HEIGHT|AREA_SYNC_MTRL|AREA_SYNC_COLOR))hm=Proj.hmGet(world_id, area_xy, temp);
  475. ElmFile elm_file; elm_file.compress=elm_file.compress_fast=true;
  476. ClientWriteSetWorldArea(elm_file.elm.writeMem(), elm_file.data.writeMem(), world_id, area_xy, area_sync_flag, *local_area, hm);
  477. {SyncLocker locker(lock); Swap(elm_file, set_elm_full_file.New());} // !! WARNING: here is 'set_elm_full_file', but we can use it !!
  478. if(Time.curTimeMs()-time>=delay || elmFileSize()>ServerSendBufSize)goto skip;
  479. }
  480. }
  481. }
  482. // iterate all objects to send, take first then iterate all remaining and group those that are from the same area (for better IO performance, send them as group)
  483. REPAD(j, ws.set_obj)if(InRange(j, ws.set_obj)) // extra check because we can remove multiple objects during one iteration
  484. {
  485. Memc<UID> obj_ids; obj_ids.add(ws.set_obj[j]); ws.set_obj.remove(j, true);
  486. if(ws.local_ver)
  487. if(ObjVer *obj_ver=ws.local_ver->obj.find(obj_ids.first())) // get object version
  488. {
  489. VecI2 area_xy=obj_ver->area_xy; // get area of that object
  490. REPA(ws.set_obj)if(ObjVer *obj_ver=ws.local_ver->obj.find(ws.set_obj[i]))if(obj_ver->area_xy==area_xy) // get all objects from that area
  491. {
  492. obj_ids.binaryInclude(ws.set_obj[i], Compare); ws.set_obj.remove(i, true);
  493. }
  494. // now we have all ID's of objects to send
  495. Memc<ObjData> objs; Proj.objGet(world_id, area_xy, obj_ids, objs); // get objects from areas
  496. ElmFile elm_file; elm_file.compress=elm_file.compress_fast=true;
  497. ClientWriteSetWorldObjs(elm_file.elm.writeMem(), elm_file.data.writeMem(), world_id, area_xy, objs, Proj.edit_path); // send to server
  498. {SyncLocker locker(lock); Swap(elm_file, set_elm_full_file.New());} // !! WARNING: here is 'set_elm_full_file', but we can use it !!
  499. if(Time.curTimeMs()-time>=delay || elmFileSize()>ServerSendBufSize)goto skip;
  500. }
  501. }
  502. if(ws.empty())world_sync.remove(i);else i++;
  503. }
  504. // mini map sync
  505. if(elmFileSize()<=ServerSendBufSize)for(int i=0; i<mini_map_sync.elms(); )
  506. {
  507. UID mini_map_id=mini_map_sync.lockedKey (i);
  508. MiniMapSync &mms=mini_map_sync.lockedData(i);
  509. for(; mms.set_image.elms(); )
  510. {
  511. VecI2 image_xy=mms.set_image.last(); mms.set_image.removeLast();
  512. if(mms.local_ver)
  513. {
  514. File image_data; image_data.readTry(Proj.gamePath(mini_map_id).tailSlash(true)+image_xy);
  515. ElmFile elm_file; elm_file.compress=true;
  516. ClientWriteSetMiniMapImage(elm_file.elm.writeMem(), elm_file.data.writeMem(), mini_map_id, image_xy, mms.local_ver->time, image_data);
  517. {SyncLocker locker(lock); Swap(elm_file, set_elm_full_file.New());} // !! WARNING: here is 'set_elm_full_file', but we can use it !!
  518. if(Time.curTimeMs()-time>=delay || elmFileSize()>ServerSendBufSize)goto skip;
  519. }
  520. }
  521. if(mms.empty())mini_map_sync.remove(i);else i++;
  522. }
  523. skip:;
  524. }
  525. }
  526. SynchronizerClass::SynchronizerClass() : compressing(false), world_sync(Compare, Create), delayed_world_sync(Compare, Create), mini_map_sync(Compare, Create), last_delayed_time(0) {}
  527. SynchronizerClass::ElmDepth::ElmDepth() : depth(0), elm(null) {}
  528. SynchronizerClass::ElmFile::ElmFile() : compress(false), compress_fast(false) {}
  529. SynchronizerClass::WorldSync::WorldSync() : server_ver(null), local_ver(null) {}
  530. SynchronizerClass::MiniMapSync::MiniMapSync() : server_ver(null), local_ver(null) {}
  531. /******************************************************************************/