머드클럽
머드게임이란?
게임은 어떤 환경에서?
본격적으로 게임해 볼까?
배너교환
머드강좌
일반 머드강좌
성천사님의 머드강좌
혼님의 머드강좌
방울꽃님의 머드강좌
리트님의 머드강좌
2Soo님의 머드강좌
내머드소개하기
접속가능머드
내머드소개하기
자료실
접속 프로그램
머드소스
머드 유틸리티
기타
커뮤니티
공지사항
등업신청
머드클럽에바란다
자유 게시판
질문 AND 답변
머드클럽 갤러리
구인구직
머드게임소개
사신전(死神戰)
사신전 소개
사신전 맵
사신전 명령어 모음집
천명(天命)
천명 소개
천명 맵
천명 명령어 모음집
마이페이지
로그인
회원가입
계정찾기
로그인
회원가입
계정찾기
텍스트머드클럽
검색
머드 소스의 전체적인 소스 강좌
리트
3
2,134
0
0
2004-09-30 01:14:29
『강좌-Source 분석/강좌 (go MUD)』 592번 <br /> 제 목:MUD 해설 1 <br /> 올린이:wonsiman(이동환 ) 99/11/25 23:07 읽음:1354 관련자료 없음 <br /> ----------------------------------------------------------------------------- <br /> <br /> <br />1. 머드는 어떻게 돌아가지요? <br /> <br /> 모든 c 프로그램이 그렇듯 main 이 있어야 돌아가지요. <br /> <br /> Envy 2.2 의 main은, 그리고 대부분의 diku 계열 머드 <br /> <br /> 의 main은 comm.c 에 있습니다. 이 comm.c 가 뭔가 하 <br /> <br /> 면, src의 README를 보시면 <br /> <br /> comm.c - Contains procedures for game TCP/IP <br /> <br /> communications <br /> <br /> 이라고 나와있습니다. tcp/ip에 대한 자세한 설명은 <br /> <br /> 접겠습니다. <br /> <br /> 여기서 mud의 main() 이 돌아갑니다. <br /> <br /> comm.c 는 크게, <br /> <br /> 시간설정(gettimeofday등) <br /> <br /> port check <br /> <br /> boot_db() <br /> <br /> log_string() <br /> <br /> game_loop_unix() <br /> <br /> program end <br /> <br /> 로 되어있지요. <br /> <br /> 시간설정의 중요성은 머드를 해보신 분이라면 다 아실테고, <br /> <br /> port 는 특별한 입력이 없으면 기본값으로 4000 번을 갖 <br /> <br /> 습니다. envy에서는요. port에 대한 자세한 설명도 접겠습 <br /> <br /> 니다. <br /> <br /> boot_db()는 매우 중요합니다. game을 구동하는 핵심요소 <br /> <br /> 중에 하나죠. zone, mob, object 등등 모두를 메모리에 <br /> <br /> 올립니다. 이게 제대로 떠야 game_loop가 돌면서 db와 <br /> <br /> 통신을 해서 게임을 엮어나가죠. <br /> <br /> log_string 에는.. 잘 떳다~ 라는 정보가 가겠죠. <br /> <br /> 이 log는 Envy22/log에 들어있습니다. <br /> <br /> 다음에 game_loop_unix가 돕니다. 이 함수가 실제 머드죠. <br /> <br /> 즉, 사용자 <=> game_loop <=> db 가 되는 거지요. <br /> <br /> 이 loop는 무한이구요, loop이 끝나면 프로그램이 끝나겠죠? <br /> <br /> 머드는 이렇게 돌아갑니다. <br /> <br /> <br />========================================================= <br /> <br />2. 머드를 부트하면 뭐가 뜰까요? <br /> <br /> 앞에 comm.c 에서 보셨듯, main()은 boot_db를 <br /> <br /> 구동합니다. db에 실제 데이타가 모두 있으니까요. <br /> <br /> boot_db()는 db.c 에 있습니다. <br /> <br /> boot_db()의 간략한 구조는, <br /> <br /> time 관련 setting (바로 앞에 random 있는건 그냥 지나 <br /> <br /> 가겠습니다. 늘 쓰이는 것이니까) <br /> <br /> skill assign <br /> <br /> area load <br /> <br /> fix_exits() <br /> <br /> boot_done() <br /> <br /> area_update() <br /> <br /> load_notes() <br /> <br /> load_ban() <br /> <br /> load_down_time() <br /> <br /> 입니다. <br /> <br /> boot_db()가 구동되면, 시간을 보고 날씨등을 만들어 <br /> <br /> 올립니다. 게임 할때, 해지고 뜨고, 밝고, 어둡고, 비오고 <br /> <br /> 등등, 처음 게임 시작할때(booting할때) 설정이 되겠죠? <br /> <br /> 바로 그겁니다. <br /> <br /> skill assign은 gsn(global skill number)을 assign 합니다. <br /> <br /> 기술이 있어야 쓰겠지요? <br /> <br /> area load 예, 존이 있어야죠. 물론. 읽어들입니다. <br /> <br /> fix_exits() 함수의 해설에는 <br /> <br /> /* <br /> * Translate all room exits from virtual to real. <br /> * Has to be done after all rooms are read in. <br /> * Check for bad or suspicious reverse exits. <br /> */ <br /> <br /> 라고 되어있습니다. 설명을 붙이지 않을께요. <br /> <br /> room_index_hash[]를 이용해 room들의 출구를 만年求? <br /> <br /> boot_done() 은 ssm.c (shared string manager) 에 있습니다. <br /> <br /> boot에 사용한 메모리를 반납하는 거죠. <br /> <br /> area_update()는, 처음 db를 구동하면, 한번은 area를 update <br /> <br /> 해줘야 합니다. area_update()에 대한 내용은 다음에 다루겠 <br /> <br /> 습니다. <br /> <br /> load_notes()는, envy에는 보드, 메일 대신에 note라는 기능 <br /> <br /> 을 씁니다. 모두에게 메시지를 보낸다거나, 공지, 개인 편지 <br /> <br /> 등이 가능하지요. 사용자가 쓴 note 들은 화일이 되어 저장 <br /> <br /> 되기 때문에, boot 시 읽어옵니다. <br /> <br /> load_ban()은 ban file을 읽어드립니다. ban 할 주소 목록이 <br /> <br /> 지요. 특정 사이트의 접속을 막는 겁니다. <br /> <br /> load_down_time()은 mud down time을 읽어옵니다. 단지 읽어 <br /> <br /> 올 뿐입니다. 머드를 바로 reboot 해주는 함수는 아닙니다. <br /> <br /> 단지, 결과에 따라서 Reboot = TRUE 로 지정해주는 것 뿐입니 <br /> <br /> 다. <br /> <br />============================================================ <br /> <br />지금까지의 순서는, main()->boot_db()죠? boot_db()에서 자세 <br />히 다루어야 할 부분은 area_update입니다. <br /> <br /> area_update()는 db.c에 있습니다. 이 함수의 역할은, <br /> <br /> /* <br /> * Repopulate areas periodically. <br /> */ <br /> 라고 머리에 있습니다. ^^; area_update는 주기적으로 반복 <br /> <br /> 됩니다. 맙 잡고 몇틱 기다리면 또 생기고... 하는 그거죠. <br /> <br /> 게임 루프가 한번 돌때마다 각각의 counter를 +1 한뒤에 <br /> <br /> 일정수 이상이 되면 area 안에 있는 플레이어들에게 <br /> <br /> send_to_char( "You hear the patter of little feet.\n\r", <br /> pch ); <br /> <br /> 를 해줍니다. area의 age 가 바로 거기에 쓰이는 거죠. <br /> <br /> if ( pArea->nplayer == 0 || pArea->age >= 15 ) 를 봐서 <br /> <br /> 참이면 reset_area( pArea ) 를 해줍니다. area_update의 <br /> <br /> 실체는 reset_area() 죠. <br /> <br /> 단, mud school에 한해서, <br /> <br /> pRoomIndex = get_room_index( ROOM_VNUM_SCHOOL ); <br /> if ( pRoomIndex && pArea == pRoomIndex->area ) <br /> pArea->age = 15 - 3; <br /> <br /> 이렇게 해서 3 tick 에 한번씩 reset을 받습니다. 맨 처음 <br /> <br /> 들어가는 초보존이기때문...이라는 사실은 다 아실겁니다. <br /> <br /> 그럼 다시, reset_area()를 봐야겠죠. 지금의 순서는, <br /> <br /> main()->boot_db()->area_update()->reset_area() 입니다. <br /> <br />========================================================== <br /> <br /> reset_area()는 한 area 만 reset 합니다. area_update()에 <br /> <br /> 보시면, <br /> <br /> for ( pArea = area_first; pArea; pArea = pArea->next ) <br /> <br /> 이 있습니다. 이 안에서 reset_area( pArea ); 로 돕니다. <br /> <br /> loop를 돌면서, mob, object, exit 등을 reset 합니다. <br /> <br /> 안에 보시면, area file을 만들때 보던 내용과 매치에 생 <br /> <br /> 각하시면 이해가 빠릅니다. <br /> <br />========================================================== <br /> <br />3. game_loop_unix()는 무슨 일을 할까요? <br /> <br /> game_loop_unix()가 mud의 운영에 핵심이라고 전에 말씀드렸 <br /> <br /> 죠? <br /> <br /> 그럼 그 개략을 알아보죠. <br /> <br /> signal(); <br /> <br /> gettimeofday(); <br /> <br /> main loop <br /> <br /> 입니다. <br /> <br /> signal은 process의 유지와 관련된 부분입니다. <br /> <br /> signal( SIGPIPE, SIG_IGN ); 니깐, <br /> <br /> /usr/include/bits/signum.h 를 보시면, <br /> <br /> #define SIGPIPE 13 /* Broken pipe (POSIX). */ <br /> <br /> #define SIG_IGN ((__sighandler_t) 1) /* Ignore signal. */ <br /> <br /> 가 있습니다. signal의 기능은, 커널이나 유저가 시그널을 <br /> <br /> 보내면 설정된 루틴을 수행하는 것이죠. 여기서는 SIGPIPE <br /> <br /> 신호를 받으면 무시하는 거에요. pipe에 대한 자세한 설명 <br /> <br /> 을 원하시면 Unix 시스템 프로그래밍 책을 참고 하세요. <br /> <br /> process 관련을 보시면 될거에요. <br /> <br /> gettimeofday 에 관한 설명도 넘어가겠습니다. <br /> <br /> 그럼 가장 중요한 main loop! <br /> <br /> 개략입니다. <br /> <br />1. while ( !merc_down ) { <br /> } <br /> <br />2. /* <br /> * Poll all active descriptors. <br /> */ <br /> <br />3. /* <br /> * New connection? <br /> */ <br /> <br />4. /* <br /> * Kick out the freaky folks. <br /> */ <br /> <br />5. /* <br /> * Process input. <br /> */ <br /> <br />6. /* <br /> * Autonomous game motion. <br /> */ <br /> update_handler( ); <br /> <br />7. /* <br /> * Output. <br /> */ <br /> <br />8. /* <br /> * Synchronize to a clock. <br /> * Sleep( last_time + 1/PULSE_PER_SECOND - now ). <br /> * Careful here of signed versus unsigned arithmetic. <br /> */ <br /> <br /> 입니다. 설명을 붙이면, <br /> <br /> 1은, 머크 다운일때까지 무한루프입니다. mud는 계속되어야 <br /> <br /> 한다! 라는 스포츠 정신아래, 리붓되기전까지는 살아있어야 <br /> <br /> 겠지요? <br /> <br /> envy가 merc에서 온것은 잘 알려져있지요. <br /> <br /> 2는, socket 관련입니다. concurrent server 만드는 법을 <br /> <br /> 참고하시면 됩니다. 여기서는 자세한 설명은 접겠습니다. <br /> <br /> 알아두셔야 할것은, 이 방법으로, 각각 연결된 플레이어들 <br /> <br /> 의 data input과 output을 통제(?) 한다는 것입니다. <br /> <br /> descriptor가 각각 접속자에게 매겨진 번호입니다. <br /> <br /> 이걸로 플레이어를 구분하여 누가 무슨 입력을 했고, 누구에 <br /> <br /> 게 무엇을 출력해야 하는지를 프로그램이 알게됩니다. <br /> <br /> 3은 새 사용자인지 파악해서 -if에 FD_ISSET( control, &in_set <br /> <br /> ) 이 있죠? 이걸로 알아냅니다. bit flag 등등의 사항도 소켓 <br /> <br /> 관련이니 접겠습니다- descriptor list에 추가합니다. 이 역할 <br /> <br /> 을 하는 new_descriptor()는 comm.c에 있고, 이 function에 <br /> <br /> site ban 기능도 포함되어 있습니다. <br /> <br /> 4는, FD_ISSET( d->descriptor, &exc_set ) 를 검사해서, 관련 <br /> <br /> descriptor list에서 지우고, 그 캐릭터의 object 를 저장해준 <br /> <br /> 후, socket을 닫아버립니다. 끊기는 거죠. ^^ <br /> <br /> 5는, descriptor_list를 뒤져서, 어떤 사용자가 새로 무엇을 <br /> <br /> 입력했는지를 검사합니다. 프로그램이 FD_ISSET 을 보면 알수 <br /> <br /> 있거든요. 여기서, read_from_descriptor()가 있는데, 밑의 <br /> <br /> read_from_buffer()와 연결되는 중요한 함수입니다. 아래에 <br /> <br /> 자세히 다루겠습니다. <br /> <br /> Process input 부분의 앞부분에, 4와 유사한 코드가 보일거에 <br /> <br /> 요, 사용자 끊는. 사용자마다의 message input buffer 를 가 <br /> <br /> 지고 있다가, 그 버퍼가 넘치면, 사용자를 쫓아냅니다 ^^. <br /> <br /> 명령을 수십, 수백개 입력해놓으면... 이렇게 되겠죠. <br /> <br /> 그 부분이 지나고나면, read_from_buffer( d ) 가 있죠? <br /> <br /> 함수 이름 그대로, buffer로 부터 읽어옵니다. 읽어온 것은 <br /> <br /> incomm[] 에 쌓이죠. 그것이 다시 interpret()와 연결됩니 <br /> <br /> 다. 이 부분은 게임의 고리로서 매우 중요합니다. <br /> <br /> 그 아래로, stop_idling(), <br /> <br /> show_string() 들이 있는데, 이름을 보시면 어떤 기능인지 <br /> <br /> 아시겠죠? 자세한 설명은 넘기겠습니다. 그 다음 나오는 <br /> <br /> interpret( d->character, d->incomm ) 가 정말 중요한 것이죠. <br /> <br /> 후에 자세한 설명을 하겠습니다. <br /> <br /> 다음에 update_handler() 를 거치게 되는데, 이 함수는 <br /> <br /> update.c에 들어있습니다. 중요하니 뒤로 미루죠. <br /> <br /> 7에서는, 5와 반대기능으로, 프로그램이 만들어낸 것을 사용자 <br /> <br /> 에게 보내주는 부분이에요. 여기의 process_output()에 대해서 <br /> <br /> 도 다음에... <br /> <br /> 마지막 8은, time을 synchronize 하는 부분입니다. 머드 플래 <br /> <br /> 이 하다보면 틱타임이 제멋대로 나가는 경우가 많죠? 그것을 <br /> <br /> 일부 수정해줍니다. <br /> <br />============================================================ <br /> <br />그럼 자세한 설명을 붙일 것은, <br /> <br />read_from_descriptor() <br /> <br />read_from_buffer() <br /> <br />interpret() <br /> <br />update_handler() <br /> <br />process_output() <br /> <br />의 다섯가지죠? 그 전에 잠시 new_descriptor() 의 내용을 조금 보 <br /> <br />지요. socket, log, ban 은 넘어가고, write_to_buffer()가 있 <br /> <br />습니다. 여기서는 area file의 motd 를 보여주는 기능으로 우 <br /> <br />선 쓰였습니다. 이것은 process_output() 안에 있는 <br /> <br />write_to_descriptor와 비교해볼 필요가 있습니다. <br /> <br />write_to_buffer와 write_to_descriptor 양쪽 모두 '사용자에게 <br /> <br />메시지를 전달한다' 라는 같은 기능을 수행합니다. 그런데 왜 <br /> <br />두가지로 나뉘어있을까요? 이유는 이렇습니다. <br /> <br />버퍼에 쓰는 것은 그 텍스트를 다른 곳에 이용하려는 목적이 있 <br /> <br />지요. 예를 들어 동시에 로그를 하거나, 여러 디스크립터에 전송 <br /> <br />할 때, 편집을 해서 보낼때 등이고요, 직접 디스크립터에 쓰는 <br /> <br />것은 일종의 low level 루틴인데 위에서 설명한 이유에 해당하지 <br /> <br />않을때 사용합니다. 또는 에코기능처럼 엔진에서 뭔가 처리할 <br /> <br />때 사용할 수도 있겠고, 여러 이유가 있다. <br /> <br />일반적으로 버퍼에 모아 한꺼번에 보내는 것이 빠릅니다. <br /> <br />시스템 관련 함수는 (예 fprintf, system, fclose 등) 자주 호출하 <br /> <br />지 않는게 성능상 유리하거든요. <br /> <br />그리고, 비슷한 기능이지만 함수를 따로 만듬으로 알아보기 쉽지 <br /> <br />요. 버퍼가 쓰였으니 뭐겠구나, 디스크립터니 뭐겠구나... <br /> <br />=========================================================== <br /> <br />4. game_loop_unix()에서 중요한 것은? <br /> <br />지금까지의 진행은 <br /> <br />main() -> boot_db() -> area_update() -> reset_area() 로 <br /> <br />boot_db()에 관한 내용이 끝났고, <br /> <br />main() -> game_loop_unix() -> main loop 입니다. <br /> <br />========================================================== <br /> <br /> read_from_descriptor() <br /> <br /> read_from_buffer() <br /> <br /> interpret() <br /> <br /> update_handler() <br /> <br /> process_output() <br /> <br /> 에 대한 해설을 붙일께요. <br /> <br />1. read_from_descriptor()가 하는 일은, 말 그대로, <br /> <br /> descriptor에서 data를 입력받는 것이죠. 사용자의 입력 <br /> <br /> 사항을 받아 머드 플레이에 쓰는 겁니다. <br /> <br /> 함수 상단에 <br /> <br /> if ( d->incomm[0] != '\0' ) <br /> return TRUE; <br /> <br /> incomm[]에 대한 내용은 다음에 보기로 하겠습니다. <br /> <br /> inbuf를 채크해서 overflow이면 사용자를 끊어버립니다. <br /> <br /> read_from_descriptor의 가장 중요한 기능은 그 다음에 <br /> <br /> 나오는, descriptor에서 읽어들여 inbuf에 써주는 것이 <br /> <br /> 죠. read() 라는 함수의 역할이, man page를 보면, <br /> <br /> read() attempts to read up to count bytes from <br /> <br /> file descriptor fd into the buffer starting at buf. <br /> <br /> If count is zero, read() returns zero and has no <br /> <br /> other results. If count is greater than SSIZE_MAX, <br /> <br /> the result is unspecified. 이렇습니다. socket 관련으 <br /> <br /> 로 꼭 쓰이는 함수입니다. read() 내부에 <br /> <br /> d->inbuf + iStartiStart 를 쓰는 것으로 알수 있듯, <br /> <br /> inbuf에 사용자가 입력한 모든 것이 들어갑니다. 한꺼번 <br /> <br /> 에 여러개를 입력했다면(다다다다!!), 다 여기에 들어가 <br /> <br /> 는 것이죠. <br /> <br /> 이렇게 해서 사용자의 입력사항은 inbuf[]에 넣어졌습니다. <br /> <br /> 이렇게 넣어진 data는 read_from_buffer()가 다룹니다. <br /> <br />2. read_from_buffer()는... <br /> <br /> /* <br /> * Transfer one line from input buffer to input line. <br /> */ <br /> <br /> 이렇습니다. inbuf[] 에서 들어온것을 incomm[]으로 넘깁 <br /> <br /> 니다. 중간에 write_to_descriptor( d->descriptor, "Line <br /> <br /> too long.\n\r", 0 ) 이 있죠? write_to_descriptor() 에 <br /> <br /> 나온 설명 그대로입니다. <br /> <br /> if ( d->inbuf[i] == '\b' && k > 0 ) <br /> --k; <br /> else if ( isascii( d->inbuf[i] ) && isprint( <br /> <br /> d->inbuf[i] ) ) <br /> d->incomm[k++] = d->inbuf[i]; <br /> <br /> 이 바로 incomm[]에 옮겨 적는 것이지요. if 안의 내용은 <br /> <br /> backspace에 관한 처리구요. <br /> <br /> 또한가지, '!' 가 있지요? <br /> <br /> /* <br /> * Do '!' substitution. <br /> */ <br /> if ( d->incomm[0] == '!' ) <br /> strcpy( d->incomm, d->inlast ); <br /> else <br /> strcpy( d->inlast, d->incomm ); <br /> <br /> 이렇게 해서 전에 했던 명령(inlast에 있는)을 incomm으로 <br /> <br /> 옮겨줍니다. 일반 명령이면 incomm에 있는 것을 inlast로 <br /> <br /> 복사해놓지요. <br /> <br /> <br />3. interpret() 와 4. update_handler() 는 내용이 중요하고 많 <br /> <br /> 기때문에, 간략한 설명만 붙이고 뒤로 넘기겠습니다. <br /> <br /> interpret() 는 incomm으로 해서 넘어온 data를 분석하여 <br /> <br /> 해석한 후, 각각의 명령들로 나누어 실행하도록 합니다. <br /> <br /> update_handler() 는 area_update(), violence_update(), <br /> <br /> mobile_update(), weather_update(), char_update(), <br /> <br /> obj_update(), list_update(),time_update, aggr_update() <br /> <br /> 많죠? 이러한 머드내의 사항들을 모두 update 합니다. <br /> <br />5. process_output() 은, 메시지 출력부입니다. <br /> <br /> write_to_buffer() 를 가지지요. <br /> <br /> 머드 플레이 하다보면 보여질 것이 한 화면을 넘으면, <br /> <br /> [Please type (c)ontinue, (r)efresh, (b)ack, (q)uit, or <br /> <br /> RETURN] 이라는 메시지가 뜨죠? process_output()에서 <br /> <br /> 가장 쉽게 눈에 띄는 부분일겁니다. 위부분에 보면 <br /> <br /> if ( d->showstr_point ) 이 있지요? 그겁니다. <br /> <br /> 아래로 bust_a_prompt() 가 있는데, 이건, envy에서는 <br /> <br /> 자신의 prompt를 취향에 맞게 바꿀수 있지요? <br /> <br /> <hh/mana/mv/money/xp> 하는 식으로요. 이 bust_a_prompt() <br /> <br /> 가 해주는 일입니다. <br /> <br /> snoop 은, 운영자(immotal) 의 기능이지요. <br /> <br /> function 끝부분에 write_to_descriptor() 가 있지요? <br /> <br /> 이것의 기능은, 지금까지 write_to_buffer()에 모여있는 <br /> <br /> 메지를 한꺼번에 모두 descriptor에 써줍니다. <br /> <br /> buffer에 모여도 결국 사용자에게 보여주려면 descriptor <br /> <br /> 를 써야 하니까요. 이렇게 buffer에 모았다가 한꺼번에 <br /> <br /> 보여준답니다. <br /> <br />========================================================= <br /> <br />5. 머드에 입력된 명령은 어떻게 처리될까요? <br /> <br /> 바로 interpret()가 하는 일입니다. <br /> <br /> 주석은 이렇습니다. <br /> <br /> /* <br /> * The main entry point for executing commands. <br /> * Can be recursively called from 'at', 'order', 'force'. <br /> */ <br /> <br /> 이건 전체 설명이구요, <br /> <br /> <br /> /* <br /> * Strip leading spaces. <br /> */ <br /> <br /> while ( isspace( *argument ) ) <br /> argument++; <br /> <br /> isspace는 argument에 space를 찾게 됩니다. 앞에 is 붙은 <br /> <br /> 함수들은 ctype.h 에 정의되어 있고, 다 is 다음 나오는 <br /> <br /> 무언가를 찾아주는 함수입니다. <br /> <br /> The values returned are nonzero if the character c falls <br /> into the tested class, and a zero value if not. <br /> <br /> man에 나와있는 is... 의 return value에 대한 설명입니다. <br /> <br /> 이렇게 되면, argument가 가리키는 단어의 시작이 space가 아 <br /> <br /> 닌 곳 까지 가겠죠? <br /> <br /> /* <br /> * No hiding and remove AFK <br /> */ <br /> <br /> 이 부분은 사용자가 AFK (away from keyboard) 일때, 키를 <br /> <br /> 입력했으니, 자동으로 플레이 상태로 돌아오는 것이지요. <br /> <br /> REMOVE_BIT( ch->act, PLR_AFK ); 이겁니다. <br /> <br /> /* <br /> * Implement freeze command. <br /> */ <br /> <br /> 이건 운영자(implement)의 명령어 freeze (아무짓도 못하게 하 <br /> <br /> 는 imple의 명령이지요)에 걸린 상태일때 처리해 주는 거구요. <br /> <br /> 단지 bit check만 해서 "당신은 얼었어요~" 메시지만 보내주 <br /> <br /> 는 것 뿐이랍니다. 메시지 출력후 return 해버리니, 사용자는 <br /> <br /> 아무 행동도 할수 없는 것이지요. <br /> <br /> /* <br /> * Grab the command word. <br /> * Special parsing so ' can be a command, <br /> * also no spaces needed after punctuation. <br /> */ <br /> <br /> 이부분이 중요한 부분입니다. 사용자가 입력한 명령어들을 <br /> <br /> 구분해서 각각으로 나누는 것이지요. 내용은 간단합니다. <br /> <br /> 실제로 argument를 구분해 주는 것은, one_argument() 지요. <br /> <br /> argument = one_argument( argument, command ); 입니다. <br /> <br /> /* <br /> * Pick off one argument from a string and return the rest. <br /> * Understands quotes. <br /> */ <br /> <br /> 설명 그대로, string에서 argument 한개만 꺼내고 나머지를 <br /> <br /> return 합니다. 꺼낸 것은 command로 가지요. <br /> <br /> <br /> /* <br /> * Look for command in command table. <br /> */ <br /> <br /> 그렇게 command로 간 argument를 여기서 찾습니다. <br /> <br /> if ( command[0] == cmd_table[cmd].name[0] <br /> && !str_prefix( command, cmd_table[cmd].name ) <br /> && ( cmd_table[cmd].level <= trust ) ) <br /> <br /> 보시다시피, cmd_table[].name[]에서 찾고 있지요? <br /> <br /> 눈여겨 볼것은 str_prefix 입니다. 왜 그런지는 숙제로 <br /> <br /> 남기지요. <br /> <br /> 이렇게해서 cmd_table에서 찾으면 found = TRUE가 됩니다. <br /> <br /> <br /> /* <br /> * Log and snoop. <br /> */ <br /> <br /> 기존의 설명을 참고하시면 어떤 기능인지 충분히 아실테니, <br /> <br /> 이 부분의 설명은 넘어가겠습니다. <br /> <br /> /* <br /> * Look for command in socials table. <br /> */ <br /> <br /> cmd_table 은 interp.c 화일 앞부분에 structure로 정의 <br /> <br /> 되어 있지요? 예. 매우 많습니다. 그러나 이게 전부는 아니 <br /> <br /> 지요. 예. social command가 있습니다. smile, cry 등등.. <br /> <br /> cmd_table에서 없으면 social_table에서 찾습니다. 그래도 <br /> <br /> 없으면, send_to_char( "Huh?\n\r", ch ) 이거죠. <br /> <br /> 그 위라인에 check_social()이 있는데, 그 기능은 쭉 한번 <br /> <br /> 훑어 보시면 알겁니다. 캐릭터의 포지션에 따라 명령 수행 <br /> <br /> 불가시 나오는 메시지라든가... 그리고 social command는 <br /> <br /> 머드상의 기능에서는 단지 메시지 출력밖에 아무것도 수 <br /> <br /> 행하는 것이 없다는 것을 기억하세요. 그렇기 때문에, <br /> <br /> table에 있는 메시지를 찾아서 check_social에서 그냥 출력 <br /> <br /> 해줄 뿐이거든요. envy의 재미있는 기능중에 하나인, <br /> <br /> mob을 slap하면 받아 치잖아요? 그 기능도 여기에 있습니다. <br /> <br /> <br /> /* <br /> * Character not in position for command? <br /> */ <br /> <br /> 여기에 대한 설명은, 위의 check_social을 보고 보시면 <br /> <br /> 비슷하기 때문에, 접겠습니다. 기능은 command에 따라 정의 <br /> <br /> 되어있는 행동 가능 포지션에 따라 가능, 불가능을 판정하 <br /> <br /> 여 메시지를 보내주는 것이니까요. <br /> <br /> /* <br /> * Dispatch the command. <br /> */ <br /> <br /> 이렇게 command를 받으면, 해야할 일, 즉 command에 따른 <br /> <br /> do_...를 수행합니다. 그러기 위해서 <br /> <br /> (*cmd_table[cmd].do_fun) ( ch, argument ) 가 필요하지요. <br /> <br /> do_fun은 merc.h에 정의되어 있습니다. <br /> <br /> 각 command에 따른 do_... 는 grep "do_" *.c 해서 보세요. <br /> <br /> 각각 필요에 따라 잘 정리되어 나누어져 있습니다. <br /> <br /> <br /> <br /> 주석이 꼼꼼하게 잘되어있어서 그것만 따라가도 되지요. <br /> <br /> envy의 좋은 점 중 하나입니다. <br /> <br />========================================================== <br /> <br />6. do_ 의 돌아가는 모습을 살펴보지요. <br /> <br /> interpret() 의 구실로 do_를 부릅니다. do_ 는 머드에서 <br /> <br /> 행해지는 거의 모든 것이라고 볼 수 있지요. 예. 때문에 <br /> <br /> 매우 중요합니다. 어떤 기술이나 행동을 덧붙이는데 꼭 <br /> <br /> 넣지 않으면 안되지요. do_ 들의 모음은 각 쓰임에 따라, <br /> <br /> act_comm.c act_info.c act_move.c act_obj.c act_wz.c <br /> <br /> fight.c magic.c 등에 있습니다. <br /> <br /> 그럼 act_comm.c에 있는 것부터 찬찬히 살펴보지요. <br /> <br />========================================================== <br /> <br />1. do_chat() <br /> <br />void do_chat( CHAR_DATA *ch, char *argument ) <br />{ <br /> talk_channel( ch, argument, CHANNEL_CHAT, "chat" ); <br /> return; <br />} <br /> <br />참 별거 없지요? 겨우 다섯줄. talk_channel()에 보내주는 것으로 <br /> <br />끝이지요? 이 talk_channel()은 거의 모든 act_comm 의 함수에 쓰 <br /> <br />입니다. 이것만 보면 communication에 대한 대부분이 끝이지요. <br /> <br />한가지, tell 은 talk_channel() 을 쓰는 function이 아닙니다. <br /> <br />우선 형식을 보면, <br /> <br />void talk_channel( CHAR_DATA *ch, char *argument, int channel, <br /> const char *verb ) <br /> <br />do_chat()의 인수와 비교해서 본다면, ch로 character 가 누군지, <br /> <br />argument 로 무슨 내용인지, channel로 무슨 type인지, verb로 <br /> <br />무슨 행동인지 나타냅니다. <br /> <br />talk_channel()의 구조는 이러합니다. <br /> <br />if ( argument[0] == '\0' ) => 내용이 없으면 ... what? <br /> <br />if ( !IS_NPC( ch ) && IS_SET( ch->act, PLR_SILENCE ) ) <br /> <br />=> player가 silence 이면 You can't ... <br /> <br />if ( IS_AFFECTED( ch, AFF_MUTE ) <br /> || IS_SET( race_table[ch->race].race_abilities, RACE_MUTE ) <br /> || IS_SET( ch->in_room->room_flags, ROOM_CONE_OF_SILENCE ) ) <br /> <br />=> mute 이면 Your lips move but no sound comes out. <br /> <br />switch ( channel ) => 일반적인 것의 표현가 immtalk 을 구분해 <br /> <br />놓았지요. <br /> <br />argument = makedrunk( argument, ch ) 는, 술먹은 사람 혀꼬이게 <br /> <br />만드는 부분인데 난수를 사용합니다. 재미있는 함수지만, 내용은 <br /> <br />중요한것이 별로 없습니다. <br /> <br />그 아래 for 문은, 사용자 리스트를 뒤져서 <br /> <br />for ( d = descriptor_list; d; d = d->next ) <br /> <br />보여줄 것인지 넘어갈 것인지를 결정하여 버퍼로 보냅니다. <br /> <br />======================================================= <br />다음은 act_info.c 입니다. <br /> <br />2. do_score() <br /> <br />sc 하면 주욱~ 나오는 자신의 score 를 보는 명령입니다. <br /> <br />예, 생각하신것 대로 별거 없습니다. <br /> <br />buf 에다가 메시지를 써주는 것 뿐이죠. 관련 정보들은, <br /> <br />ch를 통해 모두다 불러오는거니깐. 방어력이나 성향에 관련된 <br /> <br />메시지들도 특별할것 없지요. <br /> <br />3. do_look() <br /> <br />l 치면 자신의 방을 보고, l xxx 를 치면 xxx를 보는 명령어죠. <br /> <br />앞부분은, character의 position에 따라 보고 못보고를 결정해서 <br /> <br />메시지를 뿌려주지요. <br /> <br />check_blind() 부분까지 끝나면, holylight(immo 의 명령어) 이 <br /> <br />아닐때, room_is_dark() 이면 안보여~ 메시지 출력부분. <br /> <br />look에 딸린 argument가 없을때는 방과 (room description <br /> <br />이나 exits등등의 이야기는 생략하겠습니다) 그 룸에 있는 obj나 <br /> <br />pc, npc등등을 보고, 있을때는, look in <br /> <br />을 구별해서 물건 안에 보여주고, 방향 지정일때는 그쪽의 <br /> <br />description을 보여주고, 지정해서 look char 일때는 <br /> <br />show_char_to_char_1() 를 사용하지요. <br /> <br />결론은, do_look()은 argument를 보고 각각에 필요한 description <br /> <br />을 버퍼로 보내주는 역할을 수행한다는 것이지요. <br /> <br />머드란, 택스트와 숫자의 놀이니까요. <br /> <br />============================================================= <br /> <br />act_move.c 입니다. <br /> <br />4. do_sneak() <br /> <br /> sneak은.. move siliently 와 같은 thief의 기술이죠. <br /> <br /> 내용을 보면, <br /> <br /> /* Don't allow charmed mobs to do this, check player's <br />skill */ <br /> <br /> if ( ( IS_NPC( ch ) && IS_AFFECTED( ch, AFF_CHARM ) ) <br /> || ( !IS_NPC( ch ) <br /> && ch->level < skill_table[gsn_sneak].skill_level[ch-> <br /> class] ) ) <br /> <br /> 이제 소스에 익숙해지셨을테니, 아시겠죠? 어떤 내용인지. <br /> <br /> 위의 경우에 해당사항이 없으면 기술을 쓰지요. <br /> <br /> send_to_char( "You attempt to move silently.\n\r", ch ); <br /> <br /> character한테 우선 메시지를 보내주고, <br /> <br /> affect_strip( ch, gsn_sneak ); <br /> <br /> affect_strip()은 handler.c 에 있습니다. <br /> <br /> sneak이 걸려있는 상태에서라면 한번 벗겨주고, 다시 sneak <br /> <br /> 을 넣어주기 위해서지요. <br /> <br /> 그 아래 라인에서 확률등을 넣어주고, <br /> <br /> affect_t_char( ch, &af ); 해줌으로 끝납니다. <br /> <br /> 머드의 기능이 세분화 될수록 기능이 간단해졌죠? <br /> <br />======================================================== <br /> <br />do_...에 대한 내용이 너무 길어지니깐, 이제 fight.c 와 <br /> <br />magic.c를 보고 접도록 하지요. 위에의 내용에서, do가 동작 <br /> <br />하면 어떤 기능에서는 어떻게 되는구나 라는 감을 다 잡으셨을 <br /> <br />거에요. <br /> <br />5. do_backstab() <br /> <br /> theif의 매우 중요한 공격기술이지요. <br /> <br /> if ( !IS_NPC( ch ) <br /> && ch->level < <br /> skill_table[gsn_backstab].skill_level[ch->class] ) <br /> { <br /> send_to_char( <br /> "You better leave the assassin trade to thieves.\n\r", <br /> ch ); <br /> return; <br /> } <br /> <br /> global skill name 에서 찾아서, class와 레벨을 보고 불가 <br /> <br /> 판단시 메시지를 출력하고 return 합니다. <br /> <br /> one_argument( argument, arg ); <br /> <br /> 공격할 대상을 적어주지 않으면 발휘되지 않는 기술이지요? <br /> <br /> ex) backstab fido <br /> <br /> 대문에 argument를 짤라와야 하구요. <br /> <br /> 그럼 대상과 같은 room에 있는지 찾아보고(get_char_room() <br /> <br /> 은 handler.c에 정의되어 있습니다. pc -player character 와 <br /> <br /> argument가 같은 룸에 있는지 찾아주지요) <br /> <br /> victim이 자기 자신이 아닌지 보고, is_safe() check 하고( <br /> <br /> fight.c 에 있습니다. 보시면 금방 내용을 알수 있을 거에요), <br /> <br /> 무기가 piercing weapon인지 살피고, victim이 이미 싸우고 있 <br /> <br /> 지 않은지 보고, victim이 상처가 커서 주위를 매우 경계하고 <br /> <br /> 있는지 보고, killer check 하고, delay time check 하고, <br /> <br /> 마지막으로 damage를 내지요(damage 계산에 대한 설명은 넘 <br /> <br /> 기겠습니다). <br /> <br />======================================================== <br /> <br /> 마지막, magic.c 에서, <br /> <br />6. magic은.. spell을 cast 하는 것이니, do_cast()를 봐야겠지 <br /> <br /> 요. <br /> <br /> 좀 깁니다. ^^ 지루하더라도, do_... 분석의 마지막이니 참고 <br /> <br /> 보세요. <br /> <br /> spell을 cast 했습니다. 예로, cast 'magic missile' fido <br /> <br /> 이면, <br /> <br /> target_name = one_argument( argument, arg1 ); <br /> one_argument( target_name, arg2 ); <br /> <br /> 너무나 자주 접한 문장이지요. :) <br /> <br /> argument에 대한 에러메시지와 npc 일때의 경우, skill table <br /> <br /> 관련, position 관련, mute, cone of silience 다 넘어가고, <br /> <br /> mana = MANA_COST( ch, sn ); 에서 MANA_COST 는 매크로로 <br /> <br /> merc.h 에 있습니다. spell 쓰는데 mana가 얼마나 들어가는지 <br /> <br /> 계산하는, 이미 생각하고계실 문장이지요. <br /> <br /> 그 다음은 switch( skill_table[sn].target ) 인데, <br /> <br /> 그렇다면, skill_table의 중요성을 이미 간파 하셨겠지요. <br /> <br /> const.c에 있습니다. 한번 주욱 훑어보세요. <br /> <br /> 그 이후의 case 들은 target 들에게 쓸수 있는지 없는지를 <br /> <br /> 가리는 조건들입니다. <br /> <br /> if ( ch->mana < mana ) 부터는, spell을 cast 할 충분한 <br /> <br /> mana가 있는가, ventriloquate(복화술) 인가를 본 후, <br /> <br /> delay time을 check 하고, 성공 여부 계산하고 그에 따른 <br /> <br /> mana를 소모시킨 후, 성공했을시, <br /> <br /> (*skill_table[sn].spell_fun) ( sn, <br /> URANGE( 1, ch->level, LEVEL_HERO ), <br /> ch, vo ); <br /> <br /> 로 대상 spell을 작동시킵니다. <br /> <br /> 작동은, 아까의 magic missile은 magic.c 의 <br /> <br /> void spell_magic_missile()로 가지요. 필요한 것은, <br /> <br /> merc.h 에 <br /> <br /> DECLARE_SPELL_FUN( spell_magic_missile ); <br /> <br /> const.c 에 <br /> <br /> spell_magic_missile, TAR_CHAR_OFFENSIVE, <br /> POS_FIGHTING, .... <br /> <br /> 입니다. <br /> <br /> spell_magic_missile() 의 내용은 넘어갈께요 ^^ <br /> <br /> 맨 밑의 if ( skill_table[sn].target == TAR_CHAR_OFFENSIVE <br /> <br /> 이하는 spell이 TAR_CHAR_OFFENSIVE 일때 (magic missile 도 <br /> <br /> 포함됩니다) multi_hit 을 위한 내용입니다. <br /> <br />============================================================= <br /> <br />7. 드디어 대망의 update_handler() 분석이군요 <br /> <br /> 이것이 끝나면, 머드를 한바퀴 돌아본 것이 되니까요. <br /> <br /> 책걸이라도 할까요? ^^; <br /> <br />========================================================== <br /> <br /> update_handler()는 update.c 에 있습니다. 맨 마지막 부분 <br /> <br /> 에 있지요. <br /> <br /> 처음 보이는 것은 static int 로 정의된 네가지 변수들이지 <br /> <br /> 요. <br /> <br /> 왜 static을 쓰는지는 다 아시겠지만, 설명을 위해 정의를 <br /> <br /> 적자면, <br /> <br /> static은 local scope 를 가지고, 그 function이 사용되 <br /> <br /> 지 않는 동안에도 이전의 값을 그대로 가지고 있지요. <br /> <br /> 때문에, game_loop의 main loop가 한번씩 돌면서 <br /> <br /> update_handler()를 돌려줘도 update_handler()는, 지금 <br /> <br /> 해당사항을 update할 때인가... 를 판단할 수 있지요. <br /> <br /> 대략적인 구성은, <br /> <br />1. area_update() => 머드의 존을 update <br /> <br />2. violence_update() => 전투를 계속 하도록 <br /> <br />3. mobile_update() => mob의 자동 움직임 <br /> <br />4. weather_update() => 날씨 ^^; <br /> <br />5. char_update() => character와 mob 의 update <br /> <br />6. obj_update() => 증발하거나 썩는 것들의 update <br /> <br />7. list_update() => character와 object 에 관련된 사항들중 <br /> <br /> 사라진 스트링에 대하여 메모리를 반납 <br /> <br /> 합니다. <br /> <br />8. time_update() => autoshutdown 용 <br /> <br />9. aggr_update() => aggressive mob 들이 pc를 공격하기 위한 <br /> <br /> update <br /> <br /> 입니다. <br /> <br /> area_update()부터 list_update()까지는 pulse_point를 거 <br /> <br /> 쳐서 실행되는 것으로 보아, delay time이 있고, <br /> <br /> time_update()부터 aggr_update()는 main loop가 한바퀴 <br /> <br /> 도는 동안 꼭 한번씩 실행된다는 걸 알수 있지요. <br /> <br /> <br /> 보다 자세한 해설입니다. <br /> <br />1. area_update () <br /> <br />if ( --pulse_area <= 0 ) <br />{ <br /> pulse_area = number_range( PULSE_AREA / 2, <br /> 3 * PULSE_AREA / 2 ); <br /> area_update ( ); <br />} <br /> <br /> 가 처음이지요? PULSE_관련은 모두 merc.h에 정의되어 있 <br /> <br /> 습니다. <br /> <br />#define PULSE_PER_SECOND 4 <br />#define PULSE_VIOLENCE ( 2 * PULSE_PER_SECOND ) <br />#define PULSE_MOBILE ( 5 * PULSE_PER_SECOND ) <br />#define PULSE_TICK ( 30 * PULSE_PER_SECOND ) <br />#define PULSE_AREA ( 60 * PULSE_PER_SECOND ) <br /> <br /> number_range()는 db.c에 있는 function으로 random number <br /> <br /> generater지요. <br /> <br /> 그리고 area_update()를 해줍니다. 전에 봤던 function이지요? <br /> <br /> 예. MUD 해설 2 를 참고하시면 있습니다. <br /> <br />2. 그 다음은 violence_update (). <br /> <br /> fight.c에 있습니다. <br /> <br /> /* <br /> * Control the fights going on. <br /> * Called periodically by update_handler. <br /> * Slightly less efficient than Merc 2.2. Takes 10% of <br /> * total CPU time. <br /> */ <br /> <br /> 라는 주석이 붙어있지요. 내용은 포인터 찾기라, 별로 적을 <br /> <br /> 것이 없네요 ^^; <br /> <br />3. mobile_update() <br /> <br /> /* <br /> * Mob autonomous action. <br /> * This function takes 25% of ALL Merc cpu time. <br /> * -- Furey <br /> */ <br /> <br /> 25%, 상당히 많이 먹지요? 이런 부분들은 수정해서 루틴을 <br /> <br /> 덧붙일때 매우 조심하셔야 해요. 머드 전체가 느려질 수 있 <br /> <br /> 거든요. <br /> <br /> 우선 char_list를 for 로 돌리면서 모두 한번씩 보지요. <br /> <br /> special이 있는 맙들인지 보고, 수행해주고( 싸움 이외에도 <br /> <br /> special을 가진 맙들은 mayor, dave, healer 등등 많지요), <br /> <br /> 자고 있는 mob들은 넘어가고, scaenge - 청소부 mob들이면 <br /> <br /> 떨어진 물건을 줍도록 하구요, wander or flee 에서 상처가 <br /> <br /> 큰 mob들중에 도망이 가능한 mob들은 자신이 있는 room에 <br /> <br /> pc가 들어오면 flee 하지요. <br /> <br />4. weather_update() <br /> <br /> 앞부분은 switch ( ++time_info.hour ) 가 모두 장악하고 <br /> <br /> 있지요. <br /> <br /> 6시에 동이 트고, 7시에 하루를 시작해서, 19시에 날이 <br /> <br /> 저물고, 20시에 밤이 오네요. <br /> <br /> 그렇게 하루가 가고, 35일이되면 한달이 가고, 17달이 가면 <br /> <br /> 1년 이 가네요. <br /> <br /> 그 아래로부터 Weather change죠. <br /> <br /> 시간을 보고 random을 불러서 날씨를 결정하지요. <br /> <br /> weather_info.sky로. 바뀐 날씨는 <br /> <br /> for ( d = descriptor_list; d; d = d->next ) <br /> <br /> 로 모두에게 보여주구요. 위 문장, 정말 많이 봤죠? <br /> <br />5. char_update() <br /> <br /> /* <br /> * Update all chars, including mobs. <br /> * This function is performance sensitive. <br /> */ <br /> <br /> 조심해서 다루라네요. <br /> <br /> for ( ch = char_list; ch; ch = ch->next ) <br /> <br /> update쪽에서 정말 자주보이던 문장, 이제 거칠것이 없지요. <br /> <br /> /* <br /> * Find dude with oldest save time. <br /> */ <br /> <br /> save time을 살펴보구요, <br /> <br /> mana, hp, move 를 regen 시켜주고, <br /> <br /> item 쪽에 횃불이나 랜턴 시간 닳는것 봐주고요, <br /> <br /> 키입력이 없이 오래 버텨있는 캐릭터들을 void로 보내고, <br /> <br /> gain_condition( ch, COND_DRUNK, -1 ); <br /> gain_condition( ch, COND_FULL, <br /> ( -1 - race_table[ch->race].hunger_mod ) ); <br /> gain_condition( ch, COND_THIRST, <br /> ( -1 - race_table[ch->race].thirst_mod ) ); <br /> <br /> 취한거, 배고픈거, 목마른거 -1 씩 해주구요, <br /> <br /> pc들이 받고 있는 affect 의 duration 을 1씩 줄여주지요. <br /> <br /> 그 아래로는, vampire가 햇빛 아래에서 돌아다닐때, <br /> <br /> 수중호흡 불가인 race가 물에 들어갔을때, 독에 당했을때 <br /> <br /> 등등, 시간이 지날때 damage를 입어야 하는 상황에 대한 처 <br /> <br /> 리입니다. 예, 이 수치를 높이면, 많은 이들이 죽어나갈겁 <br /> <br /> 니다. <br /> <br /> 그 뒤로는 autosave와 autoquit 입니다. 마찬가지로 char_list <br /> <br /> 를 뒤져서 save_char_obj() 한 후 do_quit을 해주죠. <br /> <br />6. obj_update() <br /> <br /> /* <br /> * Update all objs. <br /> * This function is performance sensitive. <br /> */ <br /> <br /> 역시 조심해서 다루라네요. <br /> <br /> for ( obj = object_list; obj; obj = obj_next ) <br /> <br /> 예, 또 obj_list를 뒤집니다. -_- <br /> <br /> timer를 1씩 줄이구요, <br /> <br /> 0되면, <br /> <br />switch ( obj->item_type ) <br />{ <br /> default: message = "$p vanishes."; <br /> break; <br /> case ITEM_FOUNTAIN: message = "$p dries up."; <br /> break; <br /> case ITEM_CORPSE_NPC: message = "$p decays into dust."; <br /> break; <br /> case ITEM_CORPSE_PC: message = "$p decays into dust."; <br /> break; <br /> case ITEM_FOOD: message = "$p decomposes."; <br /> break; <br />} <br /> <br /> 이렇게 해버립니다. <br /> <br /> 사람이 들고다니는지, 아니면 room에 버려져있는지 보고, <br /> <br /> 메시지를 뿌려주지요. <br /> <br /> 실제로 없에는 역할은 extract_obj() 가 맡아서 합니다. <br /> <br /> handler.c에 있어요. <br /> <br /> <br />7. list_update() <br /> <br /> /* <br /> * Remove deleted EXTRA_DESCR_DATA from objects. <br /> * Remove deleted AFFECT_DATA from chars and objects. <br /> * Remove deleted CHAR_DATA and OBJ_DATA from char_list <br /> * and object_list. <br /> */ <br /> <br /> if ( delete_char ) 에서 char_list를 뒤져서 필요없는 <br /> <br /> 사항들을 free_char( ch ); 합니다. <br /> <br /> 그 아래는 obj에 관한 free_string 이구요. <br /> <br /> 왜 이런짓(?)을 해야 하는지는 아실겁니다. <br /> <br /> <br />8. time_update() <br /> <br /> current_time 을 check 해서 <br /> <br /> if ( current_time > warning1 && warning1 > 0 ) <br /> <br /> 이면 player들에게 경고 메시지 날리고, 두번 더 경고 <br /> <br /> 해 준후 reboot을 위해 end_of_game( ); 을 수행합니다. <br /> <br /> 이 function은 handler.c 에 있고, descriptor_list를 <br /> <br /> 뒤져 <br /> <br />if ( d->character->position == POS_FIGHTING ) <br /> interpret( d->character, "save" ); <br />else <br /> interpret( d->character, "quit" ); <br /> <br /> 하거나 close_socket() 을 해서 pc들이 게임을 나가도록 합니 <br /> <br /> 다. <br /> <br /> 후에 shutdown file 을 만들고 끝납니다. <br /> <br /> fpReserve = fopen ( NULL_FILE, "r" ); 로 file을 쓸 여유를 <br /> <br /> 챙겨두는 걸 잊지 않구요. <br /> <br /> <br />9. aggr_update() <br /> <br /> 드디어 마지막이군요. <br /> <br /> /* <br /> * Aggress. <br /> * <br /> * for each mortal PC <br /> * for each mob in room <br /> * aggress on some random PC <br /> * <br /> * This function takes .2% of total CPU time. <br />
0
0
새글
답변
수정
삭제
최신목록
전체목록
[필수]텍스트 머드 운영에 필요한 리눅스 명령어
다음 게시글이 없습니다.
3
Comments
노병우
2004.09.30(목) 오후 09:08:51
답변
많은 도움이 되었습니다, 앞으로도 열심히 강좌 해주세요 ^^
이명훈(1)
2004.10.03(일) 오전 03:34:42
답변
스크롤의 압박이 조금 있었지만 좋은 강좌였어요. ^^:; 앞으로도 좋은 강좌 올려 주세요 ㅎㅎ ^^:;
이기홍(1)
2006.09.17(일) 오후 07:01:45
답변
ㄷㄷㄷ 양을보고 포기,,,
코멘트를 삭제할 비밀번호를 입력하세요.
비밀번호:
댓글에 답하기
입력
닫기
정회원 이상만 코멘트 쓰기가 가능합니다.
총 게시물 4개 / 검색된 게시물: 4개
강좌요청 받을께요~^^;;
강좌의 대략적인 내용과 목표
VI 에 관한 강좌
3
1208
0
0
리트
2004-10-03
CRON 에 관한 강좌
0
1143
0
0
리트
2004-10-03
[필수]텍스트 머드 운영에 필요한 리눅스 명령어
0
1394
0
0
리트
2004-10-03
머드 소스의 전체적인 소스 강좌
3
2135
0
0
리트
2004-09-30
1
검색
글쓰기
최신목록
전체목록
시작일
종료일
분류별 검색
전체보기
검색어
이름
제목
내용
검색
닫기
로그인
회원가입
계정찾기
개인정보처리방침
이용약관
PC화면
Copyright (c) 텍스트머드클럽. All rights reserved.
쪽지를 전송하고 있습니다. 잠시 기다려주세요.
쪽지보내기
받는이(ID/닉네임)
닉네임으로 입력
내용
쪽지가 도착하였습니다.
쪽지함
쪽지 내용을 읽어오고 있습니다. 잠시 기다려주세요.
--