refactor: cleanup

This commit is contained in:
Swann Martinez 2019-04-10 17:01:21 +02:00
parent cc4033978d
commit 1789a432a1
No known key found for this signature in database
GPG Key ID: 414CCAFD8DA720E1
11 changed files with 616 additions and 527 deletions

View File

@ -14,17 +14,16 @@ import os
import sys
from . import net_operators
from . import net_ui
from . import net_ecs
from . import operators
from . import ui
def register():
net_operators.register()
net_ui.register()
net_ecs.register()
operators.register()
ui.register()
def unregister():
net_ui.unregister()
net_operators.unregister()
net_ecs.unregister()
ui.unregister()
operators.unregister()

View File

@ -1,21 +1,29 @@
import binascii
import collections
import logging
import threading
from uuid import uuid4
import binascii
import os
import sys
from random import randint
import threading
import time
from enum import Enum
from random import randint
from uuid import uuid4
try:
from .libs import umsgpack
from .libs import zmq
from .libs import dump_anything
from . import helpers
from . import message
except:
# Server import
from libs import umsgpack
from libs import zmq
from libs import dump_anything
import helpers
import message
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG)
@ -26,6 +34,7 @@ SERVER_MAX = 1
stop = False
def zpipe(ctx):
"""build inproc pipe for talking to threads
@ -48,75 +57,7 @@ class State(Enum):
SYNCING = 2
ACTIVE = 3
class RCFMessage(object):
"""
Message is formatted on wire as 2 frames:
frame 0: key (0MQ string) // property path
frame 1: id (0MQ string) // property path
frame 2: mtype (0MQ string) // property path
frame 3: body (blob) // Could be any data
"""
key = None # key (string)
id = None # User (string)
mtype = None # data mtype (string)
body = None # data blob
uuid = None
def __init__(self, key=None, uuid=None, id=None, mtype=None, body=None):
if uuid is None:
uuid = uuid4().bytes
self.key = key
self.uuid = uuid
self.mtype = mtype
self.body = body
self.id = id
def store(self, dikt):
"""Store me in a dict if I have anything to store"""
# this currently erasing old value
if self.key is not None:
dikt[self.key] = self
# elif self.key in dikt:
# del dikt[self.key]
def send(self, socket):
"""Send key-value message to socket; any empty frames are sent as such."""
key = ''.encode() if self.key is None else self.key.encode()
mtype = ''.encode() if self.mtype is None else self.mtype.encode()
body = ''.encode() if self.body is None else umsgpack.packb(self.body)
id = ''.encode() if self.id is None else self.id
try:
socket.send_multipart([key, id, mtype, body])
except:
logger.info("Fail to send {} {}".format(key,id))
@classmethod
def recv(cls, socket):
"""Reads key-value message from socket, returns new kvmsg instance."""
key, id, mtype, body = socket.recv_multipart(zmq.DONTWAIT)
key = key.decode() if key else None
id = id if id else None
mtype = mtype.decode() if body else None
body = umsgpack.unpackb(body) if body else None
return cls(key=key, id=id, mtype=mtype, body=body)
def dump(self):
if self.body is None:
size = 0
data = 'NULL'
else:
size = len(self.body)
data = repr(self.body)
print("[key:{key}][size:{size}][mtype:{mtype}] {data}".format(
key=self.key,
size=size,
mtype=self.mtype,
data=data,
))
class RCFClient(object):
ctx = None
@ -139,7 +80,8 @@ class RCFClient(object):
"""Set new value in distributed hash table
Sends [SET][key][value] to the agent
"""
self.pipe.send_multipart([b"SET", umsgpack.packb(key), umsgpack.packb(value)])
self.pipe.send_multipart(
[b"SET", umsgpack.packb(key), umsgpack.packb(value)])
def get(self, key):
"""Lookup value in distributed hash table
@ -160,13 +102,14 @@ class RCFClient(object):
global stop
stop = True
class RCFServer(object):
address = None # Server address
port = None # Server port
snapshot = None # Snapshot socket
subscriber = None # Incoming updates
def __init__(self, ctx, address, port,id):
def __init__(self, ctx, address, port, id):
self.address = address
self.port = port
self.snapshot = ctx.socket(zmq.DEALER)
@ -179,6 +122,7 @@ class RCFServer(object):
self.subscriber.linger = 0
print("connected on tcp://{}:{}".format(address.decode(), port))
class RCFClientAgent(object):
ctx = None
pipe = None
@ -210,30 +154,33 @@ class RCFClientAgent(object):
if self.server is None:
self.server = RCFServer(self.ctx, address, port, self.id)
self.publisher.connect("tcp://{}:{}".format(address.decode(), port+2))
self.publisher.connect(
"tcp://{}:{}".format(address.decode(), port+2))
else:
logger.error("E: too many servers (max. %i)", SERVER_MAX)
elif command == b"SET":
key,value = msg
key, value = msg
# Send key-value pair on to server
rcfmsg = RCFMessage(key=umsgpack.unpackb(key),id=self.id ,mtype="",body=umsgpack.unpackb(value))
rcfmsg = message.RCFMessage(key=umsgpack.unpackb(
key), id=self.id, mtype="", body=umsgpack.unpackb(value))
rcfmsg.store(self.property_map)
rcfmsg.send(self.publisher)
elif command == b"GET":
key = umsgpack.unpackb(msg[0])
value = self.property_map.get(key)
self.pipe.send(umsgpack.packb(value.body) if value else b'')
def rcf_client_agent(ctx, pipe):
agent = RCFClientAgent(ctx, pipe)
server = None
global stop
while True:
while True:
if stop:
break
# logger.info("asdasd")
@ -241,7 +188,6 @@ def rcf_client_agent(ctx, pipe):
poller.register(agent.pipe, zmq.POLLIN)
server_socket = None
if agent.state == State.INITIAL:
server = agent.server
if agent.server:
@ -261,12 +207,13 @@ def rcf_client_agent(ctx, pipe):
try:
items = dict(poller.poll(1))
except:
pass
raise
break
if agent.pipe in items:
agent.control_message()
elif server_socket in items:
rcfmsg = RCFMessage.recv(server_socket)
rcfmsg = message.RCFMessage.recv(server_socket)
if agent.state == State.SYNCING:
# Store snapshot
if rcfmsg.key == "SNAPSHOT_END":
@ -278,90 +225,58 @@ def rcf_client_agent(ctx, pipe):
if rcfmsg.id != agent.id:
rcfmsg.store(agent.property_map)
action = "update" if rcfmsg.body else "delete"
logging.info("I: received from {}:{},{} {}".format(server.address,rcfmsg.body.id, server.port, action))
logging.info("I: received from {}:{},{} {}".format(
server.address, rcfmsg.body.id, server.port, action))
else:
logger.info("IDLE")
logger.info("exit thread")
stop = False
# else: else
# agent.state = State.INITIAL
# else: else
# agent.state = State.INITIAL
class RCFServerAgent():
def __init__(self, context=zmq.Context.instance(), id="admin"):
self.context = context
self.pub_sock = None
self.request_sock = None
self.collector_sock = None
self.poller = None
class SerializationAgent(object):
ctx = None
pipe = None
self.property_map = {}
self.id = id
self.bind_ports()
# Main client loop registration
self.tick()
def __init__(self, ctx, pipe_in, pipe_out):
self.ctx = ctx
self.pipe_in = pipe_in
self.pipe_out = pipe_out
logger.info("{} client initialized".format(id))
def control_message(self):
msg = self.pipe_in.recv_multipart()
command = msg.pop(0)
def bind_ports(self):
# Update all clients
self.pub_sock = self.context.socket(zmq.PUB)
self.pub_sock.setsockopt(zmq.SNDHWM, 60)
self.pub_sock.bind("tcp://*:5556")
time.sleep(0.2)
if command == b"DUMP":
key = umsgpack.unpackb(msg[0])
# Update request
self.request_sock = self.context.socket(zmq.ROUTER)
self.request_sock.setsockopt(zmq.IDENTITY, b'SERVER')
self.request_sock.setsockopt(zmq.RCVHWM, 60)
self.request_sock.bind("tcp://*:5555")
logger.log("Dumping....")
# Update collector
self.collector_sock = self.context.socket(zmq.PULL)
self.collector_sock.setsockopt(zmq.RCVHWM, 60)
self.collector_sock.bind("tcp://*:5557")
elif command == b"LOAD":
key, value = msg
logger.log("Loading....")
# poller for socket aggregation
self.poller = zmq.Poller()
self.poller.register(self.request_sock, zmq.POLLIN)
self.poller.register(self.collector_sock, zmq.POLLIN)
def tick(self):
logger.info("{} server launched".format(id))
def serialization_agent(ctx, pipe_in, pipe_out):
agent = SerializationAgent(ctx, pipe_in, pipe_out)
server = None
while True:
# Non blocking poller
socks = dict(self.poller.poll(1000))
global stop
while True:
if stop:
break
# Snapshot system for late join (Server - Client)
if self.request_sock in socks:
msg = self.request_sock.recv_multipart(zmq.DONTWAIT)
poller = zmq.Poller()
poller.register(agent.pipe, zmq.POLLIN)
identity = msg[0]
request = msg[1]
print("asdasd")
if request == b"SNAPSHOT_REQUEST":
pass
else:
logger.info("Bad snapshot request")
break
try:
items = dict(poller.poll(1))
except:
raise
break
for k, v in self.property_map.items():
logger.info(
"Sending {} snapshot to {}".format(k, identity))
self.request_sock.send(identity, zmq.SNDMORE)
v.send(self.request_sock)
if agent.pipe in items:
agent.control_message()
msg_end_snapshot = RCFMessage(key="SNAPSHOT_END", id=identity)
self.request_sock.send(identity, zmq.SNDMORE)
msg_end_snapshot.send(self.request_sock)
logger.info("done")
# Regular update routing (Clients / Client)
elif self.collector_sock in socks:
msg = RCFMessage.recv(self.collector_sock)
# Update all clients
msg.store(self.property_map)
msg.send(self.pub_sock)

274
helpers.py Normal file
View File

@ -0,0 +1,274 @@
import bpy
from .libs import dump_anything
def dump_datablock(datablock, depth):
if datablock:
print("sending {}".format(datablock.name))
dumper = dump_anything.Dumper()
dumper.type_subset = dumper.match_subset_all
dumper.depth = depth
datablock_type = datablock.bl_rna.name
key = "{}/{}".format(datablock_type, datablock.name)
data = dumper.dump(datablock)
client.push_update(key, datablock_type, data)
def dump_datablock_attibute(datablock, attributes, depth=1):
if datablock:
dumper = dump_anything.Dumper()
dumper.type_subset = dumper.match_subset_all
dumper.depth = depth
datablock_type = datablock.bl_rna.name
key = "{}/{}".format(datablock_type, datablock.name)
data = {}
for attr in attributes:
try:
data[attr] = dumper.dump(getattr(datablock, attr))
except:
pass
client.push_update(key, datablock_type, data)
def upload_mesh(mesh):
if mesh.bl_rna.name == 'Mesh':
dump_datablock_attibute(
mesh, ['name', 'polygons', 'edges', 'vertices'], 6)
def upload_material(material):
if material.bl_rna.name == 'Material':
dump_datablock_attibute(material, ['name', 'node_tree'], 7)
def upload_gpencil(gpencil):
if gpencil.bl_rna.name == 'Grease Pencil':
dump_datablock_attibute(gpencil, ['name', 'layers','materials'], 9)
def load_mesh(target=None, data=None, create=False):
import bmesh
# TODO: handle error
mesh_buffer = bmesh.new()
for i in data["vertices"]:
mesh_buffer.verts.new(data["vertices"][i]["co"])
mesh_buffer.verts.ensure_lookup_table()
for i in data["edges"]:
verts = mesh_buffer.verts
v1 = data["edges"][i]["vertices"][0]
v2 = data["edges"][i]["vertices"][1]
mesh_buffer.edges.new([verts[v1], verts[v2]])
for p in data["polygons"]:
verts = []
for v in data["polygons"][p]["vertices"]:
verts.append(mesh_buffer.verts[v])
if len(verts) > 0:
mesh_buffer.faces.new(verts)
if target is None and create:
target = bpy.data.meshes.new(data["name"])
mesh_buffer.to_mesh(target)
# Load other meshes metadata
dump_anything.load(target, data)
def load_object(target=None, data=None, create=False):
try:
if target is None and create:
pointer = None
# Object specific constructor...
if data["data"] in bpy.data.meshes.keys():
pointer = bpy.data.meshes[data["data"]]
elif data["data"] in bpy.data.lights.keys():
pointer = bpy.data.lights[data["data"]]
elif data["data"] in bpy.data.cameras.keys():
pointer = bpy.data.cameras[data["data"]]
elif data["data"] in bpy.data.curves.keys():
pointer = bpy.data.curves[data["data"]]
elif data["data"] in bpy.data.grease_pencils.keys():
pointer = bpy.data.grease_pencils[data["data"]]
target = bpy.data.objects.new(data["name"], pointer)
# Load other meshes metadata
dump_anything.load(target, data)
import mathutils
target.matrix_world = mathutils.Matrix(data["matrix_world"])
except:
print("Object {} loading error ".format(data["name"]))
def load_collection(target=None, data=None, create=False):
try:
if target is None and create:
target = bpy.data.collections.new(data["name"])
# Load other meshes metadata
# dump_anything.load(target, data)
# load objects into collection
for object in data["objects"]:
target.objects.link(bpy.data.objects[object])
for object in target.objects.keys():
if object not in data["objects"]:
target.objects.unlink(bpy.data.objects[object])
except:
print("Collection loading error")
def load_scene(target=None, data=None, create=False):
try:
if target is None and create:
target = bpy.data.scenes.new(data["name"])
# Load other meshes metadata
dump_anything.load(target, data)
# Load master collection
for object in data["collection"]["objects"]:
if object not in target.collection.objects.keys():
target.collection.objects.link(bpy.data.objects[object])
for object in target.collection.objects.keys():
if object not in data["collection"]["objects"]:
target.collection.objects.unlink(bpy.data.objects[object])
# load collections
# TODO: Recursive link
for collection in data["collection"]["children"]:
if collection not in target.collection.children.keys():
target.collection.children.link(
bpy.data.collections[collection])
# Load annotation
if data["grease_pencil"]:
target.grease_pencil = bpy.data.grease_pencils[data["grease_pencil"]["name"]]
except:
print("Scene loading error")
def load_material(target=None, data=None, create=False):
try:
if target is None and create:
target = bpy.data.materials.new(data["name"])
# Load other meshes metadata
dump_anything.load(target, data)
# load nodes
for node in data["node_tree"]["nodes"]:
index = target.node_tree.nodes.find(node)
if index is -1:
node_type = data["node_tree"]["nodes"][node]["bl_idname"]
target.node_tree.nodes.new(type=node_type)
dump_anything.load(
target.node_tree.nodes[index], data["node_tree"]["nodes"][node])
for input in data["node_tree"]["nodes"][node]["inputs"]:
try:
target.node_tree.nodes[index].inputs[input].default_value = data[
"node_tree"]["nodes"][node]["inputs"][input]["default_value"]
except:
pass
# Load nodes links
target.node_tree.links.clear()
for link in data["node_tree"]["links"]:
current_link = data["node_tree"]["links"][link]
input_socket = target.node_tree.nodes[current_link['to_node']
['name']].inputs[current_link['to_socket']['name']]
output_socket = target.node_tree.nodes[current_link['from_node']
['name']].outputs[current_link['from_socket']['name']]
target.node_tree.links.new(input_socket, output_socket)
except:
print("Material loading error")
def load_gpencil_layer(target=None,data=None, create=False):
dump_anything.load(target, data)
for frame in data["frames"]:
try:
tframe = target.frames[frame]
except:
tframe = target.frames.new(frame)
dump_anything.load(tframe, data["frames"][frame])
for stroke in data["frames"][frame]["strokes"]:
try:
tstroke = tframe.strokes[stroke]
except:
tstroke = tframe.strokes.new()
dump_anything.load(tstroke, data["frames"][frame]["strokes"][stroke])
for point in data["frames"][frame]["strokes"][stroke]["points"]:
p = data["frames"][frame]["strokes"][stroke]["points"][point]
try:
tpoint = tstroke.points[point]
except:
tpoint = tstroke.points.add(1)
tpoint = tstroke.points[len(tstroke.points)-1]
dump_anything.load(tpoint, p)
def load_gpencil(target=None, data=None, create=False):
try:
if target is None and create:
target = bpy.data.grease_pencils.new(data["name"])
if "layers" in data.keys():
for layer in data["layers"]:
if layer not in target.layers.keys():
gp_layer = target.layers.new(data["layers"][layer]["info"])
else:
gp_layer = target.layers[layer]
load_gpencil_layer(target=gp_layer,data=data["layers"][layer],create=create)
# Load other meshes metadata
dump_anything.load(target, data)
except:
print("default loading error")
def load_light(target=None, data=None, create=False, type=None):
try:
if target is None and create:
bpy.data.lights.new(data["name"], data["type"])
# Load other meshes metadata
dump_anything.load(target, data)
except:
print("light loading error")
def load_default(target=None, data=None, create=False, type=None):
try:
if target is None and create:
getattr(bpy.data, CORRESPONDANCE[type]).new(data["name"])
# Load other meshes metadata
dump_anything.load(target, data)
except:
print("default loading error")

1
libs/bsyncio Submodule

@ -0,0 +1 @@
Subproject commit 95275093b79d9289f939079550bb47ab25c1eacd

1
libs/esper Submodule

@ -0,0 +1 @@
Subproject commit 5b6cd0c51718d5dcfa0e5613f824b5251cf092ac

84
message.py Normal file
View File

@ -0,0 +1,84 @@
from uuid import uuid4
try:
from .libs import umsgpack
from .libs import zmq
except:
# Server import
from libs import umsgpack
from libs import zmq
class RCFMessage(object):
"""
Message is formatted on wire as 2 frames:
frame 0: key (0MQ string) // property path
frame 1: id (0MQ string) // property path
frame 2: mtype (0MQ string) // property path
frame 3: body (blob) // Could be any data
"""
key = None # key (string)
id = None # User (string)
mtype = None # data mtype (string)
body = None # data blob
uuid = None
def __init__(self, key=None, uuid=None, id=None, mtype=None, body=None):
if uuid is None:
uuid = uuid4().bytes
self.key = key
self.uuid = uuid
self.mtype = mtype
self.body = body
self.id = id
def apply(self):
pass
def store(self, dikt):
"""Store me in a dict if I have anything to store"""
# this currently erasing old value
if self.key is not None:
dikt[self.key] = self
# elif self.key in dikt:
# del dikt[self.key]
def send(self, socket):
"""Send key-value message to socket; any empty frames are sent as such."""
key = ''.encode() if self.key is None else self.key.encode()
mtype = ''.encode() if self.mtype is None else self.mtype.encode()
body = ''.encode() if self.body is None else umsgpack.packb(self.body)
id = ''.encode() if self.id is None else self.id
try:
socket.send_multipart([key, id, mtype, body])
except:
logger.info("Fail to send {} {}".format(key, id))
@classmethod
def recv(cls, socket):
"""Reads key-value message from socket, returns new kvmsg instance."""
key, id, mtype, body = socket.recv_multipart(zmq.DONTWAIT)
key = key.decode() if key else None
id = id if id else None
mtype = mtype.decode() if body else None
body = umsgpack.unpackb(body) if body else None
return cls(key=key, id=id, mtype=mtype, body=body)
def dump(self):
if self.body is None:
size = 0
data = 'NULL'
else:
size = len(self.body)
data = repr(self.body)
print("[key:{key}][size:{size}][mtype:{mtype}] {data}".format(
key=self.key,
size=size,
mtype=self.mtype,
data=data,
))

View File

@ -16,13 +16,12 @@ import mathutils
from bpy_extras import view3d_utils
from gpu_extras.batch import batch_for_shader
from . import net_components, net_ui, net_draw
from .libs import dump_anything
from . import client, ui, draw
logger = logging.getLogger(__name__)
client = None
client_instance = None
server = None
context = None
drawer = None
@ -99,101 +98,52 @@ def refresh_window():
import bpy
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
def upload_client_instance_position():
global client_instance
def dump_datablock(datablock, depth):
if datablock:
print("sending {}".format(datablock.name))
dumper = dump_anything.Dumper()
dumper.type_subset = dumper.match_subset_all
dumper.depth = depth
datablock_type = datablock.bl_rna.name
key = "{}/{}".format(datablock_type, datablock.name)
data = dumper.dump(datablock)
client.push_update(key, datablock_type, data)
def dump_datablock_attibute(datablock, attributes, depth=1):
if datablock:
dumper = dump_anything.Dumper()
dumper.type_subset = dumper.match_subset_all
dumper.depth = depth
datablock_type = datablock.bl_rna.name
key = "{}/{}".format(datablock_type, datablock.name)
data = {}
for attr in attributes:
try:
data[attr] = dumper.dump(getattr(datablock, attr))
except:
pass
client.push_update(key, datablock_type, data)
def upload_mesh(mesh):
if mesh.bl_rna.name == 'Mesh':
dump_datablock_attibute(
mesh, ['name', 'polygons', 'edges', 'vertices'], 6)
def upload_material(material):
if material.bl_rna.name == 'Material':
dump_datablock_attibute(material, ['name', 'node_tree'], 7)
def upload_gpencil(gpencil):
if gpencil.bl_rna.name == 'Grease Pencil':
dump_datablock_attibute(gpencil, ['name', 'layers','materials'], 9)
def upload_client_position():
global client
if client:
key = "net/clients/{}".format(client.id.decode())
if client_instance:
key = "net/client_instances/{}".format(client_instance.id.decode())
try:
current_coords = net_draw.get_client_view_rect()
data = client.property_map[key].body
current_coords = net_draw.get_client_instance_view_rect()
data = client_instance.property_map[key].body
if data is None:
data = {}
data['location'] = current_coords
color = bpy.context.scene.session_settings.client_color
color = bpy.context.scene.session_settings.client_instance_color
data['color'] = (color.r, color.g, color.b, 1)
client.push_update(key, 'client', data)
client_instance.push_update(key, 'client_instance', data)
elif current_coords[0] != data['location'][0]:
data['location'] = current_coords
client.push_update(key, 'client', data)
client_instance.push_update(key, 'client_instance', data)
except:
pass
def update_selected_object(context):
global client
global client_instance
session = bpy.context.scene.session_settings
# Active object bounding box
if len(context.selected_objects) > 0:
if session.active_object is not context.selected_objects[0] or session.active_object.is_evaluated:
session.active_object = context.selected_objects[0]
key = "net/objects/{}".format(client.id.decode())
key = "net/objects/{}".format(client_instance.id.decode())
data = {}
data['color'] = [session.client_color.r,
session.client_color.g, session.client_color.b]
data['color'] = [session.client_instance_color.r,
session.client_instance_color.g, session.client_instance_color.b]
data['object'] = session.active_object.name
client.push_update(
key, 'clientObject', data)
client_instance.push_update(
key, 'client_instanceObject', data)
return True
elif len(context.selected_objects) == 0 and session.active_object:
session.active_object = None
data = {}
data['color'] = [session.client_color.r,
session.client_color.g, session.client_color.b]
data['color'] = [session.client_instance_color.r,
session.client_instance_color.g, session.client_instance_color.b]
data['object'] = None
key = "net/objects/{}".format(client.id.decode())
client.push_update(key, 'clientObject', data)
key = "net/objects/{}".format(client_instance.id.decode())
client_instance.push_update(key, 'client_instanceObject', data)
return True
@ -217,228 +167,8 @@ def init_scene():
for scene in bpy.data.scenes:
dump_datablock(scene, 4)
def load_mesh(target=None, data=None, create=False):
import bmesh
# TODO: handle error
mesh_buffer = bmesh.new()
for i in data["vertices"]:
mesh_buffer.verts.new(data["vertices"][i]["co"])
mesh_buffer.verts.ensure_lookup_table()
for i in data["edges"]:
verts = mesh_buffer.verts
v1 = data["edges"][i]["vertices"][0]
v2 = data["edges"][i]["vertices"][1]
mesh_buffer.edges.new([verts[v1], verts[v2]])
for p in data["polygons"]:
verts = []
for v in data["polygons"][p]["vertices"]:
verts.append(mesh_buffer.verts[v])
if len(verts) > 0:
mesh_buffer.faces.new(verts)
if target is None and create:
target = bpy.data.meshes.new(data["name"])
mesh_buffer.to_mesh(target)
# Load other meshes metadata
dump_anything.load(target, data)
def load_object(target=None, data=None, create=False):
try:
if target is None and create:
pointer = None
# Object specific constructor...
if data["data"] in bpy.data.meshes.keys():
pointer = bpy.data.meshes[data["data"]]
elif data["data"] in bpy.data.lights.keys():
pointer = bpy.data.lights[data["data"]]
elif data["data"] in bpy.data.cameras.keys():
pointer = bpy.data.cameras[data["data"]]
elif data["data"] in bpy.data.curves.keys():
pointer = bpy.data.curves[data["data"]]
elif data["data"] in bpy.data.grease_pencils.keys():
pointer = bpy.data.grease_pencils[data["data"]]
target = bpy.data.objects.new(data["name"], pointer)
# Load other meshes metadata
dump_anything.load(target, data)
import mathutils
target.matrix_world = mathutils.Matrix(data["matrix_world"])
except:
print("Object {} loading error ".format(data["name"]))
def load_collection(target=None, data=None, create=False):
try:
if target is None and create:
target = bpy.data.collections.new(data["name"])
# Load other meshes metadata
# dump_anything.load(target, data)
# load objects into collection
for object in data["objects"]:
target.objects.link(bpy.data.objects[object])
for object in target.objects.keys():
if object not in data["objects"]:
target.objects.unlink(bpy.data.objects[object])
except:
print("Collection loading error")
def load_scene(target=None, data=None, create=False):
try:
if target is None and create:
target = bpy.data.scenes.new(data["name"])
# Load other meshes metadata
dump_anything.load(target, data)
# Load master collection
for object in data["collection"]["objects"]:
if object not in target.collection.objects.keys():
target.collection.objects.link(bpy.data.objects[object])
for object in target.collection.objects.keys():
if object not in data["collection"]["objects"]:
target.collection.objects.unlink(bpy.data.objects[object])
# load collections
# TODO: Recursive link
for collection in data["collection"]["children"]:
if collection not in target.collection.children.keys():
target.collection.children.link(
bpy.data.collections[collection])
# Load annotation
if data["grease_pencil"]:
target.grease_pencil = bpy.data.grease_pencils[data["grease_pencil"]["name"]]
except:
print("Scene loading error")
def load_material(target=None, data=None, create=False):
try:
if target is None and create:
target = bpy.data.materials.new(data["name"])
# Load other meshes metadata
dump_anything.load(target, data)
# load nodes
for node in data["node_tree"]["nodes"]:
index = target.node_tree.nodes.find(node)
if index is -1:
node_type = data["node_tree"]["nodes"][node]["bl_idname"]
target.node_tree.nodes.new(type=node_type)
dump_anything.load(
target.node_tree.nodes[index], data["node_tree"]["nodes"][node])
for input in data["node_tree"]["nodes"][node]["inputs"]:
try:
target.node_tree.nodes[index].inputs[input].default_value = data[
"node_tree"]["nodes"][node]["inputs"][input]["default_value"]
except:
pass
# Load nodes links
target.node_tree.links.clear()
for link in data["node_tree"]["links"]:
current_link = data["node_tree"]["links"][link]
input_socket = target.node_tree.nodes[current_link['to_node']
['name']].inputs[current_link['to_socket']['name']]
output_socket = target.node_tree.nodes[current_link['from_node']
['name']].outputs[current_link['from_socket']['name']]
target.node_tree.links.new(input_socket, output_socket)
except:
print("Material loading error")
def load_gpencil_layer(target=None,data=None, create=False):
dump_anything.load(target, data)
for frame in data["frames"]:
try:
tframe = target.frames[frame]
except:
tframe = target.frames.new(frame)
dump_anything.load(tframe, data["frames"][frame])
for stroke in data["frames"][frame]["strokes"]:
try:
tstroke = tframe.strokes[stroke]
except:
tstroke = tframe.strokes.new()
dump_anything.load(tstroke, data["frames"][frame]["strokes"][stroke])
for point in data["frames"][frame]["strokes"][stroke]["points"]:
p = data["frames"][frame]["strokes"][stroke]["points"][point]
try:
tpoint = tstroke.points[point]
except:
tpoint = tstroke.points.add(1)
tpoint = tstroke.points[len(tstroke.points)-1]
dump_anything.load(tpoint, p)
def load_gpencil(target=None, data=None, create=False):
try:
if target is None and create:
target = bpy.data.grease_pencils.new(data["name"])
if "layers" in data.keys():
for layer in data["layers"]:
if layer not in target.layers.keys():
gp_layer = target.layers.new(data["layers"][layer]["info"])
else:
gp_layer = target.layers[layer]
load_gpencil_layer(target=gp_layer,data=data["layers"][layer],create=create)
# Load other meshes metadata
dump_anything.load(target, data)
except:
print("default loading error")
def load_light(target=None, data=None, create=False, type=None):
try:
if target is None and create:
bpy.data.lights.new(data["name"], data["type"])
# Load other meshes metadata
dump_anything.load(target, data)
except:
print("light loading error")
def load_default(target=None, data=None, create=False, type=None):
try:
if target is None and create:
getattr(bpy.data, CORRESPONDANCE[type]).new(data["name"])
# Load other meshes metadata
dump_anything.load(target, data)
except:
print("default loading error")
def update_scene(msg):
global client
global client_instance
net_vars = bpy.context.scene.session_settings
@ -480,14 +210,14 @@ def update_scene(msg):
# load_default(target=target, data=msg.body,
# create=net_vars.load_data, type=msg.mtype)
# else:
# if msg.mtype == 'client':
# if msg.mtype == 'client_instance':
# refresh_window()
# elif msg.mtype == 'clientObject':
# elif msg.mtype == 'client_instanceObject':
# selected_objects = []
# for k, v in client.property_map.items():
# if v.mtype == 'clientObject':
# if client.id != v.id:
# for k, v in client_instance.property_map.items():
# if v.mtype == 'client_instanceObject':
# if client_instance.id != v.id:
# selected_objects.append(v.body['object'])
# for obj in bpy.data.objects:
@ -517,11 +247,11 @@ def push(data_type,id):
dump_datablock(bpy.data.scenes[id], 4)
def pull(keystore):
global client
global client_instance
net_vars = bpy.context.scene.session_settings
body = client.property_map[keystore].body
data_type = client.property_map[keystore].mtype
body = client_instance.property_map[keystore].body
data_type = client_instance.property_map[keystore].mtype
target = resolve_bpy_path(keystore)
if target:
@ -553,14 +283,14 @@ def pull(keystore):
elif data_type == 'Camera':
load_default(target=target, data=body,
create=net_vars.load_data, type=mtype)
elif data_type == 'client':
elif data_type == 'client_instance':
refresh_window()
elif data_type == 'clientObject':
elif data_type == 'client_instanceObject':
selected_objects = []
for k, v in client.property_map.items():
if v.mtype == 'clientObject':
if client.id != v.id:
for k, v in client_instance.property_map.items():
if v.mtype == 'client_instanceObject':
if client_instance.id != v.id:
selected_objects.append(v.body['object'])
for obj in bpy.data.objects:
@ -613,10 +343,10 @@ def mesh_tick():
def object_tick():
obj_name = get_update("Object")
global client
global client_instance
if obj_name:
if "Object/{}".format(obj_name) in client.property_map.keys():
if "Object/{}".format(obj_name) in client_instance.property_map.keys():
dump_datablock_attibute(bpy.data.objects[obj_name], ['matrix_world'])
else:
dump_datablock(bpy.data.objects[obj_name], 1)
@ -635,7 +365,7 @@ def draw_tick():
drawer.draw()
# Upload
upload_client_position()
upload_client_instance_position()
return 0.2
@ -669,7 +399,7 @@ class session_join(bpy.types.Operator):
return True
def execute(self, context):
global client, drawer
global client_instance, drawer
net_settings = context.scene.session_settings
# Scene setup
@ -684,13 +414,13 @@ class session_join(bpy.types.Operator):
username = str(context.scene.session_settings.username)
client = net_components.RCFClient()
client.connect("127.0.0.1",5555)
client_instance = client.RCFClient()
client_instance.connect("127.0.0.1",5555)
# net_settings.is_running = True
# drawer = net_draw.HUD(client_instance=client)
# drawer = net_draw.HUD(client_instance_instance=client_instance)
# register_ticks()
return {"FINISHED"}
@ -700,7 +430,7 @@ class session_join(bpy.types.Operator):
class session_add_property(bpy.types.Operator):
bl_idname = "session.add_prop"
bl_label = "add"
bl_description = "broadcast a property to connected clients"
bl_description = "broadcast a property to connected client_instances"
bl_options = {"REGISTER"}
property_path: bpy.props.StringProperty(default="None")
@ -711,10 +441,10 @@ class session_add_property(bpy.types.Operator):
return True
def execute(self, context):
global client
global client_instance
client.set('key', 1)
print(client.get('key'))
client_instance.set('key', 1)
print(client_instance.get('key'))
# item = resolve_bpy_path(self.property_path)
# print(item)
@ -729,7 +459,7 @@ class session_add_property(bpy.types.Operator):
# data = dumper.dump(item)
# data_type = item.__class__.__name__
# client.push_update(key, data_type, data)
# client_instance.push_update(key, data_type, data)
return {"FINISHED"}
@ -737,7 +467,7 @@ class session_add_property(bpy.types.Operator):
class session_remove_property(bpy.types.Operator):
bl_idname = "session.remove_prop"
bl_label = "remove"
bl_description = "broadcast a property to connected clients"
bl_description = "broadcast a property to connected client_instances"
bl_options = {"REGISTER"}
property_path: bpy.props.StringProperty(default="None")
@ -747,10 +477,10 @@ class session_remove_property(bpy.types.Operator):
return True
def execute(self, context):
global client
global client_instance
try:
del client.property_map[self.property_path]
del client_instance.property_map[self.property_path]
return {"FINISHED"}
except:
@ -769,7 +499,7 @@ class session_create(bpy.types.Operator):
def execute(self, context):
global server
global client
global client_instance
server = subprocess.Popen(['python','server.py'], shell=False, stdout=subprocess.PIPE)
time.sleep(0.1)
@ -794,7 +524,7 @@ class session_stop(bpy.types.Operator):
def execute(self, context):
global server
global client
global client_instance
net_settings = context.scene.session_settings
@ -802,16 +532,16 @@ class session_stop(bpy.types.Operator):
server.kill()
del server
server = None
if client:
client.exit()
del client
client = None
if client_instance:
client_instance.exit()
del client_instance
client_instance = None
# bpy.ops.asyncio.stop()
net_settings.is_running = False
# unregister_ticks()
else:
logger.debug("No server/client running.")
logger.debug("No server/client_instance running.")
return {"FINISHED"}
@ -840,30 +570,30 @@ class session_settings(bpy.types.PropertyGroup):
('HOST', 'hosting', 'host a session'),
('CONNECT', 'connexion', 'connect to a session')},
default='HOST')
client_color = bpy.props.FloatVectorProperty(name="client_color",
client_color = bpy.props.FloatVectorProperty(name="client_instance_color",
subtype='COLOR',
default=randomColor())
class session_snapview(bpy.types.Operator):
bl_idname = "session.snapview"
bl_label = "draw clients"
bl_label = "draw client_instances"
bl_description = "Description that shows in blender tooltips"
bl_options = {"REGISTER"}
target_client = bpy.props.StringProperty()
target_client_instance = bpy.props.StringProperty()
@classmethod
def poll(cls, context):
return True
def execute(self, context):
global client
global client_instance
area, region, rv3d = net_draw.view3d_find()
for k, v in client.property_map.items():
if v.mtype == 'client' and v.id.decode() == self.target_client:
for k, v in client_instance.property_map.items():
if v.mtype == 'client_instance' and v.id.decode() == self.target_client_instance:
rv3d.view_location = v.body['location'][1]
rv3d.view_distance = 30.0
return {"FINISHED"}
@ -892,9 +622,9 @@ def ordered(updates):
def depsgraph_update(scene):
global client
global client_instance
if client and client.status == net_components.RCFStatus.CONNECTED:
if client_instance and client_instance.status == net_components.RCFStatus.CONNECTED:
updates = bpy.context.depsgraph.updates
update_selected_object(bpy.context)
@ -948,7 +678,7 @@ def depsgraph_update(scene):
# if c[1].id.bl_rna.name == "Object":
# if data_name in bpy.data.objects.keys():
# found = False
# for k in client.property_map.keys():
# for k in client_instance.property_map.keys():
# if data_name in k:
# found = True
# break
@ -975,7 +705,7 @@ def register():
def unregister():
global server
global client
global client_instance
# try:
# bpy.app.handlers.depsgraph_update_post.remove(depsgraph_update)
@ -986,10 +716,10 @@ def unregister():
# server.stop()
del server
server = None
if client:
# client.stop()
del client
client = None
if client_instance:
# client_instance.stop()
del client_instance
client_instance = None
from bpy.utils import unregister_class
for cls in reversed(classes):

View File

@ -1,4 +1,89 @@
from net_components import RCFServerAgent
import logging
import time
from libs import zmq
import message
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG)
class RCFServerAgent():
def __init__(self, context=zmq.Context.instance(), id="admin"):
self.context = context
self.pub_sock = None
self.request_sock = None
self.collector_sock = None
self.poller = None
self.property_map = {}
self.id = id
self.bind_ports()
# Main client loop registration
self.tick()
logger.info("{} client initialized".format(id))
def bind_ports(self):
# Update all clients
self.pub_sock = self.context.socket(zmq.PUB)
self.pub_sock.setsockopt(zmq.SNDHWM, 60)
self.pub_sock.bind("tcp://*:5556")
time.sleep(0.2)
# Update request
self.request_sock = self.context.socket(zmq.ROUTER)
self.request_sock.setsockopt(zmq.IDENTITY, b'SERVER')
self.request_sock.setsockopt(zmq.RCVHWM, 60)
self.request_sock.bind("tcp://*:5555")
# Update collector
self.collector_sock = self.context.socket(zmq.PULL)
self.collector_sock.setsockopt(zmq.RCVHWM, 60)
self.collector_sock.bind("tcp://*:5557")
# poller for socket aggregation
self.poller = zmq.Poller()
self.poller.register(self.request_sock, zmq.POLLIN)
self.poller.register(self.collector_sock, zmq.POLLIN)
def tick(self):
logger.info("{} server launched".format(id))
while True:
# Non blocking poller
socks = dict(self.poller.poll(1000))
# Snapshot system for late join (Server - Client)
if self.request_sock in socks:
msg = self.request_sock.recv_multipart(zmq.DONTWAIT)
identity = msg[0]
request = msg[1]
print("asdasd")
if request == b"SNAPSHOT_REQUEST":
pass
else:
logger.info("Bad snapshot request")
break
for k, v in self.property_map.items():
logger.info(
"Sending {} snapshot to {}".format(k, identity))
self.request_sock.send(identity, zmq.SNDMORE)
v.send(self.request_sock)
msg_end_snapshot = message.RCFMessage(key="SNAPSHOT_END", id=identity)
self.request_sock.send(identity, zmq.SNDMORE)
msg_end_snapshot.send(self.request_sock)
logger.info("done")
# Regular update routing (Clients / Client)
elif self.collector_sock in socks:
msg = message.RCFMessage.recv(self.collector_sock)
# Update all clients
msg.store(self.property_map)
msg.send(self.pub_sock)
server = RCFServerAgent()

View File

@ -1,4 +1,4 @@
from net_components import RCFClient
from client import RCFClient
import time
client = RCFClient()

View File

@ -1,6 +1,6 @@
import bpy
from . import net_components, net_operators
from . import client, operators
class SessionSettingsPanel(bpy.types.Panel):
@ -19,7 +19,7 @@ class SessionSettingsPanel(bpy.types.Panel):
scene = context.scene
row = layout.row()
if net_operators.client is None:
if operators.client_instance is None:
row = layout.row()
box = row.box()
@ -57,12 +57,12 @@ class SessionSettingsPanel(bpy.types.Panel):
else:
if net_operators.client.agent.is_alive():
if operators.client_instance.agent.is_alive():
row.label(text="Net frequency:")
row.prop(net_settings, "update_frequency", text="")
row = layout.row()
row.operator("session.stop", icon='QUIT', text="Exit")
# elif net_operators.client.status is net_components.RCFStatus.CONNECTING:
# elif operators.client.status is client.RCFStatus.CONNECTING:
# row.label(text="connecting...")
# row = layout.row()
# row.operator("session.stop", icon='QUIT', text="CANCEL")
@ -80,8 +80,8 @@ class SessionUsersPanel(bpy.types.Panel):
@classmethod
def poll(cls, context):
if net_operators.client:
return net_operators.client.status == net_components.RCFStatus.CONNECTED
if operators.client_instance:
return operators.client_instance.status == client.RCFStatus.CONNECTED
return False
def draw(self, context):
@ -91,21 +91,21 @@ class SessionUsersPanel(bpy.types.Panel):
scene = context.scene
# Create a simple row.
row = layout.row()
if net_operators.client:
if len(net_operators.client.property_map) > 0:
for key, values in net_operators.client.property_map.items():
if operators.client_instance:
if len(operators.client_instance.property_map) > 0:
for key, values in operators.client_instance.property_map.items():
if 'client' in key:
info = ""
item_box = row.box()
detail_item_box = item_box.row()
if values.id == net_operators.client.id:
if values.id == operators.client_instance.id:
info = "(self)"
# detail_item_box = item_box.row()
detail_item_box.label(
text="{} - {}".format(values.id.decode(), info))
if net_operators.client.id.decode() not in key:
if operators.client.id.decode() not in key:
detail_item_box.operator(
"session.snapview", text="", icon='VIEW_CAMERA').target_client = values.id.decode()
row = layout.row()
@ -125,9 +125,9 @@ class SessionPropertiesPanel(bpy.types.Panel):
@classmethod
def poll(cls, context):
if net_operators.client:
return net_operators.client.agent.is_alive()
# return net_operators.client.status == net_components.RCFStatus.CONNECTED
if operators.client_instance:
return operators.client_instance.agent.is_alive()
# return operators.client.status == client.RCFStatus.CONNECTED
return False
def draw(self, context):
@ -139,7 +139,7 @@ class SessionPropertiesPanel(bpy.types.Panel):
# Create a simple row.
row = layout.row()
if net_operators.client:
if operators.client:
row = layout.row(align=True)
row.prop(net_settings, "buffer", text="")
row.prop(net_settings, "add_property_depth", text="")
@ -150,8 +150,8 @@ class SessionPropertiesPanel(bpy.types.Panel):
row = layout.row()
# Property area
area_msg = row.box()
# if len(net_operators.client.property_map) > 0:
# for key, values in net_operators.client.property_map.items():
# if len(operators.client.property_map) > 0:
# for key, values in operators.client.property_map.items():
# item_box = area_msg.box()
# detail_item_box = item_box.row()
# # detail_item_box = item_box.row()
@ -173,9 +173,9 @@ class SessionTaskPanel(bpy.types.Panel):
@classmethod
def poll(cls, context):
if net_operators.client:
return net_operators.client.agent.is_alive()
# return net_operators.client.status == net_components.RCFStatus.CONNECTED
if operators.client:
return operators.client.agent.is_alive()
# return operators.client.status == client.RCFStatus.CONNECTED
return False
def draw(self, context):
@ -183,11 +183,11 @@ class SessionTaskPanel(bpy.types.Panel):
# Create a simple row.
row = layout.row()
if net_operators.update_list:
if operators.update_list:
# Property area
area_msg = row.box()
if len(net_operators.update_list) > 0:
for key, values in net_operators.update_list.items():
if len(operators.update_list) > 0:
for key, values in operators.update_list.items():
item_box = area_msg.box()
detail_item_box = item_box.row()
# detail_item_box = item_box.row()