rustplus 6.1.3__tar.gz → 6.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {rustplus-6.1.3/rustplus.egg-info → rustplus-6.2.1}/PKG-INFO +2 -2
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/__init__.py +1 -1
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/commands/command_options.py +1 -1
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/events/chat_event.py +2 -4
- rustplus-6.2.1/rustplus/identification/__init__.py +2 -0
- rustplus-6.2.1/rustplus/identification/server_details.py +85 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/remote/camera/camera_manager.py +4 -4
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/remote/camera/camera_parser.py +2 -2
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/remote/ratelimiter/__init__.py +8 -10
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/remote/websocket/ws.py +36 -39
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/rust_api.py +31 -33
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/structs/rust_chat_message.py +1 -2
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/structs/rust_item.py +1 -2
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/utils/grab_items.py +1 -3
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/utils/utils.py +5 -3
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/utils/yielding_event.py +2 -2
- {rustplus-6.1.3 → rustplus-6.2.1/rustplus.egg-info}/PKG-INFO +2 -2
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus.egg-info/SOURCES.txt +0 -2
- {rustplus-6.1.3 → rustplus-6.2.1}/setup.py +1 -1
- rustplus-6.1.3/rustplus/identification/__init__.py +0 -2
- rustplus-6.1.3/rustplus/identification/server_details.py +0 -39
- rustplus-6.1.3/rustplus/remote/proxy/__init__.py +0 -1
- rustplus-6.1.3/rustplus/remote/proxy/proxy_value_grabber.py +0 -32
- {rustplus-6.1.3 → rustplus-6.2.1}/LICENSE +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/MANIFEST.in +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/README.md +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/requirements.txt +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/annotations/__init__.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/annotations/chat_event.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/annotations/clan_event.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/annotations/command.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/annotations/entity_event.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/annotations/protobuf_event.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/annotations/team_event.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/commands/__init__.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/commands/chat_command.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/commands/chat_command_data.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/events/__init__.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/events/clan_info_event.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/events/entity_event.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/events/protobuf_event.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/events/team_event.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/exceptions/__init__.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/exceptions/exceptions.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Airfield.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Apartments_Complex.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Arctic_Research_Base.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Bandit_Camp.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Dome.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Excavator.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Ferry_Terminal.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Fishing_Village.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Gas_Station.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/HQM_Quarry.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Harbor.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Jungle_Ziggurat.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Junkyard.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Launch_Site.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Lighthouse.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Military_Base.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Military_Tunnels.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Mining_Outpost.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Missile_Silo.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Oil_Rig_Large.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Oil_Rig_Small.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Outpost.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Powerplant.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Radtown.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Satellite_Dish.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Sewer_Branch.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Stables.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Stone_Quarry.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Sulfur_Quarry.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Supermarket.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Swamp.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Trainyard.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Tunnel_Entrance.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Underwater_Lab.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/Water_Treatment.svg +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/__init__.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/cargo.png +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/chinook.png +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/chinook_blades.png +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/crate.png +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/explosion.png +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/icon.png +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/patrol.png +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/icons/vending_machine.png +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/identification/handler_list.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/identification/registered_listener.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/remote/__init__.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/remote/camera/__init__.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/remote/camera/camera_constants.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/remote/camera/structures.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/remote/fcm/__init__.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/remote/fcm/fcm_listener.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/remote/nexus/__init__.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/remote/nexus/nexus_interface.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/remote/rustplus_proto/__init__.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/remote/rustplus_proto/rustplus.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/remote/websocket/__init__.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/structs/__init__.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/structs/clans/__init__.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/structs/clans/rust_clan_info.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/structs/clans/rust_clan_message.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/structs/rust_contents.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/structs/rust_entity_info.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/structs/rust_error.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/structs/rust_info.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/structs/rust_map.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/structs/rust_marker.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/structs/rust_team_info.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/structs/rust_time.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/structs/serialization.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/structs/util.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/utils/__init__.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/utils/deprecated.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/utils/emojis.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/utils/fonts/PermanentMarker.ttf +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus/utils/fonts/__init__.py +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus.egg-info/dependency_links.txt +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus.egg-info/requires.txt +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/rustplus.egg-info/top_level.txt +0 -0
- {rustplus-6.1.3 → rustplus-6.2.1}/setup.cfg +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rustplus
|
|
3
|
-
Version: 6.1
|
|
3
|
+
Version: 6.2.1
|
|
4
4
|
Summary: A python wrapper for the Rust Plus API
|
|
5
5
|
Home-page: https://github.com/olijeffers0n/rustplus
|
|
6
6
|
Author: olijeffers0n
|
|
7
7
|
License: MIT
|
|
8
8
|
Project-URL: Issue tracker, https://github.com/olijeffers0n/rustplus/issues
|
|
9
|
-
Requires-Python: >=3.
|
|
9
|
+
Requires-Python: >=3.10.0
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
Requires-Dist: websockets
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
from rustplus.identification.handler_list import HandlerList
|
|
2
2
|
from ..structs import RustChatMessage
|
|
3
3
|
|
|
4
|
-
from typing import Union
|
|
5
|
-
|
|
6
4
|
|
|
7
5
|
class ChatEventPayload:
|
|
8
6
|
HANDLER_LIST = HandlerList()
|
|
9
7
|
|
|
10
8
|
def __init__(
|
|
11
|
-
self, message: RustChatMessage, is_clan: bool, clan_id:
|
|
9
|
+
self, message: RustChatMessage, is_clan: bool, clan_id: int | None
|
|
12
10
|
) -> None:
|
|
13
11
|
self._message = message
|
|
14
12
|
self._is_clan = is_clan
|
|
@@ -23,5 +21,5 @@ class ChatEventPayload:
|
|
|
23
21
|
return self._is_clan
|
|
24
22
|
|
|
25
23
|
@property
|
|
26
|
-
def clan_id(self) ->
|
|
24
|
+
def clan_id(self) -> int | None:
|
|
27
25
|
return self._clan_id
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import time
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
|
|
5
|
+
from websockets.typing import Subprotocol
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _b64url(s: str) -> str:
|
|
9
|
+
return base64.urlsafe_b64encode(s.encode()).decode().rstrip("=")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class DirectEndpoint:
|
|
14
|
+
ip: str
|
|
15
|
+
port: int | None = None
|
|
16
|
+
secure: bool = False
|
|
17
|
+
|
|
18
|
+
def id(self) -> str:
|
|
19
|
+
return f"{self.ip}:{self.port}" if self.port is not None else self.ip
|
|
20
|
+
|
|
21
|
+
def uri(self) -> str:
|
|
22
|
+
scheme = "wss" if self.secure else "ws"
|
|
23
|
+
host = self.ip if self.port is None else f"{self.ip}:{self.port}"
|
|
24
|
+
return f"{scheme}://{host}"
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def connect_kwargs() -> dict:
|
|
28
|
+
return {}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass(frozen=True)
|
|
32
|
+
class RelayEndpoint:
|
|
33
|
+
server_id: str
|
|
34
|
+
# secret and short-lived: excluded from repr, eq and hash
|
|
35
|
+
auth_token: str = field(repr=False, compare=False)
|
|
36
|
+
host: str = "companion-rust.facepunch.com"
|
|
37
|
+
version: int = int(time.time() * 1000)
|
|
38
|
+
|
|
39
|
+
def id(self) -> str:
|
|
40
|
+
return self.server_id
|
|
41
|
+
|
|
42
|
+
def uri(self) -> str:
|
|
43
|
+
return f"wss://{self.host}/socket/{self.server_id}?v={self.version}"
|
|
44
|
+
|
|
45
|
+
def connect_kwargs(self) -> dict:
|
|
46
|
+
return {
|
|
47
|
+
"subprotocols": [
|
|
48
|
+
Subprotocol("rustplus.auth.v1"),
|
|
49
|
+
Subprotocol(_b64url(self.auth_token)),
|
|
50
|
+
],
|
|
51
|
+
"origin": "https://app.playrust.com",
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
Endpoint = DirectEndpoint | RelayEndpoint
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass(frozen=True)
|
|
59
|
+
class ServerDetails:
|
|
60
|
+
endpoint: Endpoint
|
|
61
|
+
player_id: int
|
|
62
|
+
player_token: int = field(repr=False)
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def direct(
|
|
66
|
+
cls,
|
|
67
|
+
ip: str,
|
|
68
|
+
port: int | None,
|
|
69
|
+
player_id: int,
|
|
70
|
+
player_token: int,
|
|
71
|
+
secure: bool = False,
|
|
72
|
+
):
|
|
73
|
+
return cls(
|
|
74
|
+
DirectEndpoint(str(ip), int(port) if port else None, secure),
|
|
75
|
+
int(player_id),
|
|
76
|
+
int(player_token),
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
@classmethod
|
|
80
|
+
def facepunch(
|
|
81
|
+
cls, server_id: str, auth_token: str, player_id: int, player_token: int
|
|
82
|
+
):
|
|
83
|
+
return cls(
|
|
84
|
+
RelayEndpoint(server_id, auth_token), int(player_id), int(player_token)
|
|
85
|
+
)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import time
|
|
2
|
-
from typing import Iterable,
|
|
2
|
+
from typing import Iterable, List, Coroutine, Set, Callable
|
|
3
3
|
from PIL import Image
|
|
4
4
|
|
|
5
5
|
from .camera_parser import Parser
|
|
@@ -18,7 +18,7 @@ from .structures import CameraInfo, Entity, LimitedQueue
|
|
|
18
18
|
|
|
19
19
|
class CameraManager:
|
|
20
20
|
|
|
21
|
-
ACTIVE_INSTANCE:
|
|
21
|
+
ACTIVE_INSTANCE: "CameraManager | None" = None
|
|
22
22
|
|
|
23
23
|
def __init__(
|
|
24
24
|
self, rust_socket, cam_id: str, cam_info_message: AppCameraInfo
|
|
@@ -60,7 +60,7 @@ class CameraManager:
|
|
|
60
60
|
render_entities: bool = True,
|
|
61
61
|
entity_render_distance: float | int = float("inf"),
|
|
62
62
|
max_entity_amount: float | int = float("inf"),
|
|
63
|
-
) ->
|
|
63
|
+
) -> Image.Image | None:
|
|
64
64
|
if self._last_packets is None:
|
|
65
65
|
return None
|
|
66
66
|
|
|
@@ -97,7 +97,7 @@ class CameraManager:
|
|
|
97
97
|
render_entities: bool = True,
|
|
98
98
|
entity_render_distance: float | int = float("inf"),
|
|
99
99
|
max_entity_amount: float | int = float("inf"),
|
|
100
|
-
) ->
|
|
100
|
+
) -> Image.Image | None:
|
|
101
101
|
return await self._create_frame(
|
|
102
102
|
render_entities, entity_render_distance, max_entity_amount
|
|
103
103
|
)
|
|
@@ -2,7 +2,7 @@ import math
|
|
|
2
2
|
from importlib import resources
|
|
3
3
|
from math import radians, tan
|
|
4
4
|
import random
|
|
5
|
-
from typing import
|
|
5
|
+
from typing import Tuple, List, Any, Dict
|
|
6
6
|
import numpy as np
|
|
7
7
|
from scipy.spatial import ConvexHull
|
|
8
8
|
from PIL import Image, ImageDraw, ImageFont
|
|
@@ -166,7 +166,7 @@ class Parser:
|
|
|
166
166
|
|
|
167
167
|
return False
|
|
168
168
|
|
|
169
|
-
def next_ray(self, ray_data) -> List[
|
|
169
|
+
def next_ray(self, ray_data) -> List[float | int]:
|
|
170
170
|
byte = ray_data[self.data_pointer]
|
|
171
171
|
self.data_pointer += 1
|
|
172
172
|
|
|
@@ -64,8 +64,8 @@ class RateLimiter:
|
|
|
64
64
|
self.socket_buckets[server_details] = TokenBucket(
|
|
65
65
|
current, maximum, refresh_rate, refresh_amount
|
|
66
66
|
)
|
|
67
|
-
if server_details.
|
|
68
|
-
self.server_buckets[server_details.
|
|
67
|
+
if server_details.endpoint.id() not in self.server_buckets:
|
|
68
|
+
self.server_buckets[server_details.endpoint.id()] = TokenBucket(
|
|
69
69
|
self.SERVER_LIMIT, self.SERVER_LIMIT, 1, self.SERVER_REFRESH_AMOUNT
|
|
70
70
|
)
|
|
71
71
|
|
|
@@ -76,7 +76,7 @@ class RateLimiter:
|
|
|
76
76
|
with self.lock:
|
|
77
77
|
for bucket in [
|
|
78
78
|
self.socket_buckets.get(server_details),
|
|
79
|
-
self.server_buckets.get(server_details.
|
|
79
|
+
self.server_buckets.get(server_details.endpoint.id()),
|
|
80
80
|
]:
|
|
81
81
|
bucket.refresh()
|
|
82
82
|
if not bucket.can_consume(amount):
|
|
@@ -90,14 +90,14 @@ class RateLimiter:
|
|
|
90
90
|
with self.lock:
|
|
91
91
|
for bucket in [
|
|
92
92
|
self.socket_buckets.get(server_details),
|
|
93
|
-
self.server_buckets.get(server_details.
|
|
93
|
+
self.server_buckets.get(server_details.endpoint.id()),
|
|
94
94
|
]:
|
|
95
95
|
bucket.refresh()
|
|
96
96
|
if not bucket.can_consume(amount):
|
|
97
97
|
raise RateLimitError("Not Enough Tokens")
|
|
98
98
|
for bucket in [
|
|
99
99
|
self.socket_buckets.get(server_details),
|
|
100
|
-
self.server_buckets.get(server_details.
|
|
100
|
+
self.server_buckets.get(server_details.endpoint.id()),
|
|
101
101
|
]:
|
|
102
102
|
bucket.consume(amount)
|
|
103
103
|
|
|
@@ -112,7 +112,7 @@ class RateLimiter:
|
|
|
112
112
|
delay = 0
|
|
113
113
|
for bucket in [
|
|
114
114
|
self.socket_buckets.get(server_details),
|
|
115
|
-
self.server_buckets.get(server_details.
|
|
115
|
+
self.server_buckets.get(server_details.endpoint.id()),
|
|
116
116
|
]:
|
|
117
117
|
bucket.refresh()
|
|
118
118
|
val = (
|
|
@@ -135,8 +135,6 @@ class RateLimiter:
|
|
|
135
135
|
"""
|
|
136
136
|
with self.lock:
|
|
137
137
|
del self.socket_buckets[server_details]
|
|
138
|
-
server_str = server_details.
|
|
139
|
-
if not any(
|
|
140
|
-
d.get_server_string() == server_str for d in self.socket_buckets
|
|
141
|
-
):
|
|
138
|
+
server_str = server_details.endpoint.id()
|
|
139
|
+
if not any(d.endpoint.id() == server_str for d in self.socket_buckets):
|
|
142
140
|
del self.server_buckets[server_str]
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import shlex
|
|
2
|
-
import base64
|
|
3
2
|
import betterproto
|
|
4
3
|
from websockets.exceptions import InvalidURI, InvalidHandshake, ConnectionClosedError
|
|
5
4
|
from websockets.legacy.client import WebSocketClientProtocol
|
|
6
5
|
from websockets.client import connect
|
|
7
6
|
from asyncio import TimeoutError, Task, AbstractEventLoop
|
|
8
|
-
from typing import
|
|
7
|
+
from typing import Coroutine, Optional, Set, Dict
|
|
9
8
|
import logging
|
|
10
9
|
import asyncio
|
|
11
10
|
|
|
12
11
|
from ..camera import CameraManager
|
|
13
|
-
from ..proxy import ProxyValueGrabber
|
|
14
12
|
from ..rustplus_proto import AppMessage, AppRequest, AppError
|
|
15
13
|
from ...commands import CommandOptions, ChatCommand, ChatCommandTime
|
|
16
14
|
from ...events import (
|
|
@@ -21,7 +19,10 @@ from ...events import (
|
|
|
21
19
|
)
|
|
22
20
|
from ...events.clan_info_event import ClanInfoEventPayload
|
|
23
21
|
from ...exceptions import RequestError
|
|
24
|
-
from ...identification import
|
|
22
|
+
from ...identification import (
|
|
23
|
+
ServerDetails,
|
|
24
|
+
RegisteredListener,
|
|
25
|
+
)
|
|
25
26
|
from ...structs import RustChatMessage, RustTeamInfo, RustClanInfo
|
|
26
27
|
from ...utils import YieldingEvent, convert_time
|
|
27
28
|
from ...utils.utils import error_present
|
|
@@ -33,33 +34,21 @@ class RustWebsocket:
|
|
|
33
34
|
def __init__(
|
|
34
35
|
self,
|
|
35
36
|
server_details: ServerDetails,
|
|
36
|
-
command_options:
|
|
37
|
-
use_fp_proxy: bool,
|
|
38
|
-
use_test_server: bool,
|
|
37
|
+
command_options: CommandOptions | None,
|
|
39
38
|
debug: bool,
|
|
40
39
|
) -> None:
|
|
41
40
|
self.server_details: ServerDetails = server_details
|
|
42
|
-
self.command_options:
|
|
43
|
-
self.connection:
|
|
41
|
+
self.command_options: CommandOptions | None = command_options
|
|
42
|
+
self.connection: WebSocketClientProtocol | None = None
|
|
44
43
|
self.logger: logging.Logger = logging.getLogger("rustplus.py")
|
|
45
|
-
self.task:
|
|
44
|
+
self.task: Task | None = None
|
|
46
45
|
self.debug: bool = debug
|
|
47
|
-
self.use_test_server: bool = use_test_server
|
|
48
|
-
self.use_fp_proxy: bool = use_fp_proxy
|
|
49
46
|
|
|
50
47
|
self.responses: Dict[int, YieldingEvent] = {}
|
|
51
48
|
self.open = False
|
|
52
49
|
|
|
53
50
|
async def connect(self) -> bool:
|
|
54
|
-
|
|
55
|
-
address = (
|
|
56
|
-
(
|
|
57
|
-
f"{'wss' if self.server_details.secure else 'ws'}://"
|
|
58
|
-
+ self.server_details.get_server_string()
|
|
59
|
-
)
|
|
60
|
-
if not self.use_fp_proxy
|
|
61
|
-
else f"wss://companion-rust.facepunch.com/game/{self.server_details.ip}/{self.server_details.port}"
|
|
62
|
-
) + f"?v={ProxyValueGrabber.get_value()}"
|
|
51
|
+
address = self.server_details.endpoint.uri()
|
|
63
52
|
|
|
64
53
|
try:
|
|
65
54
|
self.connection = await connect(
|
|
@@ -67,6 +56,7 @@ class RustWebsocket:
|
|
|
67
56
|
close_timeout=0,
|
|
68
57
|
ping_interval=None,
|
|
69
58
|
max_size=1_000_000_000,
|
|
59
|
+
**self.server_details.endpoint.connect_kwargs(),
|
|
70
60
|
)
|
|
71
61
|
except (InvalidURI, OSError, InvalidHandshake, TimeoutError) as err:
|
|
72
62
|
self.logger.warning("WebSocket connection error: %s", err)
|
|
@@ -100,15 +90,25 @@ class RustWebsocket:
|
|
|
100
90
|
async def run(self) -> None:
|
|
101
91
|
while self.open:
|
|
102
92
|
try:
|
|
103
|
-
|
|
93
|
+
if self.connection is None:
|
|
94
|
+
self.logger.warning(
|
|
95
|
+
"No Current Websocket Connection, exiting run loop"
|
|
96
|
+
)
|
|
97
|
+
break
|
|
98
|
+
|
|
99
|
+
data: str | bytes = await self.connection.recv()
|
|
100
|
+
|
|
101
|
+
if isinstance(data, str):
|
|
102
|
+
# Text frame: not protobuf, so ignore it
|
|
103
|
+
self.logger.warning(
|
|
104
|
+
"Ignoring unexpected text frame (%d chars)", len(data)
|
|
105
|
+
)
|
|
106
|
+
continue
|
|
104
107
|
|
|
105
108
|
await self.run_coroutine_non_blocking(
|
|
106
109
|
self.run_proto_event(data, self.server_details)
|
|
107
110
|
)
|
|
108
111
|
|
|
109
|
-
if self.use_test_server:
|
|
110
|
-
data = base64.b64decode(data)
|
|
111
|
-
|
|
112
112
|
app_message = AppMessage()
|
|
113
113
|
app_message.parse(data)
|
|
114
114
|
|
|
@@ -134,7 +134,7 @@ class RustWebsocket:
|
|
|
134
134
|
e,
|
|
135
135
|
)
|
|
136
136
|
|
|
137
|
-
async def send_and_get(self, request: AppRequest) -> AppMessage:
|
|
137
|
+
async def send_and_get(self, request: AppRequest) -> AppMessage | None:
|
|
138
138
|
if not await self.send_message(request):
|
|
139
139
|
message = AppMessage()
|
|
140
140
|
error = AppError()
|
|
@@ -158,19 +158,14 @@ class RustWebsocket:
|
|
|
158
158
|
self.responses[request.seq] = YieldingEvent()
|
|
159
159
|
|
|
160
160
|
try:
|
|
161
|
-
|
|
162
|
-
await self.connection.send(
|
|
163
|
-
base64.b64encode(bytes(request)).decode("utf-8")
|
|
164
|
-
)
|
|
165
|
-
else:
|
|
166
|
-
await self.connection.send(bytes(request))
|
|
161
|
+
await self.connection.send(bytes(request))
|
|
167
162
|
except Exception as err:
|
|
168
163
|
self.logger.warning("WebSocket connection error: %s", err)
|
|
169
164
|
return False
|
|
170
165
|
|
|
171
166
|
return True
|
|
172
167
|
|
|
173
|
-
async def get_response(self, seq: int) ->
|
|
168
|
+
async def get_response(self, seq: int) -> AppMessage | None:
|
|
174
169
|
|
|
175
170
|
response = await self.responses[seq].wait(timeout=self.RESPONSE_TIMEOUT)
|
|
176
171
|
del self.responses[seq]
|
|
@@ -185,7 +180,9 @@ class RustWebsocket:
|
|
|
185
180
|
|
|
186
181
|
if error_present(app_message):
|
|
187
182
|
|
|
188
|
-
event: YieldingEvent = self.responses.get(
|
|
183
|
+
event: YieldingEvent | None = self.responses.get(
|
|
184
|
+
app_message.response.seq, None
|
|
185
|
+
)
|
|
189
186
|
if event is not None:
|
|
190
187
|
if self.debug:
|
|
191
188
|
self.logger.info(
|
|
@@ -315,7 +312,9 @@ class RustWebsocket:
|
|
|
315
312
|
|
|
316
313
|
else:
|
|
317
314
|
# This means that it wasn't sent by the server and is a message from the server in response to an action
|
|
318
|
-
event: YieldingEvent = self.responses.get(
|
|
315
|
+
event: YieldingEvent | None = self.responses.get(
|
|
316
|
+
app_message.response.seq, None
|
|
317
|
+
)
|
|
319
318
|
if event is not None:
|
|
320
319
|
if self.debug:
|
|
321
320
|
self.logger.info(f"Running Response Event: {app_message}")
|
|
@@ -333,9 +332,7 @@ class RustWebsocket:
|
|
|
333
332
|
return None
|
|
334
333
|
|
|
335
334
|
@staticmethod
|
|
336
|
-
async def run_proto_event(
|
|
337
|
-
data: Union[str, bytes], server_details: ServerDetails
|
|
338
|
-
) -> None:
|
|
335
|
+
async def run_proto_event(data: str | bytes, server_details: ServerDetails) -> None:
|
|
339
336
|
handlers: Set[RegisteredListener] = (
|
|
340
337
|
ProtobufEventPayload.HANDLER_LIST.get_handlers(server_details)
|
|
341
338
|
)
|
|
@@ -372,5 +369,5 @@ class RustWebsocket:
|
|
|
372
369
|
|
|
373
370
|
@staticmethod
|
|
374
371
|
async def run_coroutine_non_blocking(coroutine: Coroutine) -> Task:
|
|
375
|
-
loop: AbstractEventLoop = asyncio.
|
|
372
|
+
loop: AbstractEventLoop = asyncio.get_event_loop()
|
|
376
373
|
return loop.create_task(coroutine)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
from collections import defaultdict
|
|
3
|
-
from datetime import datetime
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
4
|
from importlib import resources
|
|
5
5
|
from io import BytesIO
|
|
6
|
-
from typing import List
|
|
6
|
+
from typing import List
|
|
7
7
|
import logging
|
|
8
8
|
from PIL import Image
|
|
9
9
|
|
|
@@ -56,10 +56,8 @@ class RustSocket:
|
|
|
56
56
|
def __init__(
|
|
57
57
|
self,
|
|
58
58
|
server_details: ServerDetails,
|
|
59
|
-
ratelimiter:
|
|
60
|
-
command_options:
|
|
61
|
-
use_fp_proxy: bool = False,
|
|
62
|
-
use_test_server: bool = False,
|
|
59
|
+
ratelimiter: RateLimiter | None = None,
|
|
60
|
+
command_options: CommandOptions | None = None,
|
|
63
61
|
debug: bool = False,
|
|
64
62
|
) -> None:
|
|
65
63
|
self.server_details = server_details
|
|
@@ -80,8 +78,6 @@ class RustSocket:
|
|
|
80
78
|
self.ws = RustWebsocket(
|
|
81
79
|
self.server_details,
|
|
82
80
|
self.command_options,
|
|
83
|
-
use_fp_proxy,
|
|
84
|
-
use_test_server,
|
|
85
81
|
debug,
|
|
86
82
|
)
|
|
87
83
|
self.seq = 1
|
|
@@ -143,7 +139,7 @@ class RustSocket:
|
|
|
143
139
|
while True:
|
|
144
140
|
await asyncio.sleep(1)
|
|
145
141
|
|
|
146
|
-
async def get_time(self) ->
|
|
142
|
+
async def get_time(self) -> RustTime | RustError:
|
|
147
143
|
"""
|
|
148
144
|
Gets the current in-game time from the server.
|
|
149
145
|
|
|
@@ -183,7 +179,7 @@ class RustSocket:
|
|
|
183
179
|
|
|
184
180
|
await self.ws.send_message(packet, True)
|
|
185
181
|
|
|
186
|
-
async def get_info(self) ->
|
|
182
|
+
async def get_info(self) -> RustInfo | RustError:
|
|
187
183
|
"""
|
|
188
184
|
Gets information on the Rust Server
|
|
189
185
|
:return: RustInfo - The info of the server
|
|
@@ -200,7 +196,7 @@ class RustSocket:
|
|
|
200
196
|
|
|
201
197
|
return RustInfo(response.response.info)
|
|
202
198
|
|
|
203
|
-
async def get_team_chat(self) ->
|
|
199
|
+
async def get_team_chat(self) -> List[RustChatMessage] | RustError:
|
|
204
200
|
"""
|
|
205
201
|
Gets the team chat from the server
|
|
206
202
|
|
|
@@ -220,7 +216,7 @@ class RustSocket:
|
|
|
220
216
|
RustChatMessage(message) for message in response.response.team_chat.messages
|
|
221
217
|
]
|
|
222
218
|
|
|
223
|
-
async def get_team_info(self) ->
|
|
219
|
+
async def get_team_info(self) -> RustTeamInfo | RustError:
|
|
224
220
|
"""
|
|
225
221
|
Gets Information on the members of your team
|
|
226
222
|
|
|
@@ -238,7 +234,7 @@ class RustSocket:
|
|
|
238
234
|
|
|
239
235
|
return RustTeamInfo(response.response.team_info)
|
|
240
236
|
|
|
241
|
-
async def get_markers(self) ->
|
|
237
|
+
async def get_markers(self) -> List[RustMarker] | RustError:
|
|
242
238
|
"""
|
|
243
239
|
Gets all the map markers from the server
|
|
244
240
|
|
|
@@ -262,9 +258,9 @@ class RustSocket:
|
|
|
262
258
|
add_events: bool = False,
|
|
263
259
|
add_vending_machines: bool = False,
|
|
264
260
|
add_team_positions: bool = False,
|
|
265
|
-
override_images: dict = None,
|
|
261
|
+
override_images: dict | None = None,
|
|
266
262
|
add_grid: bool = False,
|
|
267
|
-
) ->
|
|
263
|
+
) -> Image.Image | RustError:
|
|
268
264
|
"""
|
|
269
265
|
Gets an image of the map from the server with the specified additions
|
|
270
266
|
|
|
@@ -308,7 +304,9 @@ class RustSocket:
|
|
|
308
304
|
output = output.crop(
|
|
309
305
|
(500, 500, map_packet.height - 500, map_packet.width - 500)
|
|
310
306
|
)
|
|
311
|
-
output = output.resize((map_size, map_size), Image.LANCZOS).convert(
|
|
307
|
+
output = output.resize((map_size, map_size), Image.Resampling.LANCZOS).convert(
|
|
308
|
+
"RGBA"
|
|
309
|
+
)
|
|
312
310
|
|
|
313
311
|
if add_grid:
|
|
314
312
|
output.paste(grid := generate_grid(map_size), (5, 5), grid)
|
|
@@ -326,7 +324,7 @@ class RustSocket:
|
|
|
326
324
|
if str(monument.token) == "DungeonBase":
|
|
327
325
|
continue
|
|
328
326
|
if "underwater-lab-base" in str(monument.token):
|
|
329
|
-
# An underwater lab's interior modules (
|
|
327
|
+
# An underwater lab's interior modules (moon pools, etc.) are
|
|
330
328
|
# reported as separate markers next to the lab; they aren't
|
|
331
329
|
# standalone monuments, so don't draw them.
|
|
332
330
|
continue
|
|
@@ -387,7 +385,7 @@ class RustSocket:
|
|
|
387
385
|
|
|
388
386
|
return output
|
|
389
387
|
|
|
390
|
-
async def get_map_info(self) ->
|
|
388
|
+
async def get_map_info(self) -> RustMap | RustError:
|
|
391
389
|
"""
|
|
392
390
|
Gets the raw map data from the server
|
|
393
391
|
|
|
@@ -405,9 +403,7 @@ class RustSocket:
|
|
|
405
403
|
|
|
406
404
|
return RustMap(response.response.map)
|
|
407
405
|
|
|
408
|
-
async def get_entity_info(
|
|
409
|
-
self, eid: int = None
|
|
410
|
-
) -> Union[RustEntityInfo, RustError]:
|
|
406
|
+
async def get_entity_info(self, eid: int) -> RustEntityInfo | RustError:
|
|
411
407
|
"""
|
|
412
408
|
Gets entity info from the server
|
|
413
409
|
|
|
@@ -459,7 +455,7 @@ class RustSocket:
|
|
|
459
455
|
|
|
460
456
|
await self.ws.send_message(packet, True)
|
|
461
457
|
|
|
462
|
-
async def check_subscription_to_entity(self, eid: int) ->
|
|
458
|
+
async def check_subscription_to_entity(self, eid: int) -> bool | RustError:
|
|
463
459
|
"""
|
|
464
460
|
Checks if you are subscribed to an entity
|
|
465
461
|
|
|
@@ -481,7 +477,7 @@ class RustSocket:
|
|
|
481
477
|
|
|
482
478
|
return response.response.flag.value
|
|
483
479
|
|
|
484
|
-
async def promote_to_team_leader(self, steamid: int
|
|
480
|
+
async def promote_to_team_leader(self, steamid: int) -> None:
|
|
485
481
|
"""
|
|
486
482
|
Promotes a given user to the team leader by their 64-bit Steam ID
|
|
487
483
|
|
|
@@ -496,8 +492,8 @@ class RustSocket:
|
|
|
496
492
|
await self.ws.send_message(packet, True)
|
|
497
493
|
|
|
498
494
|
async def get_contents(
|
|
499
|
-
self, eid: int
|
|
500
|
-
) ->
|
|
495
|
+
self, eid: int, combine_stacks: bool = False
|
|
496
|
+
) -> RustContents | RustError:
|
|
501
497
|
"""
|
|
502
498
|
Gets the contents of a storage monitor-attached container
|
|
503
499
|
|
|
@@ -510,8 +506,10 @@ class RustSocket:
|
|
|
510
506
|
if isinstance(returned_data, RustError):
|
|
511
507
|
return returned_data
|
|
512
508
|
|
|
513
|
-
target_time = datetime.
|
|
514
|
-
|
|
509
|
+
target_time = datetime.fromtimestamp(
|
|
510
|
+
int(returned_data.protection_expiry), tz=timezone.utc
|
|
511
|
+
)
|
|
512
|
+
difference = target_time - datetime.now(timezone.utc)
|
|
515
513
|
|
|
516
514
|
items = []
|
|
517
515
|
|
|
@@ -552,13 +550,13 @@ class RustSocket:
|
|
|
552
550
|
|
|
553
551
|
return RustContents(difference, bool(returned_data.has_protection), items)
|
|
554
552
|
|
|
555
|
-
async def get_camera_manager(self, cam_id: str) ->
|
|
553
|
+
async def get_camera_manager(self, cam_id: str) -> CameraManager | RustError:
|
|
556
554
|
"""
|
|
557
555
|
Gets a camera manager for a given camera ID
|
|
558
556
|
|
|
559
557
|
NOTE: This will override the current camera manager if one exists for the given ID so you cannot have multiple
|
|
560
558
|
|
|
561
|
-
:param cam_id: The ID of the camera
|
|
559
|
+
:param cam_id: The ID of the target camera
|
|
562
560
|
:return CameraManager: The camera manager
|
|
563
561
|
:raises RequestError: If the camera is not found, or you cannot access it. See reason for more info
|
|
564
562
|
"""
|
|
@@ -576,7 +574,7 @@ class RustSocket:
|
|
|
576
574
|
|
|
577
575
|
return CameraManager(self, cam_id, response.response.camera_subscribe_info)
|
|
578
576
|
|
|
579
|
-
async def get_clan_info(self) ->
|
|
577
|
+
async def get_clan_info(self) -> RustClanInfo | RustError:
|
|
580
578
|
"""
|
|
581
579
|
Gets the clan information for the player's current clan.
|
|
582
580
|
|
|
@@ -594,7 +592,7 @@ class RustSocket:
|
|
|
594
592
|
|
|
595
593
|
return RustClanInfo(response.response.clan_info.clan_info)
|
|
596
594
|
|
|
597
|
-
async def get_clan_chat(self) ->
|
|
595
|
+
async def get_clan_chat(self) -> List[RustClanMessage] | RustError:
|
|
598
596
|
"""
|
|
599
597
|
Gets the clan chat for the player's current clan.
|
|
600
598
|
|
|
@@ -642,7 +640,7 @@ class RustSocket:
|
|
|
642
640
|
|
|
643
641
|
await self.ws.send_message(packet, True)
|
|
644
642
|
|
|
645
|
-
async def get_nexus_auth(self, app_key: str) ->
|
|
643
|
+
async def get_nexus_auth(self, app_key: str) -> RustAuthDetails | RustError:
|
|
646
644
|
"""
|
|
647
645
|
Gets the auth for a server. Does not require that the player token of this RustSocket is set correctly.
|
|
648
646
|
|
|
@@ -667,7 +665,7 @@ class RustSocket:
|
|
|
667
665
|
response.response.nexus_auth.player_token,
|
|
668
666
|
)
|
|
669
667
|
|
|
670
|
-
async def kick_from_team(self, steamid: int) ->
|
|
668
|
+
async def kick_from_team(self, steamid: int) -> None | RustError:
|
|
671
669
|
"""
|
|
672
670
|
Kicks a player from the team by their 64-bit Steam ID
|
|
673
671
|
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
from typing import Union
|
|
2
1
|
from ..remote.rustplus_proto import AppTeamMessage, AppClanMessage
|
|
3
2
|
from .serialization import Serializable
|
|
4
3
|
|
|
5
4
|
|
|
6
5
|
class RustChatMessage(Serializable):
|
|
7
|
-
def __init__(self, data:
|
|
6
|
+
def __init__(self, data: AppTeamMessage | AppClanMessage):
|
|
8
7
|
self._steam_id: int = data.steam_id
|
|
9
8
|
self._name: str = data.name
|
|
10
9
|
self._message: str = data.message
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
from typing import Union
|
|
2
1
|
from .serialization import Serializable
|
|
3
2
|
|
|
4
3
|
|
|
5
4
|
class RustItem(Serializable):
|
|
6
5
|
def __init__(
|
|
7
|
-
self, name: str, item_id:
|
|
6
|
+
self, name: str, item_id: str | int, quantity: int, is_blueprint: bool
|
|
8
7
|
) -> None:
|
|
9
8
|
self._name: str = name
|
|
10
9
|
self._item_id: int = int(item_id)
|