server.c (6378B)
1 #include <sys/types.h> 2 #include <sys/socket.h> 3 #include <netinet/in.h> 4 #include <arpa/inet.h> 5 6 #include <err.h> 7 #include <fcntl.h> 8 #include <poll.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <time.h> 13 #include <unistd.h> 14 15 #include "net.h" 16 17 struct server_slot { 18 struct sockaddr_in addr; 19 socklen_t addr_len; 20 time_t last_seen; 21 char name[24]; 22 uint16_t skin; 23 float x, y, z; 24 float yaw; 25 int active; 26 }; 27 28 static struct server_slot clients[MAX_PLAYERS]; 29 static int server_fd; 30 static int max_clients = MAX_PLAYERS; 31 32 static int 33 find_slot_by_addr(const struct sockaddr_in *addr) 34 { 35 int i; 36 37 for (i = 0; i < max_clients; i++) { 38 if (clients[i].active && 39 clients[i].addr.sin_addr.s_addr == addr->sin_addr.s_addr && 40 clients[i].addr.sin_port == addr->sin_port) { 41 return (i); 42 } 43 } 44 return (-1); 45 } 46 47 static int 48 find_free_slot(void) 49 { 50 int i; 51 52 for (i = 0; i < max_clients; i++) { 53 if (!clients[i].active) { 54 return (i); 55 } 56 } 57 return (-1); 58 } 59 60 static void 61 broadcast_world_state(void) 62 { 63 struct pkt_world_state pkt; 64 int i, count; 65 66 memset(&pkt, 0, sizeof(pkt)); 67 pkt.hdr.magic = NET_MAGIC; 68 pkt.hdr.type = PKT_WORLD_STATE; 69 70 count = 0; 71 for (i = 0; i < max_clients; i++) { 72 if (clients[i].active) { 73 pkt.players[count].id = (uint8_t)i; 74 pkt.players[count].skin = clients[i].skin; 75 pkt.players[count].x = clients[i].x; 76 pkt.players[count].y = clients[i].y; 77 pkt.players[count].z = clients[i].z; 78 pkt.players[count].yaw = clients[i].yaw; 79 count++; 80 } 81 } 82 pkt.player_count = (uint8_t)count; 83 84 for (i = 0; i < max_clients; i++) { 85 if (clients[i].active) { 86 sendto(server_fd, &pkt, 87 sizeof(struct pkt_header) + 1 + count * sizeof(struct player_state), 88 0, (struct sockaddr *)&clients[i].addr, clients[i].addr_len); 89 } 90 } 91 } 92 93 static void 94 handle_packet(const uint8_t *buf, ssize_t len, const struct sockaddr_in *src_addr, socklen_t addr_len) 95 { 96 const struct pkt_header *hdr; 97 struct pkt_join_ack ack; 98 int slot, i; 99 time_t now; 100 101 if (len < (ssize_t)sizeof(struct pkt_header)) { 102 return; 103 } 104 hdr = (const struct pkt_header *)buf; 105 if (hdr->magic != NET_MAGIC) { 106 return; 107 } 108 109 time(&now); 110 slot = find_slot_by_addr(src_addr); 111 112 if (hdr->type == PKT_JOIN) { 113 const struct pkt_join *pj; 114 115 if (len < (ssize_t)sizeof(struct pkt_join)) { 116 return; 117 } 118 pj = (const struct pkt_join *)buf; 119 120 if (slot < 0) { 121 slot = find_free_slot(); 122 if (slot < 0) { 123 return; 124 } 125 } 126 127 clients[slot].addr = *src_addr; 128 clients[slot].addr_len = addr_len; 129 clients[slot].last_seen = now; 130 clients[slot].skin = pj->skin; 131 strncpy(clients[slot].name, pj->name, sizeof(clients[slot].name) - 1); 132 clients[slot].name[sizeof(clients[slot].name) - 1] = '\0'; 133 clients[slot].active = 1; 134 135 memset(&ack, 0, sizeof(ack)); 136 ack.hdr.magic = NET_MAGIC; 137 ack.hdr.type = PKT_JOIN_ACK; 138 ack.hdr.player_id = (uint8_t)slot; 139 ack.your_id = (uint8_t)slot; 140 ack.game_time = 12.0f; 141 ack.game_weather = 0; 142 143 sendto(server_fd, &ack, sizeof(ack), 0, 144 (struct sockaddr *)src_addr, addr_len); 145 printf("server: player joined [id=%d, nick=%s]\n", slot, clients[slot].name); 146 return; 147 } 148 149 if (slot < 0) { 150 return; 151 } 152 clients[slot].last_seen = now; 153 154 if (hdr->type == PKT_SYNC) { 155 const struct pkt_sync *ps; 156 157 if (len < (ssize_t)sizeof(struct pkt_sync)) { 158 return; 159 } 160 ps = (const struct pkt_sync *)buf; 161 clients[slot].x = ps->x; 162 clients[slot].y = ps->y; 163 clients[slot].z = ps->z; 164 clients[slot].yaw = ps->yaw; 165 clients[slot].skin = ps->skin; 166 } else if (hdr->type == PKT_CHAT) { 167 const struct pkt_chat *pc; 168 struct pkt_chat out_chat; 169 170 if (len < (ssize_t)sizeof(struct pkt_chat)) { 171 return; 172 } 173 pc = (const struct pkt_chat *)buf; 174 printf("[%s]: %s\n", clients[slot].name, pc->msg); 175 176 memset(&out_chat, 0, sizeof(out_chat)); 177 out_chat.hdr.magic = NET_MAGIC; 178 out_chat.hdr.type = PKT_CHAT; 179 out_chat.hdr.player_id = (uint8_t)slot; 180 strncpy(out_chat.name, clients[slot].name, sizeof(out_chat.name) - 1); 181 strncpy(out_chat.msg, pc->msg, sizeof(out_chat.msg) - 1); 182 183 for (i = 0; i < max_clients; i++) { 184 if (clients[i].active) { 185 sendto(server_fd, &out_chat, sizeof(out_chat), 0, 186 (struct sockaddr *)&clients[i].addr, clients[i].addr_len); 187 } 188 } 189 } else if (hdr->type == PKT_LEAVE) { 190 clients[slot].active = 0; 191 printf("server: player left [id=%d, nick=%s]\n", slot, clients[slot].name); 192 } 193 } 194 195 int 196 main(int argc, char *argv[]) 197 { 198 struct sockaddr_in srv_addr, cli_addr; 199 struct pollfd pfd; 200 uint8_t buf[2048]; 201 ssize_t n; 202 socklen_t addr_len; 203 struct timespec t_last, t_now; 204 double elapsed; 205 int port, i; 206 time_t now; 207 208 if (argc < 3) { 209 fprintf(stderr, "usage: %s <port> <max_players>\n", argv[0]); 210 return (1); 211 } 212 213 port = atoi(argv[1]); 214 max_clients = atoi(argv[2]); 215 216 if (port <= 0 || port > 65535) { 217 errx(1, "invalid port: %s", argv[1]); 218 } 219 if (max_clients <= 0 || max_clients > MAX_PLAYERS) { 220 errx(1, "invalid max_players (1-%d): %s", MAX_PLAYERS, argv[2]); 221 } 222 223 memset(clients, 0, sizeof(clients)); 224 225 server_fd = socket(AF_INET, SOCK_DGRAM, 0); 226 if (server_fd < 0) { 227 err(1, "socket"); 228 } 229 230 fcntl(server_fd, F_SETFL, O_NONBLOCK); 231 232 memset(&srv_addr, 0, sizeof(srv_addr)); 233 srv_addr.sin_family = AF_INET; 234 srv_addr.sin_addr.s_addr = INADDR_ANY; 235 srv_addr.sin_port = htons(port); 236 237 if (bind(server_fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr)) < 0) { 238 err(1, "bind"); 239 } 240 241 printf("antizona server listening on port %d (max players: %d)...\n", port, max_clients); 242 243 pfd.fd = server_fd; 244 pfd.events = POLLIN; 245 246 clock_gettime(CLOCK_MONOTONIC, &t_last); 247 248 while (1) { 249 if (poll(&pfd, 1, 5) > 0) { 250 if (pfd.revents & POLLIN) { 251 while (1) { 252 addr_len = sizeof(cli_addr); 253 n = recvfrom(server_fd, buf, sizeof(buf), 0, 254 (struct sockaddr *)&cli_addr, &addr_len); 255 if (n < 0) { 256 break; 257 } 258 handle_packet(buf, n, &cli_addr, addr_len); 259 } 260 } 261 } 262 263 clock_gettime(CLOCK_MONOTONIC, &t_now); 264 elapsed = (t_now.tv_sec - t_last.tv_sec) + 265 (t_now.tv_nsec - t_last.tv_nsec) * 1e-9; 266 267 if (elapsed >= 0.033) { 268 time(&now); 269 for (i = 0; i < max_clients; i++) { 270 if (clients[i].active && (now - clients[i].last_seen) > 5) { 271 clients[i].active = 0; 272 printf("server: timeout player [id=%d, nick=%s]\n", i, clients[i].name); 273 } 274 } 275 broadcast_world_state(); 276 t_last = t_now; 277 } 278 } 279 280 close(server_fd); 281 return (0); 282 }