diff --git a/code/apps/playground/header/mock.h b/code/apps/playground/header/mock.h deleted file mode 100644 index 0b4701c..0000000 --- a/code/apps/playground/header/mock.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -uint64_t world_chunk_size(void) { - return 4; -} - -uint64_t world_chunk_amount(void) { - return 8; -} diff --git a/code/common/packets/packet_list.h b/code/common/packets/packet_list.h deleted file mode 100644 index 53344bd..0000000 --- a/code/common/packets/packet_list.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once -#include "packet_utils.h" -#include "packet.h" - -// NOTE(zaklaus): pkt data - diff --git a/code/common/world/blocks.c b/code/common/world/blocks.c new file mode 100644 index 0000000..f8d6a96 --- /dev/null +++ b/code/common/world/blocks.c @@ -0,0 +1,49 @@ +#include "zpl.h" +#include "world/blocks.h" + +// todo: csv parsing + utils + +#define BLOCKS_COUNT (sizeof(blocks)/sizeof(block)) + +typedef struct { + uint8_t tex_id; + char *name; + uint32_t flags; + uint32_t kind; + uint32_t biome; + char symbol; +} block; + +#include "blocks_list.c" + +uint8_t blocks_find(uint32_t biome, uint32_t kind) { + for (int i=0; i +#include "world/perlin.h" + +// adapted from: https://gist.github.com/nowl/828013#gistcomment-2807232 + +static const uint8_t PERLIN_PERM_TABLE[] = { + 208,34,231,213,32,248,233,56,161,78,24,140,71,48,140,254,245,255,247,247,40, + 185,248,251,245,28,124,204,204,76,36,1,107,28,234,163,202,224,245,128,167,204, + 9,92,217,54,239,174,173,102,193,189,190,121,100,108,167,44,43,77,180,204,8,81, + 70,223,11,38,24,254,210,210,177,32,81,195,243,125,8,169,112,32,97,53,195,13, + 203,9,47,104,125,117,114,124,165,203,181,235,193,206,70,180,174,0,167,181,41, + 164,30,116,127,198,245,146,87,224,149,206,57,4,192,210,65,210,129,240,178,105, + 228,108,245,148,140,40,35,195,38,58,65,207,215,253,65,85,208,76,62,3,237,55,89, + 232,50,217,64,244,157,199,121,252,90,17,212,203,149,152,140,187,234,177,73,174, + 193,100,192,143,97,53,145,135,19,103,13,90,135,151,199,91,239,247,33,39,145, + 101,120,99,3,186,86,99,41,237,203,111,79,220,135,158,42,30,154,120,67,87,167, + 135,176,183,191,253,115,184,21,233,58,129,233,142,39,128,211,118,137,139,255, + 114,20,218,113,154,27,127,246,250,1,8,198,250,209,92,222,173,21,88,102,219 +}; + +static int32_t perlin_noise2_sample(int32_t seed, int32_t x, int32_t y) { + int32_t yindex = (y + seed) % 256; + if (yindex < 0) + yindex += 256; + int32_t xindex = (PERLIN_PERM_TABLE[yindex] + x) % 256; + if (xindex < 0) + xindex += 256; + return PERLIN_PERM_TABLE[xindex]; +} + +static double perlin_lerp(double x, double y, double t) { + return x + t*(y - x); +} + +static double perlin_smooth_lerp(double x, double y, double t) { + return perlin_lerp(x, y, t * t * (3-2*t)); +} + +double perlin_noise2d(int32_t seed, double x, double y) { + int32_t x_int = floor(x); + int32_t y_int = floor(y); + double x_frac = x - x_int; + double y_frac = y - y_int; + int32_t s = perlin_noise2_sample(seed, x_int, y_int); + int32_t t = perlin_noise2_sample(seed, x_int+1, y_int); + int32_t u = perlin_noise2_sample(seed, x_int, y_int+1); + int32_t v = perlin_noise2_sample(seed, x_int+1, y_int+1); + double low = perlin_smooth_lerp(s, t, x_frac); + double high = perlin_smooth_lerp(u, v, x_frac); + double result = perlin_smooth_lerp(low, high, y_frac); + return result; +} + +double perlin_fbm(int32_t seed, double x, double y, double freq, uint32_t octaves) { + double xa = x*freq; + double ya = y*freq; + double amp = 1.0; + double res = 0.0; + double div = 0.0; + + for (uint32_t i=0; i max_length || !peer->data) { + // return LIBRG_WRITE_REJECT; + // } + + // /* write data and return how much we've written */ + // memcpy(buffer, peer->data, sizeof(vec3)); + // return sizeof(vec3); +} + +int32_t world_init_minimal(uint16_t block_size, uint16_t chunk_size, uint16_t world_size, world_pkt_reader_proc *reader_proc, world_pkt_writer_proc *writer_proc) { + world.chunk_size = chunk_size; + world.world_size = world_size; + if (reader_proc) world.reader_proc = reader_proc; + if (writer_proc) world.writer_proc = writer_proc; + + world.width = chunk_size * world_size; + world.height = chunk_size * world_size; + world.size = world.width * world.height; + world.block_size = block_size; + + if (world.tracker != NULL) { + librg_world_destroy(world.tracker); + world.tracker = NULL; + } + + world.tracker = librg_world_create(); + + if (world.tracker == NULL) { + zpl_printf("[ERROR] An error occurred while trying to create a server world.\n"); + return WORLD_ERROR_TRACKER_FAILED; + } + + /* config our world grid */ + librg_config_chunksize_set(world.tracker, block_size * chunk_size, block_size * chunk_size, 1); + librg_config_chunkamount_set(world.tracker, world_size, world_size, 1); + librg_config_chunkoffset_set(world.tracker, LIBRG_OFFSET_MID, LIBRG_OFFSET_MID, LIBRG_OFFSET_MID); + + return 0; +} + +int32_t world_init(int32_t seed, uint16_t block_size, uint16_t chunk_size, uint16_t world_size, world_pkt_reader_proc *reader_proc, world_pkt_writer_proc *writer_proc) { + if (world.data) { + return 0; + } + + world.seed = seed; + world_init_minimal(chunk_size, block_size, world_size, reader_proc, writer_proc); + world.data = zpl_malloc(sizeof(uint8_t)*world.size); + + if (!world.data) { + return WORLD_ERROR_OUTOFMEM; + } + + world.ecs = ecs_init(); + ecs_set_entity_range(world.ecs, 0, UINT32_MAX); + //ecs_set_threads(world.ecs, 4); + + ECS_IMPORT(world.ecs, General); + + for (int i = 0; i < chunk_size * chunk_size; ++i) { + ecs_entity_t e = ecs_new(world.ecs, 0); + ecs_set(world.ecs, e, Chunk, { + .x = i % chunk_size, + .y = i / chunk_size, + }); + + librg_entity_track(world.tracker, e); + librg_entity_chunk_set(world.tracker, e, i); + } + + // librg_event_set(world.tracker, LIBRG_WRITE_UPDATE, world_write_update); + // librg_event_set(world.tracker, LIBRG_READ_UPDATE, server_read_update); + + zpl_printf("[INFO] Created a new server world\n"); + + return world_gen(); +} + +int32_t world_destroy(void) { + librg_world_destroy(world.tracker); + ecs_fini(world.ecs); + zpl_mfree(world.data); + zpl_memset(&world, 0, sizeof(world)); + zpl_printf("[INFO] World was destroyed.\n"); + return WORLD_ERROR_NONE; +} + +int32_t world_update() { + ecs_progress(world.ecs, 0); + return 0; +} + +int32_t world_read(void* data, uint32_t datalen) { + if (world.reader_proc) { + return world.reader_proc(data, datalen); + } + return -1; +} + +int32_t world_write(pkt_header *pkt) { + if (world.writer_proc) { + return world.writer_proc(pkt); + } + return -1; +} + +uint32_t world_buf(uint8_t const **ptr, uint32_t *width) { + ZPL_ASSERT_NOT_NULL(world.data); + ZPL_ASSERT_NOT_NULL(ptr); + *ptr = world.data; + if (width) *width = world.width; + return world.size; +} + +ecs_world_t * world_ecs() { + return world.ecs; +} + +librg_world * world_tracker() { + return world.tracker; +} + +uint16_t world_chunk_size(void) { + return world.chunk_size; +} + +uint16_t world_chunk_amount(void) { + return world.world_size; +} + +#include "world_gen.c" diff --git a/code/common/world/world.h b/code/common/world/world.h new file mode 100644 index 0000000..e66c69e --- /dev/null +++ b/code/common/world/world.h @@ -0,0 +1,32 @@ +#pragma once +#include "system.h" +#include "librg.h" +#include "packets/packet.h" +#include "flecs/flecs.h" + +#define WORLD_ERROR_NONE +0x0000 +#define WORLD_ERROR_OUTOFMEM -0x0001 +#define WORLD_ERROR_INVALID_BLOCKS -0x0002 +#define WORLD_ERROR_INVALID_DIMENSIONS -0x0003 +#define WORLD_ERROR_INVALID_BUFFER -0x0004 +#define WORLD_ERROR_TRACKER_FAILED -0x0005 + +#define WORLD_PKT_READER(name) int32_t name(void* data, uint32_t datalen) +typedef WORLD_PKT_READER(world_pkt_reader_proc); + +#define WORLD_PKT_WRITER(name) int32_t name(pkt_header *pkt) +typedef WORLD_PKT_WRITER(world_pkt_writer_proc); + +int32_t world_init_minimal(uint16_t block_size, uint16_t chunk_size, uint16_t world_size, world_pkt_reader_proc *reader_proc, world_pkt_writer_proc *writer_proc); +int32_t world_init(int32_t seed, uint16_t block_size, uint16_t chunk_size, uint16_t world_size, world_pkt_reader_proc *reader_proc, world_pkt_writer_proc *writer_proc); +int32_t world_destroy(void); +int32_t world_update(void); +int32_t world_read(void* data, uint32_t datalen); +int32_t world_write(pkt_header *pkt); + +uint32_t world_buf(uint8_t const **ptr, uint32_t *width); +ecs_world_t * world_ecs(void); +librg_world * world_tracker(void); + +uint16_t world_chunk_size(void); +uint16_t world_chunk_amount(void); \ No newline at end of file diff --git a/code/common/world/world_gen.c b/code/common/world/world_gen.c new file mode 100644 index 0000000..d845a3d --- /dev/null +++ b/code/common/world/world_gen.c @@ -0,0 +1,133 @@ +#include "zpl.h" + +#include +#include + +#include "world/world.h" +#include "world/blocks.h" +#include "world/perlin.h" + +#define WORLD_BLOCK_OBSERVER(name) uint32_t name(uint32_t id, uint32_t block_idx) +typedef WORLD_BLOCK_OBSERVER(world_block_observer_proc); + +#define WORLD_PERLIN_FREQ 1.0 +#define WORLD_PERLIN_OCTAVES 1 + +static void world_fill_rect(uint32_t id, uint32_t x, uint32_t y, uint32_t w, uint32_t h, world_block_observer_proc *proc) { + for (uint32_t cy=y; cy= world.width) continue; + if (cy < 0 || cy >= world.height) continue; + uint32_t i = (cy*world.width) + cx; + + if (proc) { + uint32_t new_id = (*proc)(id, i); + if (new_id != BLOCK_INVALID) { + id = new_id; + } + else continue; + } + + world.data[i] = id; + } + } +} + +static void world_fill_circle(uint32_t id, uint32_t x, uint32_t y, uint32_t w, uint32_t h, world_block_observer_proc *proc) { + for (uint32_t cy=y; cy= world.width) continue; + if (cy < 0 || cy >= world.height) continue; + uint32_t i = (cy*world.width) + cx; + + if (proc) { + uint32_t new_id = (*proc)(id, i); + if (new_id != BLOCK_INVALID) { + id = new_id; + } + else continue; + } + + world.data[i] = id; + } + } +} + +static void world_fill_rect_anchor(uint32_t id, uint32_t x, uint32_t y, uint32_t w, uint32_t h, float ax, float ay, world_block_observer_proc *proc) { + uint32_t w2 = (uint32_t)floorf(w*ax); + uint32_t h2 = (uint32_t)floorf(h*ay); + world_fill_rect(id, x-w2, y-h2, w, h, proc); +} + +static WORLD_BLOCK_OBSERVER(shaper) { + uint32_t biome = blocks_get_biome(id); + uint32_t kind = blocks_get_kind(id); + uint32_t old_biome = blocks_get_biome(world.data[block_idx]); + uint32_t old_kind = blocks_get_kind(world.data[block_idx]); + + if (biome == old_biome) { + if (kind == BLOCK_KIND_WALL && kind == old_kind) { + return blocks_find(biome, BLOCK_KIND_HILL); + } + if (kind == BLOCK_KIND_HILL && kind == old_kind) { + return blocks_find(biome, BLOCK_KIND_HILL_SNOW); + } + } + + return id; +} + +static uint8_t world_perlin_cond(uint32_t block_idx, double chance) { + uint32_t x = block_idx % world.width; + uint32_t y = block_idx / world.width; + + return perlin_fbm(world.seed, x, y, WORLD_PERLIN_FREQ, WORLD_PERLIN_OCTAVES) < chance; +} + +static WORLD_BLOCK_OBSERVER(shaper_noise80) { + return world_perlin_cond(block_idx, 0.80) ? shaper(id, block_idx) : BLOCK_INVALID; +} + +static WORLD_BLOCK_OBSERVER(shaper_noise50) { + return world_perlin_cond(block_idx, 0.50) ? shaper(id, block_idx) : BLOCK_INVALID; +} + +static WORLD_BLOCK_OBSERVER(shaper_noise33) { + return world_perlin_cond(block_idx, 0.33) ? shaper(id, block_idx) : BLOCK_INVALID; +} + +static void world_fill_mountain(uint32_t x, uint32_t y) { + +} + +#define RAND_RANGE(x,y) (x + (int)rand()%(y-(x))) + +int32_t world_gen() { + // TODO: perform world gen + // atm, we will fill the world with ground and surround it by walls + uint32_t wall_id = blocks_find(BLOCK_BIOME_DEV, BLOCK_KIND_WALL); + uint32_t grnd_id = blocks_find(BLOCK_BIOME_DEV, BLOCK_KIND_GROUND); + uint32_t watr_id = blocks_find(BLOCK_BIOME_DEV, BLOCK_KIND_WATER); + + srand(world.seed); + + // walls + world_fill_rect(wall_id, 0, 0, world.width, world.height, NULL); + + // ground + world_fill_rect(grnd_id, 1, 1, world.width-2, world.height-2, NULL); + + // water + for (int i=0; icount; i++) { + // TODO: handle collisions + p[i].x += v[i].x * it->delta_time; + p[i].y += v[i].y * it->delta_time; + } +} + +void PhysicsImport(ecs_world_t *ecs) { + ECS_MODULE(ecs, Physics); + ecs_set_name_prefix(ecs, "Physics"); + + ECS_TAG(ecs, Walking); + ECS_TAG(ecs, Flying); + ECS_TYPE(ecs, Movement, Walking, Flying); + + ECS_COMPONENT(ecs, Velocity); + + ECS_SYSTEM(ecs, MoveWalk, EcsOnUpdate, general.Position, Velocity, SWITCH | Movement, CASE | Walking); + + ECS_SET_TYPE(Movement); + ECS_SET_ENTITY(Walking); + ECS_SET_ENTITY(Flying); + ECS_SET_COMPONENT(Velocity); + ECS_SET_ENTITY(MoveWalk); +}