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 0000000..4cf0c43
Binary files /dev/null and b/03-network/client/assets/player/idle.png differ
diff --git a/03-network/client/assets/player/jump.png b/03-network/client/assets/player/jump.png
new file mode 100644
index 0000000..dff105c
Binary files /dev/null and b/03-network/client/assets/player/jump.png differ
diff --git a/03-network/client/assets/player/land.png b/03-network/client/assets/player/land.png
new file mode 100644
index 0000000..2c24d6f
Binary files /dev/null and b/03-network/client/assets/player/land.png differ
diff --git a/03-network/client/assets/player/run.png b/03-network/client/assets/player/run.png
new file mode 100644
index 0000000..615b222
Binary files /dev/null and b/03-network/client/assets/player/run.png differ
diff --git a/03-network/client/src/main.c b/03-network/client/src/main.c
new file mode 100644
index 0000000..e12e36a
--- /dev/null
+++ b/03-network/client/src/main.c
@@ -0,0 +1,264 @@
+#include
+#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