Merge remote-tracking branch 'origin/master'

isolation_bkp/dynres
Dominik Madarász 2022-09-15 22:52:00 +02:00
commit 9a1e4bef96
58 changed files with 18534 additions and 18030 deletions

6
.gitignore vendored
View File

@ -1,5 +1,10 @@
build
build_rel
build_web
emsdk
deploy_web
run_web
butler
screenshots
build.bat
run.bat
@ -12,7 +17,6 @@ GPATH
GRTAGS
GTAGS
/run_release.bat
/package.bat
pkg
pkg.zip
eco2d.zip

View File

@ -8,11 +8,17 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR})
SET(CMAKE_USE_RELATIVE_PATHS OFF)
if(MSVC)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
endif()
if (EMSCRIPTEN)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s USE_GLFW=3 --profiling -s ASSERTIONS=1 -s WASM=1 -s INITIAL_MEMORY=268435456 -s FORCE_FILESYSTEM=1 --preload-file ${CMAKE_SOURCE_DIR}/art@art/ --shell-file ${CMAKE_SOURCE_DIR}/web/eco2d.html --post-js ${CMAKE_SOURCE_DIR}/web/eco2d-post.js")
set(CMAKE_EXECUTABLE_SUFFIX ".html")
endif ()
include_directories(code/common code/vendors code/vendors/flecs)
include(cmake/FindRaylib.cmake)

View File

@ -1,11 +1,12 @@
<div align="center">
<a href="https://github.com/zpl-c/zpl"><img src="https://user-images.githubusercontent.com/2182108/111983468-d5593e80-8b12-11eb-9c59-8c78ecc0504e.png" alt="eco2d" /></a>
<a href="https://zaklaus.itch.io/eco2d"><img src="https://user-images.githubusercontent.com/2182108/111983468-d5593e80-8b12-11eb-9c59-8c78ecc0504e.png" alt="eco2d" /></a>
</div>
<br />
<div align="center">
<a href="https://discord.gg/2fZVEym"><img src="https://img.shields.io/discord/354670964400848898?color=7289DA&style=for-the-badge" alt="discord" /></a>
<a href="https://zaklaus.itch.io/eco2d"><img src="https://static.itch.io/images/badge-color.svg" alt="play" height="28px"/></a>
</div>
<br />
@ -51,6 +52,30 @@ In the abstract sense, we call the Server the game master hosting all gameplay r
# Build the project
We use CMake to generate project files and manage builds.
## Web
We have a set of scripts ready for web development, these steps will get you up and running with a web build:
```sh
# Setup emsdk locally and configure a web project
web/setup.sh
# Build the web project
web/build.sh
# Host the files on a web server (Python3)
web/host.sh
```
## Desktop
### Pre-requisites
#### Linux
Follow [raylib-linux](https://github.com/raysan5/raylib/wiki/Working-on-GNU-Linux) guide to install dependencies on your system.
#### macOS
Follow [raylib-macos](https://github.com/raysan5/raylib/wiki/Working-on-macOS) guide to install dependencies on your system.
#### Windows
You need to have Visual Studio 2019+ installed on your system. Make sure to run the commands below in a VS Developer Command Prompt.
### Build
You can do the following on the command line to create and build this project:
```sh
git clone https://github.com/zpl-c/eco2d.git
@ -61,10 +86,6 @@ cmake --build build
Run the following command to see all the options:
```sh
Windows:
build\Debug\eco2d.exe -?
Linux:
build\eco2d.exe -?
```

View File

@ -3,6 +3,8 @@ function(link_system_libs target_name)
target_link_libraries(${target_name} winmm)
elseif (APPLE)
target_link_libraries(${target_name} pthread m dl)
elseif (EMSCRIPTEN)
target_link_libraries(${target_name} pthread m dl)
elseif (UNIX)
target_link_libraries(${target_name} pthread m dl atomic)
endif()

View File

@ -43,5 +43,6 @@ add_executable(eco2d
target_compile_definitions(eco2d PRIVATE CLIENT)
include_directories(src ../modules ../../art/gen)
target_link_libraries(eco2d raylib cwpack eco2d-modules flecs-bundle vendors-bundle)
target_compile_options(eco2d PRIVATE -Werror -Wall -Wextra -Wno-unused-function -Wno-unknown-pragmas -Wno-unused-variable -Wno-unused-parameter)
link_system_libs(eco2d)

View File

@ -16,7 +16,7 @@
// can use it in other systems
// NOTE(zaklaus): Register a block
#include "blocks/blocks_list.c"
#include "world/blocks_list.c"
// NOTE(zaklaus): Register an item
#include "items_list.c"

View File

@ -23,6 +23,8 @@ typedef struct {
static int64_t assets_frame_counter = 1;
static double assets_frame_next_draw = 0.0;
#include <time.h>
int32_t assets_setup(void) {
for (uint32_t i=0; i<ASSETS_COUNT; i++) {
asset *b = &assets[i];
@ -43,13 +45,12 @@ int32_t assets_setup(void) {
default: break;
}
}
assets_frame_next_draw = zpl_time_rel() + ASSET_FRAME_RENDER_MS;
assets_frame_next_draw = get_cached_time() + ASSET_FRAME_RENDER_MS;
return 0;
}
int32_t assets_frame(void) {
if (assets_frame_next_draw < zpl_time_rel()) {
if (assets_frame_next_draw < get_cached_time()) {
for (uint32_t i=0; i<ASSETS_COUNT; i++) {
asset *b = &assets[i];
@ -63,7 +64,7 @@ int32_t assets_frame(void) {
}
}
assets_frame_next_draw = zpl_time_rel() + ASSET_FRAME_RENDER_MS;
assets_frame_next_draw = get_cached_time() + ASSET_FRAME_RENDER_MS;
assets_frame_counter += ASSET_FRAME_SKIP;
}

View File

@ -22,7 +22,7 @@ static asset assets[] = {
ASSET_TEX(ASSET_DEV),
ASSET_TEX(ASSET_GROUND),
ASSET_TEX(ASSET_DIRT),
ASSET_ANI(ASSET_WATER),
ASSET_TEX(ASSET_WATER),
ASSET_TEX(ASSET_LAVA),
ASSET_TEX(ASSET_WALL),
ASSET_TEX(ASSET_HILL),

View File

@ -116,7 +116,7 @@ void debug_replay_start(void) {
if (records) zpl_array_free(records);
zpl_array_init_reserve(records, zpl_heap(), UINT16_MAX);
last_record_time = zpl_time_rel();
last_record_time = get_cached_time();
SetTargetFPS(60);
}
@ -155,7 +155,7 @@ void debug_replay_run(void) {
if (mime) return;
is_playing = true;
record_pos = 0;
playback_time = zpl_time_rel();
playback_time = get_cached_time();
zpl_array_init(temp_actors, zpl_heap());
plr = camera_get().ent_id;
@ -181,10 +181,10 @@ void ActSpawnBelt(void);
void debug_replay_update(void) {
if (!is_playing) return;
if (playback_time >= zpl_time_rel()) return;
if (playback_time >= get_cached_time()) return;
replay_record *r = &records[record_pos];
playback_time = zpl_time_rel() + r->delay;
playback_time = get_cached_time() + r->delay;
switch (r->kind) {
case RPKIND_KEY: {
@ -249,7 +249,7 @@ void debug_replay_update(void) {
void debug_replay_record_keystate(pkt_send_keystate state) {
if (!is_recording) return;
double record_time = zpl_time_rel();
double record_time = get_cached_time();
replay_record rec = {
.kind = RPKIND_KEY,
@ -258,13 +258,13 @@ void debug_replay_record_keystate(pkt_send_keystate state) {
};
zpl_array_append(records, rec);
last_record_time = zpl_time_rel();
last_record_time = get_cached_time();
}
void debug_replay_special_action(replay_kind kind) {
ZPL_ASSERT(kind != RPKIND_KEY);
if (!is_recording || is_playing) return;
double record_time = zpl_time_rel();
double record_time = get_cached_time();
replay_record rec = {
.kind = kind,
@ -272,5 +272,5 @@ void debug_replay_special_action(replay_kind kind) {
};
zpl_array_append(records, rec);
last_record_time = zpl_time_rel();
last_record_time = get_cached_time();
}

View File

@ -29,7 +29,7 @@ typedef struct {
#define DBG_FONT_SIZE 22
#define DBG_FONT_SPACING DBG_FONT_SIZE * 1.2f
#define DBG_START_XPOS 15
#define DBG_START_YPOS 200
#define DBG_START_YPOS 30
#define DBG_LIST_XPOS_OFFSET 10
#define DBG_SHADOW_OFFSET_XPOS 1
#define DBG_SHADOW_OFFSET_YPOS 1
@ -92,6 +92,7 @@ static debug_item items[] = {
.kind = DITEM_LIST,
.name = "general",
.list = {
.is_collapsed = true,
.items = (debug_item[]) {
{ .kind = DITEM_TEXT, .name = "delta time", .proc = DrawDeltaTime },
{ .kind = DITEM_TEXT, .name = "pos", .proc = DrawCameraPos },
@ -104,6 +105,7 @@ static debug_item items[] = {
.kind = DITEM_LIST,
.name = "world simulation",
.list = {
.is_collapsed = true,
.items = (debug_item[]) {
{ .kind = DITEM_COND, .on_success = CondIsWorldRunning },
{ .kind = DITEM_BUTTON, .name = "pause", .on_click = ActWorldToggleSim },
@ -127,6 +129,7 @@ static debug_item items[] = {
.kind = DITEM_LIST,
.name = "debug actions",
.list = {
.is_collapsed = true,
.items = (debug_item[]) {
{ .kind = DITEM_BUTTON, .name = "spawn car", .on_click = ActSpawnCar },
{ .kind = DITEM_BUTTON, .name = "place ice rink", .on_click = ActPlaceIceRink },
@ -157,6 +160,7 @@ static debug_item items[] = {
.kind = DITEM_LIST,
.name = "conn metrics",
.list = {
.is_collapsed = true,
.items = (debug_item[]) {
{ .kind = DITEM_COND, .on_success = CondClientDisconnected },
{ .kind = DITEM_TEXT, .name = "status", .proc = DrawLiteral, .text = "disconnected" },
@ -172,6 +176,7 @@ static debug_item items[] = {
},
.limit_to = L_MP,
},
#if !defined(PLATFORM_WEB)
{
.kind = DITEM_LIST,
.name = "replay system",
@ -209,6 +214,7 @@ static debug_item items[] = {
},
.limit_to = L_SP,
},
#endif
{
.kind = DITEM_LIST,
.name = "profilers",
@ -226,11 +232,13 @@ static debug_item items[] = {
.is_collapsed = 1
}
},
#if !defined(PLATFORM_WEB)
{
.kind = DITEM_BUTTON,
.name = "exit game",
.on_click = ActExitGame,
},
#endif
{.kind = DITEM_END},
};

View File

@ -16,6 +16,7 @@ ActSpawnCar(void) {
Position const* origin = ecs_get(world_ecs(), plr, Position);
Position * dest = ecs_get_mut(world_ecs(), e, Position);
*dest = *origin;
entity_set_position(e, dest->x, dest->y);
debug_replay_special_action(RPKIND_SPAWN_CAR);
}
@ -28,6 +29,7 @@ ActSpawnIcemaker(void) {
Position const* origin = ecs_get(world_ecs(), plr, Position);
Position * dest = ecs_get_mut(world_ecs(), e, Position);
*dest = *origin;
entity_set_position(e, dest->x, dest->y);
debug_replay_special_action(RPKIND_SPAWN_ICEMAKER_ITEM);
}
@ -40,6 +42,7 @@ ActSpawnChest(void) {
Position const* origin = ecs_get(world_ecs(), plr, Position);
Position * dest = ecs_get_mut(world_ecs(), e, Position);
*dest = *origin;
entity_set_position(e, dest->x, dest->y);
debug_replay_special_action(RPKIND_SPAWN_CHEST);
}
@ -52,6 +55,7 @@ ActSpawnBelt(void) {
Position const* origin = ecs_get(world_ecs(), plr, Position);
Position * dest = ecs_get_mut(world_ecs(), e, Position);
*dest = *origin;
entity_set_position(e, dest->x, dest->y);
debug_replay_special_action(RPKIND_SPAWN_BELT);
}
@ -67,6 +71,8 @@ ActSpawnCirclingDriver(void) {
Position *dest = ecs_get_mut(world_ecs(), e, Position);
*veh_dest = *origin;
*dest = *origin;
entity_set_position(ve, veh_dest->x, veh_dest->y);
entity_set_position(e, dest->x, dest->y);
Input *input = ecs_get_mut(world_ecs(), e, Input);
zpl_zero_item(input);

View File

@ -23,6 +23,7 @@ uint64_t entity_spawn(uint16_t class_id) {
#if 1
pos->x=(float)(rand() % world_dim());
pos->y=(float)(rand() % world_dim());
entity_set_position(e, pos->x, pos->y);
#else
pos->x=350.0f;
pos->y=88.0f;
@ -62,7 +63,7 @@ void entity_set_position(uint64_t ent_id, float x, float y) {
Position *p = ecs_get_mut(world_ecs(), ent_id, Position);
p->x = x;
p->y = y;
librg_entity_chunk_set(world_tracker(), ent_id, librg_chunk_from_realpos(world_tracker(), x, y, 0));
entity_wake(ent_id);
}
@ -78,7 +79,7 @@ void entity_update_action_timers() {
static double last_update_time = 0.0f;
if (!ecs_streaminfo) {
ecs_streaminfo = ecs_query_new(world_ecs(), "components.StreamInfo");
last_update_time = zpl_time_rel();
last_update_time = get_cached_time();
}
ecs_iter_t it = ecs_query_iter(world_ecs(), ecs_streaminfo);
@ -86,18 +87,18 @@ void entity_update_action_timers() {
while (ecs_query_next(&it)) {
StreamInfo *si = ecs_field(&it, StreamInfo, 1);
for (size_t i = 0; i < it.count; i++) {
if (si[i].last_update < zpl_time_rel()) {
si[i].last_update = zpl_time_rel() + si[i].tick_delay;
si[i].tick_delay += (zpl_time_rel() - last_update_time) * 0.5f;
for (int32_t i = 0; i < it.count; i++) {
if (si[i].last_update < get_cached_time()) {
si[i].last_update = get_cached_time() + si[i].tick_delay;
si[i].tick_delay += (get_cached_time() - last_update_time) * 0.5f;
}
}
}
last_update_time = zpl_time_rel();
last_update_time = get_cached_time();
}
bool entity_can_stream(uint64_t ent_id) {
StreamInfo *si = ecs_get_mut(world_ecs(), ent_id, StreamInfo);
return (si->last_update < zpl_time_rel());
return (si->last_update < get_cached_time());
}

View File

@ -50,10 +50,10 @@ pkt_desc pkt_entity_view_desc[] = {
{ PKT_END },
};
size_t entity_view_pack_struct(void *data, size_t len, entity_view view) {
size_t entity_view_pack_struct(void *data, size_t len, entity_view *view) {
cw_pack_context pc = {0};
cw_pack_context_init(&pc, data, (unsigned long)len, 0);
pkt_pack_struct(&pc, pkt_entity_view_desc, PKT_STRUCT_PTR(&view));
pkt_pack_struct(&pc, pkt_entity_view_desc, PKT_STRUCT_PTR(view));
return pc.current - pc.start;
}

View File

@ -96,7 +96,7 @@ void entity_view_destroy(entity_view_tbl *map, uint64_t ent_id);
entity_view *entity_view_get(entity_view_tbl *map, uint64_t ent_id);
void entity_view_map(entity_view_tbl *map, void (*map_proc)(uint64_t key, entity_view *value));
size_t entity_view_pack_struct(void *data, size_t len, entity_view view);
size_t entity_view_pack_struct(void *data, size_t len, entity_view *view);
entity_view entity_view_unpack_struct(void *data, size_t len);
void entity_view_mark_for_removal(entity_view_tbl *map, uint64_t ent_id);

View File

@ -12,6 +12,7 @@
#include "profiler.h"
#include "flecs/flecs_os_api_stdcpp.h"
#include "flecs/flecs.h"
#include "modules/components.h"
#include "modules/systems.h"
@ -72,7 +73,7 @@ void world_viewers_init(uint32_t num_viewers) {
}
void world_viewers_destroy() {
for (uint32_t i = 0; i < zpl_buffer_count(world_viewers); i++) {
for (zpl_isize i = 0; i < zpl_buffer_count(world_viewers); i++) {
world_view_destroy(&world_viewers[i]);
}
zpl_buffer_free(world_viewers);
@ -114,12 +115,14 @@ size_t game_world_view_count(void) {
}
void flecs_dash_init() {
#if !defined(ZPL_SYSTEM_EMSCRIPTEN)
ecs_singleton_set(world_ecs(), EcsRest, {0});
ECS_IMPORT(world_ecs(), FlecsMonitor);
#endif
}
float game_time() {
return (float)zpl_time_rel();
return (float)get_cached_time();
}
void game_init(const char *ip, uint16_t port, game_kind play_mode, uint32_t num_viewers, int32_t seed, uint16_t chunk_size, uint16_t chunk_amount, int8_t is_dash_enabled) {
@ -229,15 +232,15 @@ void game_update() {
if (game_mode == GAMEKIND_HEADLESS) {
network_server_tick();
static uint64_t ms_report = 2500;
if (ms_report < zpl_time_rel_ms()) {
ms_report = zpl_time_rel_ms() + 5000;
zpl_printf("delta: %f ms.\n", (zpl_time_rel() - last_update)*1000.0f);
static float ms_report = 2.5f;
if (ms_report < get_cached_time()) {
ms_report = get_cached_time() + 5.f;
zpl_printf("delta: %f ms.\n", (get_cached_time() - last_update)*1000.0f);
}
}
}
last_update = zpl_time_rel();
last_update = get_cached_time();
}
void game_render() {

View File

@ -33,11 +33,11 @@ Texture2D GenColorEco(Color color) {
Texture2D texgen_build_anim(asset_id id, int64_t counter) {
(void)counter;
switch (id) {
case ASSET_WATER: {
Image img = LoadImageEco("water");
ImageColorBrightness(&img, zpl_abs((counter % 64 - 32)*2));
return Image2TexEco(img);
}break;
// case ASSET_WATER: {
// Image img = LoadImageEco("water");
// ImageColorBrightness(&img, zpl_abs((counter % 64 - 32)*2));
// return Image2TexEco(img);
// }break;
default: return GenColorEco(PINK); break;
}
@ -61,6 +61,7 @@ Texture2D texgen_build_sprite(asset_id id) {
case ASSET_LAVA: return LoadTexEco("lava");
case ASSET_WOOD: return LoadTexEco("wood");
case ASSET_TREE: return LoadTexEco("tree");
case ASSET_WATER: return LoadTexEco("water");
case ASSET_BELT:
case ASSET_BELT_RIGHT: return LoadTexEco("belt_right");

View File

@ -45,7 +45,7 @@ void buildmode_draw(void) {
ItemDrop *item = &e->items[e->selected_item];
if (e->has_items && !e->inside_vehicle && item->quantity > 0 && !is_outside_range || build_is_deletion_mode) {
if (e->has_items && !e->inside_vehicle && item->quantity > 0 && (!is_outside_range || build_is_deletion_mode)) {
item_usage usage = 0;
uint16_t item_id = 0;
if (!build_is_deletion_mode){

View File

@ -86,12 +86,16 @@ void item_use(ecs_world_t *ecs, ItemDrop *it, Position p, uint64_t udata) {
ecs_entity_t e = entity_spawn_id(desc->place_item.id);
ZPL_ASSERT(world_entity_valid(e));
Position *pos = ecs_get_mut(ecs, e, Position);
pos->x = p.x;
pos->y = p.y;
entity_set_position(e, p.x, p.y);
it->quantity--;
}break;
case UKIND_DELETE:
case UKIND_END_PLACE:
case UKIND_PROXY:
break;
}
}

View File

@ -14,6 +14,11 @@
#include "modules/components.h"
#include "modules/systems.h"
#if defined(PLATFORM_WEB)
#include <emscripten/emscripten.h>
void UpdateDrawFrame(void);
#endif
#define DEFAULT_WORLD_SEED 302097
#define DEFAULT_CHUNK_SIZE 16 /* amount of blocks within a chunk (single axis) */
#define DEFAULT_WORLD_SIZE 32 /* amount of chunks within a world (single axis) */
@ -73,7 +78,9 @@ int main(int argc, char** argv) {
sighandler_register();
game_init(host, port, play_mode, num_viewers, seed, chunk_size, world_size, is_dash_enabled);
#if !defined(PLATFORM_WEB)
while (game_is_running()) {
reset_cached_time();
profile (PROF_MAIN_LOOP) {
game_input();
game_update();
@ -82,6 +89,9 @@ int main(int argc, char** argv) {
profiler_collate();
}
#else
emscripten_set_main_loop(UpdateDrawFrame, 0, 1);
#endif
game_shutdown();
sighandler_unregister();
@ -90,3 +100,25 @@ int main(int argc, char** argv) {
zpl_opts_free(&opts);
return 0;
}
#if defined(PLATFORM_WEB)
void UpdateDrawFrame(void) {
reset_cached_time();
profile (PROF_MAIN_LOOP) {
game_input();
game_update();
game_render();
}
profiler_collate();
}
#endif
static float temp_time = 0.0f;
float get_cached_time(void) {
return temp_time;
}
void reset_cached_time(void) {
temp_time = zpl_time_rel();
}

View File

@ -120,7 +120,7 @@ network_client_fetch_stats(void) {
static float incoming_bandwidth = 0.0f;
static float outgoing_bandwidth = 0.0f;
if (next_measure < zpl_time_rel()) {
if (next_measure < get_cached_time()) {
#define MAX_RATE_SAMPLES 8
static uint64_t last_total_sent = 0;
static uint64_t last_total_recv = 0;
@ -145,7 +145,7 @@ network_client_fetch_stats(void) {
incoming_bandwidth = stats.incoming_bandwidth /= MAX_RATE_SAMPLES;
outgoing_bandwidth = stats.outgoing_bandwidth /= MAX_RATE_SAMPLES;
next_measure = zpl_time_rel() + 1.0;
next_measure = get_cached_time() + 1.0;
} else {
stats.incoming_bandwidth = incoming_bandwidth;
stats.outgoing_bandwidth = outgoing_bandwidth;

View File

@ -105,6 +105,7 @@ int32_t pkt_unpack_struct(cw_unpack_context *uc, pkt_desc *desc, void *raw_blob,
}
int32_t pkt_pack_struct(cw_pack_context *pc, pkt_desc *desc, void *raw_blob, uint32_t blob_size) {
(void)blob_size;
uint8_t *blob = (uint8_t*)raw_blob;
for (pkt_desc *field = desc; field->type != CWP_NOT_AN_ITEM; ++field) {
if (field->skip_count != 0) {
@ -158,6 +159,7 @@ int32_t pkt_pack_struct(cw_pack_context *pc, pkt_desc *desc, void *raw_blob, uin
}
void pkt_dump_struct(pkt_desc *desc, void* raw_blob, uint32_t blob_size) {
(void)blob_size;
uint8_t *blob = (uint8_t*)raw_blob;
zpl_printf("{\n");
for (pkt_desc *field = desc; field->type != CWP_NOT_AN_ITEM; ++field) {

View File

@ -7,6 +7,7 @@
#include "entity_view.h"
#include "camera.h"
#include "player.h"
#include "entity.h"
#include "modules/components.h"
#include "modules/systems.h"
@ -30,9 +31,8 @@ int32_t pkt_00_init_handler(pkt_header *header) {
Position *pos = ecs_get_mut(world_ecs(), ent_id, Position);
#if 0
pos->x = world_dim()/2.0f + rand()%15*15.0f;
pos->y = world_dim()/2.0f + rand()%15*15.0f;
#if 1
entity_set_position(ent_id, world_dim()/2.0f + rand()%15*15.0f, world_dim()/2.0f + rand()%15*15.0f);
#else
pos->x = rand()%world_dim();
pos->y = rand()%world_dim();

View File

@ -29,7 +29,7 @@ int32_t pkt_01_welcome_handler(pkt_header *header) {
world_view *view = game_world_view_get(header->view_id);
zpl_printf("[INFO] initializing read-only world view id: %d...\n", header->view_id);
zpl_printf("[INFO] initializing read-only world view id: %d... (chunk_size: %d, world_size: %d)\n", header->view_id, table.chunk_size, table.world_size);
world_view_init(view, table.seed, table.ent_id, table.chunk_size, table.world_size);
game_world_view_set_active(view);
return 0;

View File

@ -20,6 +20,27 @@ size_t pkt_send_librg_update_encode(void *data, int32_t data_length, uint8_t lay
return pkt_pack_msg_size(&pc);
}
#define NUM_SAMPLES 128
static float smooth_time(float time) {
static float time_samples[NUM_SAMPLES] = {};
static int32_t curr_index = 0;
time_samples[curr_index] = time;
if (++curr_index == NUM_SAMPLES)
curr_index = 0;
float average = 0;
for (int32_t i = NUM_SAMPLES; i--; )
average += time_samples[i];
average /= NUM_SAMPLES;
time = zpl_min(time, average * 2);
return time;
}
#undef NUM_SAMPLES
int32_t pkt_send_librg_update_handler(pkt_header *header) {
cw_unpack_context uc = {0};
pkt_unpack_msg(&uc, header, 2);
@ -41,8 +62,8 @@ int32_t pkt_send_librg_update_handler(pkt_header *header) {
int32_t state = librg_world_read(view->tracker, header->view_id, uc.item.as.bin.start, uc.item.as.bin.length, NULL);
if (state < 0) zpl_printf("[ERROR] world read error: %d\n", state);
float now = (float)zpl_time_rel();
view->delta_time[layer_id] = now - view->last_update[layer_id];
float now = (float)get_cached_time();
view->delta_time[layer_id] = smooth_time(now - view->last_update[layer_id]);
view->last_update[layer_id] = now;
return state;

View File

@ -13,9 +13,20 @@
#include "debug_ui.h"
#include "utils/raylib_helpers.h"
static uint16_t screenWidth = 1600;
static uint16_t screenHeight = 900;
static float target_zoom = 1.5f;
#if defined(PLATFORM_WEB)
#include <emscripten.h>
EM_JS(int, canvas_get_width, (), {
return canvas.width;
});
EM_JS(int, canvas_get_height, (), {
return canvas.height;
});
#endif
static uint16_t screenWidth = 1024;
static uint16_t screenHeight = 768;
static float target_zoom = 0.6f;
static bool request_shutdown;
#define GFX_KIND 2
@ -27,11 +38,19 @@ static bool request_shutdown;
void platform_init() {
SetTraceLogLevel(LOG_ERROR);
#if defined(PLATFORM_WEB)
screenWidth = (uint16_t)canvas_get_width();
screenHeight = (uint16_t)canvas_get_height();
#endif
InitWindow(screenWidth, screenHeight, "eco2d");
SetWindowState(/*FLAG_WINDOW_UNDECORATED|*/FLAG_WINDOW_MAXIMIZED|FLAG_WINDOW_RESIZABLE|FLAG_MSAA_4X_HINT);
#if !defined(PLATFORM_WEB)
screenWidth = (uint16_t)GetScreenWidth();
screenHeight = (uint16_t)GetScreenHeight();
#endif
// ToggleFullscreen();
// SetTargetFPS(60.0);
@ -119,7 +138,7 @@ void platform_input() {
float mouse_z = (GetMouseWheelMove()*0.5f);
if (mouse_z != 0.0f) {
target_zoom = zpl_clamp(target_zoom+mouse_z, 0.1f, 10.0f);
target_zoom = zpl_clamp(target_zoom+mouse_z, 0.1f, 11.0f);
}
// NOTE(zaklaus): keystate handling
@ -233,8 +252,18 @@ void draw_selected_item() {
}
void platform_render() {
#if !defined(PLATFORM_WEB)
screenWidth = (uint16_t)GetScreenWidth();
screenHeight = (uint16_t)GetScreenHeight();
#else
uint16_t newScreenWidth = (uint16_t)canvas_get_width();
uint16_t newScreenHeight = (uint16_t)canvas_get_height();
if (newScreenWidth != screenWidth || newScreenHeight != screenHeight) {
screenWidth = newScreenWidth;
screenHeight = newScreenHeight;
SetWindowSize(screenWidth, screenHeight);
}
#endif
profile(PROF_ENTITY_LERP) {
game_world_view_active_entity_map(lerp_entity_positions);

View File

@ -62,7 +62,7 @@ void predict_receive_update(entity_view *d, entity_view *data) {
data->tran_time = d->tran_time;
}
#define ENTITY_DO_LERP_SP 0
#define ENTITY_DO_LERP_SP 1
void lerp_entity_positions(uint64_t key, entity_view *data) {
(void)key;

View File

@ -4,6 +4,8 @@
#define PROF_COLLATE_WINDOW 0.5
static float profiler_warmup = 3.0f;
// NOTE(zaklaus): KEEP ORDER IN SYNC WITH profiler_kind ENUM !!!
static profiler profilers[] = {
{ .id = PROF_TOTAL_TIME, .name = "measured time" },
@ -33,6 +35,13 @@ void profiler_stop(profiler_kind id) {
}
void profiler_collate() {
if (profiler_warmup > 0) {
profiler_warmup -= GetFrameTime();
for (uint32_t i = PROF_MAIN_LOOP; i < MAX_PROF; i += 1) {
profiler_reset(i);
}
return;
}
static double frame_counter = 0.0;
static uint64_t frames = 0;

View File

@ -1,13 +1,10 @@
static Camera3D render_camera_3d;
static float cam_zoom = 1.5f;
#define CAM_OVERLAY_ZOOM_LEVEL 0.80f
float zpl_lerp(float,float,float);
float zpl_to_degrees(float);
void DEBUG_draw_ground_3d(uint64_t key, entity_view * data) {
(void)key;
switch (data->kind) {
case EKIND_CHUNK: {
world_view *view = game_world_view_get_active();
@ -145,7 +142,7 @@ void renderer_debug_draw_3d(void) {
}
float renderer_zoom_get_3d(void) {
return 1.0f;
return cam_zoom;
}
void renderer_draw_single_3d(float x, float y, asset_id id, Color color) {

View File

@ -1,7 +1,7 @@
static Camera2D render_camera;
static float zoom_overlay_tran = 0.0f;
#define CAM_OVERLAY_ZOOM_LEVEL 0.80f
#define CAM_OVERLAY_ZOOM_LEVEL 0.15f
#define ALPHA(x) ColorAlpha(x, data->tran_time)
float zpl_lerp(float,float,float);
@ -23,13 +23,6 @@ void DEBUG_draw_ground(uint64_t key, entity_view * data) {
tex.texture.height *= (int32_t)scale;
DrawTextureRec(tex.texture, (Rectangle){0, 0, size, -size}, (Vector2){x, y}, ColorAlpha(WHITE, data->tran_time));
if (zoom_overlay_tran > 0.02f) {
DrawRectangleEco(x, y, size-offset, size-offset, ColorAlpha(ColorFromHSV((float)data->color, 0.13f, 0.89f), data->tran_time*zoom_overlay_tran*0.75f));
DrawTextEco(TextFormat("%d %d", (int)data->x, (int)data->y), x+15.0f, y+15.0f, 200 , ColorAlpha(BLACK, data->tran_time*zoom_overlay_tran), 0.0);
}
for (size_t ty = 0; ty < view->chunk_size; ty++) {
for (size_t tx = 0; tx < view->chunk_size; tx++) {
block_id blk_id = data->outer_blocks[(ty*view->chunk_size)+tx];
@ -44,14 +37,32 @@ void DEBUG_draw_ground(uint64_t key, entity_view * data) {
}
}
void DEBUG_draw_overlay(uint64_t key, entity_view * data) {
switch (data->kind) {
case EKIND_CHUNK: {
world_view *view = game_world_view_get_active();
float size = (float)(view->chunk_size * WORLD_BLOCK_SIZE);
float offset = 0.0;
float x = data->x * size + offset;
float y = data->y * size + offset;
DrawRectangleEco(x, y, size-offset, size-offset, ColorAlpha(ColorFromHSV((float)data->color, 0.13f, 0.89f), data->tran_time*zoom_overlay_tran*0.75f));
DrawTextEco(TextFormat("%d %d", (int)data->x, (int)data->y), x+15.0f, y+15.0f, 200 , ColorAlpha(BLACK, data->tran_time*zoom_overlay_tran), 0.0);
}break;
default:break;
}
}
extern bool inv_is_open;
void DEBUG_draw_entities(uint64_t key, entity_view * data) {
uint16_t size = 16;
uint16_t font_size = (uint16_t)lerp(4.0f, 32.0f, 0.5f/(float)render_camera.zoom);
float size = 16.f;
float font_size = lerp(4.0f, 32.0f, 0.5f/(float)render_camera.zoom);
float font_spacing = 1.1f;
float title_bg_offset = 4;
float fixed_title_offset = 8;
float fixed_title_offset = 8.f;
switch (data->kind) {
case EKIND_DEMO_NPC: {
@ -64,10 +75,10 @@ void DEBUG_draw_entities(uint64_t key, entity_view * data) {
float y = data->y;
float health = (data->hp / data->max_hp);
const char *title = TextFormat("Player %d", key);
int title_w = MeasureTextEco(title, font_size, font_spacing);
DrawRectangleEco(x-title_w/2-title_bg_offset/2, y-size-font_size-fixed_title_offset, title_w+title_bg_offset, font_size, ColorAlpha(BLACK, data->tran_time));
DrawRectangleEco(x-title_w/2-title_bg_offset/2, y-size-fixed_title_offset, title_w*health+title_bg_offset, font_size*0.2f, ColorAlpha(RED, data->tran_time));
DrawTextEco(title, x-title_w/2, y-size-font_size-fixed_title_offset, font_size, ColorAlpha(RAYWHITE, data->tran_time), font_spacing);
float title_w = MeasureTextEco(title, font_size, font_spacing);
DrawRectangleEco(x-title_w/2.f-title_bg_offset/2.f, y-size-font_size-fixed_title_offset, title_w+title_bg_offset, font_size, ColorAlpha(BLACK, data->tran_time));
DrawRectangleEco(x-title_w/2.f-title_bg_offset/2.f, y-size-fixed_title_offset, title_w*health+title_bg_offset, font_size*0.2f, ColorAlpha(RED, data->tran_time));
DrawTextEco(title, x-title_w/2.f, y-size-font_size-fixed_title_offset, font_size, ColorAlpha(RAYWHITE, data->tran_time), font_spacing);
DrawCircleEco(x, y, size, ColorAlpha(YELLOW, data->tran_time));
if (data->has_items && !data->inside_vehicle) {
@ -79,7 +90,7 @@ void DEBUG_draw_entities(uint64_t key, entity_view * data) {
DrawTexturePro(GetSpriteTexture2D(assets_find(it_kind)), ASSET_SRC_RECT(), ((Rectangle){ix, iy, 32, 32}), (Vector2){0.5f,0.5f}, 0.0f, ALPHA(WHITE));
if (!inv_is_open)
DrawTextEco(zpl_bprintf("%d", qty), ix+24, iy+24, 8, RAYWHITE, 0.0f);
DrawTextEco(zpl_bprintf("%d", qty), x+24, y+24, 8, RAYWHITE, 0.0f);
}
}
}break;
@ -87,9 +98,9 @@ void DEBUG_draw_entities(uint64_t key, entity_view * data) {
float x = data->x;
float y = data->y;
const char *title = TextFormat("Bot %d", key);
int title_w = MeasureTextEco(title, font_size, font_spacing);
DrawRectangleEco(x-title_w/2-title_bg_offset/2, y-size-font_size-fixed_title_offset, title_w+title_bg_offset, font_size, ColorAlpha(GRAY, data->tran_time));
DrawTextEco(title, x-title_w/2, y-size-font_size-fixed_title_offset, font_size, ColorAlpha(BLACK, data->tran_time), font_spacing);
float title_w = MeasureTextEco(title, font_size, font_spacing);
DrawRectangleEco(x-title_w/2.f-title_bg_offset/2.f, y-size-font_size-fixed_title_offset, title_w+title_bg_offset, font_size, ColorAlpha(GRAY, data->tran_time));
DrawTextEco(title, x-title_w/2.f, y-size-font_size-fixed_title_offset, font_size, ColorAlpha(BLACK, data->tran_time), font_spacing);
DrawCircleEco(x, y, size, ColorAlpha(PURPLE, data->tran_time));
}break;
case EKIND_ITEM: {
@ -137,6 +148,10 @@ void renderer_draw_v0(void) {
game_world_view_active_entity_map(DEBUG_draw_ground);
game_world_view_active_entity_map(DEBUG_draw_entities_low);
game_world_view_active_entity_map(DEBUG_draw_entities);
if (zoom_overlay_tran > 0.02f) {
game_world_view_active_entity_map(DEBUG_draw_overlay);
}
EndMode2D();
}

View File

@ -25,6 +25,7 @@ static BOOL WINAPI _sighandler_win32_control_handler(DWORD control_type)
#else //POSIX complaint
#include <sys/types.h>
static void _sighandler_posix_signal_handler(int sig) {
(void)sig;
game_request_close();
}
#endif

View File

@ -10,6 +10,9 @@
#define ZPL_ENABLE_MATH
#include "zpl.h"
float get_cached_time(void);
void reset_cached_time(void);
#define defer_var ZPL_CONCAT(_i_,__LINE__)
#define defer(s,e) for ( \
uint32_t defer_var = (s, 0); \

View File

@ -5,6 +5,7 @@
#include "utils/options.h"
void generate_minimap(int32_t seed, uint16_t block_size, uint16_t chunk_size, uint16_t world_size) {
(void)block_size;
world_init(seed, chunk_size, world_size);
block_id const *world;

View File

@ -10,36 +10,36 @@
static inline float lerp(float a, float b, float t) { return a * (1.0f - t) + b * t; }
static inline
void DrawTextEco(const char *text, float posX, float posY, int fontSize, Color color, float spacing) {
void DrawTextEco(const char *text, float posX, float posY, float fontSize, Color color, float spacing) {
#if 1
// Check if default font has been loaded
if (GetFontDefault().texture.id != 0) {
Vector2 position = { (float)posX , (float)posY };
Vector2 position = { posX , posY };
float defaultFontSize = 10.0; // Default Font chars height in pixel
float new_spacing = spacing == 0.0f ? (float)fontSize/defaultFontSize : spacing;
float new_spacing = spacing == 0.0f ? fontSize/defaultFontSize : spacing;
DrawTextEx(GetFontDefault(), text, position, (float)fontSize , (float)new_spacing , color);
DrawTextEx(GetFontDefault(), text, position, fontSize , new_spacing , color);
}
#endif
}
static inline
int MeasureTextEco(const char *text, int fontSize, float spacing) {
float MeasureTextEco(const char *text, float fontSize, float spacing) {
#if 1
Vector2 vec = { 0.0f, 0.0f };
// Check if default font has been loaded
if (GetFontDefault().texture.id != 0) {
float defaultFontSize = 10.0; // Default Font chars height in pixel
float new_spacing = spacing == 0.0f ? (float)fontSize/defaultFontSize : spacing;
float new_spacing = spacing == 0.0f ? fontSize/defaultFontSize : spacing;
vec = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)new_spacing);
vec = MeasureTextEx(GetFontDefault(), text, fontSize, (float)new_spacing);
}
return (int)vec.x;
return vec.x;
#else
return 0;
return 0.f;
#endif
}
@ -157,6 +157,12 @@ void EcoDrawCube(Vector3 position, float width, float height, float length, floa
// Draw codepoint at specified position in 3D space
void DrawTextCodepoint3D(Font font, int codepoint, Vector3 position, float fontSize, bool backface, Color tint)
{
(void)font;
(void)codepoint;
(void)position;
(void)fontSize;
(void)backface;
(void)tint;
#if 0
// Character index position in sprite font
// NOTE: In case a codepoint is not available in the font, index returned points to '?'

View File

@ -6,6 +6,7 @@ typedef enum {
BLOCK_FLAG_COLLISION = (1 << 1),
BLOCK_FLAG_HAZARD = (1 << 2),
BLOCK_FLAG_ESSENTIAL = (1 << 3),
BLOCK_FLAG_DESTROY_ON_COLLISION = (1 << 4),
} block_flags;
typedef uint16_t block_id;

View File

@ -16,7 +16,7 @@ static block blocks[] = {
BLOCK(ASSET_LAVA, BLOCK_FLAG_HAZARD, '!', .drag = 6.2f , .friction = 4.0f),
BLOCK(ASSET_FENCE, BLOCK_FLAG_COLLISION, '#', .drag = 1.0f , .friction = 1.0f, .bounce = 1.0f),
BLOCK(ASSET_WOOD, BLOCK_FLAG_COLLISION, '#', .drag = 1.0f , .friction = 1.0f, .bounce = 0.0f),
BLOCK(ASSET_TREE, BLOCK_FLAG_COLLISION, '@', .drag = 1.0f , .friction = 1.0f, .bounce = 0.0f),
BLOCK(ASSET_TREE, BLOCK_FLAG_COLLISION|BLOCK_FLAG_DESTROY_ON_COLLISION, '@', .drag = 1.0f , .friction = 1.0f, .bounce = 0.0f),
BLOCK(ASSET_BELT_LEFT, 0, '@', .drag = 1.0f , .friction = 1.0f, .velx = -150.0f),
BLOCK(ASSET_BELT_RIGHT, 0, '@', .drag = 1.0f , .friction = 1.0f, .velx = 150.0f),

View File

@ -14,14 +14,16 @@
#include "packets/pkt_send_librg_update.h"
#define ECO2D_STREAM_ACTIONFILTER 1
ZPL_TABLE(static, world_snapshot, world_snapshot_, entity_view);
static world_data world = {0};
static world_snapshot streamer_snapshot;
entity_view world_build_entity_view(int64_t e) {
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;
if (cached_ev) return cached_ev;
entity_view view = {0};
@ -114,7 +116,7 @@ entity_view world_build_entity_view(int64_t e) {
}
world_snapshot_set(&streamer_snapshot, e, view);
return view;
return world_snapshot_get(&streamer_snapshot, e);
}
int32_t tracker_write_create(librg_world *w, librg_event *e) {
@ -147,11 +149,11 @@ int32_t tracker_write_update(librg_world *w, librg_event *e) {
int64_t entity_id = librg_event_entity_get(w, 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);
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) {
if (view->kind == EKIND_CHUNK && !view->is_dirty) {
return LIBRG_WRITE_REJECT;
}
}
@ -159,7 +161,7 @@ int32_t tracker_write_update(librg_world *w, librg_event *e) {
// NOTE(zaklaus): action-based updates
#if ECO2D_STREAM_ACTIONFILTER
{
if (view.kind != EKIND_CHUNK && !entity_can_stream(entity_id)) {
if (view->kind != EKIND_CHUNK && !entity_can_stream(entity_id)) {
return LIBRG_WRITE_REJECT;
}
}
@ -210,7 +212,7 @@ void world_configure_tracker(void) {
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, 0);
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);
@ -307,8 +309,8 @@ int32_t world_destroy(void) {
#define WORLD_LIBRG_BUFSIZ 2000000
static void world_tracker_update(uint8_t ticker, float freq, uint8_t radius) {
if (world.tracker_update[ticker] > (float)zpl_time_rel()) return;
world.tracker_update[ticker] = (float)zpl_time_rel() + freq;
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);
@ -350,15 +352,17 @@ int32_t world_update() {
ecs_progress(world.ecs, 0.0f);
}
float fast_ms = WORLD_TRACKER_UPDATE_FAST_MS;
float normal_ms = WORLD_TRACKER_UPDATE_NORMAL_MS;
float slow_ms = WORLD_TRACKER_UPDATE_SLOW_MS;
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 (game_get_kind() != GAMEKIND_SINGLE) {
fast_ms = WORLD_TRACKER_UPDATE_MP_FAST_MS;
normal_ms = WORLD_TRACKER_UPDATE_MP_NORMAL_MS;
slow_ms = WORLD_TRACKER_UPDATE_MP_SLOW_MS;
#if 1
if (game_get_kind() == GAMEKIND_SINGLE) {
fast_ms = WORLD_TRACKER_UPDATE_FAST_MS;
normal_ms = WORLD_TRACKER_UPDATE_NORMAL_MS;
slow_ms = WORLD_TRACKER_UPDATE_SLOW_MS;
}
#endif
world_tracker_update(0, fast_ms, 1);
world_tracker_update(1, normal_ms, 2);
@ -504,6 +508,7 @@ void world_chunk_destroy_block(float x, float y, bool drop_item) {
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = x;
dest->y = y;
entity_set_position(e, dest->x, dest->y);
}
}

View File

@ -8,6 +8,7 @@
#include "world/perlin.h"
#include "modules/components.h"
#include "entity.h"
#include "vehicle.h"
#include "items.h"
#include "world/blocks_info.h"
@ -133,6 +134,9 @@ static WORLD_BLOCK_OBSERVER(shaper_noise05b) {
return world_perlin_cond_offset(block_idx, 0.05, 32, 0) ? shaper(data, id, block_idx) : BLOCK_INVALID;
}
static WORLD_BLOCK_OBSERVER(shaper_noise01b) {
return world_perlin_cond_offset(block_idx, 0.01, 32, 0) ? shaper(data, id, block_idx) : BLOCK_INVALID;
}
#else
static WORLD_BLOCK_OBSERVER(shaper_noise80) {
return rand()%10 < 8 ? shaper(id, block_idx) : BLOCK_INVALID;
@ -177,7 +181,7 @@ int32_t worldgen_test(world_data *wld) {
// ground
world_fill_rect(world->data, grnd_id, 1, 1, world->dim-2, world->dim-2, NULL);
world_fill_rect(world->data, dirt_id, 1, 1, world->dim-2, world->dim-2, shaper_noise05);
world_fill_rect(world->outer_data, tree_id, 1, 1, world->dim-2, world->dim-2, shaper_noise05b);
world_fill_rect(world->outer_data, tree_id, 1, 1, world->dim-2, world->dim-2, shaper_noise01b);
// water
#if 1
@ -215,6 +219,7 @@ int32_t worldgen_test(world_data *wld) {
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
entity_set_position(e, dest->x, dest->y);
}
#endif
@ -226,6 +231,7 @@ int32_t worldgen_test(world_data *wld) {
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
entity_set_position(e, dest->x, dest->y);
}
for (int i=0; i<RAND_RANGE(328, 164); i++) {
@ -234,6 +240,7 @@ int32_t worldgen_test(world_data *wld) {
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
entity_set_position(e, dest->x, dest->y);
}
for (int i=0; i<RAND_RANGE(328, 164); i++) {
@ -242,6 +249,7 @@ int32_t worldgen_test(world_data *wld) {
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
entity_set_position(e, dest->x, dest->y);
}
for (int i=0; i<RAND_RANGE(128, 564); i++) {
@ -250,6 +258,7 @@ int32_t worldgen_test(world_data *wld) {
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
entity_set_position(e, dest->x, dest->y);
}
for (int i=0; i<RAND_RANGE(128, 964); i++) {
@ -258,6 +267,7 @@ int32_t worldgen_test(world_data *wld) {
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
entity_set_position(e, dest->x, dest->y);
}
#endif

View File

@ -26,7 +26,7 @@ int32_t tracker_read_update(librg_world *w, librg_event *e) {
entity_view *d = entity_view_get(&view->entities, entity_id);
#if 1
if (d && d->layer_id < view->active_layer_id) {
if (zpl_time_rel_ms() - d->last_update > WORLD_TRACKER_UPDATE_NORMAL_MS) {
if ((get_cached_time()*1000.0f) - d->last_update > WORLD_TRACKER_UPDATE_NORMAL_MS) {
d->layer_id = zpl_min(WORLD_TRACKER_LAYERS-1, d->layer_id+1);
}
// NOTE(zaklaus): reject updates from slower layers
@ -34,7 +34,7 @@ int32_t tracker_read_update(librg_world *w, librg_event *e) {
}
#endif
data.last_update = zpl_time_rel_ms();
data.last_update = get_cached_time()*1000.0f;
data.layer_id = view->active_layer_id;
predict_receive_update(d, &data);
entity_view_update_or_create(&view->entities, entity_id, data);
@ -83,7 +83,7 @@ void world_view_init(world_view *view, uint32_t seed, uint64_t ent_id, uint16_t
librg_config_chunksize_set(view->tracker, WORLD_BLOCK_SIZE * chunk_size, WORLD_BLOCK_SIZE * chunk_size, 1);
librg_config_chunkamount_set(view->tracker, chunk_amount, chunk_amount, 1);
librg_config_chunkoffset_set(view->tracker, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG, 0);
librg_config_chunkoffset_set(view->tracker, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG);
librg_event_set(view->tracker, LIBRG_READ_CREATE, tracker_read_create);
librg_event_set(view->tracker, LIBRG_READ_REMOVE, tracker_read_remove);

View File

@ -27,37 +27,41 @@ void IntegratePositions(ecs_iter_t *it) {
Velocity *v = ecs_field(it, Velocity, 2);
for (int i = 0; i < it->count; i++) {
// NOTE(zaklaus): world bounds
{
float w = (float)world_dim();
p[i].x = zpl_clamp(p[i].x, 0, w-1);
p[i].y = zpl_clamp(p[i].y, 0, w-1);
if (ecs_get(it->world, it->entities[i], IsInVehicle)) {
continue;
}
#if PHY_BLOCK_COLLISION==1
// NOTE(zaklaus): X axis
{
world_block_lookup lookup = world_block_from_realpos(p[i].x+PHY_LOOKAHEAD(v[i].x), p[i].y);
uint32_t flags = blocks_get_flags(lookup.bid);
float bounce = blocks_get_bounce(lookup.bid);
if (flags & BLOCK_FLAG_COLLISION) {
v[i].x = physics_correction(lookup.ox, v[i].x, bounce);
if (zpl_abs(v[i].x) >= 0.001f || zpl_abs(v[i].y) >= 0.001f) {
// NOTE(zaklaus): world bounds
{
float w = (float)world_dim();
p[i].x = zpl_clamp(p[i].x, 0, w-1);
p[i].y = zpl_clamp(p[i].y, 0, w-1);
}
}
// NOTE(zaklaus): Y axis
{
world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y+PHY_LOOKAHEAD(v[i].y));
uint32_t flags = blocks_get_flags(lookup.bid);
float bounce = blocks_get_bounce(lookup.bid);
if (flags & BLOCK_FLAG_COLLISION) {
v[i].y = physics_correction(lookup.oy, v[i].y, bounce);
#if PHY_BLOCK_COLLISION==1
// NOTE(zaklaus): X axis
{
world_block_lookup lookup = world_block_from_realpos(p[i].x+PHY_LOOKAHEAD(v[i].x), p[i].y);
uint32_t flags = blocks_get_flags(lookup.bid);
float bounce = blocks_get_bounce(lookup.bid);
if (flags & BLOCK_FLAG_COLLISION) {
v[i].x = physics_correction(lookup.ox, v[i].x, bounce);
}
}
}
#endif
p[i].x += v[i].x * safe_dt(it);
p[i].y += v[i].y * safe_dt(it);
// NOTE(zaklaus): Y axis
{
world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y+PHY_LOOKAHEAD(v[i].y));
uint32_t flags = blocks_get_flags(lookup.bid);
float bounce = blocks_get_bounce(lookup.bid);
if (flags & BLOCK_FLAG_COLLISION) {
v[i].y = physics_correction(lookup.oy, v[i].y, bounce);
}
}
#endif
entity_set_position(it->entities[i], p[i].x+v[i].x*safe_dt(it), p[i].y+v[i].y*safe_dt(it));
}
{
debug_v2 a = {p[i].x, p[i].y};
@ -68,20 +72,6 @@ void IntegratePositions(ecs_iter_t *it) {
}
}
void UpdateTrackerPos(ecs_iter_t *it) {
Position *p = ecs_field(it, Position, 1);
for (int i = 0; i < it->count; i++){
librg_entity_chunk_set(world_tracker(), it->entities[i], librg_chunk_from_realpos(world_tracker(), p[i].x, p[i].y, 0));
{
debug_v2 a = {p[i].x-2.5f, p[i].y-2.5f};
debug_v2 b = {p[i].x+2.5f, p[i].y+2.5f};
debug_push_rect(a, b, 0x00FFFFFF);
}
}
}
#define HAZARD_BLOCK_TIME 1.0f
#define HAZARD_BLOCK_DMG 5.0f
@ -90,10 +80,10 @@ void HurtOnHazardBlock(ecs_iter_t *it) {
Health *h = ecs_field(it, Health, 2);
for (int i = 0; i < it->count; i++) {
world_block_lookup l = world_block_from_realpos(p[i].x, p[i].y);
if (blocks_get_flags(l.bid) & BLOCK_FLAG_HAZARD) {
if (h->pain_time < 0.0f) {
h->pain_time = HAZARD_BLOCK_TIME;
if (h->pain_time < 0.0f) {
h->pain_time = HAZARD_BLOCK_TIME;
world_block_lookup l = world_block_from_realpos(p[i].x, p[i].y);
if (blocks_get_flags(l.bid) & BLOCK_FLAG_HAZARD) {
h->hp -= HAZARD_BLOCK_DMG;
h->hp = zpl_max(0.0f, h->hp);
}
@ -141,6 +131,10 @@ void ApplyWorldDragOnVelocity(ecs_iter_t *it) {
Velocity *v = ecs_field(it, Velocity, 2);
for (int i = 0; i < it->count; i++) {
if (zpl_abs(v[i].x) < 0.001f && zpl_abs(v[i].y) < 0.001f) continue;
if (ecs_get(it->world, it->entities[i], IsInVehicle)) {
continue;
}
world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y);
float drag = zpl_clamp(blocks_get_drag(lookup.bid), 0.0f, 1.0f);
float friction = blocks_get_friction(lookup.bid);
@ -225,8 +219,6 @@ void SystemsImport(ecs_world_t *ecs) {
ECS_SYSTEM(ecs, ResetActivators, EcsPostUpdate, components.Input);
ECS_SYSTEM(ecs, UpdateTrackerPos, EcsPostUpdate, components.Position, components.Velocity);
ECS_SYSTEM(ecs, ClearVehicle, EcsUnSet, components.Vehicle);
ECS_SYSTEM(ecs, DisableWorldEdit, EcsPostUpdate);

View File

@ -3,7 +3,7 @@
#define ITEM_PICK_RADIUS 25.0f
#define ITEM_MERGER_RADIUS 75.0f
#define ITEM_ATTRACT_RADIUS 75.0f
#define ITEM_ATTRACT_FORCE 0.63f
#define ITEM_ATTRACT_FORCE 6.f
#define ITEM_CONTAINER_REACH_RADIUS 105.0f
@ -13,6 +13,7 @@ void PickItem(ecs_iter_t *it) {
for (int i = 0; i < it->count; i++) {
if (inv[i].pickup_time > game_time()) continue;
inv[i].pickup_time = game_time() + 0.5f;
size_t ents_count;
int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 2);
@ -20,6 +21,7 @@ void PickItem(ecs_iter_t *it) {
ItemDrop *drop = 0;
if ((drop = ecs_get_mut_if(it->world, ents[j], ItemDrop))) {
Position *p2 = ecs_get_mut(it->world, ents[j], Position);
Velocity *v2 = ecs_get_mut(it->world, ents[j], Velocity);
float dx = p2->x - p[i].x;
float dy = p2->y - p[i].y;
@ -43,9 +45,8 @@ void PickItem(ecs_iter_t *it) {
}
}
} else if (range <= ITEM_ATTRACT_RADIUS) {
entity_set_position(ents[j],
zpl_lerp(p2->x, p[i].x, ITEM_ATTRACT_FORCE*it->delta_time),
zpl_lerp(p2->y, p[i].y, ITEM_ATTRACT_FORCE*it->delta_time));
v2->x = (p[i].x - p2->x) * ITEM_ATTRACT_FORCE;
v2->y = (p[i].y - p2->y) * ITEM_ATTRACT_FORCE;
}
}
}

View File

@ -143,10 +143,17 @@ void VehicleHandling(ecs_iter_t *it) {
v[i].y += ((fr_y + bk_y) / 2.0f - p[i].y);
car->heading = zpl_arctan2(fr_y - bk_y, fr_x - bk_x);
world_block_lookup lookahead = world_block_from_realpos(p[i].x+PHY_LOOKAHEAD(v[i].x), p[i].y+PHY_LOOKAHEAD(v[i].y));
float check_x = p[i].x+PHY_LOOKAHEAD(v[i].x);
float check_y = p[i].y+PHY_LOOKAHEAD(v[i].y);
world_block_lookup lookahead = world_block_from_realpos(check_x, check_y);
uint32_t flags = blocks_get_flags(lookahead.bid);
if (flags & BLOCK_FLAG_COLLISION) {
car->force = 0.0f;
if (flags & BLOCK_FLAG_DESTROY_ON_COLLISION) {
world_chunk_destroy_block(check_x, check_y, true);
car->force *= 0.8f;
} else {
car->force = 0.0f;
}
}
for (int j = 0; j < 4; j++) {
@ -155,11 +162,9 @@ void VehicleHandling(ecs_iter_t *it) {
// NOTE(zaklaus): Update passenger position
{
Position *p2 = ecs_get_mut(it->world, pe, Position);
Velocity *v2 = ecs_get_mut(it->world, pe, Velocity);
*p2 = p[i];
entity_set_position(pe, p[i].x, p[i].y);
*v2 = v[i];
entity_wake(pe);
}
}

View File

@ -7,3 +7,4 @@ file(GLOB SRCS *.h
)
add_library(flecs-bundle STATIC ${SRCS})
target_compile_options(flecs-bundle PRIVATE "-Wno-enum-constexpr-conversion")

42
code/vendors/librg.h vendored
View File

@ -157,7 +157,7 @@
#define LIBRG_VERSION_MAJOR 7
#define LIBRG_VERSION_MINOR 0
#define LIBRG_VERSION_PATCH 1
#define LIBRG_VERSION_PATCH 2
#define LIBRG_VERSION_PRE ""
// file: librg_hedley.h
@ -21033,14 +21033,14 @@ int8_t librg_chunk_to_chunkpos(librg_world *world, librg_chunk id, int16_t *chun
return LIBRG_CHUNK_INVALID;
}
int16_t z = (int16_t)(id / (wld->worldsize.x * wld->worldsize.y));
int16_t r1 = (int16_t)(id % (wld->worldsize.x * wld->worldsize.y));
int16_t y = r1 / wld->worldsize.x;
int16_t x = r1 % wld->worldsize.x;
int64_t z = (int64_t)(id / (wld->worldsize.x * wld->worldsize.y));
int64_t r1 = (int64_t)(id % (wld->worldsize.x * wld->worldsize.y));
int64_t y = r1 / wld->worldsize.x;
int64_t x = r1 % wld->worldsize.x;
if (chunk_x) *chunk_x = x - librg_util_chunkoffset_line(0, wld->chunkoffset.x, wld->worldsize.x);
if (chunk_y) *chunk_y = y - librg_util_chunkoffset_line(0, wld->chunkoffset.y, wld->worldsize.y);
if (chunk_z) *chunk_z = z - librg_util_chunkoffset_line(0, wld->chunkoffset.z, wld->worldsize.z);
if (chunk_x) *chunk_x = (int16_t)(x - librg_util_chunkoffset_line(0, wld->chunkoffset.x, wld->worldsize.x));
if (chunk_y) *chunk_y = (int16_t)(y - librg_util_chunkoffset_line(0, wld->chunkoffset.y, wld->worldsize.y));
if (chunk_z) *chunk_z = (int16_t)(z - librg_util_chunkoffset_line(0, wld->chunkoffset.z, wld->worldsize.z));
return LIBRG_OK;
}
@ -21520,11 +21520,13 @@ int32_t librg_world_query(librg_world *world, int64_t owner_id, uint8_t chunk_ra
size_t buffer_limit = *entity_amount;
size_t total_count = zpl_array_count(wld->entity_map.entries);
librg_table_i64 results = {0};
librg_table_tbl dimensions = {0};
static librg_table_i64 results = {0};
static librg_table_tbl dimensions = {0};
librg_table_i64_init(&results, wld->allocator);
librg_table_tbl_init(&dimensions, wld->allocator);
if (!results.entries) {
librg_table_i64_init(&results, wld->allocator);
librg_table_tbl_init(&dimensions, wld->allocator);
}
/* generate a map of visible chunks (only counting owned entities) */
for (size_t i=0; i < total_count; ++i) {
@ -21621,8 +21623,19 @@ int32_t librg_world_query(librg_world *world, int64_t owner_id, uint8_t chunk_ra
for (int i = 0; i < zpl_array_count(dimensions.entries); ++i)
librg_table_i64_destroy(&dimensions.entries[i].value);
librg_table_tbl_destroy(&dimensions);
librg_table_i64_destroy(&results);
// NOTE(zaklaus): clear out our streaming snapshot
// TODO(zaklaus): move this to zpl
{
zpl_array_clear(results.hashes);
zpl_array_clear(results.entries);
}
// NOTE(zaklaus): clear out our streaming snapshot
// TODO(zaklaus): move this to zpl
{
zpl_array_clear(dimensions.hashes);
zpl_array_clear(dimensions.entries);
}
*entity_amount = LIBRG_MIN(buffer_limit, count);
return LIBRG_MAX(0, (int32_t)(count - buffer_limit));
@ -21983,4 +21996,3 @@ LIBRG_END_C_DECLS
#endif // LIBRG_IMPLEMENTATION
#endif // LIBRG_H

33513
code/vendors/zpl.h vendored

File diff suppressed because it is too large Load Diff

10
web/bind.sh 100755
View File

@ -0,0 +1,10 @@
#!/bin/bash
# Symlink sources
if [ ! -d "build_web/map" ]; then
pushd build_web
mkdir -p "map/build_web"
ln -s "/workspaces/eco2d/code" "map/code"
ln -s "/workspaces/eco2d/build_web/_deps" "map/build_web/_deps"
popd
fi

14
web/build.sh 100755
View File

@ -0,0 +1,14 @@
#!/bin/bash
set -xe
pushd build_web
if [ -f "index.html" ]; then
rm -rf index.html
fi
cmake --build . --parallel
if [ -f "eco2d.html" ]; then
mv eco2d.html index.html
fi
popd

5
web/clean.sh 100755
View File

@ -0,0 +1,5 @@
#!/bin/bash
set -xe
cmake --build build_web --target clean

39
web/deploy.sh 100755
View File

@ -0,0 +1,39 @@
#!/bin/bash
set -xe
if [ ! -d "build_web" ]; then
echo "Build directory not found. Run setup_web.sh first."
exit 1
fi
if [ ! -d "butler" ]; then
mkdir butler
wget https://broth.itch.ovh/butler/linux-amd64/LATEST/archive/default -O butler.zip
mv butler.zip butler/
pushd butler/
unzip butler.zip
rm -rf butler.zip
chmod +x ./butler
./butler -V
popd
fi
# Build the project
web/build.sh
# Package all assets
if [ ! -f "build_web/index.html" ]; then
echo "Build data not found. Compilation errors?"
exit 1
fi
mkdir -p deploy_web
cp build_web/eco2d.* deploy_web/
cp build_web/index.html deploy_web/
# Deploy to itch.io
./butler/butler push deploy_web/ zaklaus/eco2d:html-latest
# Teardown
rm -rf deploy_web

View File

@ -0,0 +1,2 @@
// Hack to enforce CLOCK_REALTIME, which is significantly faster for our purposes.
_emscripten_get_now = () => Date.now();

107
web/eco2d.html 100644
View File

@ -0,0 +1,107 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>eco2d web game</title>
<meta name="title" content="eco2d web game">
<meta name="description" content="Small C99 2D game engine with a focus on prototyping">
<meta name="keywords" content="eco2d, html5, ecs, C, librg, chunks">
<meta name="viewport" content="width=device-width">
<!-- Open Graph metatags for sharing -->
<meta property="og:title" content="eco2d web game">
<meta property="og:image:type" content="image/png">
<meta property="og:image" content="https://avatars.githubusercontent.com/u/31039603?s=400&u=371f601b81fd4c6843f910b4565a54704caaa374&v=4">
<meta property="og:site_name" content="zpl.pw">
<meta property="og:url" content="https://github.com/zpl-c/eco2d">
<meta property="og:description" content="Small C99 2D game engine with a focus on prototyping">
<!-- Twitter metatags for sharing -->
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@DMadarasz">
<meta name="twitter:title" content="eco2d web game">
<meta name="twitter:image" content="https://avatars.githubusercontent.com/u/31039603?s=400&u=371f601b81fd4c6843f910b4565a54704caaa374&v=4">
<meta name="twitter:url" content="https://github.com/zpl-c/eco2d">
<meta name="twitter:description" content="Small C99 2D game engine with a focus on prototyping">
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-R1QS3P3D1T"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-R1QS3P3D1T');
</script>
<style>
html, body { margin: 0px; padding: 0px; background-color: black; width: 100%; height: 100%;}
canvas.emscripten {
border: 0px none; background-color: black;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
}
</style>
<script type='text/javascript' src="https://cdn.jsdelivr.net/gh/eligrey/FileSaver.js/dist/FileSaver.min.js"> </script>
<script type='text/javascript'>
function saveFileFromMEMFSToDisk(memoryFSname, localFSname) // This can be called by C/C++ code
{
var isSafari = false; // Not supported, navigator.userAgent access is being restricted
//var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
var data = FS.readFile(memoryFSname);
var blob;
if (isSafari) blob = new Blob([data.buffer], { type: "application/octet-stream" });
else blob = new Blob([data.buffer], { type: "application/octet-binary" });
// NOTE: SaveAsDialog is a browser setting. For example, in Google Chrome,
// in Settings/Advanced/Downloads section you have a setting:
// 'Ask where to save each file before downloading' - which you can set true/false.
// If you enable this setting it would always ask you and bring the SaveAsDialog
saveAs(blob, localFSname);
}
</script>
</head>
<body>
<canvas class=emscripten id=canvas oncontextmenu=event.preventDefault() tabindex=-1></canvas>
<script>
var Module = {
print: (function() {
return function(text) {
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
console.log(text);
};
})(),
printErr: (function() {
return function(text) {
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
console.error(text);
};
})(),
canvas: (function() {
var canvas = document.getElementById('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
})
canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
return canvas;
})()
};
</script>
{{{ SCRIPT }}}
</body>
</html>

5
web/host.sh 100755
View File

@ -0,0 +1,5 @@
#!/bin/bash
set -xe
python -m http.server --directory build_web --bind 127.0.0.1

20
web/setup.sh 100755
View File

@ -0,0 +1,20 @@
#!/bin/bash
set -xe
# Setup emsdk
if [ ! -d "emsdk" ]; then
wget https://github.com/emscripten-core/emsdk/archive/refs/heads/main.zip -O emscripten.zip
unzip emscripten.zip
mv emsdk-main emsdk
rm -rf emscripten.zip
fi
source ./emsdk/emsdk_env.sh
emsdk update
emsdk install latest
emsdk activate latest
source ./emsdk/emsdk_env.sh
# Setup web build
emcmake cmake -S . -B build_web -DCMAKE_BUILD_TYPE=Release -DPLATFORM=Web

18
win/package.bat 100644
View File

@ -0,0 +1,18 @@
@echo off
cmake --build build_rel --parallel 32 --config Release
if not %ERRORLEVEL% == 0 exit /B 1
@rd /S /Q pkg
mkdir pkg
copy build_rel\eco2d.exe pkg
rem tools\upx -9 pkg\eco2d.exe
robocopy art pkg\art /E
@del pkg\art\*.ecotex
IF NOT "%1"=="SKIP_DEPLOY" (
pushd pkg
..\tools\7za.exe a -r ..\eco2d.zip *.*
popd
butler push eco2d.zip zaklaus/eco2d:win64-latest
)