diff --git a/client.py b/client.py index f127f5a..9519317 100644 --- a/client.py +++ b/client.py @@ -59,7 +59,6 @@ class State(Enum): ACTIVE = 3 - class RCFClient(object): ctx = None pipe = None @@ -70,7 +69,7 @@ class RCFClient(object): self.pipe, peer = zpipe(self.ctx) self.queue = queue.Queue() self.agent = threading.Thread( - target=rcf_client_agent, args=(self.ctx, peer,self.queue)) + target=rcf_client_agent, args=(self.ctx, peer,self.queue),name="net-agent") self.agent.daemon = True self.agent.start() @@ -79,12 +78,12 @@ class RCFClient(object): id, str) else id), (address.encode() if isinstance( address, str) else address), b'%d' % port]) - def set(self, key): + def set(self, key, value=None): """Set new value in distributed hash table Sends [SET][key][value] to the agent """ self.pipe.send_multipart( - [b"SET", umsgpack.packb(key)]) + [b"SET", umsgpack.packb(key),( umsgpack.packb(value) if value else umsgpack.packb('None') )]) def get(self, key): """Lookup value in distributed hash table @@ -114,6 +113,7 @@ class RCFClient(object): else: return umsgpack.unpackb(reply[0]) + class RCFServer(object): address = None # Server address port = None # Server port @@ -125,8 +125,8 @@ class RCFServer(object): self.port = port self.snapshot = ctx.socket(zmq.DEALER) self.snapshot.linger = 0 - self.snapshot.connect("tcp://{}:{}".format(address.decode(), port)) self.snapshot.setsockopt(zmq.IDENTITY, id) + self.snapshot.connect("tcp://{}:{}".format(address.decode(), port)) self.subscriber = ctx.socket(zmq.SUB) self.subscriber.setsockopt_string(zmq.SUBSCRIBE, '') self.subscriber.connect("tcp://{}:{}".format(address.decode(), port+1)) @@ -158,7 +158,7 @@ class RCFClientAgent(object): self.publisher.linger = 0 self.serial, peer = zpipe(self.ctx) self.serial_agent = threading.Thread( - target=serialization_agent, args=(self.ctx, peer)) + target=serialization_agent, args=(self.ctx, peer), name="serial-agent") self.serial_agent.daemon = True self.serial_agent.start() @@ -182,7 +182,11 @@ class RCFClientAgent(object): elif command == b"SET": key = umsgpack.unpackb(msg[0]) value = None - value = helpers.dump(key) + + value = umsgpack.unpackb(msg[1]) + + if value == 'None': + value = helpers.dump(key) if value: logger.info("{} dumped".format(key)) @@ -195,9 +199,15 @@ class RCFClientAgent(object): logger.error("Fail to dump ") elif command == b"GET": + value = [] key = umsgpack.unpackb(msg[0]) - value = self.property_map.get(key) - self.pipe.send(umsgpack.packb(value.body) if value else b'') + for k in self.property_map.keys(): + if key in k: + value.append([k,self.property_map.get(k).body]) + + # value = [self.property_map.get(key) for key in keys] + # value = self.property_map.get(key) + self.pipe.send(umsgpack.packb(value) if value else b'') elif command == b"LIST": self.pipe.send(umsgpack.packb(list(self.property_map))) @@ -246,7 +256,7 @@ def rcf_client_agent(ctx, pipe,queue): if agent.state == State.SYNCING: # Store snapshot if rcfmsg.key == "SNAPSHOT_END": - # logger.info("snapshot complete") + logger.info("snapshot complete") agent.state = State.ACTIVE else: helpers.load(rcfmsg.key,rcfmsg.body) @@ -254,7 +264,8 @@ def rcf_client_agent(ctx, pipe,queue): elif agent.state == State.ACTIVE: if rcfmsg.id != agent.id: # update_queue.put((rcfmsg.key,rcfmsg.body)) - helpers.load(rcfmsg.key,rcfmsg.body) + with lock: + helpers.load(rcfmsg.key,rcfmsg.body) # logger.info("load") # agent.serial.send_multipart([b"LOAD", umsgpack.packb(rcfmsg.key), umsgpack.packb(rcfmsg.body)]) @@ -298,6 +309,7 @@ class SerializationAgent(object): key = umsgpack.unpackb(msg[0]) value = umsgpack.unpackb(msg[1]) + helpers.load(key,value) @@ -307,7 +319,6 @@ class SerializationAgent(object): def serialization_agent(ctx, pipe): agent = SerializationAgent(ctx, pipe) - global stop while True: if stop: @@ -325,3 +336,5 @@ def serialization_agent(ctx, pipe): if agent.pipe in items: agent.control_message() + + diff --git a/draw.py b/draw.py index 3dce04b..affc2f2 100644 --- a/draw.py +++ b/draw.py @@ -47,7 +47,7 @@ def get_client_view_rect(): v2 = get_target(region, rv3d, (width, height)) v4 = get_target(region, rv3d, (width, 0)) - coords = (v1, v2, v3, v4) + coords = [v1, v2, v3, v4] indices = ( (1, 3), (2, 1), (3, 0), (2, 0) ) @@ -105,6 +105,8 @@ class HUD(object): self.draw_items.clear() + clients = self.client.get("Client") + for key, values in self.client.property_map.items(): if 'net' in key and values.body is not None and values.id != self.client.id: if values.mtype == "clientObject": diff --git a/helpers.py b/helpers.py index 9f48241..17b3645 100644 --- a/helpers.py +++ b/helpers.py @@ -41,7 +41,9 @@ def load(key, value): elif target_type == 'Camera': load_default(target=target, data=value, create=True, type=target_type) - + elif target_type == 'Client': + pass + def resolve_bpy_path(path): """ diff --git a/message.py b/message.py index 2289d8a..9bc0d42 100644 --- a/message.py +++ b/message.py @@ -48,25 +48,23 @@ class RCFMessage(object): 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]) + socket.send_multipart([key, id, body]) except: - logger.info("Fail to send {} {}".format(key, id)) + print("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, id, 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) + return cls(key=key, id=id, body=body) def dump(self): if self.body is None: diff --git a/operators.py b/operators.py index d70b49e..3af8ab8 100644 --- a/operators.py +++ b/operators.py @@ -17,7 +17,7 @@ from bpy_extras import view3d_utils from gpu_extras.batch import batch_for_shader from . import client, ui, draw, helpers - +from .libs import umsgpack logger = logging.getLogger(__name__) @@ -85,21 +85,29 @@ def refresh_window(): def upload_client_instance_position(): global client_instance + username = bpy.context.scene.session_settings.username if client_instance: - key = "net/client_instances/{}".format(client_instance.id.decode()) - + + key = "Client/{}".format(username) + # key = "Object/Cube" + print(key) try: - 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_instance_color - data['color'] = (color.r, color.g, color.b, 1) - client_instance.push_update(key, 'client_instance', data) - elif current_coords[0] != data['location'][0]: - data['location'] = current_coords - client_instance.push_update(key, 'client_instance', data) + current_coords = draw.get_client_view_rect() + client = client_instance.get(key) + # print((client[0][1][b'location'])) + + + # if data is None: + # data = {} + # data['location'] = current_coords + # color = bpy.context.scene.session_settings.client_instance_color + # data['color'] = (color.r, color.g, color.b, 1) + # client_instance.push_update(key, 'client_instance', data) + if current_coords != client[0][1][b'location']: + print(current_coords) + print(client[0][1][b'location']) + client[0][1][b'location'] = current_coords + client_instance.set(key, client[0][1]) except: pass @@ -305,11 +313,11 @@ def default_tick(): # print("pull error: {}".format(e)) # bpy.ops.session.refresh() - global client_instance + # global client_instance - if not client_instance.queue.empty(): - update = client_instance.queue.get() - helpers.load(update[0],update[1]) + # if not client_instance.queue.empty(): + # update = client_instance.queue.get() + # helpers.load(update[0],update[1]) return 0.001 @@ -342,18 +350,18 @@ def material_tick(): def draw_tick(): # drawing - global drawer + # global drawer - drawer.draw() + # drawer.draw() # Upload upload_client_instance_position() - return 0.2 + return 1 def register_ticks(): # REGISTER Updaters - # bpy.app.timers.register(draw_tick) + bpy.app.timers.register(draw_tick) # bpy.app.timers.register(mesh_tick) # bpy.app.timers.register(object_tick) bpy.app.timers.register(default_tick) @@ -363,7 +371,7 @@ def unregister_ticks(): # REGISTER Updaters # global drawer # drawer.unregister_handlers() - # bpy.app.timers.unregister(draw_tick) + bpy.app.timers.unregister(draw_tick) # bpy.app.timers.unregister(mesh_tick) # bpy.app.timers.unregister(object_tick) bpy.app.timers.unregister(default_tick) @@ -446,6 +454,24 @@ class session_add_property(bpy.types.Operator): return {"FINISHED"} +class session_get_property(bpy.types.Operator): + bl_idname = "session.get_prop" + bl_label = "get" + bl_description = "broadcast a property to connected client_instances" + bl_options = {"REGISTER"} + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + global client_instance + + client_instance.get("client") + + return {"FINISHED"} + + class session_remove_property(bpy.types.Operator): bl_idname = "session.remove_prop" bl_label = "remove" @@ -592,6 +618,7 @@ classes = ( session_join, session_refresh, session_add_property, + session_get_property, session_stop, session_create, session_settings, @@ -642,6 +669,7 @@ def depsgraph_update(scene): # if updated_data.is_updated_transform: # add_update(updated_data.id.bl_rna.name, updated_data.id.name) # else: + if is_dirty(updates): for update in ordered(updates): if update[2] == "Master Collection": @@ -650,7 +678,7 @@ def depsgraph_update(scene): client_instance.set("{}/{}".format(update[1], update[2])) - if len(updates) is 1 and len(bpy.context.selected_objects)>0: + if len(bpy.context.selected_objects)>0: updated_data = updates[0] if updated_data.id.name == bpy.context.selected_objects[0].name: if updated_data.is_updated_transform or updated_data.is_updated_geometry: diff --git a/server.py b/server.py index 3293249..d1c82dd 100644 --- a/server.py +++ b/server.py @@ -8,7 +8,7 @@ import message logger = logging.getLogger("Server") logging.basicConfig(level=logging.DEBUG) -SUPPORTED_TYPES = [ 'Material', +SUPPORTED_TYPES = [ 'Client', 'Material', 'Texture', 'Light', 'Camera','Mesh', 'Grease Pencil', 'Object', 'Action', 'Armature','Collection', 'Scene'] class RCFServerAgent(): @@ -66,6 +66,17 @@ class RCFServerAgent(): request = msg[1] if request == b"SNAPSHOT_REQUEST": + client_key = "Client/{}".format(identity.decode()) + + if client_key not in self.property_map.keys(): + logger.info("New user:{}".format(identity.decode())) + + client_dict = {} + client_dict['location'] = [0,0,0] + client_dict['active_object'] = '' + client_store = message.RCFMessage(key=client_key, id=identity,body=client_dict) + logger.info(client_store) + client_store.store(self.property_map) pass else: logger.info("Bad snapshot request")