def send_add_object_to_collection(client: Client, collection_name, obj_name): logger.info("send_add_object_to_collection %s <- %s", collection_name, obj_name) buffer = common.encode_string(collection_name) + common.encode_string( obj_name) client.add_command( common.Command(common.MessageType.ADD_OBJECT_TO_COLLECTION, buffer, 0))
def send_collection_instance(client: Client, obj): if not obj.instance_collection: return instance_name = obj.name_full instanciated_collection = obj.instance_collection.name_full buffer = common.encode_string(instance_name) + common.encode_string(instanciated_collection) client.add_command(common.Command(common.MessageType.INSTANCE_COLLECTION, buffer, 0))
def send_remove_object_from_scene(client: Client, scene_name: str, object_name: str): logger.info("send_remove_object_from_scene %s <- %s", scene_name, object_name) buffer = common.encode_string(scene_name) + common.encode_string( object_name) client.add_command( common.Command(common.MessageType.REMOVE_OBJECT_FROM_SCENE, buffer, 0))
def send_add_collection_to_scene(client: Client, scene_name: str, collection_name: str): logger.info("send_add_collection_to_scene %s <- %s", scene_name, collection_name) buffer = common.encode_string(scene_name) + common.encode_string( collection_name) client.add_command( common.Command(common.MessageType.ADD_COLLECTION_TO_SCENE, buffer, 0))
def send_object_visibility(client: Client, object_: bpy.types.Object): logger.debug("send_object_visibility %s", object_.name_full) buffer = (common.encode_string(object_.name_full) + common.encode_bool(object_.hide_viewport) + common.encode_bool(object_.hide_select) + common.encode_bool(object_.hide_render) + common.encode_bool(object_.hide_get())) client.add_command( common.Command(common.MessageType.OBJECT_VISIBILITY, buffer, 0))
def send_remove_object_from_collection(client: Client, collection_name, obj_name): logger.info("send_remove_object_from_collection %s <- %s", collection_name, obj_name) buffer = common.encode_string(collection_name) + common.encode_string( obj_name) client.add_command( common.Command(common.MessageType.REMOVE_OBJECT_FROM_COLLECTION, buffer, 0))
def send_remove_collection_from_scene(client: Client, scene_name: str, collection_name: str): logger.info("send_remove_collection_from_scene %s <- %s", scene_name, collection_name) buffer = common.encode_string(scene_name) + common.encode_string( collection_name) client.add_command( common.Command(common.MessageType.REMOVE_COLLECTION_FROM_SCENE, buffer, 0))
def send_collection(client: Client, collection: bpy.types.Collection): logger.info("send_collection %s", collection.name_full) collection_instance_offset = collection.instance_offset temporary_visibility = True layer_collection = share_data.blender_layer_collections.get( collection.name_full) if layer_collection: temporary_visibility = not layer_collection.hide_viewport buffer = (common.encode_string(collection.name_full) + common.encode_bool(not collection.hide_viewport) + common.encode_vector3(collection_instance_offset) + common.encode_bool(temporary_visibility)) client.add_command(common.Command(common.MessageType.COLLECTION, buffer, 0))
def upload_room(host: str, port: int, room_name: str, room_attributes: dict, commands: List[Command]): """ Upload a room to the server. Warning: This function is blocking, so when run from Blender the client dedicated to the user will be blocked and will accumulate lot of room updates that will be processed later. Todo: Write a non blocking version of this function to be used inside Blender, some kind of UploadClient that can exist side by side with BlenderClient. """ with Client(host, port) as client: client.join_room(room_name) client.set_room_attributes(room_name, room_attributes) client.set_room_keep_open(room_name, True) for idx, c in enumerate(commands): logger.debug("Sending command %s (%d / %d)", c.type, idx, len(commands)) client.send_command(c) # The server will send back room update messages since the room is joined. # Consume them to avoid a client/server deadlock on broadcaster full send socket read_all_messages(client.socket, timeout=0.0) client.send_command(Command(MessageType.CONTENT)) client.leave_room(room_name) if not client.wait(MessageType.LEAVE_ROOM): raise ClientDisconnectedException( "Client disconnected before the end of upload room")
def grab(self, host, port, room_name: str): with Client(host, port) as client: client.join_room(room_name) attempts_max = 20 attempts = 0 try: while attempts < attempts_max: received_commands = client.fetch_incoming_commands() attempts += 1 time.sleep(0.01) for command in received_commands: attempts = 0 if command.type <= MessageType.COMMAND: continue # Ignore command serial Id, that may not match command.id = 0 self.streams.data[command.type].append(command.data) except ClientDisconnectedException: print("Grabber: disconnected before received command stream.", file=sys.stderr) client.send_command( Command(MessageType.SET_ROOM_KEEP_OPEN, encode_string(room_name) + encode_bool(False))) client.send_command( Command(MessageType.LEAVE_ROOM, room_name.encode("utf8"))) if not client.wait(MessageType.LEAVE_ROOM): print("Grabber: disconnected before receiving LEAVE_ROOM.", file=sys.stderr)
def send_grease_pencil_mesh(client: Client, obj): grease_pencil = obj.data buffer = common.encode_string(grease_pencil.name_full) buffer += common.encode_int(len(grease_pencil.materials)) for material in grease_pencil.materials: if not material: material_name = "Default" else: material_name = material.name_full buffer += common.encode_string(material_name) buffer += common.encode_int(len(grease_pencil.layers)) for name, layer in grease_pencil.layers.items(): buffer += send_grease_pencil_layer(layer, name) client.add_command(common.Command(common.MessageType.GREASE_PENCIL_MESH, buffer, 0)) send_grease_pencil_time_offset(client, obj)
def send_grease_pencil_material(client: Client, material): gp_material = material.grease_pencil stroke_enable = gp_material.show_stroke stroke_mode = gp_material.mode stroke_style = gp_material.stroke_style stroke_color = gp_material.color stroke_overlap = gp_material.use_overlap_strokes fill_enable = gp_material.show_fill fill_style = gp_material.fill_style fill_color = gp_material.fill_color gp_material_buffer = common.encode_string(material.name_full) gp_material_buffer += common.encode_bool(stroke_enable) gp_material_buffer += common.encode_string(stroke_mode) gp_material_buffer += common.encode_string(stroke_style) gp_material_buffer += common.encode_color(stroke_color) gp_material_buffer += common.encode_bool(stroke_overlap) gp_material_buffer += common.encode_bool(fill_enable) gp_material_buffer += common.encode_string(fill_style) gp_material_buffer += common.encode_color(fill_color) client.add_command(common.Command(common.MessageType.GREASE_PENCIL_MATERIAL, gp_material_buffer, 0))
def send_grease_pencil_time_offset(client: Client, obj): grease_pencil = obj.data buffer = common.encode_string(grease_pencil.name_full) for modifier in obj.grease_pencil_modifiers: if modifier.type != "GP_TIME": continue offset = modifier.offset scale = modifier.frame_scale custom_range = modifier.use_custom_frame_range frame_start = modifier.frame_start frame_end = modifier.frame_end buffer += (common.encode_int(offset) + common.encode_float(scale) + common.encode_bool(custom_range) + common.encode_int(frame_start) + common.encode_int(frame_end)) client.add_command( common.Command(common.MessageType.GREASE_PENCIL_TIME_OFFSET, buffer, 0)) break
def test_client_is_disconnected_when_server_process_is_killed(self): server_process = ServerProcess() server_process.start() with Client(server_process.host, server_process.port) as client: self.assertTrue(client.is_connected()) client.fetch_commands() server_process.kill() self.assertRaises(common.ClientDisconnectedException, client.fetch_commands) self.assertTrue(not client.is_connected())
def download_room(host: str, port: int, room_name: str) -> Tuple[Dict[str, Any], List[Command]]: from mixer.broadcaster.common import decode_json, RoomAttributes logger.info("Downloading room %s", room_name) commands = [] with Client(host, port) as client: client.join_room(room_name) room_attributes = None try: while room_attributes is None or len(commands) < room_attributes[ RoomAttributes.COMMAND_COUNT]: received_commands = client.fetch_incoming_commands() for command in received_commands: if room_attributes is None and command.type == MessageType.LIST_ROOMS: rooms_attributes, _ = decode_json(command.data, 0) if room_name not in rooms_attributes: logger.error("Room %s does not exist on server", room_name) return {}, [] room_attributes = rooms_attributes[room_name] logger.info( "Meta data received, number of commands in the room: %d", room_attributes[RoomAttributes.COMMAND_COUNT], ) elif command.type <= MessageType.COMMAND: continue # don't store server protocol commands commands.append(command) if room_attributes is not None: logger.debug( "Command %d / %d received", len(commands), room_attributes[RoomAttributes.COMMAND_COUNT]) except ClientDisconnectedException: logger.error( f"Disconnected while downloading room {room_name} from {host}:{port}" ) return {}, [] assert room_attributes is not None client.leave_room(room_name) return room_attributes, commands
def grab(self, host, port, room_name: str): with Client(host, port) as client: client.join_room(room_name, "ignored", "ignored", True, True) attempts_max = 20 attempts = 0 try: while attempts < attempts_max: received_commands = client.fetch_incoming_commands() attempts += 1 time.sleep(0.01) for command in received_commands: attempts = 0 if command.type == MessageType.SEND_ERROR: message = decode_string(command.data, 0) raise RuntimeError( f"Received error message {message}") if command.type <= MessageType.COMMAND: continue # Ignore command serial Id, that may not match command.id = 0 self.streams.commands[command.type].append(command) except ClientDisconnectedException: raise RuntimeError( "Grabber: disconnected before received command stream.") client.send_command( Command(MessageType.SET_ROOM_KEEP_OPEN, encode_string(room_name) + encode_bool(False))) client.send_command( Command(MessageType.LEAVE_ROOM, room_name.encode("utf8"))) if not client.wait(MessageType.LEAVE_ROOM): raise RuntimeError( "Grabber: disconnected before receiving LEAVE_ROOM.") count = sum( [len(commands) for commands in self.streams.commands.values()]) assert count > 0, "No message grabbed"
def test_join_one_room_one_client(self): delay = self.delay server = self._server c0_name = "c0_name" c0_room = "c0_room" d0 = Delegate() c0 = Client() delay() self.assertEqual(server.client_count(), (0, 1)) c0.set_client_attributes({common.ClientAttributes.USERNAME: c0_name}) c0.join_room(c0_room) delay() network_consumer(c0, self._delegate) expected = (c0_name, c0_room) self.assertEqual(server.client_count(), (1, 0)) self.assertEqual(len(d0.name_room), 1) self.assertIn(expected, d0.name_room)
def send_grease_pencil_connection(client: Client, obj): buffer = common.encode_string(get_object_path(obj)) buffer += common.encode_string(obj.data.name_full) client.add_command( common.Command(common.MessageType.GREASE_PENCIL_CONNECTION, buffer, 0))
def send_light(client: Client, obj): logger.info("send_light %s", obj.name_full) light_buffer = get_light_buffer(obj) if light_buffer: client.add_command( common.Command(common.MessageType.LIGHT, light_buffer, 0))
def send_add_constraint(client: Client, object_: bpy.types.Object, constraint_type: ConstraintType, target: str): buffer = common.encode_int(constraint_type) + common.encode_string( object_.name_full) + common.encode_string(target) client.add_command( common.Command(common.MessageType.ADD_CONSTRAINT, buffer, 0))
def send_remove_constraints(client: Client, object_: bpy.types.Object, constraint_type: ConstraintType): buffer = common.encode_int(constraint_type) + common.encode_string( object_.name_full) client.add_command( common.Command(common.MessageType.REMOVE_CONSTRAINT, buffer, 0))
def test_connect(self): delay = self.delay server = self._server client1 = Client() delay() self.assertTrue(client1.is_connected()) self.assertEqual(server.client_count(), (0, 1)) client1.disconnect() delay() self.assertFalse(client1.is_connected()) self.assertEqual(server.client_count(), (0, 0)) # client2 = Client() delay() self.assertTrue(client2.is_connected()) self.assertEqual(server.client_count(), (0, 1)) client3 = Client() delay() self.assertTrue(client3.is_connected()) self.assertEqual(server.client_count(), (0, 2)) client2.disconnect() delay() self.assertFalse(client2.is_connected()) self.assertTrue(client3.is_connected()) self.assertEqual(server.client_count(), (0, 1)) client2.disconnect() delay() self.assertFalse(client2.is_connected()) self.assertTrue(client3.is_connected()) self.assertEqual(server.client_count(), (0, 1)) client3.disconnect() delay() self.assertFalse(client3.is_connected()) self.assertEqual(server.client_count(), (0, 0))
def send_scene(client: Client, scene_name: str): logger.info("send_scene %s", scene_name) buffer = common.encode_string(scene_name) client.add_command(common.Command(common.MessageType.SCENE, buffer, 0))
def send_scene_renamed(client: Client, old_name: str, new_name: str): logger.info("send_scene_renamed %s to %s", old_name, new_name) buffer = common.encode_string(old_name) + common.encode_string(new_name) client.add_command( common.Command(common.MessageType.SCENE_RENAMED, buffer, 0))
def send_camera(client: Client, obj): camera_buffer = get_camera_buffer(obj) if camera_buffer: client.add_command(common.Command(common.MessageType.CAMERA, camera_buffer, 0))
def test_join_one_room_two_clients_leave(self): delay = self.delay server = self._server c0_name = "c0_name" c0_room = "c0_room" c1_name = "c1_name" c1_room = c0_room d0 = Delegate() c0 = Client() c0.join_room(c0_room) c0.set_client_attributes({common.ClientAttributes.USERNAME: c0_name}) d1 = Delegate() c1 = Client() c1.join_room(c1_room) c1.set_client_attributes({common.ClientAttributes.USERNAME: c1_name}) c1.leave_room(c1_room) delay() network_consumer(c0, self._delegate) network_consumer(c1, self._delegate) expected = [(c0_name, c0_room)] self.assertEqual(server.client_count(), (1, 1)) self.assertEqual(len(d0.name_room), 1) self.assertCountEqual(d0.name_room, expected) self.assertListEqual(d0.name_room, d1.name_room)
def send_add_object_to_vrtist(client: Client, scene_name: str, obj_name: str): logger.debug("send_add_object_to_vrtist %s <- %s", scene_name, obj_name) buffer = common.encode_string(scene_name) + common.encode_string(obj_name) client.add_command( common.Command(common.MessageType.ADD_OBJECT_TO_VRTIST, buffer, 0))
def send_empty(client: Client, obj): path = get_object_path(obj) buffer = common.encode_string(path) if buffer: client.add_command(common.Command(common.MessageType.EMPTY, buffer, 0))
def send_collection_removed(client: Client, collection_name): logger.info("send_collection_removed %s", collection_name) buffer = common.encode_string(collection_name) client.add_command( common.Command(common.MessageType.COLLECTION_REMOVED, buffer, 0))
def send_add_object_to_scene(client: Client, scene_name: str, obj_name: str): logger.info("send_add_object_to_scene %s <- %s", scene_name, obj_name) buffer = common.encode_string(scene_name) + common.encode_string(obj_name) client.add_command( common.Command(common.MessageType.ADD_OBJECT_TO_SCENE, buffer, 0))