Recherche
 
Fermer
TRANSLATE
Documentation - CODAGE

Nombre de membres 


CODAGE

Les fichiers ENT  -  par FSMOD

Les fichiers ENT

Les fichiers additifs ent ont pour but de modifier certains aspects d'une map comme le changement de textures, l'ajout ou la suppression de points de respawn, la modification des décors, etc. Si vous voulez en connaître plus, veuillez consulter sur le site, tous les articles liés à cette fonction, notamment dans la rubrique MDK, et plus précisément dans la sous rubrique ENT Editor.

Les codes édités ci-après s'appliquent principalement aux sources CRSBOT mais restent compatibles avec les sources ROCMOD à la différence d'un seul fichier relatif à la gestion des Waypoints. Cette différence sera développée in fine, ainsi qu'une documentation sur la création des répertoires d'accueil des fichiers ent et des routes associées.

Ces codes ont été inspirés des sources GOLDRUSH dans la gestion des fichiers aef. L'idée de la multiplicité des fichiers ent pour une même map, m'est apparue évidente en exploitant le mode MDK

   Fichier g_local_h  

bas.gif Après

 qboolean    G_ParseSpawnVars( qboolean inSubBSP );

puce1.gif Ajoutez

  qboolean    G_ParseSpawnAEVars( qboolean inSubBSP ); // FSMOD

puce1.gif A la fin du fichier ajoutez

  extern vmCvar_t     g_mapVar; // FSMOD

   Fichier g_bot.c   

bas.gif Après

  static botSpawnQueue_t    botSpawnQueue[BOT_SPAWN_QUEUE_DEPTH];

puce1.gif Ajoutez


   qboolean G_findAEFile( const char* gametype );    // FSMOD
 static fileHandle_t aefFile;                     // FSMOD

 

bas.gif A la fin de la fonction G_DoesMapSupportGametype, après

  return qfalse;

puce1.gif Ajoutez

 

 // FSMOD GO
  
    if ( G_findAEFile( gametype ))
    {
        return qtrue;
    }
    return qfalse;
  }

 qboolean G_findAEFile( const char* gametype )
  {
    char entPath[128];
    vmCvar_t mapname;
    int len;

    aefFile = 0;
    trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM, 0.0, 0.0 );

    // first up, try finding an .ent file with the gametype extension in mvar folders
    switch (g_mapVar.integer)
    {
    case 1:
         Com_sprintf(entPath, 128, "maps/ent/mvar1/%s_%s.ent\0", mapname.string, level.gametypeData->name);
         break;
    case 2:
         Com_sprintf(entPath, 128, "maps/ent/mvar2/%s_%s.ent\0", mapname.string, level.gametypeData->name);
         break;
    case 3:
         Com_sprintf(entPath, 128, "maps/ent/mvar3/%s_%s.ent\0", mapname.string, level.gametypeData->name);
         break;
    case 4:
         Com_sprintf(entPath, 128, "maps/ent/mvar4/%s_%s.ent\0", mapname.string, level.gametypeData->name);
         break;
    case 5:
         Com_sprintf(entPath, 128, "maps/ent/mvar5/%s_%s.ent\0", mapname.string, level.gametypeData->name);
         break;
    case 6:
         Com_sprintf(entPath, 128, "maps/ent/mvar6/%s_%s.ent\0", mapname.string, level.gametypeData->name);
         break;
    case 7:
         Com_sprintf(entPath, 128, "maps/ent/mvar7/%s_%s.ent\0", mapname.string, level.gametypeData->name);
         break;
    case 8:
         Com_sprintf(entPath, 128, "maps/ent/mvar8/%s_%s.ent\0", mapname.string, level.gametypeData->name);
         break;
    default:
         Com_sprintf(entPath, 128, "maps/ent/%s_%s.ent\0", mapname.string, level.gametypeData->name);
         break;

    }

    len = trap_FS_FOpenFile(entPath, &aefFile, FS_READ);

    //file not found
    if (!aefFile)
    {
        return qfalse;
    }

    trap_FS_FCloseFile(aefFile);

    //file is too big, we cant read it
    if (len >= 131072)
    {
        Com_Printf( S_COLOR_RED "G_findAEFile: file %s found, but too large (%i)! Max allowed %i bytes!", entPath, len, 131072);
        return qfalse;
    }

    // found file
    return qtrue;


 // FSMOD stop

   Fichier g_spawn.c   

bas.gif Après

  #include "g_local.h"

puce1.gif Ajoutez

 

 // FSMOD GO
  qboolean G_LoadAEFile(void);       
  char *G_GetEntFileToken(void);       
  static fileHandle_t entFile;       
  extern char *buffer;           
 // FSMOD STOP

bas.gif Dans la fonction G_ParseSpawnVars, à la fin du groupe

 

 if (inSubBSP)
    {
        HandleEntityAdjustment();
    }

    return qtrue;
 }

puce1.gif Ajoutez

 

 // FSMOD GO
 qboolean G_ParseSpawnAEVars( qboolean inSubBSP )
  {
    char    keyname[MAX_TOKEN_CHARS];
    char    com_token[MAX_TOKEN_CHARS];
    char    *token;

    level.numSpawnVars = 0;
    level.numSpawnVarChars = 0;

    // parse the opening brace
    token = G_GetEntFileToken();
    if (!token)
    {
        return qfalse;
    }
    Com_sprintf(com_token, sizeof(com_token), "%s", token);
 

       if ( com_token[0] != '{' )
    {
        Com_Error( ERR_FATAL, "G_ParseSpawnVars: found %s when expecting {",com_token );
    }

    // go through all the key / value pairs
    while ( 1 )
    {
        // parse key
        token = G_GetEntFileToken();
        if (!token)
        {
            Com_Error( ERR_FATAL, "G_ParseSpawnVars: EOF without closing brace" );
        }
        Com_sprintf(keyname, sizeof(keyname), "%s", token);


        if ( keyname[0] == '}' )
        {
            break;
        }

        // parse value
        token = G_GetEntFileToken();
        if (!token)
        {
            Com_Error( ERR_FATAL, "G_ParseSpawnVars: EOF without closing brace" );
        }
        Com_sprintf(com_token, sizeof(com_token), "%s", token);


        if ( com_token[0] == '}' )
        {
            Com_Error( ERR_FATAL, "G_ParseSpawnVars: closing brace without data" );
        }

        if ( level.numSpawnVars == MAX_SPAWN_VARS )
        {
            Com_Error( ERR_FATAL, "G_ParseSpawnVars: MAX_SPAWN_VARS" );
        }
        AddSpawnField(keyname, com_token);
    }

    if (inSubBSP)
    {
        HandleEntityAdjustment();
    }
    return qtrue;
 }

 //FSMOD STOP

bas.gif Dans la fonction G_SpawnEntitiesFromString remplacez le groupe

     if (!inSubBSP)
    {
        level.spawning = qfalse;
    }

puce1.gif Par celui ci

 

 // FSMOD GO
 

  if (!inSubBSP)
    {
        if (G_LoadAEFile())
        {
            while(G_ParseSpawnAEVars(qfalse))
            {
                G_SpawnGEntityFromSpawnVars(qfalse);
            }
        }
    }

  if (!inSubBSP)
    {
        level.spawning = qfalse;
    }

 // FSMOD STOP

bas.gifA la fin du fichier g_spawn.c, après le groupe

  trap_LinkEntity ( ent );
 }

puce1.gif Ajoutez

 

 // FSMOD GO

 qboolean G_LoadAEFile(void) //GR3.0: Check (and read) AEF files (additional entities file)
  {
    char entPath[128];
    vmCvar_t mapname;
    int len;

    entFile = 0;
    trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM, 0.0, 0.0 );

    // first up, try finding an .ent file with the gametype extension in mvar folders
    switch (g_mapVar.integer)
    {
    case 1:
         Com_sprintf(entPath, 128, "maps/ent/mvar1/%s_%s.ent\0", mapname.string, level.gametypeData->name);
         break;
    case 2:
         Com_sprintf(entPath, 128, "maps/ent/mvar2/%s_%s.ent\0", mapname.string, level.gametypeData->name);
         break;
    case 3:
         Com_sprintf(entPath, 128, "maps/ent/mvar3/%s_%s.ent\0", mapname.string, level.gametypeData->name);
         break;
    case 4:
         Com_sprintf(entPath, 128, "maps/ent/mvar4/%s_%s.ent\0", mapname.string, level.gametypeData->name);
         break;
    case 5:
         Com_sprintf(entPath, 128, "maps/ent/mvar5/%s_%s.ent\0", mapname.string, level.gametypeData->name);
         break;
    case 6:
         Com_sprintf(entPath, 128, "maps/ent/mvar6/%s_%s.ent\0", mapname.string, level.gametypeData->name);
         break;
    case 7:
         Com_sprintf(entPath, 128, "maps/ent/mvar7/%s_%s.ent\0", mapname.string, level.gametypeData->name);
         break;
    case 8:
         Com_sprintf(entPath, 128, "maps/ent/mvar8/%s_%s.ent\0", mapname.string, level.gametypeData->name);
         break;
    default:
         Com_sprintf(entPath, 128, "maps/ent/%s_%s.ent\0", mapname.string, level.gametypeData->name);
         break;
    }

    len = trap_FS_FOpenFile(entPath, &entFile, FS_READ);

    if (!entFile)
    {    // failing that, just try by map name
        
 Com_sprintf(entPath, 128, "maps/ent/%s.ent\0", mapname.string);

        len = trap_FS_FOpenFile(entPath, &entFile, FS_READ);
        if (!entFile)
        {
            Com_Printf( "No additional entity file found (skipping)\n" );
            return qfalse;
        }
    }

            Com_Printf(S_COLOR_YELLOW "Reading additional entity data from \"%s\"\n", entPath);

    if (len >= 131072)
    {
        Com_Printf( S_COLOR_RED "file too large: %s is %i, max allowed is %i", entPath, len, 131072);
        trap_FS_FCloseFile(entFile);
        return qfalse;
    }

    trap_FS_Read(buffer, len, entFile);
    buffer[len] = 0;
    trap_FS_FCloseFile(entFile);

    return qtrue;
  }

 static char token[MAX_TOKEN_CHARS];

 char *G_GetEntFileToken(void)
  {
    qboolean hasNewLines = qfalse;
    const char *data;
    int c = 0, len;

    data = buffer;
    len = 0;
    token[0] = 0;

    // make sure incoming data is valid
    if (!data)
    {
        buffer = NULL;
        return NULL;
    }

    while (1)
    {
        // skip whitespace
        data = SkipWhitespace(buffer, &hasNewLines);
        if ( !data )
        {
            buffer = NULL;
            return NULL; // EOF
            //return token;
        }

        c = *data;
        // skip double slash comments
        if (c == '/' && data[1] == '/')
        {
            data += 2;
            while (*data && *data != 'n')
            {
                data++;
            }
        }
       
        else if ( c=='/' && data[1] == '*')
        {
            data += 2;
            while (*data && (*data != '*' || data[1] != '/' ))
            {
                data++;
            }

            if (*data)
            {
                data += 2;
            }
        }
        else
        {
            break;
        }
    }

    // handle quoted strings
    if (c == '"')
    {
        data++;
        while (1)
        {
            c = *data++;
            if (c=='"' || !c)
            {
                token[len] = 0;
                buffer = ( char * ) data;
                return token;
            }

            if (len < MAX_TOKEN_CHARS)
            {
                token[len] = c;
                len++;
            }
        }
    }

    // parse a regular word
    do
    {
        if (len < MAX_TOKEN_CHARS)
        {
            token[len] = c;
            len++;
        }
        data++;
        c = *data;
    } while (c>32);

    if ( len == MAX_TOKEN_CHARS )
    {

        len = 0;
    }

    token[len] = 0;

    buffer = (char *)data;

    if ( token[0] == 0 || token[0] == ' ' )
    {
        return NULL; // EOF
    }

    return token;
 }

 // FSMOD STOP

   Fichier zg_waypoint.c   

bas.gif Dans la fonction G_LoadPoints remplacez le groupe

 

  Com_sprintf(routePath, 128, "botroutes/%s_%s.wps\0", mapname.string, level.gametypeData->name);

  pointsFile = NULL;

puce1.gif Par celui-ci

 

  // FSMOD GO
         switch (g_mapVar.integer)
    {
         case 1:
         Com_sprintf(routePath, 1024, "botroutes/mvar1/%s_%s.wps\0", mapname.string, level.gametypeData->name);
         break;
         case 2:
         Com_sprintf(routePath, 1024, "botroutes/mvar2/%s_%s.wps\0", mapname.string, level.gametypeData->name);
         break;
         case 3:
         Com_sprintf(routePath, 1024, "botroutes/mvar3/%s_%s.wps\0", mapname.string, level.gametypeData->name);
         break;
         case 4:
         Com_sprintf(routePath, 1024, "botroutes/mvar4/%s_%s.wps\0", mapname.string, level.gametypeData->name);
         break;
         case 5:
         Com_sprintf(routePath, 1024, "botroutes/mvar5/%s_%s.wps\0", mapname.string, level.gametypeData->name);
         break;
         case 6:
         Com_sprintf(routePath, 1024, "botroutes/mvar6/%s_%s.wps\0", mapname.string, level.gametypeData->name);
         break;
         case 7:
         Com_sprintf(routePath, 1024, "botroutes/mvar7/%s_%s.wps\0", mapname.string, level.gametypeData->name);
         break;
         case 8:
         Com_sprintf(routePath, 1024, "botroutes/mvar8/%s_%s.wps\0", mapname.string, level.gametypeData->name);
         break;

         default:

         Com_sprintf(routePath, 1024, "botroutes/%s_%s.wps\0", mapname.string, level.gametypeData->name);

         break;
    }
 // FSMOD STOP

demo.png A noter que le fichier zg_waypoint.g est propre au mode CRSBOT car il assure la prise en charge des fichiers botroutes en extension wps.

Si vous codez sous ROCMOD il conviendra de mobiliser le fichier ai_wpnav.c qui lui, assure la gestion des fichiers botroutes en extension wnt.

   Fichier ai_wpnav.c  

bas.gif Dans la fonction int LoadPathData(const char *filename), remplacez la ligne

 Com_sprintf(routePath, 1024, "botroutes/%s.wnt\0", filename);

puce1.gif Par le groupe

 // FSMOD GO

 
   switch (g_mapVar.integer)  
     {
         case 1:
         Com_sprintf(routePath, 1024, "botroutes/mvar1/%s_%s.wnt\0", filename, bg_gametypeData[gametype].name);
         break;
         case 2:
         Com_sprintf(routePath, 1024, "botroutes/mvar2/%s_%s.wnt\0", filename, bg_gametypeData[gametype].name);
         break;
         case 3:
         Com_sprintf(routePath, 1024, "botroutes/mvar3/%s_%s.wnt\0", filename, bg_gametypeData[gametype].name);
         break;
         case 4:
         Com_sprintf(routePath, 1024, "botroutes/mvar4/%s_%s.wnt\0", filename, bg_gametypeData[gametype].name);
         break;
         case 5:
         Com_sprintf(routePath, 1024, "botroutes/mvar5/%s_%s.wnt\0", filename, bg_gametypeData[gametype].name);
         break;
         case 6:
         Com_sprintf(routePath, 1024, "botroutes/mvar6/%s_%s.wnt\0", filename, bg_gametypeData[gametype].name);
         break;
         case 7:
         Com_sprintf(routePath, 1024, "botroutes/mvar7/%s_%s.wnt\0", filename, bg_gametypeData[gametype].name);
         break;
         case 8:
         Com_sprintf(routePath, 1024, "botroutes/mvar8/%s_%s.wnt\0", filename, bg_gametypeData[gametype].name);
         break;


         default:


         Com_sprintf(routePath, 1024, "botroutes/%s_%s.wnt\0", filename, bg_gametypeData[gametype].name);


         break;

     }

  // FSMOD STOP

screen.gif  Gestion des fichiers ENT

Ces codes permettent de gérer au choix 9 entités ENT de 0 à 8. Mais avant de les exploiter il convient de se pencher un peu sur la configuration du serveur.

Plutôt qu'un long discours, voici un extrait de l'arborescence de mon serveur FSMOD.

arborescence.jpg

Vous devrez créer un répertoire maps, dans lequel vous allez créer un répertoire ent et dans lequel vous allez finalement créer autant de répertoires mvar numérotés selon le classement que vous adopterez.

Dans le répertoire botroutes vous allez créer également les mêmes répertoires que vous avez créés dans le répertoire ent. Il est important de souligner que ces répertoires auront la même numérotation.

Voyons maintenant ce que vous allez inscrire dans ces répertoires.

dir.gif  Répertoire maps

rep_maps_bsp.jpg

Le contenu de ce répertoire appelle une précision importante et tout repose sur le type de fichiers ent que vous voulez exploiter.
puce1.gifPlusieurs cas peuvent ainsi se présenter:

calendard.gif Vous voulez simplement changer les textures d'une map, en ce cas la création d'un fichier ent suffira.

calendard.gif Vous voulez simplement ajouter quelques points de respawn, en ce cas la création d'un fichier ent suffira.

calendard.gif Vous voulez modifier tous les points de respawn, en ce cas il va falloir modifier le fichier bsp des maps concernées. C'est la raison pour laquelle, à ce stade du tutoriel vous constatez la présence de fichiers bsp dans le répertoire maps.
La suppression des points de respawn natifs est absolument nécessaire car l'ajout de nouveaux points n'invalidera pas l'existence des points originels et ces derniers généreront des bots statiques pour ne pas avoir été intégrés dans le canevas des botroutes.

puce1.gif Pour illustrer cette singularité, voici un exemple de la map standard mp_finca dans laquelle une partie du thêatre non accessible dans sa version d'origine a pu être exploitée:

Version d'origine  Version modifiée
finca_orig.jpg finca_corner.jpg

Bien entendu les 2 cas font chacun l'objet d'un fichier ent différent, donc un placement dans un dossier mvar différent. Ces 2 fichiers ent inclueront les points de respawn des bots et éventuellement quelques agréments de décor, muret, poste de guet, arbres, etc.., car il est possible de modifier également le thêatre originel de mp_finca. D'ailleurs pour ce thêatre originel vous pouvez recopier les anciens points de respawn ou mieux, en créer de nouveaux. Tout est possible avec les fichiers ent.

screen.gif  Modification des fichiers BSP

puce1.gif Extraire le fichier bsp du fichier de la map concernée soit avec Pakscape, soit en renommant un clone du pk3 en zip puis en le décompressant.

puce1.gif Faire une recherche sur info_player_deathmatch et effacez tous les paragraphes qui renferment ce classname. Veillez à effacer les accolades ouvrantes et fermantes.

  {
    "classname" "info_player_deathmatch"
    "angles" "0 360 0"
    "origin" "-5392 -5120 70"
  }

puce1.gif Si vous voulez effacer les points de respawn afférents à d'autres gametypes, effacez tous les paragrahes renfermant la ligne "classname" "gametype_player"

  {
    "classname" "gametype_player"
    "gametype" "inf elim ctf dem"
    "angles" "0 90 0"
    "origin" "-1248 -1560 74"
    "spawnflags" "2"
  }

aide.png ATTENTION : Certains programmeurs utilisent le classname gametype_player pour spécifier le DM, ainsi la ligne gametype pourrait se présenter comme suit: 

       "gametype" "dm tdm inf elim ctf dem"

Il est évident que ce cas doit faire l'objet d'un même traitement que les les précédents.

A ce stade, il est important de préciser plusieurs points:

  • Les fichiers maps pk3 dans le répertoire base du serveur ne doivent jamais être modifiés. Ils servent de lien corrélatif avec leurs homologues inclus dans le répertoire base côté client.
  • Le chargement des fichier bsp du répertoire maps s'opère chronologiquement après les fichiers pk3 du répertoire base. C'est donc eux qui délivrent les informations au serveur.
  • Les fichiers ent se chargent après le fichier bsp et lui délivrent les modifications.
  • Malgré la présence de ces fichiers bsp il n'y aura aucun mismatch client /serveur dans la mesure où les nouveaux éléments codés dans les fichiers ent sont envoyés au client par les échanges de paquets, de telle sorte que le client verra exactement ce que 'voit' le serveur.

dir.gif  Les autres répertoires

Les captures suivantes n'ont qu'une vocation illustrative

rep_maps_mvar1.jpg rep_maps_mvar4.jpg

screen.gif  Les fichiers botroutes

Le principe de base énonce que si un fichier ent lié à une map a été placé dans le répertoire maps/ent/mvar1 , le fichier route associé à cette map doit être placé impérativement dans le répertoire botroutes/mvar1. La concordance dans la numérotation des répertoires mvar doit être toujours respectée.

Dans la capture suivante on s'aperçoit que les routes sont placées directement dans le répertoire botroutes sans être affectées à un répertoire mvar. La raison est simple, c'est que les maps concernées n'ont pas fait l'objet d'un fichier additif ent. Conférez à cet effet, la sortie "default" de la boucle switch des codes précédents.

botroutes.jpg

puce1.gif A noter toutefois qu'il était tout à fait possible d'inscrire ces routes dans un répertoire mvar et de le préciser au serveur par une commande qui sera décrite plus bas.

La capture suivante illustre par exemple le contenu du répertoire botroutes/mvar4.

botroutes_mvar4.jpg

aide.png IMPORTANT : Les fichiers ent ainsi que les routes associées doivent impératvement être nommés avec un suffixe d'identification du gametype. Ainsi un fichier ent du type mp_finca.ent ou un fichier route du type mp_finca.wps (wnt) ne seront pas reconnus. Il faudra leur préférer les types mp_finca_dm.ent et mp_finca_dm.wps (wnt).

screen.gif  le cvar g_mapVar

Il a pour fonction de fixer l'index numéral de 0 à 8. Il ne doit pas être intégré au fichier de configuration du serveur mais doit être précisé pour chaque map du fichier mapcycle, comme le montrent les extraits suivants:

La map UK_Prague a été enrichie d'un fichier ent dans le répertoire mvar4.

 map10
 {
  Command "map UK_Prague;kick allbots"
   cvars
   {
      g_mapVar "4"
      g_botsFile "colbot/j.txt"
      g_motd "^3[Map 10-v4]"
      scorelimit "50"
      bot_minplayers "10"
   }
 }

La map mp_hos1 n'a pas été modifiée par un fichier ent, le cvar g_mapVar est donc par défaut à 0.

  map 12
 {
  Command "map mp_hos1;kick allbots"
   cvars
   {
      g_mapVar "0"
      g_botsFile "colbot/s.txt"
      g_motd "^3[Map 12-v11]"  
      scorelimit "40"
      bot_minplayers "7"
   }
 }

screen.gif  Les routes wps et wnt

Je vous invite à consulter sur le présent site, la méthodologie dans l'élaboration des botroutes wps ou wnt . Toutefois à l'époque où elles avaient été décrites, je n'avais pas fait echo des fichiers ent dont la prise en compte est nécessaires dans les scripts de création.

puce1.gif La modification est simple.

Dans le répertoire wps ajouter l'arborescence de répertoires maps/ent et placez dans le répertoire ent les fichiers ent afférents aux maps que vous avez choisies.

Pour les routes wnt procédez de la même façon dans le répertoire wnt.

Dans les 2 cas les fichiers ent peuvent conserver leur suffixe gametype.

Puis procéder sans autre changement, conformément à la méthode développée dans ma documentation.

-=rolleyes=-

Publié le 14/01/2024 14:20   Toutes les pages   Prévisualiser...   Imprimer...   Haut
Rubriques

Vous êtes ici :   Accueil » Documentation » CODAGE