### Install Endweave Plugin Source: https://context7.com/endstonemc/endweave/llms.txt To install Endweave, copy the .whl file to your server's plugins directory and restart the server. The plugin automatically enables and detects the server's protocol version. ```bash # Copy the wheel into the plugins folder and restart the server cp endstone_endweave-*.whl /path/to/server/plugins/ # Restart Endstone — the plugin auto-enables and logs the detected server protocol # [Endweave] Detected server protocol 944 (MC 1.26.10) # [Endweave] Supported client versions: 1.21.120 - 1.26.20 ``` -------------------------------- ### Initialize Per-Connection State with Protocol.init() Source: https://context7.com/endstonemc/endweave/llms.txt Override `init` in a subclass to store per-player state that handlers can access via `wrapper.user.get()`. This state is cached for the session. ```python from dataclasses import dataclass from endstone_endweave.protocol import Protocol from endstone_endweave.connection import UserConnection @dataclass class CameraState: has_spline_id: bool = False class MyProtocol(Protocol): def init(self, connection: UserConnection) -> None: # Store per-player state; retrieve in handlers via wrapper.user.get(CameraState) connection.put(CameraState(has_spline_id=False)) def handle_camera(wrapper): state = wrapper.user.get(CameraState) if state and state.has_spline_id: wrapper.passthrough(STRING) # splineIdentifier field present in newer version ``` -------------------------------- ### EndweavePlugin: Main Plugin Entry Point Source: https://context7.com/endstonemc/endweave/llms.txt The EndweavePlugin class is the main entry point for the Endstone plugin. It handles plugin enablement by auto-detecting the server protocol, initializing the ProtocolManager and ProtocolPipeline, and registering necessary events. Configuration is managed via config.toml. ```python # src/endstone_endweave/plugin.py (abridged) from endstone.plugin import Plugin from endstone_endweave.connection import ConnectionManager from endstone_endweave.pipeline import ProtocolPipeline from endstone_endweave.protocol.manager import ProtocolManager from endstone_endweave.protocol.base import create_base_protocol from endstone_endweave.protocol.v924_to_v944 import create_protocol as v924_to_v944 class EndweavePlugin(Plugin): api_version = "0.11" def on_enable(self) -> None: self.save_default_config() server_protocol = self.server.protocol_version # e.g. 944 self._connections = ConnectionManager(server_protocol=server_protocol, logger=self.logger) self._manager = ProtocolManager() self._manager.register_base(create_base_protocol(server_protocol)) self._manager.register(v924_to_v944()) # register one step; plugin registers all steps self._pipeline = ProtocolPipeline(self._manager, self._connections, self.logger) self.register_events(self) # Server list ping is spoofed to the highest advertised version automatically ``` -------------------------------- ### Manage Version Graph and Resolve Chains with ProtocolManager Source: https://context7.com/endstonemc/endweave/llms.txt Register `Protocol` objects with `ProtocolManager` to build a version graph. Use `get_path` for multi-hop BFS chain resolution and `get_supported_versions` to find compatible clients. ```python from endstone_endweave.protocol.manager import ProtocolManager from endstone_endweave.protocol.v924_to_v944 import create_protocol as v924_v944 from endstone_endweave.protocol.v944_to_v975 import create_protocol as v944_v975 manager = ProtocolManager() manager.register(v924_v944()) manager.register(v944_v975()) # Direct lookup (single step) direct = manager.get(924, 944) # → Protocol "924->944" # Multi-hop BFS: client=924, server=975 — resolves through 944 chain = manager.get_path(975, 924) # → [Protocol "924->944", Protocol "944->975"] assert chain is not None and len(chain) == 2 # All clients that can reach server protocol 944 supported = manager.get_supported_versions(944) # → [924, 944, 975] (sorted) print(f"Supported: {supported}") ``` -------------------------------- ### Endweave config.toml Settings Source: https://context7.com/endstonemc/endweave/llms.txt The config.toml file allows runtime configuration of Endweave. Key settings include enabling update checks and controlling debug logging verbosity, including packet filtering by ID and whether to log packets after transformation. ```toml # plugins/endweave/config.toml # Poll GitHub releases on startup and notify ops on join check-for-updates = true [debug] # Enable verbose packet translation logging enabled = false # Restrict logging to specific Bedrock packet IDs (empty = all packets) # Packet 11 = START_GAME, 193 = SOUND_EVENT packets = [11, 193] # Also log packets after transformation (pre-transform is always logged) log_post_transform = false ``` -------------------------------- ### Implement Custom Protocol Step for Version Compatibility Source: https://context7.com/endstonemc/endweave/llms.txt Create a custom `Protocol` by defining a `create_protocol()` factory function. Register handlers for specific packet IDs and use `cancel_serverbound` to disable unwanted packets. The `PacketWrapper` API allows reading and writing packet data for translation. ```python # my_protocol/protocol.py from endstone_endweave.protocol import Protocol from endstone_endweave.protocol.packet_ids import PacketId from endstone_endweave.codec.wrapper import PacketWrapper from endstone_endweave.codec.types import UVAR_INT, STRING, BOOL SERVER_PROTOCOL = 975 CLIENT_PROTOCOL = 1000 # hypothetical new version def _rewrite_start_game(wrapper: PacketWrapper) -> None: """Strip a new boolean flag added in v1000 before forwarding to v975 server.""" wrapper.passthrough(UVAR_INT) # entity_id wrapper.passthrough(UVAR_INT) # runtime_entity_id wrapper.passthrough(UVAR_INT) # player_gamemode new_flag = wrapper.read(BOOL) # consume new v1000 field — not written to output # ... remaining fields pass through via PacketWrapper.to_bytes() def create_protocol() -> Protocol: p = Protocol(server_protocol=SERVER_PROTOCOL, client_protocol=CLIENT_PROTOCOL) p.register_serverbound(PacketId.START_GAME, _rewrite_start_game) p.cancel_serverbound(PacketId.NEW_V1000_ONLY_PACKET) return p # Register in EndweavePlugin.on_enable(): # from my_protocol.protocol import create_protocol as create_v975_to_v1000 # self._manager.register(create_v975_to_v1000()) ``` -------------------------------- ### Manage Per-Player State with ConnectionManager Source: https://context7.com/endstonemc/endweave/llms.txt ConnectionManager maps network addresses to UserConnection instances, which hold per-player state like client protocol and cached protocol pipelines. Use type-keyed storage for handler-local state. ```python from endstone_endweave.connection import ConnectionManager, UserConnection manager = ConnectionManager(server_protocol=944, logger=logger) # Get-or-create on first packet from an address conn: UserConnection = manager.get_or_create("192.168.1.5:19132") conn.client_protocol = 924 # set after RequestNetworkSettings is decoded print(conn.needs_translation) # True (924 != 944) print(conn.server_protocol) # 944 # Type-keyed per-connection storage from dataclasses import dataclass @dataclass class SoundState: remap_fn: object conn.put(SoundState(remap_fn=lambda v: v + 12)) state = conn.get(SoundState) # → SoundState instance conn.has(SoundState) # True conn.remove(SoundState) # Clean up when a player disconnects manager.remove_by_address("192.168.1.5:19132") # or, using the Endstone Player object: # manager.remove_by_player(player) — uses str(player.address) internally ``` -------------------------------- ### Wrap Exceptions with Context using InformativeException Source: https://context7.com/endstonemc/endweave/llms.txt Use InformativeException to wrap original exceptions and add ordered key-value context pairs for richer error reporting. The pipeline automatically attaches direction, packet ID, protocol name, and address. Suppress duplicate logs for known non-fatal errors by setting `should_be_printed = False`. ```python from endstone_endweave.exception import InformativeException try: risky_decode() except Exception as exc: err = ( InformativeException(exc) .set("Direction", "CLIENTBOUND") .set("Packet ID", "START_GAME(11) (0x0B)") .set("Protocol", "v924_to_v944") .set("Address", "10.0.0.1:19132") ) # err.message → # "Please report this on the Endweave GitHub repository # Direction: CLIENTBOUND, Packet ID: START_GAME(11) (0x0B), # Protocol: v924_to_v944, Address: 10.0.0.1:19132, Cause: ValueError: ..." logger.error(err.message) err.should_be_printed = False # suppress duplicate logs for known non-fatal errors ``` -------------------------------- ### Configure Filtered Packet Logging with DebugHandler Source: https://context7.com/endstonemc/endweave/llms.txt DebugHandler wraps the Endstone logger with optional packet-ID filtering and pre/post transform logging phases. Configure it via `config.toml` or construct directly for tests. Use `should_log` to check if a packet should be logged based on the configured filter. ```python from endstone_endweave.debug import DebugHandler, packet_label from endstone_endweave.protocol.packet_ids import PacketId # Construct from plugin config dict (mirrors config.toml [debug] section) cfg = {"debug": {"enabled": True, "packets": [11, 193], "log_post_transform": True}} handler = DebugHandler.from_config(logger, cfg) # Manual construction — log only START_GAME (11) pre and post transform handler = DebugHandler(logger, enabled=True, packets=frozenset([PacketId.START_GAME])) handler.should_log(PacketId.START_GAME) # True handler.should_log(PacketId.TEXT) # False (not in filter) # Log a packet event in structured format: # "PRE : 10.0.0.1:19132 CLIENTBOUND: START_GAME(11) (0x0B) [924] 1024b" handler.log_packet("PRE ", "10.0.0.1:19132", "CLIENTBOUND", PacketId.START_GAME, 924, 1024) # Convenience label formatter (used in error context and debug output) label = packet_label(PacketId.START_GAME) # → "START_GAME(11) (0x0B)" label = packet_label(9999) # → "9999 (0x270F)" ``` -------------------------------- ### Dispatch Packets with ProtocolPipeline Source: https://context7.com/endstonemc/endweave/llms.txt The `ProtocolPipeline` iterates through the version chain for each connection on packet events. It replaces `event.payload` with transformed bytes when changes occur. Errors are caught and logged. ```python from endstone_endweave.pipeline import ProtocolPipeline from endstone_endweave.connection import ConnectionManager from endstone_endweave.protocol.manager import ProtocolManager from endstone_endweave.debug import DebugHandler manager = ProtocolManager() # ... register protocols ... connections = ConnectionManager(server_protocol=944, logger=logger) debug = DebugHandler(logger, enabled=True, packets=frozenset([11])) # log START_GAME only pipeline = ProtocolPipeline(manager, connections, logger, debug) # Inside the plugin, these are wired to Endstone events: # @event_handler(priority=EventPriority.LOWEST) # def on_packet_receive(self, event): pipeline.on_packet_receive(event) # @event_handler(priority=EventPriority.LOWEST) # def on_packet_send(self, event): pipeline.on_packet_send(event) # Errors are caught per-packet; the packet is cancelled and the error is logged # with InformativeException context (direction, packet ID, protocol name, address). ``` -------------------------------- ### ConnectionManager and UserConnection: Per-Player State Source: https://context7.com/endstonemc/endweave/llms.txt ConnectionManager handles the mapping of network addresses to UserConnection instances. UserConnection stores client protocol information, the protocol pipeline, and a dictionary for handler-local state specific to each player. ```APIDOC ## `ConnectionManager` and `UserConnection` — per-player state `ConnectionManager` maps network addresses (`"host:port"`) to `UserConnection` instances. `UserConnection` holds the detected `client_protocol`, the cached `protocol_pipeline`, and a type-keyed storage dict for handler-local state. ```python from endstone_endweave.connection import ConnectionManager, UserConnection manager = ConnectionManager(server_protocol=944, logger=logger) # Get-or-create on first packet from an address conn: UserConnection = manager.get_or_create("192.168.1.5:19132") conn.client_protocol = 924 # set after RequestNetworkSettings is decoded print(conn.needs_translation) # True (924 != 944) print(conn.server_protocol) # 944 # Type-keyed per-connection storage from dataclasses import dataclass @dataclass class SoundState: remap_fn: object conn.put(SoundState(remap_fn=lambda v: v + 12)) state = conn.get(SoundState) # → SoundState instance conn.has(SoundState) # True conn.remove(SoundState) # Clean up when a player disconnects manager.remove_by_address("192.168.1.5:19132") # or, using the Endstone Player object: # manager.remove_by_player(player) — uses str(player.address) internally ``` ``` -------------------------------- ### PacketWrapper.map(): Encode-Conversion Shorthand Source: https://context7.com/endstonemc/endweave/llms.txt The `map` method provides a concise way to read a field with one type and write it back with a different type, which is particularly useful when only the wire encoding has changed between protocol versions. ```APIDOC ## `PacketWrapper.map()` — encode-conversion shorthand `map` reads a field with one `Type` and writes it back with a different `Type`, useful when only the wire encoding changed between versions (e.g. signed vs unsigned int). ```python from endstone_endweave.codec.wrapper import PacketWrapper from endstone_endweave.codec.types import VAR_INT, UVAR_INT, INT_LE, FLOAT_LE def rewrite_coordinate_encoding(wrapper: PacketWrapper) -> None: # Older version encoded Y as signed varint; newer as unsigned varint wrapper.map(VAR_INT, UVAR_INT) # read signed, write unsigned — value preserved # Or re-encode a little-endian int as a float wrapper.map(INT_LE, FLOAT_LE) ``` ``` -------------------------------- ### MappingData and IdShift: Declarative ID Remapping Source: https://context7.com/endstonemc/endweave/llms.txt MappingData defines version-specific enum ID shifts for various game elements like sounds and actor events. IdShift provides utilities to upgrade, downgrade, or cap these IDs, facilitating compatibility between different protocol versions. ```APIDOC ## `MappingData` and `IdShift` — declarative ID remapping `MappingData` encodes per-version-step enum ID shifts for sounds, actor events, and note block instruments. `IdShift` provides `shift_up` (upgrade path), `shift_down` (downgrade path), and `cap` (lossy downgrade) operations. These are consumed by `SoundRewriter` and `ActorDataRewriter` utilities. ```python from endstone_endweave.protocol.mapping_data import MappingData, IdShift, inserted from endstone_endweave.codec.types.enums import LevelSoundEvent # Define a mapping step: 12 new sound IDs were inserted at UNDEFINED_V860 MAPPINGS = MappingData( sound=inserted(12, at=LevelSoundEvent.UNDEFINED_V860), actor_data_sound_key=10, # ActorDataID that carries a LevelSoundEvent value ) # Upgrade: client is newer, remap old IDs up old_id = 50 new_id = MAPPINGS.sound.shift_up(old_id) # If old_id >= insert_at → new_id = old_id + 12; otherwise unchanged # Downgrade: server is older, remap new IDs back new_id_from_client = 62 server_id = MAPPINGS.sound.shift_down(new_id_from_client) # IDs in the inserted range collapse to insert_at (Undefined in old version) # Cap: newer ID that has no old equivalent → clamp to Undefined capped = MAPPINGS.sound.cap(999) # → LevelSoundEvent.UNDEFINED_V860 ``` ``` -------------------------------- ### Encode-Conversion Shorthand with PacketWrapper.map() Source: https://context7.com/endstonemc/endweave/llms.txt Use PacketWrapper.map() to read a field with one type and write it back with a different type, useful for wire encoding changes between versions. This preserves the value while changing its representation. ```python from endstone_endweave.codec.wrapper import PacketWrapper from endstone_endweave.codec.types import VAR_INT, UVAR_INT, INT_LE, FLOAT_LE def rewrite_coordinate_encoding(wrapper: PacketWrapper) -> None: # Older version encoded Y as signed varint; newer as unsigned varint wrapper.map(VAR_INT, UVAR_INT) # read signed, write unsigned — value preserved # Or re-encode a little-endian int as a float wrapper.map(INT_LE, FLOAT_LE) ``` -------------------------------- ### Rewrite Text Packet with Protocol Source: https://context7.com/endstonemc/endweave/llms.txt Register a handler to rewrite a specific packet type between protocol versions. Unregistered packets pass through unchanged. Use `cancel_clientbound` or `cancel_serverbound` to drop packets. ```python from endstone_endweave.protocol import Protocol from endstone_endweave.protocol.packet_ids import PacketId from endstone_endweave.codec.wrapper import PacketWrapper from endstone_endweave.codec.types import STRING, UVAR_INT def rewrite_text_packet(wrapper: PacketWrapper) -> None: """Translate a v944 Text packet to v924 format.""" wrapper.passthrough(UVAR_INT) # type byte — copy unchanged wrapper.passthrough(UVAR_INT) # needs_translation flag source = wrapper.read(STRING) # consume old sender field wrapper.write(STRING, source.upper()) # re-write with transformation # remaining bytes (message body) pass through automatically via to_bytes() p = Protocol(server_protocol=924, client_protocol=944, name="v924_to_v944") p.register_clientbound(PacketId.TEXT, rewrite_text_packet) p.cancel_serverbound(PacketId.EDITOR_NETWORK) # v924 can't handle this packet # Check registration assert p.has_handler_or_cancel(Direction.CLIENTBOUND, PacketId.TEXT) assert p.has_handler_or_cancel(Direction.SERVERBOUND, PacketId.EDITOR_NETWORK) ``` -------------------------------- ### Declarative ID Remapping with MappingData and IdShift Source: https://context7.com/endstonemc/endweave/llms.txt Use MappingData to define enum ID shifts between versions and IdShift to perform upgrade, downgrade, or capping operations. These are consumed by utilities like SoundRewriter and ActorDataRewriter. ```python from endstone_endweave.protocol.mapping_data import MappingData, IdShift, inserted from endstone_endweave.codec.types.enums import LevelSoundEvent # Define a mapping step: 12 new sound IDs were inserted at UNDEFINED_V860 MAPPINGS = MappingData( sound=inserted(12, at=LevelSoundEvent.UNDEFINED_V860), actor_data_sound_key=10, # ActorDataID that carries a LevelSoundEvent value ) # Upgrade: client is newer, remap old IDs up old_id = 50 new_id = MAPPINGS.sound.shift_up(old_id) # If old_id >= insert_at → new_id = old_id + 12; otherwise unchanged # Downgrade: server is older, remap new IDs back new_id_from_client = 62 server_id = MAPPINGS.sound.shift_down(new_id_from_client) # IDs in the inserted range collapse to insert_at (Undefined in old version) # Cap: newer ID that has no old equivalent → clamp to Undefined capped = MAPPINGS.sound.cap(999) # → LevelSoundEvent.UNDEFINED_V860 ``` -------------------------------- ### PacketWrapper: Field-Level Packet Transformation Source: https://context7.com/endstonemc/endweave/llms.txt PacketWrapper allows for granular modification of packet payloads by providing read and write cursors. It supports selective copying, consuming, and injecting of fields, with unread bytes automatically appended to the output. ```APIDOC ## `PacketWrapper` — field-level packet transformation API `PacketWrapper` wraps a raw `bytes` payload with separate read and write cursors. The three core operations — `passthrough`, `read`, and `write` — let handlers selectively copy, consume, or inject fields. Unread bytes are automatically appended to the output by `to_bytes()`. ```python from endstone_endweave.codec.wrapper import PacketWrapper from endstone_endweave.codec.types import ( UVAR_INT, VAR_INT, STRING, BOOL, FLOAT_LE, NETWORK_BLOCK_POS, BLOCK_POS, ) # Example: translate a BlockActorData packet from v944 (BlockPos) to v924 (NetworkBlockPos) def rewrite_block_actor_data(wrapper: PacketWrapper) -> None: x, y, z = wrapper.read(BLOCK_POS) # consume v944 BlockPos (3× varint) wrapper.write(NETWORK_BLOCK_POS, (x, y, z)) # emit v924 NetworkBlockPos (varint, uvarint, varint) # remaining NBT payload passes through automatically # Standalone usage (e.g. unit tests) raw_payload = b'\x05\x0a\x05' # packed varints for x=2, y=5, z=2 wrapper = PacketWrapper(raw_payload) x, y, z = wrapper.read(BLOCK_POS) wrapper.write(NETWORK_BLOCK_POS, (x, y, z)) result = wrapper.to_bytes() # → re-encoded bytes in NetworkBlockPos format # Cancel a packet entirely (it will be dropped by the pipeline) def drop_packet(wrapper: PacketWrapper) -> None: wrapper.cancel() # wrapper.cancelled == True; pipeline calls event.cancel() ``` ``` -------------------------------- ### Transform Packet Fields with PacketWrapper Source: https://context7.com/endstonemc/endweave/llms.txt Use PacketWrapper to selectively read, write, or pass through fields in a packet payload. Unread bytes are automatically appended to the output. Use wrapper.cancel() to drop a packet. ```python from endstone_endweave.codec.wrapper import PacketWrapper from endstone_endweave.codec.types import ( UVAR_INT, VAR_INT, STRING, BOOL, FLOAT_LE, NETWORK_BLOCK_POS, BLOCK_POS, ) # Example: translate a BlockActorData packet from v944 (BlockPos) to v924 (NetworkBlockPos) def rewrite_block_actor_data(wrapper: PacketWrapper) -> None: x, y, z = wrapper.read(BLOCK_POS) # consume v944 BlockPos (3× varint) wrapper.write(NETWORK_BLOCK_POS, (x, y, z)) # emit v924 NetworkBlockPos (varint, uvarint, varint) # remaining NBT payload passes through automatically # Standalone usage (e.g. unit tests) raw_payload = b'\x05\x0a\x05' # packed varints for x=2, y=5, z=2 wrapper = PacketWrapper(raw_payload) x, y, z = wrapper.read(BLOCK_POS) wrapper.write(NETWORK_BLOCK_POS, (x, y, z)) result = wrapper.to_bytes() # → re-encoded bytes in NetworkBlockPos format # Cancel a packet entirely (it will be dropped by the pipeline) def drop_packet(wrapper: PacketWrapper) -> None: wrapper.cancel() # wrapper.cancelled == True; pipeline calls event.cancel() ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.