Find chest openings within interaction range
This commit is contained in:
+7
-5
@@ -385,7 +385,7 @@ class LegacyGame:
|
|||||||
packet.face = 1
|
packet.face = 1
|
||||||
self.g.connection.write_packet(packet)
|
self.g.connection.write_packet(packet)
|
||||||
|
|
||||||
def open_container(self, location):
|
def open_container(self, location, face=BlockFace.TOP, cursor=None):
|
||||||
location = tuple(int(value) for value in location)
|
location = tuple(int(value) for value in location)
|
||||||
positions = {location}
|
positions = {location}
|
||||||
world = self.g.get('world')
|
world = self.g.get('world')
|
||||||
@@ -399,7 +399,7 @@ class LegacyGame:
|
|||||||
'positions': tuple(sorted(positions)),
|
'positions': tuple(sorted(positions)),
|
||||||
'requested_at': time.monotonic(),
|
'requested_at': time.monotonic(),
|
||||||
}
|
}
|
||||||
self.place_block(location, BlockFace.TOP)
|
self.place_block(location, face, cursor=cursor)
|
||||||
|
|
||||||
def handle_window(self, packet):
|
def handle_window(self, packet):
|
||||||
print(packet)
|
print(packet)
|
||||||
@@ -1048,14 +1048,16 @@ class Game(LegacyGame):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def place_block(self, location, face, hand=0, cursor_y=None,
|
def place_block(self, location, face, hand=0, cursor_y=None,
|
||||||
sneaking=False):
|
sneaking=False, cursor=None):
|
||||||
location = tuple(int(value) for value in location)
|
location = tuple(int(value) for value in location)
|
||||||
|
if cursor is None:
|
||||||
if cursor_y is None:
|
if cursor_y is None:
|
||||||
cursor_y = 0.99 if int(face) == 1 else 0.5
|
cursor_y = 0.99 if int(face) == 1 else 0.5
|
||||||
|
cursor = (0.5, cursor_y, 0.5)
|
||||||
packet = UseItemOnBlockPacket(
|
packet = UseItemOnBlockPacket(
|
||||||
hand=hand, location=location, face=int(face),
|
hand=hand, location=location, face=int(face),
|
||||||
cursor_x=0.5, cursor_y=cursor_y,
|
cursor_x=float(cursor[0]), cursor_y=float(cursor[1]),
|
||||||
cursor_z=0.5, inside_block=False, world_border_hit=False,
|
cursor_z=float(cursor[2]), inside_block=False, world_border_hit=False,
|
||||||
sequence=self._next_sequence())
|
sequence=self._next_sequence())
|
||||||
if sneaking:
|
if sneaking:
|
||||||
self.g.connection.write_packet(PlayerInputPacket(flags=0x20),
|
self.g.connection.write_packet(PlayerInputPacket(flags=0x20),
|
||||||
|
|||||||
+24
-16
@@ -69,12 +69,13 @@ class CacheItemsStates:
|
|||||||
self.area = container['position']
|
self.area = container['position']
|
||||||
self.container_item_ids = container['item_ids']
|
self.container_item_ids = container['item_ids']
|
||||||
self.is_labeled_container = True
|
self.is_labeled_container = True
|
||||||
for opening in w.find_cache_openings(self.area):
|
for opening in w.find_cache_openings(self.area, p):
|
||||||
navpath = w.path_to_place(p, opening)
|
navpath = w.path_to_place(p, opening['position'])
|
||||||
if navpath:
|
if navpath:
|
||||||
print('Going to labeled chest', container['labels'],
|
print('Going to labeled chest', container['labels'],
|
||||||
self.area)
|
self.area)
|
||||||
self.opening = opening
|
self.opening = opening['position']
|
||||||
|
self.open_interaction = opening
|
||||||
self.g.path = navpath
|
self.g.path = navpath
|
||||||
self.state = self.going_to_trapped_chest
|
self.state = self.going_to_trapped_chest
|
||||||
return
|
return
|
||||||
@@ -96,11 +97,12 @@ class CacheItemsStates:
|
|||||||
chest = self.trapped_chests[0]
|
chest = self.trapped_chests[0]
|
||||||
self.area = chest
|
self.area = chest
|
||||||
|
|
||||||
for opening in w.find_cache_openings(chest):
|
for opening in w.find_cache_openings(chest, p):
|
||||||
navpath = w.path_to_place(p, opening)
|
navpath = w.path_to_place(p, opening['position'])
|
||||||
if navpath:
|
if navpath:
|
||||||
print('navpath to trapped chest:', navpath)
|
print('navpath to trapped chest:', navpath)
|
||||||
self.opening = opening
|
self.opening = opening['position']
|
||||||
|
self.open_interaction = opening
|
||||||
self.g.path = navpath
|
self.g.path = navpath
|
||||||
self.state = self.going_to_trapped_chest
|
self.state = self.going_to_trapped_chest
|
||||||
return
|
return
|
||||||
@@ -110,12 +112,12 @@ class CacheItemsStates:
|
|||||||
|
|
||||||
def going_to_trapped_chest(self):
|
def going_to_trapped_chest(self):
|
||||||
if utils.pint(self.g.pos) == self.opening:
|
if utils.pint(self.g.pos) == self.opening:
|
||||||
self.g.look_at = self.area
|
self.g.look_at = self.open_interaction['look_at']
|
||||||
self.wait_time = 0.5
|
self.wait_time = 0.5
|
||||||
self.state = self.turn_to_trapped_chest
|
self.state = self.turn_to_trapped_chest
|
||||||
|
|
||||||
def turn_to_trapped_chest(self):
|
def turn_to_trapped_chest(self):
|
||||||
self.g.look_at = self.area
|
self.g.look_at = self.open_interaction['look_at']
|
||||||
self.wait_time -= utils.TICK
|
self.wait_time -= utils.TICK
|
||||||
if self.wait_time <= 0:
|
if self.wait_time <= 0:
|
||||||
self.state = self.open_chest
|
self.state = self.open_chest
|
||||||
@@ -144,12 +146,14 @@ class CacheItemsStates:
|
|||||||
return
|
return
|
||||||
|
|
||||||
self.area = area
|
self.area = area
|
||||||
openings = w.find_cache_openings(self.area)
|
openings = w.find_cache_openings(self.area, p)
|
||||||
|
|
||||||
for o in openings:
|
for opening in openings:
|
||||||
navpath = w.path_to_place(p, o)
|
navpath = w.path_to_place(p, opening['position'])
|
||||||
self.opening = o
|
if navpath:
|
||||||
if navpath: break
|
self.opening = opening['position']
|
||||||
|
self.open_interaction = opening
|
||||||
|
break
|
||||||
else: # for
|
else: # for
|
||||||
print('Unable to get to cache area', self.area)
|
print('Unable to get to cache area', self.area)
|
||||||
self.bad_areas.append(self.area)
|
self.bad_areas.append(self.area)
|
||||||
@@ -161,12 +165,12 @@ class CacheItemsStates:
|
|||||||
|
|
||||||
def going_to_area(self):
|
def going_to_area(self):
|
||||||
if utils.pint(self.g.pos) == self.opening:
|
if utils.pint(self.g.pos) == self.opening:
|
||||||
self.g.look_at = self.area
|
self.g.look_at = self.open_interaction['look_at']
|
||||||
self.wait_time = 0.5
|
self.wait_time = 0.5
|
||||||
self.state = self.turn_to_cache_spot
|
self.state = self.turn_to_cache_spot
|
||||||
|
|
||||||
def turn_to_cache_spot(self):
|
def turn_to_cache_spot(self):
|
||||||
self.g.look_at = self.area
|
self.g.look_at = self.open_interaction['look_at']
|
||||||
self.wait_time -= utils.TICK
|
self.wait_time -= utils.TICK
|
||||||
if self.wait_time <= 0:
|
if self.wait_time <= 0:
|
||||||
self.state = self.place_chest
|
self.state = self.place_chest
|
||||||
@@ -180,7 +184,10 @@ class CacheItemsStates:
|
|||||||
print('Opening chest')
|
print('Opening chest')
|
||||||
self.failed_transfer_slots.clear()
|
self.failed_transfer_slots.clear()
|
||||||
self.pending_transfer = None
|
self.pending_transfer = None
|
||||||
self.g.game.open_container(self.area)
|
self.g.game.open_container(
|
||||||
|
self.open_interaction['target'],
|
||||||
|
face=self.open_interaction['face'],
|
||||||
|
cursor=self.open_interaction['cursor'])
|
||||||
self.wait_time = 1
|
self.wait_time = 1
|
||||||
self.state = self.wait
|
self.state = self.wait
|
||||||
|
|
||||||
@@ -368,6 +375,7 @@ class CacheItemsStates:
|
|||||||
self.container_item_ids = None
|
self.container_item_ids = None
|
||||||
self.area = None
|
self.area = None
|
||||||
self.opening = None
|
self.opening = None
|
||||||
|
self.open_interaction = None
|
||||||
self.trapped_chests = []
|
self.trapped_chests = []
|
||||||
self.bad_areas = []
|
self.bad_areas = []
|
||||||
self.wait_time = 0
|
self.wait_time = 0
|
||||||
|
|||||||
+190
-2
@@ -3,6 +3,7 @@ import json
|
|||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
|
from math import floor
|
||||||
from itertools import count
|
from itertools import count
|
||||||
from copy import copy
|
from copy import copy
|
||||||
|
|
||||||
@@ -11,6 +12,10 @@ from mosfet import path
|
|||||||
from mosfet.info import blocks
|
from mosfet.info import blocks
|
||||||
from mosfet.info import items
|
from mosfet.info import items
|
||||||
from mosfet.info import mobs
|
from mosfet.info import mobs
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
|
|
||||||
|
CHEST_OPENING_RADIUS = 3
|
||||||
|
|
||||||
|
|
||||||
class World:
|
class World:
|
||||||
def __init__(self, global_state):
|
def __init__(self, global_state):
|
||||||
@@ -625,8 +630,191 @@ class World:
|
|||||||
self._bed_occupancy_states[bed] = occupied
|
self._bed_occupancy_states[bed] = occupied
|
||||||
return occupied
|
return occupied
|
||||||
|
|
||||||
def find_cache_openings(self, area):
|
@staticmethod
|
||||||
return self.find_bed_openings(area)
|
def _is_chest_state(state_id):
|
||||||
|
name = blocks.BLOCKS.get(state_id)
|
||||||
|
return (name is not None and name != 'ender_chest' and
|
||||||
|
(name == 'chest' or name.endswith('_chest')))
|
||||||
|
|
||||||
|
def _cache_opening_standable(self, position):
|
||||||
|
at_feet = self.block_at(*position)
|
||||||
|
above = self.block_at(*utils.padd(position, path.BLOCK_ABOVE))
|
||||||
|
below = self.block_at(*utils.padd(position, path.BLOCK_BELOW))
|
||||||
|
return (at_feet in blocks.NON_SOLID_IDS and
|
||||||
|
at_feet not in blocks.AVOID_IDS and
|
||||||
|
above in blocks.NON_SOLID_IDS and
|
||||||
|
above not in blocks.AVOID_IDS and
|
||||||
|
below is not None and
|
||||||
|
below not in blocks.NON_SOLID_IDS and
|
||||||
|
below not in blocks.AVOID_IDS)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _segment_intersects_aabb(start, end, minimum, maximum):
|
||||||
|
lower, upper = 0.0, 1.0
|
||||||
|
for axis in range(3):
|
||||||
|
origin = start[axis]
|
||||||
|
delta = end[axis] - origin
|
||||||
|
if abs(delta) < 1e-9:
|
||||||
|
if origin < minimum[axis] or origin > maximum[axis]:
|
||||||
|
return False
|
||||||
|
continue
|
||||||
|
first = (minimum[axis] - origin) / delta
|
||||||
|
second = (maximum[axis] - origin) / delta
|
||||||
|
if first > second:
|
||||||
|
first, second = second, first
|
||||||
|
lower = max(lower, first)
|
||||||
|
upper = min(upper, second)
|
||||||
|
if lower > upper:
|
||||||
|
return False
|
||||||
|
return upper >= 0.0 and lower <= 1.0
|
||||||
|
|
||||||
|
def _has_cache_line_of_sight(self, start, end, target):
|
||||||
|
first = tuple(floor(min(start[i], end[i])) for i in range(3))
|
||||||
|
last = tuple(floor(max(start[i], end[i])) for i in range(3))
|
||||||
|
for x in range(first[0], last[0] + 1):
|
||||||
|
for y in range(first[1], last[1] + 1):
|
||||||
|
for z in range(first[2], last[2] + 1):
|
||||||
|
position = (x, y, z)
|
||||||
|
if position == target:
|
||||||
|
continue
|
||||||
|
if not self._segment_intersects_aabb(
|
||||||
|
start, end, position, utils.padd(position, (1, 1, 1))):
|
||||||
|
continue
|
||||||
|
state_id = self.block_at(*position)
|
||||||
|
if state_id is None:
|
||||||
|
return False
|
||||||
|
for box in blocks.COLLISION_BOXES.get(state_id, ()):
|
||||||
|
minimum = (x + box[0], y + box[1], z + box[2])
|
||||||
|
maximum = (x + box[3], y + box[4], z + box[5])
|
||||||
|
if self._segment_intersects_aabb(
|
||||||
|
start, end, minimum, maximum):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _chest_interaction_points(target, boxes):
|
||||||
|
points = []
|
||||||
|
for box in boxes:
|
||||||
|
x0, y0, z0, x1, y1, z1 = box
|
||||||
|
xm, zm = (x0 + x1) / 2, (z0 + z1) / 2
|
||||||
|
for fraction in (0.2, 0.5, 0.8):
|
||||||
|
y = y0 + (y1 - y0) * fraction
|
||||||
|
points.extend((
|
||||||
|
(target[0] + xm, target[1] + y, target[2] + z0),
|
||||||
|
(target[0] + xm, target[1] + y, target[2] + z1),
|
||||||
|
(target[0] + x0, target[1] + y, target[2] + zm),
|
||||||
|
(target[0] + x1, target[1] + y, target[2] + zm),
|
||||||
|
))
|
||||||
|
for x_fraction, z_fraction in (
|
||||||
|
(0.25, 0.25), (0.25, 0.75),
|
||||||
|
(0.75, 0.25), (0.75, 0.75), (0.5, 0.5)):
|
||||||
|
points.append((
|
||||||
|
target[0] + x0 + (x1 - x0) * x_fraction,
|
||||||
|
target[1] + y1,
|
||||||
|
target[2] + z0 + (z1 - z0) * z_fraction,
|
||||||
|
))
|
||||||
|
return points
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _ray_chest_surface_hit(start, end, target, boxes):
|
||||||
|
negative_faces = (BlockFace.WEST, BlockFace.BOTTOM, BlockFace.NORTH)
|
||||||
|
positive_faces = (BlockFace.EAST, BlockFace.TOP, BlockFace.SOUTH)
|
||||||
|
delta = tuple(end[i] - start[i] for i in range(3))
|
||||||
|
hits = []
|
||||||
|
for box in boxes:
|
||||||
|
minimum = tuple(target[i] + box[i] for i in range(3))
|
||||||
|
maximum = tuple(target[i] + box[i + 3] for i in range(3))
|
||||||
|
enter, leave = float('-inf'), float('inf')
|
||||||
|
entering_face = None
|
||||||
|
for axis in range(3):
|
||||||
|
if abs(delta[axis]) < 1e-9:
|
||||||
|
if (start[axis] < minimum[axis] or
|
||||||
|
start[axis] > maximum[axis]):
|
||||||
|
break
|
||||||
|
continue
|
||||||
|
near = (minimum[axis] - start[axis]) / delta[axis]
|
||||||
|
far = (maximum[axis] - start[axis]) / delta[axis]
|
||||||
|
face = negative_faces[axis]
|
||||||
|
if near > far:
|
||||||
|
near, far = far, near
|
||||||
|
face = positive_faces[axis]
|
||||||
|
if near > enter:
|
||||||
|
enter = near
|
||||||
|
entering_face = face
|
||||||
|
leave = min(leave, far)
|
||||||
|
if enter > leave:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if entering_face is None or enter < 0 or enter > 1:
|
||||||
|
continue
|
||||||
|
hit_point = tuple(start[i] + delta[i] * enter
|
||||||
|
for i in range(3))
|
||||||
|
cursor = tuple(hit_point[i] - target[i] for i in range(3))
|
||||||
|
hits.append((enter, entering_face, cursor, hit_point))
|
||||||
|
if not hits:
|
||||||
|
return None
|
||||||
|
_distance, face, cursor, hit_point = min(hits, key=lambda hit: hit[0])
|
||||||
|
return face, cursor, hit_point
|
||||||
|
|
||||||
|
def find_cache_openings(self, area, center=None, radius=None):
|
||||||
|
"""Find standable nearby spots with a clear ray to a chest surface."""
|
||||||
|
area = tuple(area)
|
||||||
|
center = tuple(center if center is not None else area)
|
||||||
|
radius = CHEST_OPENING_RADIUS if radius is None else radius
|
||||||
|
state_id = self.block_at(*area)
|
||||||
|
targets = (self._double_chest_positions(area)
|
||||||
|
if self._is_chest_state(state_id) else (area,))
|
||||||
|
default_chest = min(blocks.CHEST_IDS)
|
||||||
|
interactions = {}
|
||||||
|
|
||||||
|
for target in targets:
|
||||||
|
target_state = self.block_at(*target)
|
||||||
|
boxes = blocks.COLLISION_BOXES.get(target_state) or \
|
||||||
|
blocks.COLLISION_BOXES[default_chest]
|
||||||
|
hit_points = self._chest_interaction_points(target, boxes)
|
||||||
|
for horizontal in utils.search_2d(radius + 1):
|
||||||
|
for dy in range(-radius - 1, radius + 2):
|
||||||
|
offset = (horizontal[0], dy, horizontal[2])
|
||||||
|
opening = utils.padd(target, offset)
|
||||||
|
if (opening in targets or
|
||||||
|
not self._cache_opening_standable(opening)):
|
||||||
|
continue
|
||||||
|
eye = (opening[0] + 0.5, opening[1] + 1.62,
|
||||||
|
opening[2] + 0.5)
|
||||||
|
for aim_point in hit_points:
|
||||||
|
if not self._has_cache_line_of_sight(
|
||||||
|
eye, aim_point, target):
|
||||||
|
continue
|
||||||
|
hit = self._ray_chest_surface_hit(
|
||||||
|
eye, aim_point, target, boxes)
|
||||||
|
if hit is None:
|
||||||
|
continue
|
||||||
|
face, cursor, hit_point = hit
|
||||||
|
hit_distance = utils.phyp(eye, hit_point)
|
||||||
|
if hit_distance > radius:
|
||||||
|
continue
|
||||||
|
interaction = {
|
||||||
|
'position': opening,
|
||||||
|
'target': target,
|
||||||
|
'face': face,
|
||||||
|
'cursor': cursor,
|
||||||
|
'hit_point': hit_point,
|
||||||
|
'look_at': tuple(value - 0.5
|
||||||
|
for value in hit_point),
|
||||||
|
}
|
||||||
|
distance = utils.phyp(center, opening)
|
||||||
|
interaction['distance'] = distance
|
||||||
|
interaction['hit_distance'] = hit_distance
|
||||||
|
previous = interactions.get(opening)
|
||||||
|
if (previous is None or
|
||||||
|
interaction['hit_distance'] <
|
||||||
|
previous['hit_distance']):
|
||||||
|
interactions[opening] = interaction
|
||||||
|
break
|
||||||
|
|
||||||
|
return sorted(interactions.values(),
|
||||||
|
key=lambda result: (result['distance'],
|
||||||
|
result['position']))
|
||||||
|
|
||||||
def find_objects(self, object_ids):
|
def find_objects(self, object_ids):
|
||||||
result = []
|
result = []
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import unittest
|
||||||
|
from munch import Munch
|
||||||
|
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
|
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.world import CHEST_OPENING_RADIUS, World
|
||||||
|
|
||||||
|
|
||||||
|
class FakeChunks:
|
||||||
|
def __init__(self, states):
|
||||||
|
self.states = states
|
||||||
|
|
||||||
|
def get_block_at(self, x, y, z):
|
||||||
|
return self.states.get((x, y, z), blocks.AIR)
|
||||||
|
|
||||||
|
|
||||||
|
class CacheOpeningTests(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.chest = (0, 64, 0)
|
||||||
|
chest_state = min(blocks.CHEST_IDS)
|
||||||
|
stone = blocks.STONE
|
||||||
|
states = {self.chest: chest_state}
|
||||||
|
for x in range(-4, 5):
|
||||||
|
for z in range(-4, 5):
|
||||||
|
states[(x, 63, z)] = stone
|
||||||
|
self.world = World(Munch(chunks=FakeChunks(states)))
|
||||||
|
|
||||||
|
def test_openings_include_visible_positions_up_to_configured_radius(self):
|
||||||
|
opening = (3, 64, 0)
|
||||||
|
openings = self.world.find_cache_openings(
|
||||||
|
self.chest, center=(3, 64, 0))
|
||||||
|
by_position = {tuple(candidate['position']): candidate
|
||||||
|
for candidate in openings}
|
||||||
|
|
||||||
|
self.assertEqual(CHEST_OPENING_RADIUS, 3)
|
||||||
|
self.assertIn(opening, by_position)
|
||||||
|
self.assertTrue(all(candidate['hit_distance'] <= CHEST_OPENING_RADIUS
|
||||||
|
for candidate in openings))
|
||||||
|
self.assertIn(by_position[opening]['face'], (
|
||||||
|
BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST,
|
||||||
|
BlockFace.EAST, BlockFace.TOP,
|
||||||
|
))
|
||||||
|
self.assertEqual(len(by_position[opening]['cursor']), 3)
|
||||||
|
|
||||||
|
def test_range_is_measured_from_eye_to_interaction_surface(self):
|
||||||
|
opening = (3, 63, 0)
|
||||||
|
self.world.g.chunks.states[opening] = blocks.AIR
|
||||||
|
self.world.g.chunks.states[(3, 62, 0)] = blocks.STONE
|
||||||
|
|
||||||
|
openings = self.world.find_cache_openings(
|
||||||
|
self.chest, center=opening)
|
||||||
|
by_position = {tuple(candidate['position']): candidate
|
||||||
|
for candidate in openings}
|
||||||
|
|
||||||
|
self.assertIn(opening, by_position)
|
||||||
|
self.assertLessEqual(by_position[opening]['hit_distance'], 3)
|
||||||
|
|
||||||
|
def test_sign_collision_shape_does_not_block_sight_but_solid_block_does(self):
|
||||||
|
start = (3.5, 65.62, 0.5)
|
||||||
|
target = (0.5, 64.875, 0.5)
|
||||||
|
sign_state = blocks.MCD_BLOCKS['oak_sign']['minStateId']
|
||||||
|
obstruction = (1, 65, 0)
|
||||||
|
self.world.g.chunks.states[obstruction] = sign_state
|
||||||
|
|
||||||
|
self.assertTrue(self.world._has_cache_line_of_sight(
|
||||||
|
start, target, self.chest))
|
||||||
|
|
||||||
|
self.world.g.chunks.states[obstruction] = blocks.STONE
|
||||||
|
self.assertFalse(self.world._has_cache_line_of_sight(
|
||||||
|
start, target, self.chest))
|
||||||
|
|
||||||
|
def test_virtual_chest_openings_are_found_before_placement(self):
|
||||||
|
target = (0, 65, 0)
|
||||||
|
for x in range(-3, 4):
|
||||||
|
for z in range(-3, 4):
|
||||||
|
self.world.g.chunks.states[(x, 64, z)] = blocks.STONE
|
||||||
|
|
||||||
|
openings = self.world.find_cache_openings(
|
||||||
|
target, center=(3, 65, 0))
|
||||||
|
|
||||||
|
self.assertTrue(openings)
|
||||||
|
self.assertTrue(all(candidate['target'] == target
|
||||||
|
for candidate in openings))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@@ -93,8 +93,8 @@ class ChestDetectionTests(unittest.TestCase):
|
|||||||
chunks=Munch(get_block_at=lambda *_position: None),
|
chunks=Munch(get_block_at=lambda *_position: None),
|
||||||
world=Munch(_double_chest_positions=lambda _position:
|
world=Munch(_double_chest_positions=lambda _position:
|
||||||
(self.position,)))
|
(self.position,)))
|
||||||
game.place_block = lambda location, face: sent.append(
|
game.place_block = lambda location, face, **kwargs: sent.append(
|
||||||
(location, face))
|
(location, face, kwargs))
|
||||||
|
|
||||||
game.open_container(self.position)
|
game.open_container(self.position)
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ import unittest
|
|||||||
from munch import Munch
|
from munch import Munch
|
||||||
|
|
||||||
from minecraft.networking.modern import ClientTickEnd, UseItemOnBlockPacket
|
from minecraft.networking.modern import ClientTickEnd, UseItemOnBlockPacket
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
from mosfet.game import Game
|
from mosfet.game import Game
|
||||||
|
from mosfet.info import blocks
|
||||||
from mosfet.protocol.modern import PlayerInputPacket
|
from mosfet.protocol.modern import PlayerInputPacket
|
||||||
|
|
||||||
|
|
||||||
@@ -15,6 +17,28 @@ class FakeConnection:
|
|||||||
|
|
||||||
|
|
||||||
class SneakPlacementTests(unittest.TestCase):
|
class SneakPlacementTests(unittest.TestCase):
|
||||||
|
def test_open_container_sends_visible_face_and_hit_cursor(self):
|
||||||
|
chest = (2, 65, -4)
|
||||||
|
connection = FakeConnection()
|
||||||
|
game = Game.__new__(Game)
|
||||||
|
game.g = Munch(
|
||||||
|
connection=connection,
|
||||||
|
chunks=Munch(get_block_at=lambda *position:
|
||||||
|
min(blocks.CHEST_IDS) if tuple(position) == chest
|
||||||
|
else None),
|
||||||
|
world=Munch())
|
||||||
|
game.sequence = 0
|
||||||
|
|
||||||
|
game.open_container(
|
||||||
|
chest, face=BlockFace.EAST, cursor=(0.9375, 0.5, 0.5))
|
||||||
|
|
||||||
|
packet = next(packet for packet, _force in connection.packets
|
||||||
|
if isinstance(packet, UseItemOnBlockPacket))
|
||||||
|
self.assertEqual(packet.location, chest)
|
||||||
|
self.assertEqual(packet.face, BlockFace.EAST)
|
||||||
|
self.assertEqual((packet.cursor_x, packet.cursor_y, packet.cursor_z),
|
||||||
|
(0.9375, 0.5, 0.5))
|
||||||
|
|
||||||
def test_sneaking_wraps_block_placement_and_is_released(self):
|
def test_sneaking_wraps_block_placement_and_is_released(self):
|
||||||
connection = FakeConnection()
|
connection = FakeConnection()
|
||||||
game = Game.__new__(Game)
|
game = Game.__new__(Game)
|
||||||
|
|||||||
Reference in New Issue
Block a user