Skip to content

Commit

Permalink
Merge pull request godot-rust#949 from godot-rust/qol/rpc-docs
Browse files Browse the repository at this point in the history
Add docs for `#[rpc]`
  • Loading branch information
Bromeon authored Nov 10, 2024
2 parents b3086b5 + 343eb47 commit f40fa27
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 16 deletions.
3 changes: 3 additions & 0 deletions godot-codegen/src/special_cases/special_cases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,9 @@ pub fn maybe_rename_virtual_method(rust_method_name: &str) -> &str {
}
}

// TODO method-level extra docs, for:
// - Node::rpc_config() -> link to RpcConfig.

pub fn get_class_extra_docs(class_name: &TyName) -> Option<&'static str> {
match class_name.godot_ty.as_str() {
"FileAccess" => {
Expand Down
5 changes: 0 additions & 5 deletions godot-core/src/meta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,6 @@ mod class_name;
mod godot_convert;
mod method_info;
mod property_info;
// RpcConfig uses MultiplayerPeer::TransferMode and MultiplayerApi::RpcMode, which are only enabled in `codegen-full` feature.
#[cfg(feature = "codegen-full")]
mod rpc_config;
mod sealed;
mod signature;
mod traits;
Expand All @@ -61,8 +58,6 @@ pub mod error;
pub use args::*;
pub use class_name::ClassName;
pub use godot_convert::{FromGodot, GodotConvert, ToGodot};
#[cfg(feature = "codegen-full")]
pub use rpc_config::RpcConfig;
pub use traits::{ArrayElement, GodotType, PackedArrayElement};

pub(crate) use array_type_info::ArrayTypeInfo;
Expand Down
2 changes: 1 addition & 1 deletion godot-core/src/obj/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ pub trait IndexEnum: EngineEnum {
#[diagnostic::on_unimplemented(
message = "Class `{Self}` requires a `Base<T>` field",
label = "missing field `_base: Base<...>`",
note = "A base field is required to access the base from within `self`, or when using script virtual functions",
note = "A base field is required to access the base from within `self`, for script-virtual functions or #[rpc] methods",
note = "see also: https://godot-rust.github.io/book/register/classes.html#the-base-field"
)]
pub trait WithBaseField: GodotClass + Bounds<Declarer = bounds::DeclUser> {
Expand Down
8 changes: 7 additions & 1 deletion godot-core/src/registry/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

// Note: final re-exports from godot-core are in lib.rs, mod private_register.
// Note: final re-exports from godot-core are in lib.rs, mod register::private.
// These are public here for simplicity, but many are not imported by the main crate.

pub mod callbacks;
Expand All @@ -15,5 +15,11 @@ pub mod method;
pub mod plugin;
pub mod property;

// RpcConfig uses MultiplayerPeer::TransferMode and MultiplayerApi::RpcMode, which are only enabled in `codegen-full` feature.
#[cfg(feature = "codegen-full")]
mod rpc_config;
#[cfg(feature = "codegen-full")]
pub use rpc_config::RpcConfig;

#[doc(hidden)]
pub mod godot_register_wrappers;
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ use crate::{arg_into_ref, dict};

/// Configuration for a remote procedure call, used with `#[rpc(config = ...)]`.
///
/// See [Godot documentation](https://docs.godotengine.org/en/stable/tutorials/networking/high_level_multiplayer.html#remote-procedure-calls).
/// Check documentation of the [`#[rpc]` attribute](attr.godot_api.html#rpc-attributes) for usage.
///
/// See also [Godot `@rpc` keyword](https://docs.godotengine.org/en/stable/tutorials/networking/high_level_multiplayer.html#remote-procedure-calls).
#[derive(Copy, Clone, Debug)]
pub struct RpcConfig {
pub rpc_mode: RpcMode,
Expand Down
16 changes: 10 additions & 6 deletions godot-macros/src/class/data_models/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub enum RpcAttr {
call_local: Option<bool>,
channel: Option<u32>,
},

// `args` key in the `rpc` attribute.
// Example:
// const RPC_CFG: RpcConfig = RpcConfig { mode: RpcMode::Authority, ..RpcConfig::default() };
Expand Down Expand Up @@ -73,19 +74,22 @@ pub fn make_rpc_registrations_fn(class_name: &Ident, funcs: &[FuncDefinition]) -
}

quote! {
#[allow(clippy::needless_update)] // clippy complains about using `..RpcConfig::default()` if all fields are overridden
// Clippy complains about using `..RpcConfig::default()` if all fields are overridden.
#[allow(clippy::needless_update)]
fn __register_rpcs(object: &mut dyn ::std::any::Any) {
use ::std::any::Any;
use ::godot::meta::RpcConfig;
use ::godot::register::RpcConfig;
use ::godot::classes::multiplayer_api::RpcMode;
use ::godot::classes::multiplayer_peer::TransferMode;
use ::godot::classes::Node;
use ::godot::obj::{WithBaseField, Gd};

let mut gd = object
.downcast_mut::<#class_name>()
.expect("bad type erasure when registering RPCs")
.to_gd();
let this = object
.downcast_ref::<#class_name>()
.expect("bad type erasure when registering RPCs");

// Use fully-qualified syntax, so that error message isn't just "no method named `to_gd` found".
let mut gd = ::godot::obj::WithBaseField::to_gd(this);

let node = gd.upcast_mut::<Node>();
#( #rpc_registrations )*
Expand Down
70 changes: 70 additions & 0 deletions godot-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ pub fn derive_godot_class(input: TokenStream) -> TokenStream {
/// - [User-defined functions](#user-defined-functions)
/// - [Associated functions and methods](#associated-functions-and-methods)
/// - [Virtual methods](#virtual-methods)
/// - [RPC attributes](#rpc-attributes)
/// - [Constants and signals](#signals)
///
/// # Constructors
Expand Down Expand Up @@ -676,6 +677,75 @@ pub fn derive_godot_class(input: TokenStream) -> TokenStream {
///
/// Make sure you understand the limitations in the [tutorial](https://godot-rust.github.io/book/register/virtual-functions.html).
///
/// ## RPC attributes
///
/// You can use the `#[rpc]` attribute to let your functions act as remote procedure calls (RPCs) in Godot. This is the Rust equivalent of
/// GDScript's [`@rpc` annotation](https://docs.godotengine.org/en/stable/tutorials/networking/high_level_multiplayer.html#remote-procedure-calls).
/// `#[rpc]` is only supported for classes inheriting `Node`, and they need to declare a `Base<T>` field.
///
/// The syntax follows GDScript'a `@rpc`. You can optionally specify up to four keys; omitted ones use their default value.
/// Here's an overview:
///
/// | Setting | Type | Possible values (first is default) |
/// |---------------|------------------|----------------------------------------------------|
/// | RPC mode | [`RpcMode`] | **`authority`**, `any_peer` |
/// | Sync | `bool` | **`call_remote`**, `call_local` |
/// | Transfer mode | [`TransferMode`] | **`unreliable`**, `unreliable_ordered`, `reliable` |
/// | Channel | `u32` | any |
///
/// You can also use `#[rpc(config = value)]`, with `value` being an expression of type [`RpcConfig`] in scope, for example a `const` or the
/// call to a function. This can be useful to reuse configurations across multiple RPCs.
///
/// `#[rpc]` implies `#[func]`. You can use both attributes together, if you need to configure other `#[func]`-specific keys.
///
/// For example, the following method declarations are all equivalent:
/// ```
/// use godot::classes::multiplayer_api::RpcMode;
/// use godot::classes::multiplayer_peer::TransferMode;
/// use godot::prelude::*;
/// use godot::register::RpcConfig;
///
/// # #[derive(GodotClass)]
/// # #[class(no_init, base=Node)]
/// # struct MyStruct {
/// # base: Base<Node>,
/// # }
/// #[godot_api]
/// impl MyStruct {
/// #[rpc(unreliable_ordered, channel = 2)]
/// fn with_defaults(&mut self) {}
///
/// #[rpc(authority, unreliable_ordered, call_remote, channel = 2)]
/// fn explicit(&mut self) {}
///
/// #[rpc(config = MY_RPC_CONFIG)]
/// fn external_config_const(&mut self) {}
///
/// #[rpc(config = my_rpc_provider())]
/// fn external_config_fn(&mut self) {}
/// }
///
/// const MY_RPC_CONFIG: RpcConfig = RpcConfig {
/// rpc_mode: RpcMode::AUTHORITY,
/// transfer_mode: TransferMode::UNRELIABLE_ORDERED,
/// call_local: false,
/// channel: 2,
/// };
///
/// fn my_rpc_provider() -> RpcConfig {
/// RpcConfig {
/// transfer_mode: TransferMode::UNRELIABLE_ORDERED,
/// channel: 2,
/// ..Default::default() // only possible in fn, not in const.
/// }
/// }
/// ```
///
// Note: for some reason, the intra-doc links don't work here, despite dev-dependency on godot.
/// [`RpcMode`]: ../classes/multiplayer_api/struct.RpcMode.html
/// [`TransferMode`]: ../classes/multiplayer_peer/struct.TransferMode.html
/// [`RpcConfig`]: ../register/struct.RpcConfig.html
///
/// # Constants and signals
///
/// Please refer to [the book](https://godot-rust.github.io/book/register/constants.html).
Expand Down
4 changes: 4 additions & 0 deletions godot/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,15 @@ pub mod register {
pub use godot_core::registry::property;
pub use godot_macros::{godot_api, Export, GodotClass, GodotConvert, Var};

#[cfg(feature = "__codegen-full")]
pub use godot_core::registry::RpcConfig;

/// Re-exports used by proc-macro API.
#[doc(hidden)]
pub mod private {
#[cfg(feature = "__codegen-full")]
pub use godot_core::registry::class::auto_register_rpcs;

pub use godot_core::registry::godot_register_wrappers::*;
pub use godot_core::registry::{constant, method};
}
Expand Down
16 changes: 14 additions & 2 deletions itest/rust/src/register_tests/rpc_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

use godot::classes::multiplayer_api::RpcMode;
use godot::classes::multiplayer_peer::TransferMode;
use godot::classes::{Engine, MultiplayerApi};
use godot::meta::RpcConfig;
use godot::prelude::*;
use godot::register::RpcConfig;
use godot::test::itest;

#[derive(GodotClass)]
Expand All @@ -24,6 +25,14 @@ const CACHED_CFG: RpcConfig = RpcConfig {
channel: 1,
};

fn provide_cfg() -> RpcConfig {
RpcConfig {
transfer_mode: TransferMode::RELIABLE,
channel: 1,
..Default::default()
}
}

#[godot_api]
impl RpcTest {
#[rpc]
Expand Down Expand Up @@ -65,7 +74,10 @@ impl RpcTest {
pub fn args_func_gd_self(_this: Gd<Self>) {}

#[rpc(config = CACHED_CFG)]
pub fn arg_config(&mut self) {}
pub fn arg_config_const(&mut self) {}

#[rpc(config = provide_cfg())]
pub fn arg_config_fn(&mut self) {}
}

// ----------------------------------------------------------------------------------------------------------------------------------------------
Expand Down

0 comments on commit f40fa27

Please sign in to comment.