Skip to content

sharing 1/4: foundations (modes, economy boundary, transfer library)#7962

Merged
keithharvey merged 0 commit into
sharing_tab_mergeablefrom
sharing/01-foundations
Jun 22, 2026
Merged

sharing 1/4: foundations (modes, economy boundary, transfer library)#7962
keithharvey merged 0 commit into
sharing_tab_mergeablefrom
sharing/01-foundations

Conversation

@keithharvey

@keithharvey keithharvey commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Moved to #8061

📚 Stacked split of #5704 — review bottom-up

Each PR merges into the one below it; all four squash-equal the sharing_tab branch (verified byte-identical).


Stack 1 of 4 splitting #5704 into reviewable layers.

Pure modules + their specs, no runtime wiring yet:

  • modes/sharing/* presets, sharing_mode_enums/helpers, modoptions entries
  • economy/* — waterfill solver, manual share ledger, shared_config
  • team_transfer/* core — enums, comms, shared/synced logic, context_factory, serialization, unit_sharing_categories, tech_blocking
  • lua_rules_msg + message framing, i18n interpolate, type defs, shared test builders

Targets the sharing_tab_mergeable integration base. Reviews bottom-up.

@github-actions

github-actions Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Integration Test Results

15 tests   - 1   7 ✅  - 1   4s ⏱️ -1s
 1 suites ±0   8 💤 ±0 
 1 files   ±0   0 ❌ ±0 

Results for commit 4c65388. ± Comparison against base commit 8942707.

This pull request removes 1 test.
ai_stai_factory_rect.lua

♻️ This comment has been updated with latest results.

@@ -0,0 +1,58 @@
local M = {}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The specs for this file are in PR#2. Stats updates are deferred a tick.

isCheatingEnabled = springRepo.IsCheatingEnabled(),
ext = {},
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enrichers are a mechanism for subsystems (e.g. tech_core) to inject state into a context without downstream policies caring where, for example, tax rate is coming from.

-- degrades to the base rate when tech_blocking isn't active.
---@param springRepo SpringSynced
---@param teamId number
---@return number

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A team's current sharing tax rate, resolved live from its tech level. Unlike the policy path, the waterfill solver works per-team with no sender/receiver pair, so it can't read tax off a PolicyContext; this is its context-free accessor. Tech-blocking aware (callers need no knowledge of tech_core) and degrades to the base rate when tech_blocking is inactive. For the context path, see the enrichers registered via context_factory (registerPolicyContextEnricher) — e.g. game_tech_blocking stamps ctx.taxRate.


--- Determine communication case from policy result
---@param policyResult ResourcePolicyResult
---@return integer

@keithharvey keithharvey Jun 15, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fregin love the comms files and this classifier pattern in particular. So much complexity moved to a single function. Note that it must remain exhaustive.

if not SharedConfig.isResourceSharingEnabled(ctx.springRepo) then
return Shared.CreateDenyPolicy(ctx.senderTeamId, ctx.receiverTeamId, resourceType, ctx.springRepo)
end

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is necessary to preserve existing expectations around cheat mode. I have tested the fallbacks without cheats on.

Comment thread common/luaUtilities/team_transfer/resource_transfer_synced.lua Outdated
end
return table.concat(names, " + ")
end

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This bit allows us to show "unlocked forever" if you have reached the end of unlocks in tech_core.

Comment thread luarules/gadgets/game_message.lua Outdated
-- example usecase: Spring.SendLuaRulesMsg('msg:ui.playersList.chat.needEnergyAmount:amount='..shareAmount)
-- example usecase: Spring.SendLuaRulesMsg('msg:ui.playersList.chat.needEnergyAmount:amount:'..shareAmount)

local PACKET_HEADER = "msg"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With "msg" (len 3) it matches any message starting with msg: msgFoo, msgsomethingelse, etc. With "msg:" (len 4) it only matches when the : delimiter is actually present, so it can't accidentally swallow some other gadget's msg-prefixed packet.

With len 3 it strips msg and leaves the payload as :ui.playersList... , a stray leading colon.

Comment thread spec/builders/index.lua
local ModeTestHelpers = VFS.Include("spec/builders/mode_test_helpers.lua")

---@class Builders
---@field Team TeamBuilder

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops I thought I completed this rename before this branch.

vars = vars or {}
str = escapeDoublePercent(str)
str = interpolateVariables(str, vars)
str = interpolateFormattedVariables(str, vars)

@keithharvey keithharvey Jun 16, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also fixed in a separate PR, but left here because it does allow the branch to work.

local taxRate = ctx.taxRate or SharedConfig.getTaxConfig(ctx.springRepo)
return (taxRate < 1) and taxRate or 1
end

@keithharvey keithharvey Jun 19, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The core idea of this PR: a pure function from an enriched per-(sender,receiver) context to the single answer object every downstream consumer (UI, transfer actions, chat) needs -- canShare, sendable/receivable, tax, tech gating. Variability lives in the context + enrichers, not here.

Because it's pure, policy can become data: lift the deny cascade into named, composable nodes that declare what they read/write on ctx, giving an analyzable, mode-diffable rule tree with surgical (dependency-based) cache invalidation. The load-bearing seam is a pure decision separated from imperative effect -- keep the rules pure, composable and extensible (spec kept separate from runtime state) and the math monomorphic.

This shape isn't specific to sharing: the same decision/effect split could underpin the Mission API and game behavior broadly. "Data-driven" alone isn't the win -- purity, composition and extensibility are.

@keithharvey keithharvey force-pushed the sharing/01-foundations branch 2 times, most recently from 20801f9 to 4c65388 Compare June 20, 2026 05:31
@keithharvey keithharvey changed the base branch from sharing_tab_mergeable to master June 20, 2026 05:33
@keithharvey keithharvey requested review from WatchTheFort and removed request for WatchTheFort June 20, 2026 05:34
@keithharvey keithharvey changed the base branch from master to sharing_tab_mergeable June 20, 2026 05:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant