From 8d356dbe3f056474700fcbdd563d8ea437b1464e Mon Sep 17 00:00:00 2001 From: Jared Adams Date: Sun, 2 Nov 2008 00:19:12 +0000 Subject: * Minor cleanup in login server * Char server now records client version and reports it to map server * Map server will now report all skills (even ones with dangerous indices) for client version 1 and above * Use status change val1 (instead of val2) index for speed potions, so that they can be triggered more easily from within scripts * Item database now also keeps track of the effect that items have on the spower stat * spower is now based on level + int*2 + modifier (see last point) * Minor bugfixes in support functionality for the SLang interpreter - Do not restart the map server without also restarting the char server, as the interserver protocol has changed slightly! (patch by fate) --- src/char/char.c | 14 +++++++++++--- src/login/login.c | 6 +++++- src/map/atcommand.c | 3 ++- src/map/battle.c | 6 ++++-- src/map/chrif.c | 6 ++++-- src/map/clif.c | 3 ++- src/map/itemdb.c | 19 ++++++++++--------- src/map/itemdb.h | 1 + src/map/magic-expr.c | 2 +- src/map/magic-interpreter-base.c | 6 ++++++ src/map/magic-stmt.c | 3 ++- src/map/magic.c | 4 ++-- src/map/map.h | 5 +++++ src/map/pc.c | 27 ++++++++++++++++++--------- src/map/pc.h | 2 +- src/map/script.c | 3 +-- src/map/skill.c | 2 +- 17 files changed, 76 insertions(+), 36 deletions(-) diff --git a/src/char/char.c b/src/char/char.c index 1edef7a..5de186c 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -70,6 +70,7 @@ char char_name_letters[1024] = ""; // list of letters/symbols authorised (or not struct char_session_data{ int account_id, login_id1, login_id2, sex; + unsigned short packet_tmw_version; int found_char[9]; char email[40]; // e-mail (default: a@a.com) by [Yor] time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) @@ -78,6 +79,7 @@ struct char_session_data{ #define AUTH_FIFO_SIZE 256 struct { int account_id, char_id, login_id1, login_id2, ip, char_pos, delflag, sex; + unsigned short packet_tmw_version; time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) } auth_fifo[AUTH_FIFO_SIZE]; int auth_fifo_pos = 0; @@ -1592,7 +1594,7 @@ int parse_tologin(int fd) { if (char_dat[i].account_id == acc) { int jobclass = char_dat[i].class; char_dat[i].sex = sex; - auth_fifo[i].sex = sex; +// auth_fifo[i].sex = sex; if (jobclass == 19 || jobclass == 20 || jobclass == 4020 || jobclass == 4021 || jobclass == 4042 || jobclass == 4043) { @@ -1969,12 +1971,14 @@ int parse_frommap(int fd) { !auth_fifo[i].delflag) { auth_fifo[i].delflag = 1; WFIFOW(fd,0) = 0x2afd; - WFIFOW(fd,2) = 16 + sizeof(struct mmo_charstatus); + WFIFOW(fd,2) = 18 + sizeof(struct mmo_charstatus); WFIFOL(fd,4) = RFIFOL(fd,2); WFIFOL(fd,8) = auth_fifo[i].login_id2; WFIFOL(fd,12) = (unsigned long)auth_fifo[i].connect_until_time; char_dat[auth_fifo[i].char_pos].sex = auth_fifo[i].sex; - memcpy(WFIFOP(fd,16), &char_dat[auth_fifo[i].char_pos], sizeof(struct mmo_charstatus)); + WFIFOW(fd, 16) = auth_fifo[i].packet_tmw_version; + fprintf(stderr, "From queue index %d: recalling packet version %d\n", i, auth_fifo[i].packet_tmw_version); + memcpy(WFIFOP(fd,18), &char_dat[auth_fifo[i].char_pos], sizeof(struct mmo_charstatus)); WFIFOSET(fd, WFIFOW(fd,2)); //printf("auth_fifo search success (auth #%d, account %d, character: %d).\n", i, RFIFOL(fd,2), RFIFOL(fd,6)); break; @@ -2380,6 +2384,7 @@ int parse_char(int fd) { sd->account_id = RFIFOL(fd,2); sd->login_id1 = RFIFOL(fd,6); sd->login_id2 = RFIFOL(fd,10); + sd->packet_tmw_version = RFIFOW(fd, 14); sd->sex = RFIFOB(fd,16); // send back account_id WFIFOL(fd,0) = RFIFOL(fd,2); @@ -2401,6 +2406,8 @@ int parse_char(int fd) { WFIFOL(login_fd,2) = sd->account_id; WFIFOSET(login_fd,6); } + // Record client version + auth_fifo[i].packet_tmw_version = sd->packet_tmw_version; // send characters to player mmo_char_send006b(fd, sd); } else { @@ -2523,6 +2530,7 @@ int parse_char(int fd) { auth_fifo[auth_fifo_pos].sex = sd->sex; auth_fifo[auth_fifo_pos].connect_until_time = sd->connect_until_time; auth_fifo[auth_fifo_pos].ip = session[fd]->client_addr.sin_addr.s_addr; + auth_fifo[auth_fifo_pos].packet_tmw_version = sd->packet_tmw_version; auth_fifo_pos++; } } diff --git a/src/login/login.c b/src/login/login.c index 30c6152..a9bfec6 100644 --- a/src/login/login.c +++ b/src/login/login.c @@ -130,6 +130,7 @@ int level_new_gm = 60; static struct dbt *gm_account_db; +#define VERSION_2_UPDATEHOST 0x01 // client supports updatehost //------------------------------ // Writing function of logs file //------------------------------ @@ -2786,6 +2787,8 @@ int parse_login(int fd) { WFIFOL(fd,2) = 1; // 01 = Server closed WFIFOSET(fd,3); } else { + int version_2 = RFIFOB(fd, 54); // version 2 + if (gm_level) printf("Connection of the GM (level:%d) account '%s' accepted.\n", gm_level, account.userid); else @@ -2800,7 +2803,8 @@ int parse_login(int fd) { * then the client can safely accept the 0x63 packet. The "version 2" value is not * otherwise used by eAthena. */ - if ((RFIFOW(fd, 0) == 0x64) && (RFIFOB(fd, 54) & 0x01)) + if ((RFIFOW(fd, 0) == 0x64) + && (version_2 & VERSION_2_UPDATEHOST)) { host_len = (int)strlen(update_host); if (host_len > 0) diff --git a/src/map/atcommand.c b/src/map/atcommand.c index ca2c38a..31fd0fe 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -7770,7 +7770,7 @@ int atcommand_refreshonline( static int magic_base = TMW_MAGIC; #define magic_skills_nr 6 -static char *magic_skill_names[magic_skills_nr] = {"magic", "life", "war", "transmute", "nature", "ether"}; +static char *magic_skill_names[magic_skills_nr] = {"magic", "life", "war", "transmute", "nature", "astral"}; int atcommand_magic_info(const int fd, struct map_session_data* sd, @@ -7856,6 +7856,7 @@ atcommand_set_magic(const int fd, struct map_session_data* sd, else set_skill(pl_sd, skill_index, value); + clif_skillinfoblock(pl_sd); return 0; } else clif_displaymessage(fd, "Character not found."); diff --git a/src/map/battle.c b/src/map/battle.c index 991affd..38a654e 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -963,7 +963,7 @@ int battle_get_adelay(struct block_list *bl) aspd_rate += 25; //増速ポーション使用時は減算 if(sc_data[i=SC_SPEEDPOTION2].timer!=-1 || sc_data[i=SC_SPEEDPOTION1].timer!=-1 || sc_data[i=SC_SPEEDPOTION0].timer!=-1) - aspd_rate -= sc_data[i].val2; + aspd_rate -= sc_data[i].val1; // Fate's `haste' spell works the same as the above if (sc_data[SC_HASTE].timer != -1) aspd_rate -= sc_data[SC_HASTE].val1; @@ -971,6 +971,7 @@ int battle_get_adelay(struct block_list *bl) if(sc_data[SC_DEFENDER].timer != -1) adelay += (1100 - sc_data[SC_DEFENDER].val1*100); } + if(aspd_rate != 100) adelay = adelay*aspd_rate/100; if(adelay < battle_config.monster_max_aspd<<1) adelay = battle_config.monster_max_aspd<<1; @@ -1013,12 +1014,13 @@ int battle_get_amotion(struct block_list *bl) if(sc_data[SC_STEELBODY].timer!=-1) // 金剛 aspd_rate += 25; if(sc_data[i=SC_SPEEDPOTION2].timer!=-1 || sc_data[i=SC_SPEEDPOTION1].timer!=-1 || sc_data[i=SC_SPEEDPOTION0].timer!=-1) - aspd_rate -= sc_data[i].val2; + aspd_rate -= sc_data[i].val1; if (sc_data[SC_HASTE].timer != -1) aspd_rate -= sc_data[SC_HASTE].val1; if(sc_data[SC_DEFENDER].timer != -1) amotion += (550 - sc_data[SC_DEFENDER].val1*50); } + if(aspd_rate != 100) amotion = amotion*aspd_rate/100; if(amotion < battle_config.monster_max_aspd) amotion = battle_config.monster_max_aspd; diff --git a/src/map/chrif.c b/src/map/chrif.c index ceeac26..228d061 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -914,7 +914,7 @@ int chrif_parse(int fd) switch(cmd) { case 0x2af9: chrif_connectack(fd); break; case 0x2afb: chrif_sendmapack(fd); break; - case 0x2afd: pc_authok(RFIFOL(fd,4), RFIFOL(fd,8), (time_t)RFIFOL(fd,12), (struct mmo_charstatus*)RFIFOP(fd,16)); break; + case 0x2afd: pc_authok(RFIFOL(fd,4), RFIFOL(fd,8), (time_t)RFIFOL(fd,12), RFIFOW(fd, 16), (struct mmo_charstatus*)RFIFOP(fd,18)); break; case 0x2afe: pc_authfail(RFIFOL(fd,2)); break; case 0x2b00: map_setusers(RFIFOL(fd,2)); break; case 0x2b03: clif_charselectok(RFIFOL(fd,2)); break; @@ -957,7 +957,9 @@ int send_users_tochar(int tid, unsigned int tick, int id, int data) { WFIFOW(char_fd,0) = 0x2aff; for (i = 0; i < fd_max; i++) { if (session[i] && (sd = session[i]->session_data) && sd->state.auth && - !((battle_config.hide_GM_session || (sd->status.option & OPTION_HIDE)) && pc_isGM(sd))) { + !((battle_config.hide_GM_session + || sd->state.shroud_active + || (sd->status.option & OPTION_HIDE)) && pc_isGM(sd))) { WFIFOL(char_fd,6+4*users) = sd->status.char_id; users++; } diff --git a/src/map/clif.c b/src/map/clif.c index ef5a72f..6750845 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -4188,7 +4188,8 @@ int clif_skillinfoblock(struct map_session_data *sd) WFIFOW(fd,0)=0x10f; for ( i = c = 0; i < MAX_SKILL; i++){ if( (id=sd->status.skill[i].id)!=0 - && (i < TMW_MAGIC || i > TMW_MAGIC_END)){ // [Fate] Hack: Prevent killing the client + && (sd->tmw_version >= 1 // [Fate] Version 1 and later don't crash because of bad skill IDs anymore + || (i < TMW_MAGIC || i > TMW_MAGIC_END))){ // [Fate] Hack: Prevent killing the client WFIFOW(fd,len ) = id; WFIFOW(fd,len+2) = skill_get_inf(id); WFIFOW(fd,len+4) = 0; diff --git a/src/map/itemdb.c b/src/map/itemdb.c index f68f416..2c247bb 100644 --- a/src/map/itemdb.c +++ b/src/map/itemdb.c @@ -328,7 +328,7 @@ static int itemdb_readdb(void) if(line[0]=='/' && line[1]=='/') continue; memset(str,0,sizeof(str)); - for(j=0,np=p=line;j<17 && p;j++){ + for(j=0,np=p=line;j<18 && p;j++){ while (*p == '\t' || *p == ' ') p++; str[j]=p; p=strchr(p,','); @@ -359,15 +359,16 @@ static int itemdb_readdb(void) id->atk=atoi(str[7]); id->def=atoi(str[8]); id->range=atoi(str[9]); - id->slot=atoi(str[10]); - id->class=atoi(str[11]); - id->sex=atoi(str[12]); - if(id->equip != atoi(str[13])){ - id->equip=atoi(str[13]); + id->magic_bonus = atoi(str[10]); + id->slot=atoi(str[11]); + id->class=atoi(str[12]); + id->sex=atoi(str[13]); + if(id->equip != atoi(str[14])){ + id->equip=atoi(str[14]); } - id->wlv=atoi(str[14]); - id->elv=atoi(str[15]); - id->look=atoi(str[16]); + id->wlv=atoi(str[15]); + id->elv=atoi(str[16]); + id->look=atoi(str[17]); id->flag.available=1; id->flag.value_notdc=0; id->flag.value_notoc=0; diff --git a/src/map/itemdb.h b/src/map/itemdb.h index 0edfad2..a10a857 100644 --- a/src/map/itemdb.h +++ b/src/map/itemdb.h @@ -19,6 +19,7 @@ struct item_data { int atk; int def; int range; + int magic_bonus; int slot; int look; int elv; diff --git a/src/map/magic-expr.c b/src/map/magic-expr.c index 2fbaa4f..a300a74 100644 --- a/src/map/magic-expr.c +++ b/src/map/magic-expr.c @@ -673,7 +673,7 @@ fun_awayfrom(env_t *env, int args_nr, val_t *result, val_t *args) int dx = heading_x[ARGDIR(1)]; int dy = heading_y[ARGDIR(1)]; int distance = ARGINT(2); - while (distance-- && !map_is_solid(loc->m, loc->x, loc->y)) { + while (distance-- && !map_is_solid(loc->m, loc->x + dx, loc->y + dy)) { loc->x += dx; loc->y += dy; } diff --git a/src/map/magic-interpreter-base.c b/src/map/magic-interpreter-base.c index 9338c78..6da7acb 100644 --- a/src/map/magic-interpreter-base.c +++ b/src/map/magic-interpreter-base.c @@ -293,6 +293,12 @@ spellguard_can_satisfy(spellguard_check_t *check, character_t *caster, env_t *en int tick = gettick(); int retval = check_prerequisites(caster, check->catalysts); +/* + fprintf(stderr, "Check: can satisfy? %d%d%d%d\n", retval, + caster->cast_tick <= tick, + check->mana <= caster->status.sp, + check_prerequisites(caster, check->components)); +*/ if (retval && near_miss) *near_miss = 1; // close enough! diff --git a/src/map/magic-stmt.c b/src/map/magic-stmt.c index 30b79d1..9760674 100644 --- a/src/map/magic-stmt.c +++ b/src/map/magic-stmt.c @@ -692,9 +692,10 @@ op_injure(env_t *env, int args_nr, val_t *args) damage_caused = 0; if (damage_caused || mp_damage) { - battle_damage(caster, target, damage_caused, mp_damage); + // display damage first, because dealing damage may deallocate the target. clif_damage(caster, target, gettick(), 0, 0, damage_caused, 0, 0, 0); + battle_damage(caster, target, damage_caused, mp_damage); } return 0; diff --git a/src/map/magic.c b/src/map/magic.c index 0fc2881..bc25b07 100644 --- a/src/map/magic.c +++ b/src/map/magic.c @@ -56,7 +56,7 @@ int magic_message(character_t *caster, char *spell_, size_t spell_len) { - int power = caster->status.base_level + caster->status.int_; + int power = caster->status.base_level + (caster->status.int_ * 2) + caster->spellpower_bonus_current; char *invocation_base = spell_ + 8; char *source_invocation = strchr(invocation_base, ':'); spell_t *spell; @@ -78,7 +78,7 @@ magic_message(character_t *caster, if (spell) { int near_miss; env_t *env = spell_create_env(&magic_conf, spell, caster, power, parameter); - effect_set_t *effects = spell_trigger(spell, caster, env, &near_miss); + effect_set_t *effects = (power < 1) ? NULL : spell_trigger(spell, caster, env, &near_miss); #ifdef DEBUG fprintf(stderr, "Found spell `%s', triggered = %d\n", spell_, effects != NULL); diff --git a/src/map/map.h b/src/map/map.h index dfed77f..bc34d7c 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -131,6 +131,8 @@ struct quick_regeneration { // [Fate] unsigned char tickdelay; // number of ticks to next update }; +#define VERSION_2_SKILLINFO 0x02 // client supports full skillinfo blocks + struct map_session_data { struct block_list bl; struct { @@ -173,6 +175,7 @@ struct map_session_data { } special_state; int char_id, login_id1, login_id2, sex; int packet_ver; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + unsigned char tmw_version; // tmw client version struct mmo_charstatus status; struct item_data *inventory_data[MAX_INVENTORY]; short equip_index[11]; @@ -214,6 +217,8 @@ struct map_session_data { short attack_spell_charges; // [Fate] Remaining number of charges for the attack spell short attack_spell_delay; // [Fate] ms delay after spell attack short attack_spell_range; // [Fate] spell range + short spellpower_bonus_target, spellpower_bonus_current; // [Fate] Spellpower boni. _current is the active one. + //_current slowly approximates _target, and _target is determined by equipment. short attackrange,attackrange_; int skilltimer; diff --git a/src/map/pc.c b/src/map/pc.c index 2d4e48c..ab75b2e 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -651,7 +651,7 @@ int pc_breakarmor(struct map_session_data *sd) * char鯖から送られてきたステータスを設定 *------------------------------------------ */ -int pc_authok(int id, int login_id2, time_t connect_until_time, struct mmo_charstatus *st) +int pc_authok(int id, int login_id2, time_t connect_until_time, short tmw_version, struct mmo_charstatus *st) { struct map_session_data *sd = NULL; @@ -665,6 +665,7 @@ int pc_authok(int id, int login_id2, time_t connect_until_time, struct mmo_chars return 1; sd->login_id2 = login_id2; + sd->tmw_version = tmw_version; memcpy(&sd->status, st, sizeof(*st)); @@ -1274,6 +1275,8 @@ int pc_calcstatus(struct map_session_data* sd,int first) pc_setpos(sd, sd->mapname, sd->bl.x, sd->bl.y, 3); } + sd->spellpower_bonus_target = 0; + for(i=0;i<10;i++) { index = sd->equip_index[i]; if(index < 0) @@ -1286,6 +1289,8 @@ int pc_calcstatus(struct map_session_data* sd,int first) continue; if(sd->inventory_data[index]) { + sd->spellpower_bonus_target += sd->inventory_data[index]->magic_bonus; + if(sd->inventory_data[index]->type == 4) { if(sd->status.inventory[index].card[0]!=0x00ff && sd->status.inventory[index].card[0]!=0x00fe && sd->status.inventory[index].card[0]!=(short)0xff00) { int j; @@ -1312,6 +1317,10 @@ int pc_calcstatus(struct map_session_data* sd,int first) } } } + + if (sd->spellpower_bonus_target < sd->spellpower_bonus_current) + sd->spellpower_bonus_current = sd->spellpower_bonus_target; + wele = sd->atk_ele; wele_ = sd->atk_ele_; def_ele = sd->def_ele; @@ -1793,10 +1802,10 @@ int pc_calcstatus(struct map_session_data* sd,int first) if( sd->sc_data[i=SC_SPEEDPOTION2].timer!=-1 || sd->sc_data[i=SC_SPEEDPOTION1].timer!=-1 || sd->sc_data[i=SC_SPEEDPOTION0].timer!=-1) // 増 速ポーション - aspd_rate -= sd->sc_data[i].val2; + aspd_rate -= sd->sc_data[i].val1; if (sd->sc_data[SC_HASTE].timer != -1) - aspd_rate -= sd->sc_data[SC_HASTE].val1; + aspd_rate -= sd->sc_data[SC_HASTE].val1; /* Slow down if protected */ @@ -3058,24 +3067,18 @@ int pc_takeitem(struct map_session_data *sd,struct flooritem_data *fitem) fitem->third_get_id = 0; } -fprintf(stderr, "pickuptime = %d, ticktime = %d\n", fitem->first_get_tick, tick); -fprintf(stderr, "canpickup-1 = %d\n", can_pick_item_up_from (sd, fitem->second_get_id)); can_take = can_pick_item_up_from (sd, fitem->first_get_id); -fprintf(stderr, "Can take at L%d? %d\n", __LINE__, can_take); if (!can_take) can_take = fitem->first_get_tick <= tick && can_pick_item_up_from (sd, fitem->second_get_id); -fprintf(stderr, "Can take at L%d? %d\n", __LINE__, can_take); if (!can_take) can_take = fitem->second_get_tick <= tick && can_pick_item_up_from (sd, fitem->third_get_id); -fprintf(stderr, "Can take at L%d? %d\n", __LINE__, can_take); if (!can_take) can_take = fitem->third_get_tick <= tick; -fprintf(stderr, "Can take at L%d? %d\n", __LINE__, can_take); if (can_take) { /* Can pick up */ @@ -7119,6 +7122,12 @@ static int pc_natural_heal_sub(struct map_session_data *sd,va_list ap) { nullpo_retr(0, sd); + // Hijack this callback: Adjust spellpower bonus + if (sd->spellpower_bonus_target < sd->spellpower_bonus_current) + sd->spellpower_bonus_current = sd->spellpower_bonus_target; + else if (sd->spellpower_bonus_target > sd->spellpower_bonus_current) + sd->spellpower_bonus_current += 1 + ((sd->spellpower_bonus_target - sd->spellpower_bonus_current) >> 5); + if (sd->sc_data[SC_HALT_REGENERATE].timer != -1) return 0; diff --git a/src/map/pc.h b/src/map/pc.h index f424d57..24ecd74 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -34,7 +34,7 @@ int pc_counttargeted(struct map_session_data *sd,struct block_list *src,int targ int pc_setrestartvalue(struct map_session_data *sd,int type); int pc_makesavestatus(struct map_session_data *); int pc_setnewpc(struct map_session_data*,int,int,int,int,int,int); -int pc_authok(int, int, time_t, struct mmo_charstatus *); +int pc_authok(int, int, time_t, short tmw_version, struct mmo_charstatus *); int pc_authfail(int); int pc_isequip(struct map_session_data *sd,int n); diff --git a/src/map/script.c b/src/map/script.c index c28467d..8b971a9 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -3147,6 +3147,7 @@ int buildin_skill(struct script_state *st) flag=conv_num(st,&(st->stack->stack_data[st->start+4]) ); sd=script_rid2sd(st); pc_skill(sd,id,level,flag); + clif_skillinfoblock(sd); return 0; } @@ -5281,8 +5282,6 @@ int buildin_marriage(struct script_state *st) struct map_session_data *sd=script_rid2sd(st); struct map_session_data *p_sd=map_nick2sd(partner); - fprintf(stderr, "0=%p (%d,%d), 1=%p (%d,%d)\n", sd, sd->bl.id, sd->status.partner_id, - p_sd, p_sd->bl.id, p_sd->status.partner_id); if(sd==NULL || p_sd==NULL || pc_marriage(sd,p_sd) < 0){ push_val(st->stack,C_INT,0); return 0; diff --git a/src/map/skill.c b/src/map/skill.c index cff56ba..9047e7f 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -8301,7 +8301,6 @@ int skill_status_effect(struct block_list *bl, int type, int val1, int val2, int nullpo_retr(0, opt2=battle_get_opt2(bl)); nullpo_retr(0, opt3=battle_get_opt3(bl)); - race=battle_get_race(bl); mode=battle_get_mode(bl); elem=battle_get_elem_type(bl); @@ -9043,6 +9042,7 @@ int skill_status_effect(struct block_list *bl, int type, int val1, int val2, int if(bl->type==BL_PC && updateflag) clif_updatestatus(sd,updateflag); /* ステータスをクライアントに送る */ + return 0; } /*========================================== -- cgit v1.2.3-60-g2f50