From 33cb188509c4d0e20ee3894db514d6fb3738ef74 Mon Sep 17 00:00:00 2001 From: Swann Date: Fri, 2 Oct 2020 00:05:33 +0200 Subject: [PATCH] refactor: use replication session global instance --- multi_user/__init__.py | 2 +- multi_user/delayable.py | 32 +++++------ multi_user/operators.py | 116 ++++++++++++++++---------------------- multi_user/preferences.py | 14 ++--- multi_user/ui.py | 53 ++++++++--------- 5 files changed, 96 insertions(+), 121 deletions(-) diff --git a/multi_user/__init__.py b/multi_user/__init__.py index 844c77a..c4bca3b 100644 --- a/multi_user/__init__.py +++ b/multi_user/__init__.py @@ -19,7 +19,7 @@ bl_info = { "name": "Multi-User", "author": "Swann Martinez", - "version": (0, 0, 4), + "version": (0, 1, 0), "description": "Enable real-time collaborative workflow inside blender", "blender": (2, 82, 0), "location": "3D View > Sidebar > Multi-User tab", diff --git a/multi_user/delayable.py b/multi_user/delayable.py index a8b79db..2ca0aef 100644 --- a/multi_user/delayable.py +++ b/multi_user/delayable.py @@ -19,7 +19,7 @@ import logging import bpy -from . import operators, presence, utils +from . import presence, utils from replication.constants import (FETCHED, UP, RP_COMMON, @@ -31,6 +31,7 @@ from replication.constants import (FETCHED, STATE_SRV_SYNC, REPARENT) +from replication.interface import session class Delayable(): """Delayable task interface @@ -96,29 +97,28 @@ class ApplyTimer(Timer): super().__init__(timout) def execute(self): - client = operators.client - if client and client.state['STATE'] == STATE_ACTIVE: + if session and session.state['STATE'] == STATE_ACTIVE: if self._type: - nodes = client.list(filter=self._type) + nodes = session.list(filter=self._type) else: - nodes = client.list() + nodes = session.list() for node in nodes: - node_ref = client.get(uuid=node) + node_ref = session.get(uuid=node) if node_ref.state == FETCHED: try: - client.apply(node, force=True) + session.apply(node, force=True) except Exception as e: logging.error(f"Fail to apply {node_ref.uuid}: {e}") elif node_ref.state == REPARENT: # Reload the node node_ref.remove_instance() node_ref.resolve() - client.apply(node, force=True) - for parent in client._graph.find_parents(node): + session.apply(node, force=True) + for parent in session._graph.find_parents(node): logging.info(f"Applying parent {parent}") - client.apply(parent, force=True) + session.apply(parent, force=True) node_ref.state = UP @@ -130,7 +130,6 @@ class DynamicRightSelectTimer(Timer): self._right_strategy = RP_COMMON def execute(self): - session = operators.client settings = utils.get_preferences() if session and session.state['STATE'] == STATE_ACTIVE: @@ -238,7 +237,6 @@ class Draw(Delayable): class DrawClient(Draw): def execute(self): - session = getattr(operators, 'client', None) renderer = getattr(presence, 'renderer', None) prefs = utils.get_preferences() @@ -274,22 +272,21 @@ class ClientUpdate(Timer): def execute(self): settings = utils.get_preferences() - session = getattr(operators, 'client', None) renderer = getattr(presence, 'renderer', None) if session and renderer: if session.state['STATE'] in [STATE_ACTIVE, STATE_LOBBY]: - local_user = operators.client.online_users.get( + local_user = session.online_users.get( settings.username) if not local_user: return else: - for username, user_data in operators.client.online_users.items(): + for username, user_data in session.online_users.items(): if username != settings.username: cached_user_data = self.users_metadata.get( username) - new_user_data = operators.client.online_users[username]['metadata'] + new_user_data = session.online_users[username]['metadata'] if cached_user_data is None: self.users_metadata[username] = user_data['metadata'] @@ -344,12 +341,11 @@ class SessionUserSync(Timer): super().__init__(timout) def execute(self): - session = getattr(operators, 'client', None) renderer = getattr(presence, 'renderer', None) if session and renderer: # sync online users - session_users = operators.client.online_users + session_users = session.online_users ui_users = bpy.context.window_manager.online_users for index, user in enumerate(ui_users): diff --git a/multi_user/operators.py b/multi_user/operators.py index 34bf61c..a1bb848 100644 --- a/multi_user/operators.py +++ b/multi_user/operators.py @@ -39,7 +39,7 @@ from replication.constants import (FETCHED, STATE_ACTIVE, STATE_SYNCING, RP_COMMON, UP) from replication.data import ReplicatedDataFactory from replication.exception import NonAuthorizedOperationError -from replication.interface import Session +from replication.interface import session client = None @@ -55,14 +55,14 @@ def on_connection_start(): runtime_settings = bpy.context.window_manager.session # Step 1: Constrect nodes - for node in client._graph.list_ordered(): - node_ref = client.get(node) + for node in session._graph.list_ordered(): + node_ref = session.get(node) if node_ref.state == FETCHED: node_ref.resolve() # Step 2: Load nodes - for node in client._graph.list_ordered(): - node_ref = client.get(node) + for node in session._graph.list_ordered(): + node_ref = session.get(node) if node_ref.state == FETCHED: node_ref.apply() @@ -183,11 +183,12 @@ class SessionStartOperator(bpy.types.Operator): timout=type_local_config.bl_delay_apply, target_type=type_module_class)) - client = Session( - factory=bpy_factory, + session.setup( factory=bpy_factory, python_path=bpy.app.binary_path_python, external_update_handling=use_extern_update) + client = session + if settings.update_method == 'DEPSGRAPH': delayables.append(delayable.ApplyTimer( settings.depsgraph_update_rate/1000)) @@ -202,9 +203,9 @@ class SessionStartOperator(bpy.types.Operator): try: for scene in bpy.data.scenes: - client.add(scene) + session.add(scene) - client.host( + session.host( id=settings.username, port=settings.port, ipc_port=settings.ipc_port, @@ -222,11 +223,11 @@ class SessionStartOperator(bpy.types.Operator): else: if not runtime_settings.admin: utils.clean_scene() - # regular client, no password needed + # regular session, no password needed admin_pass = None try: - client.connect( + session.connect( id=settings.username, address=settings.ip, port=settings.port, @@ -255,11 +256,11 @@ class SessionStartOperator(bpy.types.Operator): delayables.append(session_update) delayables.append(session_user_sync) - @client.register('on_connection') + @session.register('on_connection') def initialize_session(): background_exec_queue.put(on_connection_start) - @client.register('on_exit') + @session.register('on_exit') def desinitialize_session(): background_exec_queue.put(on_connection_end) @@ -299,15 +300,13 @@ class SessionInitOperator(bpy.types.Operator): return wm.invoke_props_dialog(self) def execute(self, context): - global client - if self.init_method == 'EMPTY': utils.clean_scene() for scene in bpy.data.scenes: - client.add(scene) + session.add(scene) - client.init() + session.init() return {"FINISHED"} @@ -323,11 +322,11 @@ class SessionStopOperator(bpy.types.Operator): return True def execute(self, context): - global client, delayables, stop_modal_executor + global delayables, stop_modal_executor - if client: + if session: try: - client.disconnect() + session.disconnect() except Exception as e: self.report({'ERROR'}, repr(e)) @@ -350,11 +349,11 @@ class SessionKickOperator(bpy.types.Operator): return True def execute(self, context): - global client, delayables, stop_modal_executor - assert(client) + global delayables, stop_modal_executor + assert(session) try: - client.kick(self.user) + session.kick(self.user) except Exception as e: self.report({'ERROR'}, repr(e)) @@ -381,9 +380,8 @@ class SessionPropertyRemoveOperator(bpy.types.Operator): return True def execute(self, context): - global client try: - client.remove(self.property_path) + session.remove(self.property_path) return {"FINISHED"} except: # NonAuthorizedOperationError: @@ -418,10 +416,9 @@ class SessionPropertyRightOperator(bpy.types.Operator): def execute(self, context): runtime_settings = context.window_manager.session - global client - if client: - client.change_owner(self.key, runtime_settings.clients) + if session: + session.change_owner(self.key, runtime_settings.clients) return {"FINISHED"} @@ -467,11 +464,10 @@ class SessionSnapUserOperator(bpy.types.Operator): return {'CANCELLED'} if event.type == 'TIMER': - area, region, rv3d = presence.view3d_find() - global client + area, region, rv3d = presence.view3d_find() - if client: - target_ref = client.online_users.get(self.target_client) + if session: + target_ref = session.online_users.get(self.target_client) if target_ref: target_scene = target_ref['metadata']['scene_current'] @@ -542,10 +538,8 @@ class SessionSnapTimeOperator(bpy.types.Operator): return {'CANCELLED'} if event.type == 'TIMER': - global client - - if client: - target_ref = client.online_users.get(self.target_client) + if session: + target_ref = session.online_users.get(self.target_client) if target_ref: context.scene.frame_current = target_ref['metadata']['frame_current'] @@ -568,9 +562,7 @@ class SessionApply(bpy.types.Operator): return True def execute(self, context): - global client - - client.apply(self.target) + session.apply(self.target) return {"FINISHED"} @@ -588,10 +580,9 @@ class SessionCommit(bpy.types.Operator): return True def execute(self, context): - global client - # client.get(uuid=target).diff() - client.commit(uuid=self.target) - client.push(self.target) + # session.get(uuid=target).diff() + session.commit(uuid=self.target) + session.push(self.target) return {"FINISHED"} @@ -609,16 +600,15 @@ class ApplyArmatureOperator(bpy.types.Operator): return {'CANCELLED'} if event.type == 'TIMER': - global client - if client and client.state['STATE'] == STATE_ACTIVE: - nodes = client.list(filter=bl_types.bl_armature.BlArmature) + if session and session.state['STATE'] == STATE_ACTIVE: + nodes = session.list(filter=bl_types.bl_armature.BlArmature) for node in nodes: - node_ref = client.get(uuid=node) + node_ref = session.get(uuid=node) if node_ref.state == FETCHED: try: - client.apply(node) + session.apply(node) except Exception as e: logging.error("Fail to apply armature: {e}") @@ -692,32 +682,28 @@ def sanitize_deps_graph(dummy): A future solution should be to avoid storing dataclock reference... """ - global client - - if client and client.state['STATE'] == STATE_ACTIVE: - for node_key in client.list(): - client.get(node_key).resolve() + if session and session.state['STATE'] == STATE_ACTIVE: + for node_key in session.list(): + session.get(node_key).resolve() @persistent def load_pre_handler(dummy): - global client - - if client and client.state['STATE'] in [STATE_ACTIVE, STATE_SYNCING]: + if session and session.state['STATE'] in [STATE_ACTIVE, STATE_SYNCING]: bpy.ops.session.stop() @persistent def update_client_frame(scene): - if client and client.state['STATE'] == STATE_ACTIVE: - client.update_user_metadata({ + if session and session.state['STATE'] == STATE_ACTIVE: + session.update_user_metadata({ 'frame_current': scene.frame_current }) @persistent def depsgraph_evaluation(scene): - if client and client.state['STATE'] == STATE_ACTIVE: + if session and session.state['STATE'] == STATE_ACTIVE: context = bpy.context blender_depsgraph = bpy.context.view_layer.depsgraph dependency_updates = [u for u in blender_depsgraph.updates] @@ -729,19 +715,19 @@ def depsgraph_evaluation(scene): # Is the object tracked ? if update.id.uuid: # Retrieve local version - node = client.get(update.id.uuid) + node = session.get(update.id.uuid) # Check our right on this update: # - if its ours or ( under common and diff), launch the # update process # - if its to someone else, ignore the update (go deeper ?) - if node and node.owner in [client.id, RP_COMMON] and node.state == UP: + if node and node.owner in [session.id, RP_COMMON] and node.state == UP: # Avoid slow geometry update if 'EDIT' in context.mode and \ not settings.sync_during_editmode: break - client.stash(node.uuid) + session.stash(node.uuid) else: # Distant update continue @@ -763,10 +749,8 @@ def register(): def unregister(): - global client - - if client and client.state['STATE'] == 2: - client.disconnect() + if session and session.state['STATE'] == 2: + session.disconnect() client = None from bpy.utils import unregister_class diff --git a/multi_user/preferences.py b/multi_user/preferences.py index c59be61..945fee1 100644 --- a/multi_user/preferences.py +++ b/multi_user/preferences.py @@ -27,6 +27,7 @@ from pathlib import Path from . import bl_types, environment, addon_updater_ops, presence, ui from .utils import get_preferences, get_expanded_icon from replication.constants import RP_COMMON +from replication.interface import session IP_EXPR = re.compile('\d+\.\d+\.\d+\.\d+') @@ -101,17 +102,14 @@ class ReplicatedDatablock(bpy.types.PropertyGroup): def set_sync_render_settings(self, value): self['sync_render_settings'] = value - - from .operators import client - if client and bpy.context.scene.uuid and value: + if session and bpy.context.scene.uuid and value: bpy.ops.session.apply('INVOKE_DEFAULT', target=bpy.context.scene.uuid) def set_sync_active_camera(self, value): self['sync_active_camera'] = value - from .operators import client - if client and bpy.context.scene.uuid and value: + if session and bpy.context.scene.uuid and value: bpy.ops.session.apply('INVOKE_DEFAULT', target=bpy.context.scene.uuid) @@ -440,9 +438,9 @@ def client_list_callback(scene, context): items = [(RP_COMMON, RP_COMMON, "")] username = get_preferences().username - cli = operators.client - if cli: - client_ids = cli.online_users.keys() + + if session: + client_ids = session.online_users.keys() for id in client_ids: name_desc = id if id == username: diff --git a/multi_user/ui.py b/multi_user/ui.py index 1e9cd5a..a279e55 100644 --- a/multi_user/ui.py +++ b/multi_user/ui.py @@ -18,7 +18,6 @@ import bpy -from . import operators from .utils import get_preferences, get_expanded_icon, get_folder_size from replication.constants import (ADDED, ERROR, FETCHED, MODIFIED, RP_COMMON, UP, @@ -29,6 +28,7 @@ from replication.constants import (ADDED, ERROR, FETCHED, STATE_LOBBY, STATE_LAUNCHING_SERVICES) from replication import __version__ +from replication.interface import session ICONS_PROP_STATES = ['TRIA_DOWN', # ADDED 'TRIA_UP', # COMMITED @@ -95,9 +95,9 @@ class SESSION_PT_settings(bpy.types.Panel): def draw_header(self, context): layout = self.layout - if operators.client and operators.client.state['STATE'] != STATE_INITIAL: - cli_state = operators.client.state - state = operators.client.state.get('STATE') + if session and session.state['STATE'] != STATE_INITIAL: + cli_state = session.state + state = session.state.get('STATE') connection_icon = "KEYTYPE_MOVING_HOLD_VEC" if state == STATE_ACTIVE: @@ -118,11 +118,11 @@ class SESSION_PT_settings(bpy.types.Panel): if hasattr(context.window_manager, 'session'): # STATE INITIAL - if not operators.client \ - or (operators.client and operators.client.state['STATE'] == STATE_INITIAL): + if not session \ + or (session and session.state['STATE'] == STATE_INITIAL): pass else: - cli_state = operators.client.state + cli_state = session.state row = layout.row() current_state = cli_state['STATE'] @@ -165,8 +165,8 @@ class SESSION_PT_settings_network(bpy.types.Panel): @classmethod def poll(cls, context): - return not operators.client \ - or (operators.client and operators.client.state['STATE'] == 0) + return not session \ + or (session and session.state['STATE'] == 0) def draw_header(self, context): self.layout.label(text="", icon='URL') @@ -223,8 +223,8 @@ class SESSION_PT_settings_user(bpy.types.Panel): @classmethod def poll(cls, context): - return not operators.client \ - or (operators.client and operators.client.state['STATE'] == 0) + return not session \ + or (session and session.state['STATE'] == 0) def draw_header(self, context): self.layout.label(text="", icon='USER') @@ -254,8 +254,8 @@ class SESSION_PT_advanced_settings(bpy.types.Panel): @classmethod def poll(cls, context): - return not operators.client \ - or (operators.client and operators.client.state['STATE'] == 0) + return not session \ + or (session and session.state['STATE'] == 0) def draw_header(self, context): self.layout.label(text="", icon='PREFERENCES') @@ -374,7 +374,7 @@ class SESSION_PT_user(bpy.types.Panel): @classmethod def poll(cls, context): - return operators.client and operators.client.state['STATE'] in [STATE_ACTIVE, STATE_LOBBY] + return session and session.state['STATE'] in [STATE_ACTIVE, STATE_LOBBY] def draw_header(self, context): self.layout.label(text="", icon='USER') @@ -405,7 +405,7 @@ class SESSION_PT_user(bpy.types.Panel): if active_user != 0 and active_user.username != settings.username: row = layout.row() user_operations = row.split() - if operators.client.state['STATE'] == STATE_ACTIVE: + if session.state['STATE'] == STATE_ACTIVE: user_operations.alert = context.window_manager.session.time_snap_running user_operations.operator( @@ -419,7 +419,7 @@ class SESSION_PT_user(bpy.types.Panel): text="", icon='TIME').target_client = active_user.username - if operators.client.online_users[settings.username]['admin']: + if session.online_users[settings.username]['admin']: user_operations.operator( "session.kick", text="", @@ -428,7 +428,6 @@ class SESSION_PT_user(bpy.types.Panel): class SESSION_UL_users(bpy.types.UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag): - session = operators.client settings = get_preferences() is_local_user = item.username == settings.username ping = '-' @@ -463,8 +462,8 @@ class SESSION_PT_presence(bpy.types.Panel): @classmethod def poll(cls, context): - return not operators.client \ - or (operators.client and operators.client.state['STATE'] in [STATE_INITIAL, STATE_ACTIVE]) + return not session \ + or (session and session.state['STATE'] in [STATE_INITIAL, STATE_ACTIVE]) def draw_header(self, context): self.layout.prop(context.window_manager.session, @@ -485,7 +484,7 @@ class SESSION_PT_presence(bpy.types.Panel): def draw_property(context, parent, property_uuid, level=0): settings = get_preferences() runtime_settings = context.window_manager.session - item = operators.client.get(uuid=property_uuid) + item = session.get(uuid=property_uuid) if item.state == ERROR: return @@ -553,7 +552,6 @@ class SESSION_PT_repository(bpy.types.Panel): @classmethod def poll(cls, context): - session = operators.client settings = get_preferences() admin = False @@ -562,9 +560,9 @@ class SESSION_PT_repository(bpy.types.Panel): if usr: admin = usr['admin'] return hasattr(context.window_manager, 'session') and \ - operators.client and \ - (operators.client.state['STATE'] == STATE_ACTIVE or \ - operators.client.state['STATE'] == STATE_LOBBY and admin) + session and \ + (session.state['STATE'] == STATE_ACTIVE or \ + session.state['STATE'] == STATE_LOBBY and admin) def draw_header(self, context): self.layout.label(text="", icon='OUTLINER_OB_GROUP_INSTANCE') @@ -576,7 +574,6 @@ class SESSION_PT_repository(bpy.types.Panel): settings = get_preferences() runtime_settings = context.window_manager.session - session = operators.client usr = session.online_users.get(settings.username) row = layout.row() @@ -602,11 +599,11 @@ class SESSION_PT_repository(bpy.types.Panel): types_filter = [t.type_name for t in settings.supported_datablocks if t.use_as_filter] - key_to_filter = operators.client.list( - filter_owner=settings.username) if runtime_settings.filter_owned else operators.client.list() + key_to_filter = session.list( + filter_owner=settings.username) if runtime_settings.filter_owned else session.list() client_keys = [key for key in key_to_filter - if operators.client.get(uuid=key).str_type + if session.get(uuid=key).str_type in types_filter] if client_keys: