Leave channel when disconnecting and remove channel if temporary and last user.
[umurmur.git] / src / channel.c
1 /* Copyright (C) 2009-2010, Martin Johansson <martin@fatbob.nu>
2    Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
3
4    All rights reserved.
5
6    Redistribution and use in source and binary forms, with or without
7    modification, are permitted provided that the following conditions
8    are met:
9
10    - Redistributions of source code must retain the above copyright notice,
11      this list of conditions and the following disclaimer.
12    - Redistributions in binary form must reproduce the above copyright notice,
13      this list of conditions and the following disclaimer in the documentation
14      and/or other materials provided with the distribution.
15    - Neither the name of the Developers nor the names of its contributors may
16      be used to endorse or promote products derived from this software without
17      specific prior written permission.
18
19    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22    A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
23    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31 #include <limits.h>
32 #include "log.h"
33 #include "list.h"
34 #include "client.h"
35 #include "channel.h"
36 #include "conf.h"
37
38
39 static channel_t *rootChan;
40 channel_t *defaultChan;
41 declare_list(channels); /* A flat list of the channels */
42
43 static channel_t *createChannel(int id, const char *name, const char *desc)
44 {
45         channel_t *ch;
46
47         ch = malloc(sizeof(channel_t));
48         if (ch == NULL)
49                 Log_fatal("out of memory");
50         memset(ch, 0, sizeof(channel_t));
51         ch->id = id;
52         ch->name = strdup(name);
53         if (desc)
54                 ch->desc = strdup(desc);
55         init_list_entry(&ch->subs);
56         init_list_entry(&ch->node);
57         init_list_entry(&ch->clients);
58         init_list_entry(&ch->flatlist_node);
59         init_list_entry(&ch->channel_links);
60         return ch;
61 }
62
63 static int findFreeId()
64 {
65         int id = 0;
66         channel_t *ch_itr = NULL;
67         for (id = 0; id < INT_MAX; id++) {
68                 ch_itr = NULL;
69                 while ((ch_itr = Chan_iterate(&ch_itr)) != NULL) {
70                         if (ch_itr->id == id)
71                                 break;
72                 }
73                 if (ch_itr == NULL) /* Found free id */
74                         return id;
75         }
76         return -1;
77 }
78
79 #if 0
80 /* Might be used when tree traversal becomes neccessary */
81 static channel_t *first_subchannel(channel_t *ch)
82 {
83         if (list_empty(&ch->subs))
84                 return NULL;
85         else
86                 return list_get_entry(list_get_first(&ch->subs), channel_t, node);
87 }
88
89 static channel_t *next_channel(channel_t *ch)
90 {
91         if (list_get_next(&ch->node) == &list_get_entry(&ch->node, channel_t, node)->parent->subs)
92                 return NULL;
93         else
94                 return list_get_entry(list_get_next(&ch->node), channel_t, node);       
95 }
96 #endif
97
98 channel_t *Chan_iterate(channel_t **channelpptr)
99 {
100         channel_t *ch = *channelpptr;
101
102         if (!list_empty(&channels)) {
103                 if (ch == NULL)
104                         ch = list_get_entry(list_get_first(&channels), channel_t, flatlist_node);
105                 else {
106                         if (list_get_next(&ch->flatlist_node) == &channels)
107                                 ch = NULL;
108                         else
109                                 ch = list_get_entry(list_get_next(&ch->flatlist_node), channel_t, flatlist_node);
110                 }
111         }
112
113         *channelpptr = ch;
114         return ch;
115 }
116
117 channel_t *Chan_iterate_siblings(channel_t *parent, channel_t **channelpptr)
118 {
119         channel_t *ch = *channelpptr;
120
121         if (!list_empty(&parent->subs)) {
122                 if (ch == NULL)
123                         ch = list_get_entry(list_get_first(&parent->subs), channel_t, node);
124                 else {
125                         if (list_get_next(&ch->node) == &parent->subs)
126                                 ch = NULL;
127                         else
128                                 ch = list_get_entry(list_get_next(&ch->node), channel_t, node);
129                 }
130         }
131
132         *channelpptr = ch;
133         return ch;
134 }
135                         
136 void Chan_init()
137 {
138         int i;
139         conf_channel_t chdesc;
140         conf_channel_link_t chlink;
141         const char *defaultChannelName;
142
143         defaultChannelName = getStrConf(DEFAULT_CHANNEL);
144         
145         for (i = 0; ; i++) {
146                 if (Conf_getNextChannel(&chdesc, i) < 0) {
147                         if (i == 0)
148                                 Log_fatal("No valid channels found in configuration file. Exiting.");
149                         break;
150                 }
151                 if (i == 0) {
152                         rootChan = createChannel(0, chdesc.name, chdesc.description);
153                         rootChan->noenter = chdesc.noenter;
154                         list_add_tail(&rootChan->flatlist_node, &channels);
155                         if (strcmp(defaultChannelName, chdesc.name) == 0)
156                                 defaultChan = rootChan;
157                 }
158                 else {
159                         channel_t *ch, *ch_itr = NULL;
160                         ch = Chan_createChannel(chdesc.name, chdesc.description);
161                         ch->noenter = chdesc.noenter;
162                         
163                         if (strcmp(defaultChannelName, chdesc.name) == 0) {
164                                 Log_info("Setting default channel %s", ch->name); 
165                                 defaultChan = ch;
166                         }
167                         
168                         do {
169                                 Chan_iterate(&ch_itr);
170                         } while (ch_itr != NULL && strcmp(ch_itr->name, chdesc.parent) != 0);
171                         
172                         if (ch_itr == NULL)
173                                 Log_fatal("Error in channel configuration: parent not found");
174                         else {
175                                 Chan_addChannel(ch_itr, ch);
176                                 Log_info("Adding channel '%s' parent '%s'", ch->name, chdesc.parent);
177                         }
178                 }
179         }
180         if (defaultChan == NULL)
181                 defaultChan = rootChan;
182         
183         if (defaultChan->noenter)
184                 Log_fatal("Error in channel configuration: default channel is marked as noenter");
185
186         /* Channel links */
187         for (i = 0; ; i++) {
188                 channel_t *ch_src, *ch_dst, *ch_itr = NULL;
189                 if (Conf_getNextChannelLink(&chlink, i) < 0) {
190                         if (i == 0)
191                                 Log_info("No channel links found in configuration file.");
192                         break;
193                 }
194                 ch_itr = NULL;
195                 do {
196                         Chan_iterate(&ch_itr);
197                 } while (ch_itr != NULL && strcmp(ch_itr->name, chlink.source) != 0);
198                 if (ch_itr == NULL)
199                         Log_fatal("Error in channel link configuration: source channel '%s' not found.",
200                                           chlink.source);
201                 else
202                         ch_src = ch_itr;
203                 
204                 ch_itr = NULL;          
205                 do {
206                         Chan_iterate(&ch_itr);
207                 } while (ch_itr != NULL && strcmp(ch_itr->name, chlink.destination) != 0);
208                 if (ch_itr == NULL)
209                         Log_fatal("Error in channel link configuration: destination channel '%s' not found",
210                                           chlink.destination);
211                 else
212                         ch_dst = ch_itr;
213                 
214                 list_add_tail(&ch_dst->link_node, &ch_src->channel_links);
215                 Log_info("Adding channel link %s -> %s", ch_src->name, ch_dst->name);
216         }
217 }
218
219 void Chan_free()
220 {
221         struct dlist *itr, *save;
222         channel_t *ch;
223         
224         list_iterate_safe(itr, save, &channels) {
225                 ch = list_get_entry(itr, channel_t, flatlist_node);
226                 Log_debug("Free channel %s", ch->name);
227                 free(ch->name);
228                 if (ch->desc)
229                         free(ch->desc);
230                 free(ch);
231         }
232 }
233
234 channel_t *Chan_createChannel(const char *name, const char *desc)
235 {
236         int id = findFreeId();
237         if (id < 0)
238                 Log_fatal("No free channel ID found");
239         return createChannel(id, name, desc);
240 }
241
242 void Chan_freeChannel(channel_t *ch)
243 {
244         list_del(&ch->node);
245         list_del(&ch->flatlist_node);
246         free(ch);
247 }
248
249 void Chan_addChannel(channel_t *parent, channel_t *ch)
250 {
251         list_add_tail(&ch->node, &parent->subs);
252         ch->parent = parent;
253         list_add_tail(&ch->flatlist_node, &channels);
254 }
255
256
257 int Chan_playerLeave(client_t *client)
258 {
259         channel_t *leaving = NULL;
260         int leaving_id = -1;
261         
262         if (client->channel) {
263                 list_del(&client->chan_node);
264                 leaving = (channel_t *)client->channel;
265                 if (leaving->temporary && list_empty(&leaving->clients)) {
266                         leaving_id = leaving->id;
267                         Chan_freeChannel(leaving);
268                 }
269         }
270         return leaving_id;
271 }
272
273 int Chan_playerJoin(channel_t *ch, client_t *client)
274 {
275         int leaving_id;
276         
277         Log_debug("Add player %s to channel %s", client->playerName, ch->name); 
278
279         /* Only allowed in one channel at a time */
280         leaving_id = Chan_playerLeave(client);
281         list_add_tail(&client->chan_node, &ch->clients);
282         client->channel = (void *)ch;
283         return leaving_id;
284 }
285
286 int Chan_playerJoin_id(int channelid, client_t *client)
287 {
288         channel_t *ch_itr = NULL;
289         do {
290                 Chan_iterate(&ch_itr);
291         } while (ch_itr != NULL && ch_itr->id != channelid);
292         if (ch_itr == NULL) {
293                 Log_warn("Channel id %d not found - ignoring.", channelid);
294                 return -1;
295         }
296         else
297                 return Chan_playerJoin(ch_itr, client); 
298 }
299
300 bool_t Chan_playerJoin_id_test(int channelid)
301 {
302         channel_t *ch_itr = NULL;
303         do {
304                 Chan_iterate(&ch_itr);
305         } while (ch_itr != NULL && ch_itr->id != channelid);
306         if (ch_itr == NULL) {
307                 Log_warn("Channel id %d not found - ignoring.", channelid);
308                 return false;
309         }
310         if (ch_itr->noenter)
311                 return false;
312         else
313                 return true;
314 }
315
316 #if 0
317 void Chan_addChannel_id(int parentId, channel_t *ch)
318 {
319         channel_t *ch_itr = NULL;
320         do {
321                 Chan_iterate(&ch_itr);
322         } while (ch_itr != NULL && ch_itr->id != parentId);
323         if (ch_itr == NULL)
324                 Log_warn("Chan_addChannel_id: Channel id %d not found - ignoring.", parentId);
325         else
326                 list_add_tail(&ch->node, &ch_itr->subs);
327 }
328 #endif
329
330 channel_t *Chan_fromId(int channelid)
331 {
332         channel_t *ch_itr = NULL;
333         do {
334                 Chan_iterate(&ch_itr);
335         } while (ch_itr != NULL && ch_itr->id != channelid);
336         if (ch_itr == NULL)
337                 Log_warn("Chan_fromId: Channel id %d not found.", channelid);
338         return ch_itr;
339 }
340
341 void Chan_removeChannel(channel_t *ch)
342 {
343         list_del(&ch->node);
344 }