assaultfish/engine/net/src/server.c
2026-01-07 04:24:22 +09:00

199 lines
5.3 KiB
C

#include <fishsoup.h>
#include "stb_ds.h"
fishsoup_server_t* fishsoup_server(int port) {
fishsoup_server_t* server = malloc(sizeof(*server));
int val;
if(port == 0) port = FISHSOUP_PORT;
if((server->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
free(server);
return NULL;
}
val = 1;
setsockopt(server->fd, IPPROTO_TCP, TCP_NODELAY, (char*)&val, sizeof(val));
server->address.sin_family = AF_INET;
server->address.sin_addr.s_addr = INADDR_ANY;
server->address.sin_port = htons(port);
if(bind(server->fd, (struct sockaddr*)&server->address, sizeof(server->address)) < 0) {
close(server->fd);
free(server);
return NULL;
}
if(listen(server->fd, 128) < 0) {
close(server->fd);
free(server);
return NULL;
}
server->clients = NULL;
server->on_client = NULL;
server->on_tick = NULL;
return server;
}
int fishsoup_server_clients(fishsoup_server_t* server) {
return arrlen(server->clients);
}
void fishsoup_server_loop(fishsoup_server_t* server) {
struct pollfd* pfd;
while(1) {
int i;
int ret;
pfd = malloc(sizeof(*pfd) * (1 + arrlen(server->clients)));
pfd[0].fd = server->fd;
pfd[0].events = POLLIN | POLLPRI;
for(i = 0; i < arrlen(server->clients); i++) {
pfd[1 + i].fd = server->clients[i].fd;
pfd[1 + i].events = POLLIN | POLLPRI;
}
ret = poll(pfd, 1 + arrlen(server->clients), 50);
if(ret < 0) {
free(pfd);
break;
}
if(server->on_tick != NULL) server->on_tick(server);
if(pfd[0].revents & POLLIN) {
fishsoup_client_t client;
struct sockaddr_in address;
int len = sizeof(address);
client.fd = accept(server->fd, (struct sockaddr*)&address, &len);
client.last_ping = time(NULL);
client.server.cleanup = 0;
client.server.sent_ping = 0;
client.on_packet = NULL;
arrput(server->clients, client);
if(server->on_client != NULL) server->on_client(server, &server->clients[arrlen(server->clients) - 1]);
#ifdef DEBUG
fprintf(stderr, "Client %d accepted\n", server->clients[i].fd);
#endif
}
for(i = 0; i < arrlen(server->clients); i++) {
time_t diff;
if(server->clients[i].server.cleanup) continue;
if(pfd[1 + i].revents & POLLIN) {
fishsoup_packet_t pkt;
if(fishsoup_packet_recv(server->clients[i].fd, &pkt) == 0) {
server->clients[i].server.cleanup = 1;
} else {
if(pkt.header.type == FISHSOUP_PACKET_PONG && server->clients[i].server.sent_ping) {
server->clients[i].last_ping = time(NULL);
server->clients[i].server.sent_ping = 0;
#ifdef DEBUG
fprintf(stderr, "Pong from client %d\n", server->clients[i].fd);
#endif
} else if(pkt.header.type == FISHSOUP_PACKET_PING) {
fishsoup_packet_t ret;
ret.header.type = FISHSOUP_PACKET_PONG;
ret.header.length = 0;
ret.data = NULL;
if(fishsoup_packet_send(server->clients[i].fd, &ret) == 0) {
server->clients[i].server.cleanup = 1;
} else {
#ifdef DEBUG
fprintf(stderr, "Ping from client %d, sending back pong\n", server->clients[i].fd);
#endif
}
}
if(!server->clients[i].server.cleanup && server->clients[i].on_packet != NULL) server->clients[i].on_packet(&server->clients[i], &pkt);
if(pkt.data != NULL) free(pkt.data);
}
}
if(server->clients[i].server.cleanup) continue;
diff = time(NULL) - server->clients[i].last_ping;
if(diff == FISHSOUP_PING && !server->clients[i].server.sent_ping) {
fishsoup_packet_t pkt;
pkt.header.type = FISHSOUP_PACKET_PING;
pkt.header.length = 0;
pkt.data = NULL;
if(fishsoup_packet_send(server->clients[i].fd, &pkt) == 0) {
server->clients[i].server.cleanup = 1;
#ifdef DEBUG
fprintf(stderr, "Failed to ping client %d\n", server->clients[i].fd);
#endif
} else {
server->clients[i].server.sent_ping = 1;
#ifdef DEBUG
fprintf(stderr, "Pinging client %d\n", server->clients[i].fd);
#endif
}
} else if(diff == FISHSOUP_PING * 2 && server->clients[i].server.sent_ping) {
server->clients[i].server.cleanup = 1;
#ifdef DEBUG
fprintf(stderr, "Ping timeout (client %d)\n", server->clients[i].fd);
#endif
}
}
for(i = 0; i < arrlen(server->clients); i++) {
if(server->clients[i].server.cleanup) {
#ifdef DEBUG
fprintf(stderr, "Client %d cleanup\n", server->clients[i].fd);
#endif
if(server->clients[i].on_disconnect != NULL) server->clients[i].on_disconnect(&server->clients[i]);
close(server->clients[i].fd);
arrdel(server->clients, i);
i--;
}
}
free(pfd);
}
}
void fishsoup_server_on_client(fishsoup_server_t* server, void (*on_client)(fishsoup_server_t* server, fishsoup_client_t* client)) {
server->on_client = on_client;
}
void fishsoup_server_on_tick(fishsoup_server_t* server, void (*on_tick)(fishsoup_server_t* server)) {
server->on_tick = on_tick;
}
void fishsoup_server_destroy(fishsoup_server_t* server) {
int i;
for(i = 0; i < arrlen(server->clients); i++) {
if(server->clients[i].on_disconnect != NULL) server->clients[i].on_disconnect(&server->clients[i]);
close(server->clients[i].fd);
}
arrfree(server->clients);
close(server->fd);
free(server);
}
void fishsoup_server_broadcast(fishsoup_server_t* server, fishsoup_packet_t* packet) {
int i;
for(i = 0; i < arrlen(server->clients); i++) {
if(fishsoup_packet_send(server->clients[i].fd, packet) == 0) {
server->clients[i].server.cleanup = 1;
}
}
}