Page 1 of 9

Bobs scripting woes

Posted: Sat Jul 06, 2013 6:25 pm
by TheBob
I've had a problem that one of my lua functions (rather Brianetta's actually) that searched for a far away system was called repeatedly, creating lag and occasionally a crash. I've stripped it all down to the bare bones and inserting debug messages to trace down the exact point of the problem. The interesting outcome was that the bare bones were a lot more bare than I thought. Namely, all I needed was an onCreateBB call, and lo and behold, it seems to be called perpetually. Maybe I'm just understanding the documentation wrong?
This event is triggered the first time a Ship docks with the station or is placed directly into the station (eg by Space.SpawnShipDocked or at game start)
I was under the impression that this should only happen once, when the player ship docks. From the documentation it seems possible that it is called for every ship that docks. However, for example the "package delivery" mission script which I still use as a formal template, does not check whether the ship that called onCreateBB is the player ship.

Now I don't know exactly where the problem is: If it is intended for onCreateBB to be called whenever a ship docks with a station, I'd suggest to update the documentation to make that fact clearer, and to modify existing mission scripts to check for player before adding adverts. Anything else would seem a waste of resources.

Or if, which would seem more logical, onCreateBB is indeed only intended to be called when the player docks, I'd suggest to fix the code.

I am not sure if the problem is that onCreateBB is called for every docking, however, it is clear that it is called several times, and as far as I could tell perpetually. Here's the script I used, it simply throws out a message on every onCreateBB:

Code: Select all

local systemiter = 0

local onCreateBB = function (station)
	--local num = Engine.rand:Integer(0, math.ceil(Game.system.population))
--	for i = 1,num do
		debugmessage = string.interp("onCreateBB call {num}", {num = systemiter,})
		Comms.ImportantMessage(debugmessage, "debug")
		systemiter = systemiter + 1
--	end
end

local serialize = function ()
	return { ads = ads, missions = missions }
end

local unserialize = function (data)
	loaded_data = data
end

Event.Register("onCreateBB", onCreateBB)

Mission.RegisterType('UB_RoboSurvey','Robotic Survey')

Serializer:Register("Robotic Survey", serialize, unserialize)

Re: onCreateBB called repeatedly?

Posted: Sat Jul 06, 2013 7:12 pm
by Luomu
Looking at the code, a BB is created for each station, that doesn't already have a BB, and there is at least one ship docked. So no, it's not called infinitely but there will be multiple BB creations as soon as you enter a populated system. The station's equipment & commodity market is filled at the same time.
I'm not the master of BBs so I don't know if this is sensible behavior, but it's how things currently are.

Re: onCreateBB called repeatedly?

Posted: Sat Jul 06, 2013 7:31 pm
by TheBob
Thanks for the confirmation. My major problem most probably comes from a stack overflow as one function is recursive, and I was really baffled when I saw it called over and over again even after it returned a correct value. Probably best to just cap the number of recursions and leave the rest as is. I assume onCreateBB does not get called anymore if a station already has a BB and the player lands there?

Re: onCreateBB called repeatedly?

Posted: Sat Jul 06, 2013 7:38 pm
by Luomu
TheBob wrote:I assume onCreateBB does not get called anymore if a station already has a BB and the player lands there?
Correct.

Re: onCreateBB called repeatedly?

Posted: Sun Jul 07, 2013 10:27 am
by TheBob
I changed the function inquestion (GetFarSystemPath) to be iterative instead of recursive and return nil if no system was found, and in this case cancel the creation of the advert. It still freezes at times, and I'm not expierienced enough with LUA to see why. I would be obliged if somebody could take a look at the code below and tell me if he can spot any problems with it.

Usually it freezes when I click "new game starting on ...". Sometimes it displays the first frame of the game and freezes then, sometimes it freezes right in the menu before anything else is thrown on the screen.
It can happen anytime a mission is created, and the fewer I create, the less frequently it happens. Adverts that do get created without freezing work flawlessly, so the problem must lie somewhere in the creation process.

Code: Select all

-- don't produce missions for further than this many SECTORS away
local max_mission_dist = 10
local min_mission_dist = 8
-- typical time for travel to a system max_mission_dist away
local typical_travel_time = 0.9 * max_mission_dist * 24 * 60 * 60 * 3
-- base reward from which payment is calculated. Adjust for balancing.
local base_reward = 20000



local GetFarSystemPath
GetFarSystemPath = function (distance)
--Brianettas hack to get a far system without generating a huge amount of systems
	for i = 0, 9, 1 do
		local here = StarSystem.path
	-- Fear my crazy maths.  Seriously.
		X = distance * distance
		Z = Engine.rand:Integer(1,X - 2)
		X = X - Z
		Y = Engine.rand:Integer(1,X)
		X = X - Y
		Z = math.floor(math.sqrt(Z))
		Y = math.floor(math.sqrt(Y))
		X = math.floor(math.sqrt(X))
		local OK,systempath = pcall(SystemPath.New,X,Y,Z,1)
		if OK then
			return systempath
		end
	end
--	Comms.ImportantMessage("no system found", "debug")
	return nil
end


local makeAdvert = function (station)

	--getting a random system a long distance of
	local farsystempath = GetFarSystemPath((max_mission_dist + min_mission_dist) / 2)

	if farsystempath == nil then
		return
	end
	
	--looking for an apropriate system in its vicinity
	local nearbysystems = farsystempath:GetStarSystem():GetNearbySystems((max_mission_dist - min_mission_dist) * 4, function (s) return #s:GetStationPaths() == 0 end)

	local reward, due, location, nearbysystem
	local client = Character.New()
	local flavour = Engine.rand:Integer(1,#delivery_flavours)
	local urgency = delivery_flavours[flavour].urgency
	local risk = delivery_flavours[flavour].risk


	--		local nearbysystems = Game.system:GetNearbySystems(max_mission_dist, function (s) return #s:GetStationPaths() == 0 end)
		if #nearbysystems == 0 then return end
		nearbysystem = nearbysystems[Engine.rand:Integer(1,#nearbysystems)]
		while nearbysystem:DistanceTo(Game.system) < min_mission_dist do
			nearbysystem = nearbysystems[Engine.rand:Integer(1,#nearbysystems)]
		end

		local dist = nearbysystem:DistanceTo(Game.system)
		local nearbystations = nearbysystem:GetBodyPaths()
		location = nearbystations[Engine.rand:Integer(1,#nearbystations)]
		while location:GetSystemBody().type ~= "PLANET_TERRESTRIAL" do
			location = nearbystations[Engine.rand:Integer(1,#nearbystations)]
		end

		--random number of robots to be deployed. No less than 2, no more than 10 (don't want to make things too tedious)
		local robonumber = Engine.rand:Integer(2,10)
		--surveytime for robots between 3 and 7 days
		local surveytime = Engine.rand:Integer(3,7)

		--[[reward calculation: Change base_reward in header for rough balancing
			more robots to deploy and pick up = higher reward, factor between 1.4 and 2.
			higher risk modifies reward, by 1 + risklevel / 2 (between 1 and 2.5)]]--

		reward = math.floor(base_reward * (1 + robonumber / 10) * (1 + risk / 2))
		due = Game.time + ((dist / max_mission_dist) * typical_travel_time * (1.5-urgency) * Engine.rand:Number(0.9,1.1))

	local ad = {
		station		= station,
		flavour		= flavour,
		client		= client,
		location	= location,
		due		= due,
		risk		= risk,
		urgency		= urgency,
		reward		= reward,
		isfemale	= isfemale,
		faceseed	= Engine.rand:Integer(),
		robonumber	= robonumber,
		distance 	= dist,
		surveytime 	= surveytime,
	}

	local sbody = ad.location:GetSystemBody()

	ad.desc = string.interp(delivery_flavours[flavour].adtext, {
		system	= nearbysystem.name,
		cash	= Format.Money(ad.reward),
		starport = sbody.name,
	})

	local ref = station:AddAdvert(ad.desc, onChat, onDelete)
	ads[ref] = ad
end



local onCreateBB = function (station)
		makeAdvert(station)
end

local serialize = function ()
	return { ads = ads, missions = missions }
end

local unserialize = function (data)
	loaded_data = data
end

Event.Register("onCreateBB", onCreateBB)

Mission.RegisterType('UB_RoboSurvey','Robotic Survey')

Serializer:Register("Robotic Survey", serialize, unserialize)

Re: onCreateBB called repeatedly?

Posted: Sun Jul 07, 2013 3:09 pm
by TheBob
Oooooh hells. Gotcha!

Code: Select all

      while location:GetSystemBody().type ~= "PLANET_TERRESTRIAL" do
         location = nearbystations[Engine.rand:Integer(1,#nearbystations)]
      end
This thing never actually terminates when there doesn't happen to be a terrestrial planet in the selected system. I was chasing a red hering, the recursion was never actually the problem.

Re: onCreateBB called repeatedly?

Posted: Sun Jul 07, 2013 3:17 pm
by Luomu
Excellent :)

Re: Bobs scripting woes

Posted: Sun Jul 07, 2013 3:41 pm
by TheBob
I renamed the thread, as it is very probable that I'll have more scripting related questions in the future. Like, right now. And it would rather disfigure the forum if I opened a new thread for every single one.

I try in vain to make the script do something when I unloaded a certain type of cargo on the planet noted in the mission. Now that I tried to debug some stuff to see where the problem lies and why the body passed by onCargoUnload never seems to match the body mentioned in the mission, despite me being landed on it, I found that the body returned by onCargoUnload doesn't contain a path... ergo there's not really much for me to compare with. The following code produces a crash, saying that body.path is a nil value:

Code: Select all

local onUnload = function (body, cargoType)
		print(body.path:GetSystemBody().name)
end
Event.Register("onCargoUnload", onUnload)
So, if I can't get a path, how do I compare the body with the SystemPath stored in my mission? I can't see a way to get a body object out of it.

Re: Bobs scripting woes

Posted: Sun Jul 07, 2013 4:39 pm
by TheBob
Through a bit more experimentation, I found out that body actually returns the player (at least body.label is the same as the player label).

According to the docummentation, this shouldn't be the case:
body the Body the Player was docked with (a SpaceStation) or landed on (a Planet)
Bug or wrong documentation? Since onJettison returns the ship, it would be easy to understand where the bug comes from...

Re: Bobs scripting woes

Posted: Sun Jul 07, 2013 4:51 pm
by FluffyFreak
I take it these functions are implemented in Lua only?
I can't find a matching prototype in the C++ at all.
Sorry.