Jump to content
terox

Zeu Debugging Tutorial & Foundation Template

Recommended Posts

It was suggested by Tankbuster that the following may be of use to you.

Zeu Debugging Tutorial & Foundation Template
Last updated 17th September 2014

Introduction

Everyone that writes code suffers from bugs and we all get frustrated trying to trace the cause.
This tutorial is an attempt to walk you through some methods to achieve that.
I have also attached a Foundation template that has a debugging system implemented. More on that later.

You may find the following tools and bookmarks useful, these are all essential to developing missions and am sure most of you have these or similar alternatives

Preparation
How do I know I have an error in the first place?
There are 3 areas where you can capture errors

  • Ingame, where you dont see the effect you are expecting
  • A black dialog pop up box
  • Rpt file

-ShowScriptErrors
You need to add -ShowScriptErrors to your command line startup params. This will enable the popup black dialog box if a scripting error occurs.

RPT File
This is the basic logging file that arma3 dumps too.
It is full of spam from B.I, however the more you read it the more familiar you get with their spam, which makes it easier to filter out the rubbish.
Any popup messages that -showscripterrors displays are always dumped to the rpt file
By default, your RPT file can be found in


  • Windows 7 & 8 Users: c:\Users\Your Username\AppData\Local\Arma 3\

You may need to enable hiddenfiles to see this folder
If a -profile is set, then the RPT will be created there instead


Tools & Reference material
Script editors

PBO Tools


Bookmarks
Add these links to your Bookmarks, you will constantly need to refer to them while coding your ArmA 3 Projects


Zeu Foundation Template






Tutorial
The art of debugging is being able to track down the reason why a piece of code isn't working as intended.
Why do they happen in the first place?


Types Of Error
  • Syntax error: you made a spelling, grammar or typing mistake
  • Logic error: You made an assumption that wasnt correct
  • Locality error: You ran code on the wrong node (Server, client etc)
  • Timing error: You expected something to have hapened that hasnt occurred yet

LOGICAL ERROR
You assumed something was in a particular state when in fact it wasn't
Dont assume anything is in a certain state, check it first before using it
Real World Silly example.
Don't assume a door is open, It can have other states, e.g:


  • It could be closed
  • It could be closed & Locked
  • It could be in an interim state between open and closed
  • It could not exist

In the real world, your brain would have processed all of this, even to the point where you would have tried the door handle if it were closed to see if it was locked
(Common sense (assumptions) cannot exist in the realms of programming, only definite states can exist. (On. Off or Doesnt exist)

Coding example

if (Isnil "MyVariable")then{MyVariable = 0}else{MyVariable = MyVariable + 1};
if Myvariable does not exist, then define it as having a value of 0. if it does exist, add 1 to it

LOCALITY ERROR
This is where you need knowledge on the engine's idiosyncrasies. Only experience will get you there
Some code has to be run local (on the same machine) where the object is local too
Some code can be run remotely, eg from a different machine the object is local too
If it can be run remotely and you need to just run it on 1 machine, ideally the server
Otherwise for 10 players + 1 server you would be sending 11*10 sets of instructions over the network instead of just 10
In a lot of cases, the BIS Wiki has small icons at the top of each page for the comref entries
These will give you information on where code should be run for that command
  • AL: Arguments of this scripting command have to be local to the client the command is executed on
  • AG: Arguments of this scripting command don't have to be local to the client the command is executed on
  • EL: Effects of this scripting command are NOT broadcast over the network and happen only on trhe computer where executed
  • EG: Effects of this scripting command are broadcast over the network and happen on every computer in the network

Lets take a look at this example
addWeapon
AL: Arguments of this scripting command have to be local to the client the command is executed on
EG: Effects of this scripting command are broadcast over the network and happen on every computer in the network

The arguments for this command are
1) The unit or vehicle (Object) that we are going to add a weapon to
2) The classname of the weapon (A "string") that we are going to add
We need to run the code on the machine where the unit is local to. When we do run the code correctly, every machine will see that unit with that weapon

TIMING ERROR
If some code requires other code to have defined something and that code hasnt run, an error will occur.
Example, the paramsarray is defined on the server before preinit
whereas the paramsarray is defined on the client a few milliseconds after preinit has terminated
Therefore you cannot run any code requiring paramsarray on the client until post-init
(I'll explain the timings more clearly later)

SYNTAX ERROR
This can be the most frustrating of issues, searching for a missing semi colon or terminating bracket etc
This can be reduced by good formatting of your code or using a good text editor that highlights paired brackets etc
I personally use Poseidon, there are plenty of alternatives

All these errors can be debugged using very simple lines of code which rely on just TWO commands

There are others, but they all have some form of issue which reduces their effectiveness, so for simplicity, lets stick with these two




How To Debug
Lets use the following code example throughout the rest of this tutorial
The issue is we don't know if the player is being setcaptive False. Sometimes we think he is but sometimes he never is?.
And i'm new to scripting so i don't really understand this command.


// INIT.SQF
Player setcaptive TRUE;
[20] execVM "Myscript.sqf";

// MYSCRIPT.SQF
_myvariable = _this select 0; //(this should be the number 20)
_myNewvariable= _myvariable + 5; //(this should be the number 25)
_object = Leader (group Player);
waituntil {alive player && Player distance _object < _myNewvariable};
Player setCaptive FALSE;



In all the following walk through's I will give examples for both systemchat and diag_log

No.1 Prove the script is running
We do this by adding code that will give us a visual indication that the script is running
// MYSCRIPT

systemchat "DEBUG: AT START OF SCRIPT"; // This will dump text to the screen
diag_log text "DEBUG: AT START OF SCRIPT"; // This will dump text to the rpt file

_myvariable = _this select 0; //(this should be the number 20)
_myNewvariable= _myvariable + 5; //(this should be the number 25)
_object = Leader (group Player);
waituntil {alive player && Player distance _Object < _myNewvariable};
Player setCaptive FALSE;

systemchat "DEBUG: AT END OF SCRIPT"; // This will dump text to the screen
diag_log text "DEBUG: AT END OF SCRIPT"; // This will dump text to the rpt file

If you see "DEBUG: AT START OF SCRIPT" on screen, or in the .rpt file, you know the script starts
If you see "DEBUG: AT END OF SCRIPT" on screen, or in the .rpt file, you know the code has been run and has got to the end of the script
There is an issue when using systemchat, or any other chat, side, global etc. This being that any code that is run at pre-Init or before Time == 0 (A little on this later) will not dump any chat messages to the screen.
To work around this problem, we need to "spawn" this code as in the example below
[] spawn {sleep 3; systemchat "DEBUG: AT THIS POINT";};
You should then see this 3 seconds after you launch the mission from briefing

No.2 Return some variable values
We now know the script is running, but something is amiss. Sometimes we see the "DEBUG: AT END OF SCRIPT POINT" immediately, sometimes we dont see it at all.
Perhaps the group leader is too close, maybe the distance measurement is wrong ?
Okay, so lets see what values we actually have

So we would now use a format command, as in the example below

// MYSCRIPT
_myvariable = _this select 0; //(this should be the number 20)
_myNewvariable= _myvariable + 5; //(this should be the number 25)
_object = Leader (group Player);
waituntil {alive player && Player distance _Object < _myNewvariable};
Player setCaptive FALSE;
systemchat format ["myvariable has a value of %1 ----- MyNewvariable has a value of %2",_myvariable,_myNewvariable];
diag_log text format ["myvariable has a value of %1 ----- MyNewvariable has a value of %2",_myvariable,_myNewvariable];

We could have used seperate lines to return each variable value, in this example we just combined them into 1 systemchat or diag_log

First time we run it, we got the text on screen , it stated myvariable has a value of 20 ----- MyNewvariable has a value of 25
That seems correct, however the second time we run the mission, we didn't see any text on screen or in the .rpt file
So whats going on ?

No.3 Testing a condition
Could it be the condition, waituntil {alive player && Player distance _Object < _myNewvariable}; how do we test it
When you evaluate a condition that is correctly written it will return TRUE or FALSE. Anything else is wrong
We can see this value the same way we return any other value using a format command

// MYSCRIPT
_myvariable = _this select 0; //(this should be the number 20)
_myNewvariable= _myvariable + 5; //(this should be the number 25)
_object = Leader (group Player);
systemchat format ["The condition returns: %1",(alive player && Player distance _Object < _myNewvariable)];
diag_log text format ["The condition returns: %1",(alive player && Player distance _Object < _myNewvariable)];
waituntil {alive player && Player distance _Object < _myNewvariable};
Player setCaptive FALSE;
Good, at least we get a "The condition returns: FALSE" in the .rpt and on screen when we test
Oh, next time we run it, it returns TRUE, why are we getting these inconsistencies

Well, we have proved.
1) The script runs every time
2) The distance values are correct
3) The condition seems to be working, but not as expected
The only value we havent checked is the _object, so lets check that

No.4 Checking for the type of variable
We can check what value a variable has, we can also check what type of variable it is using the typeName command
Maybe the object isn't an object, maybe its something else, array, side string and therefore we cant get a distance measurement
I am also getting desperate now, so i'll return all the values just to make sure and heavily debug this script. No matter what, now i should see where the error is
(Diag_log code omitted for clarity)

// MYSCRIPT
systemchat "DEBUG: AT START OF SCRIPT";
_myvariable = _this select 0; //(this should be the number 20)
_myNewvariable= _myvariable + 5; //(this should be the number 25)
_object = Leader (group Player);

systemchat format ["myvariable is Type %1, Value %2", typename _myvariable, _myvariable];
systemchat format ["myNewvariable is Type %1, Value %2", typename _myNewvariable, _myNewvariable];
systemchat format ["Object is Type %1, Name %2", typename _object, name _object];
systemchat format ["The condition returns: %1",(alive player && Player distance _Object < _myNewvariable)];
diag_log text format ["The condition BEFORE returns: %1",(alive player && Player distance _Object < _myNewvariable)];
waituntil {alive player && Player distance _Object < _myNewvariable};
diag_log text format ["The condition AFTER returns: %1",(alive player && Player distance _Object < _myNewvariable)];
Player setCaptive FALSE;
systemchat format ["Am I captive %1", captive player];
systemchat "DEBUG: AT END OF SCRIPT";

I run the mission, first time goes okay, I wait for the group leader to arrive and all works as expected
I run it again and i get the following debug information immediately
"DEBUG: AT START OF SCRIPT";
"myvariable is Type SCALAR, Value 20"
"myNewvariable is Type SCALAR, Value 25"
"Object is Type OBJECT, Name Terox"
"The condition returns: TRUE"
"Am I captive FALSE"
"DEBUG: AT END OF SCRIPT"



Ah now i see the problem. , I am the leader of myself and therefore all the conditions are being met immediately. I Need to stop disabling AI when i test




Flow of code (Timing)
In order to understand any timing errors you need to understand the order in which code is run. Here it is, hopefully simply explained.

When a mission is selected and started by the admin, the following events occur in the numbered order
A lot of A3 mission devs refer to Time 0 as a key point when discussing at what point code is being run
So I shall comment using Time<0, Time==0 or Time>0 to clarify what they mean by this


1) Config stage
Addon configs, Description.ext and any #includes will be run at this point

2) Pre init stage (Time < 0)
This is run after the description.ext has loaded, players have slotted up and the admin has hit continue
(This is initiated from the cfgFunctions class as defined in the description.ext. It automatically runs any function that has a preinit=1 tag
Typically only 1 function should have this. This should be the keystone that pre initialises all required Global variables and functions for all later run code
Any functions that are called from Unit, vehicle and object init fields in the mission editor (Mission.sqm)should be declared at this point so that they are defined ready for use.

3) Mission.sqm (Time < 0)
Any code in the init field of units, objects vehicles is now being processed, starting with the first East unit you placed in the editor

4) Post Mission.sqm (Time == 0)
Any code spawned, execvm from Preinit is now being processed and will run its entirety or will halt when it comes across any
  • Sleep
  • Waintuntil{} command (Where the condition is not TRUE)

B.I's Init.sqf will also run at this point and halt at any code with sleep or waituntil (if the waituntil condition is not true).

5) Briefing is loaded (Time == 0)

6) Admin starts the mission (Time > 0)

7) Post INIT (Time > 0)
Any code that has halted waiting for a condition to become true or a sleep will now continue to run.

From now until the end of the mission, any code you have will run under the following condition

For every frame every script that is running will run for a maximum of 3 milliseconds
If it hasn't completed by that time, it will halt until the next frame and then continue to run for a further 3 milliseconds
It will continue to do this until it terminates





A Little about code optimisation
Some general rules:

1) If a script is being called more than once

a) compile preprocessfilelineumbers the function at pre init stage
B) Then either

1) call it every time you want it to run (If it doesn't contain any sleep or waituntil commands)
2) Spawn it every time you want it to run (If it DOES contain any sleep or waituntil commands)
if you use Execvm for this code, every time it is run, it compiles first then processes the code
If you call precompiled code, it simply just processes from code stored in ram which is much faster

 


2) If a script is only being called once, then you could execVM, spawn or call it (There probably isn't much difference)

if you precompiled then called code that is only being run once, then you can redefine the function as =nil; to remove it from memory
We can only assume that execvm code is removed from memory by the RV engine after it has run

3) try to avoid loops, especially long ones (3 milisecond rule)
4) Use eventhandlers to reduce the requirement for loops
5) Try wherever possible to call or spawn a function instead of execVM or spawn a script
6) Keep the total run time of a function below 3 milliseconds wherever possible

Dont be too concerned if the code runs longer than 3ms, its just a best practice to try and avoid, which is not always possible

7) Dont be concerned about the length of time preinit takes, it is after all preinit and not mission real time and anything you can prepare here is good for later




If you haven't fallen asleep reading this yet, there's something a little extra to help you

Zeus Foundation template

What is it ?
Its a foundation template that would be a good starting point for anyone designing their own template for whatever mission you may want to develop
Its basically a structured system for running code
It has the following functionality
 
  • Structure to thread (route-filter) code to the appropriate node, eg server, headless client, player, jip etc
  • Ability to mass Debug user defined "systems" or "modules"
  • Defines some basic switching variables for usage in your code

So if you want to have only a JIP player run specific code, there is a structure within this template to place that code.
Every script is heavily commented, so it shouldn't take long to figure out what does what

Debug System
The debug variables are defined in the Description.ext in the Txu_Debug class
This can easily be switched to a .sqf file, such as the pre init.
I just defined it here because this is part of a template I am developing where all configuration is done on #include.hpp files

(Use a copy of the "script_template.sqf" for any new scripts you create, editing any necessary content).
At the start of each script two variables need to be defined, _sy and _sc
_sy defines the "System/module" that the script is part of. It is used as an identifier and as a filter for the debugging output
_sc is a name for the script, typically this would be the actual file name less the ".sqf" and is used as an identifier in the debug log

If debugging is enabled and the "System/module" is being monitored, the following will be logged

  • Script start
  • any variables passed to the script
  • any user defined variables you wish to monitor
  • the end of the script
  • diag tick time, recorded This is the value that comes after the "@" symbol in the debug logs


Where the log is sent is user defined and can be either

  • RPT file via diag_log
  • Systemchat
  • both


What system are being logged/debugged is defined by the strings listed in the Txu_DebugSys array
For example, in this template you have

Txu_DebugSys[]={
"PARAMS",
"RESPAWN",
"EXAMPLE",
"CORE"
};


What this means is that any script with an _sy variable equal to anything in the Txu_DebugSys will log data to the Txu_DebugLog output

As it is set now,This will output data from every system that this foundation template has.
Rather than delete a system from this array, simply comment it out.

The rpt file is by far the best logging system, allowing you to also browse the server log if you have access to it.
It also provides a permanent record of data flow and returned values etc
The systemchat is good for live monitoring (Open up the in-game chat by typing "/" and key "PAGEUP" to scroll through previous messages)



Mission Code flow for this template

This is an example of the mission code flow for the Foundation template I linked too in this thread
It is a cleaned up version of the debug log dumps to the rpt file, showing the start and end of each script in the order in which they are run
The value given after the "@" symbol is the diag_ticktime

I have added comments with the tag #****** which explains at what point in the code flow we are at

### PRE INIT STARTING


"|========================= ZEU_FOUNDATION_TEMPLATE_2014.Stratis =========================|"

"------ CORE ------ fn_pre_Init ------> START : @ 4439.49"
"------ RESPAWN ------ fget_RespawnType ------> START : @ 4439.49"
"------ RESPAWN ------ fget_RespawnType ------> END : @ 4439.49"

"------ CORE ------ init_Modules ------> START : @ 4439.49"
"------ EXAMPLE ------ init_fnc ------> START : @ 4439.49"
"------ EXAMPLE ------ init_fnc ------> END : @ 4439.49"
"------ CORE ------ init_Modules ------> END : @ 4439.49"
"------ CORE ------ fn_pre_Init ------> END : @ 4439.49"

### MISSION.SQM IS RUNNING
any code run from the unit's init field will be shown here



### POST MISSION.SQM (TIME == 0)

"------ PARAMS ------ fprocess_params ------> START : @ 4439.84"
"------ PARAMS ------ fset_params ------> START : @ 4439.84"
"------ PARAMS ------ fset_params ------> END : @ 4439.84"
"------ PARAMS ------ fprocess_params ------> END : @ 4439.84"

"------ CORE ------ init_common ------> START : @ 4439.84"
"------ CORE ------ init_common ------> END : @ 4439.84"
"------ CORE ------ init_Server ------> START : @ 4440.69"
"------ CORE ------ init_Server ------> END : @ 4440.69"
"------ CORE ------ init_Client ------> START : @ 4440.69"
"------ CORE ------ init_Client ------> END : @ 4440.69"
"------ EXAMPLE ------ init_Common ------> START: @ 4441.03"
"------ EXAMPLE ------ init_Common ------> END : @ 4441.03"
"------ CORE ------ BIS Init.sqf ------> START : @ 4441.07"
"------ CORE ------ BIS Init.sqf ------> END : @ 4441.07"
"------ EXAMPLE ------ init_Server ------> START: @ 4441.08"
"------ EXAMPLE ------ init_Server ------> END : @ 4441.08"
"------ EXAMPLE ------ init_Client ------> START: @ 4441.08"
"------ EXAMPLE ------ init_Client ------> END : @ 4441.08"

### BRIEFING IS LOADED



RPT Dump in full

Here is the full rpt file less BI spam showing additiona variables being returned
"|========================= ZEU_FOUNDATION_TEMPLATE_2014.Stratis =========================|"

"------ CORE ------ fn_pre_Init ------> START : @ 4439.49"
IsDedicated: false
IsServer true
HasInterface true
IsMultiplayer true
Txu_Client true
Txu_HC false
Isnull Player true
Txu Debug System : ENABLED
Txu Debug Logging: ["PARAMS","RESPAWN","EXAMPLE","CORE"]

"------ RESPAWN ------ fget_RespawnType ------> START : @ 4439.49"
"------ RESPAWN ------ fget_RespawnType ------> END : @ 4439.49"
"------ CORE ------ init_Modules ------> START : @ 4439.49"
"------ EXAMPLE ------ init_fnc ------> START : @ 4439.49"
"------ EXAMPLE ------ init_fnc ------> END : @ 4439.49"
"------ CORE ------ init_Modules ------> END : @ 4439.49"
"------ CORE ------ fn_pre_Init ------> END : @ 4439.49"
"------ PARAMS ------ fprocess_params ------> START : @ 4439.84"
" PARAMS \ fprocess_params......"
" --> ParamsArray : [-100,450]"
"------ PARAMS ------ fset_params ------> START : @ 4439.84"
"------ PARAMS ------ fset_params ------> DATE Option selected : @ 4439.84"
" PARAMS \ fset_params......"
" --> Hour selected : 4"
" --> Minute selected : 50"
"------ PARAMS ------ fset_params ------> END : @ 4439.84"
" PARAMS \ fprocess_params......"
" --> ParamCompile : ['DATE',450]call Txu_Core_FsetParams"
"------ PARAMS ------ fprocess_params ------> END : @ 4439.84"
"------ CORE ------ init_common ------> START : @ 4439.84"
" CORE \ init_common......"
" --> Txu_PREInit : true"
" --> Txu_client : true"
" --> Txu_HC : false"
" --> Txu_JIP : false"
" --> IsDedicated : false"
" --> IsServer : true"
" --> HasInterface : true"
" --> IsMultiplayer : true"
" --> IsNull Player : false"
" --> Txu_MPINIT : true"
"------ CORE ------ init_common ------> END : @ 4439.84"
"------ CORE ------ init_Server ------> START : @ 4440.69"
"------ CORE ------ init_Server ------> END : @ 4440.69"
"------ CORE ------ init_Client ------> START : @ 4440.69"
"------ CORE ------ init_Client ------> END : @ 4440.69"
"------ EXAMPLE ------ init_Common ------> START : @ 4441.03"
"------ EXAMPLE ------ init_Common ------> END : @ 4441.03"
"------ CORE ------ BIS Init.sqf ------> START : @ 4441.07"
"------ CORE ------ BIS Init.sqf ------> END : @ 4441.07"
"------ EXAMPLE ------ init_Server ------> START : @ 4441.08"
"------ EXAMPLE ------ init_Server ------> END : @ 4441.08"
"------ EXAMPLE ------ init_Client ------> START : @ 4441.08"
"------ EXAMPLE ------ init_Client ------> END : @ 4441.08"




Changelog


v1.1: 17th September 2014
  • Added a link to Killzone kid's Scripting Tutorial Blog
  • Added instructions for -showscripterrors
  • Added information on where to find the RPT file
Edited by terox
  • Like 7
  • Thanks 1

Share this post


Link to post
Share on other sites

Updated the links, thx m8

Share this post


Link to post
Share on other sites

With regards to script timing, how would it look if I wanted to sync mission parameters to all the clients before the assets are loaded. I have global vars in vehicle inits which sometimes seem to run before the server has a chance to init these vars?

I was told elsewhere that I should use cfgFunctions but I'm not sure what that would look like.

I'm using the following in my init.sqf:

for [{_i = 0}, {_i < count(paramsArray)}, {_i = _i + 1}] do {
	_param = (configName ((missionConfigFile >> "Params") select _i));
	_value = (paramsArray select _i);
	format["%1 = %2", _param, _value] call BIS_fnc_log;
	call compile format ["%1 = %2; publicVariable ""%1""", (configName ((missionConfigFile >> "Params") select _i)), (paramsArray select _i)];
};

Share this post


Link to post
Share on other sites

check the foundation template section and take a look at the foundation template.

cfgFunctions are defined in the description.ext and the preinit code is found in txu\core\fn_pre_Init.sqf

this code is run before the mission.sqm, eg object init fields in the mission editor.

params is also explained in the tutorial, it doesnt exist on the client until post preinit, so you would have to spawn your params code from preinit.

something like

[] spawn Myfunction;

Myfunction would have at the top

waituntil {! isnil "Paramsarray"};

when testing locally, eg when you are also the server, the code may behave differently to when testing on a dedicated server, reason being is that paramsarray exists on the server before preinit whereas on the client it doesnt exist until after preinit

Edited by Terox

Share this post


Link to post
Share on other sites

Second vote to sticky this! Have returned to reference different sections many times during my scripting learning process so far.

Share this post


Link to post
Share on other sites

Changelog

v1.1: 17th September 2014

  • Added a link to Killzone kid's Scripting Tutorial Blog
  • Added instructions for -showscripterrors
  • Added information on where to find the RPT file

Share this post


Link to post
Share on other sites

Some cute stuff in here!

It's amazing how much I thought I knew, but actually didn't! lol

Share this post


Link to post
Share on other sites

Very nice beginner MP tutorial! Loved all the explanations. Keep it up, Terox.

Share this post


Link to post
Share on other sites
8 hours ago, Opt.Acer said:

Nice  tutorial.

I'm glad to see this is still of use to some. 

Share this post


Link to post
Share on other sites
  • 16 hours ago, terox said:

    I'm glad to see this is still of use to some. 

    By the way, I am having some problem editing one mission. In that mission Zeus is cut down to only placing markers. So what I wanted to do is remove restriction because my group wants to be able to build small camps.

Do you know a way to remove it or even if you could tell me what code for that would be so I could search it up? 

Share this post


Link to post
Share on other sites

If you really want to Debug your code you could just use a Debugger now.
There is ArmaDebugEngine which can already be used for Debugging inside Arma.Studio.

They are still in Beta but I've already used it successfully for TFAR development.

Share this post


Link to post
Share on other sites

Hi Folks,

Maybe this is obvious to everyone but I've found using the "windows clipboard" really helps/speeds my script debugging efforts... I just call my logging function from critical areas of code passing it whatever respective string is needed... This allows me to track the codes execution step by step - or -  variable by variable if necessary... It doesn't vanish like a hint - it's not microscopic text like in the chat dialog - and I don't have to wade through the full log file... Simply open a new text document and hit paste after your codes execution... The only issue I haven't been able to figure out is - I can't pass the clipboard a carriage return - therefore the file output is delimited... It's easy in Notepad ++  to globally substitute carriage returns for your delimiter - a couple of keystrokes...

// FUNCTION - Log to Clipboard //

_fcLogger = {params ["_logEnt"];

private ["_logHld","_logSpc","_logPut"];

_logHld = copyfromclipboard;

_logSpc = " ## ";

_logPut = _logHld + _logEnt + _logSpc;

copytoclipboard _logPut;

};


Regards,
Scott

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×