Hello Gents,
This forum seems to have quite a number of people who know what they're doing when it comes to writing minecraft stuff, so I figured I'd ask.
I'm trying to write a function which'll move my bot to a position and set a block. Unfortunately the server always kicks me for "Cheat Detected: Distance" no matter how large of a delay I have between the move and the destroy/place action. Is there something wrong that I'm doing? My code is below:
[code]
## License: MIT, attribution welcomed.
import struct
from twisted.internet.protocol import Protocol, ClientFactory
from twisted.internet import reactor, task
from sys import stdout
import socket
NAME = "~"
VERIFICATION_STR = "~"
class Player:
def __init__(self,name,pid):
self.name = name
self.x = 0
self.y = 0
self.z = 0
self.heading = 0
self.pitch = 0
self.pid = pid
def pos(self,x,y,z):
self.x = x
self.y = y
self.z = z
def delta_pos(self,x,y,z):
self.x += x
self.y += y
self.z += z
def orient(self,heading,pitch):
self.heading = heading
self.pitch = pitch
class Action:
def __init__(self,bot):
self.bot = bot
def do(self):
pass
class MoveAction(Action):
def __init__(self,bot,x,y,z):
Action.__init__(self,bot)
self.x, self.y, self.z = x,y,z
def do(self):
self.bot.move(self.x,self.y,self.z)
class DestroyBlockAction(Action):
def __init__(self,bot,x,y,z):
Action.__init__(self,bot)
self.x, self.y, self.z = x,y,z
def do(self):
self.bot.destroy(self.x,self.y,self.z)
class SetBlockAction(Action):
def __init__(self,bot,x,y,z,type):
Action.__init__(self,bot)
self.x, self.y, self.z, self.type = x,y,z,type
def do(self):
self.bot.set(self.x,self.y,self.z,self.type)
class MinecraftBot:
def __init__(self):
self.level_data = ""
self.players = {}
self.info = None
self.action_queue = []
def onServerJoin(self, version, srv_name, motd, user_type):
print "Joined server! Ver: %s, Name: %s, Motd: %s, Your type: %s"%(version,srv_name,motd,user_type)
def onPing(self): ## atm does nothing?
pass
def onLevelStart(self): ## no actual data sent along with this
print "Receiving level data"
def onLevelData(self,length,data,percent_complete): ##
self.level_data += data
print "Downloading level, %s complete."%percent_complete
def onLevelEnd(self,x,y,z): ## size of the level (x,y,z)
print "Level downloaded. Size: (%s,%s,%s)"%(x,y,z)
f_name = "out.gz"
print "Writing %s bytes to %s"%(len(self.level_data),f_name)
with open("out.gz", "w") as f:
f.write(self.level_data)
self.level_data = None
self.start_bot()
def onSetBlock(self,x,y,z,type):
pass
def onSpawnPlayer(self,pid,name,x,y,z,heading,pitch):
p = Player(name,pid)
p.pos(x,y,z)
p.orient(heading,pitch)
self.players[pid] = p
if name == NAME: ## Name is the name of our bot.. ugly! D:
self.info = p
print "Spawned player %s [PID:%s]"%(name,pid)
def onPlayerUpdate(self,pid,x,y,z,heading,pitch):
p = self.players[pid]
p.pos(x,y,z)
p.orient(heading,pitch)
##print "%s -> (%s,%s,%s)"%(p.name,x,y,z)
def onPlayerUpdate2(self,pid,x,y,z,heading,pitch):
pass ## As I'm not sure what to do with the data
##print "PlayerUpdate2(): Is this even ever used?"
def onPositionUpdate(self,pid,delta_x,delta_y,delta_z):
p = self.players[pid]
p.delta_pos(delta_x,delta_y,delta_z)
##print "%s + (%s,%s,%s)"%(p.name,delta_x,delta_y,delta_z)
def onOrientationUpdate(self,pid,heading,pitch):
p = self.players[pid]
p.orient(heading,pitch)
##print "%s new orientation: (%s,%s)"%(p.name,heading,pitch)
def onDespawnPlayer(self,pid):
if pid in self.players:
name = self.players[pid].name
print "Removed player %s"%name
del self.players[pid]
else:
print "Removed unknown player with pid %s"%pid
def onMessage(self,pid,message): ## For some reason the bot's PID changes
print "<%s>"%(message) ## all the time and I'm not sure how to get it
## at the moment.
def onKick(self,message):
print "Got kicked! Reason: %s"%message
def onChangeUserType(self,new_type):
pass
def start_bot(self):
##self.protocol.sendMessage("hello world")
self.erase_blocks()
update = task.LoopingCall(self.update_server)
update.start(.1)
action = task.LoopingCall(self.do_action)
action.start(5)
def erase_blocks(self):
ma = MoveAction(self,0,64,120)
sa = SetBlockAction(self,0,64,120,0x01)
self.action_queue.append(ma)
self.action_queue.append(sa)
## for y in range(64,60,-1):
## for z in range(120,140,1):
## ma = MoveAction(self,0,y,z)
## da = DestroyBlockAction(self,0,y,z)
## self.action_queue.append(ma)
## self.action_queue.append(da)
def update_server(self):
## start_bot() is called right when the level is done loading
## but for a brief second, the client's pid/position is not yet sent.
if not self.info: ## for a while we won't get information about our avatar.
return
p = self.info
self.protocol.sendPosition(p.x,p.y,p.z,p.heading,p.pitch)
def do_action(self):
if self.action_queue and self.info:
i = self.action_queue.pop(0)
i.do()
def move(self,x,y,z):
self.info.x = x
self.info.y = y
self.info.z = z
def destroy(self,x,y,z):
self.protocol.setBlock(x,y,z,0x00,0x01)
## ^ ^- Stone Block Type
## |------- 0x00 is destroy signal
def set(self,x,y,z,type):
self.protocol.setBlock(x,y,z,0x01,type)
## ^- 0x01 is set block
class MinecraftBotProtocol(Protocol):
def __init__(self,bot):
self.bot = bot
self.buffer = ''
self.level_buffer = ''
self.packet_type = '\xFF' ## 0xFF is not a valid packet type. Signifying no packet here!
self.packet_length = {'\x00': 131, ## server id
'\x01': 1, ## ping
'\x02': 1, ## level initialize
'\x03': 1028, ## level data
'\x04': 7, ## level loaded
'\x06': 8, ## set block
'\x07': 74, ## spawn player
'\x08': 10, ## player update
'\x09': 7, ## player update?
'\x0a': 5, ## position update?
'\x0b': 4, ## orientation update
'\x0c': 2, ## despawn player
'\x0d': 66, ## text message
'\x0e': 65, ## kick message
'\x0f': 2 ## usermode changed
}
self.packet_name = {'\x00': 'server id',
'\x01': 'ping',
'\x02': 'level initialize',
'\x03': 'level data',
'\x04': 'level loaded',
'\x06': 'set block',
'\x07': 'spawn player',
'\x08': 'player update',
'\x09': 'player update?',
'\x0a': 'position update?',
'\x0b': 'orientation update?',
'\x0c': 'despawn player',
'\x0d': 'text message',
'\x0e': 'kick message',
'\x0f': 'usermode changed'
}
def sendMessage(self,msg):
type = 0x0d
pad = 0
while not len(msg) == 0:
chunk = msg[0:64].ljust(64)
msg = msg[len(chunk):]
packet = struct.pack('!BB64s',type,pad,chunk)
self.transport.write(packet)
def setBlock(self,x,y,z,mode,block_type):
p = self.bot.info
print "Position: (%s,%s,%s)"%(p.x,p.y,p.z)
print "Placing block at (%s,%s,%s) type: %s mode: %s"%(x,y,z,block_type,mode)
### end debug
type = 0x05
x = int(x*32)
y = int(y*32)
z = int(z*32)
packet = struct.pack('!BhhhBB',type,x,y,z,mode,block_type)
self.transport.write(packet)
print "Sending data: (%s,%s,%s) // %s)"%(x,y,z,packet)
def sendPosition(self,x,y,z,heading=0,pitch=0):
type = 0x08 ## position update packet type
pid = 255 ## the player id is always 255 when sending updates
x = int(x*32)
y = int(y*32)
z = int(z*32)
heading = heading * 256/360
pitch = pitch * 245/360
packet = struct.pack('!BBhhhBB',type,pid,x,y,z,heading,pitch)
print "Sending data: (%s,%s,%s) // %s"%(x,y,z,packet)
self.transport.write(packet)
def dataReceived(self, data):
self.buffer += data
##print('Received %s bytes'%(len(data)))
packet_type = self.buffer[0]
while (len(self.buffer)) and (len(self.buffer) >= self.packet_length[packet_type]):
self.process_data()
if self.buffer:
packet_type = self.buffer[0]
def process_data(self):
packet_type = self.buffer[0]
length = self.packet_length[packet_type]
if len(self.buffer) >= length:
##print ('Packet type: %s, Length: %s'%(self.packet_name[packet_type],length))
data = self.buffer[:length]
self.dispatch_events(data)
self.buffer = self.buffer[length:] ## trim this much data
else:
print ('Incomplete Packet [1]')
def dispatch_events(self,data):
packet_type = data[0]
data = data[1:]
if packet_type == '\x00':
d = struct.unpack('B64s64sB',data)
version = d[0]
srv_name = d[1].strip()
motd = d[2].strip()
usertype = d[3]
self.bot.onServerJoin(version,srv_name,motd,usertype)
elif packet_type == '\x01':
self.bot.onPing()
elif packet_type == '\x02':
self.bot.onLevelStart()
elif packet_type == '\x03':
d = struct.unpack('!h1024sB',data)
length = d[0]
level_data = d[1][:length]
completeness = d[2]
self.bot.onLevelData(length,level_data,completeness)
elif packet_type == '\x04':
d = struct.unpack('!hhh',data)
self.bot.onLevelEnd(*d)
elif packet_type == '\x06':
d = struct.unpack('!hhhB',data)
x = d[0]/32.0
y = d[1]/32.0
z = d[2]/32.0
type = d[3]
self.bot.onSetBlock(x,y,z,type)
elif packet_type == '\x07':
d = struct.unpack('!B64s3h2B',data)
pid = d[0]
name = d[1].strip()
x = d[2]/32.0
y = d[3]/32.0
z = d[4]/32.0
heading = d[5]*360/256
pitch = d[6]*360/256
self.bot.onSpawnPlayer(pid,name,x,y,z,heading,pitch)
elif packet_type == '\x08':
d = struct.unpack('!BhhhBB',data)
pid = d[0]
x = d[1]/32.0
y = d[2]/32.0
z = d[3]/32.0
heading = d[4]*360/256
pitch = d[5]*360/256
self.bot.onPlayerUpdate(pid,x,y,z,heading,pitch)
elif packet_type == '\x09':
d = struct.unpack('BbbbBB',data)
pid = d[0]
x = d[1]/32.0
y = d[2]/32.0
z = d[3]/32.0
heading = d[4]*360/256
pitch = d[5]*360/256
self.bot.onPlayerUpdate2(pid,x,y,z,heading,pitch)
elif packet_type == '\x0a':
d = struct.unpack('Bbbb',data)
pid = d[0]
d_x = d[1]/32.0
d_y = d[2]/32.0
d_z = d[3]/32.0
self.bot.onPositionUpdate(pid,d_x,d_y,d_z)
elif packet_type == '\x0b':
d = struct.unpack('BBB',data)
pid = d[0]
heading = d[1]*360/256
pitch = d[2]*360/256
self.bot.onOrientationUpdate(pid,heading,pitch)
elif packet_type == '\x0c':
d = struct.unpack('B',data)
self.bot.onDespawnPlayer(*d)
elif packet_type == '\x0d':
d = struct.unpack('B64s',data)
pid = d[0]
msg = d[1].strip()
self.bot.onMessage(pid,msg)
elif packet_type == '\x0e':
d = struct.unpack('64s',data)
kick_msg = d[0].strip()
self.bot.onKick(kick_msg)
elif packet_type == '\x0f':
d = struct.unpack('B',data)
self.bot.onChangeUserType(*d)
else:
print "Protocol error"
reactor.stop()
def connectionMade(self):
print("Sending connection string")
player_id = ''
player_id += struct.pack('b', 0x00) ## packet type
player_id += struct.pack('b', 0x07) ## protocol version
player_id += NAME.ljust(64,' ') ## name
player_id += VERIFICATION_STR.ljust(64, ' ') ## verification string
player_id += struct.pack('b', 0x00) ## unused
self.transport.write(player_id)
class MinecraftBotClientFactory(ClientFactory):
def __init__(self):
self.bot = MinecraftBot()
def startedConnecting(self, connector):
print 'Started to connect.'
def buildProtocol(self, addr):
print 'Connected.'
protocol = MinecraftBotProtocol(self.bot)
self.bot.protocol = protocol
return protocol
def clientConnectionLost(self, connector, reason):
print 'Lost connection. Reason:', reason
reactor.stop()
def clientConnectionFailed(self, connector, reason):
print 'Connection failed. Reason:', reason
reactor.stop()
print "Running bot"
reactor.connectTCP('72.68.131.232',25565,MinecraftBotClientFactory())
reactor.run()
[/code]
How the code works: The reactor calls dataReceived() everytime data is received. I use this to break down the packets and then note when the server is done loading and all of the players have been sent to the client. * At this time I call MinecraftBot.erase_blocks() which is supposed to queue up a bunch of actions (here I just have two -- move & then place a block).
* I also start the main bot action queue loop -- the bot goes through the queue and performs an action in the queue every 5 sections.
* There's another function, this function deals with updating the server with the player's current position 10 times a second. I'm not sure if this is required, but I saw from the protocol explanation here ( [url]http://www.minecraftwiki.net/wiki/Development_Resources[/url] ) that the client updates the server even when not moving.
Anyway, the bot-client can teleport to the new position, but gets kicked as soon as I try to place a block.
Edit:
When I receive coordinates from the server containing the player/block coordinates, I divide them by 32 to get the tile coordinates and then store the data as a float. When I send data to the server regarding the tile to be deleted, I multiply the given tile-coordinate by 32 to get the coordinates in the format that minecraft likes.
Edit2:
Well, I whipped out wireshark and started packet sniffing.
I noticed that I sent out a packet: [08] [ff][01 5e][04 33][00 0b][c1][12][05][00 07][00 20][00 00][01][24]
So I broke it down.
[code]
[08] [ff] [01 5e] [04 33] [00 0b] [c1] [12]
ID Player X Y Z Heading Pitch
[05] [00 07] [00 20] [00 00] [01] [24]
ID x y z Create White Block
For the position:
struct.unpack('!3h','\x01\x5e\x04\x33\x00\x0b')
Gives me (350, 1075, 11)
In tile cordinates that's (10,33,0)
For the tile placement:
>>> f = struct.unpack('!3h','\x00\x07\x00\x20\x00\x00')
>>> f
(7, 32, 0)
^ apparently that's ... in tile coordinates D:
[/code]
Apparently player coordinates are sent as coordinate as int(float * 32) whereas block coordinates are just .. left as is.
Fuuu.
Leaving this here for posterity.
When I wrote my Clique Craft sponge spam bot I experienced the same problem.
I solved it by sending the position packet twice, and the set block packet once after that.
Remember that if you exceed 255 in x y or z it will write 0x01 to the next byte, not the previous. The original minecraft client writes 0x01 to the previous byte. I haven't read the code you wrote because I'm in a hurry, but I will read it when I get back home from school.
I finished it :D
[url]http://www.youtube.com/watch?v=ZIeKj1A8ZRA[/url]
[code]
import struct
from twisted.internet.protocol import Protocol, ClientFactory
from twisted.internet import reactor, task
from sys import stdout
import socket, time
NAME = "xxxx"
VERIFICATION_STR = "xxxxxxx"
ONE = [ [0,1,0], [0,1,0], [0,1,0], [0,1,0], [0,1,0] ]
TWO = [ [1,1,1], [0,0,1], [1,1,1], [1,0,0], [1,1,1] ]
THREE = [ [1,1,1], [0,0,1], [0,1,1], [0,0,1], [1,1,1] ]
FOUR = [ [1,0,1], [1,0,1], [1,1,1], [0,0,1], [0,0,1] ]
FIVE = [ [1,1,1], [1,0,0], [1,1,1], [0,0,1], [1,1,1] ]
SIX = [ [1,1,1], [1,0,0], [1,1,1], [1,0,1], [1,1,1] ]
SEVEN = [ [1,1,1], [0,0,1], [0,0,1], [0,0,1], [0,0,1] ]
EIGHT = [ [1,1,1], [1,0,1], [1,1,1], [1,0,1], [1,1,1] ]
NINE = [ [1,1,1], [1,0,1], [1,1,1], [0,0,1], [1,1,1] ]
ZERO = [ [1,1,1], [1,0,1], [1,0,1], [1,0,1], [1,1,1] ]
## With any luck, the bot will write something like this
## 1 2 3 +z ## ignore these lines
## 12345679012345678901234567890123 ## they're the coordinate system.
## @@@ @@@ @@@ @ @ @@@ @@@ -1
## @ @ @ @ @ @ @ @ @ @ -2
## @ @ @@@ @@@ @@@ @@@ @@@ -3
## @ @ @ @ @ @ @ @ @ -4
## @@@ @@@ @@@ @ @@@ @@@ -5 -y
## [ p1 ] [ p2 ] [ p3 ]
def insert2DArray(arr,arr_to_insert,row,col):
a1 = arr
a2 = arr_to_insert
init_col = col
for r in range(0,len(a2)):
for c in range(0,len(a2[0])):
a1[row][col] = a2[r][c]
col+=1
row +=1
col = init_col
return a1
class Player:
def __init__(self,name,pid):
self.name = name
self.x = 0
self.y = 0
self.z = 0
self.heading = 0
self.pitch = 0
self.pid = pid
def pos(self,x,y,z):
self.x = x
self.y = y
self.z = z
def delta_pos(self,x,y,z):
self.x += x
self.y += y
self.z += z
def orient(self,heading,pitch):
self.heading = heading
self.pitch = pitch
class Action:
def __init__(self,bot):
self.bot = bot
def do(self):
pass
class SleepAction(Action): ## Okay, a much better thing would be to have
def __init__(self,bot): ## some sort of a cron thing, I know.
Action.__init__(self,bot)
def do(self):
pass
class MoveAction(Action):
def __init__(self,bot,x,y,z):
Action.__init__(self,bot)
self.x, self.y, self.z = x,y,z
def do(self):
self.bot.move(self.x,self.y,self.z)
class DestroyBlockAction(Action):
def __init__(self,bot,x,y,z):
Action.__init__(self,bot)
self.x, self.y, self.z = x,y,z
def do(self):
self.bot.destroy(self.x,self.y,self.z)
class SetBlockAction(Action):
def __init__(self,bot,x,y,z,type):
Action.__init__(self,bot)
self.x, self.y, self.z, self.type = x,y,z,type
def do(self):
self.bot.set(self.x,self.y,self.z,self.type)
class TriggerAction(Action):
def __init__(self,bot,func,args=None):
Action.__init__(self,bot)
self.f = func
self.args = args
def do(self):
if self.args:
self.f(self.args)
else:
self.f()
class TimeBot:
def __init__(self,bot,x,y,z):
self.bot = bot
## this is the upper left coordinates of the clock that will be drawn
self.init_x = x
self.init_y = y
self.init_z = z
## these will hold the positions
self.pos1 = (x,y-2,z+4)
self.pos2 = (x,y-2,z+15)
self.pos3 = (x,y-2,z+25)
self.hour_color = 0x22 ## black
self.min_color = 0x15 ## red
self.sec_color = 0x17 ## yellow
self.colon_color = 0x31 ## obsidian
## used for storing if a block is written at that coordinate
self.arr1 = [[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]]
self.arr2 = [[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]]
self.arr3 = [[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]]
self.hour = None
self.min = None
self.sec = None
self.last_spot = 01 ## 0 == hr, 1 == min, 2== sec, -1 no position yet
def onStart(self):
## This queues up a bunch of events to prepare the area that we're going to start clearing
self.erase_blocks()
ta = TriggerAction(self.bot,self.start_clock)
self.bot.action_queue.append(ta)
def fill_array(self,str):
## This function fills in a 7x5 tile array that represents two digits
arr = [[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]]
time_to_arr = {'0':ZERO,'1':ONE,'2':TWO, '3':THREE,'4':FOUR,
'5':FIVE,'6':SIX,'7':SEVEN,'8':EIGHT,'9':NINE}
first = time_to_arr[ str[0] ]
second = time_to_arr[ str[1] ]
t = insert2DArray(arr,first,0,0)
out_arr = insert2DArray(arr,second,0,4)
return out_arr
def update_tiles(self,arr_old,arr_new,x,y,z,color):
## z is col, y is row
rows = len(arr_old)
cols = len(arr_old[0])
for row in range(0,rows):
for col in range(0,cols):
old = arr_old[row][col]
new = arr_new[row][col]
if (new==1) and (old==0):
sa = SetBlockAction(self.bot,x,y-row,z+col,color)
self.bot.action_queue.append(sa)
elif (new==0) and (old==1):
da = DestroyBlockAction(self.bot,x,y-row,z+col)
self.bot.action_queue.append(da)
def sleep(self):
for x in range(0,3):
sa = SleepAction(self.bot)
self.bot.action_queue.append(sa)
def update_time(self):
hr,min,sec = time.strftime("%I %M %S",time.localtime()).split()
if not hr == self.hour:
ma = MoveAction(self.bot,*self.pos1)
self.bot.action_queue.append(ma)
if not self.last_spot == 0:
self.sleep()
hr_arr = self.fill_array(hr)
self.update_tiles(self.arr1,hr_arr,self.init_x,self.init_y,self.init_z,self.hour_color)
self.hour = hr
self.arr1 = hr_arr
self.last_spot = 0
if not min == self.min:
ma = MoveAction(self.bot,*self.pos2)
self.bot.action_queue.append(ma)
if not self.last_spot == 1:
self.sleep()
min_arr = self.fill_array(min)
self.update_tiles(self.arr2,min_arr,self.init_x,self.init_y,self.init_z+12,self.min_color)
self.min = min
self.arr2 = min_arr
self.last_spot = 1
if not sec == self.sec:
ma = MoveAction(self.bot,*self.pos3)
self.bot.action_queue.append(ma)
if not self.last_spot == 2:
self.sleep()
sec_arr = self.fill_array(sec)
self.update_tiles(self.arr3,sec_arr,self.init_x,self.init_y,self.init_z+22,self.sec_color)
self.sec = sec
self.arr3 = sec_arr
self.last_spot = 2
def draw_colons(self):
x,y,z = self.init_x,self.init_y,self.init_z
## z+15 is a good spot to draw from
ma = MoveAction(self.bot,x,y-2,z+15)
self.bot.action_queue.append(ma)
## located at z+10, z+20
sa = SleepAction(self.bot)
self.bot.action_queue.append(sa)
points = [ (x,y-1,z+10),
(x,y-1,z+20),
(x,y-3,z+10),
(x,y-3,z+20)]
for pt in points:
x,y,z = pt
sa = SetBlockAction(self.bot,x,y,z,self.colon_color)
self.bot.action_queue.append(sa)
def start_clock(self):
self.draw_colons()
self.clock_updater = task.LoopingCall(self.update_time)
self.clock_updater.start(1.0)
def erase_blocks(self):
for pos in [self.pos1,self.pos2,self.pos3]:
ma = MoveAction(self.bot,*pos)
self.bot.action_queue.append(ma)
for x in range(0,2):
self.bot.action_queue.append(SleepAction(self.bot))
c_x, c_y, c_z = pos
for y in range(c_y+2,c_y-3,-1):
for z in range(c_z-4,c_z+6,1):
da = DestroyBlockAction(self.bot,c_x,y,z)
self.bot.action_queue.append(da)
class MinecraftBot:
def __init__(self):
self.level_data = ""
self.players = {}
self.info = None
self.action_queue = []
self.time_bot = TimeBot(self,254,63,130) ## This is on the center-top of a map wall
def onServerJoin(self, version, srv_name, motd, user_type):
print "Joined server! Ver: %s, Name: %s, Motd: %s, Your type: %s"%(version,srv_name,motd,user_type)
def onPing(self): ## atm does nothing?
pass
def onLevelStart(self): ## no actual data sent along with this
print "Receiving level data"
def onLevelData(self,length,data,percent_complete): ##
self.level_data += data
print "Downloading level, %s complete."%percent_complete
def onLevelEnd(self,x,y,z): ## size of the level (x,y,z)
print "Level downloaded. Size: (%s,%s,%s)"%(x,y,z)
f_name = "out.gz"
print "Writing %s bytes to %s"%(len(self.level_data),f_name)
with open("out.gz", "w") as f:
f.write(self.level_data)
self.level_data = None
def onSetBlock(self,x,y,z,type):
pass
def onSpawnPlayer(self,pid,name,x,y,z,heading,pitch):
p = Player(name,pid)
p.pos(x,y,z)
p.orient(heading,pitch)
self.players[pid] = p
if name == NAME: ## Name is the name of our bot.. ugly! D:
self.info = p
self.start_bot() ## Coincidentally, this is when we start the bot!
print "Spawned player %s [PID:%s]"%(name,pid)
def onPlayerUpdate(self,pid,x,y,z,heading,pitch):
p = self.players[pid]
p.pos(x,y,z)
p.orient(heading,pitch)
##print "%s -> (%s,%s,%s)"%(p.name,x,y,z)
def onPlayerUpdate2(self,pid,x,y,z,heading,pitch):
pass ## As I'm not sure what to do with the data
##print "PlayerUpdate2(): Is this even ever used?"
def onPositionUpdate(self,pid,delta_x,delta_y,delta_z):
p = self.players[pid]
p.delta_pos(delta_x,delta_y,delta_z)
##print "%s + (%s,%s,%s)"%(p.name,delta_x,delta_y,delta_z)
def onOrientationUpdate(self,pid,heading,pitch):
p = self.players[pid]
p.orient(heading,pitch)
##print "%s new orientation: (%s,%s)"%(p.name,heading,pitch)
def onDespawnPlayer(self,pid):
if pid in self.players:
name = self.players[pid].name
print "Removed player %s"%name
del self.players[pid]
else:
print "Removed unknown player with pid %s"%pid
def onMessage(self,pid,message): ## For some reason the bot's PID changes
print "<%s>"%(message) ## all the time and I'm not sure how to get it
## at the moment.
def onKick(self,message):
print "Got kicked! Reason: %s"%message
def onChangeUserType(self,new_type):
pass
def start_bot(self):
## Here we'll start our bot thingamugummy
self.time_bot.onStart()
## This handles the periodic updates to the server
update = task.LoopingCall(self.update_server)
update.start(.1)
## This sends out one action every 1/10th of a second
action = task.LoopingCall(self.do_action)
action.start(.1)
def update_server(self):
## start_bot() is called right when the level is done loading
## but for a brief second, the client's pid/position is not yet sent.
if not self.info: ## for a while we won't get information about our avatar.
return
p = self.info
self.protocol.sendPosition(p.x,p.y,p.z,p.heading,p.pitch)
def do_action(self):
if self.action_queue and self.info:
i = self.action_queue.pop(0)
i.do()
def move(self,x,y,z):
self.info.x = x
self.info.y = y
self.info.z = z
def destroy(self,x,y,z):
self.protocol.setBlock(x,y,z,0x00,0x01)
## ^ ^- Stone Block Type
## |------- 0x00 is destroy signal
def set(self,x,y,z,type):
self.protocol.setBlock(x,y,z,0x01,type)
## ^- 0x01 is set block
class MinecraftBotProtocol(Protocol):
def __init__(self,bot):
self.bot = bot
self.buffer = ''
self.level_buffer = ''
self.packet_length = {'\x00': 131, ## server id
'\x01': 1, ## ping
'\x02': 1, ## level initialize
'\x03': 1028, ## level data
'\x04': 7, ## level loaded
'\x06': 8, ## set block
'\x07': 74, ## spawn player
'\x08': 10, ## player update
'\x09': 7, ## player update?
'\x0a': 5, ## position update?
'\x0b': 4, ## orientation update
'\x0c': 2, ## despawn player
'\x0d': 66, ## text message
'\x0e': 65, ## kick message
'\x0f': 2 ## usermode changed
}
self.packet_name = {'\x00': 'server id',
'\x01': 'ping',
'\x02': 'level initialize',
'\x03': 'level data',
'\x04': 'level loaded',
'\x06': 'set block',
'\x07': 'spawn player',
'\x08': 'player update',
'\x09': 'player update?',
'\x0a': 'position update?',
'\x0b': 'orientation update?',
'\x0c': 'despawn player',
'\x0d': 'text message',
'\x0e': 'kick message',
'\x0f': 'usermode changed'
}
def sendMessage(self,msg):
type = 0x0d
pad = 0 ## there's an unused pad byte for this packet
while not len(msg) == 0:
chunk = msg[0:64].ljust(64)
msg = msg[len(chunk):]
packet = struct.pack('!BB64s',type,pad,chunk)
self.transport.write(packet)
def setBlock(self,x,y,z,mode,block_type):
type = 0x05
x = int(x)
y = int(y)
z = int(z)
packet = struct.pack('!BhhhBB',type,x,y,z,mode,block_type)
self.transport.write(packet)
def sendPosition(self,x,y,z,heading=0,pitch=0):
type = 0x08 ## position update packet type
pid = 255 ## the player id is always 255 when sending updates
x = int(x*32)
y = int(y*32)
z = int(z*32)
heading = heading * 256/360
pitch = pitch * 256/360
packet = struct.pack('!BBhhhBB',type,pid,x,y,z,heading,pitch)
self.transport.write(packet)
def dataReceived(self, data):
self.buffer += data
packet_type = self.buffer[0]
while (len(self.buffer)) and (len(self.buffer) >= self.packet_length[packet_type]):
self.process_data()
if self.buffer:
packet_type = self.buffer[0]
def process_data(self):
packet_type = self.buffer[0]
length = self.packet_length[packet_type]
if len(self.buffer) >= length:
##print ('Packet type: %s, Length: %s'%(self.packet_name[packet_type],length))
data = self.buffer[:length]
self.dispatch_events(data)
self.buffer = self.buffer[length:] ## trim this much data
else:
print ('Incomplete Packet [1]')
def dispatch_events(self,data):
packet_type = data[0]
data = data[1:]
if packet_type == '\x00':
d = struct.unpack('B64s64sB',data)
version = d[0]
srv_name = d[1].strip()
motd = d[2].strip()
usertype = d[3]
self.bot.onServerJoin(version,srv_name,motd,usertype)
elif packet_type == '\x01':
self.bot.onPing()
elif packet_type == '\x02':
self.bot.onLevelStart()
elif packet_type == '\x03':
d = struct.unpack('!h1024sB',data)
length = d[0]
level_data = d[1][:length]
completeness = d[2]
self.bot.onLevelData(length,level_data,completeness)
elif packet_type == '\x04':
d = struct.unpack('!hhh',data)
self.bot.onLevelEnd(*d)
elif packet_type == '\x06':
d = struct.unpack('!hhhB',data)
x = d[0]
y = d[1]
z = d[2]
type = d[3]
self.bot.onSetBlock(x,y,z,type)
elif packet_type == '\x07':
d = struct.unpack('!B64s3h2B',data)
pid = d[0]
name = d[1].strip()
x = d[2]/32.0
y = d[3]/32.0
z = d[4]/32.0
heading = d[5]*360/256
pitch = d[6]*360/256
self.bot.onSpawnPlayer(pid,name,x,y,z,heading,pitch)
elif packet_type == '\x08':
d = struct.unpack('!BhhhBB',data)
pid = d[0]
x = d[1]/32.0
y = d[2]/32.0
z = d[3]/32.0
heading = d[4]*360/256
pitch = d[5]*360/256
self.bot.onPlayerUpdate(pid,x,y,z,heading,pitch)
elif packet_type == '\x09':
d = struct.unpack('BbbbBB',data)
pid = d[0]
x = d[1]/32.0
y = d[2]/32.0
z = d[3]/32.0
heading = d[4]*360/256
pitch = d[5]*360/256
self.bot.onPlayerUpdate2(pid,x,y,z,heading,pitch)
elif packet_type == '\x0a':
d = struct.unpack('Bbbb',data)
pid = d[0]
d_x = d[1]/32.0
d_y = d[2]/32.0
d_z = d[3]/32.0
self.bot.onPositionUpdate(pid,d_x,d_y,d_z)
elif packet_type == '\x0b':
d = struct.unpack('BBB',data)
pid = d[0]
heading = d[1]*360/256
pitch = d[2]*360/256
self.bot.onOrientationUpdate(pid,heading,pitch)
elif packet_type == '\x0c':
d = struct.unpack('B',data)
self.bot.onDespawnPlayer(*d)
elif packet_type == '\x0d':
d = struct.unpack('B64s',data)
pid = d[0]
msg = d[1].strip()
self.bot.onMessage(pid,msg)
elif packet_type == '\x0e':
d = struct.unpack('64s',data)
kick_msg = d[0].strip()
self.bot.onKick(kick_msg)
elif packet_type == '\x0f':
d = struct.unpack('B',data)
self.bot.onChangeUserType(*d)
else:
print "Protocol error"
reactor.stop()
def connectionMade(self):
print("Sending connection string")
player_id = ''
player_id += struct.pack('b', 0x00) ## packet type
player_id += struct.pack('b', 0x07) ## protocol version
player_id += NAME.ljust(64,' ') ## name
player_id += VERIFICATION_STR.ljust(64, ' ') ## verification string
player_id += struct.pack('b', 0x00) ## unused
self.transport.write(player_id)
class MinecraftBotClientFactory(ClientFactory):
def __init__(self):
self.bot = MinecraftBot()
def startedConnecting(self, connector):
print 'Started to connect.'
def buildProtocol(self, addr):
print 'Connected.'
protocol = MinecraftBotProtocol(self.bot)
self.bot.protocol = protocol
return protocol
def clientConnectionLost(self, connector, reason):
print 'Lost connection. Reason:', reason
reactor.stop()
def clientConnectionFailed(self, connector, reason):
print 'Connection failed. Reason:', reason
reactor.stop()
print "Running bot"
reactor.connectTCP('72.68.131.232',25565,MinecraftBotClientFactory())
reactor.run()
[/code]
Haha, very amusing. Good work.
Sorry, you need to Log In to post a reply to this thread.