Add Verse master-server functionality
[blender.git] / source / blender / blenkernel / intern / verse_session.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): Jiri Hnidek.
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_mesh_types.h"     /* temp */
35 #include "DNA_listBase.h"
36 #include "DNA_screen_types.h"
37 #include "DNA_userdef_types.h"
38
39 #include "BLI_dynamiclist.h"
40 #include "BLI_blenlib.h"
41
42 #include "BIF_verse.h"
43
44 #include "BKE_global.h" 
45 #include "BKE_verse.h"
46
47 struct ListBase session_list={NULL, NULL};
48 struct ListBase server_list={NULL, NULL};
49
50 static cb_ping_registered = 0;
51
52 /* list of static function prototypes */
53 static void cb_connect_terminate(const char *address, const char *bye);
54 static void cb_connect_accept(void *user_data, uint32 avatar, void *address, void *connection, const uint8 *host_id);
55 static void set_all_callbacks(void);
56 static void free_verse_session_data(struct VerseSession *session);
57 static void add_verse_server(VMSServer *server);
58 static void check_connection_state(struct VerseServer *server);
59
60 static void check_connection_state(struct VerseServer *server)
61 {
62         struct VerseSession *session;
63         session = session_list.first;
64         while(session) {
65                 if(strcmp(server->ip,session->address)==0) {
66                         server->flag = session->flag;
67                         return;
68                 }
69                 session = session->next;
70         }
71 }
72 /*
73  * add verse server to server_list. Prevents duplicate
74  * entries
75  */
76 static void add_verse_server(VMSServer *server)
77 {
78         struct VerseServer *iter, *niter;
79         VerseServer *newserver;
80         const char *name = verse_ms_field_value(server, "DE");
81         iter = server_list.first;
82
83         while(iter) {
84                 niter = iter->next;
85                 if(strcmp(iter->ip, server->ip)==0) {
86                         return;
87                 }
88                 iter = niter;
89         }
90
91         newserver = (VerseServer *)MEM_mallocN(sizeof(VerseServer), "VerseServer");
92         newserver->ip = (char *)MEM_mallocN(sizeof(char)*(strlen(server->ip)+1), "VerseServer ip");
93         strcpy(newserver->ip, server->ip);
94
95         if(name) {
96                 newserver->name = (char *)MEM_mallocN(sizeof(char)*(strlen(name)+strlen(newserver->ip)+4), "VerseServer name");
97                 strcpy(newserver->name, name);
98                 strcat(newserver->name, " (");
99                 strcat(newserver->name, newserver->ip);
100                 strcat(newserver->name, ")");
101         }
102
103         newserver->flag = 0;
104         check_connection_state(newserver);
105
106         printf("Adding new verse server: %s at %s\n", newserver->name, newserver->ip);
107
108         BLI_addtail(&server_list, newserver);
109         post_server_add();
110 }
111
112 /*
113  * callback function for ping
114  */
115 static void cb_ping(void *user, const char *address, const char *message)
116 {
117         VMSServer       **servers = verse_ms_list_parse(message);
118         if(servers != NULL)
119         {
120                 int     i, j;
121
122                 for(i = 0; servers[i] != NULL; i++)
123                 {
124                         add_verse_server(servers[i]);
125                 }
126                 free(servers);
127         }
128 }
129
130 /*
131  * callback function for connection terminated
132  */
133 static void cb_connect_terminate(const char *address, const char *bye)
134 {
135         VerseSession *session = (VerseSession*)current_verse_session();
136
137         if(!session) return;
138
139         /* remove session from list of session */
140         BLI_remlink(&session_list, session);
141         /* do post connect operations */
142         session->post_connect_terminated(session);
143         /* free session data */
144         free_verse_session_data(session);
145         /* free session */
146         MEM_freeN(session);
147 }
148
149 /*
150  * callback function for accepted connection to verse server
151  */
152 static void cb_connect_accept(
153                 void *user_data,
154                 uint32 avatar,
155                 void *address,
156                 void *connection,
157                 const uint8 *host_id)
158 {
159         struct VerseSession *session = (VerseSession*)current_verse_session();
160         struct VerseServer *server = server_list.first;
161         uint32 i, mask=0;
162
163         if(!session) return;
164
165         session->flag |= VERSE_CONNECTED;
166         session->flag &= ~VERSE_CONNECTING;
167
168         while(server) {
169                 if(strcmp(session->address, server->ip)==0) {
170                         server->flag |= VERSE_CONNECTED;
171                         server->flag &= ~VERSE_CONNECTING;
172                 }
173                 server = server->next;
174         }
175
176         printf("\tBlender was connected to verse server: %s\n", (char*)address);
177         printf("\tVerseSession->counter: %d\n", session->counter);
178
179         session->avatar = avatar;
180
181         session->post_connect_accept(session);
182
183         for(i = 0; i < V_NT_NUM_TYPES; i++)
184                 mask = mask | (1 << i);
185         verse_send_node_index_subscribe(mask);
186 }
187
188 /*
189  * set up all callbacks for sessions
190  */
191 void set_verse_session_callbacks(void)
192 {
193         /* connection */
194         verse_callback_set(verse_send_connect_accept, cb_connect_accept, NULL);
195         /* connection was terminated */
196         verse_callback_set(verse_send_connect_terminate, cb_connect_terminate, NULL);
197
198 }
199
200 /*
201  * set all callbacks used in Blender
202  */
203 static void set_all_callbacks(void)
204 {
205         /* set up all callbacks for sessions */
206         set_verse_session_callbacks();
207
208         /* set up callbacks for nodes */
209         set_node_callbacks();
210
211         /* set up all callbacks for object nodes */
212         set_object_callbacks();
213
214         /* set up all callbacks for geometry nodes */
215         set_geometry_callbacks();
216
217         /* set up all callbacks for bitmap nodes */
218         set_bitmap_callbacks();
219 }
220
221 /*
222  * this function sends and receive all packets for all sessions
223  */
224 void b_verse_update(void)
225 {
226         VerseSession *session, *next_session;
227
228         session = session_list.first;
229         while(session){
230                 next_session = session->next;
231                 verse_session_set(session->vsession);
232                 if((session->flag & VERSE_CONNECTED) || (session->flag & VERSE_CONNECTING)) {
233                         verse_callback_update(10);
234                         session->post_connect_update(session);
235                 }
236                 session = next_session;
237         }
238         if(cb_ping_registered>0) {
239                         verse_callback_update(10);
240         }
241 }
242
243 /*
244  * returns VerseSession coresponding to vsession pointer
245  */
246 VerseSession *versesession_from_vsession(VSession *vsession)
247 {
248         struct VerseSession *session;
249
250         session = session_list.first;
251
252         while(session) {
253                 if(session->vsession==vsession) return session;
254                 session = session->next;
255         }
256         
257         return session;
258 }
259
260 /*
261  * returns pointer at current VerseSession
262  */
263 VerseSession *current_verse_session(void)
264 {
265         struct VerseSession *session;
266         VSession vsession = verse_session_get();
267
268         session = session_list.first;
269
270         while(session){
271                 if(session->vsession == vsession)
272                         return session;
273                 session = session->next;
274         }
275
276         printf("error: non-existing SESSION occured!\n");
277         return NULL;
278 }
279
280 /*
281  * free VerseSession
282  */
283 static void free_verse_session_data(VerseSession *session)
284 {
285         struct VNode *vnode;
286
287         /* free data of all nodes */
288         vnode = session->nodes.lb.first;
289         while(vnode){
290                 free_verse_node_data(vnode);
291                 vnode = vnode->next;
292         }
293
294         /* free data of nodes waiting in queue */
295         vnode = session->queue.first;
296         while(vnode){
297                 free_verse_node_data(vnode);
298                 vnode = vnode->next;
299         }
300
301         /* free all VerseNodes */
302         BLI_dlist_destroy(&(session->nodes));
303         /* free all VerseNodes waiting in queque */
304         BLI_freelistN(&(session->queue));
305
306         /* free name of verse host for this session */
307         MEM_freeN(session->address);
308 }
309
310 /*
311  * free VerseSession
312  */
313 void free_verse_session(VerseSession *session)
314 {
315         /* remove session from session list*/
316         BLI_remlink(&session_list, session);
317         /* do post terminated operations */
318         session->post_connect_terminated(session);
319         /* free session data (nodes, layers) */
320         free_verse_session_data(session);
321         /* free session */
322         MEM_freeN(session);
323 }
324
325 /*
326  * create new verse session and return coresponding data structure
327  */
328 VerseSession *create_verse_session(
329                 const char *name,
330                 const char *pass,
331                 const char *address,
332                 uint8 *expected_key)
333 {
334         struct VerseSession *session;
335         VSession *vsession;
336         
337         vsession = verse_send_connect(name, pass, address, expected_key);
338
339         if(!vsession) return NULL;
340
341         session = (VerseSession*)MEM_mallocN(sizeof(VerseSession), "VerseSession");
342
343         session->flag = VERSE_CONNECTING;
344
345         session->vsession = vsession;
346         session->avatar = -1;
347
348         session->address = (char*)MEM_mallocN(sizeof(char)*(strlen(address)+1),"session adress name");
349         strcpy(session->address, address);
350
351         session->connection = NULL;
352         session->host_id = NULL;
353         session->counter = 0;
354
355         /* initialize dynamic list of nodes and node queue */
356         BLI_dlist_init(&(session->nodes));
357         session->queue.first = session->queue.last = NULL;
358
359         /* set up all client dependent functions */
360         session->post_connect_accept = post_connect_accept;
361         session->post_connect_terminated = post_connect_terminated;
362         session->post_connect_update = post_connect_update;
363
364         return session;
365 }
366
367 /*
368  * end verse session and free all session data
369  */
370 void end_verse_session(VerseSession *session, char free)
371 {
372         /* send terminate command to verse server */
373         verse_send_connect_terminate(session->address, "blender: bye bye");
374         /* update callbacks */
375         verse_callback_update(1000);
376         /* send destroy session command to verse server */
377         verse_session_destroy(session->vsession);
378         /* set up flag of verse session */
379         session->flag &= ~VERSE_CONNECTED;
380         /* do post connect operations */
381         session->post_connect_terminated(session);
382         /* free session data */
383         free_verse_session_data(session);
384         /* free structure of verse session */
385         if(free) free_verse_session(session);
386 }
387
388 void free_all_servers(void)
389 {
390         VerseServer *server, *nextserver;
391
392         server = server_list.first;
393
394         while(server) {
395                         nextserver = server->next;
396                         BLI_remlink(&server_list, server);
397                         MEM_freeN(server->name);
398                         MEM_freeN(server->ip);
399                         MEM_freeN(server);
400                         server = nextserver;
401         }
402         
403         BLI_freelistN(&server_list);
404 }
405
406 /*
407  * end connection to all verse hosts (servers) ... free all VerseSessions
408  * free all VerseServers
409  */
410 void end_all_verse_sessions(void)
411 {
412         VerseSession *session;
413
414         session = session_list.first;
415
416         while(session) {
417                 end_verse_session(session, 0);
418                 /* end next session */
419                 session = session->next;
420         }
421
422         BLI_freelistN(&session_list);
423
424         free_all_servers();
425 }
426
427 /*
428  * do a get from ms
429  */
430 void b_verse_ms_get(void)
431 {
432                 if(cb_ping_registered==0) {
433                                 /* handle ping messages (for master server) */
434                                 verse_callback_set(verse_send_ping, cb_ping, NULL);
435                                 add_screenhandler(G.curscreen, SCREEN_HANDLER_VERSE, 1);
436                                 cb_ping_registered++;
437                 }
438                 free_all_servers();
439
440                 verse_ms_get_send(U.versemaster, VERSE_MS_FIELD_DESCRIPTION, NULL);
441                 verse_callback_update(10);
442 }
443
444 /*
445  * connect to verse host, set up all callbacks, create session
446  */
447 void b_verse_connect(char *address)
448 {
449         VerseSession *session;
450
451         /* if no session was created before, then set up all callbacks */
452         if((session_list.first==NULL) && (session_list.last==NULL))
453                 set_all_callbacks();
454
455         /* create new session */
456         if(address)
457                 session = create_verse_session("Blender", "pass", address, NULL);
458
459         if(session) {
460                 /* add new session to the list of sessions */
461                 BLI_addtail(&session_list, session);
462
463                 /* add verse handler if this is first session */
464                 if(session_list.first == session_list.last)
465                         add_screenhandler(G.curscreen, SCREEN_HANDLER_VERSE, 1);
466
467         }
468 }
469
470 #endif