def findPortalNear(world: World, blockPos: BlockPos, maxHeight: int) -> Optional[BlockPos]: for totalDist in range(32): for xDist in range(totalDist): zDist = totalDist - xDist for y in range(maxHeight): if world.getBlock( BlockPos(blockPos.x + xDist, y, blockPos.z + zDist)) == 'nether_portal': return BlockPos(blockPos.x + xDist, y, blockPos.z + zDist) if world.getBlock( BlockPos(blockPos.x + xDist, y, blockPos.z - zDist)) == 'nether_portal': return BlockPos(blockPos.x + xDist, y, blockPos.z + zDist) if world.getBlock( BlockPos(blockPos.x - xDist, y, blockPos.z + zDist)) == 'nether_portal': return BlockPos(blockPos.x + xDist, y, blockPos.z + zDist) if world.getBlock( BlockPos(blockPos.x - xDist, y, blockPos.z - zDist)) == 'nether_portal': return BlockPos(blockPos.x + xDist, y, blockPos.z + zDist) return None
def isValidSpawnLocation(app, dim: Dimension, pos: BlockPos): server: ServerState = app.server floor = BlockPos(pos.x, pos.y - 1, pos.z) feet = pos head = BlockPos(pos.x, pos.y + 1, pos.z) light = dim.world.getTotalLight(server.time, pos) isOk = (dim.world.coordsOccupied(floor) and not dim.world.coordsOccupied(feet) and not dim.world.coordsOccupied(head) and light < 8) return isOk
def _decodeMultiBlockEntry(long): stateId = long >> 12 x = (long >> 8) & 0xF z = (long >> 4) & 0xF y = long & 0xF return (stateId, BlockPos(x, y, 15-z))
def tick(self, app, entity: Entity, world, entities): wanderFreq = 0.01 wanderDist = 3 pos = entity.getBlockPos() if entity.path == [] and random.random() < wanderFreq: x = pos.x + random.randint(-wanderDist, wanderDist) y = pos.y + random.randint(-2, 2) z = pos.z + random.randint(-wanderDist, wanderDist) if (not world.coordsOccupied(BlockPos(x, y, z)) and world.coordsOccupied(BlockPos(x, y - 1, z))): path = findPath(pos, BlockPos(x, y, z), world) if path is not None: entity.path = path return False
def getDestination(app, world: World, searchPos: BlockPos, maxHeight: int) -> BlockPos: existing = findPortalNear(world, searchPos, maxHeight) if existing is not None: print(f'Found existing portal at {existing}') return existing spot = findSpaceForPortal(world, searchPos, maxHeight) if spot is not None: print(f'Found space for portal at {spot}') createPortalAt(app, world, spot, clearNearby=False) return BlockPos(spot.x, spot.y + 1, spot.z) forcedPos = BlockPos(searchPos.x, maxHeight - 20, searchPos.z) createPortalAt(app, world, forcedPos, clearNearby=True) return BlockPos(forcedPos.x, forcedPos.y + 1, forcedPos.z)
def destinations(start: BlockPos, world): for dx in (-1, 0, 1): for dz in (-1, 0, 1): if dx == 0 and dz == 0: continue dy = isValidDir(start, dx, dz, world) if dy is not None: pos = BlockPos(start.x + dx, start.y + dy, start.z + dz) yield (pos)
def fromBuf(cls, buf): x, y, z = buf.unpack_position() location = BlockPos(x, y, -(z+1)) block = buf.unpack_varint() status = DiggingAction((buf.unpack_varint(),)) successful = buf.unpack('?') return cls(location, block, status, successful)
def isValidDir(start: BlockPos, dx: int, dz: int, world) -> Optional[int]: # These are in order of most to least common, for efficiency for dy in (0, 1, -1, -2, -3): dest = BlockPos(start.x + dx, start.y + dy, start.z + dz) if not canStandAt(dest, world): continue if dy == 0: if dx != 0 and dz != 0: # This is a simple, flat, diagonal path corner1 = BlockPos(start.x + dx, start.y, start.z) corner2 = BlockPos(start.x, start.y, start.z + dz) if (not canStandAt(corner1, world, needsFloor=False) or not canStandAt(corner2, world, needsFloor=False)): continue else: # This is a straight path return dy elif dx != 0 and dz != 0: # TODO: continue elif dy < 0: corner = BlockPos(start.x + dx, start.y, start.z + dz) if not canStandAt(corner, world, needsFloor=False): continue return dy elif dy > 0: corner = BlockPos(start.x, start.y + 1, start.z) if not canStandAt(corner, world, needsFloor=False): continue return dy return None
def canStandAt(pos: BlockPos, world, needsFloor=True): # FIXME: Height if world.coordsOccupied(pos): return False elif not world.coordsOccupied(BlockPos(pos.x, pos.y - 1, pos.z)): if needsFloor: return False else: return True else: return True
def isValidPos(pos: BlockPos): for dy in range(0, 4): for dx in range(-1, 4): blockId = world.getBlock( BlockPos(pos.x + dx, pos.y + dy, pos.z)) if dy == 0: if not isSolid(blockId): return False else: if blockId != 'air': return False return True
def doMobSpawning(app, server: ServerState, dim: Dimension): mobCap = len(dim.world.chunks) / 4 random.seed(time.time()) # HACK: player = server.players[0] for (chunkPos, chunk) in dim.world.chunks.items(): chunk: world.Chunk if chunk.isTicking: if len(dim.entities) > mobCap: return # FIXME: Random tick speed? x = random.randrange(0, 16) + chunkPos.x * 16 y = random.randrange( 0, world.CHUNK_HEIGHT) + chunkPos.y * world.CHUNK_HEIGHT z = random.randrange(0, 16) + chunkPos.z * 16 dist = math.sqrt((x - player.pos[0])**2 + (y - player.pos[1])**2 + (z - player.pos[2])**2) minSpawnDist = 24.0 if dist < minSpawnDist: continue if not isValidSpawnLocation(app, dim, BlockPos(x, y, z)): continue mob = random.choice(['creeper', 'zombie', 'skeleton']) packSize = 4 for _ in range(packSize): x += random.randint(-2, 2) z += random.randint(-2, 2) if isValidSpawnLocation(app, dim, BlockPos(x, y, z)): dim.entities.append( Entity(app, server.getEntityId(), mob, x, y, z))
def findSpaceForPortal(world: World, blockPos: BlockPos, maxHeight: int) -> Optional[BlockPos]: def isValidPos(pos: BlockPos): for dy in range(0, 4): for dx in range(-1, 4): blockId = world.getBlock( BlockPos(pos.x + dx, pos.y + dy, pos.z)) if dy == 0: if not isSolid(blockId): return False else: if blockId != 'air': return False return True for totalDist in range(16): for xDist in range(totalDist): zDist = totalDist - xDist for y in range(10, maxHeight - 4): if isValidPos( BlockPos(blockPos.x + xDist, y, blockPos.z + zDist)): return BlockPos(blockPos.x + xDist, y, blockPos.z + zDist) if isValidPos( BlockPos(blockPos.x + xDist, y, blockPos.z - zDist)): return BlockPos(blockPos.x + xDist, y, blockPos.z - zDist) if isValidPos( BlockPos(blockPos.x - xDist, y, blockPos.z + zDist)): return BlockPos(blockPos.x - xDist, y, blockPos.z + zDist) if isValidPos( BlockPos(blockPos.x - xDist, y, blockPos.z - zDist)): return BlockPos(blockPos.x - xDist, y, blockPos.z - zDist) return None
def __init__(self): self.nextTeleportId = 1 self.nextEntityId = 2 self.nextWindowId = 1 self.breakingBlock = 0.0 self.breakingBlockPos = BlockPos(0, 0, 0) self.openWindows = {} self.heldItems = {} self.craftSlots = {} self.tickTimes = [0.0] * 10 self.tickTimeIdx = 0 self.time = 0 self.gravity = 0.10 self.preloadProgress = 0 self.players = []
def collideY(wld: World, entity: Entity, ticks=1.0, useGravity=True): entity.pos[1] += entity.velocity[1] * ticks if entity.onGround: if not wld.hasBlockBeneath(entity): entity.onGround = False else: #if not hasattr(entity, 'flying') or not entity.flying: #type:ignore if useGravity: entity.velocity[1] -= GRAVITY * ticks [_, yPos, _] = entity.pos #yPos -= entity.height yPos -= 0.1 feetPos = roundHalfUp(yPos) if wld.hasBlockBeneath(entity): entity.onGround = True if hasattr(entity, 'flying'): entity.flying = False #type:ignore entity.velocity[1] = 0.0 #app.cameraPos[1] = (feetPos + 0.5) + entity.height entity.pos[1] = feetPos + 0.5 if not entity.onGround: for x in [ entity.pos[0] - entity.radius * 0.99, entity.pos[0] + entity.radius * 0.99 ]: for z in [ entity.pos[2] - entity.radius * 0.99, entity.pos[2] + entity.radius * 0.99 ]: hiYCoord = roundHalfUp(entity.pos[1] + entity.height) if wld.coordsOccupied(BlockPos(round(x), hiYCoord, round(z)), world.isSolid): yEdge = hiYCoord - 0.55 entity.pos[1] = yEdge - entity.height if entity.velocity[1] > 0.0: entity.velocity[1] = 0.0
def createPortalAt(app, world: World, blockPos: BlockPos, clearNearby: bool): instData = (app.textures, app.cube, app.textureIndices) for dx in (-1, 0, 1, 2): world.setBlock(instData, BlockPos(blockPos.x + dx, blockPos.y, blockPos.z), 'obsidian', {}) world.setBlock(instData, BlockPos(blockPos.x + dx, blockPos.y + 4, blockPos.z), 'obsidian', {}) for dy in (1, 2, 3): world.setBlock(instData, BlockPos(blockPos.x - 1, blockPos.y + dy, blockPos.z), 'obsidian', {}) world.setBlock(instData, BlockPos(blockPos.x + 2, blockPos.y + dy, blockPos.z), 'obsidian', {}) for dx in (0, 1): for dy in (1, 2, 3): world.setBlock(instData, BlockPos(blockPos.x + dx, blockPos.y + dy, blockPos.z), 'nether_portal', {'axis': 'x'}, doBlockUpdates=False) if clearNearby: for dz in (-1, 1): for dx in (-1, 0, 1, 2): for dy in (0, 1, 2, 3): if dx in (0, 1) and dy == 0: blockId = 'obsidian' blockState = {} else: blockId = 'air' blockState = {} world.setBlock( instData, BlockPos(blockPos.x + dx, blockPos.y + dy, blockPos.z + dz), blockId, blockState)
def getBlockPos(self) -> BlockPos: bx = roundHalfUp(self.pos[0]) by = roundHalfUp(self.pos[1]) bz = roundHalfUp(self.pos[2]) return BlockPos(bx, by, bz)
def findPortalFrame(server: ServerState, dim: Dimension, pos: BlockPos) -> Optional[Tuple[List[BlockPos], str]]: bottomPos1 = None topPos1 = None for i in range(1, 4): p = BlockPos(pos.x, pos.y - i, pos.z) if dim.world.getBlock(p) == 'obsidian': bottomPos1 = p break if bottomPos1 is None: return None for i in range(1, 4): p = BlockPos(pos.x, pos.y + i, pos.z) if dim.world.getBlock(p) == 'obsidian': topPos1 = p break if topPos1 is None: return None if topPos1.y - bottomPos1.y != 4: return None for dx, dz in ((-1, 0), (1, 0), (0, -1), (0, 1)): bottomPos2 = BlockPos(bottomPos1.x + dx, bottomPos1.y, bottomPos1.z + dz) topPos2 = BlockPos(topPos1.x + dx, topPos1.y, topPos1.z + dz) # Need 2 blocks on top, 2 blocks on bottom if dim.world.getBlock(bottomPos2) != 'obsidian': continue if dim.world.getBlock(topPos2) != 'obsidian': continue answer = [] ok = True for i in range(3): backSidePos = BlockPos(bottomPos1.x - dx, bottomPos1.y + 1 + i, bottomPos1.z - dz) backMidPos = BlockPos(bottomPos1.x, bottomPos1.y + 1 + i, bottomPos1.z) frontMidPos = BlockPos(bottomPos2.x, bottomPos2.y + 1 + i, bottomPos2.z) frontSidePos = BlockPos(bottomPos2.x + dx, bottomPos2.y + 1 + i, bottomPos2.z + dz) if (dim.world.getBlock(backSidePos) != 'obsidian' or dim.world.getBlock(frontSidePos) != 'obsidian' or dim.world.getBlock(backMidPos) != 'air' or dim.world.getBlock(frontMidPos) != 'air'): ok = False break answer.append(backMidPos) answer.append(frontMidPos) if ok: return (answer, 'x' if dx != 0 else 'z') return None
def fromBuf(cls, buf): x, y, z = buf.unpack_position() location = BlockPos(x, y, -(z+1)) blockId = buf.unpack_varint() return cls(location, blockId)
def collideXZ(wld: World, entity: Entity, ticks=1.0): hitWall = False minY = roundHalfUp((entity.pos[1])) maxY = roundHalfUp((entity.pos[1] + entity.height)) lastX = entity.pos[0] lastZ = entity.pos[2] entity.pos[0] += entity.velocity[0] * ticks for y in range(minY, maxY + 1): for z in [ entity.pos[2] - entity.radius * 0.99, entity.pos[2] + entity.radius * 0.99 ]: x = entity.pos[0] hiXBlockCoord = round((x + entity.radius)) loXBlockCoord = round((x - entity.radius)) if wld.coordsOccupied(BlockPos(hiXBlockCoord, y, round(z)), world.isSolid): # Collision on the right, so move to the left xEdge = (hiXBlockCoord - 0.5) entity.pos[0] = xEdge - entity.radius hitWall = True elif wld.coordsOccupied(BlockPos(loXBlockCoord, y, round(z)), world.isSolid): # Collision on the left, so move to the right xEdge = (loXBlockCoord + 0.5) entity.pos[0] = xEdge + entity.radius hitWall = True entity.pos[2] += entity.velocity[2] * ticks for y in range(minY, maxY + 1): for x in [ entity.pos[0] - entity.radius * 0.99, entity.pos[0] + entity.radius * 0.99 ]: z = entity.pos[2] hiZBlockCoord = round((z + entity.radius)) loZBlockCoord = round((z - entity.radius)) if wld.coordsOccupied(BlockPos(round(x), y, hiZBlockCoord), world.isSolid): zEdge = (hiZBlockCoord - 0.5) entity.pos[2] = zEdge - entity.radius hitWall = True elif wld.coordsOccupied(BlockPos(round(x), y, loZBlockCoord), world.isSolid): zEdge = (loZBlockCoord + 0.5) entity.pos[2] = zEdge + entity.radius hitWall = True entity.distanceMoved = math.sqrt((entity.pos[0] - lastX)**2 + (entity.pos[2] - lastZ)**2) return hitWall