From 2d638ef76fa7d4eb70fa2684283ff030e9a4bfc4 Mon Sep 17 00:00:00 2001 From: Swann Date: Thu, 4 Mar 2021 14:22:54 +0100 Subject: [PATCH 01/15] refactor: interface api changes --- multi_user/bl_types/__init__.py | 2 +- multi_user/operators.py | 57 ++++++++++++++++----------------- multi_user/presence.py | 2 +- multi_user/timers.py | 14 +++++--- multi_user/ui.py | 36 ++++++++++----------- 5 files changed, 56 insertions(+), 55 deletions(-) diff --git a/multi_user/bl_types/__init__.py b/multi_user/bl_types/__init__.py index 96a5e38..0e8871f 100644 --- a/multi_user/bl_types/__init__.py +++ b/multi_user/bl_types/__init__.py @@ -48,7 +48,7 @@ if bpy.app.version[1] >= 91: __all__.append('bl_volume') from . import * -from replication.data import ReplicatedDataFactory +from replication.data import DataTranslationProtocol def types_to_register(): return __all__ diff --git a/multi_user/operators.py b/multi_user/operators.py index a71d666..e81b56e 100644 --- a/multi_user/operators.py +++ b/multi_user/operators.py @@ -44,9 +44,10 @@ from bpy.app.handlers import persistent from bpy_extras.io_utils import ExportHelper, ImportHelper from replication.constants import (COMMITED, FETCHED, RP_COMMON, STATE_ACTIVE, STATE_INITIAL, STATE_SYNCING, UP) -from replication.data import ReplicatedDataFactory +from replication.data import DataTranslationProtocol +from replication.repository import Repository from replication.exception import NonAuthorizedOperationError, ContextError -from replication.interface import session +from replication.interface import session, add from . import bl_types, environment, timers, ui, utils from .presence import SessionStatusWidget, renderer, view3d_find @@ -186,7 +187,7 @@ class SessionStartOperator(bpy.types.Operator): handler.setFormatter(formatter) - bpy_factory = ReplicatedDataFactory() + bpy_protocol = DataTranslationProtocol() supported_bl_types = [] # init the factory with supported types @@ -205,7 +206,7 @@ class SessionStartOperator(bpy.types.Operator): type_local_config = settings.supported_datablocks[type_impl_name] - bpy_factory.register_type( + bpy_protocol.register_type( type_module_class.bl_class, type_module_class, check_common=type_module_class.bl_check_common) @@ -217,10 +218,7 @@ class SessionStartOperator(bpy.types.Operator): else: python_binary_path = bpy.app.binary_path_python - session.configure( - factory=bpy_factory, - python_path=python_binary_path, - external_update_handling=True) + repo = Repository(data_protocol=bpy_protocol) # Host a session if self.host: @@ -231,13 +229,14 @@ class SessionStartOperator(bpy.types.Operator): runtime_settings.internet_ip = environment.get_ip() try: + # Init repository for scene in bpy.data.scenes: - session.add(scene) + add(repo, scene) session.host( + repository= repo, id=settings.username, port=settings.port, - ipc_port=settings.ipc_port, timeout=settings.connection_timeout, password=admin_pass, cache_directory=settings.cache_directory, @@ -258,10 +257,10 @@ class SessionStartOperator(bpy.types.Operator): try: session.connect( + repository= repo, id=settings.username, address=settings.ip, port=settings.port, - ipc_port=settings.ipc_port, timeout=settings.connection_timeout, password=admin_pass ) @@ -272,15 +271,13 @@ class SessionStartOperator(bpy.types.Operator): # Background client updates service deleyables.append(timers.ClientUpdate()) deleyables.append(timers.DynamicRightSelectTimer()) - # deleyables.append(timers.PushTimer( - # queue=stagging, - # timeout=settings.depsgraph_update_rate - # )) session_update = timers.SessionStatusUpdate() session_user_sync = timers.SessionUserSync() session_background_executor = timers.MainThreadExecutor( execution_queue=background_execution_queue) + session_listen = timers.SessionListenTimer() + session_listen.register() session_update.register() session_user_sync.register() session_background_executor.register() @@ -288,7 +285,7 @@ class SessionStartOperator(bpy.types.Operator): deleyables.append(session_background_executor) deleyables.append(session_update) deleyables.append(session_user_sync) - + deleyables.append(session_listen) self.report( @@ -650,7 +647,7 @@ class ApplyArmatureOperator(bpy.types.Operator): return {'CANCELLED'} if event.type == 'TIMER': - if session and session.state['STATE'] == STATE_ACTIVE: + if session and session.state == STATE_ACTIVE: nodes = session.list(filter=bl_types.bl_armature.BlArmature) for node in nodes: @@ -795,7 +792,7 @@ class SessionSaveBackupOperator(bpy.types.Operator, ExportHelper): @classmethod def poll(cls, context): - return session.state['STATE'] == STATE_ACTIVE + return session.state == STATE_ACTIVE class SessionStopAutoSaveOperator(bpy.types.Operator): bl_idname = "session.cancel_autosave" @@ -804,7 +801,7 @@ class SessionStopAutoSaveOperator(bpy.types.Operator): @classmethod def poll(cls, context): - return (session.state['STATE'] == STATE_ACTIVE and 'SessionBackupTimer' in registry) + return (session.state == STATE_ACTIVE and 'SessionBackupTimer' in registry) def execute(self, context): autosave_timer = registry.get('SessionBackupTimer') @@ -829,7 +826,7 @@ class SessionLoadSaveOperator(bpy.types.Operator, ImportHelper): ) def execute(self, context): - from replication.graph import ReplicationGraph + from replication.repository import Repository # TODO: add filechecks @@ -849,7 +846,7 @@ class SessionLoadSaveOperator(bpy.types.Operator, ImportHelper): # init the factory with supported types - bpy_factory = ReplicatedDataFactory() + bpy_protocol = DataTranslationProtocol() for type in bl_types.types_to_register(): type_module = getattr(bl_types, type) name = [e.capitalize() for e in type.split('_')[1:]] @@ -857,16 +854,16 @@ class SessionLoadSaveOperator(bpy.types.Operator, ImportHelper): type_module_class = getattr(type_module, type_impl_name) - bpy_factory.register_type( + bpy_protocol.register_type( type_module_class.bl_class, type_module_class) - graph = ReplicationGraph() + graph = Repository() for node, node_data in nodes: node_type = node_data.get('str_type') - impl = bpy_factory.get_implementation_from_net(node_type) + impl = bpy_protocol.get_implementation_from_net(node_type) if impl: logging.info(f"Loading {node}") @@ -932,7 +929,7 @@ def update_external_dependencies(): def sanitize_deps_graph(remove_nodes: bool = False): """ Cleanup the replication graph """ - if session and session.state['STATE'] == STATE_ACTIVE: + if session and session.state == STATE_ACTIVE: start = utils.current_milli_time() rm_cpt = 0 for node_key in session.list(): @@ -957,18 +954,18 @@ def resolve_deps_graph(dummy): A future solution should be to avoid storing dataclock reference... """ - if session and session.state['STATE'] == STATE_ACTIVE: + if session and session.state == STATE_ACTIVE: sanitize_deps_graph(remove_nodes=True) @persistent def load_pre_handler(dummy): - if session and session.state['STATE'] in [STATE_ACTIVE, STATE_SYNCING]: + if session and session.state in [STATE_ACTIVE, STATE_SYNCING]: bpy.ops.session.stop() @persistent def update_client_frame(scene): - if session and session.state['STATE'] == STATE_ACTIVE: + if session and session.state == STATE_ACTIVE: session.update_user_metadata({ 'frame_current': scene.frame_current }) @@ -976,7 +973,7 @@ def update_client_frame(scene): @persistent def depsgraph_evaluation(scene): - if session and session.state['STATE'] == STATE_ACTIVE: + if session and session.state == STATE_ACTIVE: context = bpy.context blender_depsgraph = bpy.context.view_layer.depsgraph dependency_updates = [u for u in blender_depsgraph.updates] @@ -1035,7 +1032,7 @@ def register(): def unregister(): - if session and session.state['STATE'] == STATE_ACTIVE: + if session and session.state == STATE_ACTIVE: session.disconnect() from bpy.utils import unregister_class diff --git a/multi_user/presence.py b/multi_user/presence.py index 4776e03..f89844d 100644 --- a/multi_user/presence.py +++ b/multi_user/presence.py @@ -399,7 +399,7 @@ class SessionStatusWidget(Widget): text_scale = self.preferences.presence_hud_scale ui_scale = bpy.context.preferences.view.ui_scale color = [1, 1, 0, 1] - state = session.state.get('STATE') + state = session.state state_str = f"{get_state_str(state)}" if state == STATE_ACTIVE: diff --git a/multi_user/timers.py b/multi_user/timers.py index 9e1d2e6..99af20a 100644 --- a/multi_user/timers.py +++ b/multi_user/timers.py @@ -23,7 +23,7 @@ from replication.constants import (FETCHED, RP_COMMON, STATE_ACTIVE, STATE_INITIAL, STATE_LOBBY, STATE_QUITTING, STATE_SRV_SYNC, STATE_SYNCING, UP) from replication.exception import NonAuthorizedOperationError, ContextError -from replication.interface import session +from replication.interface import session, add from . import operators, utils from .presence import (UserFrustumWidget, UserNameWidget, UserSelectionWidget, @@ -71,7 +71,7 @@ class Timer(object): except Exception as e: logging.error(e) self.unregister() - session.disconnect() + session.disconnect(reason=f"Error during timer {self.id} execution") else: if self.is_running: return self._timeout @@ -100,9 +100,13 @@ class SessionBackupTimer(Timer): def execute(self): session.save(self._filepath) +class SessionListenTimer(Timer): + def execute(self): + session.listen() + class ApplyTimer(Timer): def execute(self): - if session and session.state['STATE'] == STATE_ACTIVE: + if session and session.state == STATE_ACTIVE: nodes = session.list() for node in nodes: @@ -130,7 +134,7 @@ class DynamicRightSelectTimer(Timer): def execute(self): settings = utils.get_preferences() - if session and session.state['STATE'] == STATE_ACTIVE: + if session and session.state == STATE_ACTIVE: # Find user if self._user is None: self._user = session.online_users.get(settings.username) @@ -262,7 +266,7 @@ class ClientUpdate(Timer): settings = utils.get_preferences() if session and renderer: - if session.state['STATE'] in [STATE_ACTIVE, STATE_LOBBY]: + if session.state in [STATE_ACTIVE, STATE_LOBBY]: local_user = session.online_users.get( settings.username) diff --git a/multi_user/ui.py b/multi_user/ui.py index b8f423b..abc2a18 100644 --- a/multi_user/ui.py +++ b/multi_user/ui.py @@ -71,9 +71,9 @@ class SESSION_PT_settings(bpy.types.Panel): def draw_header(self, context): layout = self.layout - if session and session.state['STATE'] != STATE_INITIAL: + if session and session.state != STATE_INITIAL: cli_state = session.state - state = session.state.get('STATE') + state = session.state connection_icon = "KEYTYPE_MOVING_HOLD_VEC" if state == STATE_ACTIVE: @@ -81,7 +81,7 @@ class SESSION_PT_settings(bpy.types.Panel): else: connection_icon = 'PROP_CON' - layout.label(text=f"Session - {get_state_str(cli_state['STATE'])}", icon=connection_icon) + layout.label(text=f"Session - {get_state_str(cli_state)}", icon=connection_icon) else: layout.label(text=f"Session - v{__version__}",icon="PROP_OFF") @@ -94,13 +94,13 @@ class SESSION_PT_settings(bpy.types.Panel): if hasattr(context.window_manager, 'session'): # STATE INITIAL if not session \ - or (session and session.state['STATE'] == STATE_INITIAL): + or (session and session.state == STATE_INITIAL): pass else: - cli_state = session.state + progress = session.state_progress row = layout.row() - current_state = cli_state['STATE'] + current_state = session.state info_msg = None if current_state in [STATE_ACTIVE]: @@ -124,8 +124,8 @@ class SESSION_PT_settings(bpy.types.Panel): if current_state in [STATE_SYNCING, STATE_SRV_SYNC, STATE_WAITING]: info_box = row.box() info_box.row().label(text=printProgressBar( - cli_state['CURRENT'], - cli_state['TOTAL'], + progress['current'], + progress['total'], length=16 )) @@ -141,7 +141,7 @@ class SESSION_PT_settings_network(bpy.types.Panel): @classmethod def poll(cls, context): return not session \ - or (session and session.state['STATE'] == 0) + or (session and session.state == 0) def draw_header(self, context): self.layout.label(text="", icon='URL') @@ -199,7 +199,7 @@ class SESSION_PT_settings_user(bpy.types.Panel): @classmethod def poll(cls, context): return not session \ - or (session and session.state['STATE'] == 0) + or (session and session.state == 0) def draw_header(self, context): self.layout.label(text="", icon='USER') @@ -230,7 +230,7 @@ class SESSION_PT_advanced_settings(bpy.types.Panel): @classmethod def poll(cls, context): return not session \ - or (session and session.state['STATE'] == 0) + or (session and session.state == 0) def draw_header(self, context): self.layout.label(text="", icon='PREFERENCES') @@ -322,7 +322,7 @@ class SESSION_PT_user(bpy.types.Panel): @classmethod def poll(cls, context): - return session and session.state['STATE'] in [STATE_ACTIVE, STATE_LOBBY] + return session and session.state in [STATE_ACTIVE, STATE_LOBBY] def draw_header(self, context): self.layout.label(text="", icon='USER') @@ -353,7 +353,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 session.state['STATE'] == STATE_ACTIVE: + if session.state == STATE_ACTIVE: user_operations.alert = context.window_manager.session.time_snap_running user_operations.operator( @@ -411,7 +411,7 @@ class SESSION_PT_presence(bpy.types.Panel): @classmethod def poll(cls, context): return not session \ - or (session and session.state['STATE'] in [STATE_INITIAL, STATE_ACTIVE]) + or (session and session.state in [STATE_INITIAL, STATE_ACTIVE]) def draw_header(self, context): self.layout.prop(context.window_manager.session, @@ -519,8 +519,8 @@ class SESSION_PT_repository(bpy.types.Panel): admin = usr['admin'] return hasattr(context.window_manager, 'session') and \ session and \ - (session.state['STATE'] == STATE_ACTIVE or \ - session.state['STATE'] == STATE_LOBBY and admin) + (session.state == STATE_ACTIVE or \ + session.state == STATE_LOBBY and admin) def draw_header(self, context): self.layout.label(text="", icon='OUTLINER_OB_GROUP_INSTANCE') @@ -536,7 +536,7 @@ class SESSION_PT_repository(bpy.types.Panel): row = layout.row() - if session.state['STATE'] == STATE_ACTIVE: + if session.state == STATE_ACTIVE: if 'SessionBackupTimer' in registry: row.alert = True row.operator('session.cancel_autosave', icon="CANCEL") @@ -579,7 +579,7 @@ class SESSION_PT_repository(bpy.types.Panel): else: row.label(text="Empty") - elif session.state['STATE'] == STATE_LOBBY and usr and usr['admin']: + elif session.state == STATE_LOBBY and usr and usr['admin']: row.operator("session.init", icon='TOOL_SETTINGS', text="Init") else: row.label(text="Waiting to start") From 875b9ce934d55cd2442eb111d754f20feeb804cd Mon Sep 17 00:00:00 2001 From: Swann Date: Thu, 4 Mar 2021 14:24:03 +0100 Subject: [PATCH 02/15] feat: temporary disable CI jobs for this branch because of breaking changes --- .gitlab-ci.yml | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index c7c6485..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,13 +0,0 @@ -stages: - - test - - build - - deploy - - doc - - - -include: - - local: .gitlab/ci/test.gitlab-ci.yml - - local: .gitlab/ci/build.gitlab-ci.yml - - local: .gitlab/ci/deploy.gitlab-ci.yml - - local: .gitlab/ci/doc.gitlab-ci.yml From b17104c67e7a4bd2da26d4eccf2e00c8862c0242 Mon Sep 17 00:00:00 2001 From: Swann Date: Fri, 5 Mar 2021 10:35:35 +0100 Subject: [PATCH 03/15] fix: naming --- multi_user/operators.py | 8 ++++---- multi_user/timers.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/multi_user/operators.py b/multi_user/operators.py index e81b56e..afde378 100644 --- a/multi_user/operators.py +++ b/multi_user/operators.py @@ -81,7 +81,7 @@ def initialize_session(): # Step 1: Constrect nodes logging.info("Constructing nodes") - for node in session._graph.list_ordered(): + for node in session._repository.list_ordered(): node_ref = session.get(uuid=node) if node_ref is None: logging.error(f"Can't construct node {node}") @@ -90,7 +90,7 @@ def initialize_session(): # Step 2: Load nodes logging.info("Loading nodes") - for node in session._graph.list_ordered(): + for node in session._repository.list_ordered(): node_ref = session.get(uuid=node) if node_ref is None: @@ -275,7 +275,7 @@ class SessionStartOperator(bpy.types.Operator): session_user_sync = timers.SessionUserSync() session_background_executor = timers.MainThreadExecutor( execution_queue=background_execution_queue) - session_listen = timers.SessionListenTimer() + session_listen = timers.SessionListenTimer(timeout=0.001) session_listen.register() session_update.register() @@ -602,7 +602,7 @@ class SessionApply(bpy.types.Operator): force=True, force_dependencies=self.reset_dependencies) if node_ref.bl_reload_parent: - for parent in session._graph.find_parents(self.target): + for parent in session._repository.find_parents(self.target): logging.debug(f"Refresh parent {parent}") session.apply(parent, force=True) except Exception as e: diff --git a/multi_user/timers.py b/multi_user/timers.py index 99af20a..e8c7340 100644 --- a/multi_user/timers.py +++ b/multi_user/timers.py @@ -119,7 +119,7 @@ class ApplyTimer(Timer): logging.error(f"Fail to apply {node_ref.uuid}: {e}") else: if node_ref.bl_reload_parent: - for parent in session._graph.find_parents(node): + for parent in session._repository.find_parents(node): logging.debug("Refresh parent {node}") session.apply(parent, force=True) From 93df5ca5fa23e98bcebd229c11a6c22d4af72800 Mon Sep 17 00:00:00 2001 From: Swann Date: Sat, 6 Mar 2021 10:20:57 +0100 Subject: [PATCH 04/15] fix: disconnect callback --- multi_user/operators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multi_user/operators.py b/multi_user/operators.py index afde378..d10670b 100644 --- a/multi_user/operators.py +++ b/multi_user/operators.py @@ -348,7 +348,7 @@ class SessionStopOperator(bpy.types.Operator): if session: try: - session.disconnect() + session.disconnect(reason='user') except Exception as e: self.report({'ERROR'}, repr(e)) From 8e3c86561f67f72f6a53bb030573dc7ed630163f Mon Sep 17 00:00:00 2001 From: Swann Date: Tue, 9 Mar 2021 10:19:51 +0100 Subject: [PATCH 05/15] refactor: move add to porcelain --- multi_user/operators.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/multi_user/operators.py b/multi_user/operators.py index d10670b..b710cf0 100644 --- a/multi_user/operators.py +++ b/multi_user/operators.py @@ -45,9 +45,10 @@ from bpy_extras.io_utils import ExportHelper, ImportHelper from replication.constants import (COMMITED, FETCHED, RP_COMMON, STATE_ACTIVE, STATE_INITIAL, STATE_SYNCING, UP) from replication.data import DataTranslationProtocol +from replication.exception import ContextError, NonAuthorizedOperationError +from replication.interface import session +from replication.porcelain import add from replication.repository import Repository -from replication.exception import NonAuthorizedOperationError, ContextError -from replication.interface import session, add from . import bl_types, environment, timers, ui, utils from .presence import SessionStatusWidget, renderer, view3d_find From 647ac46c011708ce6f014448beade2fae3413833 Mon Sep 17 00:00:00 2001 From: Swann Date: Tue, 9 Mar 2021 14:07:59 +0100 Subject: [PATCH 06/15] feat: move apply to porcelain feat: move data access to repository feat: object_store layer to repository (with GraphObjectStore) revert: missing network services --- multi_user/operators.py | 37 ++++++++++++++++++++----------------- multi_user/presence.py | 2 +- multi_user/timers.py | 21 +++++++++++---------- multi_user/ui.py | 6 +++--- multi_user/utils.py | 4 ++-- 5 files changed, 37 insertions(+), 33 deletions(-) diff --git a/multi_user/operators.py b/multi_user/operators.py index b710cf0..907056c 100644 --- a/multi_user/operators.py +++ b/multi_user/operators.py @@ -47,7 +47,7 @@ from replication.constants import (COMMITED, FETCHED, RP_COMMON, STATE_ACTIVE, from replication.data import DataTranslationProtocol from replication.exception import ContextError, NonAuthorizedOperationError from replication.interface import session -from replication.porcelain import add +from replication.porcelain import add, apply from replication.repository import Repository from . import bl_types, environment, timers, ui, utils @@ -82,8 +82,8 @@ def initialize_session(): # Step 1: Constrect nodes logging.info("Constructing nodes") - for node in session._repository.list_ordered(): - node_ref = session.get(uuid=node) + for node in session.repository.list_ordered(): + node_ref = session.repository.get_node(node) if node_ref is None: logging.error(f"Can't construct node {node}") elif node_ref.state == FETCHED: @@ -91,8 +91,8 @@ def initialize_session(): # Step 2: Load nodes logging.info("Loading nodes") - for node in session._repository.list_ordered(): - node_ref = session.get(uuid=node) + for node in session.repository.list_ordered(): + node_ref = session.repository.get_node(node) if node_ref is None: logging.error(f"Can't load node {node}") @@ -598,14 +598,17 @@ class SessionApply(bpy.types.Operator): def execute(self, context): logging.debug(f"Running apply on {self.target}") try: - node_ref = session.get(uuid=self.target) - session.apply(self.target, - force=True, - force_dependencies=self.reset_dependencies) + node_ref = session.repository.get_node(self.target) + apply(session.repository, + self.target, + force=True, + force_dependencies=self.reset_dependencies) if node_ref.bl_reload_parent: - for parent in session._repository.find_parents(self.target): + for parent in session.repository.find_parents(self.target): logging.debug(f"Refresh parent {parent}") - session.apply(parent, force=True) + apply(session.repository, + parent, + force=True) except Exception as e: self.report({'ERROR'}, repr(e)) return {"CANCELED"} @@ -652,11 +655,11 @@ class ApplyArmatureOperator(bpy.types.Operator): nodes = session.list(filter=bl_types.bl_armature.BlArmature) for node in nodes: - node_ref = session.get(uuid=node) + node_ref = session.repository.get_node(node) if node_ref.state == FETCHED: try: - session.apply(node) + apply(session.repository, node) except Exception as e: logging.error("Fail to apply armature: {e}") @@ -921,7 +924,7 @@ classes = ( def update_external_dependencies(): nodes_ids = session.list(filter=bl_types.bl_file.BlFile) for node_id in nodes_ids: - node = session.get(node_id) + node = session.repository.get_node(node_id) if node and node.owner in [session.id, RP_COMMON] \ and node.has_changed(): session.commit(node_id) @@ -934,7 +937,7 @@ def sanitize_deps_graph(remove_nodes: bool = False): start = utils.current_milli_time() rm_cpt = 0 for node_key in session.list(): - node = session.get(node_key) + node = session.repository.get_node(node_key) if node is None \ or (node.state == UP and not node.resolve(construct=False)): if remove_nodes: @@ -987,7 +990,7 @@ def depsgraph_evaluation(scene): # Is the object tracked ? if update.id.uuid: # Retrieve local version - node = session.get(uuid=update.id.uuid) + node = session.repository.get_node(update.id.uuid) # Check our right on this update: # - if its ours or ( under common and diff), launch the @@ -1011,7 +1014,7 @@ def depsgraph_evaluation(scene): continue # A new scene is created elif isinstance(update.id, bpy.types.Scene): - ref = session.get(reference=update.id) + ref = session.repository.get_node_by_datablock(update.id) if ref: ref.resolve() else: diff --git a/multi_user/presence.py b/multi_user/presence.py index f89844d..30b10d2 100644 --- a/multi_user/presence.py +++ b/multi_user/presence.py @@ -30,7 +30,7 @@ import mathutils from bpy_extras import view3d_utils from gpu_extras.batch import batch_for_shader from replication.constants import (STATE_ACTIVE, STATE_AUTH, STATE_CONFIG, - STATE_INITIAL, STATE_LAUNCHING_SERVICES, + STATE_INITIAL, CONNECTING, STATE_LOBBY, STATE_QUITTING, STATE_SRV_SYNC, STATE_SYNCING, STATE_WAITING) from replication.interface import session diff --git a/multi_user/timers.py b/multi_user/timers.py index e8c7340..9ec9ca4 100644 --- a/multi_user/timers.py +++ b/multi_user/timers.py @@ -23,7 +23,8 @@ from replication.constants import (FETCHED, RP_COMMON, STATE_ACTIVE, STATE_INITIAL, STATE_LOBBY, STATE_QUITTING, STATE_SRV_SYNC, STATE_SYNCING, UP) from replication.exception import NonAuthorizedOperationError, ContextError -from replication.interface import session, add +from replication.interface import session +from replication.porcelain import apply, add from . import operators, utils from .presence import (UserFrustumWidget, UserNameWidget, UserSelectionWidget, @@ -110,18 +111,18 @@ class ApplyTimer(Timer): nodes = session.list() for node in nodes: - node_ref = session.get(uuid=node) + node_ref = session.repository.get_node(node) if node_ref.state == FETCHED: try: - session.apply(node) + apply(session.repository, node) except Exception as e: logging.error(f"Fail to apply {node_ref.uuid}: {e}") else: if node_ref.bl_reload_parent: - for parent in session._repository.find_parents(node): + for parent in session.repository.find_parents(node): logging.debug("Refresh parent {node}") - session.apply(parent, force=True) + apply(session.repository, parent, force=True) class DynamicRightSelectTimer(Timer): @@ -148,7 +149,7 @@ class DynamicRightSelectTimer(Timer): # if an annotation exist and is tracked if annotation_gp and annotation_gp.uuid: - registered_gp = session.get(uuid=annotation_gp.uuid) + registered_gp = session.repository.get_node(annotation_gp.uuid) if is_annotating(bpy.context): # try to get the right on it if registered_gp.owner == RP_COMMON: @@ -162,7 +163,7 @@ class DynamicRightSelectTimer(Timer): affect_dependencies=False) if registered_gp.owner == settings.username: - gp_node = session.get(uuid=annotation_gp.uuid) + gp_node = session.repository.get_node(annotation_gp.uuid) if gp_node.has_changed(): session.commit(gp_node.uuid) session.push(gp_node.uuid, check_data=False) @@ -186,7 +187,7 @@ class DynamicRightSelectTimer(Timer): # change old selection right to common for obj in obj_common: - node = session.get(uuid=obj) + node = session.repository.get_node(obj) if node and (node.owner == settings.username or node.owner == RP_COMMON): recursive = True @@ -204,7 +205,7 @@ class DynamicRightSelectTimer(Timer): # change new selection to our for obj in obj_ours: - node = session.get(uuid=obj) + node = session.repository.get_node(obj) if node and node.owner == RP_COMMON: recursive = True @@ -237,7 +238,7 @@ class DynamicRightSelectTimer(Timer): owned_keys = session.list( filter_owner=settings.username) for key in owned_keys: - node = session.get(uuid=key) + node = session.repository.get_node(key) try: session.change_owner( key, diff --git a/multi_user/ui.py b/multi_user/ui.py index abc2a18..f956d0f 100644 --- a/multi_user/ui.py +++ b/multi_user/ui.py @@ -26,7 +26,7 @@ from replication.constants import (ADDED, ERROR, FETCHED, STATE_INITIAL, STATE_SRV_SYNC, STATE_WAITING, STATE_QUITTING, STATE_LOBBY, - STATE_LAUNCHING_SERVICES) + CONNECTING) from replication import __version__ from replication.interface import session from .timers import registry @@ -441,7 +441,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 = session.get(uuid=property_uuid) + item = session.repository.get_node(property_uuid) area_msg = parent.row(align=True) @@ -568,7 +568,7 @@ class SESSION_PT_repository(bpy.types.Panel): filter_owner=settings.username) if runtime_settings.filter_owned else session.list() client_keys = [key for key in key_to_filter - if session.get(uuid=key).str_type + if session.repository.get_node(key).str_type in types_filter] if client_keys: diff --git a/multi_user/utils.py b/multi_user/utils.py index 25444f9..a593015 100644 --- a/multi_user/utils.py +++ b/multi_user/utils.py @@ -36,7 +36,7 @@ from replication.constants import (STATE_ACTIVE, STATE_AUTH, STATE_INITIAL, STATE_SRV_SYNC, STATE_WAITING, STATE_QUITTING, STATE_LOBBY, - STATE_LAUNCHING_SERVICES) + CONNECTING) def find_from_attr(attr_name, attr_value, list): @@ -92,7 +92,7 @@ def get_state_str(state): state_str = 'OFFLINE' elif state == STATE_QUITTING: state_str = 'QUITTING' - elif state == STATE_LAUNCHING_SERVICES: + elif state == CONNECTING: state_str = 'LAUNCHING SERVICES' elif state == STATE_LOBBY: state_str = 'LOBBY' From 235db712fdde2d2d5e1e326d60de2d162817f3f2 Mon Sep 17 00:00:00 2001 From: Swann Date: Thu, 11 Mar 2021 15:45:48 +0100 Subject: [PATCH 07/15] fix: api --- multi_user/operators.py | 6 +++--- multi_user/timers.py | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/multi_user/operators.py b/multi_user/operators.py index 907056c..4399e7f 100644 --- a/multi_user/operators.py +++ b/multi_user/operators.py @@ -327,7 +327,7 @@ class SessionInitOperator(bpy.types.Operator): utils.clean_scene() for scene in bpy.data.scenes: - session.add(scene) + add(session.repository, scene) session.init() @@ -875,7 +875,7 @@ class SessionLoadSaveOperator(bpy.types.Operator, ImportHelper): uuid=node, dependencies=node_data['dependencies'], data=node_data['data']) - instance.store(graph) + graph.do_commit(instance) instance.state = FETCHED logging.info("Graph succefully loaded") @@ -1018,7 +1018,7 @@ def depsgraph_evaluation(scene): if ref: ref.resolve() else: - scn_uuid = session.add(update.id) + scn_uuid = add(session.repository, update.id) session.commit(scn_uuid) session.push(scn_uuid, check_data=False) def register(): diff --git a/multi_user/timers.py b/multi_user/timers.py index 9ec9ca4..c4a1ed8 100644 --- a/multi_user/timers.py +++ b/multi_user/timers.py @@ -17,6 +17,7 @@ import logging import sys +import traceback import bpy from replication.constants import (FETCHED, RP_COMMON, STATE_ACTIVE, @@ -117,7 +118,7 @@ class ApplyTimer(Timer): try: apply(session.repository, node) except Exception as e: - logging.error(f"Fail to apply {node_ref.uuid}: {e}") + traceback.print_exc() else: if node_ref.bl_reload_parent: for parent in session.repository.find_parents(node): From c7e8002fed50994ff366b74007b1469e76112906 Mon Sep 17 00:00:00 2001 From: Swann Date: Sun, 14 Mar 2021 18:32:04 +0100 Subject: [PATCH 08/15] fix: apply api clean: ipc port propertie --- docs/getting_started/quickstart.rst | 9 --------- multi_user/operators.py | 10 ++++++---- multi_user/preferences.py | 14 -------------- multi_user/timers.py | 2 +- multi_user/ui.py | 3 --- 5 files changed, 7 insertions(+), 31 deletions(-) diff --git a/docs/getting_started/quickstart.rst b/docs/getting_started/quickstart.rst index d81eff1..c3092e7 100644 --- a/docs/getting_started/quickstart.rst +++ b/docs/getting_started/quickstart.rst @@ -374,15 +374,6 @@ Network Advanced network settings -**IPC Port** is the port used for Inter Process Communication. This port is used -by the multi-user subprocesses to communicate with each other. If different instances -of multi-user are using the same IPC port, this will create conflict ! - -.. note:: - You only need to modify this setting if you need to launch multiple clients from the same - computer (or if you try to host and join from the same computer). To resolve this, you simply need to enter a different - **IPC port** for each blender instance. - **Timeout (in milliseconds)** is the maximum ping authorized before auto-disconnecting. You should only increase it if you have a bad connection. diff --git a/multi_user/operators.py b/multi_user/operators.py index aea8271..9a99097 100644 --- a/multi_user/operators.py +++ b/multi_user/operators.py @@ -32,6 +32,7 @@ from operator import itemgetter from pathlib import Path from queue import Queue from time import gmtime, strftime +import traceback try: import _pickle as pickle @@ -247,7 +248,6 @@ class SessionStartOperator(bpy.types.Operator): except Exception as e: self.report({'ERROR'}, repr(e)) logging.error(f"Error: {e}") - import traceback traceback.print_exc() # Join a session else: @@ -604,14 +604,16 @@ class SessionApply(bpy.types.Operator): force=True, force_dependencies=self.reset_dependencies) if node_ref.bl_reload_parent: - for parent in session.repository.find_parents(self.target): + for parent in session.repository.get_parents(self.target): logging.debug(f"Refresh parent {parent}") + apply(session.repository, - parent, + parent.uuid, force=True) except Exception as e: self.report({'ERROR'}, repr(e)) - return {"CANCELED"} + traceback.print_exc() + return {"CANCELLED"} return {"FINISHED"} diff --git a/multi_user/preferences.py b/multi_user/preferences.py index 1757c1b..7df200b 100644 --- a/multi_user/preferences.py +++ b/multi_user/preferences.py @@ -66,14 +66,6 @@ def update_ip(self, context): self['ip'] = "127.0.0.1" -def update_port(self, context): - max_port = self.port + 3 - - if self.ipc_port < max_port and \ - self['ipc_port'] >= self.port: - logging.error( - "IPC Port in conflict with the port, assigning a random value") - self['ipc_port'] = random.randrange(self.port+4, 10000) def update_directory(self, context): @@ -174,12 +166,6 @@ class SessionPrefs(bpy.types.AddonPreferences): supported_datablocks: bpy.props.CollectionProperty( type=ReplicatedDatablock, ) - ipc_port: bpy.props.IntProperty( - name="ipc_port", - description='internal ttl port(only useful for multiple local instances)', - default=random.randrange(5570, 70000), - update=update_port, - ) init_method: bpy.props.EnumProperty( name='init_method', description='Init repo', diff --git a/multi_user/timers.py b/multi_user/timers.py index c4a1ed8..4b99b68 100644 --- a/multi_user/timers.py +++ b/multi_user/timers.py @@ -121,7 +121,7 @@ class ApplyTimer(Timer): traceback.print_exc() else: if node_ref.bl_reload_parent: - for parent in session.repository.find_parents(node): + for parent in session.repository.get_parents(node): logging.debug("Refresh parent {node}") apply(session.repository, parent, force=True) diff --git a/multi_user/ui.py b/multi_user/ui.py index f956d0f..df5ae13 100644 --- a/multi_user/ui.py +++ b/multi_user/ui.py @@ -251,9 +251,6 @@ class SESSION_PT_advanced_settings(bpy.types.Panel): emboss=False) if settings.sidebar_advanced_net_expanded: - net_section_row = net_section.row() - net_section_row.label(text="IPC Port:") - net_section_row.prop(settings, "ipc_port", text="") net_section_row = net_section.row() net_section_row.label(text="Timeout (ms):") net_section_row.prop(settings, "connection_timeout", text="") From 3a02711baa4d8d0eafb330a2c76a130e534fb60c Mon Sep 17 00:00:00 2001 From: Swann Date: Sun, 14 Mar 2021 20:58:25 +0100 Subject: [PATCH 09/15] feat: faster root management --- multi_user/bl_types/bl_scene.py | 2 ++ multi_user/timers.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/multi_user/bl_types/bl_scene.py b/multi_user/bl_types/bl_scene.py index 08a3d69..aef762a 100644 --- a/multi_user/bl_types/bl_scene.py +++ b/multi_user/bl_types/bl_scene.py @@ -368,6 +368,8 @@ def load_sequence(sequence_data: dict, sequence_editor: bpy.types.SequenceEditor class BlScene(BlDatablock): + is_root = True + bl_id = "scenes" bl_class = bpy.types.Scene bl_check_common = True diff --git a/multi_user/timers.py b/multi_user/timers.py index 4b99b68..22e6a64 100644 --- a/multi_user/timers.py +++ b/multi_user/timers.py @@ -123,7 +123,9 @@ class ApplyTimer(Timer): if node_ref.bl_reload_parent: for parent in session.repository.get_parents(node): logging.debug("Refresh parent {node}") - apply(session.repository, parent, force=True) + apply(session.repository, + parent.uuid, + force=True) class DynamicRightSelectTimer(Timer): From 7fe1ae83b1e98cd64503e35eb8613328f726d892 Mon Sep 17 00:00:00 2001 From: Swann Date: Fri, 23 Apr 2021 11:25:15 +0200 Subject: [PATCH 10/15] feat: update replication version to the right one --- multi_user/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multi_user/__init__.py b/multi_user/__init__.py index 93e2f5c..ef7aaff 100644 --- a/multi_user/__init__.py +++ b/multi_user/__init__.py @@ -44,7 +44,7 @@ from . import environment DEPENDENCIES = { - ("replication", '0.1.26'), + ("replication", '0.1.30'), } From dd1c6a4fc7f7a841a98a228cf4fec913d4732667 Mon Sep 17 00:00:00 2001 From: Swann Date: Fri, 23 Apr 2021 11:45:47 +0200 Subject: [PATCH 11/15] feat: enable back ci --- gitlab-ci.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 gitlab-ci.yml diff --git a/gitlab-ci.yml b/gitlab-ci.yml new file mode 100644 index 0000000..c7c6485 --- /dev/null +++ b/gitlab-ci.yml @@ -0,0 +1,13 @@ +stages: + - test + - build + - deploy + - doc + + + +include: + - local: .gitlab/ci/test.gitlab-ci.yml + - local: .gitlab/ci/build.gitlab-ci.yml + - local: .gitlab/ci/deploy.gitlab-ci.yml + - local: .gitlab/ci/doc.gitlab-ci.yml From e71af6402cabdd9a158ce93a7daaf8eb4b71ea36 Mon Sep 17 00:00:00 2001 From: Swann Date: Fri, 23 Apr 2021 11:46:29 +0200 Subject: [PATCH 12/15] feat: increment addon version --- multi_user/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multi_user/__init__.py b/multi_user/__init__.py index ef7aaff..8f4011e 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, 3, 0), + "version": (0, 4, 0), "description": "Enable real-time collaborative workflow inside blender", "blender": (2, 82, 0), "location": "3D View > Sidebar > Multi-User tab", From e8cd271bd8a5538e59e70757d8e57d3e48956b7d Mon Sep 17 00:00:00 2001 From: Swann Date: Fri, 23 Apr 2021 11:48:01 +0200 Subject: [PATCH 13/15] fix: renable gitlab-ci file --- gitlab-ci.yml => .gitlab-ci.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename gitlab-ci.yml => .gitlab-ci.yml (100%) diff --git a/gitlab-ci.yml b/.gitlab-ci.yml similarity index 100% rename from gitlab-ci.yml rename to .gitlab-ci.yml From d9d8ca7ca0c216068e8be0bbe5798660d6561cad Mon Sep 17 00:00:00 2001 From: Swann Date: Fri, 23 Apr 2021 15:35:19 +0200 Subject: [PATCH 14/15] revert: image source replication until a proper fix is done --- multi_user/__init__.py | 2 +- multi_user/bl_types/bl_file.py | 2 ++ multi_user/bl_types/bl_image.py | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/multi_user/__init__.py b/multi_user/__init__.py index 8f4011e..e15a003 100644 --- a/multi_user/__init__.py +++ b/multi_user/__init__.py @@ -44,7 +44,7 @@ from . import environment DEPENDENCIES = { - ("replication", '0.1.30'), + ("replication", '0.1.31'), } diff --git a/multi_user/bl_types/bl_file.py b/multi_user/bl_types/bl_file.py index 120048d..26400c3 100644 --- a/multi_user/bl_types/bl_file.py +++ b/multi_user/bl_types/bl_file.py @@ -134,6 +134,8 @@ class BlFile(ReplicatedDatablock): if self.preferences.clear_memory_filecache: return False else: + if not self.instance: + return False memory_size = sys.getsizeof(self.data['file'])-33 disk_size = self.instance.stat().st_size return memory_size != disk_size diff --git a/multi_user/bl_types/bl_image.py b/multi_user/bl_types/bl_image.py index 3a248c6..901a201 100644 --- a/multi_user/bl_types/bl_image.py +++ b/multi_user/bl_types/bl_image.py @@ -66,7 +66,7 @@ class BlImage(BlDatablock): loader = Loader() loader.load(data, target) - target.source = data['source'] + target.source = 'FILE' target.filepath_raw = get_filepath(data['filename']) color_space_name = data["colorspace_settings"]["name"] @@ -86,7 +86,7 @@ class BlImage(BlDatablock): dumper.depth = 2 dumper.include_filter = [ "name", - 'source', + # 'source', 'size', 'height', 'alpha', From 1e832414947e69d67b491bcbe52b969148784f4b Mon Sep 17 00:00:00 2001 From: Swann Date: Fri, 30 Apr 2021 16:26:20 +0200 Subject: [PATCH 15/15] feat: remove pull socket --- multi_user/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multi_user/__init__.py b/multi_user/__init__.py index e15a003..9fe912e 100644 --- a/multi_user/__init__.py +++ b/multi_user/__init__.py @@ -44,7 +44,7 @@ from . import environment DEPENDENCIES = { - ("replication", '0.1.31'), + ("replication", '0.1.33'), }