Skip to content

Commit

Permalink
Its a start
Browse files Browse the repository at this point in the history
  • Loading branch information
olijeffers0n committed Jun 21, 2024
1 parent 0073162 commit 71b52d3
Show file tree
Hide file tree
Showing 13 changed files with 779 additions and 23 deletions.
21 changes: 3 additions & 18 deletions rustplus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,10 @@
RustPlus, An API wrapper for interfacing with the Rust+ App API
"""

from .api import RustSocket
from .api.remote.events import (
EntityEvent,
TeamEvent,
ChatEvent,
MarkerEvent,
ProtobufEvent,
RegisteredListener,
)
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 *
from .rust_api import RustSocket
from .identification import ServerID

__name__ = "rustplus"
__author__ = "olijeffers0n"
__version__ = "5.6.18"
__version__ = "6.0.0"
__support__ = "Discord: https://discord.gg/nQqJe8qvP8"
2 changes: 2 additions & 0 deletions rustplus/identification/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .registered_listener import RegisteredListener, RegisteredEntityListener
from .server_id import ServerID
49 changes: 49 additions & 0 deletions rustplus/identification/registered_listener.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from typing import Coroutine


class RegisteredListener:
def __init__(
self,
listener_id: str,
coroutine: Coroutine,
) -> None:
self.listener_id = listener_id
self._coroutine = coroutine

def get_coro(self):
return self._coroutine

def __eq__(self, other) -> bool:
if not isinstance(other, RegisteredListener):
return False

return (
self.listener_id == other.listener_id
and self._coroutine == other.get_coro()
)

def __hash__(self):
return hash((self.listener_id, self._coroutine))


class RegisteredEntityListener(RegisteredListener):
def __init__(
self,
listener_id: str,
coroutine: Coroutine,
entity_type: int,
) -> None:
super().__init__(listener_id, coroutine)
self.entity_type = entity_type

def get_entity_type(self):
return self.entity_type

def __eq__(self, other) -> bool:
if not isinstance(other, RegisteredEntityListener):
return False

return super().__eq__(other) and self.listener_id == other.listener_id

def __hash__(self):
return hash((self.listener_id, self._coroutine, self.entity_type))
26 changes: 26 additions & 0 deletions rustplus/identification/server_id.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
class ServerID:
def __init__(self, ip: str, port: str, player_id: int, player_token: int) -> None:
self.ip = ip
self.port = port
self.player_id = player_id
self.player_token = player_token

def __str__(self) -> str:
return f"{self.ip}:{self.port} {self.player_id} {self.player_token}"

def get_server_string(self) -> str:
return f"{self.ip}:{self.port}"

def __hash__(self):
return hash(self.__str__())

def __eq__(self, o: object) -> bool:
if not isinstance(o, ServerID):
return False

return (
self.ip == o.ip
and self.port == o.port
and self.player_id == o.player_id
and self.player_token == o.player_token
)
Empty file added rustplus/remote/__init__.py
Empty file.
3 changes: 3 additions & 0 deletions rustplus/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
62 changes: 62 additions & 0 deletions rustplus/remote/handler_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from collections import defaultdict
from typing import Set, Dict
from ..identification import RegisteredListener, RegisteredEntityListener, ServerID


class HandlerList:
def __init__(self) -> None:
self._handlers: Dict[ServerID, Set[RegisteredListener]] = defaultdict(set)

def unregister(self, listener: RegisteredListener, server_id: ServerID) -> None:
self._handlers[server_id].remove(listener)

def register(self, listener: RegisteredListener, server_id: ServerID) -> None:
self._handlers[server_id].add(listener)

def has(self, listener: RegisteredListener, server_id: ServerID) -> bool:
return listener in self._handlers[server_id]

def unregister_all(self) -> None:
self._handlers.clear()

def get_handlers(
self, server_id: ServerID
) -> Set[RegisteredListener]:
return self._handlers.get(server_id, set())


class EntityHandlerList(HandlerList):
def __init__(self) -> None:
super().__init__()
self._handlers: Dict[ServerID, Dict[str, Set[RegisteredEntityListener]]] = (
defaultdict(dict)
)

def unregister(self, listener: RegisteredEntityListener, server_id: ServerID) -> None:
if listener.listener_id in self._handlers.get(server_id):
self._handlers.get(server_id).get(listener.listener_id).remove(listener)

def register(self, listener: RegisteredEntityListener, server_id: ServerID) -> None:
if server_id not in self._handlers:
self._handlers[server_id] = defaultdict(set)

if listener.listener_id not in self._handlers.get(server_id):
self._handlers.get(server_id)[listener.listener_id] = set()

self._handlers.get(server_id).get(listener.listener_id).add(listener)

def has(self, listener: RegisteredEntityListener, server_id: ServerID) -> bool:
if server_id in self._handlers and listener.listener_id in self._handlers.get(
server_id
):
return listener in self._handlers.get(server_id).get(listener.listener_id)

return False

def unregister_all(self) -> None:
self._handlers.clear()

def get_handlers(
self, server_id: ServerID
) -> Dict[str, Set[RegisteredEntityListener]]:
return self._handlers.get(server_id, dict())
134 changes: 134 additions & 0 deletions rustplus/remote/ratelimiter/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import math
import time
import asyncio
from typing import Dict

from ...exceptions.exceptions import RateLimitError
from ...identification import ServerID


class TokenBucket:
def __init__(
self, current: float, maximum: float, refresh_rate: float, refresh_amount: float
) -> None:
self.current = current
self.max = maximum
self.refresh_rate = refresh_rate
self.refresh_amount = refresh_amount
self.last_update = time.time()
self.refresh_per_second = self.refresh_amount / self.refresh_rate

def can_consume(self, amount) -> bool:
if (self.current - amount) >= 0:
return True

return False

def consume(self, amount: int = 1) -> None:
self.current -= amount

def refresh(self) -> None:
time_now = time.time()

time_delta = time_now - self.last_update
self.last_update = time_now

self.current = min([self.current + time_delta * self.refresh_amount, self.max])


class RateLimiter:
SERVER_LIMIT = 50
SERVER_REFRESH_AMOUNT = 15

@classmethod
def default(cls) -> "RateLimiter":
"""
Returns a default rate limiter with 3 tokens per second
"""
return cls()

def __init__(self) -> None:
self.socket_buckets: Dict[ServerID, TokenBucket] = {}
self.server_buckets: Dict[str, TokenBucket] = {}
self.lock = asyncio.Lock()

def add_socket(
self,
server_id: ServerID,
current: float,
maximum: float,
refresh_rate: float,
refresh_amount: float,
) -> None:
self.socket_buckets[server_id] = TokenBucket(
current, maximum, refresh_rate, refresh_amount
)
if server_id.get_server_string() not in self.server_buckets:
self.server_buckets[server_id.get_server_string()] = TokenBucket(
self.SERVER_LIMIT, self.SERVER_LIMIT, 1, self.SERVER_REFRESH_AMOUNT
)

async def can_consume(self, server_id: ServerID, amount: int = 1) -> bool:
"""
Returns whether the user can consume the amount of tokens provided
"""
async with self.lock:
can_consume = True

for bucket in [
self.socket_buckets.get(server_id),
self.server_buckets.get(server_id.get_server_string()),
]:
bucket.refresh()
if not bucket.can_consume(amount):
can_consume = False

return can_consume

async def consume(self, server_id: ServerID, amount: int = 1) -> None:
"""
Consumes an amount of tokens from the bucket. You should first check to see whether it is possible with can_consume
"""
async with self.lock:
for bucket in [
self.socket_buckets.get(server_id),
self.server_buckets.get(server_id.get_server_string()),
]:
bucket.refresh()
if not bucket.can_consume(amount):
self.lock.release()
raise RateLimitError("Not Enough Tokens")
bucket.consume(amount)

async def get_estimated_delay_time(
self, server_id: ServerID, target_cost: int
) -> float:
"""
Returns how long until the amount of tokens needed will be available
"""
async with self.lock:
delay = 0
for bucket in [
self.socket_buckets.get(server_id),
self.server_buckets.get(server_id.get_server_string()),
]:
val = (
math.ceil(
(
((target_cost - bucket.current) / bucket.refresh_per_second)
+ 0.1
)
* 100
)
/ 100
)
if val > delay:
delay = val
return delay

async def remove(self, server_id: ServerID) -> None:
"""
Removes the limiter
"""
async with self.lock:
del self.socket_buckets[server_id]
1 change: 1 addition & 0 deletions rustplus/remote/websocket/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .ws import RustWebsocket
Loading

0 comments on commit 71b52d3

Please sign in to comment.