From 51177e3a198676e7493815746d95fe04f4399d46 Mon Sep 17 00:00:00 2001 From: 0xdeadbeer <64986162+0xdeadbeer@users.noreply.github.com> Date: Sat, 18 Nov 2023 11:55:22 +0100 Subject: [PATCH] 03: migrate player control to the server --- .gitignore | 2 + 02-jumping/src/main.c | 3 +- 03-network/client/Makefile | 28 +++ 03-network/client/README.md | 7 + 03-network/client/assets/player/idle.png | Bin 0 -> 1438 bytes 03-network/client/assets/player/jump.png | Bin 0 -> 1021 bytes 03-network/client/assets/player/land.png | Bin 0 -> 1500 bytes 03-network/client/assets/player/run.png | Bin 0 -> 3254 bytes 03-network/client/src/main.c | 264 +++++++++++++++++++++++ 03-network/client/src/structs.h | 40 ++++ 03-network/script | 4 + 03-network/server/Makefile | 28 +++ 03-network/server/README.md | 3 + 03-network/server/src/defs.h | 20 ++ 03-network/server/src/main.c | 181 ++++++++++++++++ 03-network/server/src/structs.h | 20 ++ 16 files changed, 598 insertions(+), 2 deletions(-) create mode 100644 03-network/client/Makefile create mode 100644 03-network/client/README.md create mode 100644 03-network/client/assets/player/idle.png create mode 100644 03-network/client/assets/player/jump.png create mode 100644 03-network/client/assets/player/land.png create mode 100644 03-network/client/assets/player/run.png create mode 100644 03-network/client/src/main.c create mode 100644 03-network/client/src/structs.h create mode 100644 03-network/script create mode 100644 03-network/server/Makefile create mode 100644 03-network/server/README.md create mode 100644 03-network/server/src/defs.h create mode 100644 03-network/server/src/main.c create mode 100644 03-network/server/src/structs.h diff --git a/.gitignore b/.gitignore index c023ce9..e7208f7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .ccls-cache/ +.cache/ build/ *.o +compile_commands.json diff --git a/02-jumping/src/main.c b/02-jumping/src/main.c index a2a2563..d0763e6 100644 --- a/02-jumping/src/main.c +++ b/02-jumping/src/main.c @@ -10,7 +10,6 @@ const Uint32 RENDERER_FLAGS = SDL_RENDERER_ACCELERATED; int GAME_RUNNING = 1; struct game game; int movement_speed = 10; -int gravity = 8; double delta_time; double last_frame; @@ -169,7 +168,7 @@ int main(int argc, char *argv[]) { switch_animation(player, idle_animation); if (game.jump) { switch_animation(player, jump_animation); - player->force -= movement_speed * 2.5; + player->force -= movement_speed * 3; player->y += player->force; player->colliding = 0; } diff --git a/03-network/client/Makefile b/03-network/client/Makefile new file mode 100644 index 0000000..5545841 --- /dev/null +++ b/03-network/client/Makefile @@ -0,0 +1,28 @@ +CC=gcc +CFLAGS=`pkg-config --cflags sdl2 SDL2_image SDL2_net` +LDFLAGS=`pkg-config --libs sdl2 SDL2_image SDL2_net` +TARGET=client +SDIR=src +ADIR=assets +ODIR=build + +SRC=$(shell find $(SDIR) -type f -name *.c) +OBJ=$(SRC:.c=.o) + +all: $(TARGET) + +.PHONY: default +$(TARGET): $(OBJ) + mkdir -p build + cp -rf $(ADIR) $(ODIR)/$(ADIR) + $(CC) -o $(ODIR)/$@ $^ $(LDFLAGS) + +%.o: %.c + $(CC) $(CFLAGS) -o $@ -c $< + +run: + $(ODIR)/$(TARGET) + +.PHONY: clean +clean: + rm -f $(ODIR)/$(TARGET) $(OBJ) diff --git a/03-network/client/README.md b/03-network/client/README.md new file mode 100644 index 0000000..0cedb97 --- /dev/null +++ b/03-network/client/README.md @@ -0,0 +1,7 @@ +

+ +

+ +# Network - Client + +Client-side code for 03-network diff --git a/03-network/client/assets/player/idle.png b/03-network/client/assets/player/idle.png new file mode 100644 index 0000000000000000000000000000000000000000..4cf0c430a8cdd6b2418cdb6c35eb528bf38bc103 GIT binary patch literal 1438 zcmZ`(eK?Z~6n~fR$}K7LSefyMqO1=^O@_?3iKmA7EMhnAaFLYjmFTWibxW#4gw0sial6{GMohW0g#y_ax$RPQqBq^C++qK&QfaxVk>aaDD-71{#s$7ZntMM?z}#YF$cJ_4(ME=5W)oA}K7 zKcRapdwadyCJKkC(iu_{CJI>>JjCs7Vx{OzkU& z>I7{?8rW`yRIqeHYFI+baL;FniXFfLnu551J;P4((<+IFVKEGVNrE?K$x}vRjS8N4 zW~ZZr8qRW%)G;M8NM|twDU}N?VZO<46Ykh>5<;gVy=Ar;!&^Z)kmJ;QdBGt#4wqyH z_UxAQ5Oo3-l>w$|cD{Z(6kVbo=~Sc!IJ?nsfBbDo8Fpjz;aF8mEr4P$sbAZ=!l*)U z*&T&BG?fIat5wsobP-dJd8+Xq#;CGwGPQQhE=~vG2h22O;BK(oai(3G7>si0<7D5z_ zw&P-7Mf~{FJ(1mH6DpFN-uQ+ADk6oSwq?Es*a@SqX5&WmN$=hyJ#cj&+@lEbTYOYQAFX8_J?b-rx*WjLucp={74?QYlNas99`2fjk-Jb3;3n zW8;87BSs5eelVN2)fXB0>Ch}AgsFJ{zP`G&m$WiXMRk%6&VOi*g}I9}s55GT6r^Z= zRBNvO3{^V6OFwUMw5*FYXU$&KH(4FzV>ZMlz!?ks%tNgSn#Qt@>4eckZ%r@tb7r0LPG`;caLV+IoBOW}hlUHEdtXC2Jh@WEm z?3Wn@7BxwnLwq5+AffrvwOLYwSok|p6C=YGY8#%url?19l}@C2*ATCo=Ih=ui*OVu zx;bLCGD|7MHzkCIZ@gRVjmEQ7U} zLs5iSL^ysQndjn_Z_)cK!3y?8`13c+Y_To1lzKf`rD(zM^$i(L=C%6^9Hexzmp9TP7J z3x2D_5a!4t4@Lw;++IjPL?TE)*M!XouFOAKKtr?u-6Zv(YkUXdi)sf zak0Mh>nO11+UfNJza4o$n<0Rk1Z07%>MV|KH|Cnd*}PH={w!JoU_k}q;T@2{2S2OR zD+a#QyBe@rHR$i1USRL^qA`5(r#B^2wb1c{XQEaxHcV>?NY(}bx#42)8SNHPEAhx+ zwgbTUmm?2ozQJTIB3(a3CTazbJxT1)Zea|u@~8|2{NOX;WZ4432(zCtQ7d>^3MYza z`pO61;cUJh#UB&2?YppT6D78R|4^6h|6#?IDsW4&75XzcI( zI|u+6TJ`se!ZF#4lOpqh+0S5p*beUt&(-{yaUWs}2=GNDGqzgZXbL{yCx91`EdrAH za5aCX+;6jxRN?`E{`@|6r*cN^Po;%Do_#4@uM~^*CnFzWVt7lNu6Os9ze}qfq(y&z zAN~3LRNk#6AiixSnJ2yu4LOwnk>Ay7)+Q(!7+wzKMJ61R2SEE47DYJB3DHoS125zG z=NfjWa)C7$d|>gvJRb?fU-ZQUv9Iw_V7=DGio;H>%!gN%iq-8`MjGM)49-BT&x$2f zCLY@o5T)$9hFE?0!P#ULO;mw=BX5G;-Lu&+QdMkLz^zYNPI6;W(H&45@yQ*hzGvh%)2y re59^(3$vL{@`WG>f*=TjXb=AZ>!Ac3Jx|zZ00000NkvXXu0mjfKxWs7 literal 0 HcmV?d00001 diff --git a/03-network/client/assets/player/land.png b/03-network/client/assets/player/land.png new file mode 100644 index 0000000000000000000000000000000000000000..2c24d6f4357ea83410f70d7fd04f5f77d36e323c GIT binary patch literal 1500 zcmZ`(c{J2}6rbS{W2x*>ae5=$WV|G@{V3aTh>@|yj1p0{q{&!jBIjj&mhfcI3rEw# zSfVC0<(ZPPq%vihC>}BlW-y?f66-h0knKKI`5_j_}FecV-Gx-bX?qJr{p zK|>(WHmPo*ATK==bCGUR0Zm4`pMhOwrG_(ac=d^&;s`cuczD`3a`2UekIYSi!vEhE_2gXUZ z`>2lA0aVUl!ipl4)M2G7&jk5- zueX+{Q<$RXgv^4)z7J56y{nwDTSZwLnds}I^b+R)xVg)oy3(v|zyYCV7jcl{V&`eH zR<{#p__VAnX<$KShGU_|+1i_^Y~F*;c^AEn#V&j7CST{#FtLYZ?zhKk-Ij#JNP( z0#rfL*Ot#*>Q^=%>3MH6XcTBxXbiDG@Fysm(P9h(sEC_y+^!6nanw)#nvb76OcW$m<%w>4#SF}U6? zU{5N6f*MeM5tjKeBh?%Cg;(DoU$P~Pfbxv8JqVlK6rBSfu8cg;TA(KLf6!HWVP3ia>!?%Bg zr!k$`xxOQaTe2s*&BU&(qC1oCXiBAn>$l@^t}2&aYq8$^?S4Tr<@tD`6H7WC zc&)=Z)T#7X3O5U_WHeguvNI@JejLy+hL92bmcyjF!hGCjpG-4Ayy zN%xK*Oj;}g15ohn=jPth?HF$`4>CKR~#|pOGo;Zn#Bt(0*3Jr z7c+#-+>V%CZ`$6*aKHl+8zHKVzWGp)VWSBDvJ-9nWcDR_kj1y73x#o9vS?jEPbL!E zC-Q11@w)sm?2~FutFt^j`I~dUCCN|s0~Wam-|BB-J)6m&)7Gyub1_jq$N^8Ks^f_B zG4ayxW2?#p^YbLa#vXQhYR)a;gt4hu90y1e1cL8TX?D4*=ow-8u{LQVISfmC*~Aeh zO600g?G$ut5L*)=9n`mCx^2CNfeHH%VT!kOp^^al51BJYu&<_ zd3BNjmEo}7p3TiqK}7Q|Z70)kXDr_zJ7Or!$XoQZ~kYxLu?(@_xT#dDwzVc3n=W^8ylyaMDlb zU2c$pm(XK1UK42TKTk5v8%yXN5+8>59J`N}m7O{n=;UA38v!*Poeg&GiPPr=CIoHn zl+f*^uer=Ylf|#^MZbS0gu+wg$J-oycQRlC$^4?!q4%iVEn!j#o!l`}DLh~lk4t5A urjx%H1FaTxt0ohAmlSPm_kZleXTci)B8?P-SwXx1Fr!?3Tv|`!vi<>k{PDv8 literal 0 HcmV?d00001 diff --git a/03-network/client/assets/player/run.png b/03-network/client/assets/player/run.png new file mode 100644 index 0000000000000000000000000000000000000000..615b2229f81fa53516db0910b73d045122d5c768 GIT binary patch literal 3254 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 +#include +#include +#include +#include "structs.h" + +#define DEBUG 1 +#define SERVER_ADDR "127.0.0.1" +#define SERVER_PORT 9080 +#define SERVER_MESSAGE_LEN 10 // 0-1 BYTE: Action details, 2-9: Other data (not sure we even need this) +#define CLIENT_MESSAGE_LEN 4 // 4 PROPERTIES (each of 4 BYTES) + +const int SCREEN_WIDTH = 1000; +const int SCREEN_HEIGHT = 480; +const Uint32 WINDOW_FLAGS = SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL; +const Uint32 RENDERER_FLAGS = SDL_RENDERER_ACCELERATED; +int GAME_RUNNING = 1; +struct game game; +int movement_speed = 10; +double delta_time; +double last_frame; + +// network +TCPsocket server_socket; + +// scene +struct object *player; + +void prepare_scene(void) { + SDL_SetRenderDrawColor(game.renderer, 96, 128, 255, 255); + SDL_RenderClear(game.renderer); +} + +void key(SDL_KeyboardEvent *event) { + if (event->repeat != 0) + return; + + if (event->keysym.scancode == SDL_SCANCODE_J) + game.left = !game.left; + if (event->keysym.scancode == SDL_SCANCODE_K) + game.right = !game.right; + if (event->keysym.scancode == SDL_SCANCODE_SPACE) + game.jump = !game.jump; +} + +void handle_input(void) { + SDL_Event event; + + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + GAME_RUNNING = 0; + break; + case SDL_KEYDOWN: + case SDL_KEYUP: + key(&event.key); + break; + default: + break; + } + if (event.type == SDL_QUIT) { + GAME_RUNNING = 0; + } + } +} + +SDL_Texture *load_texture(const char *path) { + SDL_Texture *texture; + + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Loading %s", path); + + texture = IMG_LoadTexture(game.renderer, path); + + return texture; +} + +void switch_animation(struct object *object, SDL_Texture *animation) { + if (object->texture == animation) + return; + + object->animation_slide = 0; + object->texture = animation; +} + +struct object *create_object(SDL_Texture *texture, int scale, int resolution) { + struct object *object = (struct object *) calloc(1, sizeof(struct object)); + + if (object == NULL) { + fprintf(stderr, "Error: failed allocating memory for new object\n"); + return NULL; + } + + object->texture = texture; + object->resolution = resolution; + object->animation_speed = 13; + object->scale = scale; + + return object; +} + +void draw_object(struct object *object) { + int texture_width; + SDL_Rect src; + SDL_Rect dest; + + src.x = object->animation_slide * object->resolution; + src.y = 0; + + src.w = object->resolution; + src.h = object->resolution; + + dest.x = object->x; + dest.y = object->y; + dest.w = object->resolution * object->scale; + dest.h = object->resolution * object->scale; + + SDL_RenderCopyEx(game.renderer, object->texture, &src, &dest, 0, NULL, object->flip); + + // update animation slide + SDL_QueryTexture(object->texture, NULL, NULL, &texture_width, NULL); + + object->animation_clock += object->animation_speed*(delta_time/1000); + + if (object->animation_clock >= 1) { + object->animation_clock = 0; + object->animation_slide = (object->animation_slide+1) % (texture_width / object->resolution); // clock arithmetic: jump back to first animation slide + } +} + +void present_scene(void) { + SDL_RenderPresent(game.renderer); +} + +int handle_server(void *data) { + for (;;) { + int message[CLIENT_MESSAGE_LEN]; + int recv_len = SDLNet_TCP_Recv(server_socket, message, sizeof(int)*CLIENT_MESSAGE_LEN); + + if (!recv_len) { + fprintf(stderr, "Error: failed receiving message from server\n%s\n", SDLNet_GetError()); + break; + } + + if (DEBUG) + fprintf(stdout, "Notice: received '%d' bytes from server\n", recv_len); + + player->x = message[0]; + player->y = message[1]; + player->colliding = message[2]; + player->force = message[3]; + } + + return 0; +} + +int connect_to_server(void) { + IPaddress ip; + if (SDLNet_ResolveHost(&ip, SERVER_ADDR, SERVER_PORT) != 0) { + fprintf(stderr, "Error: resolving host of server\n%s\n", SDLNet_GetError()); + return -1; + } + + server_socket = SDLNet_TCP_Open(&ip); + if (!server_socket) { + fprintf(stderr, "Error: failed opening socket to '%s' at '%d'\n%s\n", SERVER_ADDR, SERVER_PORT, SDLNet_GetError()); + return -1; + } + + SDL_CreateThread(handle_server, "server", NULL); + + return 0; +} + +int main(int argc, char *argv[]) { + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + fprintf(stderr, "Error: could not initialize SDL\n%s\n", SDL_GetError()); + return -1; + } + + if (SDLNet_Init() != 0) { + fprintf(stderr, "Error: could not initialize SDL net\n%s\n", SDL_GetError()); + return -1; + } + + game.window = SDL_CreateWindow("03-network", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, WINDOW_FLAGS); + + if (game.window == NULL) { + fprintf(stderr, "Error: could not create window\n%s\n", SDL_GetError()); + return -1; + } + + game.renderer = SDL_CreateRenderer(game.window, -1, RENDERER_FLAGS); + + if (game.renderer == NULL) { + fprintf(stderr, "Error: could not create renderer\n%s\n", SDL_GetError()); + return -1; + } + + // load player animations + SDL_Texture *idle_animation = load_texture("assets/player/idle.png"); + SDL_Texture *jump_animation = load_texture("assets/player/jump.png"); + SDL_Texture *run_animation = load_texture("assets/player/run.png"); + + player = create_object(idle_animation, 4, 48); + + if (connect_to_server() != 0) + return -1; + + // game loop + while (GAME_RUNNING) { + double current_frame = SDL_GetTicks(); + delta_time = current_frame - last_frame; + last_frame = current_frame; + + prepare_scene(); + handle_input(); + + // The reason why I don't do + // that much game development + // in my free time. Damn youuu ifs + if (player->colliding) { + if (game.left) + switch_animation(player, run_animation); + if (game.right) + switch_animation(player, run_animation); + if (!game.left && !game.right) + switch_animation(player, idle_animation); + if (game.jump) { + switch_animation(player, jump_animation); + + uint8_t message[SERVER_MESSAGE_LEN]; + message[0] = 2; + + SDLNet_TCP_Send(server_socket, message, sizeof(uint8_t)*SERVER_MESSAGE_LEN); + } + } + + if (game.left) { + player->flip = SDL_FLIP_HORIZONTAL; + uint8_t message[SERVER_MESSAGE_LEN]; + message[0] = 1; + + SDLNet_TCP_Send(server_socket, message, sizeof(uint8_t)*SERVER_MESSAGE_LEN); + } + if (game.right) { + player->flip = SDL_FLIP_NONE; + uint8_t message[SERVER_MESSAGE_LEN]; + message[0] = 0; + + SDLNet_TCP_Send(server_socket, message, sizeof(uint8_t)*SERVER_MESSAGE_LEN); + } + + draw_object(player); + + present_scene(); + + SDL_Delay(16); + } + + SDL_DestroyWindow(game.window); + SDL_Quit(); + + return 0; +} diff --git a/03-network/client/src/structs.h b/03-network/client/src/structs.h new file mode 100644 index 0000000..dc0af64 --- /dev/null +++ b/03-network/client/src/structs.h @@ -0,0 +1,40 @@ +#ifndef STRUCTS_H +#define STRUCTS_H + +#include + +struct game { + SDL_Window *window; + SDL_Renderer *renderer; + + int left; + int right; + int jump; +}; + +struct animation { + SDL_Texture *tilemap; + int resolution; + + int skippable; +}; + +struct object { + SDL_Texture *texture; + int resolution; // of the texture/every tile + + int x; + int y; + int scale; + Uint32 flip; + + // animation + double animation_clock; // everytime it reaches 1, the texture switches to the next slide + double animation_speed; // how fast will the clock reach 1 with respect to delta_time + int animation_slide; // the current slide of the animation + + int colliding; + int force; +}; + +#endif diff --git a/03-network/script b/03-network/script new file mode 100644 index 0000000..71c81da --- /dev/null +++ b/03-network/script @@ -0,0 +1,4 @@ +Message: + * 10 Bytes long + * 0-1 byte: action + * 2-10 byte: additional data diff --git a/03-network/server/Makefile b/03-network/server/Makefile new file mode 100644 index 0000000..a606484 --- /dev/null +++ b/03-network/server/Makefile @@ -0,0 +1,28 @@ +CC=gcc +CFLAGS=`pkg-config --cflags sdl2 SDL2_net` +LDFLAGS=`pkg-config --libs sdl2 SDL2_net` +TARGET=server +SDIR=src +ADIR=assets +ODIR=build + +SRC=$(shell find $(SDIR) -type f -name *.c) +OBJ=$(SRC:.c=.o) + +all: $(TARGET) + +.PHONY: default +$(TARGET): $(OBJ) + mkdir -p build + cp -rf $(ADIR) $(ODIR)/$(ADIR) + $(CC) -o $(ODIR)/$@ $^ $(LDFLAGS) + +%.o: %.c + $(CC) $(CFLAGS) -o $@ -c $< + +run: + $(ODIR)/$(TARGET) + +.PHONY: clean +clean: + rm -f $(ODIR)/$(TARGET) $(OBJ) diff --git a/03-network/server/README.md b/03-network/server/README.md new file mode 100644 index 0000000..68e6b59 --- /dev/null +++ b/03-network/server/README.md @@ -0,0 +1,3 @@ +# Network - Server + +Server-side code for 03-network diff --git a/03-network/server/src/defs.h b/03-network/server/src/defs.h new file mode 100644 index 0000000..cce5681 --- /dev/null +++ b/03-network/server/src/defs.h @@ -0,0 +1,20 @@ +#ifndef DEFS_H +#define DEFS_H + +#include + +// MCS: Message Communication Standard +#define SERVER_MESSAGE_LEN 10 +#define CLIENT_MESSAGE_LEN 4 + +// MCSA: Message Communication Standard Actions +#define RIGHT_MOVEMENT 0 +#define LEFT_MOVEMENT 1 +#define JUMP_MOVEMENT 2 +#define CROUCH_MOVEMENT 3 + +// OC: Object Constants +#define GRAVITY 2 +#define MOVEMENT_SPEED 10 + +#endif diff --git a/03-network/server/src/main.c b/03-network/server/src/main.c new file mode 100644 index 0000000..7d84991 --- /dev/null +++ b/03-network/server/src/main.c @@ -0,0 +1,181 @@ +#include +#include +#include +#include +#include +#include +#include "structs.h" +#include "defs.h" + +#define PORT 9080 +#define DEBUG 1 + +int objects_count = 0; +struct object **objects_map; + +int handle_player(void *data) { + struct connection *connection_data = (struct connection *) data; + struct object *obj = objects_map[connection_data->obj_id]; + + memset(obj, 0, sizeof(struct object)); + + SDLNet_SocketSet set = SDLNet_AllocSocketSet(1); + if (set == NULL) { + fprintf(stderr, "Error: cannot allocate memory for a new socket set\n"); + return -1; + } + + int ret = SDLNet_TCP_AddSocket(set, connection_data->socket); + if (ret == -1) { + fprintf(stderr, "Error: max socket count in socket set reached\n"); + return -1; + } + + for (;;) { + char message[SERVER_MESSAGE_LEN]; + + int ready_sockets = SDLNet_CheckSockets(set, 10); + if (ready_sockets == -1) { + fprintf(stderr, "Error: cannot call select() system call with SDLNet_CheckSockets()\n"); + continue; + } + + if (ready_sockets == 0) + goto update_client; + + int recv_len = SDLNet_TCP_Recv(connection_data->socket, message, SERVER_MESSAGE_LEN); + + if (!recv_len) { + // player disconnected + fprintf(stderr, "Error: failed receiving message\n%s\n", SDLNet_GetError()); + break; + } + + if (DEBUG) + fprintf(stdout, "Notice: received '%s' and '%d' bytes\n", message, recv_len); + + // parsing the message + uint8_t action = message[0]; + + switch (action) { + case RIGHT_MOVEMENT: + obj->x += MOVEMENT_SPEED; + break; + case LEFT_MOVEMENT: + obj->x -= MOVEMENT_SPEED; + break; + case JUMP_MOVEMENT: + obj->force -= MOVEMENT_SPEED * 2.5; + obj->colliding = 0; + break; + case CROUCH_MOVEMENT: // TODO + break; + default: + break; + } + + update_client: + if (obj->y < 200) { + obj->force += GRAVITY; + } else { + obj->colliding = 1; + // obj->force = 0; + obj->y = 200; + } + + // gravity + obj->y += obj->force; + + // Update the client with information + // Note: the order OBVIOUSLY matters + int reply[] = { + obj->x, + obj->y, + obj->colliding, + obj->force + }; + + if (DEBUG) + fprintf(stdout, "Notice: new player properties (%d, %d, %d, %d)\n", reply[0], reply[1], reply[2], reply[3]); + + SDLNet_TCP_Send(connection_data->socket, reply, sizeof(int)*CLIENT_MESSAGE_LEN); + } + + SDLNet_FreeSocketSet(set); + SDLNet_TCP_Close(connection_data->socket); + free(obj); + free(data); + + return 0; +} + +void catch_alarm(int sig) { + fprintf(stdout, "Notice: force stopping server...\n"); + exit(EXIT_SUCCESS); +} + +int main(int argc, char *argv[]) { + signal(SIGINT, catch_alarm); + + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + fprintf(stderr, "Error: could not initialize SDL\n%s\n", SDL_GetError()); + return -1; + } + + if (SDLNet_Init() != 0) { + fprintf(stderr, "Error: could not initialize SDL net\n%s\n", SDL_GetError()); + return -1; + } + + IPaddress ip; + if (SDLNet_ResolveHost(&ip, NULL, PORT) == -1) { + fprintf(stderr, "Error: failed resolving host \n%s\n", SDL_GetError()); + return -1; + } + + TCPsocket server = SDLNet_TCP_Open(&ip); + if (!server) { + fprintf(stderr, "Error: failed opening socket at %d\n%s\n", PORT, SDL_GetError()); + return -1; + } + + for (;;) { + TCPsocket client = SDLNet_TCP_Accept(server); + if (!client) { + SDL_Delay(100); + continue; + } + + fprintf(stdout, "Notice: accepted a connection from client!\n"); + + objects_map = (struct object **) realloc(objects_map, sizeof(struct object *)*(objects_count+1)); + if (objects_map == NULL) { + fprintf(stderr, "Error: failed reallocating objects hash map\n"); + return -1; + } + + struct object *new_object = (struct object *) calloc(1, sizeof(struct object)); + if (new_object == NULL) { + fprintf(stderr, "Error: failed allocating memory for new object\n"); + return -1; + } + + objects_map[objects_count] = new_object; + + struct connection *new_connection = (struct connection *) calloc(1, sizeof(struct connection)); + if (new_connection == NULL) { + fprintf(stderr, "Error: failed allocating memory for new connection object\n"); + return -1; + } + + new_object->id = objects_count; + new_connection->socket = client; + new_connection->obj_id = objects_count; + + objects_count++; + + SDL_CreateThread(handle_player, "client", new_connection); + } + + return 0; +} diff --git a/03-network/server/src/structs.h b/03-network/server/src/structs.h new file mode 100644 index 0000000..e68dc55 --- /dev/null +++ b/03-network/server/src/structs.h @@ -0,0 +1,20 @@ +#ifndef STRUCTS_H +#define STRUCTS_H + +#include + +struct connection { + TCPsocket socket; + int obj_id; +}; + +struct object { + int id; + int x; + int y; + + int colliding; + int force; +}; + +#endif