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 }