refactor: cleanup
This commit is contained in:
parent
cc4033978d
commit
1789a432a1
17
__init__.py
17
__init__.py
@ -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()
|
||||
|
||||
|
@ -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
274
helpers.py
Normal 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
1
libs/bsyncio
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 95275093b79d9289f939079550bb47ab25c1eacd
|
1
libs/esper
Submodule
1
libs/esper
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 5b6cd0c51718d5dcfa0e5613f824b5251cf092ac
|
84
message.py
Normal file
84
message.py
Normal 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,
|
||||
))
|
||||
|
@ -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):
|
87
server.py
87
server.py
@ -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()
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
from net_components import RCFClient
|
||||
from client import RCFClient
|
||||
import time
|
||||
client = RCFClient()
|
||||
|
||||
|
@ -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()
|
Loading…
x
Reference in New Issue
Block a user