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 (7775B)


      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, wseat;
     17     char  token[33];
     18     bool  synced;
     19     long long elapsed;          /* ms que llevaba el turno segun la cabecera */
     20     struct Server *emb;
     21     long long retry_at;
     22     char  seat_kind[2];
     23     bool  seat_on[2];
     24 };
     25 
     26 /* ------------------------------------------------------------ conexion */
     27 
     28 static void idle_emb(void *ctx) { srv_poll(ctx, 0); }   /* atender al servidor embebido mientras esperamos */
     29 
     30 bool net_send(Net *n, const char *fmt, ...)
     31 {
     32     char buf[SRV_LINE + 8];
     33     va_list ap;
     34     va_start(ap, fmt);
     35     int len = vsnprintf(buf, sizeof buf, fmt, ap);
     36     va_end(ap);
     37     if (len < 0 || len >= (int)sizeof buf) return false;
     38     return lc_send(&n->lc, "%s", buf);
     39 }
     40 
     41 bool net_read_line(Net *n, char *line, int len, int timeout_ms) { return lc_read_line(&n->lc, line, len, timeout_ms); }
     42 
     43 static bool handshake(Net *n, char *err, int errlen)
     44 {
     45     char line[SRV_LINE];
     46     net_send(n, "HELLO %d %s", PROTO_VERSION, n->name);
     47     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; }
     48     if (strncmp(line, "WELCOME", 7)) { snprintf(err, (size_t)errlen, "%s", line); return false; }
     49     return true;
     50 }
     51 
     52 Net *net_connect(const char *host, int port, const char *name, struct Server *emb, char *err, int errlen)
     53 {
     54     Net *n = calloc(1, sizeof *n);
     55     if (!n) return 0;
     56     n->emb = emb;
     57     snprintf(n->host, sizeof n->host, "%s", host);
     58     n->port = port;
     59     snprintf(n->name, sizeof n->name, "%s", name && *name ? name : "?");
     60     lc_init(&n->lc, lc_dial(host, port, err, errlen));
     61     if (emb) { n->lc.idle = idle_emb; n->lc.idle_ctx = emb; }
     62     if (n->lc.fd < 0 || !handshake(n, err, errlen)) {
     63         lc_close(&n->lc);
     64         free(n);
     65         return 0;
     66     }
     67     return n;
     68 }
     69 
     70 const char *net_code(Net *n) { return n->code; }
     71 
     72 /* ------------------------------------------------------------ protocolo */
     73 
     74 static void update_status(Net *n)
     75 {
     76     Session *s = n->s;
     77     if (!s) return;
     78     int k = snprintf(s->status, sizeof s->status, "%s %s", g_lang ? "Code" : "Código", n->code);
     79     if (n->lc.fd < 0) { snprintf(s->status + k, sizeof s->status - (size_t)k, " · %s", g_lang ? "reconnecting..." : "reconectando..."); return; }
     80     int other = n->seat ^ 1;
     81     if (n->seat_kind[other] == 'H') snprintf(s->status + k, sizeof s->status - (size_t)k, " · %s", g_lang ? "waiting for a rival (share the code)" : "esperando rival (pasá el código)");
     82     else if (n->seat_kind[other] == 'P')
     83         snprintf(s->status + k, sizeof s->status - (size_t)k, " · %s %s", s->names[s->my_color ^ 1], n->seat_on[other] ? "●" : "○");
     84 }
     85 
     86 static void handle_line(Net *n, const char *line)
     87 {
     88     Session *s = n->s;
     89     if (!strncmp(line, "GAME ", 5)) {
     90         char code[16], token[40];
     91         int seat;
     92         if (sscanf(line + 5, "%15s %d %39s", code, &seat, token) == 3) {
     93             snprintf(n->code, sizeof n->code, "%.7s", code);
     94             snprintf(n->token, sizeof n->token, "%.32s", token);
     95             n->seat = seat;
     96             n->synced = false;
     97         }
     98     } else if (!strncmp(line, "HDR S ", 6)) {
     99         int wseat;
    100         long long el;
    101         int used = 0;
    102         if (s && sscanf(line + 6, "%d %lld %n", &wseat, &el, &used) == 2 && used && game_hdr_parse(line + 6 + used, &s->g)) {
    103             n->wseat = wseat;
    104             n->elapsed = el;
    105             s->my_color = n->seat == wseat ? WHITE : BLACK;
    106             s->kind[s->my_color] = PL_HUMAN;
    107             s->kind[s->my_color ^ 1] = PL_REMOTE;
    108             s->eval_ply = -1;
    109         }
    110     } else if (!strncmp(line, "SEAT ", 5)) {
    111         int i, on;
    112         char k, name[64] = "";
    113         if (sscanf(line + 5, "%d %c %d %63[^\n]", &i, &k, &on, name) >= 3 && (i == 0 || i == 1)) {
    114             n->seat_kind[i] = k;
    115             n->seat_on[i] = on;
    116             if (s) {
    117                 int color = i == n->wseat ? WHITE : BLACK;
    118                 if (k == 'H') snprintf(s->names[color], sizeof s->names[color], "(%s)", g_lang ? "free" : "libre");
    119                 else snprintf(s->names[color], sizeof s->names[color], "%.23s", name);
    120             }
    121             update_status(n);
    122         }
    123     } else if (!strncmp(line, "EV ", 3)) {
    124         Act a;
    125         if (s && act_parse(line + 3, &a) && act_resolve(&s->g, &a) && game_check(&s->g, &a) == E_OK) sess_on_act(s, &a);
    126     } else if (!strcmp(line, "SYNC")) {
    127         n->synced = true;
    128         if (s) s->turn_start = now_ms() - n->elapsed;
    129         update_status(n);
    130     } else if (!strncmp(line, "ERR ", 4)) {
    131         if (s) {
    132             s->last_err = atoi(line + 4);
    133             if (s->last_err <= 0 || s->last_err >= E__COUNT) s->last_err = E_ARGS;
    134             s->err_pending = true;
    135         }
    136     }
    137 }
    138 
    139 bool net_enter_game(Net *n, Session *s, char *err, int errlen)
    140 {
    141     memset(s, 0, sizeof *s);
    142     s->local = false;
    143     s->net = n;
    144     s->eval_ply = -1;
    145     game_init(&s->g, 0, 0, false);
    146     n->s = s;
    147     char line[SRV_LINE];
    148     bool got_game = false;
    149     while (net_read_line(n, line, sizeof line, 8000)) {
    150         if (!strncmp(line, "ERR ", 4)) {
    151             const char *t = strchr(line + 4, ' ');
    152             snprintf(err, (size_t)errlen, "%s", t ? t + 1 : line);
    153             return false;
    154         }
    155         if (!strncmp(line, "GAME ", 5)) got_game = true;
    156         handle_line(n, line);
    157         if (got_game && n->synced) {
    158             char hp[160];
    159             snprintf(hp, sizeof hp, "%s:%d", n->host, n->port);
    160             saved_game_add(n->emb ? "lan" : hp, n->code, n->seat, n->token);
    161             return true;
    162         }
    163     }
    164     snprintf(err, (size_t)errlen, "%s", g_lang ? "connection lost" : "se cortó la conexión");
    165     return false;
    166 }
    167 
    168 int net_submit(Net *n, Act *a)
    169 {
    170     Session *s = n->s;
    171     if (n->lc.fd < 0 || !n->synced) return E_TURN;
    172     /* chequeo local para respuesta inmediata (el servidor vuelve a validar) */
    173     int e = game_check(&s->g, a);
    174     if (e) return e;
    175     Act b = *a;
    176     b.ms = 0;
    177     char line[64];
    178     act_fmt(&b, 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 }