Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiplayer HITL #1939

Merged
merged 9 commits into from
May 14, 2024
Merged

Multiplayer HITL #1939

merged 9 commits into from
May 14, 2024

Conversation

0mdc
Copy link
Contributor

@0mdc 0mdc commented May 3, 2024

Motivation and Context

This changeset introduces multiplayer HITL support.

phase2-2024-05-03_11.56.09.mp4

How It Works

Multiplayer is activated using a new max_client_count config value (up to 32).

habitat_hitl.networking.enable=True
habitat_hitl.networking.max_client_count=2

Overview

networking_overview

Server -> Client Communication

The server communicates to the clients via gfx-replay keyframes.

  • Keyframes are generated by habitat-sim (Recorder.cpp).
    • They are in json format.
    • They contain deltas of the world state (loads, instantiations, deletions, updates).
  • Messages are extra info included to keyframes by habitat-hitl.
    • They are user-specific.
    • They contain info like text, GUI shapes, camera transforms, etc.

Therefore, keyframes are global. They are consolidated and broadcasted.
Prior to sending keyframes, the user-specific messages are added to them.

Client -> Server Communication

The client communicates to the server via ClientStates and ConnectionRecords.

  • ClientStates are generated by siro_hitl_unity_client.
    • They contain info like user input.
  • ConnectionRecords are sent once upon connecting.
    • They contain session parameters like scene selection and user ID.

Connection

When a new connection occurs:

  • The new client is assigned a user_slot (pre-allocated based on max_client_count).
    • Each user_slot is assigned to a user_index, therefore a GUI-controllable agent.
  • A on_client_connected event is emitted containing the ConnectionRecord and user_index.

How Has This Been Tested

  • Tested on local mode, single user mode and multi-user mode.
  • Tested on AWS at scale.

Types of changes

  • [Refactoring]
  • [Development]

Checklist

  • My code follows the code style of this project.
  • I have updated the documentation if required.
  • I have read the CONTRIBUTING document.
  • I have completed my CLA (see CONTRIBUTING)
  • I have added tests to cover my changes if required.

@facebook-github-bot facebook-github-bot added the CLA Signed Do not delete this pull request or issue due to inactivity. label May 3, 2024
Copy link
Contributor Author

@0mdc 0mdc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments for reviewers.

for user_index, client in self._user_slots.items():
if client.connection_id == connection_id:
client_state["userIndex"] = user_index
# TODO: This can happen in some cases (immediately after disconnect?).
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't investigated why this happens. This is rare.

for user_index in self._user_slots.keys():
if self.is_okay_to_send_keyframes(user_index):
slot = self._user_slots[user_index]
tasks[user_index] = slot.socket.send(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This cannot fail. Exceptions occur when awaiting task.

raise ValueError(
"isClientReady key not found in initial client message."
)
# Hack: connection record may be missing. Try to get it from client state.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reliably occurs on AWS, but not on local network.

I haven't had the time to go to the bottom of this.

else networking_config.http_availability_server.code_available
networking_config.http_availability_server.code_available
if not network_mgr.has_connection()
else networking_config.http_availability_server.code_unavailable
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the code that determines whether a server is available. It is used by a load balancer in single-user and matchmaker in multi-user.

We only consider the server available when there is no connection.

In the multi-user case, the matchmaker dispatches every other user to the previous user's location.

# Gather all recent keyDown and keyUp events
for client_state in client_states:
for user_index in range(len(all_client_states)):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The big deltas below are just inclusion into a loop. No other change here.

Copy link
Contributor Author

@0mdc 0mdc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some more comments.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file will be refactored into per-user states and a comms manager.

This will be done in a later PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class will be refactored in a later pass.

Copy link
Contributor

@aclegg3 aclegg3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good. True test is whether it runs, so demo/video is good proof. 🙂

Docstrings are very informative, would love to see more where they are missing.

Thanks for the clarifying comments, overall this follows my expectations. Not enough deep context for meta/design comments. Looks functional and fine to me. 👍

examples/hitl/rearrange_v2/rearrange_v2.py Show resolved Hide resolved
Comment on lines +333 to +360
# Draw a frustum (forward is flipped (z+))
self._gui_drawer.draw_transformed_line(
mn.Vector3(0, 0, 0),
mn.Vector3(size, size, size),
color0,
color1,
)
self._gui_drawer.draw_transformed_line(
mn.Vector3(0, 0, 0),
mn.Vector3(-size, size, size),
color0,
color1,
destination_mask=server_only,
)
self._gui_drawer.draw_transformed_line(
mn.Vector3(0, 0, 0),
mn.Vector3(0, 0, pointer_len),
mn.Vector3(size, -size, size),
color0,
color1,
destination_mask=server_only,
)
self._gui_drawer.draw_transformed_line(
mn.Vector3(0, 0, 0),
mn.Vector3(-size, -size, size),
color0,
color1,
destination_mask=server_only,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be refactored into a for over the size vectors.

@0mdc 0mdc merged commit 5cb0630 into main May 14, 2024
3 of 4 checks passed
@0mdc 0mdc deleted the 0mdc/multiplayer branch May 14, 2024 14:41
dannymcy pushed a commit to dannymcy/habitat-lab that referenced this pull request Jun 26, 2024
* Add max_client_count config.

* Add multiplayer support to remote_client_state.

* Add multiplayer support to networking process.

* Make HitlDriver, ClientState and GuiDrawer fully networked.

* Formatting pass.

* Add missing include.

* Make rearrange_v2 multiplayer.

* Fixes for local mode.

* Add UserData docstring.
dannymcy pushed a commit to dannymcy/habitat-lab that referenced this pull request Jul 8, 2024
* Add max_client_count config.

* Add multiplayer support to remote_client_state.

* Add multiplayer support to networking process.

* Make HitlDriver, ClientState and GuiDrawer fully networked.

* Formatting pass.

* Add missing include.

* Make rearrange_v2 multiplayer.

* Fixes for local mode.

* Add UserData docstring.
dannymcy pushed a commit to dannymcy/habitat-lab that referenced this pull request Jul 8, 2024
* Add max_client_count config.

* Add multiplayer support to remote_client_state.

* Add multiplayer support to networking process.

* Make HitlDriver, ClientState and GuiDrawer fully networked.

* Formatting pass.

* Add missing include.

* Make rearrange_v2 multiplayer.

* Fixes for local mode.

* Add UserData docstring.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed Do not delete this pull request or issue due to inactivity.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants