Skip to content

Commit

Permalink
Implement Camera Rendering (#35)
Browse files Browse the repository at this point in the history
* Initial

* Add More camera stuff

* Finish Protobuf

* Work

* Implement Camera Movement

* Camera Close Logic

* Work on the structure of camera API

* Add camera manager to base

* Camera Renderer

* Updates for async

* Convert Colour to RGB Rather than RGBA and then pasting

* Type Hinting

* Update README.md

* Finalise the camera frame stuff

* Optimise by removing some depth
  • Loading branch information
olijeffers0n authored Mar 14, 2023
1 parent 7c5a271 commit 9a8703f
Show file tree
Hide file tree
Showing 24 changed files with 647 additions and 96 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ It should also install all the dependencies, but if not you will have to install
- Send Team Messages
- Getting the time
- Using Smart Devices
- And so much more!
- Camera Handling

# For information on all the above methods, see the [Wiki](https://rplus.ollieee.xyz)

Expand Down
44 changes: 44 additions & 0 deletions rustplus.proto
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ message AppRequest {
optional AppEmpty getClanChat = 23;
optional AppSendMessage sendClanMessage = 24;
optional AppGetNexusAuth getNexusAuth = 25;
optional AppCameraSubscribe cameraSubscribe = 30;
optional AppEmpty cameraUnsubscribe = 31;
optional AppCameraInput cameraInput = 32;
}

message AppMessage {
Expand All @@ -175,6 +178,7 @@ message AppResponse {
optional AppClanInfo clanInfo = 15;
optional AppClanChat clanChat = 16;
optional AppNexusAuth nexusAuth = 17;
optional AppCameraInfo cameraSubscribeInfo = 20;
}

message AppBroadcast {
Expand All @@ -183,6 +187,7 @@ message AppBroadcast {
optional AppEntityChanged entityChanged = 6;
optional AppClanChanged clanChanged = 7;
optional AppNewClanMessage clanMessage = 8;
optional AppCameraRays cameraRays = 10;
}

message AppEmpty {
Expand Down Expand Up @@ -385,3 +390,42 @@ message AppNewClanMessage {
required int64 clanId = 1;
required AppClanMessage message = 2;
}

message AppCameraSubscribe {
required string cameraId = 1;
}

message AppCameraInput {
required int32 buttons = 1;
required Vector2 mouseDelta = 2;
}

message AppCameraInfo {
required int32 width = 1;
required int32 height = 2;
required float nearPlane = 3;
required float farPlane = 4;
required int32 controlFlags = 5;
}

message AppCameraRays {
required float verticalFov = 1;
required int32 sampleOffset = 2;
required bytes rayData = 3;
required float distance = 4;
repeated AppCameraRays.Entity entities = 5;

enum EntityType {
Tree = 1;
Player = 2;
}

message Entity {
required uint32 entityId = 1;
required EntityType type = 2;
required Vector3 position = 3;
required Vector3 rotation = 4;
required Vector3 size = 5;
optional string name = 6;
}
}
5 changes: 3 additions & 2 deletions rustplus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@
ProtobufEvent,
RegisteredListener,
)
from .api.structures import RustMarker
from .api.structures import RustMarker, Vector
from .api.remote.fcm_listener import FCMListener
from .api.remote.ratelimiter import RateLimiter
from .api.remote.camera import CameraManager, MovementControls, CameraMovementOptions
from .commands import CommandOptions, Command
from .exceptions import *
from .conversation import ConversationFactory, Conversation, ConversationPrompt
from .utils import *

__name__ = "rustplus"
__author__ = "olijeffers0n"
__version__ = "5.5.4"
__version__ = "5.5.5"
__support__ = "Discord: https://discord.gg/nQqJe8qvP8"
13 changes: 13 additions & 0 deletions rustplus/api/base_rust_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .structures import *
from .remote.rustplus_proto import AppEmpty, AppRequest
from .remote import RustRemote, HeartBeat, MapEventListener, ServerChecker, RateLimiter
from .remote.camera import CameraManager
from ..commands import CommandOptions, CommandHandler
from ..commands.command_data import CommandData
from ..exceptions import *
Expand Down Expand Up @@ -585,3 +586,15 @@ async def get_tc_storage_contents(
Do not use this for any other storage monitor than a TC
"""
raise NotImplementedError("Not Implemented")

async def get_camera_manager(self, id: str) -> CameraManager:
"""
Gets a camera manager for a given camera ID
NOTE: This will override the current camera manager if one exists for the given ID so you cannot have multiple
:param id: The ID of the camera
:return CameraManager: The camera manager
:raises RequestError: If the camera is not found or you cannot access it. See reason for more info
"""
raise NotImplementedError("Not Implemented")
3 changes: 3 additions & 0 deletions rustplus/api/remote/camera/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .camera_manager import CameraManager
from .camera_constants import CameraMovementOptions, MovementControls
from .structures import CameraInfo
35 changes: 35 additions & 0 deletions rustplus/api/remote/camera/camera_constants.py

Large diffs are not rendered by default.

96 changes: 96 additions & 0 deletions rustplus/api/remote/camera/camera_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import time
from typing import Iterable, Union

from PIL import Image

from .camera_parser import Parser
from ..rustplus_proto import AppCameraInput, Vector2, AppEmpty
from ...structures import Vector
from .structures import CameraInfo, LimitedQueue


class CameraManager:
def __init__(self, rust_socket, cam_id, cam_info_message) -> None:
self.rust_socket = rust_socket
self._cam_id = cam_id
self._last_packets: LimitedQueue = LimitedQueue(5)
self._cam_info_message: CameraInfo = CameraInfo(cam_info_message)
self._open = True
self.parser = Parser(
self._cam_info_message.width, self._cam_info_message.height
)
self.time_since_last_subscribe = time.time()

def add_packet(self, packet) -> None:
self._last_packets.add(packet)

def has_frame_data(self) -> bool:
return len(self._last_packets) > 0

async def get_frame(self) -> Union[Image.Image, None]:
if self._last_packets is None:
return None

if not self._open:
raise Exception("Camera is closed")

for i in range(len(self._last_packets)):
await self.parser.handle_camera_ray_data(self._last_packets.get(i))
await self.parser.step()

return await self.parser.render()

def can_move(self, control_type: int) -> bool:
return self._cam_info_message.is_move_option_permissible(control_type)

async def clear_movement(self) -> None:
await self.send_combined_movement()

async def send_actions(self, actions: Iterable[int]) -> None:
await self.send_combined_movement(actions)

async def send_mouse_movement(self, mouse_delta: Vector) -> None:
await self.send_combined_movement(joystick_vector=mouse_delta)

async def send_combined_movement(
self, movements: Iterable[int] = None, joystick_vector: Vector = None
) -> None:

if joystick_vector is None:
joystick_vector = Vector()

if movements is None:
movements = []

value = 0
for movement in movements:
value = value | movement

await self.rust_socket._handle_ratelimit(0.01)
app_request = self.rust_socket._generate_protobuf()
cam_input = AppCameraInput()

cam_input.buttons = value
vector = Vector2()
vector.x = joystick_vector.x
vector.y = joystick_vector.y
cam_input.mouseDelta.CopyFrom(vector)
app_request.cameraInput.CopyFrom(cam_input)

await self.rust_socket.remote.send_message(app_request)
self.rust_socket.remote.ignored_responses.append(app_request.seq)

async def exit_camera(self) -> None:
await self.rust_socket._handle_ratelimit()
app_request = self.rust_socket._generate_protobuf()
app_request.cameraUnsubscribe.CopyFrom(AppEmpty())

await self.rust_socket.remote.send_message(app_request)
self.rust_socket.remote.ignored_responses.append(app_request.seq)

self._open = False
self._last_packets = None

async def resubscribe(self) -> None:
await self.rust_socket.remote.subscribe_to_camera(self._cam_id, True)
self.time_since_last_subscribe = time.time()
Loading

0 comments on commit 9a8703f

Please sign in to comment.