Add world settings viewing with legacy fallback
- Store map generation settings in DB when creating new worlds - Add info button to view settings for each saved world - Show legacy warning for worlds created before this feature 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -179,3 +179,9 @@ export async function getFactorioCurrentSave(token) {
|
|||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getFactorioWorldSettings(token, saveName) {
|
||||||
|
return fetchAPI(`/servers/factorio/saves/${encodeURIComponent(saveName)}/settings`, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
deleteFactorioTemplate,
|
deleteFactorioTemplate,
|
||||||
createFactorioWorld,
|
createFactorioWorld,
|
||||||
deleteFactorioSave,
|
deleteFactorioSave,
|
||||||
|
getFactorioWorldSettings,
|
||||||
serverAction
|
serverAction
|
||||||
} from '../api'
|
} from '../api'
|
||||||
import WorldGenForm from './WorldGenForm'
|
import WorldGenForm from './WorldGenForm'
|
||||||
@@ -25,6 +26,9 @@ export default function FactorioWorldManager({ server, token, onServerAction })
|
|||||||
const [creating, setCreating] = useState(false)
|
const [creating, setCreating] = useState(false)
|
||||||
const [deleteConfirm, setDeleteConfirm] = useState(null)
|
const [deleteConfirm, setDeleteConfirm] = useState(null)
|
||||||
const [actionLoading, setActionLoading] = useState(null)
|
const [actionLoading, setActionLoading] = useState(null)
|
||||||
|
const [viewingSettings, setViewingSettings] = useState(null)
|
||||||
|
const [settingsData, setSettingsData] = useState(null)
|
||||||
|
const [settingsLoading, setSettingsLoading] = useState(false)
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -137,6 +141,35 @@ export default function FactorioWorldManager({ server, token, onServerAction })
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleViewSettings = async (saveName) => {
|
||||||
|
try {
|
||||||
|
setSettingsLoading(true)
|
||||||
|
setViewingSettings(saveName)
|
||||||
|
const data = await getFactorioWorldSettings(token, saveName)
|
||||||
|
setSettingsData(data)
|
||||||
|
} catch (err) {
|
||||||
|
setError(err.message)
|
||||||
|
setViewingSettings(null)
|
||||||
|
} finally {
|
||||||
|
setSettingsLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeSettingsModal = () => {
|
||||||
|
setViewingSettings(null)
|
||||||
|
setSettingsData(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format setting value for display
|
||||||
|
const formatSettingValue = (value) => {
|
||||||
|
if (value === 0) return 'None'
|
||||||
|
if (value <= 0.2) return 'Very Low'
|
||||||
|
if (value <= 0.6) return 'Low'
|
||||||
|
if (value <= 1.2) return 'Normal'
|
||||||
|
if (value <= 2.5) return 'High'
|
||||||
|
return 'Very High'
|
||||||
|
}
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center py-12">
|
<div className="flex items-center justify-center py-12">
|
||||||
@@ -245,6 +278,117 @@ export default function FactorioWorldManager({ server, token, onServerAction })
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* View Settings Modal */}
|
||||||
|
{viewingSettings && (
|
||||||
|
<div className="modal-backdrop" onClick={closeSettingsModal}>
|
||||||
|
<div className="modal max-w-2xl fade-in-scale" onClick={e => e.stopPropagation()}>
|
||||||
|
<div className="modal-header">
|
||||||
|
<h3 className="modal-title">World Settings: {viewingSettings}</h3>
|
||||||
|
<button onClick={closeSettingsModal} className="btn btn-ghost p-1">
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="modal-body">
|
||||||
|
{settingsLoading ? (
|
||||||
|
<div className="text-center py-8 text-neutral-400">Loading settings...</div>
|
||||||
|
) : settingsData?.legacy ? (
|
||||||
|
<div className="bg-neutral-800/50 rounded-lg p-6 text-center">
|
||||||
|
<div className="text-amber-400 mb-2">⚠️ Legacy World</div>
|
||||||
|
<p className="text-neutral-400 text-sm">
|
||||||
|
{settingsData.message}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : settingsData?.settings ? (
|
||||||
|
<div className="space-y-4">
|
||||||
|
{settingsData.createdAt && (
|
||||||
|
<div className="text-neutral-500 text-xs mb-4">
|
||||||
|
Created: {new Date(settingsData.createdAt).toLocaleString()}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Terrain Settings */}
|
||||||
|
<div>
|
||||||
|
<h4 className="text-sm font-medium text-neutral-300 mb-2">Terrain</h4>
|
||||||
|
<div className="grid grid-cols-2 gap-2 text-sm">
|
||||||
|
<div className="flex justify-between p-2 bg-neutral-800/30 rounded">
|
||||||
|
<span className="text-neutral-400">Water</span>
|
||||||
|
<span className="text-white">{formatSettingValue(settingsData.settings.autoplace_controls?.water?.frequency)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between p-2 bg-neutral-800/30 rounded">
|
||||||
|
<span className="text-neutral-400">Trees</span>
|
||||||
|
<span className="text-white">{formatSettingValue(settingsData.settings.autoplace_controls?.trees?.frequency)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between p-2 bg-neutral-800/30 rounded">
|
||||||
|
<span className="text-neutral-400">Cliffs</span>
|
||||||
|
<span className="text-white">{formatSettingValue(settingsData.settings.cliff_settings?.richness)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between p-2 bg-neutral-800/30 rounded">
|
||||||
|
<span className="text-neutral-400">Starting Area</span>
|
||||||
|
<span className="text-white">{formatSettingValue(settingsData.settings.starting_area)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Resource Settings */}
|
||||||
|
<div>
|
||||||
|
<h4 className="text-sm font-medium text-neutral-300 mb-2">Resources</h4>
|
||||||
|
<div className="grid grid-cols-1 gap-2 text-sm">
|
||||||
|
{['iron-ore', 'copper-ore', 'coal', 'stone', 'uranium-ore', 'crude-oil'].map((resource) => {
|
||||||
|
const ctrl = settingsData.settings.autoplace_controls?.[resource]
|
||||||
|
const displayName = resource.replace('-ore', '').replace('-', ' ').replace(/\b\w/g, c => c.toUpperCase())
|
||||||
|
return (
|
||||||
|
<div key={resource} className="flex justify-between p-2 bg-neutral-800/30 rounded">
|
||||||
|
<span className="text-neutral-400">{displayName}</span>
|
||||||
|
<span className="text-white text-xs">
|
||||||
|
Freq: {formatSettingValue(ctrl?.frequency)} · Size: {formatSettingValue(ctrl?.size)} · Rich: {formatSettingValue(ctrl?.richness)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Enemy Settings */}
|
||||||
|
<div>
|
||||||
|
<h4 className="text-sm font-medium text-neutral-300 mb-2">Enemies</h4>
|
||||||
|
<div className="grid grid-cols-2 gap-2 text-sm">
|
||||||
|
<div className="flex justify-between p-2 bg-neutral-800/30 rounded">
|
||||||
|
<span className="text-neutral-400">Biter Bases</span>
|
||||||
|
<span className="text-white">{formatSettingValue(settingsData.settings.autoplace_controls?.['enemy-base']?.frequency)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between p-2 bg-neutral-800/30 rounded">
|
||||||
|
<span className="text-neutral-400">Peaceful Mode</span>
|
||||||
|
<span className="text-white">{settingsData.settings.peaceful_mode ? 'Yes' : 'No'}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Seed */}
|
||||||
|
{settingsData.settings.seed !== undefined && (
|
||||||
|
<div>
|
||||||
|
<h4 className="text-sm font-medium text-neutral-300 mb-2">Advanced</h4>
|
||||||
|
<div className="p-2 bg-neutral-800/30 rounded text-sm">
|
||||||
|
<span className="text-neutral-400">Seed: </span>
|
||||||
|
<span className="text-white font-mono">{settingsData.settings.seed || 'Random'}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="text-center py-8 text-neutral-400">No settings data available</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="modal-footer">
|
||||||
|
<button onClick={closeSettingsModal} className="btn btn-secondary">
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h3 className="text-sm font-medium text-neutral-300">Saved Worlds</h3>
|
<h3 className="text-sm font-medium text-neutral-300">Saved Worlds</h3>
|
||||||
@@ -280,6 +424,15 @@ export default function FactorioWorldManager({ server, token, onServerAction })
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
<button
|
||||||
|
onClick={() => handleViewSettings(save.name)}
|
||||||
|
className="btn btn-ghost text-neutral-400 hover:text-white"
|
||||||
|
title="View world settings"
|
||||||
|
>
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleStartWithSave(save.name)}
|
onClick={() => handleStartWithSave(save.name)}
|
||||||
disabled={actionLoading === save.name || server.running}
|
disabled={actionLoading === save.name || server.running}
|
||||||
|
|||||||
Reference in New Issue
Block a user