Compare commits

..

2 Commits

Author SHA1 Message Date
48866b74d3 refacctor: remove wrong charaters 2020-07-07 22:35:56 +02:00
d9f1031107 feat: initial version 2020-07-07 22:34:40 +02:00
28 changed files with 203 additions and 242 deletions

View File

@ -5,3 +5,4 @@ stages:
include: include:
- local: .gitlab/ci/test.gitlab-ci.yml - local: .gitlab/ci/test.gitlab-ci.yml
- local: .gitlab/ci/build.gitlab-ci.yml - local: .gitlab/ci/build.gitlab-ci.yml

View File

@ -1,7 +1,10 @@
build: build:
stage: build stage: build
image: debian:stable-slim image: python:latest
script: script:
- git submodule init
- git submodule update
- cd multi_user/libs/replication
- rm -rf tests .git .gitignore script - rm -rf tests .git .gitignore script
artifacts: artifacts:
@ -9,4 +12,3 @@ build:
paths: paths:
- multi_user - multi_user

View File

@ -1,14 +1,14 @@
test: test:
stage: test stage: test
image: python:3.7 image: python:latest
script: script:
- git submodule init - git submodule init
- git submodule update - git submodule update
- apt-get update - apt update
# install blender to get all required dependencies # install blender to get all required dependencies
# TODO: indtall only dependencies # TODO: indtall only dependencies
- apt install -f -y gcc python-dev python3.7-dev
- apt install -f -y blender - apt install -f -y blender
- python -m pip install blender-addon-tester - python3 -m pip install blender-addon-tester
- python scripts/test_addon.py - python3 scripts/test_addon.py

3
.gitmodules vendored
View File

@ -0,0 +1,3 @@
[submodule "multi_user/libs/replication"]
path = multi_user/libs/replication
url = https://gitlab.com/slumber/replication.git

View File

@ -11,7 +11,7 @@ This tool aims to allow multiple users to work on the same scene over the networ
## Quick installation ## Quick installation
1. Download latest release [multi_user.zip](https://gitlab.com/slumber/multi-user/-/jobs/artifacts/master/download?job=build). 1. Download latest release [multi_user.zip](/uploads/8aef79c7cf5b1d9606dc58307fd9ad8b/multi_user.zip).
2. Run blender as administrator (dependencies installation). 2. Run blender as administrator (dependencies installation).
3. Install last_version.zip from your addon preferences. 3. Install last_version.zip from your addon preferences.

View File

@ -8,4 +8,5 @@ Getting started
install install
quickstart quickstart
known_problems
glossary glossary

View File

@ -0,0 +1,46 @@
.. _known-problems:
==============
Known problems
==============
.. rubric:: What do you need to do in order to use Multi-User through internet?
1. Use Hamachi or ZeroTier (I prefer Hamachi) and create a network.
2. All participants need to join this network.
3. Go to Blender and install Multi-User in the preferneces.
4. Setup and start the session:
* **Host**: After activating Multi-User as an Add-On, press N and go on Multi-User.
Then, put the IP of your network where IP is asked for.
Leave Port and IPC Port on default(5555 and 5561). Increase the Timeout(ms) if the connection is not stable.
Then press on "host".
* **Guest**: After activating Multi-User as an Add-On, press N and go to Multi-User
Then, put the IP of your network where IP is asked for.
Leave Port and IPC Port on default(5555 and 5561)(Simpler, put the same information that the host is using.
BUT,it needs 4 ports for communication. Therefore, you need to put 5555+count of guests [up to 4]. ).
Increase the Timeout(ms) if the connection is not stable. Then press on "connexion".
.. rubric:: What do you need to check if you can't host?
You need to check, if the IP and all ports are correct. If it's not loading, because you laoded a project before hosting, it's not your fault.
Then the version is not sable yet (the project contains data, that is not made stable yet).
.. rubric:: What do you need to check if you can't connect?
Check, if you are connected to the network (VPN) of the host. Also, check if you have all of the information like the host has.
Maybe you have different versions (which shouldn't be the case after Auto-Updater is introduced).
.. rubric:: You are connected, but you dont see anything?
After pressing N, go presence overlay and check the box.
Also, go down and uncheck the box "Show only owned"(unless you need privacy ( ͡° ͜ʖ ͡°) ).
If it's still not working, hit the support channel on the discord channel "multi-user". This little helping text is produced by my own experience
(Ultr-X).
In order to bring attention to other problems, please @ me on the support channel. Every problem brought to me will be documentated to optimize and update this text.
Thank you and have fun with Multi-User, brought to you by "swann".
Here the discord server: https://discord.gg/v5eKgm

View File

@ -48,6 +48,7 @@ Documentation is organized into the following sections:
getting_started/install getting_started/install
getting_started/quickstart getting_started/quickstart
getting_started/known_problems
getting_started/glossary getting_started/glossary
.. toctree:: .. toctree::

View File

@ -186,24 +186,25 @@ Using a regular command line
You can run the dedicated server on any platform by following those steps: You can run the dedicated server on any platform by following those steps:
1. Firstly, download and intall python 3 (3.6 or above). 1. Firstly, download and intall python 3 (3.6 or above).
2. Install the replication library: 2. Download and extract the dedicated server from `here <https://gitlab.com/slumber/replication/-/archive/develop/replication-develop.zip>`_
3. Open a terminal in the extracted folder and install python dependencies by running:
.. code-block:: bash .. code-block:: bash
python -m pip install replication python -m pip install -r requirements.txt
4. Launch the server with: 4. Launch the server from the same terminal with:
.. code-block:: bash .. code-block:: bash
replication.serve python scripts/server.py
.. 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) and **admin password** (-pwd) with the following optionnal argument
.. code-block:: bash .. code-block:: bash
replication.serve -p 5555 -pwd toto -t 1000 python scripts/server.py -p 5555 -pwd toto -t 1000
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

@ -21,7 +21,7 @@ bl_info = {
"author": "Swann Martinez", "author": "Swann Martinez",
"version": (0, 0, 3), "version": (0, 0, 3),
"description": "Enable real-time collaborative workflow inside blender", "description": "Enable real-time collaborative workflow inside blender",
"blender": (2, 82, 0), "blender": (2, 80, 0),
"location": "3D View > Sidebar > Multi-User tab", "location": "3D View > Sidebar > Multi-User tab",
"warning": "Unstable addon, use it at your own risks", "warning": "Unstable addon, use it at your own risks",
"category": "Collaboration", "category": "Collaboration",
@ -45,15 +45,22 @@ from . import environment, utils
# TODO: remove dependency as soon as replication will be installed as a module # TODO: remove dependency as soon as replication will be installed as a module
DEPENDENCIES = { DEPENDENCIES = {
("replication", '0.0.20'), ("zmq","zmq"),
("deepdiff", '5.0.1'), ("jsondiff","jsondiff"),
("deepdiff", "deepdiff"),
("psutil","psutil")
} }
libs = os.path.dirname(os.path.abspath(__file__))+"\\libs\\replication\\replication"
def register(): def register():
# Setup logging policy # Setup logging policy
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO) logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO)
if libs not in sys.path:
sys.path.append(libs)
try: try:
environment.setup(DEPENDENCIES, bpy.app.binary_path_python) environment.setup(DEPENDENCIES, bpy.app.binary_path_python)
except ModuleNotFoundError: except ModuleNotFoundError:

View File

@ -722,22 +722,21 @@ class Singleton_updater(object):
self._source_zip = os.path.join(local,"source.zip") self._source_zip = os.path.join(local,"source.zip")
if self._verbose: print(f"Starting download update zip to {self._source_zip}") if self._verbose: print("Starting download update zip")
try: try:
import urllib3 request = urllib.request.Request(url)
http = urllib3.PoolManager() context = ssl._create_unverified_context()
r = http.request('GET', url, preload_content=False)
chunk_size = 1024*8
with open(self._source_zip, 'wb') as out:
while True:
data = r.read(chunk_size)
if not data:
break
out.write(data)
r.release_conn() # setup private token if appropriate
if self._engine.token != None:
if self._engine.name == "gitlab":
request.add_header('PRIVATE-TOKEN',self._engine.token)
else:
if self._verbose: print("Tokens not setup for selected engine yet")
self.urlretrieve(urllib.request.urlopen(request,context=context), self._source_zip)
# add additional checks on file size being non-zero
if self._verbose: print("Successfully downloaded update zip") if self._verbose: print("Successfully downloaded update zip")
return False return True
except Exception as e: except Exception as e:
self._error = "Error retrieving download, bad link?" self._error = "Error retrieving download, bad link?"
self._error_msg = "Error: {}".format(e) self._error_msg = "Error: {}".format(e)

View File

@ -38,7 +38,7 @@ __all__ = [
] # Order here defines execution order ] # Order here defines execution order
from . import * from . import *
from replication.data import ReplicatedDataFactory from ..libs.replication.replication.data import ReplicatedDataFactory
def types_to_register(): def types_to_register():
return __all__ return __all__

View File

@ -92,7 +92,6 @@ class BlArmature(BlDatablock):
new_bone.head = bone_data['head_local'] new_bone.head = bone_data['head_local']
new_bone.tail_radius = bone_data['tail_radius'] new_bone.tail_radius = bone_data['tail_radius']
new_bone.head_radius = bone_data['head_radius'] new_bone.head_radius = bone_data['head_radius']
# new_bone.roll = bone_data['roll']
if 'parent' in bone_data: if 'parent' in bone_data:
new_bone.parent = target.edit_bones[data['bones'] new_bone.parent = target.edit_bones[data['bones']
@ -124,8 +123,7 @@ class BlArmature(BlDatablock):
'use_connect', 'use_connect',
'parent', 'parent',
'name', 'name',
'layers', 'layers'
# 'roll',
] ]
data = dumper.dump(instance) data = dumper.dump(instance)

View File

@ -45,22 +45,13 @@ class BlCamera(BlDatablock):
if dof_settings: if dof_settings:
loader.load(target.dof, dof_settings) loader.load(target.dof, dof_settings)
background_images = data.get('background_images')
if background_images:
target.background_images.clear()
for img_name, img_data in background_images.items():
target_img = target.background_images.new()
target_img.image = bpy.data.images[img_name]
loader.load(target_img, img_data)
def _dump_implementation(self, data, instance=None): def _dump_implementation(self, data, instance=None):
assert(instance) assert(instance)
# TODO: background image support # TODO: background image support
dumper = Dumper() dumper = Dumper()
dumper.depth = 3 dumper.depth = 2
dumper.include_filter = [ dumper.include_filter = [
"name", "name",
'type', 'type',
@ -88,24 +79,7 @@ class BlCamera(BlDatablock):
'sensor_fit', 'sensor_fit',
'sensor_height', 'sensor_height',
'sensor_width', 'sensor_width',
'show_background_images',
'background_images',
'alpha',
'display_depth',
'frame_method',
'offset',
'rotation',
'scale',
'use_flip_x',
'use_flip_y',
'image'
] ]
return dumper.dump(instance) return dumper.dump(instance)
def _resolve_deps_implementation(self):
deps = []
for background in self.instance.background_images:
if background.image:
deps.append(background.image)
return deps

View File

@ -21,8 +21,8 @@ import mathutils
from .. import utils from .. import utils
from .dump_anything import Loader, Dumper from .dump_anything import Loader, Dumper
from replication.data import ReplicatedDatablock from ..libs.replication.replication.data import ReplicatedDatablock
from replication.constants import (UP, DIFF_BINARY) from ..libs.replication.replication.constants import (UP, DIFF_BINARY)
def has_action(target): def has_action(target):
@ -117,10 +117,9 @@ class BlDatablock(ReplicatedDatablock):
datablock_ref = utils.find_from_attr('uuid', self.uuid, datablock_root) datablock_ref = utils.find_from_attr('uuid', self.uuid, datablock_root)
if not datablock_ref: if not datablock_ref:
try: datablock_ref = datablock_root.get(
datablock_ref = datablock_root[self.data['name']] self.data['name'], # Resolve by name
except Exception: self._construct(data=self.data)) # If it doesn't exist create it
datablock_ref = self._construct(data=self.data)
if datablock_ref: if datablock_ref:
setattr(datablock_ref, 'uuid', self.uuid) setattr(datablock_ref, 'uuid', self.uuid)

View File

@ -21,7 +21,7 @@ import mathutils
from .dump_anything import Dumper, Loader, np_dump_collection, np_load_collection from .dump_anything import Dumper, Loader, np_dump_collection, np_load_collection
from .bl_datablock import BlDatablock from .bl_datablock import BlDatablock
from replication.exception import ContextError from ..libs.replication.replication.exception import ContextError
POINT = ['co', 'weight_softbody', 'co_deform'] POINT = ['co', 'weight_softbody', 'co_deform']

View File

@ -19,13 +19,11 @@
import bpy import bpy
import mathutils import mathutils
import logging import logging
import re
from .. import utils from .. import utils
from .dump_anything import Loader, Dumper from .dump_anything import Loader, Dumper
from .bl_datablock import BlDatablock from .bl_datablock import BlDatablock
NODE_SOCKET_INDEX = re.compile('\[(\d*)\]')
def load_node(node_data, node_tree): def load_node(node_data, node_tree):
""" Load a node into a node_tree from a dict """ Load a node into a node_tree from a dict
@ -40,13 +38,14 @@ def load_node(node_data, node_tree):
loader.load(target_node, node_data) loader.load(target_node, node_data)
for input in node_data["inputs"]: for input in node_data["inputs"]:
if hasattr(target_node.inputs[input], "default_value"): if hasattr(target_node.inputs[input], "default_value"):
try: try:
target_node.inputs[input].default_value = node_data["inputs"][input]["default_value"] target_node.inputs[input].default_value = node_data["inputs"][input]["default_value"]
except: except:
logging.error( logging.error(f"Material {input} parameter not supported, skipping")
f"Material {input} parameter not supported, skipping")
def load_links(links_data, node_tree): def load_links(links_data, node_tree):
@ -61,6 +60,7 @@ def load_links(links_data, node_tree):
for link in links_data: for link in links_data:
input_socket = node_tree.nodes[link['to_node']].inputs[int(link['to_socket'])] input_socket = node_tree.nodes[link['to_node']].inputs[int(link['to_socket'])]
output_socket = node_tree.nodes[link['from_node']].outputs[int(link['from_socket'])] output_socket = node_tree.nodes[link['from_node']].outputs[int(link['from_socket'])]
node_tree.links.new(input_socket, output_socket) node_tree.links.new(input_socket, output_socket)
@ -75,13 +75,11 @@ def dump_links(links):
links_data = [] links_data = []
for link in links: for link in links:
to_socket = NODE_SOCKET_INDEX.search(link.to_socket.path_from_id()).group(1)
from_socket = NODE_SOCKET_INDEX.search(link.from_socket.path_from_id()).group(1)
links_data.append({ links_data.append({
'to_node':link.to_node.name, 'to_node':link.to_node.name,
'to_socket': to_socket, 'to_socket':link.to_socket.path_from_id()[-2:-1],
'from_node':link.from_node.name, 'from_node':link.from_node.name,
'from_socket': from_socket, 'from_socket':link.from_socket.path_from_id()[-2:-1],
}) })
return links_data return links_data
@ -178,6 +176,7 @@ class BlMaterial(BlDatablock):
loader.load( loader.load(
target.grease_pencil, data['grease_pencil']) target.grease_pencil, data['grease_pencil'])
if data["use_nodes"]: if data["use_nodes"]:
if target.node_tree is None: if target.node_tree is None:
target.use_nodes = True target.use_nodes = True
@ -266,3 +265,4 @@ class BlMaterial(BlDatablock):
deps.append(self.instance.library) deps.append(self.instance.library)
return deps return deps

View File

@ -23,8 +23,8 @@ import logging
import numpy as np import numpy as np
from .dump_anything import Dumper, Loader, np_load_collection_primitives, np_dump_collection_primitive, np_load_collection, np_dump_collection from .dump_anything import Dumper, Loader, np_load_collection_primitives, np_dump_collection_primitive, np_load_collection, np_dump_collection
from replication.constants import DIFF_BINARY from ..libs.replication.replication.constants import DIFF_BINARY
from replication.exception import ContextError from ..libs.replication.replication.exception import ContextError
from .bl_datablock import BlDatablock from .bl_datablock import BlDatablock

View File

@ -22,7 +22,7 @@ import logging
from .dump_anything import Loader, Dumper from .dump_anything import Loader, Dumper
from .bl_datablock import BlDatablock from .bl_datablock import BlDatablock
from replication.exception import ContextError from ..libs.replication.replication.exception import ContextError
def load_pose(target_bone, data): def load_pose(target_bone, data):
@ -152,13 +152,6 @@ class BlObject(BlDatablock):
target.data.shape_keys.key_blocks[key_block].relative_key = target.data.shape_keys.key_blocks[reference] target.data.shape_keys.key_blocks[key_block].relative_key = target.data.shape_keys.key_blocks[reference]
# TODO: find another way...
if target.type == 'EMPTY':
img_key = data.get('data')
if target.data is None and img_key:
target.data = bpy.data.images.get(img_key, None)
def _dump_implementation(self, data, instance=None): def _dump_implementation(self, data, instance=None):
assert(instance) assert(instance)
@ -178,14 +171,6 @@ class BlObject(BlDatablock):
"library", "library",
"empty_display_type", "empty_display_type",
"empty_display_size", "empty_display_size",
"empty_image_offset",
"empty_image_depth",
"empty_image_side",
"show_empty_image_orthographic",
"show_empty_image_perspective",
"show_empty_image_only_axis_aligned",
"use_empty_image_alpha",
"color"
"instance_collection", "instance_collection",
"instance_type", "instance_type",
"location", "location",

View File

@ -20,7 +20,7 @@ import logging
import bpy import bpy
from . import operators, presence, utils from . import operators, presence, utils
from replication.constants import (FETCHED, from .libs.replication.replication.constants import (FETCHED,
RP_COMMON, RP_COMMON,
STATE_INITIAL, STATE_INITIAL,
STATE_QUITTING, STATE_QUITTING,
@ -239,7 +239,7 @@ class DrawClient(Draw):
class ClientUpdate(Timer): class ClientUpdate(Timer):
def __init__(self, timout=.032): def __init__(self, timout=.016):
super().__init__(timout) super().__init__(timout)
self.handle_quit = False self.handle_quit = False
self.users_metadata = {} self.users_metadata = {}
@ -298,23 +298,6 @@ class ClientUpdate(Timer):
local_user_metadata['view_corners'] = current_view_corners local_user_metadata['view_corners'] = current_view_corners
local_user_metadata['view_matrix'] = presence.get_view_matrix() local_user_metadata['view_matrix'] = presence.get_view_matrix()
session.update_user_metadata(local_user_metadata) session.update_user_metadata(local_user_metadata)
class SessionStatusUpdate(Timer):
def __init__(self, timout=1):
super().__init__(timout)
def execute(self):
presence.refresh_sidebar_view()
class SessionUserSync(Timer):
def __init__(self, timout=1):
super().__init__(timout)
def execute(self):
session = getattr(operators, 'client', None)
renderer = getattr(presence, 'renderer', None)
if session and renderer:
# sync online users # sync online users
session_users = operators.client.online_users session_users = operators.client.online_users
ui_users = bpy.context.window_manager.online_users ui_users = bpy.context.window_manager.online_users
@ -331,3 +314,15 @@ class SessionUserSync(Timer):
new_key = ui_users.add() new_key = ui_users.add()
new_key.name = user new_key.name = user
new_key.username = user new_key.username = user
elif session.state['STATE'] == STATE_QUITTING:
presence.refresh_sidebar_view()
self.handle_quit = True
elif session.state['STATE'] == STATE_INITIAL and self.handle_quit:
self.handle_quit = False
presence.refresh_sidebar_view()
operators.unregister_delayables()
presence.renderer.stop()
presence.refresh_sidebar_view()

View File

@ -23,9 +23,6 @@ import subprocess
import sys import sys
from pathlib import Path from pathlib import Path
import socket import socket
import re
VERSION_EXPR = re.compile('\d+\.\d+\.\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(
@ -50,22 +47,10 @@ def install_pip():
subprocess.run([str(PYTHON_PATH), "-m", "ensurepip"]) subprocess.run([str(PYTHON_PATH), "-m", "ensurepip"])
def install_package(name, version): def install_package(name):
logging.info(f"installing {name} version...") logging.debug(f"Using {PYTHON_PATH} for installation")
subprocess.run([str(PYTHON_PATH), "-m", "pip", "install", f"{name}=={version}"]) subprocess.run([str(PYTHON_PATH), "-m", "pip", "install", name])
def check_package_version(name, required_version):
logging.info(f"Checking {name} version...")
out = subprocess.run(f"{str(PYTHON_PATH)} -m pip show {name}", capture_output=True)
version = VERSION_EXPR.search(out.stdout.decode())
if version and version.group() == required_version:
logging.info(f"{name} is up to date")
return True
else:
logging.info(f"{name} need an update")
return False
def get_ip(): def get_ip():
""" """
@ -93,9 +78,7 @@ def setup(dependencies, python_path):
if not module_can_be_imported("pip"): if not module_can_be_imported("pip"):
install_pip() install_pip()
for package_name, package_version in dependencies: for module_name, package_name in dependencies:
if not module_can_be_imported(package_name): if not module_can_be_imported(module_name):
install_package(package_name, package_version) install_package(package_name)
module_can_be_imported(package_name) module_can_be_imported(package_name)
elif not check_package_version(package_name, package_version):
install_package(package_name, package_version)

View File

View File

@ -33,19 +33,31 @@ import mathutils
from bpy.app.handlers import persistent 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 .libs.replication.replication.constants import (FETCHED, STATE_ACTIVE,
STATE_INITIAL, STATE_INITIAL,
STATE_SYNCING) STATE_SYNCING)
from replication.data import ReplicatedDataFactory from .libs.replication.replication.data import ReplicatedDataFactory
from replication.exception import NonAuthorizedOperationError from .libs.replication.replication.exception import NonAuthorizedOperationError
from replication.interface import Session from .libs.replication.replication.interface import Session
client = None client = None
delayables = [] delayables = []
stop_modal_executor = False stop_modal_executor = False
modal_executor_queue = None
def unregister_delayables():
global delayables, stop_modal_executor
for d in delayables:
try:
d.unregister()
except:
continue
stop_modal_executor = True
# OPERATORS # OPERATORS
@ -68,6 +80,7 @@ class SessionStartOperator(bpy.types.Operator):
users = bpy.data.window_managers['WinMan'].online_users users = bpy.data.window_managers['WinMan'].online_users
admin_pass = runtime_settings.password admin_pass = runtime_settings.password
unregister_delayables()
users.clear() users.clear()
delayables.clear() delayables.clear()
@ -150,23 +163,6 @@ class SessionStartOperator(bpy.types.Operator):
delayables.append(delayable.DrawClient()) delayables.append(delayable.DrawClient())
delayables.append(delayable.DynamicRightSelectTimer()) delayables.append(delayable.DynamicRightSelectTimer())
session_update = delayable.SessionStatusUpdate()
session_user_sync = delayable.SessionUserSync()
session_update.register()
session_user_sync.register()
delayables.append(session_update)
delayables.append(session_user_sync)
@client.register('on_connection')
def initialize_session():
for node in client._graph.list_ordered():
node_ref = client.get(node)
if node_ref.state == FETCHED:
node_ref.resolve()
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()
@ -175,19 +171,8 @@ class SessionStartOperator(bpy.types.Operator):
for d in delayables: for d in delayables:
d.register() d.register()
@client.register('on_exit') global modal_executor_queue
def desinitialize_session(): modal_executor_queue = queue.Queue()
global delayables, stop_modal_executor
for d in delayables:
try:
d.unregister()
except:
continue
stop_modal_executor = True
presence.renderer.stop()
bpy.ops.session.apply_armature_operator() bpy.ops.session.apply_armature_operator()
self.report( self.report(
@ -542,7 +527,7 @@ class ApplyArmatureOperator(bpy.types.Operator):
try: try:
client.apply(node) client.apply(node)
except Exception as e: except Exception as e:
logging.error("Fail to apply armature: {e}") logging.error("Dail to apply armature: {e}")
return {'PASS_THROUGH'} return {'PASS_THROUGH'}

View File

@ -22,7 +22,7 @@ import string
import re import re
from . import utils, bl_types, environment, addon_updater_ops, presence, ui from . import utils, bl_types, environment, addon_updater_ops, presence, ui
from replication.constants import RP_COMMON from .libs.replication.replication.constants import RP_COMMON
IP_EXPR = re.compile('\d+\.\d+\.\d+\.\d+') IP_EXPR = re.compile('\d+\.\d+\.\d+\.\d+')

View File

@ -19,7 +19,6 @@
import copy import copy
import logging import logging
import math import math
import traceback
import bgl import bgl
import blf import blf
@ -312,10 +311,10 @@ class DrawFactory(object):
self.d2d_items[client_id] = (position[1], client_id, color) self.d2d_items[client_id] = (position[1], client_id, color)
except Exception as e: except Exception as e:
logging.debug(f"Draw client exception: {e} \n {traceback.format_exc()}\n pos:{position},ind:{indices}") logging.error(f"Draw client exception: {e}")
def draw3d_callback(self): def draw3d_callback(self):
bgl.glLineWidth(2.) bgl.glLineWidth(1.5)
bgl.glEnable(bgl.GL_DEPTH_TEST) bgl.glEnable(bgl.GL_DEPTH_TEST)
bgl.glEnable(bgl.GL_BLEND) bgl.glEnable(bgl.GL_BLEND)
bgl.glEnable(bgl.GL_LINE_SMOOTH) bgl.glEnable(bgl.GL_LINE_SMOOTH)

View File

@ -19,7 +19,7 @@
import bpy import bpy
from . import operators, utils from . import operators, utils
from replication.constants import (ADDED, ERROR, FETCHED, from .libs.replication.replication.constants import (ADDED, ERROR, FETCHED,
MODIFIED, RP_COMMON, UP, MODIFIED, RP_COMMON, UP,
STATE_ACTIVE, STATE_AUTH, STATE_ACTIVE, STATE_AUTH,
STATE_CONFIG, STATE_SYNCING, STATE_CONFIG, STATE_SYNCING,
@ -50,8 +50,6 @@ def printProgressBar(iteration, total, prefix='', suffix='', decimals=1, length=
From here: From here:
https://gist.github.com/greenstick/b23e475d2bfdc3a82e34eaa1f6781ee4 https://gist.github.com/greenstick/b23e475d2bfdc3a82e34eaa1f6781ee4
""" """
if total == 0:
return ""
filledLength = int(length * iteration // total) filledLength = int(length * iteration // total)
bar = fill * filledLength + fill_empty * (length - filledLength) bar = fill * filledLength + fill_empty * (length - filledLength)
return f"{prefix} |{bar}| {iteration}/{total}{suffix}" return f"{prefix} |{bar}| {iteration}/{total}{suffix}"
@ -128,18 +126,14 @@ class SESSION_PT_settings(bpy.types.Panel):
current_state = cli_state['STATE'] current_state = cli_state['STATE']
# STATE ACTIVE # STATE ACTIVE
if current_state in [STATE_ACTIVE]: if current_state in [STATE_ACTIVE, STATE_LOBBY]:
row.operator("session.stop", icon='QUIT', text="Exit") row.operator("session.stop", icon='QUIT', text="Exit")
row = layout.row() row = layout.row()
if runtime_settings.is_host: if runtime_settings.is_host:
row = row.box() row = row.box()
row.label(text=f"LAN: {runtime_settings.internet_ip}", icon='INFO') row.label(text=f"{runtime_settings.internet_ip}:{settings.port}", icon='INFO')
row = layout.row() row = layout.row()
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 # CONNECTION STATE
elif current_state in [STATE_SRV_SYNC, elif current_state in [STATE_SRV_SYNC,
STATE_SYNCING, STATE_SYNCING,
@ -362,8 +356,6 @@ class SESSION_PT_user(bpy.types.Panel):
if active_user != 0 and active_user.username != settings.username: if active_user != 0 and active_user.username != settings.username:
row = layout.row() row = layout.row()
user_operations = row.split() user_operations = row.split()
if operators.client.state['STATE'] == STATE_ACTIVE:
user_operations.alert = context.window_manager.session.time_snap_running user_operations.alert = context.window_manager.session.time_snap_running
user_operations.operator( user_operations.operator(
"session.snapview", "session.snapview",
@ -540,18 +532,9 @@ class SESSION_PT_repository(bpy.types.Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
session = operators.client
settings = utils.get_preferences()
admin = False
if session and hasattr(session,'online_users'):
usr = session.online_users.get(settings.username)
if usr:
admin = usr['admin']
return hasattr(context.window_manager, 'session') and \ return hasattr(context.window_manager, 'session') and \
operators.client and \ operators.client and \
(operators.client.state['STATE'] == STATE_ACTIVE or \ operators.client.state['STATE'] in [STATE_ACTIVE, STATE_LOBBY]
operators.client.state['STATE'] == STATE_LOBBY and admin)
def draw_header(self, context): def draw_header(self, context):
self.layout.label(text="", icon='OUTLINER_OB_GROUP_INSTANCE') self.layout.label(text="", icon='OUTLINER_OB_GROUP_INSTANCE')

View File

@ -30,11 +30,9 @@ CONSTRAINTS_TYPES = [
'COPY_ROTATION', 'COPY_SCALE', 'COPY_TRANSFORMS', 'LIMIT_DISTANCE', 'COPY_ROTATION', 'COPY_SCALE', 'COPY_TRANSFORMS', 'LIMIT_DISTANCE',
'LIMIT_LOCATION', 'LIMIT_ROTATION', 'LIMIT_SCALE', 'MAINTAIN_VOLUME', 'LIMIT_LOCATION', 'LIMIT_ROTATION', 'LIMIT_SCALE', 'MAINTAIN_VOLUME',
'TRANSFORM', 'TRANSFORM_CACHE', 'CLAMP_TO', 'DAMPED_TRACK', 'IK', 'TRANSFORM', 'TRANSFORM_CACHE', 'CLAMP_TO', 'DAMPED_TRACK', 'IK',
'LOCKED_TRACK', 'STRETCH_TO', 'TRACK_TO', 'ACTION', 'LOCKED_TRACK', 'SPLINE_IK', 'STRETCH_TO', 'TRACK_TO', 'ACTION',
'ARMATURE', 'CHILD_OF', 'FLOOR', 'FOLLOW_PATH', 'PIVOT', 'SHRINKWRAP'] 'ARMATURE', 'CHILD_OF', 'FLOOR', 'FOLLOW_PATH', 'PIVOT', 'SHRINKWRAP']
#temporary disabled 'SPLINE_IK' until its fixed
def test_object(clear_blend): def test_object(clear_blend):
bpy.ops.mesh.primitive_cube_add( bpy.ops.mesh.primitive_cube_add(
enter_editmode=False, align='WORLD', location=(0, 0, 0)) enter_editmode=False, align='WORLD', location=(0, 0, 0))