Switch to chipmunk

main
Cameron Murphy Reikes 2 years ago
parent 35fbdf2635
commit 64a82c2a26

3
.gitmodules vendored

@ -1,3 +1,6 @@
[submodule "thirdparty/enet"] [submodule "thirdparty/enet"]
path = thirdparty/enet path = thirdparty/enet
url = https://github.com/lsalzman/enet.git url = https://github.com/lsalzman/enet.git
[submodule "thirdparty/Chipmunk2D"]
path = thirdparty/Chipmunk2D
url = https://github.com/slembcke/Chipmunk2D.git

@ -31,6 +31,11 @@
"types.h": "c", "types.h": "c",
"cmath": "c", "cmath": "c",
"math.h": "c", "math.h": "c",
"corecrt_math.h": "c" "corecrt_math.h": "c",
"cppolyline.h": "c",
"chipmunk_private.h": "c",
"chipmunk.h": "c",
"cpbody.h": "c",
"chipmunk_structs.h": "c"
} }
} }

@ -1,11 +1,20 @@
@echo off @echo off
@REM what all the flags mean: https://learn.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-by-category?view=msvc-170 @REM what all the compile flags mean: https://learn.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-by-category?view=msvc-170
WHERE sokol-shdc.exe WHERE sokol-shdc.exe
IF %ERRORLEVEL% NEQ 0 ECHO ERROR download sokol-shdc from https://github.com/floooh/sokol-tools-bin/blob/master/bin/win32/sokol-shdc.exe and put it in this folder IF %ERRORLEVEL% NEQ 0 ECHO ERROR download sokol-shdc from https://github.com/floooh/sokol-tools-bin/blob/master/bin/win32/sokol-shdc.exe and put it in this folder
@REM example of how to compile shaders: sokol-shdc.exe --input triangle.glsl --output triangle.gen.h --slang glsl330:hlsl5:metal_macos @REM example of how to compile shaders: sokol-shdc.exe --input triangle.glsl --output triangle.gen.h --slang glsl330:hlsl5:metal_macos
cl /MP /Zi /Fd"flight.pdb" /I"thirdparty" /Fe"flight" /I"thirdparty\enet\include" main.c gamestate.c server.c debugdraw.c^ setlocal enabledelayedexpansion enableextensions
thirdparty\enet\callbacks.c thirdparty\enet\compress.c thirdparty\enet\host.c thirdparty\enet\list.c thirdparty\enet\packet.c thirdparty\enet\peer.c thirdparty\enet\protocol.c thirdparty\enet\win32.c Ws2_32.lib winmm.lib pushd thirdparty\Chipmunk2D\src
set MUNKSRC=
for %%x in (*.c) do set MUNKSRC=!MUNKSRC! thirdparty\Chipmunk2D\src\%%x
popd
cl /MP /Zi /Fd"flight.pdb" /Fe"flight"^
/I"thirdparty" /I"thirdparty\enet\include" /I"thirdparty\Chipmunk2D\include\chipmunk" /I"thirdparty\Chipmunk2D\include"^
main.c gamestate.c server.c debugdraw.c^
thirdparty\enet\callbacks.c thirdparty\enet\compress.c thirdparty\enet\host.c thirdparty\enet\list.c thirdparty\enet\packet.c thirdparty\enet\peer.c thirdparty\enet\protocol.c thirdparty\enet\win32.c Ws2_32.lib winmm.lib^
%MUNKSRC%

Binary file not shown.

Binary file not shown.

@ -1,7 +1,7 @@
#include <chipmunk.h>
#include "types.h" #include "types.h"
#include <stdlib.h> // malloc #include <stdio.h> // assert logging
#include <stdio.h>
void __assert(bool cond, const char *file, int line, const char *cond_string) void __assert(bool cond, const char *file, int line, const char *cond_string)
{ {
@ -17,251 +17,257 @@ void __assert(bool cond, const char *file, int line, const char *cond_string)
// super try not to depend on external libraries like enet or sokol to keep build process simple, // super try not to depend on external libraries like enet or sokol to keep build process simple,
// gamestate its own portable submodule. If need to link to other stuff document here: // gamestate its own portable submodule. If need to link to other stuff document here:
// - debug // - debug.c for debug drawing
// - chipmunk
static void integrate_acceleration(struct Body *body, float dt) void initialize(struct GameState *gs)
{ {
// position gs->space = cpSpaceNew();
}
void destroy(struct GameState *gs)
{
for (int i = 0; i < MAX_PLAYERS; i++)
{ {
V2 current = body->position; box_destroy(&gs->players[i].box);
body->position = V2add(body->position, V2sub(current, body->old_position));
body->position = V2add(body->position, V2scale(body->acceleration, dt * dt));
body->old_position = current;
} }
for (int i = 0; i < gs->num_boxes; i++)
// rotation
{ {
float current = body->rotation; box_destroy(&gs->boxes[i]);
body->rotation = body->rotation + (current - body->old_rotation);
body->rotation = body->rotation + body->angular_acceleration * dt * dt;
body->old_rotation = current;
} }
gs->num_boxes = 0;
cpSpaceDestroy(gs->space);
gs->space = NULL;
} }
struct ProcessBody struct Box box_new(struct GameState *gs, V2 pos)
{
V2 vertices[4];
struct Body *body;
};
struct ProcessBody make_process_body(struct Body *from)
{ {
assert(gs->space != NULL);
float halfbox = BOX_SIZE / 2.0f; float halfbox = BOX_SIZE / 2.0f;
struct ProcessBody to_return = cpBody *body = cpSpaceAddBody(gs->space, cpBodyNew(BOX_MASS, cpMomentForBox(BOX_MASS, BOX_SIZE, BOX_SIZE)));
{ cpShape *shape = cpBoxShapeNew(body, BOX_SIZE, BOX_SIZE, 0.0f);
.vertices = { cpSpaceAddShape(gs->space, shape);
// important that the first one is the upper right, used to deduce rotation from vertex position cpBodySetPosition(body, cpv(pos.x, pos.y));
// @Robust instead of array of vertices have type? like struct with upper_right upper_left etc
V2add(from->position, V2rotate((V2){.x = halfbox, .y = -halfbox}, from->rotation)), // upper right return (struct Box){
V2add(from->position, V2rotate((V2){.x = halfbox, .y = halfbox}, from->rotation)), // bottom right .body = body,
V2add(from->position, V2rotate((V2){.x = -halfbox, .y = halfbox}, from->rotation)), // lower left .shape = shape,
V2add(from->position, V2rotate((V2){.x = -halfbox, .y = -halfbox}, from->rotation)), // upper left
},
.body = from,
}; };
return to_return;
} }
static void project(struct ProcessBody *from, V2 axis, float *min, float *max) void box_destroy(struct Box *box)
{ {
float DotP = V2dot(axis, from->vertices[0]); cpShapeFree(box->shape);
cpBodyFree(box->body);
box->shape = NULL;
box->body = NULL;
}
// Set the minimum and maximum values to the projection of the first vertex static V2 cp_to_v2(cpVect v)
*min = DotP; {
*max = DotP; return (V2){.x = v.x, .y = v.y};
}
for (int I = 1; I < 4; I++) static cpVect v2_to_cp(V2 v)
{ {
// Project the rest of the vertices onto the axis and extend return cpv(v.x, v.y);
// the interval to the left/right if necessary }
DotP = V2dot(axis, from->vertices[I]);
*min = fmin(DotP, *min); V2 box_pos(struct Box box)
*max = fmax(DotP, *max); {
} return cp_to_v2(cpBodyGetPosition(box.body));
}
V2 box_vel(struct Box box)
{
return cp_to_v2(cpBodyGetVelocity(box.body));
}
float box_rotation(struct Box box)
{
return cpBodyGetAngle(box.body);
}
float box_angular_velocity(struct Box box)
{
return cpBodyGetAngularVelocity(box.body);
} }
static float interval_distance(float min_a, float max_a, float min_b, float max_b) #define memwrite(out, variable) \
for (char b = 0; b < sizeof(variable); b++) \
{ \
**out = ((char *)&variable)[b]; \
*out += 1; \
}
#define memread(in, variable_pointer) \
for (char b = 0; b < sizeof(*variable_pointer); b++) \
{ \
((char *)variable_pointer)[b] = **in; \
*in += 1; \
}
void ser_float(char **out, float f)
{ {
if (min_a < min_b) memwrite(out, f);
return min_b - max_a;
else
return min_a - max_b;
} }
static void move_vertices(V2 *vertices, int num, V2 shift) void des_float(char **in, float *f)
{ {
for (int i = 0; i < num; i++) memread(in, f);
{
vertices[i] = V2add(vertices[i], shift);
}
} }
void process(struct GameState *gs, float dt) void ser_int(char **out, int i)
{ {
// process input memwrite(out, i);
int num_bodies = gs->num_boxes; }
for (int i = 0; i < MAX_PLAYERS; i++)
{
struct Player *p = &gs->players[i];
if (!p->connected)
continue;
p->body.acceleration = V2scale(p->input, 5.0f);
p->body.angular_acceleration = p->input.x * 10.0f;
num_bodies += 1;
}
// @Robust do this without malloc void des_int(char **in, int *i)
{
memread(in, i);
}
struct ProcessBody *bodies = malloc(sizeof *bodies * num_bodies); void ser_bool(char **out, bool b)
int cur_body_index = 0; {
**out = (char)b;
*out += 1;
}
for (int i = 0; i < MAX_PLAYERS; i++) void des_bool(char **in, bool *b)
{ {
struct Player *p = &gs->players[i]; *b = (bool)**in;
if (!p->connected) *in += 1;
continue; }
integrate_acceleration(&p->body, dt);
bodies[cur_body_index] = make_process_body(&p->body);
cur_body_index++;
}
for (int i = 0; i < gs->num_boxes; i++) void ser_V2(char **out, V2 v)
{ {
integrate_acceleration(&gs->boxes[i].body, dt); ser_float(out, v.x);
bodies[cur_body_index] = make_process_body(&gs->boxes[i].body); ser_float(out, v.y);
cur_body_index++; }
}
assert(cur_body_index == num_bodies); void des_V2(char **in, V2 *v)
{
des_float(in, &v->x);
des_float(in, &v->y);
}
// Collision void ser_box(char **out, struct Box *b)
// @Robust handle when bodies are overlapping (even perfectly) {
for (int i = 0; i < num_bodies; i++) // box must not be null, dummy!
{ assert(b->body != NULL);
for (int ii = 0; ii < num_bodies; ii++) ser_V2(out, box_pos(*b));
{ ser_V2(out, box_vel(*b));
if (ii == i) ser_float(out, box_rotation(*b));
continue; ser_float(out, box_angular_velocity(*b));
struct ProcessBody *from = &bodies[i]; }
struct ProcessBody *to = &bodies[ii];
dbg_line(from->body->position, to->body->position);
float MinDistance = 10000.0f; // takes gamestate as argument to place box in the gamestates space
void des_box(char **in, struct Box *b, struct GameState *gs)
{
assert(b->body == NULL); // destroy the box before deserializing into it
V2 pos = {0};
V2 vel = {0};
float rot = 0.0f;
float angular_vel = 0.0f;
des_V2(in, &pos);
des_V2(in, &vel);
des_float(in, &rot);
des_float(in, &angular_vel);
*b = box_new(gs, pos);
cpBodySetVelocity(b->body, v2_to_cp(vel));
cpBodySetAngle(b->body, rot);
cpBodySetAngularVelocity(b->body, angular_vel);
}
struct Edge void ser_player(char **out, struct Player *p)
{
ser_bool(out, p->connected);
if (p->connected)
{ {
struct ProcessBody *parent; ser_box(out, &p->box);
V2 *from; ser_V2(out, p->input);
V2 *to; }
}; }
struct ProcessBody *bodies[2] = {from, to}; void des_player(char **in, struct Player *p, struct GameState *gs)
bool was_collision = false; {
V2 normal = {0}; des_bool(in, &p->connected);
struct Edge edge = {0}; if (p->connected)
for (int body_i = 0; body_i < 2; body_i++)
{
struct ProcessBody *body = bodies[body_i];
for (int edge_from_i = 0; edge_from_i < 3; edge_from_i++)
{ {
int edge_to_i = edge_from_i + 1; des_box(in, &p->box, gs);
V2 *edge_from = &body->vertices[edge_from_i]; des_V2(in, &p->input);
V2 *edge_to = &body->vertices[edge_to_i]; }
}
// normal vector of edge
V2 axis = (V2){
.x = edge_from->y - edge_to->y,
.y = edge_to->x - edge_from->x,
};
axis = V2normalize(axis);
float min_from, min_to, max_from, max_to = 0.0f; // @Robust really think about if <= makes more sense than < here...
project(from, axis, &min_from, &max_from); #define LEN_CHECK() assert(bytes - original_bytes <= max_len)
project(to, axis, &min_to, &max_to);
void into_bytes(struct ServerToClient *msg, char *bytes, int *out_len, int max_len)
{
assert(msg->cur_gs != NULL);
assert(msg != NULL);
struct GameState *gs = msg->cur_gs;
char *original_bytes = bytes;
float distance = interval_distance(min_from, min_to, max_from, max_to); ser_int(&bytes, msg->your_player);
if (distance > 0.0f) for (int i = 0; i < MAX_PLAYERS; i++)
break;
else if (fabsf(distance) < MinDistance)
{ {
MinDistance = fabsf(distance); ser_player(&bytes, &gs->players[i]);
was_collision = true; LEN_CHECK();
normal = axis;
edge = (struct Edge){
.parent = &body,
.from = edge_from,
.to = edge_to,
};
}
} }
}
float depth = MinDistance;
if (was_collision)
{
float intersection_depth = from_interval[1] - to_interval[0];
move_vertices(from->vertices, 4, V2scale(axis, intersection_depth * -0.5f)); // @Robust invalid message on num boxes bigger than max boxes
move_vertices(to->vertices, 4, V2scale(axis, intersection_depth * 0.5f)); ser_int(&bytes, gs->num_boxes);
} LEN_CHECK();
}
}
// Wall for (int i = 0; i < gs->num_boxes; i++)
if (true)
{
for (int i = 0; i < num_bodies; i++)
{
for (int v_i = 0; v_i < 4; v_i++)
{
V2 *vert = &bodies[i].vertices[v_i];
if (vert->x > 2.0f)
{ {
vert->x = 2.0f; ser_box(&bytes, &gs->boxes[i]);
} LEN_CHECK();
}
}
} }
// Correct for differences in vertex position *out_len = bytes - original_bytes;
const int edge_update_iters = 3; }
for (int iter = 0; iter < edge_update_iters; iter++)
{
for (int i = 0; i < num_bodies; i++)
{
for (int v_i = 0; v_i < 3; v_i++)
{
int other_v_i = v_i + 1;
V2 *from = &bodies[i].vertices[v_i];
V2 *to = &bodies[i].vertices[other_v_i];
V2 line = V2sub(*to, *from); void from_bytes(struct ServerToClient *msg, char *bytes, int max_len)
float len = V2length(line); {
float diff = len - BOX_SIZE; struct GameState *gs = msg->cur_gs;
line = V2normalize(line); char *original_bytes = bytes;
// destroy and free all chipmunk
destroy(gs);
initialize(gs);
*from = V2add(*from, V2scale(line, diff * 0.5f)); des_int(&bytes, &msg->your_player);
*to = V2sub(*to, V2scale(line, diff * 0.5f)); LEN_CHECK();
}
}
}
// Reupdate the positions of the bodies based on how the vertices changed for (int i = 0; i < MAX_PLAYERS; i++)
for (int i = 0; i < num_bodies; i++)
{ {
float upper_right_angle = V2angle(V2sub(bodies[i].vertices[0], bodies[i].body->position)); des_player(&bytes, &gs->players[i], gs);
bodies[i].body->rotation = upper_right_angle - (PI / 4.0f); LEN_CHECK();
}
des_int(&bytes, &gs->num_boxes);
LEN_CHECK();
V2 avg = {0}; for (int i = 0; i < gs->num_boxes; i++)
for (int v_i = 0; v_i < 4; v_i++)
{ {
avg = V2add(avg, bodies[i].vertices[v_i]); des_box(&bytes, &gs->boxes[i], gs);
LEN_CHECK();
} }
avg = V2scale(avg, 1.0f / 4.0f); }
bodies[i].body->position = avg;
void process(struct GameState *gs, float dt)
{
assert(gs->space != NULL);
// process input
for (int i = 0; i < MAX_PLAYERS; i++)
{
struct Player *p = &gs->players[i];
if (!p->connected)
continue;
cpBodyApplyForceAtWorldPoint(p->box.body, v2_to_cp(V2scale(p->input, 5.0f)), v2_to_cp(box_pos(p->box)));
} }
free(bodies); cpSpaceStep(gs->space, dt);
} }

@ -26,6 +26,8 @@ void init(void)
{ {
// @BeforeShip make all fprintf into logging to file, warning dialog boxes on failure instead of exit(-1), replace the macros in sokol with this as well, like assert // @BeforeShip make all fprintf into logging to file, warning dialog boxes on failure instead of exit(-1), replace the macros in sokol with this as well, like assert
initialize(&gs);
sg_desc sgdesc = {.context = sapp_sgcontext()}; sg_desc sgdesc = {.context = sapp_sgcontext()};
sg_setup(&sgdesc); sg_setup(&sgdesc);
if (!sg_isvalid()) if (!sg_isvalid())
@ -114,17 +116,11 @@ static void frame(void)
// and other validation instead of just casting to a struct // and other validation instead of just casting to a struct
// "Alignment of structure members can be different even among different compilers on the same platform, let alone different platforms." // "Alignment of structure members can be different even among different compilers on the same platform, let alone different platforms."
// ^^ need serialization strategy that accounts for this if multiple platforms is happening https://stackoverflow.com/questions/28455163/how-can-i-portably-send-a-c-struct-through-a-network-socket // ^^ need serialization strategy that accounts for this if multiple platforms is happening https://stackoverflow.com/questions/28455163/how-can-i-portably-send-a-c-struct-through-a-network-socket
struct ServerToClient msg; struct ServerToClient msg = {
if (event.packet->dataLength != sizeof(msg)) .cur_gs = &gs,
{ };
Log("Unknown packet size: %zd\n", event.packet->dataLength); // @Robust maximum acceptable message size?
} from_bytes(&msg, event.packet->data, event.packet->dataLength);
else
{
memcpy(&msg, event.packet->data, sizeof(msg));
myplayer = msg.your_player;
gs = msg.cur_gs;
}
enet_packet_destroy(event.packet); enet_packet_destroy(event.packet);
break; break;
case ENET_EVENT_TYPE_DISCONNECT: case ENET_EVENT_TYPE_DISCONNECT:
@ -179,7 +175,8 @@ static void frame(void)
// camera go to player // camera go to player
if (myplayer != -1) if (myplayer != -1)
{ {
sgp_translate(-gs.players[myplayer].body.position.x, -gs.players[myplayer].body.position.y); V2 pos = box_pos(gs.players[myplayer].box);
sgp_translate(-pos.x, -pos.y);
} }
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f); sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
@ -204,14 +201,14 @@ static void frame(void)
continue; continue;
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f); sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
sgp_push_transform(); sgp_push_transform();
sgp_rotate_at(p->body.rotation, p->body.position.x, p->body.position.y); sgp_rotate_at(box_rotation(p->box), box_pos(p->box).x, box_pos(p->box).y);
sgp_draw_filled_rect(p->body.position.x - halfbox, p->body.position.y - halfbox, BOX_SIZE, BOX_SIZE); sgp_draw_filled_rect(box_pos(p->box).x - halfbox, box_pos(p->box).y - halfbox, BOX_SIZE, BOX_SIZE);
sgp_pop_transform(); sgp_pop_transform();
sgp_set_color(1.0f, 0.0f, 0.0f, 1.0f); sgp_set_color(1.0f, 0.0f, 0.0f, 1.0f);
V2 vel = (V2scale(V2sub(p->body.position, p->body.old_position), 60.0f)); V2 vel = box_vel(p->box);
V2 to = V2add(p->body.position, vel); V2 to = V2add(box_pos(p->box), vel);
sgp_draw_line(p->body.position.x, p->body.position.y, to.x, to.y); sgp_draw_line(box_pos(p->box).x, box_pos(p->box).y, to.x, to.y);
} }
// boxes // boxes
@ -219,12 +216,15 @@ static void frame(void)
for (int i = 0; i < gs.num_boxes; i++) for (int i = 0; i < gs.num_boxes; i++)
{ {
sgp_set_color(0.5f, 0.5f, 0.5f, 1.0f); sgp_set_color(0.5f, 0.5f, 0.5f, 1.0f);
sgp_draw_filled_rect(gs.boxes[i].body.position.x - halfbox, gs.boxes[i].body.position.y - halfbox, BOX_SIZE, BOX_SIZE); sgp_push_transform();
sgp_rotate_at(box_rotation(gs.boxes[i]), box_pos(gs.boxes[i]).x, box_pos(gs.boxes[i]).y);
sgp_draw_filled_rect(box_pos(gs.boxes[i]).x - halfbox, box_pos(gs.boxes[i]).y - halfbox, BOX_SIZE, BOX_SIZE);
sgp_pop_transform();
sgp_set_color(1.0f, 0.0f, 0.0f, 1.0f); sgp_set_color(1.0f, 0.0f, 0.0f, 1.0f);
V2 vel = (V2scale(V2sub(gs.boxes[i].body.position, gs.boxes[i].body.old_position), 60.0f)); V2 vel = box_vel(gs.boxes[i]);
V2 to = V2add(gs.boxes[i].body.position, vel); V2 to = V2add(box_pos(gs.boxes[i]), vel);
sgp_draw_line(gs.boxes[i].body.position.x, gs.boxes[i].body.position.y, to.x, to.y); sgp_draw_line(box_pos(gs.boxes[i]).x, box_pos(gs.boxes[i]).y, to.x, to.y);
} }
} }
@ -247,6 +247,7 @@ static void frame(void)
void cleanup(void) void cleanup(void)
{ {
destroy(&gs);
sgp_shutdown(); sgp_shutdown();
sg_shutdown(); sg_shutdown();
enet_deinitialize(); enet_deinitialize();

@ -14,31 +14,20 @@ void server(void *data)
stm_setup(); stm_setup();
struct GameState gs = {0}; struct GameState gs = {0};
initialize(&gs);
// two boxes stacked on top // two boxes stacked on top
if (false) if (true)
{ {
gs.boxes[0] = (struct Box){ gs.boxes[0] = box_new(&gs, (V2){.x = 0.75f, .y = 0.0});
.body = (struct Body){ gs.boxes[1] = box_new(&gs, (V2){.x = 0.75f, .y = 0.5f});
.position = (P2){.x = 0.75f, .y = 0.0}},
};
gs.boxes[0].body.old_position = gs.boxes[0].body.position;
gs.boxes[1] = (struct Box){
.body = (struct Body){
.position = (P2){.x = 0.75f, .y = 0.5f}},
};
gs.boxes[1].body.old_position = gs.boxes[1].body.position;
gs.num_boxes = 2; gs.num_boxes = 2;
} }
// one box // one box
if (true) if (false)
{ {
gs.boxes[0] = (struct Box){ gs.boxes[0] = box_new(&gs, (V2){.x = 0.75f, .y = 0.0});
.body = (struct Body){
.position = (P2){.x = 0.75f, .y = 0.0}},
};
gs.boxes[0].body.old_position = gs.boxes[0].body.position;
gs.num_boxes = 1; gs.num_boxes = 1;
} }
@ -107,11 +96,10 @@ void server(void *data)
else else
{ {
event.peer->data = (void *)player_slot; event.peer->data = (void *)player_slot;
gs.players[player_slot] = (struct Player){.body.position = (V2){ gs.players[player_slot].box = box_new(&gs, (V2){
.x = 0.0f, .x = 0.0f,
.y = 1.0f * (float)player_slot, .y = 1.0f * (float)player_slot,
}}; });
gs.players[player_slot].body.old_position = gs.players[player_slot].body.position;
gs.players[player_slot].connected = true; gs.players[player_slot].connected = true;
} }
@ -140,8 +128,10 @@ void server(void *data)
break; break;
case ENET_EVENT_TYPE_DISCONNECT: case ENET_EVENT_TYPE_DISCONNECT:
Log("%" PRId64 " disconnected.\n", (int64_t)event.peer->data); int player_index = (int64_t)event.peer->data;
gs.players[(int64_t)event.peer->data].connected = false; Log("%" PRId64 " disconnected player index %d.\n", (int64_t)event.peer->data, player_index);
gs.players[player_index].connected = false;
box_destroy(&gs.players[player_index].box);
event.peer->data = NULL; event.peer->data = NULL;
} }
} }
@ -149,13 +139,15 @@ void server(void *data)
total_time += (float)stm_sec(stm_diff(stm_now(), last_processed_time)); total_time += (float)stm_sec(stm_diff(stm_now(), last_processed_time));
last_processed_time = stm_now(); last_processed_time = stm_now();
// @Robost if can't process quick enough will be stuck being lagged behind, think of a solution for this... // @Robost @BeforeShip if can't process quick enough will be stuck being lagged behind, think of a solution for this...
while (total_time > TIMESTEP) while (total_time > TIMESTEP)
{ {
process(&gs, TIMESTEP); process(&gs, TIMESTEP);
total_time -= TIMESTEP; total_time -= TIMESTEP;
} }
#define MAX_BYTES_SIZE 2048 * 2
static char bytes_buffer[MAX_BYTES_SIZE] = {0};
for (int i = 0; i < server->peerCount; i++) for (int i = 0; i < server->peerCount; i++)
{ {
// @Speed don't recreate the packet for every peer, gets expensive copying gamestate over and over again // @Speed don't recreate the packet for every peer, gets expensive copying gamestate over and over again
@ -164,13 +156,18 @@ void server(void *data)
continue; continue;
} }
struct ServerToClient to_send; struct ServerToClient to_send;
to_send.cur_gs = gs; to_send.cur_gs = &gs;
to_send.your_player = (int)(int64_t)server->peers[i].data; to_send.your_player = (int)(int64_t)server->peers[i].data;
ENetPacket *gamestate_packet = enet_packet_create((void *)&to_send, sizeof(struct ServerToClient), ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT);
int len = 0;
into_bytes(&to_send, bytes_buffer, &len, MAX_BYTES_SIZE);
ENetPacket *gamestate_packet = enet_packet_create((void *)bytes_buffer, len, ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT);
enet_peer_send(&server->peers[i], 0, gamestate_packet); enet_peer_send(&server->peers[i], 0, gamestate_packet);
} }
} }
destroy(&gs);
enet_host_destroy(server); enet_host_destroy(server);
enet_deinitialize(); enet_deinitialize();
} }

@ -0,0 +1 @@
Subproject commit 352d13cf6574a63a129ce40e011eec2e248cbe34

@ -13,7 +13,7 @@ WinKill, Flight
WinActivate, flightbuild WinActivate, flightbuild
If WinActive("flightbuild") If WinActive("flightbuild")
{ {
Send, build_debug.bat && flight.exe --host{Enter} Send, cd C:\Users\Cameron\Documents\flight{Enter} build_debug.bat && flight.exe --host{Enter}
} }
return return
@ -26,6 +26,6 @@ WinKill, Flight
WinActivate, flightbuild WinActivate, flightbuild
If WinActive("flightbuild") If WinActive("flightbuild")
{ {
Send, build_debug.bat && START /B flight.exe && flight.exe --host{Enter} Send, cd C:\Users\Cameron\Documents\flight{Enter} build_debug.bat && START /B flight.exe && flight.exe --host{Enter}
} }
return return

@ -1,5 +1,10 @@
#pragma once #pragma once
#define MAX_PLAYERS 4
#define BOX_SIZE 0.5f
#define MAX_BOXES 128
#define BOX_MASS 1.0f
// @Robust remove this include somehow, needed for sqrt and cos // @Robust remove this include somehow, needed for sqrt and cos
#include <math.h> #include <math.h>
@ -15,6 +20,16 @@ typedef sgp_vec2 sgp_point;
#endif #endif
#ifndef CHIPMUNK_H
typedef void cpSpace;
typedef void cpBody;
typedef void cpShape;
extern void cpShapeFree(cpShape *);
extern void cpBodyFree(cpBody *);
extern void cpSpaceFree(cpSpace *);
#endif
#include <stdbool.h> #include <stdbool.h>
#ifndef _STDBOOL #ifndef _STDBOOL
@ -31,43 +46,30 @@ typedef sgp_point P2;
#define Log(...) \ #define Log(...) \
fprintf(stdout, "%s:%d | ", __FILE__, __LINE__); \ fprintf(stdout, "%s:%d | ", __FILE__, __LINE__); \
fprintf(stdout, __VA_ARGS__) fprintf(stdout, __VA_ARGS__)
#define MAX_BOXES 32
#define MAX_PLAYERS 4
#define BOX_SIZE 0.5f
struct Body struct Box
{ {
P2 position; cpBody *body;
P2 old_position; cpShape *shape;
float rotation;
float old_rotation;
V2 acceleration;
float angular_acceleration;
}; };
struct Player // gotta update the serialization functions when this changes
struct GameState
{ {
struct Body body; cpSpace *space;
struct Player
{
struct Box box;
bool connected; bool connected;
V2 input; V2 input;
}; } players[MAX_PLAYERS];
struct GameState
{
struct Player players[MAX_PLAYERS];
int num_boxes; int num_boxes;
struct Box struct Box boxes[MAX_BOXES];
{
struct Body body;
} boxes[MAX_BOXES];
}; };
struct ServerToClient struct ServerToClient
{ {
struct GameState cur_gs; struct GameState *cur_gs;
int your_player; int your_player;
}; };
@ -80,7 +82,19 @@ struct ClientToServer
void server(void *data); void server(void *data);
// gamestate // gamestate
void initialize(struct GameState *gs); // must do this to place boxes into it and process
void destroy(struct GameState *gs);
void process(struct GameState *gs, float dt); // does in place void process(struct GameState *gs, float dt); // does in place
void into_bytes(struct ServerToClient *gs, char *out_bytes, int *out_len, int max_len);
void from_bytes(struct ServerToClient *gs, char *bytes, int max_len);
// box
struct Box box_new(struct GameState *gs, V2 pos);
void box_destroy(struct Box * box);
V2 box_pos(struct Box box);
V2 box_vel(struct Box box);
float box_rotation(struct Box box);
float box_angular_velocity(struct Box box);
// debug draw // debug draw
void dbg_drawall(); void dbg_drawall();

Loading…
Cancel
Save