mudOS源码 options.h配置详细选项
1 /* options.h配置详细选项 2 —————————————————————————- 3 将 MudOS 下载解压以后可以在相应目录的根目录中找到 options.h 这个文件。如果修 4 改了这个文件,那么每次都需要重新编译 MudOS 才能生效。重新编译之前,请执行 ma 5 ke clean 一下。 6 下面的 options.h 来自 MudOSv22.2b10,MudOS作者推荐在修改options.h 之前,先将 7 它备份成 local_options。新的特性将会自动对比local_options并且在编译的时候告知 8 。文中的选项定义并非最原始的定义。此文只是为了说明各个选项的大致含义。 9 options.h: defines for the compile-time configuration of the MudOS driver 10 */ 11 12 #ifndef _OPTIONS_H_ 13 #define _OPTIONS_H_ 14 15 /**************************************************************************** 16 如果你对自己的系统不熟悉,请使用缺省的定义 SYSMALLOC,同时将其它的undef掉。 17 你必须选择下面的其中一种定义,并且将其它的定义undef掉。 18 SYSMALLOC: 缺省选项,没有额外的系统开支,没有统计能力。 19 SMALLOC:速度比较快,但是开销相对大一些。 20 BSDMALLOC:速度更快,开销比较大。 21 ****************************************************************************/ 22 23 #define SYSMALLOC 24 #undef SMALLOC 25 #undef BSDMALLOC 26 27 /**************************************************************************** 28 WRAPPEDMALLOC:这个附加定义可以换来有限的统计能力,并且不会增加太多的系统开销。 29 DEBUGMALLOC:系统花费比较折中 30 ****************************************************************************/ 31 #undef WRAPPEDMALLOC 32 #undef DEBUGMALLOC 33 34 /**************************************************************************** 35 如果没有定义SMALLOC,请不要使用下面这个定义。 36 ****************************************************************************/ 37 #undef SBRK_OK 38 39 /**************************************************************************** 40 如果没有定义 BSDMALLOC or SMALLOC ,请不要使用下面这个定义 41 ****************************************************************************/ 42 #undef DO_MSTATS 43 44 /**************************************************************************** 45 DEBUGMALLOC定义的附加定义,用来强化DEBUGMALLOC,但是需要更多的开销。 46 set_malloc_mask(int) and debugmalloc(string,int)等efuns将可以被使用。 47 ****************************************************************************/ 48 #undef DEBUGMALLOC_EXTENSIONS 49 50 /**************************************************************************** 51 同上,是用来调试的良好定义,check_memory() efun将可以被使用。 52 ****************************************************************************/ 53 #undef CHECK_MEMORY 54 55 /**************************************************************************** 56 兼容性定义选项: 57 由于MudOS发展了很多年,所以历代MudOS为了保证原有的Lib可以不修改就被移植到新的 58 OS上面,增加了这些兼容性选项。如果你是根据OS撰写Lib的人,可以定义一些优化的选 59 项来设计自己的Lib,而不必考虑重写的问题。 60 以前的MudOS使用status这样子的定义词,这是很老的定义了,对于新的Lib设计而言, 61 这个定义毫无用处。 62 ****************************************************************************/ 63 #undef HAS_STATUS_TYPE 64 65 /**************************************************************************** 66 explode()选项,这个根据个人的习惯来定义吧 67 如果不定义下面两个选项, explode(“..x.y..z..”, “.”) 的结果是 ({ “x”, “y”, “” 68 , “z”, “” }) 69 如果定义SANE_EXPLODE_STRING 选项,其结果将是({ “”, “x”, “y”, “”, “z”, “” }) 70 如果定义 REVERSIBLE_EXPLODE_STRING 选项,其结果将是 ({ “”, “”, “x”, “y”, “”, 71 “z”, “”, “” }) 72 ****************************************************************************/ 73 #define SANE_EXPLODE_STRING 74 #undef REVERSIBLE_EXPLODE_STRING 75 76 /**************************************************************************** 77 这个选项用来确定call_other的行为,但是这个选项对于系统毫无好处可言。 78 ****************************************************************************/ 79 #undef CAST_CALL_OTHERS 80 81 /**************************************************************************** 82 如果定义了下面这个选项,那么任何传往非交互式物件(比如npc)的信息前面都会被加 83 上’]'符号,这个也不是非常需要,而且很容易被模拟出来。 84 ****************************************************************************/ 85 #undef NONINTERACTIVE_STDERR_WRITE 86 87 /**************************************************************************** 88 如果定义下面这个选项,你将不能使用set_light()这样的efuns函数,不过没有关系, 89 这个函数是在太落伍了,这个功能非常容易被模拟出来。 90 ****************************************************************************/ 91 #define NO_LIGHT 92 93 /**************************************************************************** 94 嗯,下面这个选项,是非常重要的选项,如果你定义了,那么意味着你将不能使用add_ 95 action, commands, livings等等这些相关的efuns,而不得不使用 process_input() 这 96 样的函数来定义物件所有的动作。这将是非常难堪的工作。所以保持add_action还是非 97 常有用的。 98 ****************************************************************************/ 99 #undef NO_ADD_ACTION 100 101 /**************************************************************************** 102 定义下面这个选项,将不能使用和snoop相关的函数,如果你认为没有snoop也没有关系 103 ,可以定义这个选项来保护一些隐私。 104 ****************************************************************************/ 105 #undef NO_SNOOP 106 107 /**************************************************************************** 108 这个选项和add_action一样要命,除非你立志只做一个聊天室,那么可以定义这个选项 109 ,否则,你的工作将会非常的麻烦。 110 ****************************************************************************/ 111 #undef NO_ENVIRONMENT 112 113 /**************************************************************************** 114 由于历史原因,定义wizard来管理Mud的运作,但是现在好像不是非常重要了。 115 如果定义了, wizardp() 和 related efuns 将不存在 116 如果没有定义,在使用ed()时也受限制,设置的限制参数无效 117 而且这个定义非常容易模拟出来。 118 ****************************************************************************/ 119 #undef NO_WIZARDS 120 121 /**************************************************************************** 122 下面的选项只是为了保持兼容性问题,对于大多数的LPmud而言,这个选项毫无用处。尤 123 其是一个新的Lib撰写。 124 ****************************************************************************/ 125 #undef OLD_TYPE_BEHAVIOR 126 127 /**************************************************************************** 128 定义下面这个选项,将意味着对于一个string或者buffers而言可以使用负数的index。 129 对于一个新Lib撰写,这个属性不是非常重要。 130 ****************************************************************************/ 131 #undef OLD_RANGE_BEHAVIOR 132 133 /**************************************************************************** 134 这个定义选项主要是为了保持向后兼容,对于新的Lib而言,没什么用处。重新习惯一个 135 方式,并不是非常困难的,而且,ed()对于大多数习惯使用工具撰写的人们而言,意义 136 不大。 137 ****************************************************************************/ 138 #define OLD_ED 139 140 /**************************************************************************** 141 下面这个选项比较重要,对于新的Lib撰写或者以前没有接触过LPC的人来说,这个定义 142 可以按照喜好来定。 143 (1) ‘static’ 将不再使用,而是用 ‘nosave’ 或者 ‘protected’ 代替了它。. 144 (2) ‘public’ 含义发生了变化,外部函数必须在任何一级的继承中被声明。现在publi 145 c意味着是明显可见的。 146 ****************************************************************************/ 147 #define SENSIBLE_MODIFIERS 148 149 /**************************************************************************** 150 下面的选项定义会改变MudOS的一些行为。这些对于Lib撰写,是比较重要的。 151 定义下面这个选项,将使用MD5加密法代替系统缺省的crypt()加密法。 152 ****************************************************************************/ 153 #undef CUSTOM_CRYPT 154 155 /**************************************************************************** 156 下面这个选项只是为了一些兼容性问题。定义以后,某些efuns含义会有所变化,但是没 157 有实质的区别。 158 ****************************************************************************/ 159 #undef COMPAT_32 160 161 /**************************************************************************** 162 允许统计的时候包含字符串分配,不定义可以是字符串处理更快,但字符串统计将被忽略。 163 ****************************************************************************/ 164 #define STRING_STATS 165 166 /**************************************************************************** 167 同上,数组统计。 168 ****************************************************************************/ 169 #define ARRAY_STATS 170 171 /**************************************************************************** 172 这是比较有用的选项,用来生成log文件。 173 ****************************************************************************/ 174 #define LOG_CATCHES 175 176 /**************************************************************************** 177 这个类似上面得选项,可以生成有用的调试文档。 178 ****************************************************************************/ 179 #define ARGUMENTS_IN_TRACEBACK 180 181 /**************************************************************************** 182 同上,对于新Lib撰写非常有用。 183 ****************************************************************************/ 184 #define LOCALS_IN_TRACEBACK 185 186 /**************************************************************************** 187 通过error_handler()来处理错误,非常有用的选项。 188 ****************************************************************************/ 189 #define MUDLIB_ERROR_HANDLER 190 191 /**************************************************************************** 192 下面得选项,可以详细定义,当然也可以通过配置config.cfg这样的文件来设置。 193 ****************************************************************************/ 194 #ifndef LATTICE 195 #define CONFIG_FILE_DIR “/u/tim/COMP/bin” 196 #else 197 #define CONFIG_FILE_DIR “etc:” 198 #endif 199 200 /**************************************************************************** 201 下面的选项将会对整个Lib撰写起到作用。对于新的Lib撰写,建议选择比较严格的方式 202 。而如果想保持兼容,建议选择常规的设定。 203 这些设定 其实等同于使用 #pragma 来定义。 204 如果你不太明白,使用 #define DEFAULT_PRAGMAS 0 是一个比较好的选择。 205 PRAGMA_STRICT_TYPES: 强制执行严格的书写规则,这个估计是以前议论最多的问题, 206 事实上,对于新Lib,这几乎不是问题。 207 PRAGMA_WARNINGS: 对不合法代码进行警告。非常有用。 208 PRAGMA_SAVE_TYPES: 呼叫以后保存函数情况 209 PRAGMA_SAVE_BINARY: 二进制代码保存,可以降低系统负担。 210 PRAGMA_OPTIMIZE: 代码优化 211 PRAGMA_ERROR_CONTEXT:错误报告 212 ****************************************************************************/ 213 #define DEFAULT_PRAGMAS PRAGMA_SAVE_BINARY + PRAGMA_WARNINGS + PRAGMA_ERROR_CONTEXT 214 215 /**************************************************************************** 216 对于没有使用的变量声明或者缺少的参数进行报告 217 ****************************************************************************/ 218 #define SUPPRESS_ARGUMENT_WARNINGS 219 220 /**************************************************************************** 221 这是重要的设定,为了保证系统拥有reset(),请不要定义这个选项。 222 除非不想系统拥有reset()。 223 ****************************************************************************/ 224 #undef NO_RESETS 225 226 /**************************************************************************** 227 看名字就知道,如果定义了,reset()将变得比较懒惰,你不碰它,它不呼叫。对于大多 228 数需要刷新时间的LPmud而言,还是不要定义的好。 229 ****************************************************************************/ 230 #undef LAZY_RESETS 231 232 /**************************************************************************** 233 存盘文件的缺省后缀名。 234 ****************************************************************************/ 235 #define SAVE_EXTENSION “.o” 236 237 /**************************************************************************** 238 下面两个选项使用来控制ansi的用户输入的,这个比较容易模拟出来,因为自由的ansi 239 输入可能会导致一些显示错误。 240 ****************************************************************************/ 241 #define NO_ANSI 242 #define STRIP_BEFORE_PROCESS_INPUT 243 244 /**************************************************************************** 245 笔者水平有限,不知道OPC是什么东西。只知道定义以后,可以用一个opcprof()函数来 246 统计外部函数调用的情况。下面得选项稍有不同,一次只能定义其中的一个。 247 ****************************************************************************/ 248 #undef OPCPROF 249 #undef OPCPROF_2D 250 251 /**************************************************************************** 252 当这个选项定义以后,当发生crash的时候,master.c中定义的crash()将被呼叫。 253 ****************************************************************************/ 254 #define TRAP_CRASHES 255 256 /**************************************************************************** 257 可以在call_outs中使用this_player() 258 ****************************************************************************/ 259 #define THIS_PLAYER_IN_CALL_OUT 260 261 /**************************************************************************** 262 这个定义是比较有用的,定义以后,call_out将产生一个int序列,这个序列可以方便的 263 被find_call_out()和remove_call_out()调用。这是一个效率极高的处理方式。原来使 264 用name来处理的方式相对比较低效。唯一的代价是些许系统开销。 265 ****************************************************************************/ 266 #define CALLOUT_HANDLES 267 268 /**************************************************************************** 269 调试非常有用,一般情况下不需要。 270 ****************************************************************************/ 271 #undef FLUSH_OUTPUT_IMMEDIATELY 272 273 /**************************************************************************** 274 这个定义允许在Lib中给与一些object以特权,除非你非常了解这个定义对于你所要撰写 275 的Lib的用处,不然的话,不要定义这个。 276 ****************************************************************************/ 277 #undef PRIVS 278 279 /**************************************************************************** 280 下面这个定义可以使互动物件也适用catch_tell(),当然你就必须在user.c中写入catc 281 h_tell()来处理receive(msg)。 282 ****************************************************************************/ 283 #undef INTERACTIVE_CATCH_TELL 284 285 /**************************************************************************** 286 受限制的ed模式 287 ****************************************************************************/ 288 #define RESTRICTED_ED 289 290 /**************************************************************************** 291 使用下面这个定义,你就不能在想要撰写的Lib中使用shadow,而shadow往往非常有用, 292 所以除非你明确知道不需要,不然不要定义这个选项。 293 ****************************************************************************/ 294 #undef NO_SHADOWS 295 296 /**************************************************************************** 297 定义下面这个,可以snoop shadowed object,因为shadowed object可能不知道送过来 298 的msg。 299 ****************************************************************************/ 300 #undef SNOOP_SHADOWED 301 302 /**************************************************************************** 303 这是一个有趣的定义,可以让你监听snooper的msg,也许利用起来比较有趣。当然也可 304 以不定义它。 305 ****************************************************************************/ 306 #define RECEIVE_SNOOP 307 308 /**************************************************************************** 309 这不是一个非常准确的定义,如果你想知道物件调用函数的时候,使用了多少时间,可 310 以用这个试试。不过不要对它的准确性基于太多的希望。 311 ****************************************************************************/ 312 #define PROFILE_FUNCTIONS 313 314 /**************************************************************************** 315 ‘buffer’这个类型定义的选择,按所要撰写的Lib而定,如果你可能要用到这种变量定义 316 ,就不要定义这个选项。 317 ****************************************************************************/ 318 #undef NO_BUFFER_TYPE 319 320 /**************************************************************************** 321 设置 pragma ’save_binary’ 的有效性。 322 使用这个选项,可以是Lib运行速度有所提高,并且降低系统的开销。代价就是将占用更 323 多的硬盘空间。 324 ****************************************************************************/ 325 #define BINARIES 326 327 /**************************************************************************** 328 允许定义数组变量,关键字array。比如 int array x = ({…}); 329 不过估计这样定义也不会有人反对 int *x = ({}); 330 定义这个选项与否,主要是看是不是习惯使用array这个声明,如果你来自C或者java, 331 也许会喜欢的。 332 ****************************************************************************/ 333 #define ARRAY_RESERVED_WORD 334 335 /**************************************************************************** 336 这个同上,关键字ref。按照Lib撰写人的习惯来定义比较好。 337 ****************************************************************************/ 338 #define REF_RESERVED_WORD 339 340 /**************************************************************************** 341 扩充包是可选配件 342 如果你定义了PACKAGE_XYZZY,那么你可以参看 packages/xyzzy.c 中的函数来确定可 343 以使用哪些efuns。 344 确定使用哪些扩充包,主要取决于提供的efuns是否是需要的。 345 346 ****************************************************************************/ 347 348 /**************************************************************************** 349 这个扩充包的函数还是非常有用的,比如query_ip_port() repeat_string()等等。 350 351 ****************************************************************************/ 352 353 #define PACKAGE_CONTRIB 354 355 /**************************************************************************** 356 下面这个包主要用来了解driver内部的信息。 357 ****************************************************************************/ 358 #define PACKAGE_DEVELOP 359 360 /**************************************************************************** 361 362 ****************************************************************************/ 363 #define PACKAGE_MATH 364 365 /**************************************************************************** 366 这个是图像处理包,不过,笔者也没用过。 367 ****************************************************************************/ 368 #undef PACKAGE_MATRIX 369 370 /**************************************************************************** 371 这个包的函数用来了解Mudlib的数据,比较实用。 372 ****************************************************************************/ 373 #undef PACKAGE_MUDLIB_STATS 374 375 /**************************************************************************** 376 呵呵,这个当然非常有用了。 377 ****************************************************************************/ 378 #define PACKAGE_SOCKETS 379 380 /**************************************************************************** 381 这个用处不大,尤其是中文使用者,呵呵 382 ****************************************************************************/ 383 #undef PACKAGE_PARSER 384 385 /**************************************************************************** 386 这个也没什么大的用处。允许driver在config文件里exe()命令清单 387 ****************************************************************************/ 388 #undef PACKAGE_EXTERNAL 389 390 /**************************************************************************** 391 外部扩展指令支持的数目 392 ****************************************************************************/ 393 #ifdef PACKAGE_EXTERNAL 394 #define NUM_EXTERNAL_CMDS 395 #endif 396 397 /**************************************************************************** 398 数据库支持包,想用数据库mysql的Lib撰写者比较有用, 399 ****************************************************************************/ 400 //#define PACKAGE_DB 401 #undef PACKAGE_DB 402 /**************************************************************************** 403 如果PACKAGE_DB被定义,必须选择下面中的一个来支持数据库 404 ****************************************************************************/ 405 #ifdef PACKAGE_DB 406 #define USE_MSQL 1 /* MiniSQL, it’s small; it’s free */ 407 #undef USE_MYSQL 2 /* MySQL, bigger; it’s free */ 408 #define DEFAULT_DB USE_MSQL /* default database */ 409 #endif 410 411 /**************************************************************************** 412 UID,就是用户有效识别码,国内的LPMud大多基于 wizard 以及 uid 系统,如果不 413 是新Lib的撰写者,这个是比较有用的。如果你拥有了自己的理念,可以不使用这些定义 414 。从MudOS开发者来看,他们也没有明确说不需要这些。虽然本人倒是比较喜欢放弃这些 415 设定,但是这些设定的存在,确实可以方便很多的处理。 416 如果你打算使用UID,那就定义下面这个选项。 417 ****************************************************************************/ 418 #define PACKAGE_UIDS 419 420 /**************************************************************************** 421 自动设定UID,那就不需要每次都seteuid(getuid(this_object()))。如果需要seteuid 422 (0),那么不要定义这个,比如现在大多数的Lib。 423 ****************************************************************************/ 424 #define AUTO_SETEUID 425 426 /**************************************************************************** 427 backbone这个设定是被自动认可的。 428 ****************************************************************************/ 429 #define AUTO_TRUST_BACKBONE 430 431 /**************************************************************************** 432 下面这些设定,一般情况下,不需要做出大的调整。 433 资源问题,使用32位地址。 434 ****************************************************************************/ 435 #undef USE_32BIT_ADDRESSES 436 437 /**************************************************************************** 438 心跳时间,这里是一秒。 439 ****************************************************************************/ 440 #define HEARTBEAT_INTERVAL 2000000 441 442 /**************************************************************************** 443 call_out的设定 444 ****************************************************************************/ 445 #define CALLOUT_CYCLE_SIZE 64 446 447 /**************************************************************************** 448 信息暂存设定 449 ****************************************************************************/ 450 #define LARGEST_PRINTABLE_STRING 8192 451 452 /**************************************************************************** 453 输出信息buffer设定 454 ****************************************************************************/ 455 #define MESSAGE_BUFFER_SIZE 65536 456 457 /**************************************************************************** 458 定义call_other cache的bits number 459 6 64 1k 460 8 256 4k 461 10 1024 16k 462 12 4096 64k 463 14 16384 256k 464 16 65536 1M 465 ****************************************************************************/ 466 #define APPLY_CACHE_BITS 12 467 468 /**************************************************************************** 469 call_other的cache统计 470 ****************************************************************************/ 471 #define CACHE_STATS 472 473 /**************************************************************************** 474 不定义可以快一点。 475 ****************************************************************************/ 476 #undef TRACE 477 478 /**************************************************************************** 479 LPC->C的编译 480 ****************************************************************************/ 481 #undef LPC_TO_C 482 483 /**************************************************************************** 484 看系统而定,一般不需要定义。 485 ****************************************************************************/ 486 #undef RUNTIME_LOADING 487 488 /**************************************************************************** 489 这个定义以后,允许被跟踪执行。 490 ****************************************************************************/ 491 #undef TRACE_CODE 492 493 /**************************************************************************** 494 缺省设定是32,主要关系到 heart_beat 的系统开销。 495 ****************************************************************************/ 496 #define HEART_BEAT_CHUNK 64 497 498 /**************************************************************************** 499 一般情况下,get_char不是bufferd。所以不太必要定义这个选项。 500 ****************************************************************************/ 501 #undef GET_CHAR_IS_BUFFERED 502 503 /**************************************************************************** 504 字符串定义。 505 ****************************************************************************/ 506 #define SMALL_STRING_SIZE 100 507 #define LARGE_STRING_SIZE 1000 508 509 /**************************************************************************** 510 指令buffer定义。 511 ****************************************************************************/ 512 #define COMMAND_BUF_SIZE 2000 513 514 /**************************************************************************** 515 呵呵,如果dbase深度超过了25就会溢出,这个设定就是这里定义的。当年最常见的一个 516 当机bug就是set指令的dbase溢出导致的。 517 ****************************************************************************/ 518 #define MAX_SAVE_SVALUE_DEPTH 50 519 520 /**************************************************************************** 521 25个局部变量调用,对于一个函数来说,应该是够了。数数COMBAT_D中的attack()函数 522 用到了几个局部变量。本人曾经定义超过25个变量,结果导致出错,但是那个时候不知 523 道为什么,虽然预感到这一点,可以在mud配置文件中定义。 524 ****************************************************************************/ 525 #define CFG_MAX_LOCAL_VARIABLES 100 526 #define CFG_EVALUATOR_STACK_SIZE 2000 527 #define CFG_MAX_CALL_DEPTH 100 528 529 /**************************************************************************** 530 下面定义的必须选择 4, 16, 64, 256, 1024, 4096 中的一个数字 531 ****************************************************************************/ 532 #define CFG_LIVING_HASH_SIZE 1024 533 534 /**************************************************************************** 535 NEXT_MALLOC_DEBUG: define this if using a NeXT and you want to enable 536 the malloc_check() and/or malloc_debug() efuns. Run the ‘man malloc_debug’ 537 command on the NeXT to find out what the arguments to malloc_debug(int) 538 mean. The malloc_check() efun calls the NeXT NXMallocCheck() system 539 call which does a consistency check on malloc’s data structures (this 540 consistency check is done at each malloc() and free() for certain 541 malloc_debug() levels). A non-zero return value indicates there was 542 a consistency problem. For those NeXT users wanting a bit more 543 performance out of malloc, try defining NEXT_MALLOC_DEBUG and calling the 544 malloc_debug(-1) efun (with an arg of -1). This will turn all 545 malloc debugging off and call malloc_singlethreaded() which the NeXT 546 malloc man page claims can make NeXT system malloc 10% to 15% faster. 547 548 [NOTE: This #define has no affect on the driver if not using the 549 NeXTSTEP OS.] 550 551 Warning: if you use a NeXT and define NEXT_MALLOC_DEBUG, be sure to 552 protect the use of the malloc_check() and malloc_debug() efuns 553 since setting certain debug levels can cause malloc() and free() 554 to become _very_ slow (protect efuns by using simul_efuns and 555 valid_override). 556 557 [NOTE: malloc_debug(6) is a good compromise between efficiency and 558 completeness of malloc debugging (malloc/free will be about half as fast).] 559 ****************************************************************************/ 560 #define NEXT_MALLOC_DEBUG 561 562 #endif 563 564 编译V14的时候编译DB 565 option.h修改下列配置 566 #define PACKAGE_DB 567 #ifdef PACKAGE_DB 568 #undef USE_MSQL /* MiniSQL, it’s small; it’s free */ 569 #define USE_MYSQL 2 /* MySQL, bigger; it’s free */ 570 #define DEFAULT_DB USE_MYSQL /* default database */ 571 #endif
修改 db.h 头文件:
将 24 行至 37 行注释掉,紧接着写上你自己的 MySQL 头文件的位置,
find / -name mysql.h 就找到了!
例如在 /usr/local/mysql/include/mysql.h
就写上:
#include “/usr/local/mysql/include/mysql.h”
在源代码目录里的 system_libs 文件里加上 mysql 连接库的位置,
例如在 /usr/local/mysql/lib
就加上:
-L/usr/local/mysql/lib -lmysqlclient