#include #include #include #include #include tc_chunklist_s tc_create_chunklist(u32_t capacity) { tc_chunklist_s list; list.capacity = capacity; list.entries = calloc(sizeof(tc_chunklist_entry_s), capacity); list.first_free = list.entries; list.first = NULL; return list; } void tc_delete_chunklist(tc_chunklist_s list) { for(tc_chunklist_entry_s *current = list.first; current != NULL; current = current->next) tc_chunklist_remove_item(&list, list.first->entity); free(list.entries); } tc_chunklist_entry_s * tc_chunklist_find_first_free(tc_chunklist_s *list) { for(u32_t index = 0; index < list->capacity; ++index) if(list->entries[index].entity == NULL) return &list->entries[index]; return NULL; } tc_chunklist_entry_s * tc_chunklist_find_corresponding_entry(tc_chunklist_s *list, tc_entity_s *chunk) { for(u32_t index = 0; index < list->capacity; ++index) { if(list->entries[index].entity == chunk) return &list->entries[index]; } return NULL; } bool_t tc_chunklist_contains(tc_chunklist_s *list, tc_chunk_location_s location) { for(tc_chunklist_entry_s *current = list->first; current != NULL; current = current->next) { if(current->entity == NULL) continue; tc_vec3i_s chunk_coords; chunk_coords.x = tc_get_integer_from_entity(current->entity, "grid_x"); chunk_coords.y = tc_get_integer_from_entity(current->entity, "grid_y"); chunk_coords.z = tc_get_integer_from_entity(current->entity, "grid_z"); if(tc_vec3i_equ(chunk_coords, location.grid_coords)) { return TRUE; } } return FALSE; } tc_entity_s * tc_chunklist_find_chunk_at(tc_chunklist_s *list, tc_chunk_location_s location) { u32_t num_searched = 0; for(tc_chunklist_entry_s *current = list->first; current != NULL; current = current->next) { ++num_searched; if(current->entity == NULL) continue; tc_vec3i_s chunk_coords; chunk_coords.x = tc_get_integer_from_entity(current->entity, "grid_x"); chunk_coords.y = tc_get_integer_from_entity(current->entity, "grid_y"); chunk_coords.z = tc_get_integer_from_entity(current->entity, "grid_z"); if(tc_vec3i_equ(chunk_coords, location.grid_coords)) { return current->entity; } } return NULL; } void tc_chunklist_add_item(tc_chunklist_s *list, tc_entity_s *chunk) { if(list->first_free == NULL) { puts("Chunk List overflow!"); return; } tc_chunklist_entry_s new_entry; new_entry.entity = chunk; new_entry.next = list->first; (*list->first_free) = new_entry; // Link everything together if(list->first_free->next != NULL) list->first_free->next->previous = list->first_free; list->first_free->previous = NULL; // Finally assign it list->first = list->first_free; list->first_free = tc_chunklist_find_first_free(list); } void tc_chunklist_remove_item(tc_chunklist_s *list, tc_entity_s *chunk) { tc_chunklist_entry_s *entry = tc_chunklist_find_corresponding_entry(list, chunk); if(list->first == entry) list->first = entry->next; if(entry->previous != NULL) entry->previous->next = entry->next; if(entry->next != NULL) entry->next->previous = entry->previous; entry->entity = NULL; list->first_free = entry; } tc_chunkloader_s tc_create_chunkloader(tc_chunk_location_s location) { tc_chunkloader_s loader; loader.extent.x = UPDATE_DISTANCE * 2 + 1; loader.extent.y = UPDATE_DISTANCE * 2 + 1; loader.extent.z = UPDATE_DISTANCE * 2 + 1; loader.center = location; loader.chunklist = tc_create_chunklist(512); loader.needs_reload = FALSE; return loader; } void tc_delete_chunkloader(tc_chunkloader_s loader) { for(tc_chunklist_entry_s *current = loader.chunklist.first; current != NULL; current = current->next) { tc_chunk_s *current_chunk = current->entity->specific; tc_unload_chunk_at(&loader, current_chunk->location); } tc_delete_chunklist(loader.chunklist); } bool_t tc_coord_is_within_loaders_range(tc_chunkloader_s *loader, tc_vec3i_s coord) { if(coord.x < ((loader->center.grid_coords.x - loader->extent.x / 2)-1)) return FALSE; if(coord.y < ((loader->center.grid_coords.y - loader->extent.y / 2)-1)) return FALSE; if(coord.z < ((loader->center.grid_coords.z - loader->extent.z / 2)-1)) return FALSE; if(coord.x > ((loader->center.grid_coords.x + loader->extent.x / 2)+1)) return FALSE; if(coord.y > ((loader->center.grid_coords.y + loader->extent.y / 2)+1)) return FALSE; if(coord.z > ((loader->center.grid_coords.z + loader->extent.z / 2)+1)) return FALSE; return TRUE; } tc_chunkloader_s * tc_get_corresponding_loader(tc_chunk_location_s location) { tc_vec3i_s coords = location.grid_coords; tc_world_s *world = location.world; for(uint32_t loader_index = 0; loader_index < world->num_loaders; ++loader_index) if(tc_coord_is_within_loaders_range(&world->loaders[loader_index], coords)) return &world->loaders[loader_index]; return NULL; } void tc_unload_chunk_at(tc_chunkloader_s *loader, tc_chunk_location_s location) { tc_entity_s *entity = tc_chunklist_find_chunk_at(&loader->chunklist, location); if(entity == NULL) return; tc_chunk_s *chunk = entity->specific; tc_world_s *world = location.world; tc_run_hooklist(&world->on_chunk_unload, entity); --chunk->refcounter; if(chunk->refcounter < 1) { tc_delete_entity(entity); tc_chunklist_remove_item(&loader->chunklist, entity); } } tc_entity_s * tc_load_chunk_at(tc_chunk_location_s location) { tc_chunkloader_s *loader = tc_get_corresponding_loader(location); if(loader == NULL) return NULL; tc_entity_s *entity = tc_chunklist_find_chunk_at(&loader->chunklist, location); tc_chunk_s *chunk = entity->specific; ++chunk->refcounter; return entity; } void tc_unload_out_of_range_loader_chunks(tc_chunkloader_s *loader) { tc_chunk_location_s location; location.world = loader->center.world; for(tc_chunklist_entry_s *current = loader->chunklist.first; current != NULL; current = current->next) { tc_entity_s *entity = current->entity; if(entity == NULL) { continue; } tc_chunk_s *chunk = entity->specific; location.grid_coords = chunk->location.grid_coords; location.grid_coords.x = tc_get_integer_from_entity(current->entity, "grid_x"); location.grid_coords.y = tc_get_integer_from_entity(current->entity, "grid_y"); location.grid_coords.z = tc_get_integer_from_entity(current->entity, "grid_z"); if(!tc_coord_is_within_loaders_range(loader, location.grid_coords)) { tc_unload_chunk_at(loader, location); tc_chunklist_remove_item(&loader->chunklist, current->entity); } } } tc_entity_s * tc_create_chunk_for_loader_at(tc_chunkloader_s *loader, tc_vec3i_s grid_coords) { tc_entity_s *entity = tc_create_entity("chunk", loader->center.world); tc_chunk_location_s location; location.world = loader->center.world; location.grid_coords = grid_coords; tc_set_integer_for_entity(entity, "grid_x", location.grid_coords.x); tc_set_integer_for_entity(entity, "grid_y", location.grid_coords.y); tc_set_integer_for_entity(entity, "grid_z", location.grid_coords.z); tc_world_s *world = loader->center.world; tc_send_integer_to_entity(entity, "generate", 0); tc_run_hooklist(&world->after_chunk_generate, entity->specific); tc_send_integer_to_entity(entity, "gl_upload", 0); tc_chunklist_add_item(&loader->chunklist, entity); return entity; } void tc_load_all_chunks_of_chunkloader(tc_chunkloader_s *loader) { for(int32_t x = 0; x < loader->extent.x; ++x) for(int32_t y = 0; y < loader->extent.y; ++y) for(int32_t z = 0; z < loader->extent.z; ++z) { tc_chunk_location_s location; location.grid_coords.x = (loader->center.grid_coords.x + x - (((int32_t) loader->extent.x) / 2)+1); location.grid_coords.y = (loader->center.grid_coords.y + y - (((int32_t) loader->extent.y) / 2)+1); location.grid_coords.z = (loader->center.grid_coords.z + z - (((int32_t) loader->extent.z) / 2)+1); if(!tc_chunklist_contains(&loader->chunklist, location)) { tc_create_chunk_for_loader_at(loader, location.grid_coords); } } } void tc_reload_chunkloader_zone(tc_chunkloader_s *loader) { tc_unload_out_of_range_loader_chunks(loader); tc_load_all_chunks_of_chunkloader(loader); } uint64_t world_update_tick = 0; void tc_update_world(tc_world_s *world) { if((world_update_tick % 256) == 0) { uint32_t chunkloader_index = 0; while(chunkloader_index < world->num_loaders) { tc_reload_chunkloader_zone(&world->loaders[chunkloader_index]); ++chunkloader_index; } } ++world_update_tick; }