tools: one copy of the helpers harnesses share, in lab.py
free_port (7 copies), offline_uuid (4), varint and parse_varint (3 and 2), dbits (6), fbits (4) and settled (2) move into lab.py; every caller uses lab's. The other server harnesses keep importing free_port from test_dedicated_server_parity, which now takes lab's. Imports the moved code left unused go too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,8 @@ import threading
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import lab
|
||||
|
||||
BX, BZ, SIZE = 144, 144, 96 # south-west corner and size of the platforms (inside the spawn chunks)
|
||||
LAYERS = list(range(100, 245, 16)) # platform y levels: one chunk section each, the last one reaches y = 255
|
||||
UUID_HI = 0x6000
|
||||
@@ -1743,7 +1745,7 @@ LAST = {}
|
||||
def populate(server, Bot, port, protocol, name, problems):
|
||||
"""Build the whole 1.12.2 scene, save it for good and return the (still joined) bot."""
|
||||
harness = sys.modules[Bot.__module__]
|
||||
Player = make_player(Bot, harness.varint, harness.parse_varint, harness.mcstring)
|
||||
Player = make_player(Bot, lab.varint, lab.parse_varint, harness.mcstring)
|
||||
set_protocol(protocol)
|
||||
say = lambda cmds, **kw: batch(server, cmds, problems, **kw)
|
||||
if ERA < 12:
|
||||
@@ -1874,7 +1876,7 @@ def build_flat(h, force=False):
|
||||
return []
|
||||
work = h.LOCAL / 'gen' / (version + '-flat')
|
||||
shutil.rmtree(work, ignore_errors=True)
|
||||
port = h.free_port()
|
||||
port = lab.free_port()
|
||||
|
||||
class FlatServer(h.Server):
|
||||
def __init__(self, directory): # as Server.__init__, with a flat world
|
||||
@@ -1900,7 +1902,7 @@ def build_flat(h, force=False):
|
||||
# A flat world's spawn is found far away; make it (64, 64), and let a bot visit the scene's area so that its chunks
|
||||
# are loaded (they stay loaded, being within the spawn radius).
|
||||
batch(server, ['setworldspawn 64 4 64'], problems)
|
||||
Player = make_player(h.Bot, h.varint, h.parse_varint, h.mcstring)
|
||||
Player = make_player(h.Bot, lab.varint, lab.parse_varint, h.mcstring)
|
||||
visitor = Player(port, protocol, h.BOT)
|
||||
time.sleep(2)
|
||||
for x, z in ((24, 24), (24, 72), (24, 104), (72, 24), (72, 72), (72, 104), (104, 24), (104, 72), (104, 104), (72, 123), (-8, 63), (32, 103)):
|
||||
|
||||
+16
-51
@@ -23,7 +23,6 @@ import sys
|
||||
import threading
|
||||
import time
|
||||
import urllib.request
|
||||
import uuid
|
||||
import zipfile
|
||||
import zlib
|
||||
from pathlib import Path
|
||||
@@ -31,6 +30,7 @@ from pathlib import Path
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
import datafix_scene as scene # noqa: E402
|
||||
import datafix_scene_12 as scene12 # noqa: E402
|
||||
import lab # noqa: E402
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
LOCAL = ROOT / '.local' / 'datafix'
|
||||
@@ -159,12 +159,6 @@ def install_runtime(pin, home):
|
||||
target.symlink_to(to)
|
||||
|
||||
|
||||
def free_port():
|
||||
with socket.socket() as s:
|
||||
s.bind(('127.0.0.1', 0))
|
||||
return s.getsockname()[1]
|
||||
|
||||
|
||||
class Server:
|
||||
def __init__(self, directory, jar, port, packs=(), java='java', props_override=None):
|
||||
directory.mkdir(parents=True, exist_ok=True)
|
||||
@@ -230,38 +224,9 @@ def failed(lines):
|
||||
|
||||
# ------------------------------------------------------------------ minimal bot
|
||||
|
||||
def varint(v):
|
||||
out = b''
|
||||
v &= 0xFFFFFFFF
|
||||
while True:
|
||||
b = v & 0x7F
|
||||
v >>= 7
|
||||
out += bytes([b | (0x80 if v else 0)])
|
||||
if not v:
|
||||
return out
|
||||
|
||||
|
||||
def mcstring(s):
|
||||
data = s.encode()
|
||||
return varint(len(data)) + data
|
||||
|
||||
|
||||
def parse_varint(data, pos):
|
||||
value = shift = 0
|
||||
while True:
|
||||
b = data[pos]
|
||||
pos += 1
|
||||
value |= (b & 0x7F) << shift
|
||||
shift += 7
|
||||
if not b & 0x80:
|
||||
return pos, value
|
||||
|
||||
|
||||
def offline_uuid(name):
|
||||
digest = bytearray(hashlib.md5(('OfflinePlayer:' + name).encode()).digest())
|
||||
digest[6] = (digest[6] & 0x0F) | 0x30
|
||||
digest[8] = (digest[8] & 0x3F) | 0x80
|
||||
return uuid.UUID(bytes=bytes(digest))
|
||||
return lab.varint(len(data)) + data
|
||||
|
||||
|
||||
class Bot:
|
||||
@@ -273,21 +238,21 @@ class Bot:
|
||||
self.buffer = b''
|
||||
self.protocol = protocol
|
||||
self.stop = threading.Event()
|
||||
self.send(0, varint(protocol) + mcstring('127.0.0.1') + struct.pack('>H', port) + varint(2))
|
||||
self.send(0, lab.varint(protocol) + mcstring('127.0.0.1') + struct.pack('>H', port) + lab.varint(2))
|
||||
if protocol <= 758: # 1.18.2: the name only
|
||||
self.send(0, mcstring(name))
|
||||
elif protocol == 759: # 1.19: no signature data
|
||||
self.send(0, mcstring(name) + b'\x00')
|
||||
elif protocol == 760: # 1.19.2: no signature data, optional UUID
|
||||
self.send(0, mcstring(name) + b'\x00' + b'\x01' + offline_uuid(name).bytes)
|
||||
self.send(0, mcstring(name) + b'\x00' + b'\x01' + lab.offline_uuid(name).bytes)
|
||||
elif protocol <= 763: # 1.19.3 - 1.20.1: optional UUID
|
||||
self.send(0, mcstring(name) + b'\x01' + offline_uuid(name).bytes)
|
||||
self.send(0, mcstring(name) + b'\x01' + lab.offline_uuid(name).bytes)
|
||||
else:
|
||||
self.send(0, mcstring(name) + offline_uuid(name).bytes)
|
||||
self.send(0, mcstring(name) + lab.offline_uuid(name).bytes)
|
||||
while True:
|
||||
pid, data = self.receive()
|
||||
if pid == 3:
|
||||
self.threshold = parse_varint(data, 0)[1]
|
||||
self.threshold = lab.parse_varint(data, 0)[1]
|
||||
elif pid == 2:
|
||||
break
|
||||
elif pid == 0:
|
||||
@@ -312,15 +277,15 @@ class Bot:
|
||||
elif pid == disconnect:
|
||||
raise RuntimeError(f'disconnected during configuration: {data!r}')
|
||||
elif not old and self.known_packs(data):
|
||||
self.send(7, varint(0)) # claim no packs: the server sends full registries
|
||||
self.send(7, lab.varint(0)) # claim no packs: the server sends full registries
|
||||
|
||||
@staticmethod
|
||||
def known_packs(data):
|
||||
try:
|
||||
pos, count = parse_varint(data, 0)
|
||||
pos, count = lab.parse_varint(data, 0)
|
||||
if count < 1:
|
||||
return False
|
||||
pos, n = parse_varint(data, pos)
|
||||
pos, n = lab.parse_varint(data, pos)
|
||||
return data[pos:pos + n] == b'minecraft'
|
||||
except IndexError:
|
||||
return False
|
||||
@@ -359,18 +324,18 @@ class Bot:
|
||||
return value
|
||||
|
||||
def send(self, packet_id, payload=b''):
|
||||
body = varint(packet_id) + payload
|
||||
body = lab.varint(packet_id) + payload
|
||||
if self.threshold >= 0:
|
||||
body = varint(len(body)) + zlib.compress(body) if len(body) >= self.threshold else varint(0) + body
|
||||
self.sock.sendall(varint(len(body)) + body)
|
||||
body = lab.varint(len(body)) + zlib.compress(body) if len(body) >= self.threshold else lab.varint(0) + body
|
||||
self.sock.sendall(lab.varint(len(body)) + body)
|
||||
|
||||
def receive(self):
|
||||
length = self.read_varint()
|
||||
body = self.read_exact(length)
|
||||
if self.threshold >= 0:
|
||||
pos, size = parse_varint(body, 0)
|
||||
pos, size = lab.parse_varint(body, 0)
|
||||
body = zlib.decompress(body[pos:]) if size else body[pos:]
|
||||
pos, packet_id = parse_varint(body, 0)
|
||||
pos, packet_id = lab.parse_varint(body, 0)
|
||||
return packet_id, body[pos:]
|
||||
|
||||
|
||||
@@ -428,7 +393,7 @@ def build(name, force=False):
|
||||
era = scene.era(data_version)
|
||||
work = LOCAL / 'gen' / name
|
||||
shutil.rmtree(work, ignore_errors=True)
|
||||
port = free_port()
|
||||
port = lab.free_port()
|
||||
server = Server(work, jar, port, scene.experiments(data_version), java(version), props)
|
||||
problems = []
|
||||
|
||||
|
||||
+64
-1
@@ -1,7 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Pinned Java Edition import/decompilation. Game files come from the user's own
|
||||
copy (their official launcher install and the server jar they downloaded); all
|
||||
generated files stay in .local/."""
|
||||
generated files stay in .local/. Also the small helpers the harnesses share
|
||||
(free ports, offline UUIDs, VarInts, float bits)."""
|
||||
|
||||
import argparse
|
||||
from collections import Counter
|
||||
@@ -12,11 +13,13 @@ import platform
|
||||
from pathlib import Path, PurePosixPath
|
||||
import re
|
||||
import shutil
|
||||
import socket
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import urllib.request
|
||||
import uuid
|
||||
import zipfile
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
@@ -61,6 +64,66 @@ def digest(path, algorithm):
|
||||
return hashlib.file_digest(stream, algorithm).hexdigest()
|
||||
|
||||
|
||||
def free_port():
|
||||
"""A free local TCP port, for a server a harness starts."""
|
||||
with socket.socket() as s:
|
||||
s.bind(("127.0.0.1", 0))
|
||||
return s.getsockname()[1]
|
||||
|
||||
|
||||
def offline_uuid(name):
|
||||
"""UUIDUtil.createOfflinePlayerUUID: the UUID an offline-mode server gives a player name."""
|
||||
digest = bytearray(hashlib.md5(("OfflinePlayer:" + name).encode()).digest())
|
||||
digest[6] = (digest[6] & 0x0F) | 0x30
|
||||
digest[8] = (digest[8] & 0x3F) | 0x80
|
||||
return uuid.UUID(bytes=bytes(digest))
|
||||
|
||||
|
||||
def varint(value):
|
||||
"""FriendlyByteBuf.writeVarInt: the int's 32 bits, 7 at a time."""
|
||||
out = b""
|
||||
value &= 0xFFFFFFFF
|
||||
while True:
|
||||
b = value & 0x7F
|
||||
value >>= 7
|
||||
out += bytes([b | (0x80 if value else 0)])
|
||||
if not value:
|
||||
return out
|
||||
|
||||
|
||||
def parse_varint(data, pos):
|
||||
"""The VarInt at data[pos]: (the position after it, its unsigned value)."""
|
||||
value = shift = 0
|
||||
while True:
|
||||
b = data[pos]
|
||||
pos += 1
|
||||
value |= (b & 0x7F) << shift
|
||||
shift += 7
|
||||
if not b & 0x80:
|
||||
return pos, value
|
||||
|
||||
|
||||
def fbits(value):
|
||||
"""A float's IEEE 754 bits in hex, as the Java oracles print them."""
|
||||
return struct.pack(">f", value).hex()
|
||||
|
||||
|
||||
def dbits(value):
|
||||
"""A double's IEEE 754 bits in hex, as the Java oracles print them."""
|
||||
return struct.pack(">d", value).hex()
|
||||
|
||||
|
||||
def settled(lines):
|
||||
"""One player's lines of one step, in order, with the waypoint packets reduced to the last for each waypoint:
|
||||
what the locator bar shows once the step is over. Arriving in a level, vanilla sends the same waypoint more
|
||||
than once on the way there; the packet by packet order is test_locator_bar_parity's."""
|
||||
def waypoint(line):
|
||||
parts = line.split(" ")
|
||||
return parts[3] if len(parts) > 3 and parts[2] in ("track", "untrack", "update") else None
|
||||
last = {waypoint(l): i for i, l in enumerate(lines) if waypoint(l)}
|
||||
return [l for i, l in enumerate(lines) if not waypoint(l) or last[waypoint(l)] == i]
|
||||
|
||||
|
||||
def download(url, destination, checksum, algorithm="sha1"):
|
||||
"""Never promote an unverified or partial download into the artifact cache."""
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@@ -45,7 +45,6 @@ from pathlib import Path
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import socket
|
||||
import statistics
|
||||
import subprocess
|
||||
import sys
|
||||
@@ -81,12 +80,6 @@ PROPERTIES = {
|
||||
}
|
||||
|
||||
|
||||
def free_port():
|
||||
with socket.socket() as s:
|
||||
s.bind(('127.0.0.1', 0))
|
||||
return s.getsockname()[1]
|
||||
|
||||
|
||||
def cpu_seconds(text):
|
||||
"""ps's cumulative CPU time: [[DD-]HH:]MM:SS[.ss]."""
|
||||
days, _, rest = text.rpartition('-')
|
||||
@@ -282,7 +275,7 @@ def run_once(kind, bots, behaviour, args, out):
|
||||
directory = out / name
|
||||
shutil.rmtree(directory, ignore_errors=True)
|
||||
directory.mkdir(parents=True)
|
||||
port = free_port()
|
||||
port = lab.free_port()
|
||||
props = dict(PROPERTIES, **{'server-port': str(port), 'level-seed': str(args.seed)})
|
||||
(directory / 'server.properties').write_text(''.join(f'{k}={v}\n' for k, v in props.items()))
|
||||
(directory / 'eula.txt').write_text('eula=true\n')
|
||||
|
||||
@@ -17,16 +17,12 @@ playing instance and the subtitle notifications. Horizon's `Ambience` and
|
||||
"""
|
||||
import json
|
||||
import random
|
||||
import struct
|
||||
|
||||
try:
|
||||
from . import audio_harness as h
|
||||
from . import audio_harness as h, lab
|
||||
except ImportError:
|
||||
import audio_harness as h
|
||||
|
||||
|
||||
def dbits(v):
|
||||
return struct.pack('>d', v).hex()
|
||||
import lab
|
||||
|
||||
|
||||
def settings():
|
||||
@@ -54,11 +50,11 @@ class Script:
|
||||
self.lines.append(' '.join(map(str, parts)))
|
||||
|
||||
def sounds(self, value):
|
||||
mood = '-' if value['mood'] is None else '{},{},{},{}'.format(value['mood'][0], value['mood'][1], value['mood'][2], dbits(value['mood'][3]))
|
||||
self.add('sounds', value['loop'] or '-', mood, *[f'{s},{dbits(c)}' for s, c in value['additions']])
|
||||
mood = '-' if value['mood'] is None else '{},{},{},{}'.format(value['mood'][0], value['mood'][1], value['mood'][2], lab.dbits(value['mood'][3]))
|
||||
self.add('sounds', value['loop'] or '-', mood, *[f'{s},{lab.dbits(c)}' for s, c in value['additions']])
|
||||
|
||||
def player(self, at=(0.5, 64.0, 0.5), eye=1.62, water=0, spectator=0, removed=0, mode=0, height=1.8, columns=()):
|
||||
self.add('player', *map(dbits, at), h.bits(eye), water, spectator, removed, mode, h.bits(height),
|
||||
self.add('player', *map(lab.dbits, at), h.bits(eye), water, spectator, removed, mode, h.bits(height),
|
||||
*[f'{x},{y},{z},{d}' for x, y, z, d in columns])
|
||||
|
||||
def tick(self, n=1):
|
||||
|
||||
@@ -8,11 +8,9 @@ overhangs approached from every angle with varied speeds, input vectors,
|
||||
sizes, jump boost and the canAutoJump preconditions."""
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
from pathlib import Path
|
||||
import platform
|
||||
import random
|
||||
import struct
|
||||
import subprocess
|
||||
|
||||
try:
|
||||
@@ -23,14 +21,6 @@ except ImportError:
|
||||
import test_native_client as client
|
||||
|
||||
|
||||
def fbits(v):
|
||||
return struct.pack('>f', v).hex()
|
||||
|
||||
|
||||
def dbits(v):
|
||||
return struct.pack('>d', v).hex()
|
||||
|
||||
|
||||
BLOCKS = [
|
||||
('stone', {}), ('stone', {}), ('stone', {}),
|
||||
('stone_slab', {'type': 'bottom'}), ('stone_slab', {'type': 'top'}), ('stone_slab', {'type': 'double'}),
|
||||
@@ -64,12 +54,12 @@ DIMS = [(0.6, 1.8), (0.6, 1.8), (0.6, 1.5), (0.6, 0.6), (0.3, 0.9), (1.2, 3.6),
|
||||
|
||||
def player(rng, x, y, z, yaw):
|
||||
w, h = rng.choice(DIMS)
|
||||
return dict(pos=[dbits(x), dbits(y), dbits(z)], ground=rng.random() < 0.9, shift=rng.random() < 0.08,
|
||||
yr=fbits(yaw), xr=fbits(rng.choice([0.0, 30.0, -45.0, 90.0, rng.uniform(-90, 90)])),
|
||||
speed=fbits(rng.choice([0.1, 0.13000001, 0.03, 0.0, 0.2, rng.uniform(0, 0.3)])),
|
||||
move=[fbits(v) for v in rng.choice([(0.0, 1.0), (0.0, 1.0), (1.0, 0.0), (-1.0, 0.0), (0.70710677, 0.70710677),
|
||||
return dict(pos=[lab.dbits(x), lab.dbits(y), lab.dbits(z)], ground=rng.random() < 0.9, shift=rng.random() < 0.08,
|
||||
yr=lab.fbits(yaw), xr=lab.fbits(rng.choice([0.0, 30.0, -45.0, 90.0, rng.uniform(-90, 90)])),
|
||||
speed=lab.fbits(rng.choice([0.1, 0.13000001, 0.03, 0.0, 0.2, rng.uniform(0, 0.3)])),
|
||||
move=[lab.fbits(v) for v in rng.choice([(0.0, 1.0), (0.0, 1.0), (1.0, 0.0), (-1.0, 0.0), (0.70710677, 0.70710677),
|
||||
(0.0, -1.0), (0.0, 0.0), (0.3, 0.3)])],
|
||||
width=fbits(w), height=fbits(h), boost=rng.choice([None] * 6 + [0, 1, 4, 255]),
|
||||
width=lab.fbits(w), height=lab.fbits(h), boost=rng.choice([None] * 6 + [0, 1, 4, 255]),
|
||||
enabled=rng.random() < 0.93, time=rng.choice([0] * 12 + [1, -3, 2]))
|
||||
|
||||
|
||||
@@ -86,18 +76,18 @@ def cases(rng):
|
||||
x = ox + 0.5 - dx * 1.1
|
||||
z = oz + 0.5 - dz * 1.1
|
||||
p = player(rng, x, float(oy), z, yaw)
|
||||
p.update(ground=True, shift=False, enabled=True, time=0, boost=None, move=[fbits(0.0), fbits(1.0)],
|
||||
width=fbits(0.6), height=fbits(1.8), xr=fbits(0.0))
|
||||
yield dict(blocks=blocks, player=p, xa=fbits(dx * step), za=fbits(dz * step))
|
||||
p.update(ground=True, shift=False, enabled=True, time=0, boost=None, move=[lab.fbits(0.0), lab.fbits(1.0)],
|
||||
width=lab.fbits(0.6), height=lab.fbits(1.8), xr=lab.fbits(0.0))
|
||||
yield dict(blocks=blocks, player=p, xa=lab.fbits(dx * step), za=lab.fbits(dz * step))
|
||||
# Ceilings above the player and above the step.
|
||||
for ceiling in range(2, 6):
|
||||
for step_height in (0, 1):
|
||||
blocks = [[ox, oy + step_height, oz, 'minecraft:stone', {}], [ox - 1, oy + ceiling, oz, 'minecraft:stone', {}],
|
||||
[ox, oy + ceiling + step_height, oz, 'minecraft:stone_slab', {'type': 'top'}]]
|
||||
p = player(rng, ox - 0.5, float(oy), oz + 0.5, -90.0)
|
||||
p.update(ground=True, shift=False, enabled=True, time=0, boost=None, move=[fbits(0.0), fbits(1.0)],
|
||||
width=fbits(0.6), height=fbits(1.8))
|
||||
yield dict(blocks=blocks, player=p, xa=fbits(0.2), za=fbits(0.0))
|
||||
p.update(ground=True, shift=False, enabled=True, time=0, boost=None, move=[lab.fbits(0.0), lab.fbits(1.0)],
|
||||
width=lab.fbits(0.6), height=lab.fbits(1.8))
|
||||
yield dict(blocks=blocks, player=p, xa=lab.fbits(0.2), za=lab.fbits(0.0))
|
||||
for _ in range(9000):
|
||||
blocks = placed(rng, rng.choice([1, 2, 4, 8, 16, 30]))
|
||||
x = ox + rng.uniform(-2, 3)
|
||||
@@ -108,7 +98,7 @@ def cases(rng):
|
||||
move = rng.choice([0.0, 0.0, 0.01, 0.1, 0.215, 0.3, rng.uniform(0, 0.4)])
|
||||
angle = rng.uniform(0, 6.283)
|
||||
|
||||
yield dict(blocks=blocks, player=p, xa=fbits(math.cos(angle) * move), za=fbits(math.sin(angle) * move))
|
||||
yield dict(blocks=blocks, player=p, xa=lab.fbits(math.cos(angle) * move), za=lab.fbits(math.sin(angle) * move))
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
+35
-43
@@ -20,14 +20,6 @@ except ImportError:
|
||||
import lab, oracle_harness as harness
|
||||
|
||||
|
||||
def fbits(v):
|
||||
return struct.pack('>f', v).hex()
|
||||
|
||||
|
||||
def dbits(v):
|
||||
return struct.pack('>d', v).hex()
|
||||
|
||||
|
||||
def f32(v):
|
||||
return struct.unpack('>f', struct.pack('>f', v))[0]
|
||||
|
||||
@@ -39,8 +31,8 @@ TICKS = [0, 1, 5, 10, 19, 20, 21, 72000, -5, 3]
|
||||
|
||||
def fov_fields(rng):
|
||||
use = rng.choice([None, None, ['bow', rng.choice(TICKS)], ['spyglass', rng.choice(TICKS)], ['other', rng.choice(TICKS)]])
|
||||
return dict(flying=rng.random() < 0.3, ws=fbits(rng.choice(WALK + [rng.uniform(-0.3, 0.3)])),
|
||||
speed=dbits(rng.choice(SPEED + [rng.uniform(0, 2)])), use=use)
|
||||
return dict(flying=rng.random() < 0.3, ws=lab.fbits(rng.choice(WALK + [rng.uniform(-0.3, 0.3)])),
|
||||
speed=lab.dbits(rng.choice(SPEED + [rng.uniform(0, 2)])), use=use)
|
||||
|
||||
|
||||
def fov_cases(rng):
|
||||
@@ -50,9 +42,9 @@ def fov_cases(rng):
|
||||
for use in [None, ['bow', 0], ['bow', 7], ['bow', 20], ['bow', 40], ['spyglass', 2], ['other', 5]]:
|
||||
for fp in (False, True):
|
||||
for scale in (0.0, 0.5, 1.0):
|
||||
yield dict(t='fov', flying=flying, ws=fbits(ws), speed=dbits(speed), use=use, fp=fp, scale=fbits(scale))
|
||||
yield dict(t='fov', flying=flying, ws=lab.fbits(ws), speed=lab.dbits(speed), use=use, fp=fp, scale=lab.fbits(scale))
|
||||
for _ in range(3000):
|
||||
yield dict(t='fov', **fov_fields(rng), fp=rng.random() < 0.6, scale=fbits(rng.choice([0.0, 1.0, rng.random()])))
|
||||
yield dict(t='fov', **fov_fields(rng), fp=rng.random() < 0.6, scale=lab.fbits(rng.choice([0.0, 1.0, rng.random()])))
|
||||
|
||||
|
||||
FULL = [[0, 0, 0, 1, 1, 1]]
|
||||
@@ -65,7 +57,7 @@ def world(rng, center):
|
||||
cx, cy, cz = center
|
||||
for _ in range(rng.choice([0, 3, 10, 40, 120])):
|
||||
p = (cx + rng.randint(-6, 6), cy + rng.randint(-3, 5), cz + rng.randint(-6, 6))
|
||||
cells[f'{p[0]} {p[1]} {p[2]}'] = [[dbits(v) for v in box] for box in rng.choice(SHAPES)]
|
||||
cells[f'{p[0]} {p[1]} {p[2]}'] = [[lab.dbits(v) for v in box] for box in rng.choice(SHAPES)]
|
||||
return cells
|
||||
|
||||
|
||||
@@ -81,22 +73,22 @@ def entity_state(rng, center):
|
||||
prev = [p - rng.choice([0, 0, 0.1, 0.3, 1.0, rng.uniform(-1, 1)]) for p in pos]
|
||||
kind = 'player' if rng.random() < 0.85 else 'marker'
|
||||
xr, yr = angle(rng), angle(rng)
|
||||
s = dict(kind=kind, pos=[dbits(v) for v in pos], prev=[dbits(v) for v in prev], xr=fbits(xr), yr=fbits(yr),
|
||||
xro=fbits(rng.choice([xr, xr - 5, angle(rng)])), yro=fbits(rng.choice([yr, yr + 10, angle(rng)])),
|
||||
eye=fbits(rng.choice([1.62, 1.27, 0.4, 0.2, 0.81, 3.24, rng.uniform(0, 4)])))
|
||||
s = dict(kind=kind, pos=[lab.dbits(v) for v in pos], prev=[lab.dbits(v) for v in prev], xr=lab.fbits(xr), yr=lab.fbits(yr),
|
||||
xro=lab.fbits(rng.choice([xr, xr - 5, angle(rng)])), yro=lab.fbits(rng.choice([yr, yr + 10, angle(rng)])),
|
||||
eye=lab.fbits(rng.choice([1.62, 1.27, 0.4, 0.2, 0.81, 3.24, rng.uniform(0, 4)])))
|
||||
s.update(fov_fields(rng))
|
||||
s.update(scale_attr=dbits(rng.choice([1.0, 1.0, 0.5, 2.0, 0.0625, 16.0, rng.uniform(0.0625, 3)])),
|
||||
dist=dbits(rng.choice([4.0, 4.0, 0.0, 1.0, 10.0, 32.0, rng.uniform(0, 32)])),
|
||||
s.update(scale_attr=lab.dbits(rng.choice([1.0, 1.0, 0.5, 2.0, 0.0625, 16.0, rng.uniform(0.0625, 3)])),
|
||||
dist=lab.dbits(rng.choice([4.0, 4.0, 0.0, 1.0, 10.0, 32.0, rng.uniform(0, 32)])),
|
||||
sleeping=rng.choice([None] * 6 + [[None], [0], [1], [2], [3]]),
|
||||
dead=rng.choice([None] * 6 + [0, 1, 5, 19, 20, 25]),
|
||||
mount=rng.choice([None] * 4 + [[dbits(rng.choice([1.0, 2.0, 0.5])), dbits(rng.choice([4.0, 8.0, 1.0]))]]))
|
||||
mount=rng.choice([None] * 4 + [[lab.dbits(rng.choice([1.0, 2.0, 0.5])), lab.dbits(rng.choice([4.0, 8.0, 1.0]))]]))
|
||||
return s
|
||||
|
||||
|
||||
def window(rng):
|
||||
"""Window size and Camera.depthFar for the world and hand projections."""
|
||||
w, h = rng.choice([(1600, 1000), (854, 480), (1920, 1080), (800, 1200), (1, 1), (rng.randint(1, 4000), rng.randint(1, 3000))])
|
||||
return [fbits(w), fbits(h), fbits(rng.choice([768.0, 128.0, 2048.0, 4096.0, rng.uniform(32, 4096)]))]
|
||||
return [lab.fbits(w), lab.fbits(h), lab.fbits(rng.choice([768.0, 128.0, 2048.0, 4096.0, rng.uniform(32, 4096)]))]
|
||||
|
||||
|
||||
def camera_cases(rng):
|
||||
@@ -113,11 +105,11 @@ def camera_cases(rng):
|
||||
ops.append(['tick'])
|
||||
elif kind == 'frame':
|
||||
pt = rng.choice([0.0, 1.0, 0.5, 0.25, 0.999, rng.random()])
|
||||
ops.append(['frame', fbits(pt), rng.choice([0, 0, 0, 1, 2, 3]), rng.random() < 0.05] + window(rng))
|
||||
ops.append(['frame', lab.fbits(pt), rng.choice([0, 0, 0, 1, 2, 3]), rng.random() < 0.05] + window(rng))
|
||||
else:
|
||||
ops.append(['type', rng.randrange(3)])
|
||||
yield dict(t='camera', fov=rng.choice([70, 30, 110, 90, rng.randint(30, 110)]),
|
||||
effect=dbits(rng.choice([1.0, 0.0, 0.5, rng.random()])), type=rng.choice([0, 1, 1, 2]),
|
||||
effect=lab.dbits(rng.choice([1.0, 0.0, 0.5, rng.random()])), type=rng.choice([0, 1, 1, 2]),
|
||||
cells=world(rng, center), ops=ops)
|
||||
# The hand's projection at FOV 30, 70 and 110 while walking, sprinting, under
|
||||
# speed, drawing a bow, dying and in fluids: GameRenderer.render3dHud uses
|
||||
@@ -127,13 +119,13 @@ def camera_cases(rng):
|
||||
for use in (None, ['bow', 10]):
|
||||
for dead in (None, 5):
|
||||
for fluid in (0, 1, 2):
|
||||
s = dict(kind='player', pos=[dbits(0.5), dbits(64.0), dbits(0.5)], prev=[dbits(0.5), dbits(64.0), dbits(0.5)],
|
||||
xr=fbits(0.0), yr=fbits(0.0), xro=fbits(0.0), yro=fbits(0.0), eye=fbits(1.62), flying=False,
|
||||
ws=fbits(0.1), speed=dbits(speed), use=use, scale_attr=dbits(1.0), dist=dbits(4.0),
|
||||
s = dict(kind='player', pos=[lab.dbits(0.5), lab.dbits(64.0), lab.dbits(0.5)], prev=[lab.dbits(0.5), lab.dbits(64.0), lab.dbits(0.5)],
|
||||
xr=lab.fbits(0.0), yr=lab.fbits(0.0), xro=lab.fbits(0.0), yro=lab.fbits(0.0), eye=lab.fbits(1.62), flying=False,
|
||||
ws=lab.fbits(0.1), speed=lab.dbits(speed), use=use, scale_attr=lab.dbits(1.0), dist=lab.dbits(4.0),
|
||||
sleeping=None, dead=dead, mount=None)
|
||||
ops = [['entity', s]] + [['tick']] * 8 + [['frame', fbits(pt), fluid, False, fbits(1600.0), fbits(1000.0), fbits(768.0)]
|
||||
ops = [['entity', s]] + [['tick']] * 8 + [['frame', lab.fbits(pt), fluid, False, lab.fbits(1600.0), lab.fbits(1000.0), lab.fbits(768.0)]
|
||||
for pt in (0.0, 0.5, 1.0)]
|
||||
yield dict(t='camera', fov=fov, effect=dbits(1.0), type=0, cells={}, ops=ops)
|
||||
yield dict(t='camera', fov=fov, effect=lab.dbits(1.0), type=0, cells={}, ops=ops)
|
||||
# A walled-in third-person camera stepping through every distance bound.
|
||||
for dist in [0.0, 0.5, 1.0, 2.0, 4.0, 8.0, 32.0]:
|
||||
for wall in range(0, 6):
|
||||
@@ -141,28 +133,28 @@ def camera_cases(rng):
|
||||
cells = {}
|
||||
for dx in range(-6, 7):
|
||||
for dy in range(62, 68):
|
||||
cells[f'{dx} {dy} {-wall - 1}'] = [[dbits(v) for v in box] for box in FULL]
|
||||
cells[f'{dx} {dy} {wall + 1}'] = [[dbits(v) for v in box] for box in FULL]
|
||||
s = dict(kind='player', pos=[dbits(0.5), dbits(64.0), dbits(0.5)], prev=[dbits(0.5), dbits(64.0), dbits(0.5)],
|
||||
xr=fbits(15.0), yr=fbits(yaw), xro=fbits(15.0), yro=fbits(yaw), eye=fbits(1.62), flying=False,
|
||||
ws=fbits(0.1), speed=dbits(f32(0.1)), use=None, scale_attr=dbits(1.0), dist=dbits(dist),
|
||||
cells[f'{dx} {dy} {-wall - 1}'] = [[lab.dbits(v) for v in box] for box in FULL]
|
||||
cells[f'{dx} {dy} {wall + 1}'] = [[lab.dbits(v) for v in box] for box in FULL]
|
||||
s = dict(kind='player', pos=[lab.dbits(0.5), lab.dbits(64.0), lab.dbits(0.5)], prev=[lab.dbits(0.5), lab.dbits(64.0), lab.dbits(0.5)],
|
||||
xr=lab.fbits(15.0), yr=lab.fbits(yaw), xro=lab.fbits(15.0), yro=lab.fbits(yaw), eye=lab.fbits(1.62), flying=False,
|
||||
ws=lab.fbits(0.1), speed=lab.dbits(f32(0.1)), use=None, scale_attr=lab.dbits(1.0), dist=lab.dbits(dist),
|
||||
sleeping=None, dead=None, mount=None)
|
||||
ops = [['entity', s]] + [['tick']] * 6 + [['frame', fbits(pt), 0, False] + window(rng) for pt in (0.0, 0.5, 1.0)]
|
||||
yield dict(t='camera', fov=70, effect=dbits(1.0), type=1 + (wall % 2), cells=cells, ops=ops)
|
||||
ops = [['entity', s]] + [['tick']] * 6 + [['frame', lab.fbits(pt), 0, False] + window(rng) for pt in (0.0, 0.5, 1.0)]
|
||||
yield dict(t='camera', fov=70, effect=lab.dbits(1.0), type=1 + (wall % 2), cells=cells, ops=ops)
|
||||
|
||||
|
||||
def bob_cases(rng):
|
||||
for n in range(6000):
|
||||
living = None
|
||||
if rng.random() < 0.8:
|
||||
living = dict(hurt=fbits(rng.choice([-1.0, -0.5, 0.0, 10.0, 9.5, 5.25, 1.0, rng.uniform(-2, 11)])),
|
||||
duration=rng.choice([10, 10, 0, 5, 20]), dir=fbits(rng.choice([0.0, 90.0, -45.0, 180.0, rng.uniform(-360, 360)])),
|
||||
death=rng.choice([None, None, None, fbits(rng.choice([0.0, 0.5, 19.5, 20.0, 25.0, rng.uniform(0, 30)]))]))
|
||||
living = dict(hurt=lab.fbits(rng.choice([-1.0, -0.5, 0.0, 10.0, 9.5, 5.25, 1.0, rng.uniform(-2, 11)])),
|
||||
duration=rng.choice([10, 10, 0, 5, 20]), dir=lab.fbits(rng.choice([0.0, 90.0, -45.0, 180.0, rng.uniform(-360, 360)])),
|
||||
death=rng.choice([None, None, None, lab.fbits(rng.choice([0.0, 0.5, 19.5, 20.0, 25.0, rng.uniform(0, 30)]))]))
|
||||
player = None
|
||||
if rng.random() < 0.8:
|
||||
player = [fbits(rng.choice([0.0, -0.3, -1.7, -100.25, rng.uniform(-1000, 0)])),
|
||||
fbits(rng.choice([0.0, 0.1, 0.05, 0.0999, rng.uniform(0, 0.1)]))]
|
||||
yield dict(t='bob', living=living, player=player, tilt=dbits(rng.choice([1.0, 0.0, 0.5, rng.random()])),
|
||||
player = [lab.fbits(rng.choice([0.0, -0.3, -1.7, -100.25, rng.uniform(-1000, 0)])),
|
||||
lab.fbits(rng.choice([0.0, 0.1, 0.05, 0.0999, rng.uniform(0, 0.1)]))]
|
||||
yield dict(t='bob', living=living, player=player, tilt=lab.dbits(rng.choice([1.0, 0.0, 0.5, rng.random()])),
|
||||
view=rng.random() < 0.8)
|
||||
|
||||
|
||||
@@ -174,12 +166,12 @@ def avatar_cases(rng):
|
||||
if kind == 'tick':
|
||||
ops.append(['tick'])
|
||||
elif kind == 'move':
|
||||
ops.append(['move', fbits(rng.choice([0.0, 0.2158, -0.1, rng.uniform(-1, 1)])), fbits(rng.choice([0.0, 0.2806, rng.uniform(-1, 1)]))])
|
||||
ops.append(['move', lab.fbits(rng.choice([0.0, 0.2158, -0.1, rng.uniform(-1, 1)])), lab.fbits(rng.choice([0.0, 0.2806, rng.uniform(-1, 1)]))])
|
||||
elif kind == 'bob':
|
||||
v = [rng.choice([0.0, 0.05, -0.2, rng.uniform(-0.5, 0.5)]), rng.uniform(-1, 1), rng.choice([0.0, 0.08, rng.uniform(-0.5, 0.5)])]
|
||||
ops.append(['bob', rng.random() < 0.7, rng.random() < 0.1, rng.random() < 0.1, [dbits(x) for x in v]])
|
||||
ops.append(['bob', rng.random() < 0.7, rng.random() < 0.1, rng.random() < 0.1, [lab.dbits(x) for x in v]])
|
||||
elif kind == 'frame':
|
||||
ops.append(['frame', fbits(rng.choice([0.0, 1.0, 0.5, rng.random()]))])
|
||||
ops.append(['frame', lab.fbits(rng.choice([0.0, 1.0, 0.5, rng.random()]))])
|
||||
else:
|
||||
ops.append(['reset'])
|
||||
yield dict(t='avatar', ops=ops)
|
||||
|
||||
@@ -68,10 +68,12 @@ import zlib
|
||||
|
||||
try:
|
||||
from . import lab, oracle_harness as oh, test_rust_parity as base
|
||||
from .lab import free_port # the other server harnesses import it from here
|
||||
except ImportError:
|
||||
import lab
|
||||
import oracle_harness as oh
|
||||
import test_rust_parity as base
|
||||
from lab import free_port
|
||||
|
||||
NAME = 'dedicated-server-parity'
|
||||
SEED = 12345
|
||||
@@ -89,12 +91,6 @@ PROPERTIES = {
|
||||
}
|
||||
|
||||
|
||||
def free_port():
|
||||
with socket.socket() as s:
|
||||
s.bind(('127.0.0.1', 0))
|
||||
return s.getsockname()[1]
|
||||
|
||||
|
||||
def icon_png():
|
||||
"""A 64x64 RGBA PNG (server-icon.png)."""
|
||||
rows = b''.join(b'\0' + b''.join(bytes((x * 4, y * 4, (x ^ y) * 4, 255)) for x in range(64)) for y in range(64))
|
||||
|
||||
@@ -18,14 +18,6 @@ except ImportError:
|
||||
import lab, oracle_harness as harness
|
||||
|
||||
|
||||
def fbits(v):
|
||||
return struct.pack('>f', v).hex()
|
||||
|
||||
|
||||
def dbits(v):
|
||||
return struct.pack('>d', v).hex()
|
||||
|
||||
|
||||
def f32(v):
|
||||
return struct.unpack('>f', struct.pack('>f', v))[0]
|
||||
|
||||
@@ -52,15 +44,15 @@ def step(hit=('miss',), ac=0, uc=0, ad=False, ud=False, busy=False, using=False,
|
||||
"""rot: the player's (xRot, yRot) from this step on (LocalPlayer.applyInput's hand sway)."""
|
||||
return dict(hit=list(hit), ac=ac, uc=uc, ad=ad, ud=ud, busy=busy, using=using,
|
||||
uses=uses or dict(entity='pass', block='pass', item='pass'), shrink=shrink, set=set,
|
||||
rot=None if rot is None else [fbits(rot[0]), fbits(rot[1])])
|
||||
rot=None if rot is None else [lab.fbits(rot[0]), lab.fbits(rot[1])])
|
||||
|
||||
|
||||
def case(steps, creative=False, main=None, off=None, blocks=None, haste=None, fatigue=None):
|
||||
return dict(creative=creative, main=main, off=off, haste=haste, fatigue=fatigue,
|
||||
blocks=[[*p, b] for p, b in (blocks or {}).items()], steps=steps,
|
||||
rust=dict(blocks={k: [fbits(v[0]), v[1]] for k, v in BLOCKS.items()},
|
||||
tools={k: [fbits(v[0]), v[1]] for k, v in TOOLS.items()},
|
||||
attack_speed={k: dbits(v) for k, v in SPEEDS.items()}))
|
||||
rust=dict(blocks={k: [lab.fbits(v[0]), v[1]] for k, v in BLOCKS.items()},
|
||||
tools={k: [lab.fbits(v[0]), v[1]] for k, v in TOOLS.items()},
|
||||
attack_speed={k: lab.dbits(v) for k, v in SPEEDS.items()}))
|
||||
|
||||
|
||||
def block_hit(pos, face=1):
|
||||
|
||||
@@ -72,12 +72,6 @@ def wait_for(endpoint, line, predicate, timeout=60, what=None):
|
||||
raise AssertionError(f'timed out waiting for {what or line}: {last}')
|
||||
|
||||
|
||||
def free_port():
|
||||
with socket.socket() as s:
|
||||
s.bind(('127.0.0.1', 0))
|
||||
return s.getsockname()[1]
|
||||
|
||||
|
||||
def health(relay_url):
|
||||
with urllib.request.urlopen(f'http://{relay_url}/health', timeout=5) as r:
|
||||
return json.loads(r.read())
|
||||
@@ -204,7 +198,7 @@ def leave(host, guest, name):
|
||||
|
||||
def start_relay(args, work):
|
||||
"""A relay on a free local port, as anyone would run their own."""
|
||||
port = free_port()
|
||||
port = lab.free_port()
|
||||
url = f'127.0.0.1:{port}'
|
||||
log = (work / 'relay.log').open('w+')
|
||||
process = subprocess.Popen([str(args.relay.resolve())], env=dict(os.environ, PORT=str(port)),
|
||||
|
||||
@@ -6,9 +6,7 @@ and absolute motion, sensitivity curve, cinematic smoothing, inversion,
|
||||
scroll accumulation and discrete scrolling), Entity.turn and ToggleKeyMapping,
|
||||
all driven with identical synthetic input and time sequences."""
|
||||
import json
|
||||
import math
|
||||
import random
|
||||
import struct
|
||||
|
||||
try:
|
||||
from . import lab, oracle_harness as harness
|
||||
@@ -16,14 +14,6 @@ except ImportError:
|
||||
import lab, oracle_harness as harness
|
||||
|
||||
|
||||
def fbits(v):
|
||||
return struct.pack('>f', v).hex()
|
||||
|
||||
|
||||
def dbits(v):
|
||||
return struct.pack('>d', v).hex()
|
||||
|
||||
|
||||
def timer_cases(rng):
|
||||
for n in range(700):
|
||||
tps = rng.choice([20.0, 20.0, 10.0, 60.0, 1.0, 144.0])
|
||||
@@ -38,12 +28,12 @@ def timer_cases(rng):
|
||||
now -= rng.randrange(0, 30)
|
||||
if kind == 'adv':
|
||||
mspt = rng.choice([None, None, None, 50.0, 25.0, 100.0, 500.0, 1000.0, 0.5, 5.0, 0.1])
|
||||
ops.append(['adv', now, None if mspt is None else fbits(mspt)])
|
||||
ops.append(['adv', now, None if mspt is None else lab.fbits(mspt)])
|
||||
elif kind == 'real':
|
||||
ops.append(['real', now])
|
||||
else:
|
||||
ops.append([kind, rng.random() < 0.5])
|
||||
yield dict(t='timer', tps=fbits(tps), start=start, ops=ops)
|
||||
yield dict(t='timer', tps=lab.fbits(tps), start=start, ops=ops)
|
||||
|
||||
|
||||
def rate_cases(rng):
|
||||
@@ -55,7 +45,7 @@ def rate_cases(rng):
|
||||
kind = rng.choice(['rate', 'freeze', 'step', 'stopstep', 'sprint', 'check', 'check', 'check', 'end', 'stopsprint',
|
||||
'tick', 'tick', 'tick'])
|
||||
if kind == 'rate':
|
||||
ops.append(['rate', fbits(rng.choice(rates))])
|
||||
ops.append(['rate', lab.fbits(rng.choice(rates))])
|
||||
elif kind == 'freeze':
|
||||
ops.append(['freeze', rng.random() < 0.5])
|
||||
elif kind == 'step':
|
||||
@@ -78,7 +68,7 @@ def scale_cases(rng):
|
||||
for uni in (False, True):
|
||||
sw, sh = rng.choice([(w, h), (max(w // 2, 1), max(h // 2, 1)), (w + 1, h + 3)])
|
||||
yield dict(t='scale', w=w, h=h, max=maxs, uni=uni, sw=sw, sh=sh,
|
||||
x=dbits(rng.uniform(0, w)), y=dbits(rng.uniform(0, h)))
|
||||
x=lab.dbits(rng.uniform(0, w)), y=lab.dbits(rng.uniform(0, h)))
|
||||
|
||||
|
||||
def limit_cases(rng):
|
||||
@@ -103,13 +93,13 @@ def mouse_cases(rng):
|
||||
if kind == 'move':
|
||||
x, y = rng.uniform(0, 1920), rng.uniform(0, 1080)
|
||||
big = rng.choice([1, 1, 1, 50, 5000])
|
||||
ops.append(['move', dbits(x), dbits(y), dbits(rng.uniform(-30, 30) * big), dbits(rng.uniform(-30, 30) * big)])
|
||||
ops.append(['move', lab.dbits(x), lab.dbits(y), lab.dbits(rng.uniform(-30, 30) * big), lab.dbits(rng.uniform(-30, 30) * big)])
|
||||
elif kind == 'frame':
|
||||
time += rng.choice([0.0, 0.001, 0.0069, 0.016, 0.0167, 0.05, 0.5, 2.0])
|
||||
ops.append(['frame', dbits(time)])
|
||||
ops.append(['frame', lab.dbits(time)])
|
||||
else:
|
||||
ops.append(['ignore'])
|
||||
yield dict(t='mouse', sens=dbits(sens), smooth=rng.random() < 0.4, scoping=rng.random() < 0.3,
|
||||
yield dict(t='mouse', sens=lab.dbits(sens), smooth=rng.random() < 0.4, scoping=rng.random() < 0.3,
|
||||
ix=rng.random() < 0.3, iy=rng.random() < 0.3, grabbed=rng.random() < 0.85, active=rng.random() < 0.9, ops=ops)
|
||||
|
||||
|
||||
@@ -119,14 +109,14 @@ def scroll_cases(rng):
|
||||
for _ in range(rng.randrange(1, 40)):
|
||||
x = rng.choice([0.0, 0.0, 0.0, 1.0, -1.0, 0.25, -0.25, 0.4, 2.0, 5.5, -7.0, rng.uniform(-3, 3)])
|
||||
y = rng.choice([0.0, 1.0, -1.0, 1.0, -1.0, 0.1, -0.3, 0.5, -0.5, 2.0, -3.0, 120.0, rng.uniform(-3, 3)])
|
||||
ops.append([dbits(x), dbits(y)])
|
||||
case = dict(t='scroll', discrete=rng.random() < 0.4, sens=dbits(rng.choice([1.0, 0.01, 10.0, 0.5, 2.0, 3.1622776601683795])),
|
||||
ops.append([lab.dbits(x), lab.dbits(y)])
|
||||
case = dict(t='scroll', discrete=rng.random() < 0.4, sens=lab.dbits(rng.choice([1.0, 0.01, 10.0, 0.5, 2.0, 3.1622776601683795])),
|
||||
slot=rng.randrange(9), ops=ops)
|
||||
yield case
|
||||
yield dict(case, spectator=True)
|
||||
# Bounds, recovery from each bound, fractional accumulation, and horizontal-only input.
|
||||
yield dict(t='scroll', discrete=False, sens=dbits(1.0), slot=4, spectator=True,
|
||||
ops=[[dbits(x), dbits(y)] for x, y in
|
||||
yield dict(t='scroll', discrete=False, sens=lab.dbits(1.0), slot=4, spectator=True,
|
||||
ops=[[lab.dbits(x), lab.dbits(y)] for x, y in
|
||||
[(0, 100), (0, -1), (0, -100), (0, 1), (0, .4), (0, .7),
|
||||
(0, -.4), (0, -.7), (5, 0), (-5, 0)]])
|
||||
|
||||
|
||||
@@ -109,15 +109,9 @@ BENIGN = re.compile(r'(Unpacking |OFFLINE/INSECURE|online-mode|authenticate user
|
||||
|
||||
# ---------------------------------------------------------------- vanilla server
|
||||
|
||||
def free_port():
|
||||
with socket.socket() as s:
|
||||
s.bind(('127.0.0.1', 0))
|
||||
return s.getsockname()[1]
|
||||
|
||||
|
||||
class Vanilla:
|
||||
def __init__(self, directory, port=None, world='world', extra=None):
|
||||
port = port or free_port()
|
||||
port = port or lab.free_port()
|
||||
directory.mkdir(parents=True, exist_ok=True)
|
||||
(directory / 'eula.txt').write_text('eula=true\n')
|
||||
props = {
|
||||
|
||||
@@ -41,12 +41,12 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
|
||||
try:
|
||||
from . import lab, oracle_harness as oh, test_rust_parity as base
|
||||
from .test_dedicated_server_parity import Server, free_port
|
||||
from .test_dedicated_server_parity import Server
|
||||
except ImportError:
|
||||
import lab
|
||||
import oracle_harness as oh
|
||||
import test_rust_parity as base
|
||||
from test_dedicated_server_parity import Server, free_port
|
||||
from test_dedicated_server_parity import Server
|
||||
|
||||
NAME = 'secure-chat-parity'
|
||||
SCENARIOS = {
|
||||
@@ -153,20 +153,10 @@ def dashed(u):
|
||||
return f'{u[:8]}-{u[8:12]}-{u[12:16]}-{u[16:20]}-{u[20:]}'
|
||||
|
||||
|
||||
def varint(n):
|
||||
out = b''
|
||||
while True:
|
||||
b = n & 0x7F
|
||||
n >>= 7
|
||||
out += bytes([b | (0x80 if n else 0)])
|
||||
if not n:
|
||||
return out
|
||||
|
||||
|
||||
def status(port):
|
||||
"""The status response's JSON (ServerStatus)."""
|
||||
def packet(body):
|
||||
return varint(len(body)) + body
|
||||
return lab.varint(len(body)) + body
|
||||
|
||||
def read_varint(s):
|
||||
value = 0
|
||||
@@ -177,7 +167,7 @@ def status(port):
|
||||
return value
|
||||
with socket.create_connection(('127.0.0.1', port), timeout=10) as s:
|
||||
host = b'127.0.0.1'
|
||||
s.sendall(packet(b'\x00' + varint(777) + varint(len(host)) + host + port.to_bytes(2, 'big') + b'\x01'))
|
||||
s.sendall(packet(b'\x00' + lab.varint(777) + lab.varint(len(host)) + host + port.to_bytes(2, 'big') + b'\x01'))
|
||||
s.sendall(packet(b'\x00'))
|
||||
length = read_varint(s)
|
||||
data = b''
|
||||
@@ -212,7 +202,7 @@ def chat_lines(lines):
|
||||
|
||||
|
||||
def run_side(label, scenario, command, directory, client):
|
||||
port = free_port()
|
||||
port = lab.free_port()
|
||||
shutil.rmtree(directory, ignore_errors=True)
|
||||
directory.mkdir(parents=True)
|
||||
props = dict(PROPERTIES, **SCENARIOS[scenario], **{'server-port': str(port)})
|
||||
|
||||
@@ -96,17 +96,6 @@ def run_side(label, server_command, client_command, build, world):
|
||||
SHOWN = ('add', 'remove', 'respawn', 'track', 'untrack', 'update')
|
||||
|
||||
|
||||
def settled(lines):
|
||||
"""One player's lines of one step, in order, with the waypoint packets reduced to the last for each waypoint:
|
||||
what the locator bar shows once the step is over. Arriving in a level, vanilla sends the same waypoint more
|
||||
than once on the way there; the packet by packet order is test_locator_bar_parity's."""
|
||||
def waypoint(line):
|
||||
parts = line.split(' ')
|
||||
return parts[3] if len(parts) > 3 and parts[2] in ('track', 'untrack', 'update') else None
|
||||
last = {waypoint(l): i for i, l in enumerate(lines) if waypoint(l)}
|
||||
return [l for i, l in enumerate(lines) if not waypoint(l) or last[waypoint(l)] == i]
|
||||
|
||||
|
||||
def shown(lines):
|
||||
"""Each player's adds, removes, respawns and waypoints (as the step leaves them), step by step; sorted within a
|
||||
step (the packets of one step may leave in another order within the tick)."""
|
||||
@@ -115,7 +104,7 @@ def shown(lines):
|
||||
parts = line.split(' ', 2)
|
||||
if len(parts) == 3 and parts[2].split(' ')[0] in SHOWN:
|
||||
steps.setdefault((parts[0], parts[1]), []).append(line)
|
||||
return [l for key in steps for l in sorted(settled(steps[key]))]
|
||||
return [l for key in steps for l in sorted(lab.settled(steps[key]))]
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -15,7 +15,6 @@ in sorted order (the packets of one step may leave in another order within the t
|
||||
waypoints as each step leaves them (one manager per level: a player in another level is untracked). What a death does besides its messages
|
||||
is left to its own harness.
|
||||
"""
|
||||
import hashlib
|
||||
import os
|
||||
from pathlib import Path
|
||||
import platform
|
||||
@@ -23,7 +22,6 @@ import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import uuid as uuidlib
|
||||
|
||||
try:
|
||||
from . import lab, test_rust_parity as base
|
||||
@@ -39,20 +37,12 @@ NAME = 'server-player-targets-parity'
|
||||
NETHER, OVERWORLD = 'minecraft:the_nether', 'minecraft:overworld'
|
||||
|
||||
|
||||
def offline_uuid(name):
|
||||
"""UUIDUtil.createOfflinePlayerUUID."""
|
||||
digest = bytearray(hashlib.md5(f'OfflinePlayer:{name}'.encode()).digest())
|
||||
digest[6] = (digest[6] & 0x0F) | 0x30
|
||||
digest[8] = (digest[8] & 0x3F) | 0x80
|
||||
return str(uuidlib.UUID(bytes=bytes(digest)))
|
||||
|
||||
|
||||
# (label, what the console does, what the players do).
|
||||
SCRIPT = [
|
||||
('console-give', ['give Bob minecraft:diamond 3'], []),
|
||||
('console-give-all', ['give @a minecraft:apple'], []),
|
||||
('alice-give-bob', [], ['cmd Alice give Bob minecraft:stick 2']),
|
||||
('console-give-uuid', [f'give {offline_uuid("Bob")} minecraft:iron_ingot'], []),
|
||||
('console-give-uuid', [f'give {lab.offline_uuid("Bob")} minecraft:iron_ingot'], []),
|
||||
('console-give-nearest', ['give @p minecraft:emerald'], []),
|
||||
('alice-give-random', [], ['cmd Alice give @r[name=Bob] minecraft:gold_ingot']),
|
||||
('console-gamemode', ['gamemode survival Bob'], []),
|
||||
@@ -83,17 +73,6 @@ DEATH_STEP = 'step=console-kill'
|
||||
STEP_SECONDS = 2.5
|
||||
|
||||
|
||||
def settled(lines):
|
||||
"""One player's lines of one step, in order, with the waypoint packets reduced to the last for each waypoint:
|
||||
what the locator bar shows once the step is over. Arriving in a level, vanilla sends the same waypoint more
|
||||
than once on the way there; the packet by packet order is test_locator_bar_parity's."""
|
||||
def waypoint(line):
|
||||
parts = line.split(' ')
|
||||
return parts[3] if len(parts) > 3 and parts[2] in ('track', 'untrack', 'update') else None
|
||||
last = {waypoint(l): i for i, l in enumerate(lines) if waypoint(l)}
|
||||
return [l for i, l in enumerate(lines) if not waypoint(l) or last[waypoint(l)] == i]
|
||||
|
||||
|
||||
def run_side(label, server_command, client_command, build, world):
|
||||
directory = build / label
|
||||
port = server_dir(directory, world)
|
||||
@@ -151,7 +130,7 @@ def run_side(label, server_command, client_command, build, world):
|
||||
steps.setdefault((who, step), []).append(line)
|
||||
order = {f'step={s}': i for i, (s, _, _) in enumerate(SCRIPT)}
|
||||
keys = sorted(steps, key=lambda k: (k[0], order.get(k[1], -1)))
|
||||
return [l for k in keys for l in sorted(settled(steps[k]))], errors
|
||||
return [l for k in keys for l in sorted(lab.settled(steps[k]))], errors
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -18,14 +18,10 @@ import math
|
||||
import random
|
||||
|
||||
try:
|
||||
from . import audio_harness as h
|
||||
from . import audio_harness as h, lab
|
||||
except ImportError:
|
||||
import audio_harness as h
|
||||
|
||||
|
||||
def dbits(v):
|
||||
import struct
|
||||
return struct.pack('>d', v).hex()
|
||||
import lab
|
||||
|
||||
|
||||
class Script:
|
||||
@@ -37,11 +33,11 @@ class Script:
|
||||
self.lines.append(' '.join(map(str, parts)))
|
||||
|
||||
def play(self, name, event, source=4, volume=1.0, pitch=1.0, at=(0.0, 0.0, 0.0), looping=0, delay=0, linear=True, relative=0, seed=0, kind='simple'):
|
||||
self.add('play', name, kind, event, source, h.bits(volume), h.bits(pitch), *map(dbits, at), looping, delay,
|
||||
self.add('play', name, kind, event, source, h.bits(volume), h.bits(pitch), *map(lab.dbits, at), looping, delay,
|
||||
'linear' if linear else 'none', relative, seed)
|
||||
|
||||
def cart(self, name, at, delta, removed=0):
|
||||
self.add('cart', name, *map(dbits, at), *map(dbits, delta), removed)
|
||||
self.add('cart', name, *map(lab.dbits, at), *map(lab.dbits, delta), removed)
|
||||
|
||||
def tick(self, n=1, paused=0):
|
||||
for _ in range(n):
|
||||
|
||||
@@ -9,7 +9,6 @@ level.dat, player and data/*.dat files) is applied exactly. The original is kept
|
||||
upgrade_world.py SOURCE_WORLD DEST_WORLD
|
||||
"""
|
||||
import shutil
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
@@ -24,12 +23,6 @@ ROOT = Path(__file__).resolve().parent.parent
|
||||
SERVER = lab.GAME / 'jars' / 'server.jar'
|
||||
|
||||
|
||||
def free_port():
|
||||
with socket.socket() as s:
|
||||
s.bind(('127.0.0.1', 0))
|
||||
return s.getsockname()[1]
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
sys.exit(__doc__)
|
||||
@@ -42,7 +35,7 @@ def main():
|
||||
scratch = Path(scratch)
|
||||
(scratch / 'eula.txt').write_text('eula=true\n')
|
||||
(scratch / 'server.properties').write_text(
|
||||
f'level-name=world\nonline-mode=false\nserver-port={free_port()}\n')
|
||||
f'level-name=world\nonline-mode=false\nserver-port={lab.free_port()}\n')
|
||||
shutil.copytree(source, scratch / 'world')
|
||||
# The server upgrades at startup, then keeps running as a normal server: stop it
|
||||
# once it reports "Done".
|
||||
|
||||
+1
-10
@@ -14,12 +14,10 @@ that skips the first-run screens, keeps the game running unfocused and
|
||||
renders few chunks, so a harness can drive it with vscreen keys.
|
||||
"""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import uuid
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
@@ -56,13 +54,6 @@ if sys.platform == "darwin":
|
||||
OPTIONS["enableVsync"] = "false"
|
||||
|
||||
|
||||
def offline_uuid(name):
|
||||
digest = bytearray(hashlib.md5(("OfflinePlayer:" + name).encode()).digest())
|
||||
digest[6] = (digest[6] & 0x0F) | 0x30
|
||||
digest[8] = (digest[8] & 0x3F) | 0x80
|
||||
return uuid.UUID(bytes=bytes(digest))
|
||||
|
||||
|
||||
def host_library(path):
|
||||
name = Path(path).name
|
||||
if "natives-" in name:
|
||||
@@ -127,7 +118,7 @@ def command(args):
|
||||
f"-Djava.library.path={natives}", f"-Dorg.lwjgl.system.SharedLibraryExtractPath={natives}",
|
||||
f"-Dio.netty.native.workdir={natives}",
|
||||
"-cp", os.pathsep.join(map(str, jars)), "net.minecraft.client.main.Main",
|
||||
"--username", args.name, "--uuid", offline_uuid(args.name).hex,
|
||||
"--username", args.name, "--uuid", lab.offline_uuid(args.name).hex,
|
||||
"--accessToken", "0", "--offlineDeveloperMode",
|
||||
"--version", lab.VERSION, "--gameDir", str(args.game_dir),
|
||||
"--assetsDir", str(assets), "--assetIndex", json.loads((GAME / "manifest.json").read_text())["assetIndex"]["id"],
|
||||
|
||||
+12
-43
@@ -11,12 +11,10 @@ its log reports the position it read, and console commands such as
|
||||
`data get entity NAME` can inspect the loaded player meanwhile.
|
||||
"""
|
||||
import argparse
|
||||
import hashlib
|
||||
import socket
|
||||
import struct
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
import zlib
|
||||
|
||||
try:
|
||||
@@ -25,27 +23,9 @@ except ImportError:
|
||||
import lab
|
||||
|
||||
|
||||
def offline_uuid(name):
|
||||
digest = bytearray(hashlib.md5(('OfflinePlayer:' + name).encode()).digest())
|
||||
digest[6] = (digest[6] & 0x0F) | 0x30
|
||||
digest[8] = (digest[8] & 0x3F) | 0x80
|
||||
return uuid.UUID(bytes=bytes(digest))
|
||||
|
||||
|
||||
def varint(v):
|
||||
out = b''
|
||||
v &= 0xFFFFFFFF
|
||||
while True:
|
||||
b = v & 0x7F
|
||||
v >>= 7
|
||||
out += bytes([b | (0x80 if v else 0)])
|
||||
if not v:
|
||||
return out
|
||||
|
||||
|
||||
def string(s):
|
||||
data = s.encode()
|
||||
return varint(len(data)) + data
|
||||
return lab.varint(len(data)) + data
|
||||
|
||||
|
||||
class Connection:
|
||||
@@ -73,44 +53,33 @@ class Connection:
|
||||
return value
|
||||
|
||||
def send(self, packet_id, payload=b''):
|
||||
body = varint(packet_id) + payload
|
||||
body = lab.varint(packet_id) + payload
|
||||
if self.threshold >= 0:
|
||||
if len(body) >= self.threshold:
|
||||
body = varint(len(body)) + zlib.compress(body)
|
||||
body = lab.varint(len(body)) + zlib.compress(body)
|
||||
else:
|
||||
body = varint(0) + body
|
||||
self.sock.sendall(varint(len(body)) + body)
|
||||
body = lab.varint(0) + body
|
||||
self.sock.sendall(lab.varint(len(body)) + body)
|
||||
|
||||
def receive(self):
|
||||
length = self.read_varint()
|
||||
body = self.read_exact(length)
|
||||
if self.threshold >= 0:
|
||||
pos, size = parse_varint(body, 0)
|
||||
pos, size = lab.parse_varint(body, 0)
|
||||
body = zlib.decompress(body[pos:]) if size else body[pos:]
|
||||
pos, packet_id = parse_varint(body, 0)
|
||||
pos, packet_id = lab.parse_varint(body, 0)
|
||||
return packet_id, body[pos:]
|
||||
|
||||
|
||||
def parse_varint(data, pos):
|
||||
value = shift = 0
|
||||
while True:
|
||||
b = data[pos]
|
||||
pos += 1
|
||||
value |= (b & 0x7F) << shift
|
||||
shift += 7
|
||||
if not b & 0x80:
|
||||
return pos, value
|
||||
|
||||
|
||||
def join(host, port, name, seconds):
|
||||
c = Connection(host, port)
|
||||
c.send(0, varint(lab.version_info()['protocol_version']) + string(host) + struct.pack('>H', port) + varint(2))
|
||||
c.send(0, string(name) + offline_uuid(name).bytes)
|
||||
c.send(0, lab.varint(lab.version_info()['protocol_version']) + string(host) + struct.pack('>H', port) + lab.varint(2))
|
||||
c.send(0, string(name) + lab.offline_uuid(name).bytes)
|
||||
# Login: compression, then login finished.
|
||||
while True:
|
||||
pid, data = c.receive()
|
||||
if pid == 3:
|
||||
c.threshold = parse_varint(data, 0)[1]
|
||||
c.threshold = lab.parse_varint(data, 0)[1]
|
||||
elif pid == 2:
|
||||
break
|
||||
elif pid == 0:
|
||||
@@ -119,7 +88,7 @@ def join(host, port, name, seconds):
|
||||
while True:
|
||||
pid, data = c.receive()
|
||||
if pid == 15: # select known packs: claim none, receive full registries
|
||||
c.send(7, varint(0))
|
||||
c.send(7, lab.varint(0))
|
||||
elif pid == 4: # keep alive
|
||||
c.send(4, data)
|
||||
elif pid == 5: # ping
|
||||
@@ -153,7 +122,7 @@ def main():
|
||||
p.add_argument('--uuid', action='store_true')
|
||||
args = p.parse_args()
|
||||
if args.uuid:
|
||||
print(offline_uuid(args.name))
|
||||
print(lab.offline_uuid(args.name))
|
||||
return 0
|
||||
join(args.host, args.port, args.name, args.seconds)
|
||||
return 0
|
||||
|
||||
@@ -17,12 +17,16 @@ load failures.
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
try:
|
||||
from . import lab
|
||||
except ImportError:
|
||||
import lab
|
||||
|
||||
SUSPICIOUS = re.compile(
|
||||
r'(ERROR|login failed|Exception|Couldn\'t load|Failed to|Skipping|Invalid|Unknown|corrupt|'
|
||||
r'Recreating|upgrad|DataFix|out of bounds|missing|Mismatch)', re.IGNORECASE)
|
||||
@@ -31,12 +35,6 @@ BENIGN = re.compile(r'(Unpacking |OFFLINE/INSECURE|online-mode|authenticate user
|
||||
r'Failed to load properties|Failed to load eula)')
|
||||
|
||||
|
||||
def free_port():
|
||||
with socket.socket() as s:
|
||||
s.bind(('127.0.0.1', 0))
|
||||
return s.getsockname()[1]
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser()
|
||||
p.add_argument('--jar', required=True)
|
||||
@@ -48,7 +46,7 @@ def main():
|
||||
p.add_argument('--wait-after', type=float, default=1.0)
|
||||
p.add_argument('--timeout', type=float, default=240)
|
||||
args = p.parse_args()
|
||||
port = args.port or free_port()
|
||||
port = args.port or lab.free_port()
|
||||
os.makedirs(args.dir, exist_ok=True)
|
||||
with open(os.path.join(args.dir, 'eula.txt'), 'w') as f:
|
||||
f.write('eula=true\n')
|
||||
|
||||
Reference in New Issue
Block a user