def update_server_address(self, ip, name='AWScraft'): ''' Updates the server named <name> with new IP address <ip> in servers.dat. Otherwise creates a new server entry named <name> with <ip> ''' server_file = os.path.join(self.data_directory, 'servers.dat') if not os.path.isfile(server_file): return False # Check Server File Exists with open(server_file, 'rb') as binary: server = nbt.NBTFile(buffer=binary) updated = False for entry in server['servers']: if entry['name'] == name: entry['ip'] = ip updated = True if not updated: new_server = nbt.TAG_Compound() new_server.tags.append(nbt.TAG_String(name='name', value=name)) new_server.tags.append(nbt.TAG_String(name='ip', value=ip)) server['servers'].append(new_server) os.remove(server_file) with open(server_file, 'wb') as new_binary: server.write_file(buffer=new_binary) return True
def toNbt(self) -> nbt.TAG_Compound: data = nbt.TAG_Compound() motion = nbt.TAG_List(name="Motion", type=nbt.TAG_Double) motion.append(nbt.TAG_Double(self.velocity[0])) motion.append(nbt.TAG_Double(self.velocity[1])) motion.append(nbt.TAG_Double(self.velocity[2])) data.tags.append(motion) pos = nbt.TAG_List(name="Pos", type=nbt.TAG_Double) pos.append(nbt.TAG_Double(self.pos[0])) pos.append(nbt.TAG_Double(self.pos[1])) pos.append(nbt.TAG_Double(self.pos[2])) data.tags.append(pos) data.tags.append( nbt.TAG_String(name="id", value=f"minecraft:{self.kind.name}")) data.tags.append(nbt.TAG_Float(name="Health", value=self.health)) data.tags.append(nbt.TAG_Byte(name="OnGround", value=self.onGround)) data.tags.append( nbt.TAG_Int(name="PortalCooldown", value=self.portalCooldown)) if self.extra is not None: extra = self.extra.toNbt() data.tags += extra.tags return data
def __init__( self, namespace: str, block_id: str = None, properties: dict = None, states: dict = None, ): """ Parameters ---------- namespace Namespace of the block. If no block_id is given, assume this is ``block_id`` and set namespace to ``"minecraft"`` block_id ID of the block properties Block properties states Block properties in simple form """ # Transform block properties from a simple dict to a dict containing # the weird NBT value TAG thingys. if states: properties = {k: nbt.TAG_String(v, k) for k, v in states.items()} if block_id is None: self.namespace = "minecraft" self.id = namespace else: self.namespace = namespace self.id = block_id self.properties = properties or {}
def toNbt(self, slotIdx=None) -> Optional[nbt.TAG_Compound]: if self.isEmpty(): return None else: result = nbt.TAG_Compound() result.tags.append(nbt.TAG_String(f'minecraft:{self.item}', 'id')) result.tags.append(nbt.TAG_Byte(self.amount, 'Count')) if slotIdx is not None: result.tags.append(nbt.TAG_Byte(slotIdx, 'Slot')) return result
def create_chest_block_entity(self, chest_x, chest_y, chest_z, b_type, amount): items = nbt.TAG_List(name="Items", type=nbt.TAG_Compound) # TODO make this variable by using the config file stacks = min(amount // 64, 27) remainder = amount % 64 if stacks < 27 else 0 for i in range(stacks): chest_entry = nbt.TAG_Compound() chest_entry.tags.extend([ nbt.TAG_Byte(name="Count", value=64), nbt.TAG_Byte(name="Slot", value=i), nbt.TAG_String(name="id", value=b_type), ]) items.tags.append(chest_entry) if stacks < 27: chest_entry = nbt.TAG_Compound() chest_entry.tags.extend([ nbt.TAG_Byte(name="Count", value=amount % 64), nbt.TAG_Byte(name="Slot", value=stacks), nbt.TAG_String(name="id", value=b_type), ]) items.tags.append(chest_entry) block_entity = nbt.TAG_Compound() block_entity.tags.extend([ nbt.TAG_String(name="id", value="minecraft:chest"), nbt.TAG_Int(name="x", value=chest_x), nbt.TAG_Int(name="y", value=chest_y), nbt.TAG_Int(name="z", value=chest_z), nbt.TAG_Byte(name="keepPacked", value=0), ]) block_entity.tags.append(items) new_amount = amount - (stacks * 64 + remainder) return block_entity, new_amount
def _as_schematic(self): nbtfile = nbt.NBTFile() nbtfile.name = "Schematic" nbtfile.tags.append(nbt.TAG_Short(name="Height", value=self.height)) nbtfile.tags.append(nbt.TAG_Short(name="Width", value=self.width)) nbtfile.tags.append(nbt.TAG_Short(name="Length", value=self.depth)) nbtfile.tags.append(nbt.TAG_Int(name="WEOffsetX", value=-1)) nbtfile.tags.append(nbt.TAG_Int(name="WEOffsetY", value=0)) nbtfile.tags.append(nbt.TAG_Int(name="WEOffsetZ", value=-1)) nbtfile.tags.append(nbt.TAG_Int(name="WEOriginX", value=0)) nbtfile.tags.append(nbt.TAG_Int(name="WEOriginY", value=0)) nbtfile.tags.append(nbt.TAG_Int(name="WEOriginZ", value=0)) # YZX ordering data = bytearray() blocks = bytearray() for y in range(self.height): for z in range(self.depth): for x in range(self.width): block_id, block_data = self.reader.get(x, y, z) blocks.append(block_id) data.append(block_data) blocks_tag = nbt.TAG_Byte_Array() blocks_tag.value = blocks data_tag = nbt.TAG_Byte_Array() data_tag.value = data nbtfile["Blocks"] = blocks_tag nbtfile["Data"] = data_tag nbtfile.tags.append(nbt.TAG_String(name="Materials", value=u"Alpha")) nbtfile["Entities"] = nbt.TAG_List(type=nbt.TAG_Compound) nbtfile["TileEntities"] = nbt.TAG_List(type=nbt.TAG_Compound) output = BytesIO() nbtfile.write_file(fileobj=output) as_nbt = output.getvalue() output.close() return as_nbt
def save(self) -> nbt.NBTFile: """ Saves the chunk data to a :class:`NBTFile` Notes ----- Does not contain most data a regular chunk would have, but minecraft stills accept it. """ root = nbt.NBTFile() root.tags.append(nbt.TAG_Int(name='DataVersion', value=self.version)) level = nbt.TAG_Compound() # Needs to be in a separate line because it just gets # ignored if you pass it as a kwarg in the constructor level.name = 'Level' level.tags.extend([ nbt.TAG_List(name='Entities', type=nbt.TAG_Compound), nbt.TAG_List(name='TileEntities', type=nbt.TAG_Compound), nbt.TAG_List(name='LiquidTicks', type=nbt.TAG_Compound), nbt.TAG_Int(name='xPos', value=self.x), nbt.TAG_Int(name='zPos', value=self.z), nbt.TAG_Long(name='LastUpdate', value=0), nbt.TAG_Long(name='InhabitedTime', value=0), nbt.TAG_Byte(name='isLightOn', value=1), nbt.TAG_String(name='Status', value='full') ]) sections = nbt.TAG_List(name='Sections', type=nbt.TAG_Compound) for s in self.sections: if s: p = s.palette() # Minecraft does not save sections that are just air # So we can just skip them if len(p) == 1 and p[0].name() == 'minecraft:air': continue sections.tags.append(s.save()) level.tags.append(sections) root.tags.append(level) return root
def toNbt(self) -> nbt.TAG_Compound: tag = super().toNbt() inventory = nbt.TAG_List(type=nbt.TAG_Compound, name='Inventory') for (slotIdx, item) in enumerate(self.inventory): stackTag = item.stack.toNbt(slotIdx) if stackTag is not None: inventory.append(stackTag) tag.tags.append(inventory) gameMode = 1 if self.creative else 0 tag.tags.append(nbt.TAG_Int(gameMode, 'playerGameType')) abilities = nbt.TAG_Compound() abilities.name = 'abilities' abilities.tags.append(nbt.TAG_Byte(int(self.flying), 'flying')) tag.tags.append(abilities) tag.tags.append(nbt.TAG_String(self.dimension, 'dimension')) return tag
start='[', end=']') STR_Compound = matchDict(STR_String, STR_TAG) STR_TAG << (STR_Number | STR_String | STR_Byte_Array | STR_Int_Array | STR_Long_Array | STR_List | STR_Compound) # ---------------------------------------------------------------------- STR_Byte.addParseAction(lambda t: nbt.TAG_Byte(t[0])) STR_Short.addParseAction(lambda t: nbt.TAG_Short(t[0])) STR_Int.addParseAction(lambda t: nbt.TAG_Int(t[0])) STR_Long.addParseAction(lambda t: nbt.TAG_Long(t[0])) STR_Float.addParseAction(lambda t: nbt.TAG_Float(t[0])) STR_Double.addParseAction(lambda t: nbt.TAG_Double(t[0])) STR_String.addParseAction(lambda t: nbt.TAG_String(t[0])) def make_tag_array(vals, tag_type, value_constructor=list): tag = tag_type() tag.value = value_constructor(vals) return tag STR_Byte_Array.addParseAction(lambda t: make_tag_array( t[0], nbt.TAG_Byte_Array, value_constructor=bytearray)) STR_Int_Array.addParseAction(lambda t: make_tag_array(t[0], nbt.TAG_Int_Array)) STR_Long_Array.addParseAction( lambda t: make_tag_array(t[0], nbt.TAG_Long_Array))
return template.default_value return NBTListEntry() else: return template.create_entry_from_template() template = loader.load_template(entity_id) entry = NBTCompoundEntry(convert_template(template)) return entry if __name__ == "__main__": print(create_entry_from_nbt(nbt.TAG_Byte(value=4))) compound = nbt.TAG_Compound() compound["test1"] = nbt.TAG_Int(value=-100) compound["test2"] = nbt.TAG_String(value="hello!") test1 = create_entry_from_nbt(compound) print(test1) print(create_nbt_from_entry(test1)) print("=" * 16) test2 = create_entry_from_nbt( nbt.TAG_List(value=[ nbt.TAG_String(value="test1"), nbt.TAG_String(value="test2"), nbt.TAG_String(value="test3"), ])) print(test2) print(create_nbt_from_entry(test2))
def save_chunk(self, data) -> nbt.NBTFile: """ Saves the chunk data to a :class:`NBTFile` Notes ----- Does not contain most data a regular chunk would have, but minecraft stills accept it. """ root = nbt.NBTFile() root.tags.append(nbt.TAG_Int(name="DataVersion", value=self.version)) level = nbt.TAG_Compound() # Needs to be in a separate line because it just gets # ignored if you pass it as a kwarg in the constructor level.name = "Level" if data: if data.get("Biomes") is not None: level.tags.append(data["Biomes"]) if data.get("Heightmaps") is not None: level.tags.append(data["Heightmaps"]) # level.tags.append(data["CarvingMasks"]) if data.get("Entities") is not None: level.tags.append(data["Entities"]) if data.get("TileEntities") is not None: level.tags.append(data["TileEntities"]) # if data.get("TileTicks") is not None: # level.tags.append(data["TileTicks"]) if data.get("LiquidTicks") is not None: level.tags.append(data["LiquidTicks"]) ######## if data.get("Lights") is not None: level.tags.append(data["Lights"]) if data.get("LiquidsToBeTicked") is not None: level.tags.append(data["LiquidsToBeTicked"]) if data.get("ToBeTicked") is not None: level.tags.append(data["ToBeTicked"]) if data.get("CarvingMasks") is not None: level.tags.append(data["CarvingMasks"]) ########## if data.get("PostProcessing") is not None: level.tags.append(data["PostProcessing"]) if data.get("Structures") is not None: level.tags.append(data["Structures"]) level.tags.extend([ # nbt.TAG_List(name="Entities", type=nbt.TAG_Compound), # nbt.TAG_List(name="TileEntities", type=nbt.TAG_Compound), # nbt.TAG_List(name="LiquidTicks", type=nbt.TAG_Compound), nbt.TAG_Int(name="xPos", value=self.x), nbt.TAG_Int(name="zPos", value=self.z), # nbt.TAG_Long(name="LastUpdate", value=data["LastUpdate"]), nbt.TAG_Long(name="LastUpdate", value=0), # nbt.TAG_Long(name="InhabitedTime", value=data["InhabitedTime"]), nbt.TAG_Long(name="InhabitedTime", value=0), nbt.TAG_Byte(name="isLightOn", value=1), nbt.TAG_String(name="Status", value="full"), ]) # entities = self.add_entities(data["Entities"]) # level.tags.append(entities) # nbt.TAG_List(name="Entities", type=nbt.TAG_Compound) else: level.tags.extend([ # nbt.TAG_List(name="Entities", type=nbt.TAG_Compound), nbt.TAG_List(name="TileEntities", type=nbt.TAG_Compound), nbt.TAG_List(name="LiquidTicks", type=nbt.TAG_Compound), nbt.TAG_Int(name="xPos", value=self.x), nbt.TAG_Int(name="zPos", value=self.z), nbt.TAG_Long(name="LastUpdate", value=0), nbt.TAG_Long(name="InhabitedTime", value=0), nbt.TAG_Byte(name="isLightOn", value=1), nbt.TAG_String(name="Status", value="full"), ]) sections = nbt.TAG_List(name="Sections", type=nbt.TAG_Compound) for s in self.sections: if s: p = s.palette() # Minecraft does not save sections that are just air # So we can just skip them if len(p) == 1 and p[0].name() == "minecraft:air": continue sections.tags.append(s.save()) level.tags.append(sections) root.tags.append(level) return root
def convert(path): if not os.path.isdir(path): sys.stderr.write('Path is not directory or does not exist:' + path) return False max_payload_size = 50 retry_delay = 3 profile_url = 'https://api.mojang.com/profiles/minecraft' #profile_url = 'http://api.goender.net/api/profiles/minecraft' player_data_path = os.path.join(path, 'players') target_data_path = os.path.join(path, 'playerdata') missing_data_path = os.path.join(path, 'players.missing') converted_data_path = os.path.join(path, 'players.converted') invalid_data_path = os.path.join(path, 'players.invalid') player_files = {} if not os.path.isdir(missing_data_path): os.mkdir(missing_data_path) if not os.path.isdir(converted_data_path): os.mkdir(converted_data_path) if not os.path.isdir(invalid_data_path): os.mkdir(invalid_data_path) for player_file in os.listdir(player_data_path): if os.path.isfile( os.path.join(player_data_path, player_file)) and player_file.endswith('.dat'): name = os.path.splitext(os.path.basename(player_file))[0].lower() player_files[name] = os.path.join(player_data_path, player_file) if not player_files: sys.stderr.write('No player data found!\n') return False if not os.path.isdir(target_data_path): os.mkdir(target_data_path) payload = [] current = 0 for name in player_files.keys(): current = current + 1 payload.append(name) if (float(current) % max_payload_size) != 0 and current != len(player_files): continue request = urllib2.Request(profile_url, json.dumps(payload), {'Content-Type': 'application/json'}) retry = False retry_count = 0 while True: try: response = urllib2.urlopen(request) if retry: sys.stderr.write('Retry successful! Number of retries: ' + str(retry_count) + '\n') retry = False retry_count = 0 break except Exception as e: sys.stderr.write( str(e) + " (don't worry, we'll retry until it works!)\n") retry = True time.sleep(retry_delay) retry_count = retry_count + 1 profiles = json.loads(response.read()) if isinstance(profiles, dict): # http://api.goender.net/api/profiles/minecraft data = profiles profiles = [] for name in data.keys(): profiles.append({'id': data[name], 'name': name}) if len(profiles) != len(payload): payload_names = set([name.lower() for name in payload]) response_names = set( [profile['name'].lower() for profile in profiles]) missing_names = list(payload_names - response_names) for name in missing_names: try: src = player_files[name] shutil.move( src, os.path.join(missing_data_path, os.path.basename(src))) except Exception as e: sys.stderr.write('Error moving file file: ' + src + ' (' + str(e) + ')\n') sys.stderr.write('Missing profiles from API response: ' + repr(missing_names) + '\n') payload = [] for profile in profiles: name = profile['name'].lower() if name not in player_files: continue src = player_files[name] dst = os.path.join(target_data_path, str(uuid.UUID(profile['id'])) + '.dat') try: nbtfile = nbt.NBTFile(src, 'rb') except Exception as e: sys.stderr.write('Error reading NBT file: ' + src + ' (' + str(e) + ')\n') try: shutil.move( src, os.path.join(invalid_data_path, os.path.basename(src))) except: pass continue try: bukkit = nbtfile['bukkit'] except KeyError: bukkit = nbt.TAG_Compound() bukkit.name = 'bukkit' nbtfile.tags.append(bukkit) try: lastKnownName = bukkit['lastKnownName'] except KeyError: lastKnownName = nbt.TAG_String(name='lastKnownName') bukkit.tags.append(lastKnownName) lastKnownName.value = profile['name'] nbtfile.write_file(dst) try: shutil.move( src, os.path.join(converted_data_path, os.path.basename(src))) except Exception as e: sys.stderr.write('Error moving file file: ' + src + ' (' + str(e) + ')\n') return True