diff options
| author | Magnus Auvinen <magnus.auvinen@gmail.com> | 2008-09-23 07:43:41 +0000 |
|---|---|---|
| committer | Magnus Auvinen <magnus.auvinen@gmail.com> | 2008-09-23 07:43:41 +0000 |
| commit | 33b50738e63a3c79861bcfd88cb39377f85776c4 (patch) | |
| tree | a7857f0b219e02337da6d8a1a6b66693760b9b6e | |
| parent | e21b6983abaefd0037435c76e9b41cfbbfbe51d5 (diff) | |
| download | zcatch-33b50738e63a3c79861bcfd88cb39377f85776c4.tar.gz zcatch-33b50738e63a3c79861bcfd88cb39377f85776c4.zip | |
added dead reckoning to the characters
| -rw-r--r-- | datasrc/network.py | 5 | ||||
| -rw-r--r-- | src/base/system.h | 4 | ||||
| -rw-r--r-- | src/engine/client/ec_client.c | 6 | ||||
| -rw-r--r-- | src/engine/e_if_client.h | 5 | ||||
| -rw-r--r-- | src/game/client/components/players.cpp | 44 | ||||
| -rw-r--r-- | src/game/client/gameclient.cpp | 183 | ||||
| -rw-r--r-- | src/game/client/gameclient.hpp | 17 | ||||
| -rw-r--r-- | src/game/gamecore.cpp | 200 | ||||
| -rw-r--r-- | src/game/gamecore.hpp | 5 | ||||
| -rw-r--r-- | src/game/server/entities/character.cpp | 59 | ||||
| -rw-r--r-- | src/game/server/entities/character.hpp | 5 | ||||
| -rw-r--r-- | src/game/server/gamecontext.cpp | 17 | ||||
| -rw-r--r-- | src/game/server/gamecontext.hpp | 2 |
13 files changed, 332 insertions, 220 deletions
diff --git a/datasrc/network.py b/datasrc/network.py index bb3545af..0f1d3496 100644 --- a/datasrc/network.py +++ b/datasrc/network.py @@ -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"), diff --git a/src/base/system.h b/src/base/system.h index 772ebf91..49ab26d6 100644 --- a/src/base/system.h +++ b/src/base/system.h @@ -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); diff --git a/src/engine/client/ec_client.c b/src/engine/client/ec_client.c index af7cd9d5..a1f898cb 100644 --- a/src/engine/client/ec_client.c +++ b/src/engine/client/ec_client.c @@ -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]) { diff --git a/src/engine/e_if_client.h b/src/engine/e_if_client.h index 91ea09b2..8badb4c6 100644 --- a/src/engine/e_if_client.h +++ b/src/engine/e_if_client.h @@ -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 diff --git a/src/game/client/components/players.cpp b/src/game/client/components/players.cpp index 4e315f32..43ff3e77 100644 --- a/src/game/client/components/players.cpp +++ b/src/game/client/components/players.cpp @@ -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 = 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); + 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 && prev_info && info) - { - render_player( - (const NETOBJ_CHARACTER *)prev, - (const NETOBJ_CHARACTER *)data, - (const NETOBJ_PLAYER_INFO *)prev_info, - (const NETOBJ_PLAYER_INFO *)info - ); - } - } + if(prev_info && info) + { + NETOBJ_CHARACTER prev_char = gameclient.snap.characters[i].prev; + NETOBJ_CHARACTER cur_char = gameclient.snap.characters[i].cur; + + render_player( + &prev_char, + &cur_char, + (const NETOBJ_PLAYER_INFO *)prev_info, + (const NETOBJ_PLAYER_INFO *)info + ); + } } } diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index fe9b0476..43627669 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -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)); diff --git a/src/game/client/gameclient.hpp b/src/game/client/gameclient.hpp index dc3532a7..4b397e86 100644 --- a/src/game/client/gameclient.hpp +++ b/src/game/client/gameclient.hpp @@ -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; diff --git a/src/game/gamecore.cpp b/src/game/gamecore.cpp index 7c8dabe0..cc0f3748 100644 --- a/src/game/gamecore.cpp +++ b/src/game/gamecore.cpp @@ -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) + // do hook + if(hook_state == HOOK_IDLE) { - 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; - } - } + hooked_player = -1; + hook_state = HOOK_IDLE; + hook_pos = pos; } - else - jumped &= ~1; - - // do hook - if(input.hook) + else if(hook_state >= HOOK_RETRACT_START && hook_state < HOOK_RETRACT_END) { - if(hook_state == HOOK_IDLE) - { - 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; - } - else if(hook_state >= HOOK_RETRACT_START && hook_state < HOOK_RETRACT_END) - { - hook_state++; - } - else if(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_RETRACTED; - triggered_events |= COREEVENT_HOOK_RETRACT; - hook_state = HOOK_RETRACTED; + hook_state = HOOK_RETRACT_START; + new_pos = pos + normalize(new_pos-pos) * world->tuning.hook_length; } - 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,27 +322,20 @@ void CHARACTER_CORE::tick() break; } } - - if(hook_state == HOOK_FLYING) + } + + if(hook_state == HOOK_FLYING) + { + // check against ground + if(going_to_hit_ground) { - // check against ground - if(going_to_hit_ground) - { - triggered_events |= COREEVENT_HOOK_ATTACH_GROUND; - hook_state = HOOK_GRABBED; - } - - hook_pos = new_pos; + 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_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() diff --git a/src/game/gamecore.hpp b/src/game/gamecore.hpp index 1f9dda81..08634909 100644 --- a/src/game/gamecore.hpp +++ b/src/game/gamecore.hpp @@ -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); diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 463b6640..362eecf0 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -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(¤t, sizeof(current)); + reckoningcore.write(&predicted); + core.write(¤t); + + if(mem_comp(&predicted, ¤t, 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) { diff --git a/src/game/server/entities/character.hpp b/src/game/server/entities/character.hpp index 2536bb79..e9909c96 100644 --- a/src/game/server/entities/character.hpp +++ b/src/game/server/entities/character.hpp @@ -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(); diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index dfea7e91..0f81167d 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -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(); + */ } diff --git a/src/game/server/gamecontext.hpp b/src/game/server/gamecontext.hpp index 85414183..4ef495fd 100644 --- a/src/game/server/gamecontext.hpp +++ b/src/game/server/gamecontext.hpp @@ -32,6 +32,8 @@ class GAMECONTEXT { public: GAMECONTEXT(); + ~GAMECONTEXT(); + void clear(); EVENTHANDLER events; |