PDA

View Full Version : File handling



EvoloZz
9th February 2013, 15:38
I have a problem with a script for writing something on a file
I gotta ask, how do i tell the game that on the 1. line of that file is something and on the 2. something else, so i could save 'em into variables
If i have few things that needs to be wrote on that file, and on some event i need the game to read data from the file, how do i define that?


file = openFile(filename + ".txt", "read")
if(file != -1)
here i dont know what do to...
closeFile(file);

Hopefully someone could explain me...

Jeplaa
9th February 2013, 16:13
I'm not sure whether I understand, but that way writing events


f = openFile(filename + ".txt", "write");
fprintln(f,self.name);
fprintln(f,self.score);
fprintln(f,self.etc);
closefile(f);

and that reading them


f = openFile(filename + ".txt", "read");
freadln(f);
self.name = int(fgetarg(f,0));
self.score = int(fgetarg(f,1));
self.etc = int(fgetarg(f,2));
closefile(f);

EvoloZz
9th February 2013, 16:16
if you could explain little bit better what "fgetarg" means it would be awesome, maybe getArgument or something? :P

Jeplaa
9th February 2013, 16:36
FGetArg( <filenum>, <arg> )
Module: File

Summary:
Get a specific argument number from the current line


Required Args:
1: <filenum> The file number returned by openfile
2: <arg> the argument number

kung foo man
9th February 2013, 16:47
To get rid of the file-functions, i would suggest to use the mysql-functions: http://killtube.org/downloads/cod2/exampleserver/

1) its faster
2) its more powerful
3) you can make a website about your cod2-server-stats

At start you need to get used to it, because the mysql-api is not that self-explaining and also more complex then the file-functions, but you have fully working examples (mostly copy-paste). I can also assign you a cod2-server on the debug-server.

EvoloZz
9th February 2013, 17:10
I should try that but it looks so hard :/
But now i got weird error in a hud, saying that "type undefined is not a float", i called that hud right after loading the file so everything should work, but doesn't.

Tally
9th February 2013, 17:21
I should try that but it looks so hard :/
But now i got weird error in a hud, saying that "type undefined is not a float", i called that hud right after loading the file so everything should work, but doesn't.

The golden rule of posting error messages on a modding forum: always post the full script and the exact error message you get. Anything else is too cryptic to even begin to know what is going on. We would just be guessing what it is and what is causing it.

The file functions have nothing whatsoever to do with hud elements. So, if you are getting errors relating to hud elements, it suggests that a failed script is causing a hitch in the server, and things are being returned as undefined. Knowing where you are using the file functions, and what hud element script is being affected will help us to diagnose the problem. As such, please post both in full; show where they are being called from; what map and gametype are being run; and last but not least, post the full error message you are getting.

EvoloZz
9th February 2013, 17:24
type undefined is not a float: (file 'maps/mp/gametypes/_tdm.gsc', line 585)
self.ratiohud setValue(self.ratio);
And which script do you want to see? hud or file-handle?

Tally
9th February 2013, 17:41
type undefined is not a float: (file 'maps/mp/gametypes/_tdm.gsc', line 585)
self.ratiohud setValue(self.ratio);
And which script do you want to see? hud or file-handle?

Both. Especially include where self.ratio is first initially defined.

EvoloZz
9th February 2013, 18:32
Well here's the file thing

load()
{
guid = self getGuid();
if(guid == 0)
return;

file = openFile(guid + ".txt", "read");
if(file != -1)
{
freadln(file);
self.ratio=int(fgetarg(file,0));
closeFile(file);
}

else
{
self.ratio = 0;
self write();
}
}

write()
{

guid = self getGuid();
if(guid == 0)
return;

file = openFile(guid + ".txt", "write");
closeFile(file);

file = openFile(guid + ".txt", "append");
fPrintln(file,self.ratio);
closeFile(file);
}

And the hud's like this

ratio()
{
self endon("joined_spectators");
self endon("disconnect");
self.ratio = self.score / self.deaths;

if(!isDefined(self.ratiohud))
self.ratiohud = newClientHudElem(self);
self.ratiohud.vertAlign = "fullscreen";
self.ratiohud.horzAlign = "fullscreen";
self.ratiohud.alignX = "left";
self.ratiohud.alignY = "middle";
self.ratiohud.x = 25;
self.ratiohud.y = 474;
self.ratiohud.sort = 1;
self.ratiohud.alpha = 1;
self.ratiohud.fontScale = 0.8;
self.ratiohud.archived = true;
self.ratiohud.label = (game["ratio"]);
self.ratiohud setValue(self.ratio);
}

Hud is called at playerspawn, and load file is called at playerconnect

Tally
9th February 2013, 18:46
How are you testing this? On a dedicated server? Or a local/listen server?

kung foo man
9th February 2013, 18:50
self.ratio = self.score / self.deaths;


Thats causing divide-by-zero.


Try something like:




if (self.deaths)
self.ratio = self.score / self.deaths;
else
self.ratio = 0;

EvoloZz
9th February 2013, 18:54
Ofc on dedicated, and kung i dont think that is the problem because i have tried tracking other stuff, but same happened

kung foo man
9th February 2013, 19:08
Could you try to iprintln the player.ratio after this line?



self.ratio=int(fgetarg(file,0));

Thanks to Infinity Ward, we dont have any Builtin-Debugger. :rolleyes:

EvoloZz
9th February 2013, 19:16
the server crashes right after choosing weapon, so i couldn't see it

Tally
9th February 2013, 21:48
If I were doing this, I would first get the hud working independently of the file function method. Then, once it's working correctly without any errors, I would introduce the file function method.

Doing it that way will narrow the problems down. You will then be able to isolate any errors without having to determine what effect the other method is having on things.

I'm saying this because I am pretty sure the hud code is not correct and is causing runtime errors. Sort that out first. Then move on to the file function method.

Tally
9th February 2013, 22:36
Ok, as I suspected, the ratio hud was a mess, so I wrote a test script in order to see what was crashing the server. And it was that you can't divide 0 by a 0. Below is the test script I wrote. I have no idea exactly what you are trying to do, so I just updated the value every frame. Obviously, you change that to suit your purposes. But the method is sound - don't update the value while self.ratio is zero (otherwise it throws an error) and only apply a division once both self.score and self.deaths are defined.

This is what I ended up with:


init()
{
level thread onPlayerConnect();
}

onPlayerConnect()
{
for( ;; )
{
level waittill( "connected", player );

player thread onPlayerSpawned();
player thread onPlayerKilled();
}
}

onPlayerSpawned()
{
self endon( "disconnect" );

for( ;; )
{
self waittill( "spawned_player" );

self thread ratio();
}
}

onPlayerKilled()
{
self endon( "disconnect" );

for( ;; )
{
self waittill( "killed_player" );

if( isDefined( self.ratiohud ) )
self.ratiohud destroy();
}
}

ratio()
{
self endon( "killed_player" );
self endon( "disconnect" );

if( isDefined( self.ratiohud ) ) self.ratiohud destroy();

if( !isDefined( self.ratiohud ) )
{
self.ratiohud = newClientHudElem( self );
self.ratiohud.vertAlign = "fullscreen";
self.ratiohud.horzAlign = "fullscreen";
self.ratiohud.alignX = "left";
self.ratiohud.alignY = "middle";
self.ratiohud.x = 25;
self.ratiohud.y = 474;
self.ratiohud.sort = 1;
self.ratiohud.alpha = 1;
self.ratiohud.fontScale = 0.8;
self.ratiohud.archived = true;
self.ratiohud.label = (game["ratio"]);
self.ratiohud setValue( 0 );
}

for( ;; )
{
wait( 0.05 );

self.ratio = self getRatio();

if( !isDefined( self.ratio ) )
continue;
else
{
if( isDefined( self.ratiohud ) )
self.ratiohud setValue( self.ratio );
}
}
}

getRatio()
{
value = undefined;

if( !self.score && !self.deaths )
value = 0;
else if( self.score && !self.deaths )
value = self.score;
else if( self.score && self.deaths )
value = ( self.score / self.deaths );

return( value );
}


Once you've got your hud working, you can then move on to using the file functions.

EvoloZz
10th February 2013, 07:41
That would work for the ratio, but as i already said, i also tried huds for other things and got the same error

kung foo man
10th February 2013, 07:46
type undefined is not a float: (file 'maps/mp/gametypes/_tdm.gsc', line 585)
self.ratiohud setValue(self.ratio);


You got THIS error for other huds? :D

EvoloZz
10th February 2013, 07:53
Same expect the value was different of course

kung foo man
10th February 2013, 08:06
I have still no clue whats wrong now. Please just update the error message instead of referencing to older ones (with the correspondending source code).

Tally
10th February 2013, 08:33
That would work for the ratio, but as i already said, i also tried huds for other things and got the same error

Then you need to post all your other huds as well so we can see where you've gone wrong. If the other ones are like the one I just worked on, then it is because you aren't doing any checks on the values, and the game simply wont stand for it.

The important thing is for you to post your code. If you don't post it, how are we supposed to help you? We are modders. Not mind readers. If you don't want to post your code, then don't expect any help. It really is as simple as that.

EvoloZz
10th February 2013, 10:18
Okay here's just everything, all scripts, all errors, whatever you need

_file.gsc:

init()
{
thread onPlayerConnect();
}

onPlayerConnect()
{
level waittill("connecting",player);
player thread load();
}

load()
{
guid = self getGuid();

if(guid != 0)
{
file = openFile(guid + ".txt", "read");
if(file != -1)
{
freadln(file);
self.kills = int(fgetarg(file,0));
closeFile(file);
}

else
{
self.kills = 0;
self write();
}
}

else if(guid == 0)
return;
}

write()
{

guid = self getGuid();

if(guid != 0)
{
file = openFile(guid + ".txt", "write");
closeFile(file);

file = openFile(guid + ".txt", "append");
fPrintln(file,self.kills);
closeFile(file);
}

else if(guid == 0)
return;
}


tdm.gsc:
callback_startgametype

thread maps\mp\gametypes\_file::init();
callback_playerkilled

attacker.kills++;
attacker maps\mp\gametypes\_file::write();
attacker updatehud();
updatehud

updateHud()
{
self.killhud setValue(self.kills);
}

spawnplayer

self thread kills();

hud

kills()
{
self endon("joined_spectators");
self endon("disconnect");

if(!isDefined(self.killhud))
{
self.killhud = newClientHudElem(self);
self.killhud.vertAlign = "fullscreen";
self.killhud.horzAlign = "fullscreen";
self.killhud.alignX = "left";
self.killhud.alignY = "middle";
self.killhud.x = 25;
self.killhud.y = 474;
self.killhud.sort = 1;
self.killhud.alpha = 1;
self.killhud.fontScale = 0.8;
self.killhud.archived = true;
self.killhud.label = (game["ratio"]);
self.killhud setValue(self.kills);
}
}

Error:

type undefined is not a float: (file 'maps/mp/gametypes/_tdm.gsc', line 589)
self.killhud setValue(self.kills);
*

Well i still cant figure out whats the problem, and i have tried this all just without saving into file and it worked, but it doesnt work anymore.
I think that the problem is because the file handle and hud arent connected to each other, so thats why hud doesnt understand what the fuck is self.kills.

IzNoGoD
10th February 2013, 11:29
self.score.

EvoloZz
10th February 2013, 12:09
Why would i use self.score? Because i need to track kills, but if i kill myself score goes -1...

kung foo man
10th February 2013, 13:06
When the huds are already working, i guess its the load-function:



load()
{
guid = self getGuid();

if(guid != 0)
{
file = openFile(guid + ".txt", "read");
if(file != -1)
{
freadln(file);
self.kills = int(fgetarg(file,0));
closeFile(file);
} else {
self.kills = 0;
self write();
}
}


if (!isDefined(self.kills))
std\io::print("[WARNING] self.kills not defined!");
else
std\io::print("[NOTICE] self.kills is " + self.kills);
}




std\io::print() (http://killtube.org/downloads/cod2/exampleserver/main/std/io.gsc) is printing into the console-window, so you have all debug-messages in a nutshell.

Looking forward what it will say.

IzNoGoD
10th February 2013, 13:25
load()
{
guid = self getGuid();

if(guid != 0)
{
file = openFile(guid + ".txt", "read");
if(file != -1)
{
freadln(file);
self.kills = int(fgetarg(file,0));
closeFile(file);
}

else
{
self.kills = 0;
self write();
}
}

else if(guid == 0)
return;


1. dont do an else with the inverse of the if-statement. Its useless and cod hates you for it.
2. you seem to be testing locally, meaning your guid is zero. This means self.kills is NEVER initialized.

EvoloZz
10th February 2013, 13:38
Who told you i am testing locally? do you really think i am that stupid? and i already said in an earlier post that its dedicated, omg

EvoloZz
10th February 2013, 13:51
Ok i tested the print() thing, and it just said self.kills is 0, after /reconnect or after spawning bots, the same error happened

Tally
10th February 2013, 13:55
Ok i tested the print() thing, and it just said self.kills is 0, after /reconnect or after spawning bots, the same error happened

Well, it's not an error, your file method is not working properly. So, self.kills will always be 0 since there is no update to the data since it isn't saving it properly.

BTW - is an actual file being created at all? what is it's name?

EvoloZz
10th February 2013, 13:56
Yeah, the file creates and its name is my guid...

Tally
10th February 2013, 14:15
Yeah, the file creates and its name is my guid...

Working on it. Got the file functions to store my kills. Now, I will work on getting it to read it back to me during a game.

Tally
10th February 2013, 15:46
Ok, I found out what the problem is - once a file has been created using the "write" function, it wont update. So, you are fine entering data into file the very first time. After that, it simply doesn't update any more. So, you have to use the "append" function, and read the data back from that basis.

Here is what I have:


onPlayerConnect()
{
// initialize the player kills flag
if( getPlayerKills() )
self.pers["kills"] = self.kills;
else
self.pers["kills"] = 0;
}

onPlayerKilled()
{
attacker.pers["score"]++;
attacker.score = attacker.pers["score"];
// update the player kills flag along with player score
attacker.pers["kills"]++;
// save the new player kills value
attacker thread saveKills();
}

saveKills()
{
filename = self.name + ".txt";
filehandle = openfile( filename, "append" );

if( filehandle != -1 )
{
value = "";
if( value != "" ) value += " ";
value += self.pers["kills"];

fprintln( filehandle, value );
closefile( filehandle );
}
}

getPlayerKills()
{
filename = self.name + ".txt";
file = OpenFile( filename, "read" );

if( file == -1 )
return( false );

for( ;; )
{
elems = freadln( file );

if( elems == -1 )
break;

if( elems == 0 )
continue;

line = "";
for( pos = 0; pos < elems; pos++ )
{
line = line + fgetarg( file, pos );
if( pos < elems - 1 )
line = line + ",";
}

array = strtok( line, "," );
self.kills = array.size;
}

CloseFile( file );

return( true );
}


I was using a simple player name for the data file name, but obviously you simply replace that with a more robust player GUID name (although I would shorten the file name to the last 8 digits).

I didn't do anything with a HUD element showing these values. I will take it that aspect of the thing can be easily created yourself.

EvoloZz
10th February 2013, 16:22
Thanks alot for helping me and sorry for wasting your time, but that did not fix the problem... the error is still exactly same...

Tally
10th February 2013, 16:32
Thanks alot for helping me and sorry for wasting your time, but that did not fix the problem... the error is still exactly same...

Do the hud element without the file function. Then show me that it works by making an XFire video of it working.

Then, post the code for the hud element - without the file functions.

EvoloZz
10th February 2013, 18:26
I couldn't make a video about it because it would take ages to upload it to xfire, but i hope a screenshot is enough
The hud elem is still exactly the same:

kills()
{
self endon("joined_spectators");
self endon("disconnect");

if(!isDefined(self.killhud))
{
self.killhud = newClientHudElem(self);
self.killhud.vertAlign = "fullscreen";
self.killhud.horzAlign = "fullscreen";
self.killhud.alignX = "left";
self.killhud.alignY = "middle";
self.killhud.x = 25;
self.killhud.y = 474;
self.killhud.sort = 1;
self.killhud.alpha = 1;
self.killhud.fontScale = 0.8;
self.killhud.archived = true;
self.killhud.label = (game["ratio"]);
self.killhud setValue(self.kills);
}
}

Tally
10th February 2013, 18:44
I got it to work with the file function, no problem at all:

Hud:


init()
{
game["ratio"] = &"DEMON_TEST";
precacheString( game["ratio"] );

level thread onPlayerConnect();
}

onPlayerConnect()
{
for( ;; )
{
level waittill( "connected", player );

player thread onPlayerSpawned();
player thread onPlayerKilled();
}
}

onPlayerSpawned()
{
self endon( "disconnect" );

for( ;; )
{
self waittill( "spawned_player" );

self thread ratio();
}
}

onPlayerKilled()
{
self endon( "disconnect" );

for( ;; )
{
self waittill( "killed_player" );

if( isDefined( self.ratiohud ) )
self.ratiohud destroy();
}
}

ratio()
{
self endon( "killed_player" );
self endon( "disconnect" );

if( isDefined( self.ratiohud ) ) self.ratiohud destroy();

if( !isDefined( self.ratiohud ) )
{
self.ratiohud = newClientHudElem( self );
self.ratiohud.vertAlign = "fullscreen";
self.ratiohud.horzAlign = "fullscreen";
self.ratiohud.alignX = "left";
self.ratiohud.alignY = "middle";
self.ratiohud.x = 25;
self.ratiohud.y = 474;
self.ratiohud.sort = 1;
self.ratiohud.alpha = 1;
self.ratiohud.fontScale = 0.8;
self.ratiohud.archived = true;
self.ratiohud.label = (game["ratio"]);
}

self thread UpdateHud();
}

UpdateHud()
{
value = int( self.pers["kills"] );

if( isDefined( self.ratiohud ) )
self.ratiohud setValue( value );
}


File functions:


onPlayerConnect()
{
// initialize the player kills flag
if( getPlayerKills() )
self.pers["kills"] = self.kills;
else
self.pers["kills"] = 0;
}

onPlayerScore()
{
// update the player kills flag along with player score
self.pers["kills"]++;
// save the new player kills value
self thread saveKills();
}

saveKills()
{
filename = self.name + ".txt";
filehandle = openfile( filename, "append" );

if( filehandle != -1 )
{
value = "";
if( value != "" ) value += " ";
value += self.pers["kills"];

fprintln( filehandle, value );
closefile( filehandle );
}

self thread demon\_playerhud::UpdateHud();
}

getPlayerKills()
{
filename = self.name + ".txt";
file = OpenFile( filename, "read" );

if( file == -1 )
return( false );

for( ;; )
{
elems = freadln( file );

if( elems == -1 )
break;

if( elems == 0 )
continue;

line = "";
for( pos = 0; pos < elems; pos++ )
{
line = line + fgetarg( file, pos );
if( pos < elems - 1 )
line = line + ",";
}

array = strtok( line, "," );
self.kills = array.size;
}

CloseFile( file );

return( true );
}


From within the gametype file:


Callback_PlayerConnect()
{
self demon\_playerdata::onPlayerConnect();

}



Callback_PlayerKilled()
{
attacker.pers["score"]++;
attacker.score = attacker.pers["score"];

attacker thread demon\_playerdata::onPlayerScore();
}


No errors at all.

Tally
11th February 2013, 00:25
I wasn't happy with the above attempt because it was clumsy and updated the data file every single kill. As the data file is appended, it would eventually become very large, very quickly. So, I worked on a way to update the data file either when the player disconnects, or at the end of a match/game. That would mean much less appending to the file, and thus much smaller in size. This is what I have now:

File Function:


init()
{
level thread onPlayerConnect();
level thread onGameEnd();
}

onPlayerConnect()
{
for( ;; )
{
level waittill( "connected", player );

// initialize the player kills flag
if( player getPlayerKills() )
player.pers["kills"] = player.kills;
else
player.pers["kills"] = 0;
}
}

onGameEnd()
{
for( ;; )
{
level waittill( "intermission" );

players = getentarray( "player", "classname" );
for(i = 0; i < players.size; i++)
{
player = players[i];
player thread saveKills( player.pers["kills"] );
}
}
}

onPlayerScore()
{
// update the player kills flag
self.pers["kills"]++;
self thread demon\_playerhud::UpdateHud();
}

saveKills( kills )
{
filename = self.name + ".txt";
filehandle = openfile( filename, "append" );

if( filehandle != -1 )
{
fprintln( filehandle, kills );
closefile( filehandle );
}
}

getPlayerKills()
{
filename = self.name + ".txt";
file = OpenFile( filename, "read" );

if( file == -1 )
return( false );

for( ;; )
{
elems = freadln( file );

if( elems == -1 )
break;

if( elems == 0 )
continue;

line = "";
for( pos = 0; pos < elems; pos++ )
{
line = line + fgetarg( file, pos );
if( pos < elems - 1 )
line = line + ",";
}

array = strtok( line, "," );
for( i=0; i < array.size; i++ )
self.kills = int( array[i] );
}

CloseFile( file );

return( true );
}


Player Hud:


init()
{
game["ratio"] = &"DEMON_TEST";
precacheString( game["ratio"] );

level thread onPlayerConnect();
}

onPlayerConnect()
{
for( ;; )
{
level waittill( "connected", player );

player thread onPlayerSpawned();
player thread onPlayerKilled();
}
}

onPlayerSpawned()
{
self endon( "disconnect" );

for( ;; )
{
self waittill( "spawned_player" );

self thread ratio();
}
}

onPlayerKilled()
{
self endon( "disconnect" );

for( ;; )
{
self waittill( "killed_player" );

if( isDefined( self.ratiohud ) )
self.ratiohud destroy();
}
}

ratio()
{
self endon( "killed_player" );
self endon( "disconnect" );

if( isDefined( self.ratiohud ) ) self.ratiohud destroy();

if( !isDefined( self.ratiohud ) )
{
self.ratiohud = newClientHudElem( self );
self.ratiohud.vertAlign = "fullscreen";
self.ratiohud.horzAlign = "fullscreen";
self.ratiohud.alignX = "left";
self.ratiohud.alignY = "middle";
self.ratiohud.x = 25;
self.ratiohud.y = 474;
self.ratiohud.sort = 1;
self.ratiohud.alpha = 1;
self.ratiohud.fontScale = 0.8;
self.ratiohud.archived = true;
self.ratiohud.label = (game["ratio"]);
}

self thread UpdateHud();
}

UpdateHud()
{
if( isDefined( self.ratiohud ) )
self.ratiohud setValue( self.pers["kills"] );
}


There are 2 callbacks in the gametype files:


Callback_PlayerDisconnect()
{
iprintln( &"MP_DISCONNECTED", self );

self thread demon\_playerdata::saveKills( self.pers["kills"] );
}



Callback_PlayerKilled( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration )
{

attacker thread demon\_playerdata::onPlayerScore();
}


I've never worked much with the "write" file function. I've always only really used the "read" function. It was an interesting, if somewhat frustrating, experience. I never knew that a data file will not overwrite itself when the "write" function is used. Now, to work out how to create a new line using "append". If anyone knows how, you can perhaps share and save me some experimentation time.

IzNoGoD
11th February 2013, 01:04
if you just call "write" on a file, then close it again, it will be empty. Comes in handy when trying to prevent files from getting larger.

Also, here is my account system, use it for inspiration:

http://codepad.org/vdQM3aAj

EvoloZz
11th February 2013, 14:16
Finally I got it to work, thanks a lot everyone for helping me out. The main problem (type undefined is not an int) was already fixed by Tally in an earlier post, but the same error came because of the bots (I forgot that their guid is always 0 :D), so i made the script compatible for bots also, and now there ain't any problems no more, big thanks to you guys again! :D