Connect to ip menu


(Elite) #21

Ok, now we are on the same page :slight_smile:

I will make something up tonight (code) with alot of documentation in it so it is much more clear and precise, and you can see exactly the types of function calls are necessary, the types of places they would belong in, how the whole concept should work, and things such as that. Hopefully you will gain a bit more understanding of the types of thinsg to do in certain situations when dealing with the et sdk. I know myself I am no master yet, but I know it does take some time to get comfortable with an unfamiliar system.

Anyhow, ya, tomorrow I will have something more useful for you to play with. Sorry for the delay, but the NHL playoffs are on afterall :slight_smile:


(Elite) #22

Ok, the first part is allready done (user interface). Now you need to make it functional. I am going to do some coding here in ways that may not be best for what you need, but will allow for easy additions to the system and greater flexibility. I am writing this on the fly, so you may need to adjust things slightly if they give you any errors, but it should be fine for the most part. We will wait and see. :slight_smile:

===================

To get started, create a new header file in the cgame folder, you can title it whatever you want, but for the purpose of this example we will call it ‘cg_favorites.h’. In this file is where all the data structures and function prototypes will be declared, as well as any #defines that are needed.

Open the new file, cg_favorites.h, and prepare it for usage:

#ifndef CG_FAVORITES_H
#define CG_FAVORITES_H

#endif

Now that we have our header file ready to go, we need a structure to hold the server favorite data. At this point all we need to know is the server ip and the server port. I am placing a prefix of sf_ on all the structures to avoid possible conflicts with other code that may be present in the sdk. You can use whatever prefix you like, just stick to something that makes sense (I am using sf_ in this example which represents server favorites for now).

#ifndef CG_FAVORITES_H
#define CG_FAVORITES_H

//make sure we define some limits to ensure consistency
#define SF_FILENAME            "favorites.cfg"
#define SF_MAX_FAVORITES 20        //maximum server favorites to store
#define SF_MAX_IP               15       //maximum length of ip
#define SF_MAX_PORT          4         //maximum length of port

//this structure will hold the data for the server favorites
typedef struct {
   char ip[SF_MAX_IP];
   char port[SF_MAX_PORT];
} sf_favorite_t;

#endif

Ok, now we have a place to hold the data, but what else are we going to need? Well, for starters, we will need a way to load the data into memory to be read, and we will also need a way to store the data back on a file on disk. It is also easier to break up common pieces of code into helper functions. Let’s create some function protoptypes for handling this now:

#ifndef CG_FAVORITES_H
#define CG_FAVORITES_H

//make sure we define some limits to ensure consistency
#define SF_FILENAME            "favorites.cfg"
#define SF_MAX_FAVORITES 20        //maximum server favorites to store
#define SF_MAX_IP               15       //maximum length of ip
#define SF_MAX_PORT          4         //maximum length of port

//this structure will hold the data for the server favorites
typedef struct {
   char ip[SF_MAX_IP];
   char port[SF_MAX_PORT];
} sf_favorite_t;

//function prototypes
void SF_ReadConfig(char **cnf, char *s, int size);   //read from config file
void SF_WriteConfig(char *s, fileHandle_t f);          //write to config file

void SF_LoadFavorites ();       //load the server favorites from a file
void SF_StoreFavorites ();      //save server favorites to file

void SF_Cleanup ();               //cleanup resources used by server favorites system

#endif

Ok, let’s make this new header file available to the cgame module. We can do this by including this header file in cg_local.h. Just stick it in at the bottom of the file:

#include "cg_favorites.h"

Ok, great. Now we have a framework to work from. At this point in time we will create a new file that will contain the actual code for these function prototypes and what not. Create a new file, for the purpose of this example we will call it ‘cg_favorites.c’. Once the file has been created, we need to code the functions to store data, as well as declare some instances of the server favorite structures in order to use them.


//includes
#include "cg_local.h"

//globals
sf_favorite_t *sf_favorites[SF_MAX_FAVORITES];   //server favorites

//read from config file
void SF_ReadConfig(char **cnf, char *s, int size) {
   char *t;

   t = COM_ParseExt(cnf, qfalse);
   if(!strcmp(t, "=")) {
      t = COM_ParseExt(cnf, qfalse);
   } else {
      G_Printf("XPSave: readconfig error (missing = before %s on line %d)
", t, COM_GetCurrentParseLine());
   }
   s[0] = '\0';
   while(t[0]) {
      if((s[0] == '\0' && strlen(t) <= size) || (strlen(t)+strlen(s) < size)) {
         Q_strcat(s, size, t);
         Q_strcat(s, size, " ");
      }
      t = COM_ParseExt(cnf, qfalse);
   }
   // trim the trailing space
   if(strlen(s) > 0 && s[strlen(s)-1] == ' ') {
      s[strlen(s)-1] = '\0';
   }
}

//write to config file
void SF_WriteConfig(char *s, fileHandle_t f) {
   char buf[MAX_STRING_CHARS];

   buf[0] = '\0';
   if(s[0]) {
      Q_strncpyz(buf, s, sizeof(buf));
      trap_FS_Write(buf, strlen(buf), f);
   }
   trap_FS_Write("
", 1, f);
}

//load the server favorites from a file
void SF_LoadFavorites () {
	//declare variables
	int		    len, i;
	fileHandle_t	f;
	char	          *cnf, *cnf2, *t;
	qboolean	record_open			= qfalse;
	int		     rc					   = 0;
	sf_favorite_t	*r				      = sf_favorites[0];

	//open server favorites file
	len = trap_FS_FOpenFile(SF_FILENAME, &f, FS_READ) ;
	if(len < 0) {
		CG_Printf("ERROR: could not open file (%s)
", SF_FILENAME);
		return;
	}

	//read file
	cnf = malloc(len+1);
	cnf2 = cnf;
	trap_FS_Read(cnf, len, f);
	*(cnf + len) = '\0';

	//close file
	trap_FS_FCloseFile(f);

	//cleanup old data
	SF_Cleanup();

	//parse file
	t = COM_Parse(&cnf);
	while(*t) {
		if(!Q_stricmp(t, "[favorite]")) {
			if(record_open) {
				sf_favorites[rc++] = r;
			}
			record_open = qfalse;
		}

		if(record_open) {
			if(!Q_stricmp(t, "ip")) {
				SF_ReadConfig(&cnf, r->ip, sizeof(r->ip));
			} else if(!Q_stricmp(t, "port")) {
				SF_ReadConfig(&cnf, r->port, sizeof(r->port));
			} else {
				CG_Printf("ERROR: readconfig error (parse error near %s on line %d)
", t, COM_GetCurrentParseLine());
			}
		}

		if(!Q_stricmp(t, "[favorite]")) {
			if(rc >= SF_MAX_FAVORITES) {
				CG_Printf("ERROR: SF_MAX_FAVORITES exceeded (%i)", SF_MAX_FAVORITES);
				return;
			}
			r = malloc(sizeof(sf_favorite_t));
			r->ip[0] = '\0';
			r->port[0] = '\0';
			record_open = qtrue;
		}
		t = COM_Parse(&cnf);
	}

	//make sure last record is used also
	if(record_open) {
		sf_favorites[rc++] = r;
	}

	//release resources
	free(cnf2);

	//display message
	CG_Printf("Client: loaded %d server favorites
", rc);
}

//save server favorites to file
void SF_StoreFavorites () {
	//declare variables
	int		    len, i;
	fileHandle_t	f;

	//open server favorites file
	len = trap_FS_FOpenFile(SF_FILENAME, &f, FS_WRITE);
	if(len < 0) {
		CG_Printf("ERROR: could not open file (%s)
", SF_FILENAME);
	}

	//write server favorite records to file
	for(i=0; sf_favorites[i]; i++) {
		trap_FS_Write("[favorite]
", 11, f);
		trap_FS_Write("ip   = ", 8, f);
		SF_WriteConfig(sf_favorites[i]->ip, f);
		trap_FS_Write("port = ", 8, f);
		SF_WriteConfig(sf_favorites[i]->port, f);
		trap_FS_Write("
", 1, f);
	}

	//close file
	trap_FS_FCloseFile(f);

	//display message
	CG_Printf("Client: wrote %d server favorites
", i);
}

//cleanup resources used by server favorites system
void SF_Cleanup () {
   //declare variables
   int i = 0;

   //cleanup resources
   for(i=0; sf_favorites[i]; i++) {
      free(sf_favorites[i]);
      sf_favorites[i] = NULL;
   }
}

================

Ok, so at this point we now have code in place to both read and write server favorite records. There is still much work to be done however. At this point in time the code will never be executed. You must determine when you want to load the records and what you want to do with them. Examine this code for now, and get an idea of how files are opened, closed, read, and written. You will notice several trap_* function calls that perform these actions allready for us.

I will leave it to you to finish off the code. Just give it a shot and fill in blanks where you can. I know the formattign here is bad, but the spacing didn’t look right when pasted into these forums. Some tabs were removed to give it a more aligned appearance, and most places I didn’t bother making look pretty. You get the idea.

If you have more troubles finishing this system, post what you have tried and when you would prefer to do things with the code (ie, display data), and such thinsg like that. You need to give it a good effort from here on in to actually learn anything from this system, so for any further help I will probably ask you many questions to get you thinking right before I give much code, unless it is simpler to explain with code itself.

Anyhow, good luck!


(Xterm1n8or) #23

OMG :shock:

I feel a little embarrassed now :oops: You’ve obviously spent quite some time putting this together. When i thought of doing this, i had no idea it would involve this much! A plain thankyou seems a bit lame, but for what its worth, thankyou very much :slight_smile:

It looks like i have my work cut out. Its going to take me a little while to get through this. Will let you know how i get on.

Once again, many, many thanks :smiley:


(Elite) #24

Not a problem, but like I said it’s only halfway done. I will continue to walk you through this, but try as much as possible to take it on you’re own from here. Compare it with the original XPSave code I pasted and see where I took some things from. The system is pretty much identical (in code), so hopefully this makes some more sense now. Some function calls are allready in the sdk (you can see hwo files were opened, read, written, and closed with the trap_FS_* calls, and how messages are sent to the console with the CG_Printf function call). Just remember to take it one step at a time. Even though the end result looks like lots has been done, take it function by function and eahc task will be much smaller and less complicated.

=============
I guess I shoudla put some more info up there also:

  • Call the function SF_LoadFavorites to load fav’s from file
  • Call the function SF_Store Favorites to save to file
  • The other functions are really not necessary to be called… they just assist the other functions
  • This only gets them into memory… they still need to be displayed.
    =============

Anyhow, good luck, and just give this topic another post when you need it.


(Xterm1n8or) #25

O dear, I think i’ve made a boob :roll:
I’ve been googleing, using the search in the compiler and going through tutorials on programming sites. It seems that i’ve been learning C++. Don’t tell me i’ve been learning the wrong thing all this time :frowning: If so, then i guess i should also be using the VC 2005 as opposed to VC++ 2005? Does this matter or have i been wasting time :?:

The reason i ask is because i don’t quite get whats going on with the Read bit. What i have learnt is something like this:

int SF_ReadFavorites () {
  string line;
  ifstream favorites ("favorites.txt");
  if (favorites.is_open())
  {
    while (! favorites.eof() )
    {
      getline (favorites,line);
      cout << line << endl;
    }
    favorites.close();
  }

  else cout << "Unable to open file"; 

  return 0;
}

I guess this is all wrong, and i should start reading C stuff?

EDIT: Forget this comment “VC 2005 as opposed to VC++ 2005”. There isn’t one. It’s C# which is apparently something completely different again.


(Elite) #26

C++ is very close to C (actually it’s derived from C) so it’s fine to learn C++. There probably is a chart somewhere you can find on Google that shows the keyword differences. They are slight, and there are not many. The major differences with C++ is classes. You can still program and compile your mod properly with C++ as well, but you have to be careful when combining it with C code that uses things like malloc, etc. There are some tricks for this type of thing on Google.

==============

Ok, basically that code block does this:

  • open a file
  • double check it is open, only perform actions if so
  • read each line of file until it is at the end.

(SCDS_reyalP) #27

Mixing C++ into the et SDK code is probably not something you want to do, even if you somehow get it to work. ET is written in C, not C++.


(kamikazee) #28

You are right, but it is far easier to learn C++ first, then C. (Mostly they learn you C with streams.)


(mazdaplz) #29

favorites never worked for me either, there’s something else that bugs me:
when you do a refresh of the servers and you finally find one when you want to click it it moves because the servers are still refreshing. i dont see how they overlooked a simple button that bf1942 does have called STOP (refresh)
it’s a minor detail but it would be nice :slight_smile:


(Xterm1n8or) #30

mazdaplz:
I too found this a little annoying, especially when double clicking on a server, a split second before the second click, it moves down and you end up on the wrong server :x This was one of the first things that i tried and actually got working :smiley:
I can’t take the credit for this as it has already been posted on this very forum, but i did change it so that the Stop Refresh is next to the Refresh. Made more sense to me this way.
It would seem that the coding for this has already been done, for some reason they just left it out of the game. So all you need to do, is change a few lines in the ‘playonline.menu’. You can edit it with notepad. This is what i put at the end of mine:


// Misc Buttons

    BUTTON( 12, 102+316-20, .25*((SUBWINDOW_WIDTH)-30), 14, "REFRESH LIST", .24, 11, uiScript RefreshServers )

    BUTTON( 6+6+(.25*((SUBWINDOW_WIDTH)-30))+6, 102+316-20, .25*((SUBWINDOW_WIDTH)-30), 14, "STOP REFRESH", .24, 11, clearFocus ; uiScript StopRefresh )

    BUTTON( 6+6+(.25*((SUBWINDOW_WIDTH)-30))+6+(.25*((SUBWINDOW_WIDTH)-30))+6, 102+316-20, .25*((SUBWINDOW_WIDTH)-30), 14, "SERVER INFO", .24, 11, clearFocus ; open playonline_serverinfo )
	
    NAMEDBUTTON( "bttn_pbenable", 6+6+(.25*((SUBWINDOW_WIDTH)-30))+6+(.25*((SUBWINDOW_WIDTH)-30))+6+(.25*((SUBWINDOW_WIDTH)-30))+6, 102+316-20, .25*((SUBWINDOW_WIDTH)-30), 14, "ENABLE PUNKBUSTER", .24, 11, clearFocus ; open playonline_enablepb )

    NAMEDBUTTON( "bttn_pbdisable", 6+6+(.25*((SUBWINDOW_WIDTH)-30))+6+(.25*((SUBWINDOW_WIDTH)-30))+6+(.25*((SUBWINDOW_WIDTH)-30))+6, 102+316-20, .25*((SUBWINDOW_WIDTH)-30), 14, "DISABLE PUNKBUSTER", .24, 11, clearFocus ; open playonline_disablepb )

// Buttons 

    BUTTON( 6, WINDOW_HEIGHT-24, .33*(WINDOW_WIDTH-24), 18, "BACK", .3, 14, close playonline; open main )
 
    BUTTON( 6+.33*(WINDOW_WIDTH-24)+6, WINDOW_HEIGHT-24, .34*(WINDOW_WIDTH-24), 18, "CONNECT TO IP", .3, 14, clear Focus ; open playonline_connecttoip )
 
    BUTTON( 6+.33*(WINDOW_WIDTH-24)+6+.34*(WINDOW_WIDTH-24)+6, WINDOW_HEIGHT-24, .33*(WINDOW_WIDTH-24), 18, "JOIN SERVER", .3, 14, uiScript JoinServer ) 

}

If you compare it to the original, you can see whats been added and changed.

Finally i’ve been able to help someone as opposed to asking for it :smiley:


(Xterm1n8or) #31

Ok, back to the asking again :smiley:

I have a quick question regarding the .h file first.


#define SF_MAX_IP            15       //maximum length of ip 
#define SF_MAX_PORT          4        //maximum length of port 

I’m assuming that MAX_IP is 15 not 12 because of the 3 full stops in the address?
The default port for ET is 27960. Shouldn’t the MAX_PORT then be 5? And, if the above is true, should it not be 6 to include the colon, as this is only used when entering the port number?

Secondly, i been trying to think about how and when these functions are to be called. I’m guessing that i need to open, read and display the file contents when the Connect to IP menu is opened. In the Connect to IP menu is this:


	onOpen {
		setitemcolor background backcolor 0 0 0 0 ;
		fadein background ;
		setEditFocus "efleftServer IP:"
	}

Is this where i should call SF_LoadFavorites () ?

Thirdly, i have way too many questions about the .c file. I’ve been going through it line by line and translating it into english in an attempt to understand what is going on. Still got a way to go :roll:

Well, i’m reading tutorials on the right language now so at least it looks more similar. Not any easier mind you, just more similar :frowning:

Got to go, almost 0100 up at 0600. Many thanks :smiley:


(Elite) #32

even if you somehow get it to work. ET is written in C, not C++.

The C++ language provides mechanisms for mixing code that is compiled by compatible C and C++ compilers in the same program. Just use linkage and it will work fine. It does for me. I try to use C as much as possible (just because I like it better) but where situations are better handled, I do use C++.

Access C code in C++ code:

extern "C" {
    void f();             // C linkage
    extern "C++" {
        void g();         // C++ linkage
        extern "C" void h(); // C linkage
        void g2();        // C++ linkage
    }
    extern "C++" void k();// C++ linkage
    void m();             // C linkage
}

Including C headers in C++ code:

extern "C" {
    #include "header.h"
}

Mixed language headers:

#ifdef __cplusplus
extern "C" {
#endif
... /* body of header */
#ifdef __cplusplus
} /* closing brace for extern "C" */
#endif

I think enough said…

=========================

  1. MAX_IP is 15 because of the stops , yes
  2. PORT, yes you are right, common port length is 4 it slipped my mind :slight_smile:
  3. The colon, no, should nto be counted in during length. You can add that in IF the user entered a port, otherwise use the server default (27960). It’s not data that will change, might as well save the space and cpu (poor example for saving, not much to save, but you know what I mean)

I would load the file into memory when cgame is loaded (like cg_init or something) and then just display it when the menu is to be drawn. Then when cgame is being shutdown, save it back to the file.


(SCDS_reyalP) #33

I’m well aware that you can do this. That doesn’t make it a good idea.

In any case my comment was aimed at Xterm1n8or, who seemed to be trying to just use C++ straight in the gamecode.


(Xterm1n8or) #34

I think i’ll stick with C. The game is in C and i’m still very much a novice. One programming language is more than enough for me just now :slight_smile:

I would load the file into memory when cgame is loaded (like cg_init or something) and then just display it when the menu is to be drawn. Then when cgame is being shutdown, save it back to the file.

Like i said i’m just a novice, but doesn’t the cgame get loaded pretty much at the start of playing ET. If so, would this not mean that the info is being held in memory even though it may not get used? This is why i thought of calling the function when the Connect to IP menu is opened as this is the only place and time that this info is required. What made me think about this also is malloc. I had to google this as i couldn’t find an example in the SDK. It says (somewhere) that; it allows you to allocate the correct amount of memory, and when used with free(), only for the time it is required.

Regarding malloc. I found 1 entry in the SDK that refers to this:


#undef ERR_FATAL             // this is defined in malloc.h

Only, i don’t have a malloc.h. A web search tells me that malloc.h is not required as the functions are included in the stdlib.h, only i don’t have this file either. Am i missing some files?

Cheers :smiley:


(SCDS_reyalP) #35

cgame is reloaded every time you connect. This includes map changes etc.


(Elite) #36

You do have stdlib, it will be on your host system, not in the game files. These files are standardized to ensure consistency between programs.

===============

You can load the data into memory only whne it is used, which in ways seems logical, but it is in fact the reverse of what you want, which is efficiency and the least amount of disk operations. If you load the favorites while cgame is just been loaded, you will be at a loading screen allready, so not really any game performance is lost here. If you wait to load it until it is being used, you will have to load it repeatedly over and over, and reading from the disk is much slower than from memory. This will result in many open/close, read/write operations to disk each time the favorites are viewed, which may be quite often, and all of which will be at times where performance is noticeable, in game.

Load it when cgame is loaded for performance, or, if you still want to you can load it only as needed, but there will be performance penalties doing it that way.

That being said, don’t do this for everything. You want, as much as possible, to have free ram all the time (1, so you don’t get wierd run-time errors, 2, so you don’t have to use swap in some cases). Only use this technique when it outwieghs the drawbacks. The memory used here is not much, and it is far more beneficial to ensure the least amount of disk operations are performed in this case.