server.plugins.manual_move_plugin
manual_move_plugin.py - ECS variant of the ManualMovePlugin
1"""manual_move_plugin.py - ECS variant of the ManualMovePlugin""" 2from overrides import overrides 3from microecs import World 4from robosim.plugin import Plugin, Event, Response, Message 5from microspec import Endpoint, Field 6 7class ManualMovePlugin(Plugin): 8 @property 9 @overrides 10 def endpoints(self) -> list[Endpoint]: 11 return [ 12 Endpoint("move", 13 input={"control_input": Field("float32", shape=(6, ), range=[-1, 1])}, 14 output={"move_applied": Field("dict")} 15 ) 16 ] 17 18 @overrides 19 def on_message_receive(self, world: World, entity_id: int, message: Message) -> Response: 20 # all syntax validation happens earlier thanks to microspec and self.endpoints schema 21 return Response.physics(motion_input=message.payload["control_input"]) 22 23 @overrides 24 def on_message_response(self, world: World, event: Event) -> Response: 25 if not event.applied: 26 return Response.err("move_not_applied") 27 28 entity = world.get_entity(event.target) 29 if entity.is_colliding.item(): 30 robot_ix = event.message.channel.index 31 return Response.err({"collision_error": f"robot {robot_ix} collided (eid: {event.target})"}) 32 33 return Response.ok("move_applied", new_state=entity.to_dict(serialization_field="serializable")) 34 35 @overrides 36 def to_dict(self) -> dict: 37 return {} 38 39 @overrides 40 def load_state_dict(self, state: dict): 41 pass
8class ManualMovePlugin(Plugin): 9 @property 10 @overrides 11 def endpoints(self) -> list[Endpoint]: 12 return [ 13 Endpoint("move", 14 input={"control_input": Field("float32", shape=(6, ), range=[-1, 1])}, 15 output={"move_applied": Field("dict")} 16 ) 17 ] 18 19 @overrides 20 def on_message_receive(self, world: World, entity_id: int, message: Message) -> Response: 21 # all syntax validation happens earlier thanks to microspec and self.endpoints schema 22 return Response.physics(motion_input=message.payload["control_input"]) 23 24 @overrides 25 def on_message_response(self, world: World, event: Event) -> Response: 26 if not event.applied: 27 return Response.err("move_not_applied") 28 29 entity = world.get_entity(event.target) 30 if entity.is_colliding.item(): 31 robot_ix = event.message.channel.index 32 return Response.err({"collision_error": f"robot {robot_ix} collided (eid: {event.target})"}) 33 34 return Response.ok("move_applied", new_state=entity.to_dict(serialization_field="serializable")) 35 36 @overrides 37 def to_dict(self) -> dict: 38 return {} 39 40 @overrides 41 def load_state_dict(self, state: dict): 42 pass
Plugin for world-level logic customization. Each handles a subset of network-level endpoints (e.g. 'move') A plugin can also update the physics or rendering logic through callbacks. Order of execution: [start tick] -> on_message_receive(w, eid) -> on_tick(w) -> [update] -> on_before_physics(w) -> -> [physics] -> on_after_physics(w) -> draw(w, eid?) -> on_message_response(w, eid, evt) -> [end tick]
9 @property 10 @overrides 11 def endpoints(self) -> list[Endpoint]: 12 return [ 13 Endpoint("move", 14 input={"control_input": Field("float32", shape=(6, ), range=[-1, 1])}, 15 output={"move_applied": Field("dict")} 16 ) 17 ]
the endpoints (commands) of this plugin
19 @overrides 20 def on_message_receive(self, world: World, entity_id: int, message: Message) -> Response: 21 # all syntax validation happens earlier thanks to microspec and self.endpoints schema 22 return Response.physics(motion_input=message.payload["control_input"])
Called for each message and the associated entity of this plugin. Messages influence the World via Events
24 @overrides 25 def on_message_response(self, world: World, event: Event) -> Response: 26 if not event.applied: 27 return Response.err("move_not_applied") 28 29 entity = world.get_entity(event.target) 30 if entity.is_colliding.item(): 31 robot_ix = event.message.channel.index 32 return Response.err({"collision_error": f"robot {robot_ix} collided (eid: {event.target})"}) 33 34 return Response.ok("move_applied", new_state=entity.to_dict(serialization_field="serializable"))
Called for each event generated by on_message_receive if they still need answering (.response not set)
the dict representation of this object for serialization purposes
Loads in place this object from a serialized dict representation