new crafting system

efd/v1
Dominik Madarász 2022-10-18 08:57:43 +02:00
parent 70bb456a43
commit 9e368527a3
14 changed files with 318 additions and 148 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 B

View File

@ -9,10 +9,11 @@ add_library(eco2d-foundation STATIC
src/platform/profiler.c
src/models/assets.c
src/models/components.c
src/models/components.c
src/models/items.c
src/models/entity.c
src/models/device.c
src/models/crafting.c
src/models/prefabs/player.c
src/models/prefabs/vehicle.c

View File

@ -0,0 +1,26 @@
#include "models/crafting.h"
#define R(id1,qty1)\
{\
.id = id1,\
.qty = qty1\
}
#define RECIPE(id,prod,qty,...)\
{\
.product = id,\
.product_qty = qty,\
.producer = prod,\
.reagents = (reagent[]){\
__VA_ARGS__\
}\
}
static recipe recipes[] = {
RECIPE(ASSET_BELT, ASSET_FURNACE, 4, R(ASSET_FENCE, 8), R(ASSET_WOOD, 2), {0}),
};
#define MAX_RECIPES (sizeof(recipes)/sizeof(recipes[0]))
#undef R
#undef RECIPE

View File

@ -4,24 +4,24 @@
static item_desc items[] = {
{ .kind = 0, .max_quantity = 0, },
ITEM_INGREDIENT(ASSET_FENCE, 64, ASSET_FURNACE, ASSET_BELT, 0),
ITEM_HOLD(ASSET_FENCE, 64),
ITEM_ENERGY(ASSET_COAL, ASSET_FURNACE, 64, 15.0f),
ITEM_SELF(ASSET_WOOD, 64),
ITEM_SELF(ASSET_TREE, 64),
ITEM_SELF(ASSET_TEST_TALL, 64),
// ITEM_BLUEPRINT(ASSET_BLUEPRINT, 1, 4, 4, "]]]]]CF] ]]]]]"),
ITEM_BLUEPRINT_PROXY(ASSET_BLUEPRINT_DEMO_HOUSE, ASSET_BLUEPRINT, 1, 4, 4, PROT({ ASSET_WOOD,ASSET_WOOD,ASSET_WOOD,ASSET_WOOD,
ASSET_WOOD,ASSET_FURNACE,ASSET_CHEST,ASSET_WOOD,
ASSET_WOOD,ASSET_EMPTY,ASSET_EMPTY,ASSET_WOOD,
ASSET_WOOD,ASSET_WOOD,ASSET_EMPTY,ASSET_WOOD})),
ASSET_WOOD,ASSET_FURNACE,ASSET_CHEST,ASSET_WOOD,
ASSET_WOOD,ASSET_EMPTY,ASSET_EMPTY,ASSET_WOOD,
ASSET_WOOD,ASSET_WOOD,ASSET_EMPTY,ASSET_WOOD})),
ITEM_SELF_DIR(ASSET_BELT, 999),
ITEM_PROXY(ASSET_BELT_LEFT, ASSET_BELT),
ITEM_PROXY(ASSET_BELT_RIGHT, ASSET_BELT),
ITEM_PROXY(ASSET_BELT_UP, ASSET_BELT),
ITEM_PROXY(ASSET_BELT_DOWN, ASSET_BELT),
ITEM_ENT(ASSET_CHEST, 32, ASSET_CHEST),
ITEM_ENT(ASSET_FURNACE, 32, ASSET_FURNACE),
};

View File

@ -17,7 +17,6 @@ ECS_COMPONENT_DECLARE(Inventory);
ECS_COMPONENT_DECLARE(ItemContainer);
ECS_COMPONENT_DECLARE(Producer);
ECS_COMPONENT_DECLARE(EnergySource);
ECS_COMPONENT_DECLARE(Ingredient);
ECS_COMPONENT_DECLARE(ItemRouter);
ECS_COMPONENT_DECLARE(Device);
ECS_COMPONENT_DECLARE(Blueprint);
@ -44,7 +43,6 @@ void ComponentsImport(ecs_world_t *ecs) {
ECS_COMPONENT_DEFINE(ecs, ItemContainer);
ECS_COMPONENT_DEFINE(ecs, Producer);
ECS_COMPONENT_DEFINE(ecs, EnergySource);
ECS_COMPONENT_DEFINE(ecs, Ingredient);
ECS_COMPONENT_DEFINE(ecs, ItemRouter);
ECS_COMPONENT_DEFINE(ecs, Device);
ECS_COMPONENT_DEFINE(ecs, Blueprint);

View File

@ -131,12 +131,13 @@ typedef struct {
typedef struct {
asset_id processed_item;
uint32_t processed_item_qty;
float process_time;
float energy_level;
} Producer;
typedef struct {
char _unused;
uint32_t push_qty;
} ItemRouter;
typedef struct {
@ -144,12 +145,6 @@ typedef struct {
float energy_level;
} EnergySource;
typedef struct {
asset_id producer;
asset_id additional_ingredient; // optional - can specify additional item we need in the container to craft this item
asset_id product;
} Ingredient;
typedef struct {
uint16_t asset;
@ -188,7 +183,6 @@ extern ECS_COMPONENT_DECLARE(Inventory);
extern ECS_COMPONENT_DECLARE(ItemContainer);
extern ECS_COMPONENT_DECLARE(Producer);
extern ECS_COMPONENT_DECLARE(EnergySource);
extern ECS_COMPONENT_DECLARE(Ingredient);
extern ECS_COMPONENT_DECLARE(ItemRouter);
extern ECS_COMPONENT_DECLARE(Device);
extern ECS_COMPONENT_DECLARE(Blueprint);

View File

@ -0,0 +1,147 @@
#include "crafting.h"
#include "models/items.h"
typedef struct {
asset_id id;
uint32_t qty;
} reagent;
typedef struct {
asset_id product;
uint32_t product_qty;
asset_id producer;
reagent *reagents;
} recipe;
#include "lists/crafting_list.c"
uint32_t craft__find_num_recipes_by_reagent(asset_id producer, asset_id id) {
uint32_t num_recipes=0;
for (int i = 0; i < MAX_RECIPES; ++i) {
if (recipes[i].producer == producer) {
for (int j = 0; recipes[i].reagents[j].id; ++j) {
if (recipes[i].reagents[j].id == id) {
++num_recipes;
}
}
}
}
return num_recipes;
}
recipe *craft__find_recipe_by_reagent(asset_id producer, asset_id id, uint32_t slot_id) {
for (int i = 0; i < MAX_RECIPES; ++i) {
if (recipes[i].producer == producer) {
for (int j = 0; recipes[i].reagents[j].id; ++j) {
if (recipes[i].reagents[j].id == id) {
if (slot_id > 0) {
--slot_id;
continue;
}
return &recipes[i];
}
}
}
}
return NULL;
}
bool craft_is_reagent_used_in_producer(asset_id reagent, asset_id producer) {
return craft__find_num_recipes_by_reagent(producer, reagent) > 0;
}
asset_id craft_perform_recipe(ecs_entity_t *items, asset_id producer, uint32_t *quantity) {
ZPL_ASSERT_NOT_NULL(items);
for (int i = 0; i < ITEMS_CONTAINER_SIZE; i++) {
ecs_entity_t item_slot_ent = items[i];
if (item_slot_ent == 0) continue;
Item *item = item_get_data(item_slot_ent);
if (!item) continue;
uint32_t num_recipes = craft__find_num_recipes_by_reagent(producer, item->kind);
for (uint32_t rec_i = 0; rec_i < num_recipes; ++rec_i) {
// TODO(zaklaus): slow, find a better way to retrieve known recipes
recipe *rec = craft__find_recipe_by_reagent(producer, item->kind, rec_i);
if (!rec) {
// NOTE(zaklaus): this item is not used as a reagent, skip it.
// TODO(zaklaus): is this a bug? should we assert?
continue;
}
uint8_t skip_slot=0;
// NOTE(zaklaus): analyse if all the reagents are present
for (int j = 0; rec->reagents[j].id; ++j) {
reagent *rea = &rec->reagents[j];
uint32_t pending_qty = rea->qty;
for (int k = 0; k < ITEMS_CONTAINER_SIZE; k++) {
ecs_entity_t rea_item_slot_ent = items[k];
if (rea_item_slot_ent == 0) continue;
Item *rea_item = item_get_data(rea_item_slot_ent);
if (!rea_item) continue;
if (rea->id == rea_item->kind && rea_item->quantity > 0) {
pending_qty -= zpl_min(pending_qty, rea_item->quantity);
if (pending_qty == 0) {
break;
}
}
}
if (pending_qty > 0) {
// NOTE(zaklaus): reagent not found, bail
skip_slot=1;
break;
}
}
// NOTE(zaklaus): demand not met, bye!
if (skip_slot)
continue;
// NOTE(zaklaus): deplete used reagents
for (int j = 0; rec->reagents[j].id; ++j) {
reagent *rea = &rec->reagents[j];
uint32_t pending_qty = rea->qty;
for (int k = 0; k < ITEMS_CONTAINER_SIZE; k++) {
ecs_entity_t rea_item_slot_ent = items[k];
if (rea_item_slot_ent == 0) continue;
Item *rea_item = item_get_data(rea_item_slot_ent);
if (!rea_item) continue;
if (rea->id == rea_item->kind && rea_item->quantity > 0) {
rea_item->quantity -= zpl_min(pending_qty, rea_item->quantity);
pending_qty -= zpl_min(pending_qty, rea_item->quantity);
if (rea_item->quantity == 0) {
item_despawn(rea_item_slot_ent);
items[k] = 0;
}
if (pending_qty == 0) {
break;
}
}
}
}
// NOTE(zaklaus): all done, return the product and its qty
*quantity = rec->product_qty;
return rec->product;
}
}
return 0;
}
// TODO(zaklaus):
asset_id craft_has_byproducts(asset_id product) {
return 0xFF;
}
// TODO(zaklaus):
uint32_t craft_resolve_graph(asset_id product, uint16_t *hops, uint8_t direct_cost) {
return 0;
}

View File

@ -0,0 +1,21 @@
#pragma once
#include "platform/system.h"
#include "models/assets.h"
#include "models/components.h"
// NOTE(zaklaus): resolves recipe dependencies and consumes reagents
// to enqueue a production of a new item.
// TODO(zaklaus): "items" is assumed to come from ItemContainer component.
asset_id craft_perform_recipe(ecs_entity_t *items, asset_id producer, uint32_t *quantity);
// NOTE(zaklaus): informs us on whether this product has any byproducts desired.
asset_id craft_has_byproducts(asset_id product);
// NOTE(zaklaus): mostly used by item router so we don't push reagents out
bool craft_is_reagent_used_in_producer(asset_id reagent, asset_id producer);
// NOTE(zaklaus): resolves the production chain and analyses the amount of items required
// and a number of hops (production layers) needed to produce the item.
// optionally, it allows to calculate "direct_cost" of the product.
uint32_t craft_resolve_graph(asset_id product, uint16_t *hops, uint8_t direct_cost);

View File

@ -32,7 +32,7 @@ void item_show(uint64_t ent, bool show) {
uint64_t item_spawn(asset_id kind, uint32_t qty) {
ecs_entity_t e = entity_spawn(EKIND_ITEM);
Item *d = ecs_get_mut(world_ecs(), e, Item);
*d = (Item){
.kind = item_fix_kind(kind),
@ -40,32 +40,26 @@ uint64_t item_spawn(asset_id kind, uint32_t qty) {
.merger_time = 0,
.durability = 1.0f,
};
item_desc *it = &items[item_find(kind)];
if (it->has_storage) {
ecs_add(world_ecs(), e, BlockHarvest);
ItemContainer *storage = ecs_get_mut(world_ecs(), e, ItemContainer);
*storage = (ItemContainer){0};
}
switch (it->attachment) {
case UDATA_ENERGY_SOURCE: {
EnergySource *f = ecs_get_mut(world_ecs(), e, EnergySource);
*f = (EnergySource){
.kind = it->energy_source.producer,
.energy_level = it->energy_source.energy_level,
};
} break;
case UDATA_INGREDIENT: {
Ingredient *i = ecs_get_mut(world_ecs(), e, Ingredient);
i->producer = it->ingredient.producer;
i->product = it->ingredient.product;
i->additional_ingredient = it->ingredient.additional_ingredient;
} break;
default: break;
case UDATA_ENERGY_SOURCE: {
EnergySource *f = ecs_get_mut(world_ecs(), e, EnergySource);
*f = (EnergySource){
.kind = it->energy_source.producer,
.energy_level = it->energy_source.energy_level,
};
} break;
default: break;
}
return (uint64_t)e;
}
@ -108,7 +102,7 @@ void item_use(ecs_world_t *ecs, ecs_entity_t e, Item *it, Position p, uint64_t u
asset_id item_asset = blocks_get_asset(l.bid);
item_id item_asset_id = item_find(item_asset);
if (item_asset_id == ASSET_INVALID) return;
// NOTE(zaklaus): If we replace the same item, refund 1 qty and let it replace it
if (item_asset_id == it_id) {
it->quantity++;
@ -123,7 +117,7 @@ void item_use(ecs_world_t *ecs, ecs_entity_t e, Item *it, Position p, uint64_t u
world_chunk_replace_block(l.chunk_id, l.id, blocks_find(desc->place.kind + (asset_id)udata));
it->quantity--;
}break;
case UKIND_PLACE_ITEM:{
world_block_lookup l = world_block_from_realpos(p.x, p.y);
if (l.is_outer && l.bid > 0) {
@ -133,14 +127,14 @@ void item_use(ecs_world_t *ecs, ecs_entity_t e, Item *it, Position p, uint64_t u
else if (l.bid > 0 && blocks_get_flags(l.bid) & (BLOCK_FLAG_COLLISION|BLOCK_FLAG_ESSENTIAL)) {
return;
}
ecs_entity_t e = entity_spawn_id(desc->place_item.id);
ZPL_ASSERT(world_entity_valid(e));
entity_set_position(e, p.x, p.y);
it->quantity--;
}break;
case UKIND_PLACE_ITEM_DATA:{
world_block_lookup l = world_block_from_realpos(p.x, p.y);
if (l.is_outer && l.bid > 0) {
@ -150,14 +144,14 @@ void item_use(ecs_world_t *ecs, ecs_entity_t e, Item *it, Position p, uint64_t u
else if (l.bid > 0 && blocks_get_flags(l.bid) & (BLOCK_FLAG_COLLISION|BLOCK_FLAG_ESSENTIAL)) {
return;
}
ecs_entity_t e = entity_spawn_id_with_data(desc->place_item.id, desc);
ZPL_ASSERT(world_entity_valid(e));
entity_set_position(e, p.x, p.y);
it->quantity--;
}break;
case UKIND_DELETE:
case UKIND_END_PLACE:
case UKIND_PROXY:

View File

@ -12,7 +12,7 @@ typedef enum {
UKIND_PLACE_ITEM,
UKIND_PLACE_ITEM_DATA,
UKIND_END_PLACE,
// NOTE(zaklaus): the rest of possible actions
UKIND_HOLD,
UKIND_PROXY,
@ -21,7 +21,6 @@ typedef enum {
typedef enum {
UDATA_NONE,
UDATA_ENERGY_SOURCE,
UDATA_INGREDIENT,
} item_attachment;
typedef struct {
@ -30,36 +29,30 @@ typedef struct {
item_attachment attachment;
uint32_t max_quantity;
uint8_t has_storage;
// NOTE(zaklaus): usage data
union {
struct {
asset_id kind;
bool directional; // NOTE(zaklaus): expects next 4 asset entries to be direction assets
} place;
struct {
asset_id id;
} proxy;
struct {
asset_id id;
} place_item;
};
union {
struct {
asset_id producer;
float energy_level;
} energy_source;
struct {
asset_id producer;
asset_id product;
asset_id additional_ingredient;
} ingredient;
};
// NOTE: item data
union {
struct {

View File

@ -15,7 +15,7 @@ uint64_t furnace_spawn(void) {
*producer = (Producer){0};
producer->energy_level = 69.0f;
ecs_add(world_ecs(), e, ItemRouter);
ecs_set(world_ecs(), e, ItemRouter, {1});
return (uint64_t)e;
}

View File

@ -1,3 +1,5 @@
#include "models/crafting.h"
static inline
asset_id FetchAssetAtPos(float x, float y) {
world_block_lookup lookup = world_block_from_realpos(x, y);
@ -63,6 +65,7 @@ void PushItemsOnNodes(ecs_iter_t *it) {
// We need a way to refer to specific blocks in the world so we can do easy block ID checks
// and re-build the cache when a change is detected.
float push_dx[4], push_dy[4];
uint8_t nodes = CheckForNearbyBelts(&p[i], push_dx, push_dy);
uint8_t num_nodes = (uint8_t)zpl_count_set_bits(nodes);
@ -77,13 +80,11 @@ void PushItemsOnNodes(ecs_iter_t *it) {
ecs_entity_t item_slot_ent = storage[i].items[j];
if (item_slot_ent == 0) continue;
Item *item = item_get_data(item_slot_ent);
if (!item) continue;
const Ingredient *ing = 0;
// NOTE(zaklaus): Make sure we don't push out items from input node
if ((ing = ecs_get_if(it->world, item_slot_ent, Ingredient))) {
if (ing->producer == d->asset) {
continue;
}
if (craft_is_reagent_used_in_producer(item->kind, d->asset)) {
// NOTE(zaklaus): this is an input reagent, keep it
continue;
}
while (item->quantity > 0 && num_nodes > 0) {
@ -94,14 +95,14 @@ void PushItemsOnNodes(ecs_iter_t *it) {
continue;
}
uint64_t e = item_spawn(item->kind, 1);
uint64_t e = item_spawn(item->kind, zpl_min(r->push_qty, item->quantity));
entity_set_position(e, p[i].x + push_dx[counter], p[i].y + push_dy[counter]);
Velocity *e_vel = ecs_get_mut_ex(it->world, e, Velocity);
e_vel->x = push_dx[counter];
e_vel->y = push_dy[counter];
--item->quantity;
item->quantity -= zpl_min(r->push_qty, item->quantity);
--num_nodes;
++counter;
}

View File

@ -1,3 +1,5 @@
#include "models/crafting.h"
void ProduceItems(ecs_iter_t *it) {
ItemContainer *storage = ecs_field(it, ItemContainer, 1);
Producer *producer = ecs_field(it, Producer, 2);
@ -7,6 +9,7 @@ void ProduceItems(ecs_iter_t *it) {
for (int i = 0; i < it->count; i++) {
for (int j = 0; j < ITEMS_CONTAINER_SIZE; j++) {
ecs_entity_t item_slot_ent = storage[i].items[j];
if (item_slot_ent == 0) continue;
Item *item = item_get_data(item_slot_ent);
const EnergySource *energy_source = 0;
@ -19,26 +22,18 @@ void ProduceItems(ecs_iter_t *it) {
continue;
}
// TODO(zaklaus): handle fuel
// if (producer[i].energy_level <= 0.0f) continue;
// TODO(zaklaus): use ticks
if (producer[i].process_time < game_time()) {
if (producer[i].processed_item > 0) {
uint64_t e = item_spawn(producer[i].processed_item, 1);
uint64_t e = item_spawn(producer[i].processed_item, producer[i].processed_item_qty);
entity_set_position(e, p[i].x, p[i].y);
producer[i].processed_item = 0;
} else {
const Ingredient *ing = 0;
if ((ing = ecs_get_if(it->world, item_slot_ent, Ingredient))) {
if (ing->producer == d->asset) {
if (item->quantity <= 0) {
item_despawn(item_slot_ent);
storage[i].items[j] = 0;
} else {
producer[i].processed_item = ing->product;
producer[i].process_time = game_time() + game_rules.furnace_cook_time;
}
item->quantity--;
}
}
producer[i].processed_item = craft_perform_recipe(storage[i].items, d->asset, &producer[i].processed_item_qty);
producer[i].process_time = game_time() + game_rules.furnace_cook_time;
}
}
}

View File

@ -26,86 +26,86 @@ static world_component_cache component_cache;
entity_view *world_build_entity_view(int64_t e) {
entity_view *cached_ev = world_snapshot_get(&streamer_snapshot, e);
if (cached_ev) return cached_ev;
entity_view view = {0};
const Classify *classify = ecs_get(world_ecs(), e, Classify);
ZPL_ASSERT(classify);
view.kind = classify->id;
const Position *pos = ecs_get(world_ecs(), e, Position);
if (pos) {
view.x = pos->x;
view.y = pos->y;
}
const Velocity *vel = ecs_get(world_ecs(), e, Velocity);
if (vel) {
view.flag |= EFLAG_INTERP;
view.vx = vel->x;
view.vy = vel->y;
}
const Health *health = ecs_get(world_ecs(), e, Health);
if (health) {
view.hp = health->hp;
view.max_hp = health->max_hp;
}
if (ecs_get(world_ecs(), e, Vehicle)) {
Vehicle const* veh = ecs_get(world_ecs(), e, Vehicle);
view.heading = veh->heading;
view.veh_kind = veh->veh_kind;
}
if (ecs_get(world_ecs(), e, Item)) {
Item const* dr = ecs_get(world_ecs(), e, Item);
view.asset = dr->kind;
view.quantity = dr->quantity;
view.durability = dr->durability;
}
if (ecs_get(world_ecs(), e, Device)) {
Device const* dev = ecs_get(world_ecs(), e, Device);
view.asset = dev->asset;
view.progress_active = dev->progress_active;
view.progress_value = dev->progress_value;
}
view.inside_vehicle = ecs_get(world_ecs(), e, IsInVehicle) != 0 ? true : false;
Inventory *inv = 0;
if ((inv = ecs_get_mut_if_ex(world_ecs(), e, Inventory))) {
view.has_items = true;
for (int i = 0; i < ITEMS_INVENTORY_SIZE; i += 1) {
const Item *it = ecs_get_if(world_ecs(), inv->items[i], Item);
view.items[i] = it ? *it : (Item){0};
}
const Input *in = ecs_get(world_ecs(), e, Input);
if (in){
view.selected_item = in->selected_item;
view.pick_ent = (uint64_t)in->pick_ent;
view.sel_ent = (uint64_t)in->sel_ent;
if (world_entity_valid(in->storage_ent)){
ItemContainer *ic = 0;
if ((ic = ecs_get_mut_if_ex(world_ecs(), in->storage_ent, ItemContainer))){
view.has_storage_items = true;
for (int i = 0; i < ITEMS_CONTAINER_SIZE; i += 1) {
const Item *it = ecs_get_if(world_ecs(), ic->items[i], Item);
view.storage_items[i] = it ? *it : (Item){0};
}
view.storage_selected_item = in->storage_selected_item;
}
}
}
}
Chunk *chunk = 0;
if ((chunk = ecs_get_mut_if(world_ecs(), e, Chunk))) {
view.chk_id = chunk->id;
@ -114,16 +114,16 @@ entity_view *world_build_entity_view(int64_t e) {
view.blocks_used = 1;
view.is_dirty = chunk->is_dirty;
chunk->is_dirty = false;
for (int i = 0; i < world.chunk_size*world.chunk_size; i += 1) {
view.blocks[i] = world.block_mapping[chunk->id][i];
}
for (int i = 0; i < world.chunk_size*world.chunk_size; i += 1) {
view.outer_blocks[i] = world.outer_block_mapping[chunk->id][i];
}
}
world_snapshot_set(&streamer_snapshot, e, view);
return world_snapshot_get(&streamer_snapshot, e);
}
@ -138,7 +138,7 @@ int32_t tracker_write_create(librg_world *w, librg_event *e) {
#endif
size_t actual_length = librg_event_size_get(w, e);
char *buffer = librg_event_buffer_get(w, e);
return (int32_t)entity_view_pack_struct(buffer, actual_length, world_build_entity_view(entity_id));
}
@ -159,14 +159,14 @@ int32_t tracker_write_update(librg_world *w, librg_event *e) {
size_t actual_length = librg_event_size_get(w, e);
char *buffer = librg_event_buffer_get(w, e);
entity_view *view = world_build_entity_view(entity_id);
// NOTE(zaklaus): exclude chunks from updates as they never move
{
if (view->kind == EKIND_CHUNK && !view->is_dirty) {
return LIBRG_WRITE_REJECT;
}
}
// NOTE(zaklaus): action-based updates
#if ECO2D_STREAM_ACTIONFILTER
{
@ -175,7 +175,7 @@ int32_t tracker_write_update(librg_world *w, librg_event *e) {
}
}
#endif
return (int32_t)entity_view_pack_struct(buffer, actual_length, view);
}
@ -198,15 +198,15 @@ void world_chunk_setup_grid(void) {
world.outer_block_mapping[i] = zpl_malloc(sizeof(block_id)*zpl_square(world.chunk_size));
chunk->id = i;
chunk->is_dirty = false;
for (int y = 0; y < world.chunk_size; y += 1) {
for (int x = 0; x < world.chunk_size; x += 1) {
int chk_x = chunk->x * world.chunk_size;
int chk_y = chunk->y * world.chunk_size;
block_id *c = &world.block_mapping[i][(y*world.chunk_size)+x];
*c = world.data[(chk_y+y)*world.dim + (chk_x+x)];
c = &world.outer_block_mapping[i][(y*world.chunk_size)+x];
*c = world.outer_data[(chk_y+y)*world.dim + (chk_x+x)];
}
@ -217,14 +217,14 @@ void world_chunk_setup_grid(void) {
static inline
void world_configure_tracker(void) {
world.tracker = librg_world_create();
ZPL_ASSERT_MSG(world.tracker, "[ERROR] An error occurred while trying to create a server world.");
/* config our world grid */
librg_config_chunksize_set(world.tracker, WORLD_BLOCK_SIZE * world.chunk_size, WORLD_BLOCK_SIZE * world.chunk_size, 1);
librg_config_chunkamount_set(world.tracker, world.chunk_amount, world.chunk_amount, 0);
librg_config_chunkoffset_set(world.tracker, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG);
librg_event_set(world.tracker, LIBRG_WRITE_CREATE, tracker_write_create);
librg_event_set(world.tracker, LIBRG_WRITE_REMOVE, tracker_write_remove);
librg_event_set(world.tracker, LIBRG_WRITE_UPDATE, tracker_write_update);
@ -234,14 +234,14 @@ static inline
void world_init_worldgen_data(void) {
world.data = zpl_malloc(sizeof(block_id)*world.size);
world.outer_data = zpl_malloc(sizeof(block_id)*world.size);
ZPL_ASSERT(world.data && world.outer_data);
}
static inline
void world_setup_ecs(void) {
world.ecs = ecs_init();
ECS_IMPORT(world.ecs, Components);
ECS_IMPORT(world.ecs, Systems);
world.ecs_update = ecs_query_new(world.ecs, "components.ClientInfo, components.Position");
@ -261,7 +261,7 @@ static inline
void world_generate_instance(void) {
int32_t world_build_status = worldgen_build(&world);
ZPL_ASSERT(world_build_status >= 0);
for (int i = 0; i < zpl_square(world.dim); ++i) {
if (world.data[i] == 0) {
ZPL_PANIC("Worldgen failure! Block %d is unset!\n", i);
@ -283,10 +283,10 @@ int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount) {
world.seed = seed;
world.chunk_size = chunk_size;
world.chunk_amount = chunk_amount;
world.dim = (world.chunk_size * world.chunk_amount);
world.size = world.dim * world.dim;
world_configure_tracker();
world_setup_ecs();
world_init_worldgen_data();
@ -294,9 +294,9 @@ int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount) {
world_init_mapping();
world_chunk_setup_grid();
world_free_worldgen_data();
zpl_printf("[INFO] Created a new server world\n");
return WORLD_ERROR_NONE;
}
@ -322,33 +322,33 @@ int32_t world_destroy(void) {
static void world_tracker_update(uint8_t ticker, float freq, uint8_t radius) {
if (world.tracker_update[ticker] > (float)(get_cached_time())) return;
world.tracker_update[ticker] = (float)(get_cached_time()) + freq;
profile(PROF_WORLD_WRITE) {
ecs_iter_t it = ecs_query_iter(world_ecs(), world.ecs_update);
static char buffer[WORLD_LIBRG_BUFSIZ] = {0};
world.active_layer_id = ticker;
while (ecs_query_next(&it)) {
ClientInfo *p = ecs_field(&it, ClientInfo, 1);
for (int i = 0; i < it.count; i++) {
size_t datalen = WORLD_LIBRG_BUFSIZ;
if (!p[i].active)
continue;
int32_t result = librg_world_write(world_tracker(), it.entities[i], radius, buffer, &datalen, NULL);
if (result > 0) {
zpl_printf("[info] buffer size was not enough, please increase it by at least: %d\n", result);
} else if (result < 0) {
zpl_printf("[error] an error happened writing the world %d\n", result);
}
pkt_send_librg_update((uint64_t)p[i].peer, p[i].view_id, ticker, buffer, datalen);
}
}
world_snapshot_clear(&streamer_snapshot);
}
}
@ -358,11 +358,11 @@ int32_t world_update() {
world_component_cache_clear(&component_cache);
ecs_progress(world.ecs, 0.0f);
}
float fast_ms = WORLD_TRACKER_UPDATE_MP_FAST_MS;
float normal_ms = WORLD_TRACKER_UPDATE_MP_NORMAL_MS;
float slow_ms = WORLD_TRACKER_UPDATE_MP_SLOW_MS;
#if 1
if (game_get_kind() == GAMEKIND_SINGLE) {
fast_ms = WORLD_TRACKER_UPDATE_FAST_MS;
@ -370,11 +370,11 @@ int32_t world_update() {
slow_ms = WORLD_TRACKER_UPDATE_SLOW_MS;
}
#endif
world_tracker_update(0, fast_ms, 1);
world_tracker_update(1, normal_ms, 2);
world_tracker_update(2, slow_ms, 3);
entity_update_action_timers();
debug_replay_update();
return 0;
@ -470,11 +470,11 @@ world_block_lookup world_block_from_realpos(float x, float y) {
int32_t size = world.chunk_size * WORLD_BLOCK_SIZE;
int16_t chunk_x, chunk_y;
librg_chunk_to_chunkpos(world.tracker, chunk_id, &chunk_x, &chunk_y, NULL);
// NOTE(zaklaus): pos relative to chunk
float chx = x - chunk_x * size;
float chy = y - chunk_y * size;
uint16_t bx = (uint16_t)chx / WORLD_BLOCK_SIZE;
uint16_t by = (uint16_t)chy / WORLD_BLOCK_SIZE;
uint16_t block_idx = (by*world.chunk_size)+bx;
@ -484,15 +484,15 @@ world_block_lookup world_block_from_realpos(float x, float y) {
bid = world.block_mapping[chunk_id][block_idx];
is_outer = false;
}
// NOTE(zaklaus): pos relative to block's center
float box = chx - bx * WORLD_BLOCK_SIZE - WORLD_BLOCK_SIZE/2.0f;
float boy = chy - by * WORLD_BLOCK_SIZE - WORLD_BLOCK_SIZE/2.0f;
// NOTE(zaklaus): absolute pos in world.
float abox = (uint16_t)(x / WORLD_BLOCK_SIZE) * (float)WORLD_BLOCK_SIZE + WORLD_BLOCK_SIZE/2.0f;
float aboy = (uint16_t)(y / WORLD_BLOCK_SIZE) * (float)WORLD_BLOCK_SIZE + WORLD_BLOCK_SIZE/2.0f;
world_block_lookup lookup = {
.id = block_idx,
.bid = bid,
@ -504,7 +504,7 @@ world_block_lookup world_block_from_realpos(float x, float y) {
.aoy = aboy,
.is_outer = is_outer,
};
return lookup;
}
@ -512,12 +512,12 @@ void world_chunk_destroy_block(float x, float y, bool drop_item) {
world_block_lookup l = world_block_from_realpos(x, y);
if (blocks_get_flags(l.bid) & BLOCK_FLAG_ESSENTIAL) return;
world_chunk_replace_block(l.chunk_id, l.id, 0);
if (l.is_outer && l.bid > 0 && drop_item) {
asset_id item_asset = blocks_get_asset(l.bid);
if (item_find(item_asset) == ASSET_INVALID) return;
uint64_t e = item_spawn(item_asset, 1);
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = x;
dest->y = y;
@ -530,20 +530,20 @@ world_block_lookup world_block_from_index(int64_t id, uint16_t block_idx) {
if (bid == 0) {
bid = world.block_mapping[id][block_idx];
}
int32_t size = world.chunk_size * WORLD_BLOCK_SIZE;
int16_t chunk_x, chunk_y;
librg_chunk_to_chunkpos(world.tracker, id, &chunk_x, &chunk_y, NULL);
float chx = (float)chunk_x * size;
float chy = (float)chunk_y * size;
float bx = (float)(block_idx % world.chunk_size) * WORLD_BLOCK_SIZE;
float by = (float)(block_idx / world.chunk_size) * WORLD_BLOCK_SIZE;
float box = chx + bx + WORLD_BLOCK_SIZE/2.0f;
float boy = chy + by + WORLD_BLOCK_SIZE/2.0f;
world_block_lookup lookup = {
.id = block_idx,
.bid = bid,
@ -552,7 +552,7 @@ world_block_lookup world_block_from_index(int64_t id, uint16_t block_idx) {
.oy = boy,
.chunk_e = world.chunk_mapping[id],
};
return lookup;
}
@ -644,18 +644,18 @@ void * world_component_cached(ecs_world_t *world_ecs, ecs_entity_t entity, ecs_i
if (!component_cache.entries || world.ecs_stage == NULL) {
return ecs_get_mut_id(world_ecs, entity, id);
}
static char buffer[256] = {0};
zpl_snprintf(buffer, 256, "%llu_%llu", entity, id);
uint64_t uid = zpl_crc64(buffer, zpl_strlen(buffer));
zpl_uintptr *value = world_component_cache_get(&component_cache, uid);
if (!value) {
void *the_value = ecs_get_mut_id(world_ecs, entity, id);
world_component_cache_set(&component_cache, uid, (zpl_uintptr)the_value);
value = world_component_cache_get(&component_cache, uid);
}
return (void *)*value;
}