Skip to content

Commit

Permalink
Convert Space.GetBodies() to use a type filter
Browse files Browse the repository at this point in the history
- Avoids pushing all bodies in Space to Lua every time we want to perform a very simple type filter operation
- Also avoids a Lua -> C -> Lua -> C call chain for each body
- Replace with lua-side filtering on a reduced set of bodies where class filter isn't granular enough
- Convert all classnames into ObjectType enum values for filtering
  • Loading branch information
sturnclaw committed Oct 6, 2023
1 parent c2a20f2 commit 8c58f49
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 49 deletions.
2 changes: 1 addition & 1 deletion data/libs/SpaceStation.lua
Original file line number Diff line number Diff line change
Expand Up @@ -941,7 +941,7 @@ function SpaceStation:UnlockAdvert (ref)
end

local function updateSystem ()
local stations = Space.GetBodies(function (b) return b.superType == "STARPORT" end)
local stations = Space.GetBodies("SpaceStation")
for i, station in ipairs(stations) do
-- updateStationMarket(station)
Economy.UpdateStationMarket(station:GetSystemBody())
Expand Down
28 changes: 28 additions & 0 deletions data/meta/Space.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-- Copyright © 2008-2023 Pioneer Developers. See AUTHORS.txt for details
-- Licensed under the terms of the GPL v3. See licenses/GPL-3.txt

-- This file implements type information about C++ classes for Lua static analysis

---@meta

local Space = {}

package.core["Space"] = Space

-- Returns a list of bodies simulated in the current system, optionally filtered by type
---@generic T
---@param class `T` an optional body classname to filter the returned results by
---@return T[]
---@overload fun(): Body[]
function Space.GetBodies(class) end

-- Returns a list of bodies at most dist away from the given body, optionally filtered by type
---@generic T
---@param body Body the reference body
---@param dist number the maximum distance from the reference body to search
---@param filter `T` an optional body classname to filter the returned results by
---@return T[]
---@overload fun(body: Body, dist: number): Body[]
function Space.GetBodiesNear(body, dist, filter) end

return Space
2 changes: 1 addition & 1 deletion data/modules/Assassination/Assassination.lua
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ local onShipUndocked = function (ship, station)
for ref,mission in pairs(missions) do
if mission.status == 'ACTIVE' and
mission.ship == ship then
planets = Space.GetBodies(function (body) return body:isa("Planet") end)
planets = Space.GetBodies("Planet")
if #planets == 0 then
ship:AIFlyTo(station)
mission.shipstate = 'outbound'
Expand Down
2 changes: 1 addition & 1 deletion data/modules/BulkShips.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ local spawnShips = function ()
return
end

local stations = Space.GetBodies(function (body) return body:isa("SpaceStation") and not body.isGroundStation end)
local stations = utils.filter_array(Space.GetBodies("SpaceStation"), function (body) return not body.isGroundStation end)
if #stations == 0 then
return
end
Expand Down
15 changes: 8 additions & 7 deletions data/modules/SearchRescue/SearchRescue.lua
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ local triggerAdCreation = function ()
-- Return if ad should be created based on lawlessness and min/max frequency values.
-- Ad number per system is based on how many stations a system has so a player will
-- be met with a certain number of stations that have one or more ads.
local stations = Space.GetBodies(function (body) return body.superType == 'STARPORT' end)
local stations = Space.GetBodies("SpaceStation")
local freq = Game.system.lawlessness * ad_freq_max
if freq < ad_freq_min then freq = ad_freq_min end
local ad_num_max = freq * #stations
Expand Down Expand Up @@ -1000,10 +1000,11 @@ local findNearbyStations = function (vacuum, body)
-- get station bodies within current system depending on vacuum variable
local nearbystations_raw
if vacuum == true then
nearbystations_raw = Space.GetBodies(function (body)
return body.superType == 'STARPORT' and (body.type == 'STARPORT_ORBITAL' or (not body.path:GetSystemBody().parent.hasAtmosphere)) end)
nearbystations_raw = utils.filter_array(Space.GetBodies("SpaceStation"), function (body)
return body.type == 'STARPORT_ORBITAL' or (not body.path:GetSystemBody().parent.hasAtmosphere)
end)
else
nearbystations_raw = Space.GetBodies(function (body) return body.superType == 'STARPORT' end)
nearbystations_raw = Space.GetBodies("SpaceStation")
end

-- determine distance to body
Expand Down Expand Up @@ -1039,7 +1040,7 @@ local findClosestPlanets = function ()
end

-- get planets with stations and remove from planet list
local ground_stations = Space.GetBodies(function (body) return body.type == 'STARPORT_SURFACE' end)
local ground_stations = utils.filter_array(Space.GetBodies("SpaceStation"), function (body) return body.type == 'STARPORT_SURFACE' end)
for _,ground_station in pairs(ground_stations) do
for i=#rockyplanets, 1, -1 do
if rockyplanets[i] == Space.GetBody(ground_station.path:GetSystemBody().parent.index) then
Expand All @@ -1051,7 +1052,7 @@ local findClosestPlanets = function ()

-- create dictionary of stations
local closestplanets = {}
local stations = Space.GetBodies(function (body) return body.superType == 'STARPORT' end)
local stations = Space.GetBodies("SpaceStation")
for _,station in pairs(stations) do closestplanets[station] = {} end

-- pick closest planets to stations
Expand Down Expand Up @@ -1289,7 +1290,7 @@ local makeAdvert = function (station, manualFlavour, closestplanets)
local needed_fuel
if flavour.id == 2 or flavour.id == 5 then
needed_fuel = math.max(math.floor(shipdef.fuelTankMass * 0.1), 1)
elseif flavour.id == 4 then -- different planet
elseif flavour.id == 4 then -- different planet
needed_fuel = math.max(math.floor(shipdef.fuelTankMass * 0.5), 1)
end
deliver_comm[Commodities.hydrogen] = needed_fuel
Expand Down
5 changes: 3 additions & 2 deletions data/modules/System/Explore.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ local Event = require 'Event'
local Comms = require 'Comms'
local Timer = require 'Timer'
local Lang = require 'Lang'
local utils = require 'utils'

local l = Lang.GetResource("module-system")

local exploreSystem = function (system)
Comms.Message(l.GETTING_SENSOR_DATA)
local starports = #Space.GetBodies(function (body) return body.superType == 'STARPORT' end)
local major_bodies = #Space.GetBodies(function (body) return body.superType and body.superType ~= 'STARPORT' and body.superType ~= 'NONE' end)
local starports = #Space.GetBodies("SpaceStation")
local major_bodies = #Space.GetBodies("TerrainBody")
local bodies
if major_bodies == 1 then
bodies = l.BODY
Expand Down
2 changes: 1 addition & 1 deletion data/modules/TradeShips/Debug.lua
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ debugView.registerTab('debug-trade-ships', function()
ui.sameLine()
property("Lawlessness", string.format("%.4f", Game.system.lawlessness))
ui.sameLine()
property("Total bodies in space", #Space.GetBodies())
property("Total bodies in space", Space.GetNumBodies())
end

if ui.collapsingHeader("Stations") then
Expand Down
2 changes: 1 addition & 1 deletion data/modules/TradeShips/Flow.lua
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ Flow.calculateSystemParams = function()
if system.population == 0 then return nil end

-- all ports in the system
local ports = Space.GetBodies(function (body) return body.superType == 'STARPORT' end)
local ports = Space.GetBodies("SpaceStation")
-- check if the current system can be traded in
if #ports == 0 then return nil end

Expand Down
17 changes: 16 additions & 1 deletion src/lua/LuaBody.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,20 @@ namespace PiGui {
extern int pushOnScreenPositionDirection(lua_State *l, vector3d position);
} // namespace PiGui

static LuaFlags<ObjectType> s_bodyFlags(ENUM_PhysicsObjectType);
static LuaFlags<ObjectType> s_bodyFlags ({
{ "Body", ObjectType::BODY },
{ "ModelBody", ObjectType::MODELBODY },
{ "Ship", ObjectType::SHIP },
{ "Player", ObjectType::PLAYER },
{ "SpaceStation", ObjectType::SPACESTATION },
{ "TerrainBody", ObjectType::TERRAINBODY },
{ "Planet", ObjectType::PLANET },
{ "Star", ObjectType::STAR },
{ "CargoBody", ObjectType::CARGOBODY },
{ "Projectile", ObjectType::PROJECTILE },
{ "Missile", ObjectType::MISSILE },
{ "HyperspaceCloud", ObjectType::HYPERSPACECLOUD }
});

/*
* Class: Body
Expand Down Expand Up @@ -902,4 +915,6 @@ void LuaObject<Body>::RegisterClass()
LuaObjectBase::RegisterSerializer("CargoBody", body_serializers);
LuaObjectBase::RegisterSerializer("Missile", body_serializers);
LuaObjectBase::RegisterSerializer("HyperspaceCloud", body_serializers);

s_bodyFlags.Register(Lua::manager->GetLuaState(), "Constants.ObjectType");
}
74 changes: 40 additions & 34 deletions src/lua/LuaSpace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -905,21 +905,46 @@ static int l_space_get_body(lua_State *l)
return 1;
}

/*
* Function: GetNumBodies
*
* Get the total number of bodies simulated in the current Space
*
* bodies = #Space.GetNumBodies()
*
* Return:
*
* num - the number of bodies currently existing in Space
*
* Availability:
*
* Oct. 2023
*
* Status:
*
* stable
*/
static int l_space_get_num_bodies(lua_State *l)
{
if (!Pi::game) {
return luaL_error(l, "Game is not started!");
}

LuaPush(l, Pi::game->GetSpace()->GetNumBodies());
return 1;
}

/*
* Function: GetBodies
*
* Get all the <Body> objects that match the specified filter
* Get all the <Body> objects that match the specified filter type
*
* bodies = Space.GetBodies(filter)
* bodies = Space.GetBodies([type])
*
* Parameters:
*
* filter - an option function. If specificed the function will be called
* once for each body with the <Body> object as the only parameter.
* If the filter function returns true then the <Body> will be
* included in the array returned by <GetBodies>, otherwise it will
* be omitted. If no filter function is specified then all bodies
* are returned.
* type - an optional Body classname acting as a filter on the type of the
* returned bodies
*
* Return:
*
Expand All @@ -929,13 +954,13 @@ static int l_space_get_body(lua_State *l)
* Example:
*
* > -- get all the ground-based stations
* > local stations = Space.GetBodies(function (body)
* > local stations = utils.filter_array(Space.GetBodies("SpaceStation"), function(body)
* > return body.type == "STARPORT_SURFACE"
* > end)
*
* Availability:
*
* alpha 10
* Oct. 2023
*
* Status:
*
Expand All @@ -950,35 +975,15 @@ static int l_space_get_bodies(lua_State *l)

LUA_DEBUG_START(l);

bool filter = false;
if (lua_gettop(l) >= 1) {
luaL_checktype(l, 1, LUA_TFUNCTION); // any type of function
filter = true;
}
ObjectType filterBodyType = LuaPull<ObjectType>(l, 1, ObjectType::BODY);
bool filter = filterBodyType != ObjectType::BODY;

lua_newtable(l);

int idx = 1;
for (Body *b : Pi::game->GetSpace()->GetBodies()) {
if (filter) {
lua_pushvalue(l, 1);
LuaObject<Body>::PushToLua(b);
if (int ret = lua_pcall(l, 1, 1, 0)) {
const char *errmsg("Unknown error");
if (ret == LUA_ERRRUN)
errmsg = lua_tostring(l, -1);
else if (ret == LUA_ERRMEM)
errmsg = "memory allocation failure";
else if (ret == LUA_ERRERR)
errmsg = "error in error handler function";
luaL_error(l, "Error in filter function: %s", errmsg);
}
if (!lua_toboolean(l, -1)) {
lua_pop(l, 1);
continue;
}
lua_pop(l, 1);
}
if (filter && !b->IsType(filterBodyType))
continue;

lua_pushinteger(l, idx++);
LuaObject<Body>::PushToLua(b);
Expand Down Expand Up @@ -1110,6 +1115,7 @@ void LuaSpace::Register()
{ "PutShipOnRoute", l_space_put_ship_on_route },

{ "GetBody", l_space_get_body },
{ "GetNumBodies", l_space_get_num_bodies },
{ "GetBodies", l_space_get_bodies },
{ "GetBodiesNear", l_space_get_bodies_near },

Expand Down

0 comments on commit 8c58f49

Please sign in to comment.