summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/content.h106
-rw-r--r--include/vec.h6
-rw-r--r--src/client.c137
-rw-r--r--src/server.c52
4 files changed, 221 insertions, 80 deletions
diff --git a/include/content.h b/include/content.h
index 664c84e..9d35dc8 100644
--- a/include/content.h
+++ b/include/content.h
@@ -7,9 +7,13 @@
#include <stdint.h>
#include <stdbool.h>
+#include "ser.h"
#include "vec.h"
-typedef enum {
+#define SIGHT_RANGE 10
+#define PKT_NODES_MAX (50*50)
+
+typedef enum : uint16_t {
N_VALLEY_FLOWER,
N_MOUNTAIN_FLOWER,
N_BIG_TREE,
@@ -22,16 +26,31 @@ typedef enum {
N_SAND,
} node_type;
+#define ser_node_type ser_u16
+#define deser_node_type deser_u16
+
typedef struct {
uint8_t r, g, b;
} color;
-static inline uint32_t color_to_u32(color c)
+[[maybe_unused]] static void ser_color(strbuf *w, color c)
+{
+ ser_u8(w, c.r);
+ ser_u8(w, c.g);
+ ser_u8(w, c.b);
+}
+
+[[maybe_unused]] static bool deser_color(str *r, color *c)
+{
+ return deser_u8(r, &c->r) && deser_u8(r, &c->g) && deser_u8(r, &c->b);
+}
+
+[[maybe_unused]] static uint32_t color_to_u32(color c)
{
return ((uint32_t) c.b) | (((uint32_t) c.g) << 8) | (((uint32_t) c.r) << 16);
}
-static inline color color_from_u32(uint32_t u)
+[[maybe_unused]] static color color_from_u32(uint32_t u)
{
return (color) { (u >> 16) & 0xFF, (u >> 8) & 0xFF, u & 0xFF };
}
@@ -43,51 +62,92 @@ typedef struct {
color col;
} node;
-#define SIGHT_RANGE 10
-#define NODES_PKT_MAX (50*50)
+[[maybe_unused]] static void ser_node(strbuf *w, node *n)
+{
+ bool present = n != NULL && n->present;
+ ser_bool(w, present);
+ if (!present)
+ return;
+ ser_node_type(w, n->type);
+ ser_i8(w, n->z);
+ ser_color(w, n->col);
+}
+
+[[maybe_unused]] static bool deser_node(str *r, node *n)
+{
+ if (!deser_bool(r, &n->present)) return false;
+ if (!n->present) return true;
+
+ return deser_node_type(r, &n->type)
+ && deser_i8(r, &n->z)
+ && deser_color(r, &n->col);
+}
-typedef enum {
+typedef enum : uint8_t {
DIR_RIGHT = 0,
DIR_UP,
DIR_LEFT,
DIR_DOWN,
} dir;
-static inline vec2 dir_to_vec2(dir d)
+#define ser_dir ser_u8
+#define deser_dir deser_u8
+
+[[maybe_unused]] static vec2 dir_to_vec2(dir d)
{
switch (d) {
- case DIR_RIGHT: return VEC2(0, 1);
- case DIR_UP: return VEC2(-1, 0);
- case DIR_LEFT: return VEC2(0, -1);
- case DIR_DOWN: return VEC2(1, 0);
+ case DIR_RIGHT: return VEC2(1, 0);
+ case DIR_UP: return VEC2(0, -1);
+ case DIR_LEFT: return VEC2(-1, 0);
+ case DIR_DOWN: return VEC2(0, 1);
+ default: return VEC2(0, 0);
}
}
-typedef uint8_t fail_reason;
+typedef enum : uint8_t {
+ FAIL_WRONG_PASS = 0,
+ FAIL_ALREADY_ONLINE,
+} fail_reason;
+
#define ser_fail_reason ser_u8
#define deser_fail_reason deser_u8
-enum {
- FAIL_WRONG_PASS = 0,
- FAIL_ALREADY_ONLINE,
-};
+typedef uint64_t entity_id;
+#define ser_entity_id ser_u64
+#define deser_entity_id deser_u64
-typedef uint16_t pkt_type;
-#define ser_pkt_type ser_u16
-#define deser_pkt_type deser_u16
+typedef enum : uint16_t {
+ ENTITY_PLAYER
+} entity_type;
+
+#define ser_entity_type ser_u16
+#define deser_entity_type deser_u16
+
+typedef enum : uint8_t {
+ ENTITY_ADD, // type
+ ENTITY_REMOVE,
+ ENTITY_MOVE, // x y
+} entity_cmd;
+
+#define ser_entity_cmd ser_u8
+#define deser_entity_cmd deser_u8
-enum {
+enum : uint16_t {
CPKT_HI = 0, // len motd
CPKT_FAIL, // fail_reason
CPKT_PLAYERS, // len [len name id]
- CPKT_MOVE, // player_id remove x y
+ CPKT_ENTITY, // len [entity_id command]
CPKT_NODES, // box2 [node]
CPKT_RESET_MAP,
};
-enum {
+enum : uint16_t {
SPKT_HI = 0, // len name len password
- SPKT_MOVE, // move_dir
+ SPKT_MOVE, // dir
};
+typedef uint16_t pkt_type;
+#define ser_pkt_type ser_u16
+#define deser_pkt_type deser_u16
+
#endif
diff --git a/include/vec.h b/include/vec.h
index ce1b921..d2d9e31 100644
--- a/include/vec.h
+++ b/include/vec.h
@@ -9,7 +9,7 @@
#include <stdbool.h>
#include "ser.h"
-#define VECFN static __attribute__((unused))
+#define VECFN [[maybe_unused]] static
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
@@ -22,12 +22,16 @@ typedef struct { S x; S y; } V; \
typedef struct { V pos; uvec2 size; } B; \
VECFN V V##_add(V a, V b) { return (V) { a.x+b.x, a.y+b.y }; } \
VECFN V V##_sub(V a, V b) { return (V) { a.x-b.x, a.y-b.y }; } \
+VECFN V V##_mul(V v, S s) { return (V) { v.x*s, v.y*s }; } \
+VECFN V V##_div(V v, S s) { return (V) { v.x/s, v.y/s }; } \
VECFN uvec2 V##_chksub(V a, V b) { return (uvec2) { CHKSUB(a.x, b.x), CHKSUB(a.y, b.y) }; } \
VECFN V V##_min(V a, V b) { return (V) { MIN(a.x, b.x), MIN(a.y, b.y) }; } \
VECFN V V##_max(V a, V b) { return (V) { MAX(a.x, b.x), MAX(a.y, b.y) }; } \
VECFN bool V##_eq(V a, V b) { return a.x == b.x && a.y == b.y; } \
VECFN bool V##_le(V a, V b) { return a.x <= b.x && a.y <= b.y; } \
VECFN bool V##_lt(V a, V b) { return a.x < b.x && a.y < b.y; } \
+VECFN V V##_zero() { return (V) { 0, 0 }; } \
+VECFN bool V##_iszero(V v) { return V##_eq(v, V##_zero()); } \
VECFN void ser_##V(strbuf *w, V v) { ser_##SER(w, v.x); ser_##SER(w, v.y); } \
VECFN bool deser_##V(str *r, V *v) { return deser_##SER(r, &v->x) && deser_##SER(r, &v->y); } \
VECFN void ser_##B(strbuf *w, B b) { ser_##V(w, b.pos); ser_uvec2(w, b.size); } \
diff --git a/src/client.c b/src/client.c
index 79a012d..207dc97 100644
--- a/src/client.c
+++ b/src/client.c
@@ -16,7 +16,7 @@
typedef struct {
str name;
- uint64_t id;
+ entity_id id;
} player;
typedef struct {
@@ -24,7 +24,7 @@ typedef struct {
struct termios oldtio;
str name;
str pass;
- uint64_t self_id;
+ entity_id self_id;
array(player) players;
str server_motd;
chunk map;
@@ -55,6 +55,46 @@ void gfx_clear()
printf("\e[2J");
}
+void gfx_render_node(client *c, vec2 pos, node *n)
+{
+ if (!n->present) {
+ gfx_set_color(color_from_u32(0x555555), true);
+ gfx_set_color(color_from_u32(0xffffff), false);
+ printf(" ?");
+ return;
+ }
+
+ gfx_set_color(n->col, true);
+
+ if (vec2_eq(pos, c->player_pos)) {
+ printf("🥺");
+ return;
+ }
+
+ // TODO: persistence
+ switch (n->type) {
+ case N_GRASS: if (rand() % 10 == 0) {
+ gfx_set_color(color_from_u32(0x1c571e), false);
+ printf(" M");
+ return;
+ } break;
+ case N_WATER: if (rand() % 10 == 0) {
+ gfx_set_color(color_from_u32(0x0d79de), false);
+ printf(" ~");
+ return;
+ } break;
+ case N_SAND: if (rand() % 15 == 0) {
+ gfx_set_color(color_from_u32(0xe3a112), false);
+ printf(" ~");
+ return;
+ } break;
+ default: break;
+ }
+
+ printf(" ");
+}
+
+
void gfx_render(client *c, uint64_t dtime)
{
gfx_clear();
@@ -64,15 +104,9 @@ void gfx_render(client *c, uint64_t dtime)
printf("%.*s\n", PSTR(c->players.data[i].name));
for (size_t y = 0; y < c->map.bounds.size.y; y++) {
for (size_t x = 0; x < c->map.bounds.size.y; x++) {
- node *n = chunk_index(c->map, UVEC2(x, y));
- if (!n->present) {
- gfx_set_color(color_from_u32(0x555555), true);
- gfx_set_color(color_from_u32(0xffffff), false);
- printf("??");
- } else {
- gfx_set_color(n->col, true);
- printf(" ");
- }
+ uvec2 off = UVEC2(x, y);
+ vec2 p = vec2_add(c->map.bounds.pos, CVEC2(off));
+ gfx_render_node(c, p, chunk_index(c->map, off));
}
gfx_clear_effects();
printf("\n");
@@ -107,48 +141,50 @@ void client_exit(client *c, int ret)
exit(ret);
}
-void handle_input(client *c, char ch)
+dir dir_from_key(char ch)
{
- ch = tolower(ch);
switch (ch) {
- case 'q': client_exit(c, EXIT_SUCCESS); break;
- default: break;
+ case 'w': return DIR_UP;
+ case 'a': return DIR_LEFT;
+ case 's': return DIR_DOWN;
+ case 'd': return DIR_RIGHT;
+ default: return 4;
}
}
+void handle_input(client *c, char ch)
+{
+ ch = tolower(ch);
+
+ dir move;
+ if ((move = dir_from_key(ch)) != 4)
+ SEND_PKT(c->conn, SPKT_MOVE, ser_dir(&pkt, move);)
+}
+
void map_set_center(client *c, vec2 center)
{
+ vec2 pos = vec2_sub(center, CVEC2(uvec2_div(c->map.bounds.size, 2)));
+
+ if (vec2_eq(c->map.bounds.pos, pos))
+ return;
+
chunk old = c->map;
c->map.data = c->map_swap;
- c->map.bounds.pos = center;
+ c->map.bounds.pos = pos;
c->map_swap = old.data;
chunk_clear(c->map);
chunk_copy(c->map, old);
}
-bool deser_color(str *r, color *c)
-{
- return deser_u8(r, &c->r) && deser_u8(r, &c->g) && deser_u8(r, &c->b);
-}
-
-bool deser_node(str *r, node *n)
-{
- if (!deser_bool(r, &n->present)) return false;
- if (!n->present) return true;
-
- return deser_i8(r, &n->z)
- && deser_color(r, &n->col);
-}
-
bool handle_nodes(str *pkt, client *c)
{
chunk ch;
if (!deser_vec2(pkt, &ch.bounds.pos)) return false;
if (!deser_uvec2(pkt, &ch.bounds.size)) return false;
- if (ch.bounds.size.x * ch.bounds.size.y > NODES_PKT_MAX) return false;
+ if (ch.bounds.size.x * ch.bounds.size.y > PKT_NODES_MAX) return false;
node nodes[ch.bounds.size.x * ch.bounds.size.y];
ch.pitch = ch.bounds.size.y;
@@ -181,7 +217,7 @@ bool handle_players(str *pkt, client *c)
for (c->players.len = 0; c->players.len < len; c->players.len++) {
player *p = &c->players.data[c->players.len];
if (!deser_str(pkt, &p->name)) return false;
- if (!deser_u64(pkt, &p->id)) return false;
+ if (!deser_entity_id(pkt, &p->id)) return false;
if (str_eq(p->name, c->name))
c->self_id = p->id;
p->name = str_clone(p->name);
@@ -189,15 +225,47 @@ bool handle_players(str *pkt, client *c)
return true;
}
-bool handle_hi(str *w, client *c)
+bool handle_hi(str *pkt, client *c)
{
str motd;
- if (!deser_str(w, &motd)) return false;
+ if (!deser_str(pkt, &motd)) return false;
c->server_motd = str_clone(motd);
return true;
}
+bool handle_entity(str *pkt, client *c)
+{
+ uint16_t num;
+ if (!deser_u16(pkt, &num)) return false;
+ for (uint16_t i = 0; i < num; i++) {
+ entity_id id;
+ entity_cmd cmd;
+
+ if (!deser_entity_id(pkt, &id)) return false;
+ if (!deser_entity_cmd(pkt, &cmd)) return false;
+
+ switch (cmd) {
+ case ENTITY_ADD: {
+ entity_type type;
+ if (!deser_entity_type(pkt, &type)) return false;
+ } break;
+ case ENTITY_REMOVE: break;
+ case ENTITY_MOVE:
+ vec2 pos;
+ if (!deser_vec2(pkt, &pos)) return false;
+
+ if (id == c->self_id)
+ map_set_center(c, c->player_pos = pos);
+
+ break;
+ default: return false;
+ }
+ }
+
+ return true;
+}
+
bool handle_pkt(client *c, str pkt)
{
pkt_type type;
@@ -209,6 +277,7 @@ bool handle_pkt(client *c, str pkt)
case CPKT_PLAYERS: return handle_players(&pkt, c);
case CPKT_RESET_MAP: return handle_reset_map(&pkt, c);
case CPKT_NODES: return handle_nodes(&pkt, c);
+ case CPKT_ENTITY: return handle_entity(&pkt, c);
default: return false;
}
}
diff --git a/src/server.c b/src/server.c
index 45d8384..30b5676 100644
--- a/src/server.c
+++ b/src/server.c
@@ -41,7 +41,7 @@ typedef struct {
typedef struct {
bool auth;
str name;
- uint64_t id;
+ entity_id id;
vec2 pos;
int8_t z;
peer conn;
@@ -53,7 +53,7 @@ typedef struct {
str passphrase;
array(map) maps;
arraybuf(player) players;
- uint64_t entity_id;
+ entity_id next_entity;
} game;
node *map_node(game *g, vec2 p, int8_t z)
@@ -163,23 +163,6 @@ bool map_load(map *m)
#undef TRY
}
-void ser_color(strbuf *w, color c)
-{
- ser_u8(w, c.r);
- ser_u8(w, c.g);
- ser_u8(w, c.b);
-}
-
-void ser_node(strbuf *w, node *n)
-{
- bool present = n != NULL && n->present;
- ser_bool(w, present);
- if (!present)
- return;
- ser_i8(w, n->z);
- ser_color(w, n->col);
-}
-
void send_players(game *g)
{
for (size_t i = 0; i < g->players.len; i++) {
@@ -190,7 +173,7 @@ void send_players(game *g)
player *p = &g->players.data[i];
if (!p->auth) continue;
ser_str(&pkt, p->name);
- ser_u64(&pkt, p->id);
+ ser_entity_id(&pkt, p->id);
}
)
}
@@ -276,6 +259,30 @@ bool handle_hi(str pkt, player *p, game *g)
return true;
}
+bool handle_move(str pkt, player *p, [[maybe_unused]] game *g)
+{
+ dir d;
+ if (!deser_dir(&pkt, &d)) return false;
+
+ vec2 v = dir_to_vec2(d);
+ if (vec2_iszero(v))
+ return false;
+
+ // yolo
+ p->pos = vec2_add(v, p->pos);
+ SEND_PKT(p->conn, CPKT_ENTITY,
+ ser_u16(&pkt, 1);
+ ser_entity_id(&pkt, p->id);
+ ser_entity_cmd(&pkt, ENTITY_MOVE);
+ ser_vec2(&pkt, p->pos);
+ )
+
+ // TODO: only send newly visible nodes
+ send_nodes(p, g, box2_around(p->pos, SIGHT_RANGE), p->z);
+
+ return true;
+}
+
bool handle_pkt(str pkt, player *p, game *g)
{
pkt_type type;
@@ -287,6 +294,7 @@ bool handle_pkt(str pkt, player *p, game *g)
switch (type) {
case SPKT_HI: return handle_hi(pkt, p, g);
+ case SPKT_MOVE: return handle_move(pkt, p, g);
default: return false;
}
}
@@ -298,7 +306,7 @@ int main()
game g = {0};
g.accept_fd = -1;
- g.entity_id = 1;
+ g.next_entity = 1;
g.motd = str_clone(S("Welcome to test server"));
g.passphrase = str_clone(S(""));
@@ -359,7 +367,7 @@ int main()
printf("new player\n");
player *p = ARR_APPEND(g.players);
p->auth = false;
- p->id = g.entity_id++;
+ p->id = g.next_entity++;
p->name = S("(unauthenticated)");
peer_init(&p->conn, socket, &p->name);
}