mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
wire stream: compare a click's state id as its lag behind the server; hotbar_swap swings at the sky
A run's state ids count every change its server made since the login, so a click numbered 53 in one run and 55 in the other were both up to date. hotbar_swap now also taps a number key with a swing at nothing, where vanilla sends the slot only on the next tick. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
a14ca6acf4
commit
b5ed12bc33
3 files changed
+37
-6
No files matched your search
@@ -72,6 +72,21 @@ class WireStreamTest(unittest.TestCase):
|
||||
self.assertEqual(wire_stream.decode('player_action', varint(0) + as_long(29999999, 2047, -30000000)
|
||||
+ b'\1' + varint(3))['pos'], [29999999, 2047, -30000000])
|
||||
|
||||
def test_a_click_s_state_id_compares_as_how_far_it_lags_the_server(self):
|
||||
def set_slot(state):
|
||||
return {'conn': 1, 'dir': 's2c', 'state': 'play', 'name': 'container_set_slot',
|
||||
'data': (varint(0) + varint(state) + b'\0\x24\0').hex()}
|
||||
|
||||
def click(state):
|
||||
return c2s('container_click', varint(0) + varint(state) + b'\0\x24\0' + varint(0) + b'\0\0')
|
||||
# Two runs whose servers counted differently: both clicks are up to date, the third is one behind.
|
||||
a = wire_stream.scenarios([LOGIN, set_slot(53), mark(), click(53), TICK_END])
|
||||
b = wire_stream.scenarios([LOGIN, set_slot(55), mark(), click(55), TICK_END, set_slot(56), click(55),
|
||||
TICK_END])
|
||||
self.assertEqual(a[0][0]['packets'], b[0][0]['packets'])
|
||||
self.assertEqual(a[0][0]['packets'][0][1]['stale'], 0)
|
||||
self.assertEqual(b[0][1]['packets'][0][1]['stale'], 1)
|
||||
|
||||
def test_compare_reports_the_first_field_that_differs(self):
|
||||
tick = lambda y: {'packets': [('move_player_pos', {'x': 0.5, 'y': y})], 'frame': [], 'misplaced': []} # noqa
|
||||
diffs = wire_stream.compare([tick(-60.0)], [tick(-60.0000001)], 0)
|
||||
|
||||
@@ -151,9 +151,12 @@ SCENARIOS = {
|
||||
setup=[TARGET.format(x=0.5, y=FLOOR + 1, z=3.0)] +
|
||||
[f'item replace entity {PLAYER} hotbar.{i} with minecraft:{item}' for i, item in
|
||||
enumerate(['diamond_sword', 'stone_axe', 'golden_apple', 'stone', 'bow'])],
|
||||
ticks=50, steps=merge({0: ['look 0 20']}, {t: [f'key {t % 5 + 1} tap'] for t in range(5, 15)},
|
||||
# 52-54: a swing at the sky with a new slot sends no slot before its Punch (no ensureHasSentCarriedItem
|
||||
# on a miss); the next tick's MultiPlayerGameMode.tick does.
|
||||
ticks=60, steps=merge({0: ['look 0 20']}, {t: [f'key {t % 5 + 1} tap'] for t in range(5, 15)},
|
||||
{16: ['key 2 tap', 'mouse left click'], 20: ['key 1 tap'], 21: ['key 3 tap'],
|
||||
30: ['key 5 tap', 'mouse left click'], 35: ['key q tap'], 40: ['key f tap']})),
|
||||
30: ['key 5 tap', 'mouse left click'], 35: ['key q tap'], 40: ['key f tap'],
|
||||
52: ['look 0 -80'], 54: ['key 3 tap', 'mouse left click']})),
|
||||
'dig_teleport': dict(
|
||||
setup=[f'setblock 0 {FLOOR + 1} 2 minecraft:stone'],
|
||||
ticks=60, steps={0: ['look 0 45'], 3: ['mouse left down'], 15: ['command tp @s ~ ~ ~'],
|
||||
|
||||
+17
-4
@@ -130,13 +130,26 @@ def self_id(entries):
|
||||
|
||||
def scenarios(entries):
|
||||
"""One list of ticks for each /wire-mark in the play stream, in order (the ticks after a mark run to the
|
||||
next mark); a tick is {'packets': [(name, fields)], 'frame': [(name, fields)], 'misplaced': [name]}."""
|
||||
next mark); a tick is {'packets': [(name, fields)], 'frame': [(name, fields)], 'misplaced': [name]}.
|
||||
|
||||
A click's state id becomes `stale`: how far it is behind the last one the server sent for that container
|
||||
(0 = up to date). The ids themselves count every change the server made since the login, so two runs
|
||||
whose earlier scenarios differed number the same click differently."""
|
||||
me = self_id(entries)
|
||||
c2s = [e for e in entries if e.get('dir') == 'c2s' and e.get('state') == 'play' and 'name' in e]
|
||||
runs, current = [], None
|
||||
for e in c2s:
|
||||
play = [e for e in entries if e.get('state') == 'play' and 'name' in e and e.get('dir') in ('c2s', 's2c')]
|
||||
runs, current, server_state = [], None, {}
|
||||
for e in play:
|
||||
name = e['name']
|
||||
if e['dir'] == 's2c':
|
||||
if name in ('container_set_content', 'container_set_slot'):
|
||||
r = Reader(bytes.fromhex(e.get('data', '')))
|
||||
container = r.varint()
|
||||
server_state[container] = r.varint()
|
||||
continue
|
||||
fields = decode(name, bytes.fromhex(e.get('data', '')))
|
||||
if name == 'container_click':
|
||||
last = server_state.get(fields['container'], fields['state'])
|
||||
fields = {'container': fields['container'], 'stale': last - fields.pop('state'), **fields}
|
||||
if name == 'chat_command' and fields.get('command') == 'wire-mark':
|
||||
current = [new_tick()]
|
||||
runs.append(current)
|
||||
|
||||
Reference in new issue
Block a user