Merge branch '111-improve-the-logging-process' into 'develop'

Resolve "Improve the logging process"

See merge request slumber/multi-user!42
This commit is contained in:
Swann Martinez
2020-09-15 11:03:42 +00:00
11 changed files with 235 additions and 105 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -299,8 +299,8 @@ Here is a quick list of available actions:
.. _advanced: .. _advanced:
Advanced configuration Advanced settings
====================== =================
This section contains optional settings to configure the session behavior. This section contains optional settings to configure the session behavior.
@ -309,7 +309,14 @@ This section contains optional settings to configure the session behavior.
Advanced configuration panel Advanced configuration panel
.. rubric:: Network panel -------
Network
-------
.. figure:: img/quickstart_advanced_network.png
:align: center
Advanced network settings
**IPC Port** is the port used for Inter Process Communication. This port is used **IPC Port** is the port used for Inter Process Communication. This port is used
by the multi-users subprocesses to communicate with each others. If different instances by the multi-users subprocesses to communicate with each others. If different instances
@ -323,7 +330,14 @@ of the multi-user are using the same IPC port it will create conflict !
**Timeout (in milliseconds)** is the maximum ping authorized before auto-disconnecting. **Timeout (in milliseconds)** is the maximum ping authorized before auto-disconnecting.
You should only increase it if you have a bad connection. You should only increase it if you have a bad connection.
.. rubric:: Replication panel -----------
Replication
-----------
.. figure:: img/quickstart_advanced_replication.png
:align: center
Advanced replication settings
**Synchronize render settings** (only host) enable replication of EEVEE and CYCLES render settings to match render between clients. **Synchronize render settings** (only host) enable replication of EEVEE and CYCLES render settings to match render between clients.
@ -341,4 +355,25 @@ You should only increase it if you have a bad connection.
- **Refresh**: pushed data update rate (in second) - **Refresh**: pushed data update rate (in second)
- **Apply**: pulled data update rate (in second) - **Apply**: pulled data update rate (in second)
---
Log
---
.. figure:: img/quickstart_advanced_logging.png
:align: center
Advanced log settings
**log level** allow to set the logging level of detail. Here is the detail for each values:
+-----------+-----------------------------------------------+
| Log level | Description |
+===========+===============================================+
| ERROR | Shows only critical error |
+-----------+-----------------------------------------------+
| WARNING | Shows only errors (all kind) |
+-----------+-----------------------------------------------+
| INFO | Shows only status related messages and errors |
+-----------+-----------------------------------------------+
| DEBUG | Shows every possible information. |
+-----------+-----------------------------------------------+

View File

@ -199,11 +199,11 @@ You can run the dedicated server on any platform by following those steps:
replication.serve replication.serve
.. hint:: .. hint::
You can also specify a custom **port** (-p), **timeout** (-t) and **admin password** (-pwd) with the following optionnal argument You can also specify a custom **port** (-p), **timeout** (-t), **admin password** (-pwd), **log level(ERROR, WARNING, INFO or DEBUG)** (-l) and **log file** (-lf) with the following optionnal argument
.. code-block:: bash .. code-block:: bash
replication.serve -p 5555 -pwd toto -t 1000 replication.serve -p 5555 -pwd toto -t 1000 -l INFO -lf server.log
As soon as the dedicated server is running, you can connect to it from blender (follow :ref:`how-to-join`). As soon as the dedicated server is running, you can connect to it from blender (follow :ref:`how-to-join`).

View File

@ -44,13 +44,16 @@ from . import environment, utils
DEPENDENCIES = { DEPENDENCIES = {
("replication", '0.0.21a5'), ("replication", '0.0.21a6'),
} }
def register(): def register():
# Setup logging policy # Setup logging policy
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO) logging.basicConfig(
format='%(asctime)s CLIENT %(levelname)-8s %(message)s',
datefmt='%H:%M:%S',
level=logging.INFO)
try: try:
environment.setup(DEPENDENCIES, bpy.app.binary_path_python) environment.setup(DEPENDENCIES, bpy.app.binary_path_python)

View File

@ -25,7 +25,7 @@ from pathlib import Path
import socket import socket
import re import re
VERSION_EXPR = re.compile('\d+\.\d+\.\d+') VERSION_EXPR = re.compile('\d+\.\d+\.\d+\w\d+')
THIRD_PARTY = os.path.join(os.path.dirname(os.path.abspath(__file__)), "libs") THIRD_PARTY = os.path.join(os.path.dirname(os.path.abspath(__file__)), "libs")
DEFAULT_CACHE_DIR = os.path.join( DEFAULT_CACHE_DIR = os.path.join(
@ -67,7 +67,6 @@ def check_package_version(name, required_version):
out = subprocess.run(f"{str(PYTHON_PATH)} -m pip show {name}", capture_output=True) out = subprocess.run(f"{str(PYTHON_PATH)} -m pip show {name}", capture_output=True)
version = VERSION_EXPR.search(out.stdout.decode()) version = VERSION_EXPR.search(out.stdout.decode())
if version and version.group() == required_version: if version and version.group() == required_version:
logging.info(f"{name} is up to date") logging.info(f"{name} is up to date")
return True return True

View File

@ -34,8 +34,8 @@ from bpy.app.handlers import persistent
from . import bl_types, delayable, environment, presence, ui, utils from . import bl_types, delayable, environment, presence, ui, utils
from replication.constants import (FETCHED, STATE_ACTIVE, from replication.constants import (FETCHED, STATE_ACTIVE,
STATE_INITIAL, STATE_INITIAL,
STATE_SYNCING, RP_COMMON, UP) STATE_SYNCING, RP_COMMON, UP)
from replication.data import ReplicatedDataFactory from replication.data import ReplicatedDataFactory
from replication.exception import NonAuthorizedOperationError from replication.exception import NonAuthorizedOperationError
from replication.interface import Session from replication.interface import Session
@ -71,6 +71,29 @@ class SessionStartOperator(bpy.types.Operator):
users.clear() users.clear()
delayables.clear() delayables.clear()
logging.getLogger().handlers.clear()
# logging.basicConfig(level=settings.logging_level)
formatter = logging.Formatter(
fmt='%(asctime)s CLIENT %(levelname)-8s %(message)s',
datefmt='%H:%M:%S'
)
log_directory = os.path.join(
settings.cache_directory,
"multiuser_client.log")
os.makedirs(settings.cache_directory, exist_ok=True)
logger = logging.getLogger()
handler = logging.FileHandler(log_directory, mode='w')
logger.addHandler(handler)
for handler in logger.handlers:
if isinstance(handler, logging.NullHandler):
continue
handler.setFormatter(formatter)
bpy_factory = ReplicatedDataFactory() bpy_factory = ReplicatedDataFactory()
supported_bl_types = [] supported_bl_types = []
@ -104,7 +127,8 @@ class SessionStartOperator(bpy.types.Operator):
external_update_handling=use_extern_update) external_update_handling=use_extern_update)
if settings.update_method == 'DEPSGRAPH': if settings.update_method == 'DEPSGRAPH':
delayables.append(delayable.ApplyTimer(settings.depsgraph_update_rate/1000)) delayables.append(delayable.ApplyTimer(
settings.depsgraph_update_rate/1000))
# Host a session # Host a session
if self.host: if self.host:
@ -123,7 +147,10 @@ class SessionStartOperator(bpy.types.Operator):
port=settings.port, port=settings.port,
ipc_port=settings.ipc_port, ipc_port=settings.ipc_port,
timeout=settings.connection_timeout, timeout=settings.connection_timeout,
password=admin_pass password=admin_pass,
cache_directory=settings.cache_directory,
server_log_level=logging.getLevelName(
logging.getLogger().level),
) )
except Exception as e: except Exception as e:
self.report({'ERROR'}, repr(e)) self.report({'ERROR'}, repr(e))
@ -162,7 +189,6 @@ class SessionStartOperator(bpy.types.Operator):
delayables.append(session_update) delayables.append(session_update)
delayables.append(session_user_sync) delayables.append(session_user_sync)
@client.register('on_connection') @client.register('on_connection')
def initialize_session(): def initialize_session():
settings = utils.get_preferences() settings = utils.get_preferences()
@ -177,7 +203,6 @@ class SessionStartOperator(bpy.types.Operator):
if node_ref.state == FETCHED: if node_ref.state == FETCHED:
node_ref.apply() node_ref.apply()
# Launch drawing module # Launch drawing module
if runtime_settings.enable_presence: if runtime_settings.enable_presence:
presence.renderer.run() presence.renderer.run()
@ -187,7 +212,8 @@ class SessionStartOperator(bpy.types.Operator):
d.register() d.register()
if settings.update_method == 'DEPSGRAPH': if settings.update_method == 'DEPSGRAPH':
bpy.app.handlers.depsgraph_update_post.append(depsgraph_evaluation) bpy.app.handlers.depsgraph_update_post.append(
depsgraph_evaluation)
@client.register('on_exit') @client.register('on_exit')
def desinitialize_session(): def desinitialize_session():
@ -204,7 +230,8 @@ class SessionStartOperator(bpy.types.Operator):
presence.renderer.stop() presence.renderer.stop()
if settings.update_method == 'DEPSGRAPH': if settings.update_method == 'DEPSGRAPH':
bpy.app.handlers.depsgraph_update_post.remove(depsgraph_evaluation) bpy.app.handlers.depsgraph_update_post.remove(
depsgraph_evaluation)
bpy.ops.session.apply_armature_operator() bpy.ops.session.apply_armature_operator()
@ -422,14 +449,16 @@ class SessionSnapUserOperator(bpy.types.Operator):
if target_scene != context.scene.name: if target_scene != context.scene.name:
blender_scene = bpy.data.scenes.get(target_scene, None) blender_scene = bpy.data.scenes.get(target_scene, None)
if blender_scene is None: if blender_scene is None:
self.report({'ERROR'}, f"Scene {target_scene} doesn't exist on the local client.") self.report(
{'ERROR'}, f"Scene {target_scene} doesn't exist on the local client.")
session_sessings.time_snap_running = False session_sessings.time_snap_running = False
return {"CANCELLED"} return {"CANCELLED"}
bpy.context.window.scene = blender_scene bpy.context.window.scene = blender_scene
# Update client viewmatrix # Update client viewmatrix
client_vmatrix = target_ref['metadata'].get('view_matrix', None) client_vmatrix = target_ref['metadata'].get(
'view_matrix', None)
if client_vmatrix: if client_vmatrix:
rv3d.view_matrix = mathutils.Matrix(client_vmatrix) rv3d.view_matrix = mathutils.Matrix(client_vmatrix)
@ -625,6 +654,7 @@ def update_client_frame(scene):
'frame_current': scene.frame_current 'frame_current': scene.frame_current
}) })
@persistent @persistent
def depsgraph_evaluation(scene): def depsgraph_evaluation(scene):
if client and client.state['STATE'] == STATE_ACTIVE: if client and client.state['STATE'] == STATE_ACTIVE:
@ -648,7 +678,7 @@ def depsgraph_evaluation(scene):
if node and node.owner in [client.id, RP_COMMON] and node.state == UP: if node and node.owner in [client.id, RP_COMMON] and node.state == UP:
# Avoid slow geometry update # Avoid slow geometry update
if 'EDIT' in context.mode and \ if 'EDIT' in context.mode and \
not settings.enable_editmode_updates: not settings.enable_editmode_updates:
break break
client.stash(node.uuid) client.stash(node.uuid)
@ -672,8 +702,6 @@ def register():
bpy.app.handlers.frame_change_pre.append(update_client_frame) bpy.app.handlers.frame_change_pre.append(update_client_frame)
def unregister(): def unregister():
global client global client
@ -690,4 +718,3 @@ def unregister():
bpy.app.handlers.load_pre.remove(load_pre_handler) bpy.app.handlers.load_pre.remove(load_pre_handler)
bpy.app.handlers.frame_change_pre.remove(update_client_frame) bpy.app.handlers.frame_change_pre.remove(update_client_frame)

View File

@ -21,7 +21,8 @@ import bpy
import string import string
import re import re
from . import utils, bl_types, environment, addon_updater_ops, presence, ui 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.constants import RP_COMMON
IP_EXPR = re.compile('\d+\.\d+\.\d+\.\d+') IP_EXPR = re.compile('\d+\.\d+\.\d+\.\d+')
@ -46,6 +47,7 @@ def update_panel_category(self, context):
ui.SESSION_PT_settings.bl_category = self.panel_category ui.SESSION_PT_settings.bl_category = self.panel_category
ui.register() ui.register()
def update_ip(self, context): def update_ip(self, context):
ip = IP_EXPR.search(self.ip) ip = IP_EXPR.search(self.ip)
@ -55,14 +57,25 @@ def update_ip(self, context):
logging.error("Wrong IP format") logging.error("Wrong IP format")
self['ip'] = "127.0.0.1" self['ip'] = "127.0.0.1"
def update_port(self, context): def update_port(self, context):
max_port = self.port + 3 max_port = self.port + 3
if self.ipc_port < max_port and \ if self.ipc_port < max_port and \
self['ipc_port'] >= self.port: self['ipc_port'] >= self.port:
logging.error("IPC Port in conflic with the port, assigning a random value") logging.error(
"IPC Port in conflic with the port, assigning a random value")
self['ipc_port'] = random.randrange(self.port+4, 10000) self['ipc_port'] = random.randrange(self.port+4, 10000)
def set_log_level(self, value):
logging.getLogger().setLevel(value)
def get_log_level(self):
return logging.getLogger().level
class ReplicatedDatablock(bpy.types.PropertyGroup): class ReplicatedDatablock(bpy.types.PropertyGroup):
type_name: bpy.props.StringProperty() type_name: bpy.props.StringProperty()
bl_name: bpy.props.StringProperty() bl_name: bpy.props.StringProperty()
@ -134,7 +147,8 @@ class SessionPrefs(bpy.types.AddonPreferences):
description='replication update method', description='replication update method',
items=[ items=[
('DEFAULT', "Default", "Default: Use threads to monitor databloc changes"), ('DEFAULT', "Default", "Default: Use threads to monitor databloc changes"),
('DEPSGRAPH', "Depsgraph", "Experimental: Use the blender dependency graph to trigger updates"), ('DEPSGRAPH', "Depsgraph",
"Experimental: Use the blender dependency graph to trigger updates"),
], ],
) )
# Replication update settings # Replication update settings
@ -158,17 +172,18 @@ class SessionPrefs(bpy.types.AddonPreferences):
], ],
default='CONFIG' default='CONFIG'
) )
# WIP
logging_level: bpy.props.EnumProperty( logging_level: bpy.props.EnumProperty(
name="Log level", name="Log level",
description="Log verbosity level", description="Log verbosity level",
items=[ items=[
('ERROR', "error", "show only errors"), ('ERROR', "error", "show only errors", logging.ERROR),
('WARNING', "warning", "only show warnings and errors"), ('WARNING', "warning", "only show warnings and errors", logging.WARNING),
('INFO', "info", "default level"), ('INFO', "info", "default level", logging.INFO),
('DEBUG', "debug", "show all logs"), ('DEBUG', "debug", "show all logs", logging.DEBUG),
], ],
default='INFO' default='INFO',
set=set_log_level,
get=get_log_level
) )
conf_session_identity_expanded: bpy.props.BoolProperty( conf_session_identity_expanded: bpy.props.BoolProperty(
name="Identity", name="Identity",
@ -200,7 +215,21 @@ class SessionPrefs(bpy.types.AddonPreferences):
description="Interface", description="Interface",
default=False default=False
) )
sidebar_advanced_rep_expanded: bpy.props.BoolProperty(
name="sidebar_advanced_rep_expanded",
description="sidebar_advanced_rep_expanded",
default=False
)
sidebar_advanced_log_expanded: bpy.props.BoolProperty(
name="sidebar_advanced_log_expanded",
description="sidebar_advanced_log_expanded",
default=False
)
sidebar_advanced_net_expanded: bpy.props.BoolProperty(
name="sidebar_advanced_net_expanded",
description="sidebar_advanced_net_expanded",
default=False
)
auto_check_update: bpy.props.BoolProperty( auto_check_update: bpy.props.BoolProperty(
name="Auto-check for Update", name="Auto-check for Update",
description="If enabled, auto-check for updates using an interval", description="If enabled, auto-check for updates using an interval",
@ -252,8 +281,8 @@ class SessionPrefs(bpy.types.AddonPreferences):
box = grid.box() box = grid.box()
box.prop( box.prop(
self, "conf_session_identity_expanded", text="User informations", self, "conf_session_identity_expanded", text="User informations",
icon='DISCLOSURE_TRI_DOWN' if self.conf_session_identity_expanded icon=get_expanded_icon(self.conf_session_identity_expanded),
else 'DISCLOSURE_TRI_RIGHT', emboss=False) emboss=False)
if self.conf_session_identity_expanded: if self.conf_session_identity_expanded:
box.row().prop(self, "username", text="name") box.row().prop(self, "username", text="name")
box.row().prop(self, "client_color", text="color") box.row().prop(self, "client_color", text="color")
@ -262,8 +291,8 @@ class SessionPrefs(bpy.types.AddonPreferences):
box = grid.box() box = grid.box()
box.prop( box.prop(
self, "conf_session_net_expanded", text="Netorking", self, "conf_session_net_expanded", text="Netorking",
icon='DISCLOSURE_TRI_DOWN' if self.conf_session_net_expanded icon=get_expanded_icon(self.conf_session_net_expanded),
else 'DISCLOSURE_TRI_RIGHT', emboss=False) emboss=False)
if self.conf_session_net_expanded: if self.conf_session_net_expanded:
box.row().prop(self, "ip", text="Address") box.row().prop(self, "ip", text="Address")
@ -280,8 +309,8 @@ class SessionPrefs(bpy.types.AddonPreferences):
table = box.box() table = box.box()
table.row().prop( table.row().prop(
self, "conf_session_timing_expanded", text="Refresh rates", self, "conf_session_timing_expanded", text="Refresh rates",
icon='DISCLOSURE_TRI_DOWN' if self.conf_session_timing_expanded icon=get_expanded_icon(self.conf_session_timing_expanded),
else 'DISCLOSURE_TRI_RIGHT', emboss=False) emboss=False)
if self.conf_session_timing_expanded: if self.conf_session_timing_expanded:
line = table.row() line = table.row()
@ -299,8 +328,8 @@ class SessionPrefs(bpy.types.AddonPreferences):
box = grid.box() box = grid.box()
box.prop( box.prop(
self, "conf_session_hosting_expanded", text="Hosting", self, "conf_session_hosting_expanded", text="Hosting",
icon='DISCLOSURE_TRI_DOWN' if self.conf_session_hosting_expanded icon=get_expanded_icon(self.conf_session_hosting_expanded),
else 'DISCLOSURE_TRI_RIGHT', emboss=False) emboss=False)
if self.conf_session_hosting_expanded: if self.conf_session_hosting_expanded:
row = box.row() row = box.row()
row.label(text="Init the session from:") row.label(text="Init the session from:")
@ -310,8 +339,8 @@ class SessionPrefs(bpy.types.AddonPreferences):
box = grid.box() box = grid.box()
box.prop( box.prop(
self, "conf_session_cache_expanded", text="Cache", self, "conf_session_cache_expanded", text="Cache",
icon='DISCLOSURE_TRI_DOWN' if self.conf_session_cache_expanded icon=get_expanded_icon(self.conf_session_cache_expanded),
else 'DISCLOSURE_TRI_RIGHT', emboss=False) emboss=False)
if self.conf_session_cache_expanded: if self.conf_session_cache_expanded:
box.row().prop(self, "cache_directory", text="Cache directory") box.row().prop(self, "cache_directory", text="Cache directory")
@ -319,7 +348,7 @@ class SessionPrefs(bpy.types.AddonPreferences):
box = grid.box() box = grid.box()
box.prop( box.prop(
self, "conf_session_ui_expanded", text="Interface", self, "conf_session_ui_expanded", text="Interface",
icon='DISCLOSURE_TRI_DOWN' if self.conf_session_ui_expanded else 'DISCLOSURE_TRI_RIGHT', icon=get_expanded_icon(self.conf_session_ui_expanded),
emboss=False) emboss=False)
if self.conf_session_ui_expanded: if self.conf_session_ui_expanded:
box.row().prop(self, "panel_category", text="Panel category", expand=True) box.row().prop(self, "panel_category", text="Panel category", expand=True)
@ -353,7 +382,7 @@ def client_list_callback(scene, context):
items = [(RP_COMMON, RP_COMMON, "")] items = [(RP_COMMON, RP_COMMON, "")]
username = utils.get_preferences().username username = get_preferences().username
cli = operators.client cli = operators.client
if cli: if cli:
client_ids = cli.online_users.keys() client_ids = cli.online_users.keys()

View File

@ -18,7 +18,8 @@
import bpy import bpy
from . import operators, utils from . import operators
from .utils import get_preferences, get_expanded_icon
from replication.constants import (ADDED, ERROR, FETCHED, from replication.constants import (ADDED, ERROR, FETCHED,
MODIFIED, RP_COMMON, UP, MODIFIED, RP_COMMON, UP,
STATE_ACTIVE, STATE_AUTH, STATE_ACTIVE, STATE_AUTH,
@ -112,7 +113,7 @@ class SESSION_PT_settings(bpy.types.Panel):
layout.use_property_split = True layout.use_property_split = True
row = layout.row() row = layout.row()
runtime_settings = context.window_manager.session runtime_settings = context.window_manager.session
settings = utils.get_preferences() settings = get_preferences()
if hasattr(context.window_manager, 'session'): if hasattr(context.window_manager, 'session'):
# STATE INITIAL # STATE INITIAL
@ -195,7 +196,7 @@ class SESSION_PT_settings_network(bpy.types.Panel):
layout = self.layout layout = self.layout
runtime_settings = context.window_manager.session runtime_settings = context.window_manager.session
settings = utils.get_preferences() settings = get_preferences()
# USER SETTINGS # USER SETTINGS
row = layout.row() row = layout.row()
@ -253,7 +254,7 @@ class SESSION_PT_settings_user(bpy.types.Panel):
layout = self.layout layout = self.layout
runtime_settings = context.window_manager.session runtime_settings = context.window_manager.session
settings = utils.get_preferences() settings = get_preferences()
row = layout.row() row = layout.row()
# USER SETTINGS # USER SETTINGS
@ -284,60 +285,88 @@ class SESSION_PT_advanced_settings(bpy.types.Panel):
layout = self.layout layout = self.layout
runtime_settings = context.window_manager.session runtime_settings = context.window_manager.session
settings = utils.get_preferences() settings = get_preferences()
net_section = layout.row().box() net_section = layout.row().box()
net_section.label(text="Network ", icon='TRIA_DOWN') net_section.prop(
net_section_row = net_section.row() settings,
net_section_row.label(text="IPC Port:") "sidebar_advanced_net_expanded",
net_section_row.prop(settings, "ipc_port", text="") text="Network",
net_section_row = net_section.row() icon=get_expanded_icon(settings.sidebar_advanced_net_expanded),
net_section_row.label(text="Timeout (ms):") emboss=False)
net_section_row.prop(settings, "connection_timeout", text="")
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="")
replication_section = layout.row().box() replication_section = layout.row().box()
replication_section.label(text="Replication ", icon='TRIA_DOWN') replication_section.prop(
replication_section_row = replication_section.row() settings,
replication_section_row.label(text="Sync flags", icon='COLLECTION_NEW') "sidebar_advanced_rep_expanded",
replication_section_row = replication_section.row() text="Replication",
replication_section_row.prop(settings.sync_flags, "sync_render_settings") icon=get_expanded_icon(settings.sidebar_advanced_rep_expanded),
replication_section_row = replication_section.row() emboss=False)
# replication_section_row.label(text=":", icon='EDITMODE_HLT')
replication_section_row.prop(settings, "enable_editmode_updates") if settings.sidebar_advanced_rep_expanded:
replication_section_row = replication_section.row()
if settings.enable_editmode_updates:
warning = replication_section_row.box()
warning.label(text="Don't use this with heavy meshes !", icon='ERROR')
replication_section_row = replication_section.row() replication_section_row = replication_section.row()
replication_section_row.label(text="Update method", icon='RECOVER_LAST')
replication_section_row = replication_section.row()
replication_section_row.prop(settings, "update_method", expand=True)
replication_section_row = replication_section.row()
replication_timers = replication_section_row.box()
replication_timers.label(text="Replication timers", icon='TIME')
if settings.update_method == "DEFAULT":
replication_timers = replication_timers.row()
# Replication frequencies
flow = replication_timers.grid_flow(
row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
line = flow.row(align=True)
line.label(text=" ")
line.separator()
line.label(text="refresh (sec)")
line.label(text="apply (sec)")
for item in settings.supported_datablocks: replication_section_row.label(text="Sync flags", icon='COLLECTION_NEW')
replication_section_row = replication_section.row()
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 = replication_section.row()
if settings.enable_editmode_updates:
warning = replication_section_row.box()
warning.label(text="Don't use this with heavy meshes !", icon='ERROR')
replication_section_row = replication_section.row()
replication_section_row.label(text="Update method", icon='RECOVER_LAST')
replication_section_row = replication_section.row()
replication_section_row.prop(settings, "update_method", expand=True)
replication_section_row = replication_section.row()
replication_timers = replication_section_row.box()
replication_timers.label(text="Replication timers", icon='TIME')
if settings.update_method == "DEFAULT":
replication_timers = replication_timers.row()
# Replication frequencies
flow = replication_timers.grid_flow(
row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
line = flow.row(align=True) line = flow.row(align=True)
line.prop(item, "auto_push", text="", icon=item.icon) line.label(text=" ")
line.separator() line.separator()
line.prop(item, "bl_delay_refresh", text="") line.label(text="refresh (sec)")
line.prop(item, "bl_delay_apply", text="") line.label(text="apply (sec)")
else:
replication_timers = replication_timers.row()
replication_timers.label(text="Update rate (ms):")
replication_timers.prop(settings, "depsgraph_update_rate", text="")
for item in settings.supported_datablocks:
line = flow.row(align=True)
line.prop(item, "auto_push", text="", icon=item.icon)
line.separator()
line.prop(item, "bl_delay_refresh", text="")
line.prop(item, "bl_delay_apply", text="")
else:
replication_timers = replication_timers.row()
replication_timers.label(text="Update rate (ms):")
replication_timers.prop(settings, "depsgraph_update_rate", text="")
log_section = layout.row().box()
log_section.prop(
settings,
"sidebar_advanced_log_expanded",
text="Logging",
icon=get_expanded_icon(settings.sidebar_advanced_log_expanded),
emboss=False)
if settings.sidebar_advanced_log_expanded:
log_section_row = log_section.row()
log_section_row.label(text="Log level:")
log_section_row.prop(settings, 'logging_level', text="")
class SESSION_PT_user(bpy.types.Panel): class SESSION_PT_user(bpy.types.Panel):
bl_idname = "MULTIUSER_USER_PT_panel" bl_idname = "MULTIUSER_USER_PT_panel"
bl_label = "Online users" bl_label = "Online users"
@ -356,7 +385,7 @@ class SESSION_PT_user(bpy.types.Panel):
layout = self.layout layout = self.layout
online_users = context.window_manager.online_users online_users = context.window_manager.online_users
selected_user = context.window_manager.user_index selected_user = context.window_manager.user_index
settings = utils.get_preferences() settings = get_preferences()
active_user = online_users[selected_user] if len( active_user = online_users[selected_user] if len(
online_users)-1 >= selected_user else 0 online_users)-1 >= selected_user else 0
runtime_settings = context.window_manager.session runtime_settings = context.window_manager.session
@ -402,7 +431,7 @@ class SESSION_PT_user(bpy.types.Panel):
class SESSION_UL_users(bpy.types.UIList): class SESSION_UL_users(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag):
session = operators.client session = operators.client
settings = utils.get_preferences() settings = get_preferences()
is_local_user = item.username == settings.username is_local_user = item.username == settings.username
ping = '-' ping = '-'
frame_current = '-' frame_current = '-'
@ -486,7 +515,7 @@ class SESSION_PT_services(bpy.types.Panel):
def draw_property(context, parent, property_uuid, level=0): def draw_property(context, parent, property_uuid, level=0):
settings = utils.get_preferences() settings = get_preferences()
runtime_settings = context.window_manager.session runtime_settings = context.window_manager.session
item = operators.client.get(uuid=property_uuid) item = operators.client.get(uuid=property_uuid)
@ -557,7 +586,7 @@ class SESSION_PT_repository(bpy.types.Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
session = operators.client session = operators.client
settings = utils.get_preferences() settings = get_preferences()
admin = False admin = False
if session and hasattr(session,'online_users'): if session and hasattr(session,'online_users'):
@ -576,7 +605,7 @@ class SESSION_PT_repository(bpy.types.Panel):
layout = self.layout layout = self.layout
# Filters # Filters
settings = utils.get_preferences() settings = get_preferences()
runtime_settings = context.window_manager.session runtime_settings = context.window_manager.session
session = operators.client session = operators.client

View File

@ -39,7 +39,7 @@ def find_from_attr(attr_name, attr_value, list):
def get_datablock_users(datablock): def get_datablock_users(datablock):
users = [] users = []
supported_types = get_preferences().supported_datablocks supported_types = get_preferences().supported_datablocks
if hasattr(datablock, 'users_collection') and datablock.users_collection: if hasattr(datablock, 'users_collection') and datablock.users_collection:
users.extend(list(datablock.users_collection)) users.extend(list(datablock.users_collection))
if hasattr(datablock, 'users_scene') and datablock.users_scene: if hasattr(datablock, 'users_scene') and datablock.users_scene:
@ -82,5 +82,13 @@ def resolve_from_id(id, optionnal_type=None):
def get_preferences(): def get_preferences():
return bpy.context.preferences.addons[__package__].preferences return bpy.context.preferences.addons[__package__].preferences
def current_milli_time(): def current_milli_time():
return int(round(time.time() * 1000)) return int(round(time.time() * 1000))
def get_expanded_icon(prop: bpy.types.BoolProperty) -> str:
if prop:
return 'DISCLOSURE_TRI_DOWN'
else:
return 'DISCLOSURE_TRI_RIGHT'