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

8
.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
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,7 +61,8 @@ 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");
case ASSET_BELT_LEFT: return LoadTexEco("belt_left");

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){
@ -127,4 +127,4 @@ void buildmode_draw(void) {
}
}
}
}

View File

@ -26,14 +26,14 @@ static inline asset_id item_fix_kind(asset_id id) {
uint64_t item_spawn(asset_id kind, uint32_t qty) {
ecs_entity_t e = entity_spawn(EKIND_ITEM);
ItemDrop *d = ecs_get_mut(world_ecs(), e, ItemDrop);
*d = (ItemDrop){
.kind = item_fix_kind(kind),
.quantity = qty,
.merger_time = 0,
};
return (uint64_t)e;
}
@ -58,7 +58,7 @@ void item_use(ecs_world_t *ecs, ItemDrop *it, Position p, uint64_t udata) {
asset_id item_asset = blocks_get_asset(l.bid);
item_id item_asset_id = item_find(item_asset);
if (item_asset_id == ASSET_INVALID) return;
// NOTE(zaklaus): If we replace the same item, refund 1 qty and let it replace it
if (item_asset_id == it_id) {
it->quantity++;
@ -73,7 +73,7 @@ void item_use(ecs_world_t *ecs, ItemDrop *it, Position p, uint64_t udata) {
world_chunk_replace_block(l.chunk_id, l.id, blocks_find(desc->place.kind + (asset_id)udata));
it->quantity--;
}break;
case UKIND_PLACE_ITEM:{
world_block_lookup l = world_block_from_realpos(p.x, p.y);
if (l.is_outer && l.bid > 0) {
@ -83,15 +83,19 @@ void item_use(ecs_world_t *ecs, ItemDrop *it, Position p, uint64_t udata) {
else if (l.bid > 0 && blocks_get_flags(l.bid) & (BLOCK_FLAG_COLLISION|BLOCK_FLAG_ESSENTIAL)) {
return;
}
ecs_entity_t e = entity_spawn_id(desc->place_item.id);
ZPL_ASSERT(world_entity_valid(e));
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

@ -17,16 +17,16 @@ typedef struct {
uint64_t total_received;
uint32_t outgoing_total;
uint64_t total_sent;
// NOTE(zaklaus): bandwidth (bytes/sec)
float incoming_bandwidth;
float outgoing_bandwidth;
// NOTE(zaklaus): packet integrity
uint64_t packets_sent;
uint32_t packets_lost;
float packet_loss;
// NOTE(zaklaus): ping
uint32_t ping;
uint32_t low_ping;

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,14 +38,22 @@ 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);
renderer_init();
}
@ -85,13 +104,13 @@ inline static
void platform_input_update_input_frame(game_keystate_data data) {
float mx = 0, my = 0;
platform_get_block_realpos(&mx, &my);
if (mx != last_blockpos_data.mx || my != last_blockpos_data.my){
last_blockpos_data.mx = mx;
last_blockpos_data.my = my;
game_action_send_blockpos(mx, my);
}
// NOTE(zaklaus): Test if there are any changes
if (data.x != last_input_data.x) goto send_data;
if (data.y != last_input_data.y) goto send_data;
@ -109,7 +128,7 @@ void platform_input_update_input_frame(game_keystate_data data) {
if (data.deletion_mode != last_input_data.deletion_mode) goto send_data;
if (zpl_memcompare(data.placements, last_input_data.placements, zpl_size_of(data.placements))) goto send_data;
return;
send_data:
last_input_data = data;
game_action_send_keystate(&data);
@ -117,11 +136,11 @@ void platform_input_update_input_frame(game_keystate_data data) {
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
{
float x=0.0f, y=0.0f;
@ -130,12 +149,12 @@ void platform_input() {
if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) x -= 1.0f;
if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) y += 1.0f;
if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) y -= 1.0f;
use = IsKeyPressed(KEY_SPACE);
sprint = IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT);
ctrl = IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL);
drop = IsKeyPressed(KEY_G) || player_inv.drop_item || storage_inv.drop_item;
// NOTE(zaklaus): NEW! mouse movement
Vector2 mouse_pos = GetMousePosition();
mouse_pos.x /= screenWidth;
@ -143,18 +162,18 @@ void platform_input() {
mouse_pos.x -= 0.5f;
mouse_pos.y -= 0.5f;
mouse_pos = Vector2Normalize(mouse_pos);
if (game_get_kind() == GAMEKIND_SINGLE && IsMouseButtonDown(MOUSE_MIDDLE_BUTTON)) {
x = mouse_pos.x;
y = -mouse_pos.y;
}
inv_keystate *inv = (inv_is_storage_action) ? &storage_inv : &player_inv;
inv_keystate *inv2 = (!inv_is_storage_action) ? &storage_inv : &player_inv;
// NOTE(zaklaus): don't perform picking if we manipulate our inventories
pick = (inv_is_inside||inv->item_is_held||inv2->item_is_held) ? false : IsMouseButtonDown(MOUSE_LEFT_BUTTON);
game_keystate_data in_data = {
.x = x,
.y = y,
@ -164,7 +183,7 @@ void platform_input() {
.sprint = sprint,
.ctrl = ctrl,
.pick = pick,
.drop = drop,
.storage_action = inv_is_storage_action,
.selected_item = player_inv.selected_item,
@ -173,18 +192,18 @@ void platform_input() {
.swap_storage = inv_swap_storage,
.swap_from = inv->swap_from,
.swap_to = inv->swap_to,
.deletion_mode = build_is_deletion_mode,
};
if (build_submit_placements) {
in_data.placement_num = build_num_placements;
zpl_memcopy(in_data.placements, build_placements, build_num_placements*zpl_size_of(item_placement));
}
platform_input_update_input_frame(in_data);
}
// NOTE(zaklaus): cycle through viewers
{
if (IsKeyPressed(KEY_Q)) {
@ -194,14 +213,14 @@ void platform_input() {
game_world_view_cycle_active(1);
}
}
// NOTE(zaklaus): switch render modes
{
if (IsKeyPressed(KEY_O)) {
renderer_switch(1-gfx_kind);
}
}
// NOTE(zaklaus): toggle debug drawing
#ifndef ECO2D_PROD
{
@ -218,13 +237,13 @@ void draw_selected_item() {
if (oe) {
// NOTE(zaklaus): sel item
entity_view *e = game_world_view_active_get_entity(oe->sel_ent);
if (e && e->kind == EKIND_DEVICE) {
renderer_draw_single(e->x, e->y, ASSET_BLANK, ColorAlpha(RED, 0.4f));
}else{
// NOTE(zaklaus): hover item
entity_view *e = game_world_view_active_get_entity(oe->pick_ent);
if (e && e->kind == EKIND_DEVICE) {
renderer_draw_single(e->x, e->y, ASSET_BLANK, ColorAlpha(RED, 0.1f));
}
@ -233,16 +252,26 @@ 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);
game_world_view_active_entity_map(do_entity_fadeinout);
}
assets_frame();
BeginDrawing();
{
profile (PROF_RENDER) {
@ -259,7 +288,7 @@ void platform_render() {
debug_draw();
}
EndDrawing();
if (request_shutdown) {
CloseWindow();
}

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);
@ -13,23 +13,16 @@ void DEBUG_draw_ground(uint64_t key, entity_view * data) {
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;
RenderTexture2D tex = GetChunkTexture(key);
float scale = (size)/(float)(tex.texture.width);
tex.texture.width *= (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));
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];
@ -39,7 +32,25 @@ void DEBUG_draw_ground(uint64_t key, entity_view * data) {
}
}
}break;
default:break;
}
}
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;
}
}
@ -47,12 +58,12 @@ void DEBUG_draw_ground(uint64_t key, entity_view * data) {
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: {
float x = data->x;
@ -64,12 +75,12 @@ 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) {
float ix = data->x;
float iy = data->y;
@ -77,9 +88,9 @@ void DEBUG_draw_entities(uint64_t key, entity_view * data) {
asset_id it_kind = data->items[data->selected_item].kind;
uint32_t qty = data->items[data->selected_item].quantity;
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,16 +98,16 @@ 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: {
float x = data->x - 32.f;
float y = data->y - 32.f;
DrawTexturePro(GetSpriteTexture2D(assets_find(data->asset)), ASSET_SRC_RECT(), ASSET_DST_RECT(x,y), (Vector2){0.5f,0.5f}, 0.0f, ALPHA(WHITE));
DrawTextEco(zpl_bprintf("%d", data->quantity), x, y, 10, ALPHA(RAYWHITE), 0.0f);
DrawTextEco(zpl_bprintf("%d", data->quantity), x, y, 10, ALPHA(RAYWHITE), 0.0f);
}break;
default:break;
}
@ -104,7 +115,7 @@ void DEBUG_draw_entities(uint64_t key, entity_view * data) {
void DEBUG_draw_entities_low(uint64_t key, entity_view * data) {
(void)key;
switch (data->kind) {
case EKIND_VEHICLE: {
float x = data->x;
@ -126,17 +137,21 @@ void renderer_draw_v0(void) {
render_camera.offset = (Vector2){(float)(screenWidth >> 1), (float)(screenHeight >> 1)};
render_camera.zoom = zpl_lerp(render_camera.zoom, target_zoom, GetFrameTime()*2.9978f);
camera_update();
camera game_camera = camera_get();
render_camera.target = (Vector2){(float)game_camera.x, (float)game_camera.y};
zoom_overlay_tran = zpl_lerp(zoom_overlay_tran, (target_zoom <= CAM_OVERLAY_ZOOM_LEVEL) ? 1.0f : 0.0f, GetFrameTime()*2.0f);
ClearBackground(GetColor(0x222034));
BeginMode2D(render_camera);
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();
}
@ -149,18 +164,18 @@ void renderer_init_v0(void) {
render_camera.offset = (Vector2){(float)(screenWidth >> 1), (float)(screenHeight >> 1)};
render_camera.rotation = 0.0f;
render_camera.zoom = 1.5f;
// NOTE(zaklaus): Paint the screen before we load the game
// TODO(zaklaus): Render a cool loading screen background maybe? :wink: :wink:
BeginDrawing();
ClearBackground(GetColor(0x222034));
char const *loading_text = "zpl.eco2d is loading...";
int text_w = MeasureText(loading_text, 120);
DrawText(loading_text, GetScreenWidth()-text_w-15, GetScreenHeight()-135, 120, RAYWHITE);
EndDrawing();
blocks_setup();
assets_setup();
}
@ -173,11 +188,11 @@ void renderer_shutdown_v0(void) {
void renderer_debug_draw_v0(void) {
BeginMode2D(render_camera);
debug_draw_queue *que = debug_draw_samples();
for (size_t i = 0; i < que->num_entries; i += 1) {
debug_draw_entry *e = &que->entries[i];
Color color = GetColor(e->color);
switch (e->kind) {
case DDRAW_LINE: {
float x = e->a.x;
@ -186,13 +201,13 @@ void renderer_debug_draw_v0(void) {
float y2 = e->b.y;
DrawLineV((Vector2){x, y}, (Vector2){x2, y2}, color);
}break;
case DDRAW_CIRCLE:{
float x = e->a.x;
float y = e->a.y;
DrawCircleLinesEco(x, y, e->radius, color);
}break;
case DDRAW_RECT:{
float x = e->bmin.x;
float y = e->bmin.y;
@ -200,23 +215,23 @@ void renderer_debug_draw_v0(void) {
float h = e->bmax.y - e->bmin.y;
DrawRectangleLinesEco(x, y, w, h, color);
}break;
default: {
}break;
}
}
EndMode2D();
}
void renderer_draw_single_v0(float x, float y, asset_id id, Color color) {
BeginMode2D(render_camera);
x -= 32.0f;
y -= 32.0f;
DrawTexturePro(GetSpriteTexture2D(assets_find(id)), ASSET_SRC_RECT(), ASSET_DST_RECT(x,y), (Vector2){0.5f,0.5f}, 0.0f, color);
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,8 +10,11 @@
#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); \
!defer_var; \
(defer_var += 1), e)
(defer_var += 1), e)

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

@ -9,47 +9,47 @@
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) {
static inline
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;
DrawTextEx(GetFontDefault(), text, position, (float)fontSize , (float)new_spacing , color);
float new_spacing = spacing == 0.0f ? fontSize/defaultFontSize : spacing;
DrawTextEx(GetFontDefault(), text, position, fontSize , new_spacing , color);
}
#endif
}
static inline
int MeasureTextEco(const char *text, int fontSize, float spacing) {
static inline
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;
vec = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)new_spacing);
float new_spacing = spacing == 0.0f ? fontSize/defaultFontSize : spacing;
vec = MeasureTextEx(GetFontDefault(), text, fontSize, (float)new_spacing);
}
return (int)vec.x;
return vec.x;
#else
return 0;
return 0.f;
#endif
}
static inline
static inline
void DrawCircleEco(float centerX, float centerY, float radius, Color color)
{
DrawCircleV((Vector2){ (float)centerX , (float)centerY }, radius , color);
}
static inline
static inline
void DrawRectangleEco(float posX, float posY, float width, float height, Color color)
{
DrawRectangleV((Vector2){ (float)posX , (float)posY }, (Vector2){ width , height }, color);
@ -85,68 +85,68 @@ void EcoDrawCube(Vector3 position, float width, float height, float length, floa
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
rlCheckRenderBatchLimit(36);
rlPushMatrix();
// NOTE: Transformation is applied in inverse order (scale -> rotate -> translate)
rlTranslatef(position.x, position.y, position.z);
rlRotatef(heading, 0, 1, 0);
//rlScalef(1.0f, 1.0f, 1.0f); // NOTE: Vertices are directly scaled on definition
rlBegin(RL_TRIANGLES);
rlColor4ub(color.r, color.g, color.b, color.a);
// Front face
rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left
rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right
rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left
rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Right
rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left
rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right
// Back face
rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Left
rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left
rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right
rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right
rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right
rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left
// Top face
rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left
rlVertex3f(x - width/2, y + height/2, z + length/2); // Bottom Left
rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right
rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right
rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left
rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right
// Bottom face
rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Left
rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right
rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left
rlVertex3f(x + width/2, y - height/2, z - length/2); // Top Right
rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right
rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Left
// Right face
rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right
rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right
rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left
rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left
rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right
rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left
// Left face
rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right
rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left
rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Right
rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left
rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left
rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right
@ -157,37 +157,43 @@ 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 '?'
int index = GetGlyphIndex(font, codepoint);
float scale = fontSize/(float)font.baseSize;
// Character destination rectangle on screen
// NOTE: We consider charsPadding on drawing
position.x += (float)(font.chars[index].offsetX - font.charsPadding)/(float)font.baseSize*scale;
position.z += (float)(font.chars[index].offsetY - font.charsPadding)/(float)font.baseSize*scale;
// Character source rectangle from font texture atlas
// NOTE: We consider chars padding when drawing, it could be required for outline/glow shader effects
Rectangle srcRec = { font.recs[index].x - (float)font.charsPadding, font.recs[index].y - (float)font.charsPadding,
font.recs[index].width + 2.0f*font.charsPadding, font.recs[index].height + 2.0f*font.charsPadding };
float width = (float)(font.recs[index].width + 2.0f*font.charsPadding)/(float)font.baseSize*scale;
float height = (float)(font.recs[index].height + 2.0f*font.charsPadding)/(float)font.baseSize*scale;
if (font.texture.id > 0)
{
const float x = 0.0f;
const float y = 0.0f;
const float z = 0.0f;
// normalized texture coordinates of the glyph inside the font texture (0.0f -> 1.0f)
const float tx = srcRec.x/font.texture.width;
const float ty = srcRec.y/font.texture.height;
const float tw = (srcRec.x+srcRec.width)/font.texture.width;
const float th = (srcRec.y+srcRec.height)/font.texture.height;
{
#if defined(RAYLIB_NEW_RLGL)
}
@ -199,17 +205,17 @@ void DrawTextCodepoint3D(Font font, int codepoint, Vector3 position, float fontS
#endif
rlPushMatrix();
rlTranslatef(position.x, position.y, position.z);
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
// Front Face
rlNormal3f(0.0f, 1.0f, 0.0f); // Normal Pointing Up
rlTexCoord2f(tx, ty); rlVertex3f(x, y, z); // Top Left Of The Texture and Quad
rlTexCoord2f(tx, th); rlVertex3f(x, y, z + height); // Bottom Left Of The Texture and Quad
rlTexCoord2f(tw, th); rlVertex3f(x + width, y, z + height); // Bottom Right Of The Texture and Quad
rlTexCoord2f(tw, ty); rlVertex3f(x + width, y, z); // Top Right Of The Texture and Quad
if (backface)
{
// Back Face
@ -221,7 +227,7 @@ void DrawTextCodepoint3D(Font font, int codepoint, Vector3 position, float fontS
}
rlEnd();
rlPopMatrix();
#if defined(RAYLIB_NEW_RLGL)
rlSetTexture(0);
#else
@ -234,23 +240,23 @@ void DrawTextCodepoint3D(Font font, int codepoint, Vector3 position, float fontS
void DrawText3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, Color tint) {
#if 0
int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop
float textOffsetY = 0.0f; // Offset between lines (on line break '\n')
float textOffsetX = 0.0f; // Offset X to next character to draw
float scale = fontSize/(float)font.baseSize;
for (int i = 0; i < length;)
{
// Get next codepoint from byte string and glyph index in font
int codepointByteCount = 0;
int codepoint = GetCodepoint(&text[i], &codepointByteCount);
int index = GetGlyphIndex(font, codepoint);
// NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
// but we need to draw all of the bad bytes using the '?' symbol moving one byte
if (codepoint == 0x3f) codepointByteCount = 1;
if (codepoint == '\n')
{
// NOTE: Fixed line spacing of 1.5 line-height
@ -264,46 +270,46 @@ void DrawText3D(Font font, const char *text, Vector3 position, float fontSize, f
{
DrawTextCodepoint3D(font, codepoint, (Vector3){ position.x + textOffsetX, position.y, position.z + textOffsetY }, fontSize, backface, tint);
}
if (font.chars[index].advanceX == 0) textOffsetX += (float)(font.recs[index].width + fontSpacing)/(float)font.baseSize*scale;
else textOffsetX += (float)(font.chars[index].advanceX + fontSpacing)/(float)font.baseSize*scale;
}
i += codepointByteCount; // Move text bytes counter to next codepoint
}
#endif
}
Vector3 MeasureText3D(Font font, const char* text, float fontSize, float fontSpacing, float lineSpacing) {
#if 0
#if 0
int len = TextLength(text);
int tempLen = 0; // Used to count longer text line num chars
int lenCounter = 0;
float tempTextWidth = 0.0f; // Used to count longer text line width
float scale = fontSize/(float)font.baseSize;
float textHeight = scale;
float textWidth = 0.0f;
int letter = 0; // Current character
int index = 0; // Index position in sprite font
for (int i = 0; i < len; i++)
{
lenCounter++;
int next = 0;
letter = GetCodepoint(&text[i], &next);
index = GetGlyphIndex(font, letter);
// NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
// but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1
if (letter == 0x3f) next = 1;
i += next - 1;
if (letter != '\n')
{
if (font.chars[index].advanceX != 0) textWidth += (font.chars[index].advanceX+fontSpacing)/(float)font.baseSize*scale;
@ -316,17 +322,17 @@ Vector3 MeasureText3D(Font font, const char* text, float fontSize, float fontSpa
textWidth = 0.0f;
textHeight += scale + lineSpacing/(float)font.baseSize*scale;
}
if (tempLen < lenCounter) tempLen = lenCounter;
}
if (tempTextWidth < textWidth) tempTextWidth = textWidth;
Vector3 vec = { 0 };
vec.x = tempTextWidth + (float)((tempLen - 1)*fontSpacing/(float)font.baseSize*scale); // Adds chars spacing to measure
vec.y = 0.25f;
vec.z = textHeight;
return vec;
#endif
Vector3 todo = {0};
@ -346,10 +352,10 @@ Color GenerateRandomColor(float s, float v) {
void DrawCircleLinesEco(float centerX, float centerY, float radius, Color color)
{
rlCheckRenderBatchLimit(2*36);
rlBegin(RL_LINES);
rlColor4ub(color.r, color.g, color.b, color.a);
// NOTE: Circle outline is drawn pixel by pixel every degree (0 to 360)
for (int i = 0; i < 360; i += 10)
{
@ -365,13 +371,13 @@ void DrawRectangleLinesEco(float posX, float posY, float width, float height, Co
rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(posX + 1, posY + 1);
rlVertex2f(posX + width, posY + 1);
rlVertex2f(posX + width, posY + 1);
rlVertex2f(posX + width, posY + height);
rlVertex2f(posX + width, posY + height);
rlVertex2f(posX + 1, posY + height);
rlVertex2f(posX + 1, posY + height);
rlVertex2f(posX + 1, posY + 1);
rlEnd();

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;
@ -29,4 +30,4 @@ void *blocks_get_img(block_id id);
void blocks_build_chunk_tex(uint64_t id, block_id *blocks, void *view);
void *blocks_get_chunk_tex(uint64_t id);
void blocks_remove_chunk_tex(uint64_t id);
void blocks_remove_chunk_tex(uint64_t id);

View File

@ -16,12 +16,12 @@ 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),
BLOCK(ASSET_BELT_UP, 0, '@', .drag = 1.0f , .friction = 1.0f, .vely = -150.0f),
BLOCK(ASSET_BELT_DOWN, 0, '@', .drag = 1.0f , .friction = 1.0f, .vely = 150.0f),
};
ZPL_STATIC_ASSERT(sizeof(blocks)/sizeof(block) < ZPL_U16_MAX, "too many registered blocks! (max. 65536)");
ZPL_STATIC_ASSERT(sizeof(blocks)/sizeof(block) < ZPL_U16_MAX, "too many registered blocks! (max. 65536)");

View File

@ -14,88 +14,90 @@
#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};
const Classify *classify = ecs_get(world_ecs(), e, Classify);
ZPL_ASSERT(classify);
view.kind = classify->id;
const Position *pos = ecs_get(world_ecs(), e, Position);
if (pos) {
view.x = pos->x;
view.y = pos->y;
}
const Velocity *vel = ecs_get(world_ecs(), e, Velocity);
if (vel) {
view.flag |= EFLAG_INTERP;
view.vx = vel->x;
view.vy = vel->y;
}
const Health *health = ecs_get(world_ecs(), e, Health);
if (health) {
view.hp = health->hp;
view.max_hp = health->max_hp;
}
if (ecs_get(world_ecs(), e, Vehicle)) {
Vehicle const* veh = ecs_get(world_ecs(), e, Vehicle);
view.heading = veh->heading;
}
if (ecs_get(world_ecs(), e, ItemDrop)) {
ItemDrop const* dr = ecs_get(world_ecs(), e, ItemDrop);
view.asset = dr->kind;
view.quantity = dr->quantity;
}
if (ecs_get(world_ecs(), e, Device)) {
Device const* dev = ecs_get(world_ecs(), e, Device);
view.asset = dev->asset;
}
view.inside_vehicle = ecs_get(world_ecs(), e, IsInVehicle) != 0 ? true : false;
Inventory *inv = 0;
if ((inv = ecs_get_mut_if(world_ecs(), e, Inventory))) {
view.has_items = true;
for (int i = 0; i < ITEMS_INVENTORY_SIZE; i += 1) {
view.items[i] = inv->items[i];
}
const Input *in = ecs_get(world_ecs(), e, Input);
if (in){
view.selected_item = in->selected_item;
view.pick_ent = (uint64_t)in->pick_ent;
view.sel_ent = (uint64_t)in->sel_ent;
if (world_entity_valid(in->storage_ent)){
ItemContainer *ic = 0;
if ((ic = ecs_get_mut_if(world_ecs(), in->storage_ent, ItemContainer))){
view.has_storage_items = true;
for (int i = 0; i < ITEMS_CONTAINER_SIZE; i += 1) {
view.storage_items[i] = ic->items[i];
}
view.storage_selected_item = in->storage_selected_item;
}
}
}
}
Chunk *chunk = 0;
if ((chunk = ecs_get_mut_if(world_ecs(), e, Chunk))) {
view.x = chunk->x;
@ -103,18 +105,18 @@ entity_view world_build_entity_view(int64_t e) {
view.blocks_used = 1;
view.is_dirty = chunk->is_dirty;
chunk->is_dirty = false;
for (int i = 0; i < world.chunk_size*world.chunk_size; i += 1) {
view.blocks[i] = world.block_mapping[chunk->id][i];
}
for (int i = 0; i < world.chunk_size*world.chunk_size; i += 1) {
view.outer_blocks[i] = world.outer_block_mapping[chunk->id][i];
}
}
world_snapshot_set(&streamer_snapshot, e, view);
return view;
return world_snapshot_get(&streamer_snapshot, e);
}
int32_t tracker_write_create(librg_world *w, librg_event *e) {
@ -127,7 +129,7 @@ int32_t tracker_write_create(librg_world *w, librg_event *e) {
#endif
size_t actual_length = librg_event_size_get(w, e);
char *buffer = librg_event_buffer_get(w, e);
return (int32_t)entity_view_pack_struct(buffer, actual_length, world_build_entity_view(entity_id));
}
@ -147,24 +149,24 @@ 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;
}
}
// 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;
}
}
#endif
return (int32_t)entity_view_pack_struct(buffer, actual_length, view);
}
@ -187,15 +189,15 @@ void world_chunk_setup_grid(void) {
world.outer_block_mapping[i] = zpl_malloc(sizeof(block_id)*zpl_square(world.chunk_size));
chunk->id = i;
chunk->is_dirty = false;
for (int y = 0; y < world.chunk_size; y += 1) {
for (int x = 0; x < world.chunk_size; x += 1) {
int chk_x = chunk->x * world.chunk_size;
int chk_y = chunk->y * world.chunk_size;
block_id *c = &world.block_mapping[i][(y*world.chunk_size)+x];
*c = world.data[(chk_y+y)*world.dim + (chk_x+x)];
c = &world.outer_block_mapping[i][(y*world.chunk_size)+x];
*c = world.outer_data[(chk_y+y)*world.dim + (chk_x+x)];
}
@ -206,14 +208,14 @@ void world_chunk_setup_grid(void) {
static inline
void world_configure_tracker(void) {
world.tracker = librg_world_create();
ZPL_ASSERT_MSG(world.tracker, "[ERROR] An error occurred while trying to create a server world.");
/* config our world grid */
librg_config_chunksize_set(world.tracker, WORLD_BLOCK_SIZE * world.chunk_size, WORLD_BLOCK_SIZE * world.chunk_size, 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);
librg_event_set(world.tracker, LIBRG_WRITE_CREATE, tracker_write_create);
librg_event_set(world.tracker, LIBRG_WRITE_REMOVE, tracker_write_remove);
librg_event_set(world.tracker, LIBRG_WRITE_UPDATE, tracker_write_update);
@ -223,14 +225,14 @@ static inline
void world_init_worldgen_data(void) {
world.data = zpl_malloc(sizeof(block_id)*world.size);
world.outer_data = zpl_malloc(sizeof(block_id)*world.size);
ZPL_ASSERT(world.data && world.outer_data);
}
static inline
void world_setup_ecs(void) {
world.ecs = ecs_init();
ECS_IMPORT(world.ecs, Components);
ECS_IMPORT(world.ecs, Systems);
world.ecs_update = ecs_query_new(world.ecs, "components.ClientInfo, components.Position");
@ -249,7 +251,7 @@ static inline
void world_generate_instance(void) {
int32_t world_build_status = worldgen_test(&world);
ZPL_ASSERT(world_build_status >= 0);
for (int i = 0; i < zpl_square(world.dim); ++i) {
if (world.data[i] == 0) {
ZPL_PANIC("Worldgen failure! Block %d is unset!\n", i);
@ -271,10 +273,10 @@ int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount) {
world.seed = seed;
world.chunk_size = chunk_size;
world.chunk_amount = chunk_amount;
world.dim = (world.chunk_size * world.chunk_amount);
world.size = world.dim * world.dim;
world_configure_tracker();
world_setup_ecs();
world_init_worldgen_data();
@ -282,9 +284,9 @@ int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount) {
world_init_mapping();
world_chunk_setup_grid();
world_free_worldgen_data();
zpl_printf("[INFO] Created a new server world\n");
return WORLD_ERROR_NONE;
}
@ -307,35 +309,35 @@ 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);
static char buffer[WORLD_LIBRG_BUFSIZ] = {0};
world.active_layer_id = ticker;
while (ecs_query_next(&it)) {
ClientInfo *p = ecs_field(&it, ClientInfo, 1);
for (int i = 0; i < it.count; i++) {
size_t datalen = WORLD_LIBRG_BUFSIZ;
if (!p[i].active)
continue;
int32_t result = librg_world_write(world_tracker(), it.entities[i], radius, buffer, &datalen, NULL);
if (result > 0) {
zpl_printf("[info] buffer size was not enough, please increase it by at least: %d\n", result);
} else if (result < 0) {
zpl_printf("[error] an error happened writing the world %d\n", result);
}
pkt_send_librg_update((uint64_t)p[i].peer, p[i].view_id, ticker, buffer, datalen);
}
}
// NOTE(zaklaus): clear out our streaming snapshot
// TODO(zaklaus): move this to zpl
{
@ -349,21 +351,23 @@ int32_t world_update() {
profile (PROF_UPDATE_SYSTEMS) {
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;
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;
float fast_ms = WORLD_TRACKER_UPDATE_MP_FAST_MS;
float normal_ms = WORLD_TRACKER_UPDATE_MP_NORMAL_MS;
float slow_ms = WORLD_TRACKER_UPDATE_MP_SLOW_MS;
#if 1
if (game_get_kind() == GAMEKIND_SINGLE) {
fast_ms = WORLD_TRACKER_UPDATE_FAST_MS;
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);
world_tracker_update(2, slow_ms, 3);
entity_update_action_timers();
debug_replay_update();
return 0;
@ -459,11 +463,11 @@ world_block_lookup world_block_from_realpos(float x, float y) {
int32_t size = world.chunk_size * WORLD_BLOCK_SIZE;
int16_t chunk_x, chunk_y;
librg_chunk_to_chunkpos(world.tracker, chunk_id, &chunk_x, &chunk_y, NULL);
// NOTE(zaklaus): pos relative to chunk
float chx = x - chunk_x * size;
float chy = y - chunk_y * size;
uint16_t bx = (uint16_t)chx / WORLD_BLOCK_SIZE;
uint16_t by = (uint16_t)chy / WORLD_BLOCK_SIZE;
uint16_t block_idx = (by*world.chunk_size)+bx;
@ -473,11 +477,11 @@ world_block_lookup world_block_from_realpos(float x, float y) {
bid = world.block_mapping[chunk_id][block_idx];
is_outer = false;
}
// NOTE(zaklaus): pos relative to block's center
float box = chx - bx * WORLD_BLOCK_SIZE - WORLD_BLOCK_SIZE/2.0f;
float boy = chy - by * WORLD_BLOCK_SIZE - WORLD_BLOCK_SIZE/2.0f;
world_block_lookup lookup = {
.id = block_idx,
.bid = bid,
@ -487,7 +491,7 @@ world_block_lookup world_block_from_realpos(float x, float y) {
.oy = boy,
.is_outer = is_outer,
};
return lookup;
}
@ -495,15 +499,16 @@ void world_chunk_destroy_block(float x, float y, bool drop_item) {
world_block_lookup l = world_block_from_realpos(x, y);
if (blocks_get_flags(l.bid) & BLOCK_FLAG_ESSENTIAL) return;
world_chunk_replace_block(l.chunk_id, l.id, 0);
if (l.is_outer && l.bid > 0 && drop_item) {
asset_id item_asset = blocks_get_asset(l.bid);
if (item_find(item_asset) == ASSET_INVALID) return;
uint64_t e = item_spawn(item_asset, 1);
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = x;
dest->y = y;
entity_set_position(e, dest->x, dest->y);
}
}
@ -512,14 +517,14 @@ world_block_lookup world_block_from_index(int64_t id, uint16_t block_idx) {
if (bid == 0) {
bid = world.block_mapping[id][block_idx];
}
world_block_lookup lookup = {
.id = block_idx,
.bid = bid,
.chunk_id = id,
.chunk_e = world.chunk_mapping[id],
};
return lookup;
}

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

@ -25,40 +25,44 @@ void IntegratePositions(ecs_iter_t *it) {
profile(PROF_INTEGRATE_POS) {
Position *p = ecs_field(it, Position, 1);
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);
}
}
// 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));
}
#endif
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 b = {p[i].x+v[i].x, p[i].y+v[i].y};
@ -68,32 +72,18 @@ 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
void HurtOnHazardBlock(ecs_iter_t *it) {
Position *p = ecs_field(it, Position, 1);
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);
}
@ -107,7 +97,7 @@ void HurtOnHazardBlock(ecs_iter_t *it) {
void RegenerateHP(ecs_iter_t *it) {
Health *h = ecs_field(it, Health, 1);
for (int i = 0; i < it->count; i++) {
if (h[i].pain_time < 0.0f) {
if (h[i].heal_time < 0.0f && h[i].hp < h[i].max_hp) {
@ -126,7 +116,7 @@ void RegenerateHP(ecs_iter_t *it) {
void ResetActivators(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
for (int i = 0; i < it->count; i++) {
in[i].use = false;
in[i].swap = false;
@ -139,8 +129,12 @@ void ResetActivators(ecs_iter_t *it) {
void ApplyWorldDragOnVelocity(ecs_iter_t *it) {
Position *p = ecs_field(it, Position, 1);
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);
@ -148,7 +142,7 @@ void ApplyWorldDragOnVelocity(ecs_iter_t *it) {
float vely = blocks_get_vely(lookup.bid);
v[i].x = zpl_lerp(v[i].x, zpl_max(0.0f, zpl_abs(velx))*zpl_sign(velx), PHY_WALK_DRAG*drag*friction*safe_dt(it));
v[i].y = zpl_lerp(v[i].y, zpl_max(0.0f, zpl_abs(vely))*zpl_sign(vely), PHY_WALK_DRAG*drag*friction*safe_dt(it));
if ( zpl_abs(v[i].x) > ENTITY_ACTION_VELOCITY_THRESHOLD
|| zpl_abs(v[i].y) > ENTITY_ACTION_VELOCITY_THRESHOLD) {
entity_wake(it->entities[i]);
@ -160,18 +154,18 @@ void ApplyWorldDragOnVelocity(ecs_iter_t *it) {
void PlayerClosestInteractable(ecs_iter_t *it){
Input *in = ecs_field(it, Input, 1);
for (int i = 0; i < it->count; ++i) {
size_t ents_count;
int64_t *ents = world_chunk_fetch_entities_realpos(in[i].bx, in[i].by, &ents_count);
ecs_entity_t closest_pick = 0;
float min_pick = ZPL_F32_MAX;
for (size_t j = 0; j < ents_count; j++) {
const Position *p2 = ecs_get(it->world, ents[j], Position);
if (!p2) continue;
float dx = p2->x - in[i].bx;
float dy = p2->y - in[i].by;
float range = zpl_sqrt(dx*dx + dy*dy);
@ -180,9 +174,9 @@ void PlayerClosestInteractable(ecs_iter_t *it){
closest_pick = ents[j];
}
}
in[i].pick_ent = closest_pick;
if (in[i].pick)
in[i].sel_ent = (in[i].sel_ent == closest_pick) ? 0 : closest_pick;
}
@ -199,21 +193,21 @@ void DisableWorldEdit(ecs_iter_t *it) {
void SystemsImport(ecs_world_t *ecs) {
ECS_MODULE(ecs, Systems);
ECS_SYSTEM(ecs, EnableWorldEdit, EcsOnLoad);
ECS_SYSTEM(ecs, MovementImpulse, EcsOnLoad, components.Input, components.Velocity, components.Position, !components.IsInVehicle);
ECS_SYSTEM(ecs, DemoNPCMoveAround, EcsOnLoad, components.Velocity, components.DemoNPC);
ECS_SYSTEM(ecs, ApplyWorldDragOnVelocity, EcsOnUpdate, components.Position, components.Velocity);
ECS_SYSTEM(ecs, HurtOnHazardBlock, EcsOnUpdate, components.Position, components.Health);
ECS_SYSTEM(ecs, RegenerateHP, EcsOnUpdate, components.Health);
ECS_SYSTEM(ecs, VehicleHandling, EcsOnUpdate, components.Vehicle, components.Position, components.Velocity);
ECS_SYSTEM(ecs, IntegratePositions, EcsOnValidate, components.Position, components.Velocity);
ECS_SYSTEM(ecs, EnterVehicle, EcsPostUpdate, components.Input, components.Position, !components.IsInVehicle);
ECS_SYSTEM(ecs, LeaveVehicle, EcsPostUpdate, components.Input, components.IsInVehicle, components.Velocity);
ECS_SYSTEM(ecs, PlayerClosestInteractable, EcsPostUpdate, components.Input);
ECS_SYSTEM(ecs, PickItem, EcsPostUpdate, components.Input, components.Position, components.Inventory, !components.IsInVehicle);
ECS_SYSTEM(ecs, DropItem, EcsPostUpdate, components.Input, components.Position, components.Inventory, !components.IsInVehicle);
@ -222,13 +216,11 @@ void SystemsImport(ecs_world_t *ecs) {
ECS_SYSTEM(ecs, UseItem, EcsPostUpdate, components.Input, components.Position, components.Inventory, !components.IsInVehicle);
ECS_SYSTEM(ecs, InspectContainers, EcsPostUpdate, components.Input, !components.IsInVehicle);
ECS_SYSTEM(ecs, HarvestIntoContainers, EcsPostUpdate, components.ItemContainer, components.Position);
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")

1868
code/vendors/librg.h vendored

File diff suppressed because it is too large Load Diff

33531
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
)