diff --git a/mosfet/bot.py b/mosfet/bot.py index c298c14..0fe6483 100644 --- a/mosfet/bot.py +++ b/mosfet/bot.py @@ -420,6 +420,9 @@ def start_automation(global_state, initialize=True): g.protocol_version = 777 if initialize: g.time = 0.0 + g.time_base = None + g.time_update_received_at = None + g.clock_rate = 1.0 g.path = [] g.look_at = None g.y_v = 0.0 @@ -505,6 +508,9 @@ def start_automation(global_state, initialize=True): g.get('connection') and g.connection.connected): started = 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 bot.missing_movement_chunks(g): if loaded_since is None: diff --git a/mosfet/commands.py b/mosfet/commands.py index ea34c85..f26dd41 100644 --- a/mosfet/commands.py +++ b/mosfet/commands.py @@ -221,7 +221,7 @@ class Commands: seconds = self.g.time * 3.6 start = datetime(2000, 1, 1, hour=6) 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 if command == 'count' and data: diff --git a/mosfet/game.py b/mosfet/game.py index f8cb8e5..60b99ea 100644 --- a/mosfet/game.py +++ b/mosfet/game.py @@ -179,9 +179,24 @@ class LegacyGame: self.g.holding = (item.item_id if item and 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): if hasattr(packet, 'time_of_day'): - self.g.time = packet.time_of_day % 24000 + self._set_world_clock(packet.time_of_day) return updates = getattr(packet, 'clock_updates', ()) @@ -201,11 +216,12 @@ class LegacyGame: 'minecraft:') in (dimension, 'overworld') ] 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_name = clock_names.get(update['clock_id']) - self.g.clock_rate = update['rate'] 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): g = self.g @@ -700,6 +716,10 @@ class Game(LegacyGame): self.g.connection.register_packet_listener( 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): self.g.health = None self.g.food = None @@ -1222,6 +1242,7 @@ class Game(LegacyGame): self.g.drain_dropped), flush=True) def tick(self): + self._advance_world_time() self._tick_single_drop() if self.g.get('dumping') is not None: self._tick_dump() diff --git a/tests/test_sleep_with_bed.py b/tests/test_sleep_with_bed.py index c935e53..7ebbe21 100644 --- a/tests/test_sleep_with_bed.py +++ b/tests/test_sleep_with_bed.py @@ -1,6 +1,7 @@ import io import unittest from contextlib import redirect_stdout +from unittest.mock import patch from munch import Munch from minecraft.networking.packets import PacketBuffer @@ -96,6 +97,33 @@ class TestSleepWithBed(unittest.TestCase): '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): packet = PlayerCommandPacket( entity_id=7, action_id=0, jump_boost=0)