Techneck/code/source-c/chunkloader.c

295 lines
9.8 KiB
C

#include "world.h"
#include "entity.h"
#include <stdlib.h>
#include <stdio.h>
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;
list->first = list->first_free;
// 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_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)) return FALSE;
if(coord.y < (loader->center.grid_coords.y - loader->extent.y / 2)) return FALSE;
if(coord.z < (loader->center.grid_coords.z - loader->extent.z / 2)) return FALSE;
if(coord.x > (loader->center.grid_coords.x + loader->extent.x / 2)) return FALSE;
if(coord.y > (loader->center.grid_coords.y + loader->extent.y / 2)) return FALSE;
if(coord.z > (loader->center.grid_coords.z + loader->extent.z / 2)) 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;
--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;
}