Added creation of temporary channels.
[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         strncpy(ch->name, name, MAX_TEXT);
53         strncpy(ch->desc, desc, MAX_TEXT);
54         init_list_entry(&ch->subs);
55         init_list_entry(&ch->node);
56         init_list_entry(&ch->clients);
57         init_list_entry(&ch->flatlist_node);
58         init_list_entry(&ch->channel_links);
59         return ch;
60 }
61
62 static int findFreeId()
63 {
64         int id = 0;
65         channel_t *ch_itr = NULL;
66         for (id = 0; id < INT_MAX; id++) {
67                 ch_itr = NULL;
68                 while ((ch_itr = Chan_iterate(&ch_itr)) != NULL) {
69                         if (ch_itr->id == id)
70                                 break;
71                 }
72                 if (ch_itr == NULL) /* Found free id */
73                         return id;
74         }
75         return -1;
76 }
77
78 #if 0
79 /* Might be used when tree traversal becomes neccessary */
80 static channel_t *first_subchannel(channel_t *ch)
81 {
82         if (list_empty(&ch->subs))
83                 return NULL;
84         else
85                 return list_get_entry(list_get_first(&ch->subs), channel_t, node);
86 }
87
88 static channel_t *next_channel(channel_t *ch)
89 {
90         if (list_get_next(&ch->node) == &list_get_entry(&ch->node, channel_t, node)->parent->subs)
91                 return NULL;
92         else
93                 return list_get_entry(list_get_next(&ch->node), channel_t, node);       
94 }
95 #endif
96
97 channel_t *Chan_iterate(channel_t **channelpptr)
98 {
99         channel_t *ch = *channelpptr;
100
101         if (!list_empty(&channels)) {
102                 if (ch == NULL)
103                         ch = list_get_entry(list_get_first(&channels), channel_t, flatlist_node);
104                 else {
105                         if (list_get_next(&ch->flatlist_node) == &channels)
106                                 ch = NULL;
107                         else
108                                 ch = list_get_entry(list_get_next(&ch->flatlist_node), channel_t, flatlist_node);
109                 }
110         }
111
112         *channelpptr = ch;
113         return ch;
114 }
115
116 channel_t *Chan_iterate_siblings(channel_t *parent, channel_t **channelpptr)
117 {
118         channel_t *ch = *channelpptr;
119
120         if (!list_empty(&parent->subs)) {
121                 if (ch == NULL)
122                         ch = list_get_entry(list_get_first(&parent->subs), channel_t, node);
123                 else {
124                         if (list_get_next(&ch->node) == &parent->subs)
125                                 ch = NULL;
126                         else
127                                 ch = list_get_entry(list_get_next(&ch->node), channel_t, node);
128                 }
129         }
130
131         *channelpptr = ch;
132         return ch;
133 }
134                         
135 void Chan_init()
136 {
137         int i;
138         conf_channel_t chdesc;
139         conf_channel_link_t chlink;
140         const char *defaultChannelName;
141
142         defaultChannelName = getStrConf(DEAFULT_CHANNEL);
143         
144         for (i = 0; ; i++) {
145                 if (Conf_getNextChannel(&chdesc, i) < 0) {
146                         if (i == 0)
147                                 Log_fatal("No valid channels found in configuration file. Exiting.");
148                         break;
149                 }
150                 if (i == 0) {
151                         rootChan = createChannel(0, chdesc.name, chdesc.description);
152                         list_add_tail(&rootChan->flatlist_node, &channels);
153                         if (strcmp(defaultChannelName, chdesc.name) == 0)
154                                 defaultChan = rootChan;
155                 }
156                 else {
157                         channel_t *ch, *ch_itr = NULL;
158                         ch = Chan_createChannel(chdesc.name, chdesc.description);
159                         
160                         if (strcmp(defaultChannelName, chdesc.name) == 0) {
161                                 Log_info("Setting default channel %s", ch->name); 
162                                 defaultChan = ch;
163                         }
164                         
165                         do {
166                                 Chan_iterate(&ch_itr);
167                         } while (ch_itr != NULL && strcmp(ch_itr->name, chdesc.parent) != 0);
168                         
169                         if (ch_itr == NULL)
170                                 Log_fatal("Error in channel configuration: parent not found");
171                         else {
172                                 Chan_addChannel(ch_itr, ch);
173                                 Log_info("Adding channel '%s' parent '%s'", ch->name, chdesc.parent);
174                         }
175                 }
176         }
177         if (defaultChan == NULL)
178                 defaultChan = rootChan;
179
180         /* Channel links */
181         for (i = 0; ; i++) {
182                 channel_t *ch_src, *ch_dst, *ch_itr = NULL;
183                 if (Conf_getNextChannelLink(&chlink, i) < 0) {
184                         if (i == 0)
185                                 Log_info("No channel links found in configuration file.");
186                         break;
187                 }
188                 ch_itr = NULL;
189                 do {
190                         Chan_iterate(&ch_itr);
191                 } while (ch_itr != NULL && strcmp(ch_itr->name, chlink.source) != 0);
192                 if (ch_itr == NULL)
193                         Log_fatal("Error in channel link configuration: source channel '%s' not found.", chlink.source);
194                 else
195                         ch_src = ch_itr;
196                 
197                 ch_itr = NULL;          
198                 do {
199                         Chan_iterate(&ch_itr);
200                 } while (ch_itr != NULL && strcmp(ch_itr->name, chlink.destination) != 0);
201                 if (ch_itr == NULL)
202                         Log_fatal("Error in channel link configuration: destination channel '%s' not found", chlink.destination);
203                 else
204                         ch_dst = ch_itr;
205                 
206                 list_add_tail(&ch_dst->link_node, &ch_src->channel_links);
207                 Log_info("Adding channel link %s -> %s", ch_src->name, ch_dst->name);
208         }
209 }
210
211 void Chan_free()
212 {
213         struct dlist *itr, *save;
214         
215         list_iterate_safe(itr, save, &channels) {
216                 Log_debug("Free channel %s", list_get_entry(itr, channel_t, flatlist_node)->name);
217                 free(list_get_entry(itr, channel_t, flatlist_node));
218         }
219 }
220
221 channel_t *Chan_createChannel(const char *name, const char *desc)
222 {
223         int id = findFreeId();
224         if (id < 0)
225                 Log_fatal("No free channel ID found");
226         return createChannel(id, name, desc);
227 }
228
229 void Chan_freeChannel(channel_t *ch)
230 {
231         list_del(&ch->node);
232         list_del(&ch->flatlist_node);
233         free(ch);
234 }
235
236 void Chan_addChannel(channel_t *parent, channel_t *ch)
237 {
238         list_add_tail(&ch->node, &parent->subs);
239         ch->parent = parent;
240         list_add_tail(&ch->flatlist_node, &channels);
241 }
242
243
244 int Chan_playerJoin(channel_t *ch, client_t *client)
245 {
246         channel_t *leaving = NULL;
247         int leaving_id = -1;
248         
249         /* Only allowed in one channel at a time */
250         Log_debug("Add player %s to channel %s", client->playerName, ch->name); 
251
252         if (client->channel) {
253                 list_del(&client->chan_node);
254                 leaving = (channel_t *)client->channel;
255                 if (leaving->temporary && list_empty(&leaving->clients)) {
256                         leaving_id = leaving->id;
257                         Chan_freeChannel(leaving);
258                 }
259         }
260         list_add_tail(&client->chan_node, &ch->clients);
261         client->channel = (void *)ch;
262         return leaving_id;
263 }
264
265 int Chan_playerJoin_id(int channelid, client_t *client)
266 {
267         channel_t *ch_itr = NULL;
268         do {
269                 Chan_iterate(&ch_itr);
270         } while (ch_itr != NULL && ch_itr->id != channelid);
271         if (ch_itr == NULL) {
272                 Log_warn("Channel id %d not found - ignoring.", channelid);
273                 return -1;
274         }
275         else
276                 return Chan_playerJoin(ch_itr, client); 
277 }
278
279 #if 0
280 void Chan_addChannel_id(int parentId, channel_t *ch)
281 {
282         channel_t *ch_itr = NULL;
283         do {
284                 Chan_iterate(&ch_itr);
285         } while (ch_itr != NULL && ch_itr->id != parentId);
286         if (ch_itr == NULL)
287                 Log_warn("Chan_addChannel_id: Channel id %d not found - ignoring.", parentId);
288         else
289                 list_add_tail(&ch->node, &ch_itr->subs);
290 }
291 #endif
292
293 channel_t *Chan_fromId(int channelid)
294 {
295         channel_t *ch_itr = NULL;
296         do {
297                 Chan_iterate(&ch_itr);
298         } while (ch_itr != NULL && ch_itr->id != channelid);
299         if (ch_itr == NULL)
300                 Log_warn("Chan_fromId: Channel id %d not found.", channelid);
301         return ch_itr;
302 }
303
304 void Chan_removeChannel(channel_t *ch)
305 {
306         list_del(&ch->node);
307 }