Compare commits

...

3 Commits

Author SHA1 Message Date
8fabe5859a Simplify directory structure 2020-09-08 20:13:15 -06:00
e444cf6677 Improve exception handling 2020-09-08 19:52:50 -06:00
a76d02d53c Add support for chat commands 2020-09-08 17:51:27 -06:00
19 changed files with 347 additions and 325 deletions

21
bot.py
View File

@ -13,14 +13,14 @@ SERVER = os.environ['SERVER']
import monkey_patch # must be before any possible pyCraft imports import monkey_patch # must be before any possible pyCraft imports
from custom.managers import DataManager, ChunksManager, ChatManager
from minecraft import authentication from minecraft import authentication
from minecraft.exceptions import YggdrasilError from minecraft.exceptions import YggdrasilError
from minecraft.networking.connection import Connection from minecraft.networking.connection import Connection
from minecraft.networking.packets import Packet, clientbound, serverbound from minecraft.networking.packets import Packet, clientbound, serverbound
from custom.networking.packets.clientbound.play.block_change_packet import BlockChangePacket from protocol.managers import DataManager, ChunksManager, ChatManager, ChunkNotLoadedException
from protocol.packets import BlockChangePacket
from bunch import Bunch from bunch import Bunch
from panda3d.core import LPoint3f, LVector3f from panda3d.core import LPoint3f, LVector3f
@ -53,7 +53,7 @@ def tick(global_state):
try: try:
g.chunks.get_block_at(*utils.pint(p)) g.chunks.get_block_at(*utils.pint(p))
except chunks.ChunkNotLoadedException: except ChunkNotLoadedException:
return return
#l.jobstate.run() #l.jobstate.run()
@ -161,7 +161,7 @@ def bot(global_state):
g = global_state g = global_state
g.local_state = Bunch() g.local_state = Bunch()
if 'mcdata' not in g: if not g.mcdata:
g.mcdata = DataManager('./mcdata') g.mcdata = DataManager('./mcdata')
if not g.connection: if not g.connection:
@ -175,19 +175,16 @@ def bot(global_state):
g.connection = Connection(SERVER, 25565, auth_token=auth_token) g.connection = Connection(SERVER, 25565, auth_token=auth_token)
g.chunks = ChunksManager(g.mcdata) g.chunks = ChunksManager(g.mcdata)
g.chunks.register(g.connection)
g.chat = ChatManager()
g.chat.register(g.connection)
g.connection.connect() g.connection.connect()
def packet_wrapper(handler): def packet_wrapper(handler):
def wrapper(packet): def wrapper(packet):
print('Wrapper:', handler)
handler(packet, g) handler(packet, g)
return wrapper return wrapper
g.chunks.register(g.connection)
h1 = packet_wrapper(packet_handlers.handle_join_game) h1 = packet_wrapper(packet_handlers.handle_join_game)
g.connection.register_packet_listener(h1, clientbound.play.JoinGamePacket) g.connection.register_packet_listener(h1, clientbound.play.JoinGamePacket)
@ -197,6 +194,10 @@ def bot(global_state):
h3 = packet_wrapper(packet_handlers.handle_block_change) h3 = packet_wrapper(packet_handlers.handle_block_change)
g.connection.register_packet_listener(h3, BlockChangePacket) g.connection.register_packet_listener(h3, BlockChangePacket)
g.chat = ChatManager(g)
h4 = packet_wrapper(packet_handlers.handle_chat)
g.chat.set_handler(h4)
try: try:
while not g.pos: while not g.pos:
time.sleep(TICK) time.sleep(TICK)
@ -207,9 +208,7 @@ def bot(global_state):
time.sleep(TICK) time.sleep(TICK)
print('Chunks loaded.') print('Chunks loaded.')
print('init..')
init(g) init(g)
print('done init')
while g.running: while g.running:
tick(g) tick(g)

View File

@ -1,3 +0,0 @@
from .data import DataManager
from .chunks import ChunksManager
from .chat import ChatManager

View File

@ -1,36 +0,0 @@
import json
from minecraft.networking.packets import clientbound, serverbound
class ChatManager:
def __init__(self):
return
def translate_chat(self, data):
if isinstance(data, str):
return data
elif 'extra' in data:
return "".join([self.translate_chat(x) for x in data['extra']])
elif 'text' in data:
return data['text']
else:
return "?"
def print_chat(self, chat_packet):
# TODO: Replace with handler
try:
print("[%s] %s"%(chat_packet.field_string('position'), self.translate_chat(json.loads(chat_packet.json_data))))
except Exception as ex:
print("Exception %r on message (%s): %s" % (ex, chat_packet.field_string('position'), chat_packet.json_data))
def register(self, connection):
connection.register_packet_listener(self.print_chat, clientbound.play.ChatMessagePacket)
def send(self, connection, text):
if not text:
# Prevents connection bug when sending empty chat message
return
packet = serverbound.play.ChatPacket()
packet.message = text
connection.write_packet(packet)

View File

@ -1,97 +0,0 @@
from math import floor
from ..networking.packets.clientbound.play import block_change_packet, chunk_data
class ChunksManager:
def __init__(self, data_manager):
self.data = data_manager
self.chunks = {}
self.biomes = {}
def handle_block(self, block_packet):
self.set_block_at(block_packet.location.x, block_packet.location.y, block_packet.location.z, block_packet.block_state_id)
#self.print_chunk(self.get_chunk(floor(block_packet.location.x/16), floor(block_packet.location.y/16), floor(block_packet.location.z/16)), block_packet.location.y%16)
#print('Block %s at %s'%(blocks_states[block_packet.block_state_id], block_packet.location))
def handle_multiblock(self, multiblock_packet):
for b in multiblock_packet.records:
self.handle_block(b)
def handle_chunk(self, chunk_packet):
for i in chunk_packet.chunks:
self.chunks[(chunk_packet.x, i, chunk_packet.z)] = chunk_packet.chunks[i]
self.biomes[(chunk_packet.x, None, chunk_packet.z)] = chunk_packet.biomes # FIXME
def register(self, connection):
connection.register_packet_listener(self.handle_block, block_change_packet.BlockChangePacket)
connection.register_packet_listener(self.handle_multiblock, block_change_packet.MultiBlockChangePacket)
connection.register_packet_listener(self.handle_chunk, chunk_data.ChunkDataPacket)
def get_chunk(self, x, y, z):
index = (x, y, z)
if not index in self.chunks:
raise ChunkNotLoadedException(index)
return self.chunks[index]
def get_loaded_area(self, ignore_empty=False):
first = next(iter(self.chunks.keys()))
x0 = x1 = first[0]
y0 = y1 = first[1]
z0 = z1 = first[2]
for k in self.chunks.keys():
if ignore_empty and self.chunks[k].empty:
continue
x0 = min(x0, k[0])
x1 = max(x1, k[0])
y0 = min(y0, k[1])
y1 = max(y1, k[1])
z0 = min(z0, k[2])
z1 = max(z1, k[2])
return ((x0,y0,z0),(x1,y1,z1))
def get_block_at(self, x, y, z):
c = self.get_chunk(floor(x/16), floor(y/16), floor(z/16))
return c.get_block_at(x%16, y%16, z%16)
def set_block_at(self, x, y, z, block):
c = self.get_chunk(floor(x/16), floor(y/16), floor(z/16))
c.set_block_at(x%16, y%16, z%16, block)
def print_chunk(self, chunk, y_slice):
print("This is chunk %d %d %d at slice %d:"%(chunk.x, chunk.y, chunk.z, y_slice))
print("+%s+"%("-"*16))
for z in range(16):
missing = []
print("|", end="")
for x in range(16):
sid = chunk.get_block_at(x, y_slice, z)
bloc = self.data.blocks_states[sid]
if bloc == "minecraft:air" or bloc == "minecraft:cave_air":
c = " "
elif bloc == "minecraft:grass_block" or bloc == "minecraft:dirt":
c = "-"
elif bloc == "minecraft:water":
c = "~"
elif bloc == "minecraft:lava":
c = "!"
elif bloc == "minecraft:bedrock":
c = "_"
elif bloc == "minecraft:stone":
c = "X"
else:
missing.append(bloc)
c = "?"
print(c, end="")
print("| %s"%(",".join(missing)))
print("+%s+"%("-"*16))
if chunk.entities:
print("Entities in slice: %s"%(", ".join([x['id'].decode() for x in chunk.entities])))
class ChunkNotLoadedException(Exception):
def __str__(self):
pos = self.args[0]
return "Chunk at %d %d %d not loaded (yet?)"%(pos[0], pos[1], pos[2])

View File

@ -1,32 +0,0 @@
import os
import json
class DataManager:
def __init__(self, directory):
self.blocks = {}
self.blocks_states = {}
self.blocks_properties = {}
self.registries = {}
self.biomes = {}
self.entity_type = {}
if not os.path.isdir(directory):
raise FileNotFoundError("%s is not a valid directory")
if not os.path.isfile("%s/registries.json"%(directory)):
raise FileNotFoundError("%s is not a valid minecraft data directory")
with open("%s/blocks.json"%(directory)) as f:
blocks = json.loads(f.read())
for x in blocks:
for s in blocks[x]['states']:
self.blocks_states[s['id']] = x
self.blocks_properties[s['id']] = s.get('properties', {})
with open("%s/registries.json"%(directory)) as f:
registries = json.loads(f.read())
#for x in registries["minecraft:biome"]["entries"]:
# self.biomes[registries["minecraft:biome"]["entries"][x]["protocol_id"]] = x
for x in registries["minecraft:entity_type"]["entries"]:
self.entity_type[registries["minecraft:entity_type"]["entries"][x]["protocol_id"]] = x

View File

@ -1,112 +0,0 @@
from minecraft.networking.packets import Packet
from minecraft.networking.types import (
VarInt, Integer, UnsignedByte, Position, Vector, MutableRecord,
attribute_alias, multi_attribute_alias, Long, Boolean, VarLong,
)
class BlockChangePacket(Packet):
id = 0x0B
packet_name = 'block change'
definition = [
{'location': Position},
{'block_state_id': VarInt}]
block_state_id = 0
# For protocols < 347: an accessor for (block_state_id >> 4).
@property
def blockId(self):
return self.block_state_id >> 4
@blockId.setter
def blockId(self, block_id):
self.block_state_id = (self.block_state_id & 0xF) | (block_id << 4)
# For protocols < 347: an accessor for (block_state_id & 0xF).
@property
def blockMeta(self):
return self.block_state_id & 0xF
@blockMeta.setter
def blockMeta(self, meta):
self.block_state_id = (self.block_state_id & ~0xF) | (meta & 0xF)
# This alias is retained for backward compatibility.
blockStateId = attribute_alias('block_state_id')
class MultiBlockChangePacket(Packet):
id = 0x3B
packet_name = 'multi block change'
fields = 'chunk_x', 'chunk_z', 'records'
# Access the 'chunk_x' and 'chunk_z' fields as a tuple.
chunk_pos = multi_attribute_alias(tuple, 'chunk_x', 'chunk_z')
class Record(MutableRecord):
__slots__ = 'x', 'y', 'z', 'block_state_id', 'location'
def __init__(self, **kwds):
self.block_state_id = 0
super(MultiBlockChangePacket.Record, self).__init__(**kwds)
# Access the 'x', 'y', 'z' fields as a Vector of ints.
position = multi_attribute_alias(Vector, 'x', 'y', 'z')
# For protocols < 347: an accessor for (block_state_id >> 4).
@property
def blockId(self):
return self.block_state_id >> 4
@blockId.setter
def blockId(self, block_id):
self.block_state_id = self.block_state_id & 0xF | block_id << 4
# For protocols < 347: an accessor for (block_state_id & 0xF).
@property
def blockMeta(self):
return self.block_state_id & 0xF
@blockMeta.setter
def blockMeta(self, meta):
self.block_state_id = self.block_state_id & ~0xF | meta & 0xF
# This alias is retained for backward compatibility.
blockStateId = attribute_alias('block_state_id')
def read(self, file_object, parent):
data = VarLong.read(file_object)
self.block_state_id = int(data >> 12)
self.x = int(data >> 8 & 0xf)
self.z = int(data >> 4 & 0xf)
self.y = int(data & 0xf)
# Absolute position in world to be compatible with BlockChangePacket
self.location = Vector(self.position.x + parent.chunk_x*16, self.position.y, self.position.z + parent.chunk_z*16)
def write(self, packet_buffer):
raise
UnsignedByte.send(self.x << 4 | self.z & 0xF, packet_buffer)
UnsignedByte.send(self.y, packet_buffer)
VarInt.send(self.block_state_id, packet_buffer)
def read(self, file_object):
coords = Long.read(file_object)
self.chunk_x = int(coords >> 42 & 0x3fffff)
self.chunk_z = int(coords >> 20 & 0x3fffff)
self.chunk_y = int(coords & 0xfffff)
self.unknown = Boolean.read(file_object)
array_size = VarInt.read(file_object)
self.records = []
for i in range(array_size):
record = self.Record()
record.read(file_object, self)
self.records.append(record)
def write_fields(self, packet_buffer):
raise
Integer.send(self.chunk_x, packet_buffer)
Integer.send(self.chunk_z, packet_buffer)
VarInt.send(len(self.records), packet_buffer)
for record in self.records:
record.write(packet_buffer)

View File

@ -1,12 +0,0 @@
from minecraft.networking.types.basic import Type, Byte, Short, Integer, Long, Float, Double, ShortPrefixedByteArray
class IntegerPrefixedByteArray(Type):
@staticmethod
def read(file_object):
length = Integer.read(file_object)
return struct.unpack(str(length) + "s", file_object.read(length))[0]
@staticmethod
def send(value, socket):
Integer.send(len(value), socket)
socket.send(value)

View File

@ -1,6 +1,7 @@
import importlib import importlib
import threading import threading
import time import time
import traceback
from flask import Flask from flask import Flask
app = Flask(__name__) app = Flask(__name__)
@ -15,6 +16,7 @@ global_state = Bunch()
g = global_state g = global_state
g.local_state = False g.local_state = False
g.connection = False g.connection = False
g.mcdata = False
g.pos = False g.pos = False
@app.route('/') @app.route('/')
@ -42,9 +44,16 @@ def main():
try: try:
while True: while True:
try:
g.running = True g.running = True
bot.bot(global_state) bot.bot(global_state)
importlib.reload(bot) importlib.reload(bot)
except BaseException as e:
g.running = True
traceback.print_exc()
print('Locking...')
while g.running:
time.sleep(1)
except KeyboardInterrupt: except KeyboardInterrupt:
observer.stop() observer.stop()
observer.join() observer.join()

View File

@ -1,17 +1,17 @@
import minecraft.networking.packets import minecraft.networking.packets
from custom.networking.packets.clientbound.play import chunk_data, block_change_packet from protocol import packets
def get_packets(old_get_packets): def get_packets(old_get_packets):
def wrapper(func, context): def wrapper(func, context):
print('Monkey-patch worked.') print('Monkey-patch worked.')
packets = func(context) mc_packets = func(context)
# add any custom packets here # add any custom packets here
packets.add(chunk_data.ChunkDataPacket) mc_packets.add(packets.ChunkDataPacket)
packets.add(block_change_packet.BlockChangePacket) mc_packets.add(packets.BlockChangePacket)
packets.add(block_change_packet.MultiBlockChangePacket) mc_packets.add(packets.MultiBlockChangePacket)
return packets return mc_packets
return lambda x: wrapper(old_get_packets, x) return lambda x: wrapper(old_get_packets, x)
minecraft.networking.packets.clientbound.play.get_packets = get_packets(minecraft.networking.packets.clientbound.play.get_packets) minecraft.networking.packets.clientbound.play.get_packets = get_packets(minecraft.networking.packets.clientbound.play.get_packets)

View File

@ -1,8 +1,11 @@
import re
import time import time
import importlib import importlib
from panda3d.core import LPoint3f from panda3d.core import LPoint3f
from minecraft.networking.packets import Packet, clientbound, serverbound
import utils import utils
importlib.reload(utils) importlib.reload(utils)
import path import path
@ -15,8 +18,6 @@ def handle_join_game(packet, g):
def handle_block_change(packet, g): def handle_block_change(packet, g):
l = g.local_state l = g.local_state
print('block change:')
print(packet)
if packet.block_state_id == 3887: if packet.block_state_id == 3887:
try: try:
@ -47,3 +48,52 @@ def handle_position_and_look(packet, g):
print(packet) print(packet)
p = LPoint3f(x=packet.x, y=packet.y, z=packet.z) p = LPoint3f(x=packet.x, y=packet.y, z=packet.z)
g.pos = p g.pos = p
def handle_chat(message, g):
source, text = message
reply = None
match = re.match(r'<(\w+)> (.*)', text)
if match:
sender, text = match.groups()
else:
return
if text.startswith('! '):
text = text[2:]
elif text.startswith('!'):
text = text[1:]
else:
return
if ' ' in text:
command = text.split(' ', 1)[0]
data = text.split(' ', 1)[1]
else:
command = text
if command == 'ping':
reply = 'pong'
if command == 'echo' and data:
reply = data
if command == 'respawn':
packet = serverbound.play.ClientStatusPacket()
packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN
g.connection.write_packet(packet)
reply = 'ok'
if command == 'pos':
reply = str(utils.pint(g.pos))[1:-1]
if command == 'afk':
reply = '/afk'
if command == 'error':
reply = 'ok'
raise
if reply:
print(reply)
g.chat.send(reply)

140
protocol/managers.py Normal file
View File

@ -0,0 +1,140 @@
import os
from math import floor
import json
from minecraft.networking.packets import clientbound, serverbound
from protocol import packets
class DataManager:
def __init__(self, directory):
self.blocks = {}
self.blocks_states = {}
self.blocks_properties = {}
self.registries = {}
self.biomes = {}
self.entity_type = {}
if not os.path.isdir(directory):
raise FileNotFoundError("%s is not a valid directory")
if not os.path.isfile("%s/registries.json"%(directory)):
raise FileNotFoundError("%s is not a valid minecraft data directory")
with open("%s/blocks.json"%(directory)) as f:
blocks = json.loads(f.read())
for x in blocks:
for s in blocks[x]['states']:
self.blocks_states[s['id']] = x
self.blocks_properties[s['id']] = s.get('properties', {})
with open("%s/registries.json"%(directory)) as f:
registries = json.loads(f.read())
#for x in registries["minecraft:biome"]["entries"]:
# self.biomes[registries["minecraft:biome"]["entries"][x]["protocol_id"]] = x
for x in registries["minecraft:entity_type"]["entries"]:
self.entity_type[registries["minecraft:entity_type"]["entries"][x]["protocol_id"]] = x
class ChunksManager:
def __init__(self, data_manager):
self.data = data_manager
self.chunks = {}
self.biomes = {}
def handle_block(self, block_packet):
self.set_block_at(block_packet.location.x, block_packet.location.y, block_packet.location.z, block_packet.block_state_id)
#self.print_chunk(self.get_chunk(floor(block_packet.location.x/16), floor(block_packet.location.y/16), floor(block_packet.location.z/16)), block_packet.location.y%16)
#print('Block %s at %s'%(blocks_states[block_packet.block_state_id], block_packet.location))
def handle_multiblock(self, multiblock_packet):
for b in multiblock_packet.records:
self.handle_block(b)
def handle_chunk(self, chunk_packet):
for i in chunk_packet.chunks:
self.chunks[(chunk_packet.x, i, chunk_packet.z)] = chunk_packet.chunks[i]
self.biomes[(chunk_packet.x, None, chunk_packet.z)] = chunk_packet.biomes # FIXME
def register(self, connection):
connection.register_packet_listener(self.handle_block, packets.BlockChangePacket)
connection.register_packet_listener(self.handle_multiblock, packets.MultiBlockChangePacket)
connection.register_packet_listener(self.handle_chunk, packets.ChunkDataPacket)
def get_chunk(self, x, y, z):
index = (x, y, z)
if not index in self.chunks:
raise ChunkNotLoadedException(index)
return self.chunks[index]
def get_loaded_area(self, ignore_empty=False):
first = next(iter(self.chunks.keys()))
x0 = x1 = first[0]
y0 = y1 = first[1]
z0 = z1 = first[2]
for k in self.chunks.keys():
if ignore_empty and self.chunks[k].empty:
continue
x0 = min(x0, k[0])
x1 = max(x1, k[0])
y0 = min(y0, k[1])
y1 = max(y1, k[1])
z0 = min(z0, k[2])
z1 = max(z1, k[2])
return ((x0,y0,z0),(x1,y1,z1))
def get_block_at(self, x, y, z):
c = self.get_chunk(floor(x/16), floor(y/16), floor(z/16))
return c.get_block_at(x%16, y%16, z%16)
def set_block_at(self, x, y, z, block):
c = self.get_chunk(floor(x/16), floor(y/16), floor(z/16))
c.set_block_at(x%16, y%16, z%16, block)
class ChunkNotLoadedException(Exception):
def __str__(self):
pos = self.args[0]
return "Chunk at %d %d %d not loaded (yet?)"%(pos[0], pos[1], pos[2])
class ChatManager:
def __init__(self, global_state):
self.g = global_state
self.handler = None
self.g.connection.register_packet_listener(self.print_chat, clientbound.play.ChatMessagePacket)
def translate_chat(self, data):
if isinstance(data, str):
return data
elif 'extra' in data:
return ''.join([self.translate_chat(x) for x in data['extra']])
elif 'text' in data:
return data['text']
elif 'translate' in data:
return data['translate']
else:
print(data)
return '?'
def print_chat(self, chat_packet):
try:
source = chat_packet.field_string('position')
text = self.translate_chat(json.loads(chat_packet.json_data))
print('[%s] %s'%(source, text))
except Exception as ex:
print('Exception %r on message (%s): %s' % (ex, chat_packet.field_string('position'), chat_packet.json_data))
if self.handler:
self.handler((source, text))
def set_handler(self, func):
self.handler = func
def send(self, text):
if not text:
# Prevents connection bug when sending empty chat message
return
packet = serverbound.play.ChatPacket()
packet.message = text
self.g.connection.write_packet(packet)

View File

@ -6,7 +6,120 @@ from minecraft.networking.types import (
multi_attribute_alias, Vector, UnsignedLong multi_attribute_alias, Vector, UnsignedLong
) )
from ....types.nbt import Nbt from protocol.types import Nbt
from minecraft.networking.packets import Packet
from minecraft.networking.types import (
VarInt, Integer, UnsignedByte, Position, Vector, MutableRecord,
attribute_alias, multi_attribute_alias, Long, Boolean, VarLong,
)
class BlockChangePacket(Packet):
id = 0x0B
packet_name = 'block change'
definition = [
{'location': Position},
{'block_state_id': VarInt}]
block_state_id = 0
# For protocols < 347: an accessor for (block_state_id >> 4).
@property
def blockId(self):
return self.block_state_id >> 4
@blockId.setter
def blockId(self, block_id):
self.block_state_id = (self.block_state_id & 0xF) | (block_id << 4)
# For protocols < 347: an accessor for (block_state_id & 0xF).
@property
def blockMeta(self):
return self.block_state_id & 0xF
@blockMeta.setter
def blockMeta(self, meta):
self.block_state_id = (self.block_state_id & ~0xF) | (meta & 0xF)
# This alias is retained for backward compatibility.
blockStateId = attribute_alias('block_state_id')
class MultiBlockChangePacket(Packet):
id = 0x3B
packet_name = 'multi block change'
fields = 'chunk_x', 'chunk_z', 'records'
# Access the 'chunk_x' and 'chunk_z' fields as a tuple.
chunk_pos = multi_attribute_alias(tuple, 'chunk_x', 'chunk_z')
class Record(MutableRecord):
__slots__ = 'x', 'y', 'z', 'block_state_id', 'location'
def __init__(self, **kwds):
self.block_state_id = 0
super(MultiBlockChangePacket.Record, self).__init__(**kwds)
# Access the 'x', 'y', 'z' fields as a Vector of ints.
position = multi_attribute_alias(Vector, 'x', 'y', 'z')
# For protocols < 347: an accessor for (block_state_id >> 4).
@property
def blockId(self):
return self.block_state_id >> 4
@blockId.setter
def blockId(self, block_id):
self.block_state_id = self.block_state_id & 0xF | block_id << 4
# For protocols < 347: an accessor for (block_state_id & 0xF).
@property
def blockMeta(self):
return self.block_state_id & 0xF
@blockMeta.setter
def blockMeta(self, meta):
self.block_state_id = self.block_state_id & ~0xF | meta & 0xF
# This alias is retained for backward compatibility.
blockStateId = attribute_alias('block_state_id')
def read(self, file_object, parent):
data = VarLong.read(file_object)
self.block_state_id = int(data >> 12)
self.x = int(data >> 8 & 0xf)
self.z = int(data >> 4 & 0xf)
self.y = int(data & 0xf)
# Absolute position in world to be compatible with BlockChangePacket
self.location = Vector(self.position.x + parent.chunk_x*16, self.position.y, self.position.z + parent.chunk_z*16)
def write(self, packet_buffer):
raise
UnsignedByte.send(self.x << 4 | self.z & 0xF, packet_buffer)
UnsignedByte.send(self.y, packet_buffer)
VarInt.send(self.block_state_id, packet_buffer)
def read(self, file_object):
coords = Long.read(file_object)
self.chunk_x = int(coords >> 42 & 0x3fffff)
self.chunk_z = int(coords >> 20 & 0x3fffff)
self.chunk_y = int(coords & 0xfffff)
self.unknown = Boolean.read(file_object)
array_size = VarInt.read(file_object)
self.records = []
for i in range(array_size):
record = self.Record()
record.read(file_object, self)
self.records.append(record)
def write_fields(self, packet_buffer):
raise
Integer.send(self.chunk_x, packet_buffer)
Integer.send(self.chunk_z, packet_buffer)
VarInt.send(len(self.records), packet_buffer)
for record in self.records:
record.write(packet_buffer)
class ChunkDataPacket(Packet): class ChunkDataPacket(Packet):
id = 0x20 id = 0x20

View File

@ -1,16 +1,23 @@
"""Contains definition for minecraft's NBT format.
"""
from __future__ import division from __future__ import division
from minecraft.networking.types.basic import Type, Byte, Short, Integer, Long, Float, Double, ShortPrefixedByteArray
import struct import struct
from minecraft.networking.types.utility import Vector from minecraft.networking.types.utility import Vector
from minecraft.networking.types.basic import Type, Byte, Short, Integer, Long, Float, Double, ShortPrefixedByteArray from minecraft.networking.types.basic import Type, Byte, Short, Integer, Long, Float, Double, ShortPrefixedByteArray
from .basic import IntegerPrefixedByteArray
__all__ = ( class IntegerPrefixedByteArray(Type):
'Nbt', @staticmethod
) def read(file_object):
length = Integer.read(file_object)
return struct.unpack(str(length) + "s", file_object.read(length))[0]
@staticmethod
def send(value, socket):
Integer.send(len(value), socket)
socket.send(value)
TAG_End = 0 TAG_End = 0
TAG_Byte = 1 TAG_Byte = 1
@ -26,9 +33,7 @@ TAG_Compound = 10
TAG_Int_Array = 11 TAG_Int_Array = 11
TAG_Long_Array = 12 TAG_Long_Array = 12
class Nbt(Type): class Nbt(Type):
@staticmethod @staticmethod
def read(file_object): def read(file_object):
type_id = Byte.read(file_object) type_id = Byte.read(file_object)
@ -91,5 +96,3 @@ class Nbt(Type):
def send(value, socket): def send(value, socket):
# TODO # TODO
pass pass