ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/loop.C
Revision: 1.102
Committed: Sun Nov 18 00:37:11 2018 UTC (5 years, 6 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.101: +6 -4 lines
Log Message:
some range based for loops

File Contents

# User Rev Content
1 elmex 1.1 /*
2 root 1.70 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 root 1.93 *
4 root 1.101 * Copyright (©) 2018 Marc Alexander Lehmann / the Deliantra team
5 root 1.100 * Copyright (©) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
6 root 1.93 *
7 root 1.82 * Deliantra is free software: you can redistribute it and/or modify it under
8     * the terms of the Affero GNU General Public License as published by the
9     * Free Software Foundation, either version 3 of the License, or (at your
10     * option) any later version.
11 root 1.93 *
12 root 1.55 * This program is distributed in the hope that it will be useful,
13     * but WITHOUT ANY WARRANTY; without even the implied warranty of
14     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15     * GNU General Public License for more details.
16 root 1.93 *
17 root 1.82 * You should have received a copy of the Affero GNU General Public License
18     * and the GNU General Public License along with this program. If not, see
19     * <http://www.gnu.org/licenses/>.
20 root 1.93 *
21 root 1.70 * The authors can be reached via e-mail to <support@deliantra.net>
22 pippijn 1.39 */
23 elmex 1.1
24     /**
25     * \file
26     * Main client/server loops.
27     *
28     * \date 2003-12-02
29     *
30     * loop.c mainly deals with initialization and higher level socket
31 root 1.26 * maintanance (checking for lost connections and if data has arrived.)
32 elmex 1.1 */
33    
34     #include <global.h>
35 root 1.10 #include <sproto.h>
36     #include <sockproto.h>
37 elmex 1.1
38 pippijn 1.8 #include <sys/types.h>
39     #include <sys/time.h>
40     #include <sys/socket.h>
41     #include <netinet/in.h>
42     #include <netdb.h>
43 elmex 1.1
44 root 1.16 #include <unistd.h>
45     #include <arpa/inet.h>
46 elmex 1.1
47 root 1.51 #define BG_SCRUB_RATE 4 // how often to send a face in the background
48    
49 root 1.65 #define MAX_QUEUE_DEPTH 50
50 root 1.51 #define MAX_QUEUE_BACKLOG 3.
51 elmex 1.1
52 root 1.21 void
53     client::reset_state ()
54     {
55     if (!pl)
56     return;
57    
58     pl->run_on = 0;
59     pl->fire_on = 0;
60     }
61 elmex 1.1
62 root 1.20 void
63     client::queue_command (packet_type *handler, char *data, int datalen)
64 root 1.6 {
65 root 1.51 tstamp stamp = NOW;
66 elmex 1.1
67 root 1.20 if (cmd_queue.size () >= MAX_QUEUE_DEPTH)
68 root 1.21 {
69     reset_state ();
70 root 1.99 send_packet_printf ("msg %d log command queue overflow, ignoring.", NDI_RED);
71 root 1.21 }
72 root 1.20 else
73 root 1.24 {
74 root 1.71 cmd_queue.resize (cmd_queue.size () + 1);
75 root 1.24 command &cmd = cmd_queue.back ();
76     cmd.stamp = stamp;
77     cmd.handler = handler;
78 root 1.25 cmd.data = salloc<char> (datalen + 1, data);
79 root 1.24 cmd.datalen = datalen;
80     }
81 root 1.20 }
82 elmex 1.1
83 root 1.20 bool
84     client::handle_command ()
85 elmex 1.1 {
86 root 1.48 if (!cmd_queue.empty ()
87     && state == ST_PLAYING
88     && pl->ob->speed_left > 0.f)
89 root 1.26 {
90     command &cmd = cmd_queue.front ();
91 root 1.6
92 root 1.51 if (cmd.stamp + MAX_QUEUE_BACKLOG < NOW)
93 root 1.26 {
94 root 1.79 reset_state (); // the command might actually reset some movement state etc.
95    
96     if (pl)
97     pl->failmsg (
98     "Cannot keep up with your commands, ignoring them! "
99     "H<Your character cannot keep up with the instructions you give G<him|her>. "
100     "Try issuing commands slower, or, if G<he|she> is incapacitated (paralyzed and so on), "
101     "wait till your character can act again.\n\nIf G<he|she> is permanently stuck, then "
102     "try the B<suicide> command (or use B<chat> to ask somebody to B<invite> you out).>"
103     );
104 root 1.26 }
105     else
106 root 1.48 execute (cmd.handler, cmd.data, cmd.datalen);
107    
108 root 1.71 sfree<char> (cmd.data, cmd.datalen + 1);
109 root 1.48 cmd_queue.pop_front ();
110     return true;
111 root 1.21 }
112 root 1.48 else
113     return false;
114 elmex 1.1 }
115    
116 root 1.6 void
117 root 1.42 client::tick ()
118     {
119 root 1.94 if (destroyed ())
120 root 1.42 return;
121 root 1.37
122 root 1.94 if (pl)
123     {
124     pl->dirty = true;
125 root 1.75
126 root 1.94 /* Update the players stats once per tick. More efficient than
127     * sending them whenever they change, and probably just as useful
128     */
129     pl->need_updated_stats ();
130     esrv_update_stats (pl);
131 root 1.85
132 root 1.94 if (pl->ns->update_spells)
133     esrv_update_spells (pl);
134 root 1.20
135 root 1.94 sint32 weight = pl->ob->client_weight ();
136 root 1.74
137 root 1.94 if (last_weight != weight)
138     {
139     pl->ob->update_stats ();
140     esrv_update_item (UPD_WEIGHT, pl->ob, pl->ob);
141     }
142 root 1.6
143 root 1.94 draw_client_map (pl);
144 root 1.20
145 root 1.94 if (update_look)
146     esrv_draw_look (pl);
147 root 1.41
148 root 1.94 mapinfo_queue_run ();
149     }
150 root 1.69
151 root 1.66 #if HAVE_TCP_INFO
152     // check time of last ack, and, if too old, kill connection
153     socklen_t len = sizeof (tcpi);
154    
155     if (!getsockopt (fd, IPPROTO_TCP, TCP_INFO, &tcpi, &len) && len == sizeof (tcpi))
156     {
157     if (tcpi.tcpi_snd_mss)
158     mss = tcpi.tcpi_snd_mss;
159    
160     #if 0
161     fprintf (stderr, "uack %d ack %d lost %d ret %d fack %d sst %d cwnd %d mss %d pmtu %d advmss %d EXC %d\n",
162     tcpi.tcpi_unacked,
163     tcpi.tcpi_sacked,
164     tcpi.tcpi_lost,
165     tcpi.tcpi_retrans,
166     tcpi.tcpi_fackets,
167     tcpi.tcpi_snd_ssthresh, tcpi.tcpi_snd_cwnd, tcpi.tcpi_advmss, tcpi.tcpi_pmtu, tcpi.tcpi_advmss,
168    
169     tcpi.tcpi_snd_cwnd - (tcpi.tcpi_unacked - tcpi.tcpi_sacked));
170     #endif
171    
172 root 1.80 // fast-time-out a player by checking for missing acks
173 root 1.66 // do this only when player is active
174     if (pl && pl->active
175 root 1.81 && tcpi.tcpi_last_ack_recv > int (socket_timeout * 1000))
176 root 1.66 {
177     send_msg (NDI_RED | NDI_REPLY, "connection-timeout", "safety disconnect due to tcp/ip timeout (no packets received)");
178     write_outputbuffer ();
179    
180     LOG (llevDebug, "connection on fd %d closed due to ack timeout (%u/%u/%u)\n", fd,
181     (unsigned)tcpi.tcpi_last_ack_recv, (unsigned)tcpi.tcpi_last_data_sent, (unsigned)tcpi.tcpi_unacked);
182     destroy ();
183     }
184     }
185     #endif
186    
187 root 1.90 // limit budget surplus/deficit by one mss, add per-tick budget
188     rate_avail = min (rate_avail, mss) + max_rate;
189 root 1.66
190     int max_send = rate_avail;
191    
192     #if HAVE_TCP_INFO
193     // further restrict the available bandwidth by the excess bandwidth available
194 root 1.90 min_it (max_send, (tcpi.tcpi_snd_cwnd - tcpi.tcpi_unacked + tcpi.tcpi_sacked) * mss);
195 root 1.66 #endif
196    
197 root 1.78 // round to next-lowest mss
198     max_send -= max_send % mss;
199 root 1.66
200 root 1.65 if (ixface.empty ())
201     {
202     // regularly send a new face when queue is empty
203 root 1.78 if (bg_scrub && !--bg_scrub)
204 root 1.65 while (scrub_idx < faces.size () - 1)
205     {
206     ++scrub_idx;
207    
208     if (!faces_sent [scrub_idx])
209     if (faceinfo *f = face_info (scrub_idx))
210 root 1.97 if (f->type == FT_IMAGE || f->type == FT_SOUND) // only scrub faces and sounds for now
211 root 1.65 {
212     send_face (scrub_idx, -120);
213     flush_fx ();
214    
215 root 1.66 bg_scrub = 1; // send up to one fx per tick, unless an image was requested
216 root 1.65 break;
217     }
218     }
219     }
220     else
221     {
222 root 1.66 bg_scrub = BG_SCRUB_RATE;
223 root 1.65
224 root 1.66 for (;;)
225     {
226     int avail = max_send - outputbuffer_len ();
227 root 1.64
228 root 1.66 if (avail <= 0)
229     break;
230 root 1.65
231     ixsend &ix = ixface.back ();
232 root 1.44
233 root 1.95 // estimate the packet header overhead "ix " + idx + (new)ofs
234     int pktlen = 3 + ber32::encoded_size (ix.idx) + ber32::encoded_size (ix.ofs);
235     int chunk = min (avail - packet::hdrlen, MAXSOCKBUF) - pktlen;
236    
237     // only transfer something if the amount of data transferred
238     // has a healthy relation to the header overhead
239     if (chunk < 64)
240     break;
241 root 1.46
242 root 1.95 chunk = min (chunk, (int)ix.ofs);
243 root 1.46
244 root 1.95 ix.ofs -= chunk;
245 root 1.46
246 root 1.95 //fprintf (stderr, "i%dx %6d: %5d+%4d (%4d)\n", fxix, ix.idx,ix.ofs,chunk, ixface.size());//D
247 root 1.59
248 root 1.95 packet sl ("ix");
249 root 1.46
250 root 1.95 sl << ber32 (ix.idx)
251     << ber32 (ix.ofs)
252 root 1.101 << data_n (ix.data + ix.ofs, chunk);
253 root 1.46
254 root 1.95 send_packet (sl);
255 root 1.44
256 root 1.65 if (!ix.ofs)
257     {
258 root 1.96 ix_pop ();
259 root 1.56
260 root 1.65 if (ixface.empty ())
261     break;
262 root 1.54 }
263 root 1.44 }
264     }
265 root 1.66
266     rate_avail -= outputbuffer_len ();
267     }
268    
269     void
270 root 1.86 client::flush_sockets ()
271 root 1.66 {
272 root 1.102 for (auto &&i : clients)
273     i->flush ();
274 root 1.66 }
275    
276     void
277 root 1.86 client::clock ()
278 root 1.66 {
279 root 1.102 for (auto &&i : clients)
280     i->tick ();
281 root 1.66
282     // give them all the same chances
283     flush_sockets ();
284    
285     //TODO: should not be done here, either
286 root 1.102 // iteratoes over indices as the vector can shrink.
287     // this might skip clients,. but we re-do it every tick...
288 root 1.66 for (unsigned i = 0; i < clients.size (); ++i)
289     clients[i]->refcnt_chk ();
290 elmex 1.1 }
291 root 1.43