Compare commits

...

87 Commits

Author SHA1 Message Date
Swann Martinez
12355b6457 Merge branch 'develop' into 'master'
fix: blender 3.5 compatibility

See merge request slumber/multi-user!179
2023-04-11 12:48:36 +00:00
Swann Martinez
74ad4e5e1f fix: bump add-on version to 0.5.8 2023-04-11 12:43:52 +00:00
Swann
2a88c3e5ac feat: use requirement to install dependencies
fix: _bool numpy not found
2023-04-06 09:26:29 +02:00
Swann Martinez
4c42a5be92 fix: freeze deepdiff version in replication 2022-08-18 21:40:07 +02:00
Swann Martinez
757ee7015a Merge branch 'develop' into 'master'
fix: cross-platform serialization errors

See merge request slumber/multi-user!177
2022-08-07 12:39:06 +00:00
Swann Martinez
15d66579c6 fix: deepdiff dependency error, freezing it to 5.7.0. 2022-08-07 14:36:36 +02:00
Swann Martinez
4128a47b88 fix: put back numpy types 2022-07-31 14:57:32 +02:00
Swann Martinez
689a565c75 fix: bump version 2022-07-07 14:34:33 +02:00
Swann Martinez
c5f1bf1176 fix: cross-platform serialization errors 2022-07-07 14:29:32 +02:00
Swann Martinez
4dc6781c94 Merge branch 'develop' into 'master'
v0.5.5

See merge request slumber/multi-user!176
2022-06-12 19:23:41 +00:00
Swann Martinez
5311e55208 fix: doc version number 2022-06-12 21:16:11 +02:00
Swann Martinez
4cb64e5e77 doc: update changelog and version 2022-06-12 21:10:38 +02:00
Swann Martinez
ff67b581b1 Merge branch '256-numpy-mesh-serialization-error' into 'develop'
Resolve "Numpy mesh serialization error"

See merge request slumber/multi-user!175
2022-06-12 19:04:56 +00:00
Swann Martinez
f7bec3fc08 fix: try to use other numpy types to store data collection 2022-06-11 14:00:36 +02:00
Swann Martinez
5e929db3ee Merge branch 'develop' into 'master'
v0.5.3

See merge request slumber/multi-user!174
2022-03-11 17:59:27 +00:00
Swann Martinez
629f2e1cdb feat: update changelog 2022-03-11 18:52:30 +01:00
Swann Martinez
b8fed806ed feat: update version 2022-03-11 18:38:38 +01:00
Swann Martinez
8190846b59 fix: blender 3.1 numpy loading compatibility 2022-03-11 18:38:09 +01:00
Swann Martinez
c228b6ad7f refactpr: snapshot logs 2022-03-09 11:19:09 +01:00
Swann Martinez
48651ce890 fix: uuid error when joining a server 2022-03-09 10:42:44 +01:00
Swann Martinez
26847cf459 fix: server crashing during snapshots 2022-03-08 18:06:54 +01:00
Swann Martinez
bfa6991c00 fix: server docker file 2022-03-02 14:26:01 +01:00
Swann Martinez
0c60c86775 Merge branch 'develop' into 'master'
v0.5.2

See merge request slumber/multi-user!173
2022-02-18 15:12:56 +00:00
Swann Martinez
70b6f9bcfa feat: update changelog for 0.5.2 2022-02-18 11:28:56 +01:00
Swann Martinez
8d176b55e4 Merge branch '250-geometry-nodes-attribue-toogle-doesn-t-sync' into 'develop'
Resolve "Geometry nodes attribue toogle doesn't sync"

See merge request slumber/multi-user!172
2022-02-17 09:51:41 +00:00
Swann Martinez
4c0356e724 fix: geometry node boolean parameter loading
related to #250
2022-02-17 10:43:47 +01:00
Swann Martinez
6b04d1d8d6 Merge branch '248-objects-not-selectable-after-user-leaves-session' into 'develop'
Resolve "objects not selectable after user leaves session"

See merge request slumber/multi-user!171
2022-02-15 10:03:58 +00:00
Swann Martinez
edfcdd8867 feat: bump version 2022-02-15 11:00:18 +01:00
Swann Martinez
bdd6599614 fix: objects not selectable after user leaves session (or kicked)
Related to #248
2022-02-15 10:55:26 +01:00
Swann Martinez
6efd1321ce Merge branch 'develop' into 'master'
v0.5.1

See merge request slumber/multi-user!169
2022-02-10 15:25:02 +00:00
Swann Martinez
047bd47048 Merge branch '247-auto-updater-breaks-dependency-auto-installer' into 'develop'
feat: bump addon version

See merge request slumber/multi-user!170
2022-02-10 15:20:58 +00:00
Swann Martinez
d32cbb7b30 feat: bump addon version 2022-02-10 16:20:22 +01:00
Swann Martinez
adabce3822 Merge branch '247-auto-updater-breaks-dependency-auto-installer' into 'develop'
Resolve "Auto updater breaks dependency auto installer"

See merge request slumber/multi-user!168
2022-02-10 15:10:37 +00:00
Swann Martinez
62f52db5b2 fix: auto updater with tags 2022-02-10 16:06:53 +01:00
Swann Martinez
745f45b682 fix: addon directory not cleared during an update 2022-02-10 15:44:46 +01:00
Swann Martinez
4b7573234a Merge branch 'develop' into 'master'
v0.5.0

See merge request slumber/multi-user!164
2022-02-10 13:15:45 +00:00
Swann Martinez
f84860f520 feat: update changelog 2022-02-10 12:06:29 +01:00
Swann Martinez
c7ee67d4dd fix: replication typo (@kromar) 2022-02-10 11:58:09 +01:00
Swann Martinez
7ed4644b75 feat: added 0.5.0 update to the changelog 2022-02-10 11:55:14 +01:00
Swann Martinez
e0c4a17be9 feat: update version 2022-02-10 11:23:46 +01:00
Swann Martinez
2a6181b832 fix: replication typo 2022-02-10 11:23:01 +01:00
Swann Martinez
0f7c9adec5 fix: Panel calss prefix warning 2022-02-10 11:20:11 +01:00
Swann Martinez
f094ec097c doc: remove replication version 2022-02-10 11:15:08 +01:00
Swann Martinez
2495b5b0e7 Merge branch '245-skin-modifier-vertex-radius-not-synchronized' into 'develop'
Resolve "skin modifier vertex radius not synchronized"

See merge request slumber/multi-user!165
2022-02-07 14:08:22 +00:00
Swann Martinez
cc829b66d1 fix: skin loading
related to #245
2022-02-07 15:04:36 +01:00
Swann Martinez
97cec4f9af Merge branch '224-doc-update-new-ui-0-5-0' into 'develop'
Doc update new UI 0.5.0

See merge request slumber/multi-user!150
2022-02-07 12:40:21 +00:00
Swann Martinez
3669aafcff Merge branch '244-text-material-not-replicating-for-online-sessions' into 'develop'
Resolve "Text Material not Replicating(for online sessions)"

See merge request slumber/multi-user!163
2021-12-17 15:20:05 +00:00
Swann
dfcfb84c20 fix: text curve material loading 2021-12-17 16:14:42 +01:00
Swann Martinez
5390e1a60c Merge branch '235-show-color-in-connected-user-pannel' into 'develop'
Resolve "Show color in connected user pannel"

See merge request slumber/multi-user!154
2021-12-13 21:35:33 +00:00
Swann
2910ea654b clean: row factor 2021-12-13 22:29:55 +01:00
Swann Martinez
ff2ecec18b Merge branch '243-server-crash-during-public-sessions' into 'develop'
Resolve "Server crash during public sessions"

See merge request slumber/multi-user!162
2021-12-10 15:00:06 +00:00
Swann
7555b1332a feat: update version 2021-12-10 15:56:47 +01:00
Swann
690e450349 fix: avoid to store Commit in the replication graph 2021-12-10 15:55:59 +01:00
Swann Martinez
de32bd89e3 Merge branch '237-add-draw-user-option-for-the-session-snapshot-importer' into 'develop'
Resolve "Add draw user option for the session snapshot importer"

See merge request slumber/multi-user!156
2021-11-18 15:21:36 +00:00
Swann
50e86aea15 fix user drawing options 2021-11-18 16:05:24 +01:00
Swann
c05a12343c feat: selection drawing 2021-11-18 15:22:07 +01:00
Swann
a09193fba2 feat: expose user radius and intensity 2021-11-18 11:53:24 +01:00
Swann
60e21f2b8e fix: load user 2021-11-18 11:43:01 +01:00
Swann
421f00879f feat draw users 2021-11-18 11:40:56 +01:00
Swann
5ac61b5348 Merge branch 'develop' into 235-show-color-in-connected-user-pannel 2021-11-17 16:23:03 +01:00
Swann
189e5c6cf1 Merge branch 'develop' into 235-show-color-in-connected-user-pannel 2021-11-17 16:19:03 +01:00
Swann
964e6a8c63 feat: uesr meshes 2021-11-16 09:55:13 +01:00
Swann Martinez
80c81dc934 Merge branch '240-adding-music-to-the-sequencer-isn-t-replicating' into 'develop'
Resolve "Adding music to the sequencer isn't replicating"

See merge request slumber/multi-user!159
2021-11-09 09:29:58 +00:00
Swann
563fdb693d fix: sound not loading
Related to #240
2021-11-09 10:26:47 +01:00
Swann Martinez
a64eea3cea Merge branch '239-blender-3-x-compatibility' into 'develop'
Ensure blender 3.x compatibility : Fix geometry node outputs replication

See merge request slumber/multi-user!158
2021-11-09 08:48:30 +00:00
Swann
03ad7c0066 fix: geometry nodes input / output 2021-11-08 17:34:02 +01:00
Swann Martinez
d685573834 Merge branch '239-blender-3-x-compatibility' into 'develop'
Ensure blender 3.x version check

See merge request slumber/multi-user!157
2021-11-05 15:20:35 +00:00
Swann
0681b53141 fix: version check 2021-11-05 15:39:46 +01:00
Swann
6f02b38b0e fix(replication): missing version update 2021-11-03 16:37:12 +01:00
Swann
92c773dae9 Merge branch 'develop' of gitlab.com:slumber/multi-user into develop 2021-11-03 16:34:43 +01:00
Swann
f48ade6390 fix python 3.10 compatibility (@NotFood) 2021-11-03 16:32:40 +01:00
Swann Martinez
63c4501b88 Merge branch '236-crash-with-empty-after-a-reconnection' into 'develop'
Resolve "Crash with empty after a reconnection"

See merge request slumber/multi-user!155
2021-10-29 09:40:04 +00:00
Swann
06e21c86ce fix none attribute error 2021-10-21 12:19:46 +02:00
Swann
e28d3860da user color property 2021-10-21 12:00:12 +02:00
Swann
7b247372fb test: add user color 2021-10-21 12:00:00 +02:00
Swann Martinez
9d484b00e9 Merge branch '234-user-info-in-side-panel' into 'develop'
User Info in side panel

See merge request slumber/multi-user!153
2021-08-19 16:09:24 +00:00
Fabian
de9255f71c feat: presence overlay button+UInfo in side panel 2021-08-19 18:04:07 +02:00
Swann Martinez
99528ea3e0 Merge branch '232-fix-ui-host-and-lobby' into 'develop'
Resolve "fix ui host and lobby"

See merge request slumber/multi-user!152
2021-08-16 14:03:16 +00:00
Fabian
bb342951a5 fix: lobby init 2021-08-16 15:59:19 +02:00
Fabian
438a79177b fix: host solo 2021-08-16 12:02:10 +02:00
Swann
08fc49c40f fix: session private by default 2021-07-30 14:09:40 +02:00
Swann
d7e25b1192 fix: clean docker file 2021-07-30 13:47:31 +02:00
Swann
1671422143 Merge branch 'develop' of gitlab.com:slumber/multi-user into develop 2021-07-30 13:17:29 +02:00
Swann
a9620c0752 fix: docker server command 2021-07-30 13:16:43 +02:00
Swann Martinez
ac84509b83 Merge branch 'develop' into 'master'
fix: old replication installation conflicts

See merge request slumber/multi-user!145
2021-07-20 14:28:04 +00:00
Swann Martinez
69565b3852 Merge branch 'develop' into 'master'
v0.4.0

See merge request slumber/multi-user!144
2021-07-20 13:41:29 +00:00
Swann Martinez
57fdd492ef Merge branch 'develop' into 'master'
fix: auto-updater operators registration to ensure blender 2.93 compatibility

See merge request slumber/multi-user!117
2021-04-15 13:39:47 +00:00
21 changed files with 524 additions and 228 deletions

View File

@ -217,3 +217,57 @@ All notable changes to this project will be documented in this file.
- GPencil fill stroke
- Sculpt and GPencil brushes deleted when joining a session (@Kysios)
- Auto-updater doesn't work for master and develop builds
## [0.5.0] - 2022-02-10
### Added
- New overall UI and UX (@Kysios)
- Documentation overall update (@Kysios)
- Server presets (@Kysios)
- Server online status (@Kysios)
- Draw connected user color in the user list
- Private session (access protected with a password) (@Kysios)
### Changed
- Dependencies are now installed in the addon folder and correctly cleaned during the addon removal process
### Fixed
- Python 3.10 compatibility (@notfood)
- Blender 3.x compatibility
- Skin vertex radius synchronization (@kromar)
- Sequencer audio strip synchronization
- Crash with empty after a reconnection
## [0.5.1] - 2022-02-10
### Fixed
- Auto updater breaks dependency auto installer
- Auto updater update from tag
## [0.5.2] - 2022-02-18
### Fixed
- Objects not selectable after user leaves session
- Geometry nodes attribute toogle doesn't sync
## [0.5.3] - 2022-03-11
### Changed
- Snapshots logs
### Fixed
- Server crashing during snapshots
- Blender 3.1 numpy loading error during early connection process
- Server docker arguments
## [0.5.5] - 2022-06-12
### Fixed
- Numpy mesh serialization error

View File

@ -16,12 +16,12 @@ import sys
# -- Project information -----------------------------------------------------
project = 'Multi-User 0.5.0 Documentation'
project = 'Multi-User 0.5.x Documentation'
copyright = '2020, Swann Martinez'
author = 'Swann Martinez, Poochy, Fabian'
# The full version, including alpha/beta/rc tags
version_release = '0.5.1-develop'
version_release = '0.5.5'
# -- General configuration ---------------------------------------------------

View File

@ -206,20 +206,20 @@ You can run the dedicated server on any platform by following these steps:
.. code-block:: bash
python -m pip install replication==0.1.13
python -m pip install replication
4. Launch the server with:
3. Launch the server with:
.. code-block:: bash
replication.server
replication.serve
.. hint::
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 optional arguments
.. code-block:: bash
replication.server -p 5555 -pwd admin -t 5000 -l INFO -lf server.log
replication.serve -p 5555 -pwd admin -t 5000 -l INFO -lf server.log
Here, for example, a server is instantiated on port 5555, with password 'admin', a 5 second timeout, and logging enabled.
@ -572,7 +572,7 @@ For example, I would like to launch my server with a different administrator pas
.. code-block:: bash
python3 -m replication.server -pwd supersecretpassword -p 5555 -t 3000 -l DEBUG -lf logname.log
replication.serve -pwd supersecretpassword -p 5555 -t 3000 -l DEBUG -lf logname.log
Now, my configuration should look like this:
@ -691,7 +691,7 @@ We're finally ready to launch the server. Simply run:
.. code-block:: bash
python3 -m replication.server -p 5555 -pwd admin -t 5000 -l INFO -lf server.log
replication.serve -p 5555 -pwd admin -t 5000 -l INFO -lf server.log
See :ref:`cmd-line` for a description of optional parameters

View File

@ -19,7 +19,7 @@
bl_info = {
"name": "Multi-User",
"author": "Swann Martinez",
"version": (0, 4, 0),
"version": (0, 5, 8),
"description": "Enable real-time collaborative workflow inside blender",
"blender": (2, 82, 0),
"location": "3D View > Sidebar > Multi-User tab",

View File

@ -1015,16 +1015,18 @@ class Singleton_updater(object):
for path, dirs, files in os.walk(base):
# prune ie skip updater folder
dirs[:] = [d for d in dirs if os.path.join(path,d) not in [self._updater_path]]
for directory in dirs:
shutil.rmtree(os.path.join(path,directory))
for file in files:
for ptrn in self.remove_pre_update_patterns:
if fnmatch.filter([file],ptrn):
try:
fl = os.path.join(path,file)
os.remove(fl)
if self._verbose: print("Pre-removed file "+file)
except OSError:
print("Failed to pre-remove "+file)
self.print_trace()
try:
fl = os.path.join(path,file)
os.remove(fl)
if self._verbose: print("Pre-removed file "+file)
except OSError:
print("Failed to pre-remove "+file)
self.print_trace()
# Walk through the temp addon sub folder for replacements
# this implements the overwrite rules, which apply after
@ -1701,7 +1703,7 @@ class GitlabEngine(object):
def parse_tags(self, response, updater):
if response == None:
return []
return [{"name": tag["name"], "zipball_url": self.get_zip_url(tag["commit"]["id"], updater)} for tag in response]
return [{"name": tag["name"], "zipball_url": f"https://gitlab.com/slumber/multi-user/-/jobs/artifacts/{tag['name']}/download?job=build"} for tag in response]
# -----------------------------------------------------------------------------

View File

@ -267,7 +267,7 @@ class addon_updater_update_now(bpy.types.Operator):
clean_install: bpy.props.BoolProperty(
name="Clean install",
description="If enabled, completely clear the addon's folder before installing new update, creating a fresh install",
default=False,
default=True,
options={'HIDDEN'}
)

View File

@ -43,7 +43,7 @@ __all__ = [
"bl_particle",
] # Order here defines execution order
if bpy.app.version[1] >= 91:
if bpy.app.version >= (2,91,0):
__all__.append('bl_volume')
from . import *

View File

@ -178,10 +178,10 @@ class BlCurve(ReplicatedDatablock):
loader.load(new_spline, spline)
# MATERIAL SLOTS
src_materials = data.get('materials', None)
if src_materials:
load_materials_slots(src_materials, datablock.materials)
# MATERIAL SLOTS
src_materials = data.get('materials', None)
if src_materials:
load_materials_slots(src_materials, datablock.materials)
@staticmethod
def dump(datablock: object) -> dict:

View File

@ -53,12 +53,12 @@ STROKE = [
"uv_translation",
"vertex_color_fill",
]
if bpy.app.version[1] >= 91:
if bpy.app.version >= (2,91,0):
STROKE.append('use_cyclic')
else:
STROKE.append('draw_cyclic')
if bpy.app.version[1] >= 83:
if bpy.app.version >= (2,83,0):
STROKE_POINT.append('vertex_color')
def dump_stroke(stroke):

View File

@ -37,7 +37,7 @@ class BlLightprobe(ReplicatedDatablock):
def construct(data: dict) -> object:
type = 'CUBE' if data['type'] == 'CUBEMAP' else data['type']
# See https://developer.blender.org/D6396
if bpy.app.version[1] >= 83:
if bpy.app.version >= (2,83,0):
return bpy.data.lightprobes.new(data["name"], type)
else:
logging.warning("Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
@ -49,7 +49,7 @@ class BlLightprobe(ReplicatedDatablock):
@staticmethod
def dump(datablock: object) -> dict:
if bpy.app.version[1] < 83:
if bpy.app.version < (2,83,0):
logging.warning("Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
dumper = Dumper()

View File

@ -48,7 +48,7 @@ SHAPEKEY_BLOCK_ATTR = [
]
if bpy.app.version[1] >= 93:
if bpy.app.version >= (2,93,0):
SUPPORTED_GEOMETRY_NODE_PARAMETERS = (int, str, float)
else:
SUPPORTED_GEOMETRY_NODE_PARAMETERS = (int, str)
@ -56,14 +56,24 @@ else:
blender 2.92.")
def get_node_group_inputs(node_group):
inputs = []
def get_node_group_properties_identifiers(node_group):
props_ids = []
# Inputs
for inpt in node_group.inputs:
if inpt.type in IGNORED_SOCKETS:
continue
else:
inputs.append(inpt)
return inputs
props_ids.append((inpt.identifier, inpt.type))
if inpt.type in ['INT', 'VALUE', 'BOOLEAN', 'RGBA', 'VECTOR']:
props_ids.append((f"{inpt.identifier}_attribute_name",'STR'))
props_ids.append((f"{inpt.identifier}_use_attribute", 'BOOL'))
for outpt in node_group.outputs:
if outpt.type not in IGNORED_SOCKETS and outpt.type in ['INT', 'VALUE', 'BOOLEAN', 'RGBA', 'VECTOR']:
props_ids.append((f"{outpt.identifier}_attribute_name", 'STR'))
return props_ids
# return [inpt.identifer for inpt in node_group.inputs if inpt.type not in IGNORED_SOCKETS]
@ -122,29 +132,35 @@ def load_physics(dumped_settings: dict, target: bpy.types.Object):
bpy.ops.rigidbody.constraint_remove({"object": target})
def dump_modifier_geometry_node_inputs(modifier: bpy.types.Modifier) -> list:
def dump_modifier_geometry_node_props(modifier: bpy.types.Modifier) -> list:
""" Dump geometry node modifier input properties
:arg modifier: geometry node modifier to dump
:type modifier: bpy.type.Modifier
"""
dumped_inputs = []
for inpt in get_node_group_inputs(modifier.node_group):
input_value = modifier[inpt.identifier]
dumped_props = []
for prop_value, prop_type in get_node_group_properties_identifiers(modifier.node_group):
try:
prop_value = modifier[prop_value]
except KeyError as e:
logging.error(f"fail to dump geomety node modifier property : {prop_value} ({e})")
else:
dump = None
if isinstance(prop_value, bpy.types.ID):
dump = prop_value.uuid
elif isinstance(prop_value, SUPPORTED_GEOMETRY_NODE_PARAMETERS):
dump = prop_value
elif hasattr(prop_value, 'to_list'):
dump = prop_value.to_list()
dumped_input = None
if isinstance(input_value, bpy.types.ID):
dumped_input = input_value.uuid
elif isinstance(input_value, SUPPORTED_GEOMETRY_NODE_PARAMETERS):
dumped_input = input_value
elif hasattr(input_value, 'to_list'):
dumped_input = input_value.to_list()
dumped_inputs.append(dumped_input)
dumped_props.append((dump, prop_type))
# logging.info(prop_value)
return dumped_inputs
return dumped_props
def load_modifier_geometry_node_inputs(dumped_modifier: dict, target_modifier: bpy.types.Modifier):
def load_modifier_geometry_node_props(dumped_modifier: dict, target_modifier: bpy.types.Modifier):
""" Load geometry node modifier inputs
:arg dumped_modifier: source dumped modifier to load
@ -153,17 +169,17 @@ def load_modifier_geometry_node_inputs(dumped_modifier: dict, target_modifier: b
:type target_modifier: bpy.type.Modifier
"""
for input_index, inpt in enumerate(get_node_group_inputs(target_modifier.node_group)):
dumped_value = dumped_modifier['inputs'][input_index]
input_value = target_modifier[inpt.identifier]
if isinstance(input_value, SUPPORTED_GEOMETRY_NODE_PARAMETERS):
target_modifier[inpt.identifier] = dumped_value
elif hasattr(input_value, 'to_list'):
for input_index, inpt in enumerate(get_node_group_properties_identifiers(target_modifier.node_group)):
dumped_value, dumped_type = dumped_modifier['props'][input_index]
input_value = target_modifier[inpt[0]]
if dumped_type in ['INT', 'VALUE', 'STR', 'BOOL']:
logging.info(f"{inpt[0]}/{dumped_value}")
target_modifier[inpt[0]] = dumped_value
elif dumped_type in ['RGBA', 'VECTOR']:
for index in range(len(input_value)):
input_value[index] = dumped_value[index]
elif inpt.type in ['COLLECTION', 'OBJECT']:
target_modifier[inpt.identifier] = get_datablock_from_uuid(
dumped_value, None)
elif dumped_type in ['COLLECTION', 'OBJECT', 'IMAGE', 'TEXTURE', 'MATERIAL']:
target_modifier[inpt[0]] = get_datablock_from_uuid(dumped_value, None)
def load_pose(target_bone, data):
@ -198,12 +214,12 @@ def find_data_from_name(name=None):
instance = bpy.data.speakers[name]
elif name in bpy.data.lightprobes.keys():
# Only supported since 2.83
if bpy.app.version[1] >= 83:
if bpy.app.version >= (2,83,0):
instance = bpy.data.lightprobes[name]
else:
logging.warning(
"Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
elif bpy.app.version[1] >= 91 and name in bpy.data.volumes.keys():
elif bpy.app.version >= (2,91,0) and name in bpy.data.volumes.keys():
# Only supported since 2.91
instance = bpy.data.volumes[name]
return instance
@ -250,10 +266,11 @@ def find_geometry_nodes_dependencies(modifiers: bpy.types.bpy_prop_collection) -
for mod in modifiers:
if mod.type == 'NODES' and mod.node_group:
dependencies.append(mod.node_group)
# for inpt in get_node_group_inputs(mod.node_group):
# parameter = mod.get(inpt.identifier)
# if parameter and isinstance(parameter, bpy.types.ID):
# dependencies.append(parameter)
for inpt, inpt_type in get_node_group_properties_identifiers(mod.node_group):
inpt_value = mod.get(inpt)
# Avoid to handle 'COLLECTION', 'OBJECT' to avoid circular dependencies
if inpt_type in ['IMAGE', 'TEXTURE', 'MATERIAL'] and inpt_value:
dependencies.append(inpt_value)
return dependencies
@ -387,10 +404,7 @@ def dump_modifiers(modifiers: bpy.types.bpy_prop_collection)->dict:
dumped_modifier = dumper.dump(modifier)
# hack to dump geometry nodes inputs
if modifier.type == 'NODES':
dumped_inputs = dump_modifier_geometry_node_inputs(
modifier)
dumped_modifier['inputs'] = dumped_inputs
dumped_modifier['props'] = dump_modifier_geometry_node_props(modifier)
elif modifier.type == 'PARTICLE_SYSTEM':
dumper.exclude_filter = [
"is_edited",
@ -455,7 +469,7 @@ def load_modifiers(dumped_modifiers: list, modifiers: bpy.types.bpy_prop_collect
loader.load(loaded_modifier, dumped_modifier)
if loaded_modifier.type == 'NODES':
load_modifier_geometry_node_inputs(dumped_modifier, loaded_modifier)
load_modifier_geometry_node_props(dumped_modifier, loaded_modifier)
elif loaded_modifier.type == 'PARTICLE_SYSTEM':
default = loaded_modifier.particle_system.settings
dumped_particles = dumped_modifier['particle_system']
@ -595,6 +609,13 @@ class BlObject(ReplicatedDatablock):
if datablock.data is None and img_uuid:
datablock.data = get_datablock_from_uuid(img_uuid, None)
if hasattr(datablock, 'cycles_visibility') \
and 'cycles_visibility' in data:
loader.load(datablock.cycles_visibility, data['cycles_visibility'])
if hasattr(datablock, 'modifiers'):
load_modifiers(data['modifiers'], datablock.modifiers)
if hasattr(object_data, 'skin_vertices') \
and object_data.skin_vertices\
and 'skin_vertices' in data:
@ -604,13 +625,6 @@ class BlObject(ReplicatedDatablock):
skin_data.data,
SKIN_DATA)
if hasattr(datablock, 'cycles_visibility') \
and 'cycles_visibility' in data:
loader.load(datablock.cycles_visibility, data['cycles_visibility'])
if hasattr(datablock, 'modifiers'):
load_modifiers(data['modifiers'], datablock.modifiers)
constraints = data.get('constraints')
if constraints:
load_constraints(constraints, datablock.constraints)

View File

@ -440,7 +440,7 @@ class BlScene(ReplicatedDatablock):
if seq.name not in sequences:
vse.sequences.remove(seq)
# Load existing sequences
for seq_data in sequences.value():
for seq_data in sequences.values():
load_sequence(seq_data, vse)
# If the sequence is no longer used, clear it
elif datablock.sequence_editor and not sequences:

View File

@ -26,7 +26,8 @@ import numpy as np
BPY_TO_NUMPY_TYPES = {
'FLOAT': np.float32,
'INT': np.int32,
'BOOL': np.bool}
'BOOL': bool,
'BOOLEAN': bool}
PRIMITIVE_TYPES = ['FLOAT', 'INT', 'BOOLEAN']

View File

@ -36,8 +36,6 @@ REPLICATION_DEPENDENCIES = {
LIBS = os.path.join(os.path.dirname(os.path.abspath(__file__)), "libs")
REPLICATION = os.path.join(LIBS,"replication")
PYTHON_PATH = None
SUBPROCESS_DIR = None
rtypes = []
@ -50,13 +48,13 @@ def module_can_be_imported(name: str) -> bool:
return False
def install_pip():
def install_pip(python_path):
# pip can not necessarily be imported into Blender after this
subprocess.run([str(PYTHON_PATH), "-m", "ensurepip"])
subprocess.run([str(python_path), "-m", "ensurepip"])
def install_package(name: str, install_dir: str):
logging.info(f"installing {name} version...")
def install_requirements(python_path:str, module_requirement: str, install_dir: str):
logging.info(f"Installing {module_requirement} dependencies in {install_dir}")
env = os.environ
if "PIP_REQUIRE_VIRTUALENV" in env:
# PIP_REQUIRE_VIRTUALENV is an env var to ensure pip cannot install packages outside a virtual env
@ -65,23 +63,7 @@ def install_package(name: str, install_dir: str):
# env var for the subprocess.
env = os.environ.copy()
del env["PIP_REQUIRE_VIRTUALENV"]
subprocess.run([str(PYTHON_PATH), "-m", "pip", "install", f"{name}", "-t", install_dir], env=env)
if name in sys.modules:
del sys.modules[name]
def check_package_version(name: str, required_version: str):
logging.info(f"Checking {name} version...")
out = subprocess.run([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
subprocess.run([str(python_path), "-m", "pip", "install", "-r", f"{install_dir}/{module_requirement}/requirements.txt", "-t", install_dir], env=env)
def get_ip():
@ -117,34 +99,29 @@ def remove_paths(paths: list):
if path in sys.path:
logging.debug(f"Removing {path} dir from the path.")
sys.path.remove(path)
def install_modules(dependencies: list, python_path: str, install_dir: str):
global PYTHON_PATH, SUBPROCESS_DIR
PYTHON_PATH = Path(python_path)
SUBPROCESS_DIR = PYTHON_PATH.parent
if not module_can_be_imported("pip"):
install_pip()
for package_name in dependencies:
if not module_can_be_imported(package_name):
install_package(package_name, install_dir=install_dir)
module_can_be_imported(package_name)
def register():
if bpy.app.version[1] >= 91:
if bpy.app.version >= (2,91,0):
python_binary_path = sys.executable
else:
python_binary_path = bpy.app.binary_path_python
python_path = Path(python_binary_path)
for module_name in list(sys.modules.keys()):
if 'replication' in module_name:
del sys.modules[module_name]
setup_paths([LIBS, REPLICATION])
install_modules(REPLICATION_DEPENDENCIES, python_binary_path, install_dir=LIBS)
if not module_can_be_imported("pip"):
install_pip(python_path)
deps_not_installed = [package_name for package_name in REPLICATION_DEPENDENCIES if not module_can_be_imported(package_name)]
if any(deps_not_installed):
install_requirements(python_path, module_requirement='replication', install_dir=LIBS)
def unregister():
remove_paths([REPLICATION, LIBS])

@ -1 +1 @@
Subproject commit d69f25904664088c946f0fdfd27b3d8a630c7a83
Subproject commit 3e9eb4f5c052177c2fe1e16ff5d1f042456c30d0

View File

@ -37,6 +37,7 @@ from queue import Queue
from time import gmtime, strftime
from bpy.props import FloatProperty
import bmesh
try:
import _pickle as pickle
@ -58,13 +59,126 @@ from replication.repository import Repository
from . import bl_types, environment, shared_data, timers, ui, utils
from .handlers import on_scene_update, sanitize_deps_graph
from .presence import SessionStatusWidget, renderer, view3d_find, refresh_sidebar_view
from .presence import SessionStatusWidget, renderer, view3d_find, refresh_sidebar_view, bbox_from_obj
from .timers import registry
background_execution_queue = Queue()
deleyables = []
stop_modal_executor = False
def draw_user(username, metadata, radius=0.01, intensity=10.0):
view_corners = metadata.get('view_corners')
color = metadata.get('color', (1,1,1,0))
objects = metadata.get('selected_objects', None)
user_collection = bpy.data.collections.new(username)
# User Color
user_mat = bpy.data.materials.new(username)
user_mat.use_nodes = True
nodes = user_mat.node_tree.nodes
nodes.remove(nodes['Principled BSDF'])
emission_node = nodes.new('ShaderNodeEmission')
emission_node.inputs['Color'].default_value = color
emission_node.inputs['Strength'].default_value = intensity
output_node = nodes['Material Output']
user_mat.node_tree.links.new(
emission_node.outputs['Emission'], output_node.inputs['Surface'])
# Generate camera mesh
camera_vertices = view_corners[:4]
camera_vertices.append(view_corners[6])
camera_mesh = bpy.data.meshes.new(f"{username}_camera")
camera_obj = bpy.data.objects.new(f"{username}_camera", camera_mesh)
frustum_bm = bmesh.new()
frustum_bm.from_mesh(camera_mesh)
for p in camera_vertices:
frustum_bm.verts.new(p)
frustum_bm.verts.ensure_lookup_table()
frustum_bm.edges.new((frustum_bm.verts[0], frustum_bm.verts[2]))
frustum_bm.edges.new((frustum_bm.verts[2], frustum_bm.verts[1]))
frustum_bm.edges.new((frustum_bm.verts[1], frustum_bm.verts[3]))
frustum_bm.edges.new((frustum_bm.verts[3], frustum_bm.verts[0]))
frustum_bm.edges.new((frustum_bm.verts[0], frustum_bm.verts[4]))
frustum_bm.edges.new((frustum_bm.verts[2], frustum_bm.verts[4]))
frustum_bm.edges.new((frustum_bm.verts[1], frustum_bm.verts[4]))
frustum_bm.edges.new((frustum_bm.verts[3], frustum_bm.verts[4]))
frustum_bm.edges.ensure_lookup_table()
frustum_bm.to_mesh(camera_mesh)
frustum_bm.free() # free and prevent further access
camera_obj.modifiers.new("wireframe", "SKIN")
camera_obj.data.skin_vertices[0].data[0].use_root = True
for v in camera_mesh.skin_vertices[0].data:
v.radius = [radius, radius]
camera_mesh.materials.append(user_mat)
user_collection.objects.link(camera_obj)
# Generate sight mesh
sight_mesh = bpy.data.meshes.new(f"{username}_sight")
sight_obj = bpy.data.objects.new(f"{username}_sight", sight_mesh)
sight_verts = view_corners[4:6]
sight_bm = bmesh.new()
sight_bm.from_mesh(sight_mesh)
for p in sight_verts:
sight_bm.verts.new(p)
sight_bm.verts.ensure_lookup_table()
sight_bm.edges.new((sight_bm.verts[0], sight_bm.verts[1]))
sight_bm.edges.ensure_lookup_table()
sight_bm.to_mesh(sight_mesh)
sight_bm.free()
sight_obj.modifiers.new("wireframe", "SKIN")
sight_obj.data.skin_vertices[0].data[0].use_root = True
for v in sight_mesh.skin_vertices[0].data:
v.radius = [radius, radius]
sight_mesh.materials.append(user_mat)
user_collection.objects.link(sight_obj)
# Draw selected objects
if objects:
for o in list(objects):
instance = bl_types.bl_datablock.get_datablock_from_uuid(o, None)
if instance:
bbox_mesh = bpy.data.meshes.new(f"{instance.name}_bbox")
bbox_obj = bpy.data.objects.new(
f"{instance.name}_bbox", bbox_mesh)
bbox_verts, bbox_ind = bbox_from_obj(instance, index=0)
bbox_bm = bmesh.new()
bbox_bm.from_mesh(bbox_mesh)
for p in bbox_verts:
bbox_bm.verts.new(p)
bbox_bm.verts.ensure_lookup_table()
for e in bbox_ind:
bbox_bm.edges.new(
(bbox_bm.verts[e[0]], bbox_bm.verts[e[1]]))
bbox_bm.to_mesh(bbox_mesh)
bbox_bm.free()
bpy.data.collections[username].objects.link(bbox_obj)
bbox_obj.modifiers.new("wireframe", "SKIN")
bbox_obj.data.skin_vertices[0].data[0].use_root = True
for v in bbox_mesh.skin_vertices[0].data:
v.radius = [radius, radius]
bbox_mesh.materials.append(user_mat)
bpy.context.scene.collection.children.link(user_collection)
def session_callback(name):
""" Session callback wrapper
@ -238,7 +352,7 @@ class SessionConnectOperator(bpy.types.Operator):
settings.generate_supported_types()
if bpy.app.version[1] >= 91:
if bpy.app.version >= (2,91,0):
python_binary_path = sys.executable
else:
python_binary_path = bpy.app.binary_path_python
@ -309,7 +423,7 @@ class SessionHostOperator(bpy.types.Operator):
settings.generate_supported_types()
if bpy.app.version[1] >= 91:
if bpy.app.version >= (2,91,0):
python_binary_path = sys.executable
else:
python_binary_path = bpy.app.binary_path_python
@ -863,9 +977,28 @@ class SessionLoadSaveOperator(bpy.types.Operator, ImportHelper):
maxlen=255, # Max internal buffer length, longer would be clamped.
)
draw_users: bpy.props.BoolProperty(
name="Load users",
description="Draw users in the scene",
default=False,
)
user_skin_radius: bpy.props.FloatProperty(
name="Wireframe radius",
description="Wireframe radius",
default=0.005,
)
user_color_intensity: bpy.props.FloatProperty(
name="Shading intensity",
description="Shading intensity",
default=10.0,
)
def draw(self, context):
pass
def execute(self, context):
from replication.repository import Repository
# init the factory with supported types
bpy_protocol = bl_types.get_data_translation_protocol()
repo = Repository(bpy_protocol)
@ -884,14 +1017,58 @@ class SessionLoadSaveOperator(bpy.types.Operator, ImportHelper):
# Step 2: Load nodes
for node in nodes:
porcelain.apply(repo, node.uuid)
if self.draw_users:
f = gzip.open(self.filepath, "rb")
db = pickle.load(f)
users = db.get("users")
for username, user_data in users.items():
metadata = user_data['metadata']
if metadata:
draw_user(username, metadata, radius=self.user_skin_radius, intensity=self.user_color_intensity)
return {'FINISHED'}
@classmethod
def poll(cls, context):
return True
class SESSION_PT_ImportUser(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "Users"
bl_parent_id = "FILE_PT_operator"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "SESSION_OT_load"
def draw_header(self, context):
sfile = context.space_data
operator = sfile.active_operator
self.layout.prop(operator, "draw_users", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
sfile = context.space_data
operator = sfile.active_operator
layout.enabled = operator.draw_users
layout.prop(operator, "user_skin_radius")
layout.prop(operator, "user_color_intensity")
class SessionPresetServerAdd(bpy.types.Operator):
"""Add a server to the server list preset"""
bl_idname = "session.preset_server_add"
@ -1123,6 +1300,7 @@ classes = (
SessionNotifyOperator,
SessionSaveBackupOperator,
SessionLoadSaveOperator,
SESSION_PT_ImportUser,
SessionStopAutoSaveOperator,
SessionPurgeOperator,
SessionPresetServerAdd,

View File

@ -44,13 +44,6 @@ DEFAULT_PRESETS = {
"admin_password": "admin",
"server_password": ""
},
"public session" : {
"server_name": "public session",
"ip": "51.75.71.183",
"port": 5555,
"admin_password": "",
"server_password": ""
},
}
def randomColor():
@ -374,9 +367,9 @@ class SessionPrefs(bpy.types.AddonPreferences):
description="sidebar_advanced_log_expanded",
default=False
)
sidebar_advanced_hosting_expanded: bpy.props.BoolProperty(
name="sidebar_advanced_hosting_expanded",
description="sidebar_advanced_hosting_expanded",
sidebar_advanced_uinfo_expanded: bpy.props.BoolProperty(
name="sidebar_advanced_uinfo_expanded",
description="sidebar_advanced_uinfo_expanded",
default=False
)
sidebar_advanced_net_expanded: bpy.props.BoolProperty(
@ -619,6 +612,11 @@ class SessionUser(bpy.types.PropertyGroup):
"""
username: bpy.props.StringProperty(name="username")
current_frame: bpy.props.IntProperty(name="current_frame")
color: bpy.props.FloatVectorProperty(name="color", subtype="COLOR",
min=0.0,
max=1.0,
size=4,
default=(1.0, 1.0, 1.0, 1.0))
class SessionProps(bpy.types.PropertyGroup):

View File

@ -203,10 +203,11 @@ class DynamicRightSelectTimer(Timer):
for node_id in to_lock:
node = session.repository.graph.get(node_id)
instance_mode = node.data.get('instance_type')
if instance_mode and instance_mode == 'COLLECTION':
to_lock.remove(node_id)
instances_to_lock.append(node_id)
if node and hasattr(node,'data'):
instance_mode = node.data.get('instance_type')
if instance_mode and instance_mode == 'COLLECTION':
to_lock.remove(node_id)
instances_to_lock.append(node_id)
if instances_to_lock:
try:
porcelain.lock(session.repository,

View File

@ -62,7 +62,41 @@ def printProgressBar(iteration, total, prefix='', suffix='', decimals=1, length=
bar = fill * filledLength + fill_empty * (length - filledLength)
return f"{prefix} |{bar}| {iteration}/{total}{suffix}"
def get_mode_icon(mode_name: str) -> str:
""" given a mode name retrieve a built-in icon
"""
mode_icon = "NONE"
if mode_name == "OBJECT" :
mode_icon = "OBJECT_DATAMODE"
elif mode_name == "EDIT_MESH" :
mode_icon = "EDITMODE_HLT"
elif mode_name == 'EDIT_CURVE':
mode_icon = "CURVE_DATA"
elif mode_name == 'EDIT_SURFACE':
mode_icon = "SURFACE_DATA"
elif mode_name == 'EDIT_TEXT':
mode_icon = "FILE_FONT"
elif mode_name == 'EDIT_ARMATURE':
mode_icon = "ARMATURE_DATA"
elif mode_name == 'EDIT_METABALL':
mode_icon = "META_BALL"
elif mode_name == 'EDIT_LATTICE':
mode_icon = "LATTICE_DATA"
elif mode_name == 'POSE':
mode_icon = "POSE_HLT"
elif mode_name == 'SCULPT':
mode_icon = "SCULPTMODE_HLT"
elif mode_name == 'PAINT_WEIGHT':
mode_icon = "WPAINT_HLT"
elif mode_name == 'PAINT_VERTEX':
mode_icon = "VPAINT_HLT"
elif mode_name == 'PAINT_TEXTURE':
mode_icon = "TPAINT_HLT"
elif mode_name == 'PARTICLE':
mode_icon = "PARTICLES"
elif mode_name == 'PAINT_GPENCIL' or mode_name =='EDIT_GPENCIL' or mode_name =='SCULPT_GPENCIL' or mode_name =='WEIGHT_GPENCIL' or mode_name =='VERTEX_GPENCIL':
mode_icon = "GREASEPENCIL"
return mode_icon
class SESSION_PT_settings(bpy.types.Panel):
"""Settings panel"""
bl_idname = "MULTIUSER_SETTINGS_PT_panel"
@ -149,10 +183,8 @@ class SESSION_PT_settings(bpy.types.Panel):
col.template_list("SESSION_UL_network", "", settings, "server_preset", context.window_manager, "server_index")
col.separator()
connectOp = col.row()
connectOp.operator("session.host", text="Host")
connectopcol = connectOp.column()
connectopcol.enabled =is_server_selected
connectopcol.operator("session.connect", text="Connect")
connectOp.enabled =is_server_selected
connectOp.operator("session.connect", text="Connect")
col = row.column(align=True)
col.operator("session.preset_server_add", icon="ADD", text="") # TODO : add conditions (need a name, etc..)
@ -173,12 +205,18 @@ class SESSION_PT_settings(bpy.types.Panel):
info_msg = None
if current_state == STATE_LOBBY:
usr = session.online_users.get(settings.username)
row= layout.row()
info_msg = "Waiting for the session to start."
if info_msg:
info_box = row.box()
info_box.row().label(text=info_msg,icon='INFO')
if usr and usr['admin']:
info_msg = "Init the session to start."
info_box = layout.row()
info_box.label(text=info_msg,icon='INFO')
init_row = layout.row()
init_row.operator("session.init", icon='TOOL_SETTINGS', text="Init")
else:
info_box = layout.row()
info_box.row().label(text=info_msg,icon='INFO')
# PROGRESS BAR
if current_state in [STATE_SYNCING, STATE_SRV_SYNC, STATE_WAITING]:
@ -192,10 +230,57 @@ class SESSION_PT_settings(bpy.types.Panel):
length=16
))
class SESSION_PT_host_settings(bpy.types.Panel):
bl_idname = "MULTIUSER_SETTINGS_HOST_PT_panel"
bl_label = "Hosting"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel'
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
settings = get_preferences()
return not session \
or (session and session.state == 0) \
and not settings.sidebar_advanced_shown \
and not settings.is_first_launch
def draw_header(self, context):
self.layout.label(text="", icon='NETWORK_DRIVE')
def draw(self, context):
layout = self.layout
settings = get_preferences()
#HOST
host_selection = layout.row().box()
host_selection_row = host_selection.row()
host_selection_row.label(text="Init the session from:")
host_selection_row.prop(settings, "init_method", text="")
host_selection_row = host_selection.row()
host_selection_row.label(text="Port:")
host_selection_row.prop(settings, "host_port", text="")
host_selection_row = host_selection.row()
host_selection_col = host_selection_row.column()
host_selection_col.prop(settings, "host_use_server_password", text="Server password:")
host_selection_col = host_selection_row.column()
host_selection_col.enabled = True if settings.host_use_server_password else False
host_selection_col.prop(settings, "host_server_password", text="")
host_selection_row = host_selection.row()
host_selection_col = host_selection_row.column()
host_selection_col.prop(settings, "host_use_admin_password", text="Admin password:")
host_selection_col = host_selection_row.column()
host_selection_col.enabled = True if settings.host_use_admin_password else False
host_selection_col.prop(settings, "host_admin_password", text="")
host_selection = layout.column()
host_selection.operator("session.host", text="Host")
class SESSION_PT_advanced_settings(bpy.types.Panel):
bl_idname = "MULTIUSER_SETTINGS_REPLICATION_PT_panel"
bl_label = "Advanced"
bl_label = "General Settings"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel'
@ -216,30 +301,19 @@ class SESSION_PT_advanced_settings(bpy.types.Panel):
layout = self.layout
settings = get_preferences()
#ADVANCED HOST
host_selection = layout.row().box()
host_selection.prop(
settings, "sidebar_advanced_hosting_expanded", text="Hosting",
icon=get_expanded_icon(settings.sidebar_advanced_hosting_expanded),
#ADVANCED USER INFO
uinfo_section = layout.row().box()
uinfo_section.prop(
settings,
"sidebar_advanced_uinfo_expanded",
text="User Info",
icon=get_expanded_icon(settings.sidebar_advanced_uinfo_expanded),
emboss=False)
if settings.sidebar_advanced_hosting_expanded:
host_selection_row = host_selection.row()
host_selection_row.prop(settings, "host_port", text="Port:")
host_selection_row = host_selection.row()
host_selection_row.label(text="Init the session from:")
host_selection_row.prop(settings, "init_method", text="")
host_selection_row = host_selection.row()
host_selection_col = host_selection_row.column()
host_selection_col.prop(settings, "host_use_server_password", text="Server password:")
host_selection_col = host_selection_row.column()
host_selection_col.enabled = True if settings.host_use_server_password else False
host_selection_col.prop(settings, "host_server_password", text="")
host_selection_row = host_selection.row()
host_selection_col = host_selection_row.column()
host_selection_col.prop(settings, "host_use_admin_password", text="Admin password:")
host_selection_col = host_selection_row.column()
host_selection_col.enabled = True if settings.host_use_admin_password else False
host_selection_col.prop(settings, "host_admin_password", text="")
if settings.sidebar_advanced_uinfo_expanded:
uinfo_section_row = uinfo_section.row()
uinfo_section_split = uinfo_section_row.split(factor=0.7, align=True)
uinfo_section_split.prop(settings, "username", text="")
uinfo_section_split.prop(settings, "client_color", text="")
#ADVANCED NET
net_section = layout.row().box()
@ -335,18 +409,31 @@ class SESSION_PT_user(bpy.types.Panel):
online_users)-1 >= selected_user else 0
#USER LIST
row = layout.row()
col = layout.column(align=True)
row = col.row(align=True)
row = row.split(factor=0.35, align=True)
box = row.box()
split = box.split(factor=0.35)
split.label(text="user")
split = split.split(factor=0.3)
split.label(text="mode")
split.label(text="frame")
split.label(text="location")
split.label(text="ping")
brow = box.row(align=True)
brow.label(text="user")
row = layout.row()
layout.template_list("SESSION_UL_users", "", context.window_manager,
row = row.split(factor=0.25, align=True)
box = row.box()
brow = box.row(align=True)
brow.label(text="mode")
box = row.box()
brow = box.row(align=True)
brow.label(text="frame")
box = row.box()
brow = box.row(align=True)
brow.label(text="scene")
box = row.box()
brow = box.row(align=True)
brow.label(text="ping")
row = col.row(align=True)
row.template_list("SESSION_UL_users", "", context.window_manager,
"online_users", context.window_manager, "user_index")
#OPERATOR ON USER
@ -393,45 +480,32 @@ class SESSION_UL_users(bpy.types.UIList):
frame_current = str(metadata.get('frame_current','-'))
scene_current = metadata.get('scene_current','-')
mode_current = metadata.get('mode_current','-')
if mode_current == "OBJECT" :
mode_icon = "OBJECT_DATAMODE"
elif mode_current == "EDIT_MESH" :
mode_icon = "EDITMODE_HLT"
elif mode_current == 'EDIT_CURVE':
mode_icon = "CURVE_DATA"
elif mode_current == 'EDIT_SURFACE':
mode_icon = "SURFACE_DATA"
elif mode_current == 'EDIT_TEXT':
mode_icon = "FILE_FONT"
elif mode_current == 'EDIT_ARMATURE':
mode_icon = "ARMATURE_DATA"
elif mode_current == 'EDIT_METABALL':
mode_icon = "META_BALL"
elif mode_current == 'EDIT_LATTICE':
mode_icon = "LATTICE_DATA"
elif mode_current == 'POSE':
mode_icon = "POSE_HLT"
elif mode_current == 'SCULPT':
mode_icon = "SCULPTMODE_HLT"
elif mode_current == 'PAINT_WEIGHT':
mode_icon = "WPAINT_HLT"
elif mode_current == 'PAINT_VERTEX':
mode_icon = "VPAINT_HLT"
elif mode_current == 'PAINT_TEXTURE':
mode_icon = "TPAINT_HLT"
elif mode_current == 'PARTICLE':
mode_icon = "PARTICLES"
elif mode_current == 'PAINT_GPENCIL' or mode_current =='EDIT_GPENCIL' or mode_current =='SCULPT_GPENCIL' or mode_current =='WEIGHT_GPENCIL' or mode_current =='VERTEX_GPENCIL':
mode_icon = "GREASEPENCIL"
mode_current = metadata.get('mode_current','-')
mode_icon = get_mode_icon(mode_current)
user_color = metadata.get('color',[1.0,1.0,1.0,1.0])
item.color = user_color
if user['admin']:
status_icon = 'FAKE_USER_ON'
split = layout.split(factor=0.35)
split.label(text=item.username, icon=status_icon)
split = split.split(factor=0.3)
split.label(icon=mode_icon)
split.label(text=frame_current)
split.label(text=scene_current)
split.label(text=ping)
row = layout.split(factor=0.35, align=True)
entry = row.row(align=True)
entry.scale_x = 0.05
entry.enabled = False
entry.prop(item, 'color', text="", event=False, full_event=False)
entry.enabled = True
entry.scale_x = 1.0
entry.label(icon=status_icon, text="")
entry.label(text=item.username)
row = row.split(factor=0.25, align=True)
entry = row.row()
entry.label(icon=mode_icon)
entry = row.row()
entry.label(text=frame_current)
entry = row.row()
entry.label(text=scene_current)
entry = row.row()
entry.label(text=ping)
def draw_property(context, parent, property_uuid, level=0):
settings = get_preferences()
@ -543,8 +617,7 @@ class SESSION_PT_repository(bpy.types.Panel):
admin = usr['admin']
return hasattr(context.window_manager, 'session') and \
session and \
(session.state == STATE_ACTIVE or \
session.state == STATE_LOBBY and admin) and \
session.state == STATE_ACTIVE and \
not settings.sidebar_repository_shown
def draw_header(self, context):
@ -590,12 +663,6 @@ class SESSION_PT_repository(bpy.types.Panel):
else:
layout.row().label(text="Empty")
elif session.state == STATE_LOBBY and usr and usr['admin']:
row = layout.row()
row.operator("session.init", icon='TOOL_SETTINGS', text="Init")
else:
row = layout.row()
row.label(text="Waiting to start")
class VIEW3D_PT_overlay_session(bpy.types.Panel):
bl_space_type = 'VIEW_3D'
@ -614,6 +681,9 @@ class VIEW3D_PT_overlay_session(bpy.types.Panel):
pref = get_preferences()
layout.active = settings.enable_presence
row = layout.row()
row.prop(settings, "enable_presence",text="Presence Overlay")
row = layout.row()
row.prop(settings, "presence_show_selected",text="Selected Objects")
@ -667,6 +737,7 @@ classes = (
SESSION_UL_users,
SESSION_UL_network,
SESSION_PT_settings,
SESSION_PT_host_settings,
SESSION_PT_advanced_settings,
SESSION_PT_user,
SESSION_PT_sync,

View File

@ -1,7 +1,7 @@
# Download base image debian jessie
FROM python:slim
ARG replication_version=0.1.13
ARG replication_version=0.9.1
ARG version=0.1.1
# Infos
@ -22,4 +22,4 @@ RUN pip install replication==$replication_version
# Run the server with parameters
ENTRYPOINT ["/bin/sh", "-c"]
CMD ["python3 -m replication.server -pwd ${password} -p ${port} -t ${timeout} -l ${log_level} -lf ${log_file}"]
CMD ["replication.server -apwd ${password} -spwd '' -p ${port} -t ${timeout} -l ${log_level} -lf ${log_file}"]

View File

@ -7,7 +7,7 @@ import bpy
from multi_user.bl_types.bl_lightprobe import BlLightprobe
@pytest.mark.skipif(bpy.app.version[1] < 83, reason="requires blender 2.83 or higher")
@pytest.mark.skipif(bpy.app.version < (2,83,0), reason="requires blender 2.83 or higher")
@pytest.mark.parametrize('lightprobe_type', ['PLANAR','GRID','CUBEMAP'])
def test_lightprobes(clear_blend, lightprobe_type):
bpy.ops.object.lightprobe_add(type=lightprobe_type)