Initial project structure: reusable isometric bot engine with D2R implementation

This commit is contained in:
Hoid 2026-02-14 08:50:36 +00:00
commit e0282a7111
44 changed files with 3433 additions and 0 deletions

View file

73
engine/safety/patterns.py Normal file
View 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
View 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")