1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
#include "server/pch.hh"
#include "server/world/worldgen.hh"
#include "core/io/cmdline.hh"
#include "shared/world/chunk.hh"
#include "shared/world/dimension.hh"
#include "shared/protocol.hh"
#include "shared/threading.hh"
#include "server/world/inhabited.hh"
#include "server/globals.hh"
#include "server/sessions.hh"
static bool aggressive_caching;
static emhash8::HashMap<world::Dimension*, emhash8::HashMap<chunk_pos, std::unordered_set<Session*>>> active_tasks;
class WorldgenTask final : public Task {
public:
explicit WorldgenTask(world::Dimension* dimension, const chunk_pos& cpos);
virtual ~WorldgenTask(void) = default;
virtual void process(void) override;
virtual void finalize(void) override;
private:
world::Dimension* m_dimension;
world::VoxelStorage m_voxels;
chunk_pos m_cpos;
};
WorldgenTask::WorldgenTask(world::Dimension* dimension, const chunk_pos& cpos)
{
m_dimension = dimension;
m_voxels.fill(rand()); // trolling
m_cpos = cpos;
}
void WorldgenTask::process(void)
{
if(!m_dimension->generate(m_cpos, m_voxels)) {
set_status(task_status::CANCELLED);
}
}
void WorldgenTask::finalize(void)
{
auto dim_tasks = active_tasks.find(m_dimension);
if(dim_tasks == active_tasks.cend()) {
// Normally this should never happen but
// one can never be sure about anything
// when that anything is threaded out
return;
}
auto it = dim_tasks->second.find(m_cpos);
if(it == dim_tasks->second.cend()) {
// Normally this should never happen but
// one can never be sure about anything
// when that anything is threaded out
return;
}
auto chunk = m_dimension->create_chunk(m_cpos);
chunk->set_voxels(m_voxels);
if(aggressive_caching) {
// Marking the chunk with InhabitedComponent makes
// it so that it is saved regardles of whether it was
// modified by players or not. This isn't particularly
// good for server-side disk usage but it might improve performance
m_dimension->chunks.emplace<world::Inhabited>(chunk->get_entity());
}
protocol::ChunkVoxels response;
response.voxels = m_voxels;
response.chunk = m_cpos;
auto packet = protocol::encode(response);
for(auto session : it->second) {
if(session->peer) {
// Respond with the voxels to every session
// that has requested this specific chunk for this dimension
enet_peer_send(session->peer, protocol::CHANNEL, packet);
}
}
dim_tasks->second.erase(it);
if(dim_tasks->second.empty()) {
// There are no more requests
// to generate a chunk for that
// dimension, at least for now
active_tasks.erase(dim_tasks);
}
}
void world::worldgen::init(void)
{
aggressive_caching = io::cmdline::contains("aggressive-caching");
}
bool world::worldgen::is_generating(Dimension* dimension, const chunk_pos& cpos)
{
auto dim_tasks = active_tasks.find(dimension);
if(dim_tasks == active_tasks.cend()) {
// No tasks for this dimension
return false;
}
auto it = dim_tasks->second.find(cpos);
if(it == dim_tasks->second.cend()) {
// Not generating this chunk
return false;
}
return true;
}
void world::worldgen::request_chunk(Session* session, const chunk_pos& cpos)
{
if(session->dimension) {
auto dim_tasks = active_tasks.find(session->dimension);
if(dim_tasks == active_tasks.cend()) {
dim_tasks = active_tasks.emplace(session->dimension, emhash8::HashMap<chunk_pos, std::unordered_set<Session*>>()).first;
}
auto it = dim_tasks->second.find(cpos);
if(it == dim_tasks->second.cend()) {
auto& sessions = dim_tasks->second.insert_or_assign(cpos, std::unordered_set<Session*>()).first->second;
sessions.insert(session);
threading::submit<WorldgenTask>(session->dimension, cpos);
return;
}
it->second.insert(session);
}
}
|