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
build_rel build_rel
build_web
emsdk
deploy_web
run_web
butler
screenshots screenshots
build.bat build.bat
run.bat run.bat
@ -12,7 +17,6 @@ GPATH
GRTAGS GRTAGS
GTAGS GTAGS
/run_release.bat /run_release.bat
/package.bat
pkg pkg
pkg.zip pkg.zip
eco2d.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 ${CMAKE_BINARY_DIR}/)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR})
SET(CMAKE_USE_RELATIVE_PATHS OFF)
if(MSVC) if(MSVC)
add_definitions(-D_CRT_SECURE_NO_WARNINGS) add_definitions(-D_CRT_SECURE_NO_WARNINGS)
endif() 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_directories(code/common code/vendors code/vendors/flecs)
include(cmake/FindRaylib.cmake) include(cmake/FindRaylib.cmake)

View File

@ -1,11 +1,12 @@
<div align="center"> <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> </div>
<br /> <br />
<div align="center"> <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://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> </div>
<br /> <br />
@ -51,6 +52,30 @@ In the abstract sense, we call the Server the game master hosting all gameplay r
# Build the project # Build the project
We use CMake to generate project files and manage builds. 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: You can do the following on the command line to create and build this project:
```sh ```sh
git clone https://github.com/zpl-c/eco2d.git 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: Run the following command to see all the options:
```sh ```sh
Windows:
build\Debug\eco2d.exe -?
Linux:
build\eco2d.exe -? build\eco2d.exe -?
``` ```

View File

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

View File

@ -43,5 +43,6 @@ add_executable(eco2d
target_compile_definitions(eco2d PRIVATE CLIENT) target_compile_definitions(eco2d PRIVATE CLIENT)
include_directories(src ../modules ../../art/gen) include_directories(src ../modules ../../art/gen)
target_link_libraries(eco2d raylib cwpack eco2d-modules flecs-bundle vendors-bundle) 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) link_system_libs(eco2d)

View File

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

View File

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

View File

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

View File

@ -116,7 +116,7 @@ void debug_replay_start(void) {
if (records) zpl_array_free(records); if (records) zpl_array_free(records);
zpl_array_init_reserve(records, zpl_heap(), UINT16_MAX); zpl_array_init_reserve(records, zpl_heap(), UINT16_MAX);
last_record_time = zpl_time_rel(); last_record_time = get_cached_time();
SetTargetFPS(60); SetTargetFPS(60);
} }
@ -155,7 +155,7 @@ void debug_replay_run(void) {
if (mime) return; if (mime) return;
is_playing = true; is_playing = true;
record_pos = 0; record_pos = 0;
playback_time = zpl_time_rel(); playback_time = get_cached_time();
zpl_array_init(temp_actors, zpl_heap()); zpl_array_init(temp_actors, zpl_heap());
plr = camera_get().ent_id; plr = camera_get().ent_id;
@ -181,10 +181,10 @@ void ActSpawnBelt(void);
void debug_replay_update(void) { void debug_replay_update(void) {
if (!is_playing) return; if (!is_playing) return;
if (playback_time >= zpl_time_rel()) return; if (playback_time >= get_cached_time()) return;
replay_record *r = &records[record_pos]; replay_record *r = &records[record_pos];
playback_time = zpl_time_rel() + r->delay; playback_time = get_cached_time() + r->delay;
switch (r->kind) { switch (r->kind) {
case RPKIND_KEY: { case RPKIND_KEY: {
@ -249,7 +249,7 @@ void debug_replay_update(void) {
void debug_replay_record_keystate(pkt_send_keystate state) { void debug_replay_record_keystate(pkt_send_keystate state) {
if (!is_recording) return; if (!is_recording) return;
double record_time = zpl_time_rel(); double record_time = get_cached_time();
replay_record rec = { replay_record rec = {
.kind = RPKIND_KEY, .kind = RPKIND_KEY,
@ -258,13 +258,13 @@ void debug_replay_record_keystate(pkt_send_keystate state) {
}; };
zpl_array_append(records, rec); 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) { void debug_replay_special_action(replay_kind kind) {
ZPL_ASSERT(kind != RPKIND_KEY); ZPL_ASSERT(kind != RPKIND_KEY);
if (!is_recording || is_playing) return; if (!is_recording || is_playing) return;
double record_time = zpl_time_rel(); double record_time = get_cached_time();
replay_record rec = { replay_record rec = {
.kind = kind, .kind = kind,
@ -272,5 +272,5 @@ void debug_replay_special_action(replay_kind kind) {
}; };
zpl_array_append(records, rec); 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_SIZE 22
#define DBG_FONT_SPACING DBG_FONT_SIZE * 1.2f #define DBG_FONT_SPACING DBG_FONT_SIZE * 1.2f
#define DBG_START_XPOS 15 #define DBG_START_XPOS 15
#define DBG_START_YPOS 200 #define DBG_START_YPOS 30
#define DBG_LIST_XPOS_OFFSET 10 #define DBG_LIST_XPOS_OFFSET 10
#define DBG_SHADOW_OFFSET_XPOS 1 #define DBG_SHADOW_OFFSET_XPOS 1
#define DBG_SHADOW_OFFSET_YPOS 1 #define DBG_SHADOW_OFFSET_YPOS 1
@ -92,6 +92,7 @@ static debug_item items[] = {
.kind = DITEM_LIST, .kind = DITEM_LIST,
.name = "general", .name = "general",
.list = { .list = {
.is_collapsed = true,
.items = (debug_item[]) { .items = (debug_item[]) {
{ .kind = DITEM_TEXT, .name = "delta time", .proc = DrawDeltaTime }, { .kind = DITEM_TEXT, .name = "delta time", .proc = DrawDeltaTime },
{ .kind = DITEM_TEXT, .name = "pos", .proc = DrawCameraPos }, { .kind = DITEM_TEXT, .name = "pos", .proc = DrawCameraPos },
@ -104,6 +105,7 @@ static debug_item items[] = {
.kind = DITEM_LIST, .kind = DITEM_LIST,
.name = "world simulation", .name = "world simulation",
.list = { .list = {
.is_collapsed = true,
.items = (debug_item[]) { .items = (debug_item[]) {
{ .kind = DITEM_COND, .on_success = CondIsWorldRunning }, { .kind = DITEM_COND, .on_success = CondIsWorldRunning },
{ .kind = DITEM_BUTTON, .name = "pause", .on_click = ActWorldToggleSim }, { .kind = DITEM_BUTTON, .name = "pause", .on_click = ActWorldToggleSim },
@ -127,6 +129,7 @@ static debug_item items[] = {
.kind = DITEM_LIST, .kind = DITEM_LIST,
.name = "debug actions", .name = "debug actions",
.list = { .list = {
.is_collapsed = true,
.items = (debug_item[]) { .items = (debug_item[]) {
{ .kind = DITEM_BUTTON, .name = "spawn car", .on_click = ActSpawnCar }, { .kind = DITEM_BUTTON, .name = "spawn car", .on_click = ActSpawnCar },
{ .kind = DITEM_BUTTON, .name = "place ice rink", .on_click = ActPlaceIceRink }, { .kind = DITEM_BUTTON, .name = "place ice rink", .on_click = ActPlaceIceRink },
@ -157,6 +160,7 @@ static debug_item items[] = {
.kind = DITEM_LIST, .kind = DITEM_LIST,
.name = "conn metrics", .name = "conn metrics",
.list = { .list = {
.is_collapsed = true,
.items = (debug_item[]) { .items = (debug_item[]) {
{ .kind = DITEM_COND, .on_success = CondClientDisconnected }, { .kind = DITEM_COND, .on_success = CondClientDisconnected },
{ .kind = DITEM_TEXT, .name = "status", .proc = DrawLiteral, .text = "disconnected" }, { .kind = DITEM_TEXT, .name = "status", .proc = DrawLiteral, .text = "disconnected" },
@ -172,6 +176,7 @@ static debug_item items[] = {
}, },
.limit_to = L_MP, .limit_to = L_MP,
}, },
#if !defined(PLATFORM_WEB)
{ {
.kind = DITEM_LIST, .kind = DITEM_LIST,
.name = "replay system", .name = "replay system",
@ -209,6 +214,7 @@ static debug_item items[] = {
}, },
.limit_to = L_SP, .limit_to = L_SP,
}, },
#endif
{ {
.kind = DITEM_LIST, .kind = DITEM_LIST,
.name = "profilers", .name = "profilers",
@ -226,11 +232,13 @@ static debug_item items[] = {
.is_collapsed = 1 .is_collapsed = 1
} }
}, },
#if !defined(PLATFORM_WEB)
{ {
.kind = DITEM_BUTTON, .kind = DITEM_BUTTON,
.name = "exit game", .name = "exit game",
.on_click = ActExitGame, .on_click = ActExitGame,
}, },
#endif
{.kind = DITEM_END}, {.kind = DITEM_END},
}; };

View File

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

View File

@ -23,6 +23,7 @@ uint64_t entity_spawn(uint16_t class_id) {
#if 1 #if 1
pos->x=(float)(rand() % world_dim()); pos->x=(float)(rand() % world_dim());
pos->y=(float)(rand() % world_dim()); pos->y=(float)(rand() % world_dim());
entity_set_position(e, pos->x, pos->y);
#else #else
pos->x=350.0f; pos->x=350.0f;
pos->y=88.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); Position *p = ecs_get_mut(world_ecs(), ent_id, Position);
p->x = x; p->x = x;
p->y = y; p->y = y;
librg_entity_chunk_set(world_tracker(), ent_id, librg_chunk_from_realpos(world_tracker(), x, y, 0));
entity_wake(ent_id); entity_wake(ent_id);
} }
@ -78,7 +79,7 @@ void entity_update_action_timers() {
static double last_update_time = 0.0f; static double last_update_time = 0.0f;
if (!ecs_streaminfo) { if (!ecs_streaminfo) {
ecs_streaminfo = ecs_query_new(world_ecs(), "components.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); 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)) { while (ecs_query_next(&it)) {
StreamInfo *si = ecs_field(&it, StreamInfo, 1); StreamInfo *si = ecs_field(&it, StreamInfo, 1);
for (size_t i = 0; i < it.count; i++) { for (int32_t i = 0; i < it.count; i++) {
if (si[i].last_update < zpl_time_rel()) { if (si[i].last_update < get_cached_time()) {
si[i].last_update = zpl_time_rel() + si[i].tick_delay; si[i].last_update = get_cached_time() + si[i].tick_delay;
si[i].tick_delay += (zpl_time_rel() - last_update_time) * 0.5f; 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) { bool entity_can_stream(uint64_t ent_id) {
StreamInfo *si = ecs_get_mut(world_ecs(), ent_id, StreamInfo); 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 }, { 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 pc = {0};
cw_pack_context_init(&pc, data, (unsigned long)len, 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; 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); 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)); 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); 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); void entity_view_mark_for_removal(entity_view_tbl *map, uint64_t ent_id);

View File

@ -12,6 +12,7 @@
#include "profiler.h" #include "profiler.h"
#include "flecs/flecs_os_api_stdcpp.h" #include "flecs/flecs_os_api_stdcpp.h"
#include "flecs/flecs.h"
#include "modules/components.h" #include "modules/components.h"
#include "modules/systems.h" #include "modules/systems.h"
@ -72,7 +73,7 @@ void world_viewers_init(uint32_t num_viewers) {
} }
void world_viewers_destroy() { 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]); world_view_destroy(&world_viewers[i]);
} }
zpl_buffer_free(world_viewers); zpl_buffer_free(world_viewers);
@ -114,12 +115,14 @@ size_t game_world_view_count(void) {
} }
void flecs_dash_init() { void flecs_dash_init() {
#if !defined(ZPL_SYSTEM_EMSCRIPTEN)
ecs_singleton_set(world_ecs(), EcsRest, {0}); ecs_singleton_set(world_ecs(), EcsRest, {0});
ECS_IMPORT(world_ecs(), FlecsMonitor); ECS_IMPORT(world_ecs(), FlecsMonitor);
#endif
} }
float game_time() { 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) { 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) { if (game_mode == GAMEKIND_HEADLESS) {
network_server_tick(); network_server_tick();
static uint64_t ms_report = 2500; static float ms_report = 2.5f;
if (ms_report < zpl_time_rel_ms()) { if (ms_report < get_cached_time()) {
ms_report = zpl_time_rel_ms() + 5000; ms_report = get_cached_time() + 5.f;
zpl_printf("delta: %f ms.\n", (zpl_time_rel() - last_update)*1000.0f); 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() { void game_render() {

View File

@ -33,11 +33,11 @@ Texture2D GenColorEco(Color color) {
Texture2D texgen_build_anim(asset_id id, int64_t counter) { Texture2D texgen_build_anim(asset_id id, int64_t counter) {
(void)counter; (void)counter;
switch (id) { switch (id) {
case ASSET_WATER: { // case ASSET_WATER: {
Image img = LoadImageEco("water"); // Image img = LoadImageEco("water");
ImageColorBrightness(&img, zpl_abs((counter % 64 - 32)*2)); // ImageColorBrightness(&img, zpl_abs((counter % 64 - 32)*2));
return Image2TexEco(img); // return Image2TexEco(img);
}break; // }break;
default: return GenColorEco(PINK); 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_LAVA: return LoadTexEco("lava");
case ASSET_WOOD: return LoadTexEco("wood"); case ASSET_WOOD: return LoadTexEco("wood");
case ASSET_TREE: return LoadTexEco("tree"); case ASSET_TREE: return LoadTexEco("tree");
case ASSET_WATER: return LoadTexEco("water");
case ASSET_BELT: case ASSET_BELT:
case ASSET_BELT_RIGHT: return LoadTexEco("belt_right"); 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]; 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; item_usage usage = 0;
uint16_t item_id = 0; uint16_t item_id = 0;
if (!build_is_deletion_mode){ 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); ecs_entity_t e = entity_spawn_id(desc->place_item.id);
ZPL_ASSERT(world_entity_valid(e)); ZPL_ASSERT(world_entity_valid(e));
Position *pos = ecs_get_mut(ecs, e, Position); entity_set_position(e, p.x, p.y);
pos->x = p.x;
pos->y = p.y;
it->quantity--; it->quantity--;
}break; }break;
case UKIND_DELETE:
case UKIND_END_PLACE:
case UKIND_PROXY:
break;
} }
} }

View File

@ -14,6 +14,11 @@
#include "modules/components.h" #include "modules/components.h"
#include "modules/systems.h" #include "modules/systems.h"
#if defined(PLATFORM_WEB)
#include <emscripten/emscripten.h>
void UpdateDrawFrame(void);
#endif
#define DEFAULT_WORLD_SEED 302097 #define DEFAULT_WORLD_SEED 302097
#define DEFAULT_CHUNK_SIZE 16 /* amount of blocks within a chunk (single axis) */ #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) */ #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(); sighandler_register();
game_init(host, port, play_mode, num_viewers, seed, chunk_size, world_size, is_dash_enabled); game_init(host, port, play_mode, num_viewers, seed, chunk_size, world_size, is_dash_enabled);
#if !defined(PLATFORM_WEB)
while (game_is_running()) { while (game_is_running()) {
reset_cached_time();
profile (PROF_MAIN_LOOP) { profile (PROF_MAIN_LOOP) {
game_input(); game_input();
game_update(); game_update();
@ -82,6 +89,9 @@ int main(int argc, char** argv) {
profiler_collate(); profiler_collate();
} }
#else
emscripten_set_main_loop(UpdateDrawFrame, 0, 1);
#endif
game_shutdown(); game_shutdown();
sighandler_unregister(); sighandler_unregister();
@ -90,3 +100,25 @@ int main(int argc, char** argv) {
zpl_opts_free(&opts); zpl_opts_free(&opts);
return 0; 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 incoming_bandwidth = 0.0f;
static float outgoing_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 #define MAX_RATE_SAMPLES 8
static uint64_t last_total_sent = 0; static uint64_t last_total_sent = 0;
static uint64_t last_total_recv = 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; incoming_bandwidth = stats.incoming_bandwidth /= MAX_RATE_SAMPLES;
outgoing_bandwidth = stats.outgoing_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 { } else {
stats.incoming_bandwidth = incoming_bandwidth; stats.incoming_bandwidth = incoming_bandwidth;
stats.outgoing_bandwidth = outgoing_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) { 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; uint8_t *blob = (uint8_t*)raw_blob;
for (pkt_desc *field = desc; field->type != CWP_NOT_AN_ITEM; ++field) { for (pkt_desc *field = desc; field->type != CWP_NOT_AN_ITEM; ++field) {
if (field->skip_count != 0) { 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 pkt_dump_struct(pkt_desc *desc, void* raw_blob, uint32_t blob_size) {
(void)blob_size;
uint8_t *blob = (uint8_t*)raw_blob; uint8_t *blob = (uint8_t*)raw_blob;
zpl_printf("{\n"); zpl_printf("{\n");
for (pkt_desc *field = desc; field->type != CWP_NOT_AN_ITEM; ++field) { for (pkt_desc *field = desc; field->type != CWP_NOT_AN_ITEM; ++field) {

View File

@ -7,6 +7,7 @@
#include "entity_view.h" #include "entity_view.h"
#include "camera.h" #include "camera.h"
#include "player.h" #include "player.h"
#include "entity.h"
#include "modules/components.h" #include "modules/components.h"
#include "modules/systems.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); Position *pos = ecs_get_mut(world_ecs(), ent_id, Position);
#if 0 #if 1
pos->x = world_dim()/2.0f + rand()%15*15.0f; entity_set_position(ent_id, world_dim()/2.0f + rand()%15*15.0f, world_dim()/2.0f + rand()%15*15.0f);
pos->y = world_dim()/2.0f + rand()%15*15.0f;
#else #else
pos->x = rand()%world_dim(); pos->x = rand()%world_dim();
pos->y = 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); 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); world_view_init(view, table.seed, table.ent_id, table.chunk_size, table.world_size);
game_world_view_set_active(view); game_world_view_set_active(view);
return 0; 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); 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) { int32_t pkt_send_librg_update_handler(pkt_header *header) {
cw_unpack_context uc = {0}; cw_unpack_context uc = {0};
pkt_unpack_msg(&uc, header, 2); 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); 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); if (state < 0) zpl_printf("[ERROR] world read error: %d\n", state);
float now = (float)zpl_time_rel(); float now = (float)get_cached_time();
view->delta_time[layer_id] = now - view->last_update[layer_id]; view->delta_time[layer_id] = smooth_time(now - view->last_update[layer_id]);
view->last_update[layer_id] = now; view->last_update[layer_id] = now;
return state; return state;

View File

@ -13,9 +13,20 @@
#include "debug_ui.h" #include "debug_ui.h"
#include "utils/raylib_helpers.h" #include "utils/raylib_helpers.h"
static uint16_t screenWidth = 1600; #if defined(PLATFORM_WEB)
static uint16_t screenHeight = 900; #include <emscripten.h>
static float target_zoom = 1.5f; 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; static bool request_shutdown;
#define GFX_KIND 2 #define GFX_KIND 2
@ -27,11 +38,19 @@ static bool request_shutdown;
void platform_init() { void platform_init() {
SetTraceLogLevel(LOG_ERROR); SetTraceLogLevel(LOG_ERROR);
#if defined(PLATFORM_WEB)
screenWidth = (uint16_t)canvas_get_width();
screenHeight = (uint16_t)canvas_get_height();
#endif
InitWindow(screenWidth, screenHeight, "eco2d"); InitWindow(screenWidth, screenHeight, "eco2d");
SetWindowState(/*FLAG_WINDOW_UNDECORATED|*/FLAG_WINDOW_MAXIMIZED|FLAG_WINDOW_RESIZABLE|FLAG_MSAA_4X_HINT); SetWindowState(/*FLAG_WINDOW_UNDECORATED|*/FLAG_WINDOW_MAXIMIZED|FLAG_WINDOW_RESIZABLE|FLAG_MSAA_4X_HINT);
#if !defined(PLATFORM_WEB)
screenWidth = (uint16_t)GetScreenWidth(); screenWidth = (uint16_t)GetScreenWidth();
screenHeight = (uint16_t)GetScreenHeight(); screenHeight = (uint16_t)GetScreenHeight();
#endif
// ToggleFullscreen(); // ToggleFullscreen();
// SetTargetFPS(60.0); // SetTargetFPS(60.0);
@ -119,7 +138,7 @@ void platform_input() {
float mouse_z = (GetMouseWheelMove()*0.5f); float mouse_z = (GetMouseWheelMove()*0.5f);
if (mouse_z != 0.0f) { 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 // NOTE(zaklaus): keystate handling
@ -233,8 +252,18 @@ void draw_selected_item() {
} }
void platform_render() { void platform_render() {
#if !defined(PLATFORM_WEB)
screenWidth = (uint16_t)GetScreenWidth(); screenWidth = (uint16_t)GetScreenWidth();
screenHeight = (uint16_t)GetScreenHeight(); 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) { profile(PROF_ENTITY_LERP) {
game_world_view_active_entity_map(lerp_entity_positions); 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; 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 lerp_entity_positions(uint64_t key, entity_view *data) {
(void)key; (void)key;

View File

@ -4,6 +4,8 @@
#define PROF_COLLATE_WINDOW 0.5 #define PROF_COLLATE_WINDOW 0.5
static float profiler_warmup = 3.0f;
// NOTE(zaklaus): KEEP ORDER IN SYNC WITH profiler_kind ENUM !!! // NOTE(zaklaus): KEEP ORDER IN SYNC WITH profiler_kind ENUM !!!
static profiler profilers[] = { static profiler profilers[] = {
{ .id = PROF_TOTAL_TIME, .name = "measured time" }, { .id = PROF_TOTAL_TIME, .name = "measured time" },
@ -33,6 +35,13 @@ void profiler_stop(profiler_kind id) {
} }
void profiler_collate() { 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 double frame_counter = 0.0;
static uint64_t frames = 0; static uint64_t frames = 0;

View File

@ -1,13 +1,10 @@
static Camera3D render_camera_3d; static Camera3D render_camera_3d;
static float cam_zoom = 1.5f; static float cam_zoom = 1.5f;
#define CAM_OVERLAY_ZOOM_LEVEL 0.80f
float zpl_lerp(float,float,float); float zpl_lerp(float,float,float);
float zpl_to_degrees(float); float zpl_to_degrees(float);
void DEBUG_draw_ground_3d(uint64_t key, entity_view * data) { void DEBUG_draw_ground_3d(uint64_t key, entity_view * data) {
(void)key;
switch (data->kind) { switch (data->kind) {
case EKIND_CHUNK: { case EKIND_CHUNK: {
world_view *view = game_world_view_get_active(); world_view *view = game_world_view_get_active();
@ -145,7 +142,7 @@ void renderer_debug_draw_3d(void) {
} }
float renderer_zoom_get_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) { 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 Camera2D render_camera;
static float zoom_overlay_tran = 0.0f; 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) #define ALPHA(x) ColorAlpha(x, data->tran_time)
float zpl_lerp(float,float,float); 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; tex.texture.height *= (int32_t)scale;
DrawTextureRec(tex.texture, (Rectangle){0, 0, size, -size}, (Vector2){x, y}, ColorAlpha(WHITE, data->tran_time)); 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 ty = 0; ty < view->chunk_size; ty++) {
for (size_t tx = 0; tx < view->chunk_size; tx++) { for (size_t tx = 0; tx < view->chunk_size; tx++) {
block_id blk_id = data->outer_blocks[(ty*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; extern bool inv_is_open;
void DEBUG_draw_entities(uint64_t key, entity_view * data) { void DEBUG_draw_entities(uint64_t key, entity_view * data) {
uint16_t size = 16; float size = 16.f;
uint16_t font_size = (uint16_t)lerp(4.0f, 32.0f, 0.5f/(float)render_camera.zoom); float font_size = lerp(4.0f, 32.0f, 0.5f/(float)render_camera.zoom);
float font_spacing = 1.1f; float font_spacing = 1.1f;
float title_bg_offset = 4; float title_bg_offset = 4;
float fixed_title_offset = 8; float fixed_title_offset = 8.f;
switch (data->kind) { switch (data->kind) {
case EKIND_DEMO_NPC: { case EKIND_DEMO_NPC: {
@ -64,10 +75,10 @@ void DEBUG_draw_entities(uint64_t key, entity_view * data) {
float y = data->y; float y = data->y;
float health = (data->hp / data->max_hp); float health = (data->hp / data->max_hp);
const char *title = TextFormat("Player %d", key); const char *title = TextFormat("Player %d", key);
int title_w = MeasureTextEco(title, font_size, font_spacing); float 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.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-title_bg_offset/2, y-size-fixed_title_offset, title_w*health+title_bg_offset, font_size*0.2f, ColorAlpha(RED, 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, y-size-font_size-fixed_title_offset, font_size, ColorAlpha(RAYWHITE, data->tran_time), font_spacing); 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)); DrawCircleEco(x, y, size, ColorAlpha(YELLOW, data->tran_time));
if (data->has_items && !data->inside_vehicle) { 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)); 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) 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; }break;
@ -87,9 +98,9 @@ void DEBUG_draw_entities(uint64_t key, entity_view * data) {
float x = data->x; float x = data->x;
float y = data->y; float y = data->y;
const char *title = TextFormat("Bot %d", key); const char *title = TextFormat("Bot %d", key);
int title_w = MeasureTextEco(title, font_size, font_spacing); float 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)); 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, y-size-font_size-fixed_title_offset, font_size, ColorAlpha(BLACK, data->tran_time), font_spacing); 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)); DrawCircleEco(x, y, size, ColorAlpha(PURPLE, data->tran_time));
}break; }break;
case EKIND_ITEM: { 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_ground);
game_world_view_active_entity_map(DEBUG_draw_entities_low); game_world_view_active_entity_map(DEBUG_draw_entities_low);
game_world_view_active_entity_map(DEBUG_draw_entities); 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(); EndMode2D();
} }

View File

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

View File

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

View File

@ -5,6 +5,7 @@
#include "utils/options.h" #include "utils/options.h"
void generate_minimap(int32_t seed, uint16_t block_size, uint16_t chunk_size, uint16_t world_size) { 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); world_init(seed, chunk_size, world_size);
block_id const *world; 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 float lerp(float a, float b, float t) { return a * (1.0f - t) + b * t; }
static inline 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 #if 1
// Check if default font has been loaded // Check if default font has been loaded
if (GetFontDefault().texture.id != 0) { 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 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 #endif
} }
static inline static inline
int MeasureTextEco(const char *text, int fontSize, float spacing) { float MeasureTextEco(const char *text, float fontSize, float spacing) {
#if 1 #if 1
Vector2 vec = { 0.0f, 0.0f }; Vector2 vec = { 0.0f, 0.0f };
// Check if default font has been loaded // Check if default font has been loaded
if (GetFontDefault().texture.id != 0) { if (GetFontDefault().texture.id != 0) {
float defaultFontSize = 10.0; // Default Font chars height in pixel 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 #else
return 0; return 0.f;
#endif #endif
} }
@ -157,6 +157,12 @@ void EcoDrawCube(Vector3 position, float width, float height, float length, floa
// Draw codepoint at specified position in 3D space // Draw codepoint at specified position in 3D space
void DrawTextCodepoint3D(Font font, int codepoint, Vector3 position, float fontSize, bool backface, Color tint) 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 #if 0
// Character index position in sprite font // Character index position in sprite font
// NOTE: In case a codepoint is not available in the font, index returned points to '?' // 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_COLLISION = (1 << 1),
BLOCK_FLAG_HAZARD = (1 << 2), BLOCK_FLAG_HAZARD = (1 << 2),
BLOCK_FLAG_ESSENTIAL = (1 << 3), BLOCK_FLAG_ESSENTIAL = (1 << 3),
BLOCK_FLAG_DESTROY_ON_COLLISION = (1 << 4),
} block_flags; } block_flags;
typedef uint16_t block_id; 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_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_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_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_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), 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" #include "packets/pkt_send_librg_update.h"
#define ECO2D_STREAM_ACTIONFILTER 1
ZPL_TABLE(static, world_snapshot, world_snapshot_, entity_view); ZPL_TABLE(static, world_snapshot, world_snapshot_, entity_view);
static world_data world = {0}; static world_data world = {0};
static world_snapshot streamer_snapshot; 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); 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}; entity_view view = {0};
@ -114,7 +116,7 @@ entity_view world_build_entity_view(int64_t e) {
} }
world_snapshot_set(&streamer_snapshot, e, view); 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) { 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); int64_t entity_id = librg_event_entity_get(w, e);
size_t actual_length = librg_event_size_get(w, e); size_t actual_length = librg_event_size_get(w, e);
char *buffer = librg_event_buffer_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 // 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; return LIBRG_WRITE_REJECT;
} }
} }
@ -159,7 +161,7 @@ int32_t tracker_write_update(librg_world *w, librg_event *e) {
// NOTE(zaklaus): action-based updates // NOTE(zaklaus): action-based updates
#if ECO2D_STREAM_ACTIONFILTER #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; 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."); ZPL_ASSERT_MSG(world.tracker, "[ERROR] An error occurred while trying to create a server world.");
/* config our world grid */ /* 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_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_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 #define WORLD_LIBRG_BUFSIZ 2000000
static void world_tracker_update(uint8_t ticker, float freq, uint8_t radius) { static void world_tracker_update(uint8_t ticker, float freq, uint8_t radius) {
if (world.tracker_update[ticker] > (float)zpl_time_rel()) return; if (world.tracker_update[ticker] > (float)(get_cached_time())) return;
world.tracker_update[ticker] = (float)zpl_time_rel() + freq; world.tracker_update[ticker] = (float)(get_cached_time()) + freq;
profile(PROF_WORLD_WRITE) { profile(PROF_WORLD_WRITE) {
ecs_iter_t it = ecs_query_iter(world_ecs(), world.ecs_update); 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); ecs_progress(world.ecs, 0.0f);
} }
float fast_ms = WORLD_TRACKER_UPDATE_FAST_MS; float fast_ms = WORLD_TRACKER_UPDATE_MP_FAST_MS;
float normal_ms = WORLD_TRACKER_UPDATE_NORMAL_MS; float normal_ms = WORLD_TRACKER_UPDATE_MP_NORMAL_MS;
float slow_ms = WORLD_TRACKER_UPDATE_SLOW_MS; float slow_ms = WORLD_TRACKER_UPDATE_MP_SLOW_MS;
if (game_get_kind() != GAMEKIND_SINGLE) { #if 1
fast_ms = WORLD_TRACKER_UPDATE_MP_FAST_MS; if (game_get_kind() == GAMEKIND_SINGLE) {
normal_ms = WORLD_TRACKER_UPDATE_MP_NORMAL_MS; fast_ms = WORLD_TRACKER_UPDATE_FAST_MS;
slow_ms = WORLD_TRACKER_UPDATE_MP_SLOW_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(0, fast_ms, 1);
world_tracker_update(1, normal_ms, 2); 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); Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = x; dest->x = x;
dest->y = y; dest->y = y;
entity_set_position(e, dest->x, dest->y);
} }
} }

View File

@ -8,6 +8,7 @@
#include "world/perlin.h" #include "world/perlin.h"
#include "modules/components.h" #include "modules/components.h"
#include "entity.h"
#include "vehicle.h" #include "vehicle.h"
#include "items.h" #include "items.h"
#include "world/blocks_info.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; 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 #else
static WORLD_BLOCK_OBSERVER(shaper_noise80) { static WORLD_BLOCK_OBSERVER(shaper_noise80) {
return rand()%10 < 8 ? shaper(id, block_idx) : BLOCK_INVALID; return rand()%10 < 8 ? shaper(id, block_idx) : BLOCK_INVALID;
@ -177,7 +181,7 @@ int32_t worldgen_test(world_data *wld) {
// ground // ground
world_fill_rect(world->data, grnd_id, 1, 1, world->dim-2, world->dim-2, NULL); 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->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 // water
#if 1 #if 1
@ -215,6 +219,7 @@ int32_t worldgen_test(world_data *wld) {
Position *dest = ecs_get_mut(world_ecs(), e, Position); Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE); dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = 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 #endif
@ -226,6 +231,7 @@ int32_t worldgen_test(world_data *wld) {
Position *dest = ecs_get_mut(world_ecs(), e, Position); Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE); dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = 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++) { 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); Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE); dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = 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++) { 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); Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE); dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = 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++) { 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); Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE); dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = 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++) { 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); Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE); dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = 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 #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); entity_view *d = entity_view_get(&view->entities, entity_id);
#if 1 #if 1
if (d && d->layer_id < view->active_layer_id) { 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); d->layer_id = zpl_min(WORLD_TRACKER_LAYERS-1, d->layer_id+1);
} }
// NOTE(zaklaus): reject updates from slower layers // NOTE(zaklaus): reject updates from slower layers
@ -34,7 +34,7 @@ int32_t tracker_read_update(librg_world *w, librg_event *e) {
} }
#endif #endif
data.last_update = zpl_time_rel_ms(); data.last_update = get_cached_time()*1000.0f;
data.layer_id = view->active_layer_id; data.layer_id = view->active_layer_id;
predict_receive_update(d, &data); predict_receive_update(d, &data);
entity_view_update_or_create(&view->entities, entity_id, 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_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_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_CREATE, tracker_read_create);
librg_event_set(view->tracker, LIBRG_READ_REMOVE, tracker_read_remove); 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); Velocity *v = ecs_field(it, Velocity, 2);
for (int i = 0; i < it->count; i++) { for (int i = 0; i < it->count; i++) {
// NOTE(zaklaus): world bounds if (ecs_get(it->world, it->entities[i], IsInVehicle)) {
{ continue;
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 (zpl_abs(v[i].x) >= 0.001f || zpl_abs(v[i].y) >= 0.001f) {
#if PHY_BLOCK_COLLISION==1 // NOTE(zaklaus): world bounds
// NOTE(zaklaus): X axis {
{ float w = (float)world_dim();
world_block_lookup lookup = world_block_from_realpos(p[i].x+PHY_LOOKAHEAD(v[i].x), p[i].y); p[i].x = zpl_clamp(p[i].x, 0, w-1);
uint32_t flags = blocks_get_flags(lookup.bid); p[i].y = zpl_clamp(p[i].y, 0, w-1);
float bounce = blocks_get_bounce(lookup.bid);
if (flags & BLOCK_FLAG_COLLISION) {
v[i].x = physics_correction(lookup.ox, v[i].x, bounce);
} }
}
// NOTE(zaklaus): Y axis #if PHY_BLOCK_COLLISION==1
{ // NOTE(zaklaus): X 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); world_block_lookup lookup = world_block_from_realpos(p[i].x+PHY_LOOKAHEAD(v[i].x), p[i].y);
float bounce = blocks_get_bounce(lookup.bid); uint32_t flags = blocks_get_flags(lookup.bid);
if (flags & BLOCK_FLAG_COLLISION) { float bounce = blocks_get_bounce(lookup.bid);
v[i].y = physics_correction(lookup.oy, v[i].y, bounce); 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); // NOTE(zaklaus): Y axis
p[i].y += v[i].y * safe_dt(it); {
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}; 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_TIME 1.0f
#define HAZARD_BLOCK_DMG 5.0f #define HAZARD_BLOCK_DMG 5.0f
@ -90,10 +80,10 @@ void HurtOnHazardBlock(ecs_iter_t *it) {
Health *h = ecs_field(it, Health, 2); Health *h = ecs_field(it, Health, 2);
for (int i = 0; i < it->count; i++) { for (int i = 0; i < it->count; i++) {
world_block_lookup l = world_block_from_realpos(p[i].x, p[i].y); if (h->pain_time < 0.0f) {
if (blocks_get_flags(l.bid) & BLOCK_FLAG_HAZARD) { h->pain_time = HAZARD_BLOCK_TIME;
if (h->pain_time < 0.0f) { world_block_lookup l = world_block_from_realpos(p[i].x, p[i].y);
h->pain_time = HAZARD_BLOCK_TIME; if (blocks_get_flags(l.bid) & BLOCK_FLAG_HAZARD) {
h->hp -= HAZARD_BLOCK_DMG; h->hp -= HAZARD_BLOCK_DMG;
h->hp = zpl_max(0.0f, h->hp); 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); Velocity *v = ecs_field(it, Velocity, 2);
for (int i = 0; i < it->count; i++) { 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); 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 drag = zpl_clamp(blocks_get_drag(lookup.bid), 0.0f, 1.0f);
float friction = blocks_get_friction(lookup.bid); 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, ResetActivators, EcsPostUpdate, components.Input);
ECS_SYSTEM(ecs, UpdateTrackerPos, EcsPostUpdate, components.Position, components.Velocity);
ECS_SYSTEM(ecs, ClearVehicle, EcsUnSet, components.Vehicle); ECS_SYSTEM(ecs, ClearVehicle, EcsUnSet, components.Vehicle);
ECS_SYSTEM(ecs, DisableWorldEdit, EcsPostUpdate); ECS_SYSTEM(ecs, DisableWorldEdit, EcsPostUpdate);

View File

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

View File

@ -143,10 +143,17 @@ void VehicleHandling(ecs_iter_t *it) {
v[i].y += ((fr_y + bk_y) / 2.0f - p[i].y); v[i].y += ((fr_y + bk_y) / 2.0f - p[i].y);
car->heading = zpl_arctan2(fr_y - bk_y, fr_x - bk_x); 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); uint32_t flags = blocks_get_flags(lookahead.bid);
if (flags & BLOCK_FLAG_COLLISION) { 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++) { for (int j = 0; j < 4; j++) {
@ -155,11 +162,9 @@ void VehicleHandling(ecs_iter_t *it) {
// NOTE(zaklaus): Update passenger position // NOTE(zaklaus): Update passenger position
{ {
Position *p2 = ecs_get_mut(it->world, pe, Position);
Velocity *v2 = ecs_get_mut(it->world, pe, Velocity); 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]; *v2 = v[i];
entity_wake(pe);
} }
} }

View File

@ -7,3 +7,4 @@ file(GLOB SRCS *.h
) )
add_library(flecs-bundle STATIC ${SRCS}) 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_MAJOR 7
#define LIBRG_VERSION_MINOR 0 #define LIBRG_VERSION_MINOR 0
#define LIBRG_VERSION_PATCH 1 #define LIBRG_VERSION_PATCH 2
#define LIBRG_VERSION_PRE "" #define LIBRG_VERSION_PRE ""
// file: librg_hedley.h // 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; return LIBRG_CHUNK_INVALID;
} }
int16_t z = (int16_t)(id / (wld->worldsize.x * wld->worldsize.y)); int64_t z = (int64_t)(id / (wld->worldsize.x * wld->worldsize.y));
int16_t r1 = (int16_t)(id % (wld->worldsize.x * wld->worldsize.y)); int64_t r1 = (int64_t)(id % (wld->worldsize.x * wld->worldsize.y));
int16_t y = r1 / wld->worldsize.x; int64_t y = r1 / wld->worldsize.x;
int16_t x = 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_x) *chunk_x = (int16_t)(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_y) *chunk_y = (int16_t)(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_z) *chunk_z = (int16_t)(z - librg_util_chunkoffset_line(0, wld->chunkoffset.z, wld->worldsize.z));
return LIBRG_OK; 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 buffer_limit = *entity_amount;
size_t total_count = zpl_array_count(wld->entity_map.entries); size_t total_count = zpl_array_count(wld->entity_map.entries);
librg_table_i64 results = {0}; static librg_table_i64 results = {0};
librg_table_tbl dimensions = {0}; static librg_table_tbl dimensions = {0};
librg_table_i64_init(&results, wld->allocator); if (!results.entries) {
librg_table_tbl_init(&dimensions, wld->allocator); librg_table_i64_init(&results, wld->allocator);
librg_table_tbl_init(&dimensions, wld->allocator);
}
/* generate a map of visible chunks (only counting owned entities) */ /* generate a map of visible chunks (only counting owned entities) */
for (size_t i=0; i < total_count; ++i) { 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) for (int i = 0; i < zpl_array_count(dimensions.entries); ++i)
librg_table_i64_destroy(&dimensions.entries[i].value); librg_table_i64_destroy(&dimensions.entries[i].value);
librg_table_tbl_destroy(&dimensions); // NOTE(zaklaus): clear out our streaming snapshot
librg_table_i64_destroy(&results); // 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); *entity_amount = LIBRG_MIN(buffer_limit, count);
return LIBRG_MAX(0, (int32_t)(count - buffer_limit)); return LIBRG_MAX(0, (int32_t)(count - buffer_limit));
@ -21983,4 +21996,3 @@ LIBRG_END_C_DECLS
#endif // LIBRG_IMPLEMENTATION #endif // LIBRG_IMPLEMENTATION
#endif // LIBRG_H #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
)