Add option 'noenter' to channel configuration and implement support.
[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                         rootChan->noenter = chdesc.noenter;
153                         list_add_tail(&rootChan->flatlist_node, &channels);
154                         if (strcmp(defaultChannelName, chdesc.name) == 0)
155                                 defaultChan = rootChan;
156                 }
157                 else {
158                         channel_t *ch, *ch_itr = NULL;
159                         ch = Chan_createChannel(chdesc.name, chdesc.description);
160                         ch->noenter = chdesc.noenter;
161                         
162                         if (strcmp(defaultChannelName, chdesc.name) == 0) {
163                                 Log_info("Setting default channel %s", ch->name); 
164                                 defaultChan = ch;
165                         }
166                         
167                         do {
168                                 Chan_iterate(&ch_itr);
169                         } while (ch_itr != NULL && strcmp(ch_itr->name, chdesc.parent) != 0);
170                         
171                         if (ch_itr == NULL)
172                                 Log_fatal("Error in channel configuration: parent not found");
173                         else {
174                                 Chan_addChannel(ch_itr, ch);
175                                 Log_info("Adding channel '%s' parent '%s'", ch->name, chdesc.parent);
176                         }
177                 }
178         }
179         if (defaultChan == NULL)
180                 defaultChan = rootChan;
181         
182         if (defaultChan->noenter)
183                 Log_fatal("Error in channel configuration: default channel is marked as noenter");
184
185         /* Channel links */
186         for (i = 0; ; i++) {
187                 channel_t *ch_src, *ch_dst, *ch_itr = NULL;
188                 if (Conf_getNextChannelLink(&chlink, i) < 0) {
189                         if (i == 0)
190                                 Log_info("No channel links found in configuration file.");
191                         break;
192                 }
193                 ch_itr = NULL;
194                 do {
195                         Chan_iterate(&ch_itr);
196                 } while (ch_itr != NULL && strcmp(ch_itr->name, chlink.source) != 0);
197                 if (ch_itr == NULL)
198                         Log_fatal("Error in channel link configuration: source channel '%s' not found.", chlink.source);
199                 else
200                         ch_src = ch_itr;
201                 
202                 ch_itr = NULL;          
203                 do {
204                         Chan_iterate(&ch_itr);
205                 } while (ch_itr != NULL && strcmp(ch_itr->name, chlink.destination) != 0);
206                 if (ch_itr == NULL)
207                         Log_fatal("Error in channel link configuration: destination channel '%s' not found", chlink.destination);
208                 else
209                         ch_dst = ch_itr;
210                 
211                 list_add_tail(&ch_dst->link_node, &ch_src->channel_links);
212                 Log_info("Adding channel link %s -> %s", ch_src->name, ch_dst->name);
213         }
214 }
215
216 void Chan_free()
217 {
218         struct dlist *itr, *save;
219         
220         list_iterate_safe(itr, save, &channels) {
221                 Log_debug("Free channel %s", list_get_entry(itr, channel_t, flatlist_node)->name);
222                 free(list_get_entry(itr, channel_t, flatlist_node));
223         }
224 }
225
226 channel_t *Chan_createChannel(const char *name, const char *desc)
227 {
228         int id = findFreeId();
229         if (id < 0)
230                 Log_fatal("No free channel ID found");
231         return createChannel(id, name, desc);
232 }
233
234 void Chan_freeChannel(channel_t *ch)
235 {
236         list_del(&ch->node);
237         list_del(&ch->flatlist_node);
238         free(ch);
239 }
240
241 void Chan_addChannel(channel_t *parent, channel_t *ch)
242 {
243         list_add_tail(&ch->node, &parent->subs);
244         ch->parent = parent;
245         list_add_tail(&ch->flatlist_node, &channels);
246 }
247
248
249 int Chan_playerJoin(channel_t *ch, client_t *client)
250 {
251         channel_t *leaving = NULL;
252         int leaving_id = -1;
253         
254         /* Only allowed in one channel at a time */
255         Log_debug("Add player %s to channel %s", client->playerName, ch->name); 
256
257         if (client->channel) {
258                 list_del(&client->chan_node);
259                 leaving = (channel_t *)client->channel;
260                 if (leaving->temporary && list_empty(&leaving->clients)) {
261                         leaving_id = leaving->id;
262                         Chan_freeChannel(leaving);
263                 }
264         }
265         list_add_tail(&client->chan_node, &ch->clients);
266         client->channel = (void *)ch;
267         return leaving_id;
268 }
269
270 int Chan_playerJoin_id(int channelid, client_t *client)
271 {
272         channel_t *ch_itr = NULL;
273         do {
274                 Chan_iterate(&ch_itr);
275         } while (ch_itr != NULL && ch_itr->id != channelid);
276         if (ch_itr == NULL) {
277                 Log_warn("Channel id %d not found - ignoring.", channelid);
278                 return -1;
279         }
280         else
281                 return Chan_playerJoin(ch_itr, client); 
282 }
283
284 bool_t Chan_playerJoin_id_test(int channelid)
285 {
286         channel_t *ch_itr = NULL;
287         do {
288                 Chan_iterate(&ch_itr);
289         } while (ch_itr != NULL && ch_itr->id != channelid);
290         if (ch_itr == NULL) {
291                 Log_warn("Channel id %d not found - ignoring.", channelid);
292                 return false;
293         }
294         if (ch_itr->noenter)
295                 return false;
296         else
297                 return true;
298 }
299
300 #if 0
301 void Chan_addChannel_id(int parentId, channel_t *ch)
302 {
303         channel_t *ch_itr = NULL;
304         do {
305                 Chan_iterate(&ch_itr);
306         } while (ch_itr != NULL && ch_itr->id != parentId);
307         if (ch_itr == NULL)
308                 Log_warn("Chan_addChannel_id: Channel id %d not found - ignoring.", parentId);
309         else
310                 list_add_tail(&ch->node, &ch_itr->subs);
311 }
312 #endif
313
314 channel_t *Chan_fromId(int channelid)
315 {
316         channel_t *ch_itr = NULL;
317         do {
318                 Chan_iterate(&ch_itr);
319         } while (ch_itr != NULL && ch_itr->id != channelid);
320         if (ch_itr == NULL)
321                 Log_warn("Chan_fromId: Channel id %d not found.", channelid);
322         return ch_itr;
323 }
324
325 void Chan_removeChannel(channel_t *ch)
326 {
327         list_del(&ch->node);
328 }