• Please make sure you are familiar with the forum rules. You can find them here: https://forums.tripwireinteractive.com/index.php?threads/forum-rules.2334636/

Code [Question] Using FileWriter

Youg

Grizzled Veteran
Jan 19, 2013
60
0
Hi,

I'm looking at using the FileWriter class which is in the Engine area of the devevlopment to write to a txt file a simple string whenever the player sprints. Now I've been looking at putting the logf("") line in the Dev>SRC>ROGame>Classes>ROPawn.uc with the if sprint statement so to write when called, but I can't seem to get it working when I test it.

Has anyone really used this? I can see the commented out log() statements using the old FileLog, though it doesn't give a very clear indication on how they were used. If you could help me with this would be awesome, really is a big part of what I need to do.

Thanks,
Youg.
 
It works and is for instance used in the WebAdmin\Classes\ChatLog.uc file.

Basicly it comes to:
Code:
var FileWriter  writer;

writer = spawn(class'FileWriter');
writer.OpenFile("Example.txt", FWFT_Log,, true, true);
writer.Logf("This will be written to the 'Example.txt' file");
writer.CloseFile();

Please note that a second call to OpenFile will re-create the file and by that deletes the old file. If you want to write more than one line of text, then best approach is to keep the file handle open and to only close it in the Destroyed event handler of the class that uses the FileWriter class.
 
Upvote 0
thanks Ducky for all the help, I've implemented the FileWriter function and other code of which I'm pretty sure is in the right place though it still doesn't want to create a file and log into it.

Am I adding it to the right place in the SteamApps\common\RO2...

And the code I'm using is basically following what you've put and whats being used in the ChatLog.uc:

Code:
// make the file unique
var config bool bUnique;
// timestamp var
var config bool bIncludeTimeStamp;
// declare the filewriter var
var FileWriter writer;
function CreateFileWriter()
{
 writer = spawn(class'FileWriter');
 writer.OpenFile("Sprinting.txt", FWFT_Log,,bUnique, bIncludeTimeStamp);
 writer.Logf("Data Gathering Started"$TimeStamp());
}

Then I just repeat writer.Logf("text"$TimeStamp()); whenever I need to
 
Upvote 0
Last edited:
Upvote 0
You only put your sources in the <Documents>\My Games\ROGame\Src folder. Do not put any of TWIs sources there, or else you will break a few things.
Suppose you want to write a mutator with the name MyMutator, then you create the following folder structure:
<Documents>\My Games\ROGame\Src\MyMutator\Classes
and create a file MyMutator.uc in the above Classes folder.

The content of that file should at least contain:
Code:
class MyMutator Extends ROMutator;

defaultproperties
{
}
That is the starting point from where you build up your mutator. With FileWriter it should look something like:
Code:
class MyMutator Extends ROMutator;

var FileWriter writer;

function PostBeginPlay()
{
    super.PostBeginPlay(); // Do never forget this line

    self.writer = self.Spawn(class'FileWriter');
    self.writer.OpenFile("MyLogFile", FWFT_Log, ".txt", true, true);
}

event Destroyed()
{
    super.Destroyed(); // Do never forget this line

    self.writer.CloseFile();
}


defaultproperties
{
    writer=None
}
 
Upvote 0
thanks again ducky, getting my head slowly round this now. Thought I'd briefly explain what I'm trying to do just so that its a bit more clear on whether I can do this or not.

I need to print to a log file over the course of 5rounds whenever the player sprints (crouched or normally). What I need to have logging is when they start sprinting with a timestamp, so writer.logf("sprinting"$TimeStamp()), when the heavy breathing sound cue is activated with timestamp, when the player lets go of sprint with timestamp and how much stamina they have remaining.

Would I do all this inside a tick function with ModifyPlayer(Pawn Other) called inside that so that I can access the if(Sprinting) code for to log sprinting activate?

Thanks,
Youg.
 
Last edited:
Upvote 0
You should try to keep the Logf out of the Tick event. Logf isn't a fast executing function and can cause cause lag and stuttering.
My guess would be to sub-class ROPlayerController or ROPawn and look for a suitable event there. That also gives you easy client side access. But be aware that sub-classing those classes will mean that your mutator won't work with any mods like CTF.
 
Upvote 0
Right, so inside the ROPawn.uc there is a simulated event PlayFootStepSound(int FootDown) which checks to see if the player is sprinting in order to play the particular sound with the part:

Code:
else if ( bIsSprinting )
 {
     FootStepVolume = 0.35;
 }

or there's the function SetSprinting(bool bNewIsSprinting) which isn't very clear but I believe it just sets whether the player is sprinting or not.

I took a closer look at ROPlayerController and found this inside an event called PlayerTick(float Delta Time):

Code:
 if( bSprint == 1 || bExclusiveFocus == 1 )
                {
                    Pawn.ExclusiveFocus(true,(bSprint == 1));
                }
                else
                {
                    Pawn.ExclusiveFocus(false);
                }

So which would be advised to use? Bare in mind, for the ROPlayerController there is no stamina so would have to call ROPawn.uc to get that value anyway. And to call this into my mutator its just

Code:
function ModifyPlayer(Pawn Other)
                {
                       Other.PlayerIsSprinting.logf("Sprinting");
                       Other.whateverelse...
                       Super.ModifyPlayer(Other);
                }
Right?

Thanks
 
Last edited:
Upvote 0
I've been trying to follow all the links Ducky has sent me and searching here, BeyondUnreal and Epic forums to get more of an understanding of what I need to do and have came up with this:

Code:
class sprintMutator Extends ROMutator;
var FileWriter writer;
function PostBeginPlay()
{
    super.PostBeginPlay(); // Do never forget this line
    self.writer = self.Spawn(class'FileWriter');
    self.writer.OpenFile("SprintLogFile", FWFT_Log, ".txt", true, true);
}
function ModifyPlayer(Pawn Other)
{
    local ROPawn MyPawn;
    MyPawn = ROPawn(Other);
    StaminaAmountLeft = Stamina;
    PlayerSprinting = bIsSprinting;
    PlayerStopped = !bIsSprinting;
    //SoundCueActive = ;
    Other.PlayerSprinting.writer.Logf("Player Started Sprinting"$TimeStamp());
    Other.PlayerStopped.writer.Logf("Player Stopped"$TimeStamp());
    Other.StaminaAmountLeft.writer.Logf("Stamina Remaining"$TimeStamp());
    super.ModifyPlayer(Other);
}
event Destroyed()
{
    super.Destroyed(); // Do never forget this line
    self.writer.CloseFile();
}
defaultproperties
{
    writer=None
    GroupName="SprintMutator"
    FriendlyName="Sprint Montitor Mutator"
    Description="The Mutator that assigns itself to the player"
}

I've then tried to compile it by doing the whole, create a shortcut add make at the end, create a bat file which doesn't throw up any errors until I get a very irritating message saying that steam is required to play the game.

Could I get some help as to where I'm going wrong? I've looked at the links and searched myself and can't find any answer that really makes sense to me.

Greatly Appreciated,
Youg.
 
Last edited:
Upvote 0
You must have steam active to be able to compile a mutator. Also does your code contain some lines that will give errors for sure.
Code:
 1: function ModifyPlayer(Pawn Other)
 2: {
 3:     local ROPawn MyPawn;
 4:     MyPawn = ROPawn(Other);
 5:     StaminaAmountLeft = Stamina;
 6:     PlayerSprinting = bIsSprinting;
 7:     PlayerStopped = !bIsSprinting;
 8:     //SoundCueActive = ;
 9:     Other.PlayerSprinting.writer.Logf("Player Started Sprinting"$TimeStamp());
10:     Other.PlayerStopped.writer.Logf("Player Stopped"$TimeStamp());
11:     Other.StaminaAmountLeft.writer.Logf("Stamina Remaining"$TimeStamp());
12:     super.ModifyPlayer(Other);
13: }

UE3 scripting is an object oriented language. You can only access a class member by referencing it over an instance of that class. bIsSprinting is member of ROPawn. For this reason you must access it over a ROPawn instance. In your case your ROPawn instance is the MyPawn that you initialized at line 4. This the code at line 6 should be PlayerSprinting = MyPawn.bIsSprinting; and at line 7 PlayerStopped = !MyPawn.bIsSprinting;, but both those lines doesn't make sense at all. PlayerSprinting will always be equal to !PlayerStopped. Also do you have to declare PlayerSprinting and PlayerStopped in the head of your class.
The code at lines 9, 10 and 11 won't compile too. Other is an instance of the Pawn class. That class does not have a PlayerSprinting nor PlayerStopped member. Nor do any of them have a writer member. You need tho access the writer instance like this self.writer.Logf since it is a member of your mutator class.
 
Upvote 0
So i've implemented these changes, and thought that maybe an if statement would catch and log all the information in one?

Code:
if(PlayerSprinting)
{
    self.writer...
    if(PlayerStopped)
    {
         self.writer...
    }
}

I thought !bIsSprinting meant the bool was false with bSprinting being the true and meaning the player was sprinting. If doing PlayerStopped = !MyPawn.bIsSprinting won't log when the player stops, what would be the suggestion to look at?

Finally, would I declare my PlayerSprinting and PlayerStopped as a bool each? As both are referencing a bool? In this case I could just do !PlayerSprinting instead of PlayerStopped right?
 
Upvote 0
What you want to detect is the transition from not sprinting to sprinting and visa versa. What you need to do is remember the previous value of MyPawn.bIsSprinting and i that one differs from the previous value, then you have a transition. Something like:

Code:
var bool  bPreviousIsPrinting;

function LogSprintTransition(Pawn P)
{
    local ROPawn ROP;

    ROP = ROPawn(P);

    if (self.bPreviousIsPrinting != ROP.bIsSprinting)
    {
        // If we get here, then we have caught a transition
        if (ROP.bIsSprinting)
        {
            // Player started to sprint
        }
        else
        {
            // Player stopped sprinting
        }

        self.bPreviousIsPrinting = ROP.bIsSprinting;
    }
}

defaultproperties
{
    bPreviousIsPrinting=false
}
The comments indicate which path will be taken depending on what the player does. What is left is to find the right place to call this function. In your example you use ModifyPlayer, but that one is guaranteed not the right spot, because it's only fired when a player spawns. 100% sure he ain't sprinting at that event.
 
Upvote 0
Ah, so modify player would be the wrong one then... Well I've made the changes so the mutator looks like this now:

Code:
class sprintMutator Extends ROMutator;
var FileWriter writer;
var bool bPreviousIsSprinting;
//var int StaminaAmountLeft;
function PostBeginPlay()
{
    super.PostBeginPlay();
    self.writer = self.Spawn(class'FileWriter');
    self.writer.OpenFile("SprintLogFile", FWFT_Log, ".txt", true, true);
}
function LogSprintTransition(Pawn Other)
{
    local ROPawn MyPawn;
    MyPawn = ROPawn(Other);
 
 //StaminaAmountLeft = MyPawn.Stamina;
 if(self.bPreviousIsSprinting != MyPawn.bIsSprinting)
 {
  if(MyPawn.bIsSprinting)
  {
   self.writer.Logf("Player Started Sprinting"$TimeStamp());
  }
  else
  {
   self.writer.Logf("Player Stopped"$TimeStamp());
   //self.writer.Logf("Stamina Remaining"StaminaAmountLeft());
  }
 
  self.bPreviousIsSprinting = MyPawn.bIsSprinting;
 }
}
event Destroyed()
{
    super.Destroyed();
    self.writer.CloseFile();
}
defaultproperties
{
 GroupName="SprintMutator"
 FriendlyName="Sprint Montitor Mutator"
 Description="The Mutator that assigns itself to the player"
 
    writer=None
 
 bPreviousIsSprinting = false
}

I've tried to test it by compiling it but I can't seem to work out how to compile. I've followed the instructions posted earlier a few times over now to see if I was just missing something but it really refuses to run even with steam clearly open.

So I'm just going to run through the process I'm doing and if I can get a yes thats right or wrong missing this step would be awesome. I'm saving my mutator file (.u) in

"C:\UDK\UDK-2010-09\Development\Src\SprintMutator\Classes"

The SprintMutator File is symbolically linked to
"C:\USERDIR\Documents\My Games\RedOrchestra2\Src\SprintMutator"

I then edit the ROEditer.ini file to include the line ModPackage=SprintMutator, found in:
"C:\USERDIR\Documents\My Games\RedOrchestra2\ROGame\Config"

Then I make a shortcut of the game.exe file found in:
"\SteamApps\common\RedOrchestra2\Binaries\Win64"
(this is created on the windows 7 desktop)

I edit the Target file name by adding the word "make" to the end:
"C:\Program Files (x86)\Steam\SteamApps\common\Red Orchestra 2\Binaries\Win64\ROGame.exe" make

I then run steam and click on the newly created shortcut to get the message that steam is required to run the game. I've tried all this and added a Make.bat file to run the "C:\Program Files (x86)\Steam\SteamApps\common\Red Orchestra 2\Binaries\Win64\ROGame.exe" make line but same error.

Have I managed to screw this up?
 
Upvote 0
--SNIP--
Have I managed to screw this up?

Nope. You did it all correct. That annoying steam error sometimes occurs. In most cases it's related to a missing or incorrect steam_appid.txt file. You can find that file in C:\Program Files (x86)\Steam\SteamApps\common\Red Orchestra 2\Binaries\Win64\. The content of that file should be:
Code:
35450

It must be on the first line of that file and no other lines should be present (not even an empty line). Please check that file. If it's missing or incorrect, then repair it.
 
Upvote 0
ah good to know I'm getting the setup right, though annoying about the steam error. I've got the steam_appid.txt file with 35450 on the first and only line.

Update:

I've managed to get it to run, turns out I had let steam run as admin and wasn't running the shortcut as such, this meant the game didn't see that steam was active, so running the compile now.... Fingers crossed
 
Last edited:
Upvote 0
Good news! It sort of compiled... So, nothing completely good, but I got a few warnings.

The warnings I got were all from the default properties to do with Group Name, Friendly Name and Description. Ignore the quotes, I've put them in to show all 3 warnings in one.:

C:\Program Files(x86)\Steam\SteamApps\common\Red Orchestra 2\Development\Src\sprintMutator\Classes\sprintMutator.uc("49","50","51") : Warning, Unknown property in defaults:

GroupName = "SprintMutator" (looked in sprintMutator)
FriendlyName="Sprint Monitor Mutator" (looked in sprintMutator)
Description="sprint mutator" (looked in sprintMutator)

Ah, just seen that Ducky had this problem as well, though no indication that it was resolved...

Although a .u file has been created in the Unpublished\CookedPC\Script section of ROGame but doesn't it need to be in the Published area for me to use it on the client machine? If so, how do I get it in the published area? I know its something to do with cooking the .u file, is that a process of just use the UnrealFrontend to do or do I have to cook it with a map?
 
Last edited:
Upvote 0
Good news! It sort of compiled... So, nothing completely good, but I got a few warnings.

The warnings I got were all from the default properties to do with Group Name, Friendly Name and Description. Ignore the quotes, I've put them in to show all 3 warnings in one.:

C:\Program Files(x86)\Steam\SteamApps\common\Red Orchestra 2\Development\Src\sprintMutator\Classes\sprintMutator.uc("49","50","51") : Warning, Unknown property in defaults:

GroupName = "SprintMutator" (looked in sprintMutator)
FriendlyName="Sprint Monitor Mutator" (looked in sprintMutator)
Description="sprint mutator" (looked in sprintMutator)

Ah, just seen that Ducky had this problem as well, though no indication that it was resolved...

Those properties do all not exist in ROMutator or any of it's base classes. You will need to set them in the mutator's ini file, but it's not mandatory. For now the only thing to set in the defaultpropeties sections is:
Code:
  GroupNames=("SprintMutator")

Although a .u file has been created in the Unpublished\CookedPC\Script section of ROGame but doesn't it need to be in the Published area for me to use it on the client machine? If so, how do I get it in the published area? I know its something to do with cooking the .u file, is that a process of just use the UnrealFrontend to do or do I have to cook it with a map?

There is no need to cook a mutator's .u file. Simply copy-paste it to the Published/CookedPC folder and you are all good to go.
 
Upvote 0
Great! Recompiled it and went straight to sucess, so now to testing it...

I moved it into the My Games\..\Published file structure, not the steamapps and thought I could test it through the practice maps and workshop though its not showing up under the available mutators to add. Is it just running automatically as it informed me of an:

"Ambigious package name: Using"C:\Program Files\(x86)\Steam\steamapps\common\Red Orchestra 2\ROGame\CookedPC\Scripts\sprintMutator.u", not ""C:\Program Files\(x86)\Steam\steamapps\common\Red Orchestra 2\ROGame\CookedPC\sprintMutator.u",

Also, I'll be putting this on a number of client consoles to create a file on each console while playing on a LAN server. So I guess its just put this mutator on each PC in the same folder and log into the server?
 
Upvote 0
Great! Recompiled it and went straight to sucess, so now to testing it...

I moved it into the My Games\..\Published file structure, not the steamapps and thought I could test it through the practice maps and workshop though its not showing up under the available mutators to add. Is it just running automatically as it informed me of an:

"Ambigious package name: Using"C:\Program Files\(x86)\Steam\steamapps\common\Red Orchestra 2\ROGame\CookedPC\Scripts\sprintMutator.u", not ""C:\Program Files\(x86)\Steam\steamapps\common\Red Orchestra 2\ROGame\CookedPC\sprintMutator.u",



You should not get this error. It indicates that more than one copy of the mutator does exist in one of the RO2 search paths. The search paths for the client side game are:
  • <SteamApps>\Common\Red Orchestra 2\ROGame\CookedPC
  • <Documents>\My Games\RedOrchestra2\ROGame\Published
Make sure that only one copy of your .u file exists the the folder structure below those paths. best practice too keep your mutator in the <Documents>\My Games\...\Published path.

Workshop:
You do not see the mutator at any location because your mutator has no UI hooks. Take a look at ROMutator.uc to look for examples of mutator hooks.

Web-admin UI:
You don't see the mutator there, because your mutator has no settings class. You also can not select it in the Change Map screen, because you did not define the required entries in the mutators ini file.

Also, I'll be putting this on a number of client consoles to create a file on each console while playing on a LAN server. So I guess its just put this mutator on each PC in the same folder and log into the server?

The mutator should only be placed on the LAN server. It will upload to the clients as soon as they connect. I can ensure you that your mutator will not log anything on the clients, because it does not contain any client side code. Sub-classes of ROMutator.uc do only run on the server.

My advice is to take a toor on the UE3 sides and read the documentation about replication and the network model. The site can be foudn at: http://udn.epicgames.com/Three/UnrealScriptHome.html
The replication section at: http://udn.epicgames.com/Three/ReplicationHome.html
It will give you a better understanding on how things work.
 
Last edited:
Upvote 0