├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── dependencies.yml │ └── main.yml ├── .gitignore ├── .gitpod.yml ├── .idea ├── .gitignore ├── modules.xml ├── naia.iml ├── runConfigurations │ ├── Basic_Demo_Client__mquad___Linux___RUN_.xml │ ├── Basic_Demo_Client__mquad___Wasm___DEPLOY_.xml │ ├── Basic_Demo_Client__mquad___wasm___build_.xml │ ├── Basic_Demo_Client__wbindgen___Linux___RUN_.xml │ ├── Basic_Demo_Client__wbindgen___Wasm___DEPLOY_.xml │ ├── Basic_Demo_Client__wbindgen___wasm___build_.xml │ ├── Basic_Demo_Server__RUN_.xml │ ├── Bevy_Demo_Client__Linux___RUN_.xml │ ├── Bevy_Demo_Client__Wasm___DEPLOY_.xml │ ├── Bevy_Demo_Server__RUN_.xml │ ├── Hecs_Demo_Client__Linux___RUN_.xml │ ├── Hecs_Demo_Client__Wasm___DEPLOY_.xml │ ├── Hecs_Demo_Server__RUN_.xml │ ├── Macroquad_Demo_Client__Linux___RUN_.xml │ ├── Macroquad_Demo_Client__Wasm___DEPLOY_.xml │ ├── Macroquad_Demo_Server__RUN_.xml │ ├── Socket_Demo_Client__mquad___Linux___RUN_.xml │ ├── Socket_Demo_Client__mquad___Wasm___BUILD_.xml │ ├── Socket_Demo_Client__mquad___Wasm___DEPLOY_.xml │ ├── Socket_Demo_Client__wbindgen___Linux___RUN_.xml │ ├── Socket_Demo_Client__wbindgen___Wasm___BUILD_.xml │ ├── Socket_Demo_Client__wbindgen___Wasm___DEPLOY_.xml │ ├── Socket_Demo_Server__RUN_.xml │ ├── Test_Serde.xml │ └── Test_Shared.xml └── vcs.xml ├── Cargo.toml ├── FEATURES.md ├── LICENSE-APACHE.txt ├── LICENSE-MIT.txt ├── README.md ├── adapters ├── bevy │ ├── client │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── client.rs │ │ │ ├── commands.rs │ │ │ ├── component_events.rs │ │ │ ├── components.rs │ │ │ ├── events.rs │ │ │ ├── lib.rs │ │ │ ├── plugin.rs │ │ │ └── systems.rs │ ├── server │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── commands.rs │ │ │ ├── components.rs │ │ │ ├── events.rs │ │ │ ├── lib.rs │ │ │ ├── plugin.rs │ │ │ ├── server.rs │ │ │ └── systems.rs │ └── shared │ │ ├── Cargo.toml │ │ └── src │ │ ├── change_detection.rs │ │ ├── component_access.rs │ │ ├── component_ref.rs │ │ ├── components.rs │ │ ├── lib.rs │ │ ├── plugin.rs │ │ ├── protocol.rs │ │ ├── protocol_plugin.rs │ │ ├── system_set.rs │ │ ├── world_data.rs │ │ └── world_proxy.rs └── hecs │ ├── client │ ├── Cargo.toml │ └── src │ │ └── lib.rs │ ├── server │ ├── Cargo.toml │ └── src │ │ └── lib.rs │ └── shared │ ├── Cargo.toml │ └── src │ ├── component_access.rs │ ├── component_ref.rs │ ├── lib.rs │ ├── protocol.rs │ ├── world_data.rs │ ├── world_proxy.rs │ └── world_wrapper.rs ├── client ├── Cargo.toml └── src │ ├── client.rs │ ├── client_config.rs │ ├── command_history.rs │ ├── connection │ ├── base_time_manager.rs │ ├── channel_tick_buffer_sender.rs │ ├── connection.rs │ ├── io.rs │ ├── mod.rs │ ├── tick_buffer_sender.rs │ ├── tick_queue.rs │ └── time_manager.rs │ ├── error.rs │ ├── events.rs │ ├── handshake │ ├── advanced_handshaker.rs │ ├── handshake_time_manager.rs │ ├── mod.rs │ └── simple_handshaker.rs │ ├── lib.rs │ ├── request.rs │ ├── transport │ ├── mod.rs │ ├── server_addr.rs │ ├── udp │ │ ├── addr_cell.rs │ │ ├── auth.rs │ │ ├── conditioner.rs │ │ ├── data.rs │ │ └── mod.rs │ └── webrtc.rs │ └── world │ ├── entity_mut.rs │ ├── entity_owner.rs │ ├── entity_ref.rs │ ├── global_entity_record.rs │ ├── global_world_manager.rs │ ├── mod.rs │ ├── mut_channel.rs │ └── replication_config.rs ├── demos ├── basic │ ├── client │ │ ├── app │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ │ ├── app.rs │ │ │ │ └── lib.rs │ │ ├── miniquad │ │ │ ├── Cargo.toml │ │ │ ├── Makefile.toml │ │ │ ├── index.html │ │ │ └── src │ │ │ │ └── main.rs │ │ └── wasm_bindgen │ │ │ ├── Cargo.toml │ │ │ ├── Makefile.toml │ │ │ ├── index.html │ │ │ └── src │ │ │ ├── app_loop.rs │ │ │ ├── lib.rs │ │ │ └── main.rs │ ├── server │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── app.rs │ │ │ └── main.rs │ └── shared │ │ ├── Cargo.toml │ │ └── src │ │ ├── lib.rs │ │ └── protocol │ │ ├── auth.rs │ │ ├── basic_request.rs │ │ ├── character.rs │ │ ├── mod.rs │ │ └── string_message.rs ├── bevy │ ├── client │ │ ├── Cargo.toml │ │ ├── Makefile.toml │ │ ├── index.html │ │ └── src │ │ │ ├── app.rs │ │ │ ├── components │ │ │ ├── interp.rs │ │ │ ├── markers.rs │ │ │ └── mod.rs │ │ │ ├── lib.rs │ │ │ ├── main.rs │ │ │ ├── resources.rs │ │ │ └── systems │ │ │ ├── events.rs │ │ │ ├── init.rs │ │ │ ├── input.rs │ │ │ ├── mod.rs │ │ │ └── sync.rs │ ├── server │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── main.rs │ │ │ ├── resources.rs │ │ │ └── systems │ │ │ ├── events.rs │ │ │ ├── init.rs │ │ │ └── mod.rs │ └── shared │ │ ├── Cargo.toml │ │ └── src │ │ ├── behavior │ │ ├── mod.rs │ │ └── process_command.rs │ │ ├── channels.rs │ │ ├── components │ │ ├── color.rs │ │ ├── mod.rs │ │ ├── position.rs │ │ └── shape.rs │ │ ├── lib.rs │ │ ├── messages │ │ ├── auth.rs │ │ ├── basic_request.rs │ │ ├── entity_assignment.rs │ │ ├── key_command.rs │ │ └── mod.rs │ │ └── protocol.rs ├── demo_utils │ ├── demo_world │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── component_ref.rs │ │ │ ├── entity.rs │ │ │ ├── lib.rs │ │ │ └── world.rs │ └── empty_world │ │ ├── Cargo.toml │ │ └── src │ │ └── lib.rs ├── hecs │ ├── client │ │ ├── Cargo.toml │ │ ├── Makefile.toml │ │ ├── index.html │ │ └── src │ │ │ ├── app.rs │ │ │ ├── app_loop.rs │ │ │ ├── lib.rs │ │ │ ├── main.rs │ │ │ └── systems │ │ │ ├── events.rs │ │ │ ├── mod.rs │ │ │ └── startup.rs │ ├── server │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── app.rs │ │ │ ├── main.rs │ │ │ └── systems │ │ │ ├── events.rs │ │ │ ├── mod.rs │ │ │ ├── startup.rs │ │ │ └── tick.rs │ └── shared │ │ ├── Cargo.toml │ │ └── src │ │ ├── lib.rs │ │ ├── protocol.rs │ │ └── protocol │ │ ├── auth.rs │ │ ├── marker.rs │ │ ├── name.rs │ │ └── position.rs ├── macroquad │ ├── client │ │ ├── Cargo.toml │ │ ├── Makefile.toml │ │ ├── index.html │ │ └── src │ │ │ ├── app.rs │ │ │ ├── interp.rs │ │ │ └── main.rs │ ├── server │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── app.rs │ │ │ └── main.rs │ └── shared │ │ ├── Cargo.toml │ │ └── src │ │ ├── behavior │ │ ├── mod.rs │ │ └── process_command.rs │ │ ├── channels.rs │ │ ├── components │ │ ├── color.rs │ │ ├── mod.rs │ │ ├── position.rs │ │ └── shape.rs │ │ ├── lib.rs │ │ ├── messages │ │ ├── auth.rs │ │ ├── entity_assignment.rs │ │ ├── key_command.rs │ │ └── mod.rs │ │ └── protocol.rs └── socket │ ├── client │ ├── app │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── app.rs │ │ │ └── lib.rs │ ├── miniquad │ │ ├── Cargo.toml │ │ ├── Makefile.toml │ │ ├── index.html │ │ └── src │ │ │ └── main.rs │ └── wasm_bindgen │ │ ├── Cargo.toml │ │ ├── Makefile.toml │ │ ├── index.html │ │ └── src │ │ ├── app_loop.rs │ │ ├── lib.rs │ │ └── main.rs │ ├── server │ ├── Cargo.toml │ └── src │ │ ├── app.rs │ │ └── main.rs │ └── shared │ ├── Cargo.toml │ └── src │ ├── lib.rs │ └── shared.rs ├── deny.toml ├── faq └── README.md ├── rustfmt.yml ├── server ├── Cargo.toml └── src │ ├── connection │ ├── bandwidth_monitor.rs │ ├── connection.rs │ ├── io.rs │ ├── mod.rs │ ├── ping_config.rs │ ├── ping_manager.rs │ ├── tick_buffer_messages.rs │ ├── tick_buffer_receiver.rs │ └── tick_buffer_receiver_channel.rs │ ├── error.rs │ ├── events.rs │ ├── handshake │ ├── advanced_handshaker.rs │ ├── cache_map.rs │ ├── mod.rs │ └── simple_handshaker.rs │ ├── lib.rs │ ├── request.rs │ ├── room.rs │ ├── server.rs │ ├── server_config.rs │ ├── time_manager.rs │ ├── transport │ ├── conditioner.rs │ ├── mod.rs │ ├── udp.rs │ └── webrtc.rs │ ├── user.rs │ ├── user_scope.rs │ └── world │ ├── entity_mut.rs │ ├── entity_owner.rs │ ├── entity_ref.rs │ ├── entity_room_map.rs │ ├── entity_scope_map.rs │ ├── global_entity_record.rs │ ├── global_world_manager.rs │ ├── mod.rs │ ├── mut_channel.rs │ ├── replication_config.rs │ └── server_auth_handler.rs ├── shared ├── Cargo.toml ├── derive │ ├── Cargo.toml │ └── src │ │ ├── channel.rs │ │ ├── lib.rs │ │ ├── message.rs │ │ ├── replicate.rs │ │ └── shared.rs ├── serde │ ├── Cargo.toml │ ├── TODO.txt │ ├── derive │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── impls.rs │ │ │ ├── impls │ │ │ ├── enumeration.rs │ │ │ ├── structure.rs │ │ │ └── tuple_structure.rs │ │ │ └── lib.rs │ └── src │ │ ├── bit_counter.rs │ │ ├── bit_reader.rs │ │ ├── bit_writer.rs │ │ ├── constants.rs │ │ ├── error.rs │ │ ├── file_bit_writer.rs │ │ ├── impls.rs │ │ ├── impls │ │ ├── array.rs │ │ ├── boxed.rs │ │ ├── hash.rs │ │ ├── option.rs │ │ ├── phantom.rs │ │ ├── scalars.rs │ │ ├── string.rs │ │ ├── timestamp.rs │ │ ├── tuple.rs │ │ └── vector.rs │ │ ├── integer.rs │ │ ├── lib.rs │ │ ├── outgoing_packet.rs │ │ └── serde.rs ├── src │ ├── backends │ │ ├── miniquad │ │ │ ├── mod.rs │ │ │ ├── timer.rs │ │ │ └── timestamp.rs │ │ ├── mod.rs │ │ ├── native │ │ │ ├── mod.rs │ │ │ ├── timer.rs │ │ │ └── timestamp.rs │ │ └── wasm_bindgen │ │ │ ├── mod.rs │ │ │ ├── timer.rs │ │ │ └── timestamp.rs │ ├── bigmap.rs │ ├── connection │ │ ├── ack_manager.rs │ │ ├── bandwidth_monitor.rs │ │ ├── base_connection.rs │ │ ├── compression_config.rs │ │ ├── connection_config.rs │ │ ├── decoder.rs │ │ ├── encoder.rs │ │ ├── mod.rs │ │ ├── packet_notifiable.rs │ │ ├── packet_type.rs │ │ ├── ping_store.rs │ │ ├── sequence_buffer.rs │ │ └── standard_header.rs │ ├── constants.rs │ ├── game_time.rs │ ├── handshake │ │ ├── advanced │ │ │ ├── header.rs │ │ │ └── mod.rs │ │ ├── mod.rs │ │ └── simple │ │ │ ├── header.rs │ │ │ └── mod.rs │ ├── key_generator.rs │ ├── lib.rs │ ├── messages │ │ ├── channels │ │ │ ├── channel.rs │ │ │ ├── channel_kinds.rs │ │ │ ├── default_channels.rs │ │ │ ├── mod.rs │ │ │ ├── receivers │ │ │ │ ├── channel_receiver.rs │ │ │ │ ├── fragment_receiver.rs │ │ │ │ ├── indexed_message_reader.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── ordered_reliable_receiver.rs │ │ │ │ ├── reliable_message_receiver.rs │ │ │ │ ├── reliable_receiver.rs │ │ │ │ ├── sequenced_reliable_receiver.rs │ │ │ │ ├── sequenced_unreliable_receiver.rs │ │ │ │ ├── unordered_reliable_receiver.rs │ │ │ │ └── unordered_unreliable_receiver.rs │ │ │ ├── senders │ │ │ │ ├── channel_sender.rs │ │ │ │ ├── indexed_message_writer.rs │ │ │ │ ├── message_fragmenter.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── reliable_message_sender.rs │ │ │ │ ├── reliable_sender.rs │ │ │ │ ├── request_sender.rs │ │ │ │ ├── sequenced_unreliable_sender.rs │ │ │ │ └── unordered_unreliable_sender.rs │ │ │ └── system_channel.rs │ │ ├── fragment.rs │ │ ├── message.rs │ │ ├── message_container.rs │ │ ├── message_kinds.rs │ │ ├── message_manager.rs │ │ ├── mod.rs │ │ ├── named.rs │ │ ├── request.rs │ │ └── tests │ │ │ ├── fragment.rs │ │ │ └── mod.rs │ ├── protocol.rs │ ├── sequence_list.rs │ ├── transport_udp.rs │ ├── types.rs │ ├── world │ │ ├── component │ │ │ ├── component_kinds.rs │ │ │ ├── component_update.rs │ │ │ ├── diff_mask.rs │ │ │ ├── entity_property.rs │ │ │ ├── mod.rs │ │ │ ├── property.rs │ │ │ ├── property_mutate.rs │ │ │ ├── replica_ref.rs │ │ │ └── replicate.rs │ │ ├── delegation │ │ │ ├── auth_channel.rs │ │ │ ├── entity_auth_status.rs │ │ │ ├── host_auth_handler.rs │ │ │ └── mod.rs │ │ ├── entity │ │ │ ├── entity_action.rs │ │ │ ├── entity_action_receiver.rs │ │ │ ├── entity_action_type.rs │ │ │ ├── entity_auth_event.rs │ │ │ ├── entity_converters.rs │ │ │ ├── error.rs │ │ │ ├── global_entity.rs │ │ │ ├── local_entity.rs │ │ │ └── mod.rs │ │ ├── host │ │ │ ├── entity_action_event.rs │ │ │ ├── entity_channel.rs │ │ │ ├── global_diff_handler.rs │ │ │ ├── host_world_manager.rs │ │ │ ├── host_world_writer.rs │ │ │ ├── mod.rs │ │ │ ├── mut_channel.rs │ │ │ ├── user_diff_handler.rs │ │ │ └── world_channel.rs │ │ ├── local_entity_map.rs │ │ ├── local_world_manager.rs │ │ ├── mod.rs │ │ ├── remote │ │ │ ├── entity_action_event.rs │ │ │ ├── entity_event.rs │ │ │ ├── entity_waitlist.rs │ │ │ ├── mod.rs │ │ │ ├── remote_world_manager.rs │ │ │ └── remote_world_reader.rs │ │ ├── shared_global_world_manager.rs │ │ └── world_type.rs │ └── wrapping_number.rs └── tests │ ├── derive_enum.rs │ ├── derive_replicate.rs │ ├── derive_struct.rs │ ├── derive_tuple_struct.rs │ └── derive_unit_struct.rs ├── socket ├── client │ ├── Cargo.toml │ └── src │ │ ├── backends │ │ ├── miniquad │ │ │ ├── identity_receiver.rs │ │ │ ├── mod.rs │ │ │ ├── naia_socket.js │ │ │ ├── packet_receiver.rs │ │ │ ├── packet_sender.rs │ │ │ ├── shared.rs │ │ │ └── socket.rs │ │ ├── mod.rs │ │ ├── native │ │ │ ├── identity_receiver.rs │ │ │ ├── mod.rs │ │ │ ├── packet_receiver.rs │ │ │ ├── packet_sender.rs │ │ │ ├── runtime.rs │ │ │ └── socket.rs │ │ ├── socket.rs │ │ └── wasm_bindgen │ │ │ ├── addr_cell.rs │ │ │ ├── data_channel.rs │ │ │ ├── data_port.rs │ │ │ ├── identity_receiver.rs │ │ │ ├── mod.rs │ │ │ ├── packet_receiver.rs │ │ │ ├── packet_sender.rs │ │ │ └── socket.rs │ │ ├── conditioned_packet_receiver.rs │ │ ├── error.rs │ │ ├── identity_receiver.rs │ │ ├── lib.rs │ │ ├── packet_receiver.rs │ │ ├── packet_sender.rs │ │ ├── server_addr.rs │ │ └── wasm_utils.rs ├── server │ ├── Cargo.toml │ └── src │ │ ├── async_socket.rs │ │ ├── auth_receiver.rs │ │ ├── auth_sender.rs │ │ ├── conditioned_packet_receiver.rs │ │ ├── error.rs │ │ ├── executor.rs │ │ ├── lib.rs │ │ ├── packet_receiver.rs │ │ ├── packet_sender.rs │ │ ├── server_addrs.rs │ │ ├── session.rs │ │ └── socket.rs └── shared │ ├── Cargo.toml │ └── src │ ├── backends │ ├── miniquad │ │ ├── instant.rs │ │ ├── mod.rs │ │ └── random.rs │ ├── mod.rs │ ├── native │ │ ├── instant.rs │ │ ├── mod.rs │ │ └── random.rs │ └── wasm_bindgen │ │ ├── instant.rs │ │ ├── mod.rs │ │ └── random.rs │ ├── identity_token.rs │ ├── lib.rs │ ├── link_condition_logic.rs │ ├── link_conditioner_config.rs │ ├── socket_config.rs │ ├── time_queue.rs │ └── url_parse.rs └── test ├── Cargo.toml ├── src ├── auth.rs └── lib.rs └── tests └── handshake.rs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: connorcarpenter -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | labels: 8 | - 'dependencies' 9 | - package-ecosystem: github-actions 10 | directory: / 11 | schedule: 12 | interval: weekly 13 | labels: 14 | - 'github actions' -------------------------------------------------------------------------------- /.github/workflows/dependencies.yml: -------------------------------------------------------------------------------- 1 | name: Dependencies 2 | 3 | concurrency: 4 | group: build-${{ github.event.pull_request.number || github.ref }} 5 | cancel-in-progress: true 6 | 7 | on: 8 | pull_request: 9 | paths: 10 | - 'Cargo.toml' 11 | - 'deny.toml' 12 | schedule: 13 | - cron: '0 0 * * 0' 14 | env: 15 | 16 | CARGO_TERM_COLOR: always 17 | 18 | jobs: 19 | dependencies: 20 | name: Check dependencies 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Clone repo 24 | uses: actions/checkout@v3 25 | 26 | - name: Check dependencies 27 | uses: EmbarkStudios/cargo-deny-action@v1 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | /Cargo.lock 4 | node_modules 5 | /demos/basic/client/miniquad/target 6 | /demos/basic/client/miniquad/target/ 7 | /demos/basic/client/miniquad/target/* 8 | /demos/basic/client/wasm_bindgen/target 9 | /demos/basic/client/wasm_bindgen/target/ 10 | /demos/basic/client/wasm_bindgen/target/* 11 | /demos/bevy/client/target 12 | /demos/bevy/client/target/ 13 | /demos/bevy/client/target/* 14 | /demos/hecs/client/target 15 | /demos/hecs/client/target/ 16 | /demos/hecs/client/target/* 17 | /demos/macroquad/client/target 18 | /demos/macroquad/client/target/ 19 | /demos/macroquad/client/target/* 20 | /demos/socket/client/miniquad/target 21 | /demos/socket/client/miniquad/target/ 22 | /demos/socket/client/miniquad/target/* 23 | /demos/socket/client/wasm_bindgen/target 24 | /demos/socket/client/wasm_bindgen/target/ 25 | /demos/socket/client/wasm_bindgen/target/* -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | # This configuration file was automatically generated by Gitpod. 2 | # Please adjust to your needs (see https://www.gitpod.io/docs/config-gitpod-file) 3 | # and commit this file to your remote git repository to share the goodness with others. 4 | 5 | tasks: 6 | - init: cargo build 7 | command: cargo watch -x run 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | # GitHub Copilot persisted chat sessions 10 | /copilot/chatSessions 11 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Basic_Demo_Client__mquad___Linux___RUN_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Basic_Demo_Client__mquad___Wasm___DEPLOY_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Basic_Demo_Client__mquad___wasm___build_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Basic_Demo_Client__wbindgen___Linux___RUN_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Basic_Demo_Client__wbindgen___Wasm___DEPLOY_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Basic_Demo_Client__wbindgen___wasm___build_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Basic_Demo_Server__RUN_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Bevy_Demo_Client__Linux___RUN_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Bevy_Demo_Client__Wasm___DEPLOY_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Bevy_Demo_Server__RUN_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Hecs_Demo_Client__Linux___RUN_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Hecs_Demo_Client__Wasm___DEPLOY_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Hecs_Demo_Server__RUN_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Macroquad_Demo_Client__Linux___RUN_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Macroquad_Demo_Client__Wasm___DEPLOY_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Macroquad_Demo_Server__RUN_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Socket_Demo_Client__mquad___Linux___RUN_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Socket_Demo_Client__mquad___Wasm___BUILD_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Socket_Demo_Client__mquad___Wasm___DEPLOY_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Socket_Demo_Client__wbindgen___Linux___RUN_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Socket_Demo_Client__wbindgen___Wasm___BUILD_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Socket_Demo_Client__wbindgen___Wasm___DEPLOY_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Socket_Demo_Server__RUN_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Test_Serde.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Test_Shared.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE-MIT.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Connor Carpenter 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /adapters/bevy/client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-bevy-client" 3 | version = "0.24.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../.." 6 | description = "Library to faciliate naia_client & Bevy interop" 7 | homepage = "https://github.com/naia-lib/naia" 8 | repository = "https://github.com/naia-lib/naia" 9 | license = "MIT OR Apache-2.0" 10 | edition = "2021" 11 | 12 | [badges] 13 | maintenance = { status = "actively-developed" } 14 | 15 | [features] 16 | transport_webrtc = [ "naia-client/transport_webrtc" ] 17 | transport_udp = [ "naia-client/transport_udp" ] 18 | 19 | [dependencies] 20 | naia-client = { version = "0.24", path = "../../../client", features = ["bevy_support", "wbindgen"] } 21 | naia-bevy-shared = { version = "0.24", path = "../shared" } 22 | bevy_app = { version = "0.15", default-features=false } 23 | bevy_ecs = { version = "0.15", default-features=false } 24 | log = { version = "0.4" } -------------------------------------------------------------------------------- /adapters/bevy/client/src/components.rs: -------------------------------------------------------------------------------- 1 | use bevy_ecs::component::Component; 2 | 3 | use naia_bevy_shared::HostOwned; 4 | 5 | pub type ClientOwned = HostOwned; 6 | 7 | #[derive(Component)] 8 | pub struct ServerOwned; 9 | -------------------------------------------------------------------------------- /adapters/bevy/client/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use naia_bevy_shared::{ 2 | sequence_greater_than, sequence_less_than, wrapping_diff, EntityAuthStatus, GameInstant, 3 | Random, ReceiveEvents, Replicate, ResponseSendKey, Tick, Timer, 4 | }; 5 | pub use naia_client::{ 6 | shared::{default_channels, Instant, Message, ResponseReceiveKey}, 7 | transport, ClientConfig, CommandHistory, NaiaClientError, ReplicationConfig, 8 | }; 9 | 10 | pub mod events; 11 | 12 | mod client; 13 | mod commands; 14 | pub mod component_events; 15 | mod components; 16 | mod plugin; 17 | mod systems; 18 | 19 | pub use client::Client; 20 | pub use commands::CommandsExt; 21 | pub use components::{ClientOwned, ServerOwned}; 22 | pub use plugin::Plugin; 23 | -------------------------------------------------------------------------------- /adapters/bevy/server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-bevy-server" 3 | version = "0.24.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../.." 6 | description = "Library to faciliate naia_server & Bevy interop" 7 | homepage = "https://github.com/naia-lib/naia" 8 | repository = "https://github.com/naia-lib/naia" 9 | license = "MIT OR Apache-2.0" 10 | edition = "2021" 11 | 12 | [badges] 13 | maintenance = { status = "actively-developed" } 14 | 15 | [features] 16 | transport_webrtc = [ "naia-server/transport_webrtc" ] 17 | transport_udp = [ "naia-server/transport_udp" ] 18 | 19 | [dependencies] 20 | naia-server = { version = "0.24", path = "../../../server", features = ["bevy_support"] } 21 | naia-bevy-shared = { version = "0.24", path = "../shared" } 22 | bevy_app = { version = "0.15", default-features=false } 23 | bevy_ecs = { version = "0.15", default-features=false } 24 | log = { version = "0.4" } -------------------------------------------------------------------------------- /adapters/bevy/server/src/components.rs: -------------------------------------------------------------------------------- 1 | use bevy_ecs::component::Component; 2 | 3 | use naia_bevy_shared::HostOwned; 4 | use naia_server::UserKey; 5 | 6 | pub type ServerOwned = HostOwned; 7 | 8 | #[derive(Component)] 9 | pub struct ClientOwned(pub UserKey); 10 | -------------------------------------------------------------------------------- /adapters/bevy/server/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use naia_bevy_shared::{EntityAuthStatus, Random, ReceiveEvents, Replicate, Tick}; 2 | pub use naia_server::{ 3 | shared::{ 4 | default_channels, BigMap, BigMapKey, BitReader, BitWrite, BitWriter, ConstBitLength, 5 | FileBitWriter, ResponseReceiveKey, SerdeErr, SignedInteger, SignedVariableInteger, 6 | UnsignedInteger, UnsignedVariableInteger, 7 | }, 8 | transport, ReplicationConfig, RoomKey, SerdeBevy as Serde, ServerConfig, UserKey, 9 | }; 10 | 11 | pub mod events; 12 | 13 | mod commands; 14 | mod components; 15 | mod plugin; 16 | mod server; 17 | mod systems; 18 | 19 | pub use commands::CommandsExt; 20 | pub use components::{ClientOwned, ServerOwned}; 21 | pub use plugin::Plugin; 22 | pub use server::Server; 23 | -------------------------------------------------------------------------------- /adapters/bevy/shared/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-bevy-shared" 3 | version = "0.24.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../.." 6 | description = "Library to faciliate naia & Bevy interop, functionality shared by client & server versions" 7 | homepage = "https://github.com/naia-lib/naia" 8 | repository = "https://github.com/naia-lib/naia" 9 | license = "MIT OR Apache-2.0" 10 | edition = "2021" 11 | 12 | [badges] 13 | maintenance = { status = "actively-developed" } 14 | 15 | [features] 16 | # this should be used when the underlying transport does not handle it for you (i.e. UDP) 17 | advanced_handshake = [ "naia-shared/advanced_handshake" ] 18 | 19 | [dependencies] 20 | naia-shared = { version = "0.24", path = "../../../shared", features = ["bevy_support", "wbindgen"] } 21 | bevy_app = { version = "0.15", default-features=false } 22 | bevy_ecs = { version = "0.15", default-features=false } 23 | log = { version = "0.4" } -------------------------------------------------------------------------------- /adapters/bevy/shared/src/components.rs: -------------------------------------------------------------------------------- 1 | use std::any::{Any, TypeId}; 2 | use std::collections::HashMap; 3 | 4 | use bevy_ecs::{component::Component, entity::Entity, system::Resource}; 5 | 6 | #[derive(Component, Clone, Copy)] 7 | pub struct HostOwned { 8 | type_id: TypeId, 9 | } 10 | 11 | impl HostOwned { 12 | pub fn new() -> Self { 13 | Self { 14 | type_id: TypeId::of::(), 15 | } 16 | } 17 | 18 | pub fn type_id(&self) -> TypeId { 19 | self.type_id 20 | } 21 | } 22 | 23 | #[derive(Resource)] 24 | pub struct HostOwnedMap { 25 | map: HashMap, 26 | } 27 | 28 | impl Default for HostOwnedMap { 29 | fn default() -> Self { 30 | Self { 31 | map: HashMap::new(), 32 | } 33 | } 34 | } 35 | 36 | impl HostOwnedMap { 37 | pub fn insert(&mut self, entity: Entity, host_owned: HostOwned) { 38 | self.map.insert(entity, host_owned); 39 | } 40 | 41 | pub fn remove(&mut self, entity: &Entity) -> Option { 42 | self.map.remove(entity) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /adapters/bevy/shared/src/protocol_plugin.rs: -------------------------------------------------------------------------------- 1 | use crate::Protocol; 2 | 3 | pub trait ProtocolPlugin { 4 | fn build(&self, protocol: &mut Protocol); 5 | } 6 | -------------------------------------------------------------------------------- /adapters/bevy/shared/src/system_set.rs: -------------------------------------------------------------------------------- 1 | use bevy_ecs::schedule::SystemSet; 2 | 3 | #[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)] 4 | pub struct ReceiveEvents; 5 | 6 | #[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)] 7 | pub struct BeforeReceiveEvents; 8 | 9 | #[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)] 10 | pub struct HostSyncChangeTracking; 11 | 12 | #[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)] 13 | pub struct BeforeHostSyncChangeTracking; 14 | 15 | #[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)] 16 | pub struct SendPackets; 17 | -------------------------------------------------------------------------------- /adapters/hecs/client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-hecs-client" 3 | # 0.24 is unpublished for now, due to lack of use of this crate, as well as plans for rapid releases - 12/12/2024 4 | version = "0.24.0" 5 | authors = ["connorcarpenter "] 6 | workspace = "../../.." 7 | description = "Library to faciliate naia_client & Hecs interop" 8 | homepage = "https://github.com/naia-lib/naia" 9 | repository = "https://github.com/naia-lib/naia" 10 | keywords = ["wasm", "webrtc", "udp", "networking", "gamedev"] 11 | categories = ["network-programming", "game-development", "wasm", "web-programming"] 12 | license = "MIT OR Apache-2.0" 13 | edition = "2021" 14 | 15 | [badges] 16 | maintenance = { status = "actively-developed" } 17 | 18 | [features] 19 | wbindgen = [ "naia-client/wbindgen", "naia-hecs-shared/wbindgen" ] 20 | mquad = [ "naia-client/mquad", "naia-hecs-shared/mquad" ] 21 | transport_webrtc = [ "naia-client/transport_webrtc" ] 22 | transport_udp = [ "naia-client/transport_udp" ] 23 | 24 | [dependencies] 25 | naia-client = { version = "0.24", path = "../../../client" } 26 | naia-hecs-shared = { version = "0.24", path = "../shared" } 27 | hecs = { version = "0.10" } -------------------------------------------------------------------------------- /adapters/hecs/client/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use naia_client::{ 2 | transport, Client, ClientConfig, ClientTickEvent, ConnectEvent, DespawnEntityEvent, 3 | DisconnectEvent, ErrorEvent, InsertComponentEvent, RemoveComponentEvent, SpawnEntityEvent, 4 | }; 5 | pub use naia_hecs_shared::{Protocol, WorldWrapper}; 6 | -------------------------------------------------------------------------------- /adapters/hecs/server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-hecs-server" 3 | # 0.24 is unpublished for now, due to lack of use of this crate, as well as plans for rapid releases - 12/12/2024 4 | version = "0.24.0" 5 | authors = ["connorcarpenter "] 6 | workspace = "../../.." 7 | description = "Library to faciliate naia_server & Hecs interop" 8 | homepage = "https://github.com/naia-lib/naia" 9 | repository = "https://github.com/naia-lib/naia" 10 | keywords = ["wasm", "webrtc", "udp", "networking", "gamedev"] 11 | categories = ["network-programming", "game-development", "wasm", "web-programming"] 12 | license = "MIT OR Apache-2.0" 13 | edition = "2021" 14 | 15 | [badges] 16 | maintenance = { status = "actively-developed" } 17 | 18 | [features] 19 | transport_webrtc = [ "naia-server/transport_webrtc" ] 20 | transport_udp = [ "naia-server/transport_udp" ] 21 | 22 | [dependencies] 23 | naia-server = { version = "0.24", path = "../../../server" } 24 | naia-hecs-shared = { version = "0.24", path = "../shared" } 25 | hecs = { version = "0.10" } -------------------------------------------------------------------------------- /adapters/hecs/server/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use naia_hecs_shared::{Protocol, Random, WorldProxy, WorldProxyMut, WorldWrapper}; 2 | pub use naia_server::{ 3 | transport, AuthEvent, ConnectEvent, DisconnectEvent, ErrorEvent, RoomKey, Server, ServerConfig, 4 | TickEvent, 5 | }; 6 | -------------------------------------------------------------------------------- /adapters/hecs/shared/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-hecs-shared" 3 | # 0.24 is unpublished for now, due to lack of use of this crate, as well as plans for rapid releases - 12/12/2024 4 | version = "0.24.0" 5 | authors = ["connorcarpenter "] 6 | workspace = "../../.." 7 | description = "Library to faciliate naia & Hecs interop, functionality shared by client & server versions" 8 | homepage = "https://github.com/naia-lib/naia" 9 | repository = "https://github.com/naia-lib/naia" 10 | keywords = ["wasm", "webrtc", "udp", "networking", "gamedev"] 11 | categories = ["network-programming", "game-development", "wasm", "web-programming"] 12 | license = "MIT OR Apache-2.0" 13 | edition = "2021" 14 | 15 | [badges] 16 | maintenance = { status = "actively-developed" } 17 | 18 | [features] 19 | wbindgen = [ "naia-shared/wbindgen" ] 20 | mquad = [ "naia-shared/mquad" ] 21 | 22 | [dependencies] 23 | naia-shared = { version = "0.24", path = "../../../shared" } 24 | hecs = { version = "0.10" } -------------------------------------------------------------------------------- /adapters/hecs/shared/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use naia_shared::{ 2 | BitReader, BitWrite, BitWriter, Channel, ChannelDirection, ChannelMode, ComponentFieldUpdate, 3 | ComponentKind, ComponentKinds, ComponentUpdate, ConstBitLength, DiffMask, EntityAuthAccessor, 4 | EntityProperty, GlobalEntity, HostEntity, LinkConditionerConfig, 5 | LocalEntityAndGlobalEntityConverter, LocalEntityAndGlobalEntityConverterMut, MessageBuilder, 6 | MessageContainer, MessageHecs as Message, MessageKind, MessageKinds, Named, OwnedBitReader, 7 | OwnedLocalEntity, Property, PropertyMutate, PropertyMutator, Random, ReliableSettings, 8 | RemoteEntity, ReplicaDynMut, ReplicaDynRef, ReplicateBuilder, ReplicateHecs as Replicate, 9 | SerdeErr, SerdeHecs as Serde, TickBufferSettings, UnsignedInteger, 10 | }; 11 | 12 | mod component_access; 13 | mod component_ref; 14 | mod protocol; 15 | mod world_data; 16 | mod world_proxy; 17 | mod world_wrapper; 18 | 19 | pub use protocol::Protocol; 20 | pub use world_data::WorldData; 21 | pub use world_proxy::{WorldProxy, WorldProxyMut}; 22 | pub use world_wrapper::WorldWrapper; 23 | -------------------------------------------------------------------------------- /adapters/hecs/shared/src/world_data.rs: -------------------------------------------------------------------------------- 1 | use std::{any::Any, collections::HashMap}; 2 | 3 | use naia_shared::{ComponentKind, Replicate}; 4 | 5 | use super::component_access::{ComponentAccess, ComponentAccessor}; 6 | 7 | pub struct WorldData { 8 | kind_to_accessor_map: HashMap>, 9 | } 10 | 11 | impl Default for WorldData { 12 | fn default() -> Self { 13 | Self { 14 | kind_to_accessor_map: HashMap::default(), 15 | } 16 | } 17 | } 18 | 19 | impl WorldData { 20 | pub fn new() -> Self { 21 | WorldData { 22 | kind_to_accessor_map: HashMap::new(), 23 | } 24 | } 25 | 26 | #[allow(clippy::borrowed_box)] 27 | pub(crate) fn component_access( 28 | &self, 29 | component_kind: &ComponentKind, 30 | ) -> Option<&Box> { 31 | if let Some(accessor_any) = self.kind_to_accessor_map.get(component_kind) { 32 | return accessor_any.downcast_ref::>(); 33 | } 34 | None 35 | } 36 | 37 | pub(crate) fn put_kind(&mut self, component_kind: &ComponentKind) { 38 | self.kind_to_accessor_map 39 | .insert(*component_kind, ComponentAccessor::::create()); 40 | } 41 | } 42 | 43 | unsafe impl Send for WorldData {} 44 | unsafe impl Sync for WorldData {} 45 | -------------------------------------------------------------------------------- /client/src/client_config.rs: -------------------------------------------------------------------------------- 1 | use std::{default::Default, time::Duration}; 2 | 3 | use naia_shared::ConnectionConfig; 4 | 5 | /// Contains Config properties which will be used by a Server or Client 6 | #[derive(Clone)] 7 | pub struct ClientConfig { 8 | /// Used to configure the connection with the Server 9 | pub connection: ConnectionConfig, 10 | /// The duration between the resend of certain connection handshake messages 11 | pub send_handshake_interval: Duration, 12 | /// The duration to wait before sending a ping message to the remote host, 13 | /// in order to estimate RTT time 14 | pub ping_interval: Duration, 15 | /// The number of network samples to take before completing the Connection Handshake. 16 | /// Increase this for greater accuracy of network statistics, at the cost of the handshake 17 | /// taking longer. Keep in mind that the network measurements affect how likely commands 18 | /// are able to arrive at the server before processing. 19 | pub handshake_pings: u8, 20 | } 21 | 22 | impl Default for ClientConfig { 23 | fn default() -> Self { 24 | Self { 25 | connection: ConnectionConfig::default(), 26 | send_handshake_interval: Duration::from_millis(250), 27 | ping_interval: Duration::from_secs(1), 28 | handshake_pings: 10, 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /client/src/connection/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod base_time_manager; 2 | pub mod channel_tick_buffer_sender; 3 | #[allow(clippy::module_inception)] 4 | pub mod connection; 5 | pub mod io; 6 | pub mod tick_buffer_sender; 7 | pub mod tick_queue; 8 | pub mod time_manager; 9 | -------------------------------------------------------------------------------- /client/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::{error::Error, fmt}; 2 | 3 | #[derive(Debug)] 4 | pub enum NaiaClientError { 5 | Message(String), 6 | Wrapped(Box), 7 | SendError, 8 | RecvError, 9 | IdError(u16), 10 | } 11 | 12 | impl NaiaClientError { 13 | pub fn from_message(message: &str) -> Self { 14 | Self::Message(message.to_string()) 15 | } 16 | } 17 | 18 | impl fmt::Display for NaiaClientError { 19 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 20 | match self { 21 | Self::Message(msg) => write!(f, "Naia Client Error: {}", msg), 22 | Self::Wrapped(boxed_err) => fmt::Display::fmt(boxed_err.as_ref(), f), 23 | Self::SendError => write!(f, "Naia Client Error: Send Error"), 24 | Self::RecvError => write!(f, "Naia Client Error: Recv Error"), 25 | Self::IdError(code) => write!(f, "Naia Client Error: Id Error: {}", code), 26 | } 27 | } 28 | } 29 | 30 | impl Error for NaiaClientError {} 31 | unsafe impl Send for NaiaClientError {} 32 | unsafe impl Sync for NaiaClientError {} 33 | -------------------------------------------------------------------------------- /client/src/handshake/mod.rs: -------------------------------------------------------------------------------- 1 | mod handshake_time_manager; 2 | 3 | use naia_shared::{BitReader, BitWriter, IdentityToken, OutgoingPacket}; 4 | 5 | use crate::connection::time_manager::TimeManager; 6 | 7 | cfg_if! { 8 | if #[cfg(feature = "transport_udp")] { 9 | mod advanced_handshaker; 10 | pub use advanced_handshaker::HandshakeManager; 11 | } else { 12 | mod simple_handshaker; 13 | pub use simple_handshaker::HandshakeManager; 14 | } 15 | } 16 | 17 | pub enum HandshakeResult { 18 | Connected(TimeManager), 19 | } 20 | 21 | pub trait Handshaker: Send + Sync { 22 | fn set_identity_token(&mut self, identity_token: IdentityToken); 23 | // fn is_connected(&self) -> bool; 24 | fn send(&mut self) -> Option; 25 | fn recv(&mut self, reader: &mut BitReader) -> Option; 26 | fn write_disconnect(&self) -> BitWriter; 27 | } 28 | -------------------------------------------------------------------------------- /client/src/transport/server_addr.rs: -------------------------------------------------------------------------------- 1 | use std::net::SocketAddr; 2 | 3 | /// The server's socket address, if it has been found 4 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 5 | pub enum ServerAddr { 6 | /// Client has found the server's socket address 7 | Found(SocketAddr), 8 | /// Client is still finding the server's socket address 9 | Finding, 10 | } 11 | 12 | impl ServerAddr { 13 | pub fn is_found(&self) -> bool { 14 | match self { 15 | ServerAddr::Found(_) => true, 16 | ServerAddr::Finding => false, 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /client/src/transport/udp/addr_cell.rs: -------------------------------------------------------------------------------- 1 | use std::{net::SocketAddr, sync::Arc}; 2 | 3 | use tokio::sync::RwLock; 4 | 5 | use crate::transport::ServerAddr; 6 | 7 | // MaybeAddr 8 | struct MaybeAddr(pub(crate) ServerAddr); 9 | 10 | // AddrCell 11 | #[derive(Clone)] 12 | pub struct AddrCell { 13 | cell: Arc>, 14 | } 15 | 16 | impl Default for AddrCell { 17 | fn default() -> Self { 18 | Self { 19 | cell: Arc::new(RwLock::new(MaybeAddr(ServerAddr::Finding))), 20 | } 21 | } 22 | } 23 | 24 | impl AddrCell { 25 | pub async fn recv(&self, addr: &SocketAddr) { 26 | let mut cell = self.cell.write().await; 27 | cell.0 = ServerAddr::Found(*addr); 28 | } 29 | 30 | pub fn get(&self) -> ServerAddr { 31 | match self.cell.try_read() { 32 | Ok(addr) => addr.0, 33 | Err(_) => ServerAddr::Finding, 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /client/src/transport/udp/mod.rs: -------------------------------------------------------------------------------- 1 | mod addr_cell; 2 | mod auth; 3 | mod conditioner; 4 | mod data; 5 | 6 | pub use data::Socket; 7 | -------------------------------------------------------------------------------- /client/src/world/entity_owner.rs: -------------------------------------------------------------------------------- 1 | #[derive(Copy, Clone, PartialEq, Eq)] 2 | pub enum EntityOwner { 3 | Server, 4 | Client, 5 | Local, 6 | } 7 | 8 | impl EntityOwner { 9 | pub fn is_server(&self) -> bool { 10 | match self { 11 | EntityOwner::Server => true, 12 | _ => false, 13 | } 14 | } 15 | 16 | pub fn is_client(&self) -> bool { 17 | match self { 18 | EntityOwner::Client => true, 19 | _ => false, 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /client/src/world/entity_ref.rs: -------------------------------------------------------------------------------- 1 | use std::hash::Hash; 2 | 3 | use naia_shared::{EntityAuthStatus, ReplicaRefWrapper, ReplicatedComponent, WorldRefType}; 4 | 5 | use crate::{Client, ReplicationConfig}; 6 | 7 | // EntityRef 8 | pub struct EntityRef<'s, E: Copy + Eq + Hash + Send + Sync, W: WorldRefType> { 9 | client: &'s Client, 10 | world: W, 11 | entity: E, 12 | } 13 | 14 | impl<'s, E: Copy + Eq + Hash + Send + Sync, W: WorldRefType> EntityRef<'s, E, W> { 15 | pub fn new(client: &'s Client, world: W, entity: &E) -> Self { 16 | EntityRef { 17 | client, 18 | world, 19 | entity: *entity, 20 | } 21 | } 22 | 23 | pub fn id(&self) -> E { 24 | self.entity 25 | } 26 | 27 | pub fn has_component(&self) -> bool { 28 | self.world.has_component::(&self.entity) 29 | } 30 | 31 | pub fn component(&self) -> Option> { 32 | self.world.component::(&self.entity) 33 | } 34 | 35 | pub fn replication_config(&self) -> Option { 36 | self.client.entity_replication_config(&self.entity) 37 | } 38 | 39 | pub fn authority(&self) -> Option { 40 | self.client.entity_authority_status(&self.entity) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /client/src/world/global_entity_record.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use naia_shared::{ComponentKind, GlobalEntity}; 4 | 5 | use crate::{world::entity_owner::EntityOwner, ReplicationConfig}; 6 | 7 | pub struct GlobalEntityRecord { 8 | pub global_entity: GlobalEntity, 9 | pub component_kinds: HashSet, 10 | pub owner: EntityOwner, 11 | pub replication_config: ReplicationConfig, 12 | pub is_replicating: bool, 13 | } 14 | 15 | impl GlobalEntityRecord { 16 | pub fn new(global_entity: GlobalEntity, owner: EntityOwner) -> Self { 17 | if owner == EntityOwner::Local { 18 | panic!("Should not insert Local entity in this record"); 19 | } 20 | 21 | // Host-owned entities always start public, client-owned entities always start private 22 | let replication_config = if owner.is_server() { 23 | ReplicationConfig::Public 24 | } else { 25 | ReplicationConfig::Private 26 | }; 27 | 28 | Self { 29 | global_entity, 30 | component_kinds: HashSet::new(), 31 | owner, 32 | replication_config, 33 | is_replicating: true, 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /client/src/world/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod entity_mut; 2 | pub mod entity_owner; 3 | pub mod entity_ref; 4 | pub mod global_entity_record; 5 | pub mod global_world_manager; 6 | pub mod mut_channel; 7 | pub mod replication_config; 8 | -------------------------------------------------------------------------------- /client/src/world/mut_channel.rs: -------------------------------------------------------------------------------- 1 | use std::net::SocketAddr; 2 | 3 | use naia_shared::{MutChannelType, MutReceiver}; 4 | 5 | pub struct MutChannelData { 6 | receiver: MutReceiver, 7 | } 8 | 9 | impl MutChannelData { 10 | pub fn new(diff_mask_length: u8) -> Self { 11 | Self { 12 | receiver: MutReceiver::new(diff_mask_length), 13 | } 14 | } 15 | } 16 | 17 | impl MutChannelType for MutChannelData { 18 | fn new_receiver(&mut self, address_opt: &Option) -> Option { 19 | if address_opt.is_some() { 20 | panic!( 21 | "should not initialize client MutReceiver with an address (there is only 1 server)" 22 | ); 23 | } 24 | return Some(self.receiver.clone()); 25 | } 26 | 27 | fn send(&self, diff: u8) { 28 | self.receiver.mutate(diff); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /client/src/world/replication_config.rs: -------------------------------------------------------------------------------- 1 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 2 | pub enum ReplicationConfig { 3 | Private, // this is for Client non-Public Entities 4 | Public, // this is for Server Entities and Client Public Entities 5 | Delegated, // this is for Server Delegated Entities 6 | } 7 | 8 | impl ReplicationConfig { 9 | pub fn is_delegated(&self) -> bool { 10 | match self { 11 | ReplicationConfig::Delegated => true, 12 | _ => false, 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /demos/basic/client/app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-basic-client-demo-app" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../../.." 6 | edition = "2021" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | 10 | [features] 11 | mquad = [ "naia-client/mquad", "naia-basic-demo-shared/mquad", "miniquad" ] 12 | wbindgen = [ "naia-client/wbindgen", "naia-basic-demo-shared/wbindgen", "log" ] 13 | 14 | [dependencies] 15 | naia-client = { path = "../../../../client", features = [ "transport_webrtc" ] } 16 | naia-demo-world = { path = "../../../demo_utils/demo_world" } 17 | naia-basic-demo-shared = { path = "../../shared" } 18 | cfg-if = { version = "1.0" } 19 | log = { version = "0.4", optional = true } 20 | miniquad = { version = "0.3", features = ["log-impl"], optional = true } -------------------------------------------------------------------------------- /demos/basic/client/app/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate cfg_if; 3 | 4 | mod app; 5 | pub use app::App; 6 | -------------------------------------------------------------------------------- /demos/basic/client/miniquad/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-basic-client-demo-mq" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../../.." 6 | edition = "2021" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | 10 | [[bin]] 11 | name = "app" 12 | path = "src/main.rs" 13 | 14 | [features] 15 | 16 | [dependencies] 17 | naia-basic-client-demo-app = { path = "../app", features = [ "mquad" ] } 18 | cfg-if = { version = "1.0" } 19 | miniquad = { version = "0.3", features = ["log-impl"] } -------------------------------------------------------------------------------- /demos/basic/client/miniquad/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.install-target-wasm32-unknown-unknown] 2 | command = "rustup" 3 | args = ["target", "install", "wasm32-unknown-unknown"] 4 | 5 | [tasks.install-basic-http-server] 6 | install_crate = {crate_name = "basic-http-server", binary = "basic-http-server", test_arg="--help"} 7 | 8 | [tasks.cargo-build-wasm] 9 | command = "cargo" 10 | args = ["build", "--target", "wasm32-unknown-unknown", "--target-dir", "target"] 11 | dependencies = ["install-target-wasm32-unknown-unknown"] 12 | 13 | [tasks.delete-old-wasm] 14 | command = "rm" 15 | args = ["-f", "target/app.wasm"] 16 | dependencies = ["cargo-build-wasm"] 17 | 18 | [tasks.move-wasm] 19 | command = "mv" 20 | args = ["target/wasm32-unknown-unknown/debug/app.wasm", "target/app.wasm"] 21 | dependencies = ["delete-old-wasm"] 22 | 23 | [tasks.serve] 24 | command = "basic-http-server" 25 | args = ["-x"] 26 | dependencies = ["move-wasm", "install-basic-http-server"] -------------------------------------------------------------------------------- /demos/basic/client/miniquad/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Naia Basic Client Demo - Miniquad 6 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /demos/basic/client/miniquad/src/main.rs: -------------------------------------------------------------------------------- 1 | use miniquad::*; 2 | 3 | use naia_basic_client_demo_app::App; 4 | 5 | struct Stage { 6 | app: App, 7 | } 8 | impl EventHandler for Stage { 9 | fn update(&mut self, _ctx: &mut Context) { 10 | self.app.update(); 11 | } 12 | 13 | fn draw(&mut self, ctx: &mut Context) { 14 | ctx.clear(Some((0., 1., 0., 1.)), None, None); 15 | } 16 | } 17 | 18 | fn main() { 19 | let app = App::default(); 20 | miniquad::start(conf::Conf::default(), |_ctx| Box::new(Stage { app })); 21 | } 22 | -------------------------------------------------------------------------------- /demos/basic/client/wasm_bindgen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-basic-client-demo-wb" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../../.." 6 | edition = "2021" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | 10 | [[bin]] 11 | name = "app" 12 | path = "src/main.rs" 13 | 14 | [lib] 15 | name = "app" 16 | path = "src/lib.rs" 17 | crate-type = ["cdylib", "rlib"] 18 | 19 | [features] 20 | 21 | [dependencies] 22 | naia-basic-client-demo-app = { path = "../app", features = [ "wbindgen" ] } 23 | log = { version = "0.4" } 24 | cfg-if = { version = "1.0" } 25 | 26 | [target.'cfg(not(target_arch = "wasm32"))'.dependencies] 27 | simple_logger = { version = "4.0", default-features = false, features = ["timestamps"] } 28 | 29 | [target.'cfg(target_arch = "wasm32")'.dependencies] 30 | wasm-logger = { version = "0.2" } 31 | wasm-bindgen = { version = "0.2", features = [ "serde-serialize" ] } 32 | web-sys = { version = "0.3.64", features = [ 'Window' ] } -------------------------------------------------------------------------------- /demos/basic/client/wasm_bindgen/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.install-target-wasm32-unknown-unknown] 2 | command = "rustup" 3 | args = ["target", "install", "wasm32-unknown-unknown"] 4 | 5 | [tasks.install-basic-http-server] 6 | install_crate = { crate_name = "basic-http-server", binary = "basic-http-server", test_arg="--help" } 7 | 8 | [tasks.install-wasm-bindgen-cli] 9 | install_crate = { crate_name = "wasm-bindgen-cli", binary = "wasm-bindgen", test_arg="--help" } 10 | 11 | [tasks.cargo-build-wasm] 12 | command = "cargo" 13 | args = ["build", "--target", "wasm32-unknown-unknown", "--lib", "--target-dir", "target"] 14 | dependencies = ["install-target-wasm32-unknown-unknown"] 15 | 16 | [tasks.wasm-bindgen] 17 | command = "wasm-bindgen" 18 | args = ["--out-dir", "target", "--out-name", "app", "--target", "web", "--no-typescript", "target/wasm32-unknown-unknown/debug/app.wasm"] 19 | dependencies = ["cargo-build-wasm", "install-wasm-bindgen-cli"] 20 | 21 | [tasks.serve] 22 | command = "basic-http-server" 23 | args = ["-x"] 24 | dependencies = ["wasm-bindgen", "install-basic-http-server"] -------------------------------------------------------------------------------- /demos/basic/client/wasm_bindgen/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Naia Basic Client Demo - Wasm-Bindgen 6 | 7 | 8 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /demos/basic/client/wasm_bindgen/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate cfg_if; 3 | 4 | cfg_if! { 5 | if #[cfg(target_arch = "wasm32")] { 6 | 7 | extern crate log; 8 | 9 | mod app_loop; 10 | 11 | use wasm_bindgen::prelude::*; 12 | 13 | use naia_basic_client_demo_app::App; 14 | 15 | use app_loop::start_loop; 16 | 17 | #[wasm_bindgen(start)] 18 | pub fn main() -> Result<(), JsValue> { 19 | wasm_logger::init(wasm_logger::Config::default()); 20 | 21 | start_loop(App::default()); 22 | 23 | Ok(()) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /demos/basic/client/wasm_bindgen/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate cfg_if; 3 | 4 | cfg_if! { 5 | if #[cfg(not(target_arch = "wasm32"))] { 6 | 7 | extern crate log; 8 | 9 | mod app_loop; 10 | 11 | use naia_basic_client_demo_app::App; 12 | 13 | use app_loop::start_loop; 14 | 15 | fn main() { 16 | simple_logger::SimpleLogger::new() 17 | .with_level(log::LevelFilter::Info) 18 | .init() 19 | .expect("A logger was already initialized"); 20 | 21 | start_loop(App::default()); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /demos/basic/server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-basic-server-demo" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../.." 6 | edition = "2021" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | 10 | [dependencies] 11 | naia-server = { path = "../../../server", features = [ "transport_webrtc" ] } 12 | naia-demo-world = { path = "../../demo_utils/demo_world" } 13 | naia-basic-demo-shared = { path = "../shared" } 14 | log = { version = "0.4" } 15 | simple_logger = { version = "4.0", default-features = false, features = ["timestamps"] } 16 | smol = { version = "1.3" } -------------------------------------------------------------------------------- /demos/basic/server/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | 4 | use log::LevelFilter; 5 | use simple_logger::SimpleLogger; 6 | use smol::io; 7 | 8 | mod app; 9 | use app::App; 10 | 11 | fn main() -> io::Result<()> { 12 | SimpleLogger::new() 13 | .with_level(LevelFilter::Info) 14 | .init() 15 | .expect("A logger was already initialized"); 16 | 17 | let mut app = App::new(); 18 | loop { 19 | app.update(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /demos/basic/shared/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-basic-demo-shared" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../.." 6 | edition = "2021" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | 10 | [features] 11 | wbindgen = [ "naia-shared/wbindgen" ] 12 | mquad = [ "naia-shared/mquad" ] 13 | 14 | [dependencies] 15 | naia-shared = { path = "../../../shared" } -------------------------------------------------------------------------------- /demos/basic/shared/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod protocol; 2 | 3 | pub use protocol::{protocol, Auth, BasicRequest, BasicResponse, Character, StringMessage}; 4 | -------------------------------------------------------------------------------- /demos/basic/shared/src/protocol/auth.rs: -------------------------------------------------------------------------------- 1 | use naia_shared::Message; 2 | 3 | #[derive(Message)] 4 | pub struct Auth { 5 | pub username: String, 6 | pub password: String, 7 | } 8 | 9 | impl Auth { 10 | pub fn new(username: &str, password: &str) -> Self { 11 | Self { 12 | username: username.to_string(), 13 | password: password.to_string(), 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /demos/basic/shared/src/protocol/basic_request.rs: -------------------------------------------------------------------------------- 1 | use naia_shared::{Message, Request, Response}; 2 | 3 | #[derive(Message)] 4 | pub struct BasicRequest { 5 | pub contents: String, 6 | } 7 | 8 | impl Request for BasicRequest { 9 | type Response = BasicResponse; 10 | } 11 | 12 | impl BasicRequest { 13 | pub fn new(contents: String) -> Self { 14 | Self { contents } 15 | } 16 | } 17 | 18 | #[derive(Message)] 19 | pub struct BasicResponse { 20 | pub contents: String, 21 | } 22 | 23 | impl Response for BasicResponse {} 24 | 25 | impl BasicResponse { 26 | pub fn new(contents: String) -> Self { 27 | Self { contents } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /demos/basic/shared/src/protocol/character.rs: -------------------------------------------------------------------------------- 1 | use naia_shared::{Property, Replicate, Serde}; 2 | 3 | /// Here's an example of a Custom Property 4 | #[derive(Serde, PartialEq, Clone)] 5 | pub struct FullName { 6 | /// First name 7 | pub first: String, 8 | /// Last name 9 | pub last: String, 10 | } 11 | 12 | #[derive(Replicate)] 13 | pub struct Character { 14 | pub x: Property, 15 | pub y: Property, 16 | pub fullname: Property, 17 | } 18 | 19 | impl Character { 20 | pub fn new(x: u8, y: u8, first: &str, last: &str) -> Self { 21 | Self::new_complete( 22 | x, 23 | y, 24 | FullName { 25 | first: first.to_string(), 26 | last: last.to_string(), 27 | }, 28 | ) 29 | } 30 | 31 | pub fn step(&mut self) { 32 | *self.x += 1; 33 | if *self.x > 20 { 34 | *self.x = 0; 35 | } 36 | if *self.x % 3 == 0 { 37 | *self.y = self.y.wrapping_add(1); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /demos/basic/shared/src/protocol/mod.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use naia_shared::{LinkConditionerConfig, Protocol}; 4 | 5 | mod auth; 6 | mod basic_request; 7 | mod character; 8 | mod string_message; 9 | 10 | pub use auth::Auth; 11 | pub use basic_request::{BasicRequest, BasicResponse}; 12 | pub use character::Character; 13 | pub use string_message::StringMessage; 14 | 15 | // Protocol Build 16 | pub fn protocol() -> Protocol { 17 | Protocol::builder() 18 | // Config 19 | .tick_interval(Duration::from_millis(800)) 20 | .link_condition(LinkConditionerConfig::average_condition()) 21 | // Channels 22 | .add_default_channels() 23 | // Messages 24 | .add_message::() 25 | .add_message::() 26 | // Requests 27 | .add_request::() 28 | // Components 29 | .add_component::() 30 | // Build Protocol 31 | .build() 32 | } 33 | -------------------------------------------------------------------------------- /demos/basic/shared/src/protocol/string_message.rs: -------------------------------------------------------------------------------- 1 | use naia_shared::Message; 2 | 3 | #[derive(Message)] 4 | pub struct StringMessage { 5 | pub contents: String, 6 | } 7 | 8 | impl StringMessage { 9 | pub fn new(contents: String) -> Self { 10 | Self { contents } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /demos/bevy/client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-bevy-client-demo" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../.." 6 | edition = "2021" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | 10 | [[bin]] 11 | name = "app" 12 | path = "src/main.rs" 13 | 14 | [lib] 15 | name = "app" 16 | path = "src/lib.rs" 17 | crate-type = ["cdylib", "rlib"] 18 | 19 | [features] 20 | 21 | [dependencies] 22 | naia-bevy-client = { path = "../../../adapters/bevy/client", features = ["transport_webrtc"] } 23 | naia-bevy-demo-shared = { path = "../shared" } 24 | 25 | bevy = { version = "0.15", default-features = false, features = [ "bevy_asset", "bevy_window", "bevy_winit", "bevy_core_pipeline", "bevy_render", "bevy_sprite", "x11", "webgl2"] } 26 | 27 | cfg-if = { version = "1.0" } 28 | 29 | [target.'cfg(target_arch = "wasm32")'.dependencies] 30 | wasm-bindgen = { version = "0.2", features = [ "serde-serialize" ] } -------------------------------------------------------------------------------- /demos/bevy/client/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.install-target-wasm32-unknown-unknown] 2 | command = "rustup" 3 | args = ["target", "install", "wasm32-unknown-unknown"] 4 | 5 | [tasks.install-basic-http-server] 6 | install_crate = { crate_name = "basic-http-server", binary = "basic-http-server", test_arg="--help" } 7 | 8 | [tasks.install-wasm-bindgen-cli] 9 | install_crate = { crate_name = "wasm-bindgen-cli", binary = "wasm-bindgen", test_arg="--help" } 10 | 11 | [tasks.cargo-build-wasm] 12 | command = "cargo" 13 | args = ["build", "--target", "wasm32-unknown-unknown", "--lib", "--target-dir", "target"] 14 | dependencies = ["install-target-wasm32-unknown-unknown"] 15 | 16 | [tasks.wasm-bindgen] 17 | command = "wasm-bindgen" 18 | args = ["--out-dir", "target", "--out-name", "app", "--target", "web", "--no-typescript", "target/wasm32-unknown-unknown/debug/app.wasm"] 19 | dependencies = ["cargo-build-wasm", "install-wasm-bindgen-cli"] 20 | 21 | [tasks.serve] 22 | command = "basic-http-server" 23 | args = ["-x"] 24 | dependencies = ["wasm-bindgen", "install-basic-http-server"] -------------------------------------------------------------------------------- /demos/bevy/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Naia + Bevy Demo Client 6 | 7 | 8 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /demos/bevy/client/src/components/interp.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::Component; 2 | 3 | #[derive(Component)] 4 | pub struct Interp { 5 | interp: f32, 6 | pub interp_x: f32, 7 | pub interp_y: f32, 8 | 9 | last_x: f32, 10 | last_y: f32, 11 | pub next_x: f32, 12 | pub next_y: f32, 13 | } 14 | 15 | impl Interp { 16 | pub fn new(x: i16, y: i16) -> Self { 17 | let x = x as f32; 18 | let y = y as f32; 19 | Self { 20 | interp: 0.0, 21 | interp_x: x, 22 | interp_y: y, 23 | 24 | last_x: x, 25 | last_y: y, 26 | next_x: x, 27 | next_y: y, 28 | } 29 | } 30 | 31 | pub(crate) fn next_position(&mut self, next_x: i16, next_y: i16) { 32 | self.interp = 0.0; 33 | self.last_x = self.next_x; 34 | self.last_y = self.next_y; 35 | self.interp_x = self.next_x; 36 | self.interp_y = self.next_y; 37 | self.next_x = next_x as f32; 38 | self.next_y = next_y as f32; 39 | } 40 | 41 | pub(crate) fn interpolate(&mut self, interpolation: f32) { 42 | if self.interp >= 1.0 || interpolation == 0.0 { 43 | return; 44 | } 45 | if self.interp < interpolation { 46 | self.interp = interpolation; 47 | self.interp_x = self.last_x + (self.next_x - self.last_x) * self.interp; 48 | self.interp_y = self.last_y + (self.next_y - self.last_y) * self.interp; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /demos/bevy/client/src/components/markers.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::Component; 2 | 3 | #[derive(Component)] 4 | pub struct Predicted; 5 | 6 | #[derive(Component)] 7 | pub struct Confirmed; 8 | 9 | #[derive(Component)] 10 | pub struct LocalCursor; 11 | -------------------------------------------------------------------------------- /demos/bevy/client/src/components/mod.rs: -------------------------------------------------------------------------------- 1 | mod interp; 2 | mod markers; 3 | 4 | pub use interp::Interp; 5 | pub use markers::{Confirmed, LocalCursor, Predicted}; 6 | -------------------------------------------------------------------------------- /demos/bevy/client/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate cfg_if; 3 | 4 | cfg_if! { 5 | if #[cfg(target_arch = "wasm32")] { 6 | 7 | mod resources; 8 | mod systems; 9 | mod app; 10 | mod components; 11 | 12 | use wasm_bindgen::prelude::*; 13 | 14 | #[wasm_bindgen(start)] 15 | pub fn main() -> Result<(), JsValue> { 16 | app::run(); 17 | 18 | Ok(()) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /demos/bevy/client/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate cfg_if; 3 | 4 | cfg_if! { 5 | if #[cfg(not(target_arch = "wasm32"))] { 6 | 7 | mod resources; 8 | mod systems; 9 | mod app; 10 | mod components; 11 | 12 | fn main() { 13 | app::run(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /demos/bevy/client/src/systems/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod events; 2 | pub mod input; 3 | pub mod sync; 4 | 5 | mod init; 6 | pub use init::init; 7 | -------------------------------------------------------------------------------- /demos/bevy/server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-bevy-server-demo" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../.." 6 | edition = "2021" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | 10 | [dependencies] 11 | naia-bevy-demo-shared = { path = "../shared" } 12 | naia-bevy-server = { path = "../../../adapters/bevy/server", features = [ "transport_webrtc" ] } 13 | bevy_app = { version = "0.15", default-features=false } 14 | bevy_core = { version = "0.15", default-features=false } 15 | bevy_ecs = { version = "0.15", default-features=false } 16 | bevy_log = { version = "0.15", default-features=false } 17 | -------------------------------------------------------------------------------- /demos/bevy/server/src/resources.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashMap, HashSet}; 2 | 3 | use bevy_ecs::{entity::Entity, prelude::Resource}; 4 | 5 | use naia_bevy_demo_shared::messages::BasicResponse; 6 | use naia_bevy_server::{ResponseReceiveKey, RoomKey, UserKey}; 7 | 8 | #[derive(Resource)] 9 | pub struct Global { 10 | pub main_room_key: RoomKey, 11 | pub user_to_square_map: HashMap, 12 | pub square_to_user_map: HashMap, 13 | pub response_keys: HashSet>, 14 | pub request_index: u8, 15 | } 16 | -------------------------------------------------------------------------------- /demos/bevy/server/src/systems/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod events; 2 | 3 | mod init; 4 | 5 | pub use init::init; 6 | -------------------------------------------------------------------------------- /demos/bevy/shared/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-bevy-demo-shared" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../.." 6 | edition = "2021" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | 10 | [features] 11 | 12 | [dependencies] 13 | naia-bevy-shared = { path = "../../../adapters/bevy/shared" } 14 | bevy_ecs = { version = "0.15", default-features=false} -------------------------------------------------------------------------------- /demos/bevy/shared/src/behavior/mod.rs: -------------------------------------------------------------------------------- 1 | mod process_command; 2 | pub use process_command::process_command; 3 | -------------------------------------------------------------------------------- /demos/bevy/shared/src/behavior/process_command.rs: -------------------------------------------------------------------------------- 1 | use crate::{components::Position, messages::KeyCommand}; 2 | 3 | const SQUARE_SPEED: i16 = 4; 4 | 5 | pub fn process_command(key_command: &KeyCommand, position: &mut Position) { 6 | if key_command.w { 7 | *position.y = position.y.wrapping_add(SQUARE_SPEED); 8 | } 9 | if key_command.s { 10 | *position.y = position.y.wrapping_sub(SQUARE_SPEED); 11 | } 12 | if key_command.a { 13 | *position.x = position.x.wrapping_sub(SQUARE_SPEED); 14 | } 15 | if key_command.d { 16 | *position.x = position.x.wrapping_add(SQUARE_SPEED); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /demos/bevy/shared/src/channels.rs: -------------------------------------------------------------------------------- 1 | use naia_bevy_shared::{ 2 | Channel, ChannelDirection, ChannelMode, Protocol, ProtocolPlugin, ReliableSettings, 3 | TickBufferSettings, 4 | }; 5 | 6 | #[derive(Channel)] 7 | pub struct PlayerCommandChannel; 8 | 9 | #[derive(Channel)] 10 | pub struct EntityAssignmentChannel; 11 | 12 | #[derive(Channel)] 13 | pub struct RequestChannel; 14 | 15 | // Plugin 16 | pub struct ChannelsPlugin; 17 | 18 | impl ProtocolPlugin for ChannelsPlugin { 19 | fn build(&self, protocol: &mut Protocol) { 20 | protocol 21 | .add_channel::( 22 | ChannelDirection::ClientToServer, 23 | ChannelMode::TickBuffered(TickBufferSettings::default()), 24 | ) 25 | .add_channel::( 26 | ChannelDirection::ServerToClient, 27 | ChannelMode::UnorderedReliable(ReliableSettings::default()), 28 | ) 29 | .add_channel::( 30 | ChannelDirection::Bidirectional, 31 | ChannelMode::UnorderedReliable(ReliableSettings::default()), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /demos/bevy/shared/src/components/color.rs: -------------------------------------------------------------------------------- 1 | use bevy_ecs::prelude::Component; 2 | 3 | use naia_bevy_shared::{Property, Replicate, Serde}; 4 | 5 | #[derive(Serde, PartialEq, Clone)] 6 | pub enum ColorValue { 7 | Red, 8 | Blue, 9 | Yellow, 10 | Green, 11 | White, 12 | Purple, 13 | Orange, 14 | Aqua, 15 | } 16 | 17 | #[derive(Component, Replicate)] 18 | pub struct Color { 19 | pub value: Property, 20 | } 21 | 22 | impl Color { 23 | pub fn new(value: ColorValue) -> Self { 24 | Self::new_complete(value) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /demos/bevy/shared/src/components/mod.rs: -------------------------------------------------------------------------------- 1 | use naia_bevy_shared::{Protocol, ProtocolPlugin}; 2 | 3 | mod color; 4 | pub use color::{Color, ColorValue}; 5 | 6 | mod position; 7 | pub use position::Position; 8 | 9 | mod shape; 10 | pub use shape::{Shape, ShapeValue}; 11 | 12 | // Plugin 13 | pub struct ComponentsPlugin; 14 | 15 | impl ProtocolPlugin for ComponentsPlugin { 16 | fn build(&self, protocol: &mut Protocol) { 17 | protocol 18 | .add_component::() 19 | .add_component::() 20 | .add_component::(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /demos/bevy/shared/src/components/position.rs: -------------------------------------------------------------------------------- 1 | use bevy_ecs::prelude::Component; 2 | 3 | use naia_bevy_shared::{Property, Replicate}; 4 | 5 | #[derive(Component, Replicate)] 6 | pub struct Position { 7 | pub x: Property, 8 | pub y: Property, 9 | } 10 | 11 | impl Position { 12 | pub fn new(x: i16, y: i16) -> Self { 13 | Self::new_complete(x, y) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /demos/bevy/shared/src/components/shape.rs: -------------------------------------------------------------------------------- 1 | use bevy_ecs::prelude::Component; 2 | 3 | use naia_bevy_shared::{Property, Replicate, Serde}; 4 | 5 | #[derive(Serde, PartialEq, Clone)] 6 | pub enum ShapeValue { 7 | Square, 8 | Circle, 9 | } 10 | 11 | #[derive(Component, Replicate)] 12 | pub struct Shape { 13 | pub value: Property, 14 | } 15 | 16 | impl Shape { 17 | pub fn new(value: ShapeValue) -> Self { 18 | Self::new_complete(value) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /demos/bevy/shared/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod behavior; 2 | pub mod channels; 3 | pub mod components; 4 | pub mod messages; 5 | 6 | mod protocol; 7 | pub use protocol::protocol; 8 | -------------------------------------------------------------------------------- /demos/bevy/shared/src/messages/auth.rs: -------------------------------------------------------------------------------- 1 | use naia_bevy_shared::Message; 2 | 3 | #[derive(Message)] 4 | pub struct Auth { 5 | pub username: String, 6 | pub password: String, 7 | } 8 | 9 | impl Auth { 10 | pub fn new(username: &str, password: &str) -> Self { 11 | Self { 12 | username: username.to_string(), 13 | password: password.to_string(), 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /demos/bevy/shared/src/messages/basic_request.rs: -------------------------------------------------------------------------------- 1 | use naia_bevy_shared::{Message, Request, Response}; 2 | 3 | #[derive(Message, Debug)] 4 | pub struct BasicRequest { 5 | pub contents: String, 6 | pub index: u8, 7 | } 8 | 9 | impl Request for BasicRequest { 10 | type Response = BasicResponse; 11 | } 12 | 13 | impl BasicRequest { 14 | pub fn new(contents: String, index: u8) -> Self { 15 | Self { contents, index } 16 | } 17 | } 18 | 19 | #[derive(Message, Eq, PartialEq, Hash, Debug)] 20 | pub struct BasicResponse { 21 | pub contents: String, 22 | pub index: u8, 23 | } 24 | 25 | impl Response for BasicResponse {} 26 | 27 | impl BasicResponse { 28 | pub fn new(contents: String, index: u8) -> Self { 29 | Self { contents, index } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /demos/bevy/shared/src/messages/entity_assignment.rs: -------------------------------------------------------------------------------- 1 | use naia_bevy_shared::{EntityProperty, Message}; 2 | 3 | #[derive(Message)] 4 | pub struct EntityAssignment { 5 | pub entity: EntityProperty, 6 | pub assign: bool, 7 | } 8 | 9 | impl EntityAssignment { 10 | pub fn new(assign: bool) -> Self { 11 | Self { 12 | assign, 13 | entity: EntityProperty::new(), 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /demos/bevy/shared/src/messages/key_command.rs: -------------------------------------------------------------------------------- 1 | use naia_bevy_shared::{EntityProperty, Message}; 2 | 3 | #[derive(Message)] 4 | pub struct KeyCommand { 5 | pub entity: EntityProperty, 6 | pub w: bool, 7 | pub s: bool, 8 | pub a: bool, 9 | pub d: bool, 10 | } 11 | 12 | impl KeyCommand { 13 | pub fn new(w: bool, s: bool, a: bool, d: bool) -> Self { 14 | Self { 15 | entity: EntityProperty::new(), 16 | w, 17 | s, 18 | a, 19 | d, 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /demos/bevy/shared/src/messages/mod.rs: -------------------------------------------------------------------------------- 1 | use naia_bevy_shared::{Protocol, ProtocolPlugin}; 2 | 3 | mod auth; 4 | mod basic_request; 5 | mod entity_assignment; 6 | mod key_command; 7 | 8 | pub use auth::Auth; 9 | pub use basic_request::{BasicRequest, BasicResponse}; 10 | pub use entity_assignment::EntityAssignment; 11 | pub use key_command::KeyCommand; 12 | 13 | // Plugin 14 | pub struct MessagesPlugin; 15 | 16 | impl ProtocolPlugin for MessagesPlugin { 17 | fn build(&self, protocol: &mut Protocol) { 18 | protocol 19 | .add_message::() 20 | .add_message::() 21 | .add_message::() 22 | .add_request::(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /demos/bevy/shared/src/protocol.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use naia_bevy_shared::{LinkConditionerConfig, Protocol}; 4 | 5 | use crate::{channels::ChannelsPlugin, components::ComponentsPlugin, messages::MessagesPlugin}; 6 | 7 | // Protocol Build 8 | pub fn protocol() -> Protocol { 9 | Protocol::builder() 10 | // Config 11 | .tick_interval(Duration::from_millis(40)) 12 | .link_condition(LinkConditionerConfig::poor_condition()) 13 | .enable_client_authoritative_entities() 14 | // Channels 15 | .add_plugin(ChannelsPlugin) 16 | // Messages 17 | .add_plugin(MessagesPlugin) 18 | // Components 19 | .add_plugin(ComponentsPlugin) 20 | // Build Protocol 21 | .build() 22 | } 23 | -------------------------------------------------------------------------------- /demos/demo_utils/demo_world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-demo-world" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | homepage = "https://github.com/naia-lib/naia" 6 | repository = "https://github.com/naia-lib/naia" 7 | edition = "2021" 8 | license = "MIT OR Apache-2.0" 9 | publish = false 10 | 11 | [features] 12 | 13 | [dependencies] 14 | naia-shared = { path = "../../../shared" } -------------------------------------------------------------------------------- /demos/demo_utils/demo_world/src/component_ref.rs: -------------------------------------------------------------------------------- 1 | use naia_shared::{ReplicaMutTrait, ReplicaRefTrait, Replicate}; 2 | 3 | // ComponentRef 4 | pub struct ComponentRef<'a, R: Replicate> { 5 | inner: &'a R, 6 | } 7 | 8 | impl<'a, R: Replicate> ComponentRef<'a, R> { 9 | pub fn new(inner: &'a R) -> Self { 10 | Self { inner } 11 | } 12 | } 13 | 14 | impl<'a, R: Replicate> ReplicaRefTrait for ComponentRef<'a, R> { 15 | fn to_ref(&self) -> &R { 16 | self.inner 17 | } 18 | } 19 | 20 | // ComponentMut 21 | pub struct ComponentMut<'a, R: Replicate> { 22 | inner: &'a mut R, 23 | } 24 | 25 | impl<'a, R: Replicate> ComponentMut<'a, R> { 26 | pub fn new(inner: &'a mut R) -> Self { 27 | Self { inner } 28 | } 29 | } 30 | 31 | impl<'a, R: Replicate> ReplicaRefTrait for ComponentMut<'a, R> { 32 | fn to_ref(&self) -> &R { 33 | self.inner 34 | } 35 | } 36 | 37 | impl<'a, R: Replicate> ReplicaMutTrait for ComponentMut<'a, R> { 38 | fn to_mut(&mut self) -> &mut R { 39 | self.inner 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /demos/demo_utils/demo_world/src/entity.rs: -------------------------------------------------------------------------------- 1 | use naia_shared::BigMapKey; 2 | 3 | // Entity 4 | #[derive(Clone, Copy, Eq, PartialEq, Hash)] 5 | pub struct Entity(u64); 6 | 7 | impl BigMapKey for Entity { 8 | fn to_u64(&self) -> u64 { 9 | self.0 10 | } 11 | 12 | fn from_u64(value: u64) -> Self { 13 | Entity(value) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /demos/demo_utils/demo_world/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use naia_shared::{WorldMutType, WorldRefType}; 2 | 3 | mod component_ref; 4 | mod entity; 5 | mod world; 6 | 7 | pub use entity::Entity; 8 | pub use world::World; 9 | -------------------------------------------------------------------------------- /demos/demo_utils/empty_world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-empty-world" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | homepage = "https://github.com/naia-lib/naia" 6 | repository = "https://github.com/naia-lib/naia" 7 | edition = "2021" 8 | license = "MIT OR Apache-2.0" 9 | publish = false 10 | 11 | [features] 12 | 13 | [dependencies] 14 | naia-shared = { path = "../../../shared" } -------------------------------------------------------------------------------- /demos/hecs/client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-hecs-client-demo" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../.." 6 | edition = "2021" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | 10 | [[bin]] 11 | name = "app" 12 | path = "src/main.rs" 13 | 14 | [lib] 15 | name = "app" 16 | path = "src/lib.rs" 17 | crate-type = ["cdylib", "rlib"] 18 | 19 | [features] 20 | 21 | [dependencies] 22 | naia-hecs-client = { path = "../../../adapters/hecs/client", features = [ "wbindgen", "transport_webrtc" ] } 23 | naia-hecs-demo-shared = { path = "../shared" } 24 | hecs = { version = "0.10" } 25 | log = { version = "0.4" } 26 | cfg-if = { version = "1.0" } 27 | 28 | [target.'cfg(not(target_arch = "wasm32"))'.dependencies] 29 | simple_logger = { version = "4.0", default-features = false, features = ["timestamps"] } 30 | 31 | [target.'cfg(target_arch = "wasm32")'.dependencies] 32 | wasm-logger = { version = "0.2" } 33 | wasm-bindgen = { version = "0.2", features = [ "serde-serialize" ] } 34 | web-sys = { version = "0.3.64", features = [ 'Window' ] } -------------------------------------------------------------------------------- /demos/hecs/client/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.install-target-wasm32-unknown-unknown] 2 | command = "rustup" 3 | args = ["target", "install", "wasm32-unknown-unknown"] 4 | 5 | [tasks.install-basic-http-server] 6 | install_crate = { crate_name = "basic-http-server", binary = "basic-http-server", test_arg="--help" } 7 | 8 | [tasks.install-wasm-bindgen-cli] 9 | install_crate = { crate_name = "wasm-bindgen-cli", binary = "wasm-bindgen", test_arg="--help" } 10 | 11 | [tasks.cargo-build-wasm] 12 | command = "cargo" 13 | args = ["build", "--target", "wasm32-unknown-unknown", "--lib", "--target-dir", "target"] 14 | dependencies = ["install-target-wasm32-unknown-unknown"] 15 | 16 | [tasks.wasm-bindgen] 17 | command = "wasm-bindgen" 18 | args = ["--out-dir", "target", "--out-name", "app", "--target", "web", "--no-typescript", "target/wasm32-unknown-unknown/debug/app.wasm"] 19 | dependencies = ["cargo-build-wasm", "install-wasm-bindgen-cli"] 20 | 21 | [tasks.serve] 22 | command = "basic-http-server" 23 | args = ["-x"] 24 | dependencies = ["wasm-bindgen", "install-basic-http-server"] -------------------------------------------------------------------------------- /demos/hecs/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Naia + Hecs Demo Client 6 | 7 | 8 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /demos/hecs/client/src/app.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use log::info; 4 | 5 | use hecs::Entity; 6 | 7 | use naia_hecs_client::{Client as NaiaClient, ClientConfig, WorldWrapper as World}; 8 | 9 | use naia_hecs_demo_shared::{protocol, Auth}; 10 | 11 | use super::systems::{events::process_events, startup::app_init}; 12 | 13 | pub type Client = NaiaClient; 14 | 15 | pub struct App { 16 | pub client: Client, 17 | pub world: World, 18 | pub message_count: u32, 19 | pub entity_to_id_map: HashMap, 20 | pub next_id: u32, 21 | } 22 | 23 | impl App { 24 | pub fn default() -> Self { 25 | info!("Naia Hecs Client Demo started"); 26 | 27 | app_init( 28 | ClientConfig::default(), 29 | protocol(), 30 | "http://127.0.0.1:14191", 31 | Auth::new("charlie", "12345"), 32 | ) 33 | } 34 | 35 | pub fn update(&mut self) { 36 | process_events(self); 37 | } 38 | 39 | pub fn tick(&mut self) { 40 | //info!("tick"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /demos/hecs/client/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate cfg_if; 3 | 4 | cfg_if! { 5 | if #[cfg(target_arch = "wasm32")] { 6 | 7 | mod app; 8 | mod app_loop; 9 | mod systems; 10 | 11 | use wasm_bindgen::prelude::*; 12 | 13 | use app::App; 14 | use app_loop::start_loop; 15 | 16 | #[wasm_bindgen(start)] 17 | pub fn main() -> Result<(), JsValue> { 18 | wasm_logger::init(wasm_logger::Config::default()); 19 | 20 | start_loop(App::default()); 21 | 22 | Ok(()) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /demos/hecs/client/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate cfg_if; 3 | 4 | cfg_if! { 5 | if #[cfg(not(target_arch = "wasm32"))] { 6 | 7 | mod app; 8 | mod app_loop; 9 | mod systems; 10 | 11 | use crate::app::App; 12 | use app_loop::start_loop; 13 | 14 | fn main() { 15 | simple_logger::SimpleLogger::new() 16 | .with_level(log::LevelFilter::Info) 17 | .init() 18 | .expect("A logger was already initialized"); 19 | 20 | start_loop(App::default()); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /demos/hecs/client/src/systems/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod events; 2 | pub mod startup; 3 | -------------------------------------------------------------------------------- /demos/hecs/client/src/systems/startup.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use hecs::World; 4 | 5 | use naia_hecs_client::{transport::webrtc, ClientConfig, Protocol}; 6 | 7 | use naia_hecs_demo_shared::Auth; 8 | 9 | use crate::app::{App, Client}; 10 | 11 | pub fn app_init( 12 | client_config: ClientConfig, 13 | mut protocol: Protocol, 14 | server_addr: &str, 15 | auth: Auth, 16 | ) -> App { 17 | let world = protocol.wrap_world(World::new()); 18 | 19 | let socket = webrtc::Socket::new(server_addr, protocol.socket_config()); 20 | let mut client = Client::new(client_config, protocol); 21 | client.auth(auth); 22 | client.connect(socket); 23 | 24 | App { 25 | client, 26 | world, 27 | message_count: 0, 28 | entity_to_id_map: HashMap::new(), 29 | next_id: 0, 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /demos/hecs/server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-hecs-server-demo" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../.." 6 | edition = "2021" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | 10 | [dependencies] 11 | naia-hecs-server = { path = "../../../adapters/hecs/server", features = [ "transport_webrtc" ] } 12 | naia-hecs-demo-shared = { path = "../shared" } 13 | log = { version = "0.4" } 14 | simple_logger = { version = "4.0", default-features = false, features = ["timestamps"] } 15 | smol = { version = "1.3" } 16 | hecs = { version = "0.10" } -------------------------------------------------------------------------------- /demos/hecs/server/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | 4 | use log::LevelFilter; 5 | use simple_logger::SimpleLogger; 6 | use smol::io; 7 | 8 | mod app; 9 | mod systems; 10 | 11 | use app::App; 12 | 13 | fn main() -> io::Result<()> { 14 | SimpleLogger::new() 15 | .with_level(LevelFilter::Info) 16 | .init() 17 | .expect("A logger was already initialized"); 18 | 19 | let mut app = App::new(); 20 | loop { 21 | app.update(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /demos/hecs/server/src/systems/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod events; 2 | pub mod startup; 3 | pub mod tick; 4 | -------------------------------------------------------------------------------- /demos/hecs/shared/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-hecs-demo-shared" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../.." 6 | edition = "2021" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | 10 | [features] 11 | 12 | [dependencies] 13 | naia-hecs-shared = { path = "../../../adapters/hecs/shared" } 14 | cfg-if = { version = "1.0" } 15 | log = { version = "0.4" } -------------------------------------------------------------------------------- /demos/hecs/shared/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod protocol; 2 | pub use protocol::{protocol, Auth, Marker, Name, Position}; 3 | -------------------------------------------------------------------------------- /demos/hecs/shared/src/protocol.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use naia_hecs_shared::{LinkConditionerConfig, Protocol}; 4 | 5 | mod auth; 6 | mod marker; 7 | mod name; 8 | mod position; 9 | 10 | pub use auth::Auth; 11 | pub use marker::Marker; 12 | pub use name::Name; 13 | pub use position::Position; 14 | 15 | // Protocol Build 16 | pub fn protocol() -> Protocol { 17 | Protocol::builder() 18 | // Config 19 | .tick_interval(Duration::from_millis(25)) 20 | .link_condition(LinkConditionerConfig::average_condition()) 21 | // Channels 22 | .add_default_channels() 23 | // Messages 24 | .add_message::() 25 | // Components 26 | .add_component::() 27 | .add_component::() 28 | .add_component::() 29 | // Build Protocol 30 | .build() 31 | } 32 | -------------------------------------------------------------------------------- /demos/hecs/shared/src/protocol/auth.rs: -------------------------------------------------------------------------------- 1 | use naia_hecs_shared::Message; 2 | 3 | #[derive(Message)] 4 | pub struct Auth { 5 | pub username: String, 6 | pub password: String, 7 | } 8 | 9 | impl Auth { 10 | pub fn new(username: &str, password: &str) -> Self { 11 | Self { 12 | username: username.to_string(), 13 | password: password.to_string(), 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /demos/hecs/shared/src/protocol/marker.rs: -------------------------------------------------------------------------------- 1 | use naia_hecs_shared::Replicate; 2 | 3 | #[derive(Replicate)] 4 | pub struct Marker; 5 | 6 | impl Marker { 7 | pub fn new() -> Self { 8 | Self::new_complete() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /demos/hecs/shared/src/protocol/name.rs: -------------------------------------------------------------------------------- 1 | use naia_hecs_shared::{Property, Replicate, Serde}; 2 | 3 | // Here's an example of a Custom Property 4 | #[derive(Serde, PartialEq, Clone)] 5 | pub struct Fullname { 6 | pub first: String, 7 | pub last: String, 8 | } 9 | 10 | #[derive(Replicate)] 11 | pub struct Name { 12 | pub full: Property, 13 | } 14 | 15 | impl Name { 16 | pub fn new(first: &str, last: &str) -> Self { 17 | Self::new_complete(Fullname { 18 | first: first.to_string(), 19 | last: last.to_string(), 20 | }) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /demos/hecs/shared/src/protocol/position.rs: -------------------------------------------------------------------------------- 1 | use naia_hecs_shared::{Property, Replicate}; 2 | 3 | #[derive(Replicate)] 4 | pub struct Position { 5 | pub x: Property, 6 | pub y: Property, 7 | } 8 | 9 | impl Position { 10 | pub fn new(x: u8, y: u8) -> Self { 11 | Self::new_complete(x, y) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /demos/macroquad/client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-macroquad-client-demo" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../.." 6 | edition = "2021" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | 10 | [[bin]] 11 | name = "app" 12 | path = "src/main.rs" 13 | 14 | [dependencies] 15 | naia-client = { path = "../../../client", features = [ "mquad", "transport_webrtc" ] } 16 | naia-macroquad-demo-shared = { path = "../shared" } 17 | naia-demo-world = { path = "../../demo_utils/demo_world" } 18 | macroquad = { version = "0.3.24" } -------------------------------------------------------------------------------- /demos/macroquad/client/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.install-target-wasm32-unknown-unknown] 2 | command = "rustup" 3 | args = ["target", "install", "wasm32-unknown-unknown"] 4 | 5 | [tasks.install-basic-http-server] 6 | install_crate = {crate_name = "basic-http-server", binary = "basic-http-server", test_arg="--help"} 7 | 8 | [tasks.cargo-build-wasm] 9 | command = "cargo" 10 | args = ["build", "--target", "wasm32-unknown-unknown", "--target-dir", "target"] 11 | dependencies = ["install-target-wasm32-unknown-unknown"] 12 | 13 | [tasks.delete-old-wasm] 14 | command = "rm" 15 | args = ["-f", "target/app.wasm"] 16 | dependencies = ["cargo-build-wasm"] 17 | 18 | [tasks.move-wasm] 19 | command = "mv" 20 | args = ["target/wasm32-unknown-unknown/debug/app.wasm", "target/app.wasm"] 21 | dependencies = ["delete-old-wasm"] 22 | 23 | [tasks.serve] 24 | command = "basic-http-server" 25 | args = ["-x"] 26 | dependencies = ["move-wasm", "install-basic-http-server"] 27 | -------------------------------------------------------------------------------- /demos/macroquad/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Naia Macroquad Client Demo 6 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /demos/macroquad/client/src/interp.rs: -------------------------------------------------------------------------------- 1 | pub struct Interp { 2 | interp: f32, 3 | pub interp_x: f32, 4 | pub interp_y: f32, 5 | 6 | last_x: f32, 7 | last_y: f32, 8 | pub next_x: f32, 9 | pub next_y: f32, 10 | } 11 | 12 | impl Interp { 13 | pub fn new(x: i16, y: i16) -> Self { 14 | let x = x as f32; 15 | let y = y as f32; 16 | Self { 17 | interp: 0.0, 18 | interp_x: x, 19 | interp_y: y, 20 | 21 | last_x: x, 22 | last_y: y, 23 | next_x: x, 24 | next_y: y, 25 | } 26 | } 27 | 28 | pub(crate) fn next_position(&mut self, next_x: i16, next_y: i16) { 29 | self.interp = 0.0; 30 | self.last_x = self.next_x; 31 | self.last_y = self.next_y; 32 | self.interp_x = self.next_x; 33 | self.interp_y = self.next_y; 34 | self.next_x = next_x as f32; 35 | self.next_y = next_y as f32; 36 | } 37 | 38 | pub(crate) fn interpolate(&mut self, interpolation: f32) { 39 | if self.interp >= 1.0 || interpolation == 0.0 { 40 | return; 41 | } 42 | if self.interp < interpolation { 43 | self.interp = interpolation; 44 | self.interp_x = self.last_x + (self.next_x - self.last_x) * self.interp; 45 | self.interp_y = self.last_y + (self.next_y - self.last_y) * self.interp; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /demos/macroquad/client/src/main.rs: -------------------------------------------------------------------------------- 1 | use macroquad::prelude::*; 2 | 3 | mod app; 4 | mod interp; 5 | 6 | use app::App; 7 | 8 | #[macroquad::main("NaiaMacroquadDemo")] 9 | async fn main() { 10 | let mut app = App::new(); 11 | 12 | loop { 13 | app.update(); 14 | 15 | next_frame().await 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /demos/macroquad/server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-macroquad-server-demo" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../.." 6 | edition = "2021" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | 10 | [dependencies] 11 | naia-server = { path = "../../../server", features = [ "transport_webrtc" ] } 12 | naia-demo-world = { path = "../../demo_utils/demo_world" } 13 | naia-macroquad-demo-shared = { path = "../shared" } 14 | log = { version = "0.4" } 15 | simple_logger = { version = "4.0", default-features = false, features = ["timestamps"] } -------------------------------------------------------------------------------- /demos/macroquad/server/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | extern crate naia_macroquad_demo_shared; 4 | 5 | use log::LevelFilter; 6 | use simple_logger::SimpleLogger; 7 | 8 | mod app; 9 | use app::App; 10 | 11 | fn main() { 12 | SimpleLogger::new() 13 | .with_level(LevelFilter::Info) 14 | .init() 15 | .expect("A logger was already initialized"); 16 | 17 | let mut app = App::new(); 18 | loop { 19 | app.update(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /demos/macroquad/shared/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-macroquad-demo-shared" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../.." 6 | edition = "2021" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | 10 | [features] 11 | 12 | [dependencies] 13 | naia-shared = { path = "../../../shared", features = ["mquad"] } 14 | cfg-if = { version = "1.0" } 15 | log = { version = "0.4" } -------------------------------------------------------------------------------- /demos/macroquad/shared/src/behavior/mod.rs: -------------------------------------------------------------------------------- 1 | mod process_command; 2 | 3 | pub use process_command::process_command; 4 | -------------------------------------------------------------------------------- /demos/macroquad/shared/src/behavior/process_command.rs: -------------------------------------------------------------------------------- 1 | use crate::{components::Position, messages::KeyCommand}; 2 | 3 | const SQUARE_SPEED: i16 = 4; 4 | 5 | pub fn process_command(key_command: &KeyCommand, position: &mut Position) { 6 | if key_command.w { 7 | *position.y = position.y.wrapping_sub(SQUARE_SPEED); 8 | } 9 | if key_command.s { 10 | *position.y = position.y.wrapping_add(SQUARE_SPEED); 11 | } 12 | if key_command.a { 13 | *position.x = position.x.wrapping_sub(SQUARE_SPEED); 14 | } 15 | if key_command.d { 16 | *position.x = position.x.wrapping_add(SQUARE_SPEED); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /demos/macroquad/shared/src/channels.rs: -------------------------------------------------------------------------------- 1 | use naia_shared::{ 2 | Channel, ChannelDirection, ChannelMode, Protocol, ProtocolPlugin, ReliableSettings, 3 | TickBufferSettings, 4 | }; 5 | 6 | #[derive(Channel)] 7 | pub struct PlayerCommandChannel; 8 | 9 | #[derive(Channel)] 10 | pub struct EntityAssignmentChannel; 11 | 12 | // Plugin 13 | pub struct ChannelsPlugin; 14 | 15 | impl ProtocolPlugin for ChannelsPlugin { 16 | fn build(&self, protocol: &mut Protocol) { 17 | protocol 18 | .add_channel::( 19 | ChannelDirection::ClientToServer, 20 | ChannelMode::TickBuffered(TickBufferSettings::default()), 21 | ) 22 | .add_channel::( 23 | ChannelDirection::ServerToClient, 24 | ChannelMode::UnorderedReliable(ReliableSettings::default()), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /demos/macroquad/shared/src/components/color.rs: -------------------------------------------------------------------------------- 1 | use naia_shared::{Property, Replicate, Serde}; 2 | 3 | #[derive(Serde, PartialEq, Clone)] 4 | pub enum ColorValue { 5 | Red, 6 | Blue, 7 | Yellow, 8 | Green, 9 | } 10 | 11 | #[derive(Replicate)] 12 | pub struct Color { 13 | pub value: Property, 14 | } 15 | 16 | impl Color { 17 | pub fn new(value: ColorValue) -> Self { 18 | Self::new_complete(value) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /demos/macroquad/shared/src/components/mod.rs: -------------------------------------------------------------------------------- 1 | use naia_shared::{Protocol, ProtocolPlugin}; 2 | 3 | mod color; 4 | mod position; 5 | mod shape; 6 | 7 | pub use color::{Color, ColorValue}; 8 | pub use position::Position; 9 | pub use shape::{Shape, ShapeValue}; 10 | 11 | // Plugin 12 | pub struct ComponentsPlugin; 13 | 14 | impl ProtocolPlugin for ComponentsPlugin { 15 | fn build(&self, protocol: &mut Protocol) { 16 | protocol 17 | .add_component::() 18 | .add_component::() 19 | .add_component::(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /demos/macroquad/shared/src/components/position.rs: -------------------------------------------------------------------------------- 1 | use naia_shared::{Property, Replicate}; 2 | 3 | #[derive(Replicate)] 4 | pub struct Position { 5 | pub x: Property, 6 | pub y: Property, 7 | } 8 | 9 | impl Position { 10 | pub fn new(x: i16, y: i16) -> Self { 11 | Self::new_complete(x, y) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /demos/macroquad/shared/src/components/shape.rs: -------------------------------------------------------------------------------- 1 | use naia_shared::{Property, Replicate, Serde}; 2 | 3 | #[derive(Serde, PartialEq, Clone)] 4 | pub enum ShapeValue { 5 | Square, 6 | Circle, 7 | } 8 | 9 | #[derive(Replicate)] 10 | pub struct Shape { 11 | pub value: Property, 12 | } 13 | 14 | impl Shape { 15 | pub fn new(value: ShapeValue) -> Self { 16 | Self::new_complete(value) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /demos/macroquad/shared/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod behavior; 2 | pub mod channels; 3 | pub mod components; 4 | pub mod messages; 5 | 6 | mod protocol; 7 | pub use protocol::protocol; 8 | -------------------------------------------------------------------------------- /demos/macroquad/shared/src/messages/auth.rs: -------------------------------------------------------------------------------- 1 | use naia_shared::Message; 2 | 3 | #[derive(Message)] 4 | pub struct Auth { 5 | pub username: String, 6 | pub password: String, 7 | } 8 | 9 | impl Auth { 10 | pub fn new(username: &str, password: &str) -> Self { 11 | Self { 12 | username: username.to_string(), 13 | password: password.to_string(), 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /demos/macroquad/shared/src/messages/entity_assignment.rs: -------------------------------------------------------------------------------- 1 | use naia_shared::{EntityProperty, Message}; 2 | 3 | #[derive(Message)] 4 | pub struct EntityAssignment { 5 | pub entity: EntityProperty, 6 | pub assign: bool, 7 | } 8 | 9 | impl EntityAssignment { 10 | pub fn new(assign: bool) -> Self { 11 | Self { 12 | assign, 13 | entity: EntityProperty::new(), 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /demos/macroquad/shared/src/messages/key_command.rs: -------------------------------------------------------------------------------- 1 | use naia_shared::{EntityProperty, Message}; 2 | 3 | #[derive(Message)] 4 | pub struct KeyCommand { 5 | pub entity: EntityProperty, 6 | pub w: bool, 7 | pub s: bool, 8 | pub a: bool, 9 | pub d: bool, 10 | } 11 | 12 | impl KeyCommand { 13 | pub fn new(w: bool, s: bool, a: bool, d: bool) -> Self { 14 | Self { 15 | w, 16 | s, 17 | a, 18 | d, 19 | entity: EntityProperty::new(), 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /demos/macroquad/shared/src/messages/mod.rs: -------------------------------------------------------------------------------- 1 | use naia_shared::{Protocol, ProtocolPlugin}; 2 | 3 | mod auth; 4 | mod entity_assignment; 5 | mod key_command; 6 | 7 | pub use auth::Auth; 8 | pub use entity_assignment::EntityAssignment; 9 | pub use key_command::KeyCommand; 10 | 11 | // Plugin 12 | pub struct MessagesPlugin; 13 | 14 | impl ProtocolPlugin for MessagesPlugin { 15 | fn build(&self, protocol: &mut Protocol) { 16 | protocol 17 | .add_message::() 18 | .add_message::() 19 | .add_message::(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /demos/macroquad/shared/src/protocol.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use naia_shared::{LinkConditionerConfig, Protocol}; 4 | 5 | use crate::{channels::ChannelsPlugin, components::ComponentsPlugin, messages::MessagesPlugin}; 6 | 7 | // Protocol Build 8 | pub fn protocol() -> Protocol { 9 | Protocol::builder() 10 | // Config 11 | .tick_interval(Duration::from_millis(40)) 12 | .link_condition(LinkConditionerConfig::good_condition()) 13 | .enable_client_authoritative_entities() 14 | // Channels 15 | .add_plugin(ChannelsPlugin) 16 | // Messages 17 | .add_plugin(MessagesPlugin) 18 | // Components 19 | .add_plugin(ComponentsPlugin) 20 | // Build Protocol 21 | .build() 22 | } 23 | -------------------------------------------------------------------------------- /demos/socket/client/app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-socket-client-demo-app" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../../.." 6 | edition = "2021" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | 10 | [features] 11 | mquad = [ "naia-client-socket/mquad", "naia-socket-demo-shared/mquad", "naia-shared/mquad", "miniquad" ] 12 | wbindgen = [ "naia-client-socket/wbindgen", "naia-socket-demo-shared/wbindgen", "naia-shared/wbindgen", "log" ] 13 | 14 | [dependencies] 15 | naia-client-socket = { path = "../../../../socket/client" } 16 | naia-socket-demo-shared = { path = "../../shared" } 17 | naia-shared = { path = "../../../../shared" } 18 | cfg-if = { version = "1.0" } 19 | log = { version = "0.4", optional = true } 20 | miniquad = { version = "0.3", features = ["log-impl"], optional = true } -------------------------------------------------------------------------------- /demos/socket/client/app/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate cfg_if; 3 | 4 | mod app; 5 | pub use app::App; 6 | -------------------------------------------------------------------------------- /demos/socket/client/miniquad/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-socket-client-demo-mq" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../../.." 6 | edition = "2021" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | 10 | [[bin]] 11 | name = "app" 12 | path = "src/main.rs" 13 | 14 | [features] 15 | 16 | [dependencies] 17 | naia-socket-client-demo-app = { path = "../app", features = [ "mquad" ] } 18 | cfg-if = { version = "1.0" } 19 | miniquad = { version = "0.3", features = ["log-impl"] } -------------------------------------------------------------------------------- /demos/socket/client/miniquad/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.install-target-wasm32-unknown-unknown] 2 | command = "rustup" 3 | args = ["target", "install", "wasm32-unknown-unknown"] 4 | 5 | [tasks.install-basic-http-server] 6 | install_crate = {crate_name = "basic-http-server", binary = "basic-http-server", test_arg="--help"} 7 | 8 | [tasks.cargo-build-wasm] 9 | command = "cargo" 10 | args = ["build", "--target", "wasm32-unknown-unknown", "--target-dir", "target"] 11 | dependencies = ["install-target-wasm32-unknown-unknown"] 12 | 13 | [tasks.delete-old-wasm] 14 | command = "rm" 15 | args = ["-f", "target/app.wasm"] 16 | dependencies = ["cargo-build-wasm"] 17 | 18 | [tasks.move-wasm] 19 | command = "mv" 20 | args = ["target/wasm32-unknown-unknown/debug/app.wasm", "target/app.wasm"] 21 | dependencies = ["delete-old-wasm"] 22 | 23 | [tasks.serve] 24 | command = "basic-http-server" 25 | args = ["-x"] 26 | dependencies = ["move-wasm", "install-basic-http-server"] 27 | -------------------------------------------------------------------------------- /demos/socket/client/miniquad/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Naia Socket Demo Client 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /demos/socket/client/miniquad/src/main.rs: -------------------------------------------------------------------------------- 1 | use miniquad::*; 2 | 3 | use naia_socket_client_demo_app::App; 4 | 5 | struct Stage { 6 | app: App, 7 | } 8 | impl EventHandler for Stage { 9 | fn update(&mut self, _ctx: &mut Context) { 10 | self.app.update(); 11 | } 12 | 13 | fn draw(&mut self, ctx: &mut Context) { 14 | ctx.clear(Some((0., 1., 0., 1.)), None, None); 15 | } 16 | } 17 | 18 | fn main() { 19 | let app = App::new(); 20 | miniquad::start(conf::Conf::default(), |_ctx| Box::new(Stage { app })); 21 | } 22 | -------------------------------------------------------------------------------- /demos/socket/client/wasm_bindgen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-socket-client-demo-wb" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../../.." 6 | edition = "2021" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | 10 | [[bin]] 11 | name = "app" 12 | path = "src/main.rs" 13 | 14 | [lib] 15 | name = "app" 16 | path = "src/lib.rs" 17 | crate-type = ["cdylib", "rlib"] 18 | 19 | [features] 20 | 21 | [dependencies] 22 | naia-socket-client-demo-app = { path = "../app", features = [ "wbindgen" ] } 23 | log = { version = "0.4" } 24 | cfg-if = { version = "1.0" } 25 | 26 | [target.'cfg(not(target_arch = "wasm32"))'.dependencies] 27 | simple_logger = { version = "4.0", default-features = false, features = ["timestamps"] } 28 | 29 | [target.'cfg(target_arch = "wasm32")'.dependencies] 30 | wasm-logger = { version = "0.2" } 31 | wasm-bindgen = { version = "0.2", features = [ "serde-serialize" ] } 32 | web-sys = { version = "0.3.64", features = [ 'Window' ] } -------------------------------------------------------------------------------- /demos/socket/client/wasm_bindgen/Makefile.toml: -------------------------------------------------------------------------------- 1 | [tasks.install-target-wasm32-unknown-unknown] 2 | command = "rustup" 3 | args = ["target", "install", "wasm32-unknown-unknown"] 4 | 5 | [tasks.install-basic-http-server] 6 | install_crate = { crate_name = "basic-http-server", binary = "basic-http-server", test_arg="--help" } 7 | 8 | [tasks.install-wasm-bindgen-cli] 9 | install_crate = { crate_name = "wasm-bindgen-cli", binary = "wasm-bindgen", test_arg="--help" } 10 | 11 | [tasks.cargo-build-wasm] 12 | command = "cargo" 13 | args = ["build", "--target", "wasm32-unknown-unknown", "--lib", "--target-dir", "target"] 14 | dependencies = ["install-target-wasm32-unknown-unknown"] 15 | 16 | [tasks.wasm-bindgen] 17 | command = "wasm-bindgen" 18 | args = ["--out-dir", "target", "--out-name", "app", "--target", "web", "--no-typescript", "target/wasm32-unknown-unknown/debug/app.wasm"] 19 | dependencies = ["cargo-build-wasm", "install-wasm-bindgen-cli"] 20 | 21 | [tasks.serve] 22 | command = "basic-http-server" 23 | args = ["-x"] 24 | dependencies = ["wasm-bindgen", "install-basic-http-server"] -------------------------------------------------------------------------------- /demos/socket/client/wasm_bindgen/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Naia Socket Demo Client 6 | 7 | 8 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /demos/socket/client/wasm_bindgen/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate cfg_if; 3 | 4 | cfg_if! { 5 | if #[cfg(target_arch = "wasm32")] { 6 | 7 | mod app_loop; 8 | 9 | use wasm_bindgen::prelude::*; 10 | 11 | use naia_socket_client_demo_app::App; 12 | 13 | use app_loop::start_loop; 14 | 15 | #[wasm_bindgen(start)] 16 | pub fn main() -> Result<(), JsValue> { 17 | wasm_logger::init(wasm_logger::Config::default()); 18 | 19 | start_loop(App::new()); 20 | 21 | Ok(()) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /demos/socket/client/wasm_bindgen/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate cfg_if; 3 | 4 | cfg_if! { 5 | if #[cfg(not(target_arch = "wasm32"))] { 6 | 7 | extern crate log; 8 | 9 | mod app_loop; 10 | 11 | use naia_socket_client_demo_app::App; 12 | 13 | use app_loop::start_loop; 14 | 15 | fn main() { 16 | simple_logger::SimpleLogger::new() 17 | .with_level(log::LevelFilter::Info) 18 | .init() 19 | .expect("A logger was already initialized"); 20 | 21 | start_loop(App::new()); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /demos/socket/server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-server-socket-demo" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../.." 6 | license = "MIT OR Apache-2.0" 7 | edition = "2021" 8 | publish = false 9 | 10 | [dependencies] 11 | naia-server-socket = { path = "../../../socket/server" } 12 | naia-socket-demo-shared = { path = "../shared" } 13 | log = { version = "0.4" } 14 | simple_logger = { version = "4.0", default-features = false, features = ["timestamps"] } -------------------------------------------------------------------------------- /demos/socket/server/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | 4 | use log::LevelFilter; 5 | use simple_logger::SimpleLogger; 6 | 7 | mod app; 8 | use app::App; 9 | 10 | fn main() { 11 | SimpleLogger::new() 12 | .with_level(LevelFilter::Info) 13 | .init() 14 | .expect("A logger was already initialized"); 15 | 16 | let mut app = App::new(); 17 | loop { 18 | app.update(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /demos/socket/shared/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-socket-demo-shared" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../../.." 6 | edition = "2021" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | 10 | [features] 11 | wbindgen = [ "naia-socket-shared/wbindgen" ] 12 | mquad = [ "naia-socket-shared/mquad" ] 13 | 14 | [dependencies] 15 | naia-socket-shared = { path = "../../../socket/shared" } -------------------------------------------------------------------------------- /demos/socket/shared/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod shared; 2 | pub use shared::{shared_config, PING_MSG, PONG_MSG}; 3 | -------------------------------------------------------------------------------- /demos/socket/shared/src/shared.rs: -------------------------------------------------------------------------------- 1 | use naia_socket_shared::{LinkConditionerConfig, SocketConfig}; 2 | 3 | pub const PING_MSG: &str = "PING"; 4 | pub const PONG_MSG: &str = "PONG"; 5 | 6 | pub fn shared_config() -> SocketConfig { 7 | //let link_condition = None; 8 | let link_condition = Some(LinkConditionerConfig::average_condition()); 9 | // let link_condition = Some(LinkConditionerConfig { 10 | // incoming_latency: 500, 11 | // incoming_jitter: 1, 12 | // incoming_loss: 0.0, 13 | // incoming_corruption: 0.0 14 | // }); 15 | 16 | SocketConfig::new(link_condition, None) 17 | } 18 | -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | [advisories] 2 | unmaintained = "deny" 3 | yanked = "deny" 4 | notice = "deny" 5 | 6 | [licenses] 7 | copyleft = "deny" 8 | allow-osi-fsf-free = "either" 9 | 10 | [[licenses.clarify]] 11 | name = "stretch" 12 | expression = "MIT" 13 | license-files = [] 14 | 15 | [[licenses.clarify]] 16 | name = "ring" 17 | expression = "MIT AND ISC AND OpenSSL" 18 | license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] 19 | 20 | [bans] 21 | multiple-versions = "allow" 22 | wildcards = "allow" 23 | 24 | [sources] 25 | unknown-registry = "deny" 26 | unknown-git = "deny" -------------------------------------------------------------------------------- /rustfmt.yml: -------------------------------------------------------------------------------- 1 | newline_style = "Unix" 2 | use_field_init_shorthand = true 3 | 4 | # The following lines may be uncommented on nightly Rust. 5 | # Once these features have stabilized, they should be added to the always-enabled options above. 6 | # reorder_impl_items = true 7 | # format_strings = false 8 | # imports_granularity='Crate' 9 | # unstable_features = true 10 | # wrap_comments = true 11 | # comment_width = 100 12 | # normalize_comments = true 13 | -------------------------------------------------------------------------------- /server/src/connection/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bandwidth_monitor; 2 | pub mod connection; 3 | pub mod io; 4 | pub mod ping_config; 5 | pub mod ping_manager; 6 | pub mod tick_buffer_messages; 7 | pub mod tick_buffer_receiver; 8 | pub mod tick_buffer_receiver_channel; 9 | -------------------------------------------------------------------------------- /server/src/connection/tick_buffer_messages.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use naia_shared::{Channel, ChannelKind, Message, MessageContainer, MessageKind}; 4 | 5 | use crate::{events, UserKey}; 6 | 7 | pub struct TickBufferMessages { 8 | messages: HashMap>>, 9 | empty: bool, 10 | } 11 | 12 | impl TickBufferMessages { 13 | pub fn new() -> Self { 14 | Self { 15 | messages: HashMap::new(), 16 | empty: true, 17 | } 18 | } 19 | 20 | pub(crate) fn push_message( 21 | &mut self, 22 | user_key: &UserKey, 23 | channel_kind: &ChannelKind, 24 | message: MessageContainer, 25 | ) { 26 | events::push_message_impl(&mut self.messages, user_key, channel_kind, message); 27 | self.empty = false; 28 | } 29 | 30 | pub fn read(&mut self) -> Vec<(UserKey, M)> { 31 | return events::read_channel_messages::(&mut self.messages); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /server/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::{error::Error, fmt, net::SocketAddr}; 2 | 3 | #[derive(Debug)] 4 | pub enum NaiaServerError { 5 | Message(String), 6 | Wrapped(Box), 7 | SendError(SocketAddr), 8 | RecvError, 9 | } 10 | 11 | impl NaiaServerError { 12 | pub fn from_message(message: &str) -> Self { 13 | Self::Message(message.to_string()) 14 | } 15 | } 16 | 17 | impl fmt::Display for NaiaServerError { 18 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 19 | match self { 20 | NaiaServerError::Message(msg) => write!(f, "Naia Server Error: {}", msg), 21 | NaiaServerError::Wrapped(boxed_err) => fmt::Display::fmt(boxed_err.as_ref(), f), 22 | NaiaServerError::SendError(address) => { 23 | write!(f, "Naia Server Error: SendError: {}", address) 24 | } 25 | NaiaServerError::RecvError => { 26 | write!(f, "Naia Server Error: RecvError") 27 | } 28 | } 29 | } 30 | } 31 | 32 | impl Error for NaiaServerError {} 33 | unsafe impl Send for NaiaServerError {} 34 | unsafe impl Sync for NaiaServerError {} 35 | -------------------------------------------------------------------------------- /server/src/handshake/cache_map.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::{HashMap, VecDeque}, 3 | hash::Hash, 4 | }; 5 | 6 | pub struct CacheMap { 7 | map: HashMap, 8 | keys: VecDeque, 9 | } 10 | 11 | impl CacheMap { 12 | pub fn with_capacity(capacity: usize) -> Self { 13 | Self { 14 | map: HashMap::with_capacity(capacity), 15 | keys: VecDeque::with_capacity(capacity), 16 | } 17 | } 18 | 19 | pub fn contains_key(&self, key: &K) -> bool { 20 | self.map.contains_key(key) 21 | } 22 | 23 | pub fn get_unchecked(&self, key: &K) -> &V { 24 | self.map 25 | .get(key) 26 | .expect("need to call contains_key() first to make sure this panic won't happen!") 27 | } 28 | 29 | pub fn insert(&mut self, key: K, value: V) { 30 | if self.keys.len() == self.keys.capacity() { 31 | // need to make room for other keys 32 | let popped_key = self.keys.pop_front().unwrap(); 33 | self.map.remove(&popped_key); 34 | } 35 | 36 | self.keys.push_back(key.clone()); 37 | self.map.insert(key, value); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /server/src/handshake/mod.rs: -------------------------------------------------------------------------------- 1 | use std::net::SocketAddr; 2 | 3 | use naia_shared::{BitReader, IdentityToken, OutgoingPacket, SerdeErr}; 4 | 5 | use crate::UserKey; 6 | 7 | cfg_if! { 8 | if #[cfg(feature = "transport_udp")] { 9 | mod cache_map; 10 | 11 | mod advanced_handshaker; 12 | pub use advanced_handshaker::HandshakeManager; 13 | } else { 14 | mod simple_handshaker; 15 | pub use simple_handshaker::HandshakeManager; 16 | } 17 | } 18 | 19 | pub trait Handshaker: Send + Sync { 20 | fn authenticate_user(&mut self, identity_token: &IdentityToken, user_key: &UserKey); 21 | 22 | // address is optional because user may not have been identified yet 23 | fn delete_user(&mut self, user_key: &UserKey, address_opt: Option); 24 | 25 | fn maintain_handshake( 26 | &mut self, 27 | address: &SocketAddr, 28 | reader: &mut BitReader, 29 | has_connection: bool, 30 | ) -> Result; 31 | } 32 | 33 | pub enum HandshakeAction { 34 | None, 35 | FinalizeConnection(UserKey, OutgoingPacket), 36 | SendPacket(OutgoingPacket), 37 | DisconnectUser(UserKey), 38 | } 39 | -------------------------------------------------------------------------------- /server/src/server_config.rs: -------------------------------------------------------------------------------- 1 | use std::default::Default; 2 | 3 | use naia_shared::ConnectionConfig; 4 | 5 | use crate::connection::ping_config::PingConfig; 6 | 7 | /// Contains Config properties which will be used by the Server 8 | #[derive(Clone)] 9 | pub struct ServerConfig { 10 | /// Used to configure the connections with Clients 11 | pub connection: ConnectionConfig, 12 | /// Determines whether to require that the Client send some auth message 13 | /// in order to connect. 14 | pub require_auth: bool, 15 | /// Configuration used to monitor the ping & jitter on the network 16 | pub ping: PingConfig, 17 | } 18 | 19 | impl Default for ServerConfig { 20 | fn default() -> Self { 21 | Self { 22 | connection: ConnectionConfig::default(), 23 | require_auth: true, 24 | ping: PingConfig::default(), 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server/src/world/entity_owner.rs: -------------------------------------------------------------------------------- 1 | use crate::UserKey; 2 | 3 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 4 | pub enum EntityOwner { 5 | Server, 6 | Client(UserKey), 7 | ClientWaiting(UserKey), 8 | ClientPublic(UserKey), 9 | Local, 10 | } 11 | 12 | impl EntityOwner { 13 | pub fn is_server(&self) -> bool { 14 | match self { 15 | EntityOwner::Server => true, 16 | _ => false, 17 | } 18 | } 19 | 20 | pub fn is_client(&self) -> bool { 21 | match self { 22 | EntityOwner::Client(_) 23 | | EntityOwner::ClientPublic(_) 24 | | EntityOwner::ClientWaiting(_) => true, 25 | _ => false, 26 | } 27 | } 28 | 29 | pub fn is_public(&self) -> bool { 30 | match self { 31 | EntityOwner::ClientPublic(_) | EntityOwner::Server => true, 32 | _ => false, 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /server/src/world/entity_ref.rs: -------------------------------------------------------------------------------- 1 | use std::hash::Hash; 2 | 3 | use naia_shared::{EntityAuthStatus, ReplicaRefWrapper, ReplicatedComponent, WorldRefType}; 4 | 5 | use crate::{ReplicationConfig, Server}; 6 | 7 | // EntityRef 8 | pub struct EntityRef<'s, E: Copy + Eq + Hash + Send + Sync, W: WorldRefType> { 9 | server: &'s Server, 10 | world: W, 11 | entity: E, 12 | } 13 | 14 | impl<'s, E: Copy + Eq + Hash + Send + Sync, W: WorldRefType> EntityRef<'s, E, W> { 15 | pub fn new(server: &'s Server, world: W, entity: &E) -> Self { 16 | EntityRef { 17 | server, 18 | world, 19 | entity: *entity, 20 | } 21 | } 22 | 23 | pub fn id(&self) -> E { 24 | self.entity 25 | } 26 | 27 | pub fn has_component(&self) -> bool { 28 | self.world.has_component::(&self.entity) 29 | } 30 | 31 | pub fn component(&self) -> Option> { 32 | self.world.component::(&self.entity) 33 | } 34 | 35 | pub fn replication_config(&self) -> Option { 36 | self.server.entity_replication_config(&self.entity) 37 | } 38 | 39 | pub fn authority(&self) -> Option { 40 | self.server.entity_authority_status(&self.entity) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /server/src/world/global_entity_record.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use naia_shared::{ComponentKind, GlobalEntity}; 4 | 5 | use crate::{EntityOwner, ReplicationConfig}; 6 | 7 | pub struct GlobalEntityRecord { 8 | pub global_entity: GlobalEntity, 9 | pub component_kinds: HashSet, 10 | pub owner: EntityOwner, 11 | pub replication_config: ReplicationConfig, 12 | pub is_replicating: bool, 13 | } 14 | 15 | impl GlobalEntityRecord { 16 | pub fn new(global_entity: GlobalEntity, owner: EntityOwner) -> Self { 17 | let replication_config = match &owner { 18 | EntityOwner::Server => ReplicationConfig::Public, 19 | EntityOwner::Client(_) | EntityOwner::ClientWaiting(_) => ReplicationConfig::Private, 20 | EntityOwner::ClientPublic(_) => { 21 | panic!("Should not be able to insert a ClientPublic record this way"); 22 | } 23 | EntityOwner::Local => { 24 | panic!("Should not be able to insert Local entity in this record"); 25 | } 26 | }; 27 | Self { 28 | global_entity, 29 | component_kinds: HashSet::new(), 30 | owner, 31 | replication_config, 32 | is_replicating: true, 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /server/src/world/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod entity_mut; 2 | pub mod entity_owner; 3 | pub mod entity_ref; 4 | pub mod entity_room_map; 5 | pub mod entity_scope_map; 6 | pub mod global_entity_record; 7 | pub mod global_world_manager; 8 | pub mod mut_channel; 9 | pub mod replication_config; 10 | pub mod server_auth_handler; 11 | -------------------------------------------------------------------------------- /server/src/world/mut_channel.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, net::SocketAddr}; 2 | 3 | use naia_shared::{MutChannelType, MutReceiver}; 4 | 5 | pub struct MutChannelData { 6 | receiver_map: HashMap, 7 | diff_mask_length: u8, 8 | } 9 | 10 | impl MutChannelData { 11 | pub fn new(diff_mask_length: u8) -> Self { 12 | Self { 13 | receiver_map: HashMap::new(), 14 | diff_mask_length, 15 | } 16 | } 17 | } 18 | 19 | impl MutChannelType for MutChannelData { 20 | fn new_receiver(&mut self, address_opt: &Option) -> Option { 21 | let address = address_opt.expect("cannot initialize receiver without address"); 22 | if let Some(receiver) = self.receiver_map.get(&address) { 23 | Some(receiver.clone()) 24 | } else { 25 | let receiver = MutReceiver::new(self.diff_mask_length); 26 | self.receiver_map.insert(address, receiver.clone()); 27 | 28 | Some(receiver) 29 | } 30 | } 31 | 32 | fn send(&self, diff: u8) { 33 | for (_, receiver) in self.receiver_map.iter() { 34 | receiver.mutate(diff); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /server/src/world/replication_config.rs: -------------------------------------------------------------------------------- 1 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 2 | pub enum ReplicationConfig { 3 | Private, // this is for Client non-Public Entities 4 | Public, // this is for Server Entities and Public Client Entities 5 | Delegated, // this is for Server Delegated Entities 6 | } 7 | -------------------------------------------------------------------------------- /shared/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-shared" 3 | version = "0.24.0" 4 | authors = ["connorcarpenter "] 5 | workspace = ".." 6 | description = "Common functionality shared between naia-server & naia-client crates" 7 | documentation = "https://docs.rs/naia-shared" 8 | homepage = "https://github.com/naia-lib/naia" 9 | repository = "https://github.com/naia-lib/naia" 10 | readme = "../README.md" 11 | keywords = ["webrtc", "udp", "wasm", "networking", "gamedev"] 12 | categories = ["network-programming", "game-development", "wasm", "web-programming"] 13 | license = "MIT OR Apache-2.0" 14 | edition = "2021" 15 | 16 | [badges] 17 | maintenance = { status = "actively-developed" } 18 | 19 | [features] 20 | wbindgen = [ "naia-socket-shared/wbindgen", "js-sys" ] 21 | mquad = [ "naia-socket-shared/mquad" ] 22 | bevy_support = [ "bevy_ecs" ] 23 | zstd_support = [ "zstd" ] 24 | transport_udp = [ "http" ] 25 | 26 | # this should be used when the underlying transport does not handle it for you (i.e. UDP) 27 | advanced_handshake = [] 28 | 29 | [dependencies] 30 | naia-socket-shared = { version = "0.24", path = "../socket/shared" } 31 | naia-derive = { version = "0.24", path = "derive" } 32 | naia-serde = { version = "0.24", path = "serde" } 33 | log = { version = "0.4" } 34 | cfg-if = { version = "1.0" } 35 | js-sys = { version = "0.3.64", optional = true } 36 | bevy_ecs = { version = "0.15", default-features = false, optional = true } 37 | zstd = { version = "0.12.2", optional = true } 38 | http = { version = "1.2", optional = true } -------------------------------------------------------------------------------- /shared/derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-derive" 3 | version = "0.24.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../.." 6 | description = "Procedural macros to simplify implementation of Naia Replicate & Protocolize traits" 7 | documentation = "https://docs.rs/naia-derive" 8 | homepage = "https://github.com/naia-lib/naia" 9 | repository = "https://github.com/naia-lib/naia" 10 | keywords = ["webrtc", "udp", "wasm", "networking", "gamedev"] 11 | categories = ["network-programming", "game-development", "wasm", "web-programming"] 12 | license = "MIT OR Apache-2.0" 13 | edition = "2021" 14 | 15 | [badges] 16 | maintenance = { status = "actively-developed" } 17 | 18 | [lib] 19 | proc-macro = true 20 | 21 | [features] 22 | 23 | [dependencies] 24 | naia-serde-derive = { version = "0.24", path = "../serde/derive" } 25 | proc-macro2 = "1.0" 26 | syn = { version = "2.0.29", features = ["clone-impls"] } 27 | quote = "1.0" 28 | -------------------------------------------------------------------------------- /shared/derive/src/channel.rs: -------------------------------------------------------------------------------- 1 | use quote::quote; 2 | use syn::{parse_macro_input, DeriveInput}; 3 | 4 | use super::shared::{get_struct_type, StructType}; 5 | 6 | pub fn channel_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 7 | let input = parse_macro_input!(input as DeriveInput); 8 | 9 | // Helper Properties 10 | let struct_type = get_struct_type(&input); 11 | match struct_type { 12 | StructType::Struct | StructType::TupleStruct => { 13 | panic!("Can only derive Channel on a Unit struct (i.e. `struct MyStruct;`)"); 14 | } 15 | _ => {} 16 | } 17 | 18 | // Names 19 | let struct_name = input.ident; 20 | 21 | let gen = quote! { 22 | 23 | impl Channel for #struct_name { 24 | 25 | } 26 | }; 27 | 28 | proc_macro::TokenStream::from(gen) 29 | } 30 | -------------------------------------------------------------------------------- /shared/serde/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-serde" 3 | version = "0.24.0" 4 | authors = ["connorcarpenter ", "Fedor ", "makepad "] 5 | workspace = "../.." 6 | description = "Bit-level de/serialization for naia" 7 | documentation = "https://docs.rs/naia-serde" 8 | homepage = "https://github.com/naia-lib/naia" 9 | repository = "https://github.com/naia-lib/naia" 10 | keywords = ["serialization", "bit", "binary", "networking", "gamedev"] 11 | categories = ["network-programming", "game-development", "wasm", "web-programming"] 12 | license = "MIT OR Apache-2.0" 13 | edition = "2021" 14 | 15 | [badges] 16 | maintenance = { status = "actively-developed" } 17 | 18 | [features] 19 | 20 | [dependencies] 21 | naia-serde-derive = { version = "0.24", path = "derive" } 22 | log = { version = "0.4" } 23 | cfg-if = { version = "1.0" } -------------------------------------------------------------------------------- /shared/serde/TODO.txt: -------------------------------------------------------------------------------- 1 | Implement De/Ser traits on: 2 | - Tuples (up to 12 parameters) 3 | - Bounded integers which wrap our new SerdeInteger type 4 | 5 | Completed: 6 | - Boolean 7 | - Unit Type: () 8 | - Unsigned integers: u8, u16, u32, u64, usize 9 | - Signed integers: i8, i16, i32, i64, isize 10 | - Floating point: f32, f64 11 | - char 12 | - Option 13 | - Box 14 | - String 15 | - Arrays (use const-generics to handle [T; N]) 16 | - Vec / VecDeque 17 | - HashMap / HashSet 18 | - Structs 19 | - Tuple Structs 20 | - Unit Structs 21 | - Enums -------------------------------------------------------------------------------- /shared/serde/derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-serde-derive" 3 | version = "0.24.0" 4 | authors = ["connorcarpenter ", "Fedor ", "makepad "] 5 | workspace = "../../.." 6 | description = "Derive methods for naia-serde" 7 | documentation = "https://docs.rs/naia-serde-derive" 8 | homepage = "https://github.com/naia-lib/naia" 9 | repository = "https://github.com/naia-lib/naia" 10 | keywords = ["serialization", "bit", "binary", "networking", "gamedev"] 11 | categories = ["network-programming", "game-development", "wasm", "web-programming"] 12 | license = "MIT OR Apache-2.0" 13 | edition = "2021" 14 | 15 | [badges] 16 | maintenance = { status = "actively-developed" } 17 | 18 | [lib] 19 | proc-macro = true 20 | 21 | [features] 22 | 23 | [dependencies] 24 | log = { version = "0.4" } 25 | cfg-if = { version = "1.0" } 26 | proc-macro2 = "1.0" 27 | syn = { version = "2.0.29" } 28 | quote = "1.0" -------------------------------------------------------------------------------- /shared/serde/derive/src/impls.rs: -------------------------------------------------------------------------------- 1 | mod enumeration; 2 | mod structure; 3 | mod tuple_structure; 4 | 5 | pub use enumeration::*; 6 | pub use structure::*; 7 | pub use tuple_structure::*; 8 | -------------------------------------------------------------------------------- /shared/serde/src/bit_counter.rs: -------------------------------------------------------------------------------- 1 | use crate::BitWrite; 2 | 3 | // BitCounter 4 | pub struct BitCounter { 5 | start_bits: u32, 6 | current_bits: u32, 7 | max_bits: u32, 8 | } 9 | 10 | impl BitCounter { 11 | pub fn new(start_bits: u32, current_bits: u32, max_bits: u32) -> Self { 12 | Self { 13 | start_bits, 14 | current_bits, 15 | max_bits, 16 | } 17 | } 18 | 19 | pub fn overflowed(&self) -> bool { 20 | self.current_bits > self.max_bits 21 | } 22 | 23 | pub fn bits_needed(&self) -> u32 { 24 | self.current_bits - self.start_bits 25 | } 26 | } 27 | 28 | impl BitWrite for BitCounter { 29 | fn write_bit(&mut self, _: bool) { 30 | self.current_bits += 1; 31 | } 32 | fn write_byte(&mut self, _: u8) { 33 | self.current_bits += 8; 34 | } 35 | fn count_bits(&mut self, bits: u32) { 36 | self.current_bits += bits; 37 | } 38 | fn is_counter(&self) -> bool { 39 | true 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /shared/serde/src/constants.rs: -------------------------------------------------------------------------------- 1 | const MIN_FRAGMENTATION_THRESHOLD_SIZE_BYTES: usize = 576; 2 | const IP_HEADER_SIZE_BYTES: usize = 60; 3 | const UDP_HEADER_SIZE_BYTES: usize = 8; 4 | const DTLS_HEADER_SIZE_BYTES: usize = 50; 5 | const SCTP_HEADER_SIZE_BYTES: usize = 28; 6 | /// The maximum of bytes that can be used for the payload of a given packet. 7 | /// (See #38 of ) 8 | pub const MTU_SIZE_BYTES: usize = MIN_FRAGMENTATION_THRESHOLD_SIZE_BYTES 9 | - IP_HEADER_SIZE_BYTES 10 | - UDP_HEADER_SIZE_BYTES 11 | - DTLS_HEADER_SIZE_BYTES 12 | - SCTP_HEADER_SIZE_BYTES; 13 | pub const MTU_SIZE_BITS: u32 = (MTU_SIZE_BYTES * 8) as u32; 14 | -------------------------------------------------------------------------------- /shared/serde/src/error.rs: -------------------------------------------------------------------------------- 1 | /// The error message when failing to serialize/deserialize to/from the bit 2 | /// stream. 3 | #[derive(Clone)] 4 | pub struct SerdeErr; 5 | 6 | impl std::fmt::Debug for SerdeErr { 7 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 8 | write!(f, "Bin deserialize error",) 9 | } 10 | } 11 | 12 | impl std::fmt::Display for SerdeErr { 13 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 14 | std::fmt::Debug::fmt(self, f) 15 | } 16 | } 17 | 18 | impl std::error::Error for SerdeErr {} 19 | -------------------------------------------------------------------------------- /shared/serde/src/impls.rs: -------------------------------------------------------------------------------- 1 | mod array; 2 | mod boxed; 3 | mod hash; 4 | mod option; 5 | mod phantom; 6 | mod scalars; 7 | mod string; 8 | mod tuple; 9 | mod vector; 10 | -------------------------------------------------------------------------------- /shared/serde/src/impls/phantom.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use crate::{ 4 | bit_reader::BitReader, 5 | bit_writer::BitWrite, 6 | error::SerdeErr, 7 | serde::{ConstBitLength, Serde}, 8 | }; 9 | 10 | // Unit // 11 | 12 | impl Serde for PhantomData { 13 | fn ser(&self, _: &mut dyn BitWrite) {} 14 | 15 | fn de(_: &mut BitReader) -> Result { 16 | Ok(Self) 17 | } 18 | 19 | fn bit_length(&self) -> u32 { 20 | ::const_bit_length() 21 | } 22 | } 23 | 24 | impl ConstBitLength for PhantomData { 25 | fn const_bit_length() -> u32 { 26 | 0 27 | } 28 | } 29 | 30 | // tests 31 | 32 | #[cfg(test)] 33 | mod phantom_tests { 34 | use crate::{bit_reader::BitReader, bit_writer::BitWriter, serde::Serde}; 35 | use std::marker::PhantomData; 36 | 37 | #[test] 38 | fn read_write() { 39 | // Write 40 | let mut writer = BitWriter::new(); 41 | 42 | let in_phantom = PhantomData::; 43 | 44 | in_phantom.ser(&mut writer); 45 | 46 | let buffer = writer.to_bytes(); 47 | 48 | //Read 49 | let mut reader = BitReader::new(&buffer); 50 | 51 | let out_phantom = Serde::de(&mut reader).unwrap(); 52 | 53 | assert_eq!(in_phantom, out_phantom); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /shared/serde/src/impls/timestamp.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naia-lib/naia/4b646a1fd4e54f320a85ce1735de972fa2fb95c7/shared/serde/src/impls/timestamp.rs -------------------------------------------------------------------------------- /shared/serde/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use naia_serde_derive::{ 2 | Serde, SerdeBevyClient, SerdeBevyServer, SerdeBevyShared, SerdeHecs, SerdeInternal, 3 | }; 4 | 5 | mod bit_counter; 6 | mod bit_reader; 7 | mod bit_writer; 8 | mod constants; 9 | mod error; 10 | mod file_bit_writer; 11 | mod impls; 12 | mod integer; 13 | mod outgoing_packet; 14 | mod serde; 15 | 16 | pub use bit_counter::BitCounter; 17 | pub use bit_reader::{BitReader, OwnedBitReader}; 18 | pub use bit_writer::{BitWrite, BitWriter}; 19 | pub use constants::{MTU_SIZE_BITS, MTU_SIZE_BYTES}; 20 | pub use error::SerdeErr; 21 | pub use file_bit_writer::FileBitWriter; 22 | pub use integer::{ 23 | SerdeIntegerConversion, SignedInteger, SignedVariableInteger, UnsignedInteger, 24 | UnsignedVariableInteger, 25 | }; 26 | pub use outgoing_packet::OutgoingPacket; 27 | pub use serde::{ 28 | ConstBitLength, Serde, Serde as SerdeInternal, Serde as SerdeBevyShared, 29 | Serde as SerdeBevyClient, Serde as SerdeBevyServer, Serde as SerdeHecs, 30 | }; 31 | -------------------------------------------------------------------------------- /shared/serde/src/outgoing_packet.rs: -------------------------------------------------------------------------------- 1 | use crate::MTU_SIZE_BYTES; 2 | 3 | pub struct OutgoingPacket { 4 | payload_length: usize, 5 | payload: [u8; MTU_SIZE_BYTES], 6 | } 7 | 8 | impl OutgoingPacket { 9 | pub fn new(payload_length: usize, payload: [u8; MTU_SIZE_BYTES]) -> Self { 10 | Self { 11 | payload_length, 12 | payload, 13 | } 14 | } 15 | 16 | pub fn slice(&self) -> &[u8] { 17 | &self.payload[0..self.payload_length] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /shared/serde/src/serde.rs: -------------------------------------------------------------------------------- 1 | use super::{bit_reader::BitReader, bit_writer::BitWrite, error::SerdeErr}; 2 | 3 | /// A trait for objects that can be serialized to a bitstream. 4 | pub trait Serde: Sized + Clone + PartialEq { 5 | /// Serialize Self to a BitWriter 6 | fn ser(&self, writer: &mut dyn BitWrite); 7 | 8 | /// Parse Self from a BitReader 9 | fn de(reader: &mut BitReader) -> Result; 10 | 11 | /// Return length of value in bits 12 | fn bit_length(&self) -> u32; 13 | } 14 | 15 | pub trait ConstBitLength { 16 | fn const_bit_length() -> u32; 17 | } 18 | -------------------------------------------------------------------------------- /shared/src/backends/miniquad/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod timer; 2 | pub mod timestamp; 3 | -------------------------------------------------------------------------------- /shared/src/backends/miniquad/timer.rs: -------------------------------------------------------------------------------- 1 | extern "C" { 2 | pub fn naia_now() -> f64; 3 | } 4 | 5 | use std::time::Duration; 6 | 7 | /// A Timer with a given duration after which it will enter into a "Ringing" 8 | /// state. The Timer can be reset at an given time, or manually set to start 9 | /// "Ringing" again. 10 | 11 | pub struct Timer { 12 | duration: f64, 13 | last: f64, 14 | } 15 | 16 | impl Timer { 17 | /// Creates a new Timer with a given Duration 18 | pub fn new(duration: Duration) -> Self { 19 | unsafe { 20 | Timer { 21 | last: naia_now(), 22 | duration: duration.as_millis() as f64, 23 | } 24 | } 25 | } 26 | 27 | /// Reset the Timer to stop ringing and wait till 'Duration' has elapsed 28 | /// again 29 | pub fn reset(&mut self) { 30 | unsafe { 31 | self.last = naia_now(); 32 | } 33 | } 34 | 35 | /// Gets whether or not the Timer is "Ringing" (i.e. the given Duration has 36 | /// elapsed since the last "reset") 37 | pub fn ringing(&self) -> bool { 38 | unsafe { (naia_now() - self.last) > self.duration } 39 | } 40 | 41 | /// Manually causes the Timer to enter into a "Ringing" state 42 | pub fn ring_manual(&mut self) { 43 | self.last -= self.duration; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /shared/src/backends/miniquad/timestamp.rs: -------------------------------------------------------------------------------- 1 | extern "C" { 2 | pub fn naia_now() -> f64; 3 | } 4 | 5 | pub struct Timestamp; 6 | 7 | impl Timestamp { 8 | pub fn now() -> u64 { 9 | unsafe { naia_now() as u64 } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /shared/src/backends/mod.rs: -------------------------------------------------------------------------------- 1 | cfg_if! { 2 | if #[cfg(all(target_arch = "wasm32", feature = "wbindgen"))] { 3 | mod wasm_bindgen; 4 | pub use self::wasm_bindgen::timer::Timer; 5 | pub use self::wasm_bindgen::timestamp::Timestamp; 6 | } 7 | else if #[cfg(all(target_arch = "wasm32", feature = "mquad"))] { 8 | mod miniquad; 9 | pub use self::miniquad::timer::Timer; 10 | pub use self::miniquad::timestamp::Timestamp; 11 | } 12 | else if #[cfg(not(target_arch = "wasm32"))] { 13 | mod native; 14 | pub use native::timer::Timer; 15 | pub use native::timestamp::Timestamp; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /shared/src/backends/native/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod timer; 2 | pub mod timestamp; 3 | -------------------------------------------------------------------------------- /shared/src/backends/native/timer.rs: -------------------------------------------------------------------------------- 1 | use std::time::{Duration, Instant}; 2 | 3 | /// A Timer with a given duration after which it will enter into a "Ringing" 4 | /// state. The Timer can be reset at an given time, or manually set to start 5 | /// "Ringing" again. 6 | pub struct Timer { 7 | duration: Duration, 8 | last: Instant, 9 | } 10 | 11 | impl Timer { 12 | /// Creates a new Timer with a given Duration 13 | pub fn new(duration: Duration) -> Self { 14 | Timer { 15 | last: Instant::now(), 16 | duration, 17 | } 18 | } 19 | 20 | /// Reset the Timer to stop ringing and wait till 'Duration' has elapsed 21 | /// again 22 | pub fn reset(&mut self) { 23 | self.last = Instant::now(); 24 | } 25 | 26 | /// Gets whether or not the Timer is "Ringing" (i.e. the given Duration has 27 | /// elapsed since the last "reset") 28 | pub fn ringing(&self) -> bool { 29 | Instant::now().saturating_duration_since(self.last) > self.duration 30 | } 31 | 32 | /// Manually causes the Timer to enter into a "Ringing" state 33 | pub fn ring_manual(&mut self) { 34 | self.last -= self.duration; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /shared/src/backends/native/timestamp.rs: -------------------------------------------------------------------------------- 1 | use std::time::SystemTime; 2 | 3 | pub struct Timestamp; 4 | 5 | impl Timestamp { 6 | pub fn now() -> u64 { 7 | SystemTime::now() 8 | .duration_since(SystemTime::UNIX_EPOCH) 9 | .expect("timing error!") 10 | .as_secs() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /shared/src/backends/wasm_bindgen/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod timer; 2 | pub mod timestamp; 3 | -------------------------------------------------------------------------------- /shared/src/backends/wasm_bindgen/timer.rs: -------------------------------------------------------------------------------- 1 | use js_sys::Date; 2 | use std::time::Duration; 3 | 4 | /// A Timer with a given duration after which it will enter into a "Ringing" 5 | /// state. The Timer can be reset at an given time, or manually set to start 6 | /// "Ringing" again. 7 | 8 | pub struct Timer { 9 | duration: f64, 10 | last: f64, 11 | } 12 | 13 | impl Timer { 14 | /// Creates a new Timer with a given Duration 15 | pub fn new(duration: Duration) -> Self { 16 | Timer { 17 | last: Date::now(), 18 | duration: duration.as_millis() as f64, 19 | } 20 | } 21 | 22 | /// Reset the Timer to stop ringing and wait till 'Duration' has elapsed 23 | /// again 24 | pub fn reset(&mut self) { 25 | self.last = Date::now(); 26 | } 27 | 28 | /// Gets whether or not the Timer is "Ringing" (i.e. the given Duration has 29 | /// elapsed since the last "reset") 30 | pub fn ringing(&self) -> bool { 31 | (Date::now() - self.last) > self.duration 32 | } 33 | 34 | /// Manually causes the Timer to enter into a "Ringing" state 35 | pub fn ring_manual(&mut self) { 36 | self.last -= self.duration; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /shared/src/backends/wasm_bindgen/timestamp.rs: -------------------------------------------------------------------------------- 1 | use js_sys::Date; 2 | 3 | pub struct Timestamp; 4 | 5 | impl Timestamp { 6 | pub fn now() -> u64 { 7 | Date::now() as u64 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /shared/src/connection/compression_config.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone)] 2 | pub struct CompressionConfig { 3 | pub server_to_client: Option, 4 | pub client_to_server: Option, 5 | } 6 | 7 | impl CompressionConfig { 8 | pub fn new( 9 | server_to_client: Option, 10 | client_to_server: Option, 11 | ) -> Self { 12 | Self { 13 | server_to_client, 14 | client_to_server, 15 | } 16 | } 17 | } 18 | 19 | #[derive(Clone, Eq, PartialEq)] 20 | pub enum CompressionMode { 21 | /// Compression mode using default zstd dictionary. 22 | /// 1st i32 parameter here is the compression level from -7 (fastest) to 22 23 | /// (smallest). 24 | Default(i32), 25 | /// Compression mode using custom dictionary. 26 | /// 1st i32 parameter here is the compression level from -7 (fastest) to 22 27 | /// (smallest). 2nd Vec parameter here is the dictionary itself. 28 | Dictionary(i32, Vec), 29 | /// Dictionary training mode. 30 | /// 1st usize parameter here describes the desired number of samples 31 | /// (packets) to train on. Obviously, the more samples trained on, the 32 | /// better theoretical compression. 33 | Training(usize), 34 | } 35 | -------------------------------------------------------------------------------- /shared/src/connection/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ack_manager; 2 | pub mod bandwidth_monitor; 3 | pub mod base_connection; 4 | pub mod compression_config; 5 | pub mod connection_config; 6 | pub mod decoder; 7 | pub mod encoder; 8 | pub mod packet_notifiable; 9 | pub mod packet_type; 10 | pub mod ping_store; 11 | pub mod sequence_buffer; 12 | pub mod standard_header; 13 | -------------------------------------------------------------------------------- /shared/src/connection/packet_notifiable.rs: -------------------------------------------------------------------------------- 1 | use crate::PacketIndex; 2 | 3 | /// Represents a manager that must be notified when packets have been dropped or 4 | /// delivered 5 | pub trait PacketNotifiable { 6 | /// Notifies the manager that a packet has been delivered 7 | fn notify_packet_delivered(&mut self, packet_index: PacketIndex); 8 | } 9 | -------------------------------------------------------------------------------- /shared/src/constants.rs: -------------------------------------------------------------------------------- 1 | pub const FRAGMENTATION_LIMIT_BYTES: usize = 400; 2 | pub const FRAGMENTATION_LIMIT_BITS: u32 = (FRAGMENTATION_LIMIT_BYTES as u32) * 8; 3 | -------------------------------------------------------------------------------- /shared/src/handshake/advanced/header.rs: -------------------------------------------------------------------------------- 1 | use naia_serde::SerdeInternal; 2 | 3 | #[derive(SerdeInternal, Debug, PartialEq, Eq, Clone)] 4 | pub enum HandshakeHeader { 5 | // An initial handshake message sent by the Client to the Server 6 | ClientChallengeRequest, 7 | // The Server's response to the Client's initial handshake message 8 | ServerChallengeResponse, 9 | // The handshake message validating the Client 10 | ClientValidateRequest, 11 | // The Server's response to the Client's validation request 12 | ServerValidateResponse, 13 | // The final handshake message sent by the Client 14 | ClientConnectRequest, 15 | // The final handshake message sent by the Server, indicating that the 16 | // connection has been established 17 | ServerConnectResponse, 18 | // Used to request a graceful Client disconnect from the Server 19 | Disconnect, 20 | } 21 | -------------------------------------------------------------------------------- /shared/src/handshake/advanced/mod.rs: -------------------------------------------------------------------------------- 1 | mod header; 2 | pub use header::HandshakeHeader; 3 | -------------------------------------------------------------------------------- /shared/src/handshake/mod.rs: -------------------------------------------------------------------------------- 1 | cfg_if! { 2 | if #[cfg(feature = "advanced_handshake")] { 3 | mod advanced; 4 | pub use advanced::*; 5 | } else { 6 | mod simple; 7 | pub use simple::*; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /shared/src/handshake/simple/header.rs: -------------------------------------------------------------------------------- 1 | use naia_serde::SerdeInternal; 2 | 3 | #[derive(SerdeInternal, Debug, PartialEq, Eq, Clone)] 4 | pub enum HandshakeHeader { 5 | // An initial handshake message sent by the Client to the Server 6 | ClientIdentifyRequest, 7 | // The Server's response to the Client's initial handshake message 8 | ServerIdentifyResponse, 9 | // The handshake message sent by the Client to initiate a connection 10 | ClientConnectRequest, 11 | // The handshake message sent by the Server, indicating that the 12 | // connection has been established 13 | ServerConnectResponse, 14 | // Used to request a graceful Client disconnect from the Server 15 | Disconnect, 16 | } 17 | -------------------------------------------------------------------------------- /shared/src/handshake/simple/mod.rs: -------------------------------------------------------------------------------- 1 | mod header; 2 | pub use header::HandshakeHeader; 3 | -------------------------------------------------------------------------------- /shared/src/messages/channels/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod channel; 2 | pub mod channel_kinds; 3 | pub mod default_channels; 4 | pub mod receivers; 5 | pub mod senders; 6 | pub mod system_channel; 7 | -------------------------------------------------------------------------------- /shared/src/messages/channels/receivers/channel_receiver.rs: -------------------------------------------------------------------------------- 1 | use naia_serde::{BitReader, SerdeErr}; 2 | use naia_socket_shared::Instant; 3 | 4 | use crate::messages::channels::senders::request_sender::LocalRequestId; 5 | use crate::{ 6 | messages::{message_container::MessageContainer, message_kinds::MessageKinds}, 7 | world::remote::entity_waitlist::EntityWaitlist, 8 | LocalEntityAndGlobalEntityConverter, LocalResponseId, 9 | }; 10 | 11 | pub trait ChannelReceiver

: Send + Sync { 12 | /// Read messages from an internal buffer and return their content 13 | fn receive_messages( 14 | &mut self, 15 | message_kinds: &MessageKinds, 16 | now: &Instant, 17 | entity_waitlist: &mut EntityWaitlist, 18 | converter: &dyn LocalEntityAndGlobalEntityConverter, 19 | ) -> Vec

; 20 | } 21 | 22 | pub trait MessageChannelReceiver: ChannelReceiver { 23 | /// Read messages from raw bits, parse them and store then into an internal buffer 24 | fn read_messages( 25 | &mut self, 26 | message_kinds: &MessageKinds, 27 | entity_waitlist: &mut EntityWaitlist, 28 | converter: &dyn LocalEntityAndGlobalEntityConverter, 29 | reader: &mut BitReader, 30 | ) -> Result<(), SerdeErr>; 31 | 32 | fn receive_requests_and_responses( 33 | &mut self, 34 | ) -> ( 35 | Vec<(LocalResponseId, MessageContainer)>, 36 | Vec<(LocalRequestId, MessageContainer)>, 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /shared/src/messages/channels/receivers/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod channel_receiver; 2 | pub mod fragment_receiver; 3 | pub mod indexed_message_reader; 4 | pub mod ordered_reliable_receiver; 5 | pub mod sequenced_reliable_receiver; 6 | pub mod sequenced_unreliable_receiver; 7 | pub mod unordered_reliable_receiver; 8 | pub mod unordered_unreliable_receiver; 9 | 10 | mod reliable_message_receiver; 11 | pub mod reliable_receiver; 12 | -------------------------------------------------------------------------------- /shared/src/messages/channels/receivers/sequenced_reliable_receiver.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | messages::channels::receivers::reliable_message_receiver::{ 3 | ReceiverArranger, ReliableMessageReceiver, 4 | }, 5 | sequence_less_than, 6 | types::MessageIndex, 7 | MessageContainer, 8 | }; 9 | 10 | pub type SequencedReliableReceiver = ReliableMessageReceiver; 11 | 12 | impl SequencedReliableReceiver { 13 | pub fn new() -> Self { 14 | Self::with_arranger(SequencedArranger { 15 | newest_received_message_index: 0, 16 | }) 17 | } 18 | } 19 | 20 | // SequencedArranger 21 | pub struct SequencedArranger { 22 | newest_received_message_index: MessageIndex, 23 | } 24 | 25 | impl ReceiverArranger for SequencedArranger { 26 | fn process( 27 | &mut self, 28 | start_message_index: MessageIndex, 29 | end_message_index: MessageIndex, 30 | message: MessageContainer, 31 | ) -> Vec { 32 | let mut output = Vec::new(); 33 | if !sequence_less_than(start_message_index, self.newest_received_message_index) { 34 | self.newest_received_message_index = end_message_index; 35 | output.push(message); 36 | } 37 | output 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /shared/src/messages/channels/receivers/unordered_reliable_receiver.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | messages::channels::receivers::reliable_message_receiver::{ 3 | ReceiverArranger, ReliableMessageReceiver, 4 | }, 5 | types::MessageIndex, 6 | MessageContainer, 7 | }; 8 | 9 | pub type UnorderedReliableReceiver = ReliableMessageReceiver; 10 | 11 | impl UnorderedReliableReceiver { 12 | pub fn new() -> Self { 13 | Self::with_arranger(UnorderedArranger) 14 | } 15 | } 16 | 17 | // UnorderedArranger 18 | pub struct UnorderedArranger; 19 | 20 | impl ReceiverArranger for UnorderedArranger { 21 | fn process( 22 | &mut self, 23 | _start_message_index: MessageIndex, 24 | _end_message_index: MessageIndex, 25 | message: MessageContainer, 26 | ) -> Vec { 27 | let mut output = Vec::new(); 28 | output.push(message); 29 | output 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /shared/src/messages/channels/senders/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod channel_sender; 2 | pub mod indexed_message_writer; 3 | pub mod message_fragmenter; 4 | pub mod reliable_message_sender; 5 | pub mod reliable_sender; 6 | pub mod request_sender; 7 | pub mod sequenced_unreliable_sender; 8 | pub mod unordered_unreliable_sender; 9 | -------------------------------------------------------------------------------- /shared/src/messages/channels/system_channel.rs: -------------------------------------------------------------------------------- 1 | use crate::Channel; 2 | 3 | #[derive(Channel)] 4 | pub struct SystemChannel; 5 | -------------------------------------------------------------------------------- /shared/src/messages/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod channels; 2 | pub mod fragment; 3 | pub mod message; 4 | pub mod message_container; 5 | pub mod message_kinds; 6 | pub mod message_manager; 7 | pub mod named; 8 | pub mod request; 9 | 10 | #[cfg(test)] 11 | mod tests; 12 | -------------------------------------------------------------------------------- /shared/src/messages/named.rs: -------------------------------------------------------------------------------- 1 | pub trait Named { 2 | /// Gets the String representation of the Type of the Component, used for debugging 3 | fn name(&self) -> String; 4 | } 5 | -------------------------------------------------------------------------------- /shared/src/messages/tests/mod.rs: -------------------------------------------------------------------------------- 1 | mod fragment; 2 | -------------------------------------------------------------------------------- /shared/src/types.rs: -------------------------------------------------------------------------------- 1 | pub type PacketIndex = u16; 2 | pub type Tick = u16; 3 | pub type MessageIndex = u16; 4 | pub type ShortMessageIndex = u8; 5 | 6 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 7 | pub enum HostType { 8 | Server, 9 | Client, 10 | } 11 | -------------------------------------------------------------------------------- /shared/src/world/component/component_update.rs: -------------------------------------------------------------------------------- 1 | use naia_serde::{BitReader, OwnedBitReader, SerdeErr}; 2 | 3 | use crate::{ 4 | world::component::component_kinds::ComponentKind, ComponentKinds, 5 | LocalEntityAndGlobalEntityConverter, RemoteEntity, 6 | }; 7 | 8 | pub struct ComponentUpdate { 9 | pub kind: ComponentKind, 10 | buffer: OwnedBitReader, 11 | } 12 | 13 | impl ComponentUpdate { 14 | pub fn new(kind: ComponentKind, buffer: OwnedBitReader) -> Self { 15 | Self { kind, buffer } 16 | } 17 | 18 | pub fn reader(&self) -> BitReader { 19 | self.buffer.borrow() 20 | } 21 | 22 | pub(crate) fn split_into_waiting_and_ready( 23 | self, 24 | converter: &dyn LocalEntityAndGlobalEntityConverter, 25 | component_kinds: &ComponentKinds, 26 | ) -> Result< 27 | ( 28 | Option>, 29 | Option, 30 | ), 31 | SerdeErr, 32 | > { 33 | let kind = self.kind; 34 | component_kinds.split_update(converter, &kind, self) 35 | } 36 | } 37 | 38 | pub struct ComponentFieldUpdate { 39 | id: u8, 40 | buffer: OwnedBitReader, 41 | } 42 | 43 | impl ComponentFieldUpdate { 44 | pub fn new(id: u8, buffer: OwnedBitReader) -> Self { 45 | Self { id, buffer } 46 | } 47 | 48 | pub fn field_id(&self) -> u8 { 49 | self.id 50 | } 51 | 52 | pub fn reader(&self) -> BitReader { 53 | self.buffer.borrow() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /shared/src/world/component/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod component_kinds; 2 | pub mod component_update; 3 | pub mod diff_mask; 4 | pub mod entity_property; 5 | pub mod property; 6 | pub mod property_mutate; 7 | pub mod replica_ref; 8 | pub mod replicate; 9 | -------------------------------------------------------------------------------- /shared/src/world/delegation/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod auth_channel; 2 | pub mod entity_auth_status; 3 | pub mod host_auth_handler; 4 | -------------------------------------------------------------------------------- /shared/src/world/entity/entity_action.rs: -------------------------------------------------------------------------------- 1 | use crate::world::component::component_kinds::ComponentKind; 2 | 3 | pub enum EntityAction { 4 | SpawnEntity(E, Vec), 5 | DespawnEntity(E), 6 | InsertComponent(E, ComponentKind), 7 | RemoveComponent(E, ComponentKind), 8 | Noop, 9 | } 10 | 11 | impl EntityAction { 12 | pub fn entity(&self) -> Option { 13 | match self { 14 | EntityAction::SpawnEntity(entity, _) => Some(*entity), 15 | EntityAction::DespawnEntity(entity) => Some(*entity), 16 | EntityAction::InsertComponent(entity, _) => Some(*entity), 17 | EntityAction::RemoveComponent(entity, _) => Some(*entity), 18 | EntityAction::Noop => None, 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /shared/src/world/entity/entity_action_type.rs: -------------------------------------------------------------------------------- 1 | use naia_serde::SerdeInternal; 2 | 3 | // Enum used as a shared network protocol, representing various message types 4 | // related to Entities/Components 5 | #[derive(Copy, PartialEq, Clone, SerdeInternal)] 6 | pub enum EntityActionType { 7 | // Action indicating an Entity to be created 8 | SpawnEntity, 9 | // Action indicating an Entity to be deleted 10 | DespawnEntity, 11 | // Action indicating a Component to be added to an Entity 12 | InsertComponent, 13 | // Action indicating a Component to be deleted 14 | RemoveComponent, 15 | // Action indicating a non-operation 16 | Noop, 17 | } 18 | -------------------------------------------------------------------------------- /shared/src/world/entity/error.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | #[derive(Debug)] 4 | pub struct EntityDoesNotExistError; 5 | impl Error for EntityDoesNotExistError {} 6 | impl std::fmt::Display for EntityDoesNotExistError { 7 | fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { 8 | write!(f, "Error while attempting to look-up an Entity value for conversion: Entity was not found!") 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /shared/src/world/entity/global_entity.rs: -------------------------------------------------------------------------------- 1 | use crate::BigMapKey; 2 | use naia_serde::{BitReader, BitWrite, Serde, SerdeErr}; 3 | 4 | // GlobalEntity 5 | #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] 6 | pub struct GlobalEntity(u64); 7 | 8 | impl BigMapKey for GlobalEntity { 9 | fn to_u64(&self) -> u64 { 10 | self.0 11 | } 12 | 13 | fn from_u64(value: u64) -> Self { 14 | GlobalEntity(value) 15 | } 16 | } 17 | 18 | impl Serde for GlobalEntity { 19 | fn ser(&self, _: &mut dyn BitWrite) { 20 | panic!("shouldn't call this"); 21 | } 22 | 23 | fn de(_: &mut BitReader) -> Result { 24 | panic!("shouldn't call this"); 25 | } 26 | 27 | fn bit_length(&self) -> u32 { 28 | panic!("shouldn't call this"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /shared/src/world/entity/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod entity_action; 2 | pub mod entity_action_receiver; 3 | pub mod entity_action_type; 4 | pub mod entity_auth_event; 5 | pub mod entity_converters; 6 | pub mod error; 7 | pub mod global_entity; 8 | pub mod local_entity; 9 | -------------------------------------------------------------------------------- /shared/src/world/host/entity_action_event.rs: -------------------------------------------------------------------------------- 1 | use crate::ComponentKind; 2 | 3 | #[derive(Clone, PartialEq, Eq)] 4 | pub enum EntityActionEvent { 5 | SpawnEntity(E, Vec), 6 | DespawnEntity(E), 7 | InsertComponent(E, ComponentKind), 8 | RemoveComponent(E, ComponentKind), 9 | } 10 | -------------------------------------------------------------------------------- /shared/src/world/host/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod global_diff_handler; 2 | pub mod host_world_manager; 3 | pub mod host_world_writer; 4 | pub mod mut_channel; 5 | pub mod user_diff_handler; 6 | pub mod world_channel; 7 | 8 | mod entity_action_event; 9 | mod entity_channel; 10 | -------------------------------------------------------------------------------- /shared/src/world/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod component; 2 | pub mod delegation; 3 | pub mod entity; 4 | pub mod host; 5 | pub mod local_entity_map; 6 | pub mod local_world_manager; 7 | pub mod remote; 8 | pub mod shared_global_world_manager; 9 | pub mod world_type; 10 | -------------------------------------------------------------------------------- /shared/src/world/remote/entity_action_event.rs: -------------------------------------------------------------------------------- 1 | use crate::{ComponentKind, Replicate}; 2 | 3 | pub enum EntityActionEvent { 4 | SpawnEntity(E), 5 | DespawnEntity(E), 6 | InsertComponent(E, ComponentKind), 7 | RemoveComponent(E, Box), 8 | } 9 | -------------------------------------------------------------------------------- /shared/src/world/remote/entity_event.rs: -------------------------------------------------------------------------------- 1 | use crate::{ComponentKind, EntityAuthStatus, RemoteEntity, Replicate, Tick}; 2 | 3 | pub enum EntityEvent { 4 | SpawnEntity(E), 5 | DespawnEntity(E), 6 | InsertComponent(E, ComponentKind), 7 | RemoveComponent(E, Box), 8 | UpdateComponent(Tick, E, ComponentKind), 9 | } 10 | 11 | pub enum EntityResponseEvent { 12 | SpawnEntity(E), 13 | DespawnEntity(E), 14 | InsertComponent(E, ComponentKind), 15 | RemoveComponent(E, ComponentKind), 16 | PublishEntity(E), 17 | UnpublishEntity(E), 18 | EnableDelegationEntity(E), 19 | EnableDelegationEntityResponse(E), 20 | DisableDelegationEntity(E), 21 | EntityRequestAuthority(E, RemoteEntity), 22 | EntityReleaseAuthority(E), 23 | EntityUpdateAuthority(E, EntityAuthStatus), 24 | EntityMigrateResponse(E, RemoteEntity), 25 | } 26 | -------------------------------------------------------------------------------- /shared/src/world/remote/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod entity_action_event; 2 | pub mod entity_event; 3 | pub mod entity_waitlist; 4 | pub mod remote_world_manager; 5 | pub mod remote_world_reader; 6 | -------------------------------------------------------------------------------- /shared/tests/derive_struct.rs: -------------------------------------------------------------------------------- 1 | mod some_struct { 2 | use naia_shared::Serde; 3 | 4 | #[derive(Clone, Debug, PartialEq, Serde)] 5 | pub struct SomeStruct { 6 | pub some_string: String, 7 | pub some_int: i16, 8 | pub some_bool: bool, 9 | } 10 | } 11 | 12 | use naia_shared::{BitReader, BitWriter, Serde}; 13 | 14 | use some_struct::SomeStruct; 15 | 16 | #[test] 17 | fn read_write_struct() { 18 | // Write 19 | let mut writer = BitWriter::new(); 20 | 21 | let in_1 = SomeStruct { 22 | some_string: "Hello world!".to_string(), 23 | some_int: 42, 24 | some_bool: true, 25 | }; 26 | let in_2 = SomeStruct { 27 | some_string: "Goodbye world!".to_string(), 28 | some_int: -42, 29 | some_bool: false, 30 | }; 31 | 32 | in_1.ser(&mut writer); 33 | in_2.ser(&mut writer); 34 | 35 | let bytes = writer.to_bytes(); 36 | 37 | // Read 38 | 39 | let mut reader = BitReader::new(&bytes); 40 | 41 | let out_1 = Serde::de(&mut reader).unwrap(); 42 | let out_2 = Serde::de(&mut reader).unwrap(); 43 | 44 | assert_eq!(in_1, out_1); 45 | assert_eq!(in_2, out_2); 46 | } 47 | -------------------------------------------------------------------------------- /shared/tests/derive_tuple_struct.rs: -------------------------------------------------------------------------------- 1 | mod some_struct { 2 | use naia_shared::Serde; 3 | 4 | #[derive(Clone, Debug, PartialEq, Serde)] 5 | pub struct SomeStruct(pub String, pub i16, pub bool); 6 | } 7 | 8 | use naia_shared::{BitReader, BitWriter, Serde}; 9 | use some_struct::SomeStruct; 10 | 11 | #[test] 12 | fn read_write_tuple_struct() { 13 | // Write 14 | let mut writer = BitWriter::new(); 15 | 16 | let in_1 = SomeStruct("Hello world!".to_string(), 42, true); 17 | let in_2 = SomeStruct("Goodbye world!".to_string(), -42, false); 18 | 19 | in_1.ser(&mut writer); 20 | in_2.ser(&mut writer); 21 | 22 | let bytes = writer.to_bytes(); 23 | 24 | // Read 25 | 26 | let mut reader = BitReader::new(&bytes); 27 | 28 | let out_1 = Serde::de(&mut reader).unwrap(); 29 | let out_2 = Serde::de(&mut reader).unwrap(); 30 | 31 | assert_eq!(in_1, out_1); 32 | assert_eq!(in_2, out_2); 33 | } 34 | -------------------------------------------------------------------------------- /shared/tests/derive_unit_struct.rs: -------------------------------------------------------------------------------- 1 | mod some_struct { 2 | use naia_shared::Serde; 3 | 4 | #[derive(Clone, Debug, PartialEq, Serde)] 5 | pub struct SomeStruct; 6 | } 7 | 8 | use naia_shared::{BitReader, BitWriter, Serde}; 9 | use some_struct::SomeStruct; 10 | 11 | #[test] 12 | fn read_write_unit_struct() { 13 | // Write 14 | let mut writer = BitWriter::new(); 15 | 16 | let in_1 = SomeStruct; 17 | 18 | in_1.ser(&mut writer); 19 | 20 | let bytes = writer.to_bytes(); 21 | 22 | // Read 23 | 24 | let mut reader = BitReader::new(&bytes); 25 | 26 | let out_1 = Serde::de(&mut reader).unwrap(); 27 | 28 | assert_eq!(in_1, out_1); 29 | } 30 | -------------------------------------------------------------------------------- /socket/client/src/backends/miniquad/identity_receiver.rs: -------------------------------------------------------------------------------- 1 | use super::shared::ID_CELL; 2 | use crate::{identity_receiver::IdentityReceiver, IdentityReceiverResult}; 3 | 4 | /// Handles receiving an IdentityToken from the Server through a given Client Socket 5 | #[derive(Clone)] 6 | pub struct IdentityReceiverImpl; 7 | 8 | impl IdentityReceiver for IdentityReceiverImpl { 9 | fn receive(&mut self) -> IdentityReceiverResult { 10 | unsafe { 11 | if let Some(id_cell) = &mut ID_CELL { 12 | if let Some(id_token) = id_cell.take() { 13 | return IdentityReceiverResult::Success(id_token); 14 | } 15 | } 16 | }; 17 | 18 | IdentityReceiverResult::Waiting 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /socket/client/src/backends/miniquad/mod.rs: -------------------------------------------------------------------------------- 1 | mod shared; 2 | 3 | mod identity_receiver; 4 | mod packet_receiver; 5 | mod packet_sender; 6 | mod socket; 7 | 8 | pub use identity_receiver::IdentityReceiverImpl; 9 | pub use packet_receiver::PacketReceiverImpl; 10 | pub use packet_sender::PacketSenderImpl; 11 | pub use socket::Socket; 12 | -------------------------------------------------------------------------------- /socket/client/src/backends/miniquad/packet_sender.rs: -------------------------------------------------------------------------------- 1 | use crate::{error::NaiaClientSocketError, packet_sender::PacketSender, ServerAddr}; 2 | 3 | use super::shared::{ 4 | naia_create_u8_array, naia_disconnect, naia_is_connected, naia_send, SERVER_ADDR, 5 | }; 6 | 7 | /// Handles sending messages to the Server for a given Client Socket 8 | #[derive(Clone, Default)] 9 | pub struct PacketSenderImpl; 10 | 11 | impl PacketSender for PacketSenderImpl { 12 | /// Send a Packet to the Server 13 | fn send(&self, payload: &[u8]) -> Result<(), NaiaClientSocketError> { 14 | unsafe { 15 | let ptr = payload.as_ptr(); 16 | let len = payload.len(); 17 | let js_obj = naia_create_u8_array(ptr as _, len as _); 18 | return if naia_send(js_obj) { 19 | Ok(()) 20 | } else { 21 | Err(NaiaClientSocketError::SendError) 22 | }; 23 | } 24 | } 25 | 26 | /// Get the Server's Socket address 27 | fn server_addr(&self) -> ServerAddr { 28 | unsafe { SERVER_ADDR } 29 | } 30 | 31 | fn connected(&self) -> bool { 32 | unsafe { 33 | return naia_is_connected(); 34 | } 35 | } 36 | 37 | fn disconnect(&mut self) { 38 | unsafe { 39 | naia_disconnect(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /socket/client/src/backends/mod.rs: -------------------------------------------------------------------------------- 1 | mod socket; 2 | 3 | cfg_if! { 4 | if #[cfg(all(target_arch = "wasm32", feature = "wbindgen"))] { 5 | mod wasm_bindgen; 6 | pub use self::wasm_bindgen::*; 7 | } 8 | else if #[cfg(all(target_arch = "wasm32", feature = "mquad"))] { 9 | mod miniquad; 10 | pub use self::miniquad::*; 11 | } 12 | else { 13 | mod native; 14 | pub use self::native::*; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /socket/client/src/backends/native/identity_receiver.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use tokio::sync::oneshot; 4 | 5 | use crate::{identity_receiver::IdentityReceiver, IdentityReceiverResult}; 6 | 7 | /// Handles receiving an IdentityToken from the Server through a given Client Socket 8 | #[derive(Clone)] 9 | pub struct IdentityReceiverImpl { 10 | receiver_channel: Arc>>>, 11 | } 12 | 13 | impl IdentityReceiverImpl { 14 | /// Create a new IdentityReceiver, if supplied with the Server's address & a 15 | /// reference back to the parent Socket 16 | pub fn new(receiver_channel: oneshot::Receiver>) -> Self { 17 | Self { 18 | receiver_channel: Arc::new(Mutex::new(receiver_channel)), 19 | } 20 | } 21 | } 22 | 23 | impl IdentityReceiver for IdentityReceiverImpl { 24 | fn receive(&mut self) -> IdentityReceiverResult { 25 | if let Ok(mut receiver) = self.receiver_channel.lock() { 26 | if let Ok(recv_result) = receiver.try_recv() { 27 | match recv_result { 28 | Ok(identity_token) => IdentityReceiverResult::Success(identity_token), 29 | Err(error_code) => IdentityReceiverResult::ErrorResponseCode(error_code), 30 | } 31 | } else { 32 | IdentityReceiverResult::Waiting 33 | } 34 | } else { 35 | IdentityReceiverResult::Waiting 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /socket/client/src/backends/native/mod.rs: -------------------------------------------------------------------------------- 1 | mod identity_receiver; 2 | mod packet_receiver; 3 | mod packet_sender; 4 | mod runtime; 5 | mod socket; 6 | 7 | pub use identity_receiver::IdentityReceiverImpl; 8 | pub use packet_receiver::PacketReceiverImpl; 9 | pub use packet_sender::PacketSenderImpl; 10 | pub use socket::Socket; 11 | -------------------------------------------------------------------------------- /socket/client/src/backends/native/runtime.rs: -------------------------------------------------------------------------------- 1 | use std::{future, thread}; 2 | 3 | use once_cell::sync::Lazy; 4 | use tokio::runtime::{Builder, Handle}; 5 | 6 | /// TODO: make description 7 | pub fn get_runtime() -> Handle { 8 | static GLOBAL: Lazy = Lazy::new(|| { 9 | let runtime = Builder::new_multi_thread() 10 | .enable_all() 11 | .build() 12 | .expect("was not able to build the runtime"); 13 | 14 | let runtime_handle = runtime.handle().clone(); 15 | 16 | thread::Builder::new() 17 | .name("tokio-main".to_string()) 18 | .spawn(move || { 19 | let _guard = runtime.enter(); 20 | runtime.block_on(future::pending::<()>()); 21 | }) 22 | .expect("cannot spawn executor thread"); 23 | 24 | let _guard = runtime_handle.enter(); 25 | 26 | runtime_handle 27 | }); 28 | 29 | Lazy::::force(&GLOBAL).clone() 30 | } 31 | -------------------------------------------------------------------------------- /socket/client/src/backends/socket.rs: -------------------------------------------------------------------------------- 1 | use naia_socket_shared::SocketConfig; 2 | 3 | use crate::{ 4 | identity_receiver::IdentityReceiver, packet_receiver::PacketReceiver, 5 | packet_sender::PacketSender, 6 | }; 7 | 8 | /// Used to send packets from the Client Socket 9 | #[allow(dead_code)] 10 | pub trait SocketTrait { 11 | fn connect( 12 | server_session_url: &str, 13 | config: &SocketConfig, 14 | ) -> ( 15 | Box, 16 | Box, 17 | Box, 18 | ); 19 | fn connect_with_auth( 20 | server_session_url: &str, 21 | config: &SocketConfig, 22 | auth_bytes: Vec, 23 | ) -> ( 24 | Box, 25 | Box, 26 | Box, 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /socket/client/src/backends/wasm_bindgen/addr_cell.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | net::SocketAddr, 3 | sync::{Arc, Mutex}, 4 | }; 5 | 6 | use crate::{server_addr::ServerAddr, wasm_utils::candidate_to_addr}; 7 | 8 | // MaybeAddr 9 | struct MaybeAddr(pub(crate) ServerAddr); 10 | 11 | // AddrCell 12 | #[derive(Clone)] 13 | pub struct AddrCell { 14 | cell: Arc>, 15 | } 16 | 17 | impl AddrCell { 18 | pub fn new() -> Self { 19 | AddrCell { 20 | cell: Arc::new(Mutex::new(MaybeAddr(ServerAddr::Finding))), 21 | } 22 | } 23 | 24 | pub fn receive_candidate(&self, candidate_str: &str) { 25 | self.cell 26 | .lock() 27 | .expect("This should never happen, receive_candidate() should only be called once ever during the session initialization") 28 | .0 = candidate_to_addr(candidate_str); 29 | } 30 | 31 | pub fn get(&self) -> ServerAddr { 32 | match self.cell.try_lock() { 33 | Ok(addr) => addr.0, 34 | Err(_) => ServerAddr::Finding, 35 | } 36 | } 37 | 38 | pub fn set_addr(&mut self, addr: &SocketAddr) { 39 | self.cell.lock().expect("cannot borrow AddrCell.cell!").0 = ServerAddr::Found(addr.clone()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /socket/client/src/backends/wasm_bindgen/mod.rs: -------------------------------------------------------------------------------- 1 | mod addr_cell; 2 | mod data_channel; 3 | mod data_port; 4 | 5 | mod identity_receiver; 6 | mod packet_receiver; 7 | mod packet_sender; 8 | mod socket; 9 | 10 | pub use data_channel::DataChannel; 11 | pub use data_port::DataPort; 12 | pub use identity_receiver::IdentityReceiverImpl; 13 | pub use packet_receiver::PacketReceiverImpl; 14 | pub use packet_sender::PacketSenderImpl; 15 | pub use socket::Socket; 16 | -------------------------------------------------------------------------------- /socket/client/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::{error::Error, fmt}; 2 | 3 | /// An Error type specifically related to the Naia Client Socket 4 | /// This is under construction and needs to be cleaned up 5 | #[derive(Debug)] 6 | pub enum NaiaClientSocketError { 7 | /// A simple error message 8 | Message(String), 9 | /// An error indicating an inability to send to the given address 10 | SendError, 11 | } 12 | 13 | impl fmt::Display for NaiaClientSocketError { 14 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 15 | match self { 16 | NaiaClientSocketError::Message(msg) => write!(f, "Naia Client Socket Error: {}", msg), 17 | NaiaClientSocketError::SendError => write!(f, "Naia Client Socket Send Error"), 18 | } 19 | } 20 | } 21 | 22 | impl Error for NaiaClientSocketError {} 23 | -------------------------------------------------------------------------------- /socket/client/src/identity_receiver.rs: -------------------------------------------------------------------------------- 1 | use naia_socket_shared::IdentityToken; 2 | 3 | pub enum IdentityReceiverResult { 4 | Waiting, 5 | Success(IdentityToken), 6 | ErrorResponseCode(u16), 7 | } 8 | 9 | /// Used to receive an IdentityToken from the Client Socket 10 | pub trait IdentityReceiver: IdentityReceiverClone + Send + Sync { 11 | /// Receives an IdentityToken from the Client Socket 12 | fn receive(&mut self) -> IdentityReceiverResult; 13 | } 14 | 15 | /// Used to clone Box 16 | pub trait IdentityReceiverClone { 17 | /// Clone the boxed IdentityReceiver 18 | fn clone_box(&self) -> Box; 19 | } 20 | 21 | impl IdentityReceiverClone for T { 22 | fn clone_box(&self) -> Box { 23 | Box::new(self.clone()) 24 | } 25 | } 26 | 27 | impl Clone for Box { 28 | fn clone(&self) -> Box { 29 | IdentityReceiverClone::clone_box(self.as_ref()) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /socket/client/src/packet_receiver.rs: -------------------------------------------------------------------------------- 1 | use super::{error::NaiaClientSocketError, server_addr::ServerAddr}; 2 | 3 | /// Used to receive packets from the Client Socket 4 | pub trait PacketReceiver: PacketReceiverClone + Send + Sync { 5 | /// Receives a packet from the Client Socket 6 | fn receive(&mut self) -> Result, NaiaClientSocketError>; 7 | /// Get the Server's Socket address 8 | fn server_addr(&self) -> ServerAddr; 9 | } 10 | 11 | /// Used to clone Box 12 | pub trait PacketReceiverClone { 13 | /// Clone the boxed PacketReceiver 14 | fn clone_box(&self) -> Box; 15 | } 16 | 17 | impl PacketReceiverClone for T { 18 | fn clone_box(&self) -> Box { 19 | Box::new(self.clone()) 20 | } 21 | } 22 | 23 | impl Clone for Box { 24 | fn clone(&self) -> Box { 25 | PacketReceiverClone::clone_box(self.as_ref()) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /socket/client/src/packet_sender.rs: -------------------------------------------------------------------------------- 1 | use super::{error::NaiaClientSocketError, server_addr::ServerAddr}; 2 | 3 | /// Used to send packets from the Client Socket 4 | pub trait PacketSender: PacketSenderClone + Send + Sync { 5 | /// Sends a packet from the Client Socket 6 | fn send(&self, payload: &[u8]) -> Result<(), NaiaClientSocketError>; 7 | /// Get the Server's Socket address 8 | fn server_addr(&self) -> ServerAddr; 9 | /// Get whether the Client Socket is connected 10 | fn connected(&self) -> bool; 11 | /// Disconnect the Client Socket 12 | fn disconnect(&mut self); 13 | } 14 | 15 | /// Used to clone Box 16 | pub trait PacketSenderClone { 17 | /// Clone the boxed PacketSender 18 | fn clone_box(&self) -> Box; 19 | } 20 | 21 | impl PacketSenderClone for T { 22 | fn clone_box(&self) -> Box { 23 | Box::new(self.clone()) 24 | } 25 | } 26 | 27 | impl Clone for Box { 28 | fn clone(&self) -> Box { 29 | PacketSenderClone::clone_box(self.as_ref()) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /socket/client/src/server_addr.rs: -------------------------------------------------------------------------------- 1 | use std::net::SocketAddr; 2 | 3 | /// The server's socket address, if it has been found 4 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 5 | pub enum ServerAddr { 6 | /// Client has found the server's socket address 7 | Found(SocketAddr), 8 | /// Client is still finding the server's socket address 9 | Finding, 10 | } 11 | 12 | impl ServerAddr { 13 | pub fn is_found(&self) -> bool { 14 | match self { 15 | ServerAddr::Found(_) => true, 16 | ServerAddr::Finding => false, 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /socket/server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-server-socket" 3 | version = "0.24.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../.." 6 | description = "An abstraction to provide a common API over either a UDP socket or a service that can establish WebRTC connections" 7 | documentation = "https://docs.rs/naia-server-socket" 8 | homepage = "https://github.com/naia-lib/naia" 9 | repository = "https://github.com/naia-lib/naia" 10 | keywords = ["webrtc", "udp", "server", "gamedev", "networking"] 11 | categories = ["network-programming", "game-development", "wasm", "web-programming"] 12 | license = "MIT OR Apache-2.0" 13 | edition = "2021" 14 | 15 | [badges] 16 | maintenance = { status = "actively-developed" } 17 | 18 | [dependencies] 19 | naia-socket-shared = { version = "0.24", path = "../shared" } 20 | log = { version = "0.4" } 21 | futures-channel = { version = "0.3", features = ["sink"] } 22 | futures-core = { version = "0.3" } 23 | futures-util = { version = "0.3", features = ["sink"] } 24 | smol = { version = "1.3" } 25 | once_cell = { version = "1.4.1" } 26 | webrtc-unreliable = { version = "0.5.2" } 27 | async-dup = { version = "1.2.2" } 28 | http = { version = "0.2" } 29 | base64 = { version = "0.13" } -------------------------------------------------------------------------------- /socket/server/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::{error::Error, fmt, net::SocketAddr}; 2 | 3 | /// An Error type specifically related to the Naia Server Socket 4 | /// This is under construction and needs to be cleaned up 5 | #[derive(Debug)] 6 | pub enum NaiaServerSocketError { 7 | /// A wrapped error from another library/codebase 8 | Wrapped(Box), 9 | /// An error indicating an inability to send to the given address 10 | SendError(SocketAddr), 11 | } 12 | 13 | impl fmt::Display for NaiaServerSocketError { 14 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 15 | match self { 16 | NaiaServerSocketError::Wrapped(boxed_err) => fmt::Display::fmt(boxed_err.as_ref(), f), 17 | NaiaServerSocketError::SendError(addr) => fmt::Display::fmt(&addr, f), 18 | } 19 | } 20 | } 21 | 22 | impl Error for NaiaServerSocketError {} 23 | -------------------------------------------------------------------------------- /socket/server/src/executor.rs: -------------------------------------------------------------------------------- 1 | use std::{future::Future, panic::catch_unwind, thread}; 2 | 3 | use once_cell::sync::Lazy; 4 | use smol::{block_on, future, Executor, Task}; 5 | 6 | /// TODO: make description 7 | pub fn spawn(future: impl Future + Send + 'static) -> Task { 8 | static GLOBAL: Lazy> = Lazy::new(|| { 9 | for n in 1..=4 { 10 | thread::Builder::new() 11 | .name(format!("smol-{}", n)) 12 | .spawn(|| loop { 13 | catch_unwind(|| block_on(GLOBAL.run(future::pending::<()>()))).ok(); 14 | }) 15 | .expect("cannot spawn executor thread"); 16 | } 17 | 18 | Executor::new() 19 | }); 20 | 21 | GLOBAL.spawn(future) 22 | } 23 | -------------------------------------------------------------------------------- /socket/server/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Naia Server Socket 2 | //! Provides an abstraction of a Socket capable of sending/receiving to many 3 | //! clients, using either an underlying UdpSocket or a service that can 4 | //! communicate via unreliable WebRTC datachannels 5 | 6 | #![deny( 7 | trivial_casts, 8 | trivial_numeric_casts, 9 | unstable_features, 10 | unused_import_braces, 11 | unused_qualifications 12 | )] 13 | 14 | extern crate log; 15 | 16 | mod async_socket; 17 | mod auth_receiver; 18 | mod auth_sender; 19 | mod conditioned_packet_receiver; 20 | mod error; 21 | mod packet_receiver; 22 | mod packet_sender; 23 | mod server_addrs; 24 | mod session; 25 | mod socket; 26 | 27 | /// Executor for Server 28 | pub mod executor; 29 | 30 | pub use auth_receiver::AuthReceiver; 31 | pub use auth_sender::AuthSender; 32 | pub use error::NaiaServerSocketError; 33 | pub use naia_socket_shared as shared; 34 | pub use packet_receiver::PacketReceiver; 35 | pub use packet_sender::PacketSender; 36 | pub use server_addrs::ServerAddrs; 37 | pub use socket::Socket; 38 | -------------------------------------------------------------------------------- /socket/server/src/server_addrs.rs: -------------------------------------------------------------------------------- 1 | use std::{default::Default, net::SocketAddr}; 2 | 3 | /// List of addresses needed to start listening on a ServerSocket 4 | #[derive(Clone)] 5 | pub struct ServerAddrs { 6 | /// IP Address to listen on for the signaling portion of WebRTC 7 | pub session_listen_addr: SocketAddr, 8 | /// IP Address to listen on for UDP WebRTC data channels 9 | pub webrtc_listen_addr: SocketAddr, 10 | /// The public WebRTC IP address to advertise 11 | pub public_webrtc_url: String, 12 | } 13 | 14 | impl ServerAddrs { 15 | /// Create a new ServerSocketAddrs instance which will be used to start 16 | /// listening on a ServerSocket 17 | pub fn new( 18 | session_listen_addr: SocketAddr, 19 | webrtc_listen_addr: SocketAddr, 20 | public_webrtc_url: &str, 21 | ) -> Self { 22 | Self { 23 | session_listen_addr, 24 | webrtc_listen_addr, 25 | public_webrtc_url: public_webrtc_url.to_string(), 26 | } 27 | } 28 | } 29 | 30 | impl Default for ServerAddrs { 31 | fn default() -> Self { 32 | Self::new( 33 | "127.0.0.1:14191" 34 | .parse() 35 | .expect("could not parse HTTP address/port"), 36 | "127.0.0.1:14192" 37 | .parse() 38 | .expect("could not parse WebRTC data address/port"), 39 | "http://127.0.0.1:14192", 40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /socket/shared/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-socket-shared" 3 | version = "0.24.0" 4 | authors = ["connorcarpenter "] 5 | workspace = "../.." 6 | description = "Common data types shared between naia-server-socket & naia-client-socket crates" 7 | documentation = "https://docs.rs/naia-socket-shared" 8 | homepage = "https://github.com/naia-lib/naia" 9 | repository = "https://github.com/naia-lib/naia" 10 | keywords = ["webrtc", "udp", "server", "gamedev", "networking"] 11 | categories = ["network-programming", "game-development", "wasm", "web-programming"] 12 | license = "MIT OR Apache-2.0" 13 | edition = "2021" 14 | 15 | [badges] 16 | maintenance = { status = "actively-developed" } 17 | 18 | [features] 19 | wbindgen = [ "wasm-bindgen", "js-sys" ] 20 | mquad = [ ] 21 | 22 | [dependencies] 23 | cfg-if = { version = "1.0" } 24 | log = { version = "0.4" } 25 | url = { version = "2.2.2" } 26 | wasm-bindgen = { version = "0.2", optional = true } 27 | js-sys = { version = "0.3.64", optional = true } 28 | 29 | [target.'cfg(not(target_arch = "wasm32"))'.dependencies] 30 | rand = { version = "0.8" } 31 | -------------------------------------------------------------------------------- /socket/shared/src/backends/miniquad/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod instant; 2 | pub mod random; 3 | -------------------------------------------------------------------------------- /socket/shared/src/backends/miniquad/random.rs: -------------------------------------------------------------------------------- 1 | extern "C" { 2 | pub fn naia_random() -> f64; 3 | } 4 | 5 | /// Container for cross-platform Random methods 6 | 7 | pub struct Random; 8 | 9 | impl Random { 10 | /// returns a random f32 value between an upper & lower bound 11 | pub fn gen_range_f32(lower: f32, upper: f32) -> f32 { 12 | unsafe { 13 | let rand_range: f32 = naia_random() as f32 * (upper - lower); 14 | rand_range + lower 15 | } 16 | } 17 | 18 | /// returns a random u32 value between an upper & lower bound 19 | pub fn gen_range_u32(lower: u32, upper: u32) -> u32 { 20 | unsafe { 21 | let rand_range: u32 = (naia_random() * f64::from(upper - lower)) as u32; 22 | rand_range + lower 23 | } 24 | } 25 | 26 | /// returns a random boolean value between an upper & lower bound 27 | pub fn gen_bool() -> bool { 28 | unsafe { naia_random() < 0.5 } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /socket/shared/src/backends/mod.rs: -------------------------------------------------------------------------------- 1 | cfg_if! { 2 | if #[cfg(all(target_arch = "wasm32", feature = "wbindgen"))] { 3 | mod wasm_bindgen; 4 | pub use self::wasm_bindgen::random::Random; 5 | pub use self::wasm_bindgen::instant::Instant; 6 | } 7 | else if #[cfg(all(target_arch = "wasm32", feature = "mquad"))] { 8 | mod miniquad; 9 | pub use self::miniquad::random::Random; 10 | pub use self::miniquad::instant::Instant; 11 | } 12 | else { 13 | mod native; 14 | pub use native::random::Random; 15 | pub use native::instant::Instant; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /socket/shared/src/backends/native/instant.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | /// Represents a specific moment in time 4 | #[derive(Clone, Eq, PartialEq, Ord, PartialOrd)] 5 | pub struct Instant { 6 | inner: std::time::Instant, 7 | } 8 | 9 | impl Instant { 10 | /// Creates an Instant from the moment the method is called 11 | pub fn now() -> Self { 12 | Self { 13 | inner: std::time::Instant::now(), 14 | } 15 | } 16 | 17 | /// Returns time elapsed since the Instant 18 | pub fn elapsed(&self, now: &Self) -> Duration { 19 | now.inner - self.inner 20 | } 21 | 22 | /// Returns time until the Instant occurs 23 | pub fn until(&self, now: &Self) -> Duration { 24 | self.inner.duration_since(now.inner()) 25 | } 26 | 27 | pub fn is_after(&self, other: &Self) -> bool { 28 | self.inner > other.inner 29 | } 30 | 31 | /// Adds a given number of milliseconds to the Instant 32 | pub fn add_millis(&mut self, millis: u32) { 33 | self.inner += Duration::from_millis(millis.into()); 34 | } 35 | 36 | /// Subtracts a given number of milliseconds to the Instant 37 | pub fn subtract_millis(&mut self, millis: u32) { 38 | self.inner -= Duration::from_millis(millis.into()); 39 | } 40 | 41 | /// Returns inner Instant implementation 42 | pub fn inner(&self) -> std::time::Instant { 43 | self.inner 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /socket/shared/src/backends/native/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod instant; 2 | pub mod random; 3 | -------------------------------------------------------------------------------- /socket/shared/src/backends/native/random.rs: -------------------------------------------------------------------------------- 1 | use rand::Rng; 2 | 3 | /// Container for cross-platform Random methods 4 | 5 | pub struct Random; 6 | 7 | impl Random { 8 | /// returns a random f32 value between an upper & lower bound 9 | pub fn gen_range_f32(lower: f32, upper: f32) -> f32 { 10 | rand::thread_rng().gen_range(lower..upper) 11 | } 12 | 13 | /// returns a random u32 value between an upper & lower bound 14 | pub fn gen_range_u32(lower: u32, upper: u32) -> u32 { 15 | rand::thread_rng().gen_range(lower..upper) 16 | } 17 | 18 | /// returns a random i32 value between an upper & lower bound 19 | pub fn gen_range_i32(lower: i32, upper: i32) -> i32 { 20 | rand::thread_rng().gen_range(lower..upper) 21 | } 22 | 23 | /// returns a random boolean value between an upper & lower bound 24 | pub fn gen_bool() -> bool { 25 | rand::thread_rng().gen_bool(0.5) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /socket/shared/src/backends/wasm_bindgen/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod instant; 2 | pub mod random; 3 | -------------------------------------------------------------------------------- /socket/shared/src/backends/wasm_bindgen/random.rs: -------------------------------------------------------------------------------- 1 | use js_sys::Math::random; 2 | 3 | /// Container for cross-platform Random methods 4 | 5 | pub struct Random; 6 | 7 | impl Random { 8 | /// returns a random f32 value between an upper & lower bound 9 | pub fn gen_range_f32(lower: f32, upper: f32) -> f32 { 10 | let rand_range: f32 = random() as f32 * (upper - lower); 11 | rand_range + lower 12 | } 13 | 14 | /// returns a random u32 value between an upper & lower bound 15 | pub fn gen_range_u32(lower: u32, upper: u32) -> u32 { 16 | let rand_range: u32 = (random() * f64::from(upper - lower)) as u32; 17 | rand_range + lower 18 | } 19 | 20 | /// returns a random i32 value between an upper & lower bound 21 | pub fn gen_range_i32(lower: i32, upper: i32) -> i32 { 22 | let rand_range: i32 = (random() * f64::from(upper - lower)) as i32; 23 | rand_range + lower 24 | } 25 | 26 | /// returns a random boolean value between an upper & lower bound 27 | pub fn gen_bool() -> bool { 28 | random() < 0.5 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /socket/shared/src/identity_token.rs: -------------------------------------------------------------------------------- 1 | use crate::Random; 2 | 3 | pub type IdentityToken = String; 4 | 5 | pub fn generate_identity_token() -> IdentityToken { 6 | // generate random string of 32 characters 7 | let mut token = String::new(); 8 | 9 | for _ in 0..32 { 10 | let random_char = std::char::from_u32(Random::gen_range_u32(97, 122)).unwrap(); 11 | token.push(random_char); 12 | } 13 | 14 | token 15 | } 16 | -------------------------------------------------------------------------------- /socket/shared/src/link_condition_logic.rs: -------------------------------------------------------------------------------- 1 | extern crate log; 2 | // use log::info; 3 | 4 | use super::{link_conditioner_config::LinkConditionerConfig, time_queue::TimeQueue, Instant}; 5 | use crate::Random; 6 | 7 | /// Given a config object which describes the network conditions to be 8 | /// simulated, process an incoming packet, adding it to a TimeQueue at the 9 | /// correct timestamp 10 | pub fn process_packet( 11 | config: &LinkConditionerConfig, 12 | time_queue: &mut TimeQueue, 13 | packet: T, 14 | ) { 15 | if Random::gen_range_f32(0.0, 1.0) <= config.incoming_loss { 16 | // drop the packet 17 | return; 18 | } 19 | let mut latency: u32 = config.incoming_latency; 20 | if config.incoming_jitter > 0 { 21 | if Random::gen_bool() { 22 | latency += Random::gen_range_u32(0, config.incoming_jitter); 23 | } else { 24 | latency -= Random::gen_range_u32(0, config.incoming_jitter); 25 | } 26 | } 27 | let mut packet_timestamp = Instant::now(); 28 | packet_timestamp.add_millis(latency); 29 | time_queue.add_item(packet_timestamp, packet); 30 | } 31 | -------------------------------------------------------------------------------- /socket/shared/src/socket_config.rs: -------------------------------------------------------------------------------- 1 | use std::default::Default; 2 | 3 | use super::link_conditioner_config::LinkConditionerConfig; 4 | 5 | const DEFAULT_RTC_PATH: &str = "rtc_session"; 6 | 7 | /// Contains Config properties which will be shared by Server and Client sockets 8 | #[derive(Clone)] 9 | pub struct SocketConfig { 10 | /// Configuration used to simulate network conditions 11 | pub link_condition: Option, 12 | /// The endpoint URL path to use for initiating new WebRTC sessions 13 | pub rtc_endpoint_path: String, 14 | } 15 | 16 | impl SocketConfig { 17 | /// Creates a new SocketConfig 18 | pub fn new( 19 | link_condition: Option, 20 | rtc_endpoint_path: Option, 21 | ) -> Self { 22 | let endpoint_path = { 23 | if let Some(path) = rtc_endpoint_path { 24 | path 25 | } else { 26 | DEFAULT_RTC_PATH.to_string() 27 | } 28 | }; 29 | 30 | SocketConfig { 31 | link_condition, 32 | rtc_endpoint_path: endpoint_path, 33 | } 34 | } 35 | } 36 | 37 | impl Default for SocketConfig { 38 | fn default() -> Self { 39 | Self { 40 | link_condition: None, 41 | rtc_endpoint_path: DEFAULT_RTC_PATH.to_string(), 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "naia-test" 3 | version = "0.1.0" 4 | authors = ["connorcarpenter "] 5 | workspace = ".." 6 | license = "MIT OR Apache-2.0" 7 | edition = "2021" 8 | publish = false 9 | 10 | [features] 11 | 12 | 13 | [dependencies] 14 | naia-server = { path = "../server" } 15 | naia-client = { path = "../client" } 16 | naia-shared = { path = "../shared" } 17 | 18 | -------------------------------------------------------------------------------- /test/src/auth.rs: -------------------------------------------------------------------------------- 1 | use naia_shared::Message; 2 | 3 | #[derive(Message)] 4 | pub struct Auth { 5 | pub username: String, 6 | pub password: String, 7 | } 8 | 9 | impl Auth { 10 | pub fn new(username: &str, password: &str) -> Self { 11 | Self { 12 | username: username.to_string(), 13 | password: password.to_string(), 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod auth; 2 | 3 | pub use auth::Auth; 4 | --------------------------------------------------------------------------------