ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/main.C
(Generate patch)

Comparing deliantra/server/server/main.C (file contents):
Revision 1.22 by root, Sat Sep 9 21:48:29 2006 UTC vs.
Revision 1.25 by root, Tue Sep 12 23:22:32 2006 UTC

1/*
2 * static char *rcsid_main_c =
3 * "$Id: main.C,v 1.22 2006/09/09 21:48:29 root Exp $";
4 */
5
6/* 1/*
7 CrossFire, A Multiplayer game for X-windows 2 CrossFire, A Multiplayer game for X-windows
8 3
9 Copyright (C) 2001-2003 Mark Wedel & Crossfire Development Team 4 Copyright (C) 2001-2003 Mark Wedel & Crossfire Development Team
10 Copyright (C) 1992 Frank Tore Johansen 5 Copyright (C) 1992 Frank Tore Johansen
29#include <global.h> 24#include <global.h>
30#include <object.h> 25#include <object.h>
31#include <tod.h> 26#include <tod.h>
32 27
33#ifdef HAVE_DES_H 28#ifdef HAVE_DES_H
34#include <des.h> 29# include <des.h>
35#else 30#else
36# ifdef HAVE_CRYPT_H 31# ifdef HAVE_CRYPT_H
37# include <crypt.h> 32# include <crypt.h>
38# endif 33# endif
39#endif 34#endif
40 35
41#ifndef __CEXTRACT__ 36#ifndef __CEXTRACT__
42#include <sproto.h> 37# include <sproto.h>
43#endif 38#endif
44 39
45#ifdef HAVE_TIME_H 40#ifdef HAVE_TIME_H
46#include <time.h> 41# include <time.h>
47#endif 42#endif
48 43
49#include <../random_maps/random_map.h> 44#include <../random_maps/random_map.h>
50#include <../random_maps/rproto.h> 45#include <../random_maps/rproto.h>
51#include "path.h" 46#include "path.h"
53static char days[7][4] = { 48static char days[7][4] = {
54 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 49 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
55}; 50};
56 51
57void 52void
58version (object * op) 53version (object *op)
59{ 54{
60 if (op != NULL) 55 if (op != NULL)
61 clear_win_info (op); 56 clear_win_info (op);
62 57
63 new_draw_info_format (NDI_UNIQUE, 0, op, "This is Crossfire v%s", VERSION); 58 new_draw_info_format (NDI_UNIQUE, 0, op, "This is Crossfire v%s", VERSION);
116 new_draw_info (NDI_UNIQUE, 0, op, "Mårten Woxberg [maxmc@telia.com]"); 111 new_draw_info (NDI_UNIQUE, 0, op, "Mårten Woxberg [maxmc@telia.com]");
117 new_draw_info (NDI_UNIQUE, 0, op, "And many more!"); 112 new_draw_info (NDI_UNIQUE, 0, op, "And many more!");
118} 113}
119 114
120void 115void
121info_keys (object * op) 116info_keys (object *op)
122{ 117{
123 clear_win_info (op); 118 clear_win_info (op);
124 new_draw_info (NDI_UNIQUE, 0, op, "Push `hjklynub' to walk in a direction."); 119 new_draw_info (NDI_UNIQUE, 0, op, "Push `hjklynub' to walk in a direction.");
125 new_draw_info (NDI_UNIQUE, 0, op, "Shift + dir = fire, Ctrl + dir = run"); 120 new_draw_info (NDI_UNIQUE, 0, op, "Shift + dir = fire, Ctrl + dir = run");
126 new_draw_info (NDI_UNIQUE, 0, op, "(To fire at yourself, hit `.'"); 121 new_draw_info (NDI_UNIQUE, 0, op, "(To fire at yourself, hit `.'");
139 new_draw_info (NDI_UNIQUE, 0, op, "You can type a number before most commands."); 134 new_draw_info (NDI_UNIQUE, 0, op, "You can type a number before most commands.");
140 new_draw_info (NDI_UNIQUE, 0, op, "(For instance 3d drops 3 items.)"); 135 new_draw_info (NDI_UNIQUE, 0, op, "(For instance 3d drops 3 items.)");
141} 136}
142 137
143void 138void
144start_info (object * op) 139start_info (object *op)
145{ 140{
146 char buf[MAX_BUF]; 141 char buf[MAX_BUF];
147 142
148 sprintf (buf, "Welcome to Crossfire, v%s!", VERSION); 143 sprintf (buf, "Welcome to Crossfire, v%s!", VERSION);
149 new_draw_info (NDI_UNIQUE, 0, op, buf); 144 new_draw_info (NDI_UNIQUE, 0, op, buf);
174 if (salt == NULL) 169 if (salt == NULL)
175 s[0] = c[RANDOM () % (int) strlen (c)], s[1] = c[RANDOM () % (int) strlen (c)]; 170 s[0] = c[RANDOM () % (int) strlen (c)], s[1] = c[RANDOM () % (int) strlen (c)];
176 else 171 else
177 s[0] = salt[0], s[1] = salt[1]; 172 s[0] = salt[0], s[1] = salt[1];
178 173
179# ifdef HAVE_LIBDES 174# ifdef HAVE_LIBDES
180 return (char *) des_crypt (str, s); 175 return (char *) des_crypt (str, s);
181# endif 176# endif
182 /* Default case - just use crypt */ 177 /* Default case - just use crypt */
183 return (char *) crypt (str, s); 178 return (char *) crypt (str, s);
184#endif 179#endif
185} 180}
186 181
194 * savebed. We do some error checking - its possible that the 189 * savebed. We do some error checking - its possible that the
195 * savebed map may no longer exist, so we make sure the player 190 * savebed map may no longer exist, so we make sure the player
196 * goes someplace. 191 * goes someplace.
197 */ 192 */
198void 193void
199enter_player_savebed (object * op) 194enter_player_savebed (object *op)
200{ 195{
201 mapstruct *oldmap = op->map; 196 mapstruct *oldmap = op->map;
202 object *tmp; 197 object *tmp;
203 198
204 tmp = get_object (); 199 tmp = get_object ();
229 224
230/* All this really is is a glorified remove_object that also updates 225/* All this really is is a glorified remove_object that also updates
231 * the counts on the map if needed. 226 * the counts on the map if needed.
232 */ 227 */
233void 228void
234leave_map (object * op) 229leave_map (object *op)
235{ 230{
236 mapstruct *oldmap = op->map; 231 mapstruct *oldmap = op->map;
237 232
238 remove_ob (op); 233 remove_ob (op);
239 234
254 * player to - it could be the map he just came from if the load failed for 249 * player to - it could be the map he just came from if the load failed for
255 * whatever reason. If default map coordinates are to be used, then 250 * whatever reason. If default map coordinates are to be used, then
256 * the function that calls this should figure them out. 251 * the function that calls this should figure them out.
257 */ 252 */
258static void 253static void
259enter_map (object * op, mapstruct * newmap, int x, int y) 254enter_map (object *op, mapstruct *newmap, int x, int y)
260{ 255{
261 mapstruct *oldmap = op->map; 256 mapstruct *oldmap = op->map;
262 257
263 if (out_of_map (newmap, x, y)) 258 if (out_of_map (newmap, x, y))
264 { 259 {
282 * if other spaces around are available. 277 * if other spaces around are available.
283 * Note that for the second and third calls, we could start at a position other 278 * Note that for the second and third calls, we could start at a position other
284 * than one, but then we could end up on the other side of walls and so forth. 279 * than one, but then we could end up on the other side of walls and so forth.
285 */ 280 */
286 int i = find_free_spot (op, newmap, x, y, 1, SIZEOFFREE1 + 1); 281 int i = find_free_spot (op, newmap, x, y, 1, SIZEOFFREE1 + 1);
282
287 if (i == -1) 283 if (i == -1)
288 { 284 {
289 i = find_free_spot (op, newmap, x, y, 1, SIZEOFFREE2 + 1); 285 i = find_free_spot (op, newmap, x, y, 1, SIZEOFFREE2 + 1);
290 if (i == -1) 286 if (i == -1)
291 i = find_free_spot (op, newmap, x, y, 1, SIZEOFFREE); 287 i = find_free_spot (op, newmap, x, y, 1, SIZEOFFREE);
337 /* Update any golems */ 333 /* Update any golems */
338 if (op->type == PLAYER && op->contr->ranges[range_golem] != NULL) 334 if (op->type == PLAYER && op->contr->ranges[range_golem] != NULL)
339 { 335 {
340 int i = find_free_spot (op->contr->ranges[range_golem], newmap, 336 int i = find_free_spot (op->contr->ranges[range_golem], newmap,
341 x, y, 1, SIZEOFFREE); 337 x, y, 1, SIZEOFFREE);
338
342 remove_ob (op->contr->ranges[range_golem]); 339 remove_ob (op->contr->ranges[range_golem]);
343 if (i == -1) 340 if (i == -1)
344 { 341 {
345 remove_friendly_object (op->contr->ranges[range_golem]); 342 remove_friendly_object (op->contr->ranges[range_golem]);
346 free_object (op->contr->ranges[range_golem]); 343 free_object (op->contr->ranges[range_golem]);
348 op->contr->golem_count = 0; 345 op->contr->golem_count = 0;
349 } 346 }
350 else 347 else
351 { 348 {
352 object *tmp; 349 object *tmp;
350
353 for (tmp = op->contr->ranges[range_golem]; tmp != NULL; tmp = tmp->more) 351 for (tmp = op->contr->ranges[range_golem]; tmp != NULL; tmp = tmp->more)
354 { 352 {
355 tmp->x = x + freearr_x[i] + (tmp->arch == NULL ? 0 : tmp->arch->clone.x); 353 tmp->x = x + freearr_x[i] + (tmp->arch == NULL ? 0 : tmp->arch->clone.x);
356 tmp->y = y + freearr_y[i] + (tmp->arch == NULL ? 0 : tmp->arch->clone.y); 354 tmp->y = y + freearr_y[i] + (tmp->arch == NULL ? 0 : tmp->arch->clone.y);
357 tmp->map = newmap; 355 tmp->map = newmap;
383 } 381 }
384 } 382 }
385} 383}
386 384
387void 385void
388set_map_timeout (mapstruct * oldmap) 386set_map_timeout (mapstruct *oldmap)
389{ 387{
390#if MAP_MAXTIMEOUT 388#if MAP_MAXTIMEOUT
391 oldmap->timeout = MAP_TIMEOUT (oldmap); 389 oldmap->timeout = MAP_TIMEOUT (oldmap);
392 /* Do MINTIMEOUT first, so that MAXTIMEOUT is used if that is 390 /* Do MINTIMEOUT first, so that MAXTIMEOUT is used if that is
393 * lower than the min value. 391 * lower than the min value.
394 */ 392 */
395#if MAP_MINTIMEOUT 393# if MAP_MINTIMEOUT
396 if (oldmap->timeout < MAP_MINTIMEOUT) 394 if (oldmap->timeout < MAP_MINTIMEOUT)
397 { 395 {
398 oldmap->timeout = MAP_MINTIMEOUT; 396 oldmap->timeout = MAP_MINTIMEOUT;
399 } 397 }
400#endif 398# endif
401 if (oldmap->timeout > MAP_MAXTIMEOUT) 399 if (oldmap->timeout > MAP_MAXTIMEOUT)
402 { 400 {
403 oldmap->timeout = MAP_MAXTIMEOUT; 401 oldmap->timeout = MAP_MAXTIMEOUT;
404 } 402 }
405#else 403#else
414 */ 412 */
415char * 413char *
416clean_path (const char *file) 414clean_path (const char *file)
417{ 415{
418 static char newpath[MAX_BUF], *cp; 416 static char newpath[MAX_BUF], *cp;
417 assign (newpath, file);
419 418
420 strncpy (newpath, file, MAX_BUF - 1);
421 newpath[MAX_BUF - 1] = '\0';
422 for (cp = newpath; *cp != '\0'; cp++) 419 for (cp = newpath; *cp != '\0'; cp++)
423 {
424 if (*cp == '/') 420 if (*cp == '/')
425 *cp = '_'; 421 *cp = '_';
426 } 422
427 return newpath; 423 return newpath;
428} 424}
429 425
430 426
431/* unclean_path takes a path and replaces all _ with / 427/* unclean_path takes a path and replaces all _ with /
439unclean_path (const char *src) 435unclean_path (const char *src)
440{ 436{
441 static char newpath[MAX_BUF], *cp; 437 static char newpath[MAX_BUF], *cp;
442 438
443 cp = strrchr (src, '/'); 439 cp = strrchr (src, '/');
444 if (cp) 440 assign (newpath, cp ? cp + 1 : src);
445 strncpy (newpath, cp + 1, MAX_BUF - 1);
446 else
447 strncpy (newpath, src, MAX_BUF - 1);
448 newpath[MAX_BUF - 1] = '\0';
449 441
450 for (cp = newpath; *cp != '\0'; cp++) 442 for (cp = newpath; *cp != '\0'; cp++)
451 {
452 if (*cp == '_') 443 if (*cp == '_')
453 *cp = '/'; 444 *cp = '/';
454 } 445
455 return newpath; 446 return newpath;
456} 447}
457 448
458 449
459/* The player is trying to enter a randomly generated map. In this case, generate the 450/* The player is trying to enter a randomly generated map. In this case, generate the
460 * random map as needed. 451 * random map as needed.
461 */ 452 */
462 453
463static void 454static void
464enter_random_map (object * pl, object * exit_ob) 455enter_random_map (object *pl, object *exit_ob)
465{ 456{
466 mapstruct *new_map; 457 mapstruct *new_map;
467 char newmap_name[HUGE_BUF], *cp; 458 char newmap_name[HUGE_BUF], *cp;
468 static int reference_number = 0; 459 static int reference_number = 0;
469 RMParms rp; 460 RMParms rp;
515 * the exit leading to it, that the exit will no longer work. 506 * the exit leading to it, that the exit will no longer work.
516 */ 507 */
517 if (new_map) 508 if (new_map)
518 { 509 {
519 int x, y; 510 int x, y;
511
520 x = EXIT_X (exit_ob) = MAP_ENTER_X (new_map); 512 x = EXIT_X (exit_ob) = MAP_ENTER_X (new_map);
521 y = EXIT_Y (exit_ob) = MAP_ENTER_Y (new_map); 513 y = EXIT_Y (exit_ob) = MAP_ENTER_Y (new_map);
522 EXIT_PATH (exit_ob) = newmap_name; 514 EXIT_PATH (exit_ob) = newmap_name;
523 strcpy (new_map->path, newmap_name); 515 strcpy (new_map->path, newmap_name);
524 enter_map (pl, new_map, x, y); 516 enter_map (pl, new_map, x, y);
528/* The player is trying to enter a non-randomly generated template map. In this 520/* The player is trying to enter a non-randomly generated template map. In this
529 * case, use a map file for a template 521 * case, use a map file for a template
530 */ 522 */
531 523
532static void 524static void
533enter_fixed_template_map (object * pl, object * exit_ob) 525enter_fixed_template_map (object *pl, object *exit_ob)
534{ 526{
535 mapstruct *new_map; 527 mapstruct *new_map;
536 char tmpnum[32], exitpath[HUGE_BUF], resultname[HUGE_BUF], tmpstring[HUGE_BUF], *sourcemap; 528 char tmpnum[32], exitpath[HUGE_BUF], resultname[HUGE_BUF], tmpstring[HUGE_BUF], *sourcemap;
537 const char *new_map_name; 529 const char *new_map_name;
538 530
620/* The player is trying to enter a randomly generated template map. In this 612/* The player is trying to enter a randomly generated template map. In this
621 * case, generate the map as needed. 613 * case, generate the map as needed.
622 */ 614 */
623 615
624static void 616static void
625enter_random_template_map (object * pl, object * exit_ob) 617enter_random_template_map (object *pl, object *exit_ob)
626{ 618{
627 mapstruct *new_map; 619 mapstruct *new_map;
628 char tmpnum[32], resultname[HUGE_BUF], tmpstring[HUGE_BUF]; 620 char tmpnum[32], resultname[HUGE_BUF], tmpstring[HUGE_BUF];
629 const char *new_map_name; 621 const char *new_map_name;
630 RMParms rp; 622 RMParms rp;
679 * the exit leading to it, that the exit will no longer work. 671 * the exit leading to it, that the exit will no longer work.
680 */ 672 */
681 if (new_map) 673 if (new_map)
682 { 674 {
683 int x, y; 675 int x, y;
676
684 x = EXIT_X (exit_ob) = MAP_ENTER_X (new_map); 677 x = EXIT_X (exit_ob) = MAP_ENTER_X (new_map);
685 y = EXIT_Y (exit_ob) = MAP_ENTER_Y (new_map); 678 y = EXIT_Y (exit_ob) = MAP_ENTER_Y (new_map);
686 new_map->templatemap = 1; 679 new_map->templatemap = 1;
687 enter_map (pl, new_map, x, y); 680 enter_map (pl, new_map, x, y);
688 } 681 }
690 683
691 684
692/* Code to enter/detect a character entering a unique map. 685/* Code to enter/detect a character entering a unique map.
693 */ 686 */
694static void 687static void
695enter_unique_map (object * op, object * exit_ob) 688enter_unique_map (object *op, object *exit_ob)
696{ 689{
697 char apartment[HUGE_BUF]; 690 char apartment[HUGE_BUF];
698 mapstruct *newmap; 691 mapstruct *newmap;
699 692
700 if (EXIT_PATH (exit_ob)[0] == '/') 693 if (EXIT_PATH (exit_ob)[0] == '/')
780 * Largely redone by MSW 2001-01-21 - this function was overly complex 773 * Largely redone by MSW 2001-01-21 - this function was overly complex
781 * and had some obscure bugs. 774 * and had some obscure bugs.
782 */ 775 */
783 776
784void 777void
785enter_exit (object * op, object * exit_ob) 778enter_exit (object *op, object *exit_ob)
786{ 779{
787#define PORTAL_DESTINATION_NAME "Town portal destination" /* this one should really be in a header file */ 780#define PORTAL_DESTINATION_NAME "Town portal destination" /* this one should really be in a header file */
788 object *tmp; 781 object *tmp;
782
789 /* It may be nice to support other creatures moving across 783 /* It may be nice to support other creatures moving across
790 * exits, but right now a lot of the code looks at op->contr, 784 * exits, but right now a lot of the code looks at op->contr,
791 * so thta is an RFE. 785 * so thta is an RFE.
792 */ 786 */
793 if (op->type != PLAYER) 787 if (op->type != PLAYER)
821 enter_unique_map (op, exit_ob); 815 enter_unique_map (op, exit_ob);
822 } 816 }
823 else 817 else
824 { 818 {
825 int x = EXIT_X (exit_ob), y = EXIT_Y (exit_ob); 819 int x = EXIT_X (exit_ob), y = EXIT_Y (exit_ob);
820
826 /* 'Normal' exits that do not do anything special 821 /* 'Normal' exits that do not do anything special
827 * Simple enough we don't need another routine for it. 822 * Simple enough we don't need another routine for it.
828 */ 823 */
829 mapstruct *newmap; 824 mapstruct *newmap;
825
830 if (exit_ob->map) 826 if (exit_ob->map)
831 { 827 {
832 newmap = ready_map_name (path_combine_and_normalize (exit_ob->map->path, EXIT_PATH (exit_ob)), 0); 828 newmap = ready_map_name (path_combine_and_normalize (exit_ob->map->path, EXIT_PATH (exit_ob)), 0);
833 /* Random map was previously generated, but is no longer about. Lets generate a new 829 /* Random map was previously generated, but is no longer about. Lets generate a new
834 * map. 830 * map.
968 964
969#if 0 // dead code, schmorp 965#if 0 // dead code, schmorp
970void 966void
971process_active_maps () 967process_active_maps ()
972{ 968{
973 for (mapstruct * map = first_map; map != NULL; map = map->next) 969 for (mapstruct *map = first_map; map != NULL; map = map->next)
974 if (map->in_memory == MAP_IN_MEMORY) 970 if (map->in_memory == MAP_IN_MEMORY)
975 if (players_on_map (map, TRUE)) 971 if (players_on_map (map, TRUE))
976 process_events (map); 972 process_events (map);
977} 973}
978#endif 974#endif
984 * objects have been updated, process_players2() does the processing that 980 * objects have been updated, process_players2() does the processing that
985 * is needed after the players have been updated. 981 * is needed after the players have been updated.
986 */ 982 */
987 983
988void 984void
989process_players1 (mapstruct * map) 985process_players1 (mapstruct *map)
990{ 986{
991 int flag; 987 int flag;
992 player *pl, *plnext; 988 player *pl, *plnext;
993 989
994 /* Basically, we keep looping until all the players have done their actions. */ 990 /* Basically, we keep looping until all the players have done their actions. */
1056 { 1052 {
1057 pl->ob->start_holding = 0; 1053 pl->ob->start_holding = 0;
1058 } 1054 }
1059 } 1055 }
1060 do_some_living (pl->ob); 1056 do_some_living (pl->ob);
1061 /* draw(pl->ob);*//* updated in socket code */ 1057 /* draw(pl->ob); *//* updated in socket code */
1062 } 1058 }
1063} 1059}
1064 1060
1065void 1061void
1066process_players2 (mapstruct * map) 1062process_players2 (mapstruct *map)
1067{ 1063{
1068 player *pl; 1064 player *pl;
1069 1065
1070 /* Then check if any players should use weapon-speed instead of speed */ 1066 /* Then check if any players should use weapon-speed instead of speed */
1071 for (pl = first_player; pl != NULL; pl = pl->next) 1067 for (pl = first_player; pl != NULL; pl = pl->next)
1101 pl->ob->speed_left = pl->ob->speed; 1097 pl->ob->speed_left = pl->ob->speed;
1102 } 1098 }
1103} 1099}
1104 1100
1105void 1101void
1106process_events (mapstruct * map) 1102process_events (mapstruct *map)
1107{ 1103{
1108 object *op; 1104 object *op;
1109 object *marker = get_object (); 1105 object *marker = get_object ();
1110 tag_t tag; 1106 tag_t tag;
1111 1107
1277 1273
1278/* clean up everything before exiting */ 1274/* clean up everything before exiting */
1279void 1275void
1280cleanup (void) 1276cleanup (void)
1281{ 1277{
1282 LOG (llevDebug, "Cleanup called. freeing data.\n"); 1278 LOG (llevDebug, "Cleanup called.\n");
1283 clean_tmp_files ();
1284 write_book_archive (); 1279 write_book_archive ();
1285#ifdef MEMORY_DEBUG 1280
1286 free_all_maps (); 1281 INVOKE_GLOBAL (CLEANUP);
1287 free_style_maps (); 1282
1288 free_all_object_data ();
1289 free_all_archs ();
1290 free_all_treasures ();
1291 free_all_images ();
1292 free_all_newserver ();
1293 free_all_recipes ();
1294 free_all_readable ();
1295 free_all_god ();
1296 free_all_anim ();
1297 /* See what the string data that is out there that hasn't been freed. */
1298/* LOG(llevDebug, ss_dump_table(0xff));*/
1299#endif
1300 exit (0); 1283 _exit (0);
1301} 1284}
1302 1285
1303void 1286void
1304leave (player * pl, int draw_exit) 1287leave (player *pl, int draw_exit)
1305{ 1288{
1306 if (pl != NULL) 1289 if (pl != NULL)
1307 { 1290 {
1308 /* We do this so that the socket handling routine can do the final 1291 /* We do this so that the socket handling routine can do the final
1309 * cleanup. We also leave that loop to actually handle the freeing 1292 * cleanup. We also leave that loop to actually handle the freeing
1324 INVOKE_PLAYER (LOGOUT, pl); 1307 INVOKE_PLAYER (LOGOUT, pl);
1325 LOG (llevInfo, "LOGOUT: Player named %s from ip %s\n", &pl->ob->name, pl->socket.host); 1308 LOG (llevInfo, "LOGOUT: Player named %s from ip %s\n", &pl->ob->name, pl->socket.host);
1326 } 1309 }
1327 1310
1328 char buf[MAX_BUF]; 1311 char buf[MAX_BUF];
1312
1329 sprintf (buf, "%s left the game.", &pl->ob->name); 1313 sprintf (buf, "%s left the game.", &pl->ob->name);
1330 new_draw_info (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, NULL, buf); 1314 new_draw_info (NDI_UNIQUE | NDI_ALL | NDI_DK_ORANGE, 5, NULL, buf);
1331 } 1315 }
1332 1316
1333 if (!QUERY_FLAG (pl->ob, FLAG_REMOVED)) 1317 if (!QUERY_FLAG (pl->ob, FLAG_REMOVED))

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines