diff --git a/multi_user/__init__.py b/multi_user/__init__.py index dcffd90..6af4c31 100644 --- a/multi_user/__init__.py +++ b/multi_user/__init__.py @@ -44,10 +44,12 @@ from . import environment, utils DEPENDENCIES = { - ("replication", '0.0.21a9'), + ("replication", '0.0.21a10'), } +module_error_msg = "Insufficient rights to install the multi-user \ + dependencies, aunch blender with administrator rights." def register(): # Setup logging policy logging.basicConfig( @@ -57,22 +59,22 @@ def register(): try: environment.setup(DEPENDENCIES, bpy.app.binary_path_python) - except ModuleNotFoundError: - logging.fatal("Fail to install multi-user dependencies, try to execute blender with admin rights.") - return - - from . import presence - from . import operators - from . import ui - from . import preferences - from . import addon_updater_ops - preferences.register() - addon_updater_ops.register(bl_info) - presence.register() - operators.register() - ui.register() + from . import presence + from . import operators + from . import ui + from . import preferences + from . import addon_updater_ops + preferences.register() + addon_updater_ops.register(bl_info) + presence.register() + operators.register() + ui.register() + except ModuleNotFoundError as e: + raise Exception(module_error_msg) + logging.error(module_error_msg) + bpy.types.WindowManager.session = bpy.props.PointerProperty( type=preferences.SessionProps) bpy.types.ID.uuid = bpy.props.StringProperty( diff --git a/multi_user/bl_types/bl_mesh.py b/multi_user/bl_types/bl_mesh.py index d1c322b..6af2ee6 100644 --- a/multi_user/bl_types/bl_mesh.py +++ b/multi_user/bl_types/bl_mesh.py @@ -114,7 +114,7 @@ class BlMesh(BlDatablock): def _dump_implementation(self, data, instance=None): assert(instance) - if instance.is_editmode and not self.preferences.enable_editmode_updates: + if instance.is_editmode and not self.preferences.sync_flags.sync_during_editmode: raise ContextError("Mesh is in edit mode") mesh = instance diff --git a/multi_user/bl_types/bl_object.py b/multi_user/bl_types/bl_object.py index 84929cf..edc3e26 100644 --- a/multi_user/bl_types/bl_object.py +++ b/multi_user/bl_types/bl_object.py @@ -201,7 +201,7 @@ class BlObject(BlDatablock): assert(instance) if _is_editmode(instance): - if self.preferences.enable_editmode_updates: + if self.preferences.sync_flags.sync_during_editmode: instance.update_from_editmode() else: raise ContextError("Object is in edit-mode.") diff --git a/multi_user/bl_types/bl_scene.py b/multi_user/bl_types/bl_scene.py index a4ce943..13d3d27 100644 --- a/multi_user/bl_types/bl_scene.py +++ b/multi_user/bl_types/bl_scene.py @@ -23,6 +23,9 @@ from .dump_anything import Loader, Dumper from .bl_datablock import BlDatablock from .bl_collection import dump_collection_children, dump_collection_objects, load_collection_childrens, load_collection_objects from ..utils import get_preferences +from replication.constants import (DIFF_JSON, MODIFIED) +from deepdiff import DeepDiff +import logging class BlScene(BlDatablock): bl_id = "scenes" @@ -33,6 +36,11 @@ class BlScene(BlDatablock): bl_check_common = True bl_icon = 'SCENE_DATA' + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.diff_method = DIFF_JSON + def _construct(self, data): instance = bpy.data.scenes.new(data["name"]) return instance @@ -52,23 +60,23 @@ class BlScene(BlDatablock): # Annotation if 'grease_pencil' in data.keys(): target.grease_pencil = bpy.data.grease_pencils[data['grease_pencil']] + if self.preferences.sync_flags.sync_render_settings: + if 'eevee' in data.keys(): + loader.load(target.eevee, data['eevee']) - if 'eevee' in data.keys(): - loader.load(target.eevee, data['eevee']) - - if 'cycles' in data.keys(): - loader.load(target.eevee, data['cycles']) + if 'cycles' in data.keys(): + loader.load(target.eevee, data['cycles']) - if 'render' in data.keys(): - loader.load(target.render, data['render']) + if 'render' in data.keys(): + loader.load(target.render, data['render']) - if 'view_settings' in data.keys(): - loader.load(target.view_settings, data['view_settings']) - if target.view_settings.use_curve_mapping: - #TODO: change this ugly fix - target.view_settings.curve_mapping.white_level = data['view_settings']['curve_mapping']['white_level'] - target.view_settings.curve_mapping.black_level = data['view_settings']['curve_mapping']['black_level'] - target.view_settings.curve_mapping.update() + if 'view_settings' in data.keys(): + loader.load(target.view_settings, data['view_settings']) + if target.view_settings.use_curve_mapping: + #TODO: change this ugly fix + target.view_settings.curve_mapping.white_level = data['view_settings']['curve_mapping']['white_level'] + target.view_settings.curve_mapping.black_level = data['view_settings']['curve_mapping']['black_level'] + target.view_settings.curve_mapping.update() def _dump_implementation(self, data, instance=None): assert(instance) @@ -97,10 +105,8 @@ class BlScene(BlDatablock): scene_dumper.depth = 1 scene_dumper.include_filter = None - - pref = get_preferences() - if pref.sync_flags.sync_render_settings: + if self.preferences.sync_flags.sync_render_settings: scene_dumper.exclude_filter = [ 'gi_cache_info', 'feature_set', @@ -156,3 +162,14 @@ class BlScene(BlDatablock): deps.append(self.instance.grease_pencil) return deps + + def diff(self): + exclude_path = [] + + if not self.preferences.sync_flags.sync_render_settings: + exclude_path.append("root['eevee']") + exclude_path.append("root['cycles']") + exclude_path.append("root['view_settings']") + exclude_path.append("root['render']") + + return DeepDiff(self.data, self._dump(instance=self.instance),exclude_paths=exclude_path, cache_size=5000) \ No newline at end of file diff --git a/multi_user/operators.py b/multi_user/operators.py index 7f4afc6..65b95d5 100644 --- a/multi_user/operators.py +++ b/multi_user/operators.py @@ -713,7 +713,7 @@ def depsgraph_evaluation(scene): if node and node.owner in [client.id, RP_COMMON] and node.state == UP: # Avoid slow geometry update if 'EDIT' in context.mode and \ - not settings.enable_editmode_updates: + not settings.sync_during_editmode: break client.stash(node.uuid) diff --git a/multi_user/preferences.py b/multi_user/preferences.py index 905833f..889d122 100644 --- a/multi_user/preferences.py +++ b/multi_user/preferences.py @@ -99,11 +99,29 @@ class ReplicatedDatablock(bpy.types.PropertyGroup): icon: bpy.props.StringProperty() +def update_scene_settings(self, value): + from .operators import client + + self['sync_render_settings'] = value + + if client and bpy.context.scene.uuid and value: + bpy.ops.session.apply('INVOKE_DEFAULT', target=bpy.context.scene.uuid) + class ReplicationFlags(bpy.types.PropertyGroup): + def get_scene_settings(self): + return self.get('sync_render_settings', False) + sync_render_settings: bpy.props.BoolProperty( name="Synchronize render settings", description="Synchronize render settings (eevee and cycles only)", - default=True) + default=True, + set=update_scene_settings, + get=get_scene_settings) + sync_during_editmode: bpy.props.BoolProperty( + name="Edit mode updates", + description="Enable objects update in edit mode (! Impact performances !)", + default=False + ) class SessionPrefs(bpy.types.AddonPreferences): @@ -136,8 +154,8 @@ class SessionPrefs(bpy.types.AddonPreferences): ipc_port: bpy.props.IntProperty( name="ipc_port", description='internal ttl port(only usefull for multiple local instances)', - default=5561, - update=update_port + default=random.randrange(5570,70000), + update=update_port, ) init_method: bpy.props.EnumProperty( name='init_method', @@ -171,11 +189,6 @@ class SessionPrefs(bpy.types.AddonPreferences): description='Dependency graph uppdate rate (milliseconds)', default=100 ) - enable_editmode_updates: bpy.props.BoolProperty( - name="Edit mode updates", - description="Enable objects update in edit mode (! Impact performances !)", - default=False - ) clear_memory_filecache: bpy.props.BoolProperty( name="Clear memory filecache", description="Remove filecache from memory", diff --git a/multi_user/ui.py b/multi_user/ui.py index 954a1f0..057a4ae 100644 --- a/multi_user/ui.py +++ b/multi_user/ui.py @@ -122,61 +122,31 @@ class SESSION_PT_settings(bpy.types.Panel): or (operators.client and operators.client.state['STATE'] == STATE_INITIAL): pass else: - cli_state = operators.client.state - - + cli_state = operators.client.state row = layout.row() current_state = cli_state['STATE'] - - # STATE ACTIVE - if current_state in [STATE_ACTIVE]: - row.operator("session.stop", icon='QUIT', text="Exit") - row = layout.row() - if runtime_settings.is_host: - row = row.box() - row.label(text=f"LAN: {runtime_settings.internet_ip}", icon='INFO') - row = layout.row() + info_msg = None + + if current_state in [STATE_ACTIVE] and runtime_settings.is_host: + info_msg = f"LAN: {runtime_settings.internet_ip}" if current_state == STATE_LOBBY: - row = row.box() - row.label(text=f"Waiting the session to start", icon='INFO') - row = layout.row() - row.operator("session.stop", icon='QUIT', text="Exit") - # CONNECTION STATE - elif current_state in [STATE_SRV_SYNC, - STATE_SYNCING, - STATE_AUTH, - STATE_CONFIG, - STATE_WAITING]: + info_msg = "Waiting the session to start." - if cli_state['STATE'] in [STATE_SYNCING, STATE_SRV_SYNC, STATE_WAITING]: - box = row.box() - box.label(text=printProgressBar( - cli_state['CURRENT'], - cli_state['TOTAL'], - length=16 - )) + if info_msg: + info_box = row.box() + info_box.row().label(text=info_msg,icon='INFO') - row = layout.row() - row.operator("session.stop", icon='QUIT', text="CANCEL") - elif current_state == STATE_QUITTING: - row = layout.row() - box = row.box() - - num_online_services = 0 - for name, state in operators.client.services_state.items(): - if state == STATE_ACTIVE: - num_online_services += 1 - - total_online_services = len( - operators.client.services_state) - - box.label(text=printProgressBar( - total_online_services-num_online_services, - total_online_services, + # Progress bar + 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'], length=16 )) + layout.row().operator("session.stop", icon='QUIT', text="Exit") class SESSION_PT_settings_network(bpy.types.Panel): bl_idname = "MULTIUSER_SETTINGS_NETWORK_PT_panel" @@ -321,9 +291,9 @@ class SESSION_PT_advanced_settings(bpy.types.Panel): replication_section_row.prop(settings.sync_flags, "sync_render_settings") replication_section_row = replication_section.row() - replication_section_row.prop(settings, "enable_editmode_updates") + replication_section_row.prop(settings.sync_flags, "sync_during_editmode") replication_section_row = replication_section.row() - if settings.enable_editmode_updates: + if settings.sync_flags.sync_during_editmode: warning = replication_section_row.box() warning.label(text="Don't use this with heavy meshes !", icon='ERROR') replication_section_row = replication_section.row() @@ -503,9 +473,9 @@ class SESSION_PT_presence(bpy.types.Panel): row.prop(settings, "presence_show_far_user") -class SESSION_PT_services(bpy.types.Panel): +class SESSION_PT_synchronization(bpy.types.Panel): bl_idname = "MULTIUSER_SERVICE_PT_panel" - bl_label = "Services" + bl_label = "Synchronization" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel' @@ -516,21 +486,16 @@ class SESSION_PT_services(bpy.types.Panel): return operators.client and operators.client.state['STATE'] == 2 def draw_header(self, context): - self.layout.label(text="", icon='FILE_CACHE') + self.layout.label(text="", icon='COLLECTION_NEW') def draw(self, context): layout = self.layout - online_users = context.window_manager.online_users - selected_user = context.window_manager.user_index - settings = context.window_manager.session - active_user = online_users[selected_user] if len(online_users)-1 >= selected_user else 0 - - # Create a simple row. - for name, state in operators.client.services_state.items(): - row = layout.row() - row.label(text=name) - row.label(text=get_state_str(state)) - + settings = get_preferences() + + row = layout.row() + row.prop(settings.sync_flags, "sync_render_settings") + row = layout.row() + row.prop(settings.sync_flags, "sync_during_editmode") def draw_property(context, parent, property_uuid, level=0): settings = get_preferences() @@ -681,7 +646,7 @@ classes = ( SESSION_PT_presence, SESSION_PT_advanced_settings, SESSION_PT_user, - SESSION_PT_services, + SESSION_PT_synchronization, SESSION_PT_repository, )