#include #include #include "game.h" #include "server.h" #include "network.h" #include "protocol.h" #define MAX_NAMESIZE 32 typedef struct Client { bool connected; IPaddress address; char name[MAX_NAMESIZE]; } Client; static Uint32 lastTime; static Uint32 currTime; static UDPsocket sock; static Client client[MAX_PLAYERS]; static char name[MAX_NAMESIZE] = "netwar! server"; static int clientlimit = 0; static inline float randf() { return (float) rand() / (float) RAND_MAX; } static inline float randfm() { return (rand() % 2) ? (-randf()) : (+randf()); } static inline bool cmpaddr(IPaddress a, IPaddress b) { return (a.host == b.host) && (a.port == b.port); } static int sv_find_client(IPaddress address) { for(int i = 0; i < clientlimit; i++) if(client[i].connected && cmpaddr(client[i].address, address)) return i; return -1; } static void sv_sync_client(int id, bool full) { assert(id >= 0); assert(client[id].connected); for(int i = 0; i < clientlimit; i++) if(full || g_player[i].updated) SendMessage(sock, client[id].address, sv_splr(i, g_player[i].live, g_player[i].x, g_player[i].y, g_player[i].r, g_player[i].vx, g_player[i].vy, g_player[i].vr, g_player[i].shoot)); for(int i = 0; i < MAX_BULLETS; i++) if(full || g_bullet[i].updated) SendMessage(sock, client[id].address, sv_sbul(i, g_bullet[i].owner, g_bullet[i].live, g_bullet[i].x, g_bullet[i].y, g_bullet[i].vx, g_bullet[i].vy, g_bullet[i].tick)); } static void sv_spawn_player(int id) { assert(id >= 0); g_player_set(id, true, randfm(), randfm(), randfm(), 0, 0, 0, PLAYER_SHOOT); } static void sv_kill_player(int id, bool send, const char * msg) { assert(id >= 0); assert(client[id].connected); if(send) SendMessage(sock, client[id].address, sv_kill(msg)); client[id].connected = false; g_player_set(id, false, 0, 0, 0, 0, 0, 0, 0); SDL_Log("Player %s (%i) disconnected: %s", client[id].name, id, msg); } static void sv_move_player(int id, ProtocolMessage m) { assert(id >= 0); assert(client[id].connected); if(!g_player[id].live && m.cl.does.code.bits) sv_spawn_player(id); if(!g_player[id].live) return; g_player_does(id, m.cl.does.code); } static void sv_kill_address(IPaddress address, const char * msg) { SDL_Log("sv_kill_address: %s", msg); SendMessage(sock, address, sv_kill(msg)); } static void sv_register_player(IPaddress address, ClInfo info) { if(info.version != PROTOCOL_VERSION) { sv_kill_address(address, "Uncompatible version"); return; } int i = 0; while((i < clientlimit) && (client[i].connected)) ++i; if(i >= clientlimit) { sv_kill_address(address, "Too many clients"); return; } client[i].connected = true; client[i].address = address; strncpy(client[i].name, (char*) info.name, MAX_NAMESIZE); client[i].name[MAX_NAMESIZE - 1] = 0; SDL_Log("Player %s (%i) connected", client[i].name, i); SendMessage(sock, address, sv_info(name, i, clientlimit)); sv_spawn_player(i); sv_sync_client(i, true); } static void sv_recv_ipmsg(IPaddress address, ProtocolMessage m) { int id = sv_find_client(address); if(id < 0 && m.type != CL_INFO) return; switch(m.type) { case CL_INFO: sv_register_player(address, m.cl.info); break; case CL_KILL: sv_kill_player(id, false, ""); break; case CL_DOES: sv_move_player(id, m); break; default: sv_kill_address(address, "Invalid message"); } } static void sv_recv() { IPaddress address; ProtocolMessage m; while(RecvMessage(sock, &address, &m)) sv_recv_ipmsg(address, m); } static void sv_send() { for(int i = 0; i < clientlimit; i++) if(client[i].connected) sv_sync_client(i, false); for(int i = 0; i < clientlimit; i++) g_player[i].updated = false; for(int i = 0; i < MAX_BULLETS; i++) g_bullet[i].updated = false; } void sv_start(uint16_t port) { g_init(true); sock = OpenPort(port); lastTime = SDL_GetTicks(); currTime = SDL_GetTicks(); } void sv_stop() { for(int i = 0; i < clientlimit; i++) if(client[i].connected) sv_kill_player(i, true, "Server shutdown"); ClosePort(sock); } void sv_setclientlimit(unsigned int maxclients) { // TODO disconnect clients clientlimit = (maxclients > MAX_PLAYERS) ? (MAX_PLAYERS) : (maxclients); } void sv_handle() { if(currTime - lastTime >= TICK_DELAY) { sv_recv(); g_update(); sv_send(); lastTime = SDL_GetTicks(); } currTime = SDL_GetTicks(); }