fix: depsgraph API changes

feat: function renaming
feat: pep8 compliant
This commit is contained in:
Swann Martinez 2019-05-21 16:53:55 +02:00
parent 21ba33d675
commit cef8a91bf1
No known key found for this signature in database
GPG Key ID: 414CCAFD8DA720E1
6 changed files with 104 additions and 106 deletions

View File

@ -1,24 +1,22 @@
from pathlib import Path
import addon_utils
import random
import string
import subprocess
import sys
import os
import bpy
bl_info = { bl_info = {
"name" : "rcf", "name": "Multi-User ",
"author" : "CUBE", "author": "CUBE",
"description" : "", "description": "",
"blender" : (2, 80, 0), "blender": (2, 80, 0),
"location" : "", "location": "",
"warning" : "", "warning": "",
"category" : "Collaboration" "category": "Collaboration"
} }
import bpy
import os
import sys
import subprocess
import string
import random
import bpy
import addon_utils
from pathlib import Path
python_path = Path(bpy.app.binary_path_python) python_path = Path(bpy.app.binary_path_python)
cwd_for_subprocesses = python_path.parent cwd_for_subprocesses = python_path.parent
@ -28,13 +26,13 @@ def client_list_callback(scene, context):
global client_keys global client_keys
items = [("Common", "Common", "")] items = [("Common", "Common", "")]
username = bpy.context.window_manager.session.username username = bpy.context.window_manager.session.username
if client_keys: if client_keys:
for k in client_keys: for k in client_keys:
if 'Client' in k[0]: if 'Client' in k[0]:
name = k[1] name = k[1]
if name == username: if name == username:
name += " (self)" name += " (self)"
@ -62,11 +60,11 @@ def get_package_install_directory():
return path return path
class RCFSessionProps(bpy.types.PropertyGroup): class SessionProps(bpy.types.PropertyGroup):
username: bpy.props.StringProperty( username: bpy.props.StringProperty(
name="Username", name="Username",
default="user_{}".format(randomStringDigits()) default="user_{}".format(randomStringDigits())
) )
ip: bpy.props.StringProperty( ip: bpy.props.StringProperty(
name="ip", name="ip",
description='Distant host ip', description='Distant host ip',
@ -109,16 +107,19 @@ class RCFSessionProps(bpy.types.PropertyGroup):
description='Enable overlay drawing module', description='Enable overlay drawing module',
default=True) default=True)
classes = { classes = {
RCFSessionProps, SessionProps,
} }
def register(): def register():
try: try:
import zmq import zmq
except: except:
target = get_package_install_directory() target = get_package_install_directory()
subprocess.run([str(python_path), "-m", "pip", "install", "zmq", '--target', target], cwd=cwd_for_subprocesses) subprocess.run([str(python_path), "-m", "pip", "install",
"zmq", '--target', target], cwd=cwd_for_subprocesses)
from . import operators from . import operators
from . import ui from . import ui
@ -129,7 +130,7 @@ def register():
bpy.types.ID.id = bpy.props.StringProperty(default="None") bpy.types.ID.id = bpy.props.StringProperty(default="None")
bpy.types.ID.is_dirty = bpy.props.BoolProperty(default=False) bpy.types.ID.is_dirty = bpy.props.BoolProperty(default=False)
bpy.types.WindowManager.session = bpy.props.PointerProperty( bpy.types.WindowManager.session = bpy.props.PointerProperty(
type=RCFSessionProps) type=SessionProps)
operators.register() operators.register()
ui.register() ui.register()
@ -138,7 +139,7 @@ def register():
def unregister(): def unregister():
from . import operators from . import operators
from . import ui from . import ui
ui.unregister() ui.unregister()
operators.unregister() operators.unregister()

145
client.py
View File

@ -26,6 +26,7 @@ WAITING_TIME = 0.001
SERVER_MAX = 1 SERVER_MAX = 1
DUMP_AGENTS_NUMBER = 1 DUMP_AGENTS_NUMBER = 1
class State(Enum): class State(Enum):
INITIAL = 1 INITIAL = 1
SYNCING = 2 SYNCING = 2
@ -50,7 +51,7 @@ def zpipe(ctx):
return a, b return a, b
class RCFClient(object): class Client(object):
ctx = None ctx = None
pipe = None pipe = None
net_agent = None net_agent = None
@ -64,19 +65,19 @@ class RCFClient(object):
self.serial_product = queue.Queue() self.serial_product = queue.Queue()
self.serial_feed = queue.Queue() self.serial_feed = queue.Queue()
self.stop_event= threading.Event() self.stop_event = threading.Event()
# Net agent # Net agent
self.net_agent = threading.Thread( self.net_agent = threading.Thread(
target=net_worker, args=(self.ctx, self.store, peer, self.serial_product,self.serial_feed, self.stop_event), name="net-agent") target=net_worker,
args=(self.ctx, self.store, peer, self.serial_product, self.serial_feed, self.stop_event), name="net-agent")
self.net_agent.daemon = True self.net_agent.daemon = True
self.net_agent.start() self.net_agent.start()
# Local data translation agent # Local data translation agent
self.serial_agents = [] self.serial_agents = []
for a in range(0, DUMP_AGENTS_NUMBER): for a in range(0, DUMP_AGENTS_NUMBER):
serial_agent = threading.Thread( serial_agent = threading.Thread(
target=serial_worker, args=(self.serial_product, self.serial_feed), name="serial-agent") target=serial_worker, args=(self.serial_product, self.serial_feed), name="serial-agent")
serial_agent.daemon = True serial_agent.daemon = True
serial_agent.start() serial_agent.start()
self.serial_agents.append(serial_agent) self.serial_agents.append(serial_agent)
@ -90,13 +91,11 @@ class RCFClient(object):
# Status # Status
self.active_tasks = 0 self.active_tasks = 0
def connect(self, id, address, port): def connect(self, id, address, port):
self.pipe.send_multipart([b"CONNECT", (id.encode() if isinstance( self.pipe.send_multipart([b"CONNECT", (id.encode() if isinstance(
id, str) else id), (address.encode() if isinstance( id, str) else id), (address.encode() if isinstance(
address, str) else address), b'%d' % port]) address, str) else address), b'%d' % port])
def init(self): def init(self):
""" """
Scene initialisation Scene initialisation
@ -104,26 +103,27 @@ class RCFClient(object):
self.pipe.send_multipart( self.pipe.send_multipart(
[b"INIT"]) [b"INIT"])
def disconnect(self): def disconnect(self):
""" """
Disconnect Disconnect
""" """
self.pipe.send_multipart( self.pipe.send_multipart(
[b"DISCONNECT"]) [b"DISCONNECT"])
def set(self, key, value=None, override=False): def set(self, key, value=None, override=False):
"""Set new value in distributed hash table """Set new value in distributed hash table
Sends [SET][key][value] to the agent Sends [SET][key][value] to the agent
""" """
if value: if value:
pass key = umsgpack.packb(key)
value = umsgpack.packb(value) if value else umsgpack.packb('None')
override = umsgpack.packb(override)
self.pipe.send_multipart( self.pipe.send_multipart(
[b"SET", umsgpack.packb(key), (umsgpack.packb(value) if value else umsgpack.packb('None')),umsgpack.packb(override)]) [b"SET", key, value, override])
else: else:
self.serial_feed.put(('DUMP',key,None)) self.serial_feed.put(('DUMP', key, None))
def add(self, key, value=None): def add(self, key, value=None):
"""Set new value in distributed hash table """Set new value in distributed hash table
@ -138,18 +138,17 @@ class RCFClient(object):
else: else:
return True return True
def exit(self): def exit(self):
if self.net_agent.is_alive(): if self.net_agent.is_alive():
self.disconnect() self.disconnect()
self.stop_event.set() self.stop_event.set()
for a in range(0,DUMP_AGENTS_NUMBER):
self.serial_feed.put(('STOP',None,None))
for a in range(0, DUMP_AGENTS_NUMBER):
self.serial_feed.put(('STOP', None, None))
# READ-ONLY FUNCTIONS
# READ-ONLY FUNCTIONS
def get(self, key): def get(self, key):
"""Lookup value in distributed hash table """Lookup value in distributed hash table
Sends [GET][key] to the agent and waits for a value response Sends [GET][key] to the agent and waits for a value response
@ -163,7 +162,7 @@ class RCFClient(object):
return value return value
def exist(self,key): def exist(self, key):
""" """
Fast key exist check Fast key exist check
""" """
@ -172,22 +171,20 @@ class RCFClient(object):
return True return True
else: else:
return False return False
def list(self): def list(self):
dump_list = [] dump_list = []
for k,v in self.store.items(): for k, v in self.store.items():
if 'Client' in k: if 'Client' in k:
dump_list.append([k,v.id.decode()]) dump_list.append([k, v.id.decode()])
else: else:
try: try:
dump_list.append([k,v.body['id']]) dump_list.append([k, v.body['id']])
except: except:
pass pass
return dump_list return dump_list
def state(self): def state(self):
if not self.is_busy(): if not self.is_busy():
self.pipe.send_multipart([b"STATE"]) self.pipe.send_multipart([b"STATE"])
@ -201,7 +198,7 @@ class RCFClient(object):
return None return None
class RCFServer(object): class Server(object):
address = None # Server address address = None # Server address
port = None # Server port port = None # Server port
snapshot = None # Snapshot socket snapshot = None # Snapshot socket
@ -221,7 +218,7 @@ class RCFServer(object):
print("connected on tcp://{}:{}".format(address.decode(), port)) print("connected on tcp://{}:{}".format(address.decode(), port))
class RCFClientAgent(object): class ClientAgent(object):
ctx = None ctx = None
pipe = None pipe = None
property_map = None property_map = None
@ -255,9 +252,9 @@ class RCFClientAgent(object):
port = int(msg.pop(0)) port = int(msg.pop(0))
if self.server is None: if self.server is None:
if address == '127.0.0.1' or address == 'localhost' : if address == '127.0.0.1' or address == 'localhost':
self.admin = True self.admin = True
self.server = RCFServer(self.ctx, address, port, self.id) self.server = Server(self.ctx, address, port, self.id)
self.publisher.connect( self.publisher.connect(
"tcp://{}:{}".format(address.decode(), port+2)) "tcp://{}:{}".format(address.decode(), port+2))
@ -267,19 +264,19 @@ class RCFClientAgent(object):
elif command == b"DISCONNECT": elif command == b"DISCONNECT":
if self.admin is False: if self.admin is False:
uid = self.id.decode() uid = self.id.decode()
delete_user = message.RCFMessage( delete_user = message.Message(
key="Client/{}".format(uid), id=self.id, body=None) key="Client/{}".format(uid), id=self.id, body=None)
delete_user.send(self.publisher) delete_user.send(self.publisher)
# TODO: Do we need to pass every object rights to the moderator on disconnect? # TODO: Do we need to pass every object rights to the moderator on disconnect?
# for k,v in self.property_map.items(): # for k,v in self.property_map.items():
# if v.body["id"] == uid: # if v.body["id"] == uid:
# delete_msg = message.RCFMessage( # delete_msg = message.Message(
# key=k, id=self.id, body=None) # key=k, id=self.id, body=None)
# # delete_msg.store(self.property_map) # # delete_msg.store(self.property_map)
# delete_msg.send(self.publisher) # delete_msg.send(self.publisher)
elif command == b"SET": elif command == b"SET":
key = umsgpack.unpackb(msg[0]) key = umsgpack.unpackb(msg[0])
value = umsgpack.unpackb(msg[1]) value = umsgpack.unpackb(msg[1])
@ -292,18 +289,18 @@ class RCFClientAgent(object):
value['id'] = self.id.decode() value['id'] = self.id.decode()
if value: if value:
key_id = self.id key_id = self.id
rcfmsg = message.RCFMessage( msg = message.Message(
key=key, id=key_id, body=value) key=key, id=key_id, body=value)
rcfmsg.store(self.property_map) msg.store(self.property_map)
if override: if override:
helpers.load(key,self.property_map[key].body) helpers.load(key, self.property_map[key].body)
rcfmsg.send(self.publisher) msg.send(self.publisher)
else: else:
logger.error("Fail to dump ") logger.error("Fail to dump ")
else: else:
helpers.load(key,self.property_map[key].body) helpers.load(key, self.property_map[key].body)
elif command == b"ADD": elif command == b"ADD":
key = umsgpack.unpackb(msg[0]) key = umsgpack.unpackb(msg[0])
@ -313,11 +310,11 @@ class RCFClientAgent(object):
value = helpers.dump(key) value = helpers.dump(key)
value['id'] = self.id.decode() value['id'] = self.id.decode()
if value: if value:
rcfmsg = message.RCFMessage( msg = message.Message(
key=key, id=self.id, body=value) key=key, id=self.id, body=value)
rcfmsg.store(self.property_map) msg.store(self.property_map)
rcfmsg.send(self.publisher) msg.send(self.publisher)
else: else:
logger.error("Fail to dump ") logger.error("Fail to dump ")
@ -333,12 +330,12 @@ class RCFClientAgent(object):
elif command == b"LIST": elif command == b"LIST":
dump_list = [] dump_list = []
for k,v in self.property_map.items(): for k, v in self.property_map.items():
if 'Client' in k: if 'Client' in k:
dump_list.append([k,v.id.decode()]) dump_list.append([k, v.id.decode()])
else: else:
try: try:
dump_list.append([k,v.body['id']]) dump_list.append([k, v.body['id']])
except: except:
pass pass
self.pipe.send(umsgpack.packb(dump_list) self.pipe.send(umsgpack.packb(dump_list)
@ -348,8 +345,8 @@ class RCFClientAgent(object):
self.pipe.send(umsgpack.packb(self.state.value)) self.pipe.send(umsgpack.packb(self.state.value))
def net_worker(ctx,store, pipe, serial_product, serial_feed, stop_event): def net_worker(ctx, store, pipe, serial_product, serial_feed, stop_event):
agent = RCFClientAgent(ctx,store, pipe) agent = ClientAgent(ctx, store, pipe)
server = None server = None
net_feed = serial_product net_feed = serial_product
net_product = serial_feed net_product = serial_feed
@ -363,7 +360,7 @@ def net_worker(ctx,store, pipe, serial_product, serial_feed, stop_event):
server = agent.server server = agent.server
if agent.server: if agent.server:
logger.debug("%s: waiting for server at %s:%d...", logger.debug("%s: waiting for server at %s:%d...",
agent.id.decode(), server.address, server.port) agent.id.decode(), server.address, server.port)
server.snapshot.send(b"SNAPSHOT_REQUEST") server.snapshot.send(b"SNAPSHOT_REQUEST")
agent.state = State.SYNCING agent.state = State.SYNCING
server_socket = server.snapshot server_socket = server.snapshot
@ -384,52 +381,52 @@ def net_worker(ctx,store, pipe, serial_product, serial_feed, stop_event):
if agent.pipe in items: if agent.pipe in items:
agent.control_message() agent.control_message()
elif server_socket in items: elif server_socket in items:
rcfmsg = message.RCFMessage.recv(server_socket) msg = message.Message.recv(server_socket)
if agent.state == State.SYNCING: if agent.state == State.SYNCING:
# CLient snapshot # CLient snapshot
if rcfmsg.key == "SNAPSHOT_END": if msg.key == "SNAPSHOT_END":
client_key = "Client/{}".format(agent.id.decode()) client_key = "Client/{}".format(agent.id.decode())
client_dict = {} client_dict = {}
client_dict = helpers.init_client(key=client_key) client_dict = helpers.init_client(key=client_key)
client_dict['id'] = agent.id.decode() client_dict['id'] = agent.id.decode()
client_store = message.RCFMessage( client_store = message.Message(
key=client_key, id=agent.id, body=client_dict) key=client_key, id=agent.id, body=client_dict)
client_store.store(agent.property_map) client_store.store(agent.property_map)
client_store.send(agent.publisher) client_store.send(agent.publisher)
agent.state = State.ACTIVE agent.state = State.ACTIVE
logger.debug("snapshot complete") logger.debug("snapshot complete")
else: else:
net_product.put(('LOAD',rcfmsg.key, rcfmsg.body)) net_product.put(('LOAD', msg.key, msg.body))
# helpers.load(rcfmsg.key, rcfmsg.body) # helpers.load(msg.key, msg.body)
rcfmsg.store(agent.property_map) msg.store(agent.property_map)
logger.debug("snapshot from {} stored".format(rcfmsg.id)) logger.debug("snapshot from {} stored".format(msg.id))
elif agent.state == State.ACTIVE: elif agent.state == State.ACTIVE:
if rcfmsg.id != agent.id: if msg.id != agent.id:
# with lock: # with lock:
# helpers.load(rcfmsg.key, rcfmsg.body) # helpers.load(msg.key, msg.body)
rcfmsg.store(agent.property_map) msg.store(agent.property_map)
net_product.put(('LOAD',rcfmsg.key, rcfmsg.body)) net_product.put(('LOAD', msg.key, msg.body))
else: else:
logger.debug("{} nothing to do".format(agent.id)) logger.debug("{} nothing to do".format(agent.id))
# Serialisation thread => Net thread # Serialisation thread => Net thread
if not net_feed.empty(): if not net_feed.empty():
key, value = net_feed.get() key, value = net_feed.get()
if value: if value:
# Stamp with id # Stamp with id
value['id'] = agent.id.decode() value['id'] = agent.id.decode()
# Format massage # Format massage
rcfmsg = message.RCFMessage( msg = message.Message(
key=key, id=agent.id, body=value) key=key, id=agent.id, body=value)
rcfmsg.store(agent.property_map) msg.store(agent.property_map)
rcfmsg.send(agent.publisher) msg.send(agent.publisher)
else: else:
logger.error("Fail to dump ") logger.error("Fail to dump ")
@ -440,7 +437,7 @@ def serial_worker(serial_product, serial_feed):
logger.info("serial thread launched") logger.info("serial thread launched")
while True: while True:
command,key,value = serial_feed.get() command, key, value = serial_feed.get()
if command == 'STOP': if command == 'STOP':
break break
@ -449,7 +446,7 @@ def serial_worker(serial_product, serial_feed):
value = helpers.dump(key) value = helpers.dump(key)
if value: if value:
serial_product.put((key,value)) serial_product.put((key, value))
except Exception as e: except Exception as e:
logger.error("{}".format(e)) logger.error("{}".format(e))
elif command == 'LOAD': elif command == 'LOAD':
@ -462,19 +459,19 @@ def serial_worker(serial_product, serial_feed):
logger.info("serial thread stopped") logger.info("serial thread stopped")
def watchdog_worker(serial_feed,interval, stop_event): def watchdog_worker(serial_feed, interval, stop_event):
import bpy import bpy
logger.info("watchdog thread launched with {} sec of interval".format(interval))
while not stop_event.is_set():
logger.info(
"watchdog thread launched with {} sec of interval".format(interval))
while not stop_event.is_set():
for datatype in helpers.BPY_TYPES.keys(): for datatype in helpers.BPY_TYPES.keys():
for item in getattr(bpy.data, helpers.BPY_TYPES[datatype]): for item in getattr(bpy.data, helpers.BPY_TYPES[datatype]):
key = "{}/{}".format(datatype, item.name) key = "{}/{}".format(datatype, item.name)
try: try:
if item.is_dirty: if item.is_dirty:
logger.debug("{} needs update".format(key)) logger.debug("{} needs update".format(key))
serial_feed.put(('DUMP',key,None)) serial_feed.put(('DUMP', key, None))
item.is_dirty = False item.is_dirty = False
except: except:
pass pass

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -11,7 +11,7 @@ import zmq
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
class RCFMessage(object): class Message(object):
""" """
Message is formatted on wire as 2 frames: Message is formatted on wire as 2 frames:
frame 0: key (0MQ string) // property path frame 0: key (0MQ string) // property path

View File

@ -149,7 +149,7 @@ class SessionJoinOperator(bpy.types.Operator):
if len(net_settings.ip) < 1: if len(net_settings.ip) < 1:
net_settings.ip = "127.0.0.1" net_settings.ip = "127.0.0.1"
client_instance = client.RCFClient() client_instance = client.Client()
client_instance.connect(net_settings.username, client_instance.connect(net_settings.username,
net_settings.ip, net_settings.port) net_settings.ip, net_settings.port)
@ -463,7 +463,7 @@ def depsgraph_update(scene):
if client_state == 3: if client_state == 3:
if ctx.mode in ['OBJECT','PAINT_GPENCIL']: if ctx.mode in ['OBJECT','PAINT_GPENCIL']:
updates = ctx.depsgraph.updates updates = ctx.view_layer.depsgraph.updates
username = ctx.window_manager.session.username username = ctx.window_manager.session.username

View File

@ -11,7 +11,7 @@ logging.basicConfig(level=logging.DEBUG)
SUPPORTED_TYPES = ['Client','Curve','Material','Texture', 'Light', 'Camera', 'Mesh','Armature', 'Grease Pencil', 'Object', 'Action', 'Collection', 'Scene'] SUPPORTED_TYPES = ['Client','Curve','Material','Texture', 'Light', 'Camera', 'Mesh','Armature', 'Grease Pencil', 'Object', 'Action', 'Collection', 'Scene']
class RCFServerAgent(): class ServerAgent():
def __init__(self, context=zmq.Context.instance(), id="admin"): def __init__(self, context=zmq.Context.instance(), id="admin"):
self.context = context self.context = context
@ -80,18 +80,18 @@ class RCFServerAgent():
self.request_sock.send(identity, zmq.SNDMORE) self.request_sock.send(identity, zmq.SNDMORE)
v.send(self.request_sock) v.send(self.request_sock)
msg_end_snapshot = message.RCFMessage(key="SNAPSHOT_END", id=identity) msg_end_snapshot = message.Message(key="SNAPSHOT_END", id=identity)
self.request_sock.send(identity, zmq.SNDMORE) self.request_sock.send(identity, zmq.SNDMORE)
msg_end_snapshot.send(self.request_sock) msg_end_snapshot.send(self.request_sock)
logger.info("done") logger.info("done")
# Regular update routing (Clients / Client) # Regular update routing (Clients / Client)
elif self.collector_sock in socks: elif self.collector_sock in socks:
msg = message.RCFMessage.recv(self.collector_sock) msg = message.Message.recv(self.collector_sock)
# logger.info("received object") # logger.info("received object")
# Update all clients # Update all clients
msg.store(self.property_map) msg.store(self.property_map)
msg.send(self.pub_sock) msg.send(self.pub_sock)
server = RCFServerAgent() server = ServerAgent()