18
MINECRAFT MOD PROGRAMMING Part III of Minecraft: Pi Edition October 31, 2015

MINECRAFT MOD PROGRAMMING Part III of Minecraft: Pi Edition October 31, 2015

Embed Size (px)

Citation preview

MINECRAFT MOD PROGRAMMINGPart IIIof

Minecraft: Pi Edition

October 31, 2015

Outlines• Programming Python on the Pi• Installing the Minecraft Pi Edition• A “Hello, Minecraft!” demo• 2D list (or array), how to use it?• Using Minecraft modules• Coding and testing a 2D board game• Putting the board game into Minecraft

Programming Python on the Pi• What are available on the Pi?

• Raspbian is a Debian-based free operating system optimized for the Raspberry Pi hardware (while Debian GNU/Linux is one of the most popular Linux distributions for personal computers and network servers.)

• Midori is a lightweight WebKit browser with support for HTML5 and JavaScript

• Scratch programming environment

• Python is installed on the Raspberry Pi • Python 2 & 3• Shell & IDLE

4

Setting up Minecraft on Raspberry Pi• Open a web browser on Raspberry Pi• Visit http://pi.minecraft.net• Go to the Download page• Download the latest version of the game• Save the file in the /home/pi directory• Open a fresh terminal and enter the

following command:• tar –zxvf minecraft-pi-<version>.tar.gz• Change directory with cd mcpi• ./minecraft-pi

• a new window should pop up with Minecraft: Pi Edition running inside

Client-Server Setting: how it works?

• Start Minecraft server (app)• Clients are connected to the

server (app) through socket (a fundamental networking mechanism)

Client app (in Python)

Minecraft Server app

Socket connection

command

Let’s start the game• To start Minecraft server (app),

type the following in a console:

• For a client to get connected to the server (app) and chat with other gamers, simply type the following in a fresh console:

cd /home/pi/mcpi./minecraft-pi

cd /home/pi/mcpi/api/python/mcpipython>>>import minecraft>>>mc = minecraft.Minecraft.create()>>>mc.postToChat("Hello, world!")

command

Minecraft Programming Interface• Codenamed mcpi, this API (app. prog. interface) define

the ways any mod program should interact with the server• Minecraft Pi Edition server implementation provides a minecraft module (relate topics in Parts I & II)

• An object (mc) in the Minecraft type can be used to• mc.getBlock(x,y,z): query the type

of a block at position (x,y,z)• mc.postToChat(msg): post a message

to the game chatmc.camera.setPos(x,y,z): move thecamera to a particular position

• mc.events.pollBlockHits(): get allblocks got hit since last poll x

y

z

The block module & the Block class

• The block module defines a list of all available blocks• You may use a number or a name to refer

to the type of blocks that you want to use• Simply 3, or• Use block.DIRT• Details (the first few shown to the right) are

at http://minecraft.gamepedia.com/Data_values_%28Pocket_Edition%29

• To add a block to a certain position (x,y,z)

• where 14 represents the color redmc.setBlock(x,y,z,block.WOOL,14)

Our first Minecraft mod project …• Here is our plan:

We are going to • Create a board game mod … • With Reversi game rules, and try to …• Bring it into Minecraft

• Let’s first do some (OO) design (relate topics in Part II)

Board Reversi

ReversiMCInvalideMoveException

our_mod

Block Minecraft

Vec3

minecraft

time

Detailed design for the Board class• We will first see what the Board class needs to do, as

well as the InvalidMoveException class

Board

NO_PLAYER = 0_cols_rows_board

__init__(cols,rows)get(x,y)set(x,y,player)display()

InvalidMoveException

Exception

class InvalidMoveException(Exception): pass

Now it’s time for coding!

class Board: NO_PLAYER = 0

def __init__(self, cols, rows): self._board = [] self._cols = cols self._rows = rows for i in range(rows): self._board.append(cols*[Board.NO_PLAYER])

def set(self, x, y, player): if 0 <= x < self._cols and 0 <= y < self._rows: self._board[int(y)][int(x)] = player else: raise InvalidMoveException("Outside play area")

def get(self, x, y): if 0 <= x < self._cols and 0 <= y < self._rows: return self._board[int(y)][int(x)] else: return Board.NO_PLAYER

def display(self): print("\n".join( [" ".join([str(piece) for piece in row]) for row in self._board]))

Detailed design for the Reversi class

• We will then see what the Reversi class needs to do• Define all 8 directions• Use a Board object to keep track of

game status• Keep track players

• 1 – this player• 2 – opponent

• 0 – no player: defined in Board class

• Get move from the console• Validate a move (by checking if there is

any pieces to flip)• Make the move and flip pieces• Output a message• Start the game

Reversi

DIRECTIONS = […]_board_player_opponent_score

__init__(x,y,player)getMove()validMove(x,y,player)piecesToFlip(x,y, player,direction)makeMove(x,y,player)set(x,y,player)output(message)display()play()

Getting the next move …• From the console, a player should type

the (0-based) column and row indices• For instance, if White is the next player, a

possible move is 6 6

def getMove(self): self.output("Player {0}'s move:".format(self.nextPlayer())) data = raw_input() coords = [int(coords) for coords in data.split()] coords.append(self.nextPlayer()) return coords

Get input from the console Split the two #’s

by the space

Wrap the #’s as a list (or array)Return the list

as [6, 6, 1]Append the list with player Id

Wrap the #’s as a list (or array)

Checking pieces to flip … • Try out 1 of the 8 directions in DIRECTIONS

def piecesToFlip(self,x,y,player,direction): currx = x+direction[0] curry = y+direction[1] curr = self._board.get(currx,curry) print("player = " + str(curr))  pieces = [] while curr != player and curr!= Board.NO_PLAYER: pieces.append((currx,curry)) currx = currx + direction[0] curry = curry + direction[1] curr = self._board.get(currx,curry) print("curr => " + str(curr))  if curr == player: return pieces else: return []

1

2

3

Keep trying until the end of the streak

Add a new pair of (x,y) when found

Return the list of pieces to be flipped

Return an empty list

Making a valid move… • Add and flip pieces, and display resultsdef makeMove(self,x,y,player):

# Check move validity if not self.validMove(x,y,player): raise InvalidMoveException("Invalid move (" + str(x) + "," + str(y) + ")")  # Place the piece self.set(x,y,player) self._score[player - 1] += 1  # Flip any appropriate pieces for direction in Reversi.DIRECTIONS: flip = self.piecesToFlip(x,y,player,direction) for x2,y2, in flip: self.set(x2,y2,player) self._score[player-1] += 1 self._score[self._opponent-1] -= 1  self._player, self._opponent = self._opponent, self._player print("Player is: " + str(self._player)) print("Opponent is: " + str(self._opponent))

Raise an exception for an invalid move

Increase its score by 1

Add a new piece for the current player

Adjust pieces and scores for both players

Iterate for all 8 directions

Flip pieces in that direction

Swap player and opponent

Report results

Detailed design for the ReversiMC class

• Lastly, here is what the ReversiMC class needs to do• Inherit features defined in the Reversi class• Define the blocks used for the board

itself: GOLD_BLOCK• Keep track players: blocks and names

• 1 – “White”, white block• 2 – “Black”, black block • 0 – “None”, transparent block (AIR)

• Need the z coord for positioning• Get move from the Minecraft game• Output a message• Display the results through Minecraft

chat

__init__(x,y,player)__init__(x,y,z,cols=8, rows=8) getMove(x,y,z,player)output(message)display()

ReversiMC

BOARD_MATERIAL = GOLD_BLOCKplayers=[AIR, Block(35,

0), Block(35, 15)] playerNames = ["None", "White", "Black"]

Reversi

Initialize the class …

class ReversiMC(Reversi): BOARD_MATERIAL = GOLD_BLOCK players = [AIR, Block(35, 0), Block(35, 15)] playerNames = ["None", "White", "Black"]  def __init__(self,x,y,z,cols=8,rows=8): self._pos = Vec3(x,y,z) self._farcorner = Vec3(x+cols,y,z+rows) self._minecraft = Minecraft.create() self._minecraft.setBlocks(x,y-1,z,x+cols-1,y-1,z+rows-1, ReversiMC.BOARD_MATERIAL) self._minecraft.setBlocks(x,y,z,x+cols-1,y,z+rows-1, ReversiMC.players[0])  super(ReversiMC,self).__init__(cols,rows)

Extend the base class

Define global variables

Use 3D library

Choose blocks for players

Connect to Minecraft

Link to super class initialization

Getting move from Minecraft game

def getMove(self): self.output("{0}'s move". format(ReversiMC.playerNames[self.nextPlayer()])) while True: # Note: only handle right-click events for now events = self._minecraft.events.pollBlockHits() for event in events: x,y,z = event.pos.x, event.pos.y, event.pos.z if self._pos.x <= x < self._farcorner.x and self._pos.z <= z < self._farcorner.z: return (x-self._pos.x, z-self._pos.z, self.nextPlayer())  time.sleep(0.1)

Announce next player

Minecraft gamer input

Return as a list

Validate move by checking space range

Detect hit location

Pause once for each game iteration

Game loop until

ended