/** * \brief This function tries to create link between parent and child. This * function does not check, if child had permission to be linked with parent * node. It has to be checked before this link. If link is successfully created, * then level of child node is updated. The link is not copied to the bucket! */ struct VSLink *vs_link_create(struct VSNode *parent, struct VSNode *child) { struct VSLink *link = NULL; assert(parent != NULL); assert(child != NULL); link = (struct VSLink*)malloc(sizeof(struct VSLink)); if( link != NULL ) { link->parent = parent; link->child = child; v_list_add_tail(&parent->children_links, link); child->parent_link = link; child->level = parent->level + 1; vs_node_inc_version(parent); vs_node_inc_version(child); } else { v_print_log(VRS_PRINT_WARNING, "Could not create link between %d and %d, not enough memory\n", parent->id, child->id); } return link; }
/** * \brief This function sends TagGroupCreate command to the client * * \param[in] *node_subscriber * \param[in] *node * \param[in] *tg * * \return This function return 1, when command was successfully created and * pushed to outgoing queue. When some error occur (already subscribed to * tag group, out of memory, etc.) then 0 is returned. When outgoing queue is * empty, then -1 is returned. */ int vs_taggroup_send_create(struct VSNodeSubscriber *node_subscriber, struct VSNode *node, struct VSTagGroup *tg) { struct VSession *vsession = node_subscriber->session; struct Generic_Cmd *taggroup_create_cmd; struct VSEntityFollower *taggroup_follower; /* Check if this tag group is in created/creating state */ if(!(tg->state == ENTITY_CREATING || tg->state == ENTITY_CREATED)) { v_print_log(VRS_PRINT_DEBUG_MSG, "This tag group: %d in node: %d is in %d state\n", tg->id, node->id, tg->state); return 0; } /* Check if this command, has not been already sent */ taggroup_follower = tg->tg_folls.first; while(taggroup_follower != NULL) { if(taggroup_follower->node_sub->session->session_id == vsession->session_id ) { v_print_log(VRS_PRINT_DEBUG_MSG, "Client already knows about this TagGroup: %d\n", tg->id); return 0; } taggroup_follower = taggroup_follower->next; } /* Create TagGroup create command */ taggroup_create_cmd = v_taggroup_create_create(node->id, tg->id, tg->custom_type); /* Put this command to the outgoing queue */ if(taggroup_create_cmd == NULL) { return 0; } if(v_out_queue_push_tail(vsession->out_queue, 0, node_subscriber->prio, taggroup_create_cmd) == 0) { return -1; } else { /* Add this session to the list of session, that knows about this * taggroup. Server could send them taggroup_destroy in the future. */ taggroup_follower = (struct VSEntityFollower*)calloc(1, sizeof(struct VSEntityFollower)); taggroup_follower->node_sub = node_subscriber; taggroup_follower->state = ENTITY_CREATING; v_list_add_tail(&tg->tg_folls, taggroup_follower); return 1; } return 0; }
/** * \brief This function sends Node_Create command to the subscriber of parent * node. */ int vs_node_send_create(struct VSNodeSubscriber *node_subscriber, struct VSNode *node, struct VSNode *avatar_node) { struct Generic_Cmd *node_create_cmd = NULL; struct VSEntityFollower *node_follower; /* Check if this node is in created/creating state */ if(!(node->state == ENTITY_CREATING || node->state == ENTITY_CREATED)) { v_print_log(VRS_PRINT_DEBUG_MSG, "This node: %d is in %d state\n", node->id, node->state); return 0; } /* Check if this command, has not been already sent */ node_follower = node->node_folls.first; while(node_follower != NULL) { if(node_follower->node_sub->session->session_id == node_subscriber->session->session_id && (node_follower->state == ENTITY_CREATING || node_follower->state == ENTITY_CREATED)) { v_print_log(VRS_PRINT_DEBUG_MSG, "Client already knows about node: %d\n", node->id); return 0; } node_follower = node_follower->next; } if(avatar_node != NULL){ node_create_cmd = v_node_create_create(node->id, avatar_node->id, node->owner->user_id, node->type); } else { node_create_cmd = v_node_create_create(node->id, node->parent_link->parent->id, node->owner->user_id, node->type); } if ( node_create_cmd != NULL && v_out_queue_push_tail(node_subscriber->session->out_queue, node_subscriber->prio, node_create_cmd) == 1) { /* Add this session to the list of session, that knows about this * node. Server could send them node_destroy in the future. */ node_follower = (struct VSEntityFollower*)calloc(1, sizeof(struct VSEntityFollower)); node_follower->node_sub = node_subscriber; node_follower->state = ENTITY_CREATING; v_list_add_tail(&node->node_folls, node_follower); return 1; } return 0; }
/** * \brief This function add TagCreate command to the queue of the session */ int vs_tag_send_create(struct VSEntitySubscriber *tg_subscriber, struct VSNode *node, struct VSTagGroup *tg, struct VSTag *tag) { struct Generic_Cmd *tag_create; struct VSEntityFollower *tag_follower; /* Check if this tag is in created/creating state */ if(!(tag->state == ENTITY_CREATING || tag->state == ENTITY_CREATED)) { v_print_log(VRS_PRINT_DEBUG_MSG, "This tag: %d in node: %d and tg: %d, is in %d state\n", tag->id, node->id, tg->id, tag->state); return 0; } /* Check if this command, has not been already sent */ tag_follower = tag->tag_folls.first; while(tag_follower != NULL) { if(tag_follower->node_sub->session == tg_subscriber->node_sub->session) { return 0; } tag_follower = tag_follower->next; } /* Create new Tag_Create command */ tag_create = v_tag_create_create(node->id, tg->id, tag->id, tag->data_type, tag->count, tag->custom_type); /* Put command to the outgoing queue */ if(tag_create == NULL) { return 0; } if(v_out_queue_push_tail(tg_subscriber->node_sub->session->out_queue, 0, tg_subscriber->node_sub->prio, tag_create) == 0) { return -1; } else { tag_follower = (struct VSEntityFollower*)calloc(1, sizeof(struct VSEntityFollower)); tag_follower->node_sub = tg_subscriber->node_sub; tag_follower->state = ENTITY_CREATING; v_list_add_tail(&tag->tag_folls, tag_follower); return 1; } return 0; }
int vs_layer_send_create(struct VSNodeSubscriber *node_subscriber, struct VSNode *node, struct VSLayer *layer) { struct VSession *vsession = node_subscriber->session; struct VSEntityFollower *layer_follower; struct Generic_Cmd *layer_create_cmd; /* TODO: this could be more effective */ /* Check if this command, has not been already sent */ layer_follower = layer->layer_folls.first; while(layer_follower != NULL) { if(layer_follower->node_sub->session->session_id == node_subscriber->session->session_id) { v_print_log(VRS_PRINT_DEBUG_MSG, "Client already knows about this Layer: %d\n", layer->id); return 0; } layer_follower = layer_follower->next; } if(layer->parent != NULL) { layer_create_cmd = v_layer_create_create(node->id, layer->parent->id, layer->id, layer->data_type, layer->num_vec_comp, layer->type); } else { layer_create_cmd = v_layer_create_create(node->id, RESERVED_LAYER_ID, layer->id, layer->data_type, layer->num_vec_comp, layer->type); } if(layer_create_cmd != NULL && v_out_queue_push_tail(vsession->out_queue, node_subscriber->prio, layer_create_cmd) == 1) { /* Add this session to the list of session, that knows about this * layer. Server could send them layer_destroy in the future. */ layer_follower = (struct VSEntityFollower*)calloc(1, sizeof(struct VSEntityFollower)); layer_follower->node_sub = node_subscriber; layer_follower->state = ENTITY_CREATING; v_list_add_tail(&layer->layer_folls, layer_follower); return 1; } return 0; }
/** * \brief This function creates new layer * * \param[in] *node The pointer at node, where layer will be created * \param[in] *parent The pointer at parent layer * \param[in] data_type The type of values stored in layer * \param[in] count The count of values stored in one layer item * \param[in] type The client defined type of layer * * \return This function returns pointer at new created layer */ struct VSLayer *vs_layer_create(struct VSNode *node, struct VSLayer *parent, uint8 data_type, uint8 count, uint16 type) { struct VSLayer *layer = calloc(1, sizeof(struct VSLayer)); if(layer == NULL) { return NULL; } layer->prev = NULL; layer->next = NULL; layer->id = node->last_layer_id; node->last_layer_id++; layer->data_type = data_type; layer->type = type; layer->parent = parent; layer->num_vec_comp = count; v_hash_array_init(&layer->values, HASH_MOD_65536, offsetof(VSLayerValue, id), sizeof(uint32)); /* When parent layer is not NULL, then add this layer to the linked list * of child layers */ if(parent != NULL) { v_list_add_tail(&parent->child_layers, layer); } /* Initialize linked list of child layers */ layer->child_layers.first = layer->child_layers.last = NULL; layer->layer_folls.first = layer->layer_folls.last = NULL; layer->layer_subs.first = layer->layer_subs.last = NULL; layer->state = ENTITY_RESERVED; return layer; }
/** * \brief This functions add empty packet to the history of the send packets. * \param[in] *history The structure with history of all sent packets and * commands. * \param[in] id The ID of packet, that will be add to the history of sent packets. * \return This function returns pointer at structure VSent_Packet. */ struct VSent_Packet *v_packet_history_add_packet(struct VPacket_History *history, uint32 id) { struct VSent_Packet *packet = NULL; packet = (struct VSent_Packet*)malloc(sizeof(struct VSent_Packet)); /* Check if memory for sent packet was allocated */ if(packet != NULL) { packet->cmds.first = NULL; packet->cmds.last = NULL; packet->id = id; v_print_log(VRS_PRINT_DEBUG_MSG, "Adding packet: %d to history\n", packet->id); v_list_add_tail(&history->packets, packet); } else { v_print_log(VRS_PRINT_DEBUG_MSG, "Unable to allocate enough memory for sent packet: %d\n", id); } return packet; }
/** * \brief This function add fake other users account to the list of user accounts */ int vs_add_other_users_account(struct VS_CTX *vs_ctx) { struct VSUser *other_users; int ret = 0; other_users = (struct VSUser*)calloc(1, sizeof(struct VSUser)); if(other_users != NULL) { other_users->username = strdup("others"); other_users->password = NULL; other_users->password_hash = NULL; other_users->user_id = VRS_OTHER_USERS_UID; other_users->realname = strdup("Other Users"); other_users->fake_user = 1; v_list_add_tail(&vs_ctx->users, other_users); vs_ctx->other_users = other_users; ret = 1; } return ret; }
/** * \brief This function handle node_subscribe command */ int vs_handle_taggroup_subscribe(struct VS_CTX *vs_ctx, struct VSession *vsession, struct Generic_Cmd *taggroup_subscribe) { struct VSNode *node; uint32 node_id = UINT32(taggroup_subscribe->data[0]); uint16 taggroup_id = UINT16(taggroup_subscribe->data[UINT32_SIZE]); int ret = 0; /* Try to find node */ if((node = vs_node_find(vs_ctx, node_id)) == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() node (id: %d) not found\n", __FUNCTION__, node_id); return 0; } pthread_mutex_lock(&node->mutex); /* Node has to be created */ if( vs_node_is_created(node) != 1 ) { goto end; } /* Is user owner of the node or can user read the node? */ if(vs_node_can_read(vsession, node) == 1) { struct VSNodeSubscriber *node_subscriber; struct VSTagGroup *tg; struct VSTag *tag; struct VSEntitySubscriber *tg_subscriber; struct VBucket *bucket; /* Try to find node subscriber */ node_subscriber = node->node_subs.first; while(node_subscriber != NULL) { if(node_subscriber->session == vsession) { break; } node_subscriber = node_subscriber->next; } /* Client has to be subscribed to the node first */ if(node_subscriber == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s(): client has to be subscribed to the node: %d before subscribing to the tag_group: %d\n", __FUNCTION__, node_id, taggroup_id); goto end; } /* Try to find TagGroup */ if( (tg = vs_taggroup_find(node, taggroup_id)) == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() tag_group (id: %d) in node (id: %d) not found\n", __FUNCTION__, taggroup_id, node_id); goto end; } /* Is Client already subscribed to this tag group? */ tg_subscriber = tg->tg_subs.first; while(tg_subscriber != NULL) { if(tg_subscriber->node_sub->session->session_id == vsession->session_id) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() client already subscribed to the tag_group (id: %d) in node (id: %d)\n", __FUNCTION__, taggroup_id, node_id); goto end; } tg_subscriber = tg_subscriber->next; } ret = 1; /* Add new subscriber to the list of tag group subscribers */ tg_subscriber = (struct VSEntitySubscriber*)malloc(sizeof(struct VSEntitySubscriber)); tg_subscriber->node_sub = node_subscriber; v_list_add_tail(&tg->tg_subs, tg_subscriber); /* Send tag create for all tags in this tag group * TODO: do not send all tags at once, when there is lot of tags in this * tag group. Implement this, when queue limits will be finished. */ bucket = tg->tags.lb.first; while(bucket != NULL) { tag = (struct VSTag*)bucket->data; if(tag->state == ENTITY_CREATING || tag->state == ENTITY_CREATED) { if(vs_tag_send_create(tg_subscriber, node, tg, tag) != 1) { ret = 0; } } bucket = bucket->next; } } else { v_print_log(VRS_PRINT_DEBUG_MSG, "%s(): user: %s doesn't have permissions to subscribe to taggroup: %d in node: %d\n", __FUNCTION__, ((struct VSUser *)vsession->user)->username, taggroup_id, node->id); } end: pthread_mutex_unlock(&node->mutex); return ret; }
/** * \brief This function handle changing link between nodes */ int vs_handle_link_change(struct VS_CTX *vs_ctx, struct VSession *vsession, struct Generic_Cmd *node_link) { struct VSUser *user = (struct VSUser*)vsession->user; struct VSNode *old_parent_node, *parent_node, *child_node; struct VSLink *link; struct VSNodeSubscriber *node_subscriber; struct VSEntityFollower *node_follower; uint32 parent_node_id = UINT32(node_link->data[0]); uint32 child_node_id = UINT32(node_link->data[UINT32_SIZE]); int i; /* Try to find child node */ if((child_node = vs_node_find(vs_ctx, child_node_id)) == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s():%d node (id: %d) not found\n", __FUNCTION__, __LINE__, child_node_id); return 0; } /* Child node has to be created */ if(! (child_node->state == ENTITY_CREATED || child_node->state == ENTITY_CREATING)) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s():%d node id: %d is not in NODE_CREATED state: %d\n", __FUNCTION__, __LINE__, child_node->id, child_node->state); return 0; } /* Is user owner of child node or can user write to child node? */ if(vs_node_can_write(vsession, child_node) != 1) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s():%d user: %s can't write to child node: %d (owner: %s)\n", __FUNCTION__, __LINE__, user->username, child_node->id, child_node->owner->username); return 0; } /* Old link */ link = child_node->parent_link; /* Old parent node */ old_parent_node = link->parent; /* Is user owner of old parent node or can user write to old parent node? */ if(vs_node_can_write(vsession, old_parent_node) != 1) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s():%d user: %s can't write to old parent node: %d (owner: %s)\n", __FUNCTION__, __LINE__, user->username, old_parent_node->id, old_parent_node->owner->username); return 0; } /* Try to find new parent node */ if((parent_node = vs_node_find(vs_ctx, parent_node_id)) == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s():%d node (id: %d) not found\n", __FUNCTION__, __LINE__, parent_node_id); return 0; } /* Parent node has to be created */ if(! (parent_node->state == ENTITY_CREATED || parent_node->state == ENTITY_CREATING)) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s():%d node (id: %d) is not in NODE_CREATED state: %d\n", __FUNCTION__, __LINE__, parent_node->id, parent_node->state); return 0; } /* Test if client doesn't want to recreate existing link */ if( parent_node == old_parent_node) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s():%d link between nodes (parent_id: %d) (child_id: %d) already exists\n", __FUNCTION__, __LINE__, parent_node->id, child_node->id); return 0; } /* Is user owner of parent node or can user write to parent node? */ if(vs_node_can_write(vsession, parent_node) != 1) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s():%d user: %s can't write to parent node: %d (owner: %s)\n", __FUNCTION__, __LINE__, user->username, parent_node->id, parent_node->owner->username); return 0; } /* Test, if new link could be created */ if(vs_link_test_nodes(parent_node, child_node) != 1) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s():%d node: %d can't be child of node: %d\n", __FUNCTION__, __LINE__, child_node->id, parent_node->id); return 0; } /* Remove link from old parent node */ v_list_rem_item(&old_parent_node->children_links, link); /* Add link to new parent node */ v_list_add_tail(&parent_node->children_links, link); link->parent = parent_node; /* Update child node internal properties according new parent node */ vs_link_update_child(parent_node, child_node); /* Update version in child node, parent node and old parent node */ vs_node_inc_version(parent_node); vs_node_inc_version(child_node); vs_node_inc_version(old_parent_node); /* Subscribers of old and new parent node will receive information about * changing link between nodes. Prevent double sending command Node_Link, * when clients are subscribed to both nodes. */ /* Set temporary value in all sessions */ for(i=0; i<vs_ctx->max_sessions; i++) { vs_ctx->vsessions[i]->tmp = 0; } /* Send Node_Link command to subscribers of old parent node and set * session temporary value */ node_subscriber = old_parent_node->node_subs.first; while(node_subscriber != NULL) { if(vs_node_can_read(node_subscriber->session, old_parent_node) == 1) { node_subscriber->session->tmp = 1; vs_link_change_send(node_subscriber, link); } node_subscriber = node_subscriber->next; } /* When client is subscribed to the new parent node and aware of child * node, then send to the client only node_link */ node_follower = child_node->node_folls.first; while(node_follower != NULL) { if(node_follower->node_sub->session->tmp != 1) { vs_link_change_send(node_follower->node_sub, link); node_follower->node_sub->session->tmp = 1; } node_follower = node_follower->next; } /* Send Node_Create command to subscribers of new parent node, when * subscribers were not subscribed to child node */ node_subscriber = parent_node->node_subs.first; while(node_subscriber != NULL) { if(node_subscriber->session->tmp != 1) { if(vs_node_can_read(node_subscriber->session, parent_node) == 1) { vs_node_send_create(node_subscriber, child_node, NULL); } } node_subscriber = node_subscriber->next; } return 1; }
/** * \brief This function add VQueueCommand to the priority queue */ static void _v_out_queue_command_add(struct VPrioOutQueue *prio_queue, uint8 flag, uint8 share_addr, struct VOutQueueCommand *queue_cmd, struct Generic_Cmd *cmd) { struct VOutQueueCommand *border_queue_cmd = NULL; /* Will be command added to then head or tail of queue? */ if(flag & OUT_QUEUE_ADD_TAIL) { border_queue_cmd = prio_queue->cmds.last; } else if(flag & OUT_QUEUE_ADD_HEAD) { border_queue_cmd = prio_queue->cmds.first; } /* Add command to the end of new priority queue. If needed * update counter of same commands in the queue */ if(border_queue_cmd != NULL) { /* Is ID of this command same as ID of the last command * in this priority queue? */ if( (share_addr == 1) && (border_queue_cmd->id == queue_cmd->id)) { struct Generic_Cmd *border_cmd = (struct Generic_Cmd *)border_queue_cmd->vbucket->data; /* Is the last command in this priority queue and the * first command with this ID? */ if(border_queue_cmd->counter == NULL) { /* Set initial number of commands with same ID */ border_queue_cmd->counter = (uint16*)malloc(sizeof(uint16)); *border_queue_cmd->counter = 1; /* Compute size of address that could be shared */ border_queue_cmd->share = (int8*)malloc(sizeof(int8)); *border_queue_cmd->share = v_cmd_cmp_addr(border_cmd, cmd, 0xFF); /* Allocate memory for length of compressed commands */ border_queue_cmd->len = (uint16*)malloc(sizeof(uint16)); *border_queue_cmd->len = v_cmds_len(border_cmd, *border_queue_cmd->counter, *border_queue_cmd->share, 0); } else if(*border_queue_cmd->share > 0) { /* Try to update size of address that could be shared */ *border_queue_cmd->share = v_cmd_cmp_addr(border_cmd, cmd, *border_queue_cmd->share); } /* Set up pointer */ queue_cmd->counter = border_queue_cmd->counter; queue_cmd->share = border_queue_cmd->share; queue_cmd->len = border_queue_cmd->len; /* Update values */ (*queue_cmd->counter)++; *queue_cmd->len = v_cmds_len(cmd, *queue_cmd->counter, *queue_cmd->share, *queue_cmd->len); } else { queue_cmd->counter = NULL; queue_cmd->share = NULL; queue_cmd->len = NULL; } } /* Will be command added to then head or tail of queue? */ if(flag & OUT_QUEUE_ADD_TAIL) { v_list_add_tail(&prio_queue->cmds, queue_cmd); } else if(flag & OUT_QUEUE_ADD_HEAD) { v_list_add_head(&prio_queue->cmds, queue_cmd); } }
static void cb_receive_node_create(const uint8 session_id, const uint32 node_id, const uint32 parent_id, const uint16 user_id, const uint16 custom_type) { #if NO_DEBUG_PRINT != 1 printf("%s() session_id: %d, node_id: %d, parent_id: %d, user_id: %d, custom_type: %d\n", __FUNCTION__, session_id, node_id, parent_id, user_id, custom_type); #endif if(user_id != ctx->verse.user_id) { return; } switch (custom_type) { case PARTICLE_SCENE_NODE: if(ctx->verse.particle_scene_node == NULL && parent_id == VRS_SCENE_PARENT_NODE_ID ) { /* Create node of particle scene */ ctx->verse.particle_scene_node = create_particle_scene_node(node_id); /* Add node to lookup table*/ lu_add_item(ctx->verse.lu_table, node_id, ctx->verse.particle_scene_node); /* Subscribe to this node */ vrs_send_node_subscribe(session_id, VRS_DEFAULT_PRIORITY, node_id, 0, 0); } break; case PARTICLE_SENDER_NODE: if(parent_id == ctx->verse.particle_scene_node->node_id && v_list_count_items(&ctx->verse.particle_scene_node->senders) < (int32)ctx->sender_count) { struct ParticleSenderNode *sender_node = create_particle_sender_node(ctx->verse.particle_scene_node, node_id); struct Particle_Sender *sender; /* Find unassigned sender */ sender = ctx->senders.first; while(sender != NULL) { if(sender->sender_node == NULL) { break; } sender = sender->next; } /* Set up references */ if(sender != NULL) { printf("Info: setting up references\n"); sender_node->sender = sender; sender->sender_node = sender_node; } else { printf("Error: no remaining free sender\n"); } /* Add node to lookup table*/ lu_add_item(ctx->verse.lu_table, node_id, sender_node); v_list_add_tail(&ctx->verse.particle_scene_node->senders, sender_node); vrs_send_node_subscribe(session_id, VRS_DEFAULT_PRIORITY, node_id, 0, 0); } break; } }
/** * \brief This function creates new node at verse server, when client sent * node_create command. */ static struct VSNode *vs_node_new(struct VS_CTX *vs_ctx, struct VSession *vsession, const uint16 type) { struct VSNode *node = NULL; struct VSNode find_node, *avatar_node; struct VSUser *owner; struct VBucket *bucket; struct VSNodePermission *perm; struct VSNodeSubscriber *node_subscriber; /* Try to find avatar node to be able to create initial link to * avatar node (initial parent of new created node) */ find_node.id = vsession->avatar_id; bucket = v_hash_array_find_item(&vs_ctx->data.nodes, &find_node); if(bucket != NULL) { avatar_node = (struct VSNode*)bucket->data; } else { v_print_log(VRS_PRINT_DEBUG_MSG, "vsession->avatar_id: %d not found\n", vsession->avatar_id); goto end; } /* Try to find owner of the new node */ if((owner = vs_user_find(vs_ctx, vsession->user_id)) == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "vsession->user_id: %d not found\n", vsession->user_id); goto end; } /* Try to create new verse node */ if( (node = vs_node_create(vs_ctx, avatar_node, owner, VRS_RESERVED_NODE_ID, type)) == NULL) { goto end; } /* Set initial state of this node */ node->state = ENTITY_CREATING; /* Find node representing fake user other_users */ if( vs_ctx->other_users != NULL) { /* Set access permissions for other users */ perm = (struct VSNodePermission *)calloc(1, sizeof(struct VSNodePermission)); perm->user = vs_ctx->other_users; /* TODO: implement default session permissions and use them, * when are available */ perm->permissions = vs_ctx->default_perm; v_list_add_tail(&node->permissions, perm); } /* Send node_create to all subscribers of avatar node data */ node_subscriber = avatar_node->node_subs.first; while(node_subscriber) { vs_node_send_create(node_subscriber, node, avatar_node); node_subscriber = node_subscriber->next; } end: return node; }
/** * \brief This function add session (client) to the list of clients that are * subscribed this node. */ static int vs_node_subscribe(struct VS_CTX *vs_ctx, struct VSession *vsession, struct VSNode *node, uint32 version) { struct VSNode *child_node; struct VSNodePermission *perm; struct VSNodeSubscriber *node_subscriber; struct VSLink *link; struct VBucket *bucket; struct VSTagGroup *tg; struct VSLayer *layer; int user_can_read = 0; /* Can user subscribe to this node? */ user_can_read = vs_node_can_read(vs_ctx, vsession, node); /* Add current session to the list of node subscribers */ node_subscriber = (struct VSNodeSubscriber*)calloc(1, sizeof(struct VSNodeSubscriber)); node_subscriber->session = vsession; node_subscriber->prio = VRS_DEFAULT_PRIORITY; v_list_add_tail(&node->node_subs, node_subscriber); /* TODO: send node_subscribe with version and commands with difference * between this version and current state, when versing will be supported */ if(version != 0) { v_print_log(VRS_PRINT_WARNING, "Version: %d != 0, versing is not supported yet\n", version); } /* Send node_perm commands to the new subscriber */ perm = node->permissions.first; while(perm != NULL) { vs_node_send_perm(node_subscriber, node, perm->user, perm->permissions); perm = perm->next; } /* If the node is locked, then send node_lock to the subscriber */ if(node->lock.session != NULL) { vs_node_send_lock(node_subscriber, node->lock.session, node); } /* If user doesn't have permission to subscribe to this node, then send * only node_perm command explaining, why user can't rest of this * node */ if(user_can_read == 0) { v_print_log(VRS_PRINT_DEBUG_MSG, "Insufficient permission to read content of the node: %d\n", node->id); return 0; } /* Send node_create of all child nodes of this node and corresponding * links */ link = node->children_links.first; while(link!=NULL) { child_node = link->child; vs_node_send_create(node_subscriber, child_node, NULL); link = link->next; } /* Send taggroup_create of all tag_groups in this node */ bucket = node->tag_groups.lb.first; while(bucket != NULL) { tg = (struct VSTagGroup*)bucket->data; if(tg->state == ENTITY_CREATING || tg->state == ENTITY_CREATED) { vs_taggroup_send_create(node_subscriber, node, tg); } bucket = bucket->next; } /* Send layer_create for all layers in this node */ bucket = node->layers.lb.first; while(bucket != NULL) { layer = (struct VSLayer*)bucket->data; if(layer->state == ENTITY_CREATING || layer->state == ENTITY_CREATED) { vs_layer_send_create(node_subscriber, node, layer); } bucket = bucket->next; } return 1; }
/** * \brief This function add command to the history of sent command * * When this new added command updates some recent command with * the same address, then this command is set as obsolete. Pointer at this * command is set to NULL in previous packet. * * \param[in] *history The structure containing history of sent packets * and commands * \param[in] *sent_packet The current packet * \param[in] *cmd The data of command * * \return This function returns 1, when command was added to history. * When it wasn't able to add command to history, then zero is returned. */ int v_packet_history_add_cmd(struct VPacket_History *history, struct VSent_Packet *sent_packet, struct Generic_Cmd *cmd, uint8 prio) { struct VSent_Command *sent_cmd; struct VBucket *vbucket; void *_cmd = (void*)cmd; uint8 cmd_id = cmd->id; uint16 cmd_size = v_cmd_struct_size(cmd); int ret = 0; /* Are duplications allowed for this type of commands? */ if(history->cmd_hist[cmd_id]->flag & REMOVE_HASH_DUPS) { /* Try to find command with the same address */ vbucket = v_hash_array_find_item(&history->cmd_hist[cmd_id]->cmds, _cmd); if(vbucket != NULL) { struct Generic_Cmd *obsolete_cmd = (struct Generic_Cmd*)vbucket->data; /* Bucket has to include not NULL pointer */ assert(vbucket->ptr!=NULL); assert(vbucket->data!=NULL); /* Debug print */ #if 0 v_print_log(VRS_PRINT_INFO, "Replacing obsolete command\n"); v_cmd_print(VRS_PRINT_INFO, obsolete_cmd); v_cmd_print(VRS_PRINT_INFO, cmd); #endif /* When old data are obsolete, then set pointer at command in old * command to the NULL (obsolete command would not be re-send) */ ((struct VSent_Command*)(vbucket->ptr))->vbucket = NULL; /* Remove data with the same key as cmd_data has from hashed linked * list */ ret = v_hash_array_remove_item(&history->cmd_hist[cmd_id]->cmds, vbucket->data); if(ret == 1) { /* Destroy original command */ v_cmd_destroy(&obsolete_cmd); } else { v_print_log(VRS_PRINT_DEBUG_MSG, "Could not remove obsolete command (id: %d) from history\n", cmd_id); ret = 0; } } } /* Add own command data to the hashed linked list */ vbucket = v_hash_array_add_item(&history->cmd_hist[cmd_id]->cmds, _cmd, cmd_size); if(vbucket != NULL) { /* Create new command */ sent_cmd = (struct VSent_Command*)malloc(sizeof(struct VSent_Command)); /* Check if it was possible to allocate enough memory for sent command */ if(sent_cmd != NULL) { sent_cmd->id = cmd_id; /* Add command to the linked list of sent packet */ v_list_add_tail(&sent_packet->cmds, sent_cmd); /* Set up pointer at command data */ sent_cmd->vbucket = vbucket; /* Set up pointer at owner of this command item to be able to obsolete * this command in future */ vbucket->ptr = (void*)sent_cmd; /* Store information about command priority. Lost commands should * be re-send with same priority*/ sent_cmd->prio = prio; ret = 1; } else { /* When memory wasn't allocated, then free bucket from hashed * linked list */ v_print_log(VRS_PRINT_ERROR, "Unable allocate enough memory for sent command\n"); ret = v_hash_array_remove_item(&history->cmd_hist[cmd_id]->cmds, _cmd); if(ret == 1) { v_cmd_destroy(_cmd); } ret = 0; } } else { v_print_log(VRS_PRINT_ERROR, "Unable to add command (id: %d) to packet history\n", cmd_id); ret = 0; } return ret; }
int vs_handle_layer_subscribe(struct VS_CTX *vs_ctx, struct VSession *vsession, struct Generic_Cmd *layer_subscribe_cmd) { struct VSNode *node; struct VSUser *user; struct VSLayer *layer; struct VSNodeSubscriber *node_subscriber; struct VSEntitySubscriber *layer_subscriber; struct VBucket *vbucket; struct VSLayerValue *value; uint32 node_id = UINT32(layer_subscribe_cmd->data[0]); uint16 layer_id = UINT16(layer_subscribe_cmd->data[UINT32_SIZE]); /* uint32 version = UINT32(layer_subscribe_cmd->data[UINT32_SIZE+UINT16_SIZE]); uint32 crc32 = UINT32(layer_subscribe_cmd->data[UINT32_SIZE+UINT16_SIZE+UINT32_SIZE]); */ /* Try to find node */ if((node = vs_node_find(vs_ctx, node_id)) == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() node (id: %d) not found\n", __FUNCTION__, node_id); return 0; } /* Node has to be created */ if(!(node->state == ENTITY_CREATED || node->state == ENTITY_CREATING)) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() node (id: %d) is not in CREATING or CREATED state: %d\n", __FUNCTION__, node->id, node->state); return 0; } /* Try to find user */ if((user = vs_user_find(vs_ctx, vsession->user_id)) == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() vsession->user_id: %d not found\n", __FUNCTION__, vsession->user_id); return 0; } /* User has to have permission to read the node */ if(vs_node_can_read(vs_ctx, vsession, node) != 1) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s(): user: %s can't read the node: %d\n", __FUNCTION__, user->username, node->id); return 0; } /* Try to find node subscriber */ node_subscriber = node->node_subs.first; while(node_subscriber != NULL) { if(node_subscriber->session == vsession) { break; } node_subscriber = node_subscriber->next; } /* Client has to be subscribed to the node first */ if(node_subscriber == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s(): client has to be subscribed to the node: %d before subscribing to the layer: %d\n", __FUNCTION__, node_id, layer_id); return 0; } /* Try to find layer */ if( (layer = vs_layer_find(node, layer_id)) == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() layer (id: %d) in node (id: %d) not found\n", __FUNCTION__, layer_id, node_id); return 0; } /* Layer has to be created */ if(! (layer->state == ENTITY_CREATING || layer->state == ENTITY_CREATED)) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() layer (id: %u) in node (id: %d) is not in CREATED state: %d\n", __FUNCTION__, layer->id, node->id, node->state); return 0; } /* Try to find layer subscriber (client can't be subscribed twice) */ layer_subscriber = layer->layer_subs.first; while(layer_subscriber != NULL) { if(layer_subscriber->node_sub->session->session_id == vsession->session_id) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() client already subscribed to the layer (id: %d) in node (id: %d)\n", __FUNCTION__, layer_id, node_id); return 0; } layer_subscriber = layer_subscriber->next; } /* Add new subscriber to the list of layer subscribers */ layer_subscriber = (struct VSEntitySubscriber*)malloc(sizeof(struct VSEntitySubscriber)); layer_subscriber->node_sub = node_subscriber; v_list_add_tail(&layer->layer_subs, layer_subscriber); /* Send value set for all items in this layer * TODO: do not push all values to outgoing queue at once, when there is lot * of values in this layer. Implement this, when queue limits will be * finished. */ vbucket = layer->values.lb.first; while(vbucket != NULL) { value = (struct VSLayerValue*)vbucket->data; vs_layer_send_set_value(layer_subscriber, node, layer, value); vbucket = vbucket->next; } return 1; }
static void cb_receive_node_create(const uint8 session_id, const uint32 node_id, const uint32 parent_id, const uint16 user_id, const uint16 custom_type) { struct ParticleSenderNode *sender_node; struct Particle_Sender *sender; #if NO_DEBUG_PRINT != 1 printf("%s() session_id: %d, node_id: %d, parent_id: %d, user_id: %d, custom_type: %d\n", __FUNCTION__, session_id, node_id, parent_id, user_id, custom_type); #endif if(user_id != ctx->verse.user_id) { return; } if(parent_id == ctx->verse.avatar_id) { switch(custom_type) { case PARTICLE_SCENE_NODE: if(ctx->verse.particle_scene_node == NULL) { /* Create node of particle scene */ ctx->verse.particle_scene_node = create_particle_scene_node(node_id); /* Add node to lookup table*/ lu_add_item(ctx->verse.lu_table, node_id, ctx->verse.particle_scene_node); vrs_send_node_link(session_id, VRS_DEFAULT_PRIORITY, VRS_SCENE_PARENT_NODE_ID, node_id); vrs_send_node_subscribe(session_id, VRS_DEFAULT_PRIORITY, node_id, 0, 0); vrs_send_taggroup_create(session_id, VRS_DEFAULT_PRIORITY, node_id, PARTICLE_SCENE_TG); } break; case PARTICLE_SENDER_NODE: if(v_list_count_items(&ctx->verse.particle_scene_node->senders) < (int32)ctx->sender_count) { sender_node = create_particle_sender_node(ctx->verse.particle_scene_node, node_id); /* Find unassigned sender */ sender = ctx->senders.first; while(sender != NULL) { if(sender->sender_node == NULL) { break; } sender = sender->next; } /* Create reference */ if(sender != NULL) { sender->sender_node = sender_node; sender_node->sender = sender; } /* Is it sender node created by this client */ if(parent_id == ctx->verse.avatar_id) { ctx->sender = sender; } /* Add node to lookup table*/ lu_add_item(ctx->verse.lu_table, node_id, sender_node); v_list_add_tail(&ctx->verse.particle_scene_node->senders, sender_node); vrs_send_node_subscribe(session_id, VRS_DEFAULT_PRIORITY, node_id, 0, 0); vrs_send_node_link(session_id, VRS_DEFAULT_PRIORITY, ctx->verse.particle_scene_node->node_id, node_id); vrs_send_taggroup_create(session_id, VRS_DEFAULT_PRIORITY, node_id, PARTICLE_SENDER_TG); vrs_send_layer_create(session_id, VRS_DEFAULT_PRIORITY, node_id, 0xFFFF, VRS_VALUE_TYPE_REAL32, 3, PARTICLE_POS_LAYER); } break; } } }
/** * \brief This function handle node_subscribe command */ int vs_handle_taggroup_subscribe(struct VS_CTX *vs_ctx, struct VSession *vsession, struct Generic_Cmd *taggroup_subscribe) { struct VSNode *node; uint32 node_id = UINT32(taggroup_subscribe->data[0]); uint16 taggroup_id = UINT16(taggroup_subscribe->data[UINT32_SIZE]); int ret = 0; /* Try to find node */ if((node = vs_node_find(vs_ctx, node_id)) == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() node (id: %d) not found\n", __func__, node_id); return 0; } pthread_mutex_lock(&node->mutex); /* Node has to be created */ if( vs_node_is_created(node) != 1 ) { goto end; } /* Is user owner of the node or can user read the node? */ if(vs_node_can_read(vsession, node) == 1) { struct VSNodeSubscriber *node_subscriber; struct VSTagGroup *tg; struct VSTag *tag; struct VSEntitySubscriber *tg_subscriber; struct VBucket *bucket; /* Try to find node subscriber */ node_subscriber = node->node_subs.first; while(node_subscriber != NULL) { if(node_subscriber->session == vsession) { break; } node_subscriber = node_subscriber->next; } /* Client has to be subscribed to the node first */ if(node_subscriber == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s(): client has to be subscribed to the node: %d before subscribing to the tag_group: %d\n", __func__, node_id, taggroup_id); goto end; } /* Try to find TagGroup */ if( (tg = vs_taggroup_find(node, taggroup_id)) == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() tag_group (id: %d) in node (id: %d) not found\n", __func__, taggroup_id, node_id); goto end; } /* Is Client already subscribed to this tag group? */ tg_subscriber = tg->tg_subs.first; while(tg_subscriber != NULL) { if(tg_subscriber->node_sub->session->session_id == vsession->session_id) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() client already subscribed to the tag_group (id: %d) in node (id: %d)\n", __func__, taggroup_id, node_id); goto end; } tg_subscriber = tg_subscriber->next; } ret = 1; /* Add new subscriber to the list of tag group subscribers */ tg_subscriber = (struct VSEntitySubscriber*)malloc(sizeof(struct VSEntitySubscriber)); tg_subscriber->node_sub = node_subscriber; v_list_add_tail(&tg->tg_subs, tg_subscriber); /* Try to send tag_create for all tags in this tag group */ bucket = tg->tags.lb.first; while(bucket != NULL) { tag = (struct VSTag*)bucket->data; vs_tag_send_create(tg_subscriber, node, tg, tag); /* When TCP/WebSocket is used and tag is initialized, then it * is possible (necessary) to send value too. */ if( ((vsession->flags & VRS_TP_TCP) || (vsession->flags & VRS_TP_WEBSOCKET)) && (tag->flag & TAG_INITIALIZED)) { vs_tag_send_set(vsession, node_subscriber->prio, node, tg, tag); } bucket = bucket->next; } } else { v_print_log(VRS_PRINT_DEBUG_MSG, "%s(): user: %s doesn't have permissions to subscribe to taggroup: %d in node: %d\n", __func__, ((struct VSUser *)vsession->user)->username, taggroup_id, node->id); } end: pthread_mutex_unlock(&node->mutex); return ret; }