static void Host_Pause_f (void) { if (cmd_source == src_command) { CL_Cmd_ForwardToServer (); return; } if (!pausable->int_val) SV_ClientPrintf ("Pause not allowed.\n"); else { sv.paused ^= 1; if (sv.paused) { SV_BroadcastPrintf ("%s paused the game\n", PR_GetString (&sv_pr_state, SVstring (sv_player, netname))); } else { SV_BroadcastPrintf ("%s unpaused the game\n", PR_GetString (&sv_pr_state, SVstring (sv_player, netname))); } // send notification to all clients MSG_WriteByte (&sv.reliable_datagram, svc_setpause); MSG_WriteByte (&sv.reliable_datagram, sv.paused); } }
static void PrintCallHistory (void) { int i; dfunction_t *f; if (pr_depth == 0) { Con_Printf("<NO STACK>\n"); return; } pr_stack[pr_depth].f = pr_xfunction; for (i = pr_depth; i >= 0; i--) { f = pr_stack[i].f; if (!f) { Con_Printf("<NO FUNCTION>\n"); } else { Con_Printf("%12s : %s\n", PR_GetString(f->s_file), PR_GetString(f->s_name)); } } }
/* ================ SV_CheckVelocity ================ */ TM_CALLABLE void SV_CheckVelocity (edict_t *ent) { int i; // // bound velocity // for (i=0 ; i<3 ; i++) { if (IS_NAN(ent->v.velocity[i])) { Con_Printf ("Got a NaN velocity on %s\n", PR_GetString(ent->v.classname)); ent->v.velocity[i] = 0; } if (IS_NAN(ent->v.origin[i])) { Con_Printf ("Got a NaN origin on %s\n", PR_GetString(ent->v.classname)); ent->v.origin[i] = 0; } if (ent->v.velocity[i] > sv_maxvelocity.value) ent->v.velocity[i] = sv_maxvelocity.value; else if (ent->v.velocity[i] < -sv_maxvelocity.value) ent->v.velocity[i] = -sv_maxvelocity.value; } }
void PR_Profile_f (void) { dfunction_t *f, *best; int max, num, i; if (sv.state != ss_active) return; num = 0; do { max = 0; best = NULL; for (i = 0; i < progs->numfunctions; i++) { f = &pr_functions[i]; if (f->profile > max) { max = f->profile; best = f; } } if (best) { if (num < 10) Com_Printf ("%7i %s\n", best->profile, PR_GetString(best->s_name)); num++; best->profile = 0; } } while (best); }
/* PR_Profile */ void PR_Profile (progs_t *pr) { dfunction_t *f, *best; int max; int num; int i; num = 0; do { max = 0; best = NULL; for (i = 0; i < pr->progs->numfunctions; i++) { f = &pr->pr_functions[i]; if (f->profile > max) { max = f->profile; best = f; } } if (best) { if (num < 10) Con_Printf ("%7i %s\n", best->profile, PR_GetString (pr, best->s_name)); num++; best->profile = 0; } } while (best); }
/* ============= ED_WriteGlobals ============= */ void ED_WriteGlobals (FILE *f) { ddef_t *def; int i; char *name; int type; fprintf (f,"{\n"); for (i=0 ; i<progs->numglobaldefs ; i++) { def = &pr_globaldefs[i]; type = def->type; if ( !(def->type & DEF_SAVEGLOBAL) ) continue; type &= ~DEF_SAVEGLOBAL; if (type != ev_string && type != ev_float && type != ev_entity) continue; name = PR_GetString(def->s_name); fprintf (f,"\"%s\" ", name); fprintf (f,"\"%s\"\n", PR_UglyValueString((etype_t)type, (eval_t *)&pr_globals[def->ofs])); } fprintf (f,"}\n"); }
static void CheckKTPro (void) { extern cvar_t sv_ktpro_mode; int i, len; char *s; if (!strcasecmp(sv_ktpro_mode.string, "auto")) { // attempt automatic detection is_ktpro = false; for (i = 0; i < progs->numstrings; i++) { if ((s = PR_GetString(i))) if (*s) { if ((len = strlen(s)) >= 23) if (strstr(s, "http://ktpro.does.it/ for") || strstr(s, "http://qwex.n3.net/ for")) { is_ktpro = true; Con_DPrintf ("Treat mod as ktpro.\n"); break; } i += len; } } } else is_ktpro = sv_ktpro_mode.value ? true : false; }
/* ============ PR_GlobalString Returns a string with a description and the contents of a global, padded to 20 field width ============ */ const char *PR_GlobalString (int ofs) { static char line[512]; const char *s; int i; ddef_t *def; void *val; val = (void *)&pr_globals[ofs]; def = ED_GlobalAtOfs(ofs); if (!def) sprintf (line,"%i(?)", ofs); else { s = PR_ValueString (def->type, (eval_t *)val); sprintf (line,"%i(%s)%s", ofs, PR_GetString(def->s_name), s); } i = strlen(line); for ( ; i < 20; i++) strcat (line, " "); strcat (line, " "); return line; }
/* ============ PR_GlobalString Returns a string with a description and the contents of a global, padded to 20 field width ============ */ char *PR_GlobalString (int ofs) { char *s; int i; ddef_t *def; void *val; static char line[128]; val = (void *)&pr_globals[ofs]; def = ED_GlobalAtOfs(ofs); if (!def) { snprintf (line, sizeof(line), "%i(?""?""?)", ofs); // separate the ?'s to shut up gcc } else { s = PR_ValueString ((etype_t)def->type, (eval_t *) val); snprintf (line, sizeof(line), "%i(%s)%s", ofs, PR_GetString(def->s_name), s); } i = strlen(line); for ( ; i<20 ; i++) strlcat (line, " ", sizeof(line)); strlcat (line, " ", sizeof(line)); return line; }
void PR_StackTrace (void) { dfunction_t *f; int i; if (pr_depth == 0) { Com_Printf ("<NO STACK>\n"); return; } pr_stack[pr_depth].f = pr_xfunction; for (i = pr_depth; i >= 0; i--) { if ((f = pr_stack[i].f)) Com_Printf ("%12s : %s\n", PR_GetString(f->s_file), PR_GetString(f->s_name)); else Com_Printf ("<NO FUNCTION>\n"); } }
/* ================ SV_CreateBaseline Entity baselines are used to compress the update messages to the clients -- only the fields that differ from the baseline will be transmitted ================ */ void SV_CreateBaseline (void) { int i; edict_t *svent; int entnum; for (entnum = 0; entnum < sv.num_edicts ; entnum++) { svent = EDICT_NUM(entnum); if (svent->free) continue; // create baselines for all player slots, // and any other edict that has a visible model if (entnum > MAX_CLIENTS && !svent->v.modelindex) continue; // // create entity baseline // VectorCopy (svent->v.origin, svent->baseline.origin); VectorCopy (svent->v.angles, svent->baseline.angles); svent->baseline.frame = svent->v.frame; svent->baseline.skinnum = svent->v.skin; if (entnum > 0 && entnum <= MAX_CLIENTS) { svent->baseline.colormap = entnum; svent->baseline.modelindex = SV_ModelIndex("progs/player.mdl"); } else { svent->baseline.colormap = 0; svent->baseline.modelindex = SV_ModelIndex(PR_GetString(svent->v.model)); } CRITICAL_MESSAGE(sv_signon_lock, SV_SIGNON_LOCK, 0, { // // flush the signon message out to a seperate buffer if // nearly full // SV_FlushSignon (); // // add to the message // MSG_WriteByte (&sv.signon,svc_spawnbaseline); MSG_WriteShort (&sv.signon,entnum); MSG_WriteByte (&sv.signon, svent->baseline.modelindex); MSG_WriteByte (&sv.signon, svent->baseline.frame); MSG_WriteByte (&sv.signon, svent->baseline.colormap); MSG_WriteByte (&sv.signon, svent->baseline.skinnum); for (i=0 ; i<3 ; i++) { MSG_WriteCoord(&sv.signon, svent->baseline.origin[i]); MSG_WriteAngle(&sv.signon, svent->baseline.angles[i]); } });
/* ===================== SV_DropClient Called when the player is totally leaving the server, either willingly or unwillingly. This is NOT called if the entire server is quiting or crashing. ===================== */ void SV_DropClient (client_t *drop) { // add the disconnect MSG_WriteByte (&drop->netchan.message, svc_disconnect); if (drop->state == cs_spawned) { if (!drop->spectator) { // call the prog function for removing a client // this will set the body to a dead frame, among other things pr_global_struct->self = EDICT_TO_PROG(drop->edict); PR_ExecuteProgram (pr_global_struct->ClientDisconnect); } else if (SpectatorDisconnect) { // call the prog function for removing a client // this will set the body to a dead frame, among other things pr_global_struct->self = EDICT_TO_PROG(drop->edict); PR_ExecuteProgram (SpectatorDisconnect); } } else if (dmMode.integer == DM_SIEGE) { if (PR_GetString(drop->edict->v.puzzle_inv1)[0] != '\0') { // this guy has a puzzle piece, call this function anyway // to make sure he leaves it behind Con_Printf("Client in unspawned state had puzzle piece, forcing drop\n"); pr_global_struct->self = EDICT_TO_PROG(drop->edict); PR_ExecuteProgram (pr_global_struct->ClientDisconnect); } } if (drop->spectator) Con_Printf ("Spectator %s removed\n",drop->name); else Con_Printf ("Client %s removed\n",drop->name); if (drop->download) { fclose (drop->download); drop->download = NULL; } drop->state = cs_zombie; // become free in a few seconds drop->connection_started = realtime; // for zombie timeout drop->old_frags = 0; drop->edict->v.frags = 0; drop->name[0] = 0; memset (drop->userinfo, 0, sizeof(drop->userinfo)); // send notification to all remaining clients SV_FullClientUpdate (drop, &sv.reliable_datagram); }
/* ============ PR_ValueString Returns a string describing *data in a type specific manner ============= */ char *PR_ValueString (etype_t type, eval_t *val) { static char line[256]; ddef_t *def; dfunction_t *f; type = (etype_t) (type & ~DEF_SAVEGLOBAL); switch (type) { case ev_string: snprintf (line, sizeof(line), "%s", PR_GetString(val->string)); break; case ev_entity: snprintf (line, sizeof(line), "entity %i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict)) ); break; case ev_function: f = pr_functions + val->function; snprintf (line, sizeof(line), "%s()", PR_GetString(f->s_name)); break; case ev_field: def = ED_FieldAtOfs ( val->_int ); snprintf (line, sizeof(line), ".%s", PR_GetString(def->s_name)); break; case ev_void: snprintf (line, sizeof(line), "void"); break; case ev_float: snprintf (line, sizeof(line), "%5.1f", val->_float); break; case ev_vector: snprintf (line, sizeof(line), "'%5.1f %5.1f %5.1f'", val->vector[0], val->vector[1], val->vector[2]); break; case ev_pointer: snprintf (line, sizeof(line), "pointer"); break; default: snprintf (line, sizeof(line), "bad type %i", type); break; } return line; }
/* ============ PR_UglyValueString (etype_t type, eval_t *val) Returns a string describing *data in a type specific manner Easier to parse than PR_ValueString ============= */ static const char *PR_UglyValueString (int type, eval_t *val) { static char line[512]; ddef_t *def; dfunction_t *f; type &= ~DEF_SAVEGLOBAL; switch (type) { case ev_string: sprintf (line, "%s", PR_GetString(val->string)); break; case ev_entity: sprintf (line, "%i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict))); break; case ev_function: f = pr_functions + val->function; sprintf (line, "%s", PR_GetString(f->s_name)); break; case ev_field: def = ED_FieldAtOfs ( val->_int ); sprintf (line, "%s", PR_GetString(def->s_name)); break; case ev_void: sprintf (line, "void"); break; case ev_float: sprintf (line, "%f", val->_float); break; case ev_vector: sprintf (line, "%f %f %f", val->vector[0], val->vector[1], val->vector[2]); break; default: sprintf (line, "bad type %i", type); break; } return line; }
/* ============ ED_FindFunction ============ */ static dfunction_t *ED_FindFunction (const char *fn_name) { dfunction_t *func; int i; for (i = 0; i < progs->numfunctions; i++) { func = &pr_functions[i]; if ( !strcmp(PR_GetString(func->s_name), fn_name) ) return func; } return NULL; }
/* ============ ED_FindField ============ */ ddef_t *ED_FindField (const char *name) { ddef_t *def; int i; for (i=0 ; i<progs->numfielddefs ; i++) { def = &pr_fielddefs[i]; if (!strcmp(PR_GetString(def->s_name),name) ) return def; } return NULL; }
/* ============ ED_FindFunction ============ */ dfunction_t *ED_FindFunction (char *name) { register dfunction_t *func; register int i; for (i=0 ; i<progs->numfunctions ; i++) { func = &pr_functions[i]; if (!strcmp(PR_GetString(func->s_name), name)) return func; } return NULL; }
/* ============ ED_FindGlobal ============ */ ddef_t *ED_FindGlobal (char *name) { ddef_t *def; int i; for (i=0 ; i<progs->numglobaldefs ; i++) { def = &pr_globaldefs[i]; if (!strcmp(PR_GetString(def->s_name),name) ) return def; } return NULL; }
/* PR_StackTrace */ void PR_StackTrace (progs_t *pr) { dfunction_t *f; int i; if (pr->pr_depth == 0) { Con_Printf ("<NO STACK>\n"); return; } pr->pr_stack[pr->pr_depth].f = pr->pr_xfunction; for (i = pr->pr_depth; i >= 0; i--) { f = pr->pr_stack[i].f; if (!f) { Con_Printf ("<NO FUNCTION>\n"); } else Con_Printf ("%12s : %s\n", PR_GetString (pr, f->s_file), PR_GetString (pr, f->s_name)); } }
static edict_t * FindViewthing(void) { int i; edict_t *e; for (i = 0; i < sv.num_edicts; i++) { e = EDICT_NUM(i); if (!strcmp(PR_GetString(e->v.classname), "viewthing")) return e; } Con_Printf("No viewthing on map\n"); return NULL; }
static edict_t * FindViewthing (void) { int i; edict_t *e; for (i = 0; i < sv.num_edicts; i++) { e = EDICT_NUM (&sv_pr_state, i); if (!strcmp (PR_GetString (&sv_pr_state, SVstring (e, classname)), "viewthing")) return e; } Sys_Printf ("No viewthing on map\n"); return NULL; }
/* Host_Map_f handle a map <servername> command from the console. Active clients are kicked off. */ static void Host_Map_f (void) { char name[MAX_QPATH]; const char *expanded; QFile *f; if (cmd_source != src_command) return; if (Cmd_Argc () > 2) { Sys_Printf ("map <levelname> : continue game on a new level\n"); return; } if (Cmd_Argc () == 1) { Sys_Printf ("map is %s \"%s\" (%s)\n", sv.name, PR_GetString (&sv_pr_state, SVstring (sv.edicts, message)), nice_time (sv.time)); return; } // check to make sure the level exists expanded = va ("maps/%s.bsp", Cmd_Argv (1)); QFS_FOpenFile (expanded, &f); if (!f) { Sys_Printf ("Can't find %s\n", expanded); return; } Qclose (f); cls.demonum = -1; // stop demo loop in case this fails CL_Disconnect (); Host_ShutdownServer (false); cl.loading = true; CL_UpdateScreen (cl.time); svs.serverflags = 0; // haven't completed an episode yet strcpy (name, Cmd_Argv (1)); SV_SpawnServer (name); if (!sv.active) return; if (cls.state != ca_dedicated) { Cmd_ExecuteString ("connect local", src_command); } }
/* ================ SV_SendServerinfo Sends the first message from the server to a connected client. This will be sent on the initial connection and upon each server load. ================ */ void SV_SendServerinfo (client_t *client) { const char **s; char message[2048]; int i; //johnfitz MSG_WriteByte (&client->message, svc_print); sprintf (message, "%c\nFITZQUAKE %1.2f SERVER (%i CRC)\n", 2, FITZQUAKE_VERSION, pr_crc); //johnfitz -- include fitzquake version MSG_WriteString (&client->message,message); MSG_WriteByte (&client->message, svc_serverinfo); MSG_WriteLong (&client->message, sv.protocol); //johnfitz -- sv.protocol instead of PROTOCOL_VERSION MSG_WriteByte (&client->message, svs.maxclients); if (!coop.value && deathmatch.value) MSG_WriteByte (&client->message, GAME_DEATHMATCH); else MSG_WriteByte (&client->message, GAME_COOP); MSG_WriteString (&client->message, PR_GetString(sv.edicts->v.message)); //johnfitz -- only send the first 256 model and sound precaches if protocol is 15 for (i=0,s = sv.model_precache+1 ; *s; s++,i++) if (sv.protocol != PROTOCOL_NETQUAKE || i < 256) MSG_WriteString (&client->message, *s); MSG_WriteByte (&client->message, 0); for (i=0,s = sv.sound_precache+1 ; *s ; s++,i++) if (sv.protocol != PROTOCOL_NETQUAKE || i < 256) MSG_WriteString (&client->message, *s); MSG_WriteByte (&client->message, 0); //johnfitz // send music MSG_WriteByte (&client->message, svc_cdtrack); MSG_WriteByte (&client->message, sv.edicts->v.sounds); MSG_WriteByte (&client->message, sv.edicts->v.sounds); // set view MSG_WriteByte (&client->message, svc_setview); MSG_WriteShort (&client->message, NUM_FOR_EDICT(client->edict)); MSG_WriteByte (&client->message, svc_signonnum); MSG_WriteByte (&client->message, 1); client->sendsignon = true; client->spawned = false; // need prespawn, spawn, etc }
/* ================ SV_CreateBaseline Entity baselines are used to compress the update messages to the clients -- only the fields that differ from the baseline will be transmitted ================ */ void SV_CreateBaseline (void) { int i, entnum, max_edicts; edict_t *svent; // because baselines for entnum >= 512 don't make sense // FIXME, translate baselines nums as well as packet entity nums? max_edicts = min (sv.num_edicts, 512); for (entnum = 0; entnum < max_edicts ; entnum++) { svent = EDICT_NUM(entnum); if (svent->free) continue; // create baselines for all player slots, and any other edict that has a visible model if (entnum > MAX_CLIENTS && !svent->v.modelindex) continue; // create entity baseline VectorCopy (svent->v.origin, svent->baseline.origin); VectorCopy (svent->v.angles, svent->baseline.angles); svent->baseline.frame = svent->v.frame; svent->baseline.skinnum = svent->v.skin; if (entnum > 0 && entnum <= MAX_CLIENTS) { svent->baseline.colormap = entnum; svent->baseline.modelindex = SV_ModelIndex("progs/player.mdl"); } else { svent->baseline.colormap = 0; svent->baseline.modelindex = SV_ModelIndex(PR_GetString(svent->v.model)); } // flush the signon message out to a separate buffer if // nearly full SV_FlushSignon (); // add to the message MSG_WriteByte (&sv.signon,svc_spawnbaseline); MSG_WriteShort (&sv.signon,entnum); MSG_WriteByte (&sv.signon, svent->baseline.modelindex); MSG_WriteByte (&sv.signon, svent->baseline.frame); MSG_WriteByte (&sv.signon, svent->baseline.colormap); MSG_WriteByte (&sv.signon, svent->baseline.skinnum); for (i = 0; i < 3; i++) { MSG_WriteCoord(&sv.signon, svent->baseline.origin[i]); MSG_WriteAngle(&sv.signon, svent->baseline.angles[i]); } } }
/* ============= ED_Write For savegames ============= */ void ED_Write (FILE *f, edict_t *ed) { ddef_t *d; int *v; int i, j; const char *name; int type; fprintf (f, "{\n"); if (ed->free) { fprintf (f, "}\n"); return; } for (i = 1; i < progs->numfielddefs; i++) { d = &pr_fielddefs[i]; name = PR_GetString(d->s_name); j = strlen (name); if (j > 1 && name[j - 2] == '_') continue; // skip _x, _y, _z vars v = (int *)((char *)&ed->v + d->ofs*4); // if the value is still all 0, skip the field type = d->type & ~DEF_SAVEGLOBAL; for (j = 0; j < type_size[type]; j++) { if (v[j]) break; } if (j == type_size[type]) continue; fprintf (f, "\"%s\" ", name); fprintf (f, "\"%s\"\n", PR_UglyValueString(d->type, (eval_t *)v)); } //johnfitz -- save entity alpha manually when progs.dat doesn't know about alpha if (!pr_alpha_supported && ed->alpha != ENTALPHA_DEFAULT) fprintf (f, "\"alpha\" \"%f\"\n", ENTALPHA_TOSAVE(ed->alpha)); //johnfitz fprintf (f, "}\n"); }
/* SV_Map_f handle a map <mapname> command from the console or progs. */ static void SV_Map_f (void) { const char *level; char *expanded; QFile *f; if (!curlevel) curlevel = dstring_newstr (); if (Cmd_Argc () > 2) { SV_Printf ("map <levelname> : continue game on a new level\n"); return; } if (Cmd_Argc () == 1) { SV_Printf ("map is %s \"%s\" (%s)\n", curlevel->str, PR_GetString (&sv_pr_state, SVstring (sv.edicts, message)), nice_time (sv.time)); return; } level = Cmd_Argv (1); // check to make sure the level exists expanded = nva ("maps/%s.bsp", level); f = QFS_FOpenFile (expanded); if (!f) { SV_Printf ("Can't find %s\n", expanded); free (expanded); return; } Qclose (f); free (expanded); if (sv.recording_demo) SV_Stop (0); SV_qtvChanging (); SV_BroadcastCommand ("changing\n"); SV_SendMessagesToAll (); dstring_copystr (curlevel, level); SV_SpawnServer (level); SV_qtvReconnect (); SV_BroadcastCommand ("reconnect\n"); }
char *PR_GlobalStringNoContents (int ofs) { int i; ddef_t *def; static char line[128]; def = ED_GlobalAtOfs(ofs); if (!def) snprintf (line, sizeof(line), "%i(?""?""?)", ofs); // separate the ?'s to shut up gcc else snprintf (line, sizeof(line), "%i(%s)", ofs, PR_GetString(def->s_name)); i = strlen(line); for ( ; i<20 ; i++) strlcat (line, " ", sizeof(line)); strlcat (line, " ", sizeof(line)); return line; }
const char *PR_GlobalStringNoContents (int ofs) { static char line[512]; int i; ddef_t *def; def = ED_GlobalAtOfs(ofs); if (!def) sprintf (line,"%i(?)", ofs); else sprintf (line,"%i(%s)", ofs, PR_GetString(def->s_name)); i = strlen(line); for ( ; i < 20; i++) strcat (line, " "); strcat (line, " "); return line; }
/* ============= ED_Print For debugging ============= */ void ED_Print (edict_t *ed) { ddef_t *d; int *v; int i, j, l; const char *name; int type; if (ed->free) { Con_Printf ("FREE\n"); return; } Con_SafePrintf("\nEDICT %i:\n", NUM_FOR_EDICT(ed)); //johnfitz -- was Con_Printf for (i = 1; i < progs->numfielddefs; i++) { d = &pr_fielddefs[i]; name = PR_GetString(d->s_name); l = strlen (name); if (l > 1 && name[l - 2] == '_') continue; // skip _x, _y, _z vars v = (int *)((char *)&ed->v + d->ofs*4); // if the value is still all 0, skip the field type = d->type & ~DEF_SAVEGLOBAL; for (j = 0; j < type_size[type]; j++) { if (v[j]) break; } if (j == type_size[type]) continue; Con_SafePrintf ("%s", name); //johnfitz -- was Con_Printf while (l++ < 15) Con_SafePrintf (" "); //johnfitz -- was Con_Printf Con_SafePrintf ("%s\n", PR_ValueString(d->type, (eval_t *)v)); //johnfitz -- was Con_Printf } }
/* =============== SV_SavegameComment Writes a SAVEGAME_COMMENT_LENGTH character comment FIXME: What is the right place for this function? If it stays in server, then the CL_Stat_Monsters is architecturally ugly Move it to client? On the other hand, if we make it fully client-independent, "save" and "load" could be made to work even on dedicated servers. Not sure though how useful that would be. =============== */ void SV_SavegameComment (char *text) { int i; char kills[20]; char *levelname; for (i = 0; i < SAVEGAME_COMMENT_LENGTH; i++) text[i] = ' '; levelname = PR_GetString(sv.edicts->v.message); memcpy (text, levelname, min(strlen(levelname), 21)); sprintf (kills, "kills:%3i/%-3i", CL_Stat_Monsters(), CL_Stat_TotalMonsters()); memcpy (text+22, kills, strlen(kills)); // convert space to _ to make stdio happy for (i = 0; i < SAVEGAME_COMMENT_LENGTH; i++) if (text[i] == ' ') text[i] = '_'; text[SAVEGAME_COMMENT_LENGTH] = '\0'; }