juegos

Juegos de terminal de Pancho: Catan (TUI, GUI, web, servidor, PicoCalc), ajedrez, calculadora y minijuegos
git clone https://git.lu3dhn.xyz/juegos.git
Log | Files | Refs

net.c (7684B)


      1 /* net.c - cliente de red: conexion, protocolo de lineas, reconexion. */
      2 #include <stdarg.h>
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <string.h>
      6 #include "net.h"
      7 #include "../server/server.h"
      8 
      9 struct Net {
     10     Session *s;
     11     LineConn lc;
     12     char  host[128];
     13     int   port;
     14     char  name[20];
     15     char  code[8];
     16     int   seat;
     17     char  token[33];
     18     bool  synced;
     19     Server *emb;
     20     long long retry_at;
     21     char  seat_kind[MAXP];
     22     bool  seat_on[MAXP];
     23 };
     24 
     25 /* ------------------------------------------------------------ conexion */
     26 
     27 static void idle_emb(void *ctx) { srv_poll(ctx, 0); }   /* atender al servidor embebido mientras esperamos */
     28 
     29 bool net_send(Net *n, const char *fmt, ...)
     30 {
     31     char buf[SRV_LINE + 8];
     32     va_list ap;
     33     va_start(ap, fmt);
     34     int len = vsnprintf(buf, sizeof buf, fmt, ap);
     35     va_end(ap);
     36     if (len < 0 || len >= (int)sizeof buf) return false;
     37     return lc_send(&n->lc, "%s", buf);
     38 }
     39 
     40 bool net_read_line(Net *n, char *line, int len, int timeout_ms) { return lc_read_line(&n->lc, line, len, timeout_ms); }
     41 
     42 static bool handshake(Net *n, char *err, int errlen)
     43 {
     44     char line[SRV_LINE];
     45     net_send(n, "HELLO %d %s", PROTO_VERSION, n->name);
     46     if (!net_read_line(n, line, sizeof line, 5000)) { snprintf(err, (size_t)errlen, "%s", g_lang ? "no answer from server" : "el servidor no responde"); return false; }
     47     if (strncmp(line, "WELCOME", 7)) { snprintf(err, (size_t)errlen, "%s", line); return false; }
     48     return true;
     49 }
     50 
     51 Net *net_connect(const char *host, int port, const char *name, Server *emb, char *err, int errlen)
     52 {
     53     Net *n = calloc(1, sizeof *n);
     54     if (!n) return 0;
     55     n->emb = emb;
     56     snprintf(n->host, sizeof n->host, "%s", host);
     57     n->port = port;
     58     snprintf(n->name, sizeof n->name, "%s", name && *name ? name : "?");
     59     lc_init(&n->lc, lc_dial(host, port, err, errlen));
     60     if (emb) { n->lc.idle = idle_emb; n->lc.idle_ctx = emb; }
     61     if (n->lc.fd < 0 || !handshake(n, err, errlen)) {
     62         lc_close(&n->lc);
     63         free(n);
     64         return 0;
     65     }
     66     return n;
     67 }
     68 
     69 const char *net_code(Net *n) { return n->code; }
     70 const char *net_token(Net *n) { return n->token; }
     71 int net_seat(Net *n) { return n->seat; }
     72 
     73 /* ------------------------------------------------------------ protocolo */
     74 
     75 static void update_status(Net *n)
     76 {
     77     Session *s = n->s;
     78     if (!s) return;
     79     int k = snprintf(s->status, sizeof s->status, "%s %s", g_lang ? "Code" : "Código", n->code);
     80     if (n->lc.fd < 0) { snprintf(s->status + k, sizeof s->status - (size_t)k, " · %s", g_lang ? "reconnecting..." : "reconectando..."); return; }
     81     for (int i = 0; i < s->view.o.np && k < (int)sizeof s->status - 30; i++) {
     82         if (n->seat_kind[i] == 'P' && i != n->seat)
     83             k += snprintf(s->status + k, sizeof s->status - (size_t)k, " · %.12s %s", s->names[i], n->seat_on[i] ? "●" : "○");
     84         if (n->seat_kind[i] == 'H')
     85             k += snprintf(s->status + k, sizeof s->status - (size_t)k, " · %s", g_lang ? "free seat" : "asiento libre");
     86     }
     87 }
     88 
     89 static void handle_line(Net *n, const char *line)
     90 {
     91     Session *s = n->s;
     92     if (!strncmp(line, "GAME ", 5)) {
     93         char code[16], token[40];
     94         int seat;
     95         if (sscanf(line + 5, "%15s %d %39s", code, &seat, token) == 3) {
     96             snprintf(n->code, sizeof n->code, "%s", code);
     97             snprintf(n->token, sizeof n->token, "%s", token);
     98             n->seat = seat;
     99             n->synced = false;
    100         }
    101     } else if (!strncmp(line, "HDR ", 4)) {
    102         if (s) {
    103             hdr_parse(line + 4, &s->view);
    104             s->view_undo.n = 0;
    105             sess_log_clear(s);
    106             s->local_mask = (uint8_t)(1 << n->seat);
    107         }
    108     } else if (!strncmp(line, "SEAT ", 5)) {
    109         int i, on;
    110         char k, name[64] = "";
    111         if (sscanf(line + 5, "%d %c %d %63[^\n]", &i, &k, &on, name) >= 3 && i >= 0 && i < MAXP) {
    112             n->seat_kind[i] = k;
    113             n->seat_on[i] = on;
    114             if (s) {
    115                 if (k == 'H') snprintf(s->names[i], sizeof s->names[i], "(%s)", g_lang ? "free" : "libre");
    116                 else snprintf(s->names[i], sizeof s->names[i], "%s", name);
    117             }
    118             update_status(n);
    119         }
    120     } else if (!strncmp(line, "EV ", 3)) {
    121         Move m;
    122         if (s && move_parse(line + 3, &m)) sess_on_move(s, &m);
    123     } else if (!strncmp(line, "CHAT ", 5)) {
    124         int from, to, used = 0;
    125         if (s && sscanf(line + 5, "%d %d %n", &from, &to, &used) >= 2 && used) sess_chat_in(s, from, to, line + 5 + used);
    126     } else if (!strcmp(line, "SYNC")) {
    127         n->synced = true;
    128         update_status(n);
    129     } else if (!strncmp(line, "ERR ", 4)) {
    130         if (s) {
    131             s->last_err = atoi(line + 4);
    132             if (s->last_err <= 0 || s->last_err >= E__COUNT) s->last_err = E_ARGS;
    133             s->err_pending = true;
    134         }
    135     }
    136 }
    137 
    138 bool net_enter_game(Net *n, Session *s, char *err, int errlen)
    139 {
    140     memset(s, 0, sizeof *s);
    141     s->local = false;
    142     s->net = n;
    143     s->g = &s->view;
    144     n->s = s;
    145     char line[SRV_LINE];
    146     bool got_game = false;
    147     while (net_read_line(n, line, sizeof line, 8000)) {
    148         if (!strncmp(line, "ERR ", 4)) {
    149             const char *t = strchr(line + 4, ' ');
    150             snprintf(err, (size_t)errlen, "%s", t ? t + 1 : line);
    151             return false;
    152         }
    153         if (!strncmp(line, "GAME ", 5)) got_game = true;
    154         handle_line(n, line);
    155         if (got_game && n->synced) {
    156             char hp[160];
    157             snprintf(hp, sizeof hp, "%s:%d", n->host, n->port);
    158             saved_game_add(n->emb ? "lan" : hp, n->code, n->seat, n->token);
    159             return true;
    160         }
    161     }
    162     snprintf(err, (size_t)errlen, "%s", g_lang ? "connection lost" : "se cortó la conexión");
    163     return false;
    164 }
    165 
    166 void net_on_local_move(Net *n, const Move *m) { (void)n; (void)m; }
    167 
    168 int net_submit(Net *n, Move *m)
    169 {
    170     Session *s = n->s;
    171     if (n->lc.fd < 0 || !n->synced) return E_TURN;
    172     /* chequeo local para respuesta inmediata (la autoridad vuelve a validar) */
    173     const Game *v = &s->view;
    174     bool setup_road = m->type == M_ROAD && v->phase == PH_SETUP && !v->setup_sub;
    175     int e = setup_road ? E_OK : game_check(v, m);
    176     if (e) return e;
    177     char line[128];
    178     move_fmt(m, line, sizeof line);
    179     net_send(n, "MOVE %s", line);
    180     return E_OK;
    181 }
    182 
    183 static void try_reconnect(Net *n)
    184 {
    185     char err[128];
    186     lc_init(&n->lc, lc_dial(n->host, n->port, err, sizeof err));
    187     if (n->lc.fd < 0) return;
    188     if (!handshake(n, err, sizeof err)) { lc_close(&n->lc); return; }
    189     n->lc.inlen = 0;
    190     n->synced = false;
    191     net_send(n, "OPEN %s %s", n->code, n->token);
    192 }
    193 
    194 void net_poll(Net *n)
    195 {
    196     if (n->emb) srv_poll(n->emb, 0);
    197     if (n->lc.fd < 0) {
    198         if (now_ms() >= n->retry_at) {
    199             n->retry_at = now_ms() + 3000;
    200             try_reconnect(n);
    201             update_status(n);
    202         }
    203         return;
    204     }
    205     if (!lc_pump(&n->lc)) {
    206         n->retry_at = now_ms() + 1000;
    207         update_status(n);
    208     }
    209     char line[SRV_LINE];
    210     while (lc_take(&n->lc, line, sizeof line)) handle_line(n, line);
    211     if (n->lc.fd >= 0 && now_ms() - n->lc.last_tx > 30000) net_send(n, "PING");
    212 }
    213 
    214 int net_fds(Net *n, int *fds, int max)
    215 {
    216     int k = 0;
    217     if (n->lc.fd >= 0 && k < max) fds[k++] = n->lc.fd;
    218     if (n->emb) k += srv_fds(n->emb, fds + k, max - k);
    219     return k;
    220 }
    221 
    222 int net_timeout(Net *n)
    223 {
    224     int t = n->lc.fd < 0 ? 1000 : 30000;
    225     if (n->emb) { int b = srv_timeout(n->emb); if (b < t) t = b; }
    226     return t;
    227 }
    228 
    229 void net_close(Net *n)
    230 {
    231     if (!n) return;
    232     if (n->lc.fd >= 0) { net_send(n, "LEAVE"); lc_close(&n->lc); }
    233     if (n->emb) srv_close(n->emb);
    234     free(n);
    235 }