Port loiter and harden 26.3 bed updates
This commit is contained in:
@@ -17,6 +17,10 @@ from mosfet.info import mcdata
|
||||
from mosfet.info import mobs
|
||||
|
||||
class CheckThreatsStates:
|
||||
THREAT_DISTANCE = 20
|
||||
CLEAR_DISTANCE = 30
|
||||
CHECK_INTERVAL = 2.0
|
||||
|
||||
def idle(self):
|
||||
return None
|
||||
|
||||
@@ -28,51 +32,63 @@ class CheckThreatsStates:
|
||||
w = self.g.world
|
||||
p = utils.pint(self.g.pos)
|
||||
|
||||
threats = w.find_threats(p, 40)
|
||||
threats = w.find_threats(p, self.THREAT_DISTANCE)
|
||||
|
||||
if threats:
|
||||
print('Found', len(threats), 'threats, fleeing:')
|
||||
print('Found', len(threats), 'threats, seeking safety:')
|
||||
print(threats)
|
||||
self.threats = threats
|
||||
self.state = self.find_safety
|
||||
else:
|
||||
print('Aborting, no threats')
|
||||
self.state = self.cleanup
|
||||
self.wait_time = self.CHECK_INTERVAL
|
||||
self.state = self.wait
|
||||
|
||||
def find_safety(self):
|
||||
w = self.g.world
|
||||
p = utils.pint(self.g.pos)
|
||||
|
||||
safety = w.find_blocks_indexed(p, [blocks.EMERALD_BLOCK])
|
||||
|
||||
if not safety:
|
||||
print('No emerald blocks found, aborting')
|
||||
self.state = self.cleanup
|
||||
return
|
||||
|
||||
safety.sort(key=lambda s: utils.phyp(p, s))
|
||||
print('Found emerald blocks:', safety)
|
||||
|
||||
for s in safety:
|
||||
s = utils.padd(s, path.BLOCK_ABOVE)
|
||||
navpath = w.path_to_place(p, s)
|
||||
|
||||
if navpath:
|
||||
safety_blocks = w.find_blocks_indexed(
|
||||
p, [blocks.EMERALD_BLOCK], self.THREAT_DISTANCE * 4)
|
||||
for block in safety_blocks:
|
||||
spot = utils.padd(block, path.BLOCK_ABOVE)
|
||||
if w.find_threats(spot, self.CLEAR_DISTANCE):
|
||||
continue
|
||||
navpath = w.path_to_place(p, spot)
|
||||
if navpath is not None:
|
||||
self.safety = spot
|
||||
self.external_safety = True
|
||||
self.g.path = navpath
|
||||
self.state = self.going_to_safety
|
||||
self.safety = s
|
||||
print('Going to safety', self.safety)
|
||||
print('Going to emerald safety point', self.safety)
|
||||
return
|
||||
else:
|
||||
print('Cant get to safety', self.safety)
|
||||
time.sleep(0.1)
|
||||
|
||||
print('Cant get to safety, aborting')
|
||||
self.state = self.cleanup
|
||||
# No configured emerald safe point was reachable. Retreat to a clear,
|
||||
# supported location that the modern navigator can actually reach.
|
||||
for spot in w.find_bed_areas(p, self.CLEAR_DISTANCE * 2):
|
||||
if w.find_threats(spot, self.CLEAR_DISTANCE):
|
||||
continue
|
||||
navpath = w.path_to_place(p, spot)
|
||||
if navpath is not None:
|
||||
self.safety = spot
|
||||
self.external_safety = False
|
||||
self.g.path = navpath
|
||||
self.state = self.going_to_safety
|
||||
print('Fleeing to clear area', self.safety)
|
||||
return
|
||||
|
||||
print('No reachable safe area found')
|
||||
self.wait_time = self.CHECK_INTERVAL
|
||||
self.state = self.wait
|
||||
|
||||
def going_to_safety(self):
|
||||
if utils.pint(self.g.pos) == self.safety:
|
||||
print('At safety spot, waiting to be moved')
|
||||
self.state = self.wait_for_move
|
||||
if self.external_safety:
|
||||
print('At emerald safety point, waiting for server move')
|
||||
self.state = self.wait_for_move
|
||||
else:
|
||||
print('At clear retreat spot')
|
||||
self.wait_time = 3
|
||||
self.state = self.wait
|
||||
|
||||
def wait_for_move(self):
|
||||
# wait for the server to move the bot when it's safe
|
||||
@@ -103,6 +119,8 @@ class CheckThreatsStates:
|
||||
|
||||
self.safety = None
|
||||
self.wait_time = 0
|
||||
self.threats = []
|
||||
self.external_safety = False
|
||||
|
||||
def run(self):
|
||||
self.state()
|
||||
|
||||
+106
-15
@@ -24,7 +24,11 @@ class SleepWithBedStates:
|
||||
self.my_bed = False
|
||||
self.area = None
|
||||
self.opening = None
|
||||
self.bed_head = None
|
||||
self.beds = []
|
||||
self.sleep_use_attempts = 0
|
||||
self.waiting_for_night = False
|
||||
self.waiting_for_time_update = False
|
||||
|
||||
current_time = self.g.get('time')
|
||||
if current_time is None or current_time < 12000:
|
||||
@@ -39,16 +43,55 @@ class SleepWithBedStates:
|
||||
|
||||
self.state = self.find_beds
|
||||
|
||||
@staticmethod
|
||||
def _bed_facing(state_id):
|
||||
props = blocks.PROPS.get(state_id, {})
|
||||
return {
|
||||
'north': (0, 0, -1), 'south': (0, 0, 1),
|
||||
'east': (1, 0, 0), 'west': (-1, 0, 0),
|
||||
}.get(props.get('facing'))
|
||||
|
||||
def _bed_foot(self, position):
|
||||
w = self.g.world
|
||||
state_id = w.block_at(*position)
|
||||
props = blocks.PROPS.get(state_id, {})
|
||||
if props.get('part') != 'head':
|
||||
return position
|
||||
facing = self._bed_facing(state_id)
|
||||
if facing is None:
|
||||
return position
|
||||
foot = utils.psub(position, facing)
|
||||
foot_state = w.block_at(*foot)
|
||||
if (foot_state in blocks.BED_IDS and
|
||||
blocks.PROPS.get(foot_state, {}).get('part') == 'foot'):
|
||||
return foot
|
||||
return position
|
||||
|
||||
def _bed_head(self, foot):
|
||||
state_id = self.g.world.block_at(*foot)
|
||||
facing = self._bed_facing(state_id)
|
||||
return utils.padd(foot, facing) if facing is not None else foot
|
||||
|
||||
def find_beds(self):
|
||||
print('Finding beds...')
|
||||
w = self.g.world
|
||||
p = utils.pint(self.g.pos)
|
||||
|
||||
result = ([] if self.force_new_bed else
|
||||
w.find_blocks_indexed(p, blocks.BED_IDS, 80))
|
||||
if self.force_new_bed:
|
||||
result = []
|
||||
print('Skipping existing beds; placing a new bed as requested')
|
||||
else:
|
||||
indexed = w.find_blocks_indexed(p, blocks.BED_IDS, 80)
|
||||
# Keep a tiny direct scan as a guard against stale/missing chunk
|
||||
# index entries, especially around respawn and chunk-cache changes.
|
||||
nearby = list(w.find_blocks_3d(
|
||||
p, blocks.BED_IDS, distance=8, y_limit=8))
|
||||
result = list(dict.fromkeys(nearby + indexed))
|
||||
result.sort(key=lambda bed: utils.phyp(p, bed))
|
||||
if nearby and not indexed:
|
||||
print('Found beds by local scan; chunk index had none')
|
||||
|
||||
result = list(dict.fromkeys(self._bed_foot(bed) for bed in result))
|
||||
self.beds = []
|
||||
for bed in result:
|
||||
if bed in self.bad_beds:
|
||||
@@ -78,8 +121,11 @@ class SleepWithBedStates:
|
||||
continue
|
||||
|
||||
self.area = bed
|
||||
self.bed_head = self._bed_head(bed)
|
||||
self.opening = navpath[-2] if len(navpath) > 1 else p
|
||||
self.g.path = navpath[:-1]
|
||||
self.opening_deadline = time.monotonic() + max(
|
||||
8.0, len(navpath) * 0.75)
|
||||
self.g.look_at = self.area
|
||||
self.state = self.going_to_bed
|
||||
return
|
||||
@@ -87,13 +133,23 @@ class SleepWithBedStates:
|
||||
print('No reachable beds; trying to place one')
|
||||
self.state = self.select_bed
|
||||
|
||||
def _at_opening(self):
|
||||
position = utils.pint(self.g.pos)
|
||||
return utils.phyp(position, self.opening) <= 1.25
|
||||
|
||||
def going_to_bed(self):
|
||||
if utils.pint(self.g.pos) == self.opening:
|
||||
if self._at_opening():
|
||||
print('At existing bed')
|
||||
self.g.path = []
|
||||
self.my_bed = False
|
||||
self.g.look_at = self.area
|
||||
self.wait_time = 0.5
|
||||
self.state = self.turn_to_bed
|
||||
elif time.monotonic() >= self.opening_deadline:
|
||||
print('Could not reach bed opening', self.opening,
|
||||
'; trying another bed')
|
||||
self.bad_beds.append(self.area)
|
||||
self.state = self.choose_bed
|
||||
|
||||
def turn_to_bed(self):
|
||||
self.g.look_at = self.area
|
||||
@@ -151,15 +207,22 @@ class SleepWithBedStates:
|
||||
return
|
||||
|
||||
self.g.path = navpath
|
||||
self.opening_deadline = time.monotonic() + max(
|
||||
8.0, len(navpath) * 0.75)
|
||||
self.g.look_at = self.area
|
||||
self.state = self.going_to_area
|
||||
|
||||
def going_to_area(self):
|
||||
if utils.pint(self.g.pos) == self.opening:
|
||||
if self._at_opening():
|
||||
print('At bed area')
|
||||
self.g.path = []
|
||||
self.g.look_at = self.area
|
||||
self.wait_time = 0.5
|
||||
self.state = self.turn_to_bed_area
|
||||
elif time.monotonic() >= self.opening_deadline:
|
||||
print('Could not reach bed placement opening', self.opening)
|
||||
self.bad_areas.append(self.area)
|
||||
self.state = self.cleanup
|
||||
|
||||
def turn_to_bed_area(self):
|
||||
self.g.look_at = self.area
|
||||
@@ -174,28 +237,38 @@ class SleepWithBedStates:
|
||||
support, BlockFace.TOP,
|
||||
hand=self.g.get('holding_hand', 0))
|
||||
self.my_bed = True
|
||||
self.wait_time = 0.5
|
||||
self.bed_place_deadline = time.monotonic() + 5.0
|
||||
self.state = self.wait_use
|
||||
|
||||
def wait_use(self):
|
||||
if self.wait_time > 0:
|
||||
self.wait_time -= utils.TICK
|
||||
return
|
||||
|
||||
if self.g.world.block_at(*self.area) not in blocks.BED_IDS:
|
||||
# Block updates can arrive after the use packet; poll authoritative
|
||||
# chunk state for several seconds instead of giving up after 0.5s.
|
||||
if self.g.world.block_at(*self.area) in blocks.BED_IDS:
|
||||
self.bed_head = self._bed_head(self.area)
|
||||
self.state = self.use_bed
|
||||
elif time.monotonic() >= self.bed_place_deadline:
|
||||
print('Bed placement was not confirmed at', self.area)
|
||||
self.bad_areas.append(self.area)
|
||||
self.my_bed = False
|
||||
self.state = self.cleanup
|
||||
return
|
||||
self.state = self.use_bed
|
||||
|
||||
def use_bed(self):
|
||||
w = self.g.world
|
||||
current_time = self.g.get('time')
|
||||
|
||||
if current_time is None or current_time < 12550:
|
||||
if current_time is None:
|
||||
if not getattr(self, 'waiting_for_time_update', False):
|
||||
print('Waiting for world time update before using bed')
|
||||
self.waiting_for_time_update = True
|
||||
return
|
||||
if current_time < 12550:
|
||||
if not getattr(self, 'waiting_for_night', False):
|
||||
print('Bed reached; waiting until sleep time (time={})'.format(
|
||||
current_time))
|
||||
self.waiting_for_night = True
|
||||
return
|
||||
self.waiting_for_night = False
|
||||
self.waiting_for_time_update = False
|
||||
|
||||
if w.block_at(*self.area) not in blocks.BED_IDS:
|
||||
print('Bed disappeared before use:', self.area)
|
||||
@@ -208,8 +281,13 @@ class SleepWithBedStates:
|
||||
return
|
||||
|
||||
print('Using bed')
|
||||
self.g.game.place_block(self.area, BlockFace.TOP)
|
||||
# Beds are only 9/16 block tall. Aim inside their collision shape;
|
||||
# the near-top cursor used for placing a new block misses them.
|
||||
self.g.game.place_block(
|
||||
self.area, BlockFace.TOP, cursor_y=0.5)
|
||||
self.sleep_deadline = time.monotonic() + 5.0
|
||||
self.sleep_retry_at = time.monotonic() + 1.5
|
||||
self.sleep_use_attempts = 1
|
||||
self.sleep_confirmed = False
|
||||
if not self.silent:
|
||||
self.g.chat.send('zzz')
|
||||
@@ -236,9 +314,19 @@ class SleepWithBedStates:
|
||||
elif self.g.get('correction_count', 0) and self.sleep_confirmed:
|
||||
print('Woke up by movement')
|
||||
self.state = self.break_bed
|
||||
elif (not self.sleep_confirmed and
|
||||
self.sleep_use_attempts < 2 and
|
||||
time.monotonic() >= self.sleep_retry_at):
|
||||
print('Bed use not confirmed on foot; trying head block',
|
||||
self.bed_head)
|
||||
self.g.game.place_block(
|
||||
self.bed_head, BlockFace.TOP, cursor_y=0.5)
|
||||
self.sleep_use_attempts += 1
|
||||
self.sleep_retry_at = time.monotonic() + 1.5
|
||||
elif (not self.sleep_confirmed and
|
||||
time.monotonic() >= self.sleep_deadline):
|
||||
print('Bed use was not confirmed')
|
||||
print('Bed use was not confirmed; blacklisting', self.area)
|
||||
self.bad_beds.append(self.area)
|
||||
self.state = self.cleanup
|
||||
|
||||
def break_bed(self):
|
||||
@@ -299,8 +387,11 @@ class SleepWithBedStates:
|
||||
self.bad_beds = []
|
||||
self.area = None
|
||||
self.opening = None
|
||||
self.bed_head = None
|
||||
self.bad_areas = []
|
||||
self.wait_time = 0
|
||||
self.sleep_use_attempts = 0
|
||||
self.sleep_retry_at = 0
|
||||
self.force_new_bed = False
|
||||
self.sleep_confirmed = False
|
||||
self.sleep_deadline = 0
|
||||
|
||||
Reference in New Issue
Block a user