ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/include/client.h
(Generate patch)

Comparing deliantra/server/include/client.h (file contents):
Revision 1.3 by root, Fri Dec 15 20:08:45 2006 UTC vs.
Revision 1.71 by root, Sun Jan 13 12:39:29 2008 UTC

1
2/* 1/*
3 CrossFire, A Multiplayer game for the X Window System 2 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
4 3 *
4 * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5 Copyright (C) 2002 Mark Wedel & Crossfire Development Team 5 * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team
6 Copyright (C) 1992 Frank Tore Johansen 6 * Copyright (©) 1992,2007 Frank Tore Johansen
7 7 *
8 This program is free software; you can redistribute it and/or modify 8 * Deliantra is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by 9 * it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or 10 * the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version. 11 * (at your option) any later version.
12 12 *
13 This program is distributed in the hope that it will be useful, 13 * This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details. 16 * GNU General Public License for more details.
17 17 *
18 You should have received a copy of the GNU General Public License 18 * You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software 19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 *
21 21 * The authors can be reached via e-mail to <support@deliantra.net>
22 The author can be reached via e-mail to crossfire@schmorp.de
23*/ 22 */
24
25/*
26 newserver.h defines various structures and values that are use for the
27 new client server communication method. Values defined here are only
28 used on the server side code. For shared client/server values, see
29 newclient.h
30*/
31 23
32#ifndef CLIENT_H 24#ifndef CLIENT_H
33#define CLIENT_H 25#define CLIENT_H
34 26
35/* Reduce this from 50 to 5 - as it is now, there will never be more 27#include <deque>
36 * than 3 anyways. 28#include <bitset>
37 */ 29#include <tr1/unordered_set>
38 30
39#define NUM_LOOK_OBJECTS 50 31#if HAVE_TCP_INFO
32# include <netinet/tcp.h>
33#else
34 struct tcpi_info {
35 // dummy
36 char tcpi_state;
37 char tcpi_ca_state;
38 char tcpi_retransmits;
39 char tcpi_probes;
40 char tcpi_backoff;
41 char tcpi_options;
42 char tcpi_snd_wscale;
43 char tcpi_rcv_wscale;
44 char tcpi_rto;
45 char tcpi_ato;
46 char tcpi_snd_mss;
47 char tcpi_rcv_mss;
48 char tcpi_unacked;
49 char tcpi_sacked;
50 char tcpi_lost;
51 char tcpi_retrans;
52 char tcpi_fackets;
53 char tcpi_last_data_sent;
54 char tcpi_last_ack_sent;
55 char tcpi_last_data_recv;
56 char tcpi_last_ack_recv;
57 char tcpi_pmtu;
58 char tcpi_rcv_ssthresh;
59 char tcpi_rtt;
60 char tcpi_rttvar;
61 char tcpi_snd_ssthresh;
62 char tcpi_snd_cwnd;
63 char tcpi_advmss;
64 char tcpi_reordering;
65 };
66#endif
67
68// (possibly) max. number of objects "per page" in the ground container
69#define FLOORBOX_PAGESIZE 50
40 70
41struct MapCell 71struct MapCell
42{ 72{
43 UUID player; // this is, unfortunately, very wasteful of memory space, but pretty bandwidth-efficient 73 tag_t player; // this is, unfortunately, very wasteful of memory space, but pretty bandwidth-efficient
44 int count; /* This is really darkness in the map1 command */ 74 int count; /* This is really darkness in the map1 command */
45 uint16 faces[MAP_LAYERS]; 75 faceidx faces[MAP_LAYERS];
46 uint16 smooth[MAP_LAYERS];
47 unsigned char stat_hp; // health of something in this space, or 0 76 unsigned char stat_hp; // health of something in this space, or 0
48 unsigned char flags; 77 unsigned char flags;
78 uint8_t smooth[MAP_LAYERS];
49}; 79};
50 80
51/* This basically defines the largest size an
52 * archetype may be - it is used for allocation of
53 * some structures, as well as determining how far
54 * we should look for the heads of big images.
55 */
56#define MAX_HEAD_OFFSET 8
57
58#define MAX_CLIENT_X (MAP_CLIENT_X + MAX_HEAD_OFFSET) 81#define MAX_CLIENT_X MAP_CLIENT_X
59#define MAX_CLIENT_Y (MAP_CLIENT_Y + MAX_HEAD_OFFSET) 82#define MAX_CLIENT_Y MAP_CLIENT_Y
60 83
61struct Map 84struct Map
62{ 85{
63 struct MapCell cells[MAX_CLIENT_X][MAX_CLIENT_Y]; 86 struct MapCell cells[MAX_CLIENT_X][MAX_CLIENT_Y];
64}; 87};
65 88
66/* True max is 16383 given current map compaction method */ 89/* True max is 16383 given current map compaction method */
67# define MAXANIMNUM 2000 90#define MAXANIMNUM 10000
68 91
69struct statsinfo 92struct statsinfo
70{ 93{
71 char *range, *title; 94 char *range, *title;
72}; 95};
75 * current state. we set up our on buffers for sending/receiving, so we can 98 * current state. we set up our on buffers for sending/receiving, so we can
76 * handle some higher level functions. fd is the actual file descriptor we 99 * handle some higher level functions. fd is the actual file descriptor we
77 * are using. 100 * are using.
78 */ 101 */
79 102
80enum Sock_Status { Ns_Add, Ns_Dead };
81
82/* Only one map mode can actually be used, so lets make it a switch 103/* Only one map mode can actually be used, so lets make it a switch
83 * instead of having a bunch of different fields that needed to 104 * instead of having a bunch of different fields that needed to
84 * get toggled. 105 * get toggled.
85 */ 106 */
86enum MapMode { Map0Cmd = 0, Map1Cmd = 1, Map1aCmd = 2 }; 107enum { Map0Cmd = 0, Map1Cmd = 1, Map1aCmd = 2 };
87 108
88/* The following is the setup for a ring buffer for storing outbut 109// states the socket can be in
89 * data that the OS can't handle right away. 110enum {
90 */ 111 ST_DEAD, // socket is dead
112 ST_SETUP, // initial handshake / setup / login
113 ST_PLAYING, // logged in an playing
114 ST_CUSTOM, // waiting for custom reply
91 115
92struct Buffer 116 ST_GET_PARTY_PASSWORD,
117};
118
119// a handler for a specific type of packet
120enum {
121 PF_PLAYER = 0x01, // must have valid player / will by synchronised
122 PF_PLAYING = 0x02, // must be in playing state
123 PF_COMMAND0 = 0x04, // command starts at offset 0
124 PF_COMMAND6 = 0x08, // command starts at offset 6
125};
126
127// face type.s bit 0 means "has meta info prepended"
128enum {
129 FT_FACE = 0 * 2 + 0, // faces (images)
130 FT_MUSIC = 1 * 2 + 1, // background music
131 FT_SOUND = 2 * 2 + 1, // effects
132 FT_RSRC = 3 * 2 + 0, // generic data files
133 FT_NUM,
134};
135
136struct packet_type
93{ 137{
94 char data[SOCKETBUFSIZE]; 138 const char *name;
95 int start; 139 void *cb;
140 int flags;
141};
142
143struct command
144{
145 tstamp stamp;
146 const packet_type *handler;
147 char *data;
96 int len; 148 int datalen;
97}; 149};
98 150
99/* how many times we are allowed to give the wrong password before being kicked. */ 151/* how many times we are allowed to give the wrong password before being kicked. */
100#define MAX_PASSWORD_FAILURES 5 152#define MAX_PASSWORD_FAILURES 5
101 153
102ACC_CLASS (client) // should become player when newsocket is a baseclass of player 154INTERFACE_CLASS (client) // should become player when newsocket is a baseclass of player
103struct client : zero_initialised, attachable_base 155struct client : zero_initialised, attachable
104{ 156{
105 enum Sock_Status status;
106 int ACC (RW, fd); 157 int ACC (RW, fd);
107 unsigned int inbuf_len; // number of bytes valid in inbuf 158 unsigned int inbuf_len; // number of bytes valid in inbuf
108 struct Map lastmap;
109 size_t faces_sent_len; /* This is the number of elements allocated in faces_sent[] */
110 uint8 *faces_sent; /* This is a bitmap on sent face status */
111 uint8 anims_sent[MAXANIMNUM];
112 struct statsinfo stats; 159 statsinfo stats;
160 object_vector_index ACC (RO, active);
161 player_ptr ACC (RO, pl);
162
163 /* The following is the setup for a ring buffer for storing output
164 * data that the OS can't handle right away.
165 */
166 struct
167 {
168 char data[SOCKETBUFSIZE];
169 int start;
170 int len;
171 } outputbuffer;
113 172
114 char *ACC (RW, host); /* Which host it is connected from (ip address) */ 173 char *ACC (RW, host); /* Which host it is connected from (ip address) */
115 uint8 ACC (RW, password_fails); /* how many times the player has failed to give the right password */ 174 uint8 ACC (RW, state); /* Input state of the player (name, password, etc */
116 Buffer outputbuffer; /* For undeliverable data */
117 175
176 sint8 ACC (RW, last_level); /* Last level we sent to client */
177 uint16 ACC (RW, last_flags); /* fire/run on flags for last tick */
178 float ACC (RW, last_weapon_sp); /* if diff than weapon_sp, update client */
179 sint32 ACC (RW, last_weight); /* Last weight as sent to client; -1 means do not send weight */
180 sint32 ACC (RW, last_weight_limit); /* Last weight limit transmitted to client */
181 uint32 ACC (RW, last_path_attuned); /* Last spell attunment sent to client */
182 uint32 ACC (RW, last_path_repelled); /* Last spell repelled sent to client */
183 uint32 ACC (RW, last_path_denied); /* Last spell denied sent to client */
184 living ACC (RO, last_stats); /* Last stats as sent to client */
185 float ACC (RW, last_speed); /* Last speed as sent to client */
186 sint16 ACC (RW, last_resist[NROFATTACKS]);/* last resist values sent to client */
187 sint64 ACC (RW, last_skill_exp[NUM_SKILLS]);/* shadow register. if != exp. obj update client */
188
189 bool ACC (RW, force_face0); // client workaround for buggy checksum calculation in gcfclient
190 bool ACC (RW, force_bad_checksum); // client workaround for buggy checksum calculation in gcfclient
191 bool ACC (RW, force_image_newmap); // client workaround for client not redrawing map on image upload
192 bool ACC (RW, enable_bg_scrub); // client workaround, gcfclient becomes unusable if you send it images
193
194 bool ACC (RW, afk); /* player is afk */
118 bool ACC (RW, facecache); /* If true, client is caching images */ 195 bool ACC (RW, facecache); /* If true, client is caching images */
119 bool ACC (RW, sent_scroll); 196 bool ACC (RW, sent_scroll);
120 bool ACC (RW, sound); /* does the client want sound */ 197 bool ACC (RW, sound); /* does the client want sound */
121 bool ACC (RW, exp64); /* Client wants 64 bit exp data, as well as skill data */
122 bool ACC (RW, newmapcmd); /* Send newmap command when entering new map SMACFIGGEN */ 198 bool ACC (RW, newmapcmd); /* Send newmap command when entering new map SMACFIGGEN */
123 bool ACC (RW, plugincmd); /* CF+ extend the protocol through a plug-in */ 199 bool ACC (RW, plugincmd); // extend the protocol through a plug-in */
124 bool ACC (RW, mapinfocmd); /* CF+ return map info and send map change info */ 200 bool ACC (RW, mapinfocmd); // return map info and send map change info
125 bool ACC (RW, extcmd); /* CF+ call into extensions/plugins */ 201 uint8_t ACC (RW, extcmd); // call into extensions/plugins
126 bool ACC (RW, extmap); /* CF+ extend map comamnd with extra data */ 202 bool ACC (RW, extmap); // extend map comamnd with extra data
127 bool ACC (RW, buggy_mapscroll); /* CF+ client crashes on large mapscrolls */ 203 bool ACC (RW, buggy_mapscroll); // client crashes on large mapscrolls
128 bool ACC (RW, darkness); /* True if client wants darkness information */ 204 bool ACC (RW, darkness); /* True if client wants darkness information */
129 bool ACC (RW, image2); /* Client wants image2/face2 commands */ 205 bool ACC (RW, image2); /* Client wants image2/face2 commands */
206 uint8_t ACC (RW, fxix); // client implements fx and ix (face|image extended) commands
130 bool ACC (RW, update_look); /* If true, we need to send the look window */ 207 bool ACC (RW, update_look); /* If true, we need to send the look window */
131 bool ACC (RW, has_readable_type); /* If true client accept additional text information */ 208 bool ACC (RW, has_readable_type); /* If true client accept additional text information */
132 /* used to arrange text in books, scrolls, or scripted dialogs */ 209 /* used to arrange text in books, scrolls, or scripted dialogs */
133 bool ACC (RW, monitor_spells); /* Client wishes to be informed when their spell list changes */ 210 bool ACC (RW, monitor_spells); /* Client wishes to be informed when their spell list changes */
134 bool ACC (RW, ext_mapinfos); /* If true client accept additionnal info on maps */ 211 bool ACC (RW, ext_mapinfos); /* If true client accepts additional info on maps */
135 /* Below are flags for extedend infos to pass to client 212 /* Below are flags for extedend infos to pass to client
136 * with S->C mapextended command */ 213 * with S->C mapextended command */
137 bool ACC (RW, EMI_smooth); /* Send smooth in extendmapinfos */ 214 bool ACC (RW, EMI_smooth); /* Send smooth in extendmapinfos */
215 bool ACC (RW, smoothing); // trt-style smoothing
216 bool ACC (RW, can_msg); // trt-style text messages
138 217
218 bool ACC (RW, force_newmap); // force a newmap before next map update
139 uint32 ACC (RW, supported_readables); /* each bit is a readable supported by client */ 219 uint32 ACC (RW, supported_readables); /* each bit is a readable supported by client */
140 uint32 ACC (RW, cs_version), ACC (RW, sc_version); /* versions of the client */ 220 uint32 ACC (RW, cs_version), ACC (RW, sc_version); /* versions of the client */
141 enum MapMode mapmode; /* Type of map commands the client wants. */
142 uint16 ACC (RW, look_position); /* start of drawing of look window */ 221 uint16 ACC (RW, look_position); /* start of drawing of look window */
222 uint16 ACC (RW, mss); // likely tcp maximum segment size
223 uint8 ACC (RW, mapmode); /* Type of map commands the client wants. */
143 uint8 ACC (RW, mapx), ACC (RW, mapy); /* How large a map the client wants */ 224 uint8 ACC (RW, mapx), ACC (RW, mapy); /* How large a map the client wants */
144 uint8 ACC (RW, itemcmd); /* What version of the 'item' protocol command to use */ 225 uint8 ACC (RW, itemcmd); /* What version of the 'item' protocol command to use */
145 uint8 ACC (RW, faceset); /* Set the client is using, default 0 */
146 226
147 maptile *ACC (RW, current_map); // CF+ last/current player map 227 maptile *ACC (RW, current_map); // CF+ last/current player map
228 region *ACC (RW, current_region); // CF+ last/current player region
148 int ACC (RW, current_x), ACC (RW, current_y); // CF+ last/current map position 229 int ACC (RW, current_x), ACC (RW, current_y); // CF+ last/current map position
149 char ACC (RW, version)[64]; // CF+ client name/version 230 shstr ACC (RW, version); // CF+ client name/version
231 uint8 ACC (RW, faceset); // CF+ selected faceset
150 232
233 tstamp ACC (RW, last_send); // last data send on socket.
234
235 int ACC (RW, rate_avail); // current rate balance
236 int ACC (RW, max_rate); // max. # of bytes to send per tick
237 faceidx ACC (RW, scrub_idx); // which face to send next
238 int ACC (RW, bg_scrub); // how many ticks till the next background face send
239
240 struct tcp_info tcpi;
241 tstamp next_rate_adjust;
242
243 unordered_vector<char *> mapinfo_queue;
244 void mapinfo_queue_clear ();
245 void mapinfo_queue_run ();
246 bool mapinfo_try (char *buf);
247
248 struct ixsend {
249 int16_t pri; // unused
250 faceidx idx;
251 uint32_t ofs; // if != 0, need to send remaining bytes of partial_face
252 };
253 std::vector<ixsend, slice_allocator<ixsend> > ixface; // which faces to send to the client using ix
254
255 std::vector<faceidx, slice_allocator<faceidx> > fxface; // which faces to send using fx
256 MTH void flush_fx (); // send fx if required
257
258 void do_destroy ();
259 void gather_callbacks (AV *&callbacks, event_type event) const;
260
261 iow socket_ev; void socket_cb (iow &w, int revents);
262
263 std::deque< command, slice_allocator<command> > cmd_queue;
264
265 // large structures at the end please
266 struct Map lastmap;
267 std::bitset<MAXANIMNUM> anims_sent;
268 std::bitset<MAX_FACES> faces_sent;
269 std::bitset<FT_NUM> fx_want;
270
151 /* If we get an incomplete packet, this is used to hold the data. */ 271 // if we get an incomplete packet, this is used to hold the data.
272 // we add 2 byte for the header, one for the trailing 0 byte
152 uint8 inbuf[MAXSOCKBUF]; 273 uint8 inbuf[MAXSOCKBUF + 2 + 1];
153 player *ACC (RO, pl);//TODO should not be here, preferably
154 274
155 int ACC (RW, rtt), ACC (RW, rttvar); /* round-trip time and -variance, if known */ 275 enum { MSG_BUF_SIZE = 80, MSG_BUF_COUNT = 10 };
276 struct msg_buf
277 {
278 tick_t expire;
279 int len;
280 int count;
281 char msg[MSG_BUF_SIZE];
282 } msgbuf[MSG_BUF_COUNT];
156 283
157 iow socket_ev; void socket_cb (iow &w, int got); 284 MTH bool msg_suppressed (const char *msg);
158 285
286 bool may_execute (const packet_type *pkt) const;
287 void execute (const packet_type *pkt, char *data, int datalen);
288
289 void queue_command (packet_type *handler, char *data, int datalen);
290 MTH bool handle_command ();
291 // resets movement state
292 MTH void reset_state ();
293 // resets variable data used to send stat diffs
294 MTH void reset_stats ();
295
296 MTH bool handle_packet ();
297 int next_packet (); // returns length of packet or 0
298 void skip_packet (int len); // we have processed the packet, skip it
299
300 MTH void flush ();
301 MTH void write_outputbuffer ();
302 MTH int outputbuffer_len () const { return outputbuffer.len; }
303 void send (void *buf_, int len);
304
305 void send_packet (const char *buf);
306 void send_packet (const char *buf, int len);
307 void send_packet_printf (const char *format, ...);
308 void send_packet (packet &sl);
309
310 void send_drawinfo (const char *msg, int flags = NDI_BLACK);
311
312 MTH void send_face (faceidx facenum, int pri = 0);
313 MTH void send_image (faceidx facenum);
314 MTH void send_faces (object *ob);
315 MTH void send_animation (short anim_num);
316 void send_msg (int color, const char *type, const char *msg);
317
318 MTH void play_sound (faceidx sound, int dx = 0, int dy = 0);
319 // called when something under the player changes
320 MTH void floorbox_update () { update_look = 1; }
321 // called when the player has been moved
322 MTH void floorbox_reset () { look_position = 0; floorbox_update (); }
323
324 MTH void tick (); // called every server tick to do housekeeping etc.
325
326 MTH static client *create (int fd, const char *peername);
327 MTH static void clock ();
328 MTH static void flush_sockets ();
329
330protected:
159 client (int fd, const char *from_ip); 331 client (int fd, const char *from_ip);
160 ~client (); 332 ~client ();
161
162 int read_packet (); // returns length of packet or 0
163 void skip_packet (int len); // we have processed the packet, skip it
164
165 void flush ();
166 void write_outputbuffer ();
167 void send (void *buf_, int len);
168
169 void send_packet (const char *buf);
170 void send_packet (const char *buf, int len);
171 void send_packet (packet &sl);
172}; 333};
334
335#if FOR_PERL
336 ACC (RW, tcpi.tcpi_state);
337 ACC (RW, tcpi.tcpi_ca_state);
338 ACC (RW, tcpi.tcpi_retransmits);
339 ACC (RW, tcpi.tcpi_probes);
340 ACC (RW, tcpi.tcpi_backoff);
341 ACC (RW, tcpi.tcpi_options);
342 ACC (RO, tcpi.tcpi_snd_wscale);
343 ACC (RO, tcpi.tcpi_rcv_wscale);
344 ACC (RW, tcpi.tcpi_rto);
345 ACC (RW, tcpi.tcpi_ato);
346 ACC (RW, tcpi.tcpi_snd_mss);
347 ACC (RW, tcpi.tcpi_rcv_mss);
348 ACC (RW, tcpi.tcpi_unacked);
349 ACC (RW, tcpi.tcpi_sacked);
350 ACC (RW, tcpi.tcpi_lost);
351 ACC (RW, tcpi.tcpi_retrans);
352 ACC (RW, tcpi.tcpi_fackets);
353 ACC (RW, tcpi.tcpi_last_data_sent);
354 ACC (RW, tcpi.tcpi_last_ack_sent);
355 ACC (RW, tcpi.tcpi_last_data_recv);
356 ACC (RW, tcpi.tcpi_last_ack_recv);
357 ACC (RW, tcpi.tcpi_pmtu);
358 ACC (RW, tcpi.tcpi_rcv_ssthresh);
359 ACC (RW, tcpi.tcpi_rtt);
360 ACC (RW, tcpi.tcpi_rttvar);
361 ACC (RW, tcpi.tcpi_snd_ssthresh);
362 ACC (RW, tcpi.tcpi_snd_cwnd);
363 ACC (RW, tcpi.tcpi_advmss);
364 ACC (RW, tcpi.tcpi_reordering);
365#endif
173 366
174#define CLIENT_SUPPORT_READABLES(__sockPtr,__type)\ 367#define CLIENT_SUPPORT_READABLES(__sockPtr,__type)\
175 ( ((__type)>0) &&\ 368 ( ((__type)>0) &&\
176 ((__sockPtr)->has_readable_type) && \ 369 ((__sockPtr)->has_readable_type) && \
177 ((__sockPtr)->supported_readables & (1<<(__type))) ) 370 ((__sockPtr)->supported_readables & (1<<(__type))) )
178
179/* Bitmask for the faces_sent[] array - what
180 * portion of the face have we sent?
181 */
182#define NS_FACESENT_FACE 0x1
183#define NS_FACESENT_SMOOTH 0x2
184 371
185/* Constants in the form EMI_ is for extended map infos. 372/* Constants in the form EMI_ is for extended map infos.
186 * Even if the client select the additionnal infos it wants 373 * Even if the client select the additionnal infos it wants
187 * on the map, there may exist cases where this whole info 374 * on the map, there may exist cases where this whole info
188 * is not given in one buch but in separate bunches. This 375 * is not given in one buch but in separate bunches. This
210 397
211#define FACE_TYPES 1 398#define FACE_TYPES 1
212#define PNG_FACE_INDEX 0 399#define PNG_FACE_INDEX 0
213 400
214#define VERSION_CS 1023 /* version >= 1023 understand setup cmd */ 401#define VERSION_CS 1023 /* version >= 1023 understand setup cmd */
215#define VERSION_SC 1027 402#define VERSION_SC 1026
403//#define VERSION_SC 1027 // requestinfo image_info and image_sums, makes extending faces on the fly impossible
216#define VERSION_INFO "Crossfire+ Server" 404#define VERSION_INFO "Deliantra Server"
217 405
218typedef unordered_vector<client *> sockvec; 406typedef object_vector<client, &client::active> sockvec;
219 407
220extern sockvec clients; 408extern sockvec clients;
221extern int nrofpixmaps; //TODO: hrmpf, should go 409
410#define for_all_clients(var) \
411 for (int _i = 0; _i < clients.size (); ++_i) \
412 statementvar (client *, var, clients [_i])
222 413
223#endif 414#endif
224 415

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines