@ -1,7 +1,7 @@
# include <chipmunk.h>
# include <chipmunk.h>
# define QUEUE_IMPL
# define QUEUE_IMPL
# include "stdbool.h"
# include "queue.h"
# include "queue.h"
# include "stdbool.h"
# include "types.h"
# include "types.h"
# include "ipsettings.h" // debug/developer settings
# include "ipsettings.h" // debug/developer settings
@ -30,7 +30,6 @@ void __assert(bool cond, const char* file, int line, const char* cond_string)
}
}
}
}
static V2 cp_to_v2 ( cpVect v )
static V2 cp_to_v2 ( cpVect v )
{
{
return ( V2 ) { . x = ( float ) v . x , . y = ( float ) v . y } ;
return ( V2 ) { . x = ( float ) v . x , . y = ( float ) v . y } ;
@ -55,8 +54,10 @@ Entity* get_entity_even_if_dead(GameState* gs, EntityID id)
{
{
return NULL ;
return NULL ;
}
}
assert ( id . index < gs - > cur_next_entity | | gs - > cur_next_entity = = 0 ) ;
if ( ! ( id . index < gs - > cur_next_entity | | gs - > cur_next_entity = = 0 ) )
assert ( id . index < gs - > max_entities ) ;
return NULL ;
if ( ! ( id . index < gs - > max_entities ) )
return NULL ;
Entity * to_return = & gs - > entities [ id . index ] ;
Entity * to_return = & gs - > entities [ id . index ] ;
// don't validate the generation either
// don't validate the generation either
return to_return ;
return to_return ;
@ -386,17 +387,13 @@ static void grid_correct_for_holes(GameState* gs, struct Entity* grid)
V2 cur_local_pos = entity_shape_pos ( N ) ;
V2 cur_local_pos = entity_shape_pos ( N ) ;
const V2 dirs [ ] = {
const V2 dirs [ ] = {
( V2 ) {
( V2 ) {
. x = - 1.0f , . y = 0.0f
. x = - 1.0f , . y = 0.0f } ,
} ,
( V2 ) {
( V2 ) {
. x = 1.0f , . y = 0.0f
. x = 1.0f , . y = 0.0f } ,
} ,
( V2 ) {
( V2 ) {
. x = 0.0f , . y = 1.0f
. x = 0.0f , . y = 1.0f } ,
} ,
( V2 ) {
( V2 ) {
. x = 0.0f , . y = - 1.0f
. x = 0.0f , . y = - 1.0f } ,
} ,
} ;
} ;
int num_dirs = sizeof ( dirs ) / sizeof ( * dirs ) ;
int num_dirs = sizeof ( dirs ) / sizeof ( * dirs ) ;
@ -557,6 +554,7 @@ V2 grid_world_to_local(Entity* grid, V2 world)
}
}
V2 grid_local_to_world ( Entity * grid , V2 local )
V2 grid_local_to_world ( Entity * grid , V2 local )
{
{
assert ( grid - > is_grid ) ;
return cp_to_v2 ( cpBodyLocalToWorld ( grid - > body , v2_to_cp ( local ) ) ) ;
return cp_to_v2 ( cpBodyLocalToWorld ( grid - > body , v2_to_cp ( local ) ) ) ;
}
}
// returned snapped position is in world coordinates
// returned snapped position is in world coordinates
@ -774,6 +772,8 @@ enum GameVersion
VAddedLastUsedMedbay ,
VAddedLastUsedMedbay ,
VAddedSquads ,
VAddedSquads ,
VAddedSquadInvites ,
VAddedSquadInvites ,
VRemovedTimeFromDiskSave , // did this to avoid wayy too big a time causing precision problems
VReallyRemovedTimeFromDiskSave , // apparently last one didn't work
VMax , // this minus one will be the version used
VMax , // this minus one will be the version used
} ;
} ;
@ -810,7 +810,6 @@ SerMaybeFailure ser_entityid(SerState* ser, EntityID* id)
SerMaybeFailure ser_inputframe ( SerState * ser , InputFrame * i )
SerMaybeFailure ser_inputframe ( SerState * ser , InputFrame * i )
{
{
SER_VAR ( & i - > tick ) ;
SER_VAR ( & i - > tick ) ;
SER_VAR ( & i - > id ) ;
SER_MAYBE_RETURN ( ser_V2 ( ser , & i - > movement ) ) ;
SER_MAYBE_RETURN ( ser_V2 ( ser , & i - > movement ) ) ;
SER_VAR ( & i - > take_over_squad ) ;
SER_VAR ( & i - > take_over_squad ) ;
SER_ASSERT ( i - > take_over_squad > = 0 | | i - > take_over_squad = = - 1 ) ;
SER_ASSERT ( i - > take_over_squad > = 0 | | i - > take_over_squad = = - 1 ) ;
@ -823,9 +822,7 @@ SerMaybeFailure ser_inputframe(SerState* ser, InputFrame* i)
}
}
SER_VAR ( & i - > seat_action ) ;
SER_VAR ( & i - > seat_action ) ;
SER_MAYBE_RETURN ( ser_entityid ( ser , & i - > seat_to_inhabit ) ) ;
SER_MAYBE_RETURN ( ser_V2 ( ser , & i - > hand_pos ) ) ;
SER_MAYBE_RETURN ( ser_V2 ( ser , & i - > hand_pos ) ) ;
SER_MAYBE_RETURN ( ser_entityid ( ser , & i - > grid_hand_pos_local_to ) ) ;
SER_VAR ( & i - > dobuild ) ;
SER_VAR ( & i - > dobuild ) ;
SER_VAR ( & i - > build_type ) ;
SER_VAR ( & i - > build_type ) ;
@ -855,7 +852,7 @@ SerMaybeFailure ser_player(SerState* ser, Player* p)
SerMaybeFailure ser_entity ( SerState * ser , GameState * gs , Entity * e )
SerMaybeFailure ser_entity ( SerState * ser , GameState * gs , Entity * e )
{
{
SER_VAR ( & e - > no_save_to_disk ) ; // this is always false when saving to disk?
SER_VAR ( & e - > no_save_to_disk ) ; // @Robust this is always false when saving to disk?
SER_VAR ( & e - > generation ) ;
SER_VAR ( & e - > generation ) ;
SER_VAR ( & e - > damage ) ;
SER_VAR ( & e - > damage ) ;
@ -1023,16 +1020,11 @@ SerMaybeFailure ser_server_to_client(SerState* ser, ServerToClient* s)
SER_ASSERT ( ser - > version < VMax ) ;
SER_ASSERT ( ser - > version < VMax ) ;
if ( ! ser - > save_or_load_from_disk )
if ( ! ser - > save_or_load_from_disk )
SER_MAYBE_RETURN ( ser_opus_packets ( ser , s - > playback_buffer) ) ;
SER_MAYBE_RETURN ( ser_opus_packets ( ser , s - > audio_ playback_buffer) ) ;
GameState * gs = s - > cur_gs ;
GameState * gs = s - > cur_gs ;
int cur_next_entity = 0 ;
// completely reset and destroy all gamestate data
if ( ser - > serializing )
cur_next_entity = gs - > cur_next_entity ;
SER_VAR ( & cur_next_entity ) ;
SER_ASSERT ( cur_next_entity < = ser - > max_entity_index ) ;
if ( ! ser - > serializing )
if ( ! ser - > serializing )
{
{
// avoid a memset here very expensive. que rico!
// avoid a memset here very expensive. que rico!
@ -1041,8 +1033,23 @@ SerMaybeFailure ser_server_to_client(SerState* ser, ServerToClient* s)
gs - > cur_next_entity = 0 ; // updated on deserialization
gs - > cur_next_entity = 0 ; // updated on deserialization
}
}
int cur_next_entity = 0 ;
if ( ser - > serializing )
cur_next_entity = gs - > cur_next_entity ;
SER_VAR ( & cur_next_entity ) ;
SER_ASSERT ( cur_next_entity < = ser - > max_entity_index ) ;
if ( ! ser - > save_or_load_from_disk )
SER_MAYBE_RETURN ( ser_entityid ( ser , & gs - > cur_spacestation ) ) ;
SER_VAR ( & s - > your_player ) ;
SER_VAR ( & s - > your_player ) ;
if ( ser - > version > = VReallyRemovedTimeFromDiskSave & & ser - > save_or_load_from_disk )
{
}
else
{
SER_VAR ( & gs - > time ) ;
SER_VAR ( & gs - > time ) ;
}
SER_MAYBE_RETURN ( ser_V2 ( ser , & gs - > goldpos ) ) ;
SER_MAYBE_RETURN ( ser_V2 ( ser , & gs - > goldpos ) ) ;
@ -1228,13 +1235,47 @@ bool server_to_client_deserialize(struct ServerToClient* msg, char* bytes, size_
}
}
}
}
// only serializes up to the maximum inputs the server holds
SerMaybeFailure ser_client_to_server ( SerState * ser , ClientToServer * msg )
SerMaybeFailure ser_client_to_server ( SerState * ser , ClientToServer * msg )
{
{
SER_VAR ( & ser - > version ) ;
SER_VAR ( & ser - > version ) ;
SER_MAYBE_RETURN ( ser_opus_packets ( ser , msg - > mic_data ) ) ;
SER_MAYBE_RETURN ( ser_opus_packets ( ser , msg - > mic_data ) ) ;
for ( int i = 0 ; i < INPUT_BUFFER ; i + + )
// serialize input packets
size_t num ;
if ( ser - > serializing )
{
{
SER_MAYBE_RETURN ( ser_inputframe ( ser , & msg - > inputs [ i ] ) ) ;
num = queue_num_elements ( msg - > input_data ) ;
if ( num > INPUT_QUEUE_MAX )
num = INPUT_QUEUE_MAX ;
}
SER_VAR ( & num ) ;
SER_ASSERT ( num < = INPUT_QUEUE_MAX ) ;
if ( ser - > serializing )
{
size_t to_skip = queue_num_elements ( msg - > input_data ) - num ;
size_t i = 0 ;
QUEUE_ITER ( msg - > input_data , cur_header )
{
if ( i < to_skip )
{
i + + ;
}
else
{
InputFrame * cur = ( InputFrame * ) cur_header - > data ;
SER_MAYBE_RETURN ( ser_inputframe ( ser , cur ) ) ;
}
}
}
else
{
for ( size_t i = 0 ; i < num ; i + + )
{
InputFrame * new_frame = ( InputFrame * ) queue_push_element ( msg - > input_data ) ;
SER_ASSERT ( new_frame ! = NULL ) ;
SER_MAYBE_RETURN ( ser_inputframe ( ser , new_frame ) ) ;
}
}
}
return ser_ok ;
return ser_ok ;
}
}
@ -1295,8 +1336,8 @@ bool client_to_server_deserialize(GameState* gs, struct ClientToServer* msg, cha
}
}
// has to be global var because can only get this information
// has to be global var because can only get this information
static cpShape* closest_to_point_in_radius_result = NULL ;
static THREADLOCAL cpShape * closest_to_point_in_radius_result = NULL ;
static float closest_to_point_in_radius_result_largest_dist = 0.0f ;
static THREADLOCAL float closest_to_point_in_radius_result_largest_dist = 0.0f ;
static void closest_point_callback_func ( cpShape * shape , cpContactPointSet * points , void * data )
static void closest_point_callback_func ( cpShape * shape , cpContactPointSet * points , void * data )
{
{
assert ( points - > count = = 1 ) ;
assert ( points - > count = = 1 ) ;
@ -1380,17 +1421,24 @@ uint64_t tick(GameState* gs)
return ( uint64_t ) floor ( gs - > time / ( ( double ) TIMESTEP ) ) ;
return ( uint64_t ) floor ( gs - > time / ( ( double ) TIMESTEP ) ) ;
}
}
V2 get_world_hand_pos ( GameState * gs , InputFrame * input , Entity * player )
Entity * grid_to_build_on ( GameState * gs , V2 world_hand_pos )
{
{
Entity * potential_grid = get_entity ( gs , input - > grid_hand_pos_local_to ) ;
return closest_to_point_in_radius ( gs , world_hand_pos , BUILD_BOX_SNAP_DIST_TO_SHIP ) ;
}
V2 potentially_snap_hand_pos ( GameState * gs , V2 world_hand_pos )
{
Entity * potential_grid = grid_to_build_on ( gs , world_hand_pos ) ;
if ( potential_grid ! = NULL )
if ( potential_grid ! = NULL )
{
{
return grid_local_to_world ( potential_grid , input - > hand_pos ) ;
world_hand_pos = grid_snapped_box_pos ( potential_grid , world_ hand_pos) ;
}
}
else
return world_hand_pos ;
{
return V2add ( entity_pos ( player ) , input - > hand_pos ) ;
}
}
V2 get_world_hand_pos ( GameState * gs , InputFrame * input , Entity * player )
{
return potentially_snap_hand_pos ( gs , V2add ( entity_pos ( player ) , input - > hand_pos ) ) ;
}
}
// return true if used the energy
// return true if used the energy
@ -1439,7 +1487,7 @@ EntityID create_spacestation(GameState* gs)
Entity * grid = new_entity ( gs ) ;
Entity * grid = new_entity ( gs ) ;
grid_create ( gs , grid ) ;
grid_create ( gs , grid ) ;
grid - > no_save_to_disk = true ;
grid - > no_save_to_disk = true ;
entity_set_pos ( grid , ( V2 ) { - 15 0 .0f, 0.0f } ) ;
entity_set_pos ( grid , ( V2 ) { - 15 .0f, 0.0f } ) ;
entity_ensure_in_orbit ( grid ) ;
entity_ensure_in_orbit ( grid ) ;
Entity * explosion_box = new_entity ( gs ) ;
Entity * explosion_box = new_entity ( gs ) ;
box_create ( gs , explosion_box , grid , ( V2 ) { 0 } ) ;
box_create ( gs , explosion_box , grid , ( V2 ) { 0 } ) ;
@ -1478,11 +1526,15 @@ void exit_seat(GameState* gs, Entity* seat_in, Entity* player)
cpBodySetVelocity ( player - > body , v2_to_cp ( player_vel ( gs , player ) ) ) ;
cpBodySetVelocity ( player - > body , v2_to_cp ( player_vel ( gs , player ) ) ) ;
}
}
void process_fixed_timestep ( GameState * gs )
{
process ( gs , TIMESTEP ) ;
}
void process ( GameState * gs , float dt )
void process ( GameState * gs , float dt )
{
{
assert ( gs - > space ! = NULL ) ;
assert ( gs - > space ! = NULL ) ;
assert ( dt = = TIMESTEP ) ; // @TODO fix tick being incremented every time
gs - > time + = dt ;
gs - > time + = dt ;
// process input
// process input
@ -1494,7 +1546,8 @@ void process(GameState* gs, float dt)
{
{
player - > squad = SquadNone ;
player - > squad = SquadNone ;
}
}
else {
else
{
bool squad_taken = false ;
bool squad_taken = false ;
PLAYERS_ITER ( gs - > players , other_player )
PLAYERS_ITER ( gs - > players , other_player )
{
{
@ -1601,18 +1654,19 @@ void process(GameState* gs, float dt)
// process movement
// process movement
{
{
// no cheating by making movement bigger than length 1
// no cheating by making movement bigger than length 1
float movement_strength = V2length ( player - > input . movement ) ;
V2 movement_this_tick = ( V2 ) { 0 } ;
if ( movement_strength ! = 0.0f )
if ( V2length ( player - > input . movement ) > 0.0f )
{
{
player - > input . movement = V2scale ( V2normalize ( player - > input . movement ) , clamp ( V2length ( player - > input . movement ) , 0.0f , 1.0f ) ) ;
movement_this_tick = V2scale ( V2normalize ( player - > input . movement ) , clamp ( V2length ( player - > input . movement ) , 0.0f , 1.0f ) ) ;
player - > input . movement = ( V2 ) { 0 } ;
}
}
Entity * seat_inside_of = get_entity ( gs , p - > currently_inside_of_box ) ;
Entity * seat_inside_of = get_entity ( gs , p - > currently_inside_of_box ) ;
if ( seat_inside_of = = NULL )
if ( seat_inside_of = = NULL )
{
{
cpShapeSetFilter ( p - > shape , PLAYER_SHAPE_FILTER ) ;
cpShapeSetFilter ( p - > shape , PLAYER_SHAPE_FILTER ) ;
cpBodyApplyForceAtWorldPoint ( p - > body , v2_to_cp ( V2scale ( player- > input . movement, PLAYER_JETPACK_FORCE ) ) , cpBodyGetPosition ( p - > body ) ) ;
cpBodyApplyForceAtWorldPoint ( p - > body , v2_to_cp ( V2scale ( movement_this_tick , PLAYER_JETPACK_FORCE ) ) , cpBodyGetPosition ( p - > body ) ) ;
p - > damage + = movement_ streng th * dt * PLAYER_JETPACK_SPICE_PER_SECOND ;
p - > damage + = V2length ( movement_ this_tick) * dt * PLAYER_JETPACK_SPICE_PER_SECOND ;
}
}
else
else
{
{
@ -1627,9 +1681,9 @@ void process(GameState* gs, float dt)
Entity * g = get_entity ( gs , seat_inside_of - > shape_parent_entity ) ;
Entity * g = get_entity ( gs , seat_inside_of - > shape_parent_entity ) ;
V2 target_direction = { 0 } ;
V2 target_direction = { 0 } ;
if ( V2length ( player - > input . movement ) > 0.0f )
if ( V2length ( movement_this_tick ) > 0.0f )
{
{
target_direction = V2normalize ( player- > input . movement) ;
target_direction = V2normalize ( movement_this_tick ) ;
}
}
BOXES_ITER ( gs , cur , g )
BOXES_ITER ( gs , cur , g )
{
{
@ -1652,7 +1706,7 @@ void process(GameState* gs, float dt)
V2 world_build = world_hand_pos ;
V2 world_build = world_hand_pos ;
// @Robust sanitize this input so player can't build on any grid in the world
// @Robust sanitize this input so player can't build on any grid in the world
Entity * target_grid = get_entity ( gs , player - > input . grid_hand_pos_local_to ) ;
Entity * target_grid = grid_to_build_on ( gs , world_hand_pos ) ;
cpShape * nearest = cpSpacePointQueryNearest ( gs - > space , v2_to_cp ( world_build ) , 0.01f , cpShapeFilterNew ( CP_NO_GROUP , CP_ALL_CATEGORIES , BOXES ) , & info ) ;
cpShape * nearest = cpSpacePointQueryNearest ( gs - > space , v2_to_cp ( world_build ) , 0.01f , cpShapeFilterNew ( CP_NO_GROUP , CP_ALL_CATEGORIES , BOXES ) , & info ) ;
if ( nearest ! = NULL )
if ( nearest ! = NULL )
{
{