Initial project structure: reusable isometric bot engine with D2R implementation
This commit is contained in:
commit
e0282a7111
44 changed files with 3433 additions and 0 deletions
0
engine/safety/__init__.py
Normal file
0
engine/safety/__init__.py
Normal file
73
engine/safety/patterns.py
Normal file
73
engine/safety/patterns.py
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
"""Behavioral pattern randomization for anti-detection.
|
||||
|
||||
Varies bot behavior to avoid detectable patterns like identical
|
||||
farming routes, consistent timing, or perfect execution.
|
||||
"""
|
||||
|
||||
import random
|
||||
import logging
|
||||
from typing import List, Tuple, Callable, Any
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RouteRandomizer:
|
||||
"""Randomizes farming routes and action sequences."""
|
||||
|
||||
def __init__(self, variation_factor: float = 0.15):
|
||||
self.variation_factor = variation_factor
|
||||
|
||||
def shuffle_optional_steps(
|
||||
self, steps: List[Any], required_indices: List[int] = None,
|
||||
) -> List[Any]:
|
||||
"""Shuffle non-required steps while keeping required ones in order."""
|
||||
required_indices = set(required_indices or [])
|
||||
required = [(i, s) for i, s in enumerate(steps) if i in required_indices]
|
||||
optional = [s for i, s in enumerate(steps) if i not in required_indices]
|
||||
|
||||
random.shuffle(optional)
|
||||
|
||||
result = []
|
||||
opt_iter = iter(optional)
|
||||
req_iter = iter(required)
|
||||
next_req = next(req_iter, None)
|
||||
|
||||
for i in range(len(steps)):
|
||||
if next_req and next_req[0] == i:
|
||||
result.append(next_req[1])
|
||||
next_req = next(req_iter, None)
|
||||
else:
|
||||
result.append(next(opt_iter))
|
||||
|
||||
return result
|
||||
|
||||
def vary_route(
|
||||
self, waypoints: List[Tuple[int, int]],
|
||||
) -> List[Tuple[int, int]]:
|
||||
"""Add slight variations to a route's waypoints."""
|
||||
varied = []
|
||||
for x, y in waypoints:
|
||||
offset_x = int(x * self.variation_factor * random.uniform(-1, 1))
|
||||
offset_y = int(y * self.variation_factor * random.uniform(-1, 1))
|
||||
varied.append((x + offset_x, y + offset_y))
|
||||
return varied
|
||||
|
||||
def should_skip_optional(self, skip_chance: float = 0.1) -> bool:
|
||||
"""Randomly decide to skip an optional action."""
|
||||
return random.random() < skip_chance
|
||||
|
||||
|
||||
class ActionVariator:
|
||||
"""Varies how actions are performed."""
|
||||
|
||||
@staticmethod
|
||||
def vary_count(target: int, variance: int = 1) -> int:
|
||||
"""Vary a repeat count (e.g., click 2-4 times instead of always 3)."""
|
||||
return max(1, target + random.randint(-variance, variance))
|
||||
|
||||
@staticmethod
|
||||
def random_order(actions: List[Callable]) -> List[Callable]:
|
||||
"""Randomize the order of independent actions."""
|
||||
shuffled = actions.copy()
|
||||
random.shuffle(shuffled)
|
||||
return shuffled
|
||||
68
engine/safety/timing.py
Normal file
68
engine/safety/timing.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
"""Anti-detection timing and break scheduling.
|
||||
|
||||
Manages play sessions with realistic timing patterns to avoid
|
||||
behavioral detection systems.
|
||||
"""
|
||||
|
||||
import random
|
||||
import time
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class SessionSchedule:
|
||||
"""Defines a play session schedule."""
|
||||
min_session_hours: float = 1.0
|
||||
max_session_hours: float = 4.0
|
||||
min_break_minutes: float = 10.0
|
||||
max_break_minutes: float = 45.0
|
||||
max_daily_hours: float = 12.0
|
||||
|
||||
|
||||
class SessionTimer:
|
||||
"""Manages bot session timing to mimic human play patterns."""
|
||||
|
||||
def __init__(self, schedule: Optional[SessionSchedule] = None):
|
||||
self.schedule = schedule or SessionSchedule()
|
||||
self._session_start = time.time()
|
||||
self._daily_playtime = 0.0
|
||||
self._day_start = time.time()
|
||||
self._target_duration = self._roll_session_duration()
|
||||
|
||||
def _roll_session_duration(self) -> float:
|
||||
"""Generate random session duration in seconds."""
|
||||
hours = random.uniform(
|
||||
self.schedule.min_session_hours,
|
||||
self.schedule.max_session_hours,
|
||||
)
|
||||
return hours * 3600
|
||||
|
||||
def session_elapsed(self) -> float:
|
||||
"""Seconds elapsed in current session."""
|
||||
return time.time() - self._session_start
|
||||
|
||||
def should_stop_session(self) -> bool:
|
||||
"""Check if current session should end."""
|
||||
if self.session_elapsed() >= self._target_duration:
|
||||
return True
|
||||
if self._daily_playtime + self.session_elapsed() >= self.schedule.max_daily_hours * 3600:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_break_duration(self) -> float:
|
||||
"""Get randomized break duration in seconds."""
|
||||
return random.uniform(
|
||||
self.schedule.min_break_minutes * 60,
|
||||
self.schedule.max_break_minutes * 60,
|
||||
)
|
||||
|
||||
def start_new_session(self) -> None:
|
||||
"""Start a new play session after break."""
|
||||
self._daily_playtime += self.session_elapsed()
|
||||
self._session_start = time.time()
|
||||
self._target_duration = self._roll_session_duration()
|
||||
logger.info(f"New session: {self._target_duration/3600:.1f}h target")
|
||||
Loading…
Add table
Add a link
Reference in a new issue