Dimension.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. #include "Dimension.h"
  2. #include "Constants.h"
  3. #include "Datei.h"
  4. #include "Game.h"
  5. using namespace Framework;
  6. Dimension::Dimension(int id)
  7. : dimensionId(id),
  8. gravity(9.8f),
  9. chunks(new Trie<Chunk>()),
  10. entities(new RCArray<Entity>())
  11. {}
  12. Dimension::~Dimension()
  13. {
  14. entities->release();
  15. chunks->release();
  16. }
  17. void Dimension::api(Framework::InMemoryBuffer* zRequest, NetworkMessage* zResponse, Entity* zSource)
  18. {
  19. char type;
  20. zRequest->lese(&type, 1);
  21. switch (type)
  22. {
  23. case 0: // chunk message
  24. {
  25. Punkt center;
  26. zRequest->lese((char*)&center.x, 4);
  27. zRequest->lese((char*)&center.y, 4);
  28. cs.lock();
  29. Chunk* cC = zChunk(center);
  30. if (!cC)
  31. {
  32. // TODO: have a max amount of waiting requests per player
  33. waitingRequests.add({ dynamic_cast<InMemoryBuffer*>(zRequest->getThis()), center, zSource->getId() });
  34. Game::INSTANCE->requestArea({ center.x - CHUNK_SIZE / 2, center.y - CHUNK_SIZE / 2, center.x + CHUNK_SIZE / 2 - 1, center.y + CHUNK_SIZE / 2 - 1, dimensionId });
  35. }
  36. else
  37. {
  38. cC->api(zRequest, zSource);
  39. }
  40. cs.unlock();
  41. break;
  42. }
  43. }
  44. }
  45. void Dimension::tickEntities()
  46. {
  47. for (auto entity : *entities)
  48. {
  49. if (!entity->isRemoved() && zChunk(Punkt((int)entity->getPosition().x, (int)entity->getPosition().y)))
  50. entity->prepareTick(this);
  51. }
  52. int index = 0;
  53. for (auto entity : *entities)
  54. {
  55. if (!entity->isRemoved() && zChunk(Punkt((int)entity->getPosition().x, (int)entity->getPosition().y)))
  56. entity->tick(this);
  57. index++;
  58. }
  59. }
  60. void Dimension::getAddrOf(Punkt cPos, char* addr) const
  61. {
  62. *(int*)addr = cPos.x;
  63. *((int*)addr + 1) = cPos.y;
  64. }
  65. void Dimension::getAddrOfWorld(Punkt wPos, char* addr) const
  66. {
  67. if (wPos.x < 0)
  68. wPos.x -= CHUNK_SIZE;
  69. if (wPos.y < 0) // needed because otherwise would (-8, -8) have the same adress as (8, 8)
  70. wPos.y -= CHUNK_SIZE;
  71. wPos /= CHUNK_SIZE;
  72. getAddrOf(wPos, addr);
  73. }
  74. Chunk* Dimension::zChunk(Punkt wPos) const
  75. {
  76. char addr[8];
  77. getAddrOfWorld(wPos, addr);
  78. return chunks->z(addr, 8);
  79. }
  80. Framework::Either<Block*, int> Dimension::zBlock(Vec3<int> location)
  81. {
  82. Chunk* c = zChunk(Game::INSTANCE->getChunkCenter(location.x, location.y));
  83. if (c)
  84. {
  85. int x = location.x % CHUNK_SIZE;
  86. int y = location.y % CHUNK_SIZE;
  87. if (x < 0)
  88. x += CHUNK_SIZE;
  89. if (y < 0)
  90. y += CHUNK_SIZE;
  91. return c->zBlockAt(Vec3<int>(x, y, location.z));
  92. }
  93. return 0;
  94. }
  95. Block* Dimension::zRealBlockInstance(Framework::Vec3<int> location)
  96. {
  97. Chunk* c = zChunk(Game::INSTANCE->getChunkCenter(location.x, location.y));
  98. if (c)
  99. {
  100. int x = location.x % CHUNK_SIZE;
  101. int y = location.y % CHUNK_SIZE;
  102. if (x < 0)
  103. x += CHUNK_SIZE;
  104. if (y < 0)
  105. y += CHUNK_SIZE;
  106. c->instantiateBlock(Vec3<int>(x, y, location.z));
  107. auto result = c->zBlockAt(Vec3<int>(x, y, location.z));
  108. return result.isA() ? result.getA() : 0;
  109. }
  110. return 0;
  111. }
  112. void Dimension::placeBlock(Framework::Vec3<int> location, Framework::Either<Block*, int> block)
  113. {
  114. Chunk* c = zChunk(Game::getChunkCenter(location.x, location.y));
  115. if (c)
  116. {
  117. int x = location.x % CHUNK_SIZE;
  118. int y = location.y % CHUNK_SIZE;
  119. if (x < 0)
  120. x += CHUNK_SIZE;
  121. if (y < 0)
  122. y += CHUNK_SIZE;
  123. if (block.isA())
  124. c->putBlockAt(Vec3<int>(x, y, location.z), block);
  125. else
  126. {
  127. c->putBlockAt(Vec3<int>(x, y, location.z), 0);
  128. c->putBlockTypeAt(Vec3<int>(x, y, location.z), block);
  129. }
  130. }
  131. else if (block.isA())
  132. block.getA()->release();
  133. }
  134. void Dimension::addEntity(Entity* entity)
  135. {
  136. entities->add(entity);
  137. }
  138. void Dimension::setChunk(Chunk* chunk, Punkt center)
  139. {
  140. char addr[8];
  141. getAddrOfWorld(center, addr);
  142. Chunk* old = chunks->z(addr, 8);
  143. if (old)
  144. {
  145. for (int i = 0; i < chunkList.getEintragAnzahl(); i++)
  146. {
  147. if (chunkList.get(i) == old)
  148. {
  149. chunkList.remove(i);
  150. break;
  151. }
  152. }
  153. }
  154. chunks->set(addr, 8, chunk);
  155. if (chunk)
  156. {
  157. chunkList.add(chunk);
  158. chunk->setAdded();
  159. }
  160. getAddrOfWorld(center + Punkt(CHUNK_SIZE, 0), addr);
  161. Chunk* zChunk = chunks->z(addr, 8);
  162. if (zChunk)
  163. {
  164. zChunk->setNeighbor(WEST, chunk);
  165. if (chunk)
  166. chunk->setNeighbor(EAST, zChunk);
  167. }
  168. getAddrOfWorld(center + Punkt(-CHUNK_SIZE, 0), addr);
  169. zChunk = chunks->z(addr, 8);
  170. if (zChunk)
  171. {
  172. zChunk->setNeighbor(EAST, chunk);
  173. if (chunk)
  174. chunk->setNeighbor(WEST, zChunk);
  175. }
  176. getAddrOfWorld(center + Punkt(0, CHUNK_SIZE), addr);
  177. zChunk = chunks->z(addr, 8);
  178. if (zChunk)
  179. {
  180. zChunk->setNeighbor(NORTH, chunk);
  181. if (chunk)
  182. chunk->setNeighbor(SOUTH, zChunk);
  183. }
  184. getAddrOfWorld(center + Punkt(0, -CHUNK_SIZE), addr);
  185. zChunk = chunks->z(addr, 8);
  186. if (zChunk)
  187. {
  188. zChunk->setNeighbor(SOUTH, chunk);
  189. if (chunk)
  190. chunk->setNeighbor(NORTH, zChunk);
  191. }
  192. if (chunk)
  193. {
  194. cs.lock();
  195. int index = 0;
  196. for (Iterator<RequestQueue> iterator = waitingRequests.begin(); iterator; )
  197. {
  198. Entity* zE = Game::INSTANCE->zEntity(iterator.val().sourceId);
  199. if (zE)
  200. {
  201. if (iterator.val().chunkCenter == chunk->getCenter())
  202. {
  203. chunk->api(iterator.val().request, zE);
  204. iterator.val().request->release();
  205. iterator.remove();
  206. continue;
  207. }
  208. }
  209. else
  210. {
  211. iterator.val().request->release();
  212. iterator.remove();
  213. continue;
  214. }
  215. iterator++;
  216. index++;
  217. }
  218. cs.unlock();
  219. }
  220. }
  221. void Dimension::save(Text worldDir) const
  222. {
  223. for (auto chunk = chunks->getIterator(); chunk; chunk++)
  224. {
  225. if (!chunk._)
  226. continue;
  227. Datei* file = new Datei();
  228. Text filePath = worldDir + "/dim/" + dimensionId + "/";
  229. filePath.appendHex(chunk->getCenter().x);
  230. filePath += "_";
  231. filePath.appendHex(chunk->getCenter().y);
  232. filePath += ".chunk";
  233. file->setDatei(filePath);
  234. if (file->open(Datei::Style::schreiben))
  235. chunk->save(file);
  236. file->close();
  237. file->release();
  238. }
  239. Text filePath = worldDir + "/dim/" + dimensionId + "/entities";
  240. Datei* file = new Datei();
  241. file->setDatei(filePath);
  242. if (file->open(Datei::Style::schreiben))
  243. {
  244. for (Entity* entity : *entities)
  245. {
  246. if (entity->zType()->getId() != PlayerEntityType::ID)
  247. {
  248. if (!entity->isRemoved())
  249. {
  250. int type = entity->zType()->getId();
  251. file->schreibe((char*)&type, 4);
  252. StaticRegistry<EntityType>::INSTANCE.zElement(type)->saveEntity(entity, file);
  253. }
  254. }
  255. else
  256. {
  257. Datei pFile;
  258. pFile.setDatei(worldDir + "/player/" + ((Player*)entity)->getName());
  259. if (pFile.open(Datei::Style::schreiben))
  260. PlayerEntityType::INSTANCE->saveEntity(entity, &pFile);
  261. }
  262. }
  263. file->close();
  264. }
  265. }
  266. int Dimension::getDimensionId() const
  267. {
  268. return dimensionId;
  269. }
  270. bool Dimension::hasChunck(int x, int y) const
  271. {
  272. return zChunk(Punkt(x, y));
  273. }
  274. float Dimension::getGravity() const
  275. {
  276. return gravity;
  277. }
  278. void Dimension::removeOldChunks()
  279. {
  280. Array<int> removed;
  281. int index = 0;
  282. for (Chunk* chunk : chunkList)
  283. {
  284. if (!chunk->hasObservers())
  285. removed.add(index, 0);
  286. index++;
  287. }
  288. for (int i : removed)
  289. {
  290. Chunk* chunk = chunkList.get(i);
  291. // TODO: save chunk in a seperate thread?
  292. Text filePath = Game::INSTANCE->getWorldDirectory() + "/dim/" + getDimensionId() + "/";
  293. filePath.appendHex(chunk->getCenter().x);
  294. filePath += "_";
  295. filePath.appendHex(chunk->getCenter().y);
  296. filePath += ".chunk";
  297. Datei d;
  298. d.setDatei(filePath);
  299. d.open(Datei::Style::schreiben);
  300. chunk->save(&d);
  301. d.close();
  302. chunk->prepareRemove();
  303. setChunk(0, chunk->getCenter());
  304. }
  305. }
  306. Entity* Dimension::zEntity(int id)
  307. {
  308. for (auto entity : *entities)
  309. {
  310. if (!entity->isRemoved() && entity->getId() == id)
  311. return entity;
  312. }
  313. return 0;
  314. }
  315. Entity* Dimension::zNearestEntity(Framework::Vec3<float> pos, std::function<bool(Entity*)> filter)
  316. {
  317. Entity* result = 0;
  318. float sqDist = 0;
  319. for (auto entity : *entities)
  320. {
  321. if (!entity->isRemoved() && filter(entity))
  322. {
  323. float d = pos.abstandSq(entity->getPosition());
  324. if (!result || d < sqDist)
  325. {
  326. result = entity;
  327. sqDist = d;
  328. }
  329. }
  330. }
  331. return result;
  332. }
  333. void Dimension::removeEntity(int id)
  334. {
  335. int index = 0;
  336. for (auto entity : *entities)
  337. {
  338. if (entity->getId() == id)
  339. {
  340. entities->remove(index);
  341. return;
  342. }
  343. index++;
  344. }
  345. }
  346. void Dimension::removeSubscriptions(Entity* zEntity)
  347. {
  348. for (Chunk* chunk : chunkList)
  349. chunk->removeObserver(zEntity);
  350. }