pkt: action-based entity stream throttling

isolation_bkp/dynres
Dominik Madarász 2022-08-01 11:19:52 +02:00
parent 28987c36d9
commit c7d251eb44
10 changed files with 150 additions and 71 deletions

View File

@ -10,9 +10,10 @@
uint64_t entity_spawn(uint16_t class_id) {
ecs_entity_t e = ecs_new(world_ecs(), 0);
ecs_set(world_ecs(), e, Classify, { .id = class_id });
entity_wake(e);
if (class_id != EKIND_SERVER) {
ecs_set(world_ecs(), e, Velocity, {0});
Position *pos = ecs_get_mut(world_ecs(), e, Position);
@ -23,12 +24,12 @@ uint64_t entity_spawn(uint16_t class_id) {
pos->x=350.0f;
pos->y=88.0f;
#endif
librg_entity_track(world_tracker(), e);
librg_entity_chunk_set(world_tracker(), e, librg_chunk_from_realpos(world_tracker(), pos->x, pos->y, 0));
librg_entity_owner_set(world_tracker(), e, (int64_t)e);
}
return (uint64_t)e;
}
@ -43,3 +44,39 @@ void entity_despawn(uint64_t ent_id) {
librg_entity_untrack(world_tracker(), ent_id);
ecs_delete(world_ecs(), ent_id);
}
void entity_wake(uint64_t ent_id) {
StreamInfo *si = ecs_get_mut(world_ecs(), ent_id, StreamInfo);
si->tick_delay = 0.0f;
si->last_update = 0.0f;
}
static ecs_query_t *ecs_streaminfo = NULL;
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();
}
ecs_iter_t it = ecs_query_iter(world_ecs(), ecs_streaminfo);
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;
}
}
}
last_update_time = zpl_time_rel();
}
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());
}

View File

@ -1,6 +1,13 @@
#pragma once
#include "system.h"
#define ENTITY_ACTION_VELOCITY_THRESHOLD 0.05f
uint64_t entity_spawn(uint16_t class_id /* 0 = no streaming */);
void entity_batch_despawn(uint64_t *ids, size_t num_ids);
void entity_despawn(uint64_t ent_id);
// NOTE(zaklaus): action-based entity stream throttling
void entity_wake(uint64_t ent_id);
void entity_update_action_timers();
bool entity_can_stream(uint64_t ent_id);

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,18 +58,18 @@ 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++;
} else {
return;
}
}
}
// NOTE(zaklaus): This is an inner layer block, we can't build over it if it has a collision!
else if (l.bid > 0 && blocks_get_flags(l.bid) & (BLOCK_FLAG_COLLISION|BLOCK_FLAG_ESSENTIAL)) {
return;
}
}
world_chunk_replace_block(l.chunk_id, l.id, blocks_find(desc->place.kind + (asset_id)udata));
it->quantity--;
}break;

View File

@ -10,6 +10,7 @@
#include "platform.h"
#include "profiler.h"
#include "game.h"
#include "entity.h"
#include "packets/pkt_send_librg_update.h"
@ -134,6 +135,13 @@ int32_t tracker_write_update(librg_world *w, librg_event *e) {
}
}
// NOTE(zaklaus): action-based updates
{
if (view.kind != EKIND_CHUNK && !entity_can_stream(entity_id)) {
return LIBRG_WRITE_REJECT;
}
}
return (int32_t)entity_view_pack_struct(buffer, actual_length, view);
}
@ -333,6 +341,7 @@ int32_t world_update() {
world_tracker_update(1, normal_ms, 2);
world_tracker_update(2, slow_ms, 3);
entity_update_action_timers();
debug_replay_update();
return 0;
}

View File

@ -14,6 +14,7 @@ ECS_COMPONENT_DECLARE(IsInVehicle);
ECS_COMPONENT_DECLARE(ItemDrop);
ECS_COMPONENT_DECLARE(Inventory);
ECS_COMPONENT_DECLARE(DemoNPC);
ECS_COMPONENT_DECLARE(StreamInfo);
void ComponentsImport(ecs_world_t *ecs) {
ECS_MODULE(ecs, Components);
@ -32,4 +33,5 @@ void ComponentsImport(ecs_world_t *ecs) {
ECS_COMPONENT_DEFINE(ecs, ItemDrop);
ECS_COMPONENT_DEFINE(ecs, Inventory);
ECS_COMPONENT_DEFINE(ecs, DemoNPC);
ECS_COMPONENT_DEFINE(ecs, StreamInfo);
}

View File

@ -96,6 +96,11 @@ typedef struct {
float pickup_time;
} Inventory;
typedef struct {
double last_update;
double tick_delay;
} StreamInfo;
typedef struct {char _unused;} DemoNPC;
extern ECS_COMPONENT_DECLARE(Vector2D);
@ -112,5 +117,6 @@ extern ECS_COMPONENT_DECLARE(IsInVehicle);
extern ECS_COMPONENT_DECLARE(ItemDrop);
extern ECS_COMPONENT_DECLARE(Inventory);
extern ECS_COMPONENT_DECLARE(DemoNPC);
extern ECS_COMPONENT_DECLARE(StreamInfo);
void ComponentsImport(ecs_world_t *ecs);

View File

@ -114,6 +114,7 @@ void RegenerateHP(ecs_iter_t *it) {
h[i].heal_time = HP_REGEN_TIME;
h[i].hp += HP_REGEN_RECOVERY;
h[i].hp = zpl_min(h[i].max_hp, h[i].hp);
entity_wake(it->entities[i]);
} else {
h[i].heal_time -= safe_dt(it);
}

View File

@ -8,17 +8,17 @@
void PickItem(ecs_iter_t *it) {
Position *p = ecs_field(it, Position, 2);
Inventory *inv = ecs_field(it, Inventory, 3);
for (int i = 0; i < it->count; i++) {
if (inv[i].pickup_time > game_time()) continue;
size_t ents_count;
int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 2);
for (size_t j = 0; j < ents_count; j++) {
ItemDrop *drop = 0;
if ((drop = ecs_get_mut_if(it->world, ents[j], ItemDrop))) {
Position *p2 = ecs_get_mut(it->world, ents[j], Position);
float dx = p2->x - p[i].x;
float dy = p2->y - p[i].y;
float range = zpl_sqrt(dx*dx + dy*dy);
@ -33,7 +33,8 @@ void PickItem(ecs_iter_t *it) {
item->quantity += picked_count;
drop->quantity -= picked_count;
item->kind = drop->kind;
entity_wake(ents[j]);
if (drop->quantity == 0)
item_despawn(ents[j]);
break;
@ -42,6 +43,7 @@ void PickItem(ecs_iter_t *it) {
} else if (range <= ITEM_ATTRACT_RADIUS) {
p2->x = zpl_lerp(p2->x, p[i].x, ITEM_ATTRACT_FORCE*it->delta_time);
p2->y = zpl_lerp(p2->y, p[i].y, ITEM_ATTRACT_FORCE*it->delta_time);
entity_wake(ents[j]);
}
}
}
@ -55,45 +57,45 @@ void DropItem(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
Position *p = ecs_field(it, Position, 2);
Inventory *inv = ecs_field(it, Inventory, 3);
for (int i = 0; i < it->count; i++) {
if (!in[i].drop) continue;
ItemDrop *item = &inv[i].items[in[i].selected_item];
if (item->quantity <= 0)
continue;
uint32_t dropped_count = item->quantity;
if (in[i].sprint) {
dropped_count /= 2;
} else if (in[i].ctrl) {
dropped_count = dropped_count > 0 ? 1 : 0;
}
if (dropped_count == 0)
continue;
ecs_entity_t te = item_spawn(item->kind, dropped_count);
item->quantity -= dropped_count;
ItemDrop *d = ecs_get_mut(world_ecs(), te, ItemDrop);
*d = (ItemDrop){
.kind = item->kind,
.quantity = dropped_count,
.merger_time = game_time() + ITEM_DROP_MERGER_TIME,
};
Position *ipos = ecs_get_mut(it->world, te, Position);
*ipos = p[i];
Velocity *v = ecs_get_mut(it->world, te, Velocity);
v->x = in[i].mx * 800.0f;
v->y = in[i].my * 800.0f;
inv[i].pickup_time = game_time() + ITEM_DROP_PICKUP_TIME;
in[i].drop = false;
if (item->quantity == 0) {
item->kind = 0;
}
@ -103,24 +105,24 @@ void DropItem(ecs_iter_t *it) {
void MergeItems(ecs_iter_t *it) {
Position *p = ecs_field(it, Position, 1);
ItemDrop *id = ecs_field(it, ItemDrop, 2);
for (int i = 0; i < it->count; i += 1) {
ItemDrop *item = &id[i];
if (item->merger_time < game_time())
continue;
size_t ents_count;
int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 1);
for (size_t j = 0; j < ents_count; j++) {
ItemDrop *drop = 0;
if ((drop = ecs_get_mut_if(it->world, ents[j], ItemDrop))) {
if (drop->kind != item->kind || (ecs_entity_t)ents[j] == it->entities[i] || drop->quantity == 0 || item->quantity == 0)
continue;
Position const* p2 = ecs_get(it->world, ents[j], Position);
float dx = p2->x - (p[i].x);
float dy = p2->y - (p[i].y);
float range = zpl_sqrt(dx*dx + dy*dy);
@ -137,14 +139,14 @@ void MergeItems(ecs_iter_t *it) {
void SwapItems(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
Inventory *inv = ecs_field(it, Inventory, 2);
for (int i = 0; i < it->count; i++) {
if (!in[i].swap) continue;
ItemDrop *to = &inv[i].items[in[i].swap_to];
ItemDrop *from = &inv[i].items[in[i].swap_from];
uint16_t to_id = item_find(to->kind);
if (to == from) {
// NOTE(zaklaus): do nothing
} else if (to->kind == from->kind && to->quantity > 0) {
@ -157,7 +159,7 @@ void SwapItems(ecs_iter_t *it) {
swapped_count = zpl_clamp(swapped_count, 0, item_max_quantity(to_id) - to->quantity);
to->quantity += swapped_count;
from->quantity -= swapped_count;
if (swapped_count == 0) {
ItemDrop tmp = *to;
*to = *from;
@ -177,7 +179,7 @@ void SwapItems(ecs_iter_t *it) {
*to = *from;
*from = tmp;
}
in[i].swap = false;
}
}
@ -186,15 +188,15 @@ void UseItem(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
Position *p = ecs_field(it, Position, 2);
Inventory *inv = ecs_field(it, Inventory, 3);
for (int i = 0; i < it->count; i++) {
if (!in[i].use && !in[i].num_placements) continue;
ItemDrop *item = &inv[i].items[in[i].selected_item];
if (!item || item->quantity <= 0) continue;
uint16_t item_id = item_find(item->kind);
item_usage usage = item_get_usage(item_id);
if (in[i].use && usage > UKIND_END_PLACE)
item_use(it->world, item, p[i], 0);
else if (in[i].num_placements > 0 && usage < UKIND_END_PLACE) {
@ -214,13 +216,15 @@ void UseItem(ecs_iter_t *it) {
// NOTE(zaklaus): ensure we pick the first variant
ofs = 1;
}
for (size_t j = 0; j < in[i].num_placements; j++) {
Position pos = {.x = in[i].placements_x[j], .y = in[i].placements_y[j]};
item_use(it->world, item, pos, ofs);
}
in[i].num_placements = 0;
}
entity_wake(it->entities[i]);
}
}

View File

@ -1,3 +1,5 @@
#include "entity.h"
#define PLR_MOVE_SPEED 800.0f
#define PLR_MOVE_SPEED_MULT 1.5f
@ -5,12 +7,17 @@ void MovementImpulse(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
Velocity *v = ecs_field(it, Velocity, 2);
Position *p = ecs_field(it, Position, 3);
for (int i = 0; i < it->count; i++) {
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 speed = PLR_MOVE_SPEED * (in[i].sprint ? PLR_MOVE_SPEED_MULT : 1.0f);
v[i].x += in[i].x*speed*drag*safe_dt(it);
v[i].y -= in[i].y*speed*drag*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]);
}
}
}

View File

@ -1,4 +1,5 @@
#include "debug_draw.h"
#include "entity.h"
#define VEH_ENTER_RADIUS 45.0f
@ -6,10 +7,10 @@ void LeaveVehicle(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
IsInVehicle *vehp = ecs_field(it, IsInVehicle, 2);
Velocity *v = ecs_field(it, Velocity, 3);
for (int i = 0; i < it->count; i++) {
if (!in[i].use) continue;
Vehicle *veh = 0;
if ((veh = ecs_get_mut_if(it->world, vehp->veh, Vehicle))) {
for (int k = 0; k < 4; k++) {
@ -18,10 +19,10 @@ void LeaveVehicle(ecs_iter_t *it) {
break;
}
}
in[i].use = false;
ecs_remove(it->world, it->entities[i], IsInVehicle);
// NOTE(zaklaus): push passenger out
{
float px = zpl_cos(veh->heading)*400.0f;
@ -38,29 +39,29 @@ void LeaveVehicle(ecs_iter_t *it) {
void EnterVehicle(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
Position *p = ecs_field(it, Position, 2);
for (int i = 0; i < it->count; i++) {
if (!in[i].use) continue;
size_t ents_count;
int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 2);
bool has_entered_veh = false;
for (size_t j = 0; j < ents_count; j++) {
Vehicle *veh = 0;
if (has_entered_veh) break;
if ((veh = ecs_get_mut_if(it->world, ents[j], Vehicle))) {
Position const* p2 = ecs_get(it->world, ents[j], Position);
float dx = p2->x - p[i].x;
float dy = p2->y - p[i].y;
float range = zpl_sqrt(dx*dx + dy*dy);
if (range <= VEH_ENTER_RADIUS) {
for (int k = 0; k < 4; k++) {
if (veh->seats[k] != 0) continue;
// NOTE(zaklaus): We can enter the vehicle, yay!
veh->seats[k] = it->entities[i];
ecs_set(it->world, it->entities[i], IsInVehicle, {
@ -90,27 +91,27 @@ void VehicleHandling(ecs_iter_t *it) {
Vehicle *veh = ecs_field(it, Vehicle, 1);
Position *p = ecs_field(it, Position, 2);
Velocity *v = ecs_field(it, Velocity, 3);
for (int i = 0; i < it->count; i++) {
Vehicle *car = &veh[i];
for (int j = 0; j < 4; j++) {
// NOTE(zaklaus): Perform seat cleanup
if (!world_entity_valid(veh[i].seats[j])) {
veh[i].seats[j] = 0;
continue;
}
ecs_entity_t pe = veh[i].seats[j];
// NOTE(zaklaus): Handle driver input
if (j == 0) {
Input const* in = ecs_get(it->world, pe, Input);
car->force += zpl_lerp(0.0f, in->y * VEHICLE_FORCE, (zpl_sign(in->y) == zpl_sign(car->force) ? 1.0f : 3.0f) * VEHICLE_ACCEL*safe_dt(it));
if (in->sprint) {
car->force = zpl_lerp(car->force, 0.0f, VEHICLE_BRAKE_FORCE*safe_dt(it));
if (zpl_abs(car->force) < 5.5f)
car->force = 0.0f;
}
@ -120,58 +121,63 @@ void VehicleHandling(ecs_iter_t *it) {
car->steer = zpl_clamp(car->steer, -60.0f, 60.0f);
}
}
car->force = zpl_clamp(car->force, car->reverse_speed, car->speed);
// NOTE(zaklaus): Vehicle physics
float fr_x = p[i].x + (car->wheel_base/2.0f) * zpl_cos(car->heading);
float fr_y = p[i].y + (car->wheel_base/2.0f) * zpl_sin(car->heading);
float bk_x = p[i].x - (car->wheel_base/2.0f) * zpl_cos(car->heading);
float bk_y = p[i].y - (car->wheel_base/2.0f) * zpl_sin(car->heading);
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);
bk_x += car->force * drag * zpl_cos(car->heading) * safe_dt(it)*VEHICLE_POWER;
bk_y += car->force * drag * zpl_sin(car->heading) * safe_dt(it)*VEHICLE_POWER;
fr_x += car->force * drag * zpl_cos(car->heading + zpl_to_radians(car->steer)) * safe_dt(it)*VEHICLE_POWER;
fr_y += car->force * drag * zpl_sin(car->heading + zpl_to_radians(car->steer)) * safe_dt(it)*VEHICLE_POWER;
v[i].x += ((fr_x + bk_x) / 2.0f - p[i].x);
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));
uint32_t flags = blocks_get_flags(lookahead.bid);
if (flags & BLOCK_FLAG_COLLISION) {
car->force = 0.0f;
}
for (int j = 0; j < 4; j++) {
if (!world_entity_valid(veh[i].seats[j])) continue;
ecs_entity_t pe = veh[i].seats[j];
// 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];
*v2 = v[i];
entity_wake(pe);
}
}
if (zpl_abs(car->force) > ENTITY_ACTION_VELOCITY_THRESHOLD) {
entity_wake(it->entities[i]);
}
{
debug_v2 b2 = {p[i].x + zpl_cos(car->heading)*(car->wheel_base), p[i].y + zpl_sin(car->heading)*(car->wheel_base)};
debug_push_line((debug_v2){p[i].x, p[i].y}, b2, 0x0000FFFF);
// NOTE(zaklaus): force
{
float dx = zpl_cos(car->heading);
float dy = zpl_sin(car->heading);
debug_push_circle((debug_v2){p[i].x+dx*car->force, p[i].y+dy*car->force}, 5.0f, 0x00FF00FF);
}
// NOTE(zaklaus): steer
{
float dx = zpl_sin(car->heading);
@ -185,7 +191,7 @@ void VehicleHandling(ecs_iter_t *it) {
void ClearVehicle(ecs_iter_t *it) {
Vehicle *veh = ecs_field(it, Vehicle, 1);
for (int i = 0; i < it->count; i++) {
for (int k = 0; k < 4; k++) {
if (world_entity_valid(veh[i].seats[k])) {