Skip to content

Commit

Permalink
feat: proper multiplayer interpolation
Browse files Browse the repository at this point in the history
  • Loading branch information
sarahkittyy committed Mar 30, 2023
1 parent f6b7570 commit 26e4248
Show file tree
Hide file tree
Showing 7 changed files with 510 additions and 441 deletions.
48 changes: 32 additions & 16 deletions backend/multiplayer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,29 +32,45 @@ function isValidData(data: IPlayerData): boolean {
return true;
}

interface ControlVars {
xp: number;
yp: number;
xv: number;
yv: number;
sx: number;
sy: number;
climbing: boolean;
dashing: boolean;
jumping: boolean;
dash_dir: number;
climbing_facing: number;
since_wallkick_ms: number;
time_airborne_ms: number;

this_frame: number;
last_frame: number;
grounded: boolean;
facing: number;
against_ladder_left: boolean;
against_ladder_right: boolean;
can_wallkick_left: boolean;
can_wallkick_right: boolean;
on_ice: boolean;
flip_gravity: boolean;
alt_controls: boolean;
tile_above: boolean;
}

interface IPlayerState {
id?: number;
xp?: number;
yp?: number;
xv?: number;
yv?: number;
sx?: number;
sy?: number;
controls?: ControlVars;
anim?: string;
inputs?: number;
updatedAt?: number;
}

function isValidState(state: IPlayerState): boolean {
if (state.id == undefined) return false;
if (state.xp == undefined) return false;
if (state.yp == undefined) return false;
if (state.xv == undefined) return false;
if (state.yv == undefined) return false;
if (state.sx == undefined) return false;
if (state.sy == undefined) return false;
if (state.anim == undefined) return false;
if (state.inputs == undefined) return false;
if (state.controls == undefined) return false;
if (state.updatedAt == undefined) return false;
return true;
}
Expand Down Expand Up @@ -88,7 +104,7 @@ async function postAuthentication(socket: Socket) {
let room: string | undefined = undefined;
log.info(`user ${data.name} connected`);

socket.onAny((event, ...args) => {
socket.onAny((event) => {
if (event === 'state_update') return;
log.debug(`user ${data.name} ${room ?? ''} event: ${event}`);
});
Expand Down
29 changes: 4 additions & 25 deletions game/multiplayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,48 +18,27 @@ multiplayer& multiplayer::get() {
multiplayer::player_state multiplayer::player_state::empty(int id) {
return multiplayer::player_state{
.id = id,
.xp = -999,
.yp = 999,
.xv = 0,
.yv = 0,
.sx = 1,
.sy = 1,
.controls = world::control_vars::empty,
.anim = "stand",
.inputs = 0,
.grounded = true,
.updatedAt = util::get_time(),
};
}

sio::message::ptr multiplayer::player_state::to_message() const {
auto ptr = sio::object_message::create();
ptr->get_map()["xp"] = sio::double_message::create(xp);
ptr->get_map()["yp"] = sio::double_message::create(yp);
ptr->get_map()["xv"] = sio::double_message::create(xv);
ptr->get_map()["yv"] = sio::double_message::create(yv);
ptr->get_map()["sx"] = sio::double_message::create(sx);
ptr->get_map()["sy"] = sio::double_message::create(sy);
ptr->get_map()["id"] = sio::int_message::create(auth::get().id());
ptr->get_map()["controls"] = controls.to_message();
ptr->get_map()["anim"] = sio::string_message::create(anim);
ptr->get_map()["inputs"] = sio::int_message::create(inputs);
ptr->get_map()["grounded"] = sio::bool_message::create(grounded);
ptr->get_map()["updatedAt"] = sio::int_message::create(updatedAt);
ptr->get_map()["id"] = sio::int_message::create(auth::get().id());
return ptr;
}

multiplayer::player_state multiplayer::player_state::from_message(const sio::message::ptr& msg) {
auto data = msg->get_map();
multiplayer::player_state s;
s.id = data["id"]->get_int();
s.xp = data["xp"]->get_double();
s.yp = data["yp"]->get_double();
s.xv = data["xv"]->get_double();
s.yv = data["yv"]->get_double();
s.sx = data["sx"]->get_double();
s.sy = data["sy"]->get_double();
s.controls = world::control_vars::from_message(data["controls"]);
s.anim = data["anim"]->get_string();
s.inputs = data["inputs"]->get_int();
s.grounded = data["grounded"]->get_bool();
s.updatedAt = data["updatedAt"]->get_int();
return s;
}
Expand Down
10 changes: 2 additions & 8 deletions game/multiplayer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "auth.hpp"
#include "nametag.hpp"
#include "settings.hpp"
#include "world.hpp"

class player_ghost;
class player;
Expand All @@ -37,15 +38,8 @@ class multiplayer : public sf::Drawable, public sf::Transformable {

struct player_state {
int id;
float xp;
float yp;
float xv;
float yv;
float sx;
float sy;
world::control_vars controls;
std::string anim = "stand";
int inputs = 0;
bool grounded = true;
uint64_t updatedAt;
static player_state empty(int id);
sio::message::ptr to_message() const;
Expand Down
64 changes: 9 additions & 55 deletions game/player_ghost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,71 +31,25 @@ void player_ghost::update() {
if (m_p.get_animation() != m_state.anim) {
m_p.set_animation(m_state.anim);
}
if (m_p.getScale().x != m_state.sx || m_p.getScale().y != m_state.sy) {
m_p.setScale(m_state.sx, m_state.sy);
}
m_p.update();

input_state input = input_state::from_int(m_state.inputs);
auto phys = world::phys;
uint64_t ct = util::get_time();
float t = (ct - m_last_update) / 1000.f;
m_last_update = ct;
uint64_t ct = util::get_time();
sf::Time dt = sf::milliseconds(ct - m_last_update);
m_last_update = ct;

debug::get() << "t: " << t << "\n";
debug::get() << "t: " << dt.asSeconds() << "\n";

// mini physics simulation
bool lr_inputted = false;
if (input.right) {
lr_inputted = !lr_inputted;
// wallkick
if (!input.left) {
// normal acceleration
if (m_state.xv < 0) {
m_state.xv += phys.x_decel * t;
}
m_state.xv += phys.x_accel * t;
}
}
if (input.left) {
lr_inputted = !lr_inputted;
if (!input.right) {
// normal acceleration
if (m_state.xv > 0) {
m_state.xv -= phys.x_decel * t;
}
m_state.xv -= phys.x_accel * t;
}
}
if (!lr_inputted) {
if (m_state.xv > (phys.x_decel / 2.f) * t) {
m_state.xv -= phys.x_decel * t;
} else if (m_state.xv < (-phys.x_decel / 2.f) * t) {
m_state.xv += phys.x_decel * t;
} else {
m_state.xv = 0;
}
}

m_state.xv = util::clamp(m_state.xv, -phys.xv_max, phys.xv_max); // cap xv

// float gravity_sign = m_state.sy < 0 ? -1.f : 1.f;

// if (!m_state.grounded) { m_state.yv += phys.grav * t * gravity_sign; } // gravity
// if (gravity_sign < 0) { // cap yv
// m_state.yv = util::clamp(m_state.yv, -phys.yv_max, phys.jump_v);
//} else {
// m_state.yv = util::clamp(m_state.yv, -phys.jump_v, phys.yv_max);
//}
world::control_vars& v = m_state.controls;
world::run_controls(dt, v);

m_state.xp += m_state.xv * t;
// m_state.yp += m_state.yv * t;
m_p.setPosition(m_state.xp * m_p.size().x, m_state.yp * m_p.size().y);
m_p.setPosition(v.xp * m_p.size().x, v.yp * m_p.size().y);
m_nametag.setPosition(m_p.getPosition().x, m_p.getPosition().y - m_p.size().y);
m_p.setScale(v.sx, v.sy);
}

void player_ghost::draw(sf::RenderTarget& t, sf::RenderStates s) const {
if (m_state.xp == -999) return;
if (m_state.controls.xp == -999) return;
s.transform *= getTransform();
// for the extra border tile offset
s.transform.translate(1 * m_p.size().x, 0);
Expand Down
12 changes: 1 addition & 11 deletions game/states/edit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,20 +310,10 @@ void edit::update(fsm* sm, sf::Time dt) {
multiplayer::get().emit_state(multiplayer::player_state::empty(auth::get().id()));
} else {
// gameplay mode
sf::Vector2f p = m_test_play_world->get_player_pos();
sf::Vector2f v = m_test_play_world->get_player_vel();
sf::Vector2f s = m_test_play_world->get_player_scale();
multiplayer::get().emit_state(multiplayer::player_state{
.id = auth::get().id(),
.xp = p.x,
.yp = p.y,
.xv = v.x,
.yv = v.y,
.sx = s.x,
.sy = s.y,
.controls = m_test_play_world->get_player_control_vars(),
.anim = m_test_play_world->get_player_anim(),
.inputs = int(m_test_play_world->get_player_inputs()),
.grounded = m_test_play_world->get_player_grounded(),
.updatedAt = util::get_time() });
}
}
Expand Down
Loading

0 comments on commit 26e4248

Please sign in to comment.