Jump to content

Simple statistics script


xcom

Recommended Posts

I've made a simple statistics script that is relying on the mist.DBs.deadObjects table.

For some reason no data is gathered and I can't figure it out.

 

The save statistics script -

http://pastebin.com/9ybeDJwF

 

Example of mist.DBs.deadObjects table -

 

deadObjects =

{

[16779776] =

{

["objectPos"] =

{

["y"] = 22.492967605591,

["x"] = -292872.3125,

["z"] = 654419.125,

}, -- end of ["objectPos"]

["objectType"] = "vehicle",

["objectData"] =

{

["heading"] = -1.7994767003775,

["point"] =

{

["y"] = 654545.71428571,

["x"] = -292842.85714286,

}, -- end of ["point"]

["country"] = "russia",

["skill"] = "Average",

["type"] = "BTR-80",

["groupName"] = "RuBTRs",

["unit"] =

{

["id_"] = 16779776,

}, -- end of ["unit"]

["coalition"] = "red",

["unitId"] = 27,

["countryId"] = 2,

["category"] = "vehicle",

["unitName"] = "RuBTRs1",

["playerCanDrive"] = true,

["groupId"] = 13,

["pos"] =

{

["y"] = 22.492244720459,

["x"] = -292872.25,

["z"] = 654419.375,

}, -- end of ["pos"]

}, -- end of ["objectData"]

["object"] =

{

["id_"] = 16779776,

}, -- end of ["object"]

}, -- end of [16779776]

[16780032] =

{

["objectPos"] =

{

["y"] = 22.487079620361,

["x"] = -292863.46875,

["z"] = 654411.625,

}, -- end of ["objectPos"]

["objectType"] = "vehicle",

["objectData"] =

{

["heading"] = -1.7994767003775,

["point"] =

{

["y"] = 654585.71428571,

["x"] = -292882.85714286,

}, -- end of ["point"]

["country"] = "russia",

["skill"] = "Average",

["type"] = "BTR-80",

["groupName"] = "RuBTRs",

["unit"] =

{

["id_"] = 16780032,

}, -- end of ["unit"]

["coalition"] = "red",

["unitId"] = 28,

["countryId"] = 2,

["category"] = "vehicle",

["unitName"] = "RuBTRs2",

["playerCanDrive"] = true,

["groupId"] = 13,

["pos"] =

{

["y"] = 22.486150741577,

["x"] = -292864,

["z"] = 654412.6875,

}, -- end of ["pos"]

}, -- end of ["objectData"]

["object"] =

{

["id_"] = 16780032,

}, -- end of ["object"]

}, -- end of [16780032]

[16780544] =

{

["objectPos"] =

{

["y"] = 22.290777206421,

["x"] = -292853.84375,

["z"] = 654488.3125,

}, -- end of ["objectPos"]

["objectType"] = "vehicle",

["objectData"] =

{

["heading"] = -1.7994767003775,

["point"] =

{

["y"] = 654665.71428571,

["x"] = -292962.85714286,

}, -- end of ["point"]

["country"] = "russia",

["skill"] = "Average",

["type"] = "BTR-80",

["groupName"] = "RuBTRs",

["unit"] =

{

["id_"] = 16780544,

}, -- end of ["unit"]

["coalition"] = "red",

["unitId"] = 30,

["countryId"] = 2,

["category"] = "vehicle",

["unitName"] = "RuBTRs4",

["playerCanDrive"] = true,

["groupId"] = 13,

["pos"] =

{

["y"] = 22.284147262573,

["x"] = -292853.5,

["z"] = 654490.5,

}, -- end of ["pos"]

}, -- end of ["objectData"]

["object"] =

{

["id_"] = 16780544,

}, -- end of ["object"]

}, -- end of [16780544]

} -- end of deadObjects

 

 

 

The save file that is being saved -

 

------Mission Statistics------

 

blue destroyed vehicles

Name,Type

 

red destroyed vehicles

Name,Type

 

blue destroyed helicopters

Name,Type

 

red destroyed helicopters

Name,Type

 

blue destroyed planes

Name,Type

 

red destroyed planes

Name,Type

 

blue destroyed ships

Name,Type

 

red destroyed ships

Name,Type

 

blue destroyed statics

Name,Type

 

red destroyed statics

Name,Type

 

blue destroyed buildings

Name,Type

 

red destroyed buildings

Name,Type

 

blue destroyed unknowns

Name,Type

 

red destroyed unknowns

Name,Type

 

 

Appriciate any help.

Link to comment
Share on other sites

The script now works, I've updated the pastebin script.

It will not save all the info, here is an output for example -

 

------MISSION STATISTICS------

blue destroyed vehicles
ID,Name,Type
1,Unit #007,Vulcan,


red destroyed vehicles
ID,Name,Type


blue destroyed vehicles
ID,Name,Type


red destroyed vehicles
ID,Name,Type


blue destroyed vehicles
ID,Name,Type
1,Pilot #003,A-10C,


red destroyed vehicles
ID,Name,Type
1,Pilot #004,L-39ZA,


blue destroyed vehicles
ID,Name,Type


red destroyed vehicles
ID,Name,Type


blue destroyed vehicles
ID,Name,Type


red destroyed vehicles
ID,Name,Type


blue destroyed vehicles
ID,Name,Type


red destroyed vehicles
ID,Name,Type


blue destroyed vehicles
ID,Name,Type


red destroyed vehicles
ID,Name,Type


 

For some reason, it doesn't write all the info about all the vehicles which have been destroyed.

Could it be that there's an issue with the MIST live DBs?

Link to comment
Share on other sites

Awesome, I'll keep updating the page as I create an improved version.

At the moment I'm changing the script completly to work without the MIST table as I don't know exactly why but it doesn't retreive all the units.

 

I got a few questions about this function, I don't think it would work, haven't tested yet.

1. can I call the table in this way?

ForceLosses.uCoa.uCategory[#uCategory + 1]

2. Will the #uCategory actualy get the length of the inner array in side the coalition table?

3. In the DCS scripting engine, it doesn't say anything about unit.getCategory only about group.getCategory, does that mean that I have to use - uGroup:getCategory() instead? if so, is there a way to actualy get the unit category and not the group category?

4. How exactly does the EventHandler work? it doesn't say anything about it in the DCS Scripting engine, will the event handler automatically catch events and run the specified function?

 

If anyone can give me feedback it would be helpful.

 

Here is the current code:

local unitDead = {}

local ForceLosses = 
{
["red"] = 
{
	["GROUND_UNIT"] = {},
	["AIRPLANE"] = {},
	["HELICOPTER"] = {},
	["SHIP"] = {},
	["STRUCTURE"] = {},
},
["blue"] = 
{
	["GROUND_UNIT"] = {},
	["AIRPLANE"] = {},
	["HELICOPTER"] = {},
	["SHIP"] = {},
	["STRUCTURE"] = {},
},
},


function unitDead:onEvent(e)
local uGroup = e.initiator:getGroup()
local uCoa = e.initiator:getCoalition()
local uCategory = e.initiator:getCategory()
local uName = e.initiator:getName()
local uPlayer = e.initiator:getPlayer()
if e.id == world.event.S_EVENT_DEAD then
	ForceLosses.uCoa.uCategory[#uCategory + 1] = 
	{
		["time"] = e.time,
		["unitName"] = uName,
		["groupName"] = uGroup,
		["coalition"] = uCoa,
		["player"] = uPlayer,
		["target"] = e.target,
		["weapon"] = e.weapon,
	}
end	
end
	
world.addEventHandler(unitDead)

Link to comment
Share on other sites

The above script gets stuck on this line -

function unitDead:onEvent(e)

 

error says -

[string ... :24: '(' expected near 'unitDead'

 

Is the event handler not written correctly?

I checked other event handlers and they all seem to work with onEvent function.

 

Has anyone encountered this?

Link to comment
Share on other sites

Latest script (without MIST):

 

Dead Units handler:

local unitDeadHandler = {}

FLCoalition = 
{
[1] = "red",
[2] = "blue",
}

FLUCategory =
{
[1] = "GROUND_UNIT",
[2] = "AIRPLANE",
[3] = "HELICOPTER",
[4] = "SHIP",
[5] = "STRUCTURE",
}

FLGCategory = 
{
[1] = "AIRPLANE",
[2] = "HELICOPTER",
[3] = "GROUND",
[4] = "SHIP",
}

FLOfield =
{ 
[1] = "time",
[2] = "coalition",
[3] = "player",
[4] = "unitName",
[5] = "groupName",
[6] = "weapon",
[7] = "target",
[8]	= "launcher",
[9]	= "unitcategorynumber",
[10] = "unitcategoryname",
}

ForceLosses =
{
[1] =
{
	[1] = {},
	[2] = {},
	[3] = {},
	[4] = {},
},
[2] =
{
	[1] = {},
	[2] = {},
	[3] = {},
	[4] = {},
},
}

function SecondsToClock(sSeconds)
local nSeconds = sSeconds
if nSeconds == 0 then
	--return nil;
	return "00:00:00";
else
	nHours = string.format("%02.f", math.floor(nSeconds/3600));
	nMins = string.format("%02.f", math.floor(nSeconds/60 - (nHours*60)));
	nSecs = string.format("%02.f", math.floor(nSeconds - nHours*3600 - nMins *60));
	return nHours..":"..nMins..":"..nSecs
end
end


function unitDeadHandler:onEvent(e)
if e.id == world.event.S_EVENT_DEAD 
or e.id == world.event.S_EVENT_EJECTION
or e.id == world.event.S_EVENT_PILOT_DEAD then
	local uGroup = e.initiator:getGroup()
	local uCoa = uGroup:getCoalition()
	local uCategory = e.initiator:getCategory()
	local gCategory = uGroup:getCategory() + 1
	local uName = e.initiator:getName()
	local uType = e.initiator:getTypeName()
		if e.initiator:getPlayerName() then
			local uPlayer = e.initiator:getPlayerName()
		else
			local uPlayer = "AI"
		end
		if e.weapon then
			local wCategory = e.weapon.Category
			local wLauncher = e.weapon:getLauncher()
		else
			local wLauncher = "No Weapon"
			local wCategory = "No Weapon"
		end
		local eTarget = "No Target"  --still needs to be done
	
	ForceLosses[uCoa][gCategory][#ForceLosses[uCoa][gCategory] + 1] = 
	{
		[1] = SecondsToClock(timer.getTime()),
		[2] = FLCoalition[uCoa],
		[3] = uType,
		[4] = uPlayer,
		[5] = uName,
		[6] = uGroup:getName(),
		[7] = wCategory,
		[8] = eTarget,
		[9] = wLauncher,

	}	
end	
end
	
world.addEventHandler(unitDeadHandler)

 

 

The Save Statistics script:

local fdir = lfs.writedir() .. [[Logs\Save_stat\]] .. "statistics" .. timer.getTime() .. ".txt"
local f,err = io.open(fdir,"w")
if not f then
	local errmsg = 'Error: Need to create new folder in the Logs folder with the name' .. 'Save_stat . sample: C:\Users\youname\Saved Games\DCS\Logs\Save_stat' 
	trigger.action.outText(errmsg, 10)
	return print(err)
end


--write statistic information to file
f:write("------MISSION STATISTICS------\n\n")
for coa, gCategories in ipairs(ForceLosses) do
f:write(FLCoalition[coa].." side:\n")
f:write("ID,Time,Coalition,Type,Player Name,Unit Name,Group Name,Weapon,Target,Launcher\n")
for gCategory, Olist in ipairs(gCategories) do
	f:write(FLGCategory[gCategory]..":\n")			
	for index, ODetails in ipairs(Olist) do
		f:write(index..",")
		for Ofield, data in ipairs(ODetails) do
			f:write(data..",")
		end
		f:write("\n")
	end
	f:write("Total of "..#ForceLosses[coa][gCategory].." destroyed "..FLGCategory[gCategory])
	f:write("\n\n")
end
f:write("\n\n")
end
f:close()

 

The exported statistics result:

 

------MISSION STATISTICS------

red side:
ID,Time,Coalition,Type,Player Name,Unit Name,Group Name,Weapon,Target,Launcher
AIRPLANE:
1,00:00:00,red,L-39ZA,AI,Pilot #004,New Airplane Group #001,No Weapon,No Target,
2,00:00:00,red,L-39ZA,AI,Pilot #006,New Airplane Group #002,No Weapon,No Target,
Total of 2 destroyed AIRPLANE

HELICOPTER:
1,00:00:50,red,Ka-50,AI,Pilot #005,New Helicopter Group #002,No Weapon,No Target,
2,00:01:49,red,Ka-50,AI,Pilot #009,New Helicopter Group #004,No Weapon,No Target,
Total of 2 destroyed HELICOPTER

GROUND:
1,00:00:31,red,T-90,AI,Unit #011,New Vehicle Group #006,No Weapon,No Target,
2,00:01:06,red,T-90,AI,Unit #012,New Vehicle Group #007,No Weapon,No Target,
3,00:01:37,red,T-90,AI,Unit #013,New Vehicle Group #008,No Weapon,No Target,
Total of 3 destroyed GROUND

SHIP:
1,00:01:19,red,speedboat,AI,Unit #023,New Ship Group #001,No Weapon,No Target,
2,00:01:32,red,speedboat,AI,Unit #025,New Ship Group #001,No Weapon,No Target,
Total of 2 destroyed SHIP



blue side:
ID,Time,Coalition,Type,Player Name,Unit Name,Group Name,Weapon,Target,Launcher
AIRPLANE:
1,00:00:00,blue,A-10C,AI,Pilot #003,New Airplane Group,No Weapon,No Target,
2,00:00:00,blue,A-10C,AI,Pilot #007,New Airplane Group #003,No Weapon,No Target,
Total of 2 destroyed AIRPLANE

HELICOPTER:
Total of 0 destroyed HELICOPTER

GROUND:
1,00:01:13,blue,M-1 Abrams,AI,Unit #028,New Vehicle Group #002,No Weapon,No Target,
2,00:01:34,blue,M-1 Abrams,AI,Unit #003,New Vehicle Group #002,No Weapon,No Target,
3,00:01:47,blue,M-1 Abrams,AI,Unit #007,New Vehicle Group #002,No Weapon,No Target,
4,00:02:01,blue,Vulcan,AI,Unit #1,New Vehicle Group,No Weapon,No Target,
Total of 4 destroyed GROUND

SHIP:
1,00:01:39,blue,speedboat,AI,Unit #019,New Ship Group,No Weapon,No Target,
2,00:02:04,blue,speedboat,AI,Unit #017,New Ship Group,No Weapon,No Target,
Total of 2 destroyed SHIP


Edited by xcom
Link to comment
Share on other sites

  • 2 weeks later...

Updaing to a new version of the statistics script that saves info into a file in the format of CSV -

Event handler -

local eHandler = {}

EHCoalition = 
{
[1] = "red",
[2] = "blue",
}

EHUCategory =
{
[1] = "GROUND_UNIT",
[2] = "AIRPLANE",
[3] = "HELICOPTER",
[4] = "SHIP",
[5] = "STRUCTURE",
}

EHGCategory = 
{
[1] = "AIRPLANE",
[2] = "HELICOPTER",
[3] = "GROUND",
[4] = "SHIP",
}

EHOfield =
{ 
[1] = "time",
[2] = "coalition",
[3] = "player",
[4] = "unitName",
[5] = "groupName",
[6] = "weapon",
[7] = "target",
[8]	= "launcher",
[9]	= "unitcategorynumber",
[10] = "unitcategoryname",
}

wCategoryName = 
{
  [0] = "SHELL",
  [1] = "MISSILE",
  [2] = "ROCKET",
  [3] = "BOMB",
}


eventSpecificUnitTable = {}


function SecondsToClock(sSeconds)
local nSeconds = sSeconds
if nSeconds == 0 then
	--return nil;
	return "00:00:00";
else
	nHours = string.format("%02.f", math.floor(nSeconds/3600));
	nMins = string.format("%02.f", math.floor(nSeconds/60 - (nHours*60)));
	nSecs = string.format("%02.f", math.floor(nSeconds - nHours*3600 - nMins *60));
	return nHours..":"..nMins..":"..nSecs
end
end


function eHandler:onEvent(e)
if e.initiator then
	
	local eid = e.initiator.id_
	if eventSpecificUnitTable[eid] == nil then
		eventSpecificUnitTable[eid] = {}
		eventSpecificUnitTable[eid][e.id] = {}
		eIndex = 0
	elseif eventSpecificUnitTable[eid][e.id] == nil then
		eventSpecificUnitTable[eid][e.id] = {}
		eIndex = 0
	else
		eIndex = #eventSpecificUnitTable[eid][e.id]
	end
	
	local WorldEvent = world.event[e.id]
	local uGroup = e.initiator:getGroup()
	local uCoa = uGroup:getCoalition()
	local uCategory = e.initiator:getCategory()
	local gCategory = uGroup:getCategory() + 1
	local uName = e.initiator:getName()
	local uType = e.initiator:getTypeName()
	
	local uPlayer
	if not e.initiator:getPlayerName() then
		uPlayer = "AI"
	else
		uPlayer = e.initiator:getPlayerName()
	end
	
	local wCategory
	local wFlag
	if e.weapon == nil then
		wCategory = "No Weapon"
		wFlag = "No Weapon"
	else
		local wDesc = e.weapon:getDesc()
		wCategory = wCategoryName[wDesc.category]
		wFlag = wDesc.displayName
	end
		
	local eTargetType
	local eTargetPlayer
	if e.target == nil or Object.getCategory(e.target) == Object.Category.STATIC or Object.getCategory(e.target) == Object.Category.BASE or Object.getCategory(e.target) == Object.Category.SCENERY then
		eTargetType = "No Target"
		eTargetPlayer = "No Target"
	else
		eTargetType = e.target:getTypeName()
		if not e.target:getPlayerName() then
			eTargetPlayer = "AI"
		else
			eTargetPlayer = e.target:getPlayerName()
		end
	end
	eventSpecificUnitTable[eid][e.id][eIndex + 1] = 
			{
				["Time"] = SecondsToClock(timer.getTime()),
				["PlayerName"] = uPlayer,
				["UnitType"] = uType,
				["Weapon"] = wFlag,
				["WeaponCategory"] = wCategory,
				["TargetType"] = eTargetType,
				["TargetPlayerName"] = eTargetPlayer,
			}
	end
end


world.addEventHandler(eHandler)

 

 

Save statistics script -

local fdir = lfs.writedir() .. [[Logs\Save_stat\]] .. "EventUnitHandler" .. timer.getTime() .. ".txt"
local f,err = io.open(fdir,"w")
if not f then
	local errmsg = 'Error: Need to create new folder in the Logs folder with the name' .. 'Save_stat . sample: C:\Users\youname\Saved Games\DCS\Logs\Save_stat' 
	trigger.action.outText(errmsg, 10)
	return print(err)
end


wEvent = {
  "S_EVENT_SHOT",
  "S_EVENT_HIT",
  "S_EVENT_TAKEOFF",
  "S_EVENT_LAND",
  "S_EVENT_CRASH",
  "S_EVENT_EJECTION",
  "S_EVENT_REFUELING",
  "S_EVENT_DEAD",
  "S_EVENT_PILOT_DEAD",
  "S_EVENT_BASE_CAPTURED",
  "S_EVENT_MISSION_START",
  "S_EVENT_MISSION_END",
  "S_EVENT_TOOK_CONTROL",
  "S_EVENT_REFUELING_STOP",
  "S_EVENT_BIRTH",
  "S_EVENT_HUMAN_FAILURE",
  "S_EVENT_ENGINE_STARTUP",
  "S_EVENT_ENGINE_SHUTDOWN",
  "S_EVENT_PLAYER_ENTER_UNIT",
  "S_EVENT_PLAYER_LEAVE_UNIT",
  "S_EVENT_PLAYER_COMMENT",
  "S_EVENT_SHOOTING_START",
  "S_EVENT_SHOOTING_END",
  "S_EVENT_MAX",
}

--write statistic information to file
f:write("------MISSION STATISTICS------\n\n")
f:write("UnitID,Event,EventIndex,UnitType,Time,WeaponCategory,PlayerName,TargetType,TargetPlayerName,Weapon\n")
for unit, uEvents in pairs(eventSpecificUnitTable) do
for WorldEventID, eList in pairs(uEvents) do
	for index, eDetails in ipairs(eList) do
		f:write(unit..",")
		f:write(wEvent[WorldEventID]..",")
		f:write(index..",")
		for eInfoName, eInfoData in pairs(eDetails) do
			f:write(eInfoData..",")
		end
		f:write("\n")
	end
end
end
f:close()

 

CSV format can be used to import into a DB for example which will let us do smart queries to retrieve whatever info we like.

 

Enjoy :)

Link to comment
Share on other sites

Thanks :)

 

During creation of this script I've found an issue that might be flagged as a bug, would appriciate if some Tester or anyone from ED can report it and say if it's by design or a bug, and if by design is there a way to workaround it?

 

Scenario -

1. Su-25T (client/player) launchs Kh-58 at a hawk radar site.

2. Before kh-58 missile hits the hawk radar site, exit the su-25t plane (Either by crashing/ejecting/back to observer).

3. When the missile hits and the unit is destroyed there's no message that the client/player destroyeyed it or that it has been destroyed at all.

 

*When exporting the events, a hit event is categorized but there's no info on the initiator (nil), only the target.

Link to comment
Share on other sites

I take out the events into a CSV file and then created an automated method for the created file to be loaded into a table in a MySQL DB, then I run some smart queries through PHP to identify whatever info I want and display it in the relavent tables.

 

Table examples in this simple page I built -

http://89.163.173.82/DCS/Default.html

 

I've found another issue, AFAIK, the event.(initiator/target/weapon).id_ should be unique. In my export results - https://copy.com/idJOkRkJYbdR

I've found 2 different clients getting the same event.initiator.id_ , can be seen by filtering Initiator ID - 16779784, in the file I gave here.

Should that happen? is there a different unique identifier?

 

How can I make sure this is getting to the ED team?

Link to comment
Share on other sites

  • 4 months later...

any update on this? I am thinking i may try to add this into a longitudinal DoW mission that combines this with the GCI script.

 

S!

ASUS Tuf Gaming Pro x570 / AMD Ryzen 7 5800X @ 3.8 / XFX Radeon 6900 XT / 64 GB DDR4 3200 

"This was not in the Manual I did not read", cried the Noob" - BMBM, WWIIOL

Link to comment
Share on other sites

I've found another issue, AFAIK, the event.(initiator/target/weapon).id_ should be unique. In my export results - https://copy.com/idJOkRkJYbdR

I've found 2 different clients getting the same event.initiator.id_ , can be seen by filtering Initiator ID - 16779784, in the file I gave here.

Should that happen? is there a different unique identifier?

 

Just a wild guess, but is the initiator ID maybe tied to the aircraft slot instead of the client?

Link to comment
Share on other sites

  • 1 month later...

nice stuff.

 

@xcom, do you think its possible to write a second file?

 

thinking like this:

when you have flown the mission, you have a radio item added, call it up, and it saves a list of all surviving units to a file, including the coordinates.

 

as far as i understand, it should be possible to create an empty mission then, load the information of that file, and use dynadd? function at mission start to spawn the survivors of the last mission at the places where they actualy have been at the end of the last mission?

 

RR

[sIGPIC][/sIGPIC]

"There's nothing to be gained by second guessing yourself.

You can't remake the past, so look ahead... or risk being left behind."

 

Noli Timere Messorem

"No matter how fast light travels, it finds the darkness has always been there first, and is waiting for it."

Terry Pratchett

Link to comment
Share on other sites

Ian;2200688']Just a wild guess' date=' but is the initiator ID maybe tied to the aircraft slot instead of the client?[/quote']

 

Doubt it, as in the campaigns we run there are several respawns and they are all registered with different IDs.

But in order to have a more conclusive answer, a test needs to be done.

 

nice stuff.

 

@xcom, do you think its possible to write a second file?

 

thinking like this:

when you have flown the mission, you have a radio item added, call it up, and it saves a list of all surviving units to a file, including the coordinates.

 

as far as i understand, it should be possible to create an empty mission then, load the information of that file, and use dynadd? function at mission start to spawn the survivors of the last mission at the places where they actualy have been at the end of the last mission?

 

RR

 

We are already doing it using the save script and [FSF]IAN's mission planner.

You are welcome to see it here -

http://forums.eagle.ru/showthread.php?t=121145

 

I recommend it very much as it has the ability to save a mission state and continue it afterwards, among other great features with the planner.

Link to comment
Share on other sites

thanks mate, that looks realy interesting :)

 

regards,

RR

[sIGPIC][/sIGPIC]

"There's nothing to be gained by second guessing yourself.

You can't remake the past, so look ahead... or risk being left behind."

 

Noli Timere Messorem

"No matter how fast light travels, it finds the darkness has always been there first, and is waiting for it."

Terry Pratchett

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...