04: add boilerplate code for a collision scene
BIN
04-collision/.imgs/showcase.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
28
04-collision/client/Makefile
Normal file
|
@ -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)
|
3
04-collision/client/README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Collision - Client
|
||||||
|
|
||||||
|
Client-side code for 04-collision
|
BIN
04-collision/client/assets/player/idle_down.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
04-collision/client/assets/player/idle_left.png
Normal file
After Width: | Height: | Size: 878 B |
BIN
04-collision/client/assets/player/idle_right.png
Normal file
After Width: | Height: | Size: 921 B |
BIN
04-collision/client/assets/player/idle_up.png
Normal file
After Width: | Height: | Size: 725 B |
BIN
04-collision/client/assets/player/move_down.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
04-collision/client/assets/player/move_left.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
04-collision/client/assets/player/move_right.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
04-collision/client/assets/player/move_up.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
04-collision/client/assets/player/tilemap.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
37
04-collision/client/src/defs.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef DEFS_H
|
||||||
|
#define DEFS_H
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
// Server details
|
||||||
|
#define SERVER_ADDR "127.0.0.1"
|
||||||
|
#define SERVER_PORT 9080
|
||||||
|
|
||||||
|
// ERRORS
|
||||||
|
#define STDOK 0
|
||||||
|
#define STDERR -1
|
||||||
|
#define MEMERR -2
|
||||||
|
|
||||||
|
// SDL2
|
||||||
|
#define SCREEN_WIDTH 1000
|
||||||
|
#define SCREEN_HEIGHT 480
|
||||||
|
#define WINDOW_FLAGS SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL
|
||||||
|
#define RENDERER_FLAGS SDL_RENDERER_ACCELERATED
|
||||||
|
|
||||||
|
// MCS: Message Communication Standard
|
||||||
|
#define SERVER_MESSAGE_LEN 10
|
||||||
|
#define CLIENT_MESSAGE_LEN 7
|
||||||
|
|
||||||
|
// MFS: Message Format Standard
|
||||||
|
#define PLAYER_CONNECT_FORMAT 0
|
||||||
|
#define OBJECT_PROPERTIES_FORMAT 1
|
||||||
|
#define PLAYER_DISCONNECT_FORMAT 2
|
||||||
|
|
||||||
|
// MSCA: Message Communication Standard Actions
|
||||||
|
#define LEFT_MOVEMENT 0b10000000
|
||||||
|
#define RIGHT_MOVEMENT 0b01000000
|
||||||
|
#define UP_MOVEMENT 0b00100000
|
||||||
|
#define DOWN_MOVEMENT 0b00010000
|
||||||
|
#define NO_MOVEMENT 0b00000000
|
||||||
|
|
||||||
|
#endif
|
62
04-collision/client/src/format.c
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include "defs.h"
|
||||||
|
#include "structs.h"
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
extern SDL_Texture *idle_down_animation;
|
||||||
|
extern struct object *create_object(SDL_Texture *texture, int scale, int resolution);
|
||||||
|
|
||||||
|
int handle_player_connect(int *message, struct object ***map, int *slots) {
|
||||||
|
int object_id = message[1];
|
||||||
|
|
||||||
|
if (object_id >= *slots) {
|
||||||
|
*slots = object_id+1;
|
||||||
|
*map = realloc(*map, sizeof(struct object *)*(*slots));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*map == NULL)
|
||||||
|
return MEMERR;
|
||||||
|
|
||||||
|
struct object *new_object = create_object(idle_down_animation, 4, 64);
|
||||||
|
if (new_object == NULL)
|
||||||
|
return MEMERR;
|
||||||
|
|
||||||
|
new_object->id = object_id;
|
||||||
|
new_object->x = message[2];
|
||||||
|
new_object->y = message[3];
|
||||||
|
new_object->colliding = message[4];
|
||||||
|
new_object->force = message[5];
|
||||||
|
new_object->state = message[6];
|
||||||
|
(*map)[object_id] = new_object;
|
||||||
|
|
||||||
|
return STDOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int handle_object_properties(int *message, struct object ***map, int *slots) {
|
||||||
|
int object_id = message[1];
|
||||||
|
|
||||||
|
if (object_id >= *slots) {
|
||||||
|
*slots = object_id+1;
|
||||||
|
*map = realloc(*map, sizeof(struct object *)*(*slots));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*map == NULL)
|
||||||
|
return MEMERR;
|
||||||
|
|
||||||
|
if ((*map)[object_id] == NULL) {
|
||||||
|
struct object *new_object = create_object(idle_down_animation, 4, 64);
|
||||||
|
if (new_object == NULL)
|
||||||
|
return MEMERR;
|
||||||
|
|
||||||
|
new_object->id = object_id;
|
||||||
|
(*map)[object_id] = new_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*map)[object_id]->x = message[2];
|
||||||
|
(*map)[object_id]->y = message[3];
|
||||||
|
(*map)[object_id]->colliding = message[4];
|
||||||
|
(*map)[object_id]->force = message[5];
|
||||||
|
(*map)[object_id]->state = message[6];
|
||||||
|
|
||||||
|
return STDOK;
|
||||||
|
}
|
9
04-collision/client/src/format.h
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef FORMAT_H
|
||||||
|
#define FORMAT_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
int handle_player_connect(int *message, struct object ***map, int *slots);
|
||||||
|
int handle_object_properties(int *message, struct object ***map, int *slots);
|
||||||
|
|
||||||
|
#endif
|
278
04-collision/client/src/main.c
Normal file
|
@ -0,0 +1,278 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <SDL2/SDL_image.h>
|
||||||
|
#include <SDL2/SDL_net.h>
|
||||||
|
#include "structs.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
#define DEBUG 1
|
||||||
|
|
||||||
|
int game_running = 1;
|
||||||
|
struct game game;
|
||||||
|
int movement_speed = 10;
|
||||||
|
double delta_time;
|
||||||
|
double last_frame;
|
||||||
|
|
||||||
|
// network
|
||||||
|
TCPsocket server_socket;
|
||||||
|
|
||||||
|
// textures
|
||||||
|
SDL_Texture *idle_animation;
|
||||||
|
SDL_Texture *move_left_animation;
|
||||||
|
SDL_Texture *move_right_animation;
|
||||||
|
SDL_Texture *move_up_animation;
|
||||||
|
SDL_Texture *move_down_animation;
|
||||||
|
SDL_Texture *idle_left_animation;
|
||||||
|
SDL_Texture *idle_right_animation;
|
||||||
|
SDL_Texture *idle_up_animation;
|
||||||
|
SDL_Texture *idle_down_animation;
|
||||||
|
|
||||||
|
// scene
|
||||||
|
int objects_count = 0;
|
||||||
|
struct object ** objects_map;
|
||||||
|
|
||||||
|
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_D)
|
||||||
|
game.left = !game.left;
|
||||||
|
if (event->keysym.scancode == SDL_SCANCODE_F)
|
||||||
|
game.right = !game.right;
|
||||||
|
if (event->keysym.scancode == SDL_SCANCODE_K)
|
||||||
|
game.up = !game.up;
|
||||||
|
if (event->keysym.scancode == SDL_SCANCODE_J)
|
||||||
|
game.down = !game.down;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 present_scene(void) {
|
||||||
|
SDL_RenderPresent(game.renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
int new_key_position(void **map, int slots) {
|
||||||
|
int slot = 0;
|
||||||
|
|
||||||
|
if (slots <= 0)
|
||||||
|
return slot;
|
||||||
|
|
||||||
|
if (map == NULL)
|
||||||
|
return slot;
|
||||||
|
|
||||||
|
while (slot <= slots) {
|
||||||
|
if (map[slots] == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
slot++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (message[0] == PLAYER_CONNECT_FORMAT) {
|
||||||
|
if (DEBUG)
|
||||||
|
fprintf(stdout, "DEBUG: PLAYER CONNECT MESSAGE\n");
|
||||||
|
|
||||||
|
int ret = handle_player_connect(message, &objects_map, &objects_count);
|
||||||
|
if (ret == MEMERR) {
|
||||||
|
fprintf(stderr, "MEMERR: Failed handling memory for player connect\n");
|
||||||
|
return MEMERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message[0] == OBJECT_PROPERTIES_FORMAT) {
|
||||||
|
if (DEBUG)
|
||||||
|
fprintf(stdout, "DEBUG: OBJECT PROPERTIES MESSAGE FOR ID %d\n", message[1]);
|
||||||
|
|
||||||
|
int ret = handle_object_properties(message, &objects_map, &objects_count);
|
||||||
|
if (ret == MEMERR) {
|
||||||
|
fprintf(stderr, "MEMERR: Failed handling memory for new object properties\n");
|
||||||
|
return MEMERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message[0] == PLAYER_DISCONNECT_FORMAT) {
|
||||||
|
if (DEBUG)
|
||||||
|
fprintf(stdout, "DEBUG: PLAYER DISCONNECT MESSAGE\n");
|
||||||
|
|
||||||
|
int object_id = message[1];
|
||||||
|
|
||||||
|
free(objects_map[object_id]);
|
||||||
|
objects_map[object_id] = NULL;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
move_left_animation = load_texture("assets/player/move_left.png");
|
||||||
|
move_right_animation = load_texture("assets/player/move_right.png");
|
||||||
|
move_up_animation = load_texture("assets/player/move_up.png");
|
||||||
|
move_down_animation = load_texture("assets/player/move_down.png");
|
||||||
|
idle_left_animation = load_texture("assets/player/idle_left.png");
|
||||||
|
idle_right_animation = load_texture("assets/player/idle_right.png");
|
||||||
|
idle_up_animation = load_texture("assets/player/idle_up.png");
|
||||||
|
idle_down_animation = load_texture("assets/player/idle_down.png");
|
||||||
|
idle_animation = idle_down_animation;
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
uint8_t message[SERVER_MESSAGE_LEN];
|
||||||
|
message[0] = (game.left << 7) |
|
||||||
|
(game.right << 6) |
|
||||||
|
(game.up << 5) |
|
||||||
|
(game.down << 4);
|
||||||
|
|
||||||
|
SDLNet_TCP_Send(server_socket, message, sizeof(uint8_t)*SERVER_MESSAGE_LEN);
|
||||||
|
|
||||||
|
for (int iter = 0; iter < objects_count; iter++) {
|
||||||
|
struct object *obj = objects_map[iter];
|
||||||
|
if (obj == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (obj->state & LEFT_MOVEMENT) {
|
||||||
|
switch_animation(obj, move_left_animation);
|
||||||
|
idle_animation = idle_left_animation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj->state & RIGHT_MOVEMENT) {
|
||||||
|
switch_animation(obj, move_right_animation);
|
||||||
|
idle_animation = idle_right_animation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj->state & UP_MOVEMENT) {
|
||||||
|
switch_animation(obj, move_up_animation);
|
||||||
|
idle_animation = idle_up_animation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj->state & DOWN_MOVEMENT) {
|
||||||
|
switch_animation(obj, move_down_animation);
|
||||||
|
idle_animation = idle_down_animation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj->state == NO_MOVEMENT)
|
||||||
|
switch_animation(obj, idle_animation);
|
||||||
|
|
||||||
|
obj->animation_speed = 6;
|
||||||
|
|
||||||
|
draw_object(&game, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
present_scene();
|
||||||
|
|
||||||
|
SDL_Delay(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_DestroyWindow(game.window);
|
||||||
|
SDL_Quit();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
54
04-collision/client/src/object.c
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#include "structs.h"
|
||||||
|
|
||||||
|
extern double delta_time;
|
||||||
|
|
||||||
|
struct object *create_object(SDL_Texture *texture, int scale, int resolution) {
|
||||||
|
struct object *object = (struct object *) calloc(1, sizeof(struct object));
|
||||||
|
|
||||||
|
if (object == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
object->texture = texture;
|
||||||
|
object->resolution = resolution;
|
||||||
|
object->animation_speed = 13;
|
||||||
|
object->scale = scale;
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_object(struct game *game, 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 switch_animation(struct object *object, SDL_Texture *animation) {
|
||||||
|
if (object->texture == animation)
|
||||||
|
return;
|
||||||
|
|
||||||
|
object->animation_slide = 0;
|
||||||
|
object->texture = animation;
|
||||||
|
}
|
48
04-collision/client/src/structs.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#ifndef STRUCTS_H
|
||||||
|
#define STRUCTS_H
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
struct game {
|
||||||
|
SDL_Window *window;
|
||||||
|
SDL_Renderer *renderer;
|
||||||
|
|
||||||
|
int left;
|
||||||
|
int right;
|
||||||
|
int up;
|
||||||
|
int down;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct animation {
|
||||||
|
SDL_Texture *tilemap;
|
||||||
|
int resolution;
|
||||||
|
|
||||||
|
int skippable;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct object {
|
||||||
|
SDL_Texture *texture;
|
||||||
|
int resolution; // of the texture/every tile
|
||||||
|
int id;
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
uint8_t state;
|
||||||
|
|
||||||
|
int colliding;
|
||||||
|
int force;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct object *create_object(SDL_Texture *texture, int scale, int resolution);
|
||||||
|
void draw_object(struct game *game, struct object *object);
|
||||||
|
void switch_animation(struct object *object, SDL_Texture *animation);
|
||||||
|
|
||||||
|
#endif
|
28
04-collision/server/Makefile
Normal file
|
@ -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)
|
3
04-collision/server/README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Collision - Server
|
||||||
|
|
||||||
|
Server-side code for 04-collision
|
31
04-collision/server/src/defs.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef DEFS_H
|
||||||
|
#define DEFS_H
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
#define GENERAL_DEBUG 0x0001
|
||||||
|
#define PLAYER_DEBUG 0x0010
|
||||||
|
|
||||||
|
// ERRORS
|
||||||
|
#define STDERR -1
|
||||||
|
#define MEMERR -2
|
||||||
|
|
||||||
|
// MCS: Message Communication Standard
|
||||||
|
#define SERVER_MESSAGE_LEN 10
|
||||||
|
#define CLIENT_MESSAGE_LEN 7
|
||||||
|
|
||||||
|
// MFS: Message Format Standard
|
||||||
|
#define PLAYER_CONNECT_FORMAT 0
|
||||||
|
#define OBJECT_PROPERTIES_FORMAT 1
|
||||||
|
#define PLAYER_DISCONNECT_FORMAT 2
|
||||||
|
|
||||||
|
// MCSA: Message Communication Standard Actions
|
||||||
|
#define LEFT_MOVEMENT 0b10000000
|
||||||
|
#define RIGHT_MOVEMENT 0b01000000
|
||||||
|
#define UP_MOVEMENT 0b00100000
|
||||||
|
#define DOWN_MOVEMENT 0b00010000
|
||||||
|
|
||||||
|
// OC: Object Constants
|
||||||
|
#define GRAVITY 2
|
||||||
|
#define MOVEMENT_SPEED 10
|
||||||
|
|
||||||
|
#endif
|
216
04-collision/server/src/main.c
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <SDL2/SDL_net.h>
|
||||||
|
#include <SDL2/SDL_error.h>
|
||||||
|
#include "defs.h"
|
||||||
|
#include "structs.h"
|
||||||
|
#include "structures.h"
|
||||||
|
|
||||||
|
#define DEBUG 0x0010
|
||||||
|
#define PORT 9080
|
||||||
|
|
||||||
|
struct object **objects_map;
|
||||||
|
int objects_map_size = 0;
|
||||||
|
int objects_count = 0;
|
||||||
|
|
||||||
|
struct connection **connections_map;
|
||||||
|
int connections_map_size = 0;
|
||||||
|
int connections_count = 0;
|
||||||
|
|
||||||
|
void handle_player_input(struct object *obj, char *message) {
|
||||||
|
uint8_t action = message[0];
|
||||||
|
|
||||||
|
if (action & LEFT_MOVEMENT)
|
||||||
|
obj->x -= MOVEMENT_SPEED;
|
||||||
|
|
||||||
|
if (action & RIGHT_MOVEMENT)
|
||||||
|
obj->x += MOVEMENT_SPEED;
|
||||||
|
|
||||||
|
if (action & UP_MOVEMENT)
|
||||||
|
obj->y -= MOVEMENT_SPEED;
|
||||||
|
|
||||||
|
if (action & DOWN_MOVEMENT)
|
||||||
|
obj->y += MOVEMENT_SPEED;
|
||||||
|
|
||||||
|
obj->state = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_player_physics(struct object *obj) {
|
||||||
|
}
|
||||||
|
|
||||||
|
int broadcast_event(int format, int object_id) {
|
||||||
|
struct object *obj = objects_map[object_id];
|
||||||
|
|
||||||
|
for (int iter = 0; iter < connections_map_size; iter++) {
|
||||||
|
struct connection *con = connections_map[iter];
|
||||||
|
if (con == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int message[] = {
|
||||||
|
format,
|
||||||
|
obj->id,
|
||||||
|
obj->x,
|
||||||
|
obj->y,
|
||||||
|
obj->colliding,
|
||||||
|
obj->force,
|
||||||
|
obj->state
|
||||||
|
};
|
||||||
|
|
||||||
|
SDLNet_TCP_Send(con->socket, message, sizeof(int)*CLIENT_MESSAGE_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int handle_player(void *data) {
|
||||||
|
struct connection *connection_data = (struct connection *) data;
|
||||||
|
struct object *obj = objects_map[connection_data->obj_id];
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcast_event(PLAYER_CONNECT_FORMAT, connection_data->obj_id);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
char message[SERVER_MESSAGE_LEN];
|
||||||
|
int ready_sockets = SDLNet_CheckSockets(set, 100);
|
||||||
|
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 & GENERAL_DEBUG)
|
||||||
|
fprintf(stdout, "Notice: received '%s' and '%d' bytes\n", message, recv_len);
|
||||||
|
|
||||||
|
handle_player_input(obj, message);
|
||||||
|
|
||||||
|
update_client:
|
||||||
|
handle_player_physics(obj);
|
||||||
|
broadcast_event(OBJECT_PROPERTIES_FORMAT, connection_data->obj_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Communicate client disconnect
|
||||||
|
broadcast_event(PLAYER_DISCONNECT_FORMAT, connection_data->obj_id);
|
||||||
|
|
||||||
|
SDLNet_FreeSocketSet(set);
|
||||||
|
SDLNet_TCP_Close(connection_data->socket);
|
||||||
|
|
||||||
|
objects_map[connection_data->obj_id] = NULL;
|
||||||
|
objects_count--;
|
||||||
|
|
||||||
|
connections_map[connection_data->id] = NULL;
|
||||||
|
connections_count--;
|
||||||
|
|
||||||
|
free(obj);
|
||||||
|
free(data);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void catch_alarm(int sig) {
|
||||||
|
fprintf(stdout, "Notice: force stopping server...\n");
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
int new_key_position(void **map, int slots) {
|
||||||
|
int slot = 0;
|
||||||
|
|
||||||
|
if (slots <= 0)
|
||||||
|
return slot;
|
||||||
|
|
||||||
|
if (map == NULL)
|
||||||
|
return slot;
|
||||||
|
|
||||||
|
while (slot <= slots) {
|
||||||
|
if (map[slot] == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
slot++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
int new_object_slot = map_allocate_value((void ***) &objects_map, &objects_map_size, &objects_count, sizeof(struct object));
|
||||||
|
if (new_object_slot == MEMERR) {
|
||||||
|
fprintf(stderr, "MEMERR: failed allocating memory for new object\n");
|
||||||
|
return STDERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
int new_connection_slot = map_allocate_value((void ***) &connections_map, &connections_map_size, &connections_count, sizeof(struct connection));
|
||||||
|
if (new_connection_slot == MEMERR) {
|
||||||
|
fprintf(stderr, "MEMERR: failed allocating memory for new connection\n");
|
||||||
|
return STDERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct object *new_object = objects_map[new_object_slot];
|
||||||
|
struct connection *new_connection = connections_map[new_connection_slot];
|
||||||
|
|
||||||
|
new_object->id = new_object_slot;
|
||||||
|
new_connection->id = new_connection_slot;
|
||||||
|
new_connection->socket = client;
|
||||||
|
new_connection->obj_id = new_object_slot;
|
||||||
|
|
||||||
|
if (DEBUG & PLAYER_DEBUG)
|
||||||
|
fprintf(stdout, "Created connection with id '%d' and object '%d'\n", new_connection->id, new_object->id);
|
||||||
|
|
||||||
|
SDL_CreateThread(handle_player, "client", new_connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
23
04-collision/server/src/structs.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef STRUCTS_H
|
||||||
|
#define STRUCTS_H
|
||||||
|
|
||||||
|
#include <SDL2/SDL_net.h>
|
||||||
|
|
||||||
|
struct connection {
|
||||||
|
int id;
|
||||||
|
int obj_id;
|
||||||
|
TCPsocket socket;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct object {
|
||||||
|
int id;
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
|
||||||
|
int colliding;
|
||||||
|
int force;
|
||||||
|
|
||||||
|
uint8_t state;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
55
04-collision/server/src/structures.c
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "structures.h"
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
int map_find_empty_slot(void **map, int slots) {
|
||||||
|
int slot = 0;
|
||||||
|
|
||||||
|
if (slots <= 0)
|
||||||
|
return slot;
|
||||||
|
|
||||||
|
if (map == NULL)
|
||||||
|
return slot;
|
||||||
|
|
||||||
|
while (slot < slots) {
|
||||||
|
if (map[slot] == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
slot++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's Nx fewer used slots than total slots in the map, find the first possible one.
|
||||||
|
// Else reallocate the map and use the end slot
|
||||||
|
// Then allocate an object of size BYTES and add it into the map.
|
||||||
|
int map_allocate_value(void ***map, int *total_slots, int *used_slots, int bytes) {
|
||||||
|
int half_slots = (*total_slots) / 2;
|
||||||
|
int empty_slot = 0;
|
||||||
|
|
||||||
|
if (*used_slots < half_slots) {
|
||||||
|
empty_slot = map_find_empty_slot(*map, *used_slots);
|
||||||
|
goto jump;
|
||||||
|
}
|
||||||
|
|
||||||
|
empty_slot = *total_slots;
|
||||||
|
(*total_slots)++;
|
||||||
|
*map = realloc(*map, sizeof(void *)*(*total_slots));
|
||||||
|
|
||||||
|
jump:
|
||||||
|
if (*map == NULL)
|
||||||
|
return MEMERR;
|
||||||
|
|
||||||
|
void *new_value = calloc(1, bytes);
|
||||||
|
if (new_value == NULL)
|
||||||
|
return MEMERR;
|
||||||
|
|
||||||
|
(*used_slots)++;
|
||||||
|
(*map)[empty_slot] = new_value;
|
||||||
|
|
||||||
|
return empty_slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
9
04-collision/server/src/structures.h
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef STRUCTURES_H
|
||||||
|
#define STRUCTURES_H
|
||||||
|
|
||||||
|
#include "structs.h"
|
||||||
|
|
||||||
|
int map_find_empty_slot(void **map, int slots);
|
||||||
|
int map_allocate_value(void ***map, int *total_slots, int *used_slots, int bytes);
|
||||||
|
|
||||||
|
#endif
|