WIP mc beta world save loading

This commit is contained in:
xtreme8000 2022-12-01 22:07:08 +01:00
parent c8f3f33c22
commit e18f31cdcf
6 changed files with 272 additions and 137 deletions

View file

@ -238,7 +238,7 @@ static void chunk_mesher_rebuild(struct block_data* bd, w_coord_t cx,
w_coord_t cy, w_coord_t cz,
struct displaylist* d, uint8_t* light_data,
bool count_only, size_t* vertices) {
assert(bd && d && light_data && vertices);
assert(bd && d && vertices);
for(int k = 0; k < 13; k++)
vertices[k] = 0;
@ -426,19 +426,14 @@ static void chunk_mesher_rebuild(struct block_data* bd, w_coord_t cx,
}
static void chunk_mesher_build(struct chunk_mesher_rpc* req) {
uint8_t* light_data
= malloc((CHUNK_SIZE + 2) * (CHUNK_SIZE + 2) * (CHUNK_SIZE + 2) * 3);
assert(light_data);
chunk_mesher_vertex_light(req->request.blocks, light_data);
for(int k = 0; k < 13; k++)
req->result.has_displist[k] = false;
size_t vertices[13];
chunk_mesher_rebuild(req->request.blocks, req->chunk->x, req->chunk->y,
req->chunk->z, req->result.mesh, light_data, true,
vertices);
req->chunk->z, req->result.mesh, NULL, true, vertices);
bool has_any_vertices = false;
for(int k = 0; k < 13; k++) {
if(vertices[k] > 0 && vertices[k] <= 0xFFFF * 4) {
@ -447,14 +442,20 @@ static void chunk_mesher_build(struct chunk_mesher_rpc* req) {
displaylist_begin(req->result.mesh + k, GX_QUADS, GX_VTXFMT0,
vertices[k]);
req->result.has_displist[k] = true;
has_any_vertices = true;
}
}
chunk_mesher_rebuild(req->request.blocks, req->chunk->x, req->chunk->y,
req->chunk->z, req->result.mesh, light_data, false,
vertices);
free(light_data);
if(has_any_vertices) {
uint8_t* light_data = malloc((CHUNK_SIZE + 2) * (CHUNK_SIZE + 2)
* (CHUNK_SIZE + 2) * 3);
assert(light_data);
chunk_mesher_vertex_light(req->request.blocks, light_data);
chunk_mesher_rebuild(req->request.blocks, req->chunk->x, req->chunk->y,
req->chunk->z, req->result.mesh, light_data, false,
vertices);
free(light_data);
}
for(int k = 0; k < 13; k++) {
if(req->result.has_displist[k])

View file

@ -20,20 +20,25 @@ void clin_chunk(w_coord_t x, w_coord_t y, w_coord_t z, w_coord_t sx,
uint8_t* ids_t = ids;
uint8_t* metadata_t = metadata;
uint8_t* lighting_t = lighting;
bool flip = true;
for(w_coord_t oy = y; oy < y + sy; oy++) {
for(w_coord_t ox = x; ox < x + sx; ox++) {
for(w_coord_t oz = z; oz < z + sz; oz++) {
for(w_coord_t ox = x; ox < x + sx; ox++) {
for(w_coord_t oy = y; oy < y + sy; oy++) {
uint8_t md = flip ? (*metadata_t) & 0xF : (*metadata_t) >> 4;
world_set_block(&gstate.world, ox, oy, oz,
(struct block_data) {
.type = *ids_t,
.metadata = *metadata_t,
.metadata = md,
.sky_light = (*lighting_t) & 0xF,
.torch_light = (*lighting_t) >> 4,
});
ids_t++;
metadata_t++;
lighting_t++;
flip = !flip;
if(flip)
metadata_t++;
}
}
}

View file

@ -0,0 +1,162 @@
#include <assert.h>
#include "../cNBT/nbt.h"
#include "region_archive.h"
bool region_archive_create(struct region_archive* ra, char* world_name,
w_coord_t x, w_coord_t z) {
assert(ra && world_name);
ra->offsets = malloc(sizeof(uint32_t) * REGION_SIZE * REGION_SIZE);
if(!ra->offsets)
return false;
snprintf(ra->file_name, sizeof(ra->file_name), "%s/region/r.%i.%i.mcr",
world_name, x, z); // TODO
ra->x = x;
ra->z = z;
FILE* f = fopen(ra->file_name, "rb");
if(!f) {
free(ra->offsets);
return false;
}
if(!fread(ra->offsets, sizeof(uint32_t) * REGION_SIZE * REGION_SIZE, 1,
f)) {
free(ra->offsets);
fclose(f);
return false;
}
fclose(f);
return true;
}
void region_archive_destroy(struct region_archive* ra) {
assert(ra);
free(ra->offsets);
}
bool region_archive_contains(struct region_archive* ra, w_coord_t x,
w_coord_t z) {
assert(ra);
if(CHUNK_REGION_COORD(x) != ra->x || CHUNK_REGION_COORD(z) != ra->z)
return false;
int rx = x & (REGION_SIZE - 1);
int rz = z & (REGION_SIZE - 1);
uint32_t offset = ra->offsets[rx + rz * REGION_SIZE] >> 8;
uint32_t sectors = ra->offsets[rx + rz * REGION_SIZE] & 0xFF;
return offset >= 2 && sectors >= 1;
}
bool region_archive_get_blocks(struct region_archive* ra, w_coord_t x,
w_coord_t z, uint8_t** ids, uint8_t** metadata,
uint8_t** lighting) {
assert(ra && ids && metadata && lighting);
assert(region_archive_contains(ra, x, z));
int rx = x & (REGION_SIZE - 1);
int rz = z & (REGION_SIZE - 1);
uint32_t offset = ra->offsets[rx + rz * REGION_SIZE] >> 8;
uint32_t sectors = ra->offsets[rx + rz * REGION_SIZE] & 0xFF;
// TODO: little endian
FILE* f = fopen(ra->file_name, "rb");
if(!f)
return false;
if(fseek(f, offset * REGION_SECTOR_SIZE, SEEK_SET) != 0) {
fclose(f);
return false;
}
uint32_t length;
if(!fread(&length, sizeof(uint32_t), 1, f)
|| length + sizeof(uint32_t) > sectors * REGION_SECTOR_SIZE) {
fclose(f);
return false;
}
uint8_t type;
if(!fread(&type, sizeof(uint8_t), 1, f) || type > 3) {
fclose(f);
return false;
}
void* nbt_compressed = malloc(length - 1);
if(!nbt_compressed) {
fclose(f);
return false;
}
if(!fread(nbt_compressed, length - 1, 1, f)) {
free(nbt_compressed);
fclose(f);
return false;
}
nbt_node* chunk = nbt_parse_compressed(nbt_compressed, length - 1);
free(nbt_compressed);
fclose(f);
if(!chunk)
return false;
nbt_node* n_blocks = nbt_find_by_path(chunk, ".Level.Blocks");
nbt_node* n_metadata = nbt_find_by_path(chunk, ".Level.Data");
nbt_node* n_skyl = nbt_find_by_path(chunk, ".Level.SkyLight");
nbt_node* n_torchl = nbt_find_by_path(chunk, ".Level.BlockLight");
if(!n_blocks || !n_metadata || !n_skyl || !n_torchl
|| n_blocks->type != TAG_BYTE_ARRAY || n_metadata->type != TAG_BYTE_ARRAY
|| n_skyl->type != TAG_BYTE_ARRAY || n_torchl->type != TAG_BYTE_ARRAY
|| n_blocks->payload.tag_byte_array.length
!= CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT
|| n_metadata->payload.tag_byte_array.length
!= CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT / 2
|| n_skyl->payload.tag_byte_array.length
!= CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT / 2
|| n_torchl->payload.tag_byte_array.length
!= CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT / 2) {
nbt_free(chunk);
return false;
}
*ids = malloc(CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT);
*metadata = malloc(CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT / 2);
*lighting = malloc(CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT);
memcpy(*ids, n_blocks->payload.tag_byte_array.data,
n_blocks->payload.tag_byte_array.length);
memcpy(*metadata, n_metadata->payload.tag_byte_array.data,
n_metadata->payload.tag_byte_array.length);
for(size_t k = 0; k < (size_t)n_skyl->payload.tag_byte_array.length * 2;
k++) {
uint8_t a = (n_torchl->payload.tag_byte_array.data[k / 2] & 0xF0)
| (n_skyl->payload.tag_byte_array.data[k / 2] >> 4);
uint8_t b = (n_torchl->payload.tag_byte_array.data[k / 2] << 4)
| (n_skyl->payload.tag_byte_array.data[k / 2] & 0xF);
(*lighting)[k] = (k & 1) ? a : b;
}
nbt_free(chunk);
return true;
}

View file

@ -0,0 +1,30 @@
#ifndef REGION_ARCHIVE_H
#define REGION_ARCHIVE_H
#include <stdbool.h>
#include <stdint.h>
#include "../world.h"
struct region_archive {
w_coord_t x, z;
uint32_t* offsets;
char file_name[64];
};
#define REGION_SIZE 32
#define REGION_SIZE_BITS 5
#define REGION_SECTOR_SIZE 4096
#define CHUNK_REGION_COORD(x) ((w_coord_t)floor(x / (float)REGION_SIZE))
bool region_archive_create(struct region_archive* ra, char* world_name,
w_coord_t x, w_coord_t z);
void region_archive_destroy(struct region_archive* ra);
bool region_archive_contains(struct region_archive* ra, w_coord_t x,
w_coord_t z);
bool region_archive_get_blocks(struct region_archive* ra, w_coord_t x,
w_coord_t z, uint8_t** ids, uint8_t** metadata,
uint8_t** lighting);
#endif

View file

@ -9,103 +9,37 @@
#define CHUNK_DIST2(x1, x2, z1, z2) \
(((x1) - (x2)) * ((x1) - (x2)) + ((z1) - (z2)) * ((z1) - (z2)))
static const char* chunk_files[] = {
"x0 z-10.vc", "x-1 z-2.vc", "x-3 z-5.vc", "x-5 z-8.vc", "x-8 z-11.vc",
"x0 z-11.vc", "x-1 z-3.vc", "x-3 z-6.vc", "x-5 z-9.vc", "x-8 z-1.vc",
"x0 z-1.vc", "x-1 z-4.vc", "x-3 z-7.vc", "x-6 z-10.vc", "x-8 z-2.vc",
"x0 z-2.vc", "x-1 z-5.vc", "x-3 z-8.vc", "x-6 z-11.vc", "x-8 z-3.vc",
"x0 z-3.vc", "x-1 z-6.vc", "x-3 z-9.vc", "x-6 z-1.vc", "x-8 z-4.vc",
"x0 z-4.vc", "x-1 z-7.vc", "x-4 z-10.vc", "x-6 z-2.vc", "x-8 z-5.vc",
"x0 z-5.vc", "x-1 z-8.vc", "x-4 z-11.vc", "x-6 z-3.vc", "x-8 z-6.vc",
"x0 z-6.vc", "x-1 z-9.vc", "x-4 z-1.vc", "x-6 z-4.vc", "x-8 z-7.vc",
"x0 z-7.vc", "x-2 z-10.vc", "x-4 z-2.vc", "x-6 z-5.vc", "x-8 z-8.vc",
"x0 z-8.vc", "x-2 z-11.vc", "x-4 z-3.vc", "x-6 z-6.vc", "x-8 z-9.vc",
"x0 z-9.vc", "x-2 z-1.vc", "x-4 z-4.vc", "x-6 z-7.vc", "x-9 z-10.vc",
"x-10 z-10.vc", "x-2 z-2.vc", "x-4 z-5.vc", "x-6 z-8.vc", "x-9 z-11.vc",
"x-10 z-11.vc", "x-2 z-3.vc", "x-4 z-6.vc", "x-6 z-9.vc", "x-9 z-1.vc",
"x-10 z-1.vc", "x-2 z-4.vc", "x-4 z-7.vc", "x-7 z-10.vc", "x-9 z-2.vc",
"x-10 z-2.vc", "x-2 z-5.vc", "x-4 z-8.vc", "x-7 z-11.vc", "x-9 z-3.vc",
"x-10 z-3.vc", "x-2 z-6.vc", "x-4 z-9.vc", "x-7 z-1.vc", "x-9 z-4.vc",
"x-10 z-4.vc", "x-2 z-7.vc", "x-5 z-10.vc", "x-7 z-2.vc", "x-9 z-5.vc",
"x-10 z-5.vc", "x-2 z-8.vc", "x-5 z-11.vc", "x-7 z-3.vc", "x-9 z-6.vc",
"x-10 z-6.vc", "x-2 z-9.vc", "x-5 z-1.vc", "x-7 z-4.vc", "x-9 z-7.vc",
"x-10 z-7.vc", "x-3 z-10.vc", "x-5 z-2.vc", "x-7 z-5.vc", "x-9 z-8.vc",
"x-10 z-8.vc", "x-3 z-11.vc", "x-5 z-3.vc", "x-7 z-6.vc", "x-9 z-9.vc",
"x-10 z-9.vc", "x-3 z-1.vc", "x-5 z-4.vc", "x-7 z-7.vc", "x-1 z-10.vc",
"x-3 z-2.vc", "x-5 z-5.vc", "x-7 z-8.vc", "x-1 z-11.vc", "x-3 z-3.vc",
"x-5 z-6.vc", "x-7 z-9.vc", "x-1 z-1.vc", "x-3 z-4.vc", "x-5 z-7.vc",
"x-8 z-10.vc",
};
static bool has_chunk(struct server_local* s, w_coord_t x, w_coord_t z) {
assert(s);
static w_coord_t chunk_files_lookup[sizeof(chunk_files) / sizeof(*chunk_files)]
[2];
static const char* find_chunk(int x, int z) {
for(size_t k = 0; k < sizeof(chunk_files) / sizeof(*chunk_files); k++) {
if(chunk_files_lookup[k][0] == x && chunk_files_lookup[k][1] == z)
return chunk_files[k];
for(size_t k = 0; k < s->loaded_regions_length; k++) {
if(region_archive_contains(s->loaded_regions + k, x, z))
return true;
}
return NULL;
}
static void find_chunk_cache() {
for(size_t k = 0; k < sizeof(chunk_files) / sizeof(*chunk_files); k++) {
int chunk_x, chunk_z;
FILE* f = fopen(chunk_files[k], "rb");
assert(f);
fread((uint8_t*)&chunk_x + 3, sizeof(uint8_t), 1, f);
fread((uint8_t*)&chunk_x + 2, sizeof(uint8_t), 1, f);
fread((uint8_t*)&chunk_x + 1, sizeof(uint8_t), 1, f);
fread((uint8_t*)&chunk_x + 0, sizeof(uint8_t), 1, f);
fread((uint8_t*)&chunk_z + 3, sizeof(uint8_t), 1, f);
fread((uint8_t*)&chunk_z + 2, sizeof(uint8_t), 1, f);
fread((uint8_t*)&chunk_z + 1, sizeof(uint8_t), 1, f);
fread((uint8_t*)&chunk_z + 0, sizeof(uint8_t), 1, f);
fclose(f);
chunk_files_lookup[k][0] = chunk_x;
chunk_files_lookup[k][1] = chunk_z;
}
}
static void load_chunk(const char* file, int* chunk_x, int* chunk_z,
uint8_t** ids, uint8_t** metadata, uint8_t** lighting) {
uint8_t* chunk_data = malloc(16 * 16 * 128 * 3);
FILE* f = fopen(file, "rb");
assert(f);
fread((uint8_t*)chunk_x + 3, sizeof(uint8_t), 1, f);
fread((uint8_t*)chunk_x + 2, sizeof(uint8_t), 1, f);
fread((uint8_t*)chunk_x + 1, sizeof(uint8_t), 1, f);
fread((uint8_t*)chunk_x + 0, sizeof(uint8_t), 1, f);
fread((uint8_t*)chunk_z + 3, sizeof(uint8_t), 1, f);
fread((uint8_t*)chunk_z + 2, sizeof(uint8_t), 1, f);
fread((uint8_t*)chunk_z + 1, sizeof(uint8_t), 1, f);
fread((uint8_t*)chunk_z + 0, sizeof(uint8_t), 1, f);
fread(chunk_data, 1, 16 * 16 * 128 * 3, f);
fclose(f);
*ids = malloc(16 * 16 * 128);
*metadata = malloc(16 * 16 * 128);
*lighting = malloc(16 * 16 * 128);
#define CHUNK_INDEX(x, y, z) ((x) + ((z) + (y)*16) * 16)
for(int y = 0; y < 128; y++) {
for(int z = 0; z < 16; z++) {
for(int x = 0; x < 16; x++) {
(*ids)[CHUNK_INDEX(x, y, z)]
= chunk_data[CHUNK_INDEX(x, y, z) * 3 + 0];
(*metadata)[CHUNK_INDEX(x, y, z)]
= chunk_data[CHUNK_INDEX(x, y, z) * 3 + 1];
(*lighting)[CHUNK_INDEX(x, y, z)]
= chunk_data[CHUNK_INDEX(x, y, z) * 3 + 2];
}
if(s->loaded_regions_length < MAX_REGIONS) {
if(region_archive_create(s->loaded_regions + s->loaded_regions_length,
"world", CHUNK_REGION_COORD(x),
CHUNK_REGION_COORD(z))) {
s->loaded_regions_length++;
return true;
}
}
free(chunk_data);
return false;
}
static bool load_chunk(struct server_local* s, w_coord_t x, w_coord_t z,
uint8_t** ids, uint8_t** metadata, uint8_t** lighting) {
assert(s && ids && metadata && lighting);
for(size_t k = 0; k < s->loaded_regions_length; k++) {
if(region_archive_contains(s->loaded_regions + k, x, z))
return region_archive_get_blocks(s->loaded_regions + k, x, z, ids,
metadata, lighting);
}
return false;
}
static void server_local_process(struct server_rpc* call, void* user) {
@ -118,6 +52,7 @@ static void server_local_process(struct server_rpc* call, void* user) {
s->player.x = call->payload.player_pos.x;
s->player.y = call->payload.player_pos.y;
s->player.z = call->payload.player_pos.z;
s->player.has_pos = true;
break;
}
}
@ -127,6 +62,9 @@ static void server_local_update(struct server_local* s) {
svin_process_messages(server_local_process, s, false);
if(!s->player.has_pos)
return;
s->last_chunk_load = time_get();
w_coord_t px = WCOORD_CHUNK_OFFSET(floor(s->player.x));
@ -176,7 +114,7 @@ static void server_local_update(struct server_local* s) {
w_coord_t d = CHUNK_DIST2(px, x, pz, z);
if(!loaded && (d < c_nearest_dist2 || !c_nearest)
&& find_chunk(x, z)) {
&& has_chunk(s, x, z)) {
c_nearest_dist2 = d;
c_nearest_x = x;
c_nearest_z = z;
@ -187,39 +125,31 @@ static void server_local_update(struct server_local* s) {
}
// load just one chunk
if(c_nearest) {
const char* str = find_chunk(c_nearest_x, c_nearest_z);
if(str) {
s->loaded_chunks[s->loaded_chunks_length++]
= (struct loaded_chunk) {.x = c_nearest_x, .z = c_nearest_z};
uint8_t *ids, *metadata, *lighting;
if(c_nearest && has_chunk(s, c_nearest_x, c_nearest_z)
&& load_chunk(s, c_nearest_x, c_nearest_z, &ids, &metadata, &lighting)) {
s->loaded_chunks[s->loaded_chunks_length++]
= (struct loaded_chunk) {.x = c_nearest_x, .z = c_nearest_z};
int x2, z2;
uint8_t *ids, *metadata, *lighting;
load_chunk(str, &x2, &z2, &ids, &metadata, &lighting);
assert(x2 == c_nearest_x && z2 == c_nearest_z);
clin_rpc_send(&(struct client_rpc) {
.type = CRPC_CHUNK,
.payload.chunk.x = c_nearest_x * CHUNK_SIZE,
.payload.chunk.y = 0,
.payload.chunk.z = c_nearest_z * CHUNK_SIZE,
.payload.chunk.sx = CHUNK_SIZE,
.payload.chunk.sy = WORLD_HEIGHT,
.payload.chunk.sz = CHUNK_SIZE,
.payload.chunk.ids = ids,
.payload.chunk.metadata = metadata,
.payload.chunk.lighting = lighting,
});
}
clin_rpc_send(&(struct client_rpc) {
.type = CRPC_CHUNK,
.payload.chunk.x = c_nearest_x * CHUNK_SIZE,
.payload.chunk.y = 0,
.payload.chunk.z = c_nearest_z * CHUNK_SIZE,
.payload.chunk.sx = CHUNK_SIZE,
.payload.chunk.sy = WORLD_HEIGHT,
.payload.chunk.sz = CHUNK_SIZE,
.payload.chunk.ids = ids,
.payload.chunk.metadata = metadata,
.payload.chunk.lighting = lighting,
});
}
}
static void* server_local_thread(void* user) {
find_chunk_cache();
while(1) {
server_local_update(user);
usleep(100 * 1000);
usleep(20 * 1000);
}
return NULL;
@ -228,7 +158,9 @@ static void* server_local_thread(void* user) {
void server_local_create(struct server_local* s) {
assert(s);
s->loaded_chunks_length = 0;
s->loaded_regions_length = 0;
s->last_chunk_load = time_get();
s->player.has_pos = false;
lwp_t thread;
LWP_CreateThread(&thread, server_local_thread, s, NULL, 0, 8);

View file

@ -5,19 +5,24 @@
#include <stddef.h>
#include "../world.h"
#include "region_archive.h"
#define MAX_VIEW_DISTANCE 3 // in chunks
#define MAX_REGIONS 4
#define MAX_VIEW_DISTANCE 5 // in chunks
#define MAX_CHUNKS ((MAX_VIEW_DISTANCE * 2 + 2) * (MAX_VIEW_DISTANCE * 2 + 2))
struct server_local {
struct {
double x, y, z;
bool has_pos;
} player;
struct loaded_chunk {
w_coord_t x, z; // not!!! stored in multiples of CHUNK_SIZE
bool modified;
} loaded_chunks[MAX_CHUNKS];
size_t loaded_chunks_length;
struct region_archive loaded_regions[MAX_REGIONS];
size_t loaded_regions_length;
ptime_t last_chunk_load;
};