Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Space.GetBodiesNear(), ProximityQuery module #5637

Merged
merged 4 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
139 changes: 139 additions & 0 deletions data/modules/Common/ProximityQuery.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
-- Copyright © 2008-2023 Pioneer Developers. See AUTHORS.txt for details
-- Licensed under the terms of the GPL v3. See licenses/GPL-3.txt

local Engine = require 'Engine'
local Space = require 'Space'
local Timer = require 'Timer'
local utils = require 'utils'
local Game = require 'Game'

local ProximityQuery = {}

-- ─── Context ─────────────────────────────────────────────────────────────────

local onBodyEnter = function(ctx, enter) end
local onBodyLeave = function(ctx, leave) end

-- List of bodies currently in proximity is stored with weak keys, so deleted
-- bodies are quietly cleaned up during a GC cycle
local bodyListMt = {
__mode = "k"
}

---@class ProximityQuery.Context
---@field New fun(body, dist, interval, type): self
---@field body Body
---@field dist number
local Context = utils.class 'ProximityQuery.Context'

function Context:Constructor(body, dist, type)
self.bodies = setmetatable({}, bodyListMt)
self.body = body
self.dist = dist
self.filter = type
self.iter = 0

self.onBodyEnter = onBodyEnter
self.onBodyLeave = onBodyLeave
end

function Context:Cancel()
self.iter = nil
end

---@param fn fun(ctx: ProximityQuery.Context, enter: Body)
function Context:SetBodyEnter(fn)
self.onBodyEnter = fn
return self
end

---@param fn fun(ctx: ProximityQuery.Context, leave: Body)
function Context:SetBodyLeave(fn)
self.onBodyLeave = fn
return self
end

-- Class: ProximityQuery
--
-- This class provides a helper utility to allow using Space.GetBodiesNear() in
-- an efficient manner, providing an event-based API when bodies enter or leave
-- a user-specified relevancy range of the reference body.

-- Function: CreateQuery
--
-- Register a new periodic proximity test relative to the given body
--
-- Parameters:
--
-- body - the reference body to perform testing for
-- dist - the distance (in meters) of the proximity test to perform
-- interval - how often a proximity test should be performed in seconds.
-- Smaller values are more performance-hungry.
-- type - optional body classname filter, see Space.GetBodiesNear
-- overlap - if false, all bodies of type in the radius will generate
-- initial proximity events on the first proximity test
--
-- Returns:
--
-- context - the context object for the registered test
--
function ProximityQuery.CreateQuery(body, dist, interval, type, overlap)
local context = Context.New(body, dist, type)
local cb = ProximityQuery.MakeCallback(context)

-- Queue the start of the timer at a random timestamp inside the first interval period
-- This provides natural load balancing for large numbers of callbacks created on the same frame
-- (e.g. at game start / hyperspace entry)
Timer:CallAt(Game.time + Engine.rand:Number(interval), function()

if overlap == false then
cb()
else
-- Pre-fill the list of nearby bodies (avoid spurious onBodyEnter() callbacks when creating)
for i, locBody in ipairs(Space.GetBodiesNear(body, dist, type)) do
context.bodies[locBody] = context.iter
end
end

Timer:CallEvery(interval, cb)
end)

return context
end

---@private
---@param context ProximityQuery.Context
function ProximityQuery.MakeCallback(context)
return function()
-- Callback has been cancelled or query body no longer exists,
-- stop ticking this query
if not context.iter or not context.body:exists() then
return true
end

local newIter = (context.iter + 1) % 2
context.iter = newIter

for i, locBody in ipairs(Space.GetBodiesNear(context.body, context.dist, context.filter)) do
if not context.bodies[locBody] then
context.onBodyEnter(context, locBody)
end

context.bodies[locBody] = newIter
end

local remove = {}
for locBody, ver in pairs(context.bodies) do
if ver ~= newIter then
context.onBodyLeave(context, locBody)
table.insert(remove, locBody)
end
end

for _, v in ipairs(remove) do
context.bodies[v] = nil
end
end
end

return ProximityQuery
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 @@ -1001,10 +1001,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 @@ -1040,7 +1041,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 @@ -1052,7 +1053,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 @@ -1299,7 +1300,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
31 changes: 31 additions & 0 deletions src/lua/LuaBody.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,31 @@
#include "SpaceStation.h"
#include "Star.h"

// For LuaFlags<ObjectType>
#include "enum_table.h"
#include "pigui/LuaFlags.h"

namespace PiGui {
// Declared in LuaPiGuiInternal.h
extern bool first_body_is_more_important_than(Body *, Body *);
extern int pushOnScreenPositionDirection(lua_State *l, vector3d position);
} // namespace PiGui

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 @@ -843,6 +862,16 @@ static int l_body_set_ang_velocity(lua_State *l)
return 0;
}

void pi_lua_generic_pull(lua_State *l, int index, ObjectType &objectType)
{
objectType = s_bodyFlags.LookupEnum(l, index);
}

void pi_lua_generic_push(lua_State *l, ObjectType bodyType)
{
lua_pushstring(l, EnumStrings::GetString("PhysicsObjectType", static_cast<int>(bodyType)));
}

template <>
const char *LuaObject<Body>::s_type = "Body";

Expand Down Expand Up @@ -908,4 +937,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");
}
9 changes: 9 additions & 0 deletions src/lua/LuaBody.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright © 2008-2023 Pioneer Developers. See AUTHORS.txt for details
// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt

#include "Body.h"
#include "Lua.h"
#include "LuaPushPull.h"

void pi_lua_generic_pull(lua_State *l, int index, ObjectType &bodyType);
void pi_lua_generic_push(lua_State *l, ObjectType bodyType);
2 changes: 1 addition & 1 deletion src/lua/LuaPiGui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ static Type parse_imgui_enum(lua_State *l, int index, LuaFlags<Type> lookupTable
return static_cast<Type>(lua_tointeger(l, index));
else {
luaL_traceback(l, l, NULL, 1);
Error("Expected a table or integer, got %s.\n%s\n", luaL_typename(l, index), lua_tostring(l, -1));
Error("Expected a string or integer, got %s.\n%s\n", luaL_typename(l, index), lua_tostring(l, -1));
}
return static_cast<Type>(0);
}
Expand Down
Loading
Loading