Skip to content

Commit

Permalink
feat: implement websocket and update godot-rust (#45)
Browse files Browse the repository at this point in the history
* wip, basic websocket implementation works

* fix weboscket, cargo update (update gdext)

* fix format

* copy ffmpeg dlls

* add TODO for insecure fetch and websocket
fix change realm crash

* fix access to properties websocket
  • Loading branch information
leanmendoza authored Sep 18, 2023
1 parent 9bbb32b commit 54760ca
Show file tree
Hide file tree
Showing 33 changed files with 871 additions and 151 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ jobs:
if: ${{ matrix.os == 'windows-latest' }}
run: |
cp rust/decentraland-godot-lib/target/release/decentraland_godot_lib.dll godot/lib/
cp .bin/ffmpeg/ffmpeg-6.0-full_build-shared/bin/*.dll godot/lib/
# Export section (multi platform)
- name: Export
Expand Down
6 changes: 6 additions & 0 deletions godot/.godot/global_script_class_cache.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ list=Array[Dictionary]([{
"language": &"GDScript",
"path": "res://src/logic/rust_http_requester_wrapper.gd"
}, {
"base": &"Node",
"class": &"TestSpawnAndMoveAvatars",
"icon": "",
"language": &"GDScript",
"path": "res://src/test/avatar/spawn_and_move.gd"
}, {
"base": &"Control",
"class": &"VirtualJoystick",
"icon": "",
Expand Down
95 changes: 95 additions & 0 deletions godot/src/test/avatar/spawn_and_move.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
extends Node
class_name TestSpawnAndMoveAvatars

var test_avatar_N: int = 100
var moving_avatars: bool = false
var moving_t: float = 0
var spawning_avatars: bool = false
var spawning_i: int = 0
var spawning_position: Array[Vector3] = []

var wearable_data = {}


func set_wearable_data(_wearable_data):
wearable_data = _wearable_data


func get_random_body():
var to_pick = []
for wearable_id in wearable_data:
var wearable = wearable_data[wearable_id]
if Wearables.get_category(wearable) == Wearables.Categories.BODY_SHAPE:
to_pick.push_back(wearable_id)

return to_pick.pick_random()


func get_random_wearable(category: String, body_shape_id: String):
var to_pick = []
for wearable_id in wearable_data:
var wearable = wearable_data[wearable_id]
if Wearables.get_category(wearable) == category:
if Wearables.can_equip(wearable, body_shape_id):
to_pick.push_back(wearable_id)

return to_pick.pick_random()


func _process(dt):
var avatar_scene: AvatarScene = get_node("/root/avatars")
if spawning_avatars and spawning_i < test_avatar_N:
var body_shape_id = get_random_body()
var avatar_data = {
"base_url": "https://peer.decentraland.org/content",
"name": "Avatar#" + str(spawning_i),
"body_shape": body_shape_id,
"eyes": Color(randf(), randf(), randf()),
"hair": Color(randf(), randf(), randf()),
"skin": Color(0.8, 0.6078, 0.4667, 1),
"wearables":
[
get_random_wearable(Wearables.Categories.MOUTH, body_shape_id),
get_random_wearable(Wearables.Categories.HAIR, body_shape_id),
get_random_wearable(Wearables.Categories.UPPER_BODY, body_shape_id),
get_random_wearable(Wearables.Categories.LOWER_BODY, body_shape_id),
get_random_wearable(Wearables.Categories.FEET, body_shape_id),
get_random_wearable(Wearables.Categories.EYES, body_shape_id),
],
"emotes": []
}

var initial_position := (
Vector3(randf_range(-10, 10), 0.0, randf_range(-10, 10)).normalized()
)
var transform = Transform3D(Basis.IDENTITY, initial_position)
var alias = 10000 + spawning_i
avatar_scene.add_avatar(alias)
avatar_scene.update_avatar_profile(alias, avatar_data)
avatar_scene.update_avatar_transform(alias, transform)

spawning_position.append(initial_position)

spawning_i = spawning_i + 1
if spawning_i >= test_avatar_N:
spawning_avatars = false
moving_avatars = true
elif moving_avatars:
moving_t += dt
if moving_t >= 0.1:
var WALK_SPEED = 2.0
var walk_delta = WALK_SPEED * moving_t
moving_t = 0
for i in range(spawning_position.size()):
var delta_position = (
Vector3(randf_range(-1, 1), 0.0, randf_range(-1, 1)).normalized() * walk_delta
)
var current_position = spawning_position[i]
var target_position = current_position + delta_position
var transform = Transform3D(Basis.IDENTITY, current_position)
transform = transform.looking_at(target_position)
transform.origin = target_position

spawning_position[i] = target_position
var alias = 10000 + i
avatar_scene.update_avatar_transform(alias, transform)
5 changes: 4 additions & 1 deletion rust/decentraland-godot-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ publish = false
crate-type = ["cdylib"]

[dependencies]
godot = { git = "https://github.com/godot-rust/gdext", rev = "88d22f801a11e34243890b4f3d4530a6e368b0e1", features = ["threads"] }
godot = { git = "https://github.com/godot-rust/gdext", rev = "00ba8c9d", features = ["threads"] }
rand = "0.8"
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0.92", features = ["raw_value"] }
Expand Down Expand Up @@ -45,5 +45,8 @@ num = "0.4"
http = "0.2.9"
bytes = "1.4.0"

tokio-tungstenite = "*"
futures-util = "*"

[build-dependencies]
prost-build = "0.11.8"
2 changes: 1 addition & 1 deletion rust/decentraland-godot-lib/src/av/ffmpeg_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ impl PacketIter for InputWrapper {

fn reset(&mut self) {
let Some(input) = self.get_input(false) else {
return
return;
};

if input.seek(0, ..).is_err() {
Expand Down
4 changes: 2 additions & 2 deletions rust/decentraland-godot-lib/src/av/video_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use ffmpeg_next::software::scaling::{context::Context, flag::Flags};
use ffmpeg_next::{decoder, format::context::Input, media::Type, util::frame, Packet};
use godot::engine::image::Format;
use godot::engine::{Image, ImageTexture};
use godot::prelude::{Gd, PackedByteArray, Share, Vector2};
use godot::prelude::{Gd, PackedByteArray, Vector2};
use thiserror::Error;
use tracing::debug;

Expand Down Expand Up @@ -189,7 +189,7 @@ impl FfmpegContext for VideoContext {
data_arr,
)
.unwrap();
self.texture.set_image(image.share());
self.texture.set_image(image.clone());
self.texture.update(image);
} else {
let mut image = self.texture.get_image().unwrap();
Expand Down
4 changes: 2 additions & 2 deletions rust/decentraland-godot-lib/src/av/video_stream.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use ffmpeg_next::format::input;
use godot::{
engine::ImageTexture,
prelude::{AudioStreamPlayer, Gd, InstanceId, Share},
prelude::{AudioStreamPlayer, Gd, InstanceId},
};
use tracing::{debug, warn};

Expand Down Expand Up @@ -43,7 +43,7 @@ pub fn av_sinks(
// video_sender,
// audio_sender,
source.clone(),
tex.share(),
tex.clone(),
audio_stream_player,
);

Expand Down
84 changes: 64 additions & 20 deletions rust/decentraland-godot-lib/src/avatars/avatar_scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,69 @@ impl NodeVirtual for AvatarScene {
}
}

#[godot_api]
impl AvatarScene {
#[func]
pub fn update_avatar_profile(&mut self, alias: u32, profile: Dictionary) {
let entity_id = if let Some(entity_id) = self.avatar_entity.get(&alias) {
*entity_id
} else {
// TODO: handle this condition
return;
};

self.avatar_godot_scene
.get_mut(&entity_id)
.unwrap()
.call("update_avatar".into(), &[profile.to_variant()]);
}

#[func]
pub fn update_avatar_transform(&mut self, alias: u32, transform: Transform3D) {
let entity_id = if let Some(entity_id) = self.avatar_entity.get(&alias) {
*entity_id
} else {
// TODO: handle this condition
return;
};

let dcl_transform = DclTransformAndParent::from_godot(&transform, Vector3::ZERO);

// let avatar_scene = self.avatar_godot_scene.get_mut(&entity_id).unwrap();

// // TODO: the scale seted in the transform is local
// avatar_scene.set_transform(dcl_transform.to_godot_transform_3d());
self.avatar_godot_scene.get_mut(&entity_id).unwrap().call(
"set_target".into(),
&[dcl_transform.to_godot_transform_3d().to_variant()],
);

self.crdt
.get_transform_mut()
.put(entity_id, Some(dcl_transform));
}

#[func]
pub fn add_avatar(&mut self, alias: u32) {
// TODO: the entity Self::MAX_ENTITY_ID + 1 would be a buggy avatar
let entity_id = self
.get_next_entity_id()
.unwrap_or(SceneEntityId::new(Self::MAX_ENTITY_ID + 1, 0));
self.crdt.entities.try_init(entity_id);

self.avatar_entity.insert(alias, entity_id);

let new_avatar =
godot::engine::load::<PackedScene>("res://src/decentraland_components/avatar.tscn")
.instantiate()
.unwrap()
.cast::<Node3D>();

self.base.add_child(new_avatar.clone().upcast());
self.avatar_godot_scene.insert(entity_id, new_avatar);
}
}

impl AvatarScene {
const FROM_ENTITY_ID: u16 = 10;
const MAX_ENTITY_ID: u16 = 200;
Expand Down Expand Up @@ -69,30 +132,11 @@ impl AvatarScene {
}
}

pub fn add_avatar(&mut self, alias: u32) {
// TODO: the entity Self::MAX_ENTITY_ID + 1 would be a buggy avatar
let entity_id = self
.get_next_entity_id()
.unwrap_or(SceneEntityId::new(Self::MAX_ENTITY_ID + 1, 0));
self.crdt.entities.try_init(entity_id);

self.avatar_entity.insert(alias, entity_id);

let new_avatar =
godot::engine::load::<PackedScene>("res://src/decentraland_components/avatar.tscn")
.instantiate()
.unwrap()
.cast::<Node3D>();

self.base.add_child(new_avatar.share().upcast());
self.avatar_godot_scene.insert(entity_id, new_avatar);
}

pub fn remove_avatar(&mut self, alias: u32) {
if let Some(entity_id) = self.avatar_entity.remove(&alias) {
self.crdt.kill_entity(&entity_id);
let mut avatar = self.avatar_godot_scene.remove(&entity_id).unwrap();
self.base.remove_child(avatar.share().upcast());
self.base.remove_child(avatar.clone().upcast());
avatar.queue_free();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ impl CommunicationManager {
fn init_rs(&mut self) {
let mut realm = self.realm();
let on_realm_changed =
Callable::from_object_method(self.base.share(), StringName::from("_on_realm_changed"));
Callable::from_object_method(self.base.clone(), StringName::from("_on_realm_changed"));

realm.connect("realm_changed".into(), on_realm_changed);

Expand Down Expand Up @@ -200,7 +200,7 @@ impl CommunicationManager {
tracing::info!("comms > websocket to {}", ws_url);
self.current_adapter = Adapter::WsRoom(WebSocketRoom::new(
ws_url,
self.tls_client.as_ref().unwrap().share(),
self.tls_client.as_ref().unwrap().clone(),
self.player_identity.clone(),
avatar_scene,
));
Expand Down
4 changes: 3 additions & 1 deletion rust/decentraland-godot-lib/src/comms/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,9 @@ impl AsH160 for &str {
return (&self[2..]).as_h160();
}

let Ok(hex_bytes) = hex::decode(self.as_bytes()) else { return None };
let Ok(hex_bytes) = hex::decode(self.as_bytes()) else {
return None;
};
if hex_bytes.len() != H160::len_bytes() {
return None;
}
Expand Down
14 changes: 7 additions & 7 deletions rust/decentraland-godot-lib/src/comms/ws_room.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,12 @@ impl WebSocketRoom {
return false;
}

let buf = PackedByteArray::from_iter(buf.into_iter());
let buf = PackedByteArray::from_iter(buf);
matches!(self.ws_peer.send(buf), godot::engine::global::Error::OK)
}

pub fn poll(&mut self) {
let mut peer = self.ws_peer.share();
let mut peer = self.ws_peer.clone();
peer.poll();

let ws_state = peer.get_ready_state();
Expand All @@ -167,7 +167,7 @@ impl WebSocketRoom {
godot::engine::web_socket_peer::State::STATE_CLOSED => {
if (Instant::now() - self.last_try_to_connect).as_secs() > 1 {
// TODO: see if the tls client is really required for now
let _tls_client = self.tls_client.share();
let _tls_client = self.tls_client.clone();

let ws_protocols = {
let mut v = PackedStringArray::new();
Expand Down Expand Up @@ -213,7 +213,7 @@ impl WebSocketRoom {
},
WsRoomState::IdentMessageSent => match ws_state {
godot::engine::web_socket_peer::State::STATE_OPEN => {
while let Some((packet_length, message)) = get_next_packet(peer.share()) {
while let Some((packet_length, message)) = get_next_packet(peer.clone()) {
match message {
ws_packet::Message::ChallengeMessage(challenge_msg) => {
tracing::info!("comms > peer msg {:?}", challenge_msg);
Expand Down Expand Up @@ -269,7 +269,7 @@ impl WebSocketRoom {
},
WsRoomState::ChallengeMessageSent => match ws_state {
godot::engine::web_socket_peer::State::STATE_OPEN => {
while let Some((packet_length, message)) = get_next_packet(peer.share()) {
while let Some((packet_length, message)) = get_next_packet(peer.clone()) {
match message {
ws_packet::Message::WelcomeMessage(welcome_msg) => {
// welcome_msg.
Expand Down Expand Up @@ -347,7 +347,7 @@ impl WebSocketRoom {
}

pub fn clean(&self) {
let mut peer = self.ws_peer.share();
let mut peer = self.ws_peer.clone();
peer.close();
match peer.get_ready_state() {
godot::engine::web_socket_peer::State::STATE_OPEN
Expand All @@ -359,7 +359,7 @@ impl WebSocketRoom {
}

fn _handle_messages(&mut self) {
while let Some((_packet_length, message)) = get_next_packet(self.ws_peer.share()) {
while let Some((_packet_length, message)) = get_next_packet(self.ws_peer.clone()) {
match message {
ws_packet::Message::ChallengeMessage(_)
| ws_packet::Message::PeerIdentification(_)
Expand Down
Loading

0 comments on commit 54760ca

Please sign in to comment.