From c4b04d12861ae0eac6315b1c2170013422136f80 Mon Sep 17 00:00:00 2001 From: DeaDDooMER Date: Thu, 30 Mar 2017 23:05:35 +0300 Subject: [PATCH 1/1] =?utf8?q?=D0=9F=D0=B5=D1=80=D0=B2=D0=B0=D1=8F=20?= =?utf8?q?=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D1=8F=20=D0=BF=D1=80=D0=BE=D1=82?= =?utf8?q?=D0=B5=D1=81=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=BD?= =?utf8?q?=D0=B0=D1=8F=20=D0=BD=D0=B0=20=D1=81=D0=B5=D1=80=D0=B2=D0=B5?= =?utf8?q?=D1=80=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 ++ bsdmake.sh | 3 + client.c | 101 +++++++++++++++++++++++++++++++ client.h | 9 +++ dedicated.c | 66 +++++++++++++++++++++ game.c | 65 ++++++++++++++++++++ game.h | 23 ++++++++ makefile | 18 ++++++ netwar.c | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++ network.c | 56 ++++++++++++++++++ network.h | 26 ++++++++ protocol.h | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++++ server.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++ server.h | 7 +++ start.bat | 1 + 15 files changed, 859 insertions(+) create mode 100644 .gitignore create mode 100755 bsdmake.sh create mode 100644 client.c create mode 100644 client.h create mode 100644 dedicated.c create mode 100644 game.c create mode 100644 game.h create mode 100644 makefile create mode 100644 netwar.c create mode 100644 network.c create mode 100644 network.h create mode 100644 protocol.h create mode 100644 server.c create mode 100644 server.h create mode 100644 start.bat diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cbac3ed --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.exe +*.dll +netwar +dedicated diff --git a/bsdmake.sh b/bsdmake.sh new file mode 100755 index 0000000..ad58d29 --- /dev/null +++ b/bsdmake.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exec cc -o dedicated -lSDL2 -lSDL2_net -lm dedicated.c game.c server.c network.c -I/usr/local/include -L/usr/local/lib diff --git a/client.c b/client.c new file mode 100644 index 0000000..defe5ec --- /dev/null +++ b/client.c @@ -0,0 +1,101 @@ +#include +#include + +#include "game.h" +#include "client.h" +#include "network.h" +#include "protocol.h" + +int cl_playerid; + +static bool run; +static IPaddress addr; +static UDPsocket sock; +static char nickname[32] = "Anonymous"; + +static void cl_kill_client(ProtocolMessage m) { + SDL_Log("Connection refused by server: %.*s\n", (int) sizeof(m.sv.kill.message), m.sv.kill.message); + cl_disconnect(true); +} + +static void cl_update_svinfo(ProtocolMessage m) { + SDL_Log("Connected to server %.*s\n", (int) sizeof(m.sv.info.name), m.sv.info.name); + cl_playerid = m.sv.info.clientid; + assert(cl_playerid < MAX_PLAYERS); + SDL_Log("Player id%i\n", cl_playerid); +} + +static void cl_update_svplayer(ProtocolMessage m) { + g_player_set( + m.sv.splr.clid, + m.sv.splr.live, + b2f(m.sv.splr.x), + b2f(m.sv.splr.y), + b2f(m.sv.splr.r), + b2f(m.sv.splr.vx), + b2f(m.sv.splr.vy), + b2f(m.sv.splr.vr) + ); +} + +void cl_connect(const char * host, uint16_t port) { + if(SDLNet_ResolveHost(&addr, host, (port) ? (port) : (DEFAULT_PORT)) != 0) { + SDL_Log("Unable to resolve host: %s\n", SDLNet_GetError()); + exit(1); + } + + sock = OpenPort(0); + SendMessage(sock, addr, cl_info(nickname)); + + ProtocolMessage m; + bool received = WaitMessage(sock, NULL, &m, 10000); + if(!received) { + SDL_Log("Connection timeout"); + exit(1); + } + + run = true; + if(m.type == SV_KILL) { + cl_kill_client(m); + } else if(m.type == SV_INFO) { + cl_update_svinfo(m); + } else { + SDL_Log("Invalid first message %i\n", m.type); + run = false; + } +} + +void cl_move(DoesCode code) { + g_player_does(cl_playerid, code); + SendMessage(sock, addr, cl_does(code)); +} + +void cl_recv() { + IPaddress address; + ProtocolMessage m; + if(!RecvMessage(sock, &address, &m)) + return; + + switch(m.type) { + case SV_INFO: cl_update_svinfo(m); break; + case SV_KILL: cl_kill_client(m); break; + case SV_SPLR: cl_update_svplayer(m); break; + default: SDL_Log("invalid message %i", m.type); + } +} + +void cl_disconnect(bool force) { + if(!run) + return; + + if(!force) + SendMessage(sock, addr, cl_kill()); + + ClosePort(sock); + + run = false; +} + +bool cl_isrun() { + return run; +} diff --git a/client.h b/client.h new file mode 100644 index 0000000..1a85a23 --- /dev/null +++ b/client.h @@ -0,0 +1,9 @@ +#include + +extern int cl_playerid; + +void cl_connect(const char * host, uint16_t port); +void cl_disconnect(bool force); +void cl_move(DoesCode code); +void cl_recv(); +bool cl_isrun(); diff --git a/dedicated.c b/dedicated.c new file mode 100644 index 0000000..bef813c --- /dev/null +++ b/dedicated.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include + +#include "server.h" +#include "protocol.h" + +static uint16_t port = DEFAULT_PORT; + +static void argverr() { + SDL_Log("Invalid argument\n"); + exit(1); +} + +static void useargs(char ** argv) { + int i = 1; + while(argv[i]) { + if(strcmp(argv[i], "-p") == 0) { + if(argv[++i]) + port = atoi(argv[i++]); + else + argverr(); + } else { + argverr(); + } + } +} + +static void sysinit() { + if(SDL_Init(SDL_INIT_EVENTS) != 0) { + SDL_Log("Unable to initialize SDL: %s\n", SDL_GetError()); + exit(1); + } + atexit(SDL_Quit); + + if(SDLNet_Init() != 0) { + SDL_Log("Unable to initialize SDLNet: %s\n", SDLNet_GetError()); + exit(1); + } + atexit(SDLNet_Quit); +} + +int main(int argc, char ** argv) { + sysinit(); + useargs(argv); + + sv_start(port); + sv_setclientlimit(32); + + bool run = true; + while(run) { + SDL_Event event; + while(SDL_PollEvent(&event)) + if(event.type == SDL_QUIT) + run = false; + + sv_handle(); + + SDL_Delay(1); + } + + sv_stop(); + + return 0; +} diff --git a/game.c b/game.c new file mode 100644 index 0000000..cbc8e15 --- /dev/null +++ b/game.c @@ -0,0 +1,65 @@ +#include + +#include +#include +#include + +#include "game.h" + +#define SPEED 0.00006 +#define ROTATE 0.00004 + +Player g_player[MAX_PLAYERS]; + +static void moveplayer(int id, float speed) { + g_player[id].vx += speed * cos(g_player[id].r * 2 * M_PI); + g_player[id].vy += speed * sin(g_player[id].r * 2 * M_PI); +} + +static void checkspacebound(float * a) { + float x = *a; + if(x < -1) + x = abs(x); + else if(x > 1) + x = -x; + *a = x; +} + +void g_player_set(unsigned int id, bool live, float x, float y, float r, float vx, float vy, float vr) { + assert(id < MAX_PLAYERS); + g_player[id] = (Player) { + .updated = true, + .live = live, + .x = x, + .y = y, + .r = r, + .vx = vx, + .vy = vy, + .vr = vr, + }; +} + +void g_player_does(unsigned int id, DoesCode code) { + assert(id < MAX_PLAYERS); + g_player[id].updated = true; + switch(code) { + case DOES_UP: moveplayer(id, SPEED); break; + case DOES_DOWN: moveplayer(id, -SPEED); break; + case DOES_LEFT: g_player[id].vr -= ROTATE; break; + case DOES_RIGHT: g_player[id].vr += ROTATE; break; + case DOES_FIRE: break; + } +} + +void g_update() { + for(int i = 0; i < MAX_PLAYERS; i++) { + if(g_player[i].live) { + g_player[i].updated = true; + g_player[i].x += g_player[i].vx; + g_player[i].y += g_player[i].vy; + g_player[i].r += g_player[i].vr; + checkspacebound(&g_player[i].x); + checkspacebound(&g_player[i].y); + } + } +} diff --git a/game.h b/game.h new file mode 100644 index 0000000..17c2b68 --- /dev/null +++ b/game.h @@ -0,0 +1,23 @@ +#include + +#include "protocol.h" + +#define MAX_PLAYERS 64 +#define TICK_DELAY (1000 / 60) +#define PLAYER_SIZE 0.01785 + +typedef struct Player { + /* public */ + bool live; + float x, y, r; + float vx, vy, vr; + + /* internal */ + bool updated; +} Player; + +extern Player g_player[MAX_PLAYERS]; + +void g_player_set(unsigned int id, bool live, float x, float y, float r, float vx, float vy, float vr); +void g_player_does(unsigned int id, DoesCode code); +void g_update(); diff --git a/makefile b/makefile new file mode 100644 index 0000000..973189b --- /dev/null +++ b/makefile @@ -0,0 +1,18 @@ +ifdef WIN32 + CC := x86_64-w64-mingw32-gcc + CFLAGS := -g -std=gnu11 -Wall + LDFLAGS := -lmingw32 -lSDL2main -lSDL2 -lSDL2_net -lm + OUTPOSTFIX := .exe +else + CC := gcc + CFLAGS := -g -std=gnu11 -Wall + LDFLAGS := -lSDL2_net -lSDL2 -lm +endif + +all: dedicated.o netwar.o + +netwar.o: + $(CC) $(CFLAGS) -o netwar$(OUTPOSTFIX) netwar.c client.c network.c game.c $(LDFLAGS) + +dedicated.o: + $(CC) $(CFLAGS) -o dedicated$(OUTPOSTFIX) dedicated.c server.c network.c game.c $(LDFLAGS) diff --git a/netwar.c b/netwar.c new file mode 100644 index 0000000..2419456 --- /dev/null +++ b/netwar.c @@ -0,0 +1,165 @@ +#include +#include +#include +#include +#include + +#include "game.h" +#include "client.h" +#include "protocol.h" + +#define WIDTH 640 +#define HEIGHT 480 + +static const float ship[] = { + +1.0, +0.0, -1.0, -1.0, + -1.0, -1.0, +0.0, +0.0, + +0.0, +0.0, -1.0, +1.0, + -1.0, +1.0, +1.0, +0.0, +}; + +static char * host = "localhost"; +static char * nick = "Anonymous"; +static uint16_t port = DEFAULT_PORT; + +static SDL_Window * window; +static SDL_Renderer * renderer; + +static void argverr() { + SDL_Log("Invalid argument\n"); + exit(1); +} + +static void useargs(char ** argv) { + int i = 1; + while(argv[i]) { + if(strcmp(argv[i], "-h") == 0) { + if(argv[++i]) + host = argv[i++]; + else + argverr(); + } else if(strcmp(argv[i], "-p") == 0) { + if(argv[++i]) + port = atoi(argv[i++]); + else + argverr(); + } else if(strcmp(argv[i], "-l") == 0) { + if(argv[++i]) + nick = argv[i++]; + else + argverr(); + } else { + argverr(); + } + } +} + +static void sysinit() { + if(SDL_Init(SDL_INIT_EVERYTHING) != 0) { + SDL_Log("Unable to initialize SDL: %s\n", SDL_GetError()); + exit(1); + } + atexit(SDL_Quit); + + if(SDLNet_Init() != 0) { + SDL_Log("Unable to initialize SDLNet: %s\n", SDLNet_GetError()); + exit(1); + } + atexit(SDLNet_Quit); +} + +static void openwindow() { + SDL_CreateWindowAndRenderer(WIDTH, HEIGHT, 0, &window, &renderer); + SDL_RenderPresent(renderer); +} + +static void closewindow() { + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); +} + +static void paintwindow() { + SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00); + SDL_RenderClear(renderer); + + int cx = WIDTH / 2; + int cy = HEIGHT / 2; + int shipsz = (WIDTH + HEIGHT) / 2 * PLAYER_SIZE; + + for(int i = 0; i < MAX_PLAYERS; i++) { + if(i == cl_playerid) + SDL_SetRenderDrawColor(renderer, 0x00, 0xFF, 0x00, 0x00); + else + SDL_SetRenderDrawColor(renderer, 0xFF, 0x00, 0x00, 0x00); + + int count = sizeof(ship) / sizeof(ship[0]) / 2; + int x = g_player[i].x * WIDTH / 2; + int y = g_player[i].y * HEIGHT / 2; + + SDL_Point pixship[count]; + for(int j = 0; j < count * 2; j += 2) { + float c = cos(g_player[i].r * 2 * M_PI); + float s = sin(g_player[i].r * 2 * M_PI); + float rx = ship[j + 0] * shipsz; + float ry = ship[j + 1] * shipsz; + pixship[j / 2].x = cx + x + (c * rx - s * ry); + pixship[j / 2].y = cy + y + (s * rx + c * ry); + } + + if(g_player[i].live) + SDL_RenderDrawLines(renderer, pixship, count); + } + + SDL_RenderPresent(renderer); +} + +static void keyboardhandle() { + SDL_PumpEvents(); + const Uint8 * key = SDL_GetKeyboardState(NULL); + if(key[SDL_SCANCODE_UP]) + cl_move(DOES_UP); + if(key[SDL_SCANCODE_DOWN]) + cl_move(DOES_DOWN); + if(key[SDL_SCANCODE_LEFT]) + cl_move(DOES_LEFT); + if(key[SDL_SCANCODE_RIGHT]) + cl_move(DOES_RIGHT); + if(key[SDL_SCANCODE_LCTRL]) + cl_move(DOES_FIRE); +} + +int main(int argc, char ** argv) { + sysinit(); + useargs(argv); + openwindow(); + + cl_connect(host, port); + + Uint32 lastTime = SDL_GetTicks(); + Uint32 currTime = SDL_GetTicks(); + + while(cl_isrun()) { + SDL_Event event; + while(SDL_PollEvent(&event)) + if(event.type == SDL_QUIT) + goto cleanup; + + if(currTime - lastTime >= TICK_DELAY) { + keyboardhandle(); + g_update(); + lastTime = SDL_GetTicks(); + } + currTime = SDL_GetTicks(); + + cl_recv(); + + paintwindow(); + + SDL_Delay(1); + } + +cleanup: + closewindow(); + cl_disconnect(false); + return 0; +} diff --git a/network.c b/network.c new file mode 100644 index 0000000..45adb04 --- /dev/null +++ b/network.c @@ -0,0 +1,56 @@ +#include "network.h" + +void SendMessage(UDPsocket sock, IPaddress address, ProtocolMessage m) { + UDPpacket packet = { + .channel = -1, + .data = (void *) &m, + .len = MessageSize(m), + .maxlen = sizeof(ProtocolMessage), + .status = 0, + .address = address, + }; + + if(!SDLNet_UDP_Send(sock, -1, &packet)) { + SDL_Log("Messge not sended: %s\n", SDLNet_GetError()); + exit(1); + } +} + +bool RecvMessage(UDPsocket sock, IPaddress * address, ProtocolMessage * m) { + UDPpacket packet = { + .channel = -1, + .data = (void *) m, + .len = sizeof(ProtocolMessage), + .maxlen = sizeof(ProtocolMessage), + .status = 0, + .address = (IPaddress) { 0, 0 }, + }; + + int status; + status = SDLNet_UDP_Recv(sock, &packet); + if(status < 0) + goto error_recv; + else if(status == 0) + goto error_norecv; + + if(address) + *address = packet.address; + + return true; + +error_recv: + SDL_Log("Messge not received: %s\n", SDLNet_GetError()); + exit(1); +error_norecv: + return false; +} + +bool WaitMessage(UDPsocket sock, IPaddress * address, ProtocolMessage * m, uint32_t timeout) { + uint32_t lastTime = SDL_GetTicks(); + while(SDL_GetTicks() - lastTime < timeout) { + if(RecvMessage(sock, address, m)) + return true; + SDL_Delay(1); + } + return false; +} diff --git a/network.h b/network.h new file mode 100644 index 0000000..3dbcc65 --- /dev/null +++ b/network.h @@ -0,0 +1,26 @@ +#ifndef NETWORK_H_INCLUDED +#define NETWORK_H_INCLUDED + +#include +#include + +#include "protocol.h" + +static inline UDPsocket OpenPort(uint16_t port) { + UDPsocket sock = SDLNet_UDP_Open(SDL_SwapLE16(port)); + if(!sock) { + SDL_Log("SDLNet_UDP_Open: %s\n", SDLNet_GetError()); + exit(1); + } + return sock; +} + +static inline void ClosePort(UDPsocket sock) { + SDLNet_UDP_Close(sock); +} + +void SendMessage(UDPsocket sock, IPaddress address, ProtocolMessage m); +bool RecvMessage(UDPsocket sock, IPaddress * address, ProtocolMessage * m); +bool WaitMessage(UDPsocket sock, IPaddress * address, ProtocolMessage * m, uint32_t timeout); + +#endif /* NETWORK_H_INCLUDED */ diff --git a/protocol.h b/protocol.h new file mode 100644 index 0000000..2681553 --- /dev/null +++ b/protocol.h @@ -0,0 +1,167 @@ +#ifndef PROTOCOL_H_INCLUDED +#define PROTOCOL_H_INCLUDED + +#include +#include +#include + +#define DEFAULT_PORT 29386 +#define PROTOCOL_VERSION 0 +#define PROTOCOL_F8FRAC (1 << 7) + +#define PACKED __attribute__((__packed__)) + +typedef enum { + INVALID = 0, + + CL_INFO = 1, + CL_KILL = 2, + CL_DOES = 3, + + SV_INFO = 128, + SV_KILL = 139, + SV_SPLR = 130, +} MessageType; + +typedef enum { + DOES_UP = 0, + DOES_DOWN = 1, + DOES_LEFT = 2, + DOES_RIGHT = 3, + DOES_FIRE = 4, +} DoesCode; + +typedef struct PACKED ClInfo { + uint8_t type; + uint8_t version; + uint8_t name[32]; +} ClInfo; + +typedef struct PACKED ClKill { + uint8_t type; +} ClKill; + +typedef struct PACKED ClDoes { + uint8_t type; + uint8_t code; +} ClDoes; + +typedef struct PACKED SvInfo { + uint8_t type; + uint8_t version; + uint8_t clientid; + uint8_t maxclients; + uint8_t name[32]; +} SvInfo; + +typedef struct PACKED SvKill { + uint8_t type; + uint8_t message[256]; +} SvKill; + +typedef struct PACKED SvSplr { + uint8_t type; + uint8_t clid; + uint8_t live; + uint8_t x, y, r; + uint8_t vx, vy, vr; +} SvSplr; + +typedef union ClMessage { + uint8_t type; + ClInfo info; + ClKill kill; + ClDoes does; +} ClMessage; + +typedef union SvMessage { + uint8_t type; + SvInfo info; + SvKill kill; + SvSplr splr; +} SvMessage; + +typedef union ProtocolMessage { + uint8_t type; + ClMessage cl; + SvMessage sv; +} ProtocolMessage; + +static inline int8_t f2b(float x) { return x * PROTOCOL_F8FRAC; } + +static inline float b2f(int8_t x) { return (float) x / PROTOCOL_F8FRAC; } + +static inline int MessageTypeSize(MessageType type) { + switch(type) { + case CL_INFO: return sizeof(ClInfo); + case CL_KILL: return sizeof(ClKill); + case CL_DOES: return sizeof(ClDoes); + case SV_INFO: return sizeof(SvInfo); + case SV_KILL: return sizeof(SvKill); + case SV_SPLR: return sizeof(SvSplr); + default: return sizeof(ProtocolMessage); + } +} + +static inline int MessageSize(ProtocolMessage m) { + return MessageTypeSize(m.type); +} + +static inline ProtocolMessage cl_info(const char * name) { + ClInfo m = { + .type = CL_INFO, + .version = PROTOCOL_VERSION, + }; + strncpy((char*)m.name, name, sizeof(m.name)); + return (ProtocolMessage) (ClMessage) m; +} + +static inline ProtocolMessage cl_kill() { + return (ProtocolMessage) (ClMessage) (ClKill) { + .type = CL_KILL, + }; +} + +static inline ProtocolMessage cl_does(DoesCode code) { + return (ProtocolMessage) (ClMessage) (ClDoes) { + .type = CL_DOES, + .code = code, + }; +} + +static inline ProtocolMessage sv_info(const char * name, int clientid, int maxclients) { + SvInfo m = { + .type = SV_INFO, + .version = PROTOCOL_VERSION, + .clientid = clientid, + .maxclients = maxclients, + }; + strncpy((char*) m.name, name, sizeof(m.name)); + return (ProtocolMessage) (SvMessage) m; +} + +static inline ProtocolMessage sv_kill(const char * msg) { + SvKill m = { + .type = SV_KILL, + }; + strncpy((char*) m.message, msg, sizeof(m.message)); + return (ProtocolMessage) (SvMessage) m; +} + +static inline ProtocolMessage sv_splr(int clid, bool live, float x, float y, float r, float vx, float vy, float vr) { + return (ProtocolMessage) (SvMessage) (SvSplr) { + .type = SV_SPLR, + .clid = clid, + .live = live, + .x = f2b(x), + .y = f2b(y), + .r = f2b(r), + .vx = f2b(vx), + .vy = f2b(vy), + .vr = f2b(vr), + }; +} + +#undef PACKED + +#endif /* PROTOCOL_H_INCLUDED */ diff --git a/server.c b/server.c new file mode 100644 index 0000000..ea28583 --- /dev/null +++ b/server.c @@ -0,0 +1,148 @@ +#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)); +} + +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); + 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); + + g_player_does(id, m.cl.does.code); +} + +static void sv_register_player(IPaddress address, ProtocolMessage m) { + if(m.cl.info.version != PROTOCOL_VERSION) + SendMessage(sock, address, sv_kill("Uncompatible version")); + + int i = 0; + while((i < clientlimit) && (client[i].connected)) + ++i; + + int namesize = sizeof(m.cl.info.name); + if(i >= clientlimit) { + SDL_Log("Too may clints, player %.*s disconnected", namesize, m.cl.info.name); + SendMessage(sock, address, sv_kill("Too many clients")); + return; + } + + client[i].connected = true; + client[i].address = address; + strncpy(client[i].name, (char*) m.cl.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)); + + g_player_set(i, true, randfm(), randfm(), randfm(), 0, 0, 0); + sv_sync_client(i, true); +} + +static void sv_recv() { + IPaddress address; + ProtocolMessage m; + if(!RecvMessage(sock, &address, &m)) + return; + + switch(m.type) { + case CL_INFO: sv_register_player(address, m); break; + case CL_KILL: sv_kill_player(sv_find_client(address), false, ""); break; + case CL_DOES: sv_move_player(sv_find_client(address), m); break; + default: SDL_Log("invalid message %i", m.type); + } +} + +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; +} + +void sv_start(uint16_t port) { + 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(); +} diff --git a/server.h b/server.h new file mode 100644 index 0000000..895062c --- /dev/null +++ b/server.h @@ -0,0 +1,7 @@ +#include + +void sv_start (uint16_t port); +void sv_stop (); +void sv_handle (); + +void sv_setclientlimit(unsigned int maxclients); diff --git a/start.bat b/start.bat new file mode 100644 index 0000000..3689d29 --- /dev/null +++ b/start.bat @@ -0,0 +1 @@ +netwar.exe -h succubus.deadsoftware.ru -- 2.29.2