fix: server list working (no ping/lock/pop-up)

This commit is contained in:
Fabian 2021-07-19 16:03:12 +02:00
parent cf44e547a2
commit 4846fbb589
4 changed files with 307 additions and 223 deletions

View File

@ -85,6 +85,7 @@ def register():
type=preferences.SessionUser
)
bpy.types.WindowManager.user_index = bpy.props.IntProperty()
bpy.types.WindowManager.server_index = bpy.props.IntProperty()
bpy.types.TOPBAR_MT_file_import.append(operators.menu_func_import)
@ -111,5 +112,6 @@ def unregister():
del bpy.types.ID.uuid
del bpy.types.WindowManager.online_users
del bpy.types.WindowManager.user_index
del bpy.types.WindowManager.server_index
environment.unregister()

View File

@ -28,6 +28,7 @@ import string
import sys
import time
import traceback
from uuid import uuid4
from datetime import datetime
from operator import itemgetter
from pathlib import Path
@ -146,12 +147,123 @@ def on_connection_end(reason="none"):
# OPERATORS
class SessionStartOperator(bpy.types.Operator):
bl_idname = "session.start"
bl_label = "start"
class SessionConnectOperator(bpy.types.Operator):
bl_idname = "session.connect"
bl_label = "connect"
bl_description = "connect to a net server"
host: bpy.props.BoolProperty(default=False)
@classmethod
def poll(cls, context):
return True
def execute(self, context):
global deleyables
settings = utils.get_preferences()
runtime_settings = context.window_manager.session
users = bpy.data.window_managers['WinMan'].online_users
admin_pass = settings.admin_password
server_pass = settings.server_password if settings.server_password else None
users.clear()
deleyables.clear()
logger = logging.getLogger()
if len(logger.handlers) == 1:
formatter = logging.Formatter(
fmt='%(asctime)s CLIENT %(levelname)-8s %(message)s',
datefmt='%H:%M:%S'
)
start_time = datetime.now().strftime('%Y_%m_%d_%H-%M-%S')
log_directory = os.path.join(
settings.cache_directory,
f"multiuser_{start_time}.log")
os.makedirs(settings.cache_directory, exist_ok=True)
handler = logging.FileHandler(log_directory, mode='w')
logger.addHandler(handler)
for handler in logger.handlers:
if isinstance(handler, logging.NullHandler):
continue
handler.setFormatter(formatter)
bpy_protocol = bl_types.get_data_translation_protocol()
# Check if supported_datablocks are up to date before starting the
# the session
for dcc_type_id in bpy_protocol.implementations.keys():
if dcc_type_id not in settings.supported_datablocks:
logging.info(f"{dcc_type_id} not found, \
regenerate type settings...")
settings.generate_supported_types()
if bpy.app.version[1] >= 91:
python_binary_path = sys.executable
else:
python_binary_path = bpy.app.binary_path_python
repo = Repository(
rdp=bpy_protocol,
username=settings.username)
# Join a session
if not runtime_settings.admin:
utils.clean_scene()
# regular session, no admin_password needed nor server_password
admin_pass = None
server_pass = None
try:
porcelain.remote_add(
repo,
'origin',
settings.ip,
settings.port,
server_password=server_pass,
admin_password=admin_pass)
session.connect(
repository= repo,
timeout=settings.connection_timeout,
server_password=server_pass,
admin_password=admin_pass
)
except Exception as e:
self.report({'ERROR'}, str(e))
logging.error(str(e))
# Background client updates service
deleyables.append(timers.ClientUpdate())
deleyables.append(timers.DynamicRightSelectTimer())
deleyables.append(timers.ApplyTimer(timeout=settings.depsgraph_update_rate))
session_update = timers.SessionStatusUpdate()
session_user_sync = timers.SessionUserSync()
session_background_executor = timers.MainThreadExecutor(execution_queue=background_execution_queue)
session_listen = timers.SessionListenTimer(timeout=0.001)
session_listen.register()
session_update.register()
session_user_sync.register()
session_background_executor.register()
deleyables.append(session_background_executor)
deleyables.append(session_update)
deleyables.append(session_user_sync)
deleyables.append(session_listen)
deleyables.append(timers.AnnotationUpdates())
return {"FINISHED"}
class SessionHostOperator(bpy.types.Operator):
bl_idname = "session.host"
bl_label = "host"
bl_description = "host server"
@classmethod
def poll(cls, context):
@ -213,64 +325,38 @@ class SessionStartOperator(bpy.types.Operator):
username=settings.username)
# Host a session
if self.host:
if settings.init_method == 'EMPTY':
utils.clean_scene()
if settings.init_method == 'EMPTY':
utils.clean_scene()
runtime_settings.is_host = True
runtime_settings.internet_ip = environment.get_ip()
runtime_settings.is_host = True
runtime_settings.internet_ip = environment.get_ip()
try:
# Init repository
for scene in bpy.data.scenes:
porcelain.add(repo, scene)
try:
# Init repository
for scene in bpy.data.scenes:
porcelain.add(repo, scene)
porcelain.remote_add(
repo,
'origin',
'127.0.0.1',
settings.port,
server_password=server_pass,
admin_password=admin_pass)
session.host(
repository= repo,
remote='origin',
timeout=settings.connection_timeout,
server_password=server_pass,
admin_password=admin_pass,
cache_directory=settings.cache_directory,
server_log_level=logging.getLevelName(
logging.getLogger().level),
)
except Exception as e:
self.report({'ERROR'}, repr(e))
logging.error(f"Error: {e}")
traceback.print_exc()
# Join a session
else:
if not runtime_settings.admin:
utils.clean_scene()
# regular session, no admin_password needed nor server_password
admin_pass = None
server_pass = None
try:
porcelain.remote_add(
repo,
'origin',
settings.ip,
settings.port,
server_password=server_pass,
admin_password=admin_pass)
session.connect(
repository= repo,
timeout=settings.connection_timeout,
server_password=server_pass,
admin_password=admin_pass
)
except Exception as e:
self.report({'ERROR'}, str(e))
logging.error(str(e))
porcelain.remote_add(
repo,
'origin',
'127.0.0.1',
settings.port,
server_password=server_pass,
admin_password=admin_pass)
session.host(
repository= repo,
remote='origin',
timeout=settings.connection_timeout,
server_password=server_pass,
admin_password=admin_pass,
cache_directory=settings.cache_directory,
server_log_level=logging.getLevelName(
logging.getLogger().level),
)
except Exception as e:
self.report({'ERROR'}, repr(e))
logging.error(f"Error: {e}")
traceback.print_exc()
# Background client updates service
deleyables.append(timers.ClientUpdate())
@ -838,27 +924,41 @@ class SessionLoadSaveOperator(bpy.types.Operator, ImportHelper):
class SessionPresetServerAdd(bpy.types.Operator):
"""Add a server to the server list preset"""
bl_idname = "session.preset_server_add"
bl_label = "add server preset"
bl_description = "add the current server to the server preset list"
bl_label = "Add server preset"
bl_description = "add a server to the server preset list"
bl_options = {"REGISTER"}
name : bpy.props.StringProperty(default="server_preset")
target_server_name: bpy.props.StringProperty(default="None")
@classmethod
def poll(cls, context):
return True
def invoke(self, context, event):
settings = utils.get_preferences()
settings.server_name = ""
settings.ip = "127.0.0.1"
settings.port = 5555
settings.server_password = ""
settings.admin_password = ""
assert(context)
return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
layout = self.layout
col = layout.column()
settings = utils.get_preferences()
col.prop(settings, "server_name", text="server name")
row = layout.row()
row.prop(settings, "server_name", text="Server name")
row = layout.row(align = True)
row.prop(settings, "ip", text="IP+port")
row.prop(settings, "port", text="")
row = layout.row()
row.prop(settings, "server_password", text="Server password")
row = layout.row()
row.prop(settings, "admin_password", text="Admin password")
def execute(self, context):
assert(context)
@ -868,14 +968,13 @@ class SessionPresetServerAdd(bpy.types.Operator):
existing_preset = settings.server_preset.get(settings.server_name)
new_server = existing_preset if existing_preset else settings.server_preset.add()
new_server.name = settings.server_name
new_server.name = str(uuid4())
new_server.server_name = settings.server_name
new_server.server_ip = settings.ip
new_server.server_port = settings.port
new_server.server_server_password = settings.server_password
new_server.server_admin_password = settings.admin_password
settings.server_preset_interface = settings.server_name
if new_server == existing_preset :
self.report({'INFO'}, "Server '" + settings.server_name + "' override")
else :
@ -884,6 +983,65 @@ class SessionPresetServerAdd(bpy.types.Operator):
return {'FINISHED'}
class SessionPresetServerEdit(bpy.types.Operator):
"""Edit a server to the server list preset"""
bl_idname = "session.preset_server_edit"
bl_label = "Edit server preset"
bl_description = "Edit a server from the server preset list"
bl_options = {"REGISTER"}
target_server_name: bpy.props.StringProperty(default="None")
@classmethod
def poll(cls, context):
return True
def invoke(self, context, event):
settings = utils.get_preferences()
settings_active_server = settings.server_preset.get(self.target_server_name)
if settings_active_server :
settings.server_name = settings_active_server.server_name
settings.ip = settings_active_server.server_ip
settings.port = settings_active_server.server_port
settings.server_password = settings_active_server.server_server_password
settings.admin_password = settings_active_server.server_admin_password
assert(context)
return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
layout = self.layout
settings = utils.get_preferences()
row = layout.row()
row.prop(settings, "server_name", text="Server name")
row = layout.row(align = True)
row.prop(settings, "ip", text="IP+port")
row.prop(settings, "port", text="")
row = layout.row()
row.prop(settings, "server_password", text="Server password")
row = layout.row()
row.prop(settings, "admin_password", text="Admin password")
def execute(self, context):
assert(context)
settings = utils.get_preferences()
settings_active_server = settings.server_preset.get(self.target_server_name)
server = settings_active_server if settings_active_server else settings.server_preset.add()
server.server_name = settings.server_name
server.server_ip = settings.ip
server.server_port = settings.port
server.server_server_password = settings.server_password
server.server_admin_password = settings.admin_password
self.report({'INFO'}, "Server '" + settings.server_name + "' override")
return {'FINISHED'}
class SessionPresetServerRemove(bpy.types.Operator):
"""Remove a server to the server list preset"""
bl_idname = "session.preset_server_remove"
@ -891,6 +1049,8 @@ class SessionPresetServerRemove(bpy.types.Operator):
bl_description = "remove the current server from the server preset list"
bl_options = {"REGISTER"}
target_server_name: bpy.props.StringProperty(default="None")
@classmethod
def poll(cls, context):
return True
@ -899,8 +1059,7 @@ class SessionPresetServerRemove(bpy.types.Operator):
assert(context)
settings = utils.get_preferences()
settings.server_preset.remove(settings.server_preset.find(settings.server_preset_interface))
settings.server_preset.remove(settings.server_preset.find(self.target_server_name))
return {'FINISHED'}
@ -911,7 +1070,8 @@ def menu_func_import(self, context):
classes = (
SessionStartOperator,
SessionConnectOperator,
SessionHostOperator,
SessionStopOperator,
SessionPropertyRemoveOperator,
SessionSnapUserOperator,
@ -928,6 +1088,7 @@ classes = (
SessionStopAutoSaveOperator,
SessionPurgeOperator,
SessionPresetServerAdd,
SessionPresetServerEdit,
SessionPresetServerRemove,
)

View File

@ -17,6 +17,7 @@
import random
import logging
from uuid import uuid4
import bpy
import string
import re
@ -35,12 +36,14 @@ HOSTNAME_REGEX = re.compile("^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]
DEFAULT_PRESETS = {
"localhost" : {
"server_name": "localhost",
"server_ip": "localhost",
"server_port": 5555,
"admin_password": "admin",
"server_password": ""
},
"public session" : {
"server_name": "public session",
"server_ip": "51.75.71.183",
"server_port": 5555,
"admin_password": "",
@ -112,7 +115,8 @@ class ReplicatedDatablock(bpy.types.PropertyGroup):
auto_push: bpy.props.BoolProperty(default=True)
icon: bpy.props.StringProperty()
class ServerPreset(bpy.types.PropertyGroup):
class ServerPreset(bpy.types.PropertyGroup): # TODO: self.uuid = uuid if uuid else str(uuid4())
server_name: bpy.props.StringProperty()
server_ip: bpy.props.StringProperty()
server_port: bpy.props.IntProperty(default=5555)
server_server_password: bpy.props.StringProperty(default="", subtype = "PASSWORD")
@ -190,6 +194,10 @@ class SessionPrefs(bpy.types.AddonPreferences):
description="Custom name of the server",
default='localhost',
)
server_index: bpy.props.IntProperty(
name="server_index",
description="index of the server",
)
server_password: bpy.props.StringProperty(
name="server_password",
description='Session password',
@ -540,7 +548,9 @@ class SessionPrefs(bpy.types.AddonPreferences):
if existing_preset :
continue
new_server = self.server_preset.add()
new_server.name = preset_name
new_server.name = str(uuid4())
new_server.server_name = preset_data.get('server_name')
new_server.server_index = preset_data.get('server_index')
new_server.server_ip = preset_data.get('server_ip')
new_server.server_port = preset_data.get('server_port')
new_server.server_password = preset_data.get('server_password',None)

View File

@ -16,6 +16,8 @@
# ##### END GPL LICENSE BLOCK #####
from logging import log
import logging
import bpy
import bpy.utils.previews
@ -103,7 +105,37 @@ class SESSION_PT_settings(bpy.types.Panel):
# STATE INITIAL
if not session \
or (session and session.state == STATE_INITIAL):
pass
layout = self.layout
settings = get_preferences()
server_preset = settings.server_preset
selected_server = context.window_manager.server_index if context.window_manager.server_index<=len(server_preset)-1 else 0
active_server_name = server_preset[selected_server].name if len(server_preset)>=1 else ""
is_server_selected = True if active_server_name else False # TODO : issues when removing the lowest server in the list
# Create a simple row.
row = layout.row()
box = row.box()
split = box.split(factor=0.7)
split.label(text="Server")
split.label(text="Online")
row = layout.row()
layout.template_list("SESSION_UL_network", "", settings, "server_preset", context.window_manager, "server_index") # TODO: change port to server_index
row = layout.row() # TODO : active server in template
row.operator("session.preset_server_add", text="Add") # TODO : add conditions (need a name, etc..) + add a checkbox for password without creating preferences
col = row.column()
col.enabled = is_server_selected
col.operator("session.preset_server_edit", text="Edit").target_server_name = active_server_name
col = row.column()
col.enabled = is_server_selected
col.operator("session.preset_server_remove", text="Remove").target_server_name = active_server_name
row = layout.row()
row.operator("session.host", text="Host") # TODO : add a pop-up for admin and server password ?
col = row.column()
col.enabled =is_server_selected
col.operator("session.connect", text="Connect")
else:
progress = session.state_progress
row = layout.row()
@ -139,99 +171,6 @@ class SESSION_PT_settings(bpy.types.Panel):
layout.row().operator("session.stop", icon='QUIT', text="Exit")
class SESSION_PT_settings_network(bpy.types.Panel):
bl_idname = "MULTIUSER_SETTINGS_NETWORK_PT_panel"
bl_label = "Network"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel'
@classmethod
def poll(cls, context):
return not session \
or (session and session.state == 0)
def draw_header(self, context):
self.layout.label(text="", icon='URL')
def draw(self, context):
layout = self.layout
runtime_settings = context.window_manager.session
settings = get_preferences()
# USER SETTINGS
row = layout.row()
row.prop(runtime_settings, "session_mode", expand=True)
row = layout.row()
col = row.row(align=True)
col.prop(settings, "server_preset_interface", text="")
col.operator("session.preset_server_add", icon='ADD', text="")
col.operator("session.preset_server_remove", icon='REMOVE', text="")
row = layout.row()
box = row.box()
if runtime_settings.session_mode == 'HOST':
row = box.row()
row.label(text="Port:")
row.prop(settings, "port", text="")
row = box.row()
row.label(text="Start from:")
row.prop(settings, "init_method", text="")
row = box.row()
row.label(text="Admin password:")
row.prop(settings, "admin_password", text="")
row = box.row()
row.operator("session.start", text="HOST").host = True
else:
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.prop(runtime_settings, "admin", text='Connect as admin', icon='DISCLOSURE_TRI_DOWN' if runtime_settings.admin
else 'DISCLOSURE_TRI_RIGHT')
if runtime_settings.admin:
row = box.row()
row.label(text="Password:")
row.prop(settings, "admin_password", text="")
row = box.row()
row.operator("session.start", text="CONNECT").host = False
class SESSION_PT_settings_user(bpy.types.Panel):
bl_idname = "MULTIUSER_SETTINGS_USER_PT_panel"
bl_label = "User info"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel'
@classmethod
def poll(cls, context):
return not session \
or (session and session.state == 0)
def draw_header(self, context):
self.layout.label(text="", icon='USER')
def draw(self, context):
layout = self.layout
runtime_settings = context.window_manager.session
settings = get_preferences()
row = layout.row()
# USER SETTINGS
row.prop(settings, "username", text="name")
row = layout.row()
row.prop(settings, "client_color", text="color")
row = layout.row()
class SESSION_PT_advanced_settings(bpy.types.Panel):
bl_idname = "MULTIUSER_SETTINGS_REPLICATION_PT_panel"
bl_label = "Advanced"
@ -446,57 +385,6 @@ class SESSION_UL_users(bpy.types.UIList):
split.label(text=scene_current)
split.label(text=ping)
class SESSION_PT_presence(bpy.types.Panel):
bl_idname = "MULTIUSER_MODULE_PT_panel"
bl_label = "Presence overlay"
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):
return not session \
or (session and session.state in [STATE_INITIAL, STATE_ACTIVE])
def draw_header(self, context):
self.layout.prop(context.window_manager.session,
"enable_presence", text="",icon='OVERLAY')
def draw(self, context):
layout = self.layout
settings = context.window_manager.session
pref = get_preferences()
layout.active = settings.enable_presence
row = layout.row()
row.prop(settings, "presence_show_selected",text="Selected Objects")
row = layout.row(align=True)
row.prop(settings, "presence_show_user", text="Users camera")
row.prop(settings, "presence_show_mode", text="Users mode")
col = layout.column()
if settings.presence_show_mode or settings.presence_show_user:
row = col.column()
row.prop(pref, "presence_text_distance", expand=True)
row = col.column()
row.prop(settings, "presence_show_far_user", text="Users on different scenes")
col.prop(settings, "presence_show_session_status")
if settings.presence_show_session_status :
split = layout.split()
text_pos = split.column(align=True)
text_pos.active = settings.presence_show_session_status
text_pos.prop(pref, "presence_hud_hpos", expand=True)
text_pos.prop(pref, "presence_hud_vpos", expand=True)
text_scale = split.column()
text_scale.active = settings.presence_show_session_status
text_scale.prop(pref, "presence_hud_scale", expand=True)
def draw_property(context, parent, property_uuid, level=0):
settings = get_preferences()
runtime_settings = context.window_manager.session
@ -673,13 +561,36 @@ class VIEW3D_PT_overlay_session(bpy.types.Panel):
text_scale.active = settings.presence_show_session_status
text_scale.prop(pref, "presence_hud_scale", expand=True)
class SESSION_UL_network(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag):
settings = get_preferences()
server_name = '-'
server_status = 'BLANK1'
server_private = 'BLANK1'
server_name = item.server_name
split = layout.split(factor=0.7)
# Session with/without password
# TODO : ping lock server
if settings.server_password != None:
server_private = 'LOCKED'
split.label(text=server_name, icon=server_private)
else:
split.label(text=server_name)
# Session status
# TODO : if session online : vert else rouge
# TODO : ping
from multi_user import icons
server_status = icons.icons_col["session_status_offline"]
split.label(icon_value=server_status.icon_id)
classes = (
SESSION_UL_users,
SESSION_UL_network,
SESSION_PT_settings,
SESSION_PT_settings_user,
SESSION_PT_settings_network,
SESSION_PT_presence,
SESSION_PT_advanced_settings,
SESSION_PT_user,
SESSION_PT_repository,