part 1 of cleaning up my little array macro library to be a formal API. also removed...
[blender.git] / source / blender / blenkernel / intern / verse_method.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * Contributor(s): Nathan Letwory. 
24  *
25  * ***** END GPL/BL DUAL LICENSE BLOCK *****
26  */
27
28 #ifdef WITH_VERSE
29
30 #include <string.h>
31
32 #include "MEM_guardedalloc.h"
33
34 #include "DNA_listBase.h"
35 #include "DNA_userdef_types.h"
36 #include "DNA_text_types.h"
37
38 #include "BLI_dynamiclist.h"
39 #include "BLI_blenlib.h"
40 #include "BLI_arithb.h"
41
42 #include "BIF_verse.h"
43
44 #include "BKE_bad_level_calls.h"
45 #include "BKE_library.h"
46 #include "BKE_text.h"
47 #include "BKE_verse.h"
48 #include "BKE_global.h"
49 #include "BKE_main.h"
50
51 #include "verse.h"
52
53 /* helper struct for creating method descriptions */
54 typedef struct VMethodInfo {
55         const char *name;
56         uint8 param_count;
57         const VNOParamType param_type[4];
58         const char *param_name[4];
59         uint16 id;
60 } VMethodInfo;
61
62 #ifdef VERSECHAT
63 /* array with methods for verse chat */
64 static VMethodInfo vmethod_info[] = {
65         { "join", 1, { VN_O_METHOD_PTYPE_STRING }, { "channel"}},
66         { "leave", 1, { VN_O_METHOD_PTYPE_STRING }, { "channel"}},
67         { "hear", 3, { VN_O_METHOD_PTYPE_STRING, VN_O_METHOD_PTYPE_STRING, VN_O_METHOD_PTYPE_STRING }, { "channel", "from", "msg"}}
68 };
69 #endif
70
71 /* lookup a method group based on its name */
72 struct VMethodGroup *lookup_vmethodgroup_name(ListBase *lb, const char *name) {
73         struct VMethodGroup *vmg;
74
75         for(vmg= lb->first; vmg; vmg= vmg->next)
76                 if(strcmp(vmg->name,name)==0) break;
77         
78         return vmg;
79 }
80
81 /* lookup a method group based on its group_id */
82 struct VMethodGroup *lookup_vmethodgroup(ListBase *lb, uint16 group_id) {
83         struct VMethodGroup *vmg;
84
85         for(vmg= lb->first; vmg; vmg= vmg->next)
86                 if(vmg->group_id==group_id) break;
87         
88         return vmg;
89 }
90
91 /* lookup a method based on its name */
92 struct VMethod *lookup_vmethod_name(ListBase *lb, const char *name) {
93         struct VMethod *vm;
94         for(vm= lb->first; vm; vm= vm->next)
95                 if(strcmp(vm->name,name)==0) break;
96
97         return vm;
98 }
99
100 /* lookup a method based on its method_id */
101 struct VMethod *lookup_vmethod(ListBase *lb, uint8 method_id) {
102         struct VMethod *vm;
103         for(vm= lb->first; vm; vm= vm->next)
104                 if(vm->id==method_id) break;
105
106         return vm;
107 }
108
109 #ifdef VERSECHAT
110 /*
111  * send say command
112  */
113 void send_say(const char *chan, const char *utter)
114 {
115         struct VerseSession *session = (VerseSession*)current_verse_session();
116         struct VNode *vnode;
117         struct VMethodGroup *vmg;
118         struct VMethod *vm;
119         VNOPackedParams *utterpack;
120         VNOParam args[2];
121         
122         vnode= (VNode *)(session->nodes.lb.first);
123
124         for( ; vnode; vnode= vnode->next) {
125                 if(strcmp(vnode->name, "tawksrv")==0) {
126                         vmg= lookup_vmethodgroup_name(&(vnode->methodgroups), "tawk");
127                         if(!vmg) break;
128                         vm= lookup_vmethod_name(&(vmg->methods), "say");
129                         if(!vm) break;
130                         args[0].vstring= (char *)chan;
131                         args[1].vstring= (char *)utter;
132                         if((utterpack= verse_method_call_pack(vm->param_count, vm->param_type, args))!=NULL) {
133                                 verse_send_o_method_call(vnode->id, vmg->group_id, vm->id, vnode->session->avatar, utterpack);
134                         }
135                         break;
136                 }
137
138         }
139 }
140
141 /*
142  * send logout command
143  */
144 void send_logout(VNode *vnode)
145 {
146         struct VMethodGroup *vmg;
147         struct VMethod *vm;
148         VNOPackedParams *pack;
149
150         vnode->chat_flag = CHAT_LOGGED;
151         vmg= lookup_vmethodgroup_name(&(vnode->methodgroups), "tawk");
152         if(!vmg) return;
153         vm= lookup_vmethod_name(&(vmg->methods), "logout");
154         if(!vm) return;
155
156         if((pack= verse_method_call_pack(vm->param_count, vm->param_type, NULL))!=NULL) {
157                 verse_send_o_method_call(vnode->id, vmg->group_id, vm->id, vnode->session->avatar, pack);
158         }
159         vnode->chat_flag = CHAT_NOTLOGGED;
160 }
161
162 /*
163  * send join command
164  */
165 void send_join(VNode *vnode, const char *chan)
166 {
167         struct VMethodGroup *vmg;
168         struct VMethod *vm;
169         VNOPackedParams *join;
170         VNOParam channel[1];
171
172         vmg= lookup_vmethodgroup_name(&(vnode->methodgroups), "tawk");
173         if(!vmg) return;
174         vm= lookup_vmethod_name(&(vmg->methods), "join");
175         if(!vm) return;
176
177         channel[0].vstring= (char *)chan;
178         if((join= verse_method_call_pack(vm->param_count, vm->param_type, channel))!=NULL) {
179                 verse_send_o_method_call(vnode->id, vmg->group_id, vm->id, vnode->session->avatar, join);
180         }
181 }
182
183 /*
184  * send leave command
185  */
186 void send_leave(VNode *vnode, const char *chan)
187 {
188         struct VMethodGroup *vmg;
189         struct VMethod *vm;
190         VNOPackedParams *leave;
191         VNOParam channel[1];
192
193         vmg= lookup_vmethodgroup_name(&(vnode->methodgroups), "tawk");
194         if(!vmg) return;
195         vm= lookup_vmethod_name(&(vmg->methods), "leave");
196         if(!vm) return;
197
198         channel[0].vstring= (char *)chan;
199         if((leave= verse_method_call_pack(vm->param_count, vm->param_type, channel))!=NULL) {
200                 verse_send_o_method_call(vnode->id, vmg->group_id, vm->id, vnode->session->avatar, leave);
201         }
202 }
203
204 /*
205  * send login command
206  */
207 void send_login(VNode *vnode)
208 {
209         struct VMethodGroup *vmg;
210         struct VMethod *vm;
211         VNOPackedParams *login;
212         VNOParam param[1];
213
214         vnode->chat_flag = CHAT_LOGGED;
215         vmg= lookup_vmethodgroup_name(&(vnode->methodgroups), "tawk");
216         if(!vmg) return;
217         vm= lookup_vmethod_name(&(vmg->methods), "login");
218         if(!vm) return;
219
220         param[0].vstring= U.verseuser;
221
222         if((login= verse_method_call_pack(vm->param_count, vm->param_type, param))!=NULL) {
223                 verse_send_o_method_call(vnode->id, vmg->group_id, vm->id, vnode->session->avatar, login);
224         }
225         vnode->chat_flag = CHAT_LOGGED;
226
227         vnode= lookup_vnode(vnode->session, vnode->session->avatar);
228         vmg= lookup_vmethodgroup_name(&(vnode->methodgroups), "tawk-client");
229         if(!vmg)
230                 verse_send_o_method_group_create(vnode->session->avatar, ~0, "tawk-client");
231 }
232 #endif
233
234 /*
235  * Free a VMethod
236  */
237 void free_verse_method(VMethod *vm) {
238         if(!vm) return;
239
240         MEM_freeN(vm->param_type);
241 }
242
243 /*
244  * Free methods for VMethodGroup
245  */
246 void free_verse_methodgroup(VMethodGroup *vmg)
247 {
248         struct VMethod *vm, *tmpvm;
249
250         if(!vmg) return;
251
252         vm= vmg->methods.first;
253         while(vm) {
254                 tmpvm=vm->next;
255                 free_verse_method(vm);
256                 vm= tmpvm;
257         }
258         BLI_freelistN(&(vmg->methods));
259 }
260
261 /* callback for method group creation */
262 static void cb_o_method_group_create(
263                 void *user_data,
264                 VNodeID node_id,
265                 uint16 group_id,
266                 const char *name)
267 {
268         struct VerseSession *session = (VerseSession*)current_verse_session();
269         struct VNode *vnode;
270         struct VMethodGroup *vmg;
271
272         if(!session) return;
273
274         vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id);
275
276         vmg = lookup_vmethodgroup(&(vnode->methodgroups), group_id);
277         
278         /* create method group holder in node node_id */
279         if(!vmg) {
280                 vmg= MEM_mallocN(sizeof(VMethodGroup), "VMethodGroup");
281                 vmg->group_id = group_id;
282                 vmg->methods.first = vmg->methods.last = NULL;
283                 BLI_addtail(&(vnode->methodgroups), vmg);
284                 printf("new method group with name %s (group_id %d) for node %u created\n", name, group_id, node_id);
285         }
286
287         /* this ensures name of an existing group gets updated, in case it is changed */
288         BLI_strncpy(vmg->name, (char *)name, 16);
289
290         /* subscribe to method group */
291         verse_send_o_method_group_subscribe(node_id, group_id);
292
293 #ifdef VERSECHAT
294         /* if this is our own method group, register our methods */
295         if(node_id==session->avatar) {
296                 verse_send_o_method_create(node_id, group_id, (uint8)~0u, vmethod_info[0].name,
297                                 vmethod_info[0].param_count,
298                                 (VNOParamType *)vmethod_info[0].param_type,
299                                 (const char **)vmethod_info[0].param_name);
300                 b_verse_update();
301                 verse_send_o_method_create(node_id, group_id, (uint8)~0u, vmethod_info[1].name,
302                                 vmethod_info[1].param_count,
303                                 (VNOParamType *)vmethod_info[1].param_type,
304                                 (const char **)vmethod_info[1].param_name);
305                 b_verse_update();
306                 verse_send_o_method_create(node_id, group_id, (uint8)~0u, vmethod_info[2].name,
307                                 vmethod_info[2].param_count,
308                                 (VNOParamType *)vmethod_info[2].param_type,
309                                 (const char **)vmethod_info[2].param_name);
310                 b_verse_update();
311         }
312 #endif
313 }
314
315 /* callback for method group destruction */
316 static void cb_o_method_group_destroy(
317                 void *user_data,
318                 VNodeID node_id,
319                 uint16 group_id,
320                 const char *name)
321 {
322         struct VerseSession *session = (VerseSession*)current_verse_session();
323         struct VNode *vnode;
324         struct VMethodGroup *vmg;
325         struct VMethod *vm;
326
327         printf("method group %d destroyed\n", group_id);
328
329         if(!session) return;
330
331         vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id);
332         for(vmg= vnode->methodgroups.first; vmg; vmg= vmg->next)
333                 if(vmg->group_id==group_id) break;
334
335         if(!vmg) return; /* method group doesn't exist? */
336
337         vmg->group_id = 0;
338         vmg->name[0] = '\0';
339         vm= vmg->methods.first;
340         while(vm) {
341                 /* free vm */
342                 
343         }
344
345         /* TODO: unsubscribe from method group */
346         BLI_remlink(&(vnode->methodgroups),vmg);
347         MEM_freeN(vmg);
348 }
349
350 /* callback for method creation */
351 static void cb_o_method_create(
352                 void *user_data,
353                 VNodeID node_id,
354                 uint16 group_id,
355                 uint16 method_id,
356                 const char *name,
357                 uint8 param_count,
358                 const VNOParamType *param_type,
359                 const char *param_name[])
360 {
361         struct VerseSession *session = (VerseSession*)current_verse_session();
362         struct VNode *vnode;
363         struct VMethodGroup *vmg;
364         struct VMethod *vm;
365         unsigned int size;
366         unsigned int i;
367         char *put;
368
369         if(!session) return;
370
371         vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id);
372
373         vmg= lookup_vmethodgroup((&vnode->methodgroups), group_id);
374
375         if(!vmg) return;
376
377         vm= lookup_vmethod((&vmg->methods), method_id);
378
379         if(!vm) {
380                 vm= MEM_mallocN(sizeof(VMethod), "VMethod");
381                 vm->id= method_id;
382                 vm->param_count= param_count;
383                 size= param_count* (sizeof(*vm->param_type) + sizeof(*vm->param_name));
384                 for(i= 0; i <param_count; i++) {
385                         size+=strlen(param_name[i])+1;
386                 }
387                 vm->param_type= MEM_mallocN(size, "param_type and param_name");
388                 memcpy(vm->param_type, param_type, sizeof(VNOParamType)*param_count);
389                 vm->param_name= (char **)(vm->param_type + param_count);
390                 put= (char *)(vm->param_name + param_count);
391                 for(i= 0; i < param_count; i++) {
392                         vm->param_name[i]= put;
393                         strcpy(put, param_name[i]);
394                         put += strlen(param_name[i]) + 1;
395                 }
396
397                 BLI_addtail(&(vmg->methods), vm);
398 #ifdef VERSECHAT
399                 if(strcmp(vmethod_info[0].name, name)==0) {
400                         vmethod_info[0].id = method_id;
401                 }
402 #endif
403                 printf("method %s in group %d of node %u created\n", name, group_id, node_id);
404         }
405
406         BLI_strncpy(vm->name, (char *)name, 500);
407 }
408
409 /* callback for method destruction */
410 static void cb_o_method_destroy(
411                 void *user_data,
412                 VNodeID node_id,
413                 uint16 group_id,
414                 uint16 method_id,
415                 const char *name,
416                 uint8 param_count,
417                 const VNOParamType *param_type,
418                 const char *param_name[])
419 {
420         struct VerseSession *session = (VerseSession*)current_verse_session();
421         struct VNode *vnode;
422         struct VMethodGroup *vmg;
423         struct VMethod *vm;
424
425         if(!session) return;
426
427         vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id);
428         for(vmg= vnode->methodgroups.first; vmg; vmg= vmg->next)
429                 if(vmg->group_id==group_id) break;
430
431         if(!vmg) return; /* method group doesn't exist? */
432
433         for(vm= vmg->methods.first; vm; vm= vm->next)
434                 if(vm->id==method_id) break;
435
436         if(!vm) return;
437
438         BLI_remlink(&(vmg->methods), vm);
439         MEM_freeN(vm->param_type);
440         MEM_freeN(vm);
441 }
442
443 /* callback for method calls */
444 static void cb_o_method_call(void *user_data, VNodeID node_id, uint8 group_id, uint8 method_id, VNodeID sender, VNOPackedParams *params)
445 {
446         struct VerseSession *session = (VerseSession*)current_verse_session();
447         struct VNode *vnode;
448         struct VMethodGroup *vmg;
449         struct VMethod *vm;
450         Text *text;
451         int method_idx= -1;
452
453         VNOParam arg[3];
454
455         if(!session) return;
456
457         if(session->avatar!=node_id) return;
458
459         vnode = BLI_dlist_find_link(&(session->nodes), (unsigned int)node_id);
460         vmg= lookup_vmethodgroup(&(vnode->methodgroups), group_id);
461         if(!vmg) return;
462
463         vm= lookup_vmethod(&(vmg->methods), method_id);
464         if(!vm) return;
465 #ifdef VERSECHAT
466         if(strcmp(vm->name, "join")==0) method_idx=0;
467         if(strcmp(vm->name, "leave")==0) method_idx=1;
468         if(strcmp(vm->name, "hear")==0) method_idx=2;
469         if(method_idx>-1)
470                 verse_method_call_unpack(params, vmethod_info[method_idx].param_count, vmethod_info[method_idx].param_type, arg);
471
472         switch(method_idx) {
473                 case 0:
474                         printf("Joining channel %s\n",arg[0].vstring);
475                         text=add_empty_text();
476                         text->flags |= TXT_ISCHAT;
477                         rename_id(&(text->id), arg[0].vstring);
478                         break;
479                 case 1:
480                         printf("Leaving channel %s\n",arg[0].vstring);
481                         break;
482                 case 2:
483                         {
484                                 ListBase lb = G.main->text;
485                                 ID *id= (ID *)lb.first;
486                                 char showstr[1024];
487                                 showstr[0]='\0';
488                                 text = NULL;
489                                 sprintf(showstr, "%s: %s\n", arg[1].vstring, arg[2].vstring);
490                                 for(; id; id= id->next) {
491                                         if(strcmp(id->name+2, arg[0].vstring)==0 && strcmp(arg[0].vstring, "#server")!=0) {
492                                                 text = (Text *)id;
493                                                 break;
494                                         }
495                                 }
496                                 if(text) {
497                                         txt_insert_buf(text, showstr);
498                                         txt_move_eof(text, 0);
499                                         allqueue(REDRAWCHAT, 0);
500                                 } else {
501                                         printf("%s> %s: %s\n",arg[0].vstring, arg[1].vstring, arg[2].vstring);
502                                 }
503                         }
504                         break;
505         }
506 #endif
507 }
508
509 void set_method_callbacks(void)
510 {
511         /* create and destroy method groups */
512         verse_callback_set(verse_send_o_method_group_create, cb_o_method_group_create, NULL);
513         verse_callback_set(verse_send_o_method_group_destroy, cb_o_method_group_destroy, NULL);
514
515         /* create and destroy methods */
516         verse_callback_set(verse_send_o_method_create, cb_o_method_create, NULL);
517         verse_callback_set(verse_send_o_method_destroy, cb_o_method_destroy, NULL);
518
519         /* call methods */
520         verse_callback_set(verse_send_o_method_call, cb_o_method_call, NULL);
521 }
522
523 #endif