Premier commit - Introdution au système git.
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
|
||||
# Custom for Visual Studio
|
||||
*.cs diff=csharp
|
||||
*.sln merge=union
|
||||
*.csproj merge=union
|
||||
*.vbproj merge=union
|
||||
*.fsproj merge=union
|
||||
*.dbproj merge=union
|
||||
|
||||
# Standard to msysgit
|
||||
*.doc diff=astextplain
|
||||
*.DOC diff=astextplain
|
||||
*.docx diff=astextplain
|
||||
*.DOCX diff=astextplain
|
||||
*.dot diff=astextplain
|
||||
*.DOT diff=astextplain
|
||||
*.pdf diff=astextplain
|
||||
*.PDF diff=astextplain
|
||||
*.rtf diff=astextplain
|
||||
*.RTF diff=astextplain
|
||||
@@ -0,0 +1,33 @@
|
||||
*.py[cdo]
|
||||
pythonhosted/
|
||||
|
||||
# Editor detritus
|
||||
*.vim
|
||||
*.swp
|
||||
tags
|
||||
.vscode
|
||||
|
||||
# Packaging detritus
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist
|
||||
build
|
||||
eggs
|
||||
parts
|
||||
bin
|
||||
var
|
||||
sdist
|
||||
develop-eggs
|
||||
.installed.cfg
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
coverage
|
||||
.coverage
|
||||
.tox
|
||||
.cache
|
||||
|
||||
# Generated documentation
|
||||
docs/_build
|
||||
@@ -0,0 +1,10 @@
|
||||
# Change Log
|
||||
|
||||
## 2018-05-01 v1.1.0
|
||||
|
||||
+ packaged and released onto [PyPI](https://pypi.org)
|
||||
+ it seemed ridiculous calling this v1.0.0 given the maturity of this library, so it has become v1.1.0
|
||||
|
||||
## in the past v1.0.0
|
||||
|
||||
+ the library was created it was used but never packaged.
|
||||
@@ -0,0 +1,5 @@
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,46 @@
|
||||
# Minecraft: Pi edition API Python Library
|
||||
|
||||
`mcpi` Python library for communicating with [Minecraft: Pi edition](https://minecraft.net/en-us/edition/pi/) and [RaspberryJuice](https://github.com/zhuowei/RaspberryJuice).
|
||||
|
||||
## Installation
|
||||
|
||||
### Windows
|
||||
|
||||
```
|
||||
pip3 install mcpi
|
||||
```
|
||||
|
||||
### Linux / MacOS
|
||||
|
||||
```bash
|
||||
sudo pip3 install mcpi
|
||||
```
|
||||
|
||||
## History
|
||||
|
||||
The [Minecraft: Pi edition](https://minecraft.net/en-us/edition/pi/) Python library was originally created by Mojang and released with Minecraft: Pi edition.
|
||||
|
||||
Initial supported was provided for Python 2 only, but during a sprint at PyconUK 2014 it was migrated to Python 3 and [py3minepi](https://github.com/py3minepi/py3minepi) was created.
|
||||
|
||||
The ability to hack Minecraft from Python was very popular and the [RaspberryJuice](https://github.com/zhuowei/RaspberryJuice) plugin was created for Minecraft Java edition. RaspberryJuice also extended the API adding additional features.
|
||||
|
||||
This python library supports Python 2 & 3 and Minecraft: Pi edition and RaspberryJuice.
|
||||
|
||||
Documentation for the Minecraft: Pi edition and RaspberryJuice API's can be found at [www.stuffaboutcode.com/p/minecraft-api-reference.html](http://www.stuffaboutcode.com/p/minecraft-api-reference.html).
|
||||
|
||||
It was released onto [PyPI](https://pypi.org) in May 2018.
|
||||
|
||||
If you want some cool additional tools for modifying Minecraft, check out [minecraft-stuff](https://minecraft-stuff.readthedocs.io/en/latest/).
|
||||
|
||||
## Sources
|
||||
|
||||
This library is a collection of the following sources:
|
||||
|
||||
+ [Minecraft: Pi edition](https://minecraft.net/en-us/edition/pi/)
|
||||
+ [Python 3 Minecraft: Pi edition library](https://github.com/py3minepi/py3minepi)
|
||||
|
||||
## Licenses
|
||||
|
||||
+ mcpi - [LICENSE.txt](https://github.com/martinohanlon/mcpi/blob/master/LICENSE)
|
||||
+ Minecraft: Pi edition LICENSE - [minecraft-pi-edition-LICENSE.txt](https://github.com/martinohanlon/mcpi/blob/master/minecraft-pi-edition-LICENSE.txt)
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
class Block:
|
||||
"""Minecraft PI block description. Can be sent to Minecraft.setBlock/s"""
|
||||
def __init__(self, id, data=0):
|
||||
self.id = id
|
||||
self.data = data
|
||||
|
||||
def __cmp__(self, rhs):
|
||||
return hash(self) - hash(rhs)
|
||||
|
||||
def __eq__(self, rhs):
|
||||
return self.id == rhs.id and self.data == rhs.data
|
||||
|
||||
def __hash__(self):
|
||||
return (self.id << 8) + self.data
|
||||
|
||||
def withData(self, data):
|
||||
return Block(self.id, data)
|
||||
|
||||
def __iter__(self):
|
||||
"""Allows a Block to be sent whenever id [and data] is needed"""
|
||||
return iter((self.id, self.data))
|
||||
|
||||
def __repr__(self):
|
||||
return "Block(%d, %d)"%(self.id, self.data)
|
||||
|
||||
AIR = Block(0)
|
||||
STONE = Block(1)
|
||||
GRASS = Block(2)
|
||||
DIRT = Block(3)
|
||||
COBBLESTONE = Block(4)
|
||||
WOOD_PLANKS = Block(5)
|
||||
SAPLING = Block(6)
|
||||
BEDROCK = Block(7)
|
||||
WATER_FLOWING = Block(8)
|
||||
WATER = WATER_FLOWING
|
||||
WATER_STATIONARY = Block(9)
|
||||
LAVA_FLOWING = Block(10)
|
||||
LAVA = LAVA_FLOWING
|
||||
LAVA_STATIONARY = Block(11)
|
||||
SAND = Block(12)
|
||||
GRAVEL = Block(13)
|
||||
GOLD_ORE = Block(14)
|
||||
IRON_ORE = Block(15)
|
||||
COAL_ORE = Block(16)
|
||||
WOOD = Block(17)
|
||||
LEAVES = Block(18)
|
||||
GLASS = Block(20)
|
||||
LAPIS_LAZULI_ORE = Block(21)
|
||||
LAPIS_LAZULI_BLOCK = Block(22)
|
||||
SANDSTONE = Block(24)
|
||||
BED = Block(26)
|
||||
COBWEB = Block(30)
|
||||
GRASS_TALL = Block(31)
|
||||
WOOL = Block(35)
|
||||
FLOWER_YELLOW = Block(37)
|
||||
FLOWER_CYAN = Block(38)
|
||||
MUSHROOM_BROWN = Block(39)
|
||||
MUSHROOM_RED = Block(40)
|
||||
GOLD_BLOCK = Block(41)
|
||||
IRON_BLOCK = Block(42)
|
||||
STONE_SLAB_DOUBLE = Block(43)
|
||||
STONE_SLAB = Block(44)
|
||||
BRICK_BLOCK = Block(45)
|
||||
TNT = Block(46)
|
||||
BOOKSHELF = Block(47)
|
||||
MOSS_STONE = Block(48)
|
||||
OBSIDIAN = Block(49)
|
||||
TORCH = Block(50)
|
||||
FIRE = Block(51)
|
||||
STAIRS_WOOD = Block(53)
|
||||
CHEST = Block(54)
|
||||
DIAMOND_ORE = Block(56)
|
||||
DIAMOND_BLOCK = Block(57)
|
||||
CRAFTING_TABLE = Block(58)
|
||||
FARMLAND = Block(60)
|
||||
FURNACE_INACTIVE = Block(61)
|
||||
FURNACE_ACTIVE = Block(62)
|
||||
DOOR_WOOD = Block(64)
|
||||
LADDER = Block(65)
|
||||
STAIRS_COBBLESTONE = Block(67)
|
||||
DOOR_IRON = Block(71)
|
||||
REDSTONE_ORE = Block(73)
|
||||
SNOW = Block(78)
|
||||
ICE = Block(79)
|
||||
SNOW_BLOCK = Block(80)
|
||||
CACTUS = Block(81)
|
||||
CLAY = Block(82)
|
||||
SUGAR_CANE = Block(83)
|
||||
FENCE = Block(85)
|
||||
GLOWSTONE_BLOCK = Block(89)
|
||||
BEDROCK_INVISIBLE = Block(95)
|
||||
STONE_BRICK = Block(98)
|
||||
GLASS_PANE = Block(102)
|
||||
MELON = Block(103)
|
||||
FENCE_GATE = Block(107)
|
||||
GLOWING_OBSIDIAN = Block(246)
|
||||
NETHER_REACTOR_CORE = Block(247)
|
||||
@@ -0,0 +1,63 @@
|
||||
import socket
|
||||
import select
|
||||
import sys
|
||||
from .util import flatten_parameters_to_bytestring
|
||||
|
||||
""" @author: Aron Nieminen, Mojang AB"""
|
||||
|
||||
class RequestError(Exception):
|
||||
pass
|
||||
|
||||
class Connection:
|
||||
"""Connection to a Minecraft Pi game"""
|
||||
RequestFailed = "Fail"
|
||||
|
||||
def __init__(self, address, port):
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.socket.connect((address, port))
|
||||
self.lastSent = ""
|
||||
|
||||
def drain(self):
|
||||
"""Drains the socket of incoming data"""
|
||||
while True:
|
||||
readable, _, _ = select.select([self.socket], [], [], 0.0)
|
||||
if not readable:
|
||||
break
|
||||
data = self.socket.recv(1500)
|
||||
e = "Drained Data: <%s>\n"%data.strip()
|
||||
e += "Last Message: <%s>\n"%self.lastSent.strip()
|
||||
sys.stderr.write(e)
|
||||
|
||||
def send(self, f, *data):
|
||||
"""
|
||||
Sends data. Note that a trailing newline '\n' is added here
|
||||
|
||||
The protocol uses CP437 encoding - https://en.wikipedia.org/wiki/Code_page_437
|
||||
which is mildly distressing as it can't encode all of Unicode.
|
||||
"""
|
||||
|
||||
s = b"".join([f, b"(", flatten_parameters_to_bytestring(data), b")", b"\n"])
|
||||
|
||||
self._send(s)
|
||||
|
||||
def _send(self, s):
|
||||
"""
|
||||
The actual socket interaction from self.send, extracted for easier mocking
|
||||
and testing
|
||||
"""
|
||||
self.drain()
|
||||
self.lastSent = s
|
||||
|
||||
self.socket.sendall(s)
|
||||
|
||||
def receive(self):
|
||||
"""Receives data. Note that the trailing newline '\n' is trimmed"""
|
||||
s = self.socket.makefile("r").readline().rstrip("\n")
|
||||
if s == Connection.RequestFailed:
|
||||
raise RequestError("%s failed"%self.lastSent.strip())
|
||||
return s
|
||||
|
||||
def sendReceive(self, *data):
|
||||
"""Sends and receive data"""
|
||||
self.send(*data)
|
||||
return self.receive()
|
||||
@@ -0,0 +1,45 @@
|
||||
from .vec3 import Vec3
|
||||
|
||||
class BlockEvent:
|
||||
"""An Event related to blocks (e.g. placed, removed, hit)"""
|
||||
HIT = 0
|
||||
|
||||
def __init__(self, type, x, y, z, face, entityId):
|
||||
self.type = type
|
||||
self.pos = Vec3(x, y, z)
|
||||
self.face = face
|
||||
self.entityId = entityId
|
||||
|
||||
def __repr__(self):
|
||||
sType = {
|
||||
BlockEvent.HIT: "BlockEvent.HIT"
|
||||
}.get(self.type, "???")
|
||||
|
||||
return "BlockEvent(%s, %d, %d, %d, %d, %d)"%(
|
||||
sType,self.pos.x,self.pos.y,self.pos.z,self.face,self.entityId);
|
||||
|
||||
@staticmethod
|
||||
def Hit(x, y, z, face, entityId):
|
||||
return BlockEvent(BlockEvent.HIT, x, y, z, face, entityId)
|
||||
|
||||
class ChatEvent:
|
||||
"""An Event related to chat (e.g. posts)"""
|
||||
POST = 0
|
||||
|
||||
def __init__(self, type, entityId, message):
|
||||
self.type = type
|
||||
self.entityId = entityId
|
||||
self.message = message
|
||||
|
||||
def __repr__(self):
|
||||
sType = {
|
||||
ChatEvent.POST: "ChatEvent.POST"
|
||||
}.get(self.type, "???")
|
||||
|
||||
return "ChatEvent(%s, %d, %s)"%(
|
||||
sType,self.entityId,self.message);
|
||||
|
||||
@staticmethod
|
||||
def Post(entityId, message):
|
||||
return ChatEvent(ChatEvent.POST, entityId, message)
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
*** The real license isn't finished yet, here's what goes in plain english ***
|
||||
|
||||
You may execute the minecraft-pi binary on a Raspberry Pi or an emulator
|
||||
You may use any of the source code included in the distribution for any purpose (except evil)
|
||||
|
||||
You may not redistribute any modified binary parts of the distribution
|
||||
@@ -0,0 +1,210 @@
|
||||
from .connection import Connection
|
||||
from .vec3 import Vec3
|
||||
from .event import BlockEvent, ChatEvent
|
||||
from .block import Block
|
||||
import math
|
||||
from .util import flatten
|
||||
|
||||
""" Minecraft PI low level api v0.1_1
|
||||
|
||||
Note: many methods have the parameter *arg. This solution makes it
|
||||
simple to allow different types, and variable number of arguments.
|
||||
The actual magic is a mix of flatten_parameters() and __iter__. Example:
|
||||
A Cube class could implement __iter__ to work in Minecraft.setBlocks(c, id).
|
||||
|
||||
(Because of this, it's possible to "erase" arguments. CmdPlayer removes
|
||||
entityId, by injecting [] that flattens to nothing)
|
||||
|
||||
@author: Aron Nieminen, Mojang AB"""
|
||||
|
||||
""" Updated to include functionality provided by RaspberryJuice:
|
||||
- getBlocks()
|
||||
- getDirection()
|
||||
- getPitch()
|
||||
- getRotation()
|
||||
- getPlayerEntityId()
|
||||
- pollChatPosts() """
|
||||
|
||||
def intFloor(*args):
|
||||
return [int(math.floor(x)) for x in flatten(args)]
|
||||
|
||||
class CmdPositioner:
|
||||
"""Methods for setting and getting positions"""
|
||||
def __init__(self, connection, packagePrefix):
|
||||
self.conn = connection
|
||||
self.pkg = packagePrefix
|
||||
|
||||
def getPos(self, id):
|
||||
"""Get entity position (entityId:int) => Vec3"""
|
||||
s = self.conn.sendReceive(self.pkg + b".getPos", id)
|
||||
return Vec3(*list(map(float, s.split(","))))
|
||||
|
||||
def setPos(self, id, *args):
|
||||
"""Set entity position (entityId:int, x,y,z)"""
|
||||
self.conn.send(self.pkg + b".setPos", id, args)
|
||||
|
||||
def getTilePos(self, id):
|
||||
"""Get entity tile position (entityId:int) => Vec3"""
|
||||
s = self.conn.sendReceive(self.pkg + b".getTile", id)
|
||||
return Vec3(*list(map(int, s.split(","))))
|
||||
|
||||
def setTilePos(self, id, *args):
|
||||
"""Set entity tile position (entityId:int) => Vec3"""
|
||||
self.conn.send(self.pkg + b".setTile", id, intFloor(*args))
|
||||
|
||||
def getDirection(self, id):
|
||||
"""Get entity direction (entityId:int) => Vec3"""
|
||||
s = self.conn.sendReceive(self.pkg + b".getDirection", id)
|
||||
return Vec3(*map(float, s.split(",")))
|
||||
|
||||
def getRotation(self, id):
|
||||
"""get entity rotation (entityId:int) => float"""
|
||||
return float(self.conn.sendReceive(self.pkg + b".getRotation", id))
|
||||
|
||||
def getPitch(self, id):
|
||||
"""get entity pitch (entityId:int) => float"""
|
||||
return float(self.conn.sendReceive(self.pkg + b".getPitch", id))
|
||||
|
||||
def setting(self, setting, status):
|
||||
"""Set a player setting (setting, status). keys: autojump"""
|
||||
self.conn.send(self.pkg + b".setting", setting, 1 if bool(status) else 0)
|
||||
|
||||
|
||||
class CmdEntity(CmdPositioner):
|
||||
"""Methods for entities"""
|
||||
def __init__(self, connection):
|
||||
CmdPositioner.__init__(self, connection, b"entity")
|
||||
|
||||
|
||||
class CmdPlayer(CmdPositioner):
|
||||
"""Methods for the host (Raspberry Pi) player"""
|
||||
def __init__(self, connection):
|
||||
CmdPositioner.__init__(self, connection, b"player")
|
||||
self.conn = connection
|
||||
|
||||
def getPos(self):
|
||||
return CmdPositioner.getPos(self, [])
|
||||
def setPos(self, *args):
|
||||
return CmdPositioner.setPos(self, [], args)
|
||||
def getTilePos(self):
|
||||
return CmdPositioner.getTilePos(self, [])
|
||||
def setTilePos(self, *args):
|
||||
return CmdPositioner.setTilePos(self, [], args)
|
||||
def getDirection(self):
|
||||
return CmdPositioner.getDirection(self, [])
|
||||
def getRotation(self):
|
||||
return CmdPositioner.getRotation(self, [])
|
||||
def getPitch(self):
|
||||
return CmdPositioner.getPitch(self, [])
|
||||
|
||||
class CmdCamera:
|
||||
def __init__(self, connection):
|
||||
self.conn = connection
|
||||
|
||||
def setNormal(self, *args):
|
||||
"""Set camera mode to normal Minecraft view ([entityId])"""
|
||||
self.conn.send(b"camera.mode.setNormal", args)
|
||||
|
||||
def setFixed(self):
|
||||
"""Set camera mode to fixed view"""
|
||||
self.conn.send(b"camera.mode.setFixed")
|
||||
|
||||
def setFollow(self, *args):
|
||||
"""Set camera mode to follow an entity ([entityId])"""
|
||||
self.conn.send(b"camera.mode.setFollow", args)
|
||||
|
||||
def setPos(self, *args):
|
||||
"""Set camera entity position (x,y,z)"""
|
||||
self.conn.send(b"camera.setPos", args)
|
||||
|
||||
|
||||
class CmdEvents:
|
||||
"""Events"""
|
||||
def __init__(self, connection):
|
||||
self.conn = connection
|
||||
|
||||
def clearAll(self):
|
||||
"""Clear all old events"""
|
||||
self.conn.send(b"events.clear")
|
||||
|
||||
def pollBlockHits(self):
|
||||
"""Only triggered by sword => [BlockEvent]"""
|
||||
s = self.conn.sendReceive(b"events.block.hits")
|
||||
events = [e for e in s.split("|") if e]
|
||||
return [BlockEvent.Hit(*list(map(int, e.split(",")))) for e in events]
|
||||
|
||||
def pollChatPosts(self):
|
||||
"""Triggered by posts to chat => [ChatEvent]"""
|
||||
s = self.conn.sendReceive(b"events.chat.posts")
|
||||
events = [e for e in s.split("|") if e]
|
||||
return [ChatEvent.Post(int(e[:e.find(",")]), e[e.find(",") + 1:]) for e in events]
|
||||
|
||||
class Minecraft:
|
||||
"""The main class to interact with a running instance of Minecraft Pi."""
|
||||
def __init__(self, connection):
|
||||
self.conn = connection
|
||||
|
||||
self.camera = CmdCamera(connection)
|
||||
self.entity = CmdEntity(connection)
|
||||
self.player = CmdPlayer(connection)
|
||||
self.events = CmdEvents(connection)
|
||||
|
||||
def getBlock(self, *args):
|
||||
"""Get block (x,y,z) => id:int"""
|
||||
return int(self.conn.sendReceive(b"world.getBlock", intFloor(args)))
|
||||
|
||||
def getBlockWithData(self, *args):
|
||||
"""Get block with data (x,y,z) => Block"""
|
||||
ans = self.conn.sendReceive(b"world.getBlockWithData", intFloor(args))
|
||||
return Block(*list(map(int, ans.split(","))))
|
||||
|
||||
def getBlocks(self, *args):
|
||||
"""Get a cuboid of blocks (x0,y0,z0,x1,y1,z1) => [id:int]"""
|
||||
s = self.conn.sendReceive(b"world.getBlocks", intFloor(args))
|
||||
return map(int, s.split(","))
|
||||
|
||||
def setBlock(self, *args):
|
||||
"""Set block (x,y,z,id,[data])"""
|
||||
self.conn.send(b"world.setBlock", intFloor(args))
|
||||
|
||||
def setBlocks(self, *args):
|
||||
"""Set a cuboid of blocks (x0,y0,z0,x1,y1,z1,id,[data])"""
|
||||
self.conn.send(b"world.setBlocks", intFloor(args))
|
||||
|
||||
def getHeight(self, *args):
|
||||
"""Get the height of the world (x,z) => int"""
|
||||
return int(self.conn.sendReceive(b"world.getHeight", intFloor(args)))
|
||||
|
||||
def getPlayerEntityIds(self):
|
||||
"""Get the entity ids of the connected players => [id:int]"""
|
||||
ids = self.conn.sendReceive(b"world.getPlayerIds")
|
||||
return list(map(int, ids.split("|")))
|
||||
|
||||
def getPlayerEntityId(self, name):
|
||||
"""Get the entity id of the named player => [id:int]"""
|
||||
return int(self.conn.sendReceive(b"world.getPlayerId", name))
|
||||
|
||||
def saveCheckpoint(self):
|
||||
"""Save a checkpoint that can be used for restoring the world"""
|
||||
self.conn.send(b"world.checkpoint.save")
|
||||
|
||||
def restoreCheckpoint(self):
|
||||
"""Restore the world state to the checkpoint"""
|
||||
self.conn.send(b"world.checkpoint.restore")
|
||||
|
||||
def postToChat(self, msg):
|
||||
"""Post a message to the game chat"""
|
||||
self.conn.send(b"chat.post", msg)
|
||||
|
||||
def setting(self, setting, status):
|
||||
"""Set a world setting (setting, status). keys: world_immutable, nametags_visible"""
|
||||
self.conn.send(b"world.setting", setting, 1 if bool(status) else 0)
|
||||
|
||||
@staticmethod
|
||||
def create(address = "localhost", port = 4711):
|
||||
return Minecraft(Connection(address, port))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
mc = Minecraft.create()
|
||||
mc.postToChat("Hello, Minecraft!")
|
||||
@@ -0,0 +1,36 @@
|
||||
from setuptools import setup
|
||||
|
||||
__project__ = 'mcpi'
|
||||
__desc__ = 'Python library for the Minecraft Pi edition and RaspberryJuice API'
|
||||
__version__ = '1.1.0'
|
||||
__author__ = "Martin O'Hanlon"
|
||||
__author_email__ = 'martin@ohanlonweb.com'
|
||||
__license__ = 'MIT'
|
||||
__url__ = 'https://github.com/martinohanlon/mcpi'
|
||||
|
||||
__classifiers__ = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Intended Audience :: Education",
|
||||
"Intended Audience :: Developers",
|
||||
"Topic :: Education",
|
||||
"Topic :: Games/Entertainment",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Programming Language :: Python :: 2",
|
||||
"Programming Language :: Python :: 2.7",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.3",
|
||||
"Programming Language :: Python :: 3.4",
|
||||
"Programming Language :: Python :: 3.5",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
]
|
||||
|
||||
setup(name=__project__,
|
||||
version = __version__,
|
||||
description = __desc__,
|
||||
url = __url__,
|
||||
author = __author__,
|
||||
author_email = __author_email__,
|
||||
license = __license__,
|
||||
packages = [__project__],
|
||||
classifiers = __classifiers__,
|
||||
zip_safe=False)
|
||||
@@ -0,0 +1,18 @@
|
||||
import collections
|
||||
|
||||
def flatten(l):
|
||||
for e in l:
|
||||
if isinstance(e, collections.Iterable) and not isinstance(e, str):
|
||||
for ee in flatten(e): yield ee
|
||||
else: yield e
|
||||
|
||||
def flatten_parameters_to_bytestring(l):
|
||||
return b",".join(map(_misc_to_bytes, flatten(l)))
|
||||
|
||||
def _misc_to_bytes(m):
|
||||
"""
|
||||
Convert an arbitrary object into a string encoded as a CP437 series of bytes.
|
||||
|
||||
See `Connection.send` for more details.
|
||||
"""
|
||||
return str(m).encode("cp437")
|
||||
@@ -0,0 +1,114 @@
|
||||
class Vec3:
|
||||
def __init__(self, x=0, y=0, z=0):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.z = z
|
||||
|
||||
def __add__(self, rhs):
|
||||
c = self.clone()
|
||||
c += rhs
|
||||
return c
|
||||
|
||||
def __iadd__(self, rhs):
|
||||
self.x += rhs.x
|
||||
self.y += rhs.y
|
||||
self.z += rhs.z
|
||||
return self
|
||||
|
||||
def length(self):
|
||||
return self.lengthSqr() ** .5
|
||||
|
||||
def lengthSqr(self):
|
||||
return self.x * self.x + self.y * self.y + self.z * self.z
|
||||
|
||||
def __mul__(self, k):
|
||||
c = self.clone()
|
||||
c *= k
|
||||
return c
|
||||
|
||||
def __imul__(self, k):
|
||||
self.x *= k
|
||||
self.y *= k
|
||||
self.z *= k
|
||||
return self
|
||||
|
||||
def clone(self):
|
||||
return Vec3(self.x, self.y, self.z)
|
||||
|
||||
def __neg__(self):
|
||||
return Vec3(-self.x, -self.y, -self.z)
|
||||
|
||||
def __sub__(self, rhs):
|
||||
return self.__add__(-rhs)
|
||||
|
||||
def __isub__(self, rhs):
|
||||
return self.__iadd__(-rhs)
|
||||
|
||||
def __repr__(self):
|
||||
return "Vec3(%s,%s,%s)"%(self.x,self.y,self.z)
|
||||
|
||||
def __iter__(self):
|
||||
return iter((self.x, self.y, self.z))
|
||||
|
||||
def _map(self, func):
|
||||
self.x = func(self.x)
|
||||
self.y = func(self.y)
|
||||
self.z = func(self.z)
|
||||
|
||||
def __cmp__(self, rhs):
|
||||
dx = self.x - rhs.x
|
||||
if dx != 0: return dx
|
||||
dy = self.y - rhs.y
|
||||
if dy != 0: return dy
|
||||
dz = self.z - rhs.z
|
||||
if dz != 0: return dz
|
||||
return 0
|
||||
|
||||
def __eq__(self, rhs):
|
||||
if self.x == rhs.x and self.y == rhs.y and self.z == rhs.z:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def iround(self): self._map(lambda v:int(v+0.5))
|
||||
def ifloor(self): self._map(int)
|
||||
|
||||
def rotateLeft(self): self.x, self.z = self.z, -self.x
|
||||
def rotateRight(self): self.x, self.z = -self.z, self.x
|
||||
|
||||
def testVec3():
|
||||
# Note: It's not testing everything
|
||||
|
||||
# 1.1 Test initialization
|
||||
it = Vec3(1, -2, 3)
|
||||
assert it.x == 1
|
||||
assert it.y == -2
|
||||
assert it.z == 3
|
||||
|
||||
assert it.x != -1
|
||||
assert it.y != +2
|
||||
assert it.z != -3
|
||||
|
||||
# 2.1 Test cloning and equality
|
||||
clone = it.clone()
|
||||
assert it == clone
|
||||
it.x += 1
|
||||
assert it != clone
|
||||
|
||||
# 3.1 Arithmetic
|
||||
a = Vec3(10, -3, 4)
|
||||
b = Vec3(-7, 1, 2)
|
||||
c = a + b
|
||||
assert c - a == b
|
||||
assert c - b == a
|
||||
assert a + a == a * 2
|
||||
|
||||
assert a - a == Vec3(0,0,0)
|
||||
assert a + (-a) == Vec3(0,0,0)
|
||||
|
||||
# Test repr
|
||||
e = eval(repr(it))
|
||||
assert e == it
|
||||
|
||||
if __name__ == "__main__":
|
||||
testVec3()
|
||||
Reference in New Issue
Block a user