Switch to chipmunk

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

3
.gitmodules vendored

@ -1,3 +1,6 @@
[submodule "thirdparty/enet"]
path = thirdparty/enet
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",
"cmath": "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
@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
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
cl /MP /Zi /Fd"flight.pdb" /I"thirdparty" /Fe"flight" /I"thirdparty\enet\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
setlocal enabledelayedexpansion enableextensions
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 <stdlib.h> // malloc
#include <stdio.h>
#include <stdio.h> // assert logging
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,
// 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;
body->position = V2add(body->position, V2sub(current, body->old_position));
body->position = V2add(body->position, V2scale(body->acceleration, dt * dt));
body->old_position = current;
box_destroy(&gs->players[i].box);
}
// rotation
for (int i = 0; i < gs->num_boxes; i++)
{
float current = body->rotation;
body->rotation = body->rotation + (current - body->old_rotation);
body->rotation = body->rotation + body->angular_acceleration * dt * dt;
body->old_rotation = current;
box_destroy(&gs->boxes[i]);
}
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;
};
assert(gs->space != NULL);
float halfbox = BOX_SIZE / 2.0f;
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);
cpSpaceAddShape(gs->space, shape);
cpBodySetPosition(body, cpv(pos.x, pos.y));
return (struct Box){
.body = body,
.shape = shape,
};
}
struct ProcessBody make_process_body(struct Body *from)
void box_destroy(struct Box *box)
{
float halfbox = BOX_SIZE / 2.0f;
struct ProcessBody to_return =
{
.vertices = {
// important that the first one is the upper right, used to deduce rotation from vertex position
// @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
V2add(from->position, V2rotate((V2){.x = halfbox, .y = halfbox}, from->rotation)), // bottom right
V2add(from->position, V2rotate((V2){.x = -halfbox, .y = halfbox}, from->rotation)), // lower left
V2add(from->position, V2rotate((V2){.x = -halfbox, .y = -halfbox}, from->rotation)), // upper left
},
.body = from,
};
return to_return;
cpShapeFree(box->shape);
cpBodyFree(box->body);
box->shape = NULL;
box->body = NULL;
}
static void project(struct ProcessBody *from, V2 axis, float *min, float *max)
static V2 cp_to_v2(cpVect v)
{
float DotP = V2dot(axis, from->vertices[0]);
return (V2){.x = v.x, .y = v.y};
}
// Set the minimum and maximum values to the projection of the first vertex
*min = DotP;
*max = DotP;
static cpVect v2_to_cp(V2 v)
{
return cpv(v.x, v.y);
}
for (int I = 1; I < 4; I++)
{
// Project the rest of the vertices onto the axis and extend
// the interval to the left/right if necessary
DotP = V2dot(axis, from->vertices[I]);
V2 box_pos(struct Box box)
{
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);
}
*min = fmin(DotP, *min);
*max = fmax(DotP, *max);
#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)
{
memwrite(out, f);
}
void des_float(char **in, float *f)
{
memread(in, f);
}
void ser_int(char **out, int i)
{
memwrite(out, i);
}
static float interval_distance(float min_a, float max_a, float min_b, float max_b)
void des_int(char **in, int *i)
{
if (min_a < min_b)
return min_b - max_a;
else
return min_a - max_b;
memread(in, i);
}
static void move_vertices(V2 *vertices, int num, V2 shift)
void ser_bool(char **out, bool b)
{
for (int i = 0; i < num; i++)
**out = (char)b;
*out += 1;
}
void des_bool(char **in, bool *b)
{
*b = (bool)**in;
*in += 1;
}
void ser_V2(char **out, V2 v)
{
ser_float(out, v.x);
ser_float(out, v.y);
}
void des_V2(char **in, V2 *v)
{
des_float(in, &v->x);
des_float(in, &v->y);
}
void ser_box(char **out, struct Box *b)
{
// box must not be null, dummy!
assert(b->body != NULL);
ser_V2(out, box_pos(*b));
ser_V2(out, box_vel(*b));
ser_float(out, box_rotation(*b));
ser_float(out, box_angular_velocity(*b));
}
// 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);
}
void ser_player(char **out, struct Player *p)
{
ser_bool(out, p->connected);
if (p->connected)
{
vertices[i] = V2add(vertices[i], shift);
ser_box(out, &p->box);
ser_V2(out, p->input);
}
}
void process(struct GameState *gs, float dt)
void des_player(char **in, struct Player *p, struct GameState *gs)
{
// process input
int num_bodies = gs->num_boxes;
for (int i = 0; i < MAX_PLAYERS; i++)
des_bool(in, &p->connected);
if (p->connected)
{
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;
des_box(in, &p->box, gs);
des_V2(in, &p->input);
}
}
// @Robust do this without malloc
// @Robust really think about if <= makes more sense than < here...
#define LEN_CHECK() assert(bytes - original_bytes <= max_len)
struct ProcessBody *bodies = malloc(sizeof *bodies * num_bodies);
int cur_body_index = 0;
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;
ser_int(&bytes, msg->your_player);
for (int i = 0; i < MAX_PLAYERS; i++)
{
struct Player *p = &gs->players[i];
if (!p->connected)
continue;
integrate_acceleration(&p->body, dt);
bodies[cur_body_index] = make_process_body(&p->body);
cur_body_index++;
ser_player(&bytes, &gs->players[i]);
LEN_CHECK();
}
// @Robust invalid message on num boxes bigger than max boxes
ser_int(&bytes, gs->num_boxes);
LEN_CHECK();
for (int i = 0; i < gs->num_boxes; i++)
{
integrate_acceleration(&gs->boxes[i].body, dt);
bodies[cur_body_index] = make_process_body(&gs->boxes[i].body);
cur_body_index++;
ser_box(&bytes, &gs->boxes[i]);
LEN_CHECK();
}
assert(cur_body_index == num_bodies);
*out_len = bytes - original_bytes;
}
// Collision
// @Robust handle when bodies are overlapping (even perfectly)
for (int i = 0; i < num_bodies; i++)
{
for (int ii = 0; ii < num_bodies; ii++)
{
if (ii == i)
continue;
struct ProcessBody *from = &bodies[i];
struct ProcessBody *to = &bodies[ii];
dbg_line(from->body->position, to->body->position);
float MinDistance = 10000.0f;
struct Edge
{
struct ProcessBody *parent;
V2 *from;
V2 *to;
};
struct ProcessBody *bodies[2] = {from, to};
bool was_collision = false;
V2 normal = {0};
struct Edge edge = {0};
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;
V2 *edge_from = &body->vertices[edge_from_i];
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;
project(from, axis, &min_from, &max_from);
project(to, axis, &min_to, &max_to);
float distance = interval_distance(min_from, min_to, max_from, max_to);
if (distance > 0.0f)
break;
else if (fabsf(distance) < MinDistance)
{
MinDistance = fabsf(distance);
was_collision = true;
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));
move_vertices(to->vertices, 4, V2scale(axis, intersection_depth * 0.5f));
}
}
}
void from_bytes(struct ServerToClient *msg, char *bytes, int max_len)
{
struct GameState *gs = msg->cur_gs;
// Wall
if (true)
char *original_bytes = bytes;
// destroy and free all chipmunk
destroy(gs);
initialize(gs);
des_int(&bytes, &msg->your_player);
LEN_CHECK();
for (int i = 0; i < MAX_PLAYERS; i++)
{
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;
}
}
}
des_player(&bytes, &gs->players[i], gs);
LEN_CHECK();
}
// Correct for differences in vertex position
const int edge_update_iters = 3;
for (int iter = 0; iter < edge_update_iters; iter++)
des_int(&bytes, &gs->num_boxes);
LEN_CHECK();
for (int i = 0; i < gs->num_boxes; i++)
{
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);
float len = V2length(line);
float diff = len - BOX_SIZE;
line = V2normalize(line);
*from = V2add(*from, V2scale(line, diff * 0.5f));
*to = V2sub(*to, V2scale(line, diff * 0.5f));
}
}
des_box(&bytes, &gs->boxes[i], gs);
LEN_CHECK();
}
}
// Reupdate the positions of the bodies based on how the vertices changed
for (int i = 0; i < num_bodies; i++)
void process(struct GameState *gs, float dt)
{
assert(gs->space != NULL);
// process input
for (int i = 0; i < MAX_PLAYERS; i++)
{
float upper_right_angle = V2angle(V2sub(bodies[i].vertices[0], bodies[i].body->position));
bodies[i].body->rotation = upper_right_angle - (PI / 4.0f);
V2 avg = {0};
for (int v_i = 0; v_i < 4; v_i++)
{
avg = V2add(avg, bodies[i].vertices[v_i]);
}
avg = V2scale(avg, 1.0f / 4.0f);
bodies[i].body->position = avg;
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
initialize(&gs);
sg_desc sgdesc = {.context = sapp_sgcontext()};
sg_setup(&sgdesc);
if (!sg_isvalid())
@ -114,17 +116,11 @@ static void frame(void)
// 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."
// ^^ 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;
if (event.packet->dataLength != sizeof(msg))
{
Log("Unknown packet size: %zd\n", event.packet->dataLength);
}
else
{
memcpy(&msg, event.packet->data, sizeof(msg));
myplayer = msg.your_player;
gs = msg.cur_gs;
}
struct ServerToClient msg = {
.cur_gs = &gs,
};
// @Robust maximum acceptable message size?
from_bytes(&msg, event.packet->data, event.packet->dataLength);
enet_packet_destroy(event.packet);
break;
case ENET_EVENT_TYPE_DISCONNECT:
@ -179,7 +175,8 @@ static void frame(void)
// camera go to player
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);
@ -204,14 +201,14 @@ static void frame(void)
continue;
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f);
sgp_push_transform();
sgp_rotate_at(p->body.rotation, p->body.position.x, p->body.position.y);
sgp_draw_filled_rect(p->body.position.x - halfbox, p->body.position.y - halfbox, BOX_SIZE, BOX_SIZE);
sgp_rotate_at(box_rotation(p->box), box_pos(p->box).x, box_pos(p->box).y);
sgp_draw_filled_rect(box_pos(p->box).x - halfbox, box_pos(p->box).y - halfbox, BOX_SIZE, BOX_SIZE);
sgp_pop_transform();
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 to = V2add(p->body.position, vel);
sgp_draw_line(p->body.position.x, p->body.position.y, to.x, to.y);
V2 vel = box_vel(p->box);
V2 to = V2add(box_pos(p->box), vel);
sgp_draw_line(box_pos(p->box).x, box_pos(p->box).y, to.x, to.y);
}
// boxes
@ -219,12 +216,15 @@ static void frame(void)
for (int i = 0; i < gs.num_boxes; i++)
{
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);
V2 vel = (V2scale(V2sub(gs.boxes[i].body.position, gs.boxes[i].body.old_position), 60.0f));
V2 to = V2add(gs.boxes[i].body.position, vel);
sgp_draw_line(gs.boxes[i].body.position.x, gs.boxes[i].body.position.y, to.x, to.y);
V2 vel = box_vel(gs.boxes[i]);
V2 to = V2add(box_pos(gs.boxes[i]), vel);
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)
{
destroy(&gs);
sgp_shutdown();
sg_shutdown();
enet_deinitialize();

@ -14,31 +14,20 @@ void server(void *data)
stm_setup();
struct GameState gs = {0};
initialize(&gs);
// two boxes stacked on top
if (false)
if (true)
{
gs.boxes[0] = (struct Box){
.body = (struct Body){
.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.boxes[0] = box_new(&gs, (V2){.x = 0.75f, .y = 0.0});
gs.boxes[1] = box_new(&gs, (V2){.x = 0.75f, .y = 0.5f});
gs.num_boxes = 2;
}
// one box
if (true)
if (false)
{
gs.boxes[0] = (struct Box){
.body = (struct Body){
.position = (P2){.x = 0.75f, .y = 0.0}},
};
gs.boxes[0].body.old_position = gs.boxes[0].body.position;
gs.boxes[0] = box_new(&gs, (V2){.x = 0.75f, .y = 0.0});
gs.num_boxes = 1;
}
@ -107,11 +96,10 @@ void server(void *data)
else
{
event.peer->data = (void *)player_slot;
gs.players[player_slot] = (struct Player){.body.position = (V2){
.x = 0.0f,
.y = 1.0f * (float)player_slot,
}};
gs.players[player_slot].body.old_position = gs.players[player_slot].body.position;
gs.players[player_slot].box = box_new(&gs, (V2){
.x = 0.0f,
.y = 1.0f * (float)player_slot,
});
gs.players[player_slot].connected = true;
}
@ -140,8 +128,10 @@ void server(void *data)
break;
case ENET_EVENT_TYPE_DISCONNECT:
Log("%" PRId64 " disconnected.\n", (int64_t)event.peer->data);
gs.players[(int64_t)event.peer->data].connected = false;
int player_index = (int64_t)event.peer->data;
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;
}
}
@ -149,13 +139,15 @@ void server(void *data)
total_time += (float)stm_sec(stm_diff(stm_now(), last_processed_time));
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)
{
process(&gs, 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++)
{
// @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;
}
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;
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);
}
}
destroy(&gs);
enet_host_destroy(server);
enet_deinitialize();
}

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

@ -13,7 +13,7 @@ WinKill, Flight
WinActivate, 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
@ -26,6 +26,6 @@ WinKill, Flight
WinActivate, 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

@ -1,5 +1,10 @@
#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
#include <math.h>
@ -15,6 +20,16 @@ typedef sgp_vec2 sgp_point;
#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>
#ifndef _STDBOOL
@ -31,43 +46,30 @@ typedef sgp_point P2;
#define Log(...) \
fprintf(stdout, "%s:%d | ", __FILE__, __LINE__); \
fprintf(stdout, __VA_ARGS__)
#define MAX_BOXES 32
#define MAX_PLAYERS 4
#define BOX_SIZE 0.5f
struct Body
struct Box
{
P2 position;
P2 old_position;
float rotation;
float old_rotation;
V2 acceleration;
float angular_acceleration;
};
struct Player
{
struct Body body;
bool connected;
V2 input;
cpBody *body;
cpShape *shape;
};
// gotta update the serialization functions when this changes
struct GameState
{
struct Player players[MAX_PLAYERS];
int num_boxes;
struct Box
cpSpace *space;
struct Player
{
struct Body body;
} boxes[MAX_BOXES];
struct Box box;
bool connected;
V2 input;
} players[MAX_PLAYERS];
int num_boxes;
struct Box boxes[MAX_BOXES];
};
struct ServerToClient
{
struct GameState cur_gs;
struct GameState *cur_gs;
int your_player;
};
@ -80,7 +82,19 @@ struct ClientToServer
void server(void *data);
// 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 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
void dbg_drawall();

Loading…
Cancel
Save