Get rid of character arrays.
[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_playerJoin(channel_t *ch, client_t *client)
258 {
259         channel_t *leaving = NULL;
260         int leaving_id = -1;
261         
262         /* Only allowed in one channel at a time */
263         Log_debug("Add player %s to channel %s", client->playerName, ch->name); 
264
265         if (client->channel) {
266                 list_del(&client->chan_node);
267                 leaving = (channel_t *)client->channel;
268                 if (leaving->temporary && list_empty(&leaving->clients)) {
269                         leaving_id = leaving->id;
270                         Chan_freeChannel(leaving);
271                 }
272         }
273         list_add_tail(&client->chan_node, &ch->clients);
274         client->channel = (void *)ch;
275         return leaving_id;
276 }
277
278 int Chan_playerJoin_id(int channelid, client_t *client)
279 {
280         channel_t *ch_itr = NULL;
281         do {
282                 Chan_iterate(&ch_itr);
283         } while (ch_itr != NULL && ch_itr->id != channelid);
284         if (ch_itr == NULL) {
285                 Log_warn("Channel id %d not found - ignoring.", channelid);
286                 return -1;
287         }
288         else
289                 return Chan_playerJoin(ch_itr, client); 
290 }
291
292 bool_t Chan_playerJoin_id_test(int channelid)
293 {
294         channel_t *ch_itr = NULL;
295         do {
296                 Chan_iterate(&ch_itr);
297         } while (ch_itr != NULL && ch_itr->id != channelid);
298         if (ch_itr == NULL) {
299                 Log_warn("Channel id %d not found - ignoring.", channelid);
300                 return false;
301         }
302         if (ch_itr->noenter)
303                 return false;
304         else
305                 return true;
306 }
307
308 #if 0
309 void Chan_addChannel_id(int parentId, channel_t *ch)
310 {
311         channel_t *ch_itr = NULL;
312         do {
313                 Chan_iterate(&ch_itr);
314         } while (ch_itr != NULL && ch_itr->id != parentId);
315         if (ch_itr == NULL)
316                 Log_warn("Chan_addChannel_id: Channel id %d not found - ignoring.", parentId);
317         else
318                 list_add_tail(&ch->node, &ch_itr->subs);
319 }
320 #endif
321
322 channel_t *Chan_fromId(int channelid)
323 {
324         channel_t *ch_itr = NULL;
325         do {
326                 Chan_iterate(&ch_itr);
327         } while (ch_itr != NULL && ch_itr->id != channelid);
328         if (ch_itr == NULL)
329                 Log_warn("Chan_fromId: Channel id %d not found.", channelid);
330         return ch_itr;
331 }
332
333 void Chan_removeChannel(channel_t *ch)
334 {
335         list_del(&ch->node);
336 }