added dead reckoning to the characters

This commit is contained in:
Magnus Auvinen 2008-09-23 07:43:41 +00:00
parent e21b6983ab
commit 33b50738e6
13 changed files with 334 additions and 222 deletions

View file

@ -105,12 +105,14 @@ Objects = [
]),
NetObject("Character_Core", [
NetIntAny("tick"),
NetIntAny("x"),
NetIntAny("y"),
NetIntAny("vx"),
NetIntAny("vy"),
NetIntAny("angle"),
NetIntRange("direction", -1, 1),
NetIntRange("jumped", 0, 3),
NetIntRange("hooked_player", 0, 'MAX_CLIENTS-1'),
@ -125,7 +127,6 @@ Objects = [
NetObject("Character:Character_Core", [
NetIntRange("player_state", 0, 'NUM_PLAYERSTATES-1'),
NetIntRange("wanted_direction", -1, 1),
NetIntRange("health", 0, 10),
NetIntRange("armor", 0, 10),
NetIntRange("ammocount", 0, 10),
@ -138,7 +139,7 @@ Objects = [
NetIntRange("local", 0, 1),
NetIntRange("cid", 0, 'MAX_CLIENTS-1'),
NetIntRange("team", -1, 1),
NetIntAny("score"),
NetIntAny("latency"),
NetIntAny("latency_flux"),

View file

@ -632,7 +632,9 @@ int net_tcp_send(NETSOCKET sock, const void *data, int size);
max_size - Maximum of data to write to the buffer.
Returns:
Number of bytes recvived. Negative value on failure.
Number of bytes recvived. Negative value on failure. When in
non-blocking mode, it returns 0 when there is no more data to
be fetched.
*/
int net_tcp_recv(NETSOCKET sock, void *data, int maxsize);

View file

@ -83,6 +83,8 @@ static int current_tick = 0;
static float intratick = 0;
static float ticktime = 0;
static int prev_tick = 0;
/* predicted time */
static int current_predtick = 0;
static float predintratick = 0;
@ -280,6 +282,7 @@ float client_intratick() { return intratick; }
float client_predintratick() { return predintratick; }
float client_ticktime() { return ticktime; }
int client_tick() { return current_tick; }
int client_prevtick() { return current_tick; }
int client_predtick() { return current_predtick; }
int client_tickspeed() { return SERVER_TICK_SPEED; }
float client_frametime() { return frametime; }
@ -1193,8 +1196,9 @@ static void client_update()
snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT];
snapshots[SNAP_CURRENT] = next;
/* set tick */
/* set ticks */
current_tick = snapshots[SNAP_CURRENT]->tick;
prev_tick = snapshots[SNAP_PREV]->tick;
if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV])
{

View file

@ -116,6 +116,11 @@ typedef struct
*/
int client_tick();
/*
Function: client_prevtick
Returns the tick of the previous snapshot.
*/
int client_prevtick();
/*
Function: client_intratick

View file

@ -158,7 +158,7 @@ void PLAYERS::render_player(
bool stationary = player.vx < 1 && player.vx > -1;
bool inair = col_check_point(player.x, player.y+16) == 0;
bool want_other_dir = (player.wanted_direction == -1 && vel.x > 0) || (player.wanted_direction == 1 && vel.x < 0);
bool want_other_dir = (player.direction == -1 && vel.x > 0) || (player.direction == 1 && vel.x < 0);
// evaluate animation
float walk_time = fmod(position.x, 100.0f)/100.0f;
@ -194,8 +194,8 @@ void PLAYERS::render_player(
}
gameclient.effects->skidtrail(
position+vec2(-player.wanted_direction*6,12),
vec2(-player.wanted_direction*100*length(vel),-50)
position+vec2(-player.direction*6,12),
vec2(-player.direction*100*length(vel),-50)
);
}
@ -425,27 +425,27 @@ void PLAYERS::render_player(
void PLAYERS::on_render()
{
int num = snap_num_items(SNAP_CURRENT);
for(int i = 0; i < num; i++)
//int num = snap_num_items(SNAP_CURRENT);
for(int i = 0; i < MAX_CLIENTS; i++)
{
SNAP_ITEM item;
const void *data = snap_get_item(SNAP_CURRENT, i, &item);
// only render active characters
if(!gameclient.snap.characters[i].active)
continue;
if(item.type == NETOBJTYPE_CHARACTER)
const void *prev_info = snap_find_item(SNAP_PREV, NETOBJTYPE_PLAYER_INFO, i);
const void *info = snap_find_item(SNAP_CURRENT, NETOBJTYPE_PLAYER_INFO, i);
if(prev_info && info)
{
const void *prev = snap_find_item(SNAP_PREV, item.type, item.id);
const void *prev_info = snap_find_item(SNAP_PREV, NETOBJTYPE_PLAYER_INFO, item.id);
const void *info = snap_find_item(SNAP_CURRENT, NETOBJTYPE_PLAYER_INFO, item.id);
NETOBJ_CHARACTER prev_char = gameclient.snap.characters[i].prev;
NETOBJ_CHARACTER cur_char = gameclient.snap.characters[i].cur;
if(prev && prev_info && info)
{
render_player(
(const NETOBJ_CHARACTER *)prev,
(const NETOBJ_CHARACTER *)data,
(const NETOBJ_PLAYER_INFO *)prev_info,
(const NETOBJ_PLAYER_INFO *)info
);
}
}
render_player(
&prev_char,
&cur_char,
(const NETOBJ_PLAYER_INFO *)prev_info,
(const NETOBJ_PLAYER_INFO *)info
);
}
}
}

View file

@ -150,15 +150,14 @@ void GAMECLIENT::on_console_init()
// add the some console commands
MACRO_REGISTER_COMMAND("team", "", con_team, this);
MACRO_REGISTER_COMMAND("kill", "", con_kill, this);
// let all the other components register their console commands
for(int i = 0; i < all.num; i++)
all.components[i]->on_console_init();
}
void GAMECLIENT::on_init()
{
// init all components
for(int i = 0; i < all.num; i++)
all.components[i]->on_init();
@ -250,7 +249,6 @@ int GAMECLIENT::on_snapinput(int *data)
return controls->snapinput(data);
}
void GAMECLIENT::on_connected()
{
layers_init();
@ -479,14 +477,31 @@ void GAMECLIENT::process_events()
}
}
static void evolve(NETOBJ_CHARACTER *character, int tick)
{
WORLD_CORE tempworld;
CHARACTER_CORE tempcore;
mem_zero(&tempcore, sizeof(tempcore));
tempcore.world = &tempworld;
tempcore.read(character);
//tempcore.input.direction = character->wanted_direction;
while(character->tick < tick)
{
character->tick++;
tempcore.tick(false);
tempcore.move();
tempcore.quantize();
}
tempcore.write(character);
}
void GAMECLIENT::on_snapshot()
{
// clear out the invalid pointers
mem_zero(&gameclient.snap, sizeof(gameclient.snap));
snap.local_cid = -1;
static int snapshot_count = 0;
snapshot_count++;
// secure snapshot
{
int num = snap_num_items(SNAP_CURRENT);
@ -517,11 +532,10 @@ void GAMECLIENT::on_snapshot()
}
}
// setup world view
// go trough all the items in the snapshot and gather the info we want
{
// 1. fetch local player
// 2. set him to the center
gameclient.snap.team_size[0] = gameclient.snap.team_size[1] = 0;
snap.team_size[0] = snap.team_size[1] = 0;
int num = snap_num_items(SNAP_CURRENT);
for(int i = 0; i < num; i++)
{
@ -532,88 +546,94 @@ void GAMECLIENT::on_snapshot()
{
const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data;
gameclient.clients[info->cid].team = info->team;
clients[info->cid].team = info->team;
snap.player_infos[info->cid] = info;
if(info->local)
{
gameclient.snap.local_info = info;
const void *data = snap_find_item(SNAP_CURRENT, NETOBJTYPE_CHARACTER, item.id);
if(data)
{
gameclient.snap.local_character = (const NETOBJ_CHARACTER *)data;
gameclient.local_character_pos = vec2(gameclient.snap.local_character->x, gameclient.snap.local_character->y);
const void *p = snap_find_item(SNAP_PREV, NETOBJTYPE_CHARACTER, item.id);
if(p)
gameclient.snap.local_prev_character = (NETOBJ_CHARACTER *)p;
}
snap.local_cid = item.id;
snap.local_info = info;
if (info->team == -1)
gameclient.snap.spectate = true;
snap.spectate = true;
}
// calculate team-balance
if(info->team != -1)
gameclient.snap.team_size[info->team]++;
snap.team_size[info->team]++;
}
else if(item.type == NETOBJTYPE_GAME)
gameclient.snap.gameobj = (NETOBJ_GAME *)data;
else if(item.type == NETOBJTYPE_FLAG)
else if(item.type == NETOBJTYPE_CHARACTER)
{
gameclient.snap.flags[item.id%2] = (const NETOBJ_FLAG *)data;
const void *old = snap_find_item(SNAP_PREV, NETOBJTYPE_CHARACTER, item.id);
if(old)
{
snap.characters[item.id].active = true;
snap.characters[item.id].prev = *((const NETOBJ_CHARACTER *)old);
snap.characters[item.id].cur = *((const NETOBJ_CHARACTER *)data);
// perform dead reckoning
if(snap.characters[item.id].prev.tick)
evolve(&snap.characters[item.id].prev, client_prevtick());
if(snap.characters[item.id].cur.tick)
evolve(&snap.characters[item.id].cur, client_tick());
}
}
else if(item.type == NETOBJTYPE_GAME)
snap.gameobj = (NETOBJ_GAME *)data;
else if(item.type == NETOBJTYPE_FLAG)
snap.flags[item.id%2] = (const NETOBJ_FLAG *)data;
}
}
// setup local pointers
if(snap.local_cid >= 0)
{
SNAPSTATE::CHARACTERINFO *c = &snap.characters[snap.local_cid];
if(c->active)
{
snap.local_character = &c->cur;
snap.local_prev_character = &c->prev;
local_character_pos = vec2(snap.local_character->x, snap.local_character->y);
}
}
// update render info
for(int i = 0; i < MAX_CLIENTS; i++)
gameclient.clients[i].update_render_info();
clients[i].update_render_info();
}
void GAMECLIENT::on_predict()
{
// store the previous values so we can detect prediction errors
CHARACTER_CORE before_prev_char = predicted_prev_char;
CHARACTER_CORE before_char = predicted_char;
// we can't predict without our own id or own character
if(snap.local_cid == -1 || !snap.characters[snap.local_cid].active)
return;
// repredict character
WORLD_CORE world;
world.tuning = tuning;
int local_cid = -1;
// search for players
for(int i = 0; i < snap_num_items(SNAP_CURRENT); i++)
for(int i = 0; i < MAX_CLIENTS; i++)
{
SNAP_ITEM item;
const void *data = snap_get_item(SNAP_CURRENT, i, &item);
int client_id = item.id;
if(item.type == NETOBJTYPE_CHARACTER)
{
const NETOBJ_CHARACTER *character = (const NETOBJ_CHARACTER *)data;
gameclient.clients[client_id].predicted.world = &world;
world.characters[client_id] = &gameclient.clients[client_id].predicted;
gameclient.clients[client_id].predicted.read(character);
}
else if(item.type == NETOBJTYPE_PLAYER_INFO)
{
const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data;
if(info->local)
local_cid = client_id;
}
if(!snap.characters[i].active)
continue;
gameclient.clients[i].predicted.world = &world;
world.characters[i] = &gameclient.clients[i].predicted;
gameclient.clients[i].predicted.read(&snap.characters[i].cur);
}
// we can't predict without our own id
if(local_cid == -1)
return;
// predict
for(int tick = client_tick()+1; tick <= client_predtick(); tick++)
{
// fetch the local
if(tick == client_predtick() && world.characters[local_cid])
predicted_prev_char = *world.characters[local_cid];
if(tick == client_predtick() && world.characters[snap.local_cid])
predicted_prev_char = *world.characters[snap.local_cid];
// first calculate where everyone should move
for(int c = 0; c < MAX_CLIENTS; c++)
@ -622,15 +642,17 @@ void GAMECLIENT::on_predict()
continue;
mem_zero(&world.characters[c]->input, sizeof(world.characters[c]->input));
if(local_cid == c)
if(snap.local_cid == c)
{
// apply player input
int *input = client_get_input(tick);
if(input)
world.characters[c]->input = *((NETOBJ_PLAYER_INPUT*)input);
world.characters[c]->tick(true);
}
else
world.characters[c]->tick(false);
world.characters[c]->tick();
}
// move all players and quantize their data
@ -643,14 +665,15 @@ void GAMECLIENT::on_predict()
world.characters[c]->quantize();
}
// check if we want to trigger effects
if(tick > last_new_predicted_tick)
{
last_new_predicted_tick = tick;
if(local_cid != -1 && world.characters[local_cid])
if(snap.local_cid != -1 && world.characters[snap.local_cid])
{
vec2 pos = world.characters[local_cid]->pos;
int events = world.characters[local_cid]->triggered_events;
vec2 pos = world.characters[snap.local_cid]->pos;
int events = world.characters[snap.local_cid]->triggered_events;
if(events&COREEVENT_GROUND_JUMP) gameclient.sounds->play(SOUNDS::CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos);
if(events&COREEVENT_AIR_JUMP)
{
@ -662,34 +685,26 @@ void GAMECLIENT::on_predict()
if(events&COREEVENT_HOOK_ATTACH_GROUND) gameclient.sounds->play(SOUNDS::CHN_WORLD, SOUND_HOOK_ATTACH_GROUND, 1.0f, pos);
//if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos);
}
/*
dbg_msg("predict", "%d %d %d", tick,
(int)world.players[c]->pos.x, (int)world.players[c]->pos.y,
(int)world.players[c]->vel.x, (int)world.players[c]->vel.y);*/
}
if(tick == client_predtick() && world.characters[local_cid])
predicted_char = *world.characters[local_cid];
if(tick == client_predtick() && world.characters[snap.local_cid])
predicted_char = *world.characters[snap.local_cid];
}
if(config.debug && predicted_tick == client_predtick())
if(config.debug && config.cl_predict && predicted_tick == client_predtick())
{
if(predicted_char.pos.x != before_char.pos.x ||
predicted_char.pos.y != before_char.pos.y)
{
dbg_msg("client", "prediction error, (%d %d) (%d %d)",
(int)before_char.pos.x, (int)before_char.pos.y,
(int)predicted_char.pos.x, (int)predicted_char.pos.y);
}
NETOBJ_CHARACTER_CORE before = {0}, now = {0};
before_char.write(&before);
predicted_char.write(&now);
if(predicted_prev_char.pos.x != before_prev_char.pos.x ||
predicted_prev_char.pos.y != before_prev_char.pos.y)
if(mem_comp(&before, &now, sizeof(NETOBJ_CHARACTER_CORE)) != 0)
{
dbg_msg("client", "prediction error, prev (%d %d) (%d %d)",
(int)before_prev_char.pos.x, (int)before_prev_char.pos.y,
(int)predicted_prev_char.pos.x, (int)predicted_prev_char.pos.y);
dbg_msg("client", "prediction error");
for(unsigned i = 0; i < sizeof(NETOBJ_CHARACTER_CORE)/sizeof(int); i++)
if(((int *)&before)[i] != ((int *)&now)[i])
{
dbg_msg("", "\t%d %d %d", i, ((int *)&before)[i], ((int *)&now)[i]);
}
}
}
@ -753,8 +768,6 @@ void GAMECLIENT::send_kill(int client_id)
client_send_msg();
}
void GAMECLIENT::con_team(void *result, void *user_data)
{
((GAMECLIENT*)user_data)->send_switch_team(console_arg_int(result, 0));

View file

@ -56,9 +56,26 @@ public:
const NETOBJ_PLAYER_INFO *player_infos[MAX_CLIENTS];
const NETOBJ_PLAYER_INFO *info_by_score[MAX_CLIENTS];
int local_cid;
int num_players;
int team_size[2];
bool spectate;
//
struct CHARACTERINFO
{
bool active;
// snapshots
NETOBJ_CHARACTER prev;
NETOBJ_CHARACTER cur;
// interpolated position
vec2 position;
};
CHARACTERINFO characters[MAX_CLIENTS];
};
SNAPSTATE snap;

View file

@ -179,18 +179,19 @@ void CHARACTER_CORE::reset()
triggered_events = 0;
}
void CHARACTER_CORE::tick()
void CHARACTER_CORE::tick(bool use_input)
{
float phys_size = 28.0f;
triggered_events = 0;
// get ground state
bool grounded = false;
if(col_check_point((int)(pos.x+phys_size/2), (int)(pos.y+phys_size/2+5)))
grounded = true;
if(col_check_point((int)(pos.x-phys_size/2), (int)(pos.y+phys_size/2+5)))
grounded = true;
vec2 direction = normalize(vec2(input.target_x, input.target_y));
vec2 target_direction = normalize(vec2(input.target_x, input.target_y));
vel.y += world->tuning.gravity;
@ -198,13 +199,72 @@ void CHARACTER_CORE::tick()
float accel = grounded ? world->tuning.ground_control_accel : world->tuning.air_control_accel;
float friction = grounded ? world->tuning.ground_friction : world->tuning.air_friction;
// handle movement
if(input.direction < 0)
// handle input
if(use_input)
{
direction = input.direction;
// setup angle
float a = 0;
if(input.target_x == 0)
a = atan((float)input.target_y);
else
a = atan((float)input.target_y/(float)input.target_x);
if(input.target_x < 0)
a = a+pi;
angle = (int)(a*256.0f);
// handle jump
if(input.jump)
{
if(!(jumped&1))
{
if(grounded)
{
triggered_events |= COREEVENT_GROUND_JUMP;
vel.y = -world->tuning.ground_jump_impulse;
jumped |= 1;
}
else if(!(jumped&2))
{
triggered_events |= COREEVENT_AIR_JUMP;
vel.y = -world->tuning.air_jump_impulse;
jumped |= 3;
}
}
}
else
jumped &= ~1;
// handle hook
if(input.hook)
{
if(hook_state == HOOK_IDLE)
{
hook_state = HOOK_FLYING;
hook_pos = pos+target_direction*phys_size*1.5f;
hook_dir = target_direction;
hooked_player = -1;
hook_tick = 0;
triggered_events |= COREEVENT_HOOK_LAUNCH;
}
}
else
{
hooked_player = -1;
hook_state = HOOK_IDLE;
hook_pos = pos;
}
}
// add the speed modification according to players wanted direction
if(direction < 0)
vel.x = saturated_add(-max_speed, max_speed, vel.x, -accel);
if(input.direction > 0)
if(direction > 0)
vel.x = saturated_add(-max_speed, max_speed, vel.x, accel);
if(input.direction == 0)
if(direction == 0)
vel.x *= friction;
// handle jumping
@ -213,64 +273,40 @@ void CHARACTER_CORE::tick()
if(grounded)
jumped &= ~2;
if(input.jump)
{
if(!(jumped&1))
{
if(grounded)
{
triggered_events |= COREEVENT_GROUND_JUMP;
vel.y = -world->tuning.ground_jump_impulse;
jumped |= 1;
}
else if(!(jumped&2))
{
triggered_events |= COREEVENT_AIR_JUMP;
vel.y = -world->tuning.air_jump_impulse;
jumped |= 3;
}
}
}
else
jumped &= ~1;
// do hook
if(input.hook)
if(hook_state == HOOK_IDLE)
{
if(hook_state == HOOK_IDLE)
hooked_player = -1;
hook_state = HOOK_IDLE;
hook_pos = pos;
}
else if(hook_state >= HOOK_RETRACT_START && hook_state < HOOK_RETRACT_END)
{
hook_state++;
}
else if(hook_state == HOOK_RETRACT_END)
{
hook_state = HOOK_RETRACTED;
triggered_events |= COREEVENT_HOOK_RETRACT;
hook_state = HOOK_RETRACTED;
}
else if(hook_state == HOOK_FLYING)
{
vec2 new_pos = hook_pos+hook_dir*world->tuning.hook_fire_speed;
if(distance(pos, new_pos) > world->tuning.hook_length)
{
hook_state = HOOK_FLYING;
hook_pos = pos+direction*phys_size*1.5f;
hook_dir = direction;
hooked_player = -1;
hook_tick = 0;
triggered_events |= COREEVENT_HOOK_LAUNCH;
hook_state = HOOK_RETRACT_START;
new_pos = pos + normalize(new_pos-pos) * world->tuning.hook_length;
}
else if(hook_state >= HOOK_RETRACT_START && hook_state < HOOK_RETRACT_END)
{
hook_state++;
}
else if(hook_state == HOOK_RETRACT_END)
{
hook_state = HOOK_RETRACTED;
triggered_events |= COREEVENT_HOOK_RETRACT;
hook_state = HOOK_RETRACTED;
}
else if(hook_state == HOOK_FLYING)
{
vec2 new_pos = hook_pos+hook_dir*world->tuning.hook_fire_speed;
if(distance(pos, new_pos) > world->tuning.hook_length)
{
hook_state = HOOK_RETRACT_START;
new_pos = pos + normalize(new_pos-pos) * world->tuning.hook_length;
}
// make sure that the hook doesn't go though the ground
bool going_to_hit_ground = false;
if(col_intersect_line(hook_pos, new_pos, &new_pos))
going_to_hit_ground = true;
// make sure that the hook doesn't go though the ground
bool going_to_hit_ground = false;
if(col_intersect_line(hook_pos, new_pos, &new_pos))
going_to_hit_ground = true;
// Check against other players first
// Check against other players first
if(world)
{
for(int i = 0; i < MAX_CLIENTS; i++)
{
CHARACTER_CORE *p = world->characters[i];
@ -286,26 +322,19 @@ void CHARACTER_CORE::tick()
break;
}
}
if(hook_state == HOOK_FLYING)
{
// check against ground
if(going_to_hit_ground)
{
triggered_events |= COREEVENT_HOOK_ATTACH_GROUND;
hook_state = HOOK_GRABBED;
}
hook_pos = new_pos;
}
}
}
else
{
//release_hooked();
hooked_player = -1;
hook_state = HOOK_IDLE;
hook_pos = pos;
if(hook_state == HOOK_FLYING)
{
// check against ground
if(going_to_hit_ground)
{
triggered_events |= COREEVENT_HOOK_ATTACH_GROUND;
hook_state = HOOK_GRABBED;
}
hook_pos = new_pos;
}
}
if(hook_state == HOOK_GRABBED)
@ -339,7 +368,7 @@ void CHARACTER_CORE::tick()
// the hook will boost it's power if the player wants to move
// in that direction. otherwise it will dampen everything abit
if((hookvel.x < 0 && input.direction < 0) || (hookvel.x > 0 && input.direction > 0))
if((hookvel.x < 0 && direction < 0) || (hookvel.x > 0 && direction > 0))
hookvel.x *= 0.95f;
else
hookvel.x *= 0.75f;
@ -362,7 +391,7 @@ void CHARACTER_CORE::tick()
}
}
if(true)
if(world)
{
for(int i = 0; i < MAX_CLIENTS; i++)
{
@ -437,17 +466,8 @@ void CHARACTER_CORE::write(NETOBJ_CHARACTER_CORE *obj_core)
obj_core->hook_dy = (int)(hook_dir.y*256.0f);
obj_core->hooked_player = hooked_player;
obj_core->jumped = jumped;
float a = 0;
if(input.target_x == 0)
a = atan((float)input.target_y);
else
a = atan((float)input.target_y/(float)input.target_x);
if(input.target_x < 0)
a = a+pi;
obj_core->angle = (int)(a*256.0f);
obj_core->direction = direction;
obj_core->angle = angle;
}
void CHARACTER_CORE::read(const NETOBJ_CHARACTER_CORE *obj_core)
@ -464,6 +484,8 @@ void CHARACTER_CORE::read(const NETOBJ_CHARACTER_CORE *obj_core)
hook_dir.y = obj_core->hook_dy/256.0f;
hooked_player = obj_core->hooked_player;
jumped = obj_core->jumped;
direction = obj_core->direction;
angle = obj_core->angle;
}
void CHARACTER_CORE::quantize()

View file

@ -137,12 +137,15 @@ public:
int hooked_player;
int jumped;
int direction;
int angle;
NETOBJ_PLAYER_INPUT input;
int triggered_events;
void reset();
void tick();
void tick(bool use_input);
void move();
void read(const NETOBJ_CHARACTER_CORE *obj_core);

View file

@ -34,7 +34,8 @@ static INPUT_COUNT count_input(int prev, int cur)
// player
CHARACTER::CHARACTER()
: ENTITY(NETOBJTYPE_CHARACTER)
{}
{
}
void CHARACTER::reset()
{
@ -59,6 +60,10 @@ bool CHARACTER::spawn(PLAYER *player, vec2 pos, int team)
core.world = &game.world.core;
core.pos = pos;
game.world.core.characters[player->client_id] = &core;
reckoning_tick = 0;
mem_zero(&sendcore, sizeof(sendcore));
mem_zero(&reckoningcore, sizeof(reckoningcore));
game.world.insert_entity(this);
alive = true;
@ -578,7 +583,7 @@ void CHARACTER::tick()
//core.pos = pos;
//core.jumped = jumped;
core.input = input;
core.tick();
core.tick(true);
// handle weapons
handle_weapons();
@ -592,6 +597,16 @@ void CHARACTER::tick()
void CHARACTER::tick_defered()
{
// advance the dummy
{
WORLD_CORE tempworld;
reckoningcore.world = &tempworld;
reckoningcore.tick(false);
reckoningcore.move();
reckoningcore.quantize();
}
//lastsentcore;
/*if(!dead)
{*/
vec2 start_pos = core.pos;
@ -642,6 +657,23 @@ void CHARACTER::tick_defered()
pos.x = input.target_x;
pos.y = input.target_y;
}
// update the sendcore if needed
{
NETOBJ_CHARACTER predicted;
NETOBJ_CHARACTER current;
mem_zero(&predicted, sizeof(predicted));
mem_zero(&current, sizeof(current));
reckoningcore.write(&predicted);
core.write(&current);
if(mem_comp(&predicted, &current, sizeof(NETOBJ_CHARACTER)) != 0)
{
reckoning_tick = server_tick();
sendcore = core;
reckoningcore = core;
}
}
}
bool CHARACTER::increase_health(int amount)
@ -784,16 +816,12 @@ void CHARACTER::snap(int snaping_client)
return;
NETOBJ_CHARACTER *character = (NETOBJ_CHARACTER *)snap_new_item(NETOBJTYPE_CHARACTER, player->client_id, sizeof(NETOBJ_CHARACTER));
core.write(character);
// this is to make sure that players that are just standing still
// isn't sent. this is because the physics keep bouncing between
// 0-128 when just standing.
// TODO: fix the physics so this isn't needed
if(snaping_client != player->client_id && abs(character->vy) < 256.0f)
character->vy = 0;
// write down the core
character->tick = reckoning_tick;
sendcore.write(character);
// set emote
if (emote_stop < server_tick())
{
emote_type = EMOTE_NORMAL;
@ -809,12 +837,7 @@ void CHARACTER::snap(int snaping_client)
character->weapon = active_weapon;
character->attacktick = attack_tick;
character->wanted_direction = input.direction;
/*
if(input.left && !input.right)
character->wanted_direction = -1;
else if(!input.left && input.right)
character->wanted_direction = 1;*/
character->direction = input.direction;
if(player->client_id == snaping_client)
{

View file

@ -79,6 +79,11 @@ public:
// the player core for the physics
CHARACTER_CORE core;
// info for dead reckoning
int reckoning_tick; // tick that we are performing dead reckoning from
CHARACTER_CORE sendcore; // core that we should send
CHARACTER_CORE reckoningcore; // the dead reckoning core
//
CHARACTER();

View file

@ -1,3 +1,4 @@
#include <new>
#include <engine/e_server_interface.h>
#include "gamecontext.hpp"
@ -5,14 +6,28 @@ GAMECONTEXT game;
GAMECONTEXT::GAMECONTEXT()
{
clear();
for(int i = 0; i < MAX_CLIENTS; i++)
players[i].init(-1);
}
GAMECONTEXT::~GAMECONTEXT()
{
}
void GAMECONTEXT::clear()
{
this->~GAMECONTEXT();
mem_zero(this, sizeof(*this));
new (this) GAMECONTEXT();
// reset all players
/*
for(int i = 0; i < MAX_CLIENTS; i++)
players[i].init(-1);
world.~GAMEWORLD();
mem_zero(&world, sizeof(world));
world.GAMEWORLD();
*/
}

View file

@ -32,6 +32,8 @@ class GAMECONTEXT
{
public:
GAMECONTEXT();
~GAMECONTEXT();
void clear();
EVENTHANDLER events;