fix: f-string 제거 + position/radius + pcall + 인덱스 인벤토리
- _get_global_summary: position+radius, pcall - _get_zone_summaries: f-string 제거, pcall - _detect_problems: position+radius, pcall - _get_player_info: get_contents() → 인덱스 접근 (Factorio 2.0 호환) - 모든 함수 try/except + startswith 검증
This commit is contained in:
@@ -2,9 +2,10 @@
|
|||||||
context_compressor.py — 계층적 컨텍스트 압축
|
context_compressor.py — 계층적 컨텍스트 압축
|
||||||
|
|
||||||
핵심 변경:
|
핵심 변경:
|
||||||
- game.player → game.players[1] (RCON 호환)
|
- f-string 제거 또는 최소화 (Lua 중괄호 충돌 방지)
|
||||||
- pcall로 엔티티 검색 감싸기 (Factorio 2.0 이름 변경 대응)
|
- position+radius 방식 사용
|
||||||
- type 기반 검색으로 버전 호환성 확보
|
- pcall + try/except 완전 감싸기
|
||||||
|
- get_contents() 대신 인덱스 접근 (Factorio 2.0 호환)
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
import math
|
import math
|
||||||
@@ -12,9 +13,7 @@ from factorio_rcon import FactorioRCON
|
|||||||
|
|
||||||
ZONE_SIZE = 64
|
ZONE_SIZE = 64
|
||||||
|
|
||||||
P = """local p = game.players[1]
|
P = 'local p = game.players[1] if not p then rcon.print("{}") return end '
|
||||||
if not p then rcon.print("{}") return end
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class ContextCompressor:
|
class ContextCompressor:
|
||||||
@@ -37,200 +36,117 @@ class ContextCompressor:
|
|||||||
drilldown = self._drilldown_problem_zones(zones, problems)
|
drilldown = self._drilldown_problem_zones(zones, problems)
|
||||||
return self._format_level2(global_summary, zones, problems, drilldown, player)
|
return self._format_level2(global_summary, zones, problems, drilldown, player)
|
||||||
|
|
||||||
# ── 글로벌 지표 (pcall로 안전하게) ──────────────────────────────
|
|
||||||
|
|
||||||
def _get_global_summary(self) -> dict:
|
def _get_global_summary(self) -> dict:
|
||||||
lua = P + """
|
lua = P + """
|
||||||
local surface = p.surface
|
local ok, err = pcall(function()
|
||||||
local force = p.force
|
local surface = p.surface
|
||||||
local center = p.position
|
local force = p.force
|
||||||
|
local result = {}
|
||||||
-- type 기반으로 검색 (Factorio 2.0 엔티티 이름 변경에 안전)
|
local total = 0
|
||||||
local type_list = {
|
local all = surface.find_entities_filtered{position = p.position, radius = 500, force = "player"}
|
||||||
"mining-drill", "furnace", "assembling-machine",
|
for _, e in ipairs(all) do
|
||||||
"transport-belt", "inserter", "electric-pole",
|
if e.name ~= "character" then
|
||||||
"generator", "boiler", "offshore-pump", "pipe",
|
result[e.name] = (result[e.name] or 0) + 1
|
||||||
"lab", "rocket-silo", "radar",
|
total = total + 1
|
||||||
"ammo-turret", "electric-turret", "wall", "gate",
|
end
|
||||||
"oil-refinery", "chemical-plant", "solar-panel", "accumulator",
|
|
||||||
"container"
|
|
||||||
}
|
|
||||||
|
|
||||||
local counts = {}
|
|
||||||
local total = 0
|
|
||||||
for _, t in ipairs(type_list) do
|
|
||||||
local ok, found = pcall(function()
|
|
||||||
return surface.find_entities_filtered{
|
|
||||||
area={{center.x-500,center.y-500},{center.x+500,center.y+500}},
|
|
||||||
type=t
|
|
||||||
}
|
|
||||||
end)
|
|
||||||
if ok and found then
|
|
||||||
for _, e in ipairs(found) do
|
|
||||||
counts[e.name] = (counts[e.name] or 0) + 1
|
|
||||||
total = total + 1
|
|
||||||
end
|
end
|
||||||
end
|
local current_tech = "none"
|
||||||
end
|
local tech_progress = 0
|
||||||
|
if force.current_research then
|
||||||
-- 연구 진행
|
current_tech = force.current_research.name
|
||||||
local current_tech = "none"
|
tech_progress = math.floor(force.research_progress * 100)
|
||||||
local tech_progress = 0
|
end
|
||||||
if force.current_research then
|
local researched = 0
|
||||||
current_tech = force.current_research.name
|
for _, t in pairs(force.technologies) do
|
||||||
tech_progress = math.floor(force.research_progress * 100)
|
if t.researched then researched = researched + 1 end
|
||||||
end
|
end
|
||||||
|
rcon.print(game.table_to_json({
|
||||||
local researched = 0
|
total_entities = total,
|
||||||
for _, t in pairs(force.technologies) do
|
counts = result,
|
||||||
if t.researched then researched = researched + 1 end
|
current_research = current_tech,
|
||||||
end
|
research_progress = tech_progress,
|
||||||
|
researched_count = researched,
|
||||||
rcon.print(game.table_to_json({
|
evolution = math.floor(force.evolution_factor * 100)
|
||||||
total_entities = total,
|
}))
|
||||||
counts = counts,
|
end)
|
||||||
current_research = current_tech,
|
if not ok then rcon.print("{}") end
|
||||||
research_progress = tech_progress,
|
|
||||||
researched_count = researched,
|
|
||||||
evolution = math.floor(force.evolution_factor * 100)
|
|
||||||
}))
|
|
||||||
"""
|
"""
|
||||||
raw = self.rcon.lua(lua)
|
|
||||||
try:
|
try:
|
||||||
return json.loads(raw) if raw else {}
|
raw = self.rcon.lua(lua)
|
||||||
|
return json.loads(raw) if raw and raw.startswith("{") else {}
|
||||||
except Exception:
|
except Exception:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
# ── 구역 요약 ───────────────────────────────────────────────────
|
|
||||||
|
|
||||||
def _get_zone_summaries(self) -> list[dict]:
|
def _get_zone_summaries(self) -> list[dict]:
|
||||||
lua = P + f"""
|
lua = P + """
|
||||||
local surface = p.surface
|
local ok, err = pcall(function()
|
||||||
local center = p.position
|
local surface = p.surface
|
||||||
local Z = {ZONE_SIZE}
|
local Z = 64
|
||||||
local radius = 512
|
local zones = {}
|
||||||
local zones = {{}}
|
local all = surface.find_entities_filtered{position = p.position, radius = 512, force = "player"}
|
||||||
|
for _, e in ipairs(all) do
|
||||||
local function zone_key(x, y)
|
local kx = math.floor(e.position.x / Z)
|
||||||
return math.floor(x/Z) .. "," .. math.floor(y/Z)
|
local ky = math.floor(e.position.y / Z)
|
||||||
end
|
local key = kx .. "," .. ky
|
||||||
|
if not zones[key] then
|
||||||
local all_entities = surface.find_entities_filtered{{
|
zones[key] = {key=key, x=kx*Z, y=ky*Z, entities=0, miners=0, furnaces=0, assemblers=0, belts=0, inserters=0, poles=0, turrets=0, labs=0, pumps=0}
|
||||||
area={{{{center.x-radius, center.y-radius}},
|
end
|
||||||
{{center.x+radius, center.y+radius}}}},
|
local z = zones[key]
|
||||||
force = "player"
|
z.entities = z.entities + 1
|
||||||
}}
|
local t = e.type
|
||||||
|
if t == "mining-drill" then z.miners = z.miners + 1
|
||||||
for _, e in ipairs(all_entities) do
|
elseif t == "furnace" then z.furnaces = z.furnaces + 1
|
||||||
local key = zone_key(e.position.x, e.position.y)
|
elseif t == "assembling-machine" then z.assemblers = z.assemblers + 1
|
||||||
if not zones[key] then
|
elseif t == "transport-belt" then z.belts = z.belts + 1
|
||||||
zones[key] = {{
|
elseif t == "inserter" then z.inserters = z.inserters + 1
|
||||||
key = key,
|
elseif t == "electric-pole" then z.poles = z.poles + 1
|
||||||
x = math.floor(e.position.x/Z)*Z,
|
elseif t == "ammo-turret" or t == "electric-turret" then z.turrets = z.turrets + 1
|
||||||
y = math.floor(e.position.y/Z)*Z,
|
elseif e.name == "lab" then z.labs = z.labs + 1
|
||||||
entities = 0,
|
elseif t == "offshore-pump" then z.pumps = z.pumps + 1
|
||||||
miners = 0, furnaces = 0, assemblers = 0,
|
end
|
||||||
belts = 0, inserters = 0, poles = 0,
|
end
|
||||||
turrets = 0, labs = 0, pumps = 0
|
local result = {}
|
||||||
}}
|
for _, z in pairs(zones) do
|
||||||
end
|
if z.entities > 2 then result[#result+1] = z end
|
||||||
local z = zones[key]
|
end
|
||||||
z.entities = z.entities + 1
|
rcon.print(game.table_to_json(result))
|
||||||
local n = e.name
|
end)
|
||||||
local t = e.type
|
if not ok then rcon.print("[]") end
|
||||||
if t == "mining-drill" then z.miners = z.miners + 1
|
|
||||||
elseif t == "furnace" then z.furnaces = z.furnaces + 1
|
|
||||||
elseif t == "assembling-machine" then z.assemblers = z.assemblers + 1
|
|
||||||
elseif t == "transport-belt" then z.belts = z.belts + 1
|
|
||||||
elseif t == "inserter" then z.inserters = z.inserters + 1
|
|
||||||
elseif t == "electric-pole" then z.poles = z.poles + 1
|
|
||||||
elseif t == "ammo-turret" or t == "electric-turret" then z.turrets = z.turrets + 1
|
|
||||||
elseif n == "lab" then z.labs = z.labs + 1
|
|
||||||
elseif t == "offshore-pump" then z.pumps = z.pumps + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local result = {{}}
|
|
||||||
for _, z in pairs(zones) do
|
|
||||||
if z.entities > 2 then
|
|
||||||
result[#result+1] = z
|
|
||||||
end
|
|
||||||
end
|
|
||||||
rcon.print(game.table_to_json(result))
|
|
||||||
"""
|
"""
|
||||||
raw = self.rcon.lua(lua)
|
|
||||||
try:
|
try:
|
||||||
return json.loads(raw) if raw else []
|
raw = self.rcon.lua(lua)
|
||||||
|
return json.loads(raw) if raw and raw.startswith("[") else []
|
||||||
except Exception:
|
except Exception:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# ── 문제 감지 ───────────────────────────────────────────────────
|
|
||||||
|
|
||||||
def _detect_problems(self) -> list[str]:
|
def _detect_problems(self) -> list[str]:
|
||||||
lua = P + """
|
lua = P + """
|
||||||
local surface = p.surface
|
local ok, err = pcall(function()
|
||||||
local force = p.force
|
local surface = p.surface
|
||||||
local center = p.position
|
local problems = {}
|
||||||
local problems = {}
|
local burners = surface.find_entities_filtered{position = p.position, radius = 300, type = {"mining-drill", "furnace"}}
|
||||||
|
local no_fuel = 0
|
||||||
-- 1. 연료 없는 버너 건물
|
for _, e in ipairs(burners) do
|
||||||
local burner_entities = surface.find_entities_filtered{
|
if e.burner then
|
||||||
area={{center.x-300,center.y-300},{center.x+300,center.y+300}},
|
local fi = e.burner.inventory
|
||||||
type={"mining-drill","furnace"}
|
if fi and fi.is_empty() then no_fuel = no_fuel + 1 end
|
||||||
}
|
end
|
||||||
local no_fuel = 0
|
|
||||||
for _, e in ipairs(burner_entities) do
|
|
||||||
if e.burner then
|
|
||||||
local fi = e.burner.inventory
|
|
||||||
if fi and fi.is_empty() then
|
|
||||||
no_fuel = no_fuel + 1
|
|
||||||
end
|
end
|
||||||
end
|
if no_fuel > 0 then problems[#problems+1] = "연료 부족: " .. no_fuel .. "개" end
|
||||||
end
|
local drills = surface.find_entities_filtered{position = p.position, radius = 300, type = "mining-drill"}
|
||||||
if no_fuel > 0 then
|
local depleting = 0
|
||||||
problems[#problems+1] = "연료 부족: " .. no_fuel .. "개 건물이 연료 없음"
|
for _, d in ipairs(drills) do
|
||||||
end
|
local ore = surface.find_entities_filtered{position = d.position, radius = 3, type = "resource"}
|
||||||
|
if #ore > 0 and ore[1].amount < 5000 then depleting = depleting + 1 end
|
||||||
-- 2. 꽉 찬 삽입기 (병목)
|
end
|
||||||
local inserters = surface.find_entities_filtered{
|
if depleting > 0 then problems[#problems+1] = "자원 고갈 임박: " .. depleting .. "개" end
|
||||||
area={{center.x-300,center.y-300},{center.x+300,center.y+300}},
|
rcon.print(game.table_to_json(problems))
|
||||||
type="inserter"
|
end)
|
||||||
}
|
if not ok then rcon.print("[]") end
|
||||||
local stuck = 0
|
|
||||||
for _, ins in ipairs(inserters) do
|
|
||||||
if ins.held_stack and ins.held_stack.valid_for_read then
|
|
||||||
stuck = stuck + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if stuck > 5 then
|
|
||||||
problems[#problems+1] = "벨트 병목: 삽입기 " .. stuck .. "개가 아이템 들고 멈춤"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- 3. 자원 고갈 임박
|
|
||||||
local drills = surface.find_entities_filtered{
|
|
||||||
area={{center.x-300,center.y-300},{center.x+300,center.y+300}},
|
|
||||||
type="mining-drill"
|
|
||||||
}
|
|
||||||
local depleting = 0
|
|
||||||
for _, d in ipairs(drills) do
|
|
||||||
local ore = surface.find_entities_filtered{
|
|
||||||
area={{d.position.x-3, d.position.y-3},
|
|
||||||
{d.position.x+3, d.position.y+3}},
|
|
||||||
type="resource"
|
|
||||||
}
|
|
||||||
if #ore > 0 and ore[1].amount < 5000 then
|
|
||||||
depleting = depleting + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if depleting > 0 then
|
|
||||||
problems[#problems+1] = "자원 고갈 임박: " .. depleting .. "개 채굴기 구역 5000 미만"
|
|
||||||
end
|
|
||||||
|
|
||||||
rcon.print(game.table_to_json(problems))
|
|
||||||
"""
|
"""
|
||||||
raw = self.rcon.lua(lua)
|
|
||||||
try:
|
try:
|
||||||
return json.loads(raw) if raw else []
|
raw = self.rcon.lua(lua)
|
||||||
|
return json.loads(raw) if raw and raw.startswith("[") else []
|
||||||
except Exception:
|
except Exception:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -243,31 +159,33 @@ rcon.print(game.table_to_json(problems))
|
|||||||
lines.append(
|
lines.append(
|
||||||
f" 구역({z['x']},{z['y']}): "
|
f" 구역({z['x']},{z['y']}): "
|
||||||
f"채굴기{z.get('miners',0)} 제련소{z.get('furnaces',0)} "
|
f"채굴기{z.get('miners',0)} 제련소{z.get('furnaces',0)} "
|
||||||
f"조립기{z.get('assemblers',0)} 벨트{z.get('belts',0)} "
|
f"조립기{z.get('assemblers',0)} 벨트{z.get('belts',0)}"
|
||||||
f"삽입기{z.get('inserters',0)} 전선주{z.get('poles',0)}"
|
|
||||||
)
|
)
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|
||||||
def _get_player_info(self) -> dict:
|
def _get_player_info(self) -> dict:
|
||||||
lua = P + """
|
lua = P + """
|
||||||
local inv = p.get_main_inventory()
|
local ok, err = pcall(function()
|
||||||
if not inv then rcon.print("{}") return end
|
local inv = p.get_main_inventory()
|
||||||
local contents = inv.get_contents()
|
if not inv then rcon.print("{}") return end
|
||||||
|
local inv_summary = {}
|
||||||
local inv_summary = {}
|
for i = 1, #inv do
|
||||||
for name, count in pairs(contents) do
|
local stack = inv[i]
|
||||||
if count > 0 then inv_summary[name] = count end
|
if stack.valid_for_read then
|
||||||
end
|
inv_summary[stack.name] = (inv_summary[stack.name] or 0) + stack.count
|
||||||
|
end
|
||||||
rcon.print(game.table_to_json({
|
end
|
||||||
x = math.floor(p.position.x),
|
rcon.print(game.table_to_json({
|
||||||
y = math.floor(p.position.y),
|
x = math.floor(p.position.x),
|
||||||
inventory = inv_summary
|
y = math.floor(p.position.y),
|
||||||
}))
|
inventory = inv_summary
|
||||||
|
}))
|
||||||
|
end)
|
||||||
|
if not ok then rcon.print("{}") end
|
||||||
"""
|
"""
|
||||||
raw = self.rcon.lua(lua)
|
|
||||||
try:
|
try:
|
||||||
return json.loads(raw) if raw else {}
|
raw = self.rcon.lua(lua)
|
||||||
|
return json.loads(raw) if raw and raw.startswith("{") else {}
|
||||||
except Exception:
|
except Exception:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@@ -315,17 +233,10 @@ rcon.print(game.table_to_json({
|
|||||||
return lines
|
return lines
|
||||||
|
|
||||||
def _guess_zone_role(self, z: dict) -> str:
|
def _guess_zone_role(self, z: dict) -> str:
|
||||||
miners = z.get("miners", 0)
|
if z.get("turrets", 0) > 3: return "방어구역"
|
||||||
furnaces = z.get("furnaces", 0)
|
if z.get("labs", 0) > 0: return "연구실"
|
||||||
assemblers= z.get("assemblers", 0)
|
if z.get("pumps", 0) > 0: return "정유/전력"
|
||||||
turrets = z.get("turrets", 0)
|
if z.get("assemblers", 0) > z.get("miners", 0): return "조립라인"
|
||||||
labs = z.get("labs", 0)
|
if z.get("furnaces", 0) > z.get("miners", 0): return "제련구역"
|
||||||
pumps = z.get("pumps", 0)
|
if z.get("miners", 0) > 0: return "채굴구역"
|
||||||
|
|
||||||
if turrets > 3: return "방어구역"
|
|
||||||
if labs > 0: return "연구실"
|
|
||||||
if pumps > 0: return "정유/전력"
|
|
||||||
if assemblers > miners: return "조립라인"
|
|
||||||
if furnaces > miners: return "제련구역"
|
|
||||||
if miners > 0: return "채굴구역"
|
|
||||||
return "기타"
|
return "기타"
|
||||||
|
|||||||
Reference in New Issue
Block a user