Handle loading / unloading chunks better

This commit is contained in:
Tanner Collin 2021-05-07 00:14:25 +00:00
parent 5184d4a173
commit e5fd062a4a
6 changed files with 48 additions and 38 deletions

View File

@ -65,21 +65,19 @@ def tick(global_state):
target = None target = None
# make sure current chunks are loaded for physics # make sure current chunks are loaded for physics
if not g.chunks.check_loaded(p, 288): if not g.chunks.check_loaded(g.info.render_distance):
if not g.chunks.loading: if not g.chunks.loading:
print('Loading chunks', end='', flush=True) print('Loading chunks', end='', flush=True)
g.chunks.loading = True g.chunks.loading = time.time()
packet = serverbound.play.PositionAndLookPacket(x=p.x, feet_y=p.y, z=p.z, pitch=0, yaw=0, on_ground=True) packet = serverbound.play.PositionAndLookPacket(x=p.x, feet_y=p.y, z=p.z, pitch=0, yaw=0, on_ground=True)
g.connection.write_packet(packet, force=True) g.connection.write_packet(packet, force=True)
return return
else: else:
if g.chunks.loading: if g.chunks.loading:
print() print()
print('Chunks loaded.') print('Chunks loaded in', round(time.time() - g.chunks.loading, 2), 's')
g.chunks.loading = False g.chunks.loading = False
g.chunks.unload_chunks(p)
########## object physics ########## ########## object physics ##########
# note: it's possible the chunk data is out of date when this runs # note: it's possible the chunk data is out of date when this runs

View File

@ -149,7 +149,9 @@ class Commands:
## !loaded - replies with the current loaded area ## !loaded - replies with the current loaded area
if command == 'loaded': if command == 'loaded':
reply = str(self.g.chunks.get_loaded_area()) l = self.g.chunks.get_loaded_area()
reply = '{}, {}, {} to {}, {}, {}'.format(*l[0], *l[1])
reply += ' - ' + str(len(self.g.chunks.chunks)//16) + ' chunks'
## !players - prints the current players ## !players - prints the current players
## !players clear - clears the current player list ## !players clear - clears the current player list

View File

@ -15,7 +15,7 @@ from mosfet.protocol.packets import (
ClientWindowConfirmationPacket, EntityMetadataPacket, ClientWindowConfirmationPacket, EntityMetadataPacket,
SpawnLivingEntityPacket, EntityPositionRotationPacket, DestroyEntitiesPacket, SpawnLivingEntityPacket, EntityPositionRotationPacket, DestroyEntitiesPacket,
EntityActionPacket, EntityTeleport, InteractEntityPacket, TradeListPacket, EntityActionPacket, EntityTeleport, InteractEntityPacket, TradeListPacket,
SelectTradePacket, DisconnectPacket, SelectTradePacket, DisconnectPacket, UnloadChunkPacket,
) )
from mosfet.protocol.types import Slot from mosfet.protocol.types import Slot
@ -58,6 +58,7 @@ class Game:
#register(self.handle_entity_velocity, clientbound.play.EntityVelocityPacket) #register(self.handle_entity_velocity, clientbound.play.EntityVelocityPacket)
register(self.handle_trade_list, TradeListPacket) register(self.handle_trade_list, TradeListPacket)
register(self.handle_disconnect, DisconnectPacket) register(self.handle_disconnect, DisconnectPacket)
register(self.handle_unload_chunk, UnloadChunkPacket)
#register(self.handle_packet, Packet, early=True) #register(self.handle_packet, Packet, early=True)
@ -486,6 +487,7 @@ class Game:
def handle_respawn(self, packet): def handle_respawn(self, packet):
print(packet) print(packet)
self.g.dimension = packet.world_name.replace('minecraft:', '') self.g.dimension = packet.world_name.replace('minecraft:', '')
self.g.chunks.unload_all_chunks()
def handle_player_list(self, packet): def handle_player_list(self, packet):
for action in packet.actions: for action in packet.actions:
@ -532,6 +534,9 @@ class Game:
import os import os
os._exit(1) os._exit(1)
def handle_unload_chunk(self, packet):
self.g.chunks.unload_chunk(packet.chunk_x, packet.chunk_z)
def tick(self): def tick(self):
if self.g.breaking: if self.g.breaking:
self.animate() self.animate()

View File

@ -20,6 +20,7 @@ def get_packets(old_get_packets):
mc_packets.add(packets.EntityTeleport) mc_packets.add(packets.EntityTeleport)
mc_packets.add(packets.TradeListPacket) mc_packets.add(packets.TradeListPacket)
mc_packets.add(packets.DisconnectPacket) mc_packets.add(packets.DisconnectPacket)
mc_packets.add(packets.UnloadChunkPacket)
return mc_packets return mc_packets

View File

@ -70,7 +70,6 @@ class ChunksManager:
for item_id, locations in chunk.sub_index.items(): for item_id, locations in chunk.sub_index.items():
if item_id not in self.index: if item_id not in self.index:
self.index[item_id] = [] self.index[item_id] = []
for l in locations: for l in locations:
coords = (dx + l%16, dy + l//256, dz + l%256//16) coords = (dx + l%16, dy + l//256, dz + l%256//16)
self.index[item_id].append(coords) self.index[item_id].append(coords)
@ -122,28 +121,21 @@ class ChunksManager:
self.index[block] = [] self.index[block] = []
self.index[block].append((x, y, z)) self.index[block].append((x, y, z))
def check_loaded(self, position, steps=1): def check_loaded(self, chunk_distance):
x, y, z = utils.pint(position) num = (chunk_distance * 2 + 1) ** 2
player_chunk = (x//16, 1, z//16) num_subchunks = num * 16
for i in range(steps): # TODO: base off render_distance? return len(self.chunks) >= num_subchunks
offset = utils.spiral(i)
check = utils.padd(player_chunk, offset)
if check not in self.chunks: def unload_chunk(self, x, z):
return False for y in range(16):
try:
return True del self.chunks[(x, y, z)]
except KeyError:
def unload_chunks(self, position): pass
x, y, z = utils.pint(position)
player_chunk = (x//16, 0, z//16)
loaded_chunks = list(self.chunks.keys())
for chunk in loaded_chunks:
check = (chunk[0], 0, chunk[2])
if utils.phyp_king(player_chunk, check) > 20:
del self.chunks[chunk]
def unload_all_chunks(self):
self.chunks = {}
self.index = {}
class ChunkNotLoadedException(Exception): class ChunkNotLoadedException(Exception):
def __str__(self): def __str__(self):

View File

@ -31,10 +31,10 @@ class ChunkDataPacket(Packet):
self.biomes.append(VarInt.read(file_object)) self.biomes.append(VarInt.read(file_object))
size = VarInt.read(file_object) size = VarInt.read(file_object)
self.data = file_object.read(size) self.data = file_object.read(size)
size_entities = VarInt.read(file_object) #size_entities = VarInt.read(file_object)
self.entities = [] #self.entities = []
for i in range(size_entities): #for i in range(size_entities):
self.entities.append(Nbt.read(file_object)) # self.entities.append(Nbt.read(file_object))
self.decode_chunk_data() self.decode_chunk_data()
@ -49,9 +49,9 @@ class ChunkDataPacket(Packet):
Integer.send(self.biomes[i], packet_buffer) Integer.send(self.biomes[i], packet_buffer)
VarInt.send(len(self.data), packet_buffer) VarInt.send(len(self.data), packet_buffer)
packet_buffer.send(self.data) packet_buffer.send(self.data)
VarInt.send(len(self.entities), packet_buffer) #VarInt.send(len(self.entities), packet_buffer)
for e in self.entities: #for e in self.entities:
Nbt.send(e, packet_buffer) # Nbt.send(e, packet_buffer)
def decode_chunk_data(self): def decode_chunk_data(self):
packet_data = PacketBuffer() packet_data = PacketBuffer()
@ -64,9 +64,9 @@ class ChunkDataPacket(Packet):
if self.bit_mask_y & (1 << i): if self.bit_mask_y & (1 << i):
self.chunks[i].read(packet_data) self.chunks[i].read(packet_data)
for e in self.entities: #for e in self.entities:
y = e['y'] # y = e['y']
self.chunks[floor(y/16)].entities.append(e) # self.chunks[floor(y/16)].entities.append(e)
class Chunk: class Chunk:
position = multi_attribute_alias(Vector, 'x', 'y', 'z') position = multi_attribute_alias(Vector, 'x', 'y', 'z')
@ -458,3 +458,15 @@ class DisconnectPacket(Packet):
definition = [ definition = [
{'reason': String}, {'reason': String},
] ]
class UnloadChunkPacket(Packet):
# Tells the client to unload a chunk column
# https://wiki.vg/Protocol#Unload_Chunk
id = 0x1C
packet_name = 'unload chunk'
definition = [
{'chunk_x': Integer},
{'chunk_z': Integer},
]