Advance world clock between updates

This commit is contained in:
Tanner
2026-11-07 22:14:46 +00:00
parent a16b297db7
commit 9cbadbd551
4 changed files with 59 additions and 4 deletions
+6
View File
@@ -420,6 +420,9 @@ def start_automation(global_state, initialize=True):
g.protocol_version = 777 g.protocol_version = 777
if initialize: if initialize:
g.time = 0.0 g.time = 0.0
g.time_base = None
g.time_update_received_at = None
g.clock_rate = 1.0
g.path = [] g.path = []
g.look_at = None g.look_at = None
g.y_v = 0.0 g.y_v = 0.0
@@ -505,6 +508,9 @@ def start_automation(global_state, initialize=True):
g.get('connection') and g.connection.connected): g.get('connection') and g.connection.connected):
started = time.monotonic() started = time.monotonic()
now = time.monotonic() now = time.monotonic()
game = g.get('game')
if game is not None:
game._advance_world_time()
if not initial_world_ready: if not initial_world_ready:
if not bot.missing_movement_chunks(g): if not bot.missing_movement_chunks(g):
if loaded_since is None: if loaded_since is None:
+1 -1
View File
@@ -221,7 +221,7 @@ class Commands:
seconds = self.g.time * 3.6 seconds = self.g.time * 3.6
start = datetime(2000, 1, 1, hour=6) start = datetime(2000, 1, 1, hour=6)
mctime = start + timedelta(seconds=seconds) mctime = start + timedelta(seconds=seconds)
reply = str(self.g.time) + ' - ' + mctime.strftime('%I:%M %p') reply = str(int(self.g.time)) + ' - ' + mctime.strftime('%I:%M %p')
## !count [id] - counts the number of items with that id ## !count [id] - counts the number of items with that id
if command == 'count' and data: if command == 'count' and data:
+24 -3
View File
@@ -179,9 +179,24 @@ class LegacyGame:
self.g.holding = (item.item_id if item and self.g.holding = (item.item_id if item and
getattr(item, 'present', False) else 0) getattr(item, 'present', False) else 0)
def _set_world_clock(self, total_ticks, partial_tick=0.0, rate=1.0):
self.g.time_base = float(total_ticks) + float(partial_tick)
self.g.clock_rate = float(rate)
self.g.time_update_received_at = time.monotonic()
self._advance_world_time()
def _advance_world_time(self):
received_at = self.g.get('time_update_received_at')
time_base = self.g.get('time_base')
if received_at is None or time_base is None:
return
elapsed_ticks = max(0.0, time.monotonic() - received_at) / utils.TICK
self.g.time = (time_base + elapsed_ticks *
self.g.get('clock_rate', 1.0)) % 24000
def handle_time_update(self, packet): def handle_time_update(self, packet):
if hasattr(packet, 'time_of_day'): if hasattr(packet, 'time_of_day'):
self.g.time = packet.time_of_day % 24000 self._set_world_clock(packet.time_of_day)
return return
updates = getattr(packet, 'clock_updates', ()) updates = getattr(packet, 'clock_updates', ())
@@ -201,11 +216,12 @@ class LegacyGame:
'minecraft:') in (dimension, 'overworld') 'minecraft:') in (dimension, 'overworld')
] ]
update = preferred[0] if preferred else updates[0] update = preferred[0] if preferred else updates[0]
self.g.time = update['total_ticks'] % 24000
self.g.clock_id = update['clock_id'] self.g.clock_id = update['clock_id']
self.g.clock_name = clock_names.get(update['clock_id']) self.g.clock_name = clock_names.get(update['clock_id'])
self.g.clock_rate = update['rate']
self.g.world_age = packet.age self.g.world_age = packet.age
self._set_world_clock(update['total_ticks'],
update.get('partial_tick', 0.0),
update.get('rate', 1.0))
def handle_set_slot(self, packet): def handle_set_slot(self, packet):
g = self.g g = self.g
@@ -700,6 +716,10 @@ class Game(LegacyGame):
self.g.connection.register_packet_listener( self.g.connection.register_packet_listener(
self.handle_set_held_slot, SetHeldSlotPacket) self.handle_set_held_slot, SetHeldSlotPacket)
self.g.setdefault('time_base', None)
self.g.setdefault('time_update_received_at', None)
self.g.setdefault('clock_rate', 1.0)
if not self.g.get('health_update_received', False): if not self.g.get('health_update_received', False):
self.g.health = None self.g.health = None
self.g.food = None self.g.food = None
@@ -1222,6 +1242,7 @@ class Game(LegacyGame):
self.g.drain_dropped), flush=True) self.g.drain_dropped), flush=True)
def tick(self): def tick(self):
self._advance_world_time()
self._tick_single_drop() self._tick_single_drop()
if self.g.get('dumping') is not None: if self.g.get('dumping') is not None:
self._tick_dump() self._tick_dump()
+28
View File
@@ -1,6 +1,7 @@
import io import io
import unittest import unittest
from contextlib import redirect_stdout from contextlib import redirect_stdout
from unittest.mock import patch
from munch import Munch from munch import Munch
from minecraft.networking.packets import PacketBuffer from minecraft.networking.packets import PacketBuffer
@@ -96,6 +97,33 @@ class TestSleepWithBed(unittest.TestCase):
'partial_tick': 0.5, 'rate': 1.0, 'partial_tick': 0.5, 'rate': 1.0,
}]) }])
def test_game_advances_world_clock_between_server_updates(self):
game = Game.__new__(Game)
game.g = Munch(
time_base=None, time_update_received_at=None, clock_rate=1.0,
time=0.0,
connection=Munch(registry_ids={'minecraft:world_clock': {
'minecraft:the_end': 1,
'minecraft:overworld': 0,
}}),
dimension='overworld')
packet = Munch(age=900000, clock_updates=[
{'clock_id': 1, 'total_ticks': 6000,
'partial_tick': 0.0, 'rate': 1.0},
{'clock_id': 0, 'total_ticks': 13000,
'partial_tick': 0.5, 'rate': 1.0},
])
with patch('mosfet.game.time.monotonic', return_value=100.0):
game.handle_time_update(packet)
self.assertEqual(game.g.time, 13000.5)
self.assertEqual(game.g.clock_name, 'minecraft:overworld')
with patch('mosfet.game.time.monotonic', return_value=100.5):
game._advance_world_time()
self.assertEqual(game.g.time, 13010.5)
def test_player_command_encodes_stop_sleeping(self): def test_player_command_encodes_stop_sleeping(self):
packet = PlayerCommandPacket( packet = PlayerCommandPacket(
entity_id=7, action_id=0, jump_boost=0) entity_id=7, action_id=0, jump_boost=0)