Skip to content

Commit

Permalink
🎮 Enabled character/actor collision in multiplayer.
Browse files Browse the repository at this point in the history
This is an experimental spinoff from RigsOfRods#3049. I felt inspired.
Since the collision works by "sticking" the character to the actor while in contact, I realized I could extend our existing driver-attachment logic to also handle this attachment.��It's glitchy right now, partly because the networked cab offset is always in world coordinates, so it causes sliding when the vehicle turns.

Minor cleanup: removed the `enabled` param from `SetActorCoupling()` because it was dummy.
  • Loading branch information
ohlidalp committed May 30, 2023
1 parent 206b751 commit 0dd3e60
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 49 deletions.
4 changes: 2 additions & 2 deletions source/main/GameContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ void GameContext::ChangePlayerActor(ActorPtr actor)
Character* player_character = this->GetPlayerCharacter();
if (player_character)
{
player_character->SetActorCoupling(false, nullptr);
player_character->SetActorCoupling(nullptr);
player_character->setRotation(Ogre::Radian(rotation));
player_character->setPosition(position);
}
Expand Down Expand Up @@ -518,7 +518,7 @@ void GameContext::ChangePlayerActor(ActorPtr actor)
Character* player_character = this->GetPlayerCharacter();
if (player_character)
{
player_character->SetActorCoupling(true, m_player_actor);
player_character->SetActorCoupling(m_player_actor);
}

App::GetGuiManager()->FlexbodyDebug.AnalyzeFlexbodies();
Expand Down
121 changes: 88 additions & 33 deletions source/main/gameplay/Character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,14 @@ void Character::updateCharacterRotation()
setRotation(m_character_rotation);
}

void Character::setPosition(Vector3 position) // TODO: updates OGRE objects --> belongs to GfxScene ~ only_a_ptr, 05/2018
void Character::setPosition(Vector3 position)
{
//ASYNCSCENE OLD m_character_scenenode->setPosition(position);
m_character_position = position;
m_prev_position = position;
}

Vector3 Character::getPosition()
{
//ASYNCSCENE OLDreturn m_character_scenenode->getPosition();
return m_character_position;
}

Expand Down Expand Up @@ -184,7 +182,7 @@ void Character::update(float dt)
m_last_vehicle_position = cab_position;
m_last_vehicle_rotation = Ogre::Radian(actor->getRotation());
m_last_contacting_cab = i;
m_contacting_actor = actor;
this->SetContactingActor(actor);
}
m_contacting_cab = i;
}
Expand Down Expand Up @@ -212,7 +210,7 @@ void Character::update(float dt)
m_vehicle_position = cab_position;
m_vehicle_rotation = Ogre::Radian(m_contacting_actor->getRotation());

if (App::sim_character_collisions->getBool() && App::mp_state->getEnum<MpState>() != MpState::CONNECTED)
//if (App::sim_character_collisions->getBool())
{
position += (m_vehicle_position - m_last_vehicle_position);
this->setRotation(m_character_rotation + (m_vehicle_rotation - m_last_vehicle_rotation));
Expand All @@ -224,7 +222,7 @@ void Character::update(float dt)
}
else if (m_inertia)
{
if (App::sim_character_collisions->getBool() && App::mp_state->getEnum<MpState>() != MpState::CONNECTED)
//if (App::sim_character_collisions->getBool())
{
position += m_inertia_position;
this->setRotation(m_character_rotation + m_inertia_rotation);
Expand All @@ -233,21 +231,21 @@ void Character::update(float dt)
}
else if (m_contacting_actor != nullptr && !m_contacting_actor->ar_bounding_box.contains(position)) // we lost contact, reset contacting actor
{
m_contacting_actor = nullptr;
this->SetContactingActor(nullptr);
}
}

if (m_contacting_cab == m_last_contacting_cab)
{
m_last_vehicle_position = m_vehicle_position;
}
else if (m_contacting_actor != nullptr) // we used last_contacting_cab's position for the motion, but we'll need contacting_cab's position next frame.
{
Vector3 cab_position = CalcCabAveragePos(m_contacting_actor, m_contacting_cab);
m_last_vehicle_position = cab_position;
}
m_last_contacting_cab = m_contacting_cab;
m_last_vehicle_rotation = m_vehicle_rotation;
if (m_contacting_cab == m_last_contacting_cab)
{
m_last_vehicle_position = m_vehicle_position;
}
else if (m_contacting_actor != nullptr) // we used last_contacting_cab's position for the motion, but we'll need contacting_cab's position next frame.
{
Vector3 cab_position = CalcCabAveragePos(m_contacting_actor, m_contacting_cab);
m_last_vehicle_position = cab_position;
}
m_last_contacting_cab = m_contacting_cab;
m_last_vehicle_rotation = m_vehicle_rotation;
}

// Obstacle detection
Expand Down Expand Up @@ -461,6 +459,14 @@ void Character::update(float dt)
m_anim_time = anim_time_pos;
m_net_last_anim_time = 0.0f;
}
else if (m_is_remote && m_contacting_actor)
{
// Make sure cab index from network is valid (-1 means no update arrived yet)
if (m_contacting_cab >= 0 && m_contacting_cab < m_contacting_actor->ar_num_cabs)
{
this->setPosition(m_net_cab_offset + CalcCabAveragePos(m_contacting_actor, m_contacting_cab));
}
}

#ifdef USE_SOCKETW
if ((App::mp_state->getEnum<MpState>() == MpState::CONNECTED) && !m_is_remote)
Expand Down Expand Up @@ -534,17 +540,28 @@ void Character::SendStreamData()
if (m_net_timer.getMilliseconds() - m_net_last_update_time < 100)
return;

// do not send position data if coupled to an actor already
// do not send position data if coupled (seated) with an actor already
if (m_actor_coupling)
return;

m_net_last_update_time = m_net_timer.getMilliseconds();

NetCharacterMsgPos msg;
msg.command = CHARACTER_CMD_POSITION;
msg.pos_x = m_character_position.x;
msg.pos_y = m_character_position.y;
msg.pos_z = m_character_position.z;
if (m_contacting_actor)
{
msg.command = CHARACTER_CMD_POSITION_CAB;
msg.pos_x = m_character_position.x - m_vehicle_position.x;
msg.pos_y = m_character_position.y - m_vehicle_position.y;
msg.pos_z = m_character_position.z - m_vehicle_position.z;
msg.cab_index = m_contacting_cab;
}
else
{
msg.command = CHARACTER_CMD_POSITION_GROUND;
msg.pos_x = m_character_position.x;
msg.pos_y = m_character_position.y;
msg.pos_z = m_character_position.z;
}
msg.rot_angle = m_character_rotation.valueRadians();
strncpy(msg.anim_name, m_anim_name.c_str(), CHARACTER_ANIM_NAME_LEN);
msg.anim_time = m_anim_time - m_net_last_anim_time;
Expand All @@ -561,10 +578,18 @@ void Character::receiveStreamData(unsigned int& type, int& source, unsigned int&
if (type == RoRnet::MSG2_STREAM_DATA && m_source_id == source && m_stream_id == streamid)
{
auto* msg = reinterpret_cast<NetCharacterMsgGeneric*>(buffer);
if (msg->command == CHARACTER_CMD_POSITION)
if (msg->command == CHARACTER_CMD_POSITION_GROUND || msg->command == CHARACTER_CMD_POSITION_CAB)
{
auto* pos_msg = reinterpret_cast<NetCharacterMsgPos*>(buffer);
this->setPosition(Ogre::Vector3(pos_msg->pos_x, pos_msg->pos_y, pos_msg->pos_z));
if (msg->command == CHARACTER_CMD_POSITION_GROUND)
{
this->setPosition(Ogre::Vector3(pos_msg->pos_x, pos_msg->pos_y, pos_msg->pos_z));
}
else if (msg->command == CHARACTER_CMD_POSITION_CAB)
{
m_net_cab_offset = Ogre::Vector3(pos_msg->pos_x, pos_msg->pos_y, pos_msg->pos_z);
m_contacting_cab = pos_msg->cab_index;
}
this->setRotation(Ogre::Radian(pos_msg->rot_angle));
if (strnlen(pos_msg->anim_name, CHARACTER_ANIM_NAME_LEN) < CHARACTER_ANIM_NAME_LEN)
{
Expand All @@ -574,17 +599,22 @@ void Character::receiveStreamData(unsigned int& type, int& source, unsigned int&
else if (msg->command == CHARACTER_CMD_DETACH)
{
if (m_actor_coupling != nullptr)
this->SetActorCoupling(false, nullptr);
this->SetActorCoupling(nullptr);
else if (m_contacting_actor != nullptr)
this->SetContactingActor(nullptr);
else
this->ReportError("Received command `DETACH`, but not currently attached to a vehicle. Ignoring command.");
}
else if (msg->command == CHARACTER_CMD_ATTACH)
else if (msg->command == CHARACTER_CMD_ATTACH_SEAT || msg->command == CHARACTER_CMD_ATTACH_CAB)
{
auto* attach_msg = reinterpret_cast<NetCharacterMsgAttach*>(buffer);
ActorPtr beam = App::GetGameContext()->GetActorManager()->GetActorByNetworkLinks(attach_msg->source_id, attach_msg->stream_id);
if (beam != nullptr)
ActorPtr actor = App::GetGameContext()->GetActorManager()->GetActorByNetworkLinks(attach_msg->source_id, attach_msg->stream_id);
if (actor != nullptr)
{
this->SetActorCoupling(true, beam);
if (msg->command == CHARACTER_CMD_ATTACH_SEAT)
this->SetActorCoupling(actor);
else if (msg->command == CHARACTER_CMD_ATTACH_CAB)
this->SetContactingActor(actor);
}
else
{
Expand All @@ -605,16 +635,16 @@ void Character::receiveStreamData(unsigned int& type, int& source, unsigned int&
#endif
}

void Character::SetActorCoupling(bool enabled, ActorPtr actor)
void Character::SetActorCoupling(ActorPtr actor)
{
m_actor_coupling = actor;
#ifdef USE_SOCKETW
if (App::mp_state->getEnum<MpState>() == MpState::CONNECTED && !m_is_remote)
{
if (enabled)
if (m_actor_coupling)
{
NetCharacterMsgAttach msg;
msg.command = CHARACTER_CMD_ATTACH;
msg.command = CHARACTER_CMD_ATTACH_SEAT;
msg.source_id = m_actor_coupling->ar_net_source_id;
msg.stream_id = m_actor_coupling->ar_net_stream_id;
App::GetNetwork()->AddPacket(m_stream_id, RoRnet::MSG2_STREAM_DATA, sizeof(NetCharacterMsgAttach), (char*)&msg);
Expand All @@ -629,6 +659,31 @@ void Character::SetActorCoupling(bool enabled, ActorPtr actor)
#endif // USE_SOCKETW
}

void Character::SetContactingActor(ActorPtr actor)
{
m_contacting_actor = actor;
m_contacting_cab = -1; // Indicate we have no network updates yet.
#ifdef USE_SOCKETW
if (App::mp_state->getEnum<MpState>() == MpState::CONNECTED && !m_is_remote)
{
if (m_contacting_actor)
{
NetCharacterMsgAttach msg;
msg.command = CHARACTER_CMD_ATTACH_CAB;
msg.source_id = m_contacting_actor->ar_net_source_id;
msg.stream_id = m_contacting_actor->ar_net_stream_id;
App::GetNetwork()->AddPacket(m_stream_id, RoRnet::MSG2_STREAM_DATA, sizeof(NetCharacterMsgAttach), (char*)&msg);
}
else
{
NetCharacterMsgGeneric msg;
msg.command = CHARACTER_CMD_DETACH;
App::GetNetwork()->AddPacket(m_stream_id, RoRnet::MSG2_STREAM_DATA, sizeof(NetCharacterMsgGeneric), (char*)&msg);
}
}
#endif // USE_SOCKETW
}

// --------------------------------
// GfxCharacter

Expand Down
20 changes: 12 additions & 8 deletions source/main/gameplay/Character.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ class Character
void update(float dt);
void updateCharacterRotation();
void receiveStreamData(unsigned int& type, int& source, unsigned int& streamid, char* buffer);
void SetActorCoupling(bool enabled, ActorPtr actor);
void SetActorCoupling(ActorPtr actor); //!< Seating
void SetContactingActor(ActorPtr); //!< Standing - collision
GfxCharacter* SetupGfx();

private:
Expand All @@ -71,7 +72,6 @@ class Character
void SendStreamSetup();
void SetAnimState(std::string mode, float time = 0);

ActorPtr m_actor_coupling; //!< The vehicle or machine which the character occupies
Ogre::Radian m_character_rotation;
float m_character_h_speed;
float m_character_v_speed;
Expand All @@ -84,25 +84,29 @@ class Character
bool m_is_remote;
std::string m_anim_name;
float m_anim_time;
float m_net_last_anim_time;
float m_driving_anim_length;
float m_net_last_anim_time;
std::string m_instance_name;
Ogre::UTFString m_net_username;
Ogre::Timer m_net_timer;
unsigned long m_net_last_update_time;
GfxCharacter* m_gfx_character;

// Collision with actor:
// Occupying an actor (seating):
ActorPtr m_actor_coupling; //!< The vehicle or machine which the character occupies
float m_driving_anim_length;

// Collision with actor (standing):
Ogre::Vector3 m_vehicle_position;
Ogre::Radian m_vehicle_rotation;
Ogre::Vector3 m_last_vehicle_position;
Ogre::Radian m_last_vehicle_rotation;
bool m_inertia = false;
bool m_inertia = false;
Ogre::Vector3 m_inertia_position;
Ogre::Radian m_inertia_rotation;
ActorPtr m_contacting_actor;
int m_contacting_cab;
int m_last_contacting_cab;
int m_contacting_cab = 0;
int m_last_contacting_cab = 0;
Ogre::Vector3 m_net_cab_offset = Ogre::Vector3::ZERO;
Ogre::Vector3 CalcCabAveragePos(ActorPtr actor, int cab_index);
};

Expand Down
2 changes: 1 addition & 1 deletion source/main/gameplay/CharacterFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ void CharacterFactory::UndoRemoteActorCoupling(ActorPtr actor)
{
if (c->GetActorCoupling() == actor)
{
c->SetActorCoupling(false, nullptr);
c->SetActorCoupling(nullptr);
}
}
}
Expand Down
15 changes: 10 additions & 5 deletions source/main/network/Network.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,11 @@ namespace RoR {
enum NetCharacterCmd
{
CHARACTER_CMD_INVALID,
CHARACTER_CMD_POSITION,
CHARACTER_CMD_ATTACH,
CHARACTER_CMD_DETACH
CHARACTER_CMD_POSITION_GROUND,
CHARACTER_CMD_POSITION_CAB,
CHARACTER_CMD_ATTACH_SEAT, //!< Sets 'actor coupling' for seated (+driving) animation.
CHARACTER_CMD_ATTACH_CAB, //!< Sets 'contacting actor' for walking on cab triangles.
CHARACTER_CMD_DETACH //!< Detaches from actor
};

struct NetCharacterMsgGeneric
Expand All @@ -64,10 +66,13 @@ struct NetCharacterMsgGeneric
struct NetCharacterMsgPos
{
int32_t command;
float pos_x, pos_y, pos_z;
float rot_angle;
// Both on ground and cab:
float pos_x, pos_y, pos_z; //!< Global when on ground, local when on cab.
float rot_angle; //!< Always global.
float anim_time;
char anim_name[CHARACTER_ANIM_NAME_LEN];
// Only on cab:
int32_t cab_index;
};

struct NetCharacterMsgAttach
Expand Down

0 comments on commit 0dd3e60

Please sign in to comment.