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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
|
#include "server/pch.hh"
#include "server/world/universe.hh"
#include "core/config/number.hh"
#include "core/config/string.hh"
#include "core/io/buffer.hh"
#include "core/io/config_map.hh"
#include "core/utils/epoch.hh"
#include "shared/world/chunk.hh"
#include "shared/world/dimension.hh"
#include "server/world/inhabited.hh"
#include "server/world/overworld.hh"
#include "server/globals.hh"
struct DimensionMetadata final {
std::string config_path;
std::string zvox_dir;
io::ConfigMap config;
};
static config::String universe_name("save");
static io::ConfigMap universe_config;
static config::Unsigned64 universe_config_seed;
static config::String universe_spawn_dimension("world");
static std::string universe_config_path;
static std::unordered_map<world::Dimension*, DimensionMetadata*> metadata_map;
static std::string make_chunk_filename(const DimensionMetadata* metadata, const chunk_pos& cpos)
{
const auto unsigned_x = static_cast<std::uint32_t>(cpos.x);
const auto unsigned_y = static_cast<std::uint32_t>(cpos.y);
const auto unsigned_z = static_cast<std::uint32_t>(cpos.z);
return std::format("{}/{:08X}-{:08X}-{:08X}.zvox", metadata->zvox_dir, unsigned_x, unsigned_y, unsigned_z);
}
static void add_new_dimension(world::Dimension* dimension)
{
if(globals::dimensions.count(dimension->get_name())) {
spdlog::critical("universe: dimension named {} already exists", dimension->get_name());
std::terminate();
}
auto dimension_dir = std::format("{}/{}", universe_name.get(), dimension->get_name());
if(!PHYSFS_mkdir(dimension_dir.c_str())) {
spdlog::critical("universe: {}: {}", dimension_dir, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
std::terminate();
}
auto metadata = new DimensionMetadata;
metadata->config_path = std::format("{}/dimension.conf", dimension_dir);
metadata->zvox_dir = std::format("{}/chunk", dimension_dir);
if(!PHYSFS_mkdir(metadata->zvox_dir.c_str())) {
spdlog::critical("universe: {}: {}", metadata->zvox_dir, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
std::terminate();
}
globals::dimensions.insert_or_assign(dimension->get_name(), dimension);
auto& mapped_metadata = metadata_map.insert_or_assign(dimension, metadata).first->second;
dimension->init(mapped_metadata->config);
mapped_metadata->config.load_file(mapped_metadata->config_path.c_str());
dimension->init_late(universe_config_seed.get_value());
}
static void internal_save_chunk(
const DimensionMetadata* metadata, const world::Dimension* dimension, const chunk_pos& cpos, const world::Chunk* chunk)
{
auto path = make_chunk_filename(metadata, cpos);
io::WriteBuffer buffer;
chunk->get_voxels().serialize(buffer);
if(auto file = buffer.to_file(path.c_str())) {
PHYSFS_close(file);
return;
}
}
void world::universe::init(void)
{
// If the world is newly created, the seed will
// be chosed based on the current system's view on UNIX time
universe_config_seed.set_value(utils::unix_microseconds());
// We're going to read files from directory named with
// the value of this config value. Since config is also
// read from command line, the [--universe <name>] parameter still works
globals::server_config.add_value("universe", universe_name);
universe_config.add_value("global_seed", universe_config_seed);
universe_config.add_value("spawn_dimension", universe_spawn_dimension);
}
void world::universe::init_late(void)
{
const auto universe_dir = std::string(universe_name.get());
if(!PHYSFS_mkdir(universe_dir.c_str())) {
spdlog::critical("universe: {}: {}", universe_dir, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
std::terminate();
}
universe_config_path = std::format("{}/universe.conf", universe_dir);
universe_config.load_file(universe_config_path.c_str());
add_new_dimension(new Overworld("world"));
// UNDONE: lua scripts to setup dimensions
if(globals::dimensions.empty()) {
spdlog::critical("universe: no dimensions");
std::terminate();
}
auto spawn_dimension = globals::dimensions.find(universe_spawn_dimension.get());
if(spawn_dimension == globals::dimensions.cend()) {
spdlog::critical("universe: {} is not a valid dimension name", universe_spawn_dimension.get());
std::terminate();
}
globals::spawn_dimension = spawn_dimension->second;
}
void world::universe::shutdown(void)
{
for(const auto metadata : metadata_map) {
metadata.second->config.save_file(metadata.second->config_path.c_str());
delete metadata.second;
}
metadata_map.clear();
for(const auto dimension : globals::dimensions) {
world::universe::save_all_chunks(dimension.second);
delete dimension.second;
}
globals::dimensions.clear();
globals::spawn_dimension = nullptr;
universe_config.save_file(universe_config_path.c_str());
}
world::Chunk* world::universe::load_chunk(Dimension* dimension, const chunk_pos& cpos)
{
if(auto chunk = dimension->find_chunk(cpos)) {
// Just return the existing chunk which is
// most probable to be up to date compared to
// whatever the hell is currently stored on disk
return chunk;
}
auto metadata = metadata_map.find(dimension);
if(metadata == metadata_map.cend()) {
// The dimension is for sure a weird one
return nullptr;
}
if(auto file = PHYSFS_openRead(make_chunk_filename(metadata->second, cpos).c_str())) {
VoxelStorage voxels;
io::ReadBuffer buffer(file);
voxels.deserialize(buffer);
PHYSFS_close(file);
auto chunk = dimension->create_chunk(cpos);
chunk->set_voxels(voxels);
// Make sure we're going to save it later
dimension->chunks.emplace_or_replace<Inhabited>(chunk->get_entity());
return chunk;
}
return nullptr;
}
void world::universe::save_chunk(Dimension* dimension, const chunk_pos& cpos)
{
auto metadata = metadata_map.find(dimension);
if(metadata == metadata_map.cend()) {
// Cannot save a chunk in a dimension
// that doesn't have a metadata struct
return;
}
if(auto chunk = dimension->find_chunk(cpos)) {
internal_save_chunk(metadata->second, dimension, cpos, chunk);
}
}
void world::universe::save_all_chunks(Dimension* dimension)
{
auto group = dimension->chunks.group(entt::get<ChunkComponent, Inhabited>);
auto metadata = metadata_map.find(dimension);
if(metadata == metadata_map.cend()) {
// Cannot save a chunk in a dimension
// that doesn't have a metadata struct
return;
}
for(auto [entity, chunk] : group.each()) {
internal_save_chunk(metadata->second, dimension, chunk.cpos, chunk.chunk);
}
}
|