Server Side Chunk Generation

* Server World Class
* WorldGenStream class to handle multithreaded map generation
* ServerPlayer stores active chunk boundaries

* BlockChunk stores position
* Renamed client World to LocalWorld
* Disabled client side Generation
* ServerConnection stores chunk packets to be used by the GameScene,
  which gives them to the world (change later?)

* Reenabled "BlockChunk Packet Encoding" test in tests/BlockChunk.cpp
This commit is contained in:
aurailus 2019-03-07 17:43:05 -08:00
parent 3bba3adf66
commit b66ca8284b
21 changed files with 460 additions and 86 deletions

View File

@ -110,6 +110,6 @@ set(ZEUS_SRC_FILES
client/engine/FrustumPlane.cpp client/engine/FrustumPlane.cpp
client/engine/FrustumPlane.h client/engine/FrustumPlane.h
client/engine/FrustumAABB.cpp client/engine/FrustumAABB.cpp
client/engine/FrustumAABB.h) client/engine/FrustumAABB.h server/world/World.cpp server/world/World.h server/world/WorldGenStream.cpp server/world/WorldGenStream.h)
add_library (zeusCore ${ZEUS_SRC_FILES}) add_library (zeusCore ${ZEUS_SRC_FILES})

View File

@ -30,15 +30,15 @@ GameScene::GameScene(ClientState* state) :
//The scene requires the blockAtlas for meshing and handling inputs. //The scene requires the blockAtlas for meshing and handling inputs.
world = new LocalWorld(blockAtlas); world = new LocalWorld(blockAtlas);
int SIZE = 16; // int SIZE = 16;
int SIZEV = 8; // int SIZEV = 8;
for (int i = -SIZE; i < SIZE; i++) { // for (int i = -SIZE; i < SIZE; i++) {
for (int j = -SIZE; j < SIZEV; j++) { // for (int j = -SIZE; j < SIZEV; j++) {
for (int k = -SIZE; k < SIZE; k++) { // for (int k = -SIZE; k < SIZE; k++) {
world->genNewChunk(glm::vec3(i, j, k)); // world->genNewChunk(glm::vec3(i, j, k));
} // }
} // }
} // }
player = new Player(); player = new Player();
player->create(world, state->renderer->getCamera()); player->create(world, state->renderer->getCamera());
@ -62,6 +62,14 @@ void GameScene::update() {
state->renderer->resized = false; state->renderer->resized = false;
} }
while (!server->chunkPackets.empty()) {
auto it = server->chunkPackets.begin();
Packet p = *it;
server->chunkPackets.erase(it);
world->loadChunkPacket(&p);
}
debugGui.update(player, world, window, blockAtlas, state->fps, (int)world->getMeshChunks()->size(), drawCalls); debugGui.update(player, world, window, blockAtlas, state->fps, (int)world->getMeshChunks()->size(), drawCalls);
world->update(); world->update();
} }

View File

@ -22,21 +22,21 @@ LocalWorld::LocalWorld(BlockAtlas *atlas) {
void LocalWorld::genNewChunk(glm::vec3 pos) { void LocalWorld::genNewChunk(glm::vec3 pos) {
if (!blockChunks.count(pos)) { if (!blockChunks.count(pos)) {
pendingGen.insert(pos); pendingGen.push_back(pos);
} }
} }
void LocalWorld::loadChunkPacket(Packet *p) { void LocalWorld::loadChunkPacket(Packet *p) {
// auto b = new BlockChunk(); auto b = new BlockChunk();
//
// glm::vec3 pos = glm::vec3(Packet::decodeInt(&p->data[0]), Packet::decodeInt(&p->data[4]), Packet::decodeInt(&p->data[8])); glm::vec3 pos = glm::vec3(Serializer::decodeInt(&p->data[0]), Serializer::decodeInt(&p->data[4]), Serializer::decodeInt(&p->data[8]));
//
// int len = Packet::decodeInt(&p->data[12]); int len = Serializer::decodeInt(&p->data[12]);
// std::string data(p->data.begin() + 16, p->data.begin() + 16 + len); std::string data(p->data.begin() + 16, p->data.begin() + 16 + len);
//
// b->deserialize(data); b->deserialize(data);
//
// commitChunk(pos, b); commitChunk(pos, b);
} }
void LocalWorld::commitChunk(glm::vec3 pos, BlockChunk *c) { void LocalWorld::commitChunk(glm::vec3 pos, BlockChunk *c) {
@ -61,7 +61,7 @@ void LocalWorld::attemptMeshChunk(glm::vec3 pos) {
thisChunk->adjacent[4] = getAdjacentExists(glm::vec3(pos.x, pos.y, pos.z + 1), pos); thisChunk->adjacent[4] = getAdjacentExists(glm::vec3(pos.x, pos.y, pos.z + 1), pos);
thisChunk->adjacent[5] = getAdjacentExists(glm::vec3(pos.x, pos.y, pos.z - 1), pos); thisChunk->adjacent[5] = getAdjacentExists(glm::vec3(pos.x, pos.y, pos.z - 1), pos);
if (thisChunk->allAdjacentsExist()) pendingMesh.insert(pos); if (thisChunk->allAdjacentsExist()) pendingMesh.push_back(pos);
} }
bool LocalWorld::getAdjacentExists(glm::vec3 pos, glm::vec3 otherPos) { bool LocalWorld::getAdjacentExists(glm::vec3 pos, glm::vec3 otherPos) {
@ -77,7 +77,7 @@ bool LocalWorld::getAdjacentExists(glm::vec3 pos, glm::vec3 otherPos) {
if (diff == glm::vec3(0, 0, 1)) chunk->adjacent[4] = true; if (diff == glm::vec3(0, 0, 1)) chunk->adjacent[4] = true;
if (diff == glm::vec3(0, 0,-1)) chunk->adjacent[5] = true; if (diff == glm::vec3(0, 0,-1)) chunk->adjacent[5] = true;
if (chunk->allAdjacentsExist()) pendingMesh.insert(pos); if (chunk->allAdjacentsExist()) pendingMesh.push_back(pos);
return true; return true;
} }
return false; return false;
@ -133,6 +133,13 @@ std::vector<bool>* LocalWorld::getAdjacentsCull(glm::vec3 pos) {
} }
void LocalWorld::update() { void LocalWorld::update() {
// std::sort(pendingGen.begin(), pendingGen.begin()+min(1000, (int)pendingGen.size()), [](glm::vec3 a, glm::vec3 b) {
// return glm::distance(a, glm::vec3(0, 0, 0)) < glm::distance(b, glm::vec3(0, 0, 0));
// });
// std::sort(pendingMesh.begin(), pendingMesh.end()+min(1000, (int)pendingMesh.size()), [](glm::vec3 a, glm::vec3 b) {
// return glm::distance(a, glm::vec3(0, 0, 0)) < glm::distance(b, glm::vec3(0, 0, 0));
// });
//
handleChunkGenQueue(); handleChunkGenQueue();
handleMeshGenQueue(); handleMeshGenQueue();
} }

View File

@ -100,7 +100,7 @@ private:
const int GEN_QUEUE_SIZE = 8; const int GEN_QUEUE_SIZE = 8;
const int GEN_FINISHED_SIZE = GEN_THREADS * GEN_QUEUE_SIZE; const int GEN_FINISHED_SIZE = GEN_THREADS * GEN_QUEUE_SIZE;
std::unordered_set<glm::vec3, vec3cmp> pendingGen; std::vector<glm::vec3> pendingGen;
std::vector<ChunkThreadDef*> genThreads; std::vector<ChunkThreadDef*> genThreads;
std::vector<ChunkThreadData*> finishedGen; std::vector<ChunkThreadData*> finishedGen;
@ -108,7 +108,7 @@ private:
const int MESH_QUEUE_SIZE = 8; const int MESH_QUEUE_SIZE = 8;
const int MESH_FINISHED_SIZE = GEN_THREADS * GEN_QUEUE_SIZE; const int MESH_FINISHED_SIZE = GEN_THREADS * GEN_QUEUE_SIZE;
std::unordered_set<glm::vec3, vec3cmp> pendingMesh; std::vector<glm::vec3> pendingMesh;
std::vector<MeshThreadDef*> meshThreads; std::vector<MeshThreadDef*> meshThreads;
std::vector<MeshThreadData*> finishedMesh; std::vector<MeshThreadData*> finishedMesh;

View File

@ -30,34 +30,45 @@ void ServerConnection::update(Player &player, std::vector<PlayerEntity*>& player
case ENET_EVENT_TYPE_RECEIVE: { case ENET_EVENT_TYPE_RECEIVE: {
Packet p(event.packet); Packet p(event.packet);
if (p.type == Packet::PLAYER_INFO) { switch (p.type) {
glm::vec3 playerPos = glm::vec3( case Packet::PLAYER_INFO: {
Serializer::decodeFloat(&p.data[0]), glm::vec3 playerPos = glm::vec3(
Serializer::decodeFloat(&p.data[4]), Serializer::decodeFloat(&p.data[0]),
Serializer::decodeFloat(&p.data[8]) Serializer::decodeFloat(&p.data[4]),
); Serializer::decodeFloat(&p.data[8])
player.setPos(playerPos); );
} player.setPos(playerPos);
else if (p.type == Packet::ENTITY_INFO) { break;
int peer_id = Serializer::decodeInt(&p.data[0]); }
case Packet::ENTITY_INFO: {
int peer_id = Serializer::decodeInt(&p.data[0]);
glm::vec3 playerPos = glm::vec3( glm::vec3 playerPos = glm::vec3(
Serializer::decodeFloat(&p.data[4]), Serializer::decodeFloat(&p.data[4]),
Serializer::decodeFloat(&p.data[8]), Serializer::decodeFloat(&p.data[8]),
Serializer::decodeFloat(&p.data[12]) Serializer::decodeFloat(&p.data[12])
); );
bool found = false; bool found = false;
for (auto plrEnt : playerEntities) { for (auto plrEnt : playerEntities) {
if (plrEnt->peer_id == peer_id) { if (plrEnt->peer_id == peer_id) {
plrEnt->setPosition(playerPos); plrEnt->setPosition(playerPos);
found = true; found = true;
break; break;
}
} }
if (!found) {
playerEntities.push_back(new PlayerEntity(playerPos, peer_id));
}
break;
} }
if (!found) { case Packet::CHUNK_INFO: {
playerEntities.push_back(new PlayerEntity(playerPos, peer_id)); chunkPackets.push_back(std::move(p));
break;
} }
default:
break;
} }
enet_packet_destroy(event.packet); enet_packet_destroy(event.packet);

View File

@ -25,6 +25,7 @@ public:
~ServerConnection(); ~ServerConnection();
std::vector<Packet> chunkPackets;
private: private:
bool connected = false; bool connected = false;

View File

@ -17,6 +17,11 @@ BlockChunk::BlockChunk(std::vector<int> blocks) {
this->blocks = std::move(blocks); this->blocks = std::move(blocks);
} }
BlockChunk::BlockChunk(std::vector<int> blocks, glm::vec3 pos) {
this->blocks = std::move(blocks);
this->pos = pos;
}
int BlockChunk::getBlock(glm::vec3* pos) { int BlockChunk::getBlock(glm::vec3* pos) {
unsigned int ind = ArrayTrans3D::vecToInd(pos); unsigned int ind = ArrayTrans3D::vecToInd(pos);
if (ind < 0 || ind >= 4096) return -1; if (ind < 0 || ind >= 4096) return -1;

View File

@ -15,6 +15,9 @@ class BlockChunk {
public: public:
BlockChunk(); BlockChunk();
explicit BlockChunk(std::vector<int> blocks); explicit BlockChunk(std::vector<int> blocks);
BlockChunk(std::vector<int> blocks, glm::vec3 pos);
glm::vec3 pos;
bool adjacent[6] = {false, false, false, false, false, false}; bool adjacent[6] = {false, false, false, false, false, false};
bool allAdjacentsExist(); bool allAdjacentsExist();

View File

@ -58,7 +58,7 @@ BlockChunk* MapGen::generate(glm::vec3 pos) {
buildElevation(j); buildElevation(j);
fillChunk(j); fillChunk(j);
return new BlockChunk(j.blocks); return new BlockChunk(j.blocks, pos);
} }
void MapGen::buildElevation(MapGenJob &j) { void MapGen::buildElevation(MapGenJob &j) {

View File

@ -5,6 +5,10 @@
#include "ConnectionList.h" #include "ConnectionList.h"
#include "../generic/network/PacketChannel.h" #include "../generic/network/PacketChannel.h"
ConnectionList::ConnectionList(World* world) {
this->world = world;
}
ServerPeer* ConnectionList::addPeer(ENetPeer *eNetPeer) { ServerPeer* ConnectionList::addPeer(ENetPeer *eNetPeer) {
printf("[INFO] %x:%u connected.\n", eNetPeer->address.host, eNetPeer->address.port); printf("[INFO] %x:%u connected.\n", eNetPeer->address.host, eNetPeer->address.port);
@ -31,14 +35,14 @@ void ConnectionList::removePeer(ENetPeer *eNetPeer) {
} }
} }
ServerPlayer* ConnectionList::addPlayer(ServerPeer *peer, std::string uuid) { ServerPlayer* ConnectionList::createPlayer(ServerPeer *peer, std::string uuid) {
printf("[INFO] Creating player %s for %x:%u.\n", uuid.c_str(), peer->peer->address.host, peer->peer->address.port); printf("[INFO] Creating player %s for %x:%u.\n", uuid.c_str(), peer->peer->address.host, peer->peer->address.port);
auto player = new ServerPlayer(peer); auto player = new ServerPlayer(peer);
player->pos = glm::vec3(-8, 32, -8); player->setPos(glm::vec3(0, 16, 0));
//Send Initialization Data //Send Initialization Data
auto packet = player->getInitPacket(); auto packet = player->getInitPacket();
packet.sendTo(peer->peer, PacketChannel::PLAYER_INFO); packet.sendTo(peer->peer, PacketChannel::PLAYER_INFO);
players.push_back(player); world->addPlayer(player);
} }

View File

@ -9,16 +9,19 @@
#include <string> #include <string>
#include "ServerPlayer.h" #include "ServerPlayer.h"
#include "ServerPeer.h" #include "ServerPeer.h"
#include "world/World.h"
class ConnectionList { class ConnectionList {
public: public:
explicit ConnectionList(World* world);
ServerPeer* addPeer(ENetPeer* peer); ServerPeer* addPeer(ENetPeer* peer);
void removePeer(ENetPeer* peer); void removePeer(ENetPeer* peer);
ServerPlayer* addPlayer(ServerPeer* peer, std::string uuid); ServerPlayer* createPlayer(ServerPeer *peer, std::string uuid);
public: public:
std::vector<ServerPeer*> peers; std::vector<ServerPeer*> peers;
std::vector<ServerPlayer*> players; World* world;
}; };

View File

@ -5,9 +5,9 @@
#include "Server.h" #include "Server.h"
#include "../generic/blocks/BlockChunk.h" #include "../generic/blocks/BlockChunk.h"
Server::Server() = default; Server::Server() : connections(&world) {};
Server::Server(unsigned short port) { Server::Server(unsigned short port) : connections(&world) {
this->port = port; this->port = port;
} }
@ -20,12 +20,14 @@ void Server::init() {
void Server::update() { void Server::update() {
Timer loop(""); Timer loop("");
world.update();
ENetEvent event; ENetEvent event;
while (handler.update(&event) && loop.elapsedNs() < 15L*1000000L) { while (handler.update(&event) && loop.elapsedNs() < 15L*1000000L) {
switch (event.type) { switch (event.type) {
case ENET_EVENT_TYPE_CONNECT: { case ENET_EVENT_TYPE_CONNECT: {
auto peer = connections.addPeer(event.peer); auto peer = connections.addPeer(event.peer);
connections.addPlayer(peer, "Aurailus"); connections.createPlayer(peer, "Aurailus");
break; break;
} }
case ENET_EVENT_TYPE_RECEIVE: { case ENET_EVENT_TYPE_RECEIVE: {
@ -43,7 +45,7 @@ void Server::update() {
Serializer::decodeFloat(&p.data[4]), Serializer::decodeFloat(&p.data[4]),
Serializer::decodeFloat(&p.data[8]) Serializer::decodeFloat(&p.data[8])
); );
player->pos = newPos; player->setPos(newPos);
//Send All Clients the new positon //Send All Clients the new positon
Packet r(Packet::ENTITY_INFO); Packet r(Packet::ENTITY_INFO);

View File

@ -29,6 +29,7 @@ public:
private: private:
bool alive = true; bool alive = true;
World world;
NetHandler handler; NetHandler handler;
ConnectionList connections; ConnectionList connections;

View File

@ -5,7 +5,6 @@
#ifndef ZEUS_SERVERPEER_H #ifndef ZEUS_SERVERPEER_H
#define ZEUS_SERVERPEER_H #define ZEUS_SERVERPEER_H
#include <enet/enet.h> #include <enet/enet.h>
class ServerPlayer; class ServerPlayer;

View File

@ -2,15 +2,13 @@
// Created by aurailus on 11/01/19. // Created by aurailus on 11/01/19.
// //
#include <cmath>
#include "ServerPlayer.h" #include "ServerPlayer.h"
ServerPlayer::ServerPlayer(ServerPeer *peer) { ServerPlayer::ServerPlayer(ServerPeer *peer) {
this->peer = peer; this->peer = peer;
peer->player = this; peer->player = this;
} updateBounds();
ServerPlayer::~ServerPlayer() {
} }
Packet ServerPlayer::getInitPacket() { Packet ServerPlayer::getInitPacket() {
@ -22,3 +20,38 @@ Packet ServerPlayer::getInitPacket() {
return p; return p;
} }
glm::vec3 ServerPlayer::getPos() {
return pos;
}
void ServerPlayer::setPos(glm::vec3 pos) {
this->lastPos = this->pos;
this->pos = pos;
glm::vec3 chunkPos(std::floor(pos.x / 16), std::floor(pos.y / 16), std::floor(pos.z / 16));
glm::vec3 lastChunkPos(std::floor(lastPos.x / 16), std::floor(lastPos.y / 16), std::floor(lastPos.z / 16));
if (chunkPos != lastChunkPos) {
updateBounds();
}
}
std::pair<glm::vec3, glm::vec3> ServerPlayer::getBounds() {
return {minBounds, maxBounds};
}
void ServerPlayer::updateBounds() {
glm::vec3 chunkPos(std::floor(pos.x / 16), std::floor(pos.y / 16), std::floor(pos.z / 16));
minBounds = glm::vec3(chunkPos.x - ACTIVE_RANGE, chunkPos.y - ACTIVE_RANGE, chunkPos.z - ACTIVE_RANGE);
maxBounds = glm::vec3(chunkPos.x + ACTIVE_RANGE, chunkPos.y + ACTIVE_RANGE, chunkPos.z + ACTIVE_RANGE);
}
bool ServerPlayer::isInBounds(glm::vec3 pos) {
return (pos.x >= minBounds.x && pos.x <= maxBounds.x
&& pos.y >= minBounds.y && pos.y <= maxBounds.y
&& pos.z >= minBounds.z && pos.z <= maxBounds.z);
}
ServerPlayer::~ServerPlayer() = default;

View File

@ -12,14 +12,29 @@
class ServerPlayer { class ServerPlayer {
public: public:
const static int ACTIVE_RANGE = 20;
explicit ServerPlayer(ServerPeer* peer); explicit ServerPlayer(ServerPeer* peer);
Packet getInitPacket(); Packet getInitPacket();
ServerPeer* peer; glm::vec3 getPos();
glm::vec3 pos = glm::vec3(0, 0, 0); void setPos(glm::vec3 pos);
std::pair<glm::vec3, glm::vec3> getBounds();
bool isInBounds(glm::vec3 pos);
~ServerPlayer(); ~ServerPlayer();
ServerPeer* peer;
private:
void updateBounds();
glm::vec3 pos = {0, 0, 0};
glm::vec3 lastPos = {0, 0, 0};
glm::vec3 minBounds = {0, 0, 0};
glm::vec3 maxBounds = {0, 0, 0};
}; };

View File

@ -0,0 +1,75 @@
//
// Created by aurailus on 05/03/19.
//
#include "World.h"
#include "../../generic/network/PacketChannel.h"
void World::addPlayer(ServerPlayer *player) {
this->players.push_back(player);
playerChangedChunks(player);
}
void World::playerChangedChunks(ServerPlayer *player) {
auto bounds = player->getBounds();
for (auto i = (int)bounds.first.x; i < (int)bounds.second.x; i++) {
for (auto j = (int)bounds.first.y; j < (int)bounds.second.y; j++) {
for (auto k = (int)bounds.first.z; k < (int)bounds.second.z; k++) {
generate(glm::vec3(i, j, k));
}
}
}
}
void World::generate(glm::vec3 pos) {
if(!generateQueueMap.count(pos) && !chunkMap.count(pos)) {
generateQueueMap.insert(pos);
generateQueueList.push_back(pos);
}
}
void World::update() {
while (!generateQueueList.empty()) {
auto it = generateQueueList.begin();
glm::vec3 pos = *it;
bool success = genStream.tryToQueue(pos);
if (success) {
generateQueueList.erase(it);
generateQueueMap.erase(pos);
}
else break;
}
auto finished = genStream.update();
for (auto chunk : finished) {
bool didCalcSerialized = false;
std::string serialized;
for (auto player : players) {
if (player->isInBounds(chunk->pos)) {
//Serialize the chunk
if (!didCalcSerialized) {
serialized = chunk->serialize();
didCalcSerialized = true;
}
//Send the Chunk to the player
Packet r(Packet::CHUNK_INFO);
Serializer::encodeInt(r.data, (int)chunk->pos.x);
Serializer::encodeInt(r.data, (int)chunk->pos.y);
Serializer::encodeInt(r.data, (int)chunk->pos.z);
Serializer::encodeString(r.data, serialized);
r.sendTo(player->peer->peer, PacketChannel::WORLD_INFO);
}
}
}
}

45
src/server/world/World.h Normal file
View File

@ -0,0 +1,45 @@
//
// Created by aurailus on 05/03/19.
//
#ifndef ZEUS_WORLD_H
#define ZEUS_WORLD_H
#include <unordered_map>
#include <unordered_set>
#include "../../generic/blocks/BlockChunk.h"
#include "../ServerPlayer.h"
#include "WorldGenStream.h"
class World {
public:
World() = default;
void addPlayer(ServerPlayer* player);
void update();
~World() = default;
private:
void playerChangedChunks(ServerPlayer* player);
void generate(glm::vec3 pos);
struct vec3cmp {
size_t operator()(const glm::vec3& k)const {
return std::hash<float>()(k.x) ^ std::hash<float>()(k.y) ^ std::hash<float>()(k.z);
}
};
WorldGenStream genStream;
std::vector<ServerPlayer*> players;
std::unordered_map<glm::vec3, BlockChunk*, vec3cmp> chunkMap;
std::vector<std::pair<glm::vec3, BlockChunk*>> chunkList;
std::unordered_set<glm::vec3, vec3cmp> generateQueueMap;
std::vector<glm::vec3> generateQueueList;
};
#endif //ZEUS_WORLD_H

View File

@ -0,0 +1,93 @@
//
// Created by aurailus on 06/03/19.
//
#include "WorldGenStream.h"
#pragma clang diagnostic push
#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection"
WorldGenStream::WorldGenStream() : gen(0) {
queuedTasks.reserve((unsigned long) TOTAL_QUEUE_SIZE);
threads.reserve(THREADS);
for (int i = 0; i < THREADS; i++) {
threads.emplace_back(&gen);
}
}
bool WorldGenStream::tryToQueue(glm::vec3 pos) {
unsigned long sizeOfQueue = queuedTasks.size();
if (sizeOfQueue < TOTAL_QUEUE_SIZE && !queuedMap.count(pos)) {
queuedTasks.push_back(pos);
queuedMap.insert(pos);
}
return sizeOfQueue + 1 < TOTAL_QUEUE_SIZE;
}
std::vector<BlockChunk*> WorldGenStream::update() {
std::vector<BlockChunk*> finishedChunks;
for (auto& t : threads) {
for (auto& u : t.tasks) {
if (!u.unlocked) continue;
if (u.chunk != nullptr) {
finishedChunks.push_back(u.chunk);
u.chunk = nullptr;
}
if (!queuedTasks.empty()) {
auto it = queuedTasks.begin();
glm::vec3 pos = *it;
queuedTasks.erase(it);
queuedMap.erase(pos);
u.pos = pos;
//Lock it in to allow the thread to edit it.
u.unlocked = false;
}
}
}
return finishedChunks;
}
WorldGenStream::Thread::Thread(MapGen *gen) {
this->gen = gen;
thread = std::thread(WorldGenStream::threadFunction, this);
thread.detach();
}
void WorldGenStream::threadFunction(WorldGenStream::Thread *thread) {
while (thread->keepAlive) {
bool empty = true;
for (Unit& u : thread->tasks) {
if (!u.unlocked) {
empty = false;
u.chunk = thread->gen->generate(u.pos);
u.unlocked = true;
break;
}
}
if (empty) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
}
WorldGenStream::~WorldGenStream() {
for (auto& t : threads) {
t.keepAlive = false;
t.thread.join();
}
}
#pragma clang diagnostic pop

View File

@ -0,0 +1,69 @@
//
// Created by aurailus on 06/03/19.
//
#pragma clang diagnostic push
#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection"
#ifndef ZEUS_WORLDGENSTREAM_H
#define ZEUS_WORLDGENSTREAM_H
#include <vec3.hpp>
#include <thread>
#include <unordered_set>
#include "../../generic/blocks/BlockChunk.h"
#include "../../generic/gen/MapGen.h"
class WorldGenStream {
public:
static const int THREAD_QUEUE_SIZE = 32;
static const int THREADS = 8;
static const int TOTAL_QUEUE_SIZE = THREADS * THREAD_QUEUE_SIZE;
WorldGenStream();
~WorldGenStream();
//Attempt to add `pos` to the pre-thread queue.
//Will return a boolean stating if there is more space left in the queue.
bool tryToQueue(glm::vec3 pos);
//Will return a vector of BlockChunk pointers containing finished chunks.
//Frees up the threads and starts new tasks.
std::vector<BlockChunk*> update();
struct Unit {
glm::vec3 pos {0, 0, 0};
BlockChunk* chunk = nullptr;
bool unlocked = true;
};
struct Thread {
explicit Thread(MapGen* gen);
MapGen* gen;
std::thread thread;
bool keepAlive = true;
std::vector<Unit> tasks = std::vector<Unit>(THREAD_QUEUE_SIZE);
};
std::vector<Thread> threads;
private:
static void threadFunction(Thread* thread);
struct vec3cmp {
size_t operator()(const glm::vec3& k)const {
return std::hash<float>()(k.x) ^ std::hash<float>()(k.y) ^ std::hash<float>()(k.z);
}
};
MapGen gen;
std::vector<glm::vec3> queuedTasks;
std::unordered_set<glm::vec3, vec3cmp> queuedMap;
};
#endif //ZEUS_WORLDGENSTREAM_H
#pragma clang diagnostic pop

View File

@ -47,27 +47,27 @@ TEST_CASE("Blockchunks", "[networking]") {
delete b2; delete b2;
} }
// SECTION("BlockChunk Packet Encoding") { SECTION("BlockChunk Packet Encoding") {
// auto p = new Packet(Packet::CHUNKINFO); auto p = Packet(Packet::CHUNK_INFO);
// p->addString(gzip); Serializer::encodeString(p.data, gzip);
//
// auto byteArr = p->serialize(); auto enetP = p.toENetPacket();
//
// auto p2 = Packet::deserialize(byteArr); auto p2 = Packet(enetP);
//
// int len = Packet::decodeInt(&p2->data[0]); int len = Serializer::decodeInt(&p2.data[0]);
// std::string data(p->data.begin() + 4, p->data.begin() + 4 + len); std::string data(p.data.begin() + 4, p.data.begin() + 4 + len);
//
// auto b2 = new BlockChunk(); auto b2 = new BlockChunk();
// REQUIRE(b2->deserialize(data)); REQUIRE(b2->deserialize(data));
//
// for (int j = 0; j < 4096; j++) { for (int j = 0; j < 4096; j++) {
// REQUIRE(b2->getBlock(j) == b->getBlock(j)); REQUIRE(b2->getBlock(j) == b->getBlock(j));
// } }
//
// delete b2; delete b2;
// } }
//
delete b; delete b;
INFO("Iteration " << i << " passed."); INFO("Iteration " << i << " passed.");