Merge branch '59-timeout-issue-for-scenes-that-are-large-enough' into 'develop'
Resolve "Timeout issue for scenes that are large enough" See merge request slumber/multi-user!14
This commit is contained in:
@@ -31,7 +31,7 @@ DEPENDENCIES = {
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.INFO)
|
||||
logger.setLevel(logging.WARNING)
|
||||
|
||||
#TODO: refactor config
|
||||
# UTILITY FUNCTIONS
|
||||
@@ -118,6 +118,11 @@ class SessionProps(bpy.types.PropertyGroup):
|
||||
description='Distant host port',
|
||||
default=5555
|
||||
)
|
||||
ipc_port: bpy.props.IntProperty(
|
||||
name="ipc_port",
|
||||
description='internal ttl port(only usefull for multiple local instances)',
|
||||
default=5561
|
||||
)
|
||||
is_admin: bpy.props.BoolProperty(
|
||||
name="is_admin",
|
||||
default=False
|
||||
@@ -244,7 +249,7 @@ classes = (
|
||||
|
||||
)
|
||||
|
||||
libs = os.path.dirname(os.path.abspath(__file__))+"\\libs\\replication"
|
||||
libs = os.path.dirname(os.path.abspath(__file__))+"\\libs\\replication\\replication"
|
||||
|
||||
@persistent
|
||||
def load_handler(dummy):
|
||||
|
||||
@@ -4,7 +4,7 @@ import mathutils
|
||||
from .. import utils
|
||||
from ..libs.replication.replication.data import ReplicatedDatablock
|
||||
from ..libs.replication.replication.constants import UP
|
||||
|
||||
from ..libs.replication.replication.constants import DIFF_BINARY
|
||||
|
||||
def dump_driver(driver):
|
||||
dumper = utils.dump_anything.Dumper()
|
||||
@@ -80,6 +80,8 @@ class BlDatablock(ReplicatedDatablock):
|
||||
|
||||
if self.pointer and hasattr(self.pointer, 'uuid'):
|
||||
self.pointer.uuid = self.uuid
|
||||
|
||||
self.diff_method = DIFF_BINARY
|
||||
|
||||
def library_apply(self):
|
||||
"""Apply stored data
|
||||
|
||||
@@ -15,8 +15,11 @@ def dump_image(image):
|
||||
image.save()
|
||||
|
||||
if image.source == "FILE":
|
||||
image_path = bpy.path.abspath(image.filepath_raw)
|
||||
image_directory = os.path.dirname(image_path)
|
||||
os.makedirs(image_directory, exist_ok=True)
|
||||
image.save()
|
||||
file = open(image.filepath_raw, "rb")
|
||||
file = open(image_path, "rb")
|
||||
pixels = file.read()
|
||||
file.close()
|
||||
else:
|
||||
@@ -27,7 +30,7 @@ class BlImage(BlDatablock):
|
||||
bl_id = "images"
|
||||
bl_class = bpy.types.Image
|
||||
bl_delay_refresh = 0
|
||||
bl_delay_apply = 0
|
||||
bl_delay_apply = 1
|
||||
bl_automatic_push = False
|
||||
bl_icon = 'IMAGE_DATA'
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import bpy
|
||||
import mathutils
|
||||
import logging
|
||||
|
||||
from .. import utils
|
||||
from .bl_datablock import BlDatablock
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class BlLightprobe(BlDatablock):
|
||||
bl_id = "lightprobes"
|
||||
@@ -18,10 +20,19 @@ class BlLightprobe(BlDatablock):
|
||||
|
||||
def construct(self, data):
|
||||
type = 'CUBE' if data['type'] == 'CUBEMAP' else data['type']
|
||||
return bpy.data.lightprobes.new(data["name"], type)
|
||||
# See https://developer.blender.org/D6396
|
||||
if bpy.app.version[1] >= 83:
|
||||
return bpy.data.lightprobes.new(data["name"], type)
|
||||
else:
|
||||
logger.warning("Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
||||
|
||||
|
||||
|
||||
|
||||
def dump(self, pointer=None):
|
||||
assert(pointer)
|
||||
if bpy.app.version[1] < 83:
|
||||
logger.warning("Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
||||
|
||||
dumper = utils.dump_anything.Dumper()
|
||||
dumper.depth = 1
|
||||
|
||||
@@ -3,14 +3,17 @@ import bmesh
|
||||
import mathutils
|
||||
|
||||
from .. import utils
|
||||
from ..libs.replication.replication.constants import DIFF_BINARY
|
||||
from .bl_datablock import BlDatablock
|
||||
|
||||
|
||||
def dump_mesh(mesh, data={}):
|
||||
import bmesh
|
||||
|
||||
mesh_data = data
|
||||
mesh_buffer = bmesh.new()
|
||||
|
||||
# https://blog.michelanders.nl/2016/02/copying-vertices-to-numpy-arrays-in_4.html
|
||||
mesh_buffer.from_mesh(mesh)
|
||||
|
||||
uv_layer = mesh_buffer.loops.layers.uv.verify()
|
||||
@@ -72,7 +75,8 @@ def dump_mesh(mesh, data={}):
|
||||
uv_layers.append(uv_layer.name)
|
||||
|
||||
mesh_data["uv_layers"] = uv_layers
|
||||
return mesh_data
|
||||
# return mesh_data
|
||||
|
||||
|
||||
class BlMesh(BlDatablock):
|
||||
bl_id = "meshes"
|
||||
@@ -87,15 +91,12 @@ class BlMesh(BlDatablock):
|
||||
instance.uuid = self.uuid
|
||||
return instance
|
||||
|
||||
def load(self, data, target):
|
||||
def load_implementation(self, data, target):
|
||||
if not target or not target.is_editmode:
|
||||
# 1 - LOAD MATERIAL SLOTS
|
||||
material_to_load = []
|
||||
material_to_load = utils.revers(data["materials"])
|
||||
target.materials.clear()
|
||||
# SLots
|
||||
i = 0
|
||||
|
||||
|
||||
for m in data["material_list"]:
|
||||
target.materials.append(bpy.data.materials[m])
|
||||
|
||||
@@ -106,7 +107,7 @@ class BlMesh(BlDatablock):
|
||||
v = mesh_buffer.verts.new(data["verts"][i]["co"])
|
||||
v.normal = data["verts"][i]["normal"]
|
||||
mesh_buffer.verts.ensure_lookup_table()
|
||||
|
||||
|
||||
for i in data["edges"]:
|
||||
verts = mesh_buffer.verts
|
||||
v1 = data["edges"][i]["verts"][0]
|
||||
@@ -117,12 +118,12 @@ class BlMesh(BlDatablock):
|
||||
verts = []
|
||||
for v in data["faces"][p]["verts"]:
|
||||
verts.append(mesh_buffer.verts[v])
|
||||
|
||||
|
||||
if len(verts) > 0:
|
||||
f = mesh_buffer.faces.new(verts)
|
||||
|
||||
|
||||
uv_layer = mesh_buffer.loops.layers.uv.verify()
|
||||
|
||||
|
||||
f.smooth = data["faces"][p]["smooth"]
|
||||
f.normal = data["faces"][p]["normal"]
|
||||
f.index = data["faces"][p]["index"]
|
||||
@@ -136,21 +137,23 @@ class BlMesh(BlDatablock):
|
||||
|
||||
# 3 - LOAD METADATA
|
||||
# uv's
|
||||
for uv_layer in data['uv_layers']:
|
||||
target.uv_layers.new(name=uv_layer)
|
||||
|
||||
utils.dump_anything.load(target.uv_layers, data['uv_layers'])
|
||||
|
||||
bevel_layer = mesh_buffer.verts.layers.bevel_weight.verify()
|
||||
skin_layer = mesh_buffer.verts.layers.skin.verify()
|
||||
|
||||
|
||||
utils.dump_anything.load(target, data)
|
||||
|
||||
|
||||
|
||||
def dump_implementation(self, data, pointer=None):
|
||||
assert(pointer)
|
||||
|
||||
data = utils.dump_datablock(pointer, 2)
|
||||
data = dump_mesh(pointer, data)
|
||||
dumper = utils.dump_anything.Dumper()
|
||||
dumper.depth = 2
|
||||
dumper.include_filter = [
|
||||
'name',
|
||||
]
|
||||
data = dumper.dump(pointer)
|
||||
dump_mesh(pointer, data)
|
||||
# Fix material index
|
||||
m_list = []
|
||||
for material in pointer.materials:
|
||||
@@ -163,13 +166,12 @@ class BlMesh(BlDatablock):
|
||||
|
||||
def resolve_dependencies(self):
|
||||
deps = []
|
||||
|
||||
|
||||
for material in self.pointer.materials:
|
||||
if material:
|
||||
deps.append(material)
|
||||
|
||||
|
||||
return deps
|
||||
|
||||
|
||||
def is_valid(self):
|
||||
return bpy.data.meshes.get(self.data['name'])
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ class BlObject(BlDatablock):
|
||||
if bpy.app.version[1] >= 83:
|
||||
pointer = bpy.data.lightprobes[data["data"]]
|
||||
else:
|
||||
logger.error("Lightprobe replication only supported since 2.83")
|
||||
logger.warning("Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
||||
instance = bpy.data.objects.new(data["name"], pointer)
|
||||
instance.uuid = self.uuid
|
||||
|
||||
@@ -235,6 +235,9 @@ class BlObject(BlDatablock):
|
||||
'rotation_mode',
|
||||
'location',
|
||||
'scale',
|
||||
'custom_shape',
|
||||
'use_custom_shape_bone_size',
|
||||
'custom_shape_scale',
|
||||
group_index,
|
||||
rotation
|
||||
]
|
||||
|
||||
+24
-20
@@ -3,10 +3,10 @@ import logging
|
||||
import bpy
|
||||
|
||||
from . import operators, presence, utils
|
||||
from .libs.replication.replication.constants import FETCHED, RP_COMMON
|
||||
from .libs.replication.replication.constants import FETCHED, RP_COMMON, STATE_ACTIVE, STATE_SYNCING, STATE_SRV_SYNC
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.INFO)
|
||||
logger.setLevel(logging.WARNING)
|
||||
|
||||
|
||||
class Delayable():
|
||||
@@ -64,15 +64,16 @@ class ApplyTimer(Timer):
|
||||
super().__init__(timout)
|
||||
|
||||
def execute(self):
|
||||
if operators.client:
|
||||
nodes = operators.client.list(filter=self._type)
|
||||
client = operators.client
|
||||
if client and client.state['STATE'] == STATE_ACTIVE:
|
||||
nodes = client.list(filter=self._type)
|
||||
|
||||
for node in nodes:
|
||||
node_ref = operators.client.get(uuid=node)
|
||||
node_ref = client.get(uuid=node)
|
||||
|
||||
if node_ref.state == FETCHED:
|
||||
try:
|
||||
operators.client.apply(node)
|
||||
client.apply(node)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"fail to apply {}: {}".format(node_ref.uuid, e))
|
||||
@@ -89,13 +90,13 @@ class DynamicRightSelectTimer(Timer):
|
||||
session = operators.client
|
||||
settings = bpy.context.window_manager.session
|
||||
|
||||
if session:
|
||||
if session and session.state['STATE'] == STATE_ACTIVE:
|
||||
# Find user
|
||||
if self._user is None:
|
||||
self._user = session.online_users.get(settings.username)
|
||||
|
||||
if self._right_strategy is None:
|
||||
self._right_strategy = session.get_config()[
|
||||
self._right_strategy = session.config[
|
||||
'right_strategy']
|
||||
|
||||
if self._user:
|
||||
@@ -195,23 +196,24 @@ class DrawClient(Draw):
|
||||
session = getattr(operators, 'client', None)
|
||||
renderer = getattr(presence, 'renderer', None)
|
||||
|
||||
if session and renderer:
|
||||
if session and renderer and session.state['STATE'] == STATE_ACTIVE:
|
||||
settings = bpy.context.window_manager.session
|
||||
users = session.online_users
|
||||
|
||||
for user in users.values():
|
||||
metadata = user.get('metadata')
|
||||
|
||||
if settings.presence_show_selected and 'selected_objects' in metadata.keys():
|
||||
renderer.draw_client_selection(
|
||||
user['id'], metadata['color'], metadata['selected_objects'])
|
||||
if settings.presence_show_user and 'view_corners' in metadata:
|
||||
renderer.draw_client_camera(
|
||||
user['id'], metadata['view_corners'], metadata['color'])
|
||||
if 'color' in metadata:
|
||||
if settings.presence_show_selected and 'selected_objects' in metadata.keys():
|
||||
renderer.draw_client_selection(
|
||||
user['id'], metadata['color'], metadata['selected_objects'])
|
||||
if settings.presence_show_user and 'view_corners' in metadata:
|
||||
renderer.draw_client_camera(
|
||||
user['id'], metadata['view_corners'], metadata['color'])
|
||||
|
||||
|
||||
class ClientUpdate(Timer):
|
||||
def __init__(self, timout=1):
|
||||
def __init__(self, timout=.5):
|
||||
super().__init__(timout)
|
||||
|
||||
def execute(self):
|
||||
@@ -220,9 +222,9 @@ class ClientUpdate(Timer):
|
||||
session = getattr(operators, 'client', None)
|
||||
renderer = getattr(presence, 'renderer', None)
|
||||
|
||||
if session and renderer:
|
||||
if session and renderer and session.state['STATE'] == STATE_ACTIVE:
|
||||
# Check if session has been closes prematurely
|
||||
if session.state == 0:
|
||||
if session.state['STATE'] == 0:
|
||||
bpy.ops.session.stop()
|
||||
|
||||
local_user = operators.client.online_users.get(
|
||||
@@ -233,8 +235,7 @@ class ClientUpdate(Timer):
|
||||
local_user_metadata = local_user.get('metadata')
|
||||
current_view_corners = presence.get_view_corners()
|
||||
|
||||
if not local_user_metadata:
|
||||
logger.info("init user metadata")
|
||||
if not local_user_metadata or 'color' not in local_user_metadata.keys():
|
||||
metadata = {
|
||||
'view_corners': current_view_corners,
|
||||
'view_matrix': presence.get_view_matrix(),
|
||||
@@ -273,3 +274,6 @@ class ClientUpdate(Timer):
|
||||
|
||||
# TODO: event drivent 3d view refresh
|
||||
presence.refresh_3d_view()
|
||||
# ui update
|
||||
elif session:
|
||||
presence.refresh_3d_view()
|
||||
@@ -6,7 +6,7 @@ import sys
|
||||
from pathlib import Path
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.INFO)
|
||||
logger.setLevel(logging.WARNING)
|
||||
|
||||
CONFIG_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config")
|
||||
CONFIG = os.path.join(CONFIG_DIR, "app.yaml")
|
||||
|
||||
Submodule multi_user/libs/replication updated: 692a980b6d...19b4eafda2
+92
-59
@@ -4,36 +4,32 @@ import os
|
||||
import queue
|
||||
import random
|
||||
import string
|
||||
import subprocess
|
||||
import time
|
||||
from operator import itemgetter
|
||||
from pathlib import Path
|
||||
import msgpack
|
||||
from subprocess import PIPE, Popen, TimeoutExpired
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
from bpy.app.handlers import persistent
|
||||
from bpy_extras.io_utils import ExportHelper
|
||||
|
||||
from . import bl_types, delayable, environment, presence, ui, utils
|
||||
from .libs.replication.replication.constants import (FETCHED, STATE_ACTIVE,
|
||||
STATE_INITIAL,
|
||||
STATE_SYNCING)
|
||||
from .libs.replication.replication.data import ReplicatedDataFactory
|
||||
from .libs.replication.replication.exception import NonAuthorizedOperationError
|
||||
from .libs.replication.replication.interface import Session
|
||||
from .libs.replication.replication.constants import (
|
||||
STATE_ACTIVE,
|
||||
STATE_INITIAL,
|
||||
STATE_SYNCING,
|
||||
FETCHED)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.INFO)
|
||||
logger.setLevel(logging.WARNING)
|
||||
|
||||
client = None
|
||||
delayables = []
|
||||
ui_context = None
|
||||
stop_modal_executor = False
|
||||
modal_executor_queue = None
|
||||
|
||||
server_process = None
|
||||
# OPERATORS
|
||||
|
||||
|
||||
@@ -49,7 +45,7 @@ class SessionStartOperator(bpy.types.Operator):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
global client, delayables, ui_context
|
||||
global client, delayables, ui_context, server_process
|
||||
settings = context.window_manager.session
|
||||
users = bpy.data.window_managers['WinMan'].online_users
|
||||
|
||||
@@ -85,57 +81,54 @@ class SessionStartOperator(bpy.types.Operator):
|
||||
timout=type_local_config.bl_delay_apply,
|
||||
target_type=type_module_class))
|
||||
|
||||
client = Session(factory=bpy_factory)
|
||||
client = Session(
|
||||
factory=bpy_factory,
|
||||
python_path=bpy.app.binary_path_python,
|
||||
default_strategy=settings.right_strategy)
|
||||
|
||||
# Host a session
|
||||
if self.host:
|
||||
# Scene setup
|
||||
if settings.start_empty:
|
||||
utils.clean_scene()
|
||||
|
||||
try:
|
||||
for scene in bpy.data.scenes:
|
||||
scene_uuid = client.add(scene)
|
||||
client.commit(scene_uuid)
|
||||
|
||||
client.host(
|
||||
id=settings.username,
|
||||
address=settings.ip,
|
||||
port=settings.port,
|
||||
right_strategy=settings.right_strategy
|
||||
)
|
||||
ipc_port=settings.ipc_port)
|
||||
except Exception as e:
|
||||
self.report({'ERROR'}, repr(e))
|
||||
logger.error(f"Error: {e}")
|
||||
|
||||
settings.is_admin = True
|
||||
|
||||
# Join a session
|
||||
else:
|
||||
utils.clean_scene()
|
||||
|
||||
client.connect(
|
||||
id=settings.username,
|
||||
address=settings.ip,
|
||||
port=settings.port
|
||||
)
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
if client.state == 0:
|
||||
settings.is_admin = False
|
||||
self.report(
|
||||
{'ERROR'},
|
||||
"A session is already hosted on this address")
|
||||
return {"CANCELLED"}
|
||||
|
||||
if self.host:
|
||||
for scene in bpy.data.scenes:
|
||||
scene_uuid = client.add(scene)
|
||||
|
||||
# for node in client.list():
|
||||
client.commit(scene_uuid)
|
||||
try:
|
||||
client.connect(
|
||||
id=settings.username,
|
||||
address=settings.ip,
|
||||
port=settings.port,
|
||||
ipc_port=settings.ipc_port
|
||||
)
|
||||
except Exception as e:
|
||||
self.report({'ERROR'}, repr(e))
|
||||
logger.error(f"Error: {e}")
|
||||
|
||||
# Background client updates service
|
||||
#TODO: Refactoring
|
||||
delayables.append(delayable.ClientUpdate())
|
||||
delayables.append(delayable.DrawClient())
|
||||
delayables.append(delayable.DynamicRightSelectTimer())
|
||||
|
||||
# Push all added values
|
||||
client.push_all()
|
||||
|
||||
# Launch drawing module
|
||||
if settings.enable_presence:
|
||||
presence.renderer.run()
|
||||
@@ -165,21 +158,23 @@ class SessionStopOperator(bpy.types.Operator):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
global client, delayables, stop_modal_executor
|
||||
|
||||
global client, delayables, stop_modal_executor, server_process
|
||||
assert(client)
|
||||
stop_modal_executor = True
|
||||
settings = context.window_manager.session
|
||||
settings.is_admin = False
|
||||
assert(client)
|
||||
|
||||
client.disconnect()
|
||||
|
||||
for d in delayables:
|
||||
try:
|
||||
d.unregister()
|
||||
except:
|
||||
continue
|
||||
presence.renderer.stop()
|
||||
presence.renderer.stop()
|
||||
|
||||
try:
|
||||
client.disconnect()
|
||||
except Exception as e:
|
||||
self.report({'ERROR'}, repr(e))
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -321,7 +316,7 @@ class SessionSnapTimeOperator(bpy.types.Operator):
|
||||
settings.user_snap_running = True
|
||||
|
||||
wm = context.window_manager
|
||||
self._timer = wm.event_timer_add(0.1, window=context.window)
|
||||
self._timer = wm.event_timer_add(0.05, window=context.window)
|
||||
wm.modal_handler_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
@@ -404,17 +399,18 @@ class ApplyArmatureOperator(bpy.types.Operator):
|
||||
|
||||
if event.type == 'TIMER':
|
||||
global client
|
||||
nodes = client.list(filter=bl_types.bl_armature.BlArmature)
|
||||
if client and client.state['STATE'] == STATE_ACTIVE:
|
||||
nodes = client.list(filter=bl_types.bl_armature.BlArmature)
|
||||
|
||||
for node in nodes:
|
||||
node_ref = client.get(uuid=node)
|
||||
for node in nodes:
|
||||
node_ref = client.get(uuid=node)
|
||||
|
||||
if node_ref.state == FETCHED:
|
||||
try:
|
||||
client.apply(node)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"fail to apply {}: {}".format(node_ref.uuid, e))
|
||||
if node_ref.state == FETCHED:
|
||||
try:
|
||||
client.apply(node)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"fail to apply {}: {}".format(node_ref.uuid, e))
|
||||
|
||||
return {'PASS_THROUGH'}
|
||||
|
||||
@@ -444,7 +440,6 @@ classes = (
|
||||
SessionCommit,
|
||||
ApplyArmatureOperator,
|
||||
|
||||
|
||||
)
|
||||
|
||||
|
||||
@@ -452,7 +447,7 @@ classes = (
|
||||
def load_pre_handler(dummy):
|
||||
global client
|
||||
|
||||
if client and client.state in [STATE_ACTIVE, STATE_SYNCING]:
|
||||
if client and client.state['STATE'] in [STATE_ACTIVE, STATE_SYNCING]:
|
||||
bpy.ops.session.stop()
|
||||
|
||||
|
||||
@@ -466,18 +461,52 @@ def sanitize_deps_graph(dummy):
|
||||
"""
|
||||
global client
|
||||
|
||||
if client and client.state in [STATE_ACTIVE]:
|
||||
if client and client.state['STATE'] in [STATE_ACTIVE]:
|
||||
for node_key in client.list():
|
||||
client.get(node_key).resolve()
|
||||
|
||||
|
||||
@persistent
|
||||
def update_client_frame(scene):
|
||||
if client and client.state == STATE_ACTIVE:
|
||||
if client and client.state['STATE'] == STATE_ACTIVE:
|
||||
client.update_user_metadata({
|
||||
'frame_current': scene.frame_current
|
||||
})
|
||||
|
||||
@persistent
|
||||
def depsgraph_evaluation(scene):
|
||||
if client and client.state['STATE'] == STATE_ACTIVE:
|
||||
context = bpy.context
|
||||
blender_depsgraph = bpy.context.view_layer.depsgraph
|
||||
dependency_updates = [u for u in blender_depsgraph.updates]
|
||||
session_infos = bpy.context.window_manager.session
|
||||
|
||||
# NOTE: maybe we don't need to check each update but only the first
|
||||
|
||||
for update in reversed(dependency_updates):
|
||||
# Is the object tracked ?
|
||||
if update.id.uuid:
|
||||
# Retrieve local version
|
||||
node = client.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.owner == session_infos.username:
|
||||
# Avoid slow geometry update
|
||||
if 'EDIT' in context.mode:
|
||||
break
|
||||
logger.error("UPDATE: MODIFIFY {}".format(type(update.id)))
|
||||
# client.commit(node.uuid)
|
||||
# client.push(node.uuid)
|
||||
else:
|
||||
# Distant update
|
||||
continue
|
||||
# else:
|
||||
# # New items !
|
||||
# logger.error("UPDATE: ADD")C.obj
|
||||
|
||||
|
||||
def register():
|
||||
from bpy.utils import register_class
|
||||
@@ -491,11 +520,13 @@ def register():
|
||||
|
||||
bpy.app.handlers.frame_change_pre.append(update_client_frame)
|
||||
|
||||
# bpy.app.handlers.depsgraph_update_post.append(depsgraph_evaluation)
|
||||
|
||||
|
||||
def unregister():
|
||||
global client
|
||||
|
||||
if client and client.state == 2:
|
||||
if client and client.state['STATE'] == 2:
|
||||
client.disconnect()
|
||||
client = None
|
||||
|
||||
@@ -510,6 +541,8 @@ def unregister():
|
||||
|
||||
bpy.app.handlers.frame_change_pre.remove(update_client_frame)
|
||||
|
||||
# bpy.app.handlers.depsgraph_update_post.remove(depsgraph_evaluation)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
|
||||
+132
-27
@@ -2,7 +2,11 @@ import bpy
|
||||
|
||||
from . import operators
|
||||
from .libs.replication.replication.constants import (ADDED, ERROR, FETCHED,
|
||||
MODIFIED, RP_COMMON, UP)
|
||||
MODIFIED, RP_COMMON, UP,
|
||||
STATE_ACTIVE, STATE_AUTH,
|
||||
STATE_CONFIG, STATE_SYNCING,
|
||||
STATE_INITIAL, STATE_SRV_SYNC,
|
||||
STATE_WAITING, STATE_QUITTING)
|
||||
|
||||
ICONS_PROP_STATES = ['TRIA_DOWN', # ADDED
|
||||
'TRIA_UP', # COMMITED
|
||||
@@ -11,6 +15,44 @@ ICONS_PROP_STATES = ['TRIA_DOWN', # ADDED
|
||||
'FILE_REFRESH', # UP
|
||||
'TRIA_UP'] # CHANGED
|
||||
|
||||
def printProgressBar (iteration, total, prefix = '', suffix = '', decimals = 1, length = 100, fill = '█', fill_empty=' '):
|
||||
"""
|
||||
Call in a loop to create terminal progress bar
|
||||
@params:
|
||||
iteration - Required : current iteration (Int)
|
||||
total - Required : total iterations (Int)
|
||||
prefix - Optional : prefix string (Str)
|
||||
suffix - Optional : suffix string (Str)
|
||||
decimals - Optional : positive number of decimals in percent complete (Int)
|
||||
length - Optional : character length of bar (Int)
|
||||
fill - Optional : bar fill character (Str)
|
||||
From here:
|
||||
https://gist.github.com/greenstick/b23e475d2bfdc3a82e34eaa1f6781ee4
|
||||
"""
|
||||
percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
|
||||
filledLength = int(length * iteration // total)
|
||||
bar = fill * filledLength + fill_empty * (length - filledLength)
|
||||
return '{} |{}| {}/{}{}'.format(prefix, bar, iteration,total, suffix)
|
||||
|
||||
def get_state_str(state):
|
||||
state_str = 'None'
|
||||
if state == STATE_WAITING:
|
||||
state_str = 'WARMING UP DATA'
|
||||
elif state == STATE_SYNCING:
|
||||
state_str = 'FETCHING FROM SERVER'
|
||||
elif state == STATE_AUTH:
|
||||
state_str = 'AUTHENTIFICATION'
|
||||
elif state == STATE_CONFIG:
|
||||
state_str = 'CONFIGURATION'
|
||||
elif state == STATE_ACTIVE:
|
||||
state_str = 'ONLINE'
|
||||
elif state == STATE_SRV_SYNC:
|
||||
state_str = 'PUSHING TO SERVER'
|
||||
elif state == STATE_INITIAL:
|
||||
state_str = 'INIT'
|
||||
elif state == STATE_QUITTING:
|
||||
state_str = 'QUITTING SESSION'
|
||||
return state_str
|
||||
|
||||
class SESSION_PT_settings(bpy.types.Panel):
|
||||
"""Settings panel"""
|
||||
@@ -31,22 +73,55 @@ 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 == 0):
|
||||
or (operators.client and operators.client.state['STATE'] == STATE_INITIAL):
|
||||
pass
|
||||
else:
|
||||
cli_state = operators.client.state
|
||||
|
||||
row.label(text=f"Status : {get_state_str(cli_state['STATE'])}")
|
||||
row = layout.row()
|
||||
|
||||
current_state = cli_state['STATE']
|
||||
|
||||
# STATE ACTIVE
|
||||
if operators.client.state == 2:
|
||||
row = layout.row()
|
||||
if current_state == STATE_ACTIVE:
|
||||
row.operator("session.stop", icon='QUIT', text="Exit")
|
||||
row = layout.row()
|
||||
|
||||
# STATE SYNCING
|
||||
else:
|
||||
status = "connecting..."
|
||||
row.label(text=status)
|
||||
# CONNECTION STATE
|
||||
elif current_state in [
|
||||
STATE_SRV_SYNC,
|
||||
STATE_SYNCING,
|
||||
STATE_AUTH,
|
||||
STATE_CONFIG,
|
||||
STATE_WAITING]:
|
||||
|
||||
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
|
||||
))
|
||||
|
||||
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,
|
||||
length=16
|
||||
))
|
||||
|
||||
class SESSION_PT_settings_network(bpy.types.Panel):
|
||||
bl_idname = "MULTIUSER_SETTINGS_NETWORK_PT_panel"
|
||||
@@ -59,7 +134,7 @@ class SESSION_PT_settings_network(bpy.types.Panel):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return not operators.client \
|
||||
or (operators.client and operators.client.state == 0)
|
||||
or (operators.client and operators.client.state['STATE'] == 0)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
@@ -71,24 +146,24 @@ class SESSION_PT_settings_network(bpy.types.Panel):
|
||||
row.prop(settings, "session_mode", expand=True)
|
||||
row = layout.row()
|
||||
|
||||
box = row.box()
|
||||
|
||||
row = box.row()
|
||||
row.prop(settings, "ip", text="IP")
|
||||
row = box.row()
|
||||
row.label(text="Port:")
|
||||
row.prop(settings, "port", text="")
|
||||
row = box.row()
|
||||
row.label(text="IPC Port:")
|
||||
row.prop(settings, "ipc_port", text="")
|
||||
|
||||
if settings.session_mode == 'HOST':
|
||||
box = row.box()
|
||||
row = box.row()
|
||||
row.label(text="Start empty:")
|
||||
row.prop(settings, "start_empty", text="")
|
||||
row = box.row()
|
||||
row.label(text="Port:")
|
||||
row.prop(settings, "port", text="")
|
||||
row = box.row()
|
||||
row.operator("session.start", text="HOST").host = True
|
||||
else:
|
||||
box = row.box()
|
||||
row = box.row()
|
||||
row.prop(settings, "ip", text="IP")
|
||||
row = box.row()
|
||||
row.label(text="Port:")
|
||||
row.prop(settings, "port", text="")
|
||||
|
||||
row = box.row()
|
||||
row.operator("session.start", text="CONNECT").host = False
|
||||
|
||||
@@ -104,7 +179,7 @@ class SESSION_PT_settings_user(bpy.types.Panel):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return not operators.client \
|
||||
or (operators.client and operators.client.state == 0)
|
||||
or (operators.client and operators.client.state['STATE'] == 0)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
@@ -132,7 +207,7 @@ class SESSION_PT_settings_replication(bpy.types.Panel):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return not operators.client \
|
||||
or (operators.client and operators.client.state == 0)
|
||||
or (operators.client and operators.client.state['STATE'] == 0)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
@@ -174,7 +249,7 @@ class SESSION_PT_user(bpy.types.Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return operators.client and operators.client.state == 2
|
||||
return operators.client and operators.client.state['STATE'] == 2
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
@@ -223,7 +298,7 @@ class SESSION_UL_users(bpy.types.UIList):
|
||||
if user:
|
||||
ping = str(user['latency'])
|
||||
metadata = user.get('metadata')
|
||||
if metadata:
|
||||
if metadata and 'frame_current' in metadata:
|
||||
frame_current = str(metadata['frame_current'])
|
||||
|
||||
split = layout.split(factor=0.5)
|
||||
@@ -243,7 +318,8 @@ class SESSION_PT_presence(bpy.types.Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
return not operators.client \
|
||||
or (operators.client and operators.client.state['STATE'] in [STATE_INITIAL, STATE_ACTIVE])
|
||||
|
||||
def draw_header(self, context):
|
||||
self.layout.prop(context.window_manager.session, "enable_presence", text="")
|
||||
@@ -258,6 +334,34 @@ class SESSION_PT_presence(bpy.types.Panel):
|
||||
col.prop(settings,"presence_show_user")
|
||||
row = layout.row()
|
||||
|
||||
class SESSION_PT_services(bpy.types.Panel):
|
||||
bl_idname = "MULTIUSER_SERVICE_PT_panel"
|
||||
bl_label = "Services"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "Multiuser"
|
||||
bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel'
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return operators.client and operators.client.state['STATE'] == 2
|
||||
|
||||
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))
|
||||
|
||||
|
||||
|
||||
|
||||
def draw_property(context, parent, property_uuid, level=0):
|
||||
settings = context.window_manager.session
|
||||
@@ -330,7 +434,7 @@ class SESSION_PT_outliner(bpy.types.Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return operators.client and operators.client.state == 2
|
||||
return operators.client and operators.client.state['STATE'] == 2
|
||||
|
||||
def draw_header(self, context):
|
||||
self.layout.label(text="", icon='OUTLINER_OB_GROUP_INSTANCE')
|
||||
@@ -385,7 +489,8 @@ classes = (
|
||||
SESSION_PT_presence,
|
||||
SESSION_PT_settings_replication,
|
||||
SESSION_PT_user,
|
||||
SESSION_PT_outliner
|
||||
SESSION_PT_outliner,
|
||||
SESSION_PT_services
|
||||
)
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ from . import environment, presence
|
||||
from .libs import dump_anything
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.INFO)
|
||||
logger.setLevel(logging.WARNING)
|
||||
|
||||
def has_action(target):
|
||||
return (hasattr(target, 'animation_data')
|
||||
|
||||
Reference in New Issue
Block a user